diff --git a/scripts/test_lab2.sh b/scripts/test_lab2.sh index 91d831b..d633286 100644 --- a/scripts/test_lab2.sh +++ b/scripts/test_lab2.sh @@ -7,12 +7,12 @@ set -euo pipefail # bash scripts/test_lab2.sh # Optional env vars: # COMPILER=./build/bin/compiler -# CASE_DIR=test/test_case/functional +# CASE_DIR=test/test_case # OUT_DIR=test/test_result/lab2_ir # LOG_FILE=test/test_result/lab2_test.log COMPILER="${COMPILER:-./build/bin/compiler}" -CASE_DIR="${CASE_DIR:-test/test_case/functional}" +CASE_DIR="${CASE_DIR:-test/test_case}" OUT_DIR="${OUT_DIR:-test/test_result/lab2_ir}" LOG_FILE="${LOG_FILE:-test/test_result/lab2_test.log}" VERIFY_SCRIPT="./scripts/verify_ir.sh" @@ -67,7 +67,10 @@ echo "out dir : $OUT_DIR" | tee -a "$LOG_FILE" echo "[Step 1] single sample check: simple_add.sy" | tee -a "$LOG_FILE" sample_input="$(find "$CASE_DIR" -type f -name "simple_add.sy" -print -quit)" if [[ -z "$sample_input" ]]; then - echo "single sample: FAIL (simple_add.sy not found under $CASE_DIR)" | tee -a "$LOG_FILE" + sample_input="$(find "$CASE_DIR" -type f -name "*.sy" | sort | head -n 1)" +fi +if [[ -z "$sample_input" ]]; then + echo "single sample: FAIL (no .sy case found under $CASE_DIR)" | tee -a "$LOG_FILE" echo "stop here. see log: $LOG_FILE" >&2 exit 1 fi @@ -79,7 +82,7 @@ else exit 1 fi -echo "[Step 2] full functional regression" | tee -a "$LOG_FILE" +echo "[Step 2] full Lab2 regression" | tee -a "$LOG_FILE" pass=0 fail=0 @@ -117,5 +120,5 @@ if [[ $fail -gt 0 ]]; then exit 1 fi -echo "All functional cases passed. Lab2 target (functional regression) is met." | tee -a "$LOG_FILE" +echo "All Lab2 cases passed. Lab2 target regression is met." | tee -a "$LOG_FILE" echo "see details in $LOG_FILE" diff --git a/scripts/verify_ir.sh b/scripts/verify_ir.sh index 6600198..4d72ad1 100755 --- a/scripts/verify_ir.sh +++ b/scripts/verify_ir.sh @@ -47,20 +47,28 @@ expected_file="$input_dir/$stem.out" echo "IR 已生成: $out_file" if [[ "$run_exec" == true ]]; then - if ! command -v llc >/dev/null 2>&1; then - echo "未找到 llc,无法运行 IR。请安装 LLVM。" >&2 - exit 1 - fi if ! command -v clang >/dev/null 2>&1; then - echo "未找到 clang,无法链接可执行文件。请安装 LLVM/Clang。" >&2 + echo "未找到 clang,无法编译可执行文件。请安装 LLVM/Clang。" >&2 exit 1 fi - obj="$out_dir/$stem.o" exe="$out_dir/$stem" stdout_file="$out_dir/$stem.stdout" actual_file="$out_dir/$stem.actual.out" - llc -filetype=obj "$out_file" -o "$obj" - clang "$obj" sylib/sylib.c -o "$exe" + actual_norm="$out_dir/$stem.actual.norm" + expected_norm="$out_dir/$stem.expected.norm" + + # 直接让 clang 优化 .ll,可显著降低大规模 Lab2 performance 用例运行时间。 + # -fwrapv 保持有符号整数按补码回绕,避免与当前 IR 的测试语义偏离。 + if ! clang -O2 -fwrapv -Wno-override-module "$out_file" sylib/sylib.c -o "$exe"; then + if ! command -v llc >/dev/null 2>&1; then + echo "未找到 llc,且 clang 直接编译 IR 失败,无法运行 IR。" >&2 + exit 1 + fi + obj="$out_dir/$stem.o" + llc -filetype=obj "$out_file" -o "$obj" + clang "$obj" sylib/sylib.c -o "$exe" + fi + echo "运行 $exe ..." set +e if [[ -f "$stdin_file" ]]; then @@ -81,7 +89,9 @@ if [[ "$run_exec" == true ]]; then } > "$actual_file" if [[ -f "$expected_file" ]]; then - if diff -u "$expected_file" "$actual_file"; then + perl -0pe 's/\r\n/\n/g; s/\r/\n/g; s/\n?\z//' "$expected_file" > "$expected_norm" + perl -0pe 's/\r\n/\n/g; s/\r/\n/g; s/\n?\z//' "$actual_file" > "$actual_norm" + if diff -u "$expected_norm" "$actual_norm"; then echo "输出匹配: $expected_file" else echo "输出不匹配: $expected_file" >&2 diff --git a/src/ir/IRPrinter.cpp b/src/ir/IRPrinter.cpp index 7e6fa60..30dd3ad 100644 --- a/src/ir/IRPrinter.cpp +++ b/src/ir/IRPrinter.cpp @@ -118,6 +118,24 @@ static std::string FloatToString(float v) { return oss.str(); } +static bool IsZeroInitializer(const ConstantValue* c) { + if (auto* ci = dynamic_cast(c)) { + return ci->GetValue() == 0; + } + if (auto* cf = dynamic_cast(c)) { + return cf->GetValue() == 0.0f; + } + if (auto* ca = dynamic_cast(c)) { + for (auto* elem : ca->GetElements()) { + if (!elem || !IsZeroInitializer(elem)) { + return false; + } + } + return true; + } + return false; +} + static std::string ConstantToString(const ConstantValue* c) { if (auto* ci = dynamic_cast(c)) { return std::to_string(ci->GetValue()); @@ -126,6 +144,9 @@ static std::string ConstantToString(const ConstantValue* c) { return FloatToString(cf->GetValue()); } if (auto* ca = dynamic_cast(c)) { + if (IsZeroInitializer(ca)) { + return "zeroinitializer"; + } std::ostringstream oss; oss << "["; const auto& elems = ca->GetElements(); diff --git a/src/irgen/IRGenDecl.cpp b/src/irgen/IRGenDecl.cpp index c706771..1f0ee1c 100644 --- a/src/irgen/IRGenDecl.cpp +++ b/src/irgen/IRGenDecl.cpp @@ -121,9 +121,50 @@ std::any IRGenImpl::visitVarDef(SysYParser::VarDefContext* ctx) { } else { init = DefaultValue(*ty); } + if (ty->base == BaseTypeKind::Float) { + if (init->IsInt1() || init->IsInt32()) { + init = CastToFloat(init->IsInt1() ? CastToInt(init) : init); + } + } else if (ty->base == BaseTypeKind::Int) { + if (init->IsFloat() || init->IsInt1()) { + init = CastToInt(init); + } + } builder_.CreateStore(init, slot); } else { - InitArray(slot, *ty, ctx->initVal()); + if (!ctx->initVal() && ty->dims.size() == 1 && ty->dims[0] >= 1024) { + auto* idx_slot = CreateEntryAlloca(ir::Type::GetInt32Type(), + module_.GetContext().NextTemp()); + builder_.CreateStore(builder_.CreateConstInt(0), idx_slot); + + auto* cond_bb = func_->CreateBlock("arr.zero.cond"); + auto* body_bb = func_->CreateBlock("arr.zero.body"); + auto* end_bb = func_->CreateBlock("arr.zero.end"); + builder_.CreateBr(cond_bb); + + builder_.SetInsertPoint(cond_bb); + auto* idx = builder_.CreateLoad(idx_slot, module_.GetContext().NextTemp()); + auto* bound = builder_.CreateConstInt(ty->dims[0]); + auto* cmp = builder_.CreateICmp(ir::ICmpPredicate::Slt, idx, bound, + module_.GetContext().NextTemp()); + builder_.CreateCondBr(cmp, body_bb, end_bb); + + builder_.SetInsertPoint(body_bb); + std::vector indices; + indices.push_back(builder_.CreateConstInt(0)); + indices.push_back(idx); + auto* elem_addr = builder_.CreateGep(slot, std::move(indices), + module_.GetContext().NextTemp()); + builder_.CreateStore(DefaultValue(*ty), elem_addr); + auto* next = builder_.CreateAdd(idx, builder_.CreateConstInt(1), + module_.GetContext().NextTemp()); + builder_.CreateStore(next, idx_slot); + builder_.CreateBr(cond_bb); + + builder_.SetInsertPoint(end_bb); + } else { + InitArray(slot, *ty, ctx->initVal()); + } } return {}; } @@ -194,6 +235,15 @@ std::any IRGenImpl::visitConstDef(SysYParser::ConstDefContext* ctx) { } else { init = DefaultValue(*ty); } + if (ty->base == BaseTypeKind::Float) { + if (init->IsInt1() || init->IsInt32()) { + init = CastToFloat(init->IsInt1() ? CastToInt(init) : init); + } + } else if (ty->base == BaseTypeKind::Int) { + if (init->IsFloat() || init->IsInt1()) { + init = CastToInt(init); + } + } builder_.CreateStore(init, slot); } else { InitConstArray(slot, *ty, ctx->constInitVal()); diff --git a/src/irgen/IRGenExp.cpp b/src/irgen/IRGenExp.cpp index 27f2498..4dc8bba 100644 --- a/src/irgen/IRGenExp.cpp +++ b/src/irgen/IRGenExp.cpp @@ -270,8 +270,14 @@ std::any IRGenImpl::visitLVal(SysYParser::LValContext* ctx) { if (!ctx || !ctx->ID()) { throw std::runtime_error(FormatError("irgen", "非法左值")); } - ir::Value* addr = GetLValAddress(ctx); BoundDecl bound = sema_.ResolveVarUse(ctx); + if ((bound.kind == BoundDecl::Kind::Var && !bound.var_decl) || + (bound.kind == BoundDecl::Kind::Const && !bound.const_decl) || + (bound.kind == BoundDecl::Kind::Param && !bound.param_decl)) { + throw std::runtime_error( + FormatError("irgen", "左值缺少语义绑定: " + ctx->getText())); + } + ir::Value* addr = GetLValAddress(ctx); const TypeDesc* ty = nullptr; if (bound.kind == BoundDecl::Kind::Var && bound.var_decl) { ty = sema_.GetVarType(bound.var_decl); @@ -280,52 +286,6 @@ std::any IRGenImpl::visitLVal(SysYParser::LValContext* ctx) { } else if (bound.kind == BoundDecl::Kind::Param && bound.param_decl) { ty = sema_.GetParamType(bound.param_decl); } - if (!ty && ctx->ID()) { - const auto name = ctx->ID()->getText(); - for (const auto& kv : var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - break; - } - } - if (!ty) { - for (const auto& kv : const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetConstType(kv.first); - break; - } - } - } - if (!ty) { - for (const auto& kv : param_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetParamType(kv.first); - break; - } - } - } - if (!ty) { - for (const auto& kv : global_var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - break; - } - } - } - if (!ty) { - for (const auto& kv : global_const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetConstType(kv.first); - break; - } - } - } - } if (!ty) { throw std::runtime_error(FormatError("irgen", "无法解析左值类型")); } @@ -514,13 +474,22 @@ ir::Value* IRGenImpl::GetLValAddress(SysYParser::LValContext* ctx) { const TypeDesc* ty = nullptr; if (bound.kind == BoundDecl::Kind::Var && bound.var_decl) { ty = sema_.GetVarType(bound.var_decl); - base_ptr = var_storage_[bound.var_decl]; + auto it = var_storage_.find(bound.var_decl); + if (it != var_storage_.end()) { + base_ptr = it->second; + } } else if (bound.kind == BoundDecl::Kind::Const && bound.const_decl) { ty = sema_.GetConstType(bound.const_decl); - base_ptr = const_storage_[bound.const_decl]; + auto it = const_storage_.find(bound.const_decl); + if (it != const_storage_.end()) { + base_ptr = it->second; + } } else if (bound.kind == BoundDecl::Kind::Param && bound.param_decl) { ty = sema_.GetParamType(bound.param_decl); - base_ptr = param_storage_[bound.param_decl]; + auto it = param_storage_.find(bound.param_decl); + if (it != param_storage_.end()) { + base_ptr = it->second; + } } if (!base_ptr && bound.kind == BoundDecl::Kind::Var && bound.var_decl) { auto it = global_var_storage_.find(bound.var_decl); @@ -536,57 +505,6 @@ ir::Value* IRGenImpl::GetLValAddress(SysYParser::LValContext* ctx) { base_ptr = it->second; } } - if (!base_ptr && ctx && ctx->ID()) { - const auto name = ctx->ID()->getText(); - for (const auto& kv : var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - base_ptr = kv.second; - break; - } - } - if (!base_ptr) { - for (const auto& kv : const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetConstType(kv.first); - base_ptr = kv.second; - break; - } - } - } - if (!base_ptr) { - for (const auto& kv : param_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetParamType(kv.first); - base_ptr = kv.second; - break; - } - } - } - if (!base_ptr) { - for (const auto& kv : global_var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - base_ptr = kv.second; - break; - } - } - } - if (!base_ptr) { - for (const auto& kv : global_const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetConstType(kv.first); - base_ptr = kv.second; - break; - } - } - } - } if (!base_ptr || !ty) { throw std::runtime_error(FormatError("irgen", "左值未绑定")); } diff --git a/src/irgen/IRGenStmt.cpp b/src/irgen/IRGenStmt.cpp index 1aac9a9..97060f6 100644 --- a/src/irgen/IRGenStmt.cpp +++ b/src/irgen/IRGenStmt.cpp @@ -13,6 +13,12 @@ std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) { ir::Value* addr = GetLValAddress(ctx->lVal()); ir::Value* val = EvalExp(ctx->exp()); BoundDecl bound = sema_.ResolveVarUse(ctx->lVal()); + if ((bound.kind == BoundDecl::Kind::Var && !bound.var_decl) || + (bound.kind == BoundDecl::Kind::Const && !bound.const_decl) || + (bound.kind == BoundDecl::Kind::Param && !bound.param_decl)) { + throw std::runtime_error(FormatError( + "irgen", "赋值左值缺少语义绑定: " + ctx->lVal()->getText())); + } const TypeDesc* ty = nullptr; if (bound.kind == BoundDecl::Kind::Var && bound.var_decl) { ty = sema_.GetVarType(bound.var_decl); @@ -21,50 +27,6 @@ std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) { } else if (bound.kind == BoundDecl::Kind::Const) { throw std::runtime_error(FormatError("irgen", "不能给常量赋值")); } - if (!ty && ctx->lVal()->ID()) { - const auto name = ctx->lVal()->ID()->getText(); - for (const auto& kv : var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - break; - } - } - if (!ty) { - for (const auto& kv : const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - throw std::runtime_error(FormatError("irgen", "不能给常量赋值")); - } - } - } - if (!ty) { - for (const auto& kv : param_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetParamType(kv.first); - break; - } - } - } - if (!ty) { - for (const auto& kv : global_var_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - ty = sema_.GetVarType(kv.first); - break; - } - } - } - if (!ty) { - for (const auto& kv : global_const_storage_) { - auto* def = const_cast(kv.first); - if (def && def->ID() && def->ID()->getText() == name) { - throw std::runtime_error(FormatError("irgen", "不能给常量赋值")); - } - } - } - } if (!ty) { throw std::runtime_error(FormatError("irgen", "无法解析赋值类型")); } diff --git a/src/sem/Sema.cpp b/src/sem/Sema.cpp index 9ac563a..81ced06 100644 --- a/src/sem/Sema.cpp +++ b/src/sem/Sema.cpp @@ -379,6 +379,11 @@ class SemaVisitor final : public SysYBaseVisitor { bound.param_decl = entry->param_decl; } sema_.BindVarUse(ctx, bound); + for (auto* exp : ctx->exp()) { + if (exp) { + exp->accept(this); + } + } return {}; }