前言
记录一下 A64 指令集的寻址方式。
Address Calculation Methods for Load/Store and Math Instructions in A64
Load/Store指令寻址方式
A64指令集支持以下几种主要的地址计算方式:
- 基址寄存器 (Base register only)
- 基址加偏移 (Base plus offset)
- 预索引 (Pre-indexed)
- 后索引 (Post-indexed)
- 字面相对寻址 (Literal/PC-relative)
1. 基址寄存器 (Base register only)
计算方式:直接使用基址寄存器中的值作为地址。
示例指令:
LDR X0, [X1]
计算过程:
- 地址计算:有效地址 = X1中的值
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1保持不变
2. 基址加偏移 (Base plus offset)
2.1 立即数偏移
示例指令:
LDR X0, [X1, #16]
计算过程:
- 地址计算:有效地址 = X1中的值 + 16
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1保持不变
2.2 寄存器偏移
示例指令:
LDR X0, [X1, X2, LSL #3]
计算过程:
- 地址计算:有效地址 = X1中的值 + (X2中的值 << 3)
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1和偏移寄存器X2保持不变
2.3 扩展寄存器偏移
示例指令:
LDR X0, [X1, W2, SXTW #3]
计算过程:
- 地址计算:有效地址 = X1中的值 + (符号扩展(W2)中的值 << 3)
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1和偏移寄存器W2保持不变
3. 预索引 (Pre-indexed)
示例指令:
LDR X0, [X1, #16]!
计算过程:
- 地址计算:有效地址 = X1中的值 + 16
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1更新为新计算的地址(X1 = X1 + 16)
4. 后索引 (Post-indexed)
示例指令:
LDR X0, [X1], #16
计算过程:
- 地址计算:有效地址 = X1中的值
- 数据来源:从有效地址加载数据
- 写入寄存器:加载的数据写入X0
- 基址寄存器X1在指令完成后更新:X1 = X1 + 16
5. 字面相对寻址 (Literal/PC-relative)
示例指令:
LDR X0, label
计算过程:
- 地址计算:有效地址 = PC + (有符号偏移到标签)
- 数据来源:从计算出的地址加载数据
- 写入寄存器:加载的数据写入X0
- 程序计数器PC按正常执行流程更新
数学计算指令的地址计算
1. 立即数偏移的基址加偏移
示例指令:
ADD X4, X1, #16
计算过程:
- 计算:X4 = X1 + 16
- 基址寄存器X1保持不变
- 结果保存在X4中
2. 寄存器偏移的基址加偏移
示例指令:
ADD X4, X1, X2, LSL #2
计算过程:
- 计算:X4 = X1 + (X2 << 2)
- 基址寄存器X1和偏移寄存器X2保持不变
- 结果保存在X4中
3. 扩展寄存器偏移的基址加偏移
示例指令:
ADD X4, X1, W2, SXTW #3
计算过程:
- 计算:X4 = X1 + (符号扩展(W2) << 3)
- 基址寄存器X1和偏移寄存器W2保持不变
- 结果保存在X4中
4. 复杂地址计算示例
计算超过12位立即数偏移的地址:
ADD X4, X1, #0xFFF // 第一部分:X4 = X1 + 0xFFF
ADD X4, X4, #(0x1000>>12), LSL #12 // 第二部分:X4 = X4 + (0x1000>>12 << 12)
计算过程:
- 首先计算地址的低12位:X4 = X1 + 0xFFF
- 然后计算地址的高位部分并与之前的结果相加:X4 = X4 + 0x1000
- 最终结果:X4 = X1 + 0x1FFF
5. 使用SBFIZ和UBFIZ进行地址计算
示例指令:
SBFIZ X4, X2, #3, #32 // X4 = "W2m, SXTW #3"
计算过程:
- 从X2寄存器获取位域
- 从第0位开始插入到X4的第3位位置
- 插入32位宽度的位域
- 符号扩展结果
- 这等效于X4 = 符号扩展(W2) << 3
详细例子:字节数组访问
假设我们要访问存储在数组中的一个字节,数组基地址在X1,索引在W2:
// 方法1:使用寄存器偏移的基址加偏移
LDRB W0, [X1, W2, UXTW] // 无符号扩展W2到64位并用作偏移
// 方法2:使用预索引
ADD X3, X1, W2, UXTW // 计算数组元素地址
LDRB W0, [X3] // 加载字节到W0
// 方法3:对于大索引值
ADD X3, X1, W2, UXTW // 基址 + 索引
LDRB W0, [X3] // 加载字节
这些例子说明了 A64 指令集如何灵活地处理复杂的地址计算,特别是在数组和结构体访问中。每种寻址模式都有其特定用途,程序员和编译器可以根据具体情况选择最高效的方式。
SBFIZ 和 UBFIZ 指令
SBFIZ (Signed Bitfield Insert in Zero) 和 UBFIZ (Unsigned Bitfield Insert in Zero) 是ARM A64指令集中的位域处理指令,用于在寄存器中插入位域并进行扩展处理。
SBFIZ (Signed Bitfield Insert in Zero)
SBFIZ指令将源寄存器中的一个位域提取出来,左移指定位数,并以有符号方式扩展结果。
语法格式:
SBFIZ Xd, Xn, #lsb, #width
功能:
- 清空目标寄存器Xd
- 从源寄存器Xn中提取低width位
- 将这些位左移lsb位,插入到Xd中
- 对结果进行有符号扩展(保留位域的最高位符号)
计算过程示例:
SBFIZ X4, X2, #3, #5
这条指令会:
- 从X2中提取低5位
- 将这5位左移3位
- 将结果插入到X4中(其余位置0)
- 根据提取的5位中的最高位(符号位)扩展结果
UBFIZ (Unsigned Bitfield Insert in Zero)
UBFIZ指令类似于SBFIZ,但使用无符号方式(用0填充)扩展结果。
语法格式:
UBFIZ Xd, Xn, #lsb, #width
功能:
- 清空目标寄存器Xd
- 从源寄存器Xn中提取低width位
- 将这些位左移lsb位,插入到Xd中
- 对结果进行无符号扩展(高位全部填0)
计算过程示例:
UBFIZ X4, X2, #4, #8
这条指令会:
- 从 X2 中提取低 8 位
- 将这 8 位左移 4 位
- 将结果插入到 X4 中(其余位置0)
- 高位用 0 填充
在地址计算中的应用
如 ARM 文档中所示,这些指令在 A64 架构中常用于地址计算的优化,特别是当需要使用扩展寄存器偏移时:
SBFIZ X4, X2, #3, #32 // 等效于 X4 = (SignExtend(W2) << 3)
UBFIZ X4, X2, #4, #16 // 等效于 X4 = (ZeroExtend(X2[15:0]) << 4)
这些指令提供了一种高效的方式来计算地址偏移,尤其是在以下场景中:
- 数组索引计算,需要将索引缩放到元素大小
- 结构体成员访问,需要计算字段偏移
- 使用扩展寄存器进行地址偏移时,可以替代多个指令序列
通过单个指令完成位提取、移位和扩展操作,SBFIZ和UBFIZ指令能够减少指令数量,提高代码效率和执行速度。
总结
完结撒花!!!
参考
ARM官方手册:a_a-profile_architecture_reference_manual.pdf