From 1ab937961f8eeee8b41202d920a55d004fc4ffef Mon Sep 17 00:00:00 2001 From: Lixuanwang Date: Tue, 19 Aug 2025 14:09:08 +0800 Subject: [PATCH] =?UTF-8?q?[backend-O1]=E4=BF=AE=E5=A4=8D=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E5=9C=A8-O1=E6=83=85=E5=86=B5=E4=B8=8B=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E5=A4=A7=E9=87=8Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/RISCv64/RISCv64ISel.cpp | 54 +++++++ src/backend/RISCv64/RISCv64RegAlloc.cpp | 134 ++++++++++-------- src/include/backend/RISCv64/RISCv64ISel.h | 1 + src/include/backend/RISCv64/RISCv64RegAlloc.h | 1 + src/midend/Pass/Optimize/Reg2Mem.cpp | 36 ++--- src/sysyc.cpp | 2 +- 6 files changed, 153 insertions(+), 75 deletions(-) diff --git a/src/backend/RISCv64/RISCv64ISel.cpp b/src/backend/RISCv64/RISCv64ISel.cpp index a0ad2f1..22ccc22 100644 --- a/src/backend/RISCv64/RISCv64ISel.cpp +++ b/src/backend/RISCv64/RISCv64ISel.cpp @@ -103,6 +103,60 @@ void RISCv64ISel::select() { } } + if (optLevel > 0) { + if (F && !F->getBasicBlocks().empty()) { + // 定位到第一个MachineBasicBlock,也就是函数入口 + BasicBlock* first_ir_block = F->getBasicBlocks_NoRange().front().get(); + CurMBB = bb_map.at(first_ir_block); + + int int_arg_idx = 0; + int fp_arg_idx = 0; + + for (Argument* arg : F->getArguments()) { + Type* arg_type = arg->getType(); + + // --- 处理整数/指针参数 --- + if (!arg_type->isFloat() && int_arg_idx < 8) { + // 1. 获取参数原始的、将被预着色为 a0-a7 的 vreg + unsigned original_vreg = getVReg(arg); + + // 2. 创建一个新的、安全的 vreg 来持有参数的值 + unsigned saved_vreg = getNewVReg(arg_type); + + // 3. 生成 mv saved_vreg, original_vreg 指令 + auto mv = std::make_unique(RVOpcodes::MV); + mv->addOperand(std::make_unique(saved_vreg)); + mv->addOperand(std::make_unique(original_vreg)); + CurMBB->addInstruction(std::move(mv)); + + // 4.【关键】更新vreg映射表,将arg的vreg指向新的、安全的vreg + // 这样,后续所有对该参数的 getVReg(arg) 调用都会自动获得 saved_vreg, + // 使得函数体内的代码都使用这个被保存过的值。 + vreg_map[arg] = saved_vreg; + + int_arg_idx++; + } + // --- 处理浮点参数 --- + else if (arg_type->isFloat() && fp_arg_idx < 8) { + unsigned original_vreg = getVReg(arg); + unsigned saved_vreg = getNewVReg(arg_type); + + // 对于浮点数,使用 fmv.s 指令 + auto fmv = std::make_unique(RVOpcodes::FMV_S); + fmv->addOperand(std::make_unique(saved_vreg)); + fmv->addOperand(std::make_unique(original_vreg)); + CurMBB->addInstruction(std::move(fmv)); + + // 同样更新映射 + vreg_map[arg] = saved_vreg; + + fp_arg_idx++; + } + // 对于栈传递的参数,则无需处理 + } + } + } + // 遍历基本块,进行指令选择 for (const auto& bb_ptr : F->getBasicBlocks()) { selectBasicBlock(bb_ptr.get()); diff --git a/src/backend/RISCv64/RISCv64RegAlloc.cpp b/src/backend/RISCv64/RISCv64RegAlloc.cpp index 84d397e..ee40c00 100644 --- a/src/backend/RISCv64/RISCv64RegAlloc.cpp +++ b/src/backend/RISCv64/RISCv64RegAlloc.cpp @@ -127,20 +127,46 @@ void RISCv64RegAlloc::precolorByCallingConvention() { int int_arg_idx = 0; int float_arg_idx = 0; - for (Argument* arg : F->getArguments()) { - unsigned vreg = ISel->getVReg(arg); - - if (arg->getType()->isFloat()) { - if (float_arg_idx < 8) { // fa0-fa7 - auto preg = static_cast(static_cast(PhysicalReg::F10) + float_arg_idx); - color_map[vreg] = preg; - float_arg_idx++; + if (optLevel > 0) + { + for (const auto& pair : vreg_to_value_map) { + unsigned vreg = pair.first; + Value* val = pair.second; + + // 检查这个 Value* 是不是一个 Argument 对象 + if (auto arg = dynamic_cast(val)) { + // 如果是,那么 vreg 就是最初分配给这个参数的 vreg + int arg_idx = arg->getIndex(); + + if (arg->getType()->isFloat()) { + if (arg_idx < 8) { // fa0-fa7 + auto preg = static_cast(static_cast(PhysicalReg::F10) + arg_idx); + color_map[vreg] = preg; + } + } else { // 整数或指针 + if (arg_idx < 8) { // a0-a7 + auto preg = static_cast(static_cast(PhysicalReg::A0) + arg_idx); + color_map[vreg] = preg; + } + } } - } else { // 整数或指针 - if (int_arg_idx < 8) { // a0-a7 - auto preg = static_cast(static_cast(PhysicalReg::A0) + int_arg_idx); - color_map[vreg] = preg; - int_arg_idx++; + } + } else { + for (Argument* arg : F->getArguments()) { + unsigned vreg = ISel->getVReg(arg); + + if (arg->getType()->isFloat()) { + if (float_arg_idx < 8) { // fa0-fa7 + auto preg = static_cast(static_cast(PhysicalReg::F10) + float_arg_idx); + color_map[vreg] = preg; + float_arg_idx++; + } + } else { // 整数或指针 + if (int_arg_idx < 8) { // a0-a7 + auto preg = static_cast(static_cast(PhysicalReg::A0) + int_arg_idx); + color_map[vreg] = preg; + int_arg_idx++; + } } } } @@ -477,16 +503,18 @@ void RISCv64RegAlloc::coalesce() { unsigned x = getAlias(*def.begin()); unsigned y = getAlias(*use.begin()); unsigned u, v; - if (precolored.count(y)) { u = y; v = x; } else { u = x; v = y; } + + // 进一步修正:标准化u和v的逻辑,必须同时考虑物理寄存器和已预着色的虚拟寄存器。 + // 目标是确保如果两个操作数中有一个是预着色的,它一定会被赋给 u。 + if (precolored.count(y) || coloredNodes.count(y)) { + u = y; v = x; + } else { + u = x; v = y; + } // 防御性检查,处理物理寄存器之间的传送指令 if (precolored.count(u) && precolored.count(v)) { - // 如果 u 和 v 都是物理寄存器,我们不能合并它们。 - // 这通常是一条寄存器拷贝指令,例如 `mv a2, a1`。 - // 把它加入 constrainedMoves 列表,然后直接返回,不再处理。 constrainedMoves.insert(move); - // addWorklist(u) 和 addWorklist(v) 在这里也不需要调用, - // 因为它们只对虚拟寄存器有意义。 return; } @@ -498,7 +526,7 @@ void RISCv64RegAlloc::coalesce() { if (DEEPERDEBUG) std::cerr << " -> Trivial coalesce (u == v).\n"; coalescedMoves.insert(move); addWorklist(u); - return; // 处理完毕,提前返回 + return; } if (isFPVReg(u) != isFPVReg(v)) { @@ -508,10 +536,13 @@ void RISCv64RegAlloc::coalesce() { constrainedMoves.insert(move); addWorklist(u); addWorklist(v); - return; // 立即返回,不再进行后续检查 + return; } - bool pre_interfere = adjList.at(v).count(u); + // 注意:如果v已经是u的邻居, pre_interfere 会为true。 + // 但如果v不在adjList中(例如v是预着色节点),我们需要检查u是否在v的邻居中。 + // 为了简化,我们假设adjList包含了所有虚拟寄存器。对于(Phys, Virt)对,冲突信息存储在Virt节点的邻接表中。 + bool pre_interfere = (adjList.count(v) && adjList.at(v).count(u)) || (adjList.count(u) && adjList.at(u).count(v)); if (pre_interfere) { if (DEEPERDEBUG) std::cerr << " -> Constrained (nodes already interfere).\n"; @@ -521,63 +552,50 @@ void RISCv64RegAlloc::coalesce() { return; } - bool is_u_precolored = precolored.count(u); + // 考虑物理寄存器和已预着色的虚拟寄存器 + bool u_is_effectively_precolored = precolored.count(u) || coloredNodes.count(u); bool can_coalesce = false; - if (is_u_precolored) { - // --- 场景1:u是物理寄存器,使用 George 启发式 --- - if (DEEPERDEBUG) std::cerr << " -> Trying George Heuristic (u is precolored)...\n"; + if (u_is_effectively_precolored) { + // --- 场景1:u是物理寄存器或已预着色虚拟寄存器,使用 George 启发式 --- + if (DEEPERDEBUG) std::cerr << " -> Trying George Heuristic (u is effectively precolored)...\n"; - // 步骤 1: 独立调用 adjacent(v) 获取邻居集合 VRegSet neighbors_of_v = adjacent(v); if (DEEPERDEBUG) { std::cerr << " - Neighbors of " << regIdToString(v) << " to check are (" << neighbors_of_v.size() << "): { "; for (unsigned id : neighbors_of_v) std::cerr << regIdToString(id) << " "; std::cerr << "}\n"; } - - // 步骤 2: 使用显式的 for 循环来代替 std::all_of - bool george_ok = true; // 默认假设成功,任何一个邻居失败都会将此设为 false + + bool george_ok = true; for (unsigned t : neighbors_of_v) { - if (DEEPERDEBUG) { - std::cerr << " - Checking neighbor " << regIdToString(t) << ":\n"; - } + if (DEEPERDEBUG) std::cerr << " - Checking neighbor " << regIdToString(t) << ":\n"; - // 步骤 3: 独立调用启发式函数 - bool heuristic_result = georgeHeuristic(t, u); + unsigned u_phys_id = precolored.count(u) ? u : (static_cast(PhysicalReg::PHYS_REG_START_ID) + static_cast(color_map.at(u))); + bool heuristic_result = georgeHeuristic(t, u_phys_id); if (DEEPERDEBUG) { - std::cerr << " - georgeHeuristic(" << regIdToString(t) << ", " << regIdToString(u) << ") -> " << (heuristic_result ? "OK" : "FAIL") << "\n"; + std::cerr << " - georgeHeuristic(" << regIdToString(t) << ", " << regIdToString(u_phys_id) << ") -> " << (heuristic_result ? "OK" : "FAIL") << "\n"; } if (!heuristic_result) { - george_ok = false; // 只要有一个邻居不满足条件,整个检查就失败 - break; // 并且可以立即停止检查其他邻居 + george_ok = false; + break; } } - if (DEEPERDEBUG) { - std::cerr << " -> George Heuristic final result: " << (george_ok ? "OK" : "FAIL") << "\n"; - } - - if (george_ok) { - can_coalesce = true; - } + if (DEEPERDEBUG) std::cerr << " -> George Heuristic final result: " << (george_ok ? "OK" : "FAIL") << "\n"; + if (george_ok) can_coalesce = true; } else { - // --- 场景2:u和v都是虚拟寄存器,使用 Briggs 启发式 --- + // --- 场景2:u和v都是未着色的虚拟寄存器,使用 Briggs 启发式 --- if (DEEPERDEBUG) std::cerr << " -> Trying Briggs Heuristic (u and v are virtual)...\n"; bool briggs_ok = briggsHeuristic(u, v); if (DEEPERDEBUG) std::cerr << " - briggsHeuristic(" << regIdToString(u) << ", " << regIdToString(v) << ") -> " << (briggs_ok ? "OK" : "FAIL") << "\n"; - - if (briggs_ok) { - can_coalesce = true; - } + if (briggs_ok) can_coalesce = true; } - // --- 根据启发式结果进行最终决策 --- - if (can_coalesce) { if (DEEPERDEBUG) std::cerr << " -> Heuristic OK. Combining " << regIdToString(v) << " into " << regIdToString(u) << ".\n"; coalescedMoves.insert(move); @@ -1133,7 +1151,7 @@ unsigned RISCv64RegAlloc::getAlias(unsigned n) { } void RISCv64RegAlloc::addWorklist(unsigned u) { - if (precolored.count(u)) return; + if (precolored.count(u) || color_map.count(u)) return; int K = isFPVReg(u) ? K_fp : K_int; if (!moveRelated(u) && degree.at(u) < K) { @@ -1208,8 +1226,12 @@ bool RISCv64RegAlloc::georgeHeuristic(unsigned t, unsigned u) { } int K = isFPVReg(t) ? K_fp : K_int; - // adjList.at(t) 现在是安全的,因为 degree.count(t) > 0 保证了 adjList.count(t) > 0 - return degree.at(t) < K || precolored.count(u) || adjList.at(t).count(u); + + // 缺陷 #2 修正: 移除了致命的 || precolored.count(u) 条件。 + // 在此函数的上下文中,u 总是预着色的物理寄存器ID,导致旧的条件永远为true,使整个启发式失效。 + // 正确的逻辑是检查:邻居t的度数是否小于K,或者t是否已经与u冲突。 + // return degree.at(t) < K || adjList.at(t).count(u); + return degree.at(t) < K || !adjList.at(t).count(u); } void RISCv64RegAlloc::combine(unsigned u, unsigned v) { @@ -1257,7 +1279,7 @@ void RISCv64RegAlloc::freezeMoves(unsigned u) { activeMoves.erase(move); frozenMoves.insert(move); - if (!precolored.count(v_alias) && nodeMoves(v_alias).empty() && degree.at(v_alias) < (isFPVReg(v_alias) ? K_fp : K_int)) { + if (!precolored.count(v_alias) && !coloredNodes.count(v_alias) && nodeMoves(v_alias).empty() && degree.at(v_alias) < (isFPVReg(v_alias) ? K_fp : K_int)) { freezeWorklist.erase(v_alias); simplifyWorklist.insert(v_alias); if (DEEPERDEBUG) { diff --git a/src/include/backend/RISCv64/RISCv64ISel.h b/src/include/backend/RISCv64/RISCv64ISel.h index 35fb7a7..7c52fb0 100644 --- a/src/include/backend/RISCv64/RISCv64ISel.h +++ b/src/include/backend/RISCv64/RISCv64ISel.h @@ -11,6 +11,7 @@ namespace sysy { extern int DEBUG; extern int DEEPDEBUG; +extern int optLevel; namespace sysy { diff --git a/src/include/backend/RISCv64/RISCv64RegAlloc.h b/src/include/backend/RISCv64/RISCv64RegAlloc.h index 1d76fac..123c403 100644 --- a/src/include/backend/RISCv64/RISCv64RegAlloc.h +++ b/src/include/backend/RISCv64/RISCv64RegAlloc.h @@ -12,6 +12,7 @@ extern int DEBUG; extern int DEEPDEBUG; extern int DEBUGLENGTH; // 用于限制调试输出的长度 extern int DEEPERDEBUG; // 用于更深层次的调试输出 +extern int optLevel; namespace sysy { diff --git a/src/midend/Pass/Optimize/Reg2Mem.cpp b/src/midend/Pass/Optimize/Reg2Mem.cpp index cd52f51..3e4303a 100644 --- a/src/midend/Pass/Optimize/Reg2Mem.cpp +++ b/src/midend/Pass/Optimize/Reg2Mem.cpp @@ -70,20 +70,20 @@ void Reg2MemContext::allocateMemoryForSSAValues(Function *func) { // 1. 为函数参数分配内存 builder->setPosition(entryBlock, entryBlock->begin()); // 确保在入口块的开始位置插入 - for (auto arg : func->getArguments()) { - // 默认情况下,将所有参数是提升到内存 - if (isPromotableToMemory(arg)) { - // 参数的类型就是 AllocaInst 需要分配的类型 - AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(arg->getType()), arg->getName() + ".reg2mem"); - // 将参数值 store 到 alloca 中 (这是 Mem2Reg 逆转的关键一步) - valueToAllocaMap[arg] = alloca; + // for (auto arg : func->getArguments()) { + // // 默认情况下,将所有参数是提升到内存 + // if (isPromotableToMemory(arg)) { + // // 参数的类型就是 AllocaInst 需要分配的类型 + // AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(arg->getType()), arg->getName() + ".reg2mem"); + // // 将参数值 store 到 alloca 中 (这是 Mem2Reg 逆转的关键一步) + // valueToAllocaMap[arg] = alloca; - // 确保 alloca 位于入口块的顶部,但在所有参数的 store 指令之前 - // 通常 alloca 都在 entry block 的最开始 - // 这里我们只是创建,并让 builder 决定插入位置 (通常在当前插入点) - // 如果需要严格控制顺序,可能需要手动 insert 到 instruction list - } - } + // // 确保 alloca 位于入口块的顶部,但在所有参数的 store 指令之前 + // // 通常 alloca 都在 entry block 的最开始 + // // 这里我们只是创建,并让 builder 决定插入位置 (通常在当前插入点) + // // 如果需要严格控制顺序,可能需要手动 insert 到 instruction list + // } + // } // 2. 为指令结果分配内存 // 遍历所有基本块和指令,找出所有需要分配 Alloca 的指令结果 @@ -123,11 +123,11 @@ void Reg2MemContext::allocateMemoryForSSAValues(Function *func) { } // 插入所有参数的初始 Store 指令 - for (auto arg : func->getArguments()) { - if (valueToAllocaMap.count(arg)) { // 检查是否为其分配了 alloca - builder->createStoreInst(arg, valueToAllocaMap[arg]); - } - } + // for (auto arg : func->getArguments()) { + // if (valueToAllocaMap.count(arg)) { // 检查是否为其分配了 alloca + // builder->createStoreInst(arg, valueToAllocaMap[arg]); + // } + // } builder->setPosition(entryBlock, entryBlock->terminator()); } diff --git a/src/sysyc.cpp b/src/sysyc.cpp index 78930a0..7ef22f9 100644 --- a/src/sysyc.cpp +++ b/src/sysyc.cpp @@ -28,7 +28,7 @@ static string argStopAfter; static string argInputFile; static bool argFormat = false; // 目前未使用,但保留 static string argOutputFilename; -static int optLevel = 0; // 优化级别,默认为0 (不加-O参数时) +int optLevel = 0; // 优化级别,默认为0 (不加-O参数时) void usage(int code) { const char *msg = "Usage: sysyc [options] inputfile\n\n"