指令清單 C/C++ 代碼函數
int f() { return 123; }
在開啓優化功能以後,GCC編譯器產生的彙編指令,以下所示
Optimizing GCC/MSVC(彙編輸出)優化
mov eax. 123 ret
MSVC編譯的程序和上述指令徹底一致
這個函數僅由兩條指令構成:第一條指令把數值123存放在EAX寄存器裏;根據函數調用約定,後面一條指令會把EAX的指看成返回值傳遞給調用者函數,而調用者函數(caller)會從EAX寄存器裏取值,把它看成返回結果。code
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。
在MIPS指令集裏,寄存器有兩種命名方式。一種是以數字命名($0~$31),另外一種則是以僞名稱(pseudoname)命名($V0~VA0,依次類推)。在GCC編譯器生成的彙編指令中,寄存器都採用數字方式命名。
Optimizing GCC 4.45(彙編輸出)class
j $31 li $2, 123 #0x7b
然而IDA會顯示寄存器的僞名稱。程序
Optimizing GCC 4.45 (IDA)im
jr $ra li $v0, 0x7B
根據僞名稱和寄存器數字編號的關係可知,存儲函數返回值的寄存器都是$2即($V0).此處LI指令是英文詞組「Load Immediate(加載當即數)」的縮寫。
其中,j和jr指令都屬跳轉指令,它們把執行流遞交給調用者函數,跳轉到$31即$RA寄存器中的地址。這個寄存器至關於ARM平臺的LR寄存器。
此外,爲何複製指令LI和轉移指令J/JR的位置反過來了?這屬於RISC精簡指令集的特性之一 --- 分支(轉移)延遲槽(Branch delay slot)的現象。簡單地說,無論分支(轉移)發生與否,位於分支指令的一條指令(在延遲槽裏的指令),老是被先於分支指令提交。這是RISC精簡指令集的一種特例。咱們沒必要在此處深究。總之,轉移指令後面的這條複製指令。其實是在轉移指令以前運行的。call