diff --git a/script/runit-riscv64.sh b/script/runit-riscv64.sh index 3a0026f..00e65a4 100644 --- a/script/runit-riscv64.sh +++ b/script/runit-riscv64.sh @@ -60,11 +60,7 @@ display_file_content() { # 清理临时文件的函数 clean_tmp() { echo "正在清理临时目录: ${TMP_DIR}" - rm -rf "${TMP_DIR}"/*.s \ - "${TMP_DIR}"/*_sysyc_riscv64 \ - "${TMP_DIR}"/*_sysyc_riscv64.actual_out \ - "${TMP_DIR}"/*_sysyc_riscv64.expected_stdout \ - "${TMP_DIR}"/*_sysyc_riscv64.o + rm -rf "${TMP_DIR}"/* echo "清理完成。" } diff --git a/src/backend/RISCv64/Handler/EliminateFrameIndices.cpp b/src/backend/RISCv64/Handler/EliminateFrameIndices.cpp index 6973293..59f4db3 100644 --- a/src/backend/RISCv64/Handler/EliminateFrameIndices.cpp +++ b/src/backend/RISCv64/Handler/EliminateFrameIndices.cpp @@ -29,6 +29,8 @@ unsigned EliminateFrameIndicesPass::getTypeSizeInBytes(Type* type) { } void EliminateFrameIndicesPass::runOnMachineFunction(MachineFunction* mfunc) { +// [方案一修改] 这是修改后完整的函数代码。 +// 主要的逻辑变更在原注释"[核心修改]"部分。 StackFrameInfo& frame_info = mfunc->getFrameInfo(); Function* F = mfunc->getFunc(); RISCv64ISel* isel = mfunc->getISel(); @@ -65,6 +67,8 @@ void EliminateFrameIndicesPass::runOnMachineFunction(MachineFunction* mfunc) { // 记录仅由AllocaInst分配的局部变量的总大小 frame_info.locals_size = local_var_offset - 16; + // [新增] 记录局部变量区域分配结束的最终偏移量 + frame_info.locals_end_offset = -local_var_offset; // 3. [核心修改] 在函数入口为所有栈传递的参数插入load指令 // 这个步骤至关重要:它在寄存器分配之前,为这些参数的vreg创建了明确的“定义(def)”指令。 @@ -72,6 +76,8 @@ void EliminateFrameIndicesPass::runOnMachineFunction(MachineFunction* mfunc) { if (F && isel && !mfunc->getBlocks().empty()) { MachineBasicBlock* entry_block = mfunc->getBlocks().front().get(); std::vector> arg_load_instrs; + + // 步骤 3.1: 生成所有加载栈参数的指令,暂存起来 int arg_idx = 0; for (Argument* arg : F->getArguments()) { // 根据ABI,前8个整型/指针参数通过寄存器传递,这里只处理超出部分。 @@ -104,13 +110,48 @@ void EliminateFrameIndicesPass::runOnMachineFunction(MachineFunction* mfunc) { arg_idx++; } - // 将所有新创建的参数加载指令一次性插入到入口块的起始位置 - auto& entry_instrs = entry_block->getInstructions(); - entry_instrs.insert(entry_instrs.begin(), - std::make_move_iterator(arg_load_instrs.begin()), - std::make_move_iterator(arg_load_instrs.end())); - } + // [方案一修改] 仅当有需要加载的栈参数时,才执行插入逻辑 + if (!arg_load_instrs.empty()) { + auto& entry_instrs = entry_block->getInstructions(); + auto insertion_point = entry_instrs.begin(); // 默认插入点为块的开头 + auto last_arg_save_it = entry_instrs.end(); + // 步骤 3.2: 寻找一个安全的插入点。 + // 遍历入口块的指令,找到最后一条保存“寄存器传递参数”的伪指令。 + // 这样可以确保我们在所有 a0-a7 参数被保存之后,才执行可能覆盖它们的加载指令。 + for (auto it = entry_instrs.begin(); it != entry_instrs.end(); ++it) { + MachineInstr* instr = it->get(); + // 寻找代表保存参数到栈的伪指令 + if (instr->getOpcode() == RVOpcodes::FRAME_STORE_W || + instr->getOpcode() == RVOpcodes::FRAME_STORE_D || + instr->getOpcode() == RVOpcodes::FRAME_STORE_F) { + + // 检查被保存的值是否是寄存器参数 (arg_no < 8) + auto& operands = instr->getOperands(); + if (operands.empty() || operands[0]->getKind() != MachineOperand::KIND_REG) continue; + + unsigned src_vreg = static_cast(operands[0].get())->getVRegNum(); + Value* ir_value = isel->getVRegValueMap().count(src_vreg) ? isel->getVRegValueMap().at(src_vreg) : nullptr; + + if (auto ir_arg = dynamic_cast(ir_value)) { + if (ir_arg->getIndex() < 8) { + last_arg_save_it = it; // 找到了一个保存寄存器参数的指令,更新位置 + } + } + } + } + + // 如果找到了这样的保存指令,我们的插入点就在它之后 + if (last_arg_save_it != entry_instrs.end()) { + insertion_point = std::next(last_arg_save_it); + } + + // 步骤 3.3: 在计算出的安全位置,一次性插入所有新创建的参数加载指令 + entry_instrs.insert(insertion_point, + std::make_move_iterator(arg_load_instrs.begin()), + std::make_move_iterator(arg_load_instrs.end())); + } + } // 4. 遍历所有机器指令,将访问局部变量的伪指令展开为真实指令 // 由于处理参数的逻辑已移除,这里的展开现在只针对局部变量,因此是正确的。 diff --git a/src/backend/RISCv64/Handler/PrologueEpilogueInsertion.cpp b/src/backend/RISCv64/Handler/PrologueEpilogueInsertion.cpp index ef86ea7..245a275 100644 --- a/src/backend/RISCv64/Handler/PrologueEpilogueInsertion.cpp +++ b/src/backend/RISCv64/Handler/PrologueEpilogueInsertion.cpp @@ -9,7 +9,12 @@ namespace sysy { char PrologueEpilogueInsertionPass::ID = 0; +// [函数上下文] +// ... + void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) { +// [最终修正] 这是修正后完整的函数代码。 +// 序言(4.4)和尾声(5.1)中的循环逻辑现在完全一致。 StackFrameInfo& frame_info = mfunc->getFrameInfo(); Function* F = mfunc->getFunc(); RISCv64ISel* isel = mfunc->getISel(); @@ -27,8 +32,7 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) ); } - // 2. [新增] 确定需要保存的被调用者保存寄存器 (callee-saved) - // 这部分逻辑从 CalleeSavedHandler Pass 移入,以集中管理序言生成 + // 2. 确定需要保存的被调用者保存寄存器 (callee-saved) auto& vreg_to_preg_map = frame_info.vreg_to_preg_map; std::set used_callee_saved_regs_set; const auto& callee_saved_int = getCalleeSavedIntRegs(); @@ -36,38 +40,34 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) for (const auto& pair : vreg_to_preg_map) { PhysicalReg preg = pair.second; - // 检查是否在整数或浮点 callee-saved 集合中 - // 注意:s0作为帧指针,由序言/尾声逻辑特殊处理,不在此处保存 bool is_int_cs = std::find(callee_saved_int.begin(), callee_saved_int.end(), preg) != callee_saved_int.end(); bool is_fp_cs = std::find(callee_saved_fp.begin(), callee_saved_fp.end(), preg) != callee_saved_fp.end(); if ((is_int_cs && preg != PhysicalReg::S0) || is_fp_cs) { used_callee_saved_regs_set.insert(preg); } } - // 为了确定性排序,并存入 frame_info 供尾声使用 frame_info.callee_saved_regs_to_store.assign( used_callee_saved_regs_set.begin(), used_callee_saved_regs_set.end() ); std::sort(frame_info.callee_saved_regs_to_store.begin(), frame_info.callee_saved_regs_to_store.end()); - frame_info.callee_saved_size = frame_info.callee_saved_regs_to_store.size() * 8; // 每个寄存器8字节 + frame_info.callee_saved_size = frame_info.callee_saved_regs_to_store.size() * 8; // 3. 计算最终的栈帧总大小 int total_stack_size = frame_info.locals_size + frame_info.spill_size + frame_info.callee_saved_size + - 16; // 为 ra 和 s0 固定的16字节 + 16; - int aligned_stack_size = (total_stack_size + 15) & ~15; // 16字节对齐 + int aligned_stack_size = (total_stack_size + 15) & ~15; frame_info.total_size = aligned_stack_size; - // 只有在需要分配栈空间时才生成序言和尾声 if (aligned_stack_size > 0) { // --- 4. 插入完整的序言 --- MachineBasicBlock* entry_block = mfunc->getBlocks().front().get(); auto& entry_instrs = entry_block->getInstructions(); std::vector> prologue_instrs; - // 4.1. 分配栈帧: addi sp, sp, -aligned_stack_size + // 4.1. 分配栈帧 auto alloc_stack = std::make_unique(RVOpcodes::ADDI); alloc_stack->addOperand(std::make_unique(PhysicalReg::SP)); alloc_stack->addOperand(std::make_unique(PhysicalReg::SP)); @@ -75,7 +75,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) prologue_instrs.push_back(std::move(alloc_stack)); // 4.2. 保存 ra 和 s0 - // sd ra, (aligned_stack_size - 8)(sp) auto save_ra = std::make_unique(RVOpcodes::SD); save_ra->addOperand(std::make_unique(PhysicalReg::RA)); save_ra->addOperand(std::make_unique( @@ -83,7 +82,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) std::make_unique(aligned_stack_size - 8) )); prologue_instrs.push_back(std::move(save_ra)); - // sd s0, (aligned_stack_size - 16)(sp) auto save_fp = std::make_unique(RVOpcodes::SD); save_fp->addOperand(std::make_unique(PhysicalReg::S0)); save_fp->addOperand(std::make_unique( @@ -92,60 +90,55 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) )); prologue_instrs.push_back(std::move(save_fp)); - // 4.3. 设置新的帧指针 s0: addi s0, sp, aligned_stack_size + // 4.3. 设置新的帧指针 s0 auto set_fp = std::make_unique(RVOpcodes::ADDI); set_fp->addOperand(std::make_unique(PhysicalReg::S0)); set_fp->addOperand(std::make_unique(PhysicalReg::SP)); set_fp->addOperand(std::make_unique(aligned_stack_size)); prologue_instrs.push_back(std::move(set_fp)); - // 4.4. [新增] 保存所有使用到的被调用者保存寄存器 - // 它们保存在 s0 下方,紧接着 ra/s0 的位置 - int callee_saved_offset = -16; + // 4.4. 保存所有使用到的被调用者保存寄存器 + int next_available_offset = -(16 + frame_info.locals_size + frame_info.spill_size); for (const auto& reg : frame_info.callee_saved_regs_to_store) { - callee_saved_offset -= 8; - RVOpcodes store_op = (reg >= PhysicalReg::F0 && reg <= PhysicalReg::F31) ? RVOpcodes::FSD : RVOpcodes::SD; + // [修正] 采用“先使用,后更新”逻辑 + RVOpcodes store_op = isFPR(reg) ? RVOpcodes::FSD : RVOpcodes::SD; auto save_cs_reg = std::make_unique(store_op); save_cs_reg->addOperand(std::make_unique(reg)); save_cs_reg->addOperand(std::make_unique( std::make_unique(PhysicalReg::S0), - std::make_unique(callee_saved_offset) + std::make_unique(next_available_offset) // 使用当前偏移 )); prologue_instrs.push_back(std::move(save_cs_reg)); + next_available_offset -= 8; // 为下一个寄存器准备偏移 } - // 4.5. [核心修改] 加载栈传递参数的逻辑已从此移除 - // 这项工作已经前移至 `EliminateFrameIndicesPass` 中完成, - // 以确保在寄存器分配前就将相关虚拟寄存器定义,从而修复溢出逻辑的bug。 - - // 4.6. 将所有生成的序言指令一次性插入到函数入口 + // 4.5. 将所有生成的序言指令一次性插入到函数入口 entry_instrs.insert(entry_instrs.begin(), std::make_move_iterator(prologue_instrs.begin()), std::make_move_iterator(prologue_instrs.end())); // --- 5. 插入完整的尾声 --- for (auto& mbb : mfunc->getBlocks()) { - // [修正] 使用前向迭代器查找RET指令,以确保在正确的位置(RET之前)插入尾声。 for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) { if ((*it)->getOpcode() == RVOpcodes::RET) { std::vector> epilogue_instrs; - // 5.1. [新增] 恢复被调用者保存寄存器 - callee_saved_offset = -16; + // 5.1. 恢复被调用者保存寄存器 + // [修正] 采用与序言完全相同的“先使用,后更新”逻辑 + int next_available_offset_restore = -(16 + frame_info.locals_size + frame_info.spill_size); for (const auto& reg : frame_info.callee_saved_regs_to_store) { - callee_saved_offset -= 8; - RVOpcodes load_op = (reg >= PhysicalReg::F0 && reg <= PhysicalReg::F31) ? RVOpcodes::FLD : RVOpcodes::LD; + RVOpcodes load_op = isFPR(reg) ? RVOpcodes::FLD : RVOpcodes::LD; auto restore_cs_reg = std::make_unique(load_op); restore_cs_reg->addOperand(std::make_unique(reg)); restore_cs_reg->addOperand(std::make_unique( std::make_unique(PhysicalReg::S0), - std::make_unique(callee_saved_offset) + std::make_unique(next_available_offset_restore) // 使用当前偏移 )); epilogue_instrs.push_back(std::move(restore_cs_reg)); + next_available_offset_restore -= 8; // 为下一个寄存器准备偏移 } - // 5.2. 恢复 ra 和 s0 (注意基址现在是sp) - // ld ra, (aligned_stack_size - 8)(sp) + // 5.2. 恢复 ra 和 s0 auto restore_ra = std::make_unique(RVOpcodes::LD); restore_ra->addOperand(std::make_unique(PhysicalReg::RA)); restore_ra->addOperand(std::make_unique( @@ -153,7 +146,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) std::make_unique(aligned_stack_size - 8) )); epilogue_instrs.push_back(std::move(restore_ra)); - // ld s0, (aligned_stack_size - 16)(sp) auto restore_fp = std::make_unique(RVOpcodes::LD); restore_fp->addOperand(std::make_unique(PhysicalReg::S0)); restore_fp->addOperand(std::make_unique( @@ -162,7 +154,7 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) )); epilogue_instrs.push_back(std::move(restore_fp)); - // 5.3. 释放栈帧: addi sp, sp, aligned_stack_size + // 5.3. 释放栈帧 auto dealloc_stack = std::make_unique(RVOpcodes::ADDI); dealloc_stack->addOperand(std::make_unique(PhysicalReg::SP)); dealloc_stack->addOperand(std::make_unique(PhysicalReg::SP)); @@ -174,7 +166,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) std::make_move_iterator(epilogue_instrs.begin()), std::make_move_iterator(epilogue_instrs.end())); - // 一个基本块通常只有一个终止指令,处理完就可以跳到下一个块 goto next_block; } } diff --git a/src/backend/RISCv64/RISCv64RegAlloc.cpp b/src/backend/RISCv64/RISCv64RegAlloc.cpp index 76f003b..aa6c956 100644 --- a/src/backend/RISCv64/RISCv64RegAlloc.cpp +++ b/src/backend/RISCv64/RISCv64RegAlloc.cpp @@ -647,11 +647,22 @@ void RISCv64RegAlloc::assignColors() { } } +// 在文件 RISCv64RegAlloc.cpp 中: + +// [函数上下文] +// ... +// 我将修改下面这个函数 +// ... + // 重写程序,插入溢出代码 void RISCv64RegAlloc::rewriteProgram() { - // 1. 为溢出的旧vreg在栈上分配空间 StackFrameInfo& frame_info = MFunc->getFrameInfo(); - int spill_base_offset = frame_info.locals_size + frame_info.callee_saved_size; + // 使用 EFI Pass 确定的 locals_end_offset 作为溢出分配的基准。 + // locals_end_offset 本身是负数,代表局部变量区域的下边界地址。 + int spill_current_offset = frame_info.locals_end_offset; + + // [新增] 保存溢出区域的起始点,用于最后计算总的 spill_size + const int spill_start_offset = frame_info.locals_end_offset; for (unsigned vreg : spilledNodes) { if (frame_info.spill_offsets.count(vreg)) continue; @@ -663,11 +674,19 @@ void RISCv64RegAlloc::rewriteProgram() { size = 8; // pointer } - spill_base_offset += size; - spill_base_offset = (spill_base_offset + 7) & ~7; - frame_info.spill_offsets[vreg] = -spill_base_offset; // [修正] 溢出槽也使用负偏移量 + // [修改] 在当前偏移基础上继续向下(地址变得更负)分配空间 + spill_current_offset -= size; + + // [修改] 对齐新的、更小的地址,RISC-V 要求8字节对齐 + spill_current_offset = spill_current_offset & ~7; + + // 将计算出的、不会冲突的正确偏移量存入 spill_offsets + frame_info.spill_offsets[vreg] = spill_current_offset; } - frame_info.spill_size = spill_base_offset - (frame_info.locals_size + frame_info.callee_saved_size); + + // [修改] 更新总的溢出区域大小。 + // spill_size = -(结束偏移 - 开始偏移) + frame_info.spill_size = -(spill_current_offset - spill_start_offset); // 2. 遍历所有指令,重写代码 for (auto& mbb : MFunc->getBlocks()) { diff --git a/src/include/backend/RISCv64/RISCv64LLIR.h b/src/include/backend/RISCv64/RISCv64LLIR.h index 1931617..0cd6b9b 100644 --- a/src/include/backend/RISCv64/RISCv64LLIR.h +++ b/src/include/backend/RISCv64/RISCv64LLIR.h @@ -39,7 +39,7 @@ enum class PhysicalReg { // 用于内部表示物理寄存器在干扰图中的节点ID(一个简单的特殊ID,确保不与vreg_counter冲突) // 假设 vreg_counter 不会达到这么大的值 - PHYS_REG_START_ID = 100000, + PHYS_REG_START_ID = 1000000, PHYS_REG_END_ID = PHYS_REG_START_ID + 320, // 预留足够的空间 }; @@ -280,6 +280,7 @@ private: // 栈帧信息 struct StackFrameInfo { int locals_size = 0; // 仅为AllocaInst分配的大小 + int locals_end_offset = 0; // [新增] 记录局部变量分配结束后的偏移量(相对于s0,为负) int spill_size = 0; // 仅为溢出分配的大小 int total_size = 0; // 总大小 int callee_saved_size = 0; // 保存寄存器的大小