globalisel 与 selectiondag 完全互斥,不可混用;二者数据结构隔离、控制流独立,须在 machinefunction 粒度上整体切换,迁移需重写 irtranslator、legalizer、regbankselect 和 instructionselector 等关键组件。

GlobalISel 不能“逐步接入”SelectionDAG——它是独立于 SelectionDAG 的另一套指令选择机制,二者不兼容、不共存于同一函数的代码生成流程中。你看到的“从 SelectionDAG 迁移到 GlobalISel”,本质是后端在 MachineFunction 粒度上做开关切换:要么走 SelectionDAGISel Pass,要么走 GlobalISel Pass(即 IRTranslator → Legalizer → RegBankSelect → InstructionSelect)。不存在“在 SelectionDAG 流程里插入 GlobalISel 节点”这种操作。
为什么不能混用 SelectionDAG 和 GlobalISel
根本原因在于数据结构和控制流完全隔离:
-
SelectionDAG是基于SDNode的有向无环图,整个流程围绕CurDAG实例展开,依赖SelectionDAGBuilder构建、Legalize合法化、DoInstructionSelection匹配 TableGen 模式; -
GlobalISel完全抛弃 DAG,直接在MachineIR(MachineInstr+MachineBasicBlock)上逐条处理,每个 Pass 都操作MachineFunction的原生 MIR 表示; - LLVM 后端入口函数
SelectionDAGISel::runOnMachineFunction和GlobalISel::runOnMachineFunction是互斥的——TargetMachine通过getTargetLowering()->useGlobalISel()决定启用哪一套,且该判断在MachineFunction初始化时就已固化。
实际迁移必须改哪些地方
不是“加功能”,而是重写关键基础设施。以 RISC-V 或 ARM 后端为例,需确认并实现以下组件:
-
TargetInstrInfo::getIntrinsicForOpcode():让IRTranslator知道如何把llvm.riscv.*intrinsic 映射为合法的MachineInstr序列(而非靠 SelectionDAG 的ISD::INTRINSIC_*节点); -
LegalizerInfo:明确定义每条自定义指令(如RISCV::CLZ)支持的类型组合(e.g.{s32, s64}),否则Legalizer会直接拒绝该指令; -
RegisterBankInfo:声明该指令使用的寄存器类别(如GPRBanks),否则RegBankSelect无法分配物理寄存器; -
InstructionSelector:用 TableGen 写select规则(defm CLZ : GINodeEquiv<...></...>),或手写 C++constrainSelectedInstRegClass()和selectImpl(),匹配GI_CLZ这类全局指令码; -
TargetLowering::useGlobalISel()必须返回true,且确保TargetPassConfig::createTargetSelector()注册了你的InstructionSelector实例。
常见踩坑点:你以为在用 GlobalISel,其实没生效
调试时最容易误判的地方:
- 只改了
useGlobalISel()返回true,但没实现InstructionSelector类——LLVM 会静默 fallback 到 SelectionDAG,llc -debug-only=globalisel不报错也不输出任何日志; - 自定义指令的
isPreISelOpcode()返回false,导致IRTranslator根本不生成对应MachineInstr,后续所有 GlobalISel Pass 都看不到它; -
LegalizerInfo中漏掉某一种 type pair(比如只写了s32支持,但 IR 传进来的是s16),Legalizer会尝试扩宽或截断,若未定义扩展规则,则直接 abort; - 用
llc -O0测试——GlobalISel 默认只在-O1及以上启用(可通过-global-isel强制开启,但需确保后端支持)。
真正卡住人的从来不是“怎么写”,而是“哪个环节没注册成功”或“哪条约束没覆盖到”。建议从一个最简 intrinsic(如无参数、无副作用的 __builtin_riscv_ror)开始,用 llc -debug-only=irtranslator,legalizer,instrsel 逐级观察 MIR 是否生成、是否合法、是否被选中,比通读 TableGen 语法高效得多。











