1. Cpu0Ret 节点定义
tablegen
复制代码
def Cpu0Ret : SDNode<"Cpu0ISD::Ret", SDTNone,
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
使用空签名 SDTNone(无输入输出)
属性:有链式依赖、可选粘合值、可变参数
用于表示函数返回操作
2. 操作数和寻址模式
tablegen
复制代码
def simm16 : Operand<i32> {
let DecoderMethod = "DecodeSimm16";
}
tablegen
复制代码
def mem : Operand<iPTR> {
let PrintMethod = "printMemOperand";
let MIOperandInfo = (ops CPURegs, simm16);
let EncoderMethod = "getMemEncoding";
}
tablegen
复制代码
def addr : ComplexPattern<iPTR, 2, "SelectAddr", [frameindex], [SDNPWantParent]>;
tablegen
复制代码
def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>;
3. 内存对齐检查片段
tablegen
复制代码
class AlignedLoad<PatFrag Node> : PatFrag<(ops node:$ptr), (Node node:$ptr), [{
LoadSDNode *LD = cast<LoadSDNode>(N);
return LD->getMemoryVT().getSizeInBits()/8 <= LD->getAlignment();
}]>;
包装load/store操作,检查对齐要求
确保访问大小 ≤ 内存对齐度
4. 指令格式类
算术/逻辑立即数指令
tablegen
复制代码
class ArighLogicI<bits<8> op, string instrAsm, SDNode opNode,
Operand od, PatLeaf immType, RegisterClass regClass>
: FL<op, (outs GPROut:$ra), (ins regClass:$rb, od:$imm16),
!strconcat(instrAsm, "\t$ra, $rb, $imm16"),
[(set GPROut:$ra, (opNode regClass:$rb, immType:$imm16))], IIAlu>;
内存访问基类
tablegen
复制代码
class LoadM<bits<8> op, string instrAsm, PatFrag opNode, RegisterClass regClass,
Operand od, bit pseudo>
: FMem<op, (outs regClass:$ra), (ins od:$addr),
!strconcat(instrAsm, "\t$ra, $addr"),
[(set regClass:$ra, (opNode addr:$addr))], IILoad>;
tablegen
复制代码
class StoreM<bits<8> op, string instrAsm, PatFrag opNode, RegisterClass regClass,
Operand od, bit pseudo>
: FMem<op, (outs), (ins regClass:$ra, od:$addr),
!strconcat(instrAsm, "\t$ra, $addr"),
[(opNode regClass:$ra, addr:$addr)], IIStore>;
5. 跳转指令类
tablegen
复制代码
let isBranch=1, isTerminator=1, isBarrier=1, hasDelaySlot=1,
isIndirectBranch=1 in
class JumpFR<bits<8> op, string instrAsm, RegisterClass regClass>
: FL<op, (outs), (ins regClass:$ra),
!strconcat(instrAsm, "\t$ra"), [(brind regClass:$ra)], IIBranch>;
间接跳转:jr $ra
属性:分支、终止符、屏障、延迟槽
6. 返回指令
tablegen
复制代码
class RetBase<RegisterClass regClass> : JumpFR<0x3c, "ret", regClass> {
let isReturn = 1;
let isCodeGenOnly = 1;
let hasCtrlDep = 1;
let hasExtraSrcRegAllocReq = 1;
}
继承自 JumpFR,操作码 0x3c
特殊属性标记为返回指令
7. 具体指令实例化
tablegen
复制代码
defm LD : LoadM32<0x01, "ld", load_a>; // 加载指令
defm ST : StoreM32<0x02, "st", store_a>; // 存储指令
def ADDiu : ArighLogicI<0x09, "addiu", add, simm16, immSExt16, CPURegs>; // 加法
def JR : JumpFR<0x3c, "jr", GPROut>; // 间接跳转
def RET : RetBase<GPROut>; // 返回指令
def NOP : FJ<0, (outs), (ins), "nop", [], IIAlu>; // 空操作
8. 模式匹配规则
tablegen
复制代码
def : Pat<(i32 immSExt16:$in), (ADDiu ZERO, imm:$in)>
工作流程示例
对于 C 代码:
c
复制代码
int a = 42;
return a;
LLVM IR:
llvm
复制代码
%a = alloca i32
store i32 42, i32* %a
%1 = load i32, i32* %a
ret i32 %1
SelectionDAG 会匹配:
这些 TableGen 定义最终会生成 Cpu0 的汇编代码。
//===-- Cpu0InstrInfo.td - Cpu0 Instruction defs -----------*- tablegen -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This fiel contains the Cpu0 implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Cpu0 profiles and nodes
//===----------------------------------------------------------------------===//
def SDT_Cpu0Ret : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
// Return
def Cpu0Ret : SDNode<"Cpu0ISD::Ret", SDTNone,
SDNPHasChain, SDNPOptInGlue, SDNPVariadic\]\>;
//===----------------------------------------------------------------------===//
// Instruction format superclass
//===----------------------------------------------------------------------===//
include "Cpu0InstrFormats.td"
//===----------------------------------------------------------------------===//
// Cpu0 Operand, Complex Patterns and Transformations Definitions
//===----------------------------------------------------------------------===//
// Instruction operand types
// Signed Operand
def simm16 : Operand\ {
let DecoderMethod = "DecodeSimm16";
}
// Address operand
def mem : Operand\ {
let PrintMethod = "printMemOperand";
let MIOperandInfo = (ops CPURegs, simm16);
let EncoderMethod = "getMemEncoding";
}
// Node immediate fits as 16-bit sign extended on target immediate.
// e.g. addi, andi
def immSExt16 : PatLeaf\<(imm), \[{ return isInt\<16\>(N-\>getSExtValue()); }\]\>;
// Cpu0 Address Mode! SDNode frameindex could posibily be a match since
// load and store instructions from stack used it.
def addr : ComplexPattern\;
//===----------------------------------------------------------------------===//
// Pattern fragment for load/store
//===----------------------------------------------------------------------===//
class AlignedLoad\
: PatFrag\<(ops node:$ptr), (Node node:$ptr),
\[{
LoadSDNode \*LD = cast\(N);
return LD-\>getMemoryVT().getSizeInBits()/8 \<= LD-\>getAlignment();
}\]\>;
class AlignedStore\
: PatFrag\<(ops node:$val, node:$ptr), (Node node:$val, node:$ptr),
\[{
StoreSDNode \*ST = cast\(N);
return ST-\>getMemoryVT().getSizeInBits()/8 \<= ST-\>getAlignment();
}\]\>;
// Load/Store PatFrags
def load_a : AlignedLoad\;
def store_a : AlignedStore\;
//===----------------------------------------------------------------------===//
// Instructions specific format
//===----------------------------------------------------------------------===//
// Arithmetic and logical instructions with 2 register operands
class ArighLogicI\ op, string instrAsm, SDNode opNode,
Operand od, PatLeaf immType, RegisterClass regClass\>
: FL\ {
let isReMaterializable = 1;
}
class FMem\ op, dag outs, dag ins, string asmStr, list\ pattern,
InstrItinClass itin\>
: FL\ {
bits\<20\> addr;
let Inst{19-16} = addr{19-16};
let Inst{15-0} = addr{15-0};
let DecoderMethod = "DecodeMem";
}
// Memory Load/Store
let canFoldAsLoad = 1 in
class LoadM\ op, string instrAsm, PatFrag opNode, RegisterClass regClass,
Operand od, bit pseudo\>
: FMem\ {
let isPseudo = pseudo;
}
class StoreM\ op, string instrAsm, PatFrag opNode, RegisterClass regClass,
Operand od, bit pseudo\>
: FMem\ {
let isPseudo = pseudo;
}
// 32-bit load
multiclass LoadM32\ op, string instrAsm,
PatFrag opNode, bit pseudo=0\> {
def #NAME# : LoadM\;
}
// 32-bit store
multiclass StoreM32\ op, string instrAsm,
PatFrag opNode, bit pseudo=0\> {
def #NAME# : StoreM\;
}
// JumpFR
let isBranch=1, isTerminator=1, isBarrier=1, hasDelaySlot=1,
isIndirectBranch=1 in
class JumpFR\ op, string instrAsm, RegisterClass regClass\>
: FL\ {
let rb = 0;
let imm16 = 0;
}
// Return
class RetBase\ : JumpFR\<0x3c, "ret", regClass\> {
let isReturn = 1;
let isCodeGenOnly = 1;
let hasCtrlDep = 1;
let hasExtraSrcRegAllocReq = 1;
}
//===----------------------------------------------------------------------===//
// Instruction definition
//===----------------------------------------------------------------------===//
// Load and Store Instructions
// already aligned
defm LD : LoadM32\<0x01, "ld", load_a\>;
defm ST : StoreM32\<0x02, "st", store_a\>;
// Arithmetic Instructions (ALU Immediate)
def ADDiu : ArighLogicI\<0x09, "addiu", add, simm16, immSExt16, CPURegs\>;
// Arithmetic Instructions (3-Operand, R-Type)
// Shift Instructions
// Jump Instructions
def JR : JumpFR\<0x3c, "jr", GPROut\>;
def RET : RetBase\;
// No Operation Instructions
let addr=0 in
def NOP : FJ\<0, (outs), (ins), "nop", \[\], IIAlu\>;
//===----------------------------------------------------------------------===//
// Arbitrary patterns that map to one or more instructions
//===----------------------------------------------------------------------===//
// Small Immediates
def : Pat\<(i32 immSExt16:$in), (ADDiu ZERO, imm:$in)\>;
### 语法拆解
tablegen
```
def : Pat<(i32 immSExt16:$in), (ADDiu ZERO, imm:$in)>;
```
#### 1. **`i32`** - 类型约束
* 表示这个模式匹配**返回 32 位整数类型**的节点
* 在 SelectionDAG 中,这表示匹配一个产生 `i32` 值的节点
#### 2. **`immSExt16`** - 模式匹配器
* 这是一个之前定义的 `PatLeaf`(模式叶子节点)
* 定义如下:
tablegen
```
def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>;
```
* **作用** :匹配那些**在 16 位有符号整数范围内**(-32768 到 32767)的立即数
#### 3. **`:$in`** - 变量绑定
* `$in` 是将匹配到的 DAG 节点**绑定到变量名** `in`
* 这样可以在右侧的指令构建中引用这个值
* `:` 是分隔符,前面是类型/模式,后面是变量名
### 完整含义
**左侧模式** `(i32 immSExt16:$in)` 匹配:
* 一个产生 `i32` 类型值的节点
* 该节点是一个立即数(`imm`)
* 且该立即数的值在 `-32768 ~ 32767` 范围内
* 将匹配到的节点命名为 `$in`
**右侧替换** `(ADDiu ZERO, imm:$in)`:
* 生成一条 `ADDiu` 指令
* 使用 `ZERO` 寄存器($zero)作为第一个源操作数
* `imm:$in` 将左侧匹配到的立即数作为第二个源操作数
### 实际效果示例
假设有这样的 LLVM IR:
llvm
```
%1 = add i32 0, 42 ; 常量 42
```
SelectionDAG 中的节点:
text
```
t1: i32 = Constant<42>
```
模式匹配过程:
1. `immSExt16` 检查 42 是否在 `-32768~32767` → ✅ 是
2. 匹配成功,变量 `$in` 绑定到 Constant\<42\>
3. 生成指令:`ADDiu $zero, 42`
### 为什么要这样的模式?
Cpu0 架构**没有专门的加载立即数指令** (如 ARM 的 `MOV`),而是通过:
asm
```
addiu $dest, $zero, imm
```
这条指令将 `$zero + imm` 的结果存入目标寄存器,相当于加载立即数。
### 变体示例
如果没有这个模式,LLVM 可能需要生成:
asm
```
# 假设有 lui/ori 组合(如 MIPS)
lui $dest, hi(imm)
ori $dest, $dest, lo(imm)
```
通过 `Pat` 规则,将小立即数优化为单条 `addiu` 指令,提高代码效率。
### 类似规则
tablegen
```
// 匹配 16 位无符号立即数 → ADDIU
def : Pat<(i32 immZExt16:$in), (ADDiu ZERO, imm:$in)>;
// 匹配 32 位大立即数 → LUI + ORI 组合
def : Pat<(i32 imm32:$in), (ORi (LUi hi16:$in), lo16:$in)>;
```
这个模式是典型的 **RISC 架构立即数加载优化**技巧。
imm:$in 表示什么意思
`imm:$in` 是 TableGen 中**指令构建部分的参数传递语法**,让我详细解释:
### 语法解析
tablegen
```
(ADDiu ZERO, imm:$in)
```
#### 1. **`imm`** - 操作数类型
* 表示这是一个**立即数操作数**
* 对应指令定义中的 `simm16` 类型(16位有符号立即数)
* 告诉指令编码器:这个值要作为立即数字段编码到指令中
#### 2. **`$in`** - 变量引用
* 引用左侧模式中绑定的变量 `$in`
* 将左侧匹配到的 DAG 节点的值,传递给右侧指令的立即数操作数
### 完整的数据流
tablegen
```
def : Pat<(i32 immSExt16:$in), (ADDiu ZERO, imm:$in)>;
```
**数据流动过程**:
text
```
左侧 DAG 节点 右侧指令构建
┌─────────────┐ ┌──────────────────┐
│ Constant<42>│ →→→ │ ADDiu ZERO, imm │
│ ($in) │ │ ↑ │
└─────────────┘ │ └── 使用 $in 的值 │
└──────────────────┘
```
### 具体例子
假设匹配到立即数 `1234`:
1. **左侧匹配** :`immSExt16:$in` 匹配到 `Constant<1234>`,`$in` 绑定到这个节点
2. **右侧构建** :`imm:$in` 取出 `$in` 的值 `1234`,作为立即数传给 `ADDiu`
生成的指令:
asm
```
addiu $zero, 1234
```
### 为什么需要 `imm:`?
指令定义中 `ADDiu` 的格式:
tablegen
```
def ADDiu : ArighLogicI<0x09, "addiu", add, simm16, immSExt16, CPURegs>;
```
其中 `simm16` 是立即数操作数类型:
tablegen
```
def simm16 : Operand {
let DecoderMethod = "DecodeSimm16";
}
```
`imm:$in` 中的 `imm` 告诉 TableGen:
* 这个参数对应指令的 `simm16` 操作数位置
* 需要进行立即数编码(16位有符号)
* 可能需要符号扩展等处理
### 对比其他类型的操作数
tablegen
```
// 寄存器操作数
(ADDiu GPROut:$ra, CPURegs:$rb, imm:$imm16)
// ↑ ↑
// 寄存器参数 寄存器参数
// 内存操作数
(ST CPURegs:$ra, mem:$addr)
// ↑
// 内存操作数(包含寄存器和偏移)
// 立即数操作数
(ADDiu ZERO, imm:$in)
// ↑
// 立即数操作数
```
### 常见变体
tablegen
```
// 直接使用常量(不绑定变量)
def : Pat<(i32 0), (ADDiu ZERO, 0)>;
// 绑定后多次使用
def : Pat<(add (i32 immSExt16:$a), (i32 immSExt16:$b)),
(ADDiu (ADDiu ZERO, imm:$a), imm:$b)>;
// 类型转换
def : Pat<(i32 imm:$in), (MOVi32 imm:$in)>; // 直接传送立即数
```
### 关键要点
* `imm:$in` 中的 `imm` 是**操作数类型说明符**
* `$in` 是从左侧**传递过来的值**
* 这种机制实现了 DAG 节点到指令操作数的**映射**
没有 `imm:` 前缀,TableGen 就无法知道这个 `$in` 应该作为立即数、寄存器还是内存操作数来编码。