[backend]继续增强寄存器分配器健壮性

This commit is contained in:
Lixuanwang
2025-08-05 13:50:55 +08:00
parent f2477c4af3
commit 32684d8255
8 changed files with 707 additions and 214 deletions

View File

@@ -0,0 +1,61 @@
#ifndef RISCV64_BASICBLOCKALLOC_H
#define RISCV64_BASICBLOCKALLOC_H
#include "RISCv64LLIR.h"
#include "RISCv64ISel.h"
#include <set>
#include <map>
#include <vector>
namespace sysy {
/**
* @class RISCv64BasicBlockAlloc
* @brief 一个有状态的、基本块级的贪心寄存器分配器。
*
* 该分配器作为简单但可靠的实现,它逐个处理基本块,并在块内尽可能地
* 将虚拟寄存器的值保留在物理寄存器中,以减少不必要的内存访问。
*/
class RISCv64BasicBlockAlloc {
public:
RISCv64BasicBlockAlloc(MachineFunction* mfunc);
void run();
private:
void computeLiveness();
void processBasicBlock(MachineBasicBlock* mbb);
void assignStackSlotsForAllVRegs();
// 核心分配函数
PhysicalReg ensureInReg(unsigned vreg, std::vector<std::unique_ptr<MachineInstr>>& new_instrs);
PhysicalReg allocReg(unsigned vreg, std::vector<std::unique_ptr<MachineInstr>>& new_instrs);
PhysicalReg findFreeReg(bool is_fp);
PhysicalReg spillReg(bool is_fp, std::vector<std::unique_ptr<MachineInstr>>& new_instrs);
// 状态跟踪(每个基本块开始时都会重置)
std::map<unsigned, PhysicalReg> vreg_to_preg; // 当前vreg到物理寄存器的映射
std::map<PhysicalReg, unsigned> preg_to_vreg; // 反向映射
std::set<PhysicalReg> dirty_pregs; // 被修改过、需要写回的物理寄存器
// 分配器全局信息
MachineFunction* MFunc;
RISCv64ISel* ISel;
std::map<unsigned, PhysicalReg> abi_vreg_map; // 函数参数的ABI寄存器映射
// 寄存器池和循环索引
std::vector<PhysicalReg> int_temps;
std::vector<PhysicalReg> fp_temps;
int int_temp_idx = 0;
int fp_temp_idx = 0;
// 辅助函数
PhysicalReg getNextIntTemp();
PhysicalReg getNextFpTemp();
// 活性分析结果
std::map<const MachineBasicBlock*, std::set<unsigned>> live_out;
};
} // namespace sysy
#endif // RISCV64_BASICBLOCKALLOC_H

View File

@@ -255,6 +255,19 @@ public:
void addOperand(std::unique_ptr<MachineOperand> operand) {
operands.push_back(std::move(operand));
}
/**
* @brief (为紧急溢出模式添加)将指令中所有对特定虚拟寄存器的引用替换为指定的物理寄存器。
* * @param old_vreg 需要被替换的虚拟寄存器号。
* @param preg 用于替换的物理寄存器。
*/
void replaceVRegWithPReg(unsigned old_vreg, PhysicalReg preg);
/**
* @brief (为常规溢出模式添加)根据提供的映射表,重映射指令中的虚拟寄存器。
* * @param use_remap 一个从旧vreg到新vreg的映射用于指令的use操作数。
* @param def_remap 一个从旧vreg到新vreg的映射用于指令的def操作数。
*/
void remapVRegs(const std::map<unsigned, unsigned>& use_remap, const std::map<unsigned, unsigned>& def_remap);
private:
RVOpcodes opcode;
std::vector<std::unique_ptr<MachineOperand>> operands;
@@ -319,6 +332,22 @@ private:
std::vector<std::unique_ptr<MachineBasicBlock>> blocks;
StackFrameInfo frame_info;
};
inline bool isMemoryOp(RVOpcodes opcode) {
switch (opcode) {
case RVOpcodes::LB: case RVOpcodes::LH: case RVOpcodes::LW: case RVOpcodes::LD:
case RVOpcodes::LBU: case RVOpcodes::LHU: case RVOpcodes::LWU:
case RVOpcodes::SB: case RVOpcodes::SH: case RVOpcodes::SW: case RVOpcodes::SD:
case RVOpcodes::FLW:
case RVOpcodes::FSW:
case RVOpcodes::FLD:
case RVOpcodes::FSD:
return true;
default:
return false;
}
}
void getInstrUseDef(const MachineInstr* instr, std::set<unsigned>& use, std::set<unsigned>& def);
} // namespace sysy

View File

@@ -36,7 +36,7 @@ struct LiveInterval {
class RISCv64LinearScan {
public:
RISCv64LinearScan(MachineFunction* mfunc);
void run();
bool run();
private:
// --- 核心算法流程 ---
@@ -45,11 +45,9 @@ private:
bool linearScan();
void rewriteProgram();
void applyAllocation();
void chooseRegForInterval(LiveInterval* current);
void spillAtInterval(LiveInterval* current);
// --- 辅助函数 ---
void getInstrUseDef(const MachineInstr* instr, std::set<unsigned>& use, std::set<unsigned>& def);
bool isFPVReg(unsigned vreg) const;
void collectUsedCalleeSavedRegs();
@@ -66,11 +64,12 @@ private:
std::set<unsigned> spilled_vregs; // 记录在本轮被决定溢出的vreg
bool conservative_spill_mode = false;
const PhysicalReg SPILL_TEMP_REG = PhysicalReg::T4;
// --- 寄存器池和分配结果 ---
std::vector<PhysicalReg> allocable_int_regs;
std::vector<PhysicalReg> allocable_fp_regs;
std::set<PhysicalReg> free_int_regs;
std::set<PhysicalReg> free_fp_regs;
std::map<unsigned, PhysicalReg> vreg_to_preg_map;
std::map<unsigned, PhysicalReg> abi_vreg_map;