diff --git a/doc/lab2剩余任务分工.md b/doc/lab2剩余任务分工.md new file mode 100644 index 0000000..70e63de --- /dev/null +++ b/doc/lab2剩余任务分工.md @@ -0,0 +1,70 @@ +### 人员 1:基础表达式与赋值支持(lc,已完成) + +- 任务 1.1:支持更多二元运算符(Sub, Mul, Div, Mod) +- 任务 1.2:支持一元运算符(正负号) +- 任务 1.3:支持赋值表达式 +- 任务 1.4:支持逗号分隔的多个变量声明 + +### 人员 2:控制流支持 + +- 任务 2.1:支持 if-else 条件语句 +- 任务 2.2:支持 while 循环语句 +- 任务 2.3:支持 break/continue 语句 +- 任务 2.4:支持比较和逻辑表达式 + +### 人员 3:函数与全局变量支持 + +- 任务 3.1:支持全局变量声明与初始化 +- 任务 3.2:支持函数参数处理 +- 任务 3.3:支持函数调用生成 +- 任务 3.4:支持 const 常量声明 + +
+ +
+ +## 人员 1 完成情况详细说明(更新于 2026-03-30) + +### ✅ 已完成任务 + +人员 1 已完整实现 Lab2 IR 生成的基础功能模块,包括: + +1. **二元运算符**(任务 1.1) + - 实现 `Sub`, `Mul`, `Div`, `Mod` 四种运算符 + - 修改文件:`include/ir/IR.h`, `src/ir/IRBuilder.cpp`, `src/ir/IRPrinter.cpp`, `src/irgen/IRGenExp.cpp` +2. **一元运算符**(任务 1.2) + - 实现正负号运算符(`+`, `-`) + - 新增 `UnaryInst` 类支持一元指令 + - 负号生成 `sub 0, x` 指令(LLVM IR 标准形式) +3. **赋值表达式**(任务 1.3) + - 实现变量赋值语句的 IR 生成 + - 修改文件:`src/irgen/IRGenStmt.cpp` +4. **多变量声明**(任务 1.4) + - 支持逗号分隔的变量声明(如 `int a, b, c;`) + - 支持带初始化的多变量声明(如 `int a = 1, b = 2;`) + +### 🧪 测试验证 + +- **Lab1 语法分析**:✅ 通过(10/11 functional 测试,1 个数组测试超出范围) +- **Lab2 语义分析**:✅ 通过(6 正例 + 4 反例) +- **IR 生成测试**:✅ 通过(7/7 自定义测试用例) + - 测试脚本:`./scripts/test_lab2_ir1.sh` + - 测试用例目录:`test/test_case/irgen_lab1_4/` + +### 📝 代码质量 + +- 所有修改已通过编译测试 +- 未影响原有 Lab1 和 Lab2 Sema 功能 +- 代码风格与项目保持一致 +- 关键函数添加了注释说明 + +### 🔄 协作接口 + +人员 1 的实现为后续任务提供了以下接口: + +- **表达式生成**:`visitAddExp`, `visitMulExp`, `visitUnaryExp` +- **语句生成**:`visitStmt`(支持赋值和 return) +- **变量管理**:`storage_map_` 维护变量名到栈槽位的映射 +- **IR 构建**:`IRBuilder::CreateBinary`, `IRBuilder::CreateNeg`, `IRBuilder::CreateStore` + +后续人员可以在此基础上扩展更复杂的功能(控制流、函数调用等)。 diff --git a/include/ir/IR.h b/include/ir/IR.h index b961192..6af2d96 100644 --- a/include/ir/IR.h +++ b/include/ir/IR.h @@ -152,7 +152,8 @@ class ConstantInt : public ConstantValue { }; // 后续还需要扩展更多指令类型。 -enum class Opcode { Add, Sub, Mul, Alloca, Load, Store, Ret }; +// enum class Opcode { Add, Sub, Mul, Alloca, Load, Store, Ret }; +enum class Opcode { Add, Sub, Mul, Div, Mod, Neg, Alloca, Load, Store, Ret }; // User 是所有“会使用其他 Value 作为输入”的 IR 对象的抽象基类。 // 当前实现中只有 Instruction 继承自 User。 @@ -196,7 +197,14 @@ class BinaryInst : public Instruction { BinaryInst(Opcode op, std::shared_ptr ty, Value* lhs, Value* rhs, std::string name); Value* GetLhs() const; - Value* GetRhs() const; + Value* GetRhs() const; +}; + +class UnaryInst : public Instruction { + public: + UnaryInst(Opcode op, std::shared_ptr ty, Value* operand, + std::string name); + Value* GetUnaryOperand() const; }; class ReturnInst : public Instruction { @@ -300,6 +308,9 @@ class IRBuilder { BinaryInst* CreateBinary(Opcode op, Value* lhs, Value* rhs, const std::string& name); BinaryInst* CreateAdd(Value* lhs, Value* rhs, const std::string& name); + BinaryInst* CreateSub(Value* lhs, Value* rhs, const std::string& name); + BinaryInst* CreateMul(Value* lhs, Value* rhs, const std::string& name); + UnaryInst* CreateNeg(Value* operand, const std::string& name); AllocaInst* CreateAllocaI32(const std::string& name); LoadInst* CreateLoad(Value* ptr, const std::string& name); StoreInst* CreateStore(Value* val, Value* ptr); diff --git a/include/irgen/IRGen.h b/include/irgen/IRGen.h index 231ba90..2ed1c85 100644 --- a/include/irgen/IRGen.h +++ b/include/irgen/IRGen.h @@ -26,16 +26,17 @@ class IRGenImpl final : public SysYBaseVisitor { std::any visitCompUnit(SysYParser::CompUnitContext* ctx) override; std::any visitFuncDef(SysYParser::FuncDefContext* ctx) override; - std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override; + std::any visitBlock(SysYParser::BlockContext* ctx) override; std::any visitBlockItem(SysYParser::BlockItemContext* ctx) override; std::any visitDecl(SysYParser::DeclContext* ctx) override; std::any visitStmt(SysYParser::StmtContext* ctx) override; std::any visitVarDef(SysYParser::VarDefContext* ctx) override; - std::any visitReturnStmt(SysYParser::ReturnStmtContext* ctx) override; - std::any visitParenExp(SysYParser::ParenExpContext* ctx) override; - std::any visitNumberExp(SysYParser::NumberExpContext* ctx) override; - std::any visitVarExp(SysYParser::VarExpContext* ctx) override; - std::any visitAdditiveExp(SysYParser::AdditiveExpContext* ctx) override; + std::any visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) override; + std::any visitNumber(SysYParser::NumberContext* ctx) override; + std::any visitLVal(SysYParser::LValContext* ctx) override; + std::any visitAddExp(SysYParser::AddExpContext* ctx) override; + std::any visitMulExp(SysYParser::MulExpContext* ctx) override; + std::any visitUnaryExp(SysYParser::UnaryExpContext* ctx) override; private: enum class BlockFlow { @@ -50,8 +51,8 @@ class IRGenImpl final : public SysYBaseVisitor { const SemanticContext& sema_; ir::Function* func_; ir::IRBuilder builder_; - // 名称绑定由 Sema 负责;IRGen 只维护“声明 -> 存储槽位”的代码生成状态。 - std::unordered_map storage_map_; + // 名称绑定由 Sema 负责;IRGen 只维护"变量名 -> 存储槽位"的代码生成状态。 + std::unordered_map storage_map_; }; std::unique_ptr GenerateIR(SysYParser::CompUnitContext& tree, diff --git a/scripts/test_lab2_ir1.sh b/scripts/test_lab2_ir1.sh new file mode 100755 index 0000000..65f9c3f --- /dev/null +++ b/scripts/test_lab2_ir1.sh @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# 测试 Lab2 IR 生成 - 人员 1 的任务 +# 测试内容: +# - 任务 1.1: 支持更多二元运算符(Sub, Mul, Div, Mod) +# - 任务 1.2: 支持一元运算符(正负号) +# - 任务 1.3: 支持赋值表达式 +# - 任务 1.4: 支持逗号分隔的多个变量声明 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +COMPILER="$PROJECT_ROOT/build/bin/compiler" +TEST_DIR="$PROJECT_ROOT/test/test_case/irgen_lab1_4" +RESULT_DIR="$PROJECT_ROOT/test/test_result/lab2_ir1" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo "=========================================" +echo "Lab2 IR 生成测试 - 部分任务验证" +echo "=========================================" +echo "" + +# 检查编译器是否存在 +if [[ ! -x "$COMPILER" ]]; then + echo -e "${RED}错误:编译器不存在或不可执行:$COMPILER${NC}" + echo "请先运行:cmake --build build" + exit 1 +fi + +# 检查测试目录是否存在 +if [[ ! -d "$TEST_DIR" ]]; then + echo -e "${RED}错误:测试目录不存在:$TEST_DIR${NC}" + exit 1 +fi + +# 创建结果目录 +mkdir -p "$RESULT_DIR" + +# 统计 +total=0 +passed=0 +failed=0 + +# 测试函数 +run_test() { + local input=$1 + local basename=$(basename "$input" .sy) + local expected_out="$TEST_DIR/$basename.out" + local actual_out="$RESULT_DIR/$basename.actual.out" + local ll_file="$RESULT_DIR/$basename.ll" + + ((total++)) || true + + echo -n "测试 $basename ... " + + # 生成 IR + if ! "$COMPILER" --emit-ir "$input" > "$ll_file" 2>&1; then + echo -e "${RED}IR 生成失败${NC}" + ((failed++)) || true + return 1 + fi + + # 如果需要运行并比对输出 + if [[ -f "$expected_out" ]]; then + # 编译并运行 + local exe_file="$RESULT_DIR/$basename" + if ! llc -O0 -filetype=obj "$ll_file" -o "$RESULT_DIR/$basename.o" 2>/dev/null; then + echo -e "${YELLOW}LLVM 编译失败 (llc)${NC}" + cat "$ll_file" + ((failed++)) || true + return 1 + fi + + if ! clang "$RESULT_DIR/$basename.o" -o "$exe_file" 2>/dev/null; then + echo -e "${YELLOW}链接失败 (clang)${NC}" + ((failed++)) || true + return 1 + fi + + # 运行程序,捕获返回值(低 8 位) + local exit_code=0 + "$exe_file" > "$actual_out" 2>&1 || exit_code=$? + + # 处理返回值(LLVM/AArch64 返回的是 8 位无符号整数) + if [[ $exit_code -gt 127 ]]; then + # 转换为有符号整数 + exit_code=$((exit_code - 256)) + fi + echo "$exit_code" > "$actual_out" + + # 比对输出 + if diff -q "$expected_out" "$actual_out" > /dev/null 2>&1; then + echo -e "${GREEN}✓ 通过${NC}" + ((passed++)) || true + return 0 + else + echo -e "${RED}✗ 输出不匹配${NC}" + echo " 期望:$(cat "$expected_out")" + echo " 实际:$(cat "$actual_out")" + ((failed++)) || true + return 1 + fi + else + # 没有期望输出,只检查 IR 生成 + echo -e "${GREEN}✓ IR 生成成功${NC}" + ((passed++)) || true + return 0 + fi +} + +# 查找所有测试用例 +test_files=() +while IFS= read -r -d '' file; do + test_files+=("$file") +done < <(find "$TEST_DIR" -name "*.sy" -type f -print0 | sort -z) + +if [[ ${#test_files[@]} -eq 0 ]]; then + echo -e "${RED}未找到测试用例:$TEST_DIR${NC}" + exit 1 +fi + +echo "找到 ${#test_files[@]} 个测试用例" +echo "" + +# 运行所有测试 +for test_file in "${test_files[@]}"; do + run_test "$test_file" || true +done + +# 输出统计 +echo "" +echo "=========================================" +echo "测试结果统计" +echo "=========================================" +echo -e "总数:$total" +echo -e "通过:${GREEN}$passed${NC}" +echo -e "失败:${RED}$failed${NC}" +echo "" + +if [[ $failed -eq 0 ]]; then + echo -e "${GREEN}✓ 所有测试通过!${NC}" + echo "" + echo "测试覆盖:" + echo " ✓ 任务 1.1: 二元运算符(Sub, Mul, Div, Mod)" + echo " ✓ 任务 1.2: 一元运算符(正负号)" + echo " ✓ 任务 1.3: 赋值表达式" + echo " ✓ 任务 1.4: 逗号分隔的多变量声明" + exit 0 +else + echo -e "${RED}✗ 有 $failed 个测试失败${NC}" + exit 1 +fi diff --git a/scripts/test_lab2_sema.sh b/scripts/test_lab2_sema.sh old mode 100644 new mode 100755 diff --git a/src/ir/IRBuilder.cpp b/src/ir/IRBuilder.cpp index 90f03c4..54cc5d2 100644 --- a/src/ir/IRBuilder.cpp +++ b/src/ir/IRBuilder.cpp @@ -86,4 +86,22 @@ ReturnInst* IRBuilder::CreateRet(Value* v) { return insert_block_->Append(Type::GetVoidType(), v); } +BinaryInst* IRBuilder::CreateSub(Value* lhs, Value* rhs, const std::string& name) { + return CreateBinary(Opcode::Sub, lhs, rhs, name); +} + +BinaryInst* IRBuilder::CreateMul(Value* lhs, Value* rhs, const std::string& name) { + return CreateBinary(Opcode::Mul, lhs, rhs, name); +} + +UnaryInst* IRBuilder::CreateNeg(Value* operand, const std::string& name) { + if (!insert_block_) { + throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点")); + } + if (!operand) { + throw std::runtime_error(FormatError("ir", "IRBuilder::CreateNeg 缺少操作数")); + } + return insert_block_->Append(Opcode::Neg, Type::GetInt32Type(), operand, name); +} + } // namespace ir diff --git a/src/ir/IRPrinter.cpp b/src/ir/IRPrinter.cpp index 30efbb6..3716751 100644 --- a/src/ir/IRPrinter.cpp +++ b/src/ir/IRPrinter.cpp @@ -32,6 +32,12 @@ static const char* OpcodeToString(Opcode op) { return "sub"; case Opcode::Mul: return "mul"; + case Opcode::Div: + return "sdiv"; + case Opcode::Mod: + return "srem"; + case Opcode::Neg: + return "neg"; case Opcode::Alloca: return "alloca"; case Opcode::Load: @@ -65,7 +71,9 @@ void IRPrinter::Print(const Module& module, std::ostream& os) { switch (inst->GetOpcode()) { case Opcode::Add: case Opcode::Sub: - case Opcode::Mul: { + case Opcode::Mul: + case Opcode::Div: + case Opcode::Mod: { auto* bin = static_cast(inst); os << " " << bin->GetName() << " = " << OpcodeToString(bin->GetOpcode()) << " " @@ -74,6 +82,14 @@ void IRPrinter::Print(const Module& module, std::ostream& os) { << ValueToString(bin->GetRhs()) << "\n"; break; } + case Opcode::Neg: { + auto* unary = static_cast(inst); + os << " " << unary->GetName() << " = " + << OpcodeToString(unary->GetOpcode()) << " " + << TypeToString(*unary->GetUnaryOperand()->GetType()) << " " + << ValueToString(unary->GetUnaryOperand()) << "\n"; + break; + } case Opcode::Alloca: { auto* alloca = static_cast(inst); os << " " << alloca->GetName() << " = alloca i32\n"; diff --git a/src/ir/Instruction.cpp b/src/ir/Instruction.cpp index 7928716..199b123 100644 --- a/src/ir/Instruction.cpp +++ b/src/ir/Instruction.cpp @@ -61,8 +61,9 @@ void Instruction::SetParent(BasicBlock* parent) { parent_ = parent; } BinaryInst::BinaryInst(Opcode op, std::shared_ptr ty, Value* lhs, Value* rhs, std::string name) : Instruction(op, std::move(ty), std::move(name)) { - if (op != Opcode::Add) { - throw std::runtime_error(FormatError("ir", "BinaryInst 当前只支持 Add")); + if (op != Opcode::Add && op != Opcode::Sub && op != Opcode::Mul && + op != Opcode::Div && op != Opcode::Mod) { + throw std::runtime_error(FormatError("ir", "BinaryInst 不支持的操作码")); } if (!lhs || !rhs) { throw std::runtime_error(FormatError("ir", "BinaryInst 缺少操作数")); @@ -85,6 +86,29 @@ Value* BinaryInst::GetLhs() const { return GetOperand(0); } Value* BinaryInst::GetRhs() const { return GetOperand(1); } +UnaryInst::UnaryInst(Opcode op, std::shared_ptr ty, Value* operand, + std::string name) + : Instruction(op, std::move(ty), std::move(name)) { + if (op != Opcode::Neg) { + throw std::runtime_error(FormatError("ir", "UnaryInst 不支持的操作码")); + } + if (!operand) { + throw std::runtime_error(FormatError("ir", "UnaryInst 缺少操作数")); + } + if (!type_ || !operand->GetType()) { + throw std::runtime_error(FormatError("ir", "UnaryInst 缺少类型信息")); + } + if (type_->GetKind() != operand->GetType()->GetKind()) { + throw std::runtime_error(FormatError("ir", "UnaryInst 类型不匹配")); + } + if (!type_->IsInt32()) { + throw std::runtime_error(FormatError("ir", "UnaryInst 当前只支持 i32")); + } + AddOperand(operand); +} + +Value* UnaryInst::GetUnaryOperand() const { return GetOperand(0); } + ReturnInst::ReturnInst(std::shared_ptr void_ty, Value* val) : Instruction(Opcode::Ret, std::move(void_ty), "") { if (!val) { diff --git a/src/irgen/IRGenDecl.cpp b/src/irgen/IRGenDecl.cpp index 0eb62ae..df7ccf9 100644 --- a/src/irgen/IRGenDecl.cpp +++ b/src/irgen/IRGenDecl.cpp @@ -6,18 +6,7 @@ #include "ir/IR.h" #include "utils/Log.h" -namespace { - -std::string GetLValueName(SysYParser::LValueContext& lvalue) { - if (!lvalue.ID()) { - throw std::runtime_error(FormatError("irgen", "非法左值")); - } - return lvalue.ID()->getText(); -} - -} // namespace - -std::any IRGenImpl::visitBlockStmt(SysYParser::BlockStmtContext* ctx) { +std::any IRGenImpl::visitBlock(SysYParser::BlockContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少语句块")); } @@ -63,14 +52,20 @@ std::any IRGenImpl::visitDecl(SysYParser::DeclContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少变量声明")); } - if (!ctx->btype() || !ctx->btype()->INT()) { + // 当前语法中 decl 包含 constDecl 或 varDecl,这里只支持 varDecl + auto* var_decl = ctx->varDecl(); + if (!var_decl) { + throw std::runtime_error(FormatError("irgen", "当前仅支持变量声明")); + } + if (!var_decl->bType() || !var_decl->bType()->INT()) { throw std::runtime_error(FormatError("irgen", "当前仅支持局部 int 变量声明")); } - auto* var_def = ctx->varDef(); - if (!var_def) { - throw std::runtime_error(FormatError("irgen", "非法变量声明")); + // 遍历所有 varDef + for (auto* var_def : var_decl->varDef()) { + if (var_def) { + var_def->accept(this); + } } - var_def->accept(this); return {}; } @@ -83,22 +78,26 @@ std::any IRGenImpl::visitVarDef(SysYParser::VarDefContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少变量定义")); } - if (!ctx->lValue()) { + if (!ctx->ID()) { throw std::runtime_error(FormatError("irgen", "变量声明缺少名称")); } - GetLValueName(*ctx->lValue()); - if (storage_map_.find(ctx) != storage_map_.end()) { + // 暂不支持数组声明(constIndex) + if (!ctx->constIndex().empty()) { + throw std::runtime_error(FormatError("irgen", "暂不支持数组声明")); + } + std::string var_name = ctx->ID()->getText(); + if (storage_map_.find(var_name) != storage_map_.end()) { throw std::runtime_error(FormatError("irgen", "声明重复生成存储槽位")); } auto* slot = builder_.CreateAllocaI32(module_.GetContext().NextTemp()); - storage_map_[ctx] = slot; + storage_map_[var_name] = slot; ir::Value* init = nullptr; - if (auto* init_value = ctx->initValue()) { - if (!init_value->exp()) { + if (auto* init_val = ctx->initVal()) { + if (!init_val->exp()) { throw std::runtime_error(FormatError("irgen", "当前不支持聚合初始化")); } - init = EvalExpr(*init_value->exp()); + init = EvalExpr(*init_val->exp()); } else { init = builder_.CreateConstInt(0); } diff --git a/src/irgen/IRGenExp.cpp b/src/irgen/IRGenExp.cpp index cf4797c..55c3cc2 100644 --- a/src/irgen/IRGenExp.cpp +++ b/src/irgen/IRGenExp.cpp @@ -25,20 +25,51 @@ ir::Value* IRGenImpl::EvalExpr(SysYParser::ExpContext& expr) { } -std::any IRGenImpl::visitParenExp(SysYParser::ParenExpContext* ctx) { - if (!ctx || !ctx->exp()) { - throw std::runtime_error(FormatError("irgen", "非法括号表达式")); +std::any IRGenImpl::visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) { + if (!ctx) { + throw std::runtime_error(FormatError("irgen", "非法基本表达式")); } - return EvalExpr(*ctx->exp()); + // 处理括号表达式:LPAREN exp RPAREN + if (ctx->exp()) { + return EvalExpr(*ctx->exp()); + } + // 处理 lVal(变量使用)- 交给 visitLVal 处理 + if (ctx->lVal()) { + // 直接在这里处理变量读取,避免 accept 调用可能导致的问题 + auto* lval_ctx = ctx->lVal(); + if (!lval_ctx || !lval_ctx->ID()) { + throw std::runtime_error(FormatError("irgen", "当前仅支持普通整型变量")); + } + const auto* decl = sema_.ResolveObjectUse(lval_ctx); + if (!decl) { + throw std::runtime_error( + FormatError("irgen", + "变量使用缺少语义绑定:" + lval_ctx->ID()->getText())); + } + std::string var_name = lval_ctx->ID()->getText(); + auto it = storage_map_.find(var_name); + if (it == storage_map_.end()) { + throw std::runtime_error( + FormatError("irgen", + "变量声明缺少存储槽位:" + var_name)); + } + return static_cast( + builder_.CreateLoad(it->second, module_.GetContext().NextTemp())); + } + // 处理 number + if (ctx->number()) { + return ctx->number()->accept(this); + } + throw std::runtime_error(FormatError("irgen", "不支持的基本表达式类型")); } -std::any IRGenImpl::visitNumberExp(SysYParser::NumberExpContext* ctx) { - if (!ctx || !ctx->number() || !ctx->number()->ILITERAL()) { +std::any IRGenImpl::visitNumber(SysYParser::NumberContext* ctx) { + if (!ctx || !ctx->intConst()) { throw std::runtime_error(FormatError("irgen", "当前仅支持整数字面量")); } return static_cast( - builder_.CreateConstInt(std::stoi(ctx->number()->getText()))); + builder_.CreateConstInt(std::stoi(ctx->intConst()->getText()))); } // 变量使用的处理流程: @@ -47,34 +78,127 @@ std::any IRGenImpl::visitNumberExp(SysYParser::NumberExpContext* ctx) { // 3. 最后生成 load,把内存中的值读出来。 // // 因此当前 IRGen 自己不再做名字查找,而是直接消费 Sema 的绑定结果。 -std::any IRGenImpl::visitVarExp(SysYParser::VarExpContext* ctx) { - if (!ctx || !ctx->var() || !ctx->var()->ID()) { +std::any IRGenImpl::visitLVal(SysYParser::LValContext* ctx) { + if (!ctx || !ctx->ID()) { throw std::runtime_error(FormatError("irgen", "当前仅支持普通整型变量")); } - auto* decl = sema_.ResolveVarUse(ctx->var()); + const auto* decl = sema_.ResolveObjectUse(ctx); if (!decl) { throw std::runtime_error( FormatError("irgen", - "变量使用缺少语义绑定: " + ctx->var()->ID()->getText())); + "变量使用缺少语义绑定:" + ctx->ID()->getText())); } - auto it = storage_map_.find(decl); + // 使用变量名查找存储槽位 + std::string var_name = ctx->ID()->getText(); + auto it = storage_map_.find(var_name); if (it == storage_map_.end()) { throw std::runtime_error( FormatError("irgen", - "变量声明缺少存储槽位: " + ctx->var()->ID()->getText())); + "变量声明缺少存储槽位:" + var_name)); } return static_cast( builder_.CreateLoad(it->second, module_.GetContext().NextTemp())); } -std::any IRGenImpl::visitAdditiveExp(SysYParser::AdditiveExpContext* ctx) { - if (!ctx || !ctx->exp(0) || !ctx->exp(1)) { - throw std::runtime_error(FormatError("irgen", "非法加法表达式")); +std::any IRGenImpl::visitAddExp(SysYParser::AddExpContext* ctx) { + if (!ctx) { + throw std::runtime_error(FormatError("irgen", "非法加减法表达式")); + } + + // 如果是 mulExp 直接返回(addExp : mulExp) + if (ctx->mulExp() && ctx->addExp() == nullptr) { + return ctx->mulExp()->accept(this); + } + + // 处理 addExp op mulExp 的递归形式 + if (!ctx->addExp() || !ctx->mulExp()) { + throw std::runtime_error(FormatError("irgen", "非法加减法表达式结构")); } - ir::Value* lhs = EvalExpr(*ctx->exp(0)); - ir::Value* rhs = EvalExpr(*ctx->exp(1)); + + ir::Value* lhs = std::any_cast(ctx->addExp()->accept(this)); + ir::Value* rhs = std::any_cast(ctx->mulExp()->accept(this)); + + ir::Opcode op = ir::Opcode::Add; + if (ctx->ADD()) { + op = ir::Opcode::Add; + } else if (ctx->SUB()) { + op = ir::Opcode::Sub; + } else { + throw std::runtime_error(FormatError("irgen", "未知的加减运算符")); + } + return static_cast( - builder_.CreateBinary(ir::Opcode::Add, lhs, rhs, - module_.GetContext().NextTemp())); + builder_.CreateBinary(op, lhs, rhs, module_.GetContext().NextTemp())); } + + +std::any IRGenImpl::visitUnaryExp(SysYParser::UnaryExpContext* ctx) { + if (!ctx) { + throw std::runtime_error(FormatError("irgen", "非法一元表达式")); + } + + // 如果是 primaryExp 直接返回(unaryExp : primaryExp) + if (ctx->primaryExp()) { + return ctx->primaryExp()->accept(this); + } + + // 处理函数调用(unaryExp : ID LPAREN funcRParams? RPAREN) + // 当前暂不支持,留给后续扩展 + if (ctx->ID()) { + throw std::runtime_error(FormatError("irgen", "暂不支持函数调用")); + } + + // 处理一元运算符(unaryExp : addUnaryOp unaryExp) + if (ctx->addUnaryOp() && ctx->unaryExp()) { + ir::Value* operand = std::any_cast(ctx->unaryExp()->accept(this)); + + // 判断是正号还是负号 + if (ctx->addUnaryOp()->SUB()) { + // 负号:生成 sub 0, operand(LLVM IR 中没有 neg 指令) + ir::Value* zero = builder_.CreateConstInt(0); + return static_cast( + builder_.CreateSub(zero, operand, module_.GetContext().NextTemp())); + } else if (ctx->addUnaryOp()->ADD()) { + // 正号:直接返回操作数(+x 等价于 x) + return operand; + } else { + throw std::runtime_error(FormatError("irgen", "未知的一元运算符")); + } + } + + throw std::runtime_error(FormatError("irgen", "不支持的一元表达式类型")); +} + +std::any IRGenImpl::visitMulExp(SysYParser::MulExpContext* ctx) { + if (!ctx) { + throw std::runtime_error(FormatError("irgen", "非法乘除法表达式")); + } + + // 如果是 unaryExp 直接返回(mulExp : unaryExp) + if (ctx->unaryExp() && ctx->mulExp() == nullptr) { + return ctx->unaryExp()->accept(this); + } + + // 处理 mulExp op unaryExp 的递归形式 + if (!ctx->mulExp() || !ctx->unaryExp()) { + throw std::runtime_error(FormatError("irgen", "非法乘除法表达式结构")); + } + + ir::Value* lhs = std::any_cast(ctx->mulExp()->accept(this)); + ir::Value* rhs = std::any_cast(ctx->unaryExp()->accept(this)); + + ir::Opcode op = ir::Opcode::Mul; + if (ctx->MUL()) { + op = ir::Opcode::Mul; + } else if (ctx->DIV()) { + op = ir::Opcode::Div; + } else if (ctx->MOD()) { + op = ir::Opcode::Mod; + } else { + throw std::runtime_error(FormatError("irgen", "未知的乘除运算符")); + } + + return static_cast( + builder_.CreateBinary(op, lhs, rhs, module_.GetContext().NextTemp())); +} \ No newline at end of file diff --git a/src/irgen/IRGenFunc.cpp b/src/irgen/IRGenFunc.cpp index 4912d03..4ee5b3e 100644 --- a/src/irgen/IRGenFunc.cpp +++ b/src/irgen/IRGenFunc.cpp @@ -29,7 +29,7 @@ IRGenImpl::IRGenImpl(ir::Module& module, const SemanticContext& sema) // 编译单元的 IR 生成当前只实现了最小功能: // - Module 已在 GenerateIR 中创建,这里只负责继续生成其中的内容; -// - 当前会读取编译单元中的函数定义,并交给 visitFuncDef 生成函数 IR; +// - 当前会读取编译单元中的 topLevelItem,找到 funcDef 后生成函数 IR; // // 当前还没有实现: // - 多个函数定义的遍历与生成; @@ -38,12 +38,15 @@ std::any IRGenImpl::visitCompUnit(SysYParser::CompUnitContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少编译单元")); } - auto* func = ctx->funcDef(); - if (!func) { - throw std::runtime_error(FormatError("irgen", "缺少函数定义")); + // 遍历所有 topLevelItem,找到 funcDef + for (auto* item : ctx->topLevelItem()) { + if (item && item->funcDef()) { + item->funcDef()->accept(this); + // 当前只支持单个函数,找到第一个后就返回 + return {}; + } } - func->accept(this); - return {}; + throw std::runtime_error(FormatError("irgen", "缺少函数定义")); } // 函数 IR 生成当前实现了: @@ -61,12 +64,11 @@ std::any IRGenImpl::visitCompUnit(SysYParser::CompUnitContext* ctx) { // - 入口块中的参数初始化逻辑。 // ... -// 因此这里目前只支持最小的“无参 int 函数”生成。 std::any IRGenImpl::visitFuncDef(SysYParser::FuncDefContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少函数定义")); } - if (!ctx->blockStmt()) { + if (!ctx->block()) { throw std::runtime_error(FormatError("irgen", "函数体为空")); } if (!ctx->ID()) { @@ -80,7 +82,7 @@ std::any IRGenImpl::visitFuncDef(SysYParser::FuncDefContext* ctx) { builder_.SetInsertPoint(func_->GetEntry()); storage_map_.clear(); - ctx->blockStmt()->accept(this); + ctx->block()->accept(this); // 语义正确性主要由 sema 保证,这里只兜底检查 IR 结构是否合法。 VerifyFunctionStructure(*func_); return {}; diff --git a/src/irgen/IRGenStmt.cpp b/src/irgen/IRGenStmt.cpp index 751550c..de6a857 100644 --- a/src/irgen/IRGenStmt.cpp +++ b/src/irgen/IRGenStmt.cpp @@ -9,9 +9,9 @@ // 语句生成当前只实现了最小子集。 // 目前支持: // - return ; +// - 赋值语句:lVal = exp; // // 还未支持: -// - 赋值语句 // - if / while 等控制流 // - 空语句、块语句嵌套分发之外的更多语句形态 @@ -19,21 +19,51 @@ std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) { if (!ctx) { throw std::runtime_error(FormatError("irgen", "缺少语句")); } - if (ctx->returnStmt()) { - return ctx->returnStmt()->accept(this); + + // 检查是否是赋值语句:lVal ASSIGN exp SEMICOLON + if (ctx->lVal() && ctx->ASSIGN()) { + if (!ctx->exp()) { + throw std::runtime_error(FormatError("irgen", "赋值语句缺少表达式")); + } + + // 1. 计算右值表达式的值 + ir::Value* rhs = EvalExpr(*ctx->exp()); + + // 2. 找到左值变量对应的存储槽位 + auto* lval_ctx = ctx->lVal(); + if (!lval_ctx || !lval_ctx->ID()) { + throw std::runtime_error(FormatError("irgen", "当前仅支持普通整型变量赋值")); + } + + const auto* decl = sema_.ResolveObjectUse(lval_ctx); + if (!decl) { + throw std::runtime_error( + FormatError("irgen", + "变量使用缺少语义绑定:" + lval_ctx->ID()->getText())); + } + + std::string var_name = lval_ctx->ID()->getText(); + auto it = storage_map_.find(var_name); + if (it == storage_map_.end()) { + throw std::runtime_error( + FormatError("irgen", + "变量声明缺少存储槽位:" + var_name)); + } + + // 3. 生成 store 指令 + builder_.CreateStore(rhs, it->second); + return BlockFlow::Continue; } - throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型")); -} - - -std::any IRGenImpl::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) { - if (!ctx) { - throw std::runtime_error(FormatError("irgen", "缺少 return 语句")); - } - if (!ctx->exp()) { - throw std::runtime_error(FormatError("irgen", "return 缺少表达式")); + + // 检查是否是 return 语句:RETURN exp? SEMICOLON + if (ctx->RETURN()) { + if (!ctx->exp()) { + throw std::runtime_error(FormatError("irgen", "return 缺少表达式")); + } + ir::Value* v = EvalExpr(*ctx->exp()); + builder_.CreateRet(v); + return BlockFlow::Terminated; } - ir::Value* v = EvalExpr(*ctx->exp()); - builder_.CreateRet(v); - return BlockFlow::Terminated; + + throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型")); } diff --git a/src/sem/Sema.cpp b/src/sem/Sema.cpp index f0b49e5..fc73f9d 100644 --- a/src/sem/Sema.cpp +++ b/src/sem/Sema.cpp @@ -434,9 +434,11 @@ class SemaVisitor final : public SysYBaseVisitor { if (!ctx) { ThrowSemaError(ctx, "非法乘法表达式"); } - if (ctx->unaryExp()) { + // 如果是 mulExp : unaryExp 形式(没有 MUL/DIV/MOD token),直接处理 unaryExp + if (!ctx->MUL() && !ctx->DIV() && !ctx->MOD()) { return EvalExpr(*ctx->unaryExp()); } + // 否则是 mulExp MUL/DIV/MOD unaryExp 形式 ExprInfo lhs = EvalExpr(*ctx->mulExp()); ExprInfo rhs = EvalExpr(*ctx->unaryExp()); return EvalArithmetic(*ctx, lhs, rhs, ctx->MUL() ? '*' : (ctx->DIV() ? '/' : '%')); @@ -446,9 +448,11 @@ class SemaVisitor final : public SysYBaseVisitor { if (!ctx) { ThrowSemaError(ctx, "非法加法表达式"); } - if (ctx->mulExp()) { + // 如果是 addExp : mulExp 形式(没有 ADD/SUB token),直接处理 mulExp + if (!ctx->ADD() && !ctx->SUB()) { return EvalExpr(*ctx->mulExp()); } + // 否则是 addExp ADD/SUB mulExp 形式 ExprInfo lhs = EvalExpr(*ctx->addExp()); ExprInfo rhs = EvalExpr(*ctx->mulExp()); return EvalArithmetic(*ctx, lhs, rhs, ctx->ADD() ? '+' : '-'); @@ -544,7 +548,7 @@ class SemaVisitor final : public SysYBaseVisitor { const std::string name = ctx.ID()->getText(); const ObjectBinding* symbol = symbols_.Lookup(name); if (!symbol) { - ThrowSemaError(&ctx, "使用了未声明的标识符: " + name); + ThrowSemaError(&ctx, "使用了未声明的标识符:" + name); } sema_.BindObjectUse(&ctx, *symbol); diff --git a/test/test_case/irgen_lab1_4/01_simple_add.out b/test/test_case/irgen_lab1_4/01_simple_add.out new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/test/test_case/irgen_lab1_4/01_simple_add.out @@ -0,0 +1 @@ +3 diff --git a/test/test_case/irgen_lab1_4/01_simple_add.sy b/test/test_case/irgen_lab1_4/01_simple_add.sy new file mode 100644 index 0000000..8b5f091 --- /dev/null +++ b/test/test_case/irgen_lab1_4/01_simple_add.sy @@ -0,0 +1,6 @@ +// 测试:简单加法 +int main() { + int a = 1; + int b = 2; + return a + b; +} diff --git a/test/test_case/irgen_lab1_4/02_sub_mul.out b/test/test_case/irgen_lab1_4/02_sub_mul.out new file mode 100644 index 0000000..81b5c5d --- /dev/null +++ b/test/test_case/irgen_lab1_4/02_sub_mul.out @@ -0,0 +1 @@ +37 diff --git a/test/test_case/irgen_lab1_4/02_sub_mul.sy b/test/test_case/irgen_lab1_4/02_sub_mul.sy new file mode 100644 index 0000000..8ddeca9 --- /dev/null +++ b/test/test_case/irgen_lab1_4/02_sub_mul.sy @@ -0,0 +1,8 @@ +// 测试:减法和乘法 +int main() { + int a = 10; + int b = 3; + int c = a - b; + int d = a * b; + return c + d; +} diff --git a/test/test_case/irgen_lab1_4/03_div_mod.out b/test/test_case/irgen_lab1_4/03_div_mod.out new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/test/test_case/irgen_lab1_4/03_div_mod.out @@ -0,0 +1 @@ +5 diff --git a/test/test_case/irgen_lab1_4/03_div_mod.sy b/test/test_case/irgen_lab1_4/03_div_mod.sy new file mode 100644 index 0000000..beefc9e --- /dev/null +++ b/test/test_case/irgen_lab1_4/03_div_mod.sy @@ -0,0 +1,8 @@ +// 测试:除法和取模 +int main() { + int a = 20; + int b = 6; + int c = a / b; + int d = a % b; + return c + d; +} diff --git a/test/test_case/irgen_lab1_4/04_unary.out b/test/test_case/irgen_lab1_4/04_unary.out new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/test/test_case/irgen_lab1_4/04_unary.out @@ -0,0 +1 @@ +5 diff --git a/test/test_case/irgen_lab1_4/04_unary.sy b/test/test_case/irgen_lab1_4/04_unary.sy new file mode 100644 index 0000000..c36c5e1 --- /dev/null +++ b/test/test_case/irgen_lab1_4/04_unary.sy @@ -0,0 +1,7 @@ +// 测试:一元运算符(正负号) +int main() { + int a = 5; + int b = -a; + int c = +10; + return b + c; +} diff --git a/test/test_case/irgen_lab1_4/05_assign.out b/test/test_case/irgen_lab1_4/05_assign.out new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/test/test_case/irgen_lab1_4/05_assign.out @@ -0,0 +1 @@ +20 diff --git a/test/test_case/irgen_lab1_4/05_assign.sy b/test/test_case/irgen_lab1_4/05_assign.sy new file mode 100644 index 0000000..5e1eae2 --- /dev/null +++ b/test/test_case/irgen_lab1_4/05_assign.sy @@ -0,0 +1,7 @@ +// 测试:赋值表达式 +int main() { + int a = 10; + int b = 20; + a = b; + return a; +} diff --git a/test/test_case/irgen_lab1_4/06_multi_decl.out b/test/test_case/irgen_lab1_4/06_multi_decl.out new file mode 100644 index 0000000..1e8b314 --- /dev/null +++ b/test/test_case/irgen_lab1_4/06_multi_decl.out @@ -0,0 +1 @@ +6 diff --git a/test/test_case/irgen_lab1_4/06_multi_decl.sy b/test/test_case/irgen_lab1_4/06_multi_decl.sy new file mode 100644 index 0000000..fa95159 --- /dev/null +++ b/test/test_case/irgen_lab1_4/06_multi_decl.sy @@ -0,0 +1,5 @@ +// 测试:逗号分隔的多变量声明 +int main() { + int a = 1, b = 2, c = 3; + return a + b + c; +} diff --git a/test/test_case/irgen_lab1_4/07_comprehensive.out b/test/test_case/irgen_lab1_4/07_comprehensive.out new file mode 100644 index 0000000..81b5c5d --- /dev/null +++ b/test/test_case/irgen_lab1_4/07_comprehensive.out @@ -0,0 +1 @@ +37 diff --git a/test/test_case/irgen_lab1_4/07_comprehensive.sy b/test/test_case/irgen_lab1_4/07_comprehensive.sy new file mode 100644 index 0000000..753aeae --- /dev/null +++ b/test/test_case/irgen_lab1_4/07_comprehensive.sy @@ -0,0 +1,14 @@ +// 测试:综合测试(所有功能) +int main() { + int a = 10, b = 5; + int c = a + b; + int d = a - b; + int e = a * 2; + int f = a / b; + int g = a % b; + int h = -c; + int i = +d; + a = b + c; + b = d + e; + return a + b + f + g + h + i; +}