From 8b6ad43df09232d5c46b734879173367971529a6 Mon Sep 17 00:00:00 2001 From: Junhe Wu <2561075610@qq.com> Date: Tue, 19 May 2026 01:09:11 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BA=93=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=94=B9=E5=8A=A8=E9=80=A0=E6=88=90=E7=9A=84=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fix/ir-opt/2026-05-19-01/CHANGELOG.md | 87 +++++++++++++++++++ src/irgen/IRGenExp.cpp | 11 ++- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 .changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md diff --git a/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md b/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md new file mode 100644 index 0000000..9fa193b --- /dev/null +++ b/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md @@ -0,0 +1,87 @@ +# Fix: starttime/stoptime 库函数链接错误 + +**日期**: 2026-05-19 +**分支**: fix/ir-opt +**测试结果**: 737/737 通过 (--O0 --run) + +## 问题描述 + +运行 `bash scripts/run_ir_test.sh --O0 --run` 时,所有调用 `starttime()` / `stoptime()` 的 performance 测试用例在链接阶段失败: + +``` +undefined reference to `starttime' +undefined reference to `stoptime' +``` + +## 根因分析 + +`sylib/sylib.c` 中实际定义的计时函数是 `_sysy_starttime(int lineno)` 和 `_sysy_stoptime(int lineno)`。`sylib/sylib.h` 中的 `starttime()` / `stoptime()` 只是 C 预处理器宏: + +```c +#define starttime() _sysy_starttime(__LINE__) +#define stoptime() _sysy_stoptime(__LINE__) +``` + +SysY 编译器在 IR 生成时直接将 `starttime` / `stoptime` 作为 LLVM IR 函数名,导致: +1. 链接器找不到 `starttime` / `stoptime` 符号(实际符号是 `_sysy_starttime` / `_sysy_stoptime`) +2. 宏中的 `__LINE__` 无法通过 C 预处理器展开,调用未自动传入行号参数 + +## 修改内容 + +**文件**: `src/irgen/IRGenExp.cpp` + +### 修改1: EnsureExternalDecl 函数名映射(第167行) + +将外部函数声明的名称匹配从 `"starttime" || "stoptime"` 改为 `"_sysy_starttime" || "_sysy_stoptime"`: + +```cpp +// Before +} else if (name == "starttime" || name == "stoptime") { + module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), + {ir::Type::GetInt32Type()}); + +// After +} else if (name == "_sysy_starttime" || name == "_sysy_stoptime") { + module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), + {ir::Type::GetInt32Type()}); +``` + +### 修改2: 调用点名称映射 + 行号自动插入(第291行) + +在函数调用实参收集前,将 SysY 层面的 `starttime` / `stoptime` 映射为实际的库函数名,并自动插入源码行号作为实参: + +```cpp +std::string callee_name = ctx->Ident()->getText(); + +// 收集实参 +std::vector args; + +// 映射 starttime/stoptime 到实际的库函数名,并自动插入行号 +if (callee_name == "starttime") { + callee_name = "_sysy_starttime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); +} else if (callee_name == "stoptime") { + callee_name = "_sysy_stoptime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); +} +``` + +## 效果 + +修改前生成的 IR: +```llvm +declare void @starttime(i32) +declare void @stoptime(i32) +... +call void @starttime() ; 无参数,链接失败 +call void @stoptime() +``` + +修改后生成的 IR: +```llvm +declare void @_sysy_starttime(i32) +declare void @_sysy_stoptime(i32) +... +call void @_sysy_starttime(i32 65) ; 正确函数名 + 行号参数 +call void @_sysy_stoptime(i32 84) +``` diff --git a/src/irgen/IRGenExp.cpp b/src/irgen/IRGenExp.cpp index d1b88bb..a3495c4 100644 --- a/src/irgen/IRGenExp.cpp +++ b/src/irgen/IRGenExp.cpp @@ -164,7 +164,7 @@ void IRGenImpl::EnsureExternalDecl(const std::string& name) { module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), {ir::Type::GetInt32Type(), ir::Type::GetPtrFloat32Type()}); - } else if (name == "starttime" || name == "stoptime") { + } else if (name == "_sysy_starttime" || name == "_sysy_stoptime") { module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), {ir::Type::GetInt32Type()}); } else { @@ -290,6 +290,15 @@ std::any IRGenImpl::visitUnaryExp(SysYParser::UnaryExpContext* ctx) { // 收集实参 std::vector args; + + // 映射 starttime/stoptime 到实际的库函数名,并自动插入行号 + if (callee_name == "starttime") { + callee_name = "_sysy_starttime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); + } else if (callee_name == "stoptime") { + callee_name = "_sysy_stoptime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); + } if (ctx->funcRParams()) { for (auto* exp : ctx->funcRParams()->exp()) { // 检查是否是数组变量(无索引的 lVar),若是则传指针而非 load From 75c65e4728c6592f47c18960cb93b77d951b0399 Mon Sep 17 00:00:00 2001 From: Junhe Wu <2561075610@qq.com> Date: Tue, 19 May 2026 22:44:07 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86ir-opt=E7=9A=84?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E4=B8=8D=E9=80=9A=E8=BF=87=E3=80=81=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E4=B8=8D=E9=80=9A=E8=BF=87=E7=AD=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fix/ir-opt/2026-05-19-02/CHANGELOG.md | 89 +++++++++++++++++++ .../fix/ir-opt/2026-05-19-03/CHANGELOG.md | 80 +++++++++++++++++ include/ir/IR.h | 3 + src/ir/BasicBlock.cpp | 19 ++-- src/ir/passes/CSE.cpp | 14 ++- src/ir/passes/ConstFold.cpp | 5 +- src/ir/passes/ConstProp.cpp | 53 ++--------- src/ir/passes/DCE.cpp | 1 + src/ir/passes/Mem2Reg.cpp | 5 ++ src/ir/passes/PassManager.cpp | 14 +-- 10 files changed, 217 insertions(+), 66 deletions(-) create mode 100644 .changelog/fix/ir-opt/2026-05-19-02/CHANGELOG.md create mode 100644 .changelog/fix/ir-opt/2026-05-19-03/CHANGELOG.md diff --git a/.changelog/fix/ir-opt/2026-05-19-02/CHANGELOG.md b/.changelog/fix/ir-opt/2026-05-19-02/CHANGELOG.md new file mode 100644 index 0000000..f8a9a05 --- /dev/null +++ b/.changelog/fix/ir-opt/2026-05-19-02/CHANGELOG.md @@ -0,0 +1,89 @@ +# Fix: IR O1 优化导致 09_BFS/10_DFS 输出错误及编译器崩溃 + +**日期**: 2026-05-19 +**分支**: fix/ir-opt +**测试结果**: O1 前 422 项测试全部通过(后续 dead-code-elimination 性能问题为已存在缺陷) + +## 问题描述 + +运行 `bash ./scripts/run_ir_test.sh --O1 --run` 时: +1. 编译器在生成 `testdata/h_functional/10_DFS.sy` 的 IR 时崩溃/卡死 +2. `testdata/h_functional/09_BFS.sy` 的输出结果不正确(全为 0,期望有 1) +3. `testdata/h_functional/10_DFS.sy` 的可执行文件运行时死循环 + +## 根因分析 + +共发现 4 个缺陷: + +### 缺陷 1 (关键): ConstProp 删除指令前未清理操作数引用 +`src/ir/passes/ConstProp.cpp` 中 ZExt/SIToFP/FPToSI 折叠后直接调用 `RemoveFromParent()` 销毁指令,未先调用 `SetOperand(i, nullptr)` 断开 use-def 链。导致指令操作数的 `uses_` 列表中残留指向已释放内存的悬空指针,后续 `ReplaceAllUsesWith` 遍历 use 列表时访问已释放内存,造成崩溃(UB)。 + +### 缺陷 2 (关键): ConstFold 的 `removed` 集合存储悬空指针 +`src/ir/passes/ConstFold.cpp` 中 `DetachAndRemove` 销毁指令后将其地址存入 `std::unordered_set removed`。若新指令被分配到同一地址,会被错误跳过,导致漏优化。 + +### 缺陷 3 (关键): CSE 跨 Store/Call 指令错误消除 Load +`src/ir/passes/CSE.cpp` 的局部值编号不感知内存副作用:`Store`/`Call` 指令可能修改全局变量,但 `available` 映射中缓存的 `Load` 结果不会被失效。例如 `pop_queue()` 中: +```c +h = h + 1; // store @h +int res = que[h]; // load @h 应读到新值,但 CSE 用 store 前的旧值替代 +return que[h]; // 同上 +``` +导致 `que[old_h]` 被返回而非 `que[new_h]`,BFS 路径搜索失败。 + +### 缺陷 4: SanitizePhis 使用错误的 undef 类型 +`src/ir/passes/PassManager.cpp` 中 `SanitizePhis` 固定使用 `ctx.GetConstInt(0)` 作为浮点 PHI 节点的 undef 值,造成 LLVM IR 类型不匹配。同时修复无效 incoming 时过于激进——value 和 block 只有一个为 null 时却同时替换两者。 + +## 修改内容 + +### 修改 1: ConstProp.cpp — 移除冗余折叠,委托给 ConstFold +ConstFold 已使用 `DetachAndRemove`(先清操作数再删除)正确处理 ZExt/SIToFP/FPToSI 折叠。移除 ConstProp 中的重复实现,改为直接返回 false: +```cpp +bool RunConstProp(Function& func, Context& ctx) { + (void)func; (void)ctx; + return false; +} +``` + +### 修改 2: ConstFold.cpp — 用 GetParent() 检查替代 removed 集合 +```cpp +// Before: +std::unordered_set removed; +if (removed.count(inst)) continue; +removed.insert(inst); + +// After: +if (inst->GetParent() == nullptr) continue; +``` + +### 修改 3: CSE.cpp — Store/Call 指令清空 available 映射 +```cpp +// 新增: Store/Call 可能修改内存,保守清空 available 表 +if (ip->GetOpcode() == Opcode::Store || + ip->GetOpcode() == Opcode::Call) { + available.clear(); + continue; +} +``` + +### 修改 4: PassManager.cpp — SanitizePhis 类型感知 + 精准修复 +```cpp +// 根据 PHI 类型选择 undef 值 +Value* undef_val = phi->GetType()->IsFloat32() + ? static_cast(ctx.GetConstFloat(0.0f)) + : static_cast(ctx.GetConstInt(0)); + +// 仅修复确实为 null 的操作数 +if (!inc_val) phi->SetOperand(i * 2, undef_val); +if (!inc_bb) phi->SetOperand(i * 2 + 1, entry); +``` + +## 效果 + +- 09_BFS/10_DFS 全部通过 O1 测试 +- 编译器不再崩溃或卡死 +- O1 生成的 IR 通过 llc 验证 +- 前 422 项测试 0 失败 + +## 已知遗留问题 + +dead-code-elimination-1.sy(100,000 个局部变量的压力测试)在 O1 下编译极慢(~940s),根因是 Mem2Reg 中 `BasicBlock::RemoveInstruction` 的 O(n) 线性查找被调用 O(n) 次,整体 O(n²)。此问题在本次修复前即存在(之前被 10_DFS 卡死掩盖),留待后续修复。 diff --git a/.changelog/fix/ir-opt/2026-05-19-03/CHANGELOG.md b/.changelog/fix/ir-opt/2026-05-19-03/CHANGELOG.md new file mode 100644 index 0000000..4925f39 --- /dev/null +++ b/.changelog/fix/ir-opt/2026-05-19-03/CHANGELOG.md @@ -0,0 +1,80 @@ +# Fix: Mem2Reg 中 RemoveInstruction 的 O(n²) 性能问题导致 dead-code-elimination 编译极慢 + +**日期**: 2026-05-19 +**分支**: fix/ir-opt +**测试结果**: 全部 737 项测试通过,dead-code-elimination-1/2/3 编译时间从 ~940s 降至 ~2-3s + +## 问题描述 + +`dead-code-elimination-1/2/3.sy`(三个完全相同的 100,000 局部变量压力测试,仅输入规模不同:163/16384/1638400)在 O1 下编译极慢(~940s),远超合理范围。 + +## 根因分析 + +`BasicBlock::RemoveInstruction` 对 `std::vector>` 做指针线性扫描获取迭代器,再执行 `vector::erase`(需要移动后续所有元素),每次调用均为 O(n),其中 n 为基本块内指令数。 + +Mem2Reg 提升 SSA 后需删除所有已提升的 load、store 和 alloca 指令。在 100K 变量的压力测试中,所有指令集中在单个入口基本块内(~300K 条指令),Mem2Reg 对每条指令调用一次 `RemoveFromParent()` → `RemoveInstruction()`: + +- 调用次数:~300K 次 +- 每次平均扫描:~150K 个元素 +- 总比较次数:~45B 次 +- 每次 erase 还需移动平均 150K 个 `unique_ptr` + +## 修改内容 + +### 修改 1: IR.h — 新增 `SweepDeadInstructions()` 声明和 `dead_instructions_` 成员 + +```cpp +// public 区域(InsertBefore 之后): +void SweepDeadInstructions(); + +// private 区域(instructions_ 之后): +std::unordered_set dead_instructions_; +``` + +### 修改 2: BasicBlock.cpp — RemoveInstruction 改为 O(1) 惰性标记 + +```cpp +// Before: +void BasicBlock::RemoveInstruction(Instruction* inst) { + for (auto it = instructions_.begin(); it != instructions_.end(); ++it) { + if (it->get() == inst) { + instructions_.erase(it); + return; + } + } +} + +// After: O(1) 标记 + 惰性清除 +void BasicBlock::RemoveInstruction(Instruction* inst) { + dead_instructions_.insert(inst); + inst->SetParent(nullptr); +} + +void BasicBlock::SweepDeadInstructions() { + if (dead_instructions_.empty()) return; + auto it = std::remove_if(instructions_.begin(), instructions_.end(), + [this](const std::unique_ptr& p) { + return dead_instructions_.count(p.get()) > 0; + }); + instructions_.erase(it, instructions_.end()); + dead_instructions_.clear(); +} +``` + +### 修改 3-6: 各 pass 在删除循环后调用 SweepDeadInstructions + +- **Mem2Reg.cpp**: 删除所有 load/store/alloca 后遍历所有 block 调用 `SweepDeadInstructions()` — **主要修复点** +- **DCE.cpp**: 每个 block 的 `to_remove` 循环后调用 `SweepDeadInstructions()` +- **CSE.cpp**: 每个 block 的 `to_remove` 循环后调用 `SweepDeadInstructions()` +- **ConstFold.cpp**: 每个 block 的折叠循环后调用 `SweepDeadInstructions()` + +## 效果 + +| 测试用例 | 修复前 | 修复后 | +|----------|--------|--------| +| dead-code-elimination-1 | ~940s | ~2s | +| dead-code-elimination-2 | ~940s | ~3s | +| dead-code-elimination-3 | ~940s | ~3s | +| 全部 737 项测试 | — | 0 失败 | + +约 300-400x 加速。三个用例输出与预期完全一致。 diff --git a/include/ir/IR.h b/include/ir/IR.h index 6e7349d..0292def 100644 --- a/include/ir/IR.h +++ b/include/ir/IR.h @@ -410,6 +410,8 @@ class BasicBlock : public Value { void RemoveInstruction(Instruction* inst); // 在 before 之前插入指令;before 为 nullptr 时追加到末尾 void InsertBefore(Instruction* inst, Instruction* before); + // 批量清除已标记的 dead 指令(单次 O(n) sweep) + void SweepDeadInstructions(); template T* Append(Args&&... args) { @@ -449,6 +451,7 @@ class BasicBlock : public Value { private: Function* parent_ = nullptr; std::vector> instructions_; + std::unordered_set dead_instructions_; std::vector predecessors_; std::vector successors_; }; diff --git a/src/ir/BasicBlock.cpp b/src/ir/BasicBlock.cpp index c69db10..5b9c0c1 100644 --- a/src/ir/BasicBlock.cpp +++ b/src/ir/BasicBlock.cpp @@ -62,12 +62,19 @@ void BasicBlock::AddSuccessor(BasicBlock* bb) { void BasicBlock::ClearSuccessors() { successors_.clear(); } void BasicBlock::RemoveInstruction(Instruction* inst) { - for (auto it = instructions_.begin(); it != instructions_.end(); ++it) { - if (it->get() == inst) { - instructions_.erase(it); - return; - } - } + dead_instructions_.insert(inst); + inst->SetParent(nullptr); +} + +void BasicBlock::SweepDeadInstructions() { + if (dead_instructions_.empty()) return; + + auto it = std::remove_if(instructions_.begin(), instructions_.end(), + [this](const std::unique_ptr& p) { + return dead_instructions_.count(p.get()) > 0; + }); + instructions_.erase(it, instructions_.end()); + dead_instructions_.clear(); } void BasicBlock::InsertBefore(Instruction* inst, Instruction* before) { diff --git a/src/ir/passes/CSE.cpp b/src/ir/passes/CSE.cpp index eff6d69..c80f370 100644 --- a/src/ir/passes/CSE.cpp +++ b/src/ir/passes/CSE.cpp @@ -92,13 +92,18 @@ bool RunCSE(Function& func) { for (auto& inst : bb->GetInstructions()) { auto* ip = inst.get(); - std::string key = MakeKey(ip); - if (key.empty()) { - // 不可消除的指令:如果它有结果,可以考虑将其加入可用集 - // 但为了简单,这里不处理 + + // Store/Call 指令可能修改内存,使之前缓存的 Load/Gep 结果失效。 + // 保守地清空整个 available 表,避免跨副作用的错误 CSE。 + if (ip->GetOpcode() == Opcode::Store || + ip->GetOpcode() == Opcode::Call) { + available.clear(); continue; } + std::string key = MakeKey(ip); + if (key.empty()) continue; + auto it = available.find(key); if (it != available.end()) { // 找到已有的等价指令,替换使用 @@ -115,6 +120,7 @@ bool RunCSE(Function& func) { ip->SetOperand(i, nullptr); ip->RemoveFromParent(); } + bb->SweepDeadInstructions(); } return changed; diff --git a/src/ir/passes/ConstFold.cpp b/src/ir/passes/ConstFold.cpp index 2a2edcb..2cb0050 100644 --- a/src/ir/passes/ConstFold.cpp +++ b/src/ir/passes/ConstFold.cpp @@ -135,7 +135,6 @@ bool FoldBinaryWithCtx(BinaryInst* bin, Context& ctx) { bool RunConstFold(Function& func, Context& ctx) { bool changed = false; - std::unordered_set removed; bool any_changed = true; while (any_changed) { @@ -147,7 +146,7 @@ bool RunConstFold(Function& func, Context& ctx) { insts.push_back(inst.get()); for (auto* inst : insts) { - if (removed.count(inst)) continue; + if (inst->GetParent() == nullptr) continue; bool folded = false; switch (inst->GetOpcode()) { case Opcode::Add: case Opcode::Sub: case Opcode::Mul: @@ -174,11 +173,11 @@ bool RunConstFold(Function& func, Context& ctx) { default: break; } if (folded) { - removed.insert(inst); any_changed = true; changed = true; } } + bb->SweepDeadInstructions(); } } return changed; diff --git a/src/ir/passes/ConstProp.cpp b/src/ir/passes/ConstProp.cpp index ced51c5..9b7a780 100644 --- a/src/ir/passes/ConstProp.cpp +++ b/src/ir/passes/ConstProp.cpp @@ -11,53 +11,12 @@ namespace ir { bool RunConstProp(Function& func, Context& ctx) { - bool changed = false; - - for (auto& bb : func.GetBlocks()) { - std::vector insts; - for (auto& inst : bb->GetInstructions()) - insts.push_back(inst.get()); - - for (auto* inst : insts) { - if (inst->GetParent() == nullptr) continue; - // 检查是否为"复制"类指令:直接将一个操作数作为结果传播 - // 实际上常量传播由 ConstFold 配合 use-def 链完成 - // 这里处理简单的常量替换 - switch (inst->GetOpcode()) { - case Opcode::ZExt: { - auto* ze = static_cast(inst); - if (auto* ci = dynamic_cast(ze->GetSrc())) { - ze->ReplaceAllUsesWith(ctx.GetConstInt(ci->GetValue() != 0 ? 1 : 0)); - ze->RemoveFromParent(); - changed = true; - } - break; - } - case Opcode::SIToFP: { - auto* si = static_cast(inst); - if (auto* ci = dynamic_cast(si->GetSrc())) { - si->ReplaceAllUsesWith( - ctx.GetConstFloat(static_cast(ci->GetValue()))); - si->RemoveFromParent(); - changed = true; - } - break; - } - case Opcode::FPToSI: { - auto* fp = static_cast(inst); - if (auto* cf = dynamic_cast(fp->GetSrc())) { - fp->ReplaceAllUsesWith( - ctx.GetConstInt(static_cast(cf->GetValue()))); - fp->RemoveFromParent(); - changed = true; - } - break; - } - default: break; - } - } - } - return changed; + // ZExt/SIToFP/FPToSI 常量折叠已在 ConstFold 中统一处理(使用 + // DetachAndRemove 安全断开 use-def 链后再删除指令),此处不再重复处理, + // 否则 RemoveFromParent 直接销毁指令会导致其操作数的 uses_ 列表残留悬空指针。 + (void)func; + (void)ctx; + return false; } } // namespace ir diff --git a/src/ir/passes/DCE.cpp b/src/ir/passes/DCE.cpp index 7f34f60..0b7e788 100644 --- a/src/ir/passes/DCE.cpp +++ b/src/ir/passes/DCE.cpp @@ -60,6 +60,7 @@ bool RunDCE(Function& func) { ip->RemoveFromParent(); changed = true; } + bb->SweepDeadInstructions(); } return changed; diff --git a/src/ir/passes/Mem2Reg.cpp b/src/ir/passes/Mem2Reg.cpp index 7f5237a..4147339 100644 --- a/src/ir/passes/Mem2Reg.cpp +++ b/src/ir/passes/Mem2Reg.cpp @@ -184,6 +184,11 @@ bool RunMem2Reg(Function& func, Context& ctx) { info.alloca->RemoveFromParent(); } + // 批量清除已标记的指令(一次 O(n) sweep 替代每次 O(n) 删除) + for (auto& bb : func.GetBlocks()) { + bb->SweepDeadInstructions(); + } + return true; } diff --git a/src/ir/passes/PassManager.cpp b/src/ir/passes/PassManager.cpp index f6d270f..4abacc4 100644 --- a/src/ir/passes/PassManager.cpp +++ b/src/ir/passes/PassManager.cpp @@ -31,9 +31,13 @@ static void SanitizePhis(Function& func, Context& ctx) { if (phis.empty()) continue; auto& preds = bb->GetPredecessors(); - Value* undef_val = ctx.GetConstInt(0); for (auto* phi : phis) { + // 根据 PHI 节点的类型选择正确的 undef 值 + Value* undef_val = phi->GetType()->IsFloat32() + ? static_cast(ctx.GetConstFloat(0.0f)) + : static_cast(ctx.GetConstInt(0)); + // 收集已有的 incoming 块 std::unordered_set existing; for (size_t i = 0; i < phi->GetNumIncoming(); ++i) { @@ -47,14 +51,12 @@ static void SanitizePhis(Function& func, Context& ctx) { } } - // 修复无效的 incoming + // 修复无效的 incoming:仅修复确实为 null 的操作数 for (size_t i = 0; i < phi->GetNumIncoming(); ++i) { auto* inc_val = phi->GetIncomingValue(i); auto* inc_bb = phi->GetIncomingBlock(i); - if (!inc_val || !inc_bb) { - phi->SetOperand(i * 2, undef_val); - phi->SetOperand(i * 2 + 1, entry); - } + if (!inc_val) phi->SetOperand(i * 2, undef_val); + if (!inc_bb) phi->SetOperand(i * 2 + 1, entry); } } }