GCC生成的ARM和x86汇编代码的差异

时间:2014-02-23 12:36:13

标签: gcc assembly compiler-construction x86 arm

让我们用一个简单的C代码来设置寄存器:

int main()
{
    int *a = (int*)111111;
    *a = 0x1000;
    return 0;
}

当我使用1级优化编译ARM(arm-none-eabi-gcc)的代码时,汇编代码如下:

mov     r2, #4096
mov     r3, #110592
str     r2, [r3, #519]
mov     r0, #0
bx      lr

看起来地址111111被解析为最近的4K边界(110592)并移动到r3,然后通过将519添加到110592(= 111111)来存储值4096(0x1000)。为什么会这样?

在x86中,程序集很简单:

movl    $4096, 111111
movl    $0, %eax
ret

3 个答案:

答案 0 :(得分:5)

这种编码背后的原因是因为x86具有可变大小的指令 - 从1个字节到16个字节(可能还有更多的前缀)。

ARM指令是32位宽(不计算Thumb模式),这意味着根本不可能在单个操作码中编码所有32位宽常数(即时)。

固定大小的体系结构通常使用一些方法来加载大常量:

1)  movi  #r1, Imm8  ; // Here Imm8 or ImmX is simply X least significant bits
2)  movhi #r1, Imm16 ; // Here Imm16 loads the 16 MSB of the register
3)  load  #r1, (PC + ImmX);  // use PC-relative address to put constant in code
4)  movn  #r1, Imm8 ;  // load the inverse of Imm8 (for signed constants) 
5)  mov(i/n) #1, Imm8 << N;       // where N=0,8,16,24

可变大小的体系结构OTOH可以将所有常量放在一条指令中:

xx xx xx 00 10 00 00 11 11 11 00 ; // assuming that it takes 3 bytes to encode
                                 ; // the instruction and the addressing mode
; added with 4 bytes to encode the 4096 and 4 bytes to encode 0x00111111

答案 1 :(得分:2)

地址必须分为两部分,因为这个特定的常量不能用一条指令加载到寄存器中。

ARM documentation指定了某些指令(例如MOV)中允许的直接常量的限制:

  

在ARM指令中,常量可以包含任何可以生成的值   通过将a 8位值向右旋转任意偶数位   32位字。

     

在32位Thumb-2指令中,常量可以是:

     

通过移动剩余的8位值可以产生的任何常数   32位字内的任意位数。

     

任何形式为0x00XY00XY的常量。
  任何形式为0xXY00XY00的常量   任何形式为0xXYXYXYXY的常量。

111111(十六进制中的1B207)不能表示为上述任何一个,因此编译器必须将其拆分。

1105921B000因此它满足第一个条件(8位值0x1B向左旋转12位)并且可以使用MOV指令加载。

另一方面,STR指令对于使用的偏移量有a different set of limitations。特别是,519(0x207)属于ARM模式下字存储/加载所允许的-4095到4095范围。


在这种特定情况下,编译器设法仅将常量拆分为两部分。如果您的立即有更多位,则可能必须生成更多指令,或使用文字池加载。例如,如果我使用0xABCDEF78,我会得到这个(对于ARMv7):

movw    r3, #61439
movt    r3, 43981
mov     r2, #4096
str     r2, [r3, #-135]
mov     r0, #0
bx      lr

对于没有MOVW / MOVT的架构(例如ARMv4),GCC似乎回归到文字池:

    mov     r2, #4096
    ldr     r3, .L2
    str     r2, [r3, #-135]
    mov     r0, #0
    bx      lr
.L3:
    .align  2
.L2:
    .word   -1412567041

答案 2 :(得分:1)

编译器可能正在利用ARM immediate value encoding来减少代码大小。基本上110592是0x1B << 12,这可以实现一些简化。看一下程序arm-none-eabi-objdump -d的输出,检查每条指令的长度。