指令清单 C/C++ 代码


int f()
{
    return 123;
}

1. x86

在开启优化功能之后,GCC编译器产生的汇编指令,如下所示
Optimizing GCC/MSVC(汇编输出)

mov eax. 123
ret

MSVC编译的程序和上述指令完全一致
这个函数仅由两条指令构成:第一条指令把数值123存放在EAX寄存器里;根据函数调用约定,后面一条指令会把EAX的指当作返回值传递给调用者函数,而调用者函数(caller)会从EAX寄存器里取值,把它当作返回结果。

2. ARM

Optimizing Keil 6/2013(ARM模式)

PROC
    MOV  r0, #0x7b ;123
    BX     lr
    ENDP

ARM程序使用R0寄存器传递函数返回值,所以指令把数值123赋值给R0.
ARM程序使用LR寄存器(Link Register)存储函数结束之后的返回地址(RA/Return Address).x86程序使用“栈”结构存储上述返回地址。可见, BX lr指令作用是跳转到返回地址,即返回到调用者函数,然后继续执行调用体caller的后续指令。

x86和ARM指令集的MOV指令确实和对应单词“move”没有什么瓜葛。它的作用是复制copy,而非移动move。

3. MIPS

在MIPS指令集里,寄存器有两种命名方式。一种是以数字命名($0~$31),另一种则是以伪名称(pseudoname)命名($V0~VA0,依次类推)。在GCC编译器生成的汇编指令中,寄存器都采用数字方式命名。
Optimizing GCC 4.45(汇编输出)

j  $31
li $2, 123   #0x7b

然而IDA会显示寄存器的伪名称。

Optimizing GCC 4.45 (IDA)

jr  $ra
li $v0, 0x7B

根据伪名称和寄存器数字编号的关系可知,存储函数返回值的寄存器都是$2即($V0).此处LI指令是英文词组“Load Immediate(加载立即数)”的缩写。
其中,j和jr指令都属跳转指令,它们把执行流递交给调用者函数,跳转到$31即$RA寄存器中的地址。这个寄存器相当于ARM平台的LR寄存器。
此外,为什么复制指令LI和转移指令J/JR的位置反过来了?这属于RISC精简指令集的特性之一 --- 分支(转移)延迟槽(Branch delay slot)的现象。简单地说,不管分支(转移)发生与否,位于分支指令的一条指令(在延迟槽里的指令),总是被先于分支指令提交。这是RISC精简指令集的一种特例。我们不必在此处深究。总之,转移指令后面的这条复制指令。实际上是在转移指令之前运行的。


pumpkin9
10 声望3 粉丝

学习要看到核心,抓住本质。 --EX