首頁>技術>

鴻蒙核心原始碼中文註解 >> 精讀核心原始碼,中文註解分析,深挖地基工程,大腦永久記憶,四大原始碼倉每日同步更新

#include <stdio.h>#include <math.h>struct reg{//引數遠超暫存器數量    int Rn[100];     int pc;};int framePoint(reg cpu){    return cpu.Rn[0] * cpu.pc;}int main(){    reg cpu;    cpu.Rn[0] = 1;    cpu.pc = 2;    return framePoint(cpu);}
//編譯器: armv7-a gcc (9.2.1)framePoint(reg):        sub     sp, sp, #16     @申請棧空間        str     fp, [sp, #-4]!  @保護main函式棧幀,等同於push {fp}        add     fp, sp, #0      @fp變成framePoint棧幀,同時也指向了棧頂        add     ip, fp, #4      @定位到入棧口,讓4個引數依次入棧         stm     ip, {r0, r1, r2, r3}@r0-r3依次入棧儲存        ldr     r3, [fp, #4]    @取值cpu.pc = 2            ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1        mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc        mov     r0, r3          @返回值由r0儲存        add     sp, fp, #0      @重置sp,和add     fp, sp, #0配套出現        ldr     fp, [sp], #4    @恢復main函式棧幀        add     sp, sp, #16     @歸還棧空間,sp回落到main函式棧頂位置        bx      lr              @跳回main函式main:        push    {fp, lr}        @入棧儲存呼叫函式現場                             add     fp, sp, #4      @fp指向sp+4,即main棧幀的底部        sub     sp, sp, #800    @分配800個線性地址,即main棧幀的頂部        mov     r3, #1          @r3 = 1        str     r3, [fp, #-408] @將1放置 fp-408處,即:cpu.Rn[0]處        mov     r3, #2          @r3 = 2        str     r3, [fp, #-8]   @將2放置 fp-8處,即:cpu.pc        mov     r0, sp          @r0 = sp        sub     r3, fp, #392    @r3 = fp - 392        mov     r2, #388        @只複製388,剩下4個由暫存器傳參        mov     r1, r3          @儲存由r1儲存r3,用於memcpy        bl      memcpy          @複製結構體部分內容,將r1的內容複製r2的數量到r0        sub     r3, fp, #408    @定位到結構體剩餘未複製處        ldm     r3, {r0, r1, r2, r3} @將剩餘結構體內容透過暫存器傳參        bl      framePoint(reg)         @跳轉到framePoint執行        mov     r3, r0          @framePoint的返回值先給r3        nop @用於程式指令的對齊        mov     r0, r3          @再將返回值給r0        sub     sp, fp, #4      @恢復SP值        pop     {fp, lr}        @出棧恢復呼叫函式現場        bx      lr              @跳回呼叫函式

兩個函式對應兩段彙編,乾淨利落,去除中間各項干擾,只有一個結構體reg,以下詳細講解如何傳遞它,以及它在棧中的資料變化是怎樣的?

入參方式

結構體總共101個棧空間(一個棧空間單位四個位元組),對應就是404個線性地址.main上來就申請了 sub sp, sp, #800 @申請800個線性地址給main,即 200個棧空間

int main(){    reg cpu;    cpu.Rn[0] = 1;    cpu.pc = 2;    return framePoint(cpu);}

但main函式只有一個變數,只需101個棧空間,其他都算上也用不了200個.為什麼要這麼做呢?而且注意下里面的數字 388, 408, 392 這些都是什麼意思?看完main彙編能得到一個結論是 200個棧空間中除了存放了main函式本身的變數外 ,還存放了要傳遞給framePoint函式的部分引數值,存放了多少個?答案是 388/4 = 97個. 注意變數沒有共用,而是複製了一部分出來.如何複製的?繼續看

memcpy彙編呼叫
        mov     r0, sp          @r0 = sp        sub     r3, fp, #392    @r3 = fp - 392        mov     r2, #388        @只複製388,剩下4個由暫存器傳參        mov     r1, r3          @儲存由r1儲存r3,用於memcpy        bl      memcpy          @複製結構體部分內容,將r1的內容複製r2的數量到r0        sub     r3, fp, #408    @定位到結構體剩餘未複製處        ldm     r3, {r0, r1, r2, r3} @將剩餘結構體內容透過暫存器傳參

看這段彙編複製,意思是從r1開始位置複製r2數量的資料到r0的位置,注意只複製了 388個,也就是 388/4 = 97個棧空間.剩餘的4個透過暫存器傳的引數.ldm代表從fp-408的位置將記憶體地址的值連續的給r0 - r3暫存器,即位置(fp-396,fp-400,fp-404,fp-408)的值.執行下來的結果就是

r3 = fp-408, r2 = fp-404 ,r1 = fp-400 ,r0 = fp-396 得到虛擬地址的值,這些值整好是memcpy沒有複製到變數剩餘的值
逐句分析 framePoint
framePoint(reg):        sub     sp, sp, #16     @申請棧空間        str     fp, [sp, #-4]!  @保護main函式棧幀,等同於push {fp}        add     fp, sp, #0      @fp變成framePoint棧幀,同時也指向了棧頂        add     ip, fp, #4      @定位到入棧口,讓4個引數依次入棧         stm     ip, {r0, r1, r2, r3}@r0-r3入棧儲存        ldr     r3, [fp, #4]    @取值cpu.pc = 2            ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1        mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc        mov     r0, r3          @返回值由r0儲存        add     sp, fp, #0      @重置sp,和add     fp, sp, #0配套出現        ldr     fp, [sp], #4    @恢復main函式棧幀        add     sp, sp, #16     @歸還棧空間,sp回落到main函式棧頂位置        bx      lr              @跳回main函式
framePoint申請了4個棧空間目的是用來存放四個暫存器值的,以上彙編程式碼逐句分析.第一句: sub     sp, sp, #16     @申請棧空間,用來存放r0-r3四個引數第二句: str     fp, [sp, #-4]!  @保護main的fp,等同於push {fp},為什麼這裡要把main函式的fp放到 [sp, #-4]! 位置,注意 !號,表示SP的位置要變動,因為這裡必須要保證引數的連續性.第三句: add     fp, sp, #0      @指定framePoint的棧幀位置,同時指向了棧頂 SP第四句: add     ip, fp, #4      @很關鍵,用了ip暫存器,因為此時 fp sp 都已經確定了,但別忘了 r0 - r3 還沒有呢.從哪個位置入棧呢, fp+4位置,因為 main函式的棧幀已經入棧了,在已經fp的位置.中間隔了四個空位,就是給 r0-r3留的.第五句: stm     ip, {r0, r1, r2, r3}@r0-r3入棧,填滿了剩下的四個空位.第六句: ldr     r3, [fp, #4]    @取的就是cpu.pc = 2的值,因為上一句就是從這裡依次入棧的,最後一個當然就是cpu.pc了.第七句: ldr     r2, [fp, #404]  @取值cpu.Rn[0] = 1,其實這一句已經是跳到了main函式的棧幀取值了,所以看明白了沒有,並不是在傳統意義上理解的在framePoint的棧幀中取值.第八句: mul     r3, r2, r3      @cpu.Rn[0] * cpu.pc 做乘法運算第九句: mov     r0, r3          @返回值r0儲存運算結構, 目的是return第十句: add sp, fp, #0          @重置sp,其實這一句可以最佳化掉,因為此時sp = fp第十一句: ldr     fp, [sp], #4  @恢復fp,等同於pop {fp},因為函式執行完了,需要回到main函數了,所以要拿到main的棧幀第十二句: add     sp, sp, #16   @歸還棧空間,等於把四個入參抹掉了.最後一句: bx      lr            @跳回main函式,如此 fp 和 lr 暫存器中儲存的都是 main函式的資訊,就可以安全著陸了.
總結

因為暫存器數量有限,所以只能透過這種方式來傳遞大的引數,想想也只能在main函式棧中儲存大部分引數,同時又必須確保資料的連續性,好像也只能用這種辦法了,一部分透過暫存器傳,一部分透過複製的方式倒是挺有意思的.

鴻蒙核心原始碼中文註解 >> 精讀核心原始碼,中文註解分析,深挖地基工程,大腦永久記憶,四大原始碼倉每日同步更新

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 圖解 LRU LFU ARC FIFO 快取淘汰演算法