diff --git a/doc/Lab2-中间表示生成.md b/doc/Lab2-中间表示生成.md index 4f93b6b..3be8846 100644 --- a/doc/Lab2-中间表示生成.md +++ b/doc/Lab2-中间表示生成.md @@ -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 的正确性: diff --git a/include/irgen/IRGen.h b/include/irgen/IRGen.h index 6f09a69..ae9789e 100644 --- a/include/irgen/IRGen.h +++ b/include/irgen/IRGen.h @@ -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 storage_map_; + std::unordered_map storage_map_; }; std::unique_ptr GenerateIR(SysYParser::CompUnitContext& tree, diff --git a/include/sem/Sema.h b/include/sem/Sema.h index 793dfc6..9ac057b 100644 --- a/include/sem/Sema.h +++ b/include/sem/Sema.h @@ -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 + std::unordered_map var_uses_; }; diff --git a/include/sem/SymbolTable.h b/include/sem/SymbolTable.h index 40cc1df..c9396dd 100644 --- a/include/sem/SymbolTable.h +++ b/include/sem/SymbolTable.h @@ -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 table_; + std::unordered_map table_; }; diff --git a/src/irgen/IRGenDecl.cpp b/src/irgen/IRGenDecl.cpp index ab30713..4e83134 100644 --- a/src/irgen/IRGenDecl.cpp +++ b/src/irgen/IRGenDecl.cpp @@ -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); } diff --git a/src/irgen/IRGenExp.cpp b/src/irgen/IRGenExp.cpp index aac258b..efe8723 100644 --- a/src/irgen/IRGenExp.cpp +++ b/src/irgen/IRGenExp.cpp @@ -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(&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(&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(&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(&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", "暂不支持的表达式形式")); } diff --git a/src/irgen/IRGenFunc.cpp b/src/irgen/IRGenFunc.cpp index 80f7b77..6683183 100644 --- a/src/irgen/IRGenFunc.cpp +++ b/src/irgen/IRGenFunc.cpp @@ -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_); } diff --git a/src/sem/SymbolTable.cpp b/src/sem/SymbolTable.cpp index ec0a721..ffeea89 100644 --- a/src/sem/SymbolTable.cpp +++ b/src/sem/SymbolTable.cpp @@ -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; }