AAPCS(Procedure Call Standard for the ARM Architecture)笔记

这篇文章是对AAPCS文档的简单翻译, 因之前要讲课又没在网上找到中文版, 就决定尝试自己翻译一下, 个人理解不保证翻译正确性.
所有拿捏不准的地方全部保留英文(一来方便读者对上下文理解, 二来防止前面翻译出错引起后面误导), 部分不重要章节省略翻译, 一些有疑惑的术语列举如下:
variant             个人理解是符合基本标准的一种具体实现, 直接按字面意思翻译为变型.
conformance         直接翻译成遵循守则, 对翻译有异议的读者建议看原文以防我误导.
Aggregate           翻译为集合似乎更通顺点, 反正就当结构体理解就对了.
public interface    个人理解是对外可见的接口, 翻译为公共接口(public interface).
veneer              见[ARM Compiler toolchain Using the Linker]的overview of veneers一节. 含义是为支持32位空间跳转而由链接器生成的额外代码. 暂时翻译为胶合代码.

文档编号: ARM IHI 0042F current through ABI release 2.10
日期: 2015/11/24

 

引言:
ARM架构程序调用标准(Procedure Call Standard for the ARM Architecture, AAPCS)描述了ARM架构下应用程序二进制接口(Application Binary Interface, ABI)程序调用的标准.
如何获取最新的释出标准: 登录http://infocenter.arm.com/获取最新版本(如果你的手册超过一年以上)(对于ARM软件开发工具部分, ARM架构ABI部分无需更新).

 

1. 关于此文档

1.1. 修改历史
略.

1.2. 参考书籍
AAPCS                   Procedure Call Standard for the ARM Architecture        ARM架构程序调用标准.
AAELF                   ELF for the ARM Architecture                            ARM架构ELF.
BSABI                   ABI for the ARM Architecture(Base Standard)             ARM架构ABI(基本标准).
CPPABI                  C++ ABI for the ARM Architecture                        ARM架构C++ ABI.
ARM ARM                 The ARM Architecture Reference Manual 2nd               ARM架构参考手册 ARM DDI 0100E, ISBN 0 201 737191.
                                                                                登录http://infocenter.arm.com/help/index.jsp获取ARMv5 Architecture Reference Manual.
ACLE                    ARM C Language Extensions                               ARM C语言扩展 ARM IHI 0053A.
                                                                                登录http://infocenter.arm.com/help/index.jsp获取ARM C Language Extensions.
GC++ABI                 Generic C++ ABI                                         通用C++ ABI.
                                                                                登录http://mentorembedded.github.com/cxx-abi/abi.html.

1.3. 术语与缩略语
ABI                     应用程序二进制接口.
                        1. 在指定的运行环境下可执行文件必须遵循的执行规范. 如the Linux ABI for the ARM Architecture.
                        2. 单独生成的可重定位文件为能够被静态链接及可执行所需遵循的规范.
                        如the C++ ABI for the ARM Architecture / the Run-time ABI for the ARM Architecture / the C Library ABI for the ARM Architecture.
EABI                    适用于嵌入式程序的ABI.
PCS                     程序调用标准.
AAPCS                   ARM架构程序调用标准(本标准).
APCS                    ARM程序调用标准.
TPCS                    Thumb程序调用标准.
ATPCS                   ARM-Thumb程序调用标准.
PIC                     位置无关代码.
PID                     位置无关数据.
routine / subroutine    一个控制权可以被转移, 且当其任务完成时将控制权返回给它的调用者的程序片段.
                        routine用来在出现复杂调用时明确关系: routine是调用者(caller), subroutine是被调用者(calllee).
Procedure               一个无返回值的routine.
Function                一个带返回值的routine.
Activation stack        当前记录的routine的栈(AKA call-frame stack).
Activation record       被routine用来存储寄存器值与本地变量的内存, 通常分配在栈上(AKA call-frame).
Argument / Parameter    这两个术语是可以相互替代的. 它们可以代表程序被调用时传入参数的形参或实参本身, 根据上下文来决定.
Externally visible      [An interface] between separately compiled or separately assembled routines.
Variadic routine        如果程序的参数个数与类型是由调用者而非被调用者本身决定的, 那么该程序是变长程序.
Global register         一个寄存器它的值在程序运行时从不保存或销毁, 它的值只可能通过一种运行环境指明的方式被更新.
Program state           程序内存的状态, 包括寄存器值.
Scratch register        被用来在一次计算中存储一个立即数(通常这些值在程序源代码中没有名字且只有有限的生命周期)的寄存器(AKA temporary register).
Variable register       被用来存储变量的值(通常是程序的局部变量, 在程序源代码中有名字)的寄存器(AKA v-register).

更多的术语将在第一次出现时进行定义.

1.4. 使用许可
略.

1.5. 鸣谢
略.

 

2. 概要
AAPCS定义了单独编写, 编译, 汇编的子程序如何组合工作. 它描述了调用程序与被调用程序间的协议:
(1). 调用方有义务创建program state来保证被调用程序可以执行.
(2). 被调用方有义务在调用过程中保护该program state.
(3). 被调用方有权利修改该program state.
本标准指定了一系列PCS变型(variant)的基础, 这些变型(variant)由以下项不同优先级决定:
(1). 代码大小.
(2). 性能.
(3). 功能(i.e. 调试 运行时检查 支持共享库).
每个变型(variant)的某些方面(如是否支持使用R9)是由运行环境决定的. 因此:
(1). 代码严格遵循基本标准来保证各个变型(variant)的PCS兼容性是可能的.
(2). 遵循一个变型(variant)标准的代码通常不兼容其它变型(variant)代码.
(3). 遵循基本标准或一个变型(variant)标准的代码不能保证一定兼容使用同样标准的执行环境. 一个运行环境可能需要除PCS外其它的需求.
本标准除介绍外还分四个小节:
(1). 数据布局.
(2). 栈布局与函数调用.
(3). Variations available for processor extensions, or when the execution environment restricts the addressing model.
(4). The C and C++ language bindings for plain data types.
本标准不规范公开可见的C / C++语言实体表示(这是在CPPABI中规范的). 同样它不要求不可见的语言实体表示.

 

3. 介绍
AAPCS由第五版APCS与第三版TPCS构成. AAPCS本身又是组成完整ARM架构ABI的一部分.

3.1. 设计目的
AAPCS的目的是:
(1). 平等的支持Thumb状态与ARM状态.
(2). 支持Thumb状态与ARM状态的切换.
(3). 支持在高性能ARM架构上高效执行.
(4). 明确区分强制要求与自行选择(implementation discretion).
(5). 最小化与ATPCS的二进制差异.

3.2. 遵循守则(Conformance)
AAPCS定义了单独编译与汇编的程序如何组合工作. 在这些程序间有一个外部可见的接口. 通常情况下, 不是所有的外部可见的软件接口都是公开可见或任意使用的.
事实上, (由目标代码格式严格定义的)机器层面的外部可见的概念与更高层面上, (系统定义或应用程序定义的)面向应用程序的外部可见的概念是不同的.
AAPCS的遵循要求(注1):
(1). 在任何时候栈空间限制与栈对齐规则都必须遵守(见5.1.1. 通用栈限制).
(2). 在静态链接阶段, 每个使用BL类型的控制转移指令的调用都必须遵守使用IP寄存器的规范(见5.3.1.1. 链接器对IP的使用).
(3). 每个公共可见接口(public visible interface)的程序都遵守相关PCS变型(variant).
(4). 每个公共可见接口(public visible interface)的数据元素都遵守数据布局的规则(2).

注1: 遵循本定义可最大化执行自由度.
i.e. 如果已知外部可见接口的两侧使用相同编译器编译那么该接口不是公共可见的, AAPCS允许接口做私有的约定如使用额外的参数寄存器或使用非标准格式传递参数.
尽管如此, 栈不变性必须仍被保持, 否则程序调用链上其它遵循规范的程序就可能失败. 使用IP的规则必须被遵守否则静态链接器可能生成一个不起作用的执行程序.
Conformance at a publicly visible interface does not depend on what happens behind that interface. Thus, for example, a tree of non-public, non-conforming calls can conform because the root of the tree offers a publicly visible, conforming interface and the other constraints are satisfied.
注2: 数据元素包括: 接口定义的程序参数, 接口定义的静态数据及所有通过接口传递的指针指向的数据.

 

4. 数据类型与对齐

4.1. 基本数据类型
如下表所示, 基本数据类型的字节大小与字节对齐. NULL指针永远代表所有位均为零.
类型                    机器类型                字节大小    字节对齐    备注
integral                unsigned byte            1            1         字符
                        signed byte              1            1
                        unsigned half-word       2            2
                        signed half-word         2            2
                        unsigned word            4            4
                        signed word              4            4
                        unsigned double-word     8            8
                        signed double-word       8            8
floating point          half precision           2            2         见4.1.1 半精度浮点
                        single precision         4            4         浮点数的编码见[ARM ARM] C2章节 VFP编程模型 2.1.1 单精度格式与2.1.2 双精度格式
                        double precision         8            8
Containerized vector    64-bit vector            8            8         见4.1.2 矩阵向量(Containerized vector)
                        128-bit vector           16           16
Pointer                 Data pointer             4            4         指针计算应为无符号
                        Code pointer             4            4         代码指针的位0代表目标指令类型(0为ARM, 1为Thumb)
4.1.1. 半精度浮点
VFPv3架构的一个可选的扩展提供硬件支持半精度值. 目前支持两种格式: IEEE754r指定的格式与另一种提供额外范围(但没有NaNs或无穷大)的格式. AAPCS的基本标准指定使用IEEE754r的变型(variant)但两种格式均可以被程序变型(variant)使用.
4.1.2. 矩阵向量(Containerized vector)
矩阵向量(Containerized vector)的内容对大多数PCS标准是不透明的, 对其布局的唯一要求是内存格式(基本类型在内存中存储的方式)与在程序调用接口时不同类型寄存器的映射. 如果一门语言绑定了数据类型直接映射到矩阵向量(Containerized vector)的定义那么它必须定义是如何映射的.

4.2. 字节序
从软件角度看, 内存是一组可寻址的字节数组. ABI支持两种由硬件实现的内存视图.
(1). 在小端内存视图下一个数据对象的最低有效字节是该数据对象所占内存的最低字节地址.
(2). 在小端内存视图下一个数据对象的最低有效字节是该数据对象所占内存的最高字节地址.
一个对象的最低有效位永远是位0.
所有对象都是统一字节序, 即映射可以对应更大或更小的对象. 一个双字在内存中的映射如下所示:
大端字节序数据对象在内存中布局
Data Object                                 Memory
|31 ............................ 0|
-----------------------------------         ---------
|MSB    |        |        |LSB    | --->    |M+3    |
-----------------------------------         ---------
    |       |       |------------------>    |M+2    |
    |       |                               ---------
    |       |-------------------------->    |M+1    |
    |                                       ---------
    |---------------------------------->    |M+0    |
                                            ---------
小端字节序数据对象在内存中布局
Data Object                                 Memory
                                            ---------
    |---------------------------------->    |M+3    |
    |                                       ---------
    |       |-------------------------->    |M+2    |
    |       |                               ---------
    |       |       |------------------>    |M+1    |
-----------------------------------         ---------
|MSB    |        |        |LSB    | --->    |M+0    |
-----------------------------------         ---------
|31 ............................ 0|

4.3. 复合类型
复合类型是一种或多种基本数据类型的集合但以单一实体在程序调用层级被操作. 复合类型可以是:
(1). 一个成员顺序排布在内存中的结构体(Aggregate).
(2). 一个每个成员都具有相同地址的联合体.
(3). 一个基本类型是其它类型的数组.
复合类型的定义是递归的, 即复合类型可以包含另一个复合类型作为其成员.
(1). The member alignment of an element of a composite type is the alignment of that member after the application of any language alignment modifiers to that member.
(2). 复合类型的自然对齐方式是取每个复合类型的"顶层"成员的对齐的最大值, 在整个复合类型的对齐调整之前.
4.3.1. 结构体(Aggregate)
(1). 结构体(Aggregate)的对齐应以其最大的组成成员对齐.
(2). 结构体(Aggregate)的大小应该是其对齐的最小倍数同时能够容纳所有的成员.
4.3.2. 联合体
(1). 联合体的对齐应以其最大的组成成员对齐.
(2). 联合体的大小应该是其对齐的最小倍数同时能够容纳最大的成员.
4.3.3. 数组
(1). 数组的对齐应以其基本类型对齐.
(2). 数组的大小应该是其基本类型的大小乘以数组元素个数.
4.3.4 位域
结构体(Aggregate)的基本数据类型成员可被细分为位域; 如果位域成员未使用的部分有充足的空间可以在自然对齐的地址上存放之后的成员则之后的成员可以使用该位域成员未使用的部分空间. 为计算结构体(Aggregate)的对齐, 位域类型成员应以字段的基本类型为对齐(注1). 结构体(Aggregate)中位域的布局应根据使用语言定义的.
4.3.5. 奇次结构体(Homogeneous Aggregate)
奇次结构体(Homogeneous Aggregate)是一种复合类型, 组成它的所有基本数据类型都是相同的. The test for homogeneity is applied after data layout is completed and without regard to access control or other source language restrictions.
由矩阵向量(Containerized vector)构成的结构体(Aggregate)被认为是Homogeneous的如果所有成员都是相同大小, 即使矩阵向量(Containerized vector)内部的格式不相同. i.e. 一个包含9字节向量4个半字向量的结构体满足奇次结构体(Homogeneous Aggregate)的要求.
奇次结构体(Homogeneous Aggregate)有基本类型, 该基本类型为每个元素的基本数据类型. 奇次结构体(Homogeneous Aggregate)的总大小是该基本类型乘以元素个数, 它的对齐是以其基本类型对齐为准.

注1: 这是为允许C结构体struct {int a:8; char b[7];} 大小为8字节且以4字节对齐.

 

5. 基本程序调用标准
基本标准定义了(ARM指令集与Thumb指令集通用的)机器层面的, 只与核心寄存器相关的调用标准. 它应被应用于无浮点硬件或高度需要Tbumb代码相关的系统中.

5.1. 机器寄存器
ARM架构定义了一套核心指令集与许多由协处理器执行的额外指令. 核心指令集可以访问核心寄存器, 而协处理器可以为特定操作提供额外的寄存器.
5.1.1. 核心寄存器
ARM与Thumb指令集共可见16个32位核心寄存器. 它们以r0-r15或R0-R15标记. 寄存器名字在汇编语言中可以用大写或小写表示. 在本规范中大写代表该寄存器在PCS中有固定作用. 下表总结了本标准中核心寄存器的作用. 除核心寄存器外还有一个状态寄存器(CPSR)可被符合标准的代码(conforming code)访问.
寄存器      别名        特殊名          程序调用标准中作用
r15                     PC              程序计数器.
r14                     LR              链接寄存器.
r13                     SP              栈指针.
r12                     IP              程序调用过程中备份寄存器.
r11         v8                          变量寄存器8.
r10         v7                          变量寄存器7.
r9                      v6 / SB / TR    平台寄存器, 该寄存器意义由平台标准指定.
r8          v5                          变量寄存器5.
r7          v4                          变量寄存器4.
r6          v3                          变量寄存器3.
r5          v2                          变量寄存器2.
r4          v1                          变量寄存器1.
r3          a4                          参数 / 备份寄存器4.
r2          a3                          参数 / 备份寄存器3.
r1          a2                          参数 / 结果 / 备份寄存器2.
r0          a1                          参数 / 结果 / 备份寄存器1.
前四个寄存器(r0-r3)用来在子程序调用时传递参数并当函数返回时返回结果. 同时它们也用于子程序中暂存立即数(但通常只在子程序内部).
寄存器r12(IP)在程序与子程序调用时被链接器用作备份寄存器(具体见5.3.1.1. 链接器对IP的使用), 同时它也可以在子程序中暂存立即数.
寄存器r9的作用是平台指定的. 虚拟平台可以将任何作用交给r9寄存器并书面说明使用方法. i.e. 它可以被设计用来作为静态基址(static base)在一个位置无关的数据模型中, 或被设计为线程寄存器(thread register)在一个线程本地存储的环境中. 对这个寄存器的使用要求调用过程中保存的值是永久的. 对于没有这些特殊需求的虚拟平台可将r9设计为额外的参数寄存器v6.
寄存器r4-r8, r10, r11被用来保存程序的局部变量. 其中仅v1-v4被Thumb指令集使用, 但AAPCS并不要求Thumb代码只能使用这些寄存器.
子程序必须保护r4-r8, r10, r11与SP(以及一些PCS变型(variant)中将r9作为v6)寄存器的内容.
不论何种PCS变型(variant), r12-r15都有特殊作用, 分别别称为IP, SP, LR, PC.
CPSR是一个全局寄存器, 它有如下属性:
(1). 当进入公共接口(public interface)或从接口返回时, 位N, Z, C, V及Q(27-31位)与位GE[3:0](16-19位)是未定义的. 只有在支持这些特性的处理器上执行程序位Q与GE[3:0]才可被修改.
(2). 在ARM 6架构上, 位E可被(在小端字节序或big-endian-8模式下执行的)应用程序用作暂时的改变访问内存时数据的字节序. 应用程序必须有指定的字节序且在进入公共接口(public interface)或从接口返回时设置位E必须遵守应用程序指定的字节序.
(3). 位T(5位)与位J(24位)是程序执行状态位. 只有被用来修改这些位的指令才能修改它们.
(4). 位A, I, F与M[4:0](0-7位)是特权位且仅能被设计在特权模式下显式操作的程序修改.
(5). 所有其它位均为保留位且不能被修改. 不论它们为0或为1或在公共接口(public interface)调用时是否保持其含义均是未定义的.
5.1.1.1. 操作大于32位的数据
大于32位的基本类型可以在函数调用时作为参数传入或作为结果返回. 当这些类型保存在核心寄存器中时必须遵守以下规则:
(1). 一个双字大小的类型通过两个连续的寄存器传递(i.e. r0与r1, r2与r3). 值通过单个LDM指令将从内存中加载到寄存器中.
(2). 一个四字大小的矩阵向量(Containerized vector)通过四个连续的寄存器传递. 值通过单个LDM指令将从内存中加载到寄存器中.
5.1.2. 协处理器寄存器
机器寄存器组可通过附加寄存器扩展, 这些附加寄存器在协处理器指令空间中使用指令访问. 使用这些不用来传递参数或返回结果的协处理器寄存器是兼容基本标准的. 每个协处理器可能有额外的管理自身的寄存器的规则.
注意: 即使协处理器不用来传递参数, 有些语言运行时支持的特性要求了解程序中全部协处理器的状态以保证正常运行(i.e. C语言中setjmp()与C++异常机制).
5.1.2.1. VFP寄存器使用规则(VFP v2 v3与高级SIMD扩展)
VFP-v2协处理器有32个单精度寄存器s0-s31, 同时支持以16个双精度寄存器d0-d15形式访问(d0包括s0, s1, d1包括s2, s3, 依次类推). 此外根据不同实现还有3个及以上的系统寄存器.
VFP-v3增加额外16个双精度定时器d16-d31, 但它们不支持以单精度方式访问.
高级SMID扩展使用VFP寄存器组, 使用双精度寄存器作为64位矢量, 以及定义四字寄存器作为128位矢量(q0包括d0, d1, q1包括d2, d3, 依次类推).
寄存器s16-s31(d8-d15, q4-q7)在子程序调用时必须被保护, 寄存器s0-s15(d0-d7, q0-q3)无需被保护(且在标准程序调用变型(variant)中可被用于传递参数或返回结果), 寄存器d16-d31(q8-q15)如果存在, 无需保护.
FPSCR是唯一可被符合标准的代码(conforming code)访问的寄存器. 它是一个有以下特性的全局寄存器:
(1). 状态码位(28-31位), cumulative saturation位(QC, 27位)与cumulative exception-status位(0-4位)在公共接口(public interface)调用时不被保护.
(2). 异常控制位(8-12位), 取整模式位(22-23位)与flush-to-zero位(24位)的修改可通过调用特定的支持函数且会影响到应用程序的全局状态.
(3). 长度位(16-18位)与跨距位(20-21位)必须在进入与返回接口时保持为零.
(4). 所有其它位均为保留位且不能被修改. 不论它们为0或为1或在公共接口(public interface)调用时是否保持其含义均是未定义的.

5.2. 进程, 内存与栈
AAPCS应用于单一线程的程序(以下简称进程). 一个进程有一个由底层的机器寄存器定义的程序状态与它可访问的内存内容. 一个进程的内存通常可以分为五类:
(1). 代码(被执行的程序), 对进程而言它必须是可读的但不需要可写.
(2). 只读静态数据.
(3). 可写静态数据.
(4). 堆.
(5). 栈.
可写静态数据又可进一步细分为初始化数据, 清零初始化数据, 未初始化数据. 除栈外其它类型不需要占用单一连续内存区域. 一个进程必须有代码与栈, 但可以没有其它类型内存.
堆是一块(或若干)由进程自己管理(i.e. 使用C malloc函数)的内存区域, 通常用于创建动态数据对象.
符合标准的程序必须只能执行被设计用来存放代码的内存区域中的指令.
5.2.1. 栈
栈是一块用来存储局部变量与当参数寄存器不足时传递额外参数给子程序的连续内存区域. 栈的实现是满降栈(full-descending)且当前地址保存在寄存器SP中. 通常情况下栈有一个基址与长度限制, 但程序在实际运行时无法确定两者的值. 栈可以是固定大小也可以是动态扩展的(通过向下调整栈长度限制). 栈维护的规则有两条: 一组任何时候都必须遵守的约束与一条额外的在公共接口(public interface)中遵守的约束.
5.2.1.1. 通用栈约束
任何时候以下约束都要被遵守:
(1). 栈长限制 < SP <= 栈基址, 栈指针必须落在栈空间内.
(2). SP % 4 == 0, 栈指针必须以单字对齐.
(3). 一个进程只能访问(读写)整个栈中以[SP, 栈基址 - 1]的闭区间(SP是寄存器r13的值).
注意: 这意味着为满足栈约束以下格式的指令可能会失败, 即使寄存器指向栈空间.
ldmxx reg, {..., sp, ...}
如果指令的执行在sp被加载后中断了, 栈指针将无法被恢复, 因此重启指令可能会违反第三条约束.
5.2.1.2. 栈在公共接口(public interface)中的约束
在公共接口(public interface)中栈还需遵守以下约束:
(1). SP % 8 == 0, 栈指针必须以双字对齐.

5.3. 子程序调用
ARM指令集与Thumb指令集均包含一个基本的子程序调用指令BL, 用于执行带链接的跳转操作. 执行BL指令的结果是将程序计数器(PC)的下一个值(即返回地址)传入链接寄存器(LR)并将目的地址传入程序计数器(PC). 链接寄存器的位0置位代表BL指令从Thumb状态执行的, 置零代表BL指令从ARM状态执行的. 指令执行结果是将控制转移到目的地址, 将LR中的返回地址作为额外的参数传给被调用的子程序. 当返回地址重新装载回PC时控制返回到BL的下一条指令(见5.6. 相互转化).
一个子程序调用可以由任何具有该作用的指令序列组成, 举例而言, 在ARM模式下若想调用一个以寄存器r4寻址的子程序并(在子程序结束后)控制返回到接下来的指令, 仅需:
mov LR, PC
bx r4
...
注意: 以上序列将不在Thumb状态起作用因为设置LR的指令不会将代表Thunb状态的位传递到LR[0].
在ARM v5架构下ARM状态与Thumb状态均提供BLX指令, 它可以调用一个以寄存器寻址的子程序并正确的将返回地址设置为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值为程序计数器的下一个值.
5.3.1.1. 链接器对IP的使用
ARM状态与Thumb状态下BL指令均无法寻址32位空间, 所以链接器在调用程序与被调用子程序间插入胶合代码(veneer)是必要的. 胶合代码(veneer)也可以用来支持ARM-Thumb状态相互转换或动态链接. 任何插入的胶合代码(veneer)必须保护除IP(r12)以外的所有寄存器及状态码标记位. 符合标准的程序必须假定一段修改了IP的胶合代码(veneer)可能会插入任何跳转指令用于重定位以支持程序互联或长分支跳转. a conforming program must assume that a veneer that alters IP may be inserted at any branch instruction that is exposed to a relocation that supports inter-working or long branches.
注意: R_ARM_CALL, R_ARM_JUMP24, R_ARM_PC24, R_ARM_THM_CALL, R_ARM_THM_JUMP24与R_ARM_THM_JUMP19是具有这些属性的ELF重定位类型. 详情见[AAELF].

5.4. 结果返回
函数返回的结果需要遵守的规则是由结果的类型决定的, 对于基本标准:
(1). 类型是半精度浮点的返回值存储在r0的低16位中返回.
(2). 类型是长度小于4字节的基本数据类型的返回值以零扩展或符号扩展为单字后存储在r0中返回.
(3). 类型是单字长度的基本数据类型的返回值(i.e. int, float)存储在r0中返回.
(4). 类型是双字长度的基本数据类型的返回值(i.e. long long, double及64位矩阵向量(Containerized vector))存储在r0与r1中返回.
(5). 类型是128位的矩阵向量(Containerized vector)的返回值存储在r0-r3中返回.
(6). 类型是长度不大于4字节的复合数据类型存储在r0中返回. 返回值的格式是假设结果存储在以单字对齐的内存地址上并通过一个一个LDR指令加载到r0中, 任何r0中超过结果边界的位都是未指定的值.
(7). 类型是长度大于4字节或(无论调用程序还是被调用程序)都不能静态的决定长度的复合数据类型, 其返回值存储在函数被调用时传递的额外参数所指向的内存地址上, 用于保存结果的内存在函数调用期间任意时间点均可能被修改.

5.5. 传递参数
基本标准允许通过核心寄存器r0-r3及栈传递参数. 对于只需少量参数的子程序使用寄存器传递参数可以极大减少调用开销.
参数传递可以用两级抽象模型:
(1). 从源语言参数到机器类型的映射.
(2). 机器类型的集合来产生最终的参数列表.
从源语言参数到机器类型的映射是由每种语言分开描述并指定的(C与C++语言相关内容见7 ARM C与C++映射), 其结果是一个将要传递给子程序的有序的参数列表.
接下来的描述中会假设有许多协处理器可以用来传递或接收参数. 协处理器寄存器分为许多类型. 一个参数至多是一类协处理器寄存器的候选对象. 一个参数如适合分配在一个协处理器寄存器上则被称为协处理器寄存器候选对象(CPRC).
在基本标准中没有参数是协处理器寄存器的候选对象.
变参函数必须总是整理(marshaling)已使其符合基本标准.
对于调用者而言, 在整理(marshaling)之前需先保证足够的栈空间(已被分配)来保存存储在栈上的参数: 实际应用中所需要的栈空间在参数整理(marshaling)完以前是未知的. 被调用者可修改任何用来传递调用者参数的栈空间.
当一个复合类型参数(部分或全部)被分配给核心寄存器, 其表现相当于参数先被存储到以字对齐的内存地址上然后使用合适的复数加载指令将其加载到连续的寄存器中.
阶段A. 初始化
本阶段仅在参数处理之前出现一次.
A.1. 下一个核心寄存器编号(NCRN)被设置为r0.
A.2.cp. 开始协处理器参数寄存器初始化.
A.3. 下一个存储参数栈地址(NSAA)被设置为当前栈指针值(SP).
A.4. 如果子程序是将返回值保存在内存中的函数则返回值的地址将放在r0中且NCRN被设置为r1.
阶段B. 预填充与扩展参数
对于每个在列表中的参数都需遵守以下原则中的第一条.
B.1. 如果参数是复合类型且其长度不能被(调用者或被调用者)静态决定, 那么需将参数拷贝至内存并将参数本身用指向该拷贝的指针替换.
B.2. 如果参数是整数的基本数据类型且小于单字, 那么需将参数以零扩展或符号扩展至一个字并将其长度设置为4字节. 如果参数是半精度浮点类型, 则将它的长度设置为4字节并将其当做拷贝至32位寄存器的低位且其余位填充未指定数据.
B.3.cp. 如果参数是CPRC那么任何对协处理器寄存器类的准备规则都需应用.
B.4. 如果参数是复合类型且其大小不是4字节的倍数, 那么它的大小需要向上对齐到最近的4的倍数.
B.5. 如果参数是调整过对齐的类型那么它以实际参数的拷贝传递参数, 其拷贝的对齐按如下规则. 对于基本数据类型, 其对齐以该基本类型的自然对齐为准. 对于复合数据类型, 拷贝的对齐需要以4字节对齐(如果其自然对齐不大于4字节)或8字节对齐(如果其自然对齐大于等于8字节). 拷贝的对齐适用于整理(marshaling)规则.
阶段C. 参数分配给寄存器与栈
对于列表中每个参数依次应用以下规则直至参数被分配.
C.1.cp. 如果参数是CPRC且有足够的对应类型的未分配的协处理器寄存器, 则参数被分配给协处理器寄存器.
C.2.cp. 如果参数是CPRC且任何对应类型的未分配的协处理器寄存器均被标记为不能使用, 则向上调整NSAA直至其地址与参数对齐然后将参数拷贝至调整后的NSAA, NSAA进一步增长参数长度的大小.
C.3. 如果参数需要以双字对齐, NCRN需向上对齐到偶数个寄存器编号.
C.4. 如果参数以字为单位的大小不超过r4减去NCRN, 参数将被拷贝至以NCRN开始的核心寄存器, NCRN增加被使用的寄存器个数的大小. 如果使用一个LDM命令将值从内存装载到寄存器中, 则连续的寄存器保存参数的各个部分. 至此参数完成分配.
C.5. 如果NCRN小于r4且NSAA等于SP参数将被分开存储在核心寄存器与栈上. 参数的第一部分被拷贝到以NCRN开始直至r3的核心寄存器中, 其余的部分被拷贝至以NSAA开始的栈上. 然后NCRN被设置为r4且NSAA增长参数长度减去传参寄存器个数的大小. 至此参数完成分配.
C.6. 将NCRN设置为r4.
C.7. 如果参数要求双字对齐则NSAA向上对齐到双字.
C.8. 将参数拷贝至NSAA, NSAA增长参数长度的大小.
需要注意的是以上算法对除C与C++以外的语言做出了规定因为它提供了传递数组与动态长度的值. 规则是让调用者总是能够静态决定用于存放不通过寄存器传递的参数的栈空间的大小, 即便函数是变参的.
进一步可以观察到以下几点:
(1). 初始栈地址是传递给子程序的栈指针的值. 因此编译过程中可能需要运行两次以上算法, 第一次确定所需栈空间大小, 第二次确定最终的栈地址.
(2). 一个双字对齐的类型总是存储在以偶数起始的核心寄存器内, 或双字对齐的栈地址, 即使它不是结构体(Aggregate)的第一个参数.
(3). 参数首先被分配在寄存器中且仅(有参数寄存器不够用时)额外的参数才保存在栈上.
(4). 基本数据类型的参数总是全部保存在寄存器中或全部保存在栈上.
(5). 根据C.5.规则至多只有一个参数可被分开保存在寄存器与栈上.
(6). CPRC对象可被保存在协处理器寄存器中或栈上, 但不能保存在核心处理器中.
(7). 因一个参数至多只能是一类协处理器寄存器的候选者, 该规则对多个协处理器在不影响其使用情况下同样适用.
(8). 一个参数只能被分配在核心寄存器与栈上如果它之前的CPRC已被分配在协处理器寄存器中.

5.6. 相互转化
AAPCS要求所有子程序调用与返回均支持ARM状态与Thumb状态相互转化. 这对编译各类ARM架构影响如下:

ARMv5与ARMv6
通过函数指针的调用应使用以下方式之一:
blx Rm  ;对于正常子程序调用
bx Rm   ;对于尾调用
使用bl<cond>或b<cond>的函数调用需要链接器生成的胶合代码(veneer), 如果产生了状态转换. 因此有时使用一个带有无条件bl指令的指令序列会更高效.
返回序列可能会使用载入多个操作来直接装载PC或合适的bx指令.
以下传统的返回方式在有状态转换时无法使用:
mov PC, Rm

ARMv4T
除ARMv5的约束外, ARMv4T还有以下额外约束:
涉及状态改变且使用bl的调用同样需要链接器生成的代码.
通过函数指针的调用必须使用对应ARM状态的指令序列.
mov LR, PC
bx Rm
但是该指令序列不适用于Thumb状态, 所以通常使用一个bl指令外加胶合代码(veneer)来替代bx的作用.
返回的指令序列必须恢复被保存的寄存器且使用bx指令来返回调用者.

ARMv4
ARMv4架构既不支持Thumb状态又不支持bx指令, 因此它不完全兼容AAPCS.
推荐对ARMv4的代码使用ARMv4T相互转化的序列进行编译, 但因所有bx指令均服从R_ARM_V4BX重定位[AAELF], 一个链接器链接的代码可能会将所有bx Rm类指令转变为mov PC, Rm. 但可重定位文件仍与此标准兼容.

 

6. 标准变型(variant)
本节内容仅适用于非变参函数, 对于变参函数总是使用基本标准来参数传递与结果返回.

6.1. VFP与高级SIMD寄存器参数
本变型(variant)改变了浮点值在调用程序与子程序间传递的方式且提升了性能, 当存在VFP协处理器或高级SIMD扩展时.
6.1.1. 寄存器与内存格式之间的映射
使用VFP在接口调用时传递参数如下所示:
(1). 半精度浮点类型以将其从内存拷贝至单精度寄存器的低16位方式传递.
(2). 单精度浮点类型以将其从内存拷贝至单精度寄存器(使用VLDR指令)的方式传递.
(3). 双精度浮点类型以将其从内存拷贝至双精度寄存器(使用VLDR指令)的方式传递.
(4). 64位矩阵向量(Containerized vector)类型以将其从内存拷贝至64位矢量寄存器(Dn)(使用VLDR指令)的方式传递.
(5). 128位矩阵向量(Containerized vector)类型以将其从内存拷贝至128位矢量寄存器(Qn)(使用一条VLDM指令及两个分量的64位矢量寄存器, i.e. VLDM r0, {d2, d3}将传递q1)的方式传递.
6.1.2. 程序调用
调用存储寄存器的集合与基本标准相同(5.1.2.1. VFP寄存器使用规则).
6.1.2.1. VFP协处理器候选
对VFP而言以下参数类型是VFP CPRC:
(1). 半精度浮点.
(2). 单精度浮点.
(3). 双精度浮点.
(4). 64位或128位矩阵向量(Containerized vector).
(5). 有一到四个基本类型是单精度浮点或双精度浮点的奇次结构体(Homogeneous Aggregate).
(6). 有一到四个基本类型是64位矩阵向量(Containerized vector)的奇次结构体(Homogeneous Aggregate).
(7). 有一到四个基本类型是128位矩阵向量(Containerized vector)的奇次结构体(Homogeneous Aggregate).
注意: 变参函数中没有VFP CPRC.
6.1.2.2. 结果返回
一个类型满足VFP CPRC条件的结果会从以最小的寄存器号起始的恰当的连续VFP寄存器中返回. 所有其它类型按基本标准返回.
6.1.2.3. 参数传递
有一类VFP协处理器寄存器类使用s0-s15(d0-d7)来传递参数. 以下是VFP协处理器守则:
A.2.vfp 浮点参数寄存器被标记为未分配.
B.3.vfp 无事可做.
C.1.vfp 如果参数是一个VFP CPRC且存在足够的未分配的对应类型的连续VFP寄存器则参数被分配到最低编码起始的寄存器.
C.2.vfp 如果参数是一个VFP CPRC且任何未分配的VFP寄存器均被标记为不可使用, 则NSAA向上调整直到它正确的以参数大小对齐再将参数拷贝至调整后NSAA起始的栈上. NSAA再增加参数的大小. 至此参数完成分配.
注意规则要求回填因前面参数的对齐约束导致跳过的未使用的寄存器, 只要没有VFP CPRC被分配到栈上回填就会一直持续.

6.2. 可选格式(alternative format)的半精度浮点值
代码可被编译为使用可选格式(alternative format)的半精度浮点值的程序. 传递参数与返回结果的守则可以按基本标准或VFP与高级SIMD守则.

6.3. 读写位置无关(RWPI)
为要求读写位置无关(i.e. 单一地址空间类DLL模型)的执行环境而编译与汇编的代码使用静态基地址来寻址可写数据. 核心寄存器r9重命名为SB并用于保存静态基地址: 因此该寄存器任何时候都不能用于保存其它数据(注1).

注1: 尽管未被标准授权, 编译器通常通过加载SB偏移再加上SB基址来得到静态数据的地址. 偏移通常是一个32位的值, 从文字池(literal pool)中装载的PC相关的值. 在静态链接时字面值通常受R_ARM_SBREL32类型重定位. 数据地址相对于SB的偏移是可执行文件布局的一项属性, 它是在静态链接时固定的. 它不依赖数据在何处载入, 它是在运行时由SB捕获的.

6.4. 变型(variant)兼容性
第6章描述的标准变型(variant)所产生的代码是与基本标准不兼容的. 尽管如此仍然存在跨越兼容多个变型(variant)的代码子集.
本节描述了理论层面各个变型(variant)的兼容性. 但是工具链是否接受使用不同标准编译的兼容对象, 还是拒绝非兼容对象, 是由具体实现决定的.
6.4.1. VFP与基本标准兼容性
以VFP调用标准编译的代码如果未使用浮点或矩阵向量(Containerized vector)作为参数或结果则兼容基本标准(反之亦然), 或者如果程序是变参程序.
6.4.2. RWPI与基本标准兼容性
以基本标准编译的代码如果未使用寄存器r9则与RWPI调用标准兼容. 但是平台ABI可能进一步限制具有兼容性的代码子集.
6.4.3. VFP与RWPI标准兼容性
VFP标准变型(variant)与RWPI寻址变型(variant)的结合创造了第三种变型(variant). 以上描述的规则的有机结合决定了代码是否具有兼容性.
6.4.4. 半精度格式兼容性
The set of values that can be represented in Alternative format differs from the set that can be represented in IEEE754r format rendering code built to use either format incompatible with code that uses the other. Never-the-less, most code will make no use of either format and will therefore be compatible with both variants.

 

7. ARM C与C++语言映射
本章描述了ARM编译器如何将C特性映射到机器层面标准. C++作为C扩展的超集它同样描述了映射C++的特性.

7.1. 数据类型
7.1.1. 算术类型
C算术类型与基本数据类型的映射如下表所述:
C/C++类型                   机器类型                                    备注
char                        unsigned byte                               LDRB指令是无符号的.
unsigned char               unsigned byte
signed char                 signed byte
[signed] short              signed half word
unsigned short              unsigned half word
[signed] int                signed word
unsigned int                unsigned word
[signed] long               signed word
unsigned long               unsigned word
[signed] long long          signed double word                          仅C99.
unsigned long long          unsigned double word                        仅C99.
__fp16                      half precision(IEEE754r or Alternative)     见ACLE. 在变参函数调用中以双精度值传递.
float                       single precision(IEEE754)
double                      double precision(IEEE754)
long double                 double precision(IEEE754)
float _Imaginary            single precision(IEEE754)                   仅C99.
double _Imaginary           double precision(IEEE754)                   仅C99.
long double _Imaginary      double precision(IEEE754)                   仅C99.
float _Complex              2 single precision(IEEE754)                 仅C99. 布局是struct {float re; float im;};.
double _Complex             2 double precision(IEEE754)                 仅C99. 布局是struct {float re; float im;};.
long double _Complex        2 double precision(IEEE754)                 仅C99. 布局是struct {float re; float im;};.
_Bool/bool                  unsigned byte                               仅C99. 假=0, 真=1.
wchar_t                     see text                                    C++內建, C中用typedef定义, 类型由平台决定.
wchar_t的首选类型是unsigned int. 然而一个虚拟平台可能选择unsigned short来替代. 一个平台标准必须书面说明它的类型.
7.1.2. 指针类型
指针的容器按下表所述, C++的引用类型是视作指针的一种实现:
指针类型                    机器类型                                    备注
T *                         数据指针                                    T为任意数据类型
T (*F)()                    代码指针                                    F为任意函数类型
T&                          数据指针                                    C++引用
7.1.3. 枚举类型
本ABI将枚举类型的表示方法的选择交由平台ABI(无论是标准定义的还是实际使用的)或交由接口协议如果没有定义平台ABI.
(1). 一个枚举类型通常占据单字(int或unsigned int). 如果单字长度不能代表所有的枚举值则该类型占据双字(long long或unsigned long long).
(2). 枚举类型的存储容器的类型是以能包含该枚举全部枚举值的最小整型.
讨论:
C与C++语言标准对枚举类型的定义没有涉及二进制接口的定义且遗留以下问题:
(1). 枚举类型的容器是否有固定大小(正如大多数系统环境所希望的)或其大小不超过所保存的枚举值的大小(正如大多数嵌入式用户所希望的)?
(2). 当一个枚举值(i.e. MAXINT+1)溢出其容器(i.e. int)会发生什么?
(3). 枚举类型的值(在C/C++要求的转换后)是有符号还是无符号的?
关于最后一个问题C与C++语言标准声明:
(1). [C]每个枚举类型都需要兼容整型类型. 类型的选择是实现定义的, 但它必须能够表示所有枚举成员的值.
(2). [C++]枚举类型不是整型但...枚举类型的值可被转换为以下类型的值: int, unsigned int, long, unsigned long.
基于本ABI, 声明允许定义了可移植二进制包接口的头文件以一种严格遵循的可移植的方式强迫其客户采用一个32位有符号(int或long)表示一个枚举类型(通过定义一个负枚举值一个正枚举值并确保枚举数的范围超过16位且不超过32位). 否则必须通过调用平台ABI或分离的接口定义来建立对二进制表示的通用解释.
7.1.4. 额外类型
C与C++均要求系统提供基于基本类型的额外类型定义. 通常这些类型是通过包含适当的头文件来定义的. 然而在C++中基础类型size_t()可以无需使用任何头文件而简单使用::operator new()来暴露它, 而va_list的定义由编译器内部实现. 一个遵守AAPCS的对象必须遵守以下定义.
类型定义                    基础类型                                                    备注
size_t                      unsgined int                                                For consistent C++ mangling of ::operator new().
va_list                     struct __va_list {void *__ap;}                              va_list可寻址参数列表的任意对象.
                                                                                        因此第一个参数对象因以单字对齐(所有对象均至少以单字对齐).
                                                                                        但任何双字对齐的对象均在正确的双字对齐的地址上. 在C++中__va_list是std命令空间中的.
7.1.5. volatile数据类型
数据类型的声明可以用volatile描述符描述. 编译器不会移除任何对volatile数据类型的访问除非它能证明包含该访问的代码永远不会执行; 然而编译器会忽略自动变量的volatile描述符除非函数调用setjmp(). 结构体或联合体的volatile描述符被解释为递归的应用到组成结构的每个基本数据类型的描述符. 访问带volatile描述符的基本数据类型必须总是访问整个类型. 指定结构体或联合体的成员是volatile的的行为是未定义的. 同样的, 改变描述符或其大小的类型转换也是未定义的行为.
不是所有ARM架构都提供以各种宽度访问类型; 举例而言, 早于ARMv4的架构没有以16位访问的指令, 同样也没有64位访问的指令. 因此处理器中内存系统对部分或全部内存有严格的总线宽度限制. 这种情况下对volatile类型的唯一保证是类型的每一个字节都会精确访问一次且任何包含volatile数据且不属于该类型的字节都不做访问. 然而, 如果编译器有一条可用指令恰好能访问该类型, 则它应该使用该指令而非选择更小或更大的方式访问.
7.1.6. 结构体, 联合体与类布局
结构体与联合体通过组成它们的基本数据类型设计布局(见4.3. 复合类型), 所有成员以声明顺序排布. 额外针对C++非POD类的布局规则在CPPABI中描述.
7.1.7 位域
一个位域可能包含任意多个整型(包括枚举与布尔量). 一个位域序列按声明顺序使用以下规则布局.
对于每个位域, 其类型容器是:
(1). 它的声明类型如果它的大小不超过它声明类型的大小.
(2). 不大于它大小的最大整型类型如果它的大小超过了它声明类型的大小(见7.1.7.3. 超长的位域).
容器类型有助于包含结构体(aggregate)的对齐, 这样的方式这种类型不会出现零长度或匿名位域的例外.
注意C++标准声明匿名位域不是一个成员, 所以非零长度匿名位域是否有助于结构体(aggregate)对齐是不确定的, 在本ABI中是的.
每个位域的内容由它的容器类型的一个实例完全包含.
一开始我们定义位域的布局不大于其容器类型.
7.1.7.1 不大于其容器的位域
假设F为我们需要确定地址的位域, 我们定义其容器地址CA(F)为字节地址
CA(F) = &(container(F));
该地址永远自然对齐其容器类型, 即
CA(F) % sizeof(container(F)) == 0.
F相对容器的位偏移K(F)的定义依赖字节序:
(1). 对于大端字节序K(F)是从容器最高有效位到位域的最高有效位的偏移.
(2). 对于小端字节序K(F)是从容器最低有效位到位域的最低有效位的偏移.
可以通过加载位域的容器, 根据字节序, K(F), 容器大小及位宽来设置偏移与掩码, 如有必要再做符号扩展, 来得到位域.
F的位地址BA(F)现在可以定义为:
BA(F) = CA(F) * 8 + K(F);
对于位地址BA落在容器宽度C与对齐A(A小于等于C)(均以位为单位), 定义未分配容器位(UCB)为:
UCB(BA, C, A) = C - (BA % A);
进一步定义截断函数(truncation function):
TRUNCATE(X, Y) = Y * [X / Y];
即乘以倍数Y后最大不超过X.
现在我们可以定义下一个容器位地址(NCBA), 当当前容器有足够空间保存下一个位域时就会使用到:
NCBA(BA, A) = TRUNCATE(BA + A - 1, A);
在布局一个位域序列的每个阶段时都有:
(1). 当前位地址(CBA).
(2). 容器地址C与对齐地址A, 由将要被布局的位域类型决定(8, 16, 32, ...).
(3). 位域宽度W.
对每个位域, 其排布由
(1). 如果宽度W为零, 设置CBA = NCBA(CBA, A).
(2). 如果W > UCB(CBA, C, A), 设置CBA = NCBA(CBA, A).
(3). 设置BA(F) = CBA.
(4). 设置CBA = CBA + W.
注意AAPCS不允许对外暴露的接口包含填充结构体或位域. However a scheme for laying out packed bit-fields can be achieved by reducing the alignment, A, in the above rules to below that of the natural container type. ARMCC uses an alignment of A=8 in these cases, but GCC uses an alignment of A=1.
7.1.7.2. 位域提取表达式
为访问宽度W, 容器宽度C, 位地址BA(F)的位域F:
(1). 装载自然对齐的字节地址为TRUNCATE(BA(F), C) / 8的容器到寄存器R(或两个寄存器如果容器是64位的).
(2). 设置Q = MAX(32, C).
(3). 小端字节序情况下设置R = (R << ((Q - W) - (BA MOD C))) >> (Q - W).
(4). 大端字节序情况下设置R = (R << (BA MOD C)) >> (Q - W).
long long位域使用64位移位操作; 通常情况下这些表达式可以简化为32位操作(见7.1.7.5. Volatile bit-fields preserving number and width of container accesses for volatile bit-fields).
7.1.7.3. 超长的位域
C++允许位域的宽度规格超过其容器大小, 其分配规则在[GC++ABI]中规定. 仍使用上述的符号, 超长的位域宽度为W, 容器的宽度为C, 对齐为A:
(1). 选择一个新的容器宽度C', 其基本数据类型的宽度的最大值小于等于W. 该容器的对齐是A'. 注意C'大于等于C, A'大于等于A.
(2). 如果C'大于UCB(CBA, C', A')则设置CBA=NCBA(CBA, A'). 这确保了位域被放在下一个容器的起始地址.
(3). 使用(C, C', A')代替(W, C, A)来分配一个普通位域.
(4). 设置CBA=CBA+W-C.
注意尽管标准C++没有long long数据类型, 但这是该语言常见的一种扩展. 为避免出现这类类型引起超长位域布局的改变规则描述根据基本数据类型(见4.1. 基本数据类型)64位整型总是存在.
超长的位域的访问可以通过访问其容器类型.
7.1.7.4. 组合位域与非位域成员
一个位域的容器可能跨越非位域成员. 为决定位域成员的布局CBA被设为第一个未分配位在处理非位域类型后.
注意任何在处理位域成员前的结构体填充均视作结构体的一部分且在决定CBA时必须考虑进去.
当一个非位域成员跟随一个位域成员时, 它被放在已分配位域后最低可接受的地址.
注意当布局基本数据类型时考虑它们全是宽度等于其容器的位域是可能的. 7.1.7.1.中的规则位域不得大于其容器可以用来决定结构体的地址.
7.1.7.5. Volatile bit-fields preserving number and width of container accesses
当一个volatile位域被读取时, 必须使用该容器类型宽度的访问方式精确的读容器一次.
当一个volatile位域被读取时, 必须使用该容器类型宽度的访问方式精确的读写容器各一次. 两次访问操作不是原子的.
多次访问同意volatile位域, 或同一容器的额外volatile位域不会被同步. 举例而言一个位域的增加总是需要两次读与一次写操作实现.
注意即使位域的宽度与对齐暗示可以使用更小的类型来提升效率, volatile访问规则仍需被遵守. 对于写操作而言即使整个容器的内容都被替换读的过程仍必须进行.
如果容器跨越两个volatile位域则访问一个位域会触发访问另外一个位域. i.e. struct S {volatile int a:8; volatile char b:2;}; 访问a将会访问b, 但反过来却不是.
如果非volatile位域的容器跨越volatile位域那么访问非volatile位域是否触发对volatile位域的访问是未定义的.

7.2. 参数传递转换
子程序调用的参数列表是通过获取用户参数的被指定的顺序来形成的.
(1). 对C而言每个参数都是通过源代码中指定值形成的, 除了那些通过第一个元素的地址传递的数组.
(2). 对C++而言一个隐式this参数作为一个额外参数在第一个用户参数之前传递, 其它编组C++参数的规则见CPPABI.
(3). 对变参函数而言, 省略参数中的float参数被转换为double.
然后按程序调用标准守则(见5.5. 参数传递)或对应变型(variant)处理参数列表.

 

附录A. 高级SIMD扩展的支持
略.

 

posted @ 2018-02-22 15:21  Five100Miles  阅读(2419)  评论(0编辑  收藏  举报