irbuilder 是唯一安全插入新指令的方式,必须指定合法插入点,新指令需显式被使用或替换旧值,类型必须严格匹配,否则破坏ssa导致验证失败或pass崩溃。

直接在 IR 中插入一条新指令,不能靠“新建 + 替换指针”这种粗暴方式——IRBuilder 是唯一安全、可控、符合 LLVM SSA 约束的入口。
用 IRBuilder 在指定位置插入指令
LLVM 不允许你手动构造 Instruction 对象后硬塞进基本块;所有新指令必须经由 IRBuilder 创建,并明确指定插入点。否则会破坏 use-def 链,触发 verify 失败或后续 Pass 崩溃。
-
IRBuilder的构造必须传入合法的插入点:可以是某条已有指令(如Inst),也可以是基本块的getFirstInsertionPt()或getTerminator() - 插入位置决定新指令的执行顺序和数据依赖:插在
load之前,就不能用它的结果;插在store之后,可能被 DCE 掉 - 不要复用同一个
IRBuilder实例跨基本块插入;每次换块都要重建,否则插入点失效
示例:在 Inst 前插入一个 add
IRBuilder Builder(Inst); Value *NewAdd = Builder.CreateAdd(Builder.getInt32(1), Builder.getInt32(2), "my.add");
插入后如何让其他指令用上这个新值
新指令只是“存在”,不代表它自动被消费。如果你希望某条指令(比如 ICmp)的某个操作数换成这个新值,必须显式调用 replaceUsesOfWith 或 replaceAllUsesWith ——但注意:这仅适用于你**替换的是一个 Value*(如 Instruction* 或 Constant*)的全部使用点**。
- 若新指令返回值是
Value*(如CreateAdd),且你想把它作为某条指令的操作数,直接传进去即可:Builder.CreateICmpEQ(NewAdd, ...) - 若你已有一条旧指令
OldInst,想让它所有使用点都改用NewAdd,才调OldInst->replaceAllUsesWith(NewAdd) - 千万别对刚创建的新指令调
replaceAllUsesWith——它还没被任何指令用过,调了也没效果
别碰 BasicBlock::getInstList().push_back() 这类底层 API
有人试图绕过 IRBuilder,直接往基本块指令链表里 push 新指令。这在语法上能编译,但几乎必然失败:
- 新指令的
Parent字段不会被自动设置,后续遍历基本块时会跳过它 LLVM 的验证器( - 其操作数的 use 链不会被正确注册,导致
replaceAllUsesWith找不到引用关系 - 哪怕侥幸通过 verify,在
LoopInfo或DominatorTree更新时大概率 crash
verifyFunction)会立刻报 "Instruction not in a basic block"
插入常量 vs 插入计算指令:别忽略类型匹配
IRBuilder 创建指令时,类型错误是高频崩溃源。例如:
-
Builder.getInt32(42)返回ConstantInt*,类型是i32 - 但若你把它喂给期望
i64的CreateShl,LLVM 不自动隐式转换,而是直接断言失败或生成非法 IR - 务必用
Builder.getInt64(42)或显式Builder.CreateSExt(..., Type::getInt64Ty(...)) - 函数调用参数同理:实参类型必须与被调函数签名逐位匹配,差一个
const或符号位都会在verify阶段报错
最稳妥的做法:用 Value::getType() 检查上游值类型,再决定如何构造或转换。
真正麻烦的从来不是“怎么插”,而是“插完之后 IR 是否仍满足 SSA 和 use-def 一致性”——哪怕只漏掉一次 replaceAllUsesWith 或插错位置,后续 Pass 就可能在完全无关的模块里崩溃,而且堆栈不指向你的代码。











