[backend] implemented call function parameter passing using registers

This commit is contained in:
Lixuanwang
2025-06-25 06:27:05 +08:00
parent 24913641f2
commit c04f508171

View File

@@ -152,24 +152,15 @@ std::string RISCv32CodeGen::function_gen(Function* func) {
ss << ".globl " << func->getName() << "\n"; // 声明函数为全局符号
ss << func->getName() << ":\n"; // 函数入口标签
// 执行寄存器分配
// 注意vreg_counter 和 value_vreg_map 在 register_allocation 内部会被初始化
auto alloc = register_allocation(func);
int stack_size = alloc.stack_size;
// 执行寄存器分配。alloc_result 包含了虚拟寄存器到物理寄存器的映射,
// 以及 AllocaInst 到栈偏移的映射 (stack_map)。
// 在 register_allocation 内部,我们将确保参数和它们的 AllocaInst 之间建立映射。
RegAllocResult alloc_result = register_allocation(func);
int stack_size = alloc_result.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;
@@ -181,16 +172,68 @@ std::string RISCv32CodeGen::function_gen(Function* func) {
ss << " mv s0, sp\n"; // 设置新的帧指针
}
// *** 新增的逻辑:处理传入的函数参数 ***
// 将传入的寄存器参数 (a0-a7 / f10-f17) 保存到对应的栈槽 (AllocaInst)。
// 这适用于参数被前端视为局部变量并立即 alloca 的情况。
int arg_idx = 0;
BasicBlock* entry_bb = func->getEntryBlock(); // 获取函数的入口基本块
if (entry_bb) { // 确保入口基本块存在
// 遍历入口基本块的形式参数列表 (这些通常是对应的 AllocaInst)
for (AllocaInst* alloca_for_param : entry_bb->getArguments()) {
// RISC-V ABI 规定前8个参数通过寄存器传递 (a0-a7 for int, f10-f17 for float)
if (arg_idx >= 8) {
std::cerr << "警告: 函数 '" << func->getName() << "' 的参数 (索引 " << arg_idx << ") 数量超过了 RISC-V 寄存器传递限制 (8个参数)。\n"
<< " 这些参数目前未通过栈正确处理,可能导致错误。\n";
break; // 简化处理暂时不支持超过8个的栈传递参数
}
// 检查该 AllocaInst 是否有栈映射(应该在 register_allocation 阶段被分配)
if (alloc_result.stack_map.count(alloca_for_param)) { // AllocaInst* 直接作为键
int offset = alloc_result.stack_map.at(alloca_for_param); // 获取栈偏移
// 获取 AllocaInst 所分配的内存的基类型例如i32* %x0 对应的基类型是 i32
Type* allocated_type = alloca_for_param->getType()->as<PointerType>()->getBaseType();
// 根据参数类型选择源寄存器和存储指令
if (allocated_type->isInt()) {
PhysicalReg arg_reg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + arg_idx);
std::string arg_reg_str = reg_to_string(arg_reg);
ss << " sw " << arg_reg_str << ", " << offset << "(s0)\n";
} else if (allocated_type->isFloat()) {
PhysicalReg farg_reg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::F10) + arg_idx); // RISC-V 浮点参数通常从f10开始
std::string farg_reg_str = reg_to_string(farg_reg);
ss << " fsw " << farg_reg_str << ", " << offset << "(s0)\n";
} else {
// 如果遇到不支持的类型,抛出运行时错误
throw std::runtime_error("Unsupported function argument type encountered during parameter saving to stack.");
}
} else {
std::cerr << "警告: 函数参数对应的 AllocaInst '"
<< (alloca_for_param->getName().empty() ? "anonymous" : alloca_for_param->getName())
<< "' 没有在栈映射中找到。这可能导致后续代码生成错误。\n";
}
arg_idx++;
}
} else {
std::cerr << "错误: 函数 '" << func->getName() << "' 没有入口基本块。\n";
}
// *** 新增逻辑结束 ***
// 生成每个基本块的代码
int block_idx = 0; // 用于生成默认 entry 标签的索引
for (const auto& bb : func->getBasicBlocks()) {
ss << basicBlock_gen(bb.get(), alloc, block_idx++);
// basicBlock_gen 的第三个参数是 int block_idx与 RISCv32Backend.h 中的定义一致
ss << basicBlock_gen(bb.get(), alloc_result, block_idx++);
}
// 函数尾声 (Epilogue) 由 RETURN DAGNode 的指令选择处理,确保正确恢复栈和寄存器
// 注意:这里的尾声生成在 select_instructions 的 RETURN case 中实现,
// 因此在 function_gen 结束时,只需要返回 ss.str()。
return ss.str();
}
// 基本块代码生成
std::string RISCv32CodeGen::basicBlock_gen(BasicBlock* bb, const RegAllocResult& alloc, int block_idx) {
std::stringstream ss;
@@ -778,8 +821,8 @@ void RISCv32CodeGen::select_instructions(DAGNode* node, const RegAllocResult& al
ss_inst << "sub " << dest_reg << ", x0, " << src_reg;
break;
case UnaryInst::kNot:
// 整数逻辑非:not rd, rs (等价于 xori rd, rs, -1)
ss_inst << "not " << dest_reg << ", " << src_reg;
// 整数逻辑非:seqz rd, rs (rs == 0 时 rd = 1否则 rd = 0)
ss_inst << "seqz " << dest_reg << ", " << src_reg;
break;
case UnaryInst::kFNeg:
case UnaryInst::kFNot: