refactor(irgen): 完善irgen代码和文档,提升扩展兼容性

master
jing 1 day ago
parent a091d9108a
commit f19d23c656

@ -2,49 +2,83 @@
## 1. 本实验定位
Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 SysY 语法正确翻译为 IR。
Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 SysY 语法正确翻译为 IR。
## 2. Lab2 要求
需要同学完成:
1. 熟悉 IR 相关数据结构与构建接口。
2. 理解当前语法树 -> IR 的最小实现流程。
3. 在现有框架上扩展 IR 生成能力使其覆盖课程要求的Sysy语法。
2. 理解当前语法树 -> 语义检查 -> IR 的最小实现流程。
3. 在现有框架上补充语义检查与 IR 生成功能,使其覆盖课程要求的 SysY 语法。
## 3. 相关文件
以下文件与本实验内容相关,建议优先阅读。
- `include/sem/Sema.h`
- `include/sem/SymbolTable.h`
- `src/sem/Sema.cpp`
- `src/sem/SymbolTable.cpp`
- `include/ir/IR.h`
- `src/ir/Context.cpp`
- `src/ir/Value.cpp`
- `src/ir/Instruction.cpp`
- `src/ir/BasicBlock.cpp`
- `src/ir/Function.cpp`
- `src/ir/Module.cpp`
- `src/ir/IRBuilder.cpp`
- `src/ir/IRPrinter.cpp`
- `include/irgen/IRGen.h`
- `src/irgen/IRGenDecl.cpp`
- `src/irgen/IRGenStmt.cpp`
- `src/irgen/IRGenExp.cpp`
- `src/irgen/IRGenFunc.cpp`
- `src/irgen/IRGenDriver.cpp`
## 4. 当前最小示例实现说明
当前语法树 -> IR 仅覆盖最小子集:
当前语法树 -> 语义检查 -> IR 仅覆盖最小子集:
1. 常量整数、变量引用、二元加法表达式。
2. 局部变量声明(当前采用 LLVM 前端常见的 `alloca/load/store` 内存模型)。
3. `return` 语句。
4. 单函数 `main` 的最小流程。
其中,`sema` 负责最基本的名称绑定与合法性检查,`irgen` 在此基础上继续生成 IR。
如果语义检查阶段没有补全,后续 IR 生成阶段通常也无法正确处理变量引用、声明绑定等逻辑。
说明:当前阶段变量统一采用内存模型:先 `alloca` 分配栈槽,再通过 `store/load` 读写。即使变量由常量初始化(如 `int a = 1;`),也会先 `store` 到栈槽,而不是直接把变量替换成 SSA 值。后续实验中,同学可按需求再重构。
此外,当前 IR 还维护了最基本的 use-def 关系:每个 `Value` 会记录哪些 `Instruction` 使用了它。
这对后续做数据流分析、死代码删除、常量传播等优化会很有帮助;但目前相关实现,接口仍不完整,后续实验中还需要同学继续补充和完善。
## 5. 构建与运行
## 5. 语法树与 Sema / IRGen 的关系
当前项目中的 `sema``irgen` 都不是面向独立 AST 设计的,而是直接遍历 ANTLR 生成的语法树节点来完成语义检查与 IR 生成。
因此,`src/antlr4/SysY.g4` 中 rule 的命名、层级结构以及 labeled alternative 的写法,会直接影响 `SysYParser::*Context` 的类型名和访问接口;一旦 grammar 发生变化,`sem` / `irgen` 中对应的遍历逻辑通常也需要同步修改。
这也是为什么在 Lab1 扩展 grammar 后Lab2 常常还需要继续修改 `sem` / `irgen`
不是因为 IR 本身一定变了,而是因为“语法树长什么样”,直接决定了语义检查和 IR 生成代码该如何遍历和取信息。
如果 grammar 扩展后 `sem` / `irgen` 没有同步修改,常见现象包括:
1. 编译阶段报错,例如某个 `SysYParser::*Context` 类型不存在,或某个成员函数不存在。
2. 运行阶段报错,例如进入 `暂不支持的表达式形式`、`暂不支持的语句类型`,或名称绑定失败等分支。
遇到这类问题时,需要同学自行对照 `SysY.g4`、ANTLR 生成的 `SysYParser.h`,以及 `src/sem` / `src/irgen` 中的遍历逻辑完成对应的接口调整与功能补全。ANTLR 生成的结构可参考 `build/generated/antlr4/SysYParser.h``build/generated/antlr4/SysYParser.cpp`;其中前者更适合查看各类 `SysYParser::*Context` 的名字与成员函数,后者可辅助查看规则展开后的具体实现。
## 6. 构建与运行
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j "$(nproc)"
```
## 6. Lab2 验证方式
## 7. Lab2 验证方式
可先用单个样例检查 IR 输出是否基本正确:
@ -52,11 +86,7 @@ cmake --build build -j "$(nproc)"
./build/bin/compiler --emit-ir test/test_case/functional/simple_add.sy
```
如需打印语法树:
```bash
./build/bin/compiler --emit-parse-tree test/test_case/functional/simple_add.sy
```
推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路。`--run` 模式下会自动读取同名 `.in`,并将程序输出与退出码和同名 `.out` 比对,用于验证 IR 的正确性:

@ -22,27 +22,25 @@ class IRGenImpl {
public:
IRGenImpl(ir::Module& module, const SemanticContext& sema);
void Gen(SysYParser::CompUnitContext& cu);
void Gen(SysYParser::CompUnitContext& cu);
private:
void GenFuncDef(SysYParser::FuncDefContext& func);
void GenBlock(SysYParser::BlockContext& block);
void GenBlock(SysYParser::BlockStmtContext& block);
bool GenBlockItem(SysYParser::BlockItemContext& item);
void GenDecl(SysYParser::DeclContext& decl);
bool GenStmt(SysYParser::StmtContext& stmt);
void GenVarDecl(SysYParser::VarDeclContext& decl);
void GenVarDef(SysYParser::VarDefContext& decl);
void GenReturnStmt(SysYParser::ReturnStmtContext& ret);
ir::Value* GenExpr(SysYParser::ExpContext& expr);
ir::Value* GenAddExpr(SysYParser::AddExpContext& add);
ir::Value* GenPrimary(SysYParser::PrimaryContext& primary);
ir::Module& module_;
const SemanticContext& sema_;
ir::Function* func_;
ir::IRBuilder builder_;
// 名称绑定由 Sema 负责IRGen 只维护“声明 -> 存储槽位”的代码生成状态。
std::unordered_map<SysYParser::VarDeclContext*, ir::Value*> storage_map_;
std::unordered_map<SysYParser::VarDefContext*, ir::Value*> storage_map_;
};
std::unique_ptr<ir::Module> GenerateIR(SysYParser::CompUnitContext& tree,

@ -7,20 +7,20 @@
class SemanticContext {
public:
void BindVarUse(SysYParser::PrimaryContext* use,
SysYParser::VarDeclContext* decl) {
void BindVarUse(SysYParser::VarContext* use,
SysYParser::VarDefContext* decl) {
var_uses_[use] = decl;
}
SysYParser::VarDeclContext* ResolveVarUse(
const SysYParser::PrimaryContext* use) const {
SysYParser::VarDefContext* ResolveVarUse(
const SysYParser::VarContext* use) const {
auto it = var_uses_.find(use);
return it == var_uses_.end() ? nullptr : it->second;
}
private:
std::unordered_map<const SysYParser::PrimaryContext*,
SysYParser::VarDeclContext*>
std::unordered_map<const SysYParser::VarContext*,
SysYParser::VarDefContext*>
var_uses_;
};

@ -8,10 +8,10 @@
class SymbolTable {
public:
void Add(const std::string& name, SysYParser::VarDeclContext* decl);
void Add(const std::string& name, SysYParser::VarDefContext* decl);
bool Contains(const std::string& name) const;
SysYParser::VarDeclContext* Lookup(const std::string& name) const;
SysYParser::VarDefContext* Lookup(const std::string& name) const;
private:
std::unordered_map<std::string, SysYParser::VarDeclContext*> table_;
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
};

@ -6,7 +6,18 @@
#include "ir/IR.h"
#include "utils/Log.h"
void IRGenImpl::GenBlock(SysYParser::BlockContext& block) {
namespace {
std::string GetLValueName(SysYParser::LValueContext& lvalue) {
if (!lvalue.ID()) {
throw std::runtime_error(FormatError("irgen", "非法左值"));
}
return lvalue.ID()->getText();
}
} // namespace
void IRGenImpl::GenBlock(SysYParser::BlockStmtContext& block) {
for (auto* item : block.blockItem()) {
if (item) {
if (GenBlockItem(*item)) {
@ -29,14 +40,21 @@ bool IRGenImpl::GenBlockItem(SysYParser::BlockItemContext& item) {
}
void IRGenImpl::GenDecl(SysYParser::DeclContext& decl) {
if (decl.varDecl()) {
GenVarDecl(*decl.varDecl());
return;
if (!decl.btype() || !decl.btype()->INT()) {
throw std::runtime_error(FormatError("irgen", "当前仅支持局部 int 变量声明"));
}
auto* var_def = decl.varDef();
if (!var_def) {
throw std::runtime_error(FormatError("irgen", "非法变量声明"));
}
throw std::runtime_error(FormatError("irgen", "暂不支持的声明类型"));
GenVarDef(*var_def);
}
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
void IRGenImpl::GenVarDef(SysYParser::VarDefContext& decl) {
if (!decl.lValue()) {
throw std::runtime_error(FormatError("irgen", "变量声明缺少名称"));
}
GetLValueName(*decl.lValue());
if (storage_map_.find(&decl) != storage_map_.end()) {
throw std::runtime_error(FormatError("irgen", "声明重复生成存储槽位"));
}
@ -44,8 +62,11 @@ void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
storage_map_[&decl] = slot;
ir::Value* init = nullptr;
if (decl.exp()) {
init = GenExpr(*decl.exp());
if (auto* init_value = decl.initValue()) {
if (!init_value->exp()) {
throw std::runtime_error(FormatError("irgen", "当前不支持聚合初始化"));
}
init = GenExpr(*init_value->exp());
} else {
init = builder_.CreateConstInt(0);
}

@ -7,49 +7,38 @@
#include "utils/Log.h"
ir::Value* IRGenImpl::GenExpr(SysYParser::ExpContext& expr) {
if (!expr.addExp()) {
throw std::runtime_error(FormatError("irgen", "非法表达式"));
if (auto* paren = dynamic_cast<SysYParser::ParenExpContext*>(&expr)) {
return GenExpr(*paren->exp());
}
return GenAddExpr(*expr.addExp());
}
ir::Value* IRGenImpl::GenAddExpr(SysYParser::AddExpContext& add) {
// 当前表达式层次仍是最小实现,直接贴合 addExp -> primary 的语法形状。
const auto& terms = add.primary();
if (terms.empty()) {
throw std::runtime_error(FormatError("irgen", "空加法表达式"));
}
ir::Value* acc = GenPrimary(*terms[0]);
for (size_t i = 1; i < terms.size(); ++i) {
ir::Value* rhs = GenPrimary(*terms[i]);
std::string name = module_.GetContext().NextTemp();
acc = builder_.CreateBinary(ir::Opcode::Add, acc, rhs, name);
}
return acc;
}
ir::Value* IRGenImpl::GenPrimary(SysYParser::PrimaryContext& primary) {
if (primary.Number()) {
return builder_.CreateConstInt(std::stoi(primary.Number()->getText()));
if (auto* number = dynamic_cast<SysYParser::NumberExpContext*>(&expr)) {
if (!number->number() || !number->number()->ILITERAL()) {
throw std::runtime_error(FormatError("irgen", "当前仅支持整数字面量"));
}
return builder_.CreateConstInt(std::stoi(number->number()->getText()));
}
if (primary.Ident()) {
auto* decl = sema_.ResolveVarUse(&primary);
if (auto* var = dynamic_cast<SysYParser::VarExpContext*>(&expr)) {
if (!var->var() || !var->var()->ID()) {
throw std::runtime_error(FormatError("irgen", "当前仅支持普通整型变量"));
}
auto* decl = sema_.ResolveVarUse(var->var());
if (!decl) {
throw std::runtime_error(
FormatError("irgen",
"变量使用缺少语义绑定: " + primary.Ident()->getText()));
"变量使用缺少语义绑定: " + var->var()->ID()->getText()));
}
auto it = storage_map_.find(decl);
if (it == storage_map_.end()) {
throw std::runtime_error(
FormatError("irgen",
"变量声明缺少存储槽位: " + primary.Ident()->getText()));
"变量声明缺少存储槽位: " + var->var()->ID()->getText()));
}
return builder_.CreateLoad(it->second, module_.GetContext().NextTemp());
}
if (primary.exp()) {
return GenExpr(*primary.exp());
if (auto* binary = dynamic_cast<SysYParser::AdditiveExpContext*>(&expr)) {
ir::Value* lhs = GenExpr(*binary->exp(0));
ir::Value* rhs = GenExpr(*binary->exp(1));
return builder_.CreateBinary(ir::Opcode::Add, lhs, rhs,
module_.GetContext().NextTemp());
}
throw std::runtime_error(FormatError("irgen", "暂不支持的表达式形式"));
}

@ -28,26 +28,31 @@ IRGenImpl::IRGenImpl(ir::Module& module, const SemanticContext& sema)
builder_(module.GetContext(), nullptr) {}
void IRGenImpl::Gen(SysYParser::CompUnitContext& cu) {
if (!cu.funcDef()) {
throw std::runtime_error(FormatError("irgen", "缺少 main 定义"));
auto* func = cu.funcDef();
if (func && func->ID() && func->ID()->getText() == "main") {
GenFuncDef(*func);
return;
}
GenFuncDef(*cu.funcDef());
throw std::runtime_error(FormatError("irgen", "缺少 main 定义"));
}
void IRGenImpl::GenFuncDef(SysYParser::FuncDefContext& func) {
if (!func.block()) {
if (!func.blockStmt()) {
throw std::runtime_error(FormatError("irgen", "函数体为空"));
}
if (!func.Ident()) {
if (!func.ID()) {
throw std::runtime_error(FormatError("irgen", "缺少函数名"));
}
if (!func.funcType() || !func.funcType()->INT()) {
throw std::runtime_error(FormatError("irgen", "当前仅支持 int main"));
}
func_ = module_.CreateFunction(
func.Ident()->getText(), module_.GetContext().Int32());
func.ID()->getText(), module_.GetContext().Int32());
builder_.SetInsertPoint(func_->GetEntry());
storage_map_.clear();
GenBlock(*func.block());
GenBlock(*func.blockStmt());
// 语义正确性主要由 sema 保证,这里只兜底检查 IR 结构是否合法。
VerifyFunctionStructure(*func_);
}

@ -3,7 +3,7 @@
#include "sem/SymbolTable.h"
void SymbolTable::Add(const std::string& name,
SysYParser::VarDeclContext* decl) {
SysYParser::VarDefContext* decl) {
table_[name] = decl;
}
@ -11,7 +11,7 @@ bool SymbolTable::Contains(const std::string& name) const {
return table_.find(name) != table_.end();
}
SysYParser::VarDeclContext* SymbolTable::Lookup(const std::string& name) const {
SysYParser::VarDefContext* SymbolTable::Lookup(const std::string& name) const {
auto it = table_.find(name);
return it == table_.end() ? nullptr : it->second;
}

Loading…
Cancel
Save