LLVM Loop循环的中间代码生成

张开发
2026/5/21 17:39:33 15 分钟阅读
LLVM Loop循环的中间代码生成
Value *ExprForAST::Codegen() { // 1. 生成循环起始值的代码 // 对应: i 1 // 生成IR: %startval [计算起始值的指令] Value *StartVal Start-Codegen(); if (StartVal 0) return 0; // 2. 获取当前函数和基本块信息 // 获取包含当前代码的函数对象 Function *TheFunction Builder.GetInsertBlock()-getParent(); // 获取当前插入点所在的基本块循环前置块的前驱 // 这个块将在跳转到循环之前执行 BasicBlock *PreheaderBB Builder.GetInsertBlock(); // 创建循环体的基本块命名为loop BasicBlock *LoopBB BasicBlock::Create(getGlobalContext(), loop, TheFunction); // 3. 生成无条件跳转进入循环 // 生成IR: br label %loop // 从当前块跳转到循环体 Builder.CreateBr(LoopBB); // 将IR生成器的插入点设置到循环体基本块 Builder.SetInsertPoint(LoopBB); // 4. 创建PHI节点 // 生成IR: %i phi i32 [起始值, %preheader], [下一次值, %loop] // 参数2表示有2个可能的值来源 PHINode *Variable Builder.CreatePHI( Type::getInt32Ty(getGlobalContext()), 2, Var_Name.c_str()); // 添加第一个来源从前置块来的时候取起始值 // 对应: [起始值, %preheader] Variable-addIncoming(StartVal, PreheaderBB); // 5. 保存旧的变量映射 // 保存当前变量名对应的旧值可能在外部作用域已存在 Value *OldVal Named_Values[Var_Name]; // 将PHI节点绑定到变量名使得循环体内对该变量的引用使用PHI节点 // 这样循环体内的代码就能正确使用当前的循环变量值 Named_Values[Var_Name] Variable; // 6. 生成循环体的代码 // 生成IR: [循环体内的所有指令] // 注意循环体内对变量i的引用会使用上面的PHI节点 if (Body-Codegen() 0) return 0; // 7. 生成步长值 // 对应: step (默认为1) Value *StepVal; if (Step) { StepVal Step-Codegen(); if (StepVal 0) return 0; } else { // 默认步长为1 // 生成IR: 1 StepVal ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 1); } // 8. 计算下一次的循环变量值 // 生成IR: %nextvar add i32 %i, 步长 Value *NextVar Builder.CreateAdd(Variable, StepVal, nextvar); // 9. 生成循环结束条件的代码 // 对应: i n 这样的条件表达式 // 生成IR: %endcond [计算结束条件的指令] Value *EndCond End-Codegen(); if (EndCond 0) return EndCond; // 10. 将结束条件转换为布尔值 // 生成IR: %loopcond icmp ne i32 %endcond, 0 // 将整数值转换为布尔条件非0为真 EndCond Builder.CreateICmpNE( EndCond, ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0), loopcond); // 11. 记录循环结束块 BasicBlock *LoopEndBB Builder.GetInsertBlock(); // 12. 创建循环后的基本块 // 当循环条件为假时跳转到这个块 BasicBlock *AfterBB BasicBlock::Create( getGlobalContext(), afterloop, TheFunction); // 13. 生成条件分支指令 // 生成IR: br i1 %loopcond, label %loop, label %afterloop // 如果条件为真继续循环为假退出循环 Builder.CreateCondBr(EndCond, LoopBB, AfterBB); // 将IR生成器的插入点设置到循环后的基本块 Builder.SetInsertPoint(AfterBB); // 14. 为PHI节点添加第二个来源 // 对应: [%nextvar, %loop] // 当从循环体跳转回来时PHI节点取计算出的下一次值 Variable-addIncoming(NextVar, LoopEndBB); // 15. 恢复变量映射 // 循环结束后恢复之前保存的旧值如果存在 if (OldVal) Named_Values[Var_Name] OldVal; // 恢复旧值 else Named_Values.erase(Var_Name); // 删除临时绑定 // 16. 返回空值void // 循环表达式不产生有用的值返回0 return Constant::getNullValue(Type::getInt32Ty(getGlobalContext())); }完整IR生成示例对于代码for i 1, i n, 1 in x 1上述函数生成的完整IR如下define i32 printstar(i32 %n, i32 %x) { entry: ; PreheaderBB ; Step 3: 无条件跳转到循环 br label %loop loop: ; LoopBB ; Step 4: PHI节点i的值来自两个地方 %i phi i32 [1, %entry], [%nextvar, %loop] ; Step 6: 循环体 Body-Codegen() ; x 1 的计算 %addtmp add i32 %x, 1 ; Step 8: 计算下一次的值 %nextvar add i32 %i, 1 ; Step 9-10: 计算并转换结束条件 %cmptmp icmp ult i32 %i, %n %loopcond icmp ne i1 %cmptmp, false ; Step 13: 条件分支 br i1 %loopcond, label %loop, label %afterloop afterloop: ; AfterBB ; Step 16: 返回 ret i32 0 }关键设计要点代码行作用对应IRCreateBr(LoopBB)跳入循环br label %loopCreatePHI(..., 2, ...)创建PHI节点%i phi i32addIncoming(StartVal, PreheaderBB)初始值来源[1, %entry]Builder.CreateAdd(Variable, StepVal)计算步长%nextvar addCreateCondBr(EndCond, LoopBB, AfterBB)条件跳转br i1 %loopcond, label %loop, label %afterloopaddIncoming(NextVar, LoopEndBB)迭代值来源[%nextvar, %loop]这个实现完美展示了如何使用LLVM的PHI节点来实现SSA形式下的循环变量更新。

更多文章