Compare commits

...

10 Commits

Author SHA1 Message Date
Lixuanwang
ed8fc32a23 [deploy]部署版本3 2025-07-29 00:48:17 +08:00
Lixuanwang
9ba08126fb Merge branch 'peephole' into backend 2025-07-28 23:46:34 +08:00
Lixuanwang
875100ec01 [backend]为单次运行脚本添加了clean参数 2025-07-28 23:41:36 +08:00
Lixuanwang
b0cecca081 Merge branch 'backend-rec' into backend 2025-07-28 23:40:58 +08:00
Lixuanwang
fcc3806342 Merge branch 'backend-bss' into backend 2025-07-28 17:31:47 +08:00
Lixuanwang
792dc9c1f6 Merge branch 'backend-bss' into backend-rec 2025-07-28 17:31:23 +08:00
Lixuanwang
429e477776 [backend]引入了对.bss和.data段的区分 2025-07-28 17:29:18 +08:00
Lixuanwang
2e8b564d8f [backend]修复了递归函数的调用问题,引入了新的bug? 2025-07-27 01:07:08 +08:00
Lixuanwang
2dd6a17fca [backend]saving1 2025-07-27 00:34:45 +08:00
Lixuanwang
af318b6c0e [backend]尝试在寄存器分配逻辑中区分调用者保存、被调用者保存寄存器 2025-07-26 21:45:01 +08:00
14 changed files with 562 additions and 719 deletions

View File

@@ -33,11 +33,11 @@ add_executable(sysyc
RISCv64ISel.cpp
RISCv64RegAlloc.cpp
RISCv64AsmPrinter.cpp
# RISCv64Passes.cpp
RISCv64Peephole.cpp
PreRA_Scheduler.cpp
PostRA_Scheduler.cpp
CalleeSavedHandler.cpp
RISCv64LLIR.cpp
)
# 设置 include 路径,包含 ANTLR 运行时库和项目头文件

View File

@@ -12,27 +12,25 @@ bool CalleeSavedHandler::runOnFunction(Function *F, AnalysisManager& AM) {
}
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) {
@@ -41,66 +39,82 @@ 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) {
if ((*it)->getOpcode() == RVOpcodes::RET) {
// 以相反的顺序恢复
current_offset = -16;
// 1. 创建一个临时vector来存储所有需要插入的恢复指令
std::vector<std::unique_ptr<MachineInstr>> restore_instrs;
int current_offset_load = base_offset;
// 以相同的顺序(例如 s1, s2, ...)创建恢复指令
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)
));
mbb->getInstructions().insert(it, std::move(ld));
restore_instrs.push_back(std::move(ld));
current_offset_load -= 8;
}
break; // 处理完一个基本块的ret即可
// 2. 使用 make_move_iterator 一次性将所有恢复指令插入到 RET 指令之前
// 这可以高效地转移指令的所有权,并且只让迭代器失效一次。
if (!restore_instrs.empty()) {
mbb->getInstructions().insert(it,
std::make_move_iterator(restore_instrs.begin()),
std::make_move_iterator(restore_instrs.end())
);
}
// 找到了RET并处理完毕后就可以跳出内层循环继续寻找下一个基本块
break;
}
}
}

View File

@@ -35,15 +35,26 @@ void RISCv64AsmPrinter::run(std::ostream& os, bool debug) {
void RISCv64AsmPrinter::printPrologue() {
StackFrameInfo& frame_info = MFunc->getFrameInfo();
// 序言需要为保存ra和s0预留16字节
int total_stack_size = frame_info.locals_size + frame_info.spill_size + 16;
// 计算总栈帧大小。
// 包含三部分:局部变量区、寄存器溢出区、以及为被调用者保存(callee-saved)寄存器预留的区域。
// 最后再加上为保存 ra 和 s0 固定的16字节。
int total_stack_size = frame_info.locals_size +
frame_info.spill_size +
frame_info.callee_saved_size +
16;
// 保持栈指针16字节对齐
int aligned_stack_size = (total_stack_size + 15) & ~15;
frame_info.total_size = aligned_stack_size;
frame_info.total_size = aligned_stack_size; // 更新最终的栈大小
// 只有在需要分配栈空间时才生成指令
if (aligned_stack_size > 0) {
// 1. 一次性分配整个栈帧
*OS << " addi sp, sp, -" << aligned_stack_size << "\n";
// 2. 在新的栈顶附近保存 ra 和 s0
*OS << " sd ra, " << (aligned_stack_size - 8) << "(sp)\n";
*OS << " sd s0, " << (aligned_stack_size - 16) << "(sp)\n";
// 3. 设置新的帧指针 s0使其指向栈帧的底部高地址
*OS << " addi s0, sp, " << aligned_stack_size << "\n";
}
}
@@ -115,7 +126,18 @@ void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
case RVOpcodes::MV: *OS << "mv "; break; case RVOpcodes::NEG: *OS << "neg "; break;
case RVOpcodes::NEGW: *OS << "negw "; break; case RVOpcodes::SEQZ: *OS << "seqz "; break;
case RVOpcodes::SNEZ: *OS << "snez "; break;
case RVOpcodes::CALL: *OS << "call "; break;
case RVOpcodes::CALL: { // [核心修改] 为CALL指令添加特殊处理逻辑
*OS << "call ";
// 遍历所有操作数,只寻找并打印函数名标签
for (const auto& op : instr->getOperands()) {
if (op->getKind() == MachineOperand::KIND_LABEL) {
printOperand(op.get());
break; // 找到标签后即可退出
}
}
*OS << "\n";
return; // 处理完毕,直接返回,不再执行后续的通用操作数打印
}
case RVOpcodes::LABEL:
break;
case RVOpcodes::FRAME_LOAD_W:

View File

@@ -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()) {

View File

@@ -582,7 +582,22 @@ void RISCv64ISel::selectNode(DAGNode* node) {
}
auto call_instr = std::make_unique<MachineInstr>(RVOpcodes::CALL);
// [协议] 如果函数有返回值,将它的目标虚拟寄存器作为第一个操作数
if (!call->getType()->isVoid()) {
unsigned dest_vreg = getVReg(call);
call_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
}
// 将函数名标签作为后续操作数
call_instr->addOperand(std::make_unique<LabelOperand>(call->getCallee()->getName()));
// 将所有参数的虚拟寄存器也作为后续操作数供getInstrUseDef分析
for (size_t i = 0; i < num_operands; ++i) {
if (node->operands[i]->kind != DAGNode::CONSTANT) { // 常量参数已直接加载无需作为use
call_instr->addOperand(std::make_unique<RegOperand>(getVReg(node->operands[i]->value)));
}
}
CurMBB->addInstruction(std::move(call_instr));
if (num_operands > 8) {
@@ -596,12 +611,12 @@ void RISCv64ISel::selectNode(DAGNode* node) {
CurMBB->addInstruction(std::move(dealloc_instr));
}
// 处理返回值从a0移动到目标虚拟寄存器
if (!call->getType()->isVoid()) {
auto mv_instr = std::make_unique<MachineInstr>(RVOpcodes::MV);
mv_instr->addOperand(std::make_unique<RegOperand>(getVReg(call)));
mv_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
CurMBB->addInstruction(std::move(mv_instr));
}
// if (!call->getType()->isVoid()) {
// auto mv_instr = std::make_unique<MachineInstr>(RVOpcodes::MV);
// mv_instr->addOperand(std::make_unique<RegOperand>(getVReg(call)));
// mv_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
// CurMBB->addInstruction(std::move(mv_instr));
// }
break;
}

6
src/RISCv64LLIR.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include "RISCv64LLIR.h"
#include <vector>
namespace sysy {
}

View File

@@ -1,7 +1,10 @@
#include "RISCv64RegAlloc.h"
#include "RISCv64ISel.h"
#include "RISCv64AsmPrinter.h" // For DEBUG output
#include <algorithm>
#include <vector>
#include <iostream> // For DEBUG output
#include <cassert> // For assert
namespace sysy {
@@ -15,35 +18,69 @@ RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
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();
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
handleCallingConvention();
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
eliminateFrameIndices();
analyzeLiveness();
buildInterferenceGraph();
colorGraph();
rewriteFunction();
{ // 使用大括号创建一个局部作用域避免printer变量泄露
if (DEBUG) {
std::cerr << "\n===== LLIR after eliminateFrameIndices for function: "
<< MFunc->getName() << " =====\n";
// 1. 创建一个 AsmPrinter 实例,传入当前的 MachineFunction
RISCv64AsmPrinter printer(MFunc);
// 2. 调用 run 方法,将结果打印到标准错误流 (std::cerr)
// 3. 必须将 debug 参数设为 true
// 因为此时指令中仍然包含虚拟寄存器 (%vreg)
// debug模式下的 AsmPrinter 才能正确处理它们而不会报错。
printer.run(std::cerr, true);
std::cerr << "===== End of LLIR =====\n\n";
}
}
// 阶段 3: 活跃性分析
analyzeLiveness();
// 阶段 4: 构建干扰图包含CALL指令对调用者保存寄存器的影响
buildInterferenceGraph();
// 阶段 5: 图着色算法分配物理寄存器
colorGraph();
// 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器)
rewriteFunction();
}
/**
* @brief 处理调用约定,预先为函数参数分配物理寄存器。
* @brief 处理调用约定,预先为函数参数和调用返回值分配物理寄存器。
* 这个函数现在负责处理调用约定的两个方面:
* 1. 作为被调用者(callee),如何接收传入的参数。
* 2. 作为调用者(caller),如何接收调用的其他函数的返回值。
*/
void RISCv64RegAlloc::handleCallingConvention() {
Function* F = MFunc->getFunc();
RISCv64ISel* isel = MFunc->getISel();
// --- 部分1处理函数传入参数的预着色 ---
// 获取函数的Argument对象列表
if (F) {
auto& args = F->getArguments();
// RISC-V RV64G调用约定前8个整型/指针参数通过 a0-a7 传递
int arg_idx = 0;
// 遍历 AllocaInst* 列表
// 遍历 Argument* 列表
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);
@@ -56,8 +93,35 @@ void RISCv64RegAlloc::handleCallingConvention() {
arg_idx++;
}
}
// // --- 部分2[新逻辑] 遍历所有指令为CALL指令的返回值预着色为 a0 ---
// // 这是为了强制寄存器分配器知道call的结果物理上出现在a0寄存器。
for (auto& mbb : MFunc->getBlocks()) {
for (auto& instr : mbb->getInstructions()) {
if (instr->getOpcode() == RVOpcodes::CALL) {
// 根据协议如果CALL有返回值其目标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()) {
unsigned ret_vreg = reg_op->getVRegNum();
// 强制将这个虚拟寄存器预着色为 a0
color_map[ret_vreg] = PhysicalReg::A0;
if (DEBUG) {
std::cout << "[DEBUG] Pre-coloring vreg" << ret_vreg
<< " to a0 for CALL instruction." << std::endl;
}
}
}
}
}
}
}
/**
* @brief 消除帧索引,为局部变量和栈参数分配栈偏移量,并展开伪指令。
*/
void RISCv64RegAlloc::eliminateFrameIndices() {
StackFrameInfo& frame_info = MFunc->getFrameInfo();
// 初始偏移量为保存ra和s0留出空间。
@@ -185,79 +249,117 @@ 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(preg_to_vreg_id_map.at(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返回值
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());
}
}
// 遍历所有操作数非第一个寄存器操作数均视为use
bool first_reg_skipped = false;
for (const auto& op : instr->getOperands()) {
// 根据 s1 分支 ISel 定义的协议来解析操作数列表
bool first_reg_operand_is_def = true;
for (auto& op : instr->getOperands()) {
if (op->getKind() == MachineOperand::KIND_REG) {
if (!first_reg_skipped) {
first_reg_skipped = true;
continue; // 跳过我们已经作为def处理的返回值
}
auto reg_op = static_cast<RegOperand*>(op.get());
if (reg_op->isVirtual()) {
use.insert(reg_op->getVRegNum());
// 协议:第一个寄存器操作数是返回值 (def)
if (first_reg_operand_is_def) {
def.insert(reg_op->getVRegNum());
first_reg_operand_is_def = false;
} else {
// 后续所有寄存器操作数都是参数 (use)
use.insert(reg_op->getVRegNum());
}
} else { // [修复] CALL指令也可能定义物理寄存器如a0
if (first_reg_operand_is_def) {
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
def.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
}
first_reg_operand_is_def = false;
} else {
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
use.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
}
}
}
}
}
// **重要**: CALL指令还隐式定义杀死了所有调用者保存的寄存器。
// 一个完整的实现会在这里将所有caller-saved寄存器标记为def
// 以确保任何跨调用存活的变量都不会被分配到这些寄存器中。
// 这个简化的实现暂不处理隐式def但这是未来优化的关键点。
return; // CALL 指令处理完毕,直接返回
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 (first_reg_is_def) {
// --- 处理定义Def ---
if (reg_op->isVirtual()) {
def.insert(reg_op->getVRegNum());
is_def = false; // 一条指令通常只有一个目标寄存ator
} else {
} else { // 物理寄存器也可以是 Def
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
def.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
}
}
first_reg_is_def = false; // **关键**:处理完第一个寄存器后,立即更新标志
} else {
// --- 处理使用Use ---
if (reg_op->isVirtual()) {
use.insert(reg_op->getVRegNum());
} else { // 物理寄存器也可以是 Use
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
use.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
}
}
}
} 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());
auto base_reg = mem_op->getBase();
if (base_reg->isVirtual()) {
use.insert(base_reg->getVRegNum());
} else {
PhysicalReg preg = base_reg->getPReg();
if (preg_to_vreg_id_map.count(preg)) {
use.insert(preg_to_vreg_id_map.at(preg));
}
}
// 对于存储内存指令 (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());
} else {
if (preg_to_vreg_id_map.count(src_reg_op->getPReg())) {
use.insert(preg_to_vreg_id_map.at(src_reg_op->getPReg()));
}
}
}
}
}
@@ -301,49 +403,109 @@ unsigned RISCv64RegAlloc::getTypeSizeInBytes(Type* type) {
}
void RISCv64RegAlloc::analyzeLiveness() {
// === 阶段 1: 预计算每个基本块的 use 和 def 集合 ===
// 这样可以避免在主循环中重复计算
std::map<MachineBasicBlock*, LiveSet> block_uses;
std::map<MachineBasicBlock*, LiveSet> block_defs;
for (auto& mbb_ptr : MFunc->getBlocks()) {
MachineBasicBlock* mbb = mbb_ptr.get();
LiveSet uses, defs;
for (auto& instr_ptr : mbb->getInstructions()) {
LiveSet instr_use, instr_def;
getInstrUseDef(instr_ptr.get(), instr_use, instr_def);
// use[B] = use[B] U (instr_use - def[B])
for (unsigned u : instr_use) {
if (defs.find(u) == defs.end()) {
uses.insert(u);
}
}
// def[B] = def[B] U instr_def
defs.insert(instr_def.begin(), instr_def.end());
}
block_uses[mbb] = uses;
block_defs[mbb] = defs;
}
// === 阶段 2: 在“块”粒度上进行迭代数据流分析,直到收敛 ===
std::map<MachineBasicBlock*, LiveSet> block_live_in;
std::map<MachineBasicBlock*, LiveSet> block_live_out;
bool changed = true;
while (changed) {
changed = false;
// 以逆后序遍历基本块,可以加速收敛,但简单的逆序对于大多数情况也有效
for (auto it = MFunc->getBlocks().rbegin(); it != MFunc->getBlocks().rend(); ++it) {
auto& mbb = *it;
LiveSet live_out;
// 2.1 计算 live_out[B] = U_{S in succ(B)} live_in[S]
LiveSet new_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());
}
}
new_live_out.insert(block_live_in[succ].begin(), block_live_in[succ].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;
}
// 2.2 计算 live_in[B] = use[B] U (live_out[B] - def[B])
LiveSet live_out_minus_def = new_live_out;
for (unsigned d : block_defs[mbb.get()]) {
live_out_minus_def.erase(d);
}
LiveSet new_live_in = block_uses[mbb.get()];
new_live_in.insert(live_out_minus_def.begin(), live_out_minus_def.end());
// 2.3 检查 live_in 和 live_out 是否变化,以判断是否达到不动点
if (block_live_out[mbb.get()] != new_live_out) {
changed = true;
block_live_out[mbb.get()] = new_live_out;
}
if (block_live_in[mbb.get()] != new_live_in) {
changed = true;
block_live_in[mbb.get()] = new_live_in;
}
}
}
// === 阶段 3: 进行一次指令粒度的遍历,填充最终的 live_in_map 和 live_out_map ===
// 此时块级别的活跃信息已经稳定,我们只需遍历一次即可
for (auto& mbb_ptr : MFunc->getBlocks()) {
MachineBasicBlock* mbb = mbb_ptr.get();
LiveSet live_out = block_live_out[mbb]; // 从已收敛的块级 live_out 开始
for (auto instr_it = mbb->getInstructions().rbegin(); instr_it != mbb->getInstructions().rend(); ++instr_it) {
MachineInstr* instr = instr_it->get();
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_out = live_in;
}
}
}
// 辅助函数,用于清晰地打印寄存器集合。可以放在 .cpp 文件的顶部。
void RISCv64RegAlloc::printLiveSet(const LiveSet& s, const std::string& name, std::ostream& os) {
os << " " << name << ": { ";
for (unsigned vreg : s) {
// 为了可读性将物理寄存器对应的特殊ID进行转换
if (vreg >= static_cast<unsigned>(sysy::PhysicalReg::PHYS_REG_START_ID)) {
os << "preg(" << (vreg - static_cast<unsigned>(sysy::PhysicalReg::PHYS_REG_START_ID)) << ") ";
} else {
os << "%vreg" << vreg << " ";
}
}
os << "}\n";
}
void RISCv64RegAlloc::buildInterferenceGraph() {
std::set<unsigned> all_vregs;
// 收集所有虚拟寄存器和物理寄存器在干扰图中的节点ID
for (auto& mbb : MFunc->getBlocks()) {
for(auto& instr : mbb->getInstructions()) {
LiveSet use, def;
@@ -352,23 +514,100 @@ 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] = {}; }
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());
// 创建一个临时的AsmPrinter用于打印指令方便调试
RISCv64AsmPrinter temp_printer(MFunc);
temp_printer.setStream(std::cerr);
for (auto& mbb : MFunc->getBlocks()) {
if (DEEPDEBUG) std::cerr << "--- Building Graph for Basic Block: " << mbb->getName() << " ---\n";
for (auto& instr_ptr : mbb->getInstructions()) {
MachineInstr* instr = instr_ptr.get();
if (DEEPDEBUG) {
// 打印当前正在处理的指令
std::cerr << " Instr: ";
temp_printer.printInstruction(instr, true); // 使用 true 来打印虚拟寄存器
}
LiveSet def, use;
getInstrUseDef(instr, use, def);
const LiveSet& live_out = live_out_map.at(instr);
// [新增调试逻辑] 打印所有相关的寄存器集合
if (DEEPDEBUG) {
printLiveSet(use, "Use ", std::cerr);
printLiveSet(def, "Def ", std::cerr);
printLiveSet(live_out, "Live_Out", std::cerr); // 这是我们最关心的信息
}
// 标准干扰图构建def 与 live_out 中的其他变量干扰
for (unsigned d : def) {
for (unsigned l : live_out) {
if (d != l) {
// [新增调试逻辑] 打印添加的干扰边及其原因
if (DEEPDEBUG && interference_graph[d].find(l) == interference_graph[d].end()) {
std::cerr << " Edge (Def-LiveOut): %vreg" << d << " <-> %vreg" << l << "\n";
}
interference_graph[d].insert(l);
interference_graph[l].insert(d);
}
}
}
// 在非move指令中def 与 use 互相干扰
if (instr->getOpcode() != RVOpcodes::MV) {
for (unsigned d : def) {
for (unsigned u : use) {
if (d != u) {
// [新增调试逻辑] 打印添加的干扰边及其原因
if (DEEPDEBUG && interference_graph[d].find(u) == interference_graph[d].end()) {
std::cerr << " Edge (Def-Use) : %vreg" << d << " <-> %vreg" << u << "\n";
}
interference_graph[d].insert(u);
interference_graph[u].insert(d);
}
}
}
}
// *** 处理 CALL 指令的隐式 def ***
if (instr->getOpcode() == RVOpcodes::CALL) {
// 你的原始CALL调试信息
if (DEEPDEBUG) {
std::string live_out_str;
for (unsigned vreg : live_out) {
live_out_str += "%vreg" + std::to_string(vreg) + " ";
}
std::cerr << "[DEEPDEBUG] buildInterferenceGraph: CALL instruction found. Live out set is: {"
<< live_out_str << "}" << std::endl;
}
// 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) { // 避免自己和自己干扰
// [新增调试逻辑] 打印添加的干扰边及其原因
if (DEEPDEBUG && interference_graph[cs_vreg_id].find(live_vreg_out) == interference_graph[cs_vreg_id].end()) {
std::cerr << " Edge (CALL) : preg(" << static_cast<int>(cs_reg) << ") <-> %vreg" << live_vreg_out << "\n";
}
interference_graph[cs_vreg_id].insert(live_vreg_out);
interference_graph[live_vreg_out].insert(cs_vreg_id);
}
}
}
}
if (DEEPDEBUG) std::cerr << " ----------------\n";
}
}
}
@@ -376,7 +615,8 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
void RISCv64RegAlloc::colorGraph() {
std::vector<unsigned> sorted_vregs;
for (auto const& [vreg, neighbors] : interference_graph) {
if (color_map.find(vreg) == color_map.end()) {
// 只为未预着色的虚拟寄存器排序和着色
if (color_map.find(vreg) == color_map.end() && vreg < static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID)) {
sorted_vregs.push_back(vreg);
}
}
@@ -389,9 +629,18 @@ 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)) {
// --- 关键改进 (来自 rec 分支) ---
// 情况 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 neighbor_preg = static_cast<PhysicalReg>(neighbor_id - static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID));
used_colors.insert(neighbor_preg);
}
}

View File

@@ -1,529 +0,0 @@
#include "SysYIRAnalyser.h"
#include <iostream>
namespace sysy {
void ControlFlowAnalysis::init() {
// 初始化分析器
auto &functions = pModule->getFunctions();
for (const auto &function : functions) {
auto func = function.second.get();
auto basicBlocks = func->getBasicBlocks();
for (auto &basicBlock : basicBlocks) {
blockAnalysisInfo[basicBlock.get()] = new BlockAnalysisInfo();
blockAnalysisInfo[basicBlock.get()]->clear();
}
functionAnalysisInfo[func] = new FunctionAnalysisInfo();
functionAnalysisInfo[func]->clear();
}
}
void ControlFlowAnalysis::runControlFlowAnalysis() {
// 运行控制流分析
clear(); // 清空之前的分析结果
init(); // 初始化分析器
computeDomNode();
computeDomTree();
computeDomFrontierAllBlk();
}
void ControlFlowAnalysis::intersectOP4Dom(std::unordered_set<BasicBlock *> &dom, const std::unordered_set<BasicBlock *> &other) {
// 计算交集
for (auto it = dom.begin(); it != dom.end();) {
if (other.find(*it) == other.end()) {
// 如果other中没有这个基本块则从dom中删除
it = dom.erase(it);
} else {
++it;
}
}
}
auto ControlFlowAnalysis::findCommonDominator(BasicBlock *a, BasicBlock *b) -> BasicBlock * {
// 查找两个基本块的共同支配结点
while (a != b) {
BlockAnalysisInfo* infoA = blockAnalysisInfo[a];
BlockAnalysisInfo* infoB = blockAnalysisInfo[b];
// 如果深度不同,则向上移动到直接支配结点
// TODO空间换时间倍增优化优先级较低
while (infoA->getDomDepth() > infoB->getDomDepth()) {
a = const_cast<BasicBlock*>(infoA->getIdom());
infoA = blockAnalysisInfo[a];
}
while (infoB->getDomDepth() > infoA->getDomDepth()) {
b = const_cast<BasicBlock*>(infoB->getIdom());
infoB = blockAnalysisInfo[b];
}
if (a == b) break;
a = const_cast<BasicBlock*>(infoA->getIdom());
b = const_cast<BasicBlock*>(infoB->getIdom());
}
return a;
}
void ControlFlowAnalysis::computeDomNode(){
auto &functions = pModule->getFunctions();
// 分析每个函数内的基本块
for (const auto &function : functions) {
auto func = function.second.get();
auto basicBlocks = func->getBasicBlocks();
std::unordered_set<BasicBlock *> domSetTmp;
// 一开始把domSetTmp置为所有block
auto entry_block = func->getEntryBlock();
entry_block->setName("Entry");
blockAnalysisInfo[entry_block]->addDominants(entry_block);
for (auto &basicBlock : basicBlocks) {
domSetTmp.emplace(basicBlock.get());
}
// 初始化
for (auto &basicBlock : basicBlocks) {
if (basicBlock.get() != entry_block) {
blockAnalysisInfo[basicBlock.get()]->setDominants(domSetTmp);
// 先把所有block的必经结点都设为N
}
}
// 支配节点计算公式
//DOM[B]={B} {⋂P∈pred(B) DOM[P]}
// 其中pred(B)是B的所有前驱结点
// 迭代计算支配结点,直到不再变化
// 这里使用迭代法,直到支配结点不再变化
// TODOLengauer-Tarjan 算法可以更高效地计算支配结点
// 或者按照CFG拓扑序遍历效率更高
bool changed = true;
while (changed) {
changed = false;
// 循环非start结点
for (auto &basicBlock : basicBlocks) {
if (basicBlock.get() != entry_block) {
auto olddom =
blockAnalysisInfo[basicBlock.get()]->getDominants();
std::unordered_set<BasicBlock *> dom =
blockAnalysisInfo[basicBlock->getPredecessors().front()]->getDominants();
// 对于每个基本块,计算其支配结点
// 取其前驱结点的支配结点的交集和自己
for (auto pred : basicBlock->getPredecessors()) {
intersectOP4Dom(dom, blockAnalysisInfo[pred]->getDominants());
}
dom.emplace(basicBlock.get());
blockAnalysisInfo[basicBlock.get()]->setDominants(dom);
if (dom != olddom) {
changed = true;
}
}
}
}
}
}
// TODO SEMI-NCA算法改进
void ControlFlowAnalysis::computeDomTree() {
// 构造支配树
auto &functions = pModule->getFunctions();
for (const auto &function : functions) {
auto func = function.second.get();
auto basicBlocks = func->getBasicBlocks();
auto entry_block = func->getEntryBlock();
blockAnalysisInfo[entry_block]->setIdom(entry_block);
blockAnalysisInfo[entry_block]->setDomDepth(0); // 入口块深度为0
bool changed = true;
while (changed) {
changed = false;
for (auto &basicBlock : basicBlocks) {
if (basicBlock.get() == entry_block) continue;
BasicBlock *new_idom = nullptr;
for (auto pred : basicBlock->getPredecessors()) {
// 跳过未处理的前驱
if (blockAnalysisInfo[pred]->getIdom() == nullptr) continue;
// new_idom = (new_idom == nullptr) ? pred : findCommonDominator(new_idom, pred);
if (new_idom == nullptr)
new_idom = pred;
else
new_idom = findCommonDominator(new_idom, pred);
}
// 更新直接支配节点
if (new_idom && new_idom != blockAnalysisInfo[basicBlock.get()]->getIdom()) {
// 移除旧的支配关系
if (blockAnalysisInfo[basicBlock.get()]->getIdom()) {
blockAnalysisInfo[const_cast<BasicBlock*>(blockAnalysisInfo[basicBlock.get()]->getIdom())]->removeSdoms(basicBlock.get());
}
// 设置新的支配关系
// std::cout << "Block: " << basicBlock->getName()
// << " New Idom: " << new_idom->getName() << std::endl;
blockAnalysisInfo[basicBlock.get()]->setIdom(new_idom);
blockAnalysisInfo[new_idom]->addSdoms(basicBlock.get());
// 更新深度 = 直接支配节点深度 + 1
blockAnalysisInfo[basicBlock.get()]->setDomDepth(
blockAnalysisInfo[new_idom]->getDomDepth() + 1);
changed = true;
}
}
}
}
// for (auto &basicBlock : basicBlocks) {
// if (basicBlock.get() != func->getEntryBlock()) {
// auto dominats =
// blockAnalysisInfo[basicBlock.get()]->getDominants();
// bool found = false;
// // 从前驱结点开始寻找直接支配结点
// std::queue<BasicBlock *> q;
// for (auto pred : basicBlock->getPredecessors()) {
// q.push(pred);
// }
// // BFS遍历前驱结点直到找到直接支配结点
// while (!found && !q.empty()) {
// auto curr = q.front();
// q.pop();
// if (curr == basicBlock.get())
// continue;
// if (dominats.count(curr) != 0U) {
// blockAnalysisInfo[basicBlock.get()]->setIdom(curr);
// blockAnalysisInfo[curr]->addSdoms(basicBlock.get());
// found = true;
// } else {
// for (auto pred : curr->getPredecessors()) {
// q.push(pred);
// }
// }
// }
// }
// }
}
// std::unordered_set<BasicBlock *> ControlFlowAnalysis::computeDomFrontier(BasicBlock *block) {
// std::unordered_set<BasicBlock *> ret_list;
// // 计算 localDF
// for (auto local_successor : block->getSuccessors()) {
// if (local_successor->getIdom() != block) {
// ret_list.emplace(local_successor);
// }
// }
// // 计算 upDF
// for (auto up_successor : block->getSdoms()) {
// auto childrenDF = computeDF(up_successor);
// for (auto w : childrenDF) {
// if (block != w->getIdom() || block == w) {
// ret_list.emplace(w);
// }
// }
// }
// return ret_list;
// }
void ControlFlowAnalysis::computeDomFrontierAllBlk() {
auto &functions = pModule->getFunctions();
for (const auto &function : functions) {
auto func = function.second.get();
auto basicBlocks = func->getBasicBlocks();
// 按支配树深度排序(从深到浅)
std::vector<BasicBlock *> orderedBlocks;
for (auto &bb : basicBlocks) {
orderedBlocks.push_back(bb.get());
}
std::sort(orderedBlocks.begin(), orderedBlocks.end(),
[this](BasicBlock *a, BasicBlock *b) {
return blockAnalysisInfo[a]->getDomDepth() > blockAnalysisInfo[b]->getDomDepth();
});
// 计算支配边界
for (auto block : orderedBlocks) {
std::unordered_set<BasicBlock *> df;
// Local DF: 直接后继中不被当前块支配的
for (auto succ : block->getSuccessors()) {
// 当前块不支配该后继(即不是其直接支配节点)
if (blockAnalysisInfo[succ]->getIdom() != block) {
df.insert(succ);
}
}
// Up DF: 从支配子树中继承
for (auto child : blockAnalysisInfo[block]->getSdoms()) {
for (auto w : blockAnalysisInfo[child]->getDomFrontiers()) {
// 如果w不被当前块支配
if (block != blockAnalysisInfo[w]->getIdom()) {
df.insert(w);
}
}
}
blockAnalysisInfo[block]->setDomFrontiers(df);
}
}
}
// ==========================
// dataflow analysis utils
// ==========================
// 先引用学长的代码
// TODO: Worklist 增加逆后序遍历机制
void DataFlowAnalysisUtils::forwardAnalyze(Module *pModule){
std::map<DataFlowAnalysis *, bool> workAnalysis;
for (auto &dataflow : forwardAnalysisList) {
dataflow->init(pModule);
}
for (const auto &function : pModule->getFunctions()) {
for (auto &dataflow : forwardAnalysisList) {
workAnalysis.emplace(dataflow, false);
}
while (!workAnalysis.empty()) {
for (const auto &block : function.second->getBasicBlocks()) {
for (auto &elem : workAnalysis) {
if (elem.first->analyze(pModule, block.get())) {
elem.second = true;
}
}
}
std::map<DataFlowAnalysis *, bool> tmp;
std::remove_copy_if(workAnalysis.begin(), workAnalysis.end(), std::inserter(tmp, tmp.end()),
[](const std::pair<DataFlowAnalysis *, bool> &elem) -> bool { return !elem.second; });
workAnalysis.swap(tmp);
for (auto &elem : workAnalysis) {
elem.second = false;
}
}
}
}
void DataFlowAnalysisUtils::backwardAnalyze(Module *pModule) {
std::map<DataFlowAnalysis *, bool> workAnalysis;
for (auto &dataflow : backwardAnalysisList) {
dataflow->init(pModule);
}
for (const auto &function : pModule->getFunctions()) {
for (auto &dataflow : backwardAnalysisList) {
workAnalysis.emplace(dataflow, false);
}
while (!workAnalysis.empty()) {
for (const auto &block : function.second->getBasicBlocks()) {
for (auto &elem : workAnalysis) {
if (elem.first->analyze(pModule, block.get())) {
elem.second = true;
}
}
}
std::map<DataFlowAnalysis *, bool> tmp;
std::remove_copy_if(workAnalysis.begin(), workAnalysis.end(), std::inserter(tmp, tmp.end()),
[](const std::pair<DataFlowAnalysis *, bool> &elem) -> bool { return !elem.second; });
workAnalysis.swap(tmp);
for (auto &elem : workAnalysis) {
elem.second = false;
}
}
}
}
std::set<User *> ActiveVarAnalysis::getUsedSet(Instruction *inst) {
using Kind = Instruction::Kind;
std::vector<User *> operands;
for (const auto &operand : inst->getOperands()) {
operands.emplace_back(dynamic_cast<User *>(operand->getValue()));
}
std::set<User *> result;
switch (inst->getKind()) {
// phi op
case Kind::kPhi:
case Kind::kCall:
result.insert(std::next(operands.begin()), operands.end());
break;
case Kind::kCondBr:
result.insert(operands[0]);
break;
case Kind::kBr:
case Kind::kAlloca:
break;
// mem op
case Kind::kStore:
// StoreInst 的第一个操作数是被存储的值,第二个操作数是存储的变量
// 后续的是可能的数组维度
result.insert(operands[0]);
result.insert(operands.begin() + 2, operands.end());
break;
case Kind::kLoad:
case Kind::kLa: {
auto variable = dynamic_cast<AllocaInst *>(operands[0]);
auto global = dynamic_cast<GlobalValue *>(operands[0]);
auto constArray = dynamic_cast<ConstantVariable *>(operands[0]);
if ((variable != nullptr && variable->getNumDims() == 0) || (global != nullptr && global->getNumDims() == 0) ||
(constArray != nullptr && constArray->getNumDims() == 0)) {
result.insert(operands[0]);
}
result.insert(std::next(operands.begin()), operands.end());
break;
}
case Kind::kGetSubArray: {
for (unsigned i = 2; i < operands.size(); i++) {
// 数组的维度信息
result.insert(operands[i]);
}
break;
}
case Kind::kMemset: {
result.insert(std::next(operands.begin()), operands.end());
break;
}
case Kind::kInvalid:
// Binary
case Kind::kAdd:
case Kind::kSub:
case Kind::kMul:
case Kind::kDiv:
case Kind::kRem:
case Kind::kICmpEQ:
case Kind::kICmpNE:
case Kind::kICmpLT:
case Kind::kICmpLE:
case Kind::kICmpGT:
case Kind::kICmpGE:
case Kind::kFAdd:
case Kind::kFSub:
case Kind::kFMul:
case Kind::kFDiv:
case Kind::kFCmpEQ:
case Kind::kFCmpNE:
case Kind::kFCmpLT:
case Kind::kFCmpLE:
case Kind::kFCmpGT:
case Kind::kFCmpGE:
case Kind::kAnd:
case Kind::kOr:
// Unary
case Kind::kNeg:
case Kind::kNot:
case Kind::kFNot:
case Kind::kFNeg:
case Kind::kFtoI:
case Kind::kItoF:
// terminator
case Kind::kReturn:
result.insert(operands.begin(), operands.end());
break;
default:
assert(false);
break;
}
result.erase(nullptr);
return result;
}
User * ActiveVarAnalysis::getDefine(Instruction *inst) {
User *result = nullptr;
if (inst->isStore()) {
StoreInst* store = dynamic_cast<StoreInst *>(inst);
auto operand = store->getPointer();
AllocaInst* variable = dynamic_cast<AllocaInst *>(operand);
GlobalValue* global = dynamic_cast<GlobalValue *>(operand);
if ((variable != nullptr && variable->getNumDims() != 0) || (global != nullptr && global->getNumDims() != 0)) {
// 如果是数组变量或者全局变量,则不返回定义
// TODO兼容数组变量
result = nullptr;
} else {
result = dynamic_cast<User *>(operand);
}
} else if (inst->isPhi()) {
result = dynamic_cast<User *>(inst->getOperand(0));
} else if (inst->isBinary() || inst->isUnary() || inst->isCall() ||
inst->isLoad() || inst->isLa()) {
result = dynamic_cast<User *>(inst);
}
return result;
}
void ActiveVarAnalysis::init(Module *pModule) {
for (const auto &function : pModule->getFunctions()) {
for (const auto &block : function.second->getBasicBlocks()) {
activeTable.emplace(block.get(), std::vector<std::set<User *>>{});
for (unsigned i = 0; i < block->getNumInstructions() + 1; i++)
activeTable.at(block.get()).emplace_back();
}
}
}
// 活跃变量分析公式 每个块内的分析动作供分析器调用
bool ActiveVarAnalysis::analyze(Module *pModule, BasicBlock *block) {
bool changed = false; // 标记数据流结果是否有变化
std::set<User *> activeSet{}; // 当前计算的活跃变量集合
// 步骤1: 计算基本块出口的活跃变量集 (OUT[B])
// 公式: OUT[B] = _{S ∈ succ(B)} IN[S]
for (const auto &succ : block->getSuccessors()) {
// 获取后继块入口的活跃变量集 (IN[S])
auto succActiveSet = activeTable.at(succ).front();
// 合并所有后继块的入口活跃变量
activeSet.insert(succActiveSet.begin(), succActiveSet.end());
}
// 步骤2: 处理基本块出口处的活跃变量集
const auto &instructions = block->getInstructions();
const auto numInstructions = instructions.size();
// 获取旧的出口活跃变量集 (block出口对应索引numInstructions)
const auto &oldEndActiveSet = activeTable.at(block)[numInstructions];
// 检查出口活跃变量集是否有变化
if (!std::equal(activeSet.begin(), activeSet.end(),
oldEndActiveSet.begin(), oldEndActiveSet.end()))
{
changed = true; // 标记变化
activeTable.at(block)[numInstructions] = activeSet; // 更新出口活跃变量集
}
// 步骤3: 逆序遍历基本块中的指令
// 从最后一条指令开始向前计算每个程序点的活跃变量
auto instructionIter = instructions.end();
instructionIter--; // 指向最后一条指令
// 从出口向入口遍历 (索引从numInstructions递减到1)
for (unsigned i = numInstructions; i > 0; i--) {
auto inst = instructionIter->get(); // 当前指令
auto used = getUsedSet(inst);
User *defined = getDefine(inst);
// 步骤3.3: 计算指令入口的活跃变量 (IN[i])
// 公式: IN[i] = use_i (OUT[i] - def_i)
activeSet.erase(defined); // 移除被定义的变量 (OUT[i] - def_i)
activeSet.insert(used.begin(), used.end()); // 添加使用的变量
// 获取旧的入口活跃变量集 (位置i-1对应当前指令的入口)
const auto &oldActiveSet = activeTable.at(block)[i - 1];
// 检查活跃变量集是否有变化
if (!std::equal(activeSet.begin(), activeSet.end(),
oldActiveSet.begin(), oldActiveSet.end()))
{
changed = true; // 标记变化
activeTable.at(block)[i - 1] = activeSet; // 更新入口活跃变量集
}
instructionIter--; // 移动到前一条指令
}
return changed; // 返回数据流结果是否变化
}
} // namespace sysy

View File

@@ -1,36 +0,0 @@
// PassManager.cpp
#include "SysYIRPassManager.h"
#include <iostream>
namespace sysy {
void PassManager::run(Module& M) {
// 首先运行Module级别的Pass
for (auto& pass : modulePasses) {
std::cout << "Running Module Pass: " << pass->getPassName() << std::endl;
pass->runOnModule(M);
}
// 然后对每个函数运行Function级别的Pass
auto& functions = M.getFunctions();
for (auto& pair : functions) {
Function& F = *(pair.second); // 获取Function的引用
std::cout << " Processing Function: " << F.getName() << std::endl;
// 在每个函数上运行FunctionPasses
bool changedInFunction;
do {
changedInFunction = false;
for (auto& pass : functionPasses) {
// 对于FunctionPasses可以考虑一个迭代执行的循环直到稳定
std::cout << " Running Function Pass: " << pass->getPassName() << std::endl;
changedInFunction |= pass->runOnFunction(F);
}
} while (changedInFunction); // 循环直到函数稳定这模拟了您SysYCFGOpt的while(changed)逻辑
}
// 分析Pass的运行可以在其他Pass需要时触发或者在特定的PassManager阶段触发
// 对于依赖于分析结果的Pass可以在其run方法中通过PassManager::getAnalysis()来获取
}
} // namespace sysy

View File

@@ -12,22 +12,25 @@ namespace sysy {
class RISCv64AsmPrinter {
public:
RISCv64AsmPrinter(MachineFunction* mfunc);
// 主入口
void run(std::ostream& os, bool debug = false);
void printInstruction(MachineInstr* instr, bool debug = false);
// 辅助函数
void setStream(std::ostream& os) { OS = &os; }
private:
// 打印各个部分
void printPrologue();
void printEpilogue();
void printBasicBlock(MachineBasicBlock* mbb, bool debug = false);
void printInstruction(MachineInstr* instr, bool debug = false);
// 辅助函数
std::string regToString(PhysicalReg reg);
void printOperand(MachineOperand* op);
MachineFunction* MFunc;
std::ostream* OS;
std::ostream* OS = nullptr;
};
} // namespace sysy

View File

@@ -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的内部实现细节

View File

@@ -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 = 100000,
PHYS_REG_END_ID = PHYS_REG_START_ID + 320, // 预留足够的空间
};
// 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

View File

@@ -2,6 +2,10 @@
#define RISCV64_REGALLOC_H
#include "RISCv64LLIR.h"
#include "RISCv64ISel.h" // 包含 RISCv64ISel.h 以访问 ISel 和 Value 类型
extern int DEBUG;
extern int DEEPDEBUG;
namespace sysy {
@@ -56,9 +60,13 @@ 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);
// 辅助函数,用于打印集合
static void printLiveSet(const LiveSet& s, const std::string& name, std::ostream& os);
};

View File

@@ -20,6 +20,7 @@ QEMU_RISCV64="qemu-riscv64"
# --- 初始化变量 ---
EXECUTE_MODE=false
CLEAN_MODE=false
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
EXEC_TIMEOUT=5 # qemu 自动化执行超时 (秒)
@@ -37,6 +38,7 @@ show_help() {
echo ""
echo "选项:"
echo " -e, --executable 编译为可执行文件并运行测试 (必须)。"
echo " -c, --clean 清理 tmp 临时目录下的所有文件。"
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。"
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
echo " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 5)。"
@@ -74,6 +76,10 @@ for arg in "$@"; do
-e|--executable)
EXECUTE_MODE=true
;;
-c|--clean)
CLEAN_MODE=true
shift
;;
-sct|-gct|-et|-ml|--max-lines)
# 选项和其值将在下一个循环中处理
;;
@@ -110,6 +116,23 @@ for arg in "$@"; do
esac
done
if ${CLEAN_MODE}; then
echo "检测到 -c/--clean 选项,正在清空 ${TMP_DIR}..."
if [ -d "${TMP_DIR}" ]; then
# 使用 * 而不是 . 来确保只删除内容,不删除目录本身
# 忽略 rm 可能因目录为空而报告的错误
rm -rf "${TMP_DIR}"/* 2>/dev/null
echo "清理完成。"
else
echo "临时目录 ${TMP_DIR} 不存在,无需清理。"
fi
# 如果只提供了 -c 选项而没有其他 .sy 文件,则在清理后退出
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE}; then
exit 0
fi
fi
# --- 主逻辑开始 ---
if ! ${EXECUTE_MODE}; then
echo "错误: 请提供 -e 或 --executable 选项来运行测试。"
@@ -138,6 +161,7 @@ for sy_file in "${SY_FILES[@]}"; do
ir_file="${TMP_DIR}/${base_name}_sysyc_riscv64.ll"
assembly_file="${TMP_DIR}/${base_name}.s"
assembly_debug_file="${TMP_DIR}/${base_name}_d.s"
executable_file="${TMP_DIR}/${base_name}"
input_file="${source_dir}/${base_name}.in"
output_reference_file="${source_dir}/${base_name}.out"
@@ -162,6 +186,7 @@ for sy_file in "${SY_FILES[@]}"; do
echo -e "\e[31m错误: SysY 编译失败或超时。\e[0m"
is_passed=0
fi
# timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s asmd "${sy_file}" > "${assembly_debug_file}" 2>&1
# 步骤 2: GCC 编译
if [ "$is_passed" -eq 1 ]; then