[backend]修复了一个栈管理问题

This commit is contained in:
Lixuanwang
2025-07-30 20:40:56 +08:00
parent 8e94f89931
commit 03b62b138f
4 changed files with 66 additions and 90 deletions

View File

@ -1,45 +1,45 @@
#include "CalleeSavedHandler.h"
#include <set>
#include <vector> //
#include <vector>
#include <algorithm>
#include <iterator> //
#include <iterator>
namespace sysy {
char CalleeSavedHandler::ID = 0;
// 辅助函数,用于判断一个物理寄存器是否为浮点寄存器
static bool is_fp_reg(PhysicalReg reg) {
return reg >= PhysicalReg::F0 && reg <= PhysicalReg::F31;
}
bool CalleeSavedHandler::runOnFunction(Function *F, AnalysisManager& AM) {
// This pass works on MachineFunction level, not IR level
return false;
}
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
// 此 Pass 负责分析、分配栈空间并插入 callee-saved 寄存器的保存/恢复指令。
// 它通过与 FrameInfo 协作,确保为 callee-saved 寄存器分配的空间与局部变量/溢出槽的空间不冲突。
StackFrameInfo& frame_info = mfunc->getFrameInfo();
// [修改] 分别记录被使用的整数和浮点被调用者保存寄存器
std::set<PhysicalReg> used_int_callee_saved;
std::set<PhysicalReg> used_fp_callee_saved;
std::set<PhysicalReg> used_callee_saved;
// 1. 扫描所有指令,找出被使用的s寄存器 (s1-s11) 和 fs寄存器 (fs0-fs11)
// 1. 扫描所有指令,找出被使用的callee-saved寄存器
// 这个Pass在RegAlloc之后运行所以可以访问到物理寄存器
for (auto& mbb : mfunc->getBlocks()) {
for (auto& instr : mbb->getInstructions()) {
for (auto& op : instr->getOperands()) {
auto check_and_insert_reg = [&](RegOperand* reg_op) {
if (!reg_op->isVirtual()) {
if (reg_op && !reg_op->isVirtual()) {
PhysicalReg preg = reg_op->getPReg();
// [修改] 区分整数和浮点被调用者保存寄存器
// s0 由序言/尾声处理器专门处理,这里不计入
// 检查整数 s1-s11
if (preg >= PhysicalReg::S1 && preg <= PhysicalReg::S11) {
used_int_callee_saved.insert(preg);
used_callee_saved.insert(preg);
}
// fs0-fs11 在我们的枚举中对应 f8,f9,f18-f27
// 检查浮点 fs0-fs11 (f8,f9,f18-f27)
else if ((preg >= PhysicalReg::F8 && preg <= PhysicalReg::F9) || (preg >= PhysicalReg::F18 && preg <= PhysicalReg::F27)) {
used_fp_callee_saved.insert(preg);
used_callee_saved.insert(preg);
}
}
};
@ -53,60 +53,44 @@ void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
}
}
// 如果没有使用任何需要处理的 callee-saved 寄存器,则直接返回
if (used_int_callee_saved.empty() && used_fp_callee_saved.empty()) {
frame_info.callee_saved_size = 0; // 确保大小被初始化
if (used_callee_saved.empty()) {
frame_info.callee_saved_size = 0;
return;
}
// 2. 计算为 callee-saved 寄存器分配的栈空间大小
// 每个寄存器在RV64中都占用8字节
int callee_saved_size = (used_int_callee_saved.size() + used_fp_callee_saved.size()) * 8;
frame_info.callee_saved_size = callee_saved_size;
// 2. 计算并更新 frame_info
frame_info.callee_saved_size = used_callee_saved.size() * 8;
// 为了布局确定性和恢复顺序一致,对寄存器排序
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
std::sort(sorted_regs.begin(), sorted_regs.end());
// 3. 在函数序言中插入保存指令
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
auto& entry_instrs = entry_block->getInstructions();
// 插入点通常在函数入口标签之后
// 插入点在函数入口标签之后,或者就是最开始
auto insert_pos = entry_instrs.begin();
if (!entry_instrs.empty() && entry_instrs.front()->getOpcode() == RVOpcodes::LABEL) {
insert_pos = std::next(insert_pos);
}
// 为了布局确定性,对寄存器进行排序并按序保存
std::vector<PhysicalReg> sorted_int_regs(used_int_callee_saved.begin(), used_int_callee_saved.end());
std::vector<PhysicalReg> sorted_fp_regs(used_fp_callee_saved.begin(), used_fp_callee_saved.end());
std::sort(sorted_int_regs.begin(), sorted_int_regs.end());
std::sort(sorted_fp_regs.begin(), sorted_fp_regs.end());
std::vector<std::unique_ptr<MachineInstr>> save_instrs;
int current_offset = -16; // ra和s0已占用-8和-16从-24开始分配
// [关键] 从局部变量区域之后开始分配空间
int current_offset = - (16 + frame_info.locals_size);
// 准备整数保存指令 (sd)
for (PhysicalReg reg : sorted_int_regs) {
for (PhysicalReg reg : sorted_regs) {
current_offset -= 8;
auto sd = std::make_unique<MachineInstr>(RVOpcodes::SD);
sd->addOperand(std::make_unique<RegOperand>(reg));
sd->addOperand(std::make_unique<MemOperand>(
RVOpcodes save_op = is_fp_reg(reg) ? RVOpcodes::FSD : RVOpcodes::SD;
auto save_instr = std::make_unique<MachineInstr>(save_op);
save_instr->addOperand(std::make_unique<RegOperand>(reg));
save_instr->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0), // 基址为帧指针 s0
std::make_unique<ImmOperand>(current_offset)
));
save_instrs.push_back(std::move(sd));
}
// 准备浮点保存指令 (fsd)
for (PhysicalReg reg : sorted_fp_regs) {
current_offset -= 8;
auto fsd = std::make_unique<MachineInstr>(RVOpcodes::FSD); // 使用浮点保存指令
fsd->addOperand(std::make_unique<RegOperand>(reg));
fsd->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0),
std::make_unique<ImmOperand>(current_offset)
));
save_instrs.push_back(std::move(fsd));
save_instrs.push_back(std::move(save_instr));
}
// 一次性插入所有保存指令
if (!save_instrs.empty()) {
entry_instrs.insert(insert_pos,
std::make_move_iterator(save_instrs.begin()),
@ -118,40 +102,27 @@ void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
if ((*it)->getOpcode() == RVOpcodes::RET) {
std::vector<std::unique_ptr<MachineInstr>> restore_instrs;
current_offset = -16; // 重置偏移量用于恢复
// [关键] 使用与保存时完全相同的逻辑来计算偏移量
current_offset = - (16 + frame_info.locals_size);
// 准备恢复整数寄存器 (ld) - 以与保存时相同的顺序
for (PhysicalReg reg : sorted_int_regs) {
for (PhysicalReg reg : sorted_regs) {
current_offset -= 8;
auto ld = std::make_unique<MachineInstr>(RVOpcodes::LD);
ld->addOperand(std::make_unique<RegOperand>(reg));
ld->addOperand(std::make_unique<MemOperand>(
RVOpcodes restore_op = is_fp_reg(reg) ? RVOpcodes::FLD : RVOpcodes::LD;
auto restore_instr = std::make_unique<MachineInstr>(restore_op);
restore_instr->addOperand(std::make_unique<RegOperand>(reg));
restore_instr->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0),
std::make_unique<ImmOperand>(current_offset)
));
restore_instrs.push_back(std::move(ld));
restore_instrs.push_back(std::move(restore_instr));
}
// 准备恢复浮点寄存器 (fld)
for (PhysicalReg reg : sorted_fp_regs) {
current_offset -= 8;
auto fld = std::make_unique<MachineInstr>(RVOpcodes::FLD); // 使用浮点加载指令
fld->addOperand(std::make_unique<RegOperand>(reg));
fld->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0),
std::make_unique<ImmOperand>(current_offset)
));
restore_instrs.push_back(std::move(fld));
}
// 一次性插入所有恢复指令
if (!restore_instrs.empty()) {
mbb->getInstructions().insert(it,
std::make_move_iterator(restore_instrs.begin()),
std::make_move_iterator(restore_instrs.end()));
}
// 处理完一个基本块的RET后迭代器已失效需跳出当前块的循环
goto next_block_label;
}
}

View File

@ -12,11 +12,10 @@ std::string RISCv64CodeGen::code_gen() {
return module_gen();
}
// 模块级代码生成
std::string RISCv64CodeGen::module_gen() {
std::stringstream ss;
// --- 步骤1将全局变量分为.data和.bss两组 ---
// --- 步骤1将全局变量(GlobalValue)分为.data和.bss两组 ---
std::vector<GlobalValue*> data_globals;
std::vector<GlobalValue*> bss_globals;
@ -41,7 +40,7 @@ std::string RISCv64CodeGen::module_gen() {
}
}
// --- 步骤2生成 .bss 段的代码 ---
// --- 步骤2生成 .bss 段的代码 (这部分不变) ---
if (!bss_globals.empty()) {
ss << ".bss\n";
for (GlobalValue* global : bss_globals) {
@ -57,9 +56,12 @@ std::string RISCv64CodeGen::module_gen() {
}
}
// --- 步骤3生成 .data 段的代码 ---
if (!data_globals.empty()) {
ss << ".data\n"; // 切换到 .data 段
// --- [修改] 步骤3生成 .data 段的代码 ---
// 我们需要检查 data_globals 和 常量列表是否都为空
if (!data_globals.empty() || !module->getConsts().empty()) {
ss << ".data\n";
// a. 先处理普通的全局变量 (GlobalValue)
for (GlobalValue* global : data_globals) {
ss << ".globl " << global->getName() << "\n";
ss << global->getName() << ":\n";
@ -106,7 +108,7 @@ std::string RISCv64CodeGen::module_gen() {
}
}
// --- 处理函数 (.text段) ---
// --- 处理函数 (.text段) 的逻辑保持不变 ---
if (!module->getFunctions().empty()) {
ss << ".text\n";
for (const auto& func_pair : module->getFunctions()) {

View File

@ -165,10 +165,6 @@ void RISCv64RegAlloc::handleCallingConvention() {
*/
void RISCv64RegAlloc::eliminateFrameIndices() {
StackFrameInfo& frame_info = MFunc->getFrameInfo();
// 初始偏移量为保存ra和s0留出空间。
// 假设序言是 addi sp, sp, -stack_size; sd ra, stack_size-8(sp); sd s0, stack_size-16(sp);
int current_offset = 16;
Function* F = MFunc->getFunc();
RISCv64ISel* isel = MFunc->getISel();
@ -190,12 +186,15 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
}
}
// 处理局部变量
// 遍历AllocaInst来计算局部变量所需的总空间
// [关键修改] 为局部变量分配空间时起始点必须考虑为ra, s0以及所有callee-saved寄存器预留的空间。
// 布局顺序为: [s0/ra, 16字节] -> [callee-saved, callee_saved_size字节] -> [局部变量...]
int local_var_offset = 16 + frame_info.callee_saved_size;
int locals_start_offset = local_var_offset; // 记录局部变量区域的起始点,用于计算总大小
// 处理局部变量 (AllocaInst)
for (auto& bb : F->getBasicBlocks()) {
for (auto& inst : bb->getInstructions()) {
if (auto alloca = dynamic_cast<AllocaInst*>(inst.get())) {
// 获取Alloca指令指向的类型 (例如 alloca i32* 中,获取 i32)
Type* allocated_type = alloca->getType()->as<PointerType>()->getBaseType();
int size = getTypeSizeInBytes(allocated_type);
@ -203,14 +202,17 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
size = (size + 7) & ~7;
if (size == 0) size = 8; // 至少分配8字节
current_offset += size;
local_var_offset += size;
unsigned alloca_vreg = isel->getVReg(alloca);
// 局部变量使用相对于s0的负向偏移
frame_info.alloca_offsets[alloca_vreg] = -current_offset;
frame_info.alloca_offsets[alloca_vreg] = -local_var_offset;
}
}
}
frame_info.locals_size = current_offset;
// [修复] 正确计算并设置locals_size
// 它只应该包含由AllocaInst分配的局部变量的总大小。
frame_info.locals_size = local_var_offset - locals_start_offset;
// 遍历所有机器指令,将伪指令展开为真实指令
for (auto& mbb : MFunc->getBlocks()) {

View File

@ -279,7 +279,8 @@ struct StackFrameInfo {
std::map<unsigned, int> alloca_offsets; // <AllocaInst的vreg, 栈偏移>
std::map<unsigned, int> spill_offsets; // <溢出vreg, 栈偏移>
std::set<PhysicalReg> used_callee_saved_regs; // 使用的保存寄存器
std::map<unsigned, PhysicalReg> vreg_to_preg_map;
std::map<unsigned, PhysicalReg> vreg_to_preg_map;
std::vector<PhysicalReg> callee_saved_regs; // 用于存储需要保存的被调用者保存寄存器列表
};
// 机器函数