ARM 可扩展向量扩展(Scalable Vector Extension,SVE)是继 Neon 之后的 SIMD 扩展。SVE 允许在 CPU 实现中采用灵活的向量长度,其取值范围可从最小 128 位至最大 2048 位,但必须是 2 的幂次。因此有效的向量长度实现为 128、256、512、1024 和 2048 位。SVE 设计确保同一应用程序可在支持 SVE 的不同实现上运行,无需重新编译代码。SVE 提升了该架构对高性能计算(HPC)和机器学习(ML)应用的适用性,这些应用需要处理海量数据。
SVE 引入了以下关键特性:
- 可扩展向量(Scalable vectors);
- 逐通道谓词执行(Per-lane predication);
- 聚集加载与分散存储(Gather-load and scatter-store);
- 投机执行向量化(Speculative vectorization);
- 水平与串行化向量运算(Horizontal and serialized vector operations)。
这些特性在处理大型数据集时有助于实现循环的向量化与优化。
1. SVE 寄存器
SVE 新增了以下寄存器:
- 32 个可扩展向量寄存器:
Z0-Z31
- 16 个可扩展谓词寄存器:
P0-P15
- 一个首次故障谓词寄存器:
FFR
- 可扩展向量系统控制寄存器 :
ZCR_Elx
下面我们依次查看这些寄存器。
1.1. 可扩展向量寄存器
可扩展向量寄存器 Z0-Z31
在微架构中可实现 128-2048 位宽度。其低 128 位与 Neon 固定 128 位 V0-V31
向量寄存器共享。下图展示了可扩展向量寄存器 Z0-Z31
:

可扩展向量可容纳 64 位、32 位、16 位和 8 位元素,支持整数、双精度、单精度和半精度浮点元素,可为每个异常等级(EL)配置向量长度。
1.2. 可扩展谓词寄存器
为了控制哪些激活元素参与运算,SVE 指令集中大量使用谓词寄存器作为掩码,这也为向量运算提供了灵活性。下图展示了可扩展谓词寄存器 P0-P15
:

谓词寄存器通常作为数据操作的位掩码使用,每个谓词寄存器的长度是 Zx
的 1/8,P0-P7
是用于加载、存储和算术运算的控制谓词,P8-P15
是用于循环管理的额外谓词。
1.3. 首次故障谓词寄存器
首故障寄存器(FFR)是一个特殊的谓词寄存器,由首故障加载和存储指令进行设置,用于指示每个元素的加载和存储操作的成功程度。FFR 旨在支持投机内存访问,这在许多情况下使向量化更容易且更安全。
1.4. 可扩展向量系统控制寄存器
在已实现的最大向量长度范围内,还可以通过 ZCR_Elx
寄存器为每个异常级别配置向量长度。向量长度可以是 128 位至已实现的最大非流式 SVE 向量长度之间的任意 2 的幂次方值。
特权异常级别可通过可扩展向量控制寄存器 ZCR_El1
、 ZCR_El2
和 ZCR_El3
的 LEN
字段,来约束该异常级别及更低特权异常级别的向量长度:

可扩展向量系统控制寄存器指示 SVE 实现特性:ZCR_Elx.LEN
字段用于当前及更低异常级别的向量长度,其他位保留供未来使用。
2. SVE 汇编语法
SVE 汇编语法格式由操作码、目标寄存器、谓词寄存器(若指令支持谓词掩码)以及输入操作符组成。以下指令示例展示了该格式的具体细节。
示例1:
LDFF1D {<Zt>.D}, <Pg>/Z, [<Xn|SP>, <Zm>.D, LSL #3]
其中:
<Zt>
是向量,可以是Z0-Z31
;<Zt>.D
和<Zm>.D
指定目标向量和操作数向量的元素类型,无需指定元素数量;<Pg>
是谓词,可以是P0-P15
;/Z
是清零谓词化;<Zm>
指定了聚集加载地址模式的偏移量。
示例2:
ADD <Zdn>.<T>, <Pg>/M, <Zdn>.<T>, <Zm>.<T>
其中:
/M
是合并谓词;<Zdn>
既是目标寄存器也是输入操作数之一。为方便起见,指令语法在两处都显示为<Zdn>
。在汇编编码中,为简化处理,它们仅被编码一次。
示例3:
ORRS <Pd>.B, <Pg>.Z, <Pn>.B, <Pm>.B
S
是对谓词条件标志 NZCV 的新解释;<Pg>
是一个支配谓词,在示例操作中充当“位掩码”的角色。
3. 逐通道谓词执行
为支持对选定元素的灵活操作,SVE 引入了 16 个控制谓词寄存器 P0-P15
,用于指示向量激活通道的有效操作。例如:
ADD Z0.D, P0/M, Z0.D, Z1.D
上述指令将激活元素 Z0 和 Z1 相加,并将结果放入 Z0。P0 表示操作数中哪些元素是激活状态和非激活状态。P0 后的 M 表示合并(Merge),这表示非激活元素将被合并,因此 Z0 的非激活元素在加法操作后仍保持其原始值。
(不知道怎么翻译 Merge,其表示 Z0 中对应元素不参与运算,保持原有的内容不变。)

如果 P0 后是 /Z
,这表示置零,那么目标寄存器的非激活元素在操作后将变为零。例如:
CPY Z0.B, P0/Z, #0xFF
上述指令将带符号整数 0xFF 复制到 Z0,其中 Z0.B 的非激活元素将被设置为 0。

4. 聚集加载与分散存储
SVE 中的寻址模式允许在聚集加载和分散存储指令中将向量用作基地址和偏移量,从而实现非连续内存位置的访问。例如:
LD1SB Z0.S, P0/Z, [Z1.S]
上述指令从由向量 Z1 的每个 32 位元素生成的基地址中加载有符号字节,放置到 Z0 中激活的 32 位元素中。
LD1SB Z0.D, P0/Z, [X0, Z1.D]
上述指令从由 64 位 X0 标量作为基地址、Z1 的每个 64 位元素作为偏移所生成的地址中加载有符号字节,放置到 Z0 中激活的 32 位元素中。
以下示例展示了 LD1SB Z0.S, P0/Z, [Z1.S],
的加载操作,其中 P0Z1
包含分散地址。加载完成后,每个 Z0.S
的最低字节将使用从分散内存位置获取的数据进行更新。

5. 谓词驱动的循环控制与管理
作为 SVE 的核心特性,谓词不仅能够灵活控制向量运算的各个元素,还支持谓词驱动的循环控制。谓词驱动的循环控制与管理使循环控制既高效又灵活。该特性通过在谓词寄存器中记录激活与非激活元素的索引,消除了处理部分向量额外循环头尾的开销。谓词驱动的循环控制与管理意味着,在后续循环迭代中,只有激活元素会执行预期操作。例如:
WHILEL0 P0.S, x8, x9
B.FIRST Loop_start
第一条指令生成 P0 中的谓词,从最低编号的元素开始为真,直到第一个无符号标量操作数 X8 的自增值大于等于第二个标量操作数 X9,然后变为假,直到最高编号的元素。
B.FIRST
(等同于B.MI)与 B.NFRST
(等同于B.PL)通常用于判断上述指令生成的 P0 的第一个元素所代表的谓词,决定是否进行分支,以作为循环的结束条件或继续条件。

6. 面向软件管理投机的向量分区
投机加载对传统向量的内存读取带来挑战,若读取过程中某些元素发生故障,难以撤销加载操作并追踪哪些元素加载失败。Neon 不支持投机加载。为实现向量的投机加载(例如 LDRFF
),SVE 引入了首故障向量加载指令。为允许向量访问跨越无效页面,SVE 还引入了首故障谓词寄存器(FFR)。当使用首故障向量加载指令向 SVE 向量加载数据时,FFR 寄存器会更新每个元素的加载成功或失败结果。发生加载故障时,FFR 会立即标记对应元素,将其余元素标记为 0 或(谓词为)假,且不会触发异常。通常使用 RDFFR
指令读取 FFR 状态。当首元素为假时, RDFFR
指令结束迭代;若首元素为真,则 RDFFR
指令继续迭代。FFR 长度与谓词向量相同,其值可通过 SETFFR
指令初始化。 以下示例使用 LDFF1D
从内存读取数据,同时 FFR 会相应更新:
LDFF1D Z0.D, P0/Z, [Z1.D, #0]
上述指令从由向量 Z1 生成的基地址加上 0 生成的内存地址中,将双字首次故障行为收集到 Z0 的激活元素中。非激活元素不会读取设备内存或生成故障信号,并在目标向量中设置为 0。从有效内存的成功加载将使 FFR 中的元素设置为真。首次故障的加载将使相应的元素设置为 0、FFR 中的其余元素为假。

7. 扩展浮点与水平归约运算
为支持向量中的高效归约操作并满足不同的精度要求,SVE 增强了浮点与水平归约运算。这些指令可能采用顺序或基于树结构的浮点归约排序方式,其中操作顺序可能导致不同的舍入结果。这些操作在结果可重复性与性能之间进行权衡。例如:
FADDA D0, P0/M, D1, Z2.D
上述指令从源向量中按严格顺序从低到高添加浮点数,并将结果累加到 SIMD&FP 标量寄存器中。该示例指令将 D1 和 Z2.D 中所有激活元素相加,并将结果放入标量寄存器 D0 中。向量元素严格按从低到高的顺序处理,源标量 D1 提供初始值。
FADDA 会忽略源向量中的非激活元素,而 FADDV 将执行递归成对累加并结果放入标量寄存器。
