- 一.C數據類型和對齊
- 二.RVG調用約定
- 三.Soft-Float調用約定
- 四.總結
- 使用編譯器生成匯編代碼分析調用過程
- 五.參考
一.C數據類型和對齊
所有數據保持自然對齊。
ILP32,LP64
| C type | Description | Bytes in RV32 | Bytes in RV64 |
|---|---|---|---|
| char/unsigned char | 8-bit unsigned integer,zero-extended | 1 | 1 |
| signed char | 8-bit signed integer,sign-extended | ||
| short | 16-bit signed integer,sign-extended | 2 | 2 |
| unsigned short | 16-bit unsigned integer,zeroextended | ||
| int | int都是32位 | 4 | 4 |
| long | 指針和long和整數寄存器一樣寬 | 4 | 8 |
| long long | long long都是64位 | 8 | 8 |
| void * | 指針和long和整數寄存器一樣寬 | 4 | 8 |
| float | 32-bit IEEE 754-2008 | 4 | 4 |
| double | 64-bit IEEE 754-2008 | 8 | 8 |
| long double | 128-bit IEEE floating-point | ||
| IEEE floating-point | 16 | 16 |
在RV64中,32位類型不管是int還是unsigned都是符號擴展到64位。
二.RVG調用約定
a0-a7,fa0-fa7:用于函數傳遞參數,其中0-1用于返回值,a表示arguments。
都是調用者負責保存,因為是傳參肯定是在函數調用前就要準備好,所部不可能是被調用者去負責保存。
- 如果函數參數為結構體的字段,每一個都是指針對齊的,則參數寄存器是結構體前面8個指針字
pointer-words的影子shadow。
如果是小于8個的浮點值,則使用fai傳遞;小于8個的整數則使用ai傳遞。
如果浮點參數是聯合體unions的字段,或者結構體的數組字段,則使用整數寄存器傳遞。
另外可變參數函數中除了顯示指定的參數外的參數,如果是浮點數也是使用整數寄存器傳遞。
- 小于指針字
pointer-word的參數使用低位傳遞,子指針字sub-pointer-word的參數通過棧傳遞時,使用指針字pointer-word的低地址,因為RISC-V是小端的存儲系統。 - 當原始參數兩倍于指針字
pointer-word時通過棧傳遞,使用自然對齊。當它們使用整數寄存器傳遞時,使用對齊的偶-奇寄存器對,偶寄存器存低位。比如RV32的void foo(int, long long)使用a0傳遞第一個參數,a2-a3傳遞第二個參數,因為由偶寄存器對齊,且a2存低位,返回值通過a0傳遞。 - 兩倍于指針字
pointer-word的參數通過引用傳遞。 - 結構體中部分參數未使用整數寄存器傳遞的使用棧傳遞,棧指針
sp指向第一個未使用整數寄存器傳遞的參數。 a0,a1,fa0,fa1用于函數返回值。只有結構體成員只有一個或者兩個浮點成員,或者primitives時才使用浮點寄存器返回;其他的由a0-a1組成的兩倍指針字的two pointer-words大小返回;更大的返回值通過內存傳遞;調用者負責分配這個內存,并傳遞指向該內存的指針,隱含的作為第一個參數傳遞給被調用者。- 標準
RISC-V調用中,棧向下生長,并且保持16字節對齊。 - 7個臨時整數寄存器
t0-t6,12個臨時浮點寄存器ft0-ft11在調用過程是可變的,如果后面需要使用則必須由調用者負責保存。其中t表示Temporaries。
- 這里有點疑惑,臨時寄存器是被調用者使用的,只有被調用者才知道自己要用哪些寄存器,為什么不是被調用者負責保存?
這樣理解,因為這些寄存器是可變的,對于被調用者來說既然是可變的則可以隨便使用,也就是可能被被調用者修改,所以對于調用者來說,如果這些寄存器的值不能被破壞則自己需要負責保存。
- 12個整數寄存器
s0-s11,12個浮點寄存器fs0-fs11在調用過程是必須保持的,所以如果被調用者需要使用則必須由被調用者保存。
實際上上面的8和9.,t和s寄存器的可變volatile和保持preserved是對被調用者來說的,也就是對被調用者申明,告訴被調用者,
t這些寄存器是可變的,那么被調用者可以隨便使用,此時調用者則必須考慮被被調用者隨便使用而修改,需要調用者保存;
s這些寄存器是保持的,那么被調用者不能隨便使用,如果要用就要負責保存。
所以對于a寄存器也可以這樣理解,因為a寄存器用于傳遞參數,所以是被調用者隨便使用的,即不保持的,所以需要調用者負責保存,并賦參數值。
| Register | ABI Name | Description | Saver |
|---|---|---|---|
| x0 | zero | 硬件固定為0 | / |
| x1 | ra | 返回地址 | Caller調用者 |
| x2 | sp | 棧指針 | Callee被調用者 |
| x3 | gp | 全局指針 | / |
| x4 | tp | 線程指針 | / |
| x5-x7 | t0-t2 | 臨時使用 | Caller調用者 |
| x8 | s0/fp | 保存寄存器/幀指針 | Callee被調用者 |
| x9 | s1 | 保存寄存器 | Callee被調用者 |
| x10-x11 | a0-a1 | 函數參數/返回值 | Caller調用者 |
| x12-x17 | a2-a7 | 函數參數 | Caller調用者 |
| x18-x27 | s2-s11 | 保存寄存器 | Callee被調用者 |
| x28-x31 | t3-t6 | 臨時使用 | Caller調用者 |
| f0-f7 | ft0-ft7 | FP臨時使用 | Caller調用者 |
| f8-f9 | fs0-fs1 | FP保存寄存器 | Callee被調用者 |
| f10-f11 | fa0-fa1 | FP函數參數/返回值 | Caller調用者 |
| f12-f17 | fa2-fa7 | FP參數 | Caller調用者 |
| f18-f27 | fs2-fs11 | FP保存寄存器 | Callee被調用者 |
| f28-f31 | ft8-ft11 | FP臨時使用 | Caller調用者 |
三.Soft-Float調用約定
在沒有浮點硬件,或者不使用F,D,Q擴展的硬件浮點,不使用浮點寄存器,完全由軟件實現浮點。
整數參數的傳入和返回值和RVG一樣。
浮點參數和返回值,通過整數寄存器傳遞,原則是使用大小相同的整數寄存器傳遞。
比如RV32的
double foo(int, double, long double)
則第一個參數通過a0傳遞;
第二個參數通過a2和a3傳遞;
第三個參數通過a4傳引用傳遞;
結果通過a0和a1傳遞。
如果是RV64則
則第一個參數通過a0傳遞;
第二個參數通過a1傳遞;
第三個參數通過a2-a3傳遞;
結果通過a0傳遞。
動態舍入模式和產生的異常標志通過C99的fenv.h提供的接口訪問。
四.總結
從以下幾個部分去理解
- 寄存器
理解函數參數的傳遞與返回值,a0-a1,a2-a7,fa0-fa1,fa2-fa7,0-1用于返回值。
理解ra寄存器,函數的返回地址
理解SP棧指針,理解棧的向下生長,理解進入子函數時減少sp分配空間,分配的空間用于存儲s寄存器和局部變量使用,和退出子函數時增加sp恢復sp。也就是調用完子函數返回后sp要保持不變。
理解t0-t6,ft0-ft11;s0-s11.fs0-fs11,這里重點站在被調用者角度去理解可變和保持,進而理解誰負責保存寄存器。
- 函數調用
jal ra label或者jal ra rd imm簡化為偽指令jal label或者jalr rd(立即數為0)。jal跳轉即將PC + 4存儲到ra寄存器,即函數返回后的下一條執行的指令。jalr類似只是設置PC為rd + imm。
注意與無條件跳轉jal x0 label和jalr x0 rd imm,偽指令j label ,jr rd(立即數為0)的區別,無條件跳轉是不返回了的所以不保存返回地址到ra,而是保存到了x0寄存器,而x0寄存器是硬件固定為0的,所以相當于不保存,
兩者指令是統一的,這也體現了RISC-V指令設計的簡潔統一的美學。
其中jal的l可以理解為link,類似于ARM的LR寄存器的L。
- 進入和退出函數
除非使用棧傳遞參數,否則子函數返回后sp必須保持不變。
所有的s寄存器在子函數返回后必須保持,這也是其保持的含義,也是為什么被調用者需要負責保存。
子函數退出時返回ra處執行
函數進入時的處理:減少sp,s寄存器個數和局部變量大小的空間,存儲使用到的s寄存器到棧中。如果還有子函數調用則存儲ra到棧中(因為子函數的子函數的返回值要存到ra會覆蓋ra)。
函數退出時的處理:恢復棧中保存的s寄存器,更新sp值。如果有需要恢復ra值,恢復sp值到函數進入之前的值,返回到ra處執行。
最好通過編寫c代碼,使用編譯工具生成匯編代碼,對照c和匯編代碼的方式去理解。
五.參考
-
riscv-calling.pdf [Volume I: RISC-V User-Level ISA V2.1draft:Chapter 18Calling Convention]
-
Understanding RISC-V Calling Convention.pdf [Nick Riasanovsky]
審核編輯:湯梓紅
-
代碼
+關注
關注
30文章
4967瀏覽量
73960 -
編譯器
+關注
關注
1文章
1672瀏覽量
51598 -
數據類型
+關注
關注
0文章
237瀏覽量
14185 -
RISC-V
+關注
關注
48文章
2886瀏覽量
52989
發布評論請先 登錄
為什么選擇RISC-V?
RISC-V是什么
一文看懂RISC-V代碼密度
RISC-V 發展
RISC-V規范的演進 RISC-V何時爆發?
RISC-V學習筆記【1】RISC-V概述
openEuler加入RISC-V Landscape
解鎖RISC-V技術力量丨曹英杰:RISC-V與大模型探索
RISC-V Summit China 2024 青稞RISC-V+接口PHY,賦能RISC-V高效落地
加入全球 RISC-V Advocate 行列,共筑 RISC-V 的未來 !
RISC-V ABI約定
評論