AIX 向量编程

某些PowerPC®处理器实现了单指令多数据(SIMD)式向量扩展。

通常称为 AltiVec 或 VMX , PowerPC 体系结构的向量扩展提供了用于执行向量和矩阵数学函数的附加指令集。

向量算术逻辑单元是 SIMD 型算术单元,其中单指令对每个向量的所有数据元素执行相同的操作。 AIX® 5.3推荐技术等级为 5300-30,是首个支持矢量编程的AIX版本。 IBM® PowerPC970处理器是AIX支持的第一款实现了向量扩展的处理器。 这些处理器当前位于随 BladeCenter提供的 JS20 刀片服务器中。

向量扩展概述

向量扩展包含另一组 32 个 128 位的寄存器,这些寄存器可包含各种向量,包括 8 位、16 位 或 32 位有符号或无符号整数或 32 位 IEEE 单精度浮点数。 有一个向量状态和控制寄存器,它包含一个指示饱和度的粘滞状态位,以及一个用于启用 Java™ 或非 Java 方式进行浮点运算的控制位。

AIX 为每个新进程初始化的缺省方式是启用 Java 方式,这将提供符合 IEEE 标准的浮点操作。 备用的非 Java 方式会导致浮点计算的精确度较低,这样,对于某些实现和特定操作,计算速度可能会快很多。 例如,在以 Java 方式运行的 PowerPC 970 处理器上,如果输入操作数或结果为非正常,那么某些向量浮点指令将迂到异常,从而导致操作系统进行高成本仿真。 由于此原因,最好在可接受舍入时考虑显式地启用非 Java 方式,或小心地进行尝试以避免向量计算出现不正常的值。

向量扩展还包含超过 160 条在向量寄存器和内存之间提供装入和存储访问权的指令,这些指令用在寄存器操作、浮点算术、整数算术、逻辑运算和向量比较操作中。 浮点算术指令使用 IEEE 754-1985 单精度格式,但不报告 IEEE 异常。 如 IEEE 对未捕获的异常所指定的那样,对所有异常情况将生成缺省结果。 仅提供 IEEE 缺省舍入方式,即舍入成最接近的数。 未提供任何浮点除法或平方根指令,但对除法提供了倒数求值指令并对平方根提供了倒数平方根求值指令。

还有一个具有特殊用途的 32 位寄存器,该寄存器由软件管理以表示正在使用的向量寄存器的位掩码。 这使操作系统能够在进行上下文切换管理时优化向量保存和恢复算法。

向量能力的运行时确定

一个可通过读取 _system_configuration 结构的 vmx_version 字段来确定系统是否支持向量扩展的程序。 如果此字段不为零,那么系统处理器和操作系统支持向量扩展。 在 /usr/include/sys/systemcfg.h 中提供了用于执行此测试的 __power_vmx() 宏。 这对根据情况利用向量扩展(如果存在)或使用同等功能的标量代码路径(如果未提供向量扩展)的软件可能很有用。

AIX ABI 扩展

AIX 应用程序二进制接口 (ABI) 已扩展为支持添加向量寄存器状态和约定。 请参阅 汇编程序语言参考 ,以获取 ABI 扩展的完整描述。

AIX 支持 AltiVec 编程接口规范。 下面是 C 和 C++ 向量数据类型的表。 所有向量数据类型的大小都为 16 个字节,并且必须在 16 字节边界上对齐。 包含向量类型的聚集必须遵循使聚集与其最大成员的需求一致的常规约定。 如果包含向量类型的聚集被压缩,那么不能保证向量类型的 16 字节对齐。 需要支持 AltiVec 编程接口规范的 AIX 编译器。

表 1. 新的 C 和 C++ 向量数据类型
新的 C 和 C++ 类型 内容
vector unsigned characters 16 个无符号字符
vector signed characters 16 个有符号字符
vector bool characters 16 个无符号字符
vector unsigned short 8 个无符号短整型数
vector signed short 8 个有符号短整型数
vector bool short 8 个无符号短整型数
vector unsigned integers 4 个无符号整数
vector signed integers 4 个有符号整数
vector bool integers 4 个无符号整数
vector float 4 个浮点数

下表概括了向量寄存器用法约定。

表 2。 向量寄存器约定
寄存器类型 寄存器 状态 使用
VR VR0 易失性 临时寄存器
VR1 易失性 临时寄存器
VR2 易失性

第一个向量参数

第一个函数返回值向量

VR3 易失性 第二个向量参数,临时
VR4 易失性 第三个向量参数,临时
VR5 易失性 第四个向量参数,临时
VR6 易失性 第五个向量参数,临时
VR7 易失性 第六个向量参数,临时
VR8 易失性 第七个向量参数,临时
VR9 易失性 第八个向量参数,临时
VR10 易失性 第九个向量参数,临时
VR11 易失性 第十个向量参数,临时
VR12 易失性 第十一个向量参数,临时
VR13 易失性 第十二个向量参数,临时
VR14:19 易失性 临时
VR20:31 保留(缺省方式)

非易失性(扩展 ABI 方式)

当使用缺省的向量启用方式时,这些寄存器将被保留并且不得使用。

在扩展 ABI 向量启用方式下,这些寄存器为非易失性寄存器,并且经过函数调用,它们的值仍将保留

特殊用途 VRSAVE 已预留 AIX ABI 中,不使用 VRSAVE。 ABI 兼容程序不得使用或改变 VRSAVE。
特殊用途 VSCR 易失性 向量状态和控制寄存器包含饱和度状态位和非 Java 方式控制位。

AltiVec 编程接口规范定义 VRSAVE 寄存器用作使用中的向量寄存器的位屏蔽。 AIX 要求应用程序从不修改 VRSAVE 寄存器。

函数中的前 12 个向量参数将被置于 VR2 至 VR13。 不需要的向量参数寄存器包含输入到函数中之前未定义的值。 长度不可变的参数列表中的向量参数未映射到通用寄存器 (GPR) 中。 任何其他向量参数(第十三个及其之后的向量参数)将传递给程序堆栈(16 字节对齐)上内存中的参数区域内它们相应的映射位置,该位置对应于它们在参数列表中的位置。

对于长度可变的参数列表,va_list 将继续作为指针指向下一个参数的内存位置。 当 va_arg() 访问向量类型时,va_list 必须首先与 16 字节边界对齐。 可变长度参数列表的接收程序和使用程序负责在检索向量类型参数之前执行此对齐操作。

由值传递的非压缩结构或并集(其中的任意位置都有一个向量成员)将与堆栈上的 16 字节边界对齐。

使用可变长度参数列表的函数使参数区域内的映射的所有参数根据它们的类型进行排序和对齐。 可变长度参数列表的前八个字(32 位)或双字(64 位)映射到 GPR 中的 r3 至 r10。 这包括向量参数。

将返回值声明为向量数据类型的函数将返回值放置于 VR2 中。 返回向量类型或具有向量参数的任何函数都需要函数原型。 通常,这可避免编译器将 VR 映射到 GPR 中。

旧 ABI 兼容性和互操作性

由于接口(例如 setjmp()、longjmp()、sigsetjmp()、siglongjmp()、_setjmp()、_longjmp()、getcontext()、setcontext()、makecontext() 和 swapcontext())必须保存和恢复非易失性机器状态的特性,因此在考虑旧的和向量扩展的 ABI 模块之间相关性时将带来风险。 为了使问题复杂化, libc 中的 setjmp 函数系列位于 libc 的静态成员中, 这意味着每个现有 AIX 二进制文件都具有 setjmp 系列的静态绑定副本,以及与它所链接的 AIX 版本一起存在的其他副本。 此外,现有 AIX 二进制文件具有 jmpbufs 和 ucontext 数据结构定义,这些定义不足以容纳任何其他非易失性向量寄存器状态。

在任何旧模块和新模块交错调用或旧模块可能执行 longjmp() 或 setcontext() 的回调情况下,绕过向量扩展模块的常规链接约定会有破坏非易失性向量寄存器状态的风险。

因此,当 AIX ABI 定义非易失性向量寄存器时,在 AIX 编译器中使用向量 (AltiVec) 时,缺省编译方式是不使用任何非易失性向量寄存器。 这将产生一个缺省编译环境,在该环境中可安全地使用向量 (AltiVec) ,而不会对与旧二进制文件的互操作性带来任何风险。

对于互操作性和模块相关性完全已知的应用程序,可启用其他允许使用非易失性向量寄存器的编译选项。 此方式应仅在以下情况下使用:所有相关的旧模块和行为完全已知并被了解为与一些函数(例如 setjmp()、sigsetjmp()、_setjmp() 或 getcontext())没有任何相关性,或确保使用常规子例程链接约定执行所有模块转换并且未使用对上游旧模块的回调。

缺省 AltiVec 编译环境根据 AltiVec 技术编程接口手册预定义了 __VEC__

当启用使用非易失性向量寄存器的选项时,编译环境还必须预定义 __EXTABI__。 您可以通过显式定义 __AIXEXTABI来编译支持扩展 ABI 的非向量模块。 这将确保那些模块可安全地与启用了向量的模块(已启用以使用非易失性向量寄存器)进行交互。

扩展上下文

为了支持向量扩展以及其他扩展 (例如用户密钥) 所需的其他机器状态, AIX 5.3 引入了对扩展上下文结构的支持。 主要应用程序可见的对机器上下文信息的使用情况显示在提供给信号处理程序的 sigcontext 结构中,并且,将从信号处理程序返回对上述 sigcontext 中导致的对机器上下文的激活操作。 sigcontext 结构实际上是较大的 ucontext 结构的一部分。 这两个结构最多有 sizeof(struct sigcontext) 是相同的。 当 AIX 构建要传递到信号处理程序的信号上下文时,它实际上会在信号处理程序的堆栈上构建 ucontext 结构。 信号上下文的机器上下文部分必须包含无意中断的上下文的所有活动的机器状态(易失性和非易失性机器状态)。 要完成此操作而不影响二进制文件与现有信号处理程序的兼容性,那么先前在 ucontext 结构中保留的空间现在将充当有关是否提供了扩展上下文信息的指示。

ucontext, __extctx 中新定义了一个字段,即扩展上下文结构 struct __extctx 的地址(如 sys/context.h 文件中所定义)。 ucontext 结构中的新字段 __extctx_magic 指示当 __extctx_magic 的值等于 __EXTCTX_MAGIC 的值时扩展上下文信息是否有效。 当信号传递和返回时,将使用向量扩展的线程的其他向量机器状态作为此新的上下文扩展的成员保存并恢复到 ucontext 结构。

ucontext 结构还用于 API (例如 getcontext () , setcontext () , swapcontext () 和 makecontext ())。 在这些情况下,需要保存的上下文是由于自愿操作,对于自愿操作,调用链接约定只需要保存非易失性机器状态。 由于 AIX上的缺省向量启用方式 (如 ABI 部分中所述) 是不使用非易失性向量寄存器,因此大多数应用程序都不需要 ucontext 结构的扩展。 如果应用程序选择显式地启用非易失性向量寄存器的使用,那么它将选取已有用于 __extctx 字段空间的扩展大小的 ucontext 结构,编译器编译的 __EXTABI__ 的隐式定义包含了该字段。 扩展的 ucontext 还可由 __AIXEXTABI 的显式定义选取。

类似地,因为不使用非易失性向量寄存器,所以用于 setjmp() 或 longjmp() 的 jmp_buf 不需要对启用了向量(缺省方式)的应用程序进行更改。 由于编译器编译的 __EXTABI__ 的隐式定义,显式启用非易失性向量寄存器将导致分配较大的 jmp_buf。 扩展跳转缓冲区还可由 __AIXEXTABI 的显式定义激活。

有关扩展上下文信息的更详细的布局,请参阅 sys/context.h 头文件。

向量内存分配和对齐

向量数据类型引入需要 16 字节对齐的数据类型。 根据 AltiVec 编程接口规范,一组 malloc 子例程 (vec_malloc , vec_free , vec_realloc 和 vec_calloc) 由提供 16 字节对齐分配的 AIX 提供。

启用了向量的编译(编译器隐式定义了 _VEC_)将导致对旧的 malloc 和 calloc 的任何调用分别被重定向到它们的向量安全子例程 vec_malloc 和 vec_calloc。 还可显式编译非向量代码以通过显式定义 __AIXVEC 来拾取这些相同的 malloc 和 calloc 重定向。 还可在运行时控制缺省 malloc()、realloc() 和 calloc() 分配的对齐。

首先,在任何程序的外部,可将新的环境变量 MALLOCALIGN 设为每个 malloc() 分配所需的缺省对齐格式。 例如:
MALLOCALIGN=16;  export MALLOCALIGN

MALLOCALIGN 环境变量可设为 2 的任何次幂,该值大于或等于相应执行方式(4 个字节对应于 32 位方式,8 个字节对应于 64 位方式)下的指针大小。 如果对 MALLOCALIGN 设置的值无效,那么该值将上入为 2 的下一次幂并且所有后续的 malloc() 分配将按该值对齐。

并且,在程序内部,该程序可对 mallopt() 接口使用新的命令选项来指定将来的分配所需的对齐格式。 例如:
rc = mallopt(M_MALIGN, 16);

有关更多信息,请参阅 mallopt 和 MALLOCALIGN。

向量数据类型的 printf 和 scanf

根据 AltiVec 编程接口规范,将支持添加到 AIX 版本的 scanf , fscanf , sscanf , wsscanf , printf , fprintf , sprintf , snprintf , wsprintf , vprintf , vfprintf , vsprintf 和 vwsprintf 的新向量转换格式字符串。 新的大小格式转换程序如下:

  • vl 或 lv 使用一个参数并修改现有的整型转换,这对于输出转换将生成 vector signed int、vector unsigned int 或 vector bool,或对于输入转换将生成 vector signed int * 或 vector unsigned int *。 然后,数据将被作为一系列四个 4 字节的部分进行处理,每个部分都应用后续的转换格式。
  • vh 或 hv 使用一个参数并修改现有的短整型转换,对于输出转换生成 vector signed short 或 vector unsigned short,或者对于输入转换生成 vector signed short * 或 vector unsigned short *。 数据将被作为一连串的八个 2 字节的部分进行处理,每个部分应用后续的转换格式。
  • v 使用一个参数并修改 1 字节整数、1 字节字符或 4 字节浮点转换。 如果转换为浮点转换,那么对于输出转换的结果为 vector float,或者对于输入转换的结果为 vector float *。 数据将被视为一连串的四个 4 字节浮点部分,每个部分应用后续的转换格式。 如果转换是整数或字符转换,那么对于输出转换的结果是 vector signed char、vector unsigned char 或 vector bool char,或者对于输入转换的结果是 vector signed char * 或 vector unsigned char *。 数据将被作为一连串的十六个 1 字节部分进行处理,每个部分应用后续的转换格式。

可应用于单数形式的向量数据类型的任何转换格式都可与向量形式一起使用。 %d、%x、%X、%u、%i 和 %o 整数转换可与 %lv、%vl、%hv、%vh 和 %v 向量长度限定符一起应用。 %c 字符转换可与 %v 向量长度限定符一起应用。 %a、%A、%e、%E、%f、%F、%g 和 %G 浮点约定可与 %v 向量长度限定符一起应用。

对于输入转换,可指定可选分隔符字符(不包括分隔符前面的空格)。 如果未指定任何分隔符,那么缺省分隔符为空格(包括分隔符前面的空格字符),除非转换是 c,否则缺省转换为空。

对于输出转换,可在向量大小转换之前紧挨着指定可选分隔字符。 如果未指定任何分隔符,那么除非转换是 c,否则缺省分隔符为空格,并且缺省为空。

线程应用程序

还支持多线程应用程序利用向量扩展。 这些应用程序在系统系统范围(1:1 线程模型)和进程范围(M:N 线程模型)内受支持。 如果某个多线程应用程序是在启用了非易失性向量寄存器的情况下编译的,那么该应用程序的 pthread 将被标记为扩展 ABI pthread。 结果将会是在 pthread 库中对那些线程分配较大的上下文保存缓冲区。 dbx AIX 调试器还提供对支持向量的多线程程序的机器级别调试的全面支持。

编译器

支持向量扩展的 AIX 编译器必须符合 AIX ABI Vector Extension。 如前所述, AIX 上支持向量的缺省编译方式应该是禁用非易失性向量寄存器。 可提供用于启用非易失性向量寄存器的显式选项,并且您可在了解有关新旧模块互操作性的问题和风险之后自行决定是否启用该选项。

启用非易失性向量寄存器时, C 或 C++ 编译器必须预定义 __EXTABI__。 此外,当为任何形式的向量编译启用时,期望 C 或 C++ 编译器预定义 __VEC__。 如果编译未启用向量的 C 或 C++ 模块以与启用向量的 Fortran 模块进行链接,那么最好在定义了至少 __AIXVEC (类似于 __VEC__的显式定义) 的情况下显式编译 C 或 C++ 模块。 以及 __AIXEXTABI (类似于 __EXTABI的显式定义) (如果在 Fortran 模块中启用了非易失性向量寄存器)。

除 AltiVec 编程接口规范(为向量编程提供对 C 和 C++ 语言的显式扩展)外,某些编译器在确定支持向量扩展的目标处理器时将可能允许在某些优化设置中利用向量扩展。

有关更多详细信息,请参阅编译器文档。

汇编程序

AIX 汇编程序 (在 /usr/ccs/bin/as 目录中) 现在支持向量扩展定义的附加指令集,特别是由 PowerPC 970 处理器实现的指令集。 可使用新的 -m970 汇编方式或源文件中的 .machine 970 pseudo op 来启用新向量指令的汇编。 请参阅 汇编程序语言参考 以获取更多信息。

调试器

/usr/ccs/bin/dbx, 中的 dbx AIX 调试器支持对支持向量的程序进行机器级别调试。 此支持包含对新向量指令进行反向编译以及显示和设置向量寄存器的能力。 定义了新的 $instructionset 值 970 ,用于在未在 PowerPC 970 系统上运行 dbx 时启用特定于 PowerPC 970的指令 (包括向量指令) 的反汇编。 请注意,如果在 PowerPC 970上运行 dbx ,那么缺省 $instructionset 将为 970。

要查看向量寄存器,必须使用子命令 unset $novregs,因为缺省情况下,不会显示向量寄存器。 另外,如果处理器不支持向量扩展,或者正在检查的进程或线程未在使用向量扩展,那么将不会显示向量寄存器状态。 否则,寄存器子命令将以原始十六进制格式显示所有向量寄存器及其内容。

还可逐个显示根据基本类型进行了格式转换的向量寄存器。 例如,显示 $vr0 会将寄存器 VR0 的内容显示为 4 个整数的序列。 显示 $vr0c 会将寄存器 VR0 的内容显示为 16 个字符的序列。 显示 $vr0s 会将寄存器 VR0 的内容显示为 8 个短整型数的序列,而显示 $vr0f 会将寄存器 VR0 的内容显示为 4 个浮点的序列。

可对全部向量寄存器进行赋值(例如,赋值 $vr0 = $vr1),或者就像对数组的某个元素赋值一样,对向量寄存器的个别向量元素进行赋值。 例如,赋值 $vr0[3] = 0x11223344 仅会设置 VR0 的第四个整数成员。赋值 $vr0f[0] = 1.123 仅会导致 VR0 的第一个浮点成员设为值 1.123。

您可以在函数或程序的整个执行过程中跟踪向量寄存器,例如 main 中的 tracei $vr0 将在 main () 中每次修改时显示 VR0 的内容。 同样,通过将其中一个格式寄存器 ($vr0f, $vr0c, $vr0s) 指定为 tracei ,将相应地格式化内容的每个显示。

只要编译器将向量数据类型表示为它们的基本类型的序列,dbx 就应该也能够将转换了格式的向量数据类型显示为一个数组。

有关更多信息,请参阅 dbx 命令文档。

PTT_READ_VEC 和 PTT_WRITE_VEC 形式的新 ptrace 操作中还启用了第三方调试器,用于读取或写入线程的向量寄存器状态。 有关详细信息,请参阅 ptrace 文档。

还增强了 /proc 文件系统以支持基于 /proc 的调试器。 分别扩展启用了向量的进程和线程的 status 和 lwpstatus 文件以包含向量寄存器状态。 编写进程或线程控制文件以设置向量寄存器状态时支持新的控制消息 PCSVREG。 有关更多详细信息,请参阅 /proc 文件参考。

核心文件

AIX 还支持将向量机状态包含为支持向量的进程或线程的核心文件的一部分。 仅当进程或线程正在使用向量扩展时,该线程的核心映像中才会包含向量机器状态。 请注意,如果选择AIX 4.3 之前的核心文件格式,那么将不包含向量状态。 向量状态仅在当前核心文件格式中受支持。 可使用 dbx 命令读取并显示启用了向量的核心文件的向量机器状态。