595 lines
27 KiB
C++
595 lines
27 KiB
C++
#include "RISCv64RegAlloc.h"
|
||
#include "RISCv64ISel.h"
|
||
#include <algorithm>
|
||
#include <vector>
|
||
#include <iostream> // For DEBUG output
|
||
#include <cassert> // For assert
|
||
|
||
namespace sysy {
|
||
|
||
RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
|
||
allocable_int_regs = {
|
||
PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3,
|
||
PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6,
|
||
PhysicalReg::A0, PhysicalReg::A1, PhysicalReg::A2, PhysicalReg::A3,
|
||
PhysicalReg::A4, PhysicalReg::A5, PhysicalReg::A6, PhysicalReg::A7,
|
||
PhysicalReg::S0, PhysicalReg::S1, PhysicalReg::S2, PhysicalReg::S3,
|
||
PhysicalReg::S4, PhysicalReg::S5, PhysicalReg::S6, PhysicalReg::S7,
|
||
PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11,
|
||
};
|
||
|
||
// 映射物理寄存器到特殊的虚拟寄存器ID,用于干扰图中的物理寄存器节点
|
||
// 确保这些特殊ID不会与vreg_counter生成的常规虚拟寄存器ID冲突
|
||
for (PhysicalReg preg : allocable_int_regs) {
|
||
preg_to_vreg_id_map[preg] = static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID) + static_cast<unsigned>(preg);
|
||
}
|
||
}
|
||
|
||
// 寄存器分配的主入口点
|
||
void RISCv64RegAlloc::run() {
|
||
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
|
||
handleCallingConvention();
|
||
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
|
||
eliminateFrameIndices();
|
||
// 阶段 3: 活跃性分析
|
||
analyzeLiveness();
|
||
// 阶段 4: 构建干扰图(包含CALL指令对调用者保存寄存器的影响)
|
||
buildInterferenceGraph();
|
||
// 阶段 5: 图着色算法分配物理寄存器
|
||
colorGraph();
|
||
// 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器)
|
||
rewriteFunction();
|
||
}
|
||
|
||
/**
|
||
* @brief 处理调用约定,预先为函数参数分配物理寄存器。
|
||
*/
|
||
void RISCv64RegAlloc::handleCallingConvention() {
|
||
Function* F = MFunc->getFunc();
|
||
RISCv64ISel* isel = MFunc->getISel();
|
||
|
||
// 获取函数的Argument对象列表
|
||
if (F) {
|
||
auto& args = F->getArguments();
|
||
// RISC-V RV64G调用约定:前8个整型/指针参数通过 a0-a7 传递
|
||
int arg_idx = 0;
|
||
// 遍历 AllocaInst* 列表
|
||
for (Argument* arg : args) {
|
||
if (arg_idx >= 8) {
|
||
break;
|
||
}
|
||
// 获取该 Argument 对象对应的虚拟寄存器ID
|
||
// 通过 MachineFunction -> RISCv64ISel -> vreg_map 来获取
|
||
const auto& vreg_map_from_isel = MFunc->getISel()->getVRegMap();
|
||
assert(vreg_map_from_isel.count(arg) && "Argument not found in ISel's vreg_map!");
|
||
// 1. 获取该 Argument 对象对应的虚拟寄存器
|
||
unsigned vreg = isel->getVReg(arg);
|
||
|
||
// 2. 根据参数索引,确定对应的物理寄存器 (a0, a1, ...)
|
||
auto preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + arg_idx);
|
||
|
||
// 3. 在 color_map 中,将 vreg "预着色" 为对应的物理寄存器
|
||
color_map[vreg] = preg;
|
||
|
||
arg_idx++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 消除帧索引,为局部变量和栈参数分配栈偏移量,并展开伪指令。
|
||
*/
|
||
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();
|
||
|
||
// 在处理局部变量前,首先为栈参数计算偏移量。
|
||
if (F) {
|
||
int arg_idx = 0;
|
||
for (Argument* arg : F->getArguments()) {
|
||
// 我们只关心第8个索引及之后的参数(即第9个参数开始)
|
||
if (arg_idx >= 8) {
|
||
// 计算偏移量:第一个栈参数(idx=8)在0(s0),第二个(idx=9)在8(s0),以此类推。
|
||
int offset = (arg_idx - 8) * 8;
|
||
unsigned vreg = isel->getVReg(arg);
|
||
|
||
// 将这个vreg和它的栈偏移存入map。
|
||
// 我们可以复用alloca_offsets,因为它们都代表“vreg到栈偏移”的映射。
|
||
frame_info.alloca_offsets[vreg] = offset;
|
||
}
|
||
arg_idx++;
|
||
}
|
||
}
|
||
|
||
// 处理局部变量
|
||
// 遍历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);
|
||
|
||
// RISC-V要求栈地址8字节对齐
|
||
size = (size + 7) & ~7;
|
||
if (size == 0) size = 8; // 至少分配8字节
|
||
|
||
current_offset += size;
|
||
unsigned alloca_vreg = isel->getVReg(alloca);
|
||
// 局部变量使用相对于s0的负向偏移
|
||
frame_info.alloca_offsets[alloca_vreg] = -current_offset;
|
||
}
|
||
}
|
||
}
|
||
frame_info.locals_size = current_offset;
|
||
|
||
// 遍历所有机器指令,将伪指令展开为真实指令
|
||
for (auto& mbb : MFunc->getBlocks()) {
|
||
std::vector<std::unique_ptr<MachineInstr>> new_instructions;
|
||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||
RVOpcodes opcode = instr_ptr->getOpcode();
|
||
|
||
// --- MODIFICATION START: 处理区分宽度的伪指令 ---
|
||
if (opcode == RVOpcodes::FRAME_LOAD_W || opcode == RVOpcodes::FRAME_LOAD_D) {
|
||
// 确定要生成的真实加载指令是 lw 还是 ld
|
||
RVOpcodes real_load_op = (opcode == RVOpcodes::FRAME_LOAD_W) ? RVOpcodes::LW : RVOpcodes::LD;
|
||
|
||
auto& operands = instr_ptr->getOperands();
|
||
unsigned dest_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||
auto addr_vreg = isel->getNewVReg();
|
||
|
||
// 展开为: addi addr_vreg, s0, offset
|
||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||
addi->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||
new_instructions.push_back(std::move(addi));
|
||
|
||
// 展开为: lw/ld dest_vreg, 0(addr_vreg)
|
||
auto load_instr = std::make_unique<MachineInstr>(real_load_op);
|
||
load_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||
load_instr->addOperand(std::make_unique<MemOperand>(
|
||
std::make_unique<RegOperand>(addr_vreg),
|
||
std::make_unique<ImmOperand>(0)));
|
||
new_instructions.push_back(std::move(load_instr));
|
||
|
||
} else if (opcode == RVOpcodes::FRAME_STORE_W || opcode == RVOpcodes::FRAME_STORE_D) {
|
||
// 确定要生成的真实存储指令是 sw 还是 sd
|
||
RVOpcodes real_store_op = (opcode == RVOpcodes::FRAME_STORE_W) ? RVOpcodes::SW : RVOpcodes::SD;
|
||
|
||
auto& operands = instr_ptr->getOperands();
|
||
unsigned src_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||
auto addr_vreg = isel->getNewVReg();
|
||
|
||
// 展开为: addi addr_vreg, s0, offset
|
||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||
addi->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||
new_instructions.push_back(std::move(addi));
|
||
|
||
// 展开为: sw/sd src_vreg, 0(addr_vreg)
|
||
auto store_instr = std::make_unique<MachineInstr>(real_store_op);
|
||
store_instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||
store_instr->addOperand(std::make_unique<MemOperand>(
|
||
std::make_unique<RegOperand>(addr_vreg),
|
||
std::make_unique<ImmOperand>(0)));
|
||
new_instructions.push_back(std::move(store_instr));
|
||
|
||
} else if (instr_ptr->getOpcode() == RVOpcodes::FRAME_ADDR) {
|
||
auto& operands = instr_ptr->getOperands();
|
||
unsigned dest_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||
|
||
// 将 `frame_addr rd, rs` 展开为 `addi rd, s0, offset`
|
||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||
addi->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||
new_instructions.push_back(std::move(addi));
|
||
} else {
|
||
new_instructions.push_back(std::move(instr_ptr));
|
||
}
|
||
// --- MODIFICATION END ---
|
||
}
|
||
mbb->getInstructions() = std::move(new_instructions);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 计算给定 MachineInstr 的 Use (读取) 和 Def (写入) 寄存器集合。
|
||
* 这是活跃性分析的基础。
|
||
* @param instr 要分析的机器指令。
|
||
* @param use 存储 Use 寄存器(虚拟寄存器 ID)的集合。
|
||
* @param def 存储 Def 寄存器(虚拟寄存器 ID)的集合。
|
||
*/
|
||
void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& def) {
|
||
bool first_reg_is_def = true; // 默认情况下,指令的第一个寄存器操作数是定义 (def)
|
||
auto opcode = instr->getOpcode();
|
||
|
||
// 1. 特殊指令的 `is_def` 标志调整
|
||
// 这些指令的第一个寄存器操作数是源操作数 (use),而不是目标操作数 (def)。
|
||
if (opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||
opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||
opcode == RVOpcodes::RET || opcode == RVOpcodes::J) {
|
||
first_reg_is_def = false;
|
||
}
|
||
|
||
// JAL 和 JALR 指令定义 ra (x1)
|
||
if (opcode == RVOpcodes::JAL || opcode == RVOpcodes::JALR) {
|
||
// 使用 ra 对应的特殊虚拟寄存器ID
|
||
def.insert(static_cast<unsigned>(PhysicalReg::RA));
|
||
first_reg_is_def = false; // JAL/JALR 的第一个操作数是 ra,已经处理为 def
|
||
}
|
||
|
||
// 2. CALL 指令的特殊处理
|
||
if (opcode == RVOpcodes::CALL) {
|
||
// 1.1 处理返回值 (def)
|
||
// 约定:如果CALL指令有返回值,IR阶段会将返回值vreg作为指令的第一个操作数。
|
||
if (!instr->getOperands().empty() && instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) {
|
||
auto reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||
if (reg_op->isVirtual()) {
|
||
def.insert(reg_op->getVRegNum());
|
||
}
|
||
}
|
||
|
||
// 1.2 处理参数 (use)
|
||
// 参数通常是指令的后续操作数
|
||
bool first_operand_processed = false; // 用于跳过已作为def处理的返回值
|
||
for (const auto& op : instr->getOperands()) {
|
||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||
if (!first_operand_processed) { // 如果是第一个操作数
|
||
first_operand_processed = true;
|
||
// 如果第一个操作数是返回值(已被加入def),则跳过
|
||
if (def.count(static_cast<RegOperand*>(op.get())->getVRegNum())) {
|
||
continue;
|
||
}
|
||
}
|
||
// 否则,该寄存器是 use
|
||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||
if (reg_op->isVirtual()) {
|
||
use.insert(reg_op->getVRegNum());
|
||
}
|
||
}
|
||
}
|
||
|
||
// **重要**: CALL指令隐式定义(杀死)了所有调用者保存的寄存器。
|
||
// **这部分逻辑不在getInstrUseDef中直接处理**。
|
||
// 而是通过`buildInterferenceGraph`中添加物理寄存器节点与活跃虚拟寄存器之间的干扰边来完成。
|
||
// 这样 Liveness Analysis 可以在虚拟寄存器层面进行,而物理寄存器干扰的复杂性则留给干扰图。
|
||
|
||
return; // CALL 指令处理完毕,直接返回
|
||
}
|
||
|
||
// 3. 对其他所有指令的通用处理逻辑
|
||
for (const auto& op : instr->getOperands()) {
|
||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||
if (reg_op->isVirtual()) { // 只有虚拟寄存器才需要处理 Use/Def
|
||
// 如果是第一个寄存器操作数,且指令类型表明它是定义 (def),则加入 def 集合
|
||
// 否则,它是 use (读取)
|
||
if (first_reg_is_def) {
|
||
def.insert(reg_op->getVRegNum());
|
||
first_reg_is_def = false; // 确保每条指令只定义一个目标寄存器
|
||
} else {
|
||
use.insert(reg_op->getVRegNum());
|
||
}
|
||
}
|
||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||
// 内存操作数 `offset(base)` 中的 `base` 寄存器是 `use`
|
||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||
if (mem_op->getBase()->isVirtual()) {
|
||
use.insert(mem_op->getBase()->getVRegNum());
|
||
}
|
||
// 对于存储内存指令 (SW, SD),要存储的值(第一个操作数)也是 `use`
|
||
if ((opcode == RVOpcodes::SW || opcode == RVOpcodes::SD) &&
|
||
!instr->getOperands().empty() && // 确保有操作数
|
||
instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) { // 且第一个操作数是寄存器
|
||
auto src_reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||
if (src_reg_op->isVirtual()) {
|
||
use.insert(src_reg_op->getVRegNum());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 计算一个类型在内存中占用的字节数。
|
||
* @param type 需要计算大小的IR类型。
|
||
* @return 该类型占用的字节数。
|
||
*/
|
||
unsigned RISCv64RegAlloc::getTypeSizeInBytes(Type* type) {
|
||
if (!type) {
|
||
assert(false && "Cannot get size of a null type.");
|
||
return 0;
|
||
}
|
||
|
||
switch (type->getKind()) {
|
||
// 对于SysY语言,基本类型int和float都占用4字节
|
||
case Type::kInt:
|
||
case Type::kFloat:
|
||
return 4;
|
||
|
||
// 指针类型在RISC-V 64位架构下占用8字节
|
||
// 虽然SysY没有'int*'语法,但数组变量在IR层面本身就是指针类型
|
||
case Type::kPointer:
|
||
return 8;
|
||
|
||
// 数组类型的总大小 = 元素数量 * 单个元素的大小
|
||
case Type::kArray: {
|
||
auto arrayType = type->as<ArrayType>();
|
||
// 递归调用以计算元素大小
|
||
return arrayType->getNumElements() * getTypeSizeInBytes(arrayType->getElementType());
|
||
}
|
||
|
||
// 其他类型,如Void, Label等不占用栈空间,或者不应该出现在这里
|
||
default:
|
||
// 如果遇到未处理的类型,触发断言,方便调试
|
||
assert(false && "Unsupported type for size calculation.");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
void RISCv64RegAlloc::analyzeLiveness() {
|
||
bool changed = true;
|
||
while (changed) {
|
||
changed = false;
|
||
for (auto it = MFunc->getBlocks().rbegin(); it != MFunc->getBlocks().rend(); ++it) {
|
||
auto& mbb = *it;
|
||
LiveSet live_out;
|
||
for (auto succ : mbb->successors) {
|
||
if (!succ->getInstructions().empty()) {
|
||
auto first_instr = succ->getInstructions().front().get();
|
||
if (live_in_map.count(first_instr)) {
|
||
live_out.insert(live_in_map.at(first_instr).begin(), live_in_map.at(first_instr).end());
|
||
}
|
||
}
|
||
}
|
||
|
||
for (auto instr_it = mbb->getInstructions().rbegin(); instr_it != mbb->getInstructions().rend(); ++instr_it) {
|
||
MachineInstr* instr = instr_it->get();
|
||
LiveSet old_live_in = live_in_map[instr];
|
||
live_out_map[instr] = live_out;
|
||
|
||
LiveSet use, def;
|
||
getInstrUseDef(instr, use, def);
|
||
|
||
LiveSet live_in = use;
|
||
LiveSet diff = live_out;
|
||
for (auto vreg : def) {
|
||
diff.erase(vreg);
|
||
}
|
||
live_in.insert(diff.begin(), diff.end());
|
||
live_in_map[instr] = live_in;
|
||
|
||
live_out = live_in;
|
||
|
||
if (live_in_map[instr] != old_live_in) {
|
||
changed = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void RISCv64RegAlloc::buildInterferenceGraph() {
|
||
std::set<unsigned> all_vregs;
|
||
// 收集所有虚拟寄存器和物理寄存器在干扰图中的节点ID
|
||
for (auto& mbb : MFunc->getBlocks()) {
|
||
for(auto& instr : mbb->getInstructions()) {
|
||
LiveSet use, def;
|
||
getInstrUseDef(instr.get(), use, def);
|
||
for(auto u : use) all_vregs.insert(u);
|
||
for(auto d : def) all_vregs.insert(d);
|
||
}
|
||
}
|
||
// 添加所有物理寄存器对应的特殊虚拟寄存器ID到all_vregs,作为干扰图节点
|
||
for (auto preg : allocable_int_regs) {
|
||
all_vregs.insert(preg_to_vreg_id_map.at(preg));
|
||
}
|
||
|
||
|
||
for (auto vreg : all_vregs) { interference_graph[vreg] = {}; }
|
||
|
||
for (auto& mbb : MFunc->getBlocks()) {
|
||
for (auto& instr : mbb->getInstructions()) {
|
||
LiveSet def, use;
|
||
getInstrUseDef(instr.get(), use, def);
|
||
const LiveSet& live_out = live_out_map.at(instr.get());
|
||
|
||
// 标准干扰图构建:def 与 live_out 中的其他变量干扰
|
||
for (unsigned d : def) {
|
||
for (unsigned l : live_out) {
|
||
if (d != l) {
|
||
interference_graph[d].insert(l);
|
||
interference_graph[l].insert(d);
|
||
}
|
||
}
|
||
}
|
||
|
||
// *** 核心修改点:处理 CALL 指令的隐式 def ***
|
||
if (instr->getOpcode() == RVOpcodes::CALL) {
|
||
// CALL 指令会定义(杀死)所有调用者保存的寄存器。
|
||
// 因此,所有调用者保存的物理寄存器都与 CALL 指令的 live_out 中的所有变量冲突。
|
||
const std::vector<PhysicalReg>& caller_saved_regs = getCallerSavedIntRegs();
|
||
for (PhysicalReg cs_reg : caller_saved_regs) {
|
||
unsigned cs_vreg_id = preg_to_vreg_id_map.at(cs_reg); // 获取物理寄存器对应的特殊vreg ID
|
||
|
||
// 将这个物理寄存器节点与 CALL 指令的 live_out 中的所有虚拟寄存器添加干扰边。
|
||
for (unsigned live_vreg_out : live_out) {
|
||
if (cs_vreg_id != live_vreg_out) { // 避免自己和自己干扰
|
||
interference_graph[cs_vreg_id].insert(live_vreg_out);
|
||
interference_graph[live_vreg_out].insert(cs_vreg_id);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void RISCv64RegAlloc::colorGraph() {
|
||
std::vector<unsigned> sorted_vregs;
|
||
for (auto const& [vreg, neighbors] : interference_graph) {
|
||
if (color_map.find(vreg) == color_map.end()) {
|
||
sorted_vregs.push_back(vreg);
|
||
}
|
||
}
|
||
|
||
// 排序
|
||
std::sort(sorted_vregs.begin(), sorted_vregs.end(), [&](unsigned a, unsigned b) {
|
||
return interference_graph[a].size() > interference_graph[b].size();
|
||
});
|
||
|
||
// 着色
|
||
for (unsigned vreg : sorted_vregs) {
|
||
std::set<PhysicalReg> used_colors;
|
||
for (unsigned neighbor : interference_graph.at(vreg)) {
|
||
if (color_map.count(neighbor)) {
|
||
used_colors.insert(color_map.at(neighbor));
|
||
}
|
||
}
|
||
|
||
bool colored = false;
|
||
for (PhysicalReg preg : allocable_int_regs) {
|
||
if (used_colors.find(preg) == used_colors.end()) {
|
||
color_map[vreg] = preg;
|
||
colored = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!colored) {
|
||
spilled_vregs.insert(vreg);
|
||
}
|
||
}
|
||
}
|
||
|
||
void RISCv64RegAlloc::rewriteFunction() {
|
||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||
int current_offset = frame_info.locals_size;
|
||
|
||
// --- FIX 1: 动态计算溢出槽大小 ---
|
||
// 根据溢出虚拟寄存器的真实类型,为其在栈上分配正确大小的空间。
|
||
for (unsigned vreg : spilled_vregs) {
|
||
// 从反向映射中查找 vreg 对应的 IR Value
|
||
assert(vreg_to_value_map.count(vreg) && "Spilled vreg not found in map!");
|
||
Value* val = vreg_to_value_map.at(vreg);
|
||
|
||
// 使用辅助函数获取类型大小
|
||
int size = getTypeSizeInBytes(val->getType());
|
||
|
||
// 保持栈8字节对齐
|
||
current_offset += size;
|
||
current_offset = (current_offset + 7) & ~7;
|
||
|
||
frame_info.spill_offsets[vreg] = -current_offset;
|
||
}
|
||
frame_info.spill_size = current_offset - frame_info.locals_size;
|
||
|
||
for (auto& mbb : MFunc->getBlocks()) {
|
||
std::vector<std::unique_ptr<MachineInstr>> new_instructions;
|
||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||
LiveSet use, def;
|
||
getInstrUseDef(instr_ptr.get(), use, def);
|
||
|
||
// --- FIX 2: 为溢出的 'use' 操作数插入正确的加载指令 ---
|
||
for (unsigned vreg : use) {
|
||
if (spilled_vregs.count(vreg)) {
|
||
// 同样地,根据 vreg 的类型决定使用 lw 还是 ld
|
||
assert(vreg_to_value_map.count(vreg));
|
||
Value* val = vreg_to_value_map.at(vreg);
|
||
RVOpcodes load_op = val->getType()->isPointer() ? RVOpcodes::LD : RVOpcodes::LW;
|
||
|
||
int offset = frame_info.spill_offsets.at(vreg);
|
||
auto load = std::make_unique<MachineInstr>(load_op);
|
||
load->addOperand(std::make_unique<RegOperand>(vreg));
|
||
load->addOperand(std::make_unique<MemOperand>(
|
||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||
std::make_unique<ImmOperand>(offset)
|
||
));
|
||
new_instructions.push_back(std::move(load));
|
||
}
|
||
}
|
||
|
||
new_instructions.push_back(std::move(instr_ptr));
|
||
|
||
// --- FIX 3: 为溢出的 'def' 操作数插入正确的存储指令 ---
|
||
for (unsigned vreg : def) {
|
||
if (spilled_vregs.count(vreg)) {
|
||
// 根据 vreg 的类型决定使用 sw 还是 sd
|
||
assert(vreg_to_value_map.count(vreg));
|
||
Value* val = vreg_to_value_map.at(vreg);
|
||
RVOpcodes store_op = val->getType()->isPointer() ? RVOpcodes::SD : RVOpcodes::SW;
|
||
|
||
int offset = frame_info.spill_offsets.at(vreg);
|
||
auto store = std::make_unique<MachineInstr>(store_op);
|
||
store->addOperand(std::make_unique<RegOperand>(vreg));
|
||
store->addOperand(std::make_unique<MemOperand>(
|
||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||
std::make_unique<ImmOperand>(offset)
|
||
));
|
||
new_instructions.push_back(std::move(store));
|
||
}
|
||
}
|
||
}
|
||
mbb->getInstructions() = std::move(new_instructions);
|
||
}
|
||
|
||
// 最后的虚拟寄存器到物理寄存器的替换过程保持不变
|
||
for (auto& mbb : MFunc->getBlocks()) {
|
||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||
for (auto& op_ptr : instr_ptr->getOperands()) {
|
||
|
||
// 情况一:操作数本身就是一个寄存器 (例如 add rd, rs1, rs2 中的所有操作数)
|
||
if(op_ptr->getKind() == MachineOperand::KIND_REG) {
|
||
auto reg_op = static_cast<RegOperand*>(op_ptr.get());
|
||
if (reg_op->isVirtual()) {
|
||
unsigned vreg = reg_op->getVRegNum();
|
||
if (color_map.count(vreg)) {
|
||
PhysicalReg preg = color_map.at(vreg);
|
||
reg_op->setPReg(preg);
|
||
} else if (spilled_vregs.count(vreg)) {
|
||
// 如果vreg被溢出,替换为专用的溢出物理寄存器t6
|
||
reg_op->setPReg(PhysicalReg::T6);
|
||
}
|
||
}
|
||
}
|
||
// 情况二:操作数是一个内存地址 (例如 lw rd, offset(rs1) 中的 offset(rs1))
|
||
else if (op_ptr->getKind() == MachineOperand::KIND_MEM) {
|
||
auto mem_op = static_cast<MemOperand*>(op_ptr.get());
|
||
// 获取内存操作数内部的“基址寄存器”
|
||
auto base_reg_op = mem_op->getBase();
|
||
|
||
// 对这个基址寄存器,执行与情况一完全相同的替换逻辑
|
||
if(base_reg_op->isVirtual()){
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
} // namespace sysy
|