GCC生成的ARM和x86汇编代码的差异 [英] Difference in ARM and x86 assembly code generated by GCC

查看:420
本文介绍了GCC生成的ARM和x86汇编代码的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们使用一个简单的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

解决方案

之所以采用这种编码,是因为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

Let us take a simple C code for setting a register:

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

When I compile this code for ARM (arm-none-eabi-gcc) with level 1 optimization, the assembly code is something like:

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

Looks like the address 111111 was resolved to the closest 4K boundary (110592) and moved to r3, and then the value 4096(0x1000) was stored by adding 519 to 110592 (=111111). Why does this happen?

In x86, the assembly is straightforward:

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

解决方案

The reason behind this encoding, is because x86 has variable sized instructions -- from 1 byte up to 16 bytes (and possibly even more with prefixes).

ARM instruction is 32 bits wide (not counting Thumb modes), which means that it's simply not possible to encode all 32-bit wide constants (immediates) in a single opcode.

Fixed sized architectures typically use a few methods to load large constants:

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

Variable sized architectures OTOH can put all the constants in a single instruction:

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

这篇关于GCC生成的ARM和x86汇编代码的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆