寄存器和寄存器类必须在 .td 文件中用 tablegen 声明;寄存器定义物理寄存器的命名、别名、子寄存器及硬件编码,registerclass 则定义可分配寄存器池并影响指令约束匹配与选择。

寄存器和寄存器类必须在 .td 文件里用 TableGen 声明,不能手写 C++ 实现;寄存器类(RegisterClass)决定哪些寄存器能被分配给某条指令,而寄存器(Register)本身只是命名+别名+子寄存器关系的静态描述。
寄存器定义:用 Register 类或其子类声明物理寄存器
每个物理寄存器都要对应一个 def 记录,继承自 Register 或目标专用子类(如 X86Reg、AArch64Reg)。关键字段包括:
-
AsmName是汇编器看到的名字(如"x0"),必须与目标 ABI 一致 -
Aliases列出重叠寄存器(如x0和w0必须互为Aliases) -
SubRegs指定直接子寄存器(如x0的SubRegs = [w0, b0, h0, s0, d0]),不能漏掉中间层级 -
HWEncoding是该寄存器在机器码中的编码值,必须唯一且符合硬件规范
常见错误:漏写 Aliases 导致寄存器分配器认为 x0 和 w0 可同时活跃,生成非法代码;把 SubRegs 写成嵌套结构(如 [w0, [b0]])会触发 TableGen 报错 "SubRegs must be a flat list of Register objects"。
RegisterClass 定义:用 add/sequence 构建可分配寄存器池
RegisterClass 不是类型定义,而是寄存器分配器的“可用名单”。它必须包含至少一个寄存器,且所有成员需满足同一约束(位宽、用途、是否可压栈等):
- 寄存器列表用
add拼接,支持sequence "r%u", 0, 15生成r0–r15 -
Size字段必须与寄存器实际宽度一致(如 AArch64 的GPR类设let Size = 64,哪怕它也含w0) -
isAllocatable = 0可禁用某寄存器(如PC、SP在某些模式下不可分配) - 若指令需要特定寄存器对(如 x86 的
AX:DX),需额外定义CompositeRegisterClass并在指令中显式引用
典型陷阱:let Size = 32 却把 x0 加入该类,TableGen 不报错但寄存器分配器会在 32 位模式下误选 x0,导致指令编码失败;用 trunc GPR, 8 截断时未确认底层寄存器顺序,导致前 8 个不是预期寄存器。
寄存器类如何影响指令选择和约束处理
指令的操作数约束(Operand 的 Constraint 字段)和 SelectionDAG 匹配都依赖 RegisterClass 名称。例如:
- 内联汇编
"=r"约束会查默认整数寄存器类(如GPR) - 指令定义中
(outs GPR:$dst)表示输出必须来自GPR类,否则匹配失败 - 若某指令仅支持低 8 个通用寄存器,应单独定义
GPR_LO8类并用于该指令,而非在Pattern中硬编码r0–r7
容易忽略的点:寄存器类名称必须全局唯一且大小写敏感;RegisterClass 的 Namespace(如 "ARM")会影响其在 C++ 代码中的生成类名(ARM::GPRRegisterClass),若与目标子系统不匹配,会导致 XXXRegisterInfo::getPointerRegClass() 返回空指针。











