首页 > 代码库 > ARMV8 Procedure Call Standard
ARMV8 Procedure Call Standard
1.前言
2. 术语说明
Term |
Note |
ABI |
Application Binary Interface 应用程序二进制接口 |
EABI |
Embedded ABI 嵌入式ABI |
PCS |
Procedure Call Standard 程序调用标准 |
AAPCS |
PCS for ARM Architecture ARM体系结构程序调用标准 |
APCS |
ARM Procedure Call Standard ARM程序调用标准(已经废弃) |
TPCS |
Thumb Procedure Call Standard Thumb程序调用标准(已经废弃) |
ATPCS |
ARM-Thumb Procedure Call Standard |
PIC,PID |
Position-independent code, position-independent data 位置独立代码和数据 |
Activation(call-frame) stack |
程序调用记录栈 |
Activation record(call frame) |
用于程序保存寄存器和本地变量的内存(通常在栈上申请) |
Variadic routine |
模板函数, 由调用程序决定它的参数个数和类型,而不是被调用函数 |
Scratch register( temporary register) |
草稿寄存器, 用来保存计算的中间值(通常在程序中未命名且有限的生命周期). |
Variable register |
变量寄存器, 用来保存变量的值, 通常用于程序的本地变量 |
Program State |
程序状态, 程序的内存状态,包括寄存器的值 |
3. AAPCS的范围
AAPCS64定义了各个子函数如何被汇编到一起协同工作。描述了调用者例程和被调用者例程或例程与它的执行环境间的约定,主要包括:
(1)在被调用程序(called)开始执行之前, 调用者(caller)负责创建一个程序状态 (一般是SP设置,以及参数传递)
(2)被调用程序保存调用者(caller)传递过来的程序状态
(3)被调用程序正确修改它的程序状态
(4)所有例程保持全局变量不变;
指定了一组PCS各个变体的一些比较重要的选择,包括:
(1)字节序;
(2)数据类型的大小和格式;
(3)浮点操作使用硬浮点还是软浮点;
AAPCS包括4个小节:
(1)Data的排布;
(2)栈的排布,函数之间的调用;
(3)处理器扩展的变化,或何时执行环境约束地址模型???
(4)C C++语言数据类型的绑定???
4. 数据类型与对齐
5. 寄存器的使用规则
5.1 通用目的的寄存器
5.2 SIMD和float point寄存器
(1) v0-v7, are used to pass argument values into a subroutine and to return result values from
(2) a function;
(3) Registers v8-v15 must be preserved by a callee across subroutine calls
(4) the remaining registers (v0-v7, v16-v31)do not need to be preserved (or should be preserved by the caller)
(5) only the bottom 64 bits of each value stored in v8-v15 need to be preserved1; it is the responsibility of the caller to preserve larger values
(6) The FPSR is a status register that holds the cumulative exception bits of the floating-point unit
It contains the fields IDC, IXC, UFC, OFC, DZC, IOC and QC. These fields are not preserved across a public interface and may have any value on entry to a subroutine.
The FPCR is used to control the behavior of the floating-point unit. It is a global register with the following properties.
o The exception-control bits (8-12), rounding mode bits (22-23) and flush-to-zero bits (24) may be modified by calls to specific support functions that affect the global state of the application.
o All other bits are reserved and must not be modified. It is not defined whether the bits read as zero or one, or whether they are preserved across a public interface
6. processes, Memory and Stack的使用规则
6.1 进程的组成部分
一个进程的memory通常可以分为5部分,除了栈要求是连续的,其它部分可以不连续;一个进程只有code和stack是必须的,其它可以没有。
categories |
note |
code (the program being executed) |
which must be readable, but need not be writable, by the process. |
read-only static data |
|
writable static data |
进一步划分为initialized, zero-initialized and uninitialized data |
the heap |
内存区域,由进程自己管理 |
the stack |
栈是连续的内存区域,用来存储本地变量,当参数寄存器不够用时,会通过栈传递参数给子函数; 栈是向下增长的,栈指针保存在特殊功能寄存器SP中; 栈可以动态增长也可以是定长的。 栈的通用约束: (1)SP只能访问stack base和stack limit之间的memory,即Stack-limit < SP <= stack-base (2)SP必须对齐在16个字节上,即SP mod 16 = 0 (3)函数只能访问自己能回溯的那些栈帧。例如f1调用f2,而f2函数又调用了f3,那么f3是可以访问自己的stack以及f2和f1的stack,也就是说,函数可以访问[SP, stack-base – 1]之间的内容; 调用外部接口的栈约束: SP必须对齐在16个字节上,即SP mod 16 = 0 |
6.2 内存地址
1. 内存地址是由一个或多个互斥的区域组成的,任何一个区域都不会跨越地址0;
2. Tag 地址???
6.3 Stack
1.栈是连续的内存区域,用来存储本地变量,当参数寄存器不够用时,会通过栈传递参数给子函数; 栈是向下增长的,栈指针保存在特殊功能寄存器SP中; 栈可以动态增长也可以是定长的。 2. 栈的通用约束: (1)SP只能访问stack base和stack limit之间的memory,即Stack-limit < SP <= stack-base (2)SP必须对齐在16个字节上,即SP mod 16 = 0 (3)函数只能访问自己能回溯的那些栈帧。例如f1调用f2,而f2函数又调用了f3,那么f3是可以访问自己的stack以及f2和f1的stack,也就是说,函数可以访问[SP, stack-base – 1]之间的内容; 3. 调用外部接口的栈约束: SP必须对齐在16个字节上,即SP mod 16 = 0 |
6.4 栈帧指针
每个被调用的函数都有一个栈帧结构(位于SP与FP之间),它存储在栈中,通过栈中两个64bit值链接在调用者函数的栈帧后面,高地址的64bit保存返回地址LR,低地址的64bit值保存调用者函数的栈帧指针FP,由于被调用函数的FP+4就是调用者函数的SP指针,因此不需要保存;被调用者FP指向保存调用者函数栈帧地址的低64bit的位置,表示被调用函数栈帧的开始,SP指向被调用函数栈帧的结束;
如下图:
7. Subroutine Calls
8. 参数传递规则
根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变化的子程序,两者是同一的,参数固定的子程序可以认为可变参数是空的
Stage |
Note |
|
Stage A---Initialization
|
A.1 The Next General-purpose Register Number (NGRN) is set to zero; A.2 The Next SIMD and Floating-point Register Number (NSRN) is set to zero; A.3 The next stacked argument address (NSAA) is set to the current stack-pointer value (SP). |
This stage is performed exactly once, before processing of the arguments commences. |
Stage B – Pre-padding and extension of arguments |
B.1 如果参数类型是组合类型,调用者和被调用者无法静态获取大小,参数会被拷贝到内存,参数会被拷贝到内存的指针取代。 |
|
Stage C – Assignment of arguments to registers and stack |
C.1 如果参数是Half-, Single-, Double- or Quad- precision Floating-point or Short Vector Type,并且NSRN<8,参数会分配给寄存器v[NSRN]; C.2 HFA和HVA?? C.3 HFA和HVA?? C.4 HFA和HVA?? C.5 如果参数是Half- or Single- precision Floating Point type,并且参数大小被设置为8字节,那么就像参数被保存到64bit寄存器一样,其它位会被不确定的数值填充??? C6. 如果参数类型是HFA, an HVA, a Half-, Single-, Double- or Quad- precision Floating-point or Short Vector Type,则参数被拷贝到NSAA,NSAA会加上参数的大小 C7. 如果参数类型是Integral or Pointer Type,参数大小小于等于8字节,如果NGRN<8,参数被拷贝到x[NGRN],NGRN增加1; C8. 如果参数有一个16字节的对齐,NGRN会up到偶数 C9. 如果参数类型是Integral Type,参数大小为16字节,如果NGRN<7,则参数被拷贝到x[NGRN]和x[NGRN+1], x[NGRN]包含的是地地址的数据,NGRN增加2; C10. 如果参数是Composite Type,如果按double-words的个数<8-NGRN, 参数会被拷贝到连续的通用寄存器,就好像LDR从连续的内存拷贝数据到连续的寄存器中。NGRN会加上使用的寄存器的个数; C11. NGRN被设置为8; C12. NSAA被up到8,或者参数自然对齐?? C13.如果参数是composite type, 参数被保存到NSAA, NSAA会增加参数的大小; C14. 参数大小小于8字节,会被设置为8字节,与参数拷贝到64bit寄存器是一样的 C15. 参数被拷贝到NSAA指示的内存,NSAA增加参数大小 |
For each argument in the list the rules are applied in turn until the argument has been allocated |
9. 返回结果的规则
返回结果的类型决定了返回的方式:
Items |
Note |
If the type, T, of the result of a function is such that: void func(T arg) |
Arg会按照参数传递原则传递给一个寄存器,结果也会返回到这个寄存器 |
其它情况 |
调用者会保留一段内存,将内存地址存放到x8,传递给被调用者,被调用者根据运行情况随时更新内容 |
10.参考文档
ARMV8 Procedure Call Standard