跳转至

地址与数据对齐

“对齐”在 c220 上不是一个统一数字。编写汇编时至少要区分指令编码、Scalar 数据访问、Vector 操作数、MTE 拆包,以及 cache line/Bank 仲裁五个层次。只有前几类可能决定指令是否有效;line 和 Bank 粒度通常只决定请求如何归并、占用端口或发生冲突。

本页使用以下记号:

\[ \operatorname{align\_down}(a,G)=\left\lfloor\frac{a}{G}\right\rfloor G \]
\[ \operatorname{align\_up}(a,G)=\left\lceil\frac{a}{G}\right\rceil G \]

其中 \(a\) 是字节地址,\(G\) 是字节粒度。\(G\) 为 2 的幂时,可分别写成 a & ~(G - 1)(a + G - 1) & ~(G - 1)

先判断是哪一种对齐

层次 典型规则 不满足时的含义
指令与标签 指令 4 B;分支标签 4 B;ADRP 页 4096 B 汇编/重定位约束或错误的 PC
Scalar 数据 最终有效地址按访问宽度 ½/4/8 B 执行期输出非对齐诊断;普通请求仍继续形成
Vector 数据 普通源和目标 32 B,少数归约目标使用动态阈值 记录地址非对齐指令异常
MTE/AIPP 拆包 常见 32 B;部分路径按 64 B 或接口粒度拆分 扩大覆盖范围、补齐或产生多个 uop,不等同于通用许可
cache line/Bank D-cache 64 B、UB Bank 32 B、L1 Bank 32 B line 归并、Bank 占用和冲突;通常不是独立的地址合法性判断

bisheng 能检查什么

bisheng 能固定立即数字段、标签和重定位的编码约束,却通常不知道 X 寄存器在设备执行时的值。下列程序能够汇编:

LD.b64 X0, [X1], #1

这里的 #1 是 1 字节偏移。汇编成功只说明 12 位有符号立即数可以表示 1;最终地址 X1 + 1 是否按 8 字节对齐,要到执行期才能判断。相同原则适用于 Xs 描述字中的 stride、repeat,以及 MTE、Vector 和 Cube 的地址寄存器。

因此,立即数表中的 alignment=1 表示字段以 1 字节为单位,不表示 .b64 访问允许任意字节地址。分支的 alignment=4ADRPalignment=4096 才是标签/页的编码约束。

Scalar LSU

有效地址

\(W\) 为访问宽度:.b8=1.b16=2.b32=4.b64=8 字节。普通地址模式下,各形式使用:

\[ EA_{reg}=X_n+X_m\times W \]
\[ EA_{imm}=X_n+\operatorname{sext}(imm_{12}) \]

LDP/STP 的 6 位有符号偏移同样按字节加入基址;成对的第二个元素位于第一个元素之后。原子 store 的寄存器索引和立即数形式分别沿用上述两种计算。

运行时检查

普通 Scalar 访存检查:

\[ EA\bmod W=0 \]

不满足时会输出地址非对齐诊断。该路径没有把诊断升级为独立的指令异常,之后仍会构造访存请求;因此程序不能依靠这种行为取得可移植的“非对齐加载/存储”语义。

DC_PRELOAD 的寄存器和立即数形式不执行上述诊断。预取路径把请求粒度设为 64 B,但这表示 cache-line 预取范围,并不要求预取地址本身是 64 B 整数倍。

推荐始终让 Scalar 数据满足自然对齐,且让成对访问的首地址按元素宽度对齐:

b8  : EA % 1 == 0
b16 : EA % 2 == 0
b32 : EA % 4 == 0
b64 : EA % 8 == 0

Vector 地址检查

普通规则

Vector 指令在发射前按源地址 0、源地址 1、目标地址三个槽检查。普通阈值为:

地址槽 普通阈值
source 0 32 B
source 1 32 B
destination 32 B

每个启用的地址槽都必须满足 address % threshold == 0。任一槽失败都会记录地址非对齐指令异常;这比 Scalar 的诊断路径更严格。

归约目标的动态阈值

源地址仍使用 32 B。部分归约指令只改变 destination 的阈值。令 \(E\) 为后缀对应的元素字节数:

指令 模式 destination 对齐
VCADD .f16 2 B
VCADD 其他合法 dtype 4 B
VCMAX / VCMIN reduce_mode=0/1 \(2E\) B
VCMAX / VCMIN reduce_mode=2 \(E\) B
VCMAX / VCMIN reduce_mode=3 4 B
VCGMAX / VCGMIN / VCGADD .f16 16 B
VCGMAX / VCGMIN / VCGADD 其他合法 dtype 32 B
VCPADD 所有已知形式 32 B

reduce_mode 就是 VCADD/VCMAX/VCMIN 文本形式末尾的立即数。它既改变归约布局,也可能改变结果地址要求,不能只按立即数位宽判断合法性。

专用地址槽

有些指令不使用普通的“两个源、一个目标”组合:

指令 执行的通用模数检查
VGATHERVGATHERBMOVEVVMOVMASK_XD 仅 destination,使用 destination 动态阈值
VMOVMASK_XN 仅 source 0,32 B
VREDUCEVREDUCEV2 source 0 和 destination;仅当内部归约模式非 0 时再检查 source 1
MOVEVAMOVEMASKLD_VADVNCHWCONV 不进入普通地址模数检查

“不进入普通检查”不表示任意地址都安全。这些指令使用 VA、MASK、VAD 或专用重排状态,合法性还取决于相应描述字和逐 lane 地址。

MTE 的对齐、补齐与拆包

MTE 没有一个适用于所有搬运指令的固定 address % N 判定。许多路径会把一个逻辑请求转换成覆盖对齐边界的多个物理片段。

32 字节覆盖区间

普通对齐缓存先计算:

\[ base_{32}=\operatorname{align\_down}(EA,32) \]
\[ covered=bytes+(EA-base_{32}) \]

随后按接口最大包长或指令选择的顺序粒度拆包。末包可能按 32 B 向上补齐;特定输入路径直接按 64 B 分包。由此得到两条开发规则:

  1. 非 32 B 对齐的逻辑起点可能让底层请求覆盖逻辑区间之前的字节。
  2. bytes 没跨边界不代表底层包也不跨边界;边界检查必须使用扩展后的覆盖区间。

MOV_SRC_TO_DST_ALIGN

MOV_SRC_TO_DST_ALIGN 使用专用对齐搬运路径。它按 32 B 计算头部填充:

\[ head\_pad=(-offset)\bmod 32 \]

元素宽度为 1、2 或 4 B 时,填充数据分别按相同宽度复制。其行推进在源、目标两侧使用一组“原始跨度”和“32 B 向上对齐跨度”,搬运方向决定哪一侧采用:

\[ span_{32}(n)=\operatorname{align\_up}(n,32) \]

因此这条指令能够处理指定格式的头尾不齐,但不能推广成“所有 MTE 指令都支持任意非对齐地址”。普通 MOV_<SRC>_TO_<DST> 仍要按它自己的 Xs、burst、stride 和地址空间规则建立描述字。

AIPP 读取

AIPP 读取以路径给出的读粒度 \(G\) 扩大请求:

\[ read\_begin=\operatorname{align\_down}(first,G) \]
\[ read\_bytes=\operatorname{align\_up}(last+1-read\_begin,G) \]

再以 \(G\) 为步长生成读取 uop。该实现按 \(\lfloor\log_2G\rfloor\) 做移位取整,所以 \(G\) 应为 2 的幂。逻辑图像区域即使从非对齐位置开始,映射也必须覆盖扩大后的首尾请求。

cache line 与 Bank 粒度

下面的数字用于请求归并和冲突分析,不能代替前面的 operand 对齐规则。

资源 当前执行模型粒度 地址映射
D-cache line 64 B line_base = floor(EA / 64) * 64
UB Bank 32 B bank = ((EA >> 12) & 0x30) | ((EA >> 5) & 0x0f)
L1 Bank 32 B,8 Banks start = floor(EA / 32) % 8
L1 buffer line 16 B 接口分段粒度,不是通用 operand 对齐
L0A/L0B 请求 line 512 B Cube/MTE 请求分段和占用粒度

UB 公式等价于 bank = concat(EA[17:16], EA[8:5])。因此地址相差 32 B 通常切换低 4 位 Bank,地址相差 64 KiB 切换高 2 位;自然对齐并不能保证同拍多路访问无 Bank 冲突。

L1 的请求 Bank mask 使用:

\[ start=\left\lfloor\frac{EA}{B}\right\rfloor\bmod N \]
\[ span=\left\lceil\frac{bytes}{B}\right\rceil \]

其中当前 \(B=32\)\(N=8\)span 不额外加入 EA % B 的头部偏移,说明到达该层的请求应已由上游拆分,或以 Bank 宽度对齐。手写直接面向 L1 的描述字时,保守做法是让每段首地址按 32 B 对齐。

地址窗口与扩大后的边界

Vector 读写还会检查配置描述字定义的 32 B 粒度异常窗口。对一个 64 位窗口字 \(D\)

\[ lower=32\times D[31:16] \]
\[ upper=(D\mathbin{>>}27)\mathbin{\&}\mathtt{0x1fffe0}=32\times D[47:32] \]

读窗口由 \(D[29]\) 启用,写窗口由 \(D[28]\) 启用;地址落入闭区间 [lower, upper] 时记录数据访问异常。它与 address % alignment 是两次独立判断。

对 MTE/AIPP,还应使用 align_down 后的首地址和 align_up 后的末地址检查映射范围。仅验证逻辑 payload 的首尾,可能漏掉为拆包或补齐而访问的前导/尾随字节。

Cube 地址

Cube 指令没有套用 Vector 那种统一的 operand 模数检查。地址先由 L1/L0 布局、Cube shape 和描述字展开,再由 L0 请求 line、端口和 Bank 处理。没有通用检查不等于任意地址有效;手写 MMAD/加载描述字时应按 tile 布局构造 L0A/L0B/L0C 地址,并把 512 B line 视为请求和冲突粒度,而不是宣称所有 Cube operand 都有一个固定 512 B 硬对齐条件。

推荐检查清单

  1. 先按立即数表检查字段范围和编码缩放。
  2. 计算完整 \(EA\),不要只检查基址或立即数。
  3. Scalar 按后缀宽度自然对齐;Vector 普通地址按 32 B,对归约目标应用动态表。
  4. MTE 使用 Xs 展开每个 burst/stride,并把首尾扩展到实际分包粒度。
  5. 将扩展区间与 GM/UB/L1/L0 的映射边界比较。
  6. 合法之后再用 UB/L1 Bank 公式分析冲突;不要把“无异常”和“无冲突”当成同一件事。

机器可读版本见地址与数据对齐规则 CSV