AMD64架构下的 SystemV ABI (1)
1 简介
AMD64 架构是 x86 架构的一种扩展。任何实现 AMD64 架构规范的处理器都会为早期的 Intel 8086 架构(包括 Intel 386,Intel Pentium 和 AMD K6-2 这类 32 位处理器)提供兼容模式。满足 AMD64 ABI 的操作系统可以为这些兼容模式下运行的程序提供支持。AMD64 ABI 并不适用于这类程序。本文档仅适用于在 AMD64 架构提供的 “长”模式下运行的程序。
使用 AMD64 指令集的二进制可以被编写为 32 位模型的程序,在这种模型下,其 C 语言数据类型 int、long 和所有的指针类型是 32 位对象(ILP32);也可以被编写为 64 位模型的程序,其 C 语言数据类型 int 是 32 位对象,但是 long 和 所有的指针类型是 64 位对象(LP64)。该规范涵盖了 LP64 和 ILP32 编程模型。
除非另有说明,AMD64 架构的 ABI 遵循 Intel386 ABI 中描述的约定。AMD64 ABI 仅指示对 Intel386 ABI 进行改进的地方,而不是复制全部的 Intel386 ABI。
鉴于没有尝试为 C 语言之外的语言特殊指定 ABI,这里假设很多编程语言可以直接链接用 C 语言编写的代码,本文档的 ABI 规范也遵守这个假设。
2 软件安装
该文档没有特殊指定软件在 AMD64 架构下的安装方式。
3 低级别系统信息
3.1 机器接口
3.1.1 处理器架构
任何应用程序都可以期望 AMD64 处理器实现了下面微架构级别
表中提到的基线特性。大多数特性名称都对应于处理器手册中提到的 CPUID
位。但 OSFXSR
和 SCE
是例外,他们由 %cr4
寄存器和 IA32_EFER
MSR(译注:Model Specific Registers) 中的位控制。
表中除基线架构外,还定义了多个由后来 CPU 模块实现的微架构级别,这些级别从 x86-64-v2
开始,旨在支持在与其兼容的系统上的加载优化实现。这个级别是累加的,前面级别的特性隐式被后面的级别所包含。
级别 x86-64-v3
和 x86-64-v4
仅在相应功能已完全启用时可用。 这意味着系统必须通过处理器手册中针对这些功能的完整检查,包括对使用 xgetbv
获得的 XCR0
功能标志的验证。
微架构级别的建议使用
下表中的微架构级别名称可以用于目录名称(可以被动态链接器根据当前 CPU 的支持级别进行搜索),也可以被编译器用于选择 CPU 功能组。发行版还可以指定它需要某个级别的 CPU 支持。
级别 | CPU 特性 | 示例指令 |
---|---|---|
(baseline) | CMOV | cmov |
CX8 | cmpxchg8b | |
FPU | fld | |
FXSR | fxsave | |
MMX | emms | |
OSFXSR | fxsave | |
SCE | syscall | |
SSE | cvtss2si | |
SSE2 | cvtpi2pd | |
x86-64-v2 | CMPXCHG16B | cmpxchg16b |
LAHF-SAHF | lahf | |
POPCNT | popcnt | |
SSE3 | addsubpd | |
SSE4_1 | blendpd | |
SSE4_2 | pcmpestri | |
SSSE3 | phaddd | |
x86-64-v3 | AVX | vzeroall |
AVX2 | vpermd | |
BMI1 | andn | |
BMI2 | bzhi | |
F16C | vcvtph2ps | |
FMA | vfmadd132pd | |
LZCNT | lzcnt | |
MOVBE | movbe | |
OSXSAVE | xgetbv | |
x86-64-v4 | AVX512F | kmovw |
AVX512BW | vdbpsadbw | |
AVX512CD | vplzcntd | |
AVX512DQ | vpmullq | |
AVX512VL | N/A |
举个例子,如果要选择第二级 x86-64-v3
,程序员必须使用 -march=x86-64-v3
的 GCC 参数来构建共享对象(shared object)。生成的共享对象需要安装到目录 /usr/lib64/glibc-hwcaps/x86-64-v3
或 /usr/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3
(防止一个发行版存在多架构文件系统布局)。为了支持仅实现 K8 基线的系统,一个后备(fallback)实现必须被安装到默认位置:/usr/lib64
或 /usr/lib/x86_64-linux/gnu
。 它必须使用 -march=x86-64(默认值)
构建。如果不遵循此准则,在系统不支持这些被优化过的共享对象对应的级别的情况下,加载这些共享库将会失败。
安装在对应的 glibc-hwcaps
子目录下的共享对象可以使用该级别和更早级别的 CPU 功能,而无需进一步的检测。 对本节中未列出或仅在以后级别列出的其他 CPU 功能的运行时检测仍然是必需的(即使所有当前的 CPU 都一起实现了那些 CPU 功能)。
如果发行版需要某个级别的支持,它们会使用适当的 -march=
选项构建所有内容,并将构建的二进制文件安装在默认位置。当针对此类发行版时,程序员可以使用相同的 -march=
选项构建他们的二进制文件并将它们安装到默认位置。为更高级别优化的共享对象仍然可以安装到具有适当名称的子目录中。
3.1.2 数据表示
在本规范中,术语 byte
指代一个 8 位对象,术语 twobyte
指代一个 16 位对象,术语 fourbyte
指代一个 32 为对象,术语 eightbyte
指代一个 64 位对象,术语 sixteenbyte
指代一个 128 位对象。
基本类型
下图展示了 ISO C
和处理器标量类型的对应关系。其中:__int128
, _Float16
, __float80
, __float128
, __m64
, __m128
, __m256
和 __m512
是可选的。
__float128
类型使用 15 位指数、113 位尾数(浮点最高有效位是隐式的,被指数 e 隐含,详情参见 IEEE754)和指数偏差16383(译注:在浮点数中,指数存储的值通过指数偏差从实际值进行偏移,进行偏置是因为指数必须是有符号值才能表示微小值和巨大值,但是通常用补码表示有符号值,这会增加比较的难度,为了解决这个问题,指数被存储为适合比较的无符号值,并且在解释时通过减去偏差将其转换为有符号范围内的指数)。
long double
类型使用 15 位指数,64 位尾数,显式最高有效位(详情参见 IEEE754)和指数偏差 16383。虽然 long double
需要 16 字节的存储空间,但只有前 10 字节是有效的。 剩下的六个字节是尾部填充,这些字节的内容是未定义的。
类型 | C 语言 | sizeof | 对齐 | AMD64 架构 |
---|---|---|---|---|
整形 | _Bool | 1 | 1 | boolean |
char | 1 | 1 | signed byte | |
signed char | 1 | 1 | signed byte | |
unsigned char | 1 | 1 | unsigned byte | |
signed short | 2 | 2 | signed twobyte | |
unsigned short | 2 | 2 | unsigned twobyte | |
signed int | 4 | 4 | signed fourbyte | |
enum | 4 | 4 | signed fourbyte | |
unsigned int | 4 | 4 | unsigned fourbyte | |
signed long (LP64) | 8 | 8 | signed eightbyte | |
unsigned long (LP64) | 8 | 8 | unsigned eightbyte | |
signed long (ILP32) | 4 | 4 | signed fourbyte | |
unsigned long (ILP32) | 4 | 4 | unsigned fourbyte | |
signed long long | 8 | 8 | signed eightbyte | |
unsigned long long | 8 | 8 | unsigned eightbyte | |
__int128 | 16 | 16 | signed sixteenbyte | |
signed __int128 | 16 | 16 | signed sixteenbyte | |
unsigned __int128 | 16 | 16 | unsigned sixteenbyte | |
指针 | 任意类型 * (LP64) | 8 | 8 | unsigned eightbyte |
任意类型 (*)() (LP64) | 8 | 8 | unsigned eightbyte | |
任意类型 * (ILP32) | 4 | 4 | unsigned fourbyte | |
任意类型 (*)() (ILP32) | 4 | 4 | unsigned fourbyte | |
浮点 | _Float16 | 2 | 2 | 16-bit (IEEE-754) |
float | 4 | 4 | single (IEEE-754) | |
double | 8 | 8 | double (IEEE-754) | |
__float80 | 16 | 16 | 80-bit extended (IEEE-754) | |
long double | 16 | 16 | 80-bit extended (IEEE-754) | |
__float128 | 16 | 16 | 128-bit extended (IEEE-754) | |
long double | 16 | 16 | 128-bit extended (IEEE-754) | |
十进制浮点 | _Decimal32 | 4 | 4 | 32bit BID (IEEE-754R) |
_Decimal64 | 8 | 8 | 64bit BID (IEEE-754R) | |
_Decimal128 | 16 | 16 | 128bit BID (IEEE-754R) | |
数据包(Packed) | __m64 | 8 | 8 | MMX and 3DNow! |
__m128 | 16 | 16 | SSE and SSE-2 | |
__m256 | 32 | 32 | AVX | |
__m512 | 64 | 64 | AVX-512 |