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

Abh*_*ury 2 compiler-construction x86 assembly gcc arm

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

int main()
{
    int *a = (int*)111111;
    *a = 0x1000;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

mov     r2, #4096
mov     r3, #110592
str     r2, [r3, #519]
mov     r0, #0
bx      lr
Run Code Online (Sandbox Code Playgroud)

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

在x86中,程序集很简单:

movl    $4096, 111111
movl    $0, %eax
ret
Run Code Online (Sandbox Code Playgroud)

Aki*_*nen 6

这种编码背后的原因是因为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
Run Code Online (Sandbox Code Playgroud)

可变大小的体系结构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
Run Code Online (Sandbox Code Playgroud)