[backend-IRC]修复了现场管理与溢出处理的栈偏移量错误问题
This commit is contained in:
@@ -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 "清理完成。"
|
||||
}
|
||||
|
||||
|
||||
@@ -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<std::unique_ptr<MachineInstr>> 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++;
|
||||
}
|
||||
|
||||
// 将所有新创建的参数加载指令一次性插入到入口块的起始位置
|
||||
// [方案一修改] 仅当有需要加载的栈参数时,才执行插入逻辑
|
||||
if (!arg_load_instrs.empty()) {
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
entry_instrs.insert(entry_instrs.begin(),
|
||||
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<RegOperand*>(operands[0].get())->getVRegNum();
|
||||
Value* ir_value = isel->getVRegValueMap().count(src_vreg) ? isel->getVRegValueMap().at(src_vreg) : nullptr;
|
||||
|
||||
if (auto ir_arg = dynamic_cast<Argument*>(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. 遍历所有机器指令,将访问局部变量的伪指令展开为真实指令
|
||||
// 由于处理参数的逻辑已移除,这里的展开现在只针对局部变量,因此是正确的。
|
||||
|
||||
@@ -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<PhysicalReg> 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<std::unique_ptr<MachineInstr>> prologue_instrs;
|
||||
|
||||
// 4.1. 分配栈帧: addi sp, sp, -aligned_stack_size
|
||||
// 4.1. 分配栈帧
|
||||
auto alloc_stack = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
alloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
alloc_stack->addOperand(std::make_unique<RegOperand>(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<MachineInstr>(RVOpcodes::SD);
|
||||
save_ra->addOperand(std::make_unique<RegOperand>(PhysicalReg::RA));
|
||||
save_ra->addOperand(std::make_unique<MemOperand>(
|
||||
@@ -83,7 +82,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc)
|
||||
std::make_unique<ImmOperand>(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<MachineInstr>(RVOpcodes::SD);
|
||||
save_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
save_fp->addOperand(std::make_unique<MemOperand>(
|
||||
@@ -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<MachineInstr>(RVOpcodes::ADDI);
|
||||
set_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
set_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
set_fp->addOperand(std::make_unique<ImmOperand>(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<MachineInstr>(store_op);
|
||||
save_cs_reg->addOperand(std::make_unique<RegOperand>(reg));
|
||||
save_cs_reg->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(callee_saved_offset)
|
||||
std::make_unique<ImmOperand>(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<std::unique_ptr<MachineInstr>> 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<MachineInstr>(load_op);
|
||||
restore_cs_reg->addOperand(std::make_unique<RegOperand>(reg));
|
||||
restore_cs_reg->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(callee_saved_offset)
|
||||
std::make_unique<ImmOperand>(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<MachineInstr>(RVOpcodes::LD);
|
||||
restore_ra->addOperand(std::make_unique<RegOperand>(PhysicalReg::RA));
|
||||
restore_ra->addOperand(std::make_unique<MemOperand>(
|
||||
@@ -153,7 +146,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc)
|
||||
std::make_unique<ImmOperand>(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<MachineInstr>(RVOpcodes::LD);
|
||||
restore_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
restore_fp->addOperand(std::make_unique<MemOperand>(
|
||||
@@ -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<MachineInstr>(RVOpcodes::ADDI);
|
||||
dealloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
dealloc_stack->addOperand(std::make_unique<RegOperand>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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; // 保存寄存器的大小
|
||||
|
||||
Reference in New Issue
Block a user