让我们用一个简单的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
编译器可能正在利用ARM即时值编码来减小代码大小。基本上110592是0x1B
地址必须分成两部分,因为这个特定常量不能用一条指令加载到寄存器中。
ARM文档规定了某些指令(如MOV)中允许的立即数常量的限制:
在ARM指令中,常量可以具有任何值,该值可以通过将8位值右旋转32位字中的任何偶数位来产生。
在32位Thumb-2指令中,常量可以是:
通过将一个8位值左移32位字中的任意位数而产生的任何常量。
0x00XY00XY形式的任何常数<0xXY00XY00形式的任何常数<0xXYXYXYXY形式的任何常数。
值111111(十六进制中的1B207)不能表示为上述任何值,因此编译器必须将其拆分。
110592是1B000,因此它满足第一个条件(8位值0x1B向左旋转12位),可以使用MOV指令加载。
另一方面,STR
指令对使用的偏移量有一组不同的限制。特别是,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
这种编码背后的原因是,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
我正在为64位mips机器使用gcc编译器。我注意到生成的一段汇编代码很有趣。下面是详细信息: 通常,bnez将立即跳到0xb0。但在0xb0之后的块中,我确信程序必须使用a1作为参数。但是我们可以看到,在0xb0之后,a1从未出现在块中。 但是a1在0x58中使用,就在bnez(0x54)之后。 那么0x54和0x58指令有可能同时执行吗?超标量处理器通过同时将多条指令分派到处理器上的冗余功能单
至少在GCC中,如果我们提供生成汇编代码的选项,编译器会通过创建一个包含汇编代码的文件来服从。但是,当我们简单地运行命令而没有任何选项时,它不会在内部生成汇编代码吗? 如果是,那么为什么它需要首先生成一个汇编代码,然后将其翻译成机器语言?
我试图翻译成x86汇编,以帮助我更好地理解在x86汇编中编码的概念,我觉得自己被困在了如何开始编写这段代码上。
问题内容: 我想分解一下我拥有的可引导x86磁盘的MBR(前512个字节)。我已将MBR复制到使用 对可以反汇编文件的Linux实用程序有何建议? 问题答案: 您可以使用objdump。根据本文的语法为:
我还想知道是否会有更直接的方法来编译和运行生成的代码。
因此,通常关于通过汇编代码提高性能的问题的答案是“不要打扰,编译器比你聪明”。我明白了。 但是,我注意到优化的线性代数库(例如ACML)可以比标准编译库实现2到5倍的性能改进。例如,在我的8核机器上,与现有的单线程BLAS实现相比,优化的矩阵乘法运行速度快了30倍以上,这意味着,在考虑了由于使用所有内核而提高的8倍之后,仅仅通过优化仍然可以提高4倍。 所以在我看来,优化的汇编代码确实可以带来巨大的