llvm ir 的 br 指令不直接映射为机器指令,需先由 icmp 生成 i1 条件值,再经 selectiondag 转为 isd::brcond,最终通过 tablegen pattern 匹配生成 cmp+jeq 或 beq 等目标指令;其符号性(slt/ult)决定比较逻辑与跳转指令选择,且必须在 legalization 阶段确保 operand 类型、跳转距离合法。

LLVM IR 的 br 指令怎么映射到目标机器指令
LLVM IR 层的分支逻辑全靠 br 指令承载:无条件跳转是 br label %target,条件跳转是 br i1 %cond, label %true, label %false。后端不能直接“翻译”这个指令,必须拆解成两步:先处理条件判断(通常来自 icmp),再生成对应跳转。
关键点在于,br 本身不带比较语义,它只消费一个 i1 值——这个值往往来自前一条 icmp 指令的输出。所以后端在 SelectionDAG 阶段会看到 ISD::brcond 节点,其操作数包含一个比较结果 SDValue 和两个目标 BasicBlock。
- 如果你的目标机(如 Cpu032I)依赖 condition code(比如 SW 寄存器),就必须把
icmp映射为一条显式比较指令(如CMP),再让JEQ/JNE等 J 类型指令读取 SW - 如果目标机支持 B 类型指令(如
BEQ reg1, reg2, offset),就可以跳过 condition code,直接在brcondpattern 中匹配icmp + br组合,生成单条比较跳转指令 - TableGen 中的 pattern 必须严格对齐 operand 数量和类型,例如
(brcond (i1 (setcc ...)), $true, $false)才能触发 JEQ/JNE 选择;漏掉setcc就会 fallback 到非法指令报错
为什么 icmp 的符号性(slt vs ult)会影响跳转指令选择
LLVM IR 的 icmp 指令明确区分有符号(slt, sge)和无符号(ult, uge)比较,而目标机的跳转指令往往也分两类:比如 Cpu032I 的 JLT 对应有符号小于,JULT(若存在)才对应无符号小于。但很多 RISC 架构(包括原始 Cpu0)并不提供全套无符号跳转指令,这时就必须靠比较指令的结果位来区分。
典型陷阱是:IR 中写的是 %cmp = icmp ult i32 %a, %b,但后端只实现了 CMP + JLT pattern,结果生成了错误的有符号比较逻辑,导致高位为 1 的数(如 0xFFFFFFFF)被误判为“小”。
- 务必检查你实现的
icmplowering 是否根据ICmpInst::Predicate枚举值分路径:对ICMP_ULT应生成能反映无符号关系的底层比较(例如用SUBU+ 查看进位/借位位,而非SUB+ 符号位) - 在 TableGen 的
def定义里,用let Predicates = [IsUnsigned]等条件约束 pattern,避免有/无符号跳转混用 - 测试用例必须覆盖边界值:比如
icmp ult i8 255, 0在无符号下应为 true,有符号下为 false
SelectionDAG 中 ISD::brcond 合法化失败的常见原因
当 llc -march=yourtarget 报错 “Cannot select: tXX: i1 = brcond ...”,说明 SelectionDAG 的 legalizer 没找到匹配的指令 pattern,或者 operand 类型不合法。这不是语法错误,而是后端描述缺失。
根本原因通常是这三类之一:
-
brcond的 condition 操作数类型不是目标机支持的整型宽度(例如 IR 用i1,但你的CMP指令只接受i32输入,需插入 zero-extend 或 trunc) - 目标 BasicBlock 的地址无法在当前跳转指令的位移范围内(J 类型通常只有 26-bit 目标域),legalizer 不会自动拆成
MOVHI+JMP,得自己写 Custom Lowering - 你忘了在
YourTargetInstrInfo.td里为跳转指令声明isTerminator = 1和isBranch = 1,导致指令调度器不识别其控制流语义
从 br 到汇编:中间经过哪些不可见的 pass
你写的 br i1 %c, label %t, label %f 到最终生成 jeq $sw, $t 或 beq $r1, $r2, Lt,背后至少经过四层转换,每层都可能出问题:
- SelectionDAGBuilder 把
br转成ISD::brcond节点,并把%c的定义(比如ISD::SETCC)作为 operand 连接过去 - Instruction Selection 阶段用 TableGen 自动生成的 matcher 匹配该
brcond+SETCC组合,生成JEQ或BEQ的SDNode - Legalization 阶段检查 operand 宽度、跳转距离等,若不合法,会调用你写的
LowerBRCOND函数做自定义修复 - 最后 MachineInstr 构建时,
JEQ的 operand 会被填入具体寄存器编号或 BasicBlock 符号,由 AsmPrinter 输出为汇编标签
最容易被忽略的是 Legalization 后的 fallback 行为:它不会报错,而是静默降级为软件实现(比如用 mov + beqz 模拟),但性能极差且难以调试——必须开 llc -debug-only=isel 看日志确认是否走到了你期望的 pattern。











