A64寻址方式详解

前言

记录一下 A64 指令集的寻址方式。

Address Calculation Methods for Load/Store and Math Instructions in A64

Load/Store指令寻址方式

A64指令集支持以下几种主要的地址计算方式:

  1. 基址寄存器 (Base register only)
  2. 基址加偏移 (Base plus offset)
  3. 预索引 (Pre-indexed)
  4. 后索引 (Post-indexed)
  5. 字面相对寻址 (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

这条指令会:

  1. 从X2中提取低5位
  2. 将这5位左移3位
  3. 将结果插入到X4中(其余位置0)
  4. 根据提取的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

这条指令会:

  1. 从 X2 中提取低 8 位
  2. 将这 8 位左移 4 位
  3. 将结果插入到 X4 中(其余位置0)
  4. 高位用 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值