Compare commits
12 Commits
backend-bs
...
deploy-202
| Author | SHA1 | Date | |
|---|---|---|---|
| ed8fc32a23 | |||
| 9ba08126fb | |||
| 875100ec01 | |||
| b0cecca081 | |||
| 434bcea98e | |||
| da5c2bb41d | |||
| fcc3806342 | |||
| 792dc9c1f6 | |||
| 634a84f29c | |||
| 2e8b564d8f | |||
| 2dd6a17fca | |||
| 78dee0d72a |
@ -33,7 +33,10 @@ add_executable(sysyc
|
||||
RISCv64ISel.cpp
|
||||
RISCv64RegAlloc.cpp
|
||||
RISCv64AsmPrinter.cpp
|
||||
RISCv64Passes.cpp
|
||||
RISCv64Peephole.cpp
|
||||
PreRA_Scheduler.cpp
|
||||
PostRA_Scheduler.cpp
|
||||
CalleeSavedHandler.cpp
|
||||
RISCv64LLIR.cpp
|
||||
)
|
||||
|
||||
|
||||
123
src/CalleeSavedHandler.cpp
Normal file
123
src/CalleeSavedHandler.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "CalleeSavedHandler.h"
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char CalleeSavedHandler::ID = 0;
|
||||
|
||||
bool CalleeSavedHandler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// This pass works on MachineFunction level, not IR level
|
||||
return false;
|
||||
}
|
||||
|
||||
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寄存器 (s1-s11)
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
for (auto& op : instr->getOperands()) {
|
||||
auto check_and_insert_reg = [&](RegOperand* reg_op) {
|
||||
if (!reg_op->isVirtual()) {
|
||||
PhysicalReg preg = reg_op->getPReg();
|
||||
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) {
|
||||
check_and_insert_reg(static_cast<MemOperand*>(op.get())->getBase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (used_callee_saved.empty()) {
|
||||
frame_info.callee_saved_size = 0; // 确保大小被初始化
|
||||
return; // 无需操作
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 为了栈帧布局确定性,对寄存器进行排序
|
||||
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
|
||||
std::sort(sorted_regs.begin(), sorted_regs.end());
|
||||
|
||||
// 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) {
|
||||
auto sd = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
sd->addOperand(std::make_unique<RegOperand>(reg));
|
||||
sd->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0), // 基址为帧指针 s0
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
// 从头部插入,但要放在函数标签之后
|
||||
entry_instrs.insert(entry_instrs.begin() + 1, std::move(sd));
|
||||
current_offset -= 8;
|
||||
}
|
||||
|
||||
// 5. 【已修复】在函数结尾(ret之前)插入恢复指令,使用反向遍历来避免迭代器失效
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
// 使用手动控制的反向循环
|
||||
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
// 1. 创建一个临时vector来存储所有需要插入的恢复指令
|
||||
std::vector<std::unique_ptr<MachineInstr>> restore_instrs;
|
||||
|
||||
int current_offset_load = base_offset;
|
||||
// 以相同的顺序(例如 s1, s2, ...)创建恢复指令
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
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_load)
|
||||
));
|
||||
restore_instrs.push_back(std::move(ld));
|
||||
current_offset_load -= 8;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
383
src/PostRA_Scheduler.cpp
Normal file
383
src/PostRA_Scheduler.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
#include "PostRA_Scheduler.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#define MAX_SCHEDULING_BLOCK_SIZE 10000 // 限制调度块大小,避免过大导致性能问题
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PostRA_Scheduler::ID = 0;
|
||||
|
||||
// 检查指令是否是加载指令 (LW, LD)
|
||||
bool isLoadInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::LW || opcode == RVOpcodes::LD ||
|
||||
opcode == RVOpcodes::LH || opcode == RVOpcodes::LB ||
|
||||
opcode == RVOpcodes::LHU || opcode == RVOpcodes::LBU ||
|
||||
opcode == RVOpcodes::LWU;
|
||||
}
|
||||
|
||||
// 检查指令是否是存储指令 (SW, SD)
|
||||
bool isStoreInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||||
opcode == RVOpcodes::SH || opcode == RVOpcodes::SB;
|
||||
}
|
||||
|
||||
// 检查指令是否为控制流指令
|
||||
bool isControlFlowInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::RET || opcode == RVOpcodes::J ||
|
||||
opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::CALL;
|
||||
}
|
||||
|
||||
// 获取指令定义的寄存器 - 修复版本
|
||||
std::set<PhysicalReg> getDefinedRegisters(MachineInstr* instr) {
|
||||
std::set<PhysicalReg> defined_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// CALL指令可能定义返回值寄存器
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 存储指令不定义寄存器
|
||||
if (isStoreInstr(instr)) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 分支指令不定义寄存器
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::J || opcode == RVOpcodes::RET) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,第一个寄存器操作数通常是定义的
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 获取指令使用的寄存器 - 修复版本
|
||||
std::set<PhysicalReg> getUsedRegisters(MachineInstr* instr) {
|
||||
std::set<PhysicalReg> used_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
bool first_reg_skipped = false;
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_reg_skipped) {
|
||||
first_reg_skipped = true;
|
||||
continue; // 跳过返回值寄存器
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于存储指令,所有寄存器操作数都是使用的
|
||||
if (isStoreInstr(instr)) {
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于分支指令,所有寄存器操作数都是使用的
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU) {
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,除了第一个寄存器操作数(通常是定义),其余都是使用的
|
||||
bool first_reg = true;
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (first_reg) {
|
||||
first_reg = false;
|
||||
continue; // 跳过第一个寄存器(定义)
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 获取内存访问的基址和偏移
|
||||
struct MemoryAccess {
|
||||
PhysicalReg base_reg;
|
||||
int64_t offset;
|
||||
bool valid;
|
||||
|
||||
MemoryAccess() : valid(false) {}
|
||||
MemoryAccess(PhysicalReg base, int64_t off) : base_reg(base), offset(off), valid(true) {}
|
||||
};
|
||||
|
||||
MemoryAccess getMemoryAccess(MachineInstr* instr) {
|
||||
if (!isLoadInstr(instr) && !isStoreInstr(instr)) {
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 查找内存操作数
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
return MemoryAccess(mem_op->getBase()->getPReg(), mem_op->getOffset()->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 检查内存依赖 - 加强版本
|
||||
bool hasMemoryDependency(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 如果都不是内存指令,没有内存依赖
|
||||
if (!isLoadInstr(instr1) && !isStoreInstr(instr1) &&
|
||||
!isLoadInstr(instr2) && !isStoreInstr(instr2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryAccess mem1 = getMemoryAccess(instr1);
|
||||
MemoryAccess mem2 = getMemoryAccess(instr2);
|
||||
|
||||
if (!mem1.valid || !mem2.valid) {
|
||||
// 如果无法确定内存访问模式,保守地认为存在依赖
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果访问相同的内存位置
|
||||
if (mem1.base_reg == mem2.base_reg && mem1.offset == mem2.offset) {
|
||||
// Store->Load: RAW依赖
|
||||
// Load->Store: WAR依赖
|
||||
// Store->Store: WAW依赖
|
||||
return isStoreInstr(instr1) || isStoreInstr(instr2);
|
||||
}
|
||||
|
||||
// 不同内存位置通常没有依赖,但为了安全起见,
|
||||
// 如果涉及store指令,我们需要更保守
|
||||
if (isStoreInstr(instr1) && isLoadInstr(instr2)) {
|
||||
// 保守处理:不同store和load之间可能有别名
|
||||
return false; // 这里可以根据需要调整策略
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在依赖关系 - 修复版本
|
||||
bool hasDependency(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 检查RAW依赖:instr1定义的寄存器是否被instr2使用
|
||||
auto defined_regs1 = getDefinedRegisters(instr1);
|
||||
auto used_regs2 = getUsedRegisters(instr2);
|
||||
|
||||
for (const auto& reg : defined_regs1) {
|
||||
if (used_regs2.find(reg) != used_regs2.end()) {
|
||||
return true; // RAW依赖 - instr2读取instr1写入的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAR依赖:instr1使用的寄存器是否被instr2定义
|
||||
auto used_regs1 = getUsedRegisters(instr1);
|
||||
auto defined_regs2 = getDefinedRegisters(instr2);
|
||||
|
||||
for (const auto& reg : used_regs1) {
|
||||
if (defined_regs2.find(reg) != defined_regs2.end()) {
|
||||
return true; // WAR依赖 - instr2覆盖instr1需要的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAW依赖:两个指令定义相同寄存器
|
||||
for (const auto& reg : defined_regs1) {
|
||||
if (defined_regs2.find(reg) != defined_regs2.end()) {
|
||||
return true; // WAW依赖 - 两条指令写入同一寄存器
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(instr1, instr2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否可以安全地将instr1和instr2交换位置
|
||||
bool canSwapInstructions(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 不能移动控制流指令
|
||||
if (isControlFlowInstr(instr1) || isControlFlowInstr(instr2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查双向依赖关系
|
||||
return !hasDependency(instr1, instr2) && !hasDependency(instr2, instr1);
|
||||
}
|
||||
|
||||
// 新增:验证调度结果的正确性
|
||||
void validateSchedule(const std::vector<MachineInstr*>& instr_list) {
|
||||
for (int i = 0; i < (int)instr_list.size(); i++) {
|
||||
for (int j = i + 1; j < (int)instr_list.size(); j++) {
|
||||
MachineInstr* earlier = instr_list[i];
|
||||
MachineInstr* later = instr_list[j];
|
||||
|
||||
// 检查是否存在被违反的依赖关系
|
||||
auto defined_regs = getDefinedRegisters(earlier);
|
||||
auto used_regs = getUsedRegisters(later);
|
||||
|
||||
// 检查RAW依赖
|
||||
for (const auto& reg : defined_regs) {
|
||||
if (used_regs.find(reg) != used_regs.end()) {
|
||||
// 这是正常的依赖关系,earlier应该在later之前
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(earlier, later)) {
|
||||
MemoryAccess mem1 = getMemoryAccess(earlier);
|
||||
MemoryAccess mem2 = getMemoryAccess(later);
|
||||
|
||||
if (mem1.valid && mem2.valid &&
|
||||
mem1.base_reg == mem2.base_reg && mem1.offset == mem2.offset) {
|
||||
if (isStoreInstr(earlier) && isLoadInstr(later)) {
|
||||
// Store->Load依赖,顺序正确
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在基本块内对指令进行调度优化 - 完全重写版本
|
||||
void scheduleBlock(MachineBasicBlock* mbb) {
|
||||
auto& instructions = mbb->getInstructions();
|
||||
if (instructions.size() <= 1) return;
|
||||
if (instructions.size() > MAX_SCHEDULING_BLOCK_SIZE) {
|
||||
return; // 跳过超大块,防止卡住
|
||||
}
|
||||
|
||||
std::vector<MachineInstr*> instr_list;
|
||||
for (auto& instr : instructions) {
|
||||
instr_list.push_back(instr.get());
|
||||
}
|
||||
|
||||
// 使用更严格的调度策略,避免破坏依赖关系
|
||||
bool changed = true;
|
||||
int max_iterations = 10; // 限制迭代次数避免死循环
|
||||
int iteration = 0;
|
||||
|
||||
while (changed && iteration < max_iterations) {
|
||||
changed = false;
|
||||
iteration++;
|
||||
|
||||
for (int i = 0; i < (int)instr_list.size() - 1; i++) {
|
||||
MachineInstr* instr1 = instr_list[i];
|
||||
MachineInstr* instr2 = instr_list[i + 1];
|
||||
|
||||
// 只进行非常保守的优化
|
||||
bool should_swap = false;
|
||||
|
||||
// 策略1: 将load指令提前,减少load-use延迟
|
||||
if (isLoadInstr(instr2) && !isLoadInstr(instr1) && !isStoreInstr(instr1)) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
// 策略2: 将非关键store指令延后,为其他指令让路
|
||||
else if (isStoreInstr(instr1) && !isLoadInstr(instr2) && !isStoreInstr(instr2)) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
|
||||
if (should_swap) {
|
||||
std::swap(instr_list[i], instr_list[i + 1]);
|
||||
changed = true;
|
||||
|
||||
// 调试输出
|
||||
// std::cout << "Swapped instructions at positions " << i << " and " << (i+1) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证调度结果的正确性
|
||||
validateSchedule(instr_list);
|
||||
|
||||
// 将调度后的指令顺序写回
|
||||
std::map<MachineInstr*, std::unique_ptr<MachineInstr>> instr_map;
|
||||
for (auto& instr : instructions) {
|
||||
instr_map[instr.get()] = std::move(instr);
|
||||
}
|
||||
|
||||
instructions.clear();
|
||||
for (auto instr : instr_list) {
|
||||
instructions.push_back(std::move(instr_map[instr]));
|
||||
}
|
||||
}
|
||||
|
||||
bool PostRA_Scheduler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// 这个函数在IR级别运行,但我们需要在机器指令级别运行
|
||||
// 所以我们返回false,表示没有对IR进行修改
|
||||
return false;
|
||||
}
|
||||
|
||||
void PostRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
// std::cout << "Running Post-RA Local Scheduler... " << std::endl;
|
||||
|
||||
// 遍历每个机器基本块
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
scheduleBlock(mbb.get());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
36
src/PreRA_Scheduler.cpp
Normal file
36
src/PreRA_Scheduler.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "PreRA_Scheduler.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PreRA_Scheduler::ID = 0;
|
||||
|
||||
bool PreRA_Scheduler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// TODO: 在此实现寄存器分配前的指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行重排。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 分析每个基本块内指令的数据依赖关系,构建依赖图(DAG)。
|
||||
// 2.
|
||||
// 根据目标处理器的流水线特性(指令延迟等),使用列表调度等算法对指令进行重排。
|
||||
// 3. 此时操作的是虚拟寄存器,只存在真依赖,调度自由度最大。
|
||||
//
|
||||
// std::cout << "Running Pre-RA Instruction Scheduler..." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
// TODO: 在此实现寄存器分配前的指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行重排。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 分析每个基本块内指令的数据依赖关系,构建依赖图(DAG)。
|
||||
// 2.
|
||||
// 根据目标处理器的流水线特性(指令延迟等),使用列表调度等算法对指令进行重排。
|
||||
// 3. 此时操作的是虚拟寄存器,只存在真依赖,调度自由度最大。
|
||||
//
|
||||
// std::cout << "Running Pre-RA Instruction Scheduler..." << std::endl;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -1,149 +0,0 @@
|
||||
#include "RISCv64Passes.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// --- 寄存器分配前优化 ---
|
||||
|
||||
void PreRA_Scheduler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// TODO: 在此实现寄存器分配前的指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行重排。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 分析每个基本块内指令的数据依赖关系,构建依赖图(DAG)。
|
||||
// 2. 根据目标处理器的流水线特性(指令延迟等),使用列表调度等算法对指令进行重排。
|
||||
// 3. 此时操作的是虚拟寄存器,只存在真依赖,调度自由度最大。
|
||||
//
|
||||
// std::cout << "Running Pre-RA Instruction Scheduler..." << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// --- 寄存器分配后优化 ---
|
||||
|
||||
void PeepholeOptimizer::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// TODO: 在此实现窥孔优化。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行扫描和替换。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 维护一个大小固定(例如3-5条指令)的滑动窗口。
|
||||
// 2. 识别特定的冗余模式,例如:
|
||||
// - `mv a0, a1` 后紧跟 `mv a1, a0` (可消除的交换)
|
||||
// - `sw t0, 12(s0)` 后紧跟 `lw t1, 12(s0)` (冗余加载)
|
||||
// - 强度削减: `mul x, x, 2` -> `slli x, x, 1`
|
||||
// 3. 识别后,直接修改MachineInstr列表(删除、替换或插入指令)。
|
||||
//
|
||||
// std::cout << "Running Post-RA Peephole Optimizer..." << std::endl;
|
||||
}
|
||||
|
||||
void PostRA_Scheduler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// TODO: 在此实现寄存器分配后的局部指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 重点关注由寄存器分配器插入的spill/fill代码。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 识别出用于spill/fill的lw/sw指令。
|
||||
// 2. 在不违反数据依赖(包括物理寄存器引入的伪依赖)的前提下,
|
||||
// 尝试将lw指令向上移动,使其与使用它的指令之间有足够的距离,以隐藏访存延迟。
|
||||
// 3. 同样,可以尝试将sw指令向下移动。
|
||||
//
|
||||
// std::cout << "Running Post-RA Local Scheduler..." << std::endl;
|
||||
}
|
||||
|
||||
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
StackFrameInfo& frame_info = mfunc->getFrameInfo();
|
||||
std::set<PhysicalReg> used_callee_saved;
|
||||
|
||||
// 1. 扫描所有指令,找出被使用的s寄存器
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
for (auto& op : instr->getOperands()) {
|
||||
|
||||
// 辅助Lambda,用于检查和插入寄存器
|
||||
auto check_and_insert_reg = [&](RegOperand* reg_op) {
|
||||
if (!reg_op->isVirtual()) {
|
||||
PhysicalReg preg = reg_op->getPReg();
|
||||
// --- 关键检查点 ---
|
||||
// 必须严格判断是否在 s0-s11 的范围内。
|
||||
// a0, t0 等寄存器绝对不应被视为被调用者保存寄存器。
|
||||
if (preg >= PhysicalReg::S0 && preg <= PhysicalReg::S11) {
|
||||
used_callee_saved.insert(preg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
check_and_insert_reg(static_cast<RegOperand*>(op.get()));
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
check_and_insert_reg(static_cast<MemOperand*>(op.get())->getBase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有使用s寄存器(除了可能作为帧指针的s0),则无需操作
|
||||
if (used_callee_saved.empty() || (used_callee_saved.size() == 1 && used_callee_saved.count(PhysicalReg::S0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 将结果存入StackFrameInfo,供后续使用
|
||||
frame_info.used_callee_saved_regs = used_callee_saved;
|
||||
|
||||
// 2. 在函数序言插入保存指令
|
||||
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
auto prologue_end = entry_instrs.begin();
|
||||
|
||||
// 找到序言结束的位置(通常是addi s0, sp, size之后)
|
||||
for (auto it = entry_instrs.begin(); it != entry_instrs.end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::ADDI &&
|
||||
(*it)->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
static_cast<RegOperand*>((*it)->getOperands()[0].get())->getPReg() == PhysicalReg::S0)
|
||||
{
|
||||
prologue_end = std::next(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 为了栈帧布局确定性,对寄存器进行排序
|
||||
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
|
||||
std::sort(sorted_regs.begin(), sorted_regs.end());
|
||||
|
||||
int current_offset = -16; // ra和s0已经占用了-8和-16的位置
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
if (reg == PhysicalReg::S0) continue; // s0已经在序言中处理
|
||||
current_offset -= 8;
|
||||
auto sd = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
sd->addOperand(std::make_unique<RegOperand>(reg));
|
||||
sd->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0), // 假设s0是帧指针
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
entry_instrs.insert(prologue_end, std::move(sd));
|
||||
}
|
||||
|
||||
// 3. 在函数结尾(ret之前)插入恢复指令
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
// 以相反的顺序恢复
|
||||
current_offset = -16;
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
if (reg == PhysicalReg::S0) continue;
|
||||
current_offset -= 8;
|
||||
auto ld = std::make_unique<MachineInstr>(RVOpcodes::LD);
|
||||
ld->addOperand(std::make_unique<RegOperand>(reg));
|
||||
ld->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
mbb->getInstructions().insert(it, std::move(ld));
|
||||
}
|
||||
break; // 处理完一个基本块的ret即可
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
652
src/RISCv64Peephole.cpp
Normal file
652
src/RISCv64Peephole.cpp
Normal file
@ -0,0 +1,652 @@
|
||||
#include "RISCv64Peephole.h"
|
||||
#include <functional>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PeepholeOptimizer::ID = 0;
|
||||
|
||||
bool PeepholeOptimizer::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// This pass works on MachineFunction level, not IR level
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeepholeOptimizer::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
if (!mfunc)
|
||||
return;
|
||||
using namespace sysy;
|
||||
|
||||
// areRegsEqual: 检查两个寄存器操作数是否相等(考虑虚拟和物理寄存器)。
|
||||
auto areRegsEqual = [](RegOperand *r1, RegOperand *r2) {
|
||||
if (!r1 || !r2 || r1->isVirtual() != r2->isVirtual()) {
|
||||
return false;
|
||||
}
|
||||
if (r1->isVirtual()) {
|
||||
return r1->getVRegNum() == r2->getVRegNum();
|
||||
} else {
|
||||
return r1->getPReg() == r2->getPReg();
|
||||
}
|
||||
};
|
||||
|
||||
// 改进的 isRegUsedLater 函数 - 更完整和准确的实现
|
||||
auto isRegUsedLater =
|
||||
[&](const std::vector<std::unique_ptr<MachineInstr>> &instrs,
|
||||
RegOperand *reg, size_t start_idx) -> bool {
|
||||
for (size_t j = start_idx; j < instrs.size(); ++j) {
|
||||
auto *instr = instrs[j].get();
|
||||
auto opcode = instr->getOpcode();
|
||||
|
||||
// 检查所有操作数
|
||||
for (size_t k = 0; k < instr->getOperands().size(); ++k) {
|
||||
bool isDefOperand = false;
|
||||
|
||||
// 更完整的定义操作数判断逻辑
|
||||
if (k == 0) { // 第一个操作数通常是目标寄存器
|
||||
switch (opcode) {
|
||||
// 算术和逻辑指令 - 第一个操作数是定义
|
||||
case RVOpcodes::MV:
|
||||
case RVOpcodes::ADDI:
|
||||
case RVOpcodes::SLLI:
|
||||
case RVOpcodes::SRLI:
|
||||
case RVOpcodes::SRAI:
|
||||
case RVOpcodes::SLTI:
|
||||
case RVOpcodes::SLTIU:
|
||||
case RVOpcodes::XORI:
|
||||
case RVOpcodes::ORI:
|
||||
case RVOpcodes::ANDI:
|
||||
case RVOpcodes::ADD:
|
||||
case RVOpcodes::SUB:
|
||||
case RVOpcodes::SLL:
|
||||
case RVOpcodes::SLT:
|
||||
case RVOpcodes::SLTU:
|
||||
case RVOpcodes::XOR:
|
||||
case RVOpcodes::SRL:
|
||||
case RVOpcodes::SRA:
|
||||
case RVOpcodes::OR:
|
||||
case RVOpcodes::AND:
|
||||
case RVOpcodes::MUL:
|
||||
case RVOpcodes::DIV:
|
||||
case RVOpcodes::REM:
|
||||
case RVOpcodes::LW:
|
||||
case RVOpcodes::LH:
|
||||
case RVOpcodes::LB:
|
||||
case RVOpcodes::LHU:
|
||||
case RVOpcodes::LBU:
|
||||
|
||||
// 存储指令 - 第一个操作数是使用(要存储的值)
|
||||
case RVOpcodes::SW:
|
||||
case RVOpcodes::SH:
|
||||
case RVOpcodes::SB:
|
||||
// 分支指令 - 第一个操作数是使用
|
||||
case RVOpcodes::BEQ:
|
||||
case RVOpcodes::BNE:
|
||||
case RVOpcodes::BLT:
|
||||
case RVOpcodes::BGE:
|
||||
case RVOpcodes::BLTU:
|
||||
case RVOpcodes::BGEU:
|
||||
// 跳转指令 - 可能使用寄存器
|
||||
case RVOpcodes::JALR:
|
||||
isDefOperand = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
// 对于未知指令,保守地假设第一个操作数可能是使用
|
||||
isDefOperand = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是定义操作数,检查是否使用了目标寄存器
|
||||
if (!isDefOperand) {
|
||||
if (instr->getOperands()[k]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto *use_reg =
|
||||
static_cast<RegOperand *>(instr->getOperands()[k].get());
|
||||
if (areRegsEqual(reg, use_reg))
|
||||
return true;
|
||||
}
|
||||
// 检查内存操作数中的基址寄存器
|
||||
if (instr->getOperands()[k]->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto *mem =
|
||||
static_cast<MemOperand *>(instr->getOperands()[k].get());
|
||||
if (areRegsEqual(reg, mem->getBase()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 检查寄存器是否在指令中被重新定义(用于更精确的分析)
|
||||
auto isRegRedefinedAt =
|
||||
[](MachineInstr *instr, RegOperand *reg,
|
||||
const std::function<bool(RegOperand *, RegOperand *)> &areRegsEqual)
|
||||
-> bool {
|
||||
if (instr->getOperands().empty())
|
||||
return false;
|
||||
|
||||
auto opcode = instr->getOpcode();
|
||||
// 只有当第一个操作数是定义操作数时才检查
|
||||
switch (opcode) {
|
||||
case RVOpcodes::MV:
|
||||
case RVOpcodes::ADDI:
|
||||
case RVOpcodes::ADD:
|
||||
case RVOpcodes::SUB:
|
||||
case RVOpcodes::MUL:
|
||||
case RVOpcodes::LW:
|
||||
// ... 其他定义指令
|
||||
if (instr->getOperands()[0]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto *def_reg =
|
||||
static_cast<RegOperand *>(instr->getOperands()[0].get());
|
||||
return areRegsEqual(reg, def_reg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 检查是否为存储-加载模式,支持不同大小的访问
|
||||
auto isStoreLoadPattern = [](MachineInstr *store_instr,
|
||||
MachineInstr *load_instr) -> bool {
|
||||
auto store_op = store_instr->getOpcode();
|
||||
auto load_op = load_instr->getOpcode();
|
||||
|
||||
// 检查存储-加载对应关系
|
||||
return (store_op == RVOpcodes::SW && load_op == RVOpcodes::LW) || // 32位
|
||||
(store_op == RVOpcodes::SH &&
|
||||
load_op == RVOpcodes::LH) || // 16位有符号
|
||||
(store_op == RVOpcodes::SH &&
|
||||
load_op == RVOpcodes::LHU) || // 16位无符号
|
||||
(store_op == RVOpcodes::SB &&
|
||||
load_op == RVOpcodes::LB) || // 8位有符号
|
||||
(store_op == RVOpcodes::SB &&
|
||||
load_op == RVOpcodes::LBU) || // 8位无符号
|
||||
(store_op == RVOpcodes::SD && load_op == RVOpcodes::LD); // 64位
|
||||
};
|
||||
|
||||
// 检查两个内存访问是否访问相同的内存位置
|
||||
auto areMemoryAccessesEqual =
|
||||
[&areRegsEqual](MachineInstr *store_instr, MemOperand *store_mem,
|
||||
MachineInstr *load_instr, MemOperand *load_mem) -> bool {
|
||||
// 基址寄存器必须相同
|
||||
if (!areRegsEqual(store_mem->getBase(), load_mem->getBase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 偏移量必须相同
|
||||
if (store_mem->getOffset()->getValue() !=
|
||||
load_mem->getOffset()->getValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查访问大小是否兼容
|
||||
auto store_op = store_instr->getOpcode();
|
||||
auto load_op = load_instr->getOpcode();
|
||||
|
||||
// 获取访问大小(字节数)
|
||||
auto getAccessSize = [](RVOpcodes opcode) -> int {
|
||||
switch (opcode) {
|
||||
case RVOpcodes::LB:
|
||||
case RVOpcodes::LBU:
|
||||
case RVOpcodes::SB:
|
||||
return 1; // 8位
|
||||
case RVOpcodes::LH:
|
||||
case RVOpcodes::LHU:
|
||||
case RVOpcodes::SH:
|
||||
return 2; // 16位
|
||||
case RVOpcodes::LW:
|
||||
case RVOpcodes::SW:
|
||||
return 4; // 32位
|
||||
case RVOpcodes::LD:
|
||||
case RVOpcodes::SD:
|
||||
return 8; // 64位
|
||||
default:
|
||||
return -1; // 未知
|
||||
}
|
||||
};
|
||||
|
||||
int store_size = getAccessSize(store_op);
|
||||
int load_size = getAccessSize(load_op);
|
||||
|
||||
// 只有访问大小完全匹配时才能进行优化
|
||||
// 这避免了部分重叠访问的复杂情况
|
||||
return store_size > 0 && store_size == load_size;
|
||||
};
|
||||
|
||||
// 简单的内存别名分析:检查两个内存访问之间是否可能有冲突的内存操作
|
||||
auto isMemoryAccessSafe =
|
||||
[&](const std::vector<std::unique_ptr<MachineInstr>> &instrs,
|
||||
size_t store_idx, size_t load_idx, MemOperand *mem) -> bool {
|
||||
// 检查存储和加载之间是否有可能影响内存的指令
|
||||
for (size_t j = store_idx + 1; j < load_idx; ++j) {
|
||||
auto *between_instr = instrs[j].get();
|
||||
auto between_op = between_instr->getOpcode();
|
||||
|
||||
// 检查是否有其他内存写入操作
|
||||
switch (between_op) {
|
||||
case RVOpcodes::SW:
|
||||
case RVOpcodes::SH:
|
||||
case RVOpcodes::SB:
|
||||
case RVOpcodes::SD: {
|
||||
// 如果有其他存储操作,需要检查是否可能访问相同的内存
|
||||
if (between_instr->getOperands().size() >= 2 &&
|
||||
between_instr->getOperands()[1]->getKind() ==
|
||||
MachineOperand::KIND_MEM) {
|
||||
|
||||
auto *other_mem =
|
||||
static_cast<MemOperand *>(between_instr->getOperands()[1].get());
|
||||
|
||||
// 保守的别名分析:如果使用不同的基址寄存器,假设可能别名
|
||||
if (!areRegsEqual(mem->getBase(), other_mem->getBase())) {
|
||||
return false; // 可能的别名,不安全
|
||||
}
|
||||
|
||||
// 如果基址相同但偏移量不同,检查是否重叠
|
||||
int64_t offset1 = mem->getOffset()->getValue();
|
||||
int64_t offset2 = other_mem->getOffset()->getValue();
|
||||
|
||||
// 获取访问大小来检查重叠
|
||||
auto getAccessSize = [](RVOpcodes opcode) -> int {
|
||||
switch (opcode) {
|
||||
case RVOpcodes::SB:
|
||||
return 1;
|
||||
case RVOpcodes::SH:
|
||||
return 2;
|
||||
case RVOpcodes::SW:
|
||||
return 4;
|
||||
case RVOpcodes::SD:
|
||||
return 8;
|
||||
default:
|
||||
return 4; // 默认假设4字节
|
||||
}
|
||||
};
|
||||
|
||||
int size1 = getAccessSize(RVOpcodes::SW); // 从原存储指令推断
|
||||
int size2 = getAccessSize(between_op);
|
||||
|
||||
// 检查内存区域是否重叠
|
||||
bool overlaps =
|
||||
!(offset1 + size1 <= offset2 || offset2 + size2 <= offset1);
|
||||
if (overlaps) {
|
||||
return false; // 内存重叠,不安全
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 函数调用可能有副作用
|
||||
case RVOpcodes::JAL:
|
||||
case RVOpcodes::JALR:
|
||||
return false; // 函数调用可能修改内存,不安全
|
||||
|
||||
// 原子操作或其他可能修改内存的指令
|
||||
// 根据具体的RISC-V扩展添加更多指令
|
||||
default:
|
||||
// 对于未知指令,采用保守策略
|
||||
// 可以根据具体需求调整
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // 没有发现潜在的内存冲突
|
||||
};
|
||||
|
||||
// isPowerOfTwo: 检查数值是否为2的幂次,并返回其指数。
|
||||
auto isPowerOfTwo = [](int64_t n) -> int {
|
||||
if (n <= 0 || (n & (n - 1)) != 0)
|
||||
return -1;
|
||||
int shift = 0;
|
||||
while (n > 1) {
|
||||
n >>= 1;
|
||||
shift++;
|
||||
}
|
||||
return shift;
|
||||
};
|
||||
|
||||
for (auto &mbb_uptr : mfunc->getBlocks()) {
|
||||
auto &mbb = *mbb_uptr;
|
||||
auto &instrs = mbb.getInstructions();
|
||||
if (instrs.size() < 2)
|
||||
continue; // 基本块至少需要两条指令进行窥孔
|
||||
|
||||
// 遍历指令序列进行窥孔优化
|
||||
for (size_t i = 0; i + 1 < instrs.size();) {
|
||||
auto *mi1 = instrs[i].get();
|
||||
auto *mi2 = instrs[i + 1].get();
|
||||
bool changed = false;
|
||||
|
||||
// 1. 消除冗余交换移动: mv a, b; mv b, a -> mv a, b
|
||||
if (mi1->getOpcode() == RVOpcodes::MV &&
|
||||
mi2->getOpcode() == RVOpcodes::MV) {
|
||||
if (mi1->getOperands().size() == 2 && mi2->getOperands().size() == 2) {
|
||||
if (mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi2->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi2->getOperands()[1]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto *dst1 = static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *src1 = static_cast<RegOperand *>(mi1->getOperands()[1].get());
|
||||
auto *dst2 = static_cast<RegOperand *>(mi2->getOperands()[0].get());
|
||||
auto *src2 = static_cast<RegOperand *>(mi2->getOperands()[1].get());
|
||||
if (areRegsEqual(dst1, src2) && areRegsEqual(src1, dst2)) {
|
||||
instrs.erase(instrs.begin() + i + 1); // 移除第二条指令
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. 冗余加载消除: sw t0, offset(base); lw t1, offset(base) -> 替换或消除
|
||||
// lw 添加ld sd支持
|
||||
else if (isStoreLoadPattern(mi1, mi2)) {
|
||||
if (mi1->getOperands().size() == 2 && mi2->getOperands().size() == 2) {
|
||||
if (mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_MEM &&
|
||||
mi2->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi2->getOperands()[1]->getKind() == MachineOperand::KIND_MEM) {
|
||||
|
||||
auto *store_val =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *store_mem =
|
||||
static_cast<MemOperand *>(mi1->getOperands()[1].get());
|
||||
auto *load_val =
|
||||
static_cast<RegOperand *>(mi2->getOperands()[0].get());
|
||||
auto *load_mem =
|
||||
static_cast<MemOperand *>(mi2->getOperands()[1].get());
|
||||
|
||||
// 检查内存访问是否匹配(基址、偏移量和访问大小)
|
||||
if (areMemoryAccessesEqual(mi1, store_mem, mi2, load_mem)) {
|
||||
// 进行简单的内存别名分析
|
||||
if (isMemoryAccessSafe(instrs, i, i + 1, store_mem)) {
|
||||
if (areRegsEqual(store_val, load_val)) {
|
||||
// sw r1, mem; lw r1, mem -> 消除冗余的lw
|
||||
instrs.erase(instrs.begin() + i + 1);
|
||||
changed = true;
|
||||
} else {
|
||||
// sw r1, mem; lw r2, mem -> 替换lw为mv r2, r1
|
||||
auto newInstr = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
newInstr->addOperand(std::make_unique<RegOperand>(*load_val));
|
||||
newInstr->addOperand(
|
||||
std::make_unique<RegOperand>(*store_val));
|
||||
instrs[i + 1] = std::move(newInstr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. 强度削减: mul y, x, 2^n -> slli y, x, n
|
||||
else if (mi1->getOpcode() == RVOpcodes::MUL &&
|
||||
mi1->getOperands().size() == 3) {
|
||||
auto *dst_op = mi1->getOperands()[0].get();
|
||||
auto *src1_op = mi1->getOperands()[1].get();
|
||||
auto *src2_op = mi1->getOperands()[2].get();
|
||||
|
||||
if (dst_op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto *dst_reg = static_cast<RegOperand *>(dst_op);
|
||||
RegOperand *src_reg = nullptr;
|
||||
int shift = -1;
|
||||
|
||||
if (src1_op->getKind() == MachineOperand::KIND_REG &&
|
||||
src2_op->getKind() == MachineOperand::KIND_IMM) {
|
||||
shift =
|
||||
isPowerOfTwo(static_cast<ImmOperand *>(src2_op)->getValue());
|
||||
if (shift >= 0)
|
||||
src_reg = static_cast<RegOperand *>(src1_op);
|
||||
} else if (src1_op->getKind() == MachineOperand::KIND_IMM &&
|
||||
src2_op->getKind() == MachineOperand::KIND_REG) {
|
||||
shift =
|
||||
isPowerOfTwo(static_cast<ImmOperand *>(src1_op)->getValue());
|
||||
if (shift >= 0)
|
||||
src_reg = static_cast<RegOperand *>(src2_op);
|
||||
}
|
||||
|
||||
if (src_reg && shift >= 0 &&
|
||||
shift <= 31) { // RISC-V 移位量限制 (0-31)
|
||||
auto newInstr = std::make_unique<MachineInstr>(RVOpcodes::SLLI);
|
||||
newInstr->addOperand(std::make_unique<RegOperand>(*dst_reg));
|
||||
newInstr->addOperand(std::make_unique<RegOperand>(*src_reg));
|
||||
newInstr->addOperand(std::make_unique<ImmOperand>(shift));
|
||||
instrs[i] = std::move(newInstr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 4. 地址计算优化: addi dst, base, imm1; lw/sw val, imm2(dst) -> lw/sw
|
||||
// val, (imm1+imm2)(base)
|
||||
else if (mi1->getOpcode() == RVOpcodes::ADDI &&
|
||||
mi1->getOperands().size() == 3) {
|
||||
auto opcode2 = mi2->getOpcode();
|
||||
if (opcode2 == RVOpcodes::LW || opcode2 == RVOpcodes::SW) {
|
||||
if (mi2->getOperands().size() == 2 &&
|
||||
mi2->getOperands()[1]->getKind() == MachineOperand::KIND_MEM &&
|
||||
mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[2]->getKind() == MachineOperand::KIND_IMM) {
|
||||
|
||||
auto *addi_dst =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *addi_base =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[1].get());
|
||||
auto *addi_imm =
|
||||
static_cast<ImmOperand *>(mi1->getOperands()[2].get());
|
||||
|
||||
auto *mem_op =
|
||||
static_cast<MemOperand *>(mi2->getOperands()[1].get());
|
||||
auto *mem_base = mem_op->getBase();
|
||||
auto *mem_imm = mem_op->getOffset();
|
||||
|
||||
// 检查 ADDI 的目标寄存器是否是内存操作的基址
|
||||
if (areRegsEqual(addi_dst, mem_base)) {
|
||||
// 改进的使用检查:考虑寄存器可能在后续被重新定义的情况
|
||||
bool canOptimize = true;
|
||||
|
||||
// 检查从 i+2 开始的指令
|
||||
for (size_t j = i + 2; j < instrs.size(); ++j) {
|
||||
auto *later_instr = instrs[j].get();
|
||||
|
||||
// 如果寄存器被重新定义,那么它后面的使用就不相关了
|
||||
if (isRegRedefinedAt(later_instr, addi_dst, areRegsEqual)) {
|
||||
break; // 寄存器被重新定义,可以安全优化
|
||||
}
|
||||
|
||||
// 如果寄存器被使用,则不能优化
|
||||
if (isRegUsedLater(instrs, addi_dst, j)) {
|
||||
canOptimize = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canOptimize) {
|
||||
int64_t new_offset = addi_imm->getValue() + mem_imm->getValue();
|
||||
// 检查新偏移量是否符合 RISC-V 12位有符号立即数范围
|
||||
if (new_offset >= -2048 && new_offset <= 2047) {
|
||||
auto new_mem_op = std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(*addi_base),
|
||||
std::make_unique<ImmOperand>(new_offset));
|
||||
mi2->getOperands()[1] = std::move(new_mem_op);
|
||||
instrs.erase(instrs.begin() + i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 5. 冗余移动指令消除: mv x, y; op z, x, ... -> op z, y, ... (如果 x
|
||||
// 之后不再使用)
|
||||
else if (mi1->getOpcode() == RVOpcodes::MV &&
|
||||
mi1->getOperands().size() == 2) {
|
||||
if (mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_REG) {
|
||||
|
||||
auto *mv_dst = static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *mv_src = static_cast<RegOperand *>(mi1->getOperands()[1].get());
|
||||
|
||||
// 检查第二条指令是否使用了 mv 的目标寄存器
|
||||
std::vector<size_t> use_positions;
|
||||
for (size_t k = 1; k < mi2->getOperands().size(); ++k) {
|
||||
if (mi2->getOperands()[k]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto *use_reg =
|
||||
static_cast<RegOperand *>(mi2->getOperands()[k].get());
|
||||
if (areRegsEqual(mv_dst, use_reg)) {
|
||||
use_positions.push_back(k);
|
||||
}
|
||||
}
|
||||
// 也检查内存操作数中的基址寄存器
|
||||
else if (mi2->getOperands()[k]->getKind() ==
|
||||
MachineOperand::KIND_MEM) {
|
||||
auto *mem =
|
||||
static_cast<MemOperand *>(mi2->getOperands()[k].get());
|
||||
if (areRegsEqual(mv_dst, mem->getBase())) {
|
||||
// 对于内存操作数,我们需要创建新的MemOperand
|
||||
auto new_mem = std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(*mv_src),
|
||||
std::make_unique<ImmOperand>(mem->getOffset()->getValue()));
|
||||
mi2->getOperands()[k] = std::move(new_mem);
|
||||
use_positions.push_back(k); // 标记已处理
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_positions.empty()) {
|
||||
// 改进的后续使用检查
|
||||
bool canOptimize = true;
|
||||
for (size_t j = i + 2; j < instrs.size(); ++j) {
|
||||
auto *later_instr = instrs[j].get();
|
||||
|
||||
// 如果寄存器被重新定义,后续使用就不相关了
|
||||
if (isRegRedefinedAt(later_instr, mv_dst, areRegsEqual)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查是否还有其他使用
|
||||
if (isRegUsedLater(instrs, mv_dst, j)) {
|
||||
canOptimize = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canOptimize) {
|
||||
// 替换所有寄存器使用(内存操作数已在上面处理)
|
||||
for (size_t pos : use_positions) {
|
||||
if (mi2->getOperands()[pos]->getKind() ==
|
||||
MachineOperand::KIND_REG) {
|
||||
mi2->getOperands()[pos] =
|
||||
std::make_unique<RegOperand>(*mv_src);
|
||||
}
|
||||
}
|
||||
instrs.erase(instrs.begin() + i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 6. 连续加法指令合并: addi t1, t0, imm1; addi t2, t1, imm2 -> addi t2,
|
||||
// t0, (imm1+imm2)
|
||||
else if (mi1->getOpcode() == RVOpcodes::ADDI &&
|
||||
mi2->getOpcode() == RVOpcodes::ADDI) {
|
||||
if (mi1->getOperands().size() == 3 && mi2->getOperands().size() == 3) {
|
||||
if (mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[2]->getKind() == MachineOperand::KIND_IMM &&
|
||||
mi2->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi2->getOperands()[1]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi2->getOperands()[2]->getKind() == MachineOperand::KIND_IMM) {
|
||||
|
||||
auto *addi1_dst =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *addi1_src =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[1].get());
|
||||
auto *addi1_imm =
|
||||
static_cast<ImmOperand *>(mi1->getOperands()[2].get());
|
||||
|
||||
auto *addi2_dst =
|
||||
static_cast<RegOperand *>(mi2->getOperands()[0].get());
|
||||
auto *addi2_src =
|
||||
static_cast<RegOperand *>(mi2->getOperands()[1].get());
|
||||
auto *addi2_imm =
|
||||
static_cast<ImmOperand *>(mi2->getOperands()[2].get());
|
||||
|
||||
// 检查第一个ADDI的目标是否是第二个ADDI的源
|
||||
if (areRegsEqual(addi1_dst, addi2_src)) {
|
||||
// 改进的中间寄存器使用检查
|
||||
bool canOptimize = true;
|
||||
for (size_t j = i + 2; j < instrs.size(); ++j) {
|
||||
auto *later_instr = instrs[j].get();
|
||||
|
||||
// 如果中间寄存器被重新定义,后续使用不相关
|
||||
if (isRegRedefinedAt(later_instr, addi1_dst, areRegsEqual)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查是否有其他使用
|
||||
if (isRegUsedLater(instrs, addi1_dst, j)) {
|
||||
canOptimize = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canOptimize) {
|
||||
int64_t new_imm = addi1_imm->getValue() + addi2_imm->getValue();
|
||||
// 检查新立即数范围
|
||||
if (new_imm >= -2048 && new_imm <= 2047) {
|
||||
auto newInstr =
|
||||
std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
newInstr->addOperand(
|
||||
std::make_unique<RegOperand>(*addi2_dst));
|
||||
newInstr->addOperand(
|
||||
std::make_unique<RegOperand>(*addi1_src));
|
||||
newInstr->addOperand(std::make_unique<ImmOperand>(new_imm));
|
||||
instrs[i + 1] = std::move(newInstr);
|
||||
instrs.erase(instrs.begin() + i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. ADD with zero optimization: add r1, r2, zero -> mv r1, r2
|
||||
else if (mi1->getOpcode() == RVOpcodes::ADD &&
|
||||
mi1->getOperands().size() == 3) {
|
||||
if (mi1->getOperands()[0]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[1]->getKind() == MachineOperand::KIND_REG &&
|
||||
mi1->getOperands()[2]->getKind() == MachineOperand::KIND_REG) {
|
||||
|
||||
auto *add_dst =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[0].get());
|
||||
auto *add_src1 =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[1].get());
|
||||
auto *add_src2 =
|
||||
static_cast<RegOperand *>(mi1->getOperands()[2].get());
|
||||
|
||||
// 检查第二个源操作数是否为ZERO寄存器
|
||||
if (!add_src2->isVirtual() &&
|
||||
add_src2->getPReg() == PhysicalReg::ZERO) {
|
||||
// 创建新的 MV 指令
|
||||
auto newInstr = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
newInstr->addOperand(std::make_unique<RegOperand>(*add_dst));
|
||||
newInstr->addOperand(std::make_unique<RegOperand>(*add_src1));
|
||||
instrs[i] = std::move(newInstr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据是否发生变化调整遍历索引
|
||||
if (!changed) {
|
||||
++i; // 没有优化,继续检查下一对指令
|
||||
} else {
|
||||
// 发生变化,适当回退以捕获新的优化机会。
|
||||
// 这是一种安全的回退策略,可以触发连锁优化,且不会导致无限循环。
|
||||
if (i > 0) {
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,5 +1,6 @@
|
||||
#include "RISCv64RegAlloc.h"
|
||||
#include "RISCv64ISel.h"
|
||||
#include "RISCv64AsmPrinter.h" // For DEBUG output
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream> // For DEBUG output
|
||||
@ -30,7 +31,21 @@ void RISCv64RegAlloc::run() {
|
||||
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
|
||||
handleCallingConvention();
|
||||
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
|
||||
eliminateFrameIndices();
|
||||
eliminateFrameIndices();
|
||||
{ // 使用大括号创建一个局部作用域,避免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指令对调用者保存寄存器的影响)
|
||||
@ -42,18 +57,22 @@ void RISCv64RegAlloc::run() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@ -74,6 +93,30 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,76 +273,92 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
// JAL 和 JALR 指令定义 ra (x1)
|
||||
if (opcode == RVOpcodes::JAL || opcode == RVOpcodes::JALR) {
|
||||
// 使用 ra 对应的特殊虚拟寄存器ID
|
||||
def.insert(static_cast<unsigned>(PhysicalReg::RA));
|
||||
def.insert(preg_to_vreg_id_map.at(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()) {
|
||||
// 根据 s1 分支 ISel 定义的协议来解析操作数列表
|
||||
bool first_reg_operand_is_def = true;
|
||||
for (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());
|
||||
// 协议:第一个寄存器操作数是返回值 (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指令隐式定义(杀死)了所有调用者保存的寄存器。
|
||||
// **这部分逻辑不在getInstrUseDef中直接处理**。
|
||||
// 而是通过`buildInterferenceGraph`中添加物理寄存器节点与活跃虚拟寄存器之间的干扰边来完成。
|
||||
// 这样 Liveness Analysis 可以在虚拟寄存器层面进行,而物理寄存器干扰的复杂性则留给干扰图。
|
||||
|
||||
return; // CALL 指令处理完毕,直接返回
|
||||
return; // CALL 指令处理完毕
|
||||
}
|
||||
|
||||
// 3. 对其他所有指令的通用处理逻辑
|
||||
// 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) {
|
||||
|
||||
if (first_reg_is_def) {
|
||||
// --- 处理定义(Def) ---
|
||||
if (reg_op->isVirtual()) {
|
||||
def.insert(reg_op->getVRegNum());
|
||||
first_reg_is_def = false; // 确保每条指令只定义一个目标寄存器
|
||||
} 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) { // 且第一个操作数是寄存器
|
||||
!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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,45 +403,104 @@ 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);
|
||||
// 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());
|
||||
|
||||
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.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() {
|
||||
@ -401,27 +519,75 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
all_vregs.insert(preg_to_vreg_id_map.at(preg));
|
||||
}
|
||||
|
||||
|
||||
// 初始化干扰图邻接表
|
||||
for (auto vreg : all_vregs) { interference_graph[vreg] = {}; }
|
||||
|
||||
// 创建一个临时的AsmPrinter用于打印指令,方便调试
|
||||
RISCv64AsmPrinter temp_printer(MFunc);
|
||||
temp_printer.setStream(std::cerr);
|
||||
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
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.get(), use, def);
|
||||
const LiveSet& live_out = live_out_map.at(instr.get());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *** 核心修改点:处理 CALL 指令的隐式 def ***
|
||||
// 在非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();
|
||||
@ -431,12 +597,17 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
// 将这个物理寄存器节点与 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -444,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);
|
||||
}
|
||||
}
|
||||
@ -457,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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的所有前驱结点
|
||||
// 迭代计算支配结点,直到不再变化
|
||||
// 这里使用迭代法,直到支配结点不再变化
|
||||
// TODO:Lengauer-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
|
||||
|
||||
@ -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
|
||||
33
src/include/CalleeSavedHandler.h
Normal file
33
src/include/CalleeSavedHandler.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef CALLEE_SAVED_HANDLER_H
|
||||
#define CALLEE_SAVED_HANDLER_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class CalleeSavedHandler
|
||||
* @brief 处理被调用者保存寄存器(Callee-Saved Registers)的Pass。
|
||||
* * 这个Pass在寄存器分配之后运行。它的主要职责是:
|
||||
* 1. 扫描整个函数,找出所有被使用的 `s` 系列寄存器。
|
||||
* 2. 在函数序言中插入 `sd` 指令来保存这些寄存器。
|
||||
* 3. 在函数结尾(ret指令前)插入 `ld` 指令来恢复这些寄存器。
|
||||
* 4. 正确计算因保存这些寄存器而需要的额外栈空间,并更新StackFrameInfo。
|
||||
*/
|
||||
class CalleeSavedHandler : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
CalleeSavedHandler() : Pass("callee-saved-handler", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // CALLEE_SAVED_HANDLER_H
|
||||
30
src/include/PostRA_Scheduler.h
Normal file
30
src/include/PostRA_Scheduler.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef POST_RA_SCHEDULER_H
|
||||
#define POST_RA_SCHEDULER_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class PostRA_Scheduler
|
||||
* @brief 寄存器分配后的局部指令调度器
|
||||
* * 主要目标是优化寄存器分配器插入的spill/fill代码(lw/sw),
|
||||
* 尝试将加载指令提前,以隐藏其访存延迟。
|
||||
*/
|
||||
class PostRA_Scheduler : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PostRA_Scheduler() : Pass("post-ra-scheduler", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // POST_RA_SCHEDULER_H
|
||||
30
src/include/PreRA_Scheduler.h
Normal file
30
src/include/PreRA_Scheduler.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef PRE_RA_SCHEDULER_H
|
||||
#define PRE_RA_SCHEDULER_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class PreRA_Scheduler
|
||||
* @brief 寄存器分配前的指令调度器
|
||||
* * 在虚拟寄存器上进行操作,此时调度自由度最大,
|
||||
* 主要目标是隐藏指令延迟,提高流水线效率。
|
||||
*/
|
||||
class PreRA_Scheduler : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PreRA_Scheduler() : Pass("pre-ra-scheduler", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // PRE_RA_SCHEDULER_H
|
||||
@ -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
|
||||
|
||||
@ -39,8 +39,8 @@ enum class PhysicalReg {
|
||||
|
||||
// 用于内部表示物理寄存器在干扰图中的节点ID(一个简单的特殊ID,确保不与vreg_counter冲突)
|
||||
// 假设 vreg_counter 不会达到这么大的值
|
||||
PHYS_REG_START_ID = 10000,
|
||||
PHYS_REG_END_ID = PHYS_REG_START_ID + 32, // 预留足够的空间
|
||||
PHYS_REG_START_ID = 100000,
|
||||
PHYS_REG_END_ID = PHYS_REG_START_ID + 320, // 预留足够的空间
|
||||
};
|
||||
|
||||
// RISC-V 指令操作码枚举
|
||||
@ -195,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; // 使用的保存寄存器
|
||||
|
||||
@ -2,74 +2,14 @@
|
||||
#define RISCV64_PASSES_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "RISCv64Peephole.h"
|
||||
#include "PreRA_Scheduler.h"
|
||||
#include "PostRA_Scheduler.h"
|
||||
#include "CalleeSavedHandler.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class BackendPass
|
||||
* @brief 所有优化Pass的抽象基类 (可选,但推荐)
|
||||
* * 定义一个通用的接口,所有优化都应该实现它。
|
||||
*/
|
||||
class BackendPass {
|
||||
public:
|
||||
virtual ~BackendPass() = default;
|
||||
virtual void runOnMachineFunction(MachineFunction* mfunc) = 0;
|
||||
};
|
||||
|
||||
|
||||
// --- 寄存器分配前优化 ---
|
||||
|
||||
/**
|
||||
* @class PreRA_Scheduler
|
||||
* @brief 寄存器分配前的指令调度器
|
||||
* * 在虚拟寄存器上进行操作,此时调度自由度最大,
|
||||
* 主要目标是隐藏指令延迟,提高流水线效率。
|
||||
*/
|
||||
class PreRA_Scheduler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class CalleeSavedHandler
|
||||
* @brief 处理被调用者保存寄存器(Callee-Saved Registers)的Pass。
|
||||
* * 这个Pass在寄存器分配之后运行。它的主要职责是:
|
||||
* 1. 扫描整个函数,找出所有被使用的 `s` 系列寄存器。
|
||||
* 2. 在函数序言中插入 `sd` 指令来保存这些寄存器。
|
||||
* 3. 在函数结尾(ret指令前)插入 `ld` 指令来恢复这些寄存器。
|
||||
* 4. 正确计算因保存这些寄存器而需要的额外栈空间,并更新StackFrameInfo。
|
||||
*/
|
||||
class CalleeSavedHandler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
// --- 寄存器分配后优化 ---
|
||||
|
||||
/**
|
||||
* @class PeepholeOptimizer
|
||||
* @brief 窥孔优化器
|
||||
* * 在已分配物理寄存器的指令流上,通过一个小的滑动窗口来查找
|
||||
* 并替换掉一些冗余或低效的指令模式。
|
||||
*/
|
||||
class PeepholeOptimizer : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PostRA_Scheduler
|
||||
* @brief 寄存器分配后的局部指令调度器
|
||||
* * 主要目标是优化寄存器分配器插入的spill/fill代码(lw/sw),
|
||||
* 尝试将加载指令提前,以隐藏其访存延迟。
|
||||
*/
|
||||
class PostRA_Scheduler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // RISCV64_PASSES_H
|
||||
30
src/include/RISCv64Peephole.h
Normal file
30
src/include/RISCv64Peephole.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef RISCV64_PEEPHOLE_H
|
||||
#define RISCV64_PEEPHOLE_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class PeepholeOptimizer
|
||||
* @brief 窥孔优化器
|
||||
* * 在已分配物理寄存器的指令流上,通过一个小的滑动窗口来查找
|
||||
* 并替换掉一些冗余或低效的指令模式。
|
||||
*/
|
||||
class PeepholeOptimizer : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PeepholeOptimizer() : Pass("peephole-optimizer", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // RISCV64_PEEPHOLE_H
|
||||
@ -4,6 +4,9 @@
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "RISCv64ISel.h" // 包含 RISCv64ISel.h 以访问 ISel 和 Value 类型
|
||||
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class RISCv64RegAlloc {
|
||||
@ -61,6 +64,9 @@ private:
|
||||
|
||||
// 用于计算类型大小的辅助函数
|
||||
unsigned getTypeSizeInBytes(Type* type);
|
||||
|
||||
// 辅助函数,用于打印集合
|
||||
static void printLiveSet(const LiveSet& s, const std::string& name, std::ostream& os);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user