Compare commits
4 Commits
peephole
...
backend-re
| Author | SHA1 | Date | |
|---|---|---|---|
| 792dc9c1f6 | |||
| 429e477776 | |||
| 2e8b564d8f | |||
| af318b6c0e |
@ -34,6 +34,7 @@ add_executable(sysyc
|
||||
RISCv64RegAlloc.cpp
|
||||
RISCv64AsmPrinter.cpp
|
||||
RISCv64Passes.cpp
|
||||
RISCv64LLIR.cpp
|
||||
)
|
||||
|
||||
# 设置 include 路径,包含 ANTLR 运行时库和项目头文件
|
||||
|
||||
@ -16,13 +16,59 @@ std::string RISCv64CodeGen::code_gen() {
|
||||
std::string RISCv64CodeGen::module_gen() {
|
||||
std::stringstream ss;
|
||||
|
||||
// 1. 处理全局变量 (.data段)
|
||||
if (!module->getGlobals().empty()) {
|
||||
ss << ".data\n";
|
||||
for (const auto& global : module->getGlobals()) {
|
||||
// --- [新逻辑] 步骤1:将全局变量分为.data和.bss两组 ---
|
||||
std::vector<GlobalValue*> data_globals;
|
||||
std::vector<GlobalValue*> bss_globals;
|
||||
|
||||
for (const auto& global_ptr : module->getGlobals()) {
|
||||
GlobalValue* global = global_ptr.get();
|
||||
const auto& init_values = global->getInitValues();
|
||||
|
||||
// 判断是否为大型零初始化数组,以便放入.bss段
|
||||
bool is_large_zero_array = false;
|
||||
// 规则:初始化列表只有一项,且该项是值为0的整数,且数量大于一个阈值(例如16)
|
||||
if (init_values.getValues().size() == 1) {
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(init_values.getValues()[0])) {
|
||||
if (const_val->isInt() && const_val->getInt() == 0 && init_values.getNumbers()[0] > 16) {
|
||||
is_large_zero_array = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_large_zero_array) {
|
||||
bss_globals.push_back(global);
|
||||
} else {
|
||||
data_globals.push_back(global);
|
||||
}
|
||||
}
|
||||
|
||||
// --- [新逻辑] 步骤2:生成 .bss 段的代码 ---
|
||||
if (!bss_globals.empty()) {
|
||||
ss << ".bss\n"; // 切换到 .bss 段
|
||||
for (GlobalValue* global : bss_globals) {
|
||||
// 获取数组总大小(元素个数 * 元素大小)
|
||||
// 在SysY中,我们假设元素都是4字节(int或float)
|
||||
unsigned count = global->getInitValues().getNumbers()[0];
|
||||
unsigned total_size = count * 4;
|
||||
|
||||
ss << " .align 3\n"; // 8字节对齐 (2^3)
|
||||
ss << ".globl " << global->getName() << "\n";
|
||||
ss << ".type " << global->getName() << ", @object\n";
|
||||
ss << ".size " << global->getName() << ", " << total_size << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
// 使用 .space 指令来预留指定大小的零填充空间
|
||||
ss << " .space " << total_size << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// --- [旧逻辑保留] 步骤3:生成 .data 段的代码 ---
|
||||
if (!data_globals.empty()) {
|
||||
ss << ".data\n"; // 切换到 .data 段
|
||||
for (GlobalValue* global : data_globals) {
|
||||
ss << ".globl " << global->getName() << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
const auto& init_values = global->getInitValues();
|
||||
// 使用您原有的逻辑来处理显式初始化的值
|
||||
for (size_t i = 0; i < init_values.getValues().size(); ++i) {
|
||||
auto val = init_values.getValues()[i];
|
||||
auto count = init_values.getNumbers()[i];
|
||||
@ -41,7 +87,7 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理函数 (.text段)
|
||||
// --- 处理函数 (.text段) 的逻辑保持不变 ---
|
||||
if (!module->getFunctions().empty()) {
|
||||
ss << ".text\n";
|
||||
for (const auto& func_pair : module->getFunctions()) {
|
||||
|
||||
6
src/RISCv64LLIR.cpp
Normal file
6
src/RISCv64LLIR.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "RISCv64LLIR.h"
|
||||
#include <vector>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
}
|
||||
@ -52,27 +52,25 @@ void PostRA_Scheduler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
}
|
||||
|
||||
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// 【最终方案】: 此 Pass 负责分析、分配栈空间并插入 callee-saved 寄存器的保存/恢复指令。
|
||||
// 它通过与 FrameInfo 协作,确保为 callee-saved 寄存器分配的空间与局部变量/溢出槽的空间不冲突。
|
||||
// 这样做可以使生成的 sd/ld 指令能被后续的优化 Pass (如 PostRA-Scheduler) 处理。
|
||||
|
||||
StackFrameInfo& frame_info = mfunc->getFrameInfo();
|
||||
std::set<PhysicalReg> used_callee_saved;
|
||||
|
||||
// 1. 扫描所有指令,找出被使用的s寄存器
|
||||
// 1. 扫描所有指令,找出被使用的s寄存器 (s1-s11)
|
||||
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) {
|
||||
if (preg >= PhysicalReg::S1 && 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) {
|
||||
@ -81,66 +79,72 @@ void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有使用s寄存器(除了可能作为帧指针的s0),则无需操作
|
||||
if (used_callee_saved.empty() || (used_callee_saved.size() == 1 && used_callee_saved.count(PhysicalReg::S0))) {
|
||||
return;
|
||||
|
||||
if (used_callee_saved.empty()) {
|
||||
frame_info.callee_saved_size = 0; // 确保大小被初始化
|
||||
return; // 无需操作
|
||||
}
|
||||
|
||||
// 将结果存入StackFrameInfo,供后续使用
|
||||
frame_info.used_callee_saved_regs = used_callee_saved;
|
||||
// 2. 计算为 callee-saved 寄存器分配的栈空间
|
||||
// 这里的关键是,偏移的基准点要在局部变量和溢出槽之下。
|
||||
int callee_saved_size = used_callee_saved.size() * 8;
|
||||
frame_info.callee_saved_size = callee_saved_size; // 将大小存入 FrameInfo
|
||||
|
||||
// 3. 计算无冲突的栈偏移
|
||||
// 栈向下增长,所以偏移是负数。
|
||||
// ra/s0 占用 -8 和 -16。局部变量和溢出区在它们之下。callee-saved 区在更下方。
|
||||
// 我们使用相对于 s0 的偏移。s0 将指向栈顶 (sp + total_size)。
|
||||
int base_offset = -16 - frame_info.locals_size - frame_info.spill_size;
|
||||
|
||||
// 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的位置
|
||||
// 4. 在函数序言插入保存指令
|
||||
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
auto prologue_end = entry_instrs.begin();
|
||||
|
||||
// 找到序言结束的位置(通常是addi s0, sp, size之后,但为了让优化器看到,我们插在更前面)
|
||||
// 合理的位置是在 IR 指令开始之前,即在任何非序言指令(如第一个标签)之前。
|
||||
// 为简单起见,我们直接插入到块的开头,后续重排 pass 会处理。
|
||||
// (更优的实现会寻找一个特定的插入点)
|
||||
|
||||
int current_offset = base_offset;
|
||||
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<RegOperand>(PhysicalReg::S0), // 基址为帧指针 s0
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
entry_instrs.insert(prologue_end, std::move(sd));
|
||||
// 从头部插入,但要放在函数标签之后
|
||||
entry_instrs.insert(entry_instrs.begin() + 1, std::move(sd));
|
||||
current_offset -= 8;
|
||||
}
|
||||
|
||||
// 3. 在函数结尾(ret之前)插入恢复指令
|
||||
// 5. 【已修复】在函数结尾(ret之前)插入恢复指令,使用反向遍历来避免迭代器失效
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
// 使用手动控制的反向循环
|
||||
for (auto it = mbb->getInstructions().end(); it != mbb->getInstructions().begin(); ) {
|
||||
// 在循环开始时就递减迭代器
|
||||
--it;
|
||||
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
// 以相反的顺序恢复
|
||||
current_offset = -16;
|
||||
int current_offset_load = base_offset;
|
||||
// 以相同的顺序恢复(从 s1 开始)
|
||||
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)
|
||||
std::make_unique<ImmOperand>(current_offset_load)
|
||||
));
|
||||
// 在 'it' (即 RET 指令) 之前插入。
|
||||
// 因为我们是反向遍历,所以这不会影响下一次循环的 'it'。
|
||||
mbb->getInstructions().insert(it, std::move(ld));
|
||||
current_offset_load -= 8;
|
||||
}
|
||||
break; // 处理完一个基本块的ret即可
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include "RISCv64ISel.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream> // For DEBUG output
|
||||
#include <cassert> // For assert
|
||||
|
||||
namespace sysy {
|
||||
|
||||
@ -11,19 +13,33 @@ RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
|
||||
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::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() {
|
||||
handleCallingConvention();
|
||||
eliminateFrameIndices();
|
||||
analyzeLiveness();
|
||||
buildInterferenceGraph();
|
||||
colorGraph();
|
||||
rewriteFunction();
|
||||
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
|
||||
handleCallingConvention();
|
||||
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
|
||||
eliminateFrameIndices();
|
||||
// 阶段 3: 活跃性分析
|
||||
analyzeLiveness();
|
||||
// 阶段 4: 构建干扰图(包含CALL指令对调用者保存寄存器的影响)
|
||||
buildInterferenceGraph();
|
||||
// 阶段 5: 图着色算法分配物理寄存器
|
||||
colorGraph();
|
||||
// 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器)
|
||||
rewriteFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,7 +59,10 @@ void RISCv64RegAlloc::handleCallingConvention() {
|
||||
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);
|
||||
|
||||
@ -58,6 +77,9 @@ void RISCv64RegAlloc::handleCallingConvention() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 消除帧索引,为局部变量和栈参数分配栈偏移量,并展开伪指令。
|
||||
*/
|
||||
void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
// 初始偏移量,为保存ra和s0留出空间。
|
||||
@ -185,30 +207,38 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算给定 MachineInstr 的 Use (读取) 和 Def (写入) 寄存器集合。
|
||||
* 这是活跃性分析的基础。
|
||||
* @param instr 要分析的机器指令。
|
||||
* @param use 存储 Use 寄存器(虚拟寄存器 ID)的集合。
|
||||
* @param def 存储 Def 寄存器(虚拟寄存器 ID)的集合。
|
||||
*/
|
||||
void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& def) {
|
||||
bool is_def = true;
|
||||
bool first_reg_is_def = true; // 默认情况下,指令的第一个寄存器操作数是定义 (def)
|
||||
auto opcode = instr->getOpcode();
|
||||
|
||||
// --- MODIFICATION START: 细化对指令的 use/def 定义 ---
|
||||
|
||||
// 对于没有定义目标寄存器的指令,预先设置 is_def = false
|
||||
// 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) {
|
||||
is_def = false;
|
||||
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
|
||||
}
|
||||
|
||||
// 对 CALL 指令进行特殊处理
|
||||
// 2. CALL 指令的特殊处理
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// CALL 指令的第一个操作数通常是目标函数标签,不是寄存器。
|
||||
// 它可能会有一个可选的返回值(def),以及一系列参数(use)。
|
||||
// 这里的处理假定 CALL 的机器指令操作数布局是:
|
||||
// [可选: dest_vreg (def)], [函数标签], [可选: arg1_vreg (use)], [可选: arg2_vreg (use)], ...
|
||||
|
||||
// 我们需要一种方法来识别哪些操作数是def,哪些是use。
|
||||
// 一个简单的约定:如果第一个操作数是寄存器,则它是def(返回值)。
|
||||
// 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()) {
|
||||
@ -216,14 +246,19 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有操作数,非第一个寄存器操作数均视为use
|
||||
bool first_reg_skipped = false;
|
||||
// 1.2 处理参数 (use)
|
||||
// 参数通常是指令的后续操作数
|
||||
bool first_operand_processed = false; // 用于跳过已作为def处理的返回值
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_reg_skipped) {
|
||||
first_reg_skipped = true;
|
||||
continue; // 跳过我们已经作为def处理的返回值
|
||||
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());
|
||||
@ -231,34 +266,43 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
}
|
||||
}
|
||||
|
||||
// **重要**: CALL指令还隐式定义(杀死)了所有调用者保存的寄存器。
|
||||
// 一个完整的实现会在这里将所有caller-saved寄存器标记为def,
|
||||
// 以确保任何跨调用存活的变量都不会被分配到这些寄存器中。
|
||||
// 这个简化的实现暂不处理隐式def,但这是未来优化的关键点。
|
||||
// **重要**: CALL指令隐式定义(杀死)了所有调用者保存的寄存器。
|
||||
// **这部分逻辑不在getInstrUseDef中直接处理**。
|
||||
// 而是通过`buildInterferenceGraph`中添加物理寄存器节点与活跃虚拟寄存器之间的干扰边来完成。
|
||||
// 这样 Liveness Analysis 可以在虚拟寄存器层面进行,而物理寄存器干扰的复杂性则留给干扰图。
|
||||
|
||||
return; // CALL 指令处理完毕,直接返回
|
||||
}
|
||||
|
||||
// --- MODIFICATION END ---
|
||||
|
||||
// 对其他所有指令的通用处理逻辑
|
||||
// 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()) {
|
||||
if (is_def) {
|
||||
if (reg_op->isVirtual()) { // 只有虚拟寄存器才需要处理 Use/Def
|
||||
// 如果是第一个寄存器操作数,且指令类型表明它是定义 (def),则加入 def 集合
|
||||
// 否则,它是 use (读取)
|
||||
if (first_reg_is_def) {
|
||||
def.insert(reg_op->getVRegNum());
|
||||
is_def = false; // 一条指令通常只有一个目标寄存ator
|
||||
first_reg_is_def = false; // 确保每条指令只定义一个目标寄存器
|
||||
} else {
|
||||
use.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
// 内存操作数 `offset(base)` 中的 base 寄存器是 use
|
||||
// 内存操作数 `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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,6 +388,7 @@ void RISCv64RegAlloc::analyzeLiveness() {
|
||||
|
||||
void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
std::set<unsigned> all_vregs;
|
||||
// 收集所有虚拟寄存器和物理寄存器在干扰图中的节点ID
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for(auto& instr : mbb->getInstructions()) {
|
||||
LiveSet use, def;
|
||||
@ -352,6 +397,11 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
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] = {}; }
|
||||
|
||||
@ -361,6 +411,7 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
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) {
|
||||
@ -369,6 +420,24 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *** 核心修改点:处理 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -389,10 +458,27 @@ void RISCv64RegAlloc::colorGraph() {
|
||||
// 着色
|
||||
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));
|
||||
for (unsigned neighbor_id : interference_graph.at(vreg)) {
|
||||
// --- 修改开始 ---
|
||||
|
||||
// 情况 1: 邻居是一个已经被着色的虚拟寄存器
|
||||
if (color_map.count(neighbor_id)) {
|
||||
used_colors.insert(color_map.at(neighbor_id));
|
||||
}
|
||||
// 情况 2: 邻居本身就是一个代表物理寄存器的节点
|
||||
else if (neighbor_id >= static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID)) {
|
||||
// 需要一个反向映射来从特殊ID找回PhysicalReg
|
||||
// 假设你有这样一个映射 inv_preg_to_vreg_id_map
|
||||
// 或者,你可以重新计算
|
||||
for (auto const& [preg, id] : preg_to_vreg_id_map) {
|
||||
if (id == neighbor_id) {
|
||||
used_colors.insert(preg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 修改结束 ---
|
||||
}
|
||||
|
||||
bool colored = false;
|
||||
|
||||
@ -17,6 +17,8 @@ public:
|
||||
// 公开接口,以便后续模块(如RegAlloc)可以查询或创建vreg
|
||||
unsigned getVReg(Value* val);
|
||||
unsigned getNewVReg() { return vreg_counter++; }
|
||||
// 获取 vreg_map 的公共接口
|
||||
const std::map<Value*, unsigned>& getVRegMap() const { return vreg_map; }
|
||||
|
||||
private:
|
||||
// DAG节点定义,作为ISel的内部实现细节
|
||||
|
||||
@ -35,7 +35,12 @@ enum class PhysicalReg {
|
||||
// (保持您原有的 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
|
||||
F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
|
||||
// 用于内部表示物理寄存器在干扰图中的节点ID(一个简单的特殊ID,确保不与vreg_counter冲突)
|
||||
// 假设 vreg_counter 不会达到这么大的值
|
||||
PHYS_REG_START_ID = 10000,
|
||||
PHYS_REG_END_ID = PHYS_REG_START_ID + 32, // 预留足够的空间
|
||||
};
|
||||
|
||||
// RISC-V 指令操作码枚举
|
||||
@ -67,6 +72,9 @@ enum class RVOpcodes {
|
||||
FRAME_ADDR, // 获取栈帧变量的地址
|
||||
};
|
||||
|
||||
// 定义一个全局辅助函数或常量,提供调用者保存寄存器列表
|
||||
const std::vector<PhysicalReg>& getCallerSavedIntRegs();
|
||||
|
||||
class MachineOperand;
|
||||
class RegOperand;
|
||||
class ImmOperand;
|
||||
@ -187,6 +195,7 @@ struct StackFrameInfo {
|
||||
int locals_size = 0; // 仅为AllocaInst分配的大小
|
||||
int spill_size = 0; // 仅为溢出分配的大小
|
||||
int total_size = 0; // 总大小
|
||||
int callee_saved_size = 0; // 保存寄存器的大小
|
||||
std::map<unsigned, int> alloca_offsets; // <AllocaInst的vreg, 栈偏移>
|
||||
std::map<unsigned, int> spill_offsets; // <溢出vreg, 栈偏移>
|
||||
std::set<PhysicalReg> used_callee_saved_regs; // 使用的保存寄存器
|
||||
@ -215,6 +224,15 @@ private:
|
||||
StackFrameInfo frame_info;
|
||||
};
|
||||
|
||||
inline const std::vector<PhysicalReg>& getCallerSavedIntRegs() {
|
||||
static const std::vector<PhysicalReg> 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
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
} // namespace sysy
|
||||
|
||||
#endif // RISCV64_LLIR_H
|
||||
@ -2,6 +2,7 @@
|
||||
#define RISCV64_REGALLOC_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "RISCv64ISel.h" // 包含 RISCv64ISel.h 以访问 ISel 和 Value 类型
|
||||
|
||||
namespace sysy {
|
||||
|
||||
@ -56,6 +57,7 @@ private:
|
||||
// 存储vreg到IR Value*的反向映射
|
||||
// 这个map将在run()函数开始时被填充,并在rewriteFunction()中使用。
|
||||
std::map<unsigned, Value*> vreg_to_value_map;
|
||||
std::map<PhysicalReg, unsigned> preg_to_vreg_id_map; // 物理寄存器到特殊vreg ID的映射
|
||||
|
||||
// 用于计算类型大小的辅助函数
|
||||
unsigned getTypeSizeInBytes(Type* type);
|
||||
|
||||
Reference in New Issue
Block a user