Files
mysysy/src/RISCv32Backend.cpp
2025-06-24 00:35:38 +08:00

1108 lines
56 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "RISCv32Backend.h"
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <regex>
#include <iomanip>
#include <functional> // For std::function
namespace sysy {
// 可用于分配的通用寄存器
const std::vector<RISCv32CodeGen::PhysicalReg> RISCv32CodeGen::allocable_regs = {
PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3,
PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6,
PhysicalReg::A0, PhysicalReg::A1, PhysicalReg::A2, PhysicalReg::A3,
PhysicalReg::A4, PhysicalReg::A5, PhysicalReg::A6, PhysicalReg::A7,
PhysicalReg::S0, PhysicalReg::S1, PhysicalReg::S2, PhysicalReg::S3,
PhysicalReg::S4, PhysicalReg::S5, PhysicalReg::S6, PhysicalReg::S7,
PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11
};
// 将物理寄存器枚举转换为字符串
std::string RISCv32CodeGen::reg_to_string(PhysicalReg reg) {
switch (reg) {
case PhysicalReg::ZERO: return "x0"; // 零寄存器
case PhysicalReg::RA: return "ra"; // 返回地址寄存器
case PhysicalReg::SP: return "sp"; // 栈指针
case PhysicalReg::GP: return "gp"; // 全局指针
case PhysicalReg::TP: return "tp"; // 线程指针
case PhysicalReg::T0: return "t0"; // 临时寄存器
case PhysicalReg::T1: return "t1"; // 临时寄存器
case PhysicalReg::T2: return "t2"; // 临时寄存器
case PhysicalReg::S0: return "s0"; // 帧指针 / 保存的寄存器
case PhysicalReg::S1: return "s1"; // 保存的寄存器
case PhysicalReg::A0: return "a0"; // 函数参数 / 返回值寄存器
case PhysicalReg::A1: return "a1"; // 函数参数
case PhysicalReg::A2: return "a2"; // 函数参数
case PhysicalReg::A3: return "a3"; // 函数参数
case PhysicalReg::A4: return "a4"; // 函数参数
case PhysicalReg::A5: return "a5"; // 函数参数
case PhysicalReg::A6: return "a6"; // 函数参数
case PhysicalReg::A7: return "a7"; // 函数参数
case PhysicalReg::S2: return "s2"; // 保存的寄存器
case PhysicalReg::S3: return "s3"; // 保存的寄存器
case PhysicalReg::S4: return "s4"; // 保存的寄存器
case PhysicalReg::S5: return "s5"; // 保存的寄存器
case PhysicalReg::S6: return "s6"; // 保存的寄存器
case PhysicalReg::S7: return "s7"; // 保存的寄存器
case PhysicalReg::S8: return "s8"; // 保存的寄存器
case PhysicalReg::S9: return "s9"; // 保存的寄存器
case PhysicalReg::S10: return "s10"; // 保存的寄存器
case PhysicalReg::S11: return "s11"; // 保存的寄存器
case PhysicalReg::T3: return "t3"; // 临时寄存器
case PhysicalReg::T4: return "t4"; // 临时寄存器
case PhysicalReg::T5: return "t5"; // 临时寄存器
case PhysicalReg::T6: return "t6"; // 临时寄存器
default: return "UNKNOWN_REG";
}
}
// 总体代码生成入口
std::string RISCv32CodeGen::code_gen() {
std::stringstream ss;
ss << module_gen();
return ss.str();
}
// 模块级代码生成 (处理全局变量和函数)
std::string RISCv32CodeGen::module_gen() {
std::stringstream ss;
bool has_globals = !module->getGlobals().empty();
if (has_globals) {
ss << ".data\n"; // 数据段
for (const auto& global : module->getGlobals()) {
ss << ".globl " << global->getName() << "\n"; // 声明全局符号
ss << global->getName() << ":\n"; // 标签
const auto& init_values = global->getInitValues();
for (size_t i = 0; i < init_values.getValues().size(); ++i) {
auto val = init_values.getValues()[i];
auto count = init_values.getNumbers()[i];
if (auto constant = dynamic_cast<ConstantValue*>(val)) {
for (unsigned j = 0; j < count; ++j) {
if (constant->isInt()) {
ss << " .word " << constant->getInt() << "\n"; // 整数常量
} else {
float f = constant->getFloat();
uint32_t float_bits = *(uint32_t*)&f;
ss << " .word " << float_bits << "\n"; // 浮点常量 (按位存储)
}
}
}
}
}
}
if (!module->getFunctions().empty()) {
ss << ".text\n"; // 代码段
for (const auto& func : module->getFunctions()) {
ss << function_gen(func.second.get());
}
}
return ss.str();
}
// 函数级代码生成
std::string RISCv32CodeGen::function_gen(Function* func) {
std::stringstream ss;
ss << ".globl " << func->getName() << "\n"; // 声明函数为全局符号
ss << func->getName() << ":\n"; // 函数入口标签
// 执行寄存器分配
auto alloc = register_allocation(func);
int stack_size = alloc.stack_size;
// 函数序言 (Prologue)
// 保存 ra 和 s0, 调整栈指针
// s0 指向当前帧的底部(分配局部变量/溢出空间后的 sp
// 栈布局:
// +------------+ 高地址
// | 参数溢出 | (如果参数超过 8 个)
// +------------+
// | 保存的 RA | (stack_size - 4)(sp)
// +------------+
// | 保存的 S0 | (stack_size - 8)(sp)
// +------------+
// | 局部变量/溢出 | 0(sp) 到 (stack_size - 8 - 局部变量大小)(sp)
// +------------+ 低地址
// 确保栈大小 16 字节对齐
int aligned_stack_size = (stack_size + 15) & ~15;
// 只有当需要栈空间时才生成序言
if (aligned_stack_size > 0) {
ss << " addi sp, sp, -" << aligned_stack_size << "\n"; // 调整栈指针
ss << " sw ra, " << (aligned_stack_size - 4) << "(sp)\n"; // 保存返回地址
ss << " sw s0, " << (aligned_stack_size - 8) << "(sp)\n"; // 保存帧指针
ss << " mv s0, sp\n"; // 设置新的帧指针
}
// 生成每个基本块的代码
int block_idx = 0; // 用于生成默认 entry 标签的索引
for (const auto& bb : func->getBasicBlocks()) {
ss << basicBlock_gen(bb.get(), alloc, block_idx++);
}
// 函数尾声 (Epilogue) 由 RETURN DAGNode 的指令选择处理,确保正确恢复栈和寄存器
return ss.str();
}
// 基本块代码生成
std::string RISCv32CodeGen::basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc, int block_idx) {
std::stringstream ss;
// 修复空标签问题:如果块名为空,使用伪名称 (例如 entry_block_0)
std::string bb_name = bb->getName();
if (bb_name.empty()) {
bb_name = ENTRY_BLOCK_PSEUDO_NAME + std::to_string(block_idx);
// 如果是第一个块,且没有名称,则通常是函数入口块,可以考虑直接使用 "entry"
// 但为了通用性,伪名称更安全
if (block_idx == 0) { // 第一个块通常是 entry 块
bb_name = "entry";
}
}
ss << bb_name << ":\n"; // 基本块标签
// 为每个基本块重置虚拟寄存器映射和计数器,因为 DAG 是按块构建的
// 注意:这里的重置可能会影响跨块的活跃性分析,如果 DAG 构建是跨块的,则不能在这里重置
// 但目前 DAG 是按块构建,所以在此重置是合理的。
value_vreg_map.clear();
vreg_counter = 0; // 为每个块重置虚拟寄存器计数器
// 构建当前基本块的 DAG
// 注意DAGNode 的唯一性在当前函数范围内是重要的,
// 所以 build_dag 应该返回一个完整的 DAG 节点列表,而不是每次都创建新的。
// 为了简化,这里仍然按块构建,但需要注意跨块值的使用。
auto dag_nodes_for_bb = build_dag(bb);
print_dag(dag_nodes_for_bb, bb_name); // 打印 DAG 调试信息
// 存储最终生成的指令
std::set<DAGNode*> emitted_nodes; // 跟踪已发射的节点,防止重复
std::vector<std::string> ordered_insts; // 用于收集指令并按序排列
// 在 DAG 中遍历并生成指令。由于 select_instructions 可能会递归地为操作数选择指令,
// 并且 emit_instructions 也会递归地发射,我们需要一个机制来确保指令的正确顺序和唯一性。
// 最简单的方法是逆拓扑序遍历所有节点,确保其操作数先被处理。
// 但是目前的 DAG 构建方式可能不支持直接的拓扑排序,
// 我们将依赖 emit_instructions 的递归特性来处理依赖。
// 遍历 DAG 的根节点(没有用户的节点,或者 Store/Return/Branch 节点)
// 从这些节点开始递归发射指令。
// NOTE: 这种发射方式可能不总是产生最优的代码顺序,但可以确保依赖关系。
for (auto it = dag_nodes_for_bb.rbegin(); it != dag_nodes_for_bb.rend(); ++it) {
DAGNode* node = it->get();
// 只有那些没有用户或者代表副作用如STORE, RETURN, BRANCH的节点才需要作为发射的“根”
// 否则,它们会被其用户节点递归地发射
// 然而,为了确保所有指令都被发射,我们通常从所有节点(或者至少是副作用节点)开始发射
// 并且利用 emitted_nodes 集合防止重复
// 这里简化为对所有 DAG 节点进行一次 select_instructions 和 emit_instructions 调用。
// emit_instructions 会通过递归处理其操作数来保证依赖顺序。
select_instructions(node, alloc); // 为当前节点选择指令
}
// 收集所有指令到一个临时的 vector 中,然后进行排序
// 注意:这里的发射逻辑需要重新设计,目前的 emit_instructions 是直接添加到 std::vector<std::string>& insts 中
// 并且期望是按顺序添加的,这在递归时难以保证。
// 更好的方法是让 emit_instructions 直接输出到 stringstream并控制递归顺序。
// 但是为了最小化改动,我们先保持 emit_instructions 的现有签名,
// 然后在它内部处理指令的收集和去重。
// 重新设计 emit_instructions 的调用方式
// 这里的思路是,每个 DAGNode 都存储了自己及其依赖(如果未被其他节点引用)的指令。
// 最终,我们遍历 BasicBlock 中的所有原始 IR 指令,找到它们对应的 DAGNode然后发射。
// 这是因为 IR 指令的顺序决定了代码的逻辑顺序。
// 遍历 IR 指令,并找到对应的 DAGNode 进行发射
// 由于 build_dag 是从 IR 指令顺序构建的,我们应该按照 IR 指令的顺序来发射。
emitted_nodes.clear(); // 再次清空已发射节点集合
// 临时存储每个 IR 指令对应的 DAGNode因为 DAGNode 列表是平铺的
std::map<Instruction*, DAGNode*> inst_to_dag_node;
for (const auto& dag_node_ptr : dag_nodes_for_bb) {
if (dag_node_ptr->value && dynamic_cast<Instruction*>(dag_node_ptr->value)) {
inst_to_dag_node[dynamic_cast<Instruction*>(dag_node_ptr->value)] = dag_node_ptr.get();
}
}
for (const auto& inst_ptr : bb->getInstructions()) {
DAGNode* node_to_emit = nullptr;
// 查找当前 IR 指令在 DAG 中对应的节点。
// 注意:不是所有 IR 指令都会直接映射到一个“根”DAGNode (例如,某些值可能只作为操作数存在)
// 但终结符(如 Branch, Return和 Store 指令总是重要的。
// 对于 load/binary 等,我们应该在 build_dag 中确保它们有一个结果 vreg并被后续指令使用。
// 如果一个 IR 指令是某个 DAGNode 的 value那么我们就发射那个 DAGNode。
if (inst_to_dag_node.count(inst_ptr.get())) {
node_to_emit = inst_to_dag_node.at(inst_ptr.get());
}
if (node_to_emit) {
// 注意select_instructions 已经在上面统一调用过,这里只需要 emit。
// 但如果 select_instructions 没有递归地为所有依赖选择指令,这里可能需要重新考虑。
// 为了简化,我们假定 select_instructions 在第一次被调用时(通常在 emit 之前)已经递归地为所有操作数选择了指令。
// 直接将指令添加到 ss 中,而不是通过 vector 中转
emit_instructions(node_to_emit, ss, alloc, emitted_nodes);
}
}
return ss.str();
}
// DAG 构建
std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(BasicBlock* bb) {
std::vector<std::unique_ptr<DAGNode>> nodes_storage; // 存储所有 unique_ptr
std::map<Value*, DAGNode*> value_to_node; // 将 IR Value* 映射到原始 DAGNode*,用于快速查找
// 辅助函数,用于创建 DAGNode 并管理其所有权
auto create_node = [&](DAGNode::NodeKind kind, Value* val = nullptr) -> DAGNode* {
// 优化:如果一个值已经有节点并且它不是控制流/存储/Alloca地址则重用它 (CSE)
// 对于 AllocaInst我们想创建一个代表其地址的节点但不一定直接为 AllocaInst 本身分配虚拟寄存器。
if (val && value_to_node.count(val) && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH && kind != DAGNode::ALLOCA_ADDR) {
return value_to_node[val];
}
auto node = std::make_unique<DAGNode>(kind);
node->value = val;
// 为产生结果的值分配虚拟寄存器
if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH && kind != DAGNode::ALLOCA_ADDR) {
node->result_vreg = "v" + std::to_string(vreg_counter++);
value_vreg_map[val] = node->result_vreg; // 将 IR Value 映射到其虚拟寄存器
} else if (kind == DAGNode::ALLOCA_ADDR) {
// 对于 AllocaInst 的地址,我们将虚拟寄存器分配给 DAGNode 本身,
// 而不是直接在 value_vreg_map 中分配给 AllocaInst (因为 AllocaInst 是内存位置)。
node->result_vreg = "v" + std::to_string(vreg_counter++);
// 考虑是否需要将 AllocaInst 映射到其虚拟寄存器,如果它被视为指针值
// value_vreg_map[val] = node->result_vreg; // 考虑这是否需要/正确
}
DAGNode* raw_node_ptr = node.get();
nodes_storage.push_back(std::move(node)); // 存储 unique_ptr
// 仅当 IR Value 表示一个计算值时,才将其映射到创建的 DAGNode
if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH) {
value_to_node[val] = raw_node_ptr;
}
return raw_node_ptr;
};
for (const auto& inst_ptr : bb->getInstructions()) {
auto inst = inst_ptr.get();
if (auto alloca = dynamic_cast<AllocaInst*>(inst)) {
// AllocaInst 本身不产生寄存器中的值,但其地址将被 load/store 使用。
// 创建一个节点来表示分配内存的地址。
// 这个地址将是 s0 (帧指针) 的偏移量。
// 我们将 AllocaInst 指针存储在 DAGNode 的 `value` 字段中。
auto alloca_addr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
// 此节点将有一个 result_vreg表示计算出的地址 (s0 + 偏移量)
// 实际偏移量将在寄存器分配阶段 (stack_map) 确定。
} else if (auto store = dynamic_cast<StoreInst*>(inst)) {
auto store_node = create_node(DAGNode::STORE, store); // 将 store inst 绑定到 node
// 获取要存储的值
Value* val_to_store_ir = store->getValue();
DAGNode* val_node = nullptr;
if (value_to_node.count(val_to_store_ir)) {
val_node = value_to_node[val_to_store_ir];
} else if (auto constant = dynamic_cast<ConstantValue*>(val_to_store_ir)) {
val_node = create_node(DAGNode::CONSTANT, constant);
} else { // 这是一个尚未在此块中计算的值,假设它需要加载 (从内存或参数)
val_node = create_node(DAGNode::LOAD, val_to_store_ir);
}
// 获取内存位置的指针
Value* ptr_ir = store->getPointer();
DAGNode* ptr_node = nullptr;
if (value_to_node.count(ptr_ir)) {
ptr_node = value_to_node[ptr_ir];
} else if (auto alloca = dynamic_cast<AllocaInst*>(ptr_ir)) {
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
} else { // 必须是存储在虚拟寄存器中的指针
ptr_node = create_node(DAGNode::LOAD, ptr_ir); // 这是一个产生指针的指令
}
store_node->operands.push_back(val_node);
store_node->operands.push_back(ptr_node);
val_node->users.push_back(store_node);
ptr_node->users.push_back(store_node);
} else if (auto load = dynamic_cast<LoadInst*>(inst)) {
if (value_to_node.count(load)) continue; // 共同子表达式消除 (CSE)
auto load_node = create_node(DAGNode::LOAD, load); // 为 load_node 分配 result_vreg 并映射 load
Value* ptr_ir = load->getPointer();
DAGNode* ptr_node = nullptr;
if (value_to_node.count(ptr_ir)) {
ptr_node = value_to_node[ptr_ir];
} else if (auto alloca = dynamic_cast<AllocaInst*>(ptr_ir)) {
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
} else { // 必须是存储在虚拟寄存器中的指针
ptr_node = create_node(DAGNode::LOAD, ptr_ir); // 这是一个产生指针的指令
}
load_node->operands.push_back(ptr_node);
ptr_node->users.push_back(load_node);
} else if (auto bin = dynamic_cast<BinaryInst*>(inst)) {
if (value_to_node.count(bin)) continue; // CSE
auto bin_node = create_node(DAGNode::BINARY, bin);
auto get_operand_node = [&](Value* operand_ir) -> DAGNode* {
if (value_to_node.count(operand_ir)) {
return value_to_node[operand_ir];
} else if (auto constant = dynamic_cast<ConstantValue*>(operand_ir)) {
return create_node(DAGNode::CONSTANT, constant);
} else {
// 这是一个由另一个指令或参数产生的值,如果不在 map 中,则假设需要加载
return create_node(DAGNode::LOAD, operand_ir);
}
};
DAGNode* lhs_node = get_operand_node(bin->getLhs());
DAGNode* rhs_node = get_operand_node(bin->getRhs());
bin_node->operands.push_back(lhs_node);
bin_node->operands.push_back(rhs_node);
lhs_node->users.push_back(bin_node);
rhs_node->users.push_back(bin_node);
} else if (auto call = dynamic_cast<CallInst*>(inst)) {
if (value_to_node.count(call)) continue; // CSE (如果结果被重用)
auto call_node = create_node(DAGNode::CALL, call); // 如果调用返回一个值,则分配 result_vreg
for (auto arg : call->getArguments()) {
auto arg_val_ir = arg->getValue();
DAGNode* arg_node = nullptr;
if (value_to_node.count(arg_val_ir)) {
arg_node = value_to_node[arg_val_ir];
} else if (auto constant = dynamic_cast<ConstantValue*>(arg_val_ir)) {
arg_node = create_node(DAGNode::CONSTANT, constant);
} else {
arg_node = create_node(DAGNode::LOAD, arg_val_ir);
}
call_node->operands.push_back(arg_node);
arg_node->users.push_back(call_node);
}
} else if (auto ret = dynamic_cast<ReturnInst*>(inst)) {
auto ret_node = create_node(DAGNode::RETURN, ret); // 将 return inst 绑定到 node
if (ret->hasReturnValue()) {
auto val_ir = ret->getReturnValue();
DAGNode* val_node = nullptr;
if (value_to_node.count(val_ir)) {
val_node = value_to_node[val_ir];
} else if (auto constant = dynamic_cast<ConstantValue*>(val_ir)) {
val_node = create_node(DAGNode::CONSTANT, constant);
} else {
val_node = create_node(DAGNode::LOAD, val_ir);
}
ret_node->operands.push_back(val_node);
val_node->users.push_back(ret_node);
}
} else if (auto cond_br = dynamic_cast<CondBrInst*>(inst)) {
auto br_node = create_node(DAGNode::BRANCH, cond_br); // 将 cond_br inst 绑定到 node
auto cond_ir = cond_br->getCondition();
if (auto constant_cond = dynamic_cast<ConstantValue*>(cond_ir)) {
// 优化常量条件分支为无条件跳转
br_node->inst = "j " + (constant_cond->getInt() ? cond_br->getThenBlock()->getName() : cond_br->getElseBlock()->getName());
// 对于直接跳转,不需要操作数
} else {
DAGNode* cond_node = nullptr;
if (value_to_node.count(cond_ir)) {
cond_node = value_to_node[cond_ir];
} else if (auto bin_cond = dynamic_cast<BinaryInst*>(cond_ir)) {
cond_node = create_node(DAGNode::BINARY, bin_cond);
} else { // 必须是一个需要加载的值
cond_node = create_node(DAGNode::LOAD, cond_ir);
}
br_node->operands.push_back(cond_node);
cond_node->users.push_back(br_node);
}
} else if (auto uncond_br = dynamic_cast<UncondBrInst*>(inst)) {
auto br_node = create_node(DAGNode::BRANCH, uncond_br); // 将 uncond_br inst 绑定到 node
br_node->inst = "j " + uncond_br->getBlock()->getName();
}
}
return nodes_storage;
}
// 打印 DAG
void RISCv32CodeGen::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, const std::string& bb_name) {
std::cerr << "=== DAG for Basic Block: " << bb_name << " ===\n";
std::set<DAGNode*> visited;
// 辅助映射,用于在打印输出中为节点分配顺序 ID
std::map<DAGNode*, int> node_to_id;
int current_id = 0;
for (const auto& node_ptr : dag) {
node_to_id[node_ptr.get()] = current_id++;
}
std::function<void(DAGNode*, int)> print_node = [&](DAGNode* node, int indent) {
if (!node) return;
std::string current_indent(indent, ' ');
int node_id = node_to_id.count(node) ? node_to_id[node] : -1; // 获取分配的 ID
std::cerr << current_indent << "Node#" << node_id << ": " << node->getNodeKindString();
if (!node->result_vreg.empty()) {
std::cerr << " (vreg: " << node->result_vreg << ")";
}
if (node->value) {
std::cerr << " [";
if (auto inst = dynamic_cast<Instruction*>(node->value)) {
std::cerr << inst->getKindString();
if (!inst->getName().empty()) {
std::cerr << "(" << inst->getName() << ")";
}
} else if (auto constant = dynamic_cast<ConstantValue*>(node->value)) {
if (constant->isInt()) {
std::cerr << "ConstInt(" << constant->getInt() << ")";
} else {
std::cerr << "ConstFloat(" << constant->getFloat() << ")";
}
} else if (auto global = dynamic_cast<GlobalValue*>(node->value)) {
std::cerr << "Global(" << global->getName() << ")";
} else if (auto alloca = dynamic_cast<AllocaInst*>(node->value)) {
std::cerr << "Alloca(" << (alloca->getName().empty() ? ("%" + std::to_string(reinterpret_cast<uintptr_t>(alloca) % 1000)) : alloca->getName()) << ")";
}
std::cerr << "]";
}
std::cerr << " -> Inst: \"" << node->inst << "\""; // 打印选定的指令
std::cerr << "\n";
if (visited.find(node) != visited.end()) {
std::cerr << current_indent << " (已打印后代)\n";
return; // 避免循环的无限递归
}
visited.insert(node);
if (!node->operands.empty()) {
std::cerr << current_indent << " 操作数:\n";
for (auto operand : node->operands) {
print_node(operand, indent + 4);
}
}
// 移除了 users 打印,以简化输出并避免 DAG 中的冗余递归。
// Users 更适用于向上遍历,而不是向下遍历。
};
// 遍历 DAG以尊重依赖的方式打印。
// 当前实现:遍历所有节点,从作为“根”的节点开始打印(没有用户或副作用节点)。
// 每次打印新的根时,重置 visited 集合,以允许共享子图被重新打印(尽管这不是最高效的方式)。
for (const auto& node_ptr : dag) {
// 只有那些没有用户或者表示副作用(如 store/branch/return的节点才被视为“根”
// 这样可以确保所有指令(包括那些没有明确结果的)都被打印
if (node_ptr->users.empty() || node_ptr->kind == DAGNode::STORE || node_ptr->kind == DAGNode::RETURN || node_ptr->kind == DAGNode::BRANCH) {
visited.clear(); // 为每个根重置 visited允许重新打印共享子图
print_node(node_ptr.get(), 0);
}
}
std::cerr << "=== DAG 结束 ===\n\n";
}
// 指令选择
void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& alloc) {
if (!node) return;
// ALLOCA_ADDR 节点不直接映射到一条指令,它表示的是地址的计算
if (!node->inst.empty() && node->kind != DAGNode::ALLOCA_ADDR) return; // 指令已选择
// 首先递归地为操作数选择指令
for (auto operand : node->operands) {
if (operand) {
select_instructions(operand, alloc);
}
}
std::stringstream ss_inst; // 使用 stringstream 构建指令
// 获取分配的物理寄存器,如果没有分配,则使用临时寄存器 (T0)
auto get_preg_or_temp = [&](const std::string& vreg) {
if (alloc.vreg_to_preg.count(vreg)) {
return reg_to_string(alloc.vreg_to_preg.at(vreg));
}
// 如果虚拟寄存器未分配给物理寄存器,则表示它已溢出或是一个临时值。
// 为简化,我们使用固定临时寄存器 t0 用于溢出值或需要加载到寄存器中的立即数。
// 更健壮的后端会在此处显式处理溢出。
return reg_to_string(PhysicalReg::T0); // 回退到临时寄存器
};
// 获取分配的栈变量的内存偏移量
auto get_stack_offset = [&](Value* val) {
if (alloc.stack_map.count(val)) {
return std::to_string(alloc.stack_map.at(val));
}
return std::string("0"); // 默认或错误情况
};
switch (node->kind) {
case DAGNode::CONSTANT: {
if (auto constant = dynamic_cast<ConstantValue*>(node->value)) {
std::string dest_reg = get_preg_or_temp(node->result_vreg);
if (constant->isInt()) {
ss_inst << "li " << dest_reg << ", " << constant->getInt();
} else {
float f = constant->getFloat();
uint32_t float_bits = *(uint32_t*)&f;
// 对于浮点数,先加载整数位,然后转换
ss_inst << "li " << dest_reg << ", " << float_bits << "\n";
ss_inst << "fmv.w.x " << dest_reg << ", " << dest_reg; // 假设 dest_reg 可以同时用于整数和浮点数 (FPU 寄存器并非总是如此)
}
} else if (auto global = dynamic_cast<GlobalValue*>(node->value)) {
std::string dest_reg = get_preg_or_temp(node->result_vreg);
ss_inst << "la " << dest_reg << ", " << global->getName(); // 加载全局变量地址
}
break;
}
case DAGNode::ALLOCA_ADDR: {
// 对于 AllocaInst我们想计算其地址 (s0 + 偏移量) 并放入 result_vreg
if (auto alloca_inst = dynamic_cast<AllocaInst*>(node->value)) {
std::string dest_reg = get_preg_or_temp(node->result_vreg);
int offset = alloc.stack_map.at(alloca_inst);
// 帧指针 s0 已经指向当前帧的基地址。
// 偏移量是相对于 s0 的。
ss_inst << "addi " << dest_reg << ", s0, " << offset;
}
break;
}
case DAGNode::LOAD: {
if (node->operands.empty() || !node->operands[0]) break;
std::string dest_reg = get_preg_or_temp(node->result_vreg);
DAGNode* ptr_node = node->operands[0]; // 操作数是指针
// 检查指针本身是否是 AllocaInst
if (ptr_node->kind == DAGNode::ALLOCA_ADDR) { // 检查节点类型,而非其值
if (auto alloca_inst = dynamic_cast<AllocaInst*>(ptr_node->value)) {
int offset = alloc.stack_map.at(alloca_inst);
ss_inst << "lw " << dest_reg << ", " << offset << "(s0)";
}
} else {
// 指针在寄存器中 (可能是全局地址,或 GEP/其他计算结果)
std::string ptr_reg = get_preg_or_temp(ptr_node->result_vreg);
ss_inst << "lw " << dest_reg << ", 0(" << ptr_reg << ")"; // 从 ptr_reg 中的地址加载
}
break;
}
case DAGNode::STORE: {
if (node->operands.size() < 2 || !node->operands[0] || !node->operands[1]) break;
DAGNode* val_node = node->operands[0]; // 要存储的值
DAGNode* ptr_node = node->operands[1]; // 目标地址
std::string src_reg;
if (val_node->kind == DAGNode::CONSTANT) {
// 如果存储的是常量,先将其加载到临时寄存器 (t0)
if (auto constant = dynamic_cast<ConstantValue*>(val_node->value)) {
src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器用于常量
ss_inst << "li " << src_reg << ", " << constant->getInt(); // 这行指令将作为 store 指令的一部分被添加
} else { // 存储全局地址
src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器
ss_inst << "la " << src_reg << ", " << dynamic_cast<GlobalValue*>(val_node->value)->getName();
}
} else {
src_reg = get_preg_or_temp(val_node->result_vreg);
}
// 将 li/la 指令与 sw 指令放在同一行,用 \n 分隔emit_instructions 会正确处理
// 这里将 store 指令放在 li/la 指令的后面
ss_inst << (ss_inst.str().empty() ? "" : "\n"); // 如果前面有指令,则换行
// 检查指针是否是 AllocaInst (栈变量)
if (ptr_node->kind == DAGNode::ALLOCA_ADDR) {
if (auto alloca_inst = dynamic_cast<AllocaInst*>(ptr_node->value)) {
int offset = alloc.stack_map.at(alloca_inst);
ss_inst << "sw " << src_reg << ", " << offset << "(s0)";
}
} else {
// 指针在寄存器中 (可能是全局地址,或 GEP/其他计算结果)
std::string ptr_reg = get_preg_or_temp(ptr_node->result_vreg);
ss_inst << "sw " << src_reg << ", 0(" << ptr_reg << ")";
}
break;
}
case DAGNode::BINARY: {
if (node->operands.size() < 2 || !node->operands[0] || !node->operands[1]) break;
auto bin = dynamic_cast<BinaryInst*>(node->value);
if (!bin) break;
std::string dest_reg = get_preg_or_temp(node->result_vreg);
std::string lhs_reg = get_preg_or_temp(node->operands[0]->result_vreg);
std::string rhs_reg = get_preg_or_temp(node->operands[1]->result_vreg);
std::string opcode;
switch (bin->getKind()) {
case BinaryInst::kAdd: opcode = "add"; break;
case BinaryInst::kSub: opcode = "sub"; break;
case BinaryInst::kMul: opcode = "mul"; break;
case BinaryInst::kICmpEQ: // 实现 A == B 为 sub A, B; seqz D, (A-B)
ss_inst << "sub " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n";
ss_inst << "seqz " << dest_reg << ", " << dest_reg; // 如果等于零则设置
node->inst = ss_inst.str(); // 设置指令并返回
return;
case Instruction::kDiv: opcode = "div"; break; // 整数除法
case Instruction::kRem: opcode = "rem"; break; // 整数余数
case BinaryInst::kICmpGE: // A >= B <=> !(A < B) <=> !(slt D, A, B) <=> slt D, A, B; xori D, D, 1
ss_inst << "slt " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n";
ss_inst << "xori " << dest_reg << ", " << dest_reg << ", 1";
node->inst = ss_inst.str();
return;
case BinaryInst::kICmpGT: // A > B <=> B < A
opcode = "slt";
ss_inst << opcode << " " << dest_reg << ", " << rhs_reg << ", " << lhs_reg; // slt rd, rs2, rs1 (如果 rs2 < rs1)
node->inst = ss_inst.str();
return;
case BinaryInst::kICmpLE: // A <= B <=> !(A > B) <=> !(slt D, B, A) <=> slt D, B, A; xori D, D, 1
ss_inst << "slt " << dest_reg << ", " << rhs_reg << ", " << lhs_reg << "\n";
ss_inst << "xori " << dest_reg << ", " << dest_reg << ", 1";
node->inst = ss_inst.str();
return;
case BinaryInst::kICmpLT: // A < B
opcode = "slt";
ss_inst << opcode << " " << dest_reg << ", " << lhs_reg << ", " << rhs_reg;
node->inst = ss_inst.str();
return;
case BinaryInst::kICmpNE: // A != B <=> ! (A == B) <=> sub D, A, B; snez D, D
ss_inst << "sub " << dest_reg << ", " << lhs_reg << ", " << rhs_reg << "\n";
ss_inst << "snez " << dest_reg << ", " << dest_reg;
node->inst = ss_inst.str();
return;
default:
// 处理未知二进制操作或抛出错误
throw std::runtime_error("Unsupported binary instruction kind: " + bin->getKindString());
}
if (!opcode.empty()) {
ss_inst << opcode << " " << dest_reg << ", " << lhs_reg << ", " << rhs_reg;
}
break;
}
case DAGNode::CALL: {
if (!node->value) break;
auto call = dynamic_cast<CallInst*>(node->value);
if (!call) break;
// 将参数放入 a0-a7
for (size_t i = 0; i < node->operands.size() && i < 8; ++i) {
if (node->operands[i] && !node->operands[i]->result_vreg.empty()) {
ss_inst << "mv " << reg_to_string(static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + i))
<< ", " << get_preg_or_temp(node->operands[i]->result_vreg) << "\n";
} else if (node->operands[i] && node->operands[i]->kind == DAGNode::CONSTANT) {
// 直接将常量参数加载到 A 寄存器
if (auto const_val = dynamic_cast<ConstantValue*>(node->operands[i]->value)) {
ss_inst << "li " << reg_to_string(static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + i))
<< ", " << const_val->getInt() << "\n";
} else if (auto global_val = dynamic_cast<GlobalValue*>(node->operands[i]->value)) {
ss_inst << "la " << reg_to_string(static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + i))
<< ", " << global_val->getName() << "\n";
}
}
}
ss_inst << "call " << call->getCallee()->getName(); // 使用 'call' 伪指令
// 如果函数返回一个值,将其从 a0 移动到结果虚拟寄存器
if ((call->getType()->isInt() || call->getType()->isFloat()) && !node->result_vreg.empty()) {
ss_inst << "\nmv " << get_preg_or_temp(node->result_vreg) << ", a0";
}
break;
}
case DAGNode::RETURN: {
// 如果有返回值,将其移动到 a0
if (!node->operands.empty() && node->operands[0]) {
std::string return_val_reg = get_preg_or_temp(node->operands[0]->result_vreg);
// 对于返回值,直接将最终结果移动到 a0
// 确保 load 的结果最终进入 a0避免不必要的 t0 中转
ss_inst << "mv a0, " << return_val_reg << "\n";
}
// 函数尾声 (Epilogue): 恢复 s0, ra, 并调整 sp
if (alloc.stack_size > 0) {
int aligned_stack_size = (alloc.stack_size + 15) & ~15;
ss_inst << "lw ra, " << (aligned_stack_size - 4) << "(sp)\n"; // 恢复 ra
ss_inst << "lw s0, " << (aligned_stack_size - 8) << "(sp)\n"; // 恢复 s0
ss_inst << "addi sp, sp, " << aligned_stack_size << "\n"; // 恢复栈指针
}
ss_inst << "ret"; // 返回
break;
}
case DAGNode::BRANCH: {
auto br = dynamic_cast<CondBrInst*>(node->value);
auto uncond_br = dynamic_cast<UncondBrInst*>(node->value);
if (node->inst.empty()) { // 如果不是通过常量传播优化的直接跳转
if (br) {
if (node->operands.empty() || !node->operands[0]) break;
std::string cond_reg = get_preg_or_temp(node->operands[0]->result_vreg);
std::string then_block = br->getThenBlock()->getName();
std::string else_block = br->getElseBlock()->getName();
// 修复空标签问题
if (then_block.empty()) {
then_block = ENTRY_BLOCK_PSEUDO_NAME + "then"; // 临时命名
}
if (else_block.empty()) {
else_block = ENTRY_BLOCK_PSEUDO_NAME + "else"; // 临时命名
}
ss_inst << "bnez " << cond_reg << ", " << then_block << "\n"; // 如果条件不为零 (真),则跳转到 then 块
ss_inst << "j " << else_block; // 无条件跳转到 else 块
} else if (uncond_br) {
std::string target_block = uncond_br->getBlock()->getName();
if (target_block.empty()) { // 修复空标签问题
target_block = ENTRY_BLOCK_PSEUDO_NAME + "target"; // 临时命名
}
ss_inst << "j " << target_block; // 无条件跳转
}
} else {
// 这个分支节点已经被 build_dag 中的常量传播优化为直接跳转。
// 它的 'inst' 字段已经设置。直接复制它。
ss_inst << node->inst;
}
break;
}
default:
// 对于不直接映射到指令的节点 (例如 `alloca` 本身,其地址由其地址节点处理)
// 或未处理的指令类型,将 inst 留空。
break;
}
node->inst = ss_inst.str(); // 存储生成的指令
}
// 修改:指令发射,直接输出到 stringstream处理依赖和去重
void RISCv32CodeGen::emit_instructions(DAGNode* node, std::stringstream& ss, const RegAllocResult& alloc, std::set<DAGNode*>& emitted_nodes) {
if (!node || emitted_nodes.count(node)) {
return; // 已发射或为空
}
// 递归地发射操作数以确保满足依赖关系
for (auto operand : node->operands) {
if (operand) {
emit_instructions(operand, ss, alloc, emitted_nodes);
}
}
// 标记当前节点为已发射
emitted_nodes.insert(node);
// 分割多行指令并处理每一行
std::stringstream node_inst_ss(node->inst);
std::string line;
while (std::getline(node_inst_ss, line, '\n')) {
// 清除前导/尾随空白并移除行开头的潜在标签
line = std::regex_replace(line, std::regex("^\\s*[^\\s:]*:\\s*"), ""); // 移除标签(例如 `label: inst`
line = std::regex_replace(line, std::regex("^\\s+|\\s+$"), ""); // 清除空白
if (line.empty()) continue;
// 处理虚拟寄存器替换和溢出/加载逻辑
std::string processed_line = line;
// 如果是 store 或 load并且操作数是 ALLOCA_ADDR那么地址计算addi s0, offset应该在实际的 sw/lw 之前
// 但由于 DAG 结构ALLOCA_ADDR 节点本身会生成一条 addi 指令。
// 我们需要确保这条 addi 指令在 store/load 之前被发射。
// emit_instructions 的递归调用已经处理了操作数的发射,所以 `ptr_node->inst` 应该已经生成。
// 替换结果虚拟寄存器 (如果此行中存在)
if (!node->result_vreg.empty()) {
std::string preg = reg_to_string(PhysicalReg::T0); // 默认到 T0
if (alloc.vreg_to_preg.count(node->result_vreg)) {
preg = reg_to_string(alloc.vreg_to_preg.at(node->result_vreg));
}
// 如果结果需要溢出到栈
if (node->value && alloc.stack_map.count(node->value) && alloc.vreg_to_preg.find(node->result_vreg) == alloc.vreg_to_preg.end()) {
// 这意味着此指令的结果将溢出。我们应该在计算后生成一个存储指令。
// 注意:这是一个简化的溢出方法;真实的溢出策略更复杂。
int offset = alloc.stack_map.at(node->value);
std::string spill_reg = reg_to_string(PhysicalReg::T0); // 使用 t0 进行溢出
// 将结果寄存器替换为 spill_reg
processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), spill_reg);
// 如果当前节点不是 STORE 本身,则需要添加一个 store 指令
if (node->kind != DAGNode::STORE) {
std::string store_inst = "sw " + spill_reg + ", " + std::to_string(offset) + "(s0)";
ss << " " << store_inst << "\n";
}
} else {
// 如果结果不溢出或者已经被分配了物理寄存器
processed_line = std::regex_replace(processed_line, std::regex("\\b" + node->result_vreg + "\\b"), preg);
}
}
// 替换操作数虚拟寄存器 (如果此行中存在)
for (auto operand : node->operands) {
if (operand && !operand->result_vreg.empty()) {
std::string operand_preg = reg_to_string(PhysicalReg::T0);
if (alloc.vreg_to_preg.count(operand->result_vreg)) {
operand_preg = reg_to_string(alloc.vreg_to_preg.at(operand->result_vreg));
} else if (operand->value && alloc.stack_map.count(operand->value)) {
// 此操作数已溢出,在使用前将其加载到临时寄存器 (t0)。
int offset = alloc.stack_map.at(operand->value);
std::string load_inst = "lw " + reg_to_string(PhysicalReg::T0) + ", " + std::to_string(offset) + "(s0)";
// 这里直接发射 load 指令
ss << " " << load_inst << "\n";
operand_preg = reg_to_string(PhysicalReg::T0); // 使用 t0 作为此指令的源
}
processed_line = std::regex_replace(processed_line, std::regex("\\b" + operand->result_vreg + "\\b"), operand_preg);
}
}
// 添加处理后的指令
ss << " " << processed_line << "\n";
}
}
// 活跃性分析 (基本保持不变,因为是通用的数据流分析)
std::map<Instruction*, std::set<std::string>> RISCv32CodeGen::liveness_analysis(Function* func) {
std::map<Instruction*, std::set<std::string>> live_in, live_out;
bool changed = true;
// 初始化所有 live_in/out 集合为空
for (const auto& bb : func->getBasicBlocks()) {
for (const auto& inst_ptr : bb->getInstructions()) {
live_in[inst_ptr.get()] = {};
live_out[inst_ptr.get()] = {};
}
}
while (changed) {
changed = false;
// 逆序遍历基本块
for (auto it = func->getBasicBlocks_NoRange().rbegin(); it != func->getBasicBlocks_NoRange().rend(); ++it) {
auto bb = it->get();
// 在基本块内逆序遍历指令
for (auto inst_it = bb->getInstructions().rbegin(); inst_it != bb->getInstructions().rend(); ++inst_it) {
auto inst = inst_it->get();
std::set<std::string> current_live_in = live_in[inst];
std::set<std::string> current_live_out = live_out[inst];
std::set<std::string> new_live_out;
// 后继的 live_in 集合的并集
if (inst->isTerminator()) {
if (auto br = dynamic_cast<CondBrInst*>(inst)) {
new_live_out.insert(live_in[br->getThenBlock()->getInstructions().front().get()].begin(),
live_in[br->getThenBlock()->getInstructions().front().get()].end());
new_live_out.insert(live_in[br->getElseBlock()->getInstructions().front().get()].begin(),
live_in[br->getElseBlock()->getInstructions().front().get()].end());
} else if (auto uncond = dynamic_cast<UncondBrInst*>(inst)) {
new_live_out.insert(live_in[uncond->getBlock()->getInstructions().front().get()].begin(),
live_in[uncond->getBlock()->getInstructions().front().get()].end());
}
// 返回指令没有后继,所以 new_live_out 保持为空
} else {
auto next_inst_it = std::next(inst_it);
if (next_inst_it != bb->getInstructions().rend()) {
// 不是终结符,所以块中的下一条指令是后继
new_live_out = live_in[next_inst_it->get()];
}
}
// 计算当前指令的 use 和 def 集合
std::set<std::string> use_set, def_set;
// 定义 (Def)
if (value_vreg_map.count(inst)) {
def_set.insert(value_vreg_map.at(inst));
}
// 使用 (Use)
if (auto bin = dynamic_cast<BinaryInst*>(inst)) {
if (value_vreg_map.count(bin->getLhs())) use_set.insert(value_vreg_map.at(bin->getLhs()));
if (value_vreg_map.count(bin->getRhs())) use_set.insert(value_vreg_map.at(bin->getRhs()));
} else if (auto call = dynamic_cast<CallInst*>(inst)) {
for (auto arg : call->getArguments()) {
if (value_vreg_map.count(arg->getValue())) use_set.insert(value_vreg_map.at(arg->getValue()));
}
} else if (auto load = dynamic_cast<LoadInst*>(inst)) {
if (value_vreg_map.count(load->getPointer())) use_set.insert(value_vreg_map.at(load->getPointer()));
} else if (auto store = dynamic_cast<StoreInst*>(inst)) {
if (value_vreg_map.count(store->getValue())) use_set.insert(value_vreg_map.at(store->getValue()));
if (value_vreg_map.count(store->getPointer())) use_set.insert(value_vreg_map.at(store->getPointer()));
} else if (auto ret = dynamic_cast<ReturnInst*>(inst)) {
if (ret->hasReturnValue() && value_vreg_map.count(ret->getReturnValue()))
use_set.insert(value_vreg_map.at(ret->getReturnValue()));
} else if (auto cond_br = dynamic_cast<CondBrInst*>(inst)) {
if (value_vreg_map.count(cond_br->getCondition()))
use_set.insert(value_vreg_map.at(cond_br->getCondition()));
}
// AllocaInst 不直接“使用”或“定义”虚拟寄存器,其地址是常量。
// 计算新的 live_in = use U (new_live_out - def)
std::set<std::string> new_live_in = use_set;
for (const auto& vreg : new_live_out) {
if (def_set.find(vreg) == def_set.end()) {
new_live_in.insert(vreg);
}
}
// 检查收敛
if (new_live_in != current_live_in || new_live_out != current_live_out) {
live_in[inst] = new_live_in;
live_out[inst] = new_live_out;
changed = true;
}
}
}
}
return live_in;
}
// 干扰图构建 (基本保持不变)
std::map<std::string, std::set<std::string>> RISCv32CodeGen::build_interference_graph(
const std::map<Instruction*, std::set<std::string>>& live_sets) {
std::map<std::string, std::set<std::string>> graph;
// 确保 live_sets 中所有存在的虚拟寄存器最初都在图中
for (const auto& pair : live_sets) {
for (const auto& vreg : pair.second) {
graph[vreg] = {}; // 初始化空集合
}
}
for (const auto& pair : live_sets) {
auto inst = pair.first;
const auto& live_after_inst = pair.second; // 这实际上是下一条指令/基本块入口的 live_in
std::string defined_vreg;
if (value_vreg_map.count(inst)) {
defined_vreg = value_vreg_map.at(inst);
}
// 将从 defined vreg 到此时所有其他活跃 vreg 的边添加
if (!defined_vreg.empty()) {
for (const auto& live_vreg : live_after_inst) {
if (live_vreg != defined_vreg) { // 虚拟寄存器不与其自身干扰
graph[defined_vreg].insert(live_vreg);
graph[live_vreg].insert(defined_vreg); // 对称边
}
}
}
}
return graph;
}
// 图着色 (简化版,贪婪着色) (基本保持不变)
void RISCv32CodeGen::color_graph(std::map<std::string, PhysicalReg>& vreg_to_preg,
const std::map<std::string, std::set<std::string>>& interference_graph) {
vreg_to_preg.clear(); // 清除之前的映射
// 按度数(从大到小)对虚拟寄存器排序以改进着色效果
std::vector<std::pair<std::string, int>> vreg_degrees;
for (const auto& entry : interference_graph) {
vreg_degrees.push_back({entry.first, (int)entry.second.size()});
}
std::sort(vreg_degrees.begin(), vreg_degrees.end(),
[](const auto& a, const auto& b) { return a.second > b.second; }); // 按度数降序排序
for (const auto& vreg_deg_pair : vreg_degrees) {
const std::string& vreg = vreg_deg_pair.first;
std::set<PhysicalReg> used_colors; // 邻居使用的物理寄存器
// 收集干扰邻居的颜色
if (interference_graph.count(vreg)) {
for (const auto& neighbor_vreg : interference_graph.at(vreg)) {
if (vreg_to_preg.count(neighbor_vreg)) {
used_colors.insert(vreg_to_preg.at(neighbor_vreg));
}
}
}
// 查找第一个可用的颜色(物理寄存器)
bool colored = false;
for (PhysicalReg preg : allocable_regs) {
if (used_colors.find(preg) == used_colors.end()) {
vreg_to_preg[vreg] = preg;
colored = true;
break;
}
}
if (!colored) {
// 溢出 (Spilling): 如果没有可用的物理寄存器,这个虚拟寄存器必须溢出到内存。
// 对于这个简化示例,我们没有实现完整的溢出。
// 一个常见的方法是为其分配一个特殊的“溢出”指示符,并在代码生成中处理它。
// 目前,我们只是不为其分配物理寄存器,`get_preg_or_temp` 将使用默认的 `t0` 或触发栈加载/存储。
std::cerr << "警告: 无法为 " << vreg << " 分配寄存器。它很可能会溢出到栈。\n";
// 更完整的编译器会将此虚拟寄存器添加到 `alloc.stack_map` 并在此处管理其栈偏移量。
}
}
}
// 寄存器分配
RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* func) {
// 1. Phi 节点消除 (如果 IR 中有 Phi 节点,需要在活跃性分析前消除)
eliminate_phi(func); // 确保首先调用此函数
// 为每个函数重置计数器
alloca_offset_counter = 0;
vreg_counter = 0;
value_vreg_map.clear(); // 为每个函数清除
// 在活跃性分析之前,为 alloca 指令和函数参数分配虚拟寄存器,
// 并为 allocas 建立初始栈映射。
RegAllocResult alloc_result;
// 为所有产生值的指令分配虚拟寄存器。
// 这部分实际上在 build_dag 中发生。
// 但是,为了使活跃性分析工作,所有可能使用的 Value* 都必须有一个虚拟寄存器。
// 我们可以遍历指令 (在 DAG 构建之前) 来填充 `value_vreg_map`。
// 如果 DAG 分配虚拟寄存器,这有点像先有鸡还是先有蛋的问题。
// 让我们假设 build_dag 在分配虚拟寄存器时填充 value_vreg_map。
// 计算 AllocaInst 的栈偏移量
int current_stack_offset = 0; // 相对于 s0 (帧指针)
// 参数由 a0-a7 处理,所以除非它们溢出,否则这里不需要直接为它们分配栈空间。
// 收集函数中所有唯一的 AllocaInst
std::set<AllocaInst*> allocas_in_func;
for (const auto& bb_ptr : func->getBasicBlocks()) {
for (const auto& inst_ptr : bb_ptr->getInstructions()) {
if (auto alloca = dynamic_cast<AllocaInst*>(inst_ptr.get())) {
allocas_in_func.insert(alloca);
}
}
}
// 为 alloca 指令分配栈空间
for (auto alloca : allocas_in_func) {
// 为 4 字节整数 (i32) 分配空间
int size = 4; // 假设 i32如果存在其他类型则调整
alloc_result.stack_map[alloca] = current_stack_offset;
current_stack_offset += size;
}
// 确保栈大小是 16 的倍数,并考虑保存的 ra 和 s0
// ra 在 (stack_size - 4)(sp)
// s0 在 (stack_size - 8)(sp)
// 所以最小栈大小必须是 8 + current_stack_offset。
alloc_result.stack_size = current_stack_offset + 8; // 用于 s0 和 ra
// 对齐到 16 字节以符合 ABI
alloc_result.stack_size = (alloc_result.stack_size + 15) & ~15;
// 2. 活跃性分析
std::map<Instruction*, std::set<std::string>> live_sets = liveness_analysis(func);
// 3. 构建干扰图
std::map<std::string, std::set<std::string>> interference_graph = build_interference_graph(live_sets);
// 4. 图着色
color_graph(alloc_result.vreg_to_preg, interference_graph);
return alloc_result;
}
// Phi 消除 (简化版,将 Phi 的结果直接复制到每个前驱基本块的末尾)
void RISCv32CodeGen::eliminate_phi(Function* func) {
// 这是一个占位符。适当的 phi 消除将涉及
// 在每个前驱基本块的末尾插入 `mov` 指令,用于每个 phi 操作数。
// 对于给定的 IR 示例,没有 phi 节点,所以这可能不是严格必要的,
// 但如果前端生成 phi 节点,则有此阶段是好的做法。
// 目前,我们假设没有生成 phi 节点或者它们已在前端处理。
}
} // namespace sysy