diff --git a/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md b/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md new file mode 100644 index 0000000..9fa193b --- /dev/null +++ b/.changelog/fix/ir-opt/2026-05-19-01/CHANGELOG.md @@ -0,0 +1,87 @@ +# Fix: starttime/stoptime 库函数链接错误 + +**日期**: 2026-05-19 +**分支**: fix/ir-opt +**测试结果**: 737/737 通过 (--O0 --run) + +## 问题描述 + +运行 `bash scripts/run_ir_test.sh --O0 --run` 时,所有调用 `starttime()` / `stoptime()` 的 performance 测试用例在链接阶段失败: + +``` +undefined reference to `starttime' +undefined reference to `stoptime' +``` + +## 根因分析 + +`sylib/sylib.c` 中实际定义的计时函数是 `_sysy_starttime(int lineno)` 和 `_sysy_stoptime(int lineno)`。`sylib/sylib.h` 中的 `starttime()` / `stoptime()` 只是 C 预处理器宏: + +```c +#define starttime() _sysy_starttime(__LINE__) +#define stoptime() _sysy_stoptime(__LINE__) +``` + +SysY 编译器在 IR 生成时直接将 `starttime` / `stoptime` 作为 LLVM IR 函数名,导致: +1. 链接器找不到 `starttime` / `stoptime` 符号(实际符号是 `_sysy_starttime` / `_sysy_stoptime`) +2. 宏中的 `__LINE__` 无法通过 C 预处理器展开,调用未自动传入行号参数 + +## 修改内容 + +**文件**: `src/irgen/IRGenExp.cpp` + +### 修改1: EnsureExternalDecl 函数名映射(第167行) + +将外部函数声明的名称匹配从 `"starttime" || "stoptime"` 改为 `"_sysy_starttime" || "_sysy_stoptime"`: + +```cpp +// Before +} else if (name == "starttime" || name == "stoptime") { + module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), + {ir::Type::GetInt32Type()}); + +// After +} else if (name == "_sysy_starttime" || name == "_sysy_stoptime") { + module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), + {ir::Type::GetInt32Type()}); +``` + +### 修改2: 调用点名称映射 + 行号自动插入(第291行) + +在函数调用实参收集前,将 SysY 层面的 `starttime` / `stoptime` 映射为实际的库函数名,并自动插入源码行号作为实参: + +```cpp +std::string callee_name = ctx->Ident()->getText(); + +// 收集实参 +std::vector args; + +// 映射 starttime/stoptime 到实际的库函数名,并自动插入行号 +if (callee_name == "starttime") { + callee_name = "_sysy_starttime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); +} else if (callee_name == "stoptime") { + callee_name = "_sysy_stoptime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); +} +``` + +## 效果 + +修改前生成的 IR: +```llvm +declare void @starttime(i32) +declare void @stoptime(i32) +... +call void @starttime() ; 无参数,链接失败 +call void @stoptime() +``` + +修改后生成的 IR: +```llvm +declare void @_sysy_starttime(i32) +declare void @_sysy_stoptime(i32) +... +call void @_sysy_starttime(i32 65) ; 正确函数名 + 行号参数 +call void @_sysy_stoptime(i32 84) +``` diff --git a/src/irgen/IRGenExp.cpp b/src/irgen/IRGenExp.cpp index d1b88bb..a3495c4 100644 --- a/src/irgen/IRGenExp.cpp +++ b/src/irgen/IRGenExp.cpp @@ -164,7 +164,7 @@ void IRGenImpl::EnsureExternalDecl(const std::string& name) { module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), {ir::Type::GetInt32Type(), ir::Type::GetPtrFloat32Type()}); - } else if (name == "starttime" || name == "stoptime") { + } else if (name == "_sysy_starttime" || name == "_sysy_stoptime") { module_.DeclareExternalFunc(name, ir::Type::GetVoidType(), {ir::Type::GetInt32Type()}); } else { @@ -290,6 +290,15 @@ std::any IRGenImpl::visitUnaryExp(SysYParser::UnaryExpContext* ctx) { // 收集实参 std::vector args; + + // 映射 starttime/stoptime 到实际的库函数名,并自动插入行号 + if (callee_name == "starttime") { + callee_name = "_sysy_starttime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); + } else if (callee_name == "stoptime") { + callee_name = "_sysy_stoptime"; + args.push_back(builder_.CreateConstInt(ctx->getStart()->getLine())); + } if (ctx->funcRParams()) { for (auto* exp : ctx->funcRParams()->exp()) { // 检查是否是数组变量(无索引的 lVar),若是则传指针而非 load