[backend]添加了一个Pass,将调用者、被调用者寄存器实现转移到其中

This commit is contained in:
Lixuanwang
2025-07-26 18:38:04 +08:00
parent 8ae7478ef3
commit 540742be0c
7 changed files with 142 additions and 57 deletions

View File

@ -46,46 +46,6 @@ void RISCv64AsmPrinter::printPrologue() {
*OS << " sd s0, " << (aligned_stack_size - 16) << "(sp)\n";
*OS << " addi s0, sp, " << aligned_stack_size << "\n";
}
// // 为函数参数分配寄存器
// Function* F = MFunc->getFunc();
// if (F && F->getEntryBlock()) {
// int arg_idx = 0;
// RISCv64ISel* isel = MFunc->getISel();
// // 获取函数所有参数的类型列表
// auto param_types = F->getParamTypes();
// for (AllocaInst* alloca_for_param : F->getEntryBlock()->getArguments()) {
// if (arg_idx >= 8) break;
// unsigned vreg = isel->getVReg(alloca_for_param);
// if (frame_info.alloca_offsets.count(vreg)) {
// int offset = frame_info.alloca_offsets.at(vreg);
// auto arg_reg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + arg_idx);
// // 1. 获取当前参数的真实类型
// // 注意F->getParamTypes() 返回的是一个 range-based view需要转换为vector或直接使用
// Type* current_param_type = nullptr;
// int temp_idx = 0;
// for(auto p_type : param_types) {
// if (temp_idx == arg_idx) {
// current_param_type = p_type;
// break;
// }
// temp_idx++;
// }
// assert(current_param_type && "Could not find parameter type.");
// // 2. 根据类型决定使用 "sw" 还是 "sd"
// const char* store_op = current_param_type->isPointer() ? "sd" : "sw";
// // 3. 打印正确的存储指令
// *OS << " " << store_op << " " << regToString(arg_reg) << ", " << offset << "(s0)\n";
// }
// arg_idx++;
// }
// }
}
void RISCv64AsmPrinter::printEpilogue() {

View File

@ -73,6 +73,10 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
RISCv64RegAlloc reg_alloc(mfunc.get());
reg_alloc.run();
// 阶段 3.5: 处理被调用者保存寄存器
CalleeSavedHandler callee_handler;
callee_handler.runOnMachineFunction(mfunc.get());
// 阶段 4: 窥孔优化 (Peephole Optimization)
PeepholeOptimizer peephole;
peephole.runOnMachineFunction(mfunc.get());

View File

@ -51,4 +51,99 @@ void PostRA_Scheduler::runOnMachineFunction(MachineFunction* mfunc) {
// std::cout << "Running Post-RA Local Scheduler..." << std::endl;
}
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
StackFrameInfo& frame_info = mfunc->getFrameInfo();
std::set<PhysicalReg> used_callee_saved;
// 1. 扫描所有指令找出被使用的s寄存器
for (auto& mbb : mfunc->getBlocks()) {
for (auto& instr : mbb->getInstructions()) {
for (auto& op : instr->getOperands()) {
// 辅助Lambda用于检查和插入寄存器
auto check_and_insert_reg = [&](RegOperand* reg_op) {
if (!reg_op->isVirtual()) {
PhysicalReg preg = reg_op->getPReg();
// --- 关键检查点 ---
// 必须严格判断是否在 s0-s11 的范围内。
// a0, t0 等寄存器绝对不应被视为被调用者保存寄存器。
if (preg >= PhysicalReg::S0 && preg <= PhysicalReg::S11) {
used_callee_saved.insert(preg);
}
}
};
if (op->getKind() == MachineOperand::KIND_REG) {
check_and_insert_reg(static_cast<RegOperand*>(op.get()));
} else if (op->getKind() == MachineOperand::KIND_MEM) {
check_and_insert_reg(static_cast<MemOperand*>(op.get())->getBase());
}
}
}
}
// 如果没有使用s寄存器除了可能作为帧指针的s0则无需操作
if (used_callee_saved.empty() || (used_callee_saved.size() == 1 && used_callee_saved.count(PhysicalReg::S0))) {
return;
}
// 将结果存入StackFrameInfo供后续使用
frame_info.used_callee_saved_regs = used_callee_saved;
// 2. 在函数序言插入保存指令
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
auto& entry_instrs = entry_block->getInstructions();
auto prologue_end = entry_instrs.begin();
// 找到序言结束的位置通常是addi s0, sp, size之后
for (auto it = entry_instrs.begin(); it != entry_instrs.end(); ++it) {
if ((*it)->getOpcode() == RVOpcodes::ADDI &&
(*it)->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
static_cast<RegOperand*>((*it)->getOperands()[0].get())->getPReg() == PhysicalReg::S0)
{
prologue_end = std::next(it);
break;
}
}
// 为了栈帧布局确定性,对寄存器进行排序
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
std::sort(sorted_regs.begin(), sorted_regs.end());
int current_offset = -16; // ra和s0已经占用了-8和-16的位置
for (PhysicalReg reg : sorted_regs) {
if (reg == PhysicalReg::S0) continue; // s0已经在序言中处理
current_offset -= 8;
auto sd = std::make_unique<MachineInstr>(RVOpcodes::SD);
sd->addOperand(std::make_unique<RegOperand>(reg));
sd->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0), // 假设s0是帧指针
std::make_unique<ImmOperand>(current_offset)
));
entry_instrs.insert(prologue_end, std::move(sd));
}
// 3. 在函数结尾ret之前插入恢复指令
for (auto& mbb : mfunc->getBlocks()) {
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
if ((*it)->getOpcode() == RVOpcodes::RET) {
// 以相反的顺序恢复
current_offset = -16;
for (PhysicalReg reg : sorted_regs) {
if (reg == PhysicalReg::S0) continue;
current_offset -= 8;
auto ld = std::make_unique<MachineInstr>(RVOpcodes::LD);
ld->addOperand(std::make_unique<RegOperand>(reg));
ld->addOperand(std::make_unique<MemOperand>(
std::make_unique<RegOperand>(PhysicalReg::S0),
std::make_unique<ImmOperand>(current_offset)
));
mbb->getInstructions().insert(it, std::move(ld));
}
break; // 处理完一个基本块的ret即可
}
}
}
}
} // namespace sysy

View File

@ -36,7 +36,6 @@ void RISCv64RegAlloc::handleCallingConvention() {
// 获取函数的Argument对象列表
if (F) {
auto& args = F->getArguments();
// RISC-V RV64G调用约定前8个整型/指针参数通过 a0-a7 传递
int arg_idx = 0;
// 遍历 AllocaInst* 列表
@ -494,10 +493,6 @@ void RISCv64RegAlloc::rewriteFunction() {
if (color_map.count(vreg)) {
PhysicalReg preg = color_map.at(vreg);
reg_op->setPReg(preg);
// 检查这个物理寄存器是否是 s0-s11
if (preg >= PhysicalReg::S0 && preg <= PhysicalReg::S11) {
used_callee_saved_regs.insert(preg);
}
} else if (spilled_vregs.count(vreg)) {
// 如果vreg被溢出替换为专用的溢出物理寄存器t6
reg_op->setPReg(PhysicalReg::T6);
@ -512,14 +507,16 @@ void RISCv64RegAlloc::rewriteFunction() {
// 对这个基址寄存器,执行与情况一完全相同的替换逻辑
if(base_reg_op->isVirtual()){
unsigned vreg = base_reg_op->getVRegNum();
if(color_map.count(vreg)) {
// 如果基址vreg被成功着色替换
base_reg_op->setPReg(color_map.at(vreg));
} else if (spilled_vregs.count(vreg)) {
// 如果基址vreg被溢出替换为t6
base_reg_op->setPReg(PhysicalReg::T6);
}
unsigned vreg = base_reg_op->getVRegNum();
if(color_map.count(vreg)) {
// 如果基址vreg被成功着色替换
PhysicalReg preg = color_map.at(vreg);
base_reg_op->setPReg(preg);
} else if (spilled_vregs.count(vreg)) {
// 如果基址vreg被溢出替换为t6
base_reg_op->setPReg(PhysicalReg::T6);
}
}
}
}

View File

@ -18,8 +18,24 @@ namespace sysy {
// 物理寄存器定义
enum class PhysicalReg {
ZERO, RA, SP, GP, TP, T0, T1, T2, S0, S1, A0, A1, A2, A3, A4, A5, A6, A7, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, T3, T4, T5, T6,
F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31
// --- 特殊功能寄存器 ---
ZERO, RA, SP, GP, TP,
// --- 整数寄存器 (按调用约定分组) ---
// 临时寄存器 (调用者保存)
T0, T1, T2, T3, T4, T5, T6,
// 保存寄存器 (被调用者保存)
S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11,
// 参数/返回值寄存器 (调用者保存)
A0, A1, A2, A3, A4, A5, A6, A7,
// --- 浮点寄存器 ---
// (保持您原有的 F0-F31 命名)
F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11,
F12, F13, F14, F15, F16, F17, F18, F19, F20, F21,
F22, F23, F24, F25, F26, F27, F28, F29, F30, F31
};
// RISC-V 指令操作码枚举
@ -173,6 +189,7 @@ struct StackFrameInfo {
int total_size = 0; // 总大小
std::map<unsigned, int> alloca_offsets; // <AllocaInst的vreg, 栈偏移>
std::map<unsigned, int> spill_offsets; // <溢出vreg, 栈偏移>
std::set<PhysicalReg> used_callee_saved_regs; // 使用的保存寄存器
};
// 机器函数

View File

@ -31,6 +31,20 @@ public:
};
/**
* @class CalleeSavedHandler
* @brief 处理被调用者保存寄存器(Callee-Saved Registers)的Pass。
* * 这个Pass在寄存器分配之后运行。它的主要职责是
* 1. 扫描整个函数,找出所有被使用的 `s` 系列寄存器。
* 2. 在函数序言中插入 `sd` 指令来保存这些寄存器。
* 3. 在函数结尾ret指令前插入 `ld` 指令来恢复这些寄存器。
* 4. 正确计算因保存这些寄存器而需要的额外栈空间并更新StackFrameInfo。
*/
class CalleeSavedHandler : public BackendPass {
public:
void runOnMachineFunction(MachineFunction* mfunc) override;
};
// --- 寄存器分配后优化 ---
/**

View File

@ -56,8 +56,6 @@ private:
// 存储vreg到IR Value*的反向映射
// 这个map将在run()函数开始时被填充并在rewriteFunction()中使用。
std::map<unsigned, Value*> vreg_to_value_map;
std::set<PhysicalReg> used_callee_saved_regs;
// 用于计算类型大小的辅助函数
unsigned getTypeSizeInBytes(Type* type);