|
|
我先快速读取当前 IRGen 的入口与表达式/语句文件,然后按“节点到接口调用映射”给你一份可以直接交给 IRGen 同学实现的调用说明。
|
|
|
|
|
|
Read [](file:///home/shrink/nudt-compiler-cpp/include/ir/IR.h#320-320), lines 320 to 387
|
|
|
|
|
|
Read [](file:///home/shrink/nudt-compiler-cpp/include/irgen/IRGen.h)
|
|
|
|
|
|
Read [](file:///home/shrink/nudt-compiler-cpp/src/irgen/IRGenExp.cpp)
|
|
|
|
|
|
Read [](file:///home/shrink/nudt-compiler-cpp/src/irgen/IRGenStmt.cpp)
|
|
|
|
|
|
Read [](file:///home/shrink/nudt-compiler-cpp/src/irgen/IRGenFunc.cpp)
|
|
|
|
|
|
可以,下面给你一份面向 IRGen 同学的“如何调用你新增 IR 接口”的实现说明,按落地顺序写。
|
|
|
|
|
|
先看调用边界
|
|
|
|
|
|
1. IRGen 只负责把语法树翻译成 IR,不做符号查找
|
|
|
变量和函数绑定依赖 Sema 结果,参考 IRGenExp.cpp 的变量读取流程。
|
|
|
2. 所有指令创建都通过 IRBuilder
|
|
|
新增接口定义在 IR.h。
|
|
|
3. 终结指令规则必须遵守
|
|
|
Ret/Br/CondBr 一旦插入当前块,这个块不能再插普通指令,规则在 Instruction.cpp 和 BasicBlock.cpp。
|
|
|
|
|
|
建议 IRGen 调用映射
|
|
|
|
|
|
一、表达式层
|
|
|
|
|
|
1. 加减乘除模
|
|
|
- a + b: CreateAdd(lhs, rhs, temp)
|
|
|
- a - b: CreateSub(lhs, rhs, temp)
|
|
|
- a * b: CreateMul(lhs, rhs, temp)
|
|
|
- a / b: CreateDiv(lhs, rhs, temp)
|
|
|
- a % b: CreateMod(lhs, rhs, temp)
|
|
|
|
|
|
2. 比较表达式
|
|
|
- == != < <= > >= 统一走 CreateCmp(op, lhs, rhs, temp)
|
|
|
- 返回 i32,约定 0 为假,非 0 为真
|
|
|
|
|
|
3. 一元运算建议
|
|
|
- -x: CreateSub(const0, x, temp)
|
|
|
- !x: CreateCmp(Eq, x, const0, temp)
|
|
|
- +x: 直接返回 x
|
|
|
|
|
|
4. 变量读取和赋值
|
|
|
- 读取: 先由 Sema 绑定 use 到 decl,再从 storage_map 找槽位,CreateLoad
|
|
|
- 赋值: EvalExpr(rhs) 后对槽位 CreateStore(rhs_val, slot)
|
|
|
|
|
|
对应修改文件:
|
|
|
- IRGenExp.cpp
|
|
|
- IRGenDecl.cpp
|
|
|
- IRGenStmt.cpp
|
|
|
|
|
|
二、控制流层
|
|
|
|
|
|
1. if/else
|
|
|
- 创建三个块: then, else, merge
|
|
|
- cond = EvalExpr(condExp)
|
|
|
- 当前块插 CreateCondBr(cond, then, else)
|
|
|
- 切入 then 块生成语句,末尾若未终结则 CreateBr(merge)
|
|
|
- 切入 else 块同理
|
|
|
- 最后 SetInsertPoint(merge)
|
|
|
|
|
|
2. while
|
|
|
- 创建三个块: cond, body, exit
|
|
|
- 进入前先 CreateBr(cond)
|
|
|
- cond 块计算条件,CreateCondBr(cond_val, body, exit)
|
|
|
- body 块生成循环体,若未终结则回跳 CreateBr(cond)
|
|
|
- 最后 SetInsertPoint(exit)
|
|
|
|
|
|
3. break/continue
|
|
|
- IRGen 维护循环栈,每层保存 continue_target 和 break_target
|
|
|
- break 直接 CreateBr(break_target)
|
|
|
- continue 直接 CreateBr(continue_target)
|
|
|
|
|
|
对应修改文件:
|
|
|
- IRGenStmt.cpp
|
|
|
|
|
|
三、函数与调用层
|
|
|
|
|
|
1. 函数定义
|
|
|
- 用 Module.CreateFunction(name, ret_type, param_types) 建函数
|
|
|
- 入口块 SetInsertPoint(entry)
|
|
|
- 形参落栈策略建议:每个参数先 alloca,再 store 到本地槽位,后续统一按 load/store 访问
|
|
|
|
|
|
2. 函数调用
|
|
|
- 先拿到 callee 对应的 Function
|
|
|
- 实参逐个 EvalExpr 得到 args
|
|
|
- 调用 CreateCall(callee, args, temp)
|
|
|
- 若返回值非 void,把返回 Value 继续参与表达式计算
|
|
|
|
|
|
对应修改文件:
|
|
|
- IRGenFunc.cpp
|
|
|
- IRGenExp.cpp
|
|
|
|
|
|
四、你要提醒 IRGen 同学的两个关键点
|
|
|
|
|
|
1. 插入点管理
|
|
|
每次生成 Br/CondBr/Ret 后,必须切换到新块再继续生成。否则会触发块已终结错误。
|
|
|
2. grammar 上下文名先对齐
|
|
|
你们当前 IRGen 里还在用 ParenExpContext、AdditiveExpContext 这类命名,实际 grammar 是另一套层级时要先改 Visitor 入口,否则接口再全也接不上。可先核对 SysY.g4 与 IRGen.h。
|
|
|
|
|
|
最小接入顺序
|
|
|
|
|
|
1. 先接表达式算术与比较(M1)
|
|
|
2. 再接 if/while/break/continue(M3)
|
|
|
3. 再接函数参数和调用(M2)
|
|
|
|
|
|
如果你要,我下一步可以给你一版“按当前 grammar 的具体 visitor 函数名”逐条对照表,IRGen 同学可以直接照着改函数签名和调用点。 |