[backend] fixed many bugs
This commit is contained in:
@@ -264,16 +264,18 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
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; // 考虑这是否需要/正确
|
||||
// 注意:这里的vreg分配是在每个块中独立进行的,但寄存器分配器是在函数级别运行的
|
||||
// 我们在寄存器分配前,已经为整个函数的所有value预分配了vreg
|
||||
if (val && value_vreg_map.count(val)) {
|
||||
node->result_vreg = value_vreg_map.at(val);
|
||||
} else if (val && kind != DAGNode::STORE && kind != DAGNode::RETURN && kind != DAGNode::BRANCH) {
|
||||
// 如果一个值(例如常量)在预分配阶段没有vreg,这里可以给一个
|
||||
if(value_vreg_map.find(val) == value_vreg_map.end()){
|
||||
value_vreg_map[val] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
node->result_vreg = value_vreg_map.at(val);
|
||||
}
|
||||
|
||||
|
||||
DAGNode* raw_node_ptr = node.get();
|
||||
nodes_storage.push_back(std::move(node)); // 存储 unique_ptr
|
||||
@@ -317,6 +319,8 @@ std::vector<std::unique_ptr<RISCv32CodeGen::DAGNode>> RISCv32CodeGen::build_dag(
|
||||
if (value_to_node.count(ptr_ir)) {
|
||||
ptr_node = value_to_node[ptr_ir];
|
||||
} else if (auto alloca = dynamic_cast<AllocaInst*>(ptr_ir)) {
|
||||
// 如果是alloca,我们应该找到代表它地址的节点
|
||||
// 为了简化,如果没找到,就创建一个
|
||||
ptr_node = create_node(DAGNode::ALLOCA_ADDR, alloca);
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_ir)) {
|
||||
ptr_node = create_node(DAGNode::CONSTANT, global); // 全局地址将被加载
|
||||
@@ -513,8 +517,7 @@ void RISCv32CodeGen::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag,
|
||||
// 指令选择
|
||||
void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& alloc) {
|
||||
if (!node) return;
|
||||
// ALLOCA_ADDR 节点不直接映射到一条指令,它表示的是地址的计算
|
||||
if (!node->inst.empty() && node->kind != DAGNode::ALLOCA_ADDR) return; // 指令已选择
|
||||
if (!node->inst.empty()) return; // 指令已选择
|
||||
|
||||
// 首先递归地为操作数选择指令
|
||||
for (auto operand : node->operands) {
|
||||
@@ -564,14 +567,9 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
|
||||
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;
|
||||
}
|
||||
// FIX: 这个节点本身不生成指令。
|
||||
// 它的使用者(LOAD/STORE)会利用它的信息生成更优化的寻址指令。
|
||||
// 将 node->inst 留空以避免生成冗余的 `addi` 指令。
|
||||
break;
|
||||
}
|
||||
case DAGNode::LOAD: {
|
||||
@@ -601,18 +599,14 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
|
||||
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 指令的一部分被添加
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg); // 常量也应该有vreg
|
||||
// 注意:这里的li指令会由CONSTANT节点自己生成,STORE节点不应重复生成
|
||||
} else { // 存储全局地址
|
||||
src_reg = reg_to_string(PhysicalReg::T0); // 使用临时寄存器
|
||||
ss_inst << "la " << src_reg << ", " << dynamic_cast<GlobalValue*>(val_node->value)->getName();
|
||||
src_reg = get_preg_or_temp(val_node->result_vreg);
|
||||
}
|
||||
} 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) {
|
||||
@@ -803,52 +797,20 @@ void RISCv32CodeGen::emit_instructions(DAGNode* node, std::stringstream& ss, con
|
||||
// 处理虚拟寄存器替换和溢出/加载逻辑
|
||||
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` 应该已经生成。
|
||||
// 注意:这里的替换逻辑比较脆弱,因为 select_instructions 已经直接生成了物理寄存器名
|
||||
// 在一个更健壮的系统中,select_instructions 会生成带vreg的指令,而这里会进行替换
|
||||
// 当前的实现下,这个替换逻辑大部分时间是空操作,但为了安全保留
|
||||
|
||||
// 替换结果虚拟寄存器 (如果此行中存在)
|
||||
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 {
|
||||
// 如果结果不溢出或者已经被分配了物理寄存器
|
||||
if (!node->result_vreg.empty() && alloc.vreg_to_preg.count(node->result_vreg)) {
|
||||
std::string preg = reg_to_string(alloc.vreg_to_preg.at(node->result_vreg));
|
||||
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 作为此指令的源
|
||||
}
|
||||
if (operand && !operand->result_vreg.empty() && alloc.vreg_to_preg.count(operand->result_vreg)) {
|
||||
std::string operand_preg = reg_to_string(alloc.vreg_to_preg.at(operand->result_vreg));
|
||||
processed_line = std::regex_replace(processed_line, std::regex("\\b" + operand->result_vreg + "\\b"), operand_preg);
|
||||
}
|
||||
}
|
||||
@@ -912,27 +874,15 @@ std::map<Instruction*, std::set<std::string>> RISCv32CodeGen::liveness_analysis(
|
||||
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()));
|
||||
// 使用 (Use) - 遍历指令的操作数
|
||||
for(const auto& operand_use : inst->getOperands()){
|
||||
Value* operand = operand_use->getValue();
|
||||
// 只有非立即数的值才生活在虚拟寄存器中
|
||||
if(!dynamic_cast<ConstantValue*>(operand) && value_vreg_map.count(operand)){
|
||||
use_set.insert(value_vreg_map.at(operand));
|
||||
}
|
||||
} 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;
|
||||
@@ -1041,26 +991,38 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
eliminate_phi(func); // 确保首先调用此函数
|
||||
|
||||
// 为每个函数重置计数器
|
||||
alloca_offset_counter = 0;
|
||||
vreg_counter = 0;
|
||||
value_vreg_map.clear(); // 为每个函数清除
|
||||
|
||||
// 在活跃性分析之前,为 alloca 指令和函数参数分配虚拟寄存器,
|
||||
// 并为 allocas 建立初始栈映射。
|
||||
RegAllocResult alloc_result;
|
||||
// FIX: 在进行活跃性分析之前,为所有产生值的指令分配虚拟寄存器。
|
||||
// 这确保了活跃性分析和寄存器分配器有可操作的虚拟寄存器。
|
||||
for (const auto& bb_ptr : func->getBasicBlocks()) {
|
||||
for (const auto& inst_ptr : bb_ptr->getInstructions()) {
|
||||
Instruction* inst = inst_ptr.get();
|
||||
// 如果指令产生一个非 void 的结果,它就需要一个地方来存储这个结果。
|
||||
// 我们为其分配一个虚拟寄存器。
|
||||
if (!inst->getType()->isVoid()) {
|
||||
if (value_vreg_map.find(inst) == value_vreg_map.end()) {
|
||||
value_vreg_map[inst] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
}
|
||||
// 也为常量操作数分配vreg,以便它们可以参与活跃性分析
|
||||
for(const auto& operand_use : inst->getOperands()){
|
||||
Value* operand = operand_use->getValue();
|
||||
if(dynamic_cast<ConstantValue*>(operand) || dynamic_cast<GlobalValue*>(operand)){
|
||||
if (value_vreg_map.find(operand) == value_vreg_map.end()) {
|
||||
value_vreg_map[operand] = "v" + std::to_string(vreg_counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 为所有产生值的指令分配虚拟寄存器。
|
||||
// 这部分实际上在 build_dag 中发生。
|
||||
// 但是,为了使活跃性分析工作,所有可能使用的 Value* 都必须有一个虚拟寄存器。
|
||||
// 我们可以遍历指令 (在 DAG 构建之前) 来填充 `value_vreg_map`。
|
||||
// 如果 DAG 分配虚拟寄存器,这有点像先有鸡还是先有蛋的问题。
|
||||
// 让我们假设 build_dag 在分配虚拟寄存器时填充 value_vreg_map。
|
||||
RegAllocResult alloc_result;
|
||||
|
||||
// 计算 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()) {
|
||||
@@ -1083,8 +1045,6 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
// 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);
|
||||
@@ -1095,6 +1055,9 @@ RISCv32CodeGen::RegAllocResult RISCv32CodeGen::register_allocation(Function* fun
|
||||
// 4. 图着色
|
||||
color_graph(alloc_result.vreg_to_preg, interference_graph);
|
||||
|
||||
// 完整的溢出处理逻辑比较复杂,这里暂时省略。
|
||||
// 如果一个vreg没有被着色,get_preg_or_temp会回退到t0,这对于简单情况可能够用。
|
||||
|
||||
return alloc_result;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ int main() {
|
||||
const int b = 2;
|
||||
int c;
|
||||
|
||||
if (a == b)
|
||||
if (a != b)
|
||||
c = b - a + 20; // 21 <- this
|
||||
else
|
||||
c = a * b + b + b + 10; // 16
|
||||
|
||||
Reference in New Issue
Block a user