帮我修改todo9,目前的情况是, 能通过的测试样例如下: 1: int k=1234; int w...
创建于:2025年5月28日
创建于:2025年5月28日
帮我修改todo9,目前的情况是,
能通过的测试样例如下:
1:
int k=1234;
int w[2]={1,2};
float kw[3]={1.01,4.11,13.99};
float a=4.11;
float d=-13.99;
int main(){
putint(w[0]);
return w[1];
}
2:
int k=1234;
int w[2]={1,2};
float kw[3]={1.01,4.11,13.99};
float a=4.11;
float d=-13.99;
int main(){
putfloat(kw[0]);
return kw[1];
}
3:
int k = 1234;
int w[2] = {1, 2};
float kw[3] = {1.01, 4.11, 13.99};
float a = 4.11;
float d = -13.99;
float farr[2][3] = {
{1.1, 2.2, 3.3},
{4.4, 5.5, 6.6}
};
int main() {
putfloat(farr[0][2]);
return 3;
}
不能通过的测试样例如下:
1:
int k = 1234;
int w[2] = {1, 2};
float kw[3] = {1.01, 4.11, 13.99};
float a = 4.11;
float d = -13.99;
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int main() {
return arr[1][1];
}
2:
int k = 1234;
int w[2] = {1, 2};
float kw[3] = {1.01, 4.11, 13.99};
float a = 4.11;
float d = -13.99;
int arr3d[2][2][2] = {
{
{1, 2},
{3, 4}
},
{
{5, 6},
{7, 8}
}
};
int main() {
return arr3d[1][1][1];
}
3:
int k = 1234;
int w[2] = {1, 2};
float kw[3] = {1.01, 4.11, 13.99};
float a = 4.11;
float d = -13.99;
float f3d[2][2][2] = {
{
{1.1, 2.2},
{3.3, 4.4}
},
{
{5.5, 6.6},
{7.7, 8.8}
}
};
int main() {
putfloat(f3d[0][1][1]);
return 5;
}
#include "CodeGen.hpp"
#include "CodeGenUtil.hpp"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <utility>
#include <vector>
void CodeGen::allocate() {
unsigned offset = PROLOGUE_OFFSET_BASE;
text// 为每个参数分配栈空间(如果没有被分配寄存器) for (auto &arg : context.func->get_args()) { auto size = arg.get_type()->get_size(); offset = ALIGN(offset + size, size); context.offset_map[&arg] = -static_cast<int>(offset); } // 为每条指令结果分配栈空间 for (auto &bb : context.func->get_basic_blocks()) { for (auto &instr : bb.get_instructions()) { // 每个非 void 的定值都分配栈空间 if (not instr.is_void()) { auto size = instr.get_type()->get_size(); offset = ALIGN(offset + size, size); context.offset_map[&instr] = -static_cast<int>(offset); } // 为数组 alloca 分配额外空间(不对齐) if (instr.is_alloca()) { auto *alloca_inst = static_cast<AllocaInst *>(&instr); auto alloc_size = alloca_inst->get_alloca_type()->get_size(); offset += alloc_size; context.array_start_offset[alloca_inst] = offset; } } } // 最终的帧大小对齐为 16 的倍数 context.frame_size = ALIGN(offset, PROLOGUE_ALIGN);
}
void CodeGen::gen_prologue() {
makeSureInRange("addi", SP, SP, -context.frame_size, "add");
makeSureInRange("sd", RA_reg, SP, context.frame_size - 8, "stx");
makeSureInRange("sd", FP, SP, context.frame_size - 16, "stx");
makeSureInRange("addi", FP, SP, context.frame_size, "add");
// 将函数参数转移到栈帧上
int garg_cnt = 0;
int farg_cnt = 0;
for (auto &arg : context.func->get_args()) {
if (arg.get_type()->is_float_type()) {
store_from_freg(&arg, FReg::fa(farg_cnt++));
} else { // int or pointer
store_from_greg(&arg, Reg::a(garg_cnt++));
}
}
}
void CodeGen::gen_epilogue() {
// TODO1:根据你的理解实现函数的 epilogue
// 提示:可能包括的步骤:恢复ra、恢复s0、恢复sp、返回到调用方
// 恢复 ra 和 s0
makeSureInRange("ld", RA_reg, SP, context.frame_size - 8, "ldx");
makeSureInRange("ld", FP, SP, context.frame_size - 16, "ldx");
// 恢复 sp
makeSureInRange("addi", SP, SP, context.frame_size, "add");
// 返回到调用方
append_inst("jr ra");
//TODO1-------------end
}
// 将一个值 val 加载到目标通用寄存器 reg 中
void CodeGen::load_to_greg(Value *val, const Reg ®) {
assert(val->get_type()->is_integer_type() ||
val->get_type()->is_pointer_type());
textif (auto *constant = dynamic_cast<ConstantInt *>(val)) {// 如果 val 是一个常数整数 int32_t val = constant->get_value(); if (IS_IMM_12(val)) { append_inst(ADDI, {reg.print(), "zero", std::to_string(val)}); } else { load_large_int32(val, reg);// 如果常数太大,用 load_large_int32 处理 } } else if (auto *global = dynamic_cast<GlobalVariable *>(val)) { // 如果是全局变量,生成地址加载指令 append_inst(LOAD_ADDR, {reg.print(), global->get_name()}); } else { //剩余情况从栈中加载到寄存器 load_from_stack_to_greg(val, reg); }
}
// 加载一个 32 位大整数到寄存器(通常是伪指令 li 会被展开成 lui+addi)
void CodeGen::load_large_int32(int32_t val, const Reg ®) {
append_inst(LI, {reg.print(), std::to_string(val)});
}
// 加载一个 64 位整数到寄存器,先加载高 32 位并左移,再加载低 32 位
void CodeGen::load_large_int64(int64_t val, const Reg ®) {
auto low_32 = static_cast<int32_t>(val & LOW_32_MASK); // 提取低 32 位
auto high_32 = static_cast<int32_t>(val >> 32); // 提取高 32 位
load_large_int32(high_32, reg);
append_inst(SLLI, {reg.print(), reg.print(), "32"}); // 加载高 32 位并左移 32 位
load_large_int32(low_32, reg);// 覆盖写入低 32 位
}
// 从栈帧中加载某个变量 val 到通用寄存器 reg 中
void CodeGen::load_from_stack_to_greg(Value *val, const Reg ®) {
// 获取该变量在当前函数栈帧中的偏移
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
auto *type = val->get_type(); // 获取该变量的类型(用于确定加载指令)
if (IS_IMM_12(offset)) { // 如果 offset 能够用 12 位立即数表示,可以直接使用 offset(fp) 格式访问内存
if (type->is_int1_type()) {
append_inst(LOAD_BYTE, {reg.print(), "fp", offset_str});
} else if (type->is_int32_type()) {
append_inst(LOAD_WORD, {reg.print(), "fp", offset_str});
} else { // Pointer
append_inst(LOAD_DOUBLE, {reg.print(), "fp", offset_str});
}
} else { // 如果偏移过大,不能直接编码到指令中,先将 offset 加载到寄存器
load_large_int64(offset, reg); // reg = offset
append_inst(ADD, {reg.print(), "fp", reg.print()}); // reg = fp + offset
if (type->is_int1_type()) {
append_inst(LOAD_BYTE, {reg.print(), reg.print(), "0"});
} else if (type->is_int32_type()) {
append_inst(LOAD_WORD, {reg.print(), reg.print(), "0"});
} else { // Pointer
append_inst(LOAD_DOUBLE, {reg.print(), reg.print(), "0"});
}
}
}
// 将通用寄存器 reg 中的值存储到 val 对应的栈上位置(以 fp 为基址)
void CodeGen::store_from_greg(Value *val, const Reg ®) {
// 获取该变量在当前函数栈帧中的偏移
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
auto *type = val->get_type(); // 获取该变量的类型(用于确定加载指令)
if (IS_IMM_12(offset)) { // 如果 offset 能够用 12 位立即数表示,可以直接使用 offset(fp) 格式访问内存
if (type->is_int1_type()) {
append_inst(STORE_BYTE, {reg.print(), "fp", offset_str});
} else if (type->is_int32_type()) {
append_inst(STORE_WORD, {reg.print(), "fp", offset_str});
} else { // Pointer
append_inst(STORE_DOUBLE, {reg.print(), "fp", offset_str});
}
} else { // 对于 offset 超出立即数范围的情况,需要通过地址计算访问
auto addr = Reg::s(11); // 使用临时寄存器 s11 作为中间地址计算(可更换)
load_large_int64(offset, addr);
append_inst(ADD , {addr.print(), "fp", addr.print()});
if (type->is_int1_type()) {
append_inst(STORE_BYTE, {reg.print(), addr.print(), "0"});
} else if (type->is_int32_type()) {
append_inst(STORE_WORD, {reg.print(), addr.print(), "0"});
} else { // Pointer
append_inst(STORE_DOUBLE, {reg.print(), addr.print(), "0"});
}
}
}
// 将一个浮点类型的 Value 加载到浮点寄存器 freg 中
void CodeGen::load_to_freg(Value *val, const FReg &freg) {
assert(val->get_type()->is_float_type());
if (auto *constant = dynamic_cast<ConstantFP *>(val)) { // 若是浮点常量,加载立即数
float val = constant->get_value();
load_float_imm(val, freg);
} else { // 从栈中加载浮点变量
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
if (IS_IMM_12(offset)) {
append_inst(FLOAD_SINGLE, {freg.print(), "fp", offset_str});
} else { // 偏移过大,使用寄存器间接寻址
auto addr = Reg::s(11); // 临时通用寄存器 s11
load_large_int64(offset, addr); // 加载偏移
append_inst(ADD, {addr.print(), "fp", addr.print()}); // addr = fp + offset
append_inst(FLOAD_SINGLE, {freg.print(), addr.print(), "0"}); // 从 addr 加载
}
}
}
// 将 float 常量加载进浮点寄存器 freg
void CodeGen::load_float_imm(float val, const FReg &r) {
int32_t bytes = *reinterpret_cast<int32_t *>(&val); // 将 float 解释为 32 位整数(IEEE 754 bit pattern)
load_large_int32(bytes, Reg::s(11));
append_inst("fmv.s.x", {r.print(), Reg::s(11).print()}); // 使用 fmv.s.x 指令将整数位模式转成 float 放入 freg
}
// 将浮点寄存器 r 中的值存储回栈中 val 对应的位置
void CodeGen::store_from_freg(Value *val, const FReg &r) {
auto offset = context.offset_map.at(val);
if (IS_IMM_12(offset)) {
auto offset_str = std::to_string(offset);
append_inst(FSTORE_SINGLE, {r.print(), "fp", offset_str});
} else { // 偏移过大,需要间接寻址
auto addr = Reg::s(11);
load_large_int64(offset, addr);
append_inst(ADD, {addr.print(), "fp", addr.print()}); // addr = fp + offset
append_inst(FSTORE_SINGLE, {r.print(), addr.print(), "0"}); // 从 r 存到 addr
}
}
void CodeGen::gen_ret() {
// TODO2:函数返回操作,你需要思考如何处理返回值(a0/fa0),如何返回到调用者(可以使用j指令、context中或许有你需要的信息)
auto *retType = context.func->get_return_type();
if (retType->is_void_type()) {
} else if (retType->is_float_type()) {
load_to_freg(context.inst->get_operand(0), FReg::fa(0));
} else if (retType->is_integer_type() || retType->is_pointer_type()) {
load_to_greg(context.inst->get_operand(0), Reg::a(0));
} else {
assert(false && "Unhandled return type");
}
gen_epilogue();
// TODO2----------------end
}
void CodeGen::gen_br() {
auto *branchInst = static_cast<BranchInst *>(context.inst);
if (branchInst->is_cond_br()) {
// TODO6:补全条件跳转操作
// 提示: 根据条件表达式的结果(reg t1 != 0),选择跳转到 true 分支或 false 分支。
// 你可能会用到blt、j等指令
auto *cond = branchInst->get_operand(0);
load_to_greg(cond, Reg::t(1));
auto *trueBB = static_cast<BasicBlock *>(branchInst->get_operand(1));
auto *falseBB = static_cast<BasicBlock *>(branchInst->get_operand(2));
append_inst("bnez t1, " + label_name(trueBB));
append_inst("j " + label_name(falseBB));
// TODO6-------------------end
} else {
// 无条件跳转
auto *branchbb = static_cast<BasicBlock *>(branchInst->get_operand(0));
append_inst("j " + label_name(branchbb)); // 跳转到目标基本块
}
}
void CodeGen::gen_binary() {
// 分别将左右操作数加载到 t0 t1
load_to_greg(context.inst->get_operand(0), Reg::t(0));
load_to_greg(context.inst->get_operand(1), Reg::t(1));
// 根据指令类型生成汇编
switch (context.inst->get_instr_type()) {
case Instruction::add:
output.emplace_back("add t2, t0, t1");
break;
case Instruction::sub:
output.emplace_back("sub t2, t0, t1");
break;
case Instruction::mul:
output.emplace_back("mul t2, t0, t1");
break;
case Instruction::sdiv:
output.emplace_back("div t2, t0, t1");
break;
case Instruction::srem:
output.emplace_back("remw t2, t0, t1");
break;
default:
assert(false);
}
// 将结果填入栈帧中
store_from_greg(context.inst, Reg::t(2));
}
void CodeGen::gen_alloca() {
auto *alloca_inst = static_cast<AllocaInst *>(context.inst);
auto shuzu_offset = context.array_start_offset[alloca_inst];
std::string temp_reg = "t1";
// 加载偏移量到临时寄存器
load_large_int32(shuzu_offset, Reg::t(0));
// 计算栈地址:fp - shuzu_offset
append_inst(SUB + string(" ") + temp_reg + ", fp, t0");
store_from_greg(context.inst, Reg::t(1));
}
void CodeGen::gen_load() {
auto ptr = context.inst->get_operand(0);//在指针类型auto*和auto没有任何区别
auto *type = context.inst->get_type();
load_to_greg(ptr, Reg::t(0));
std::string sreg ="t0";
if (type->is_float_type()) {
std::string dest="ft0";
append_inst(FLOAD_SINGLE,{dest, sreg, "0"});//ft0=M[t0+0]
store_from_freg(context.inst, FReg::ft(0));
} else {
// TODO3: 补全load整型变量的情况,考虑int1 int32 int64
std::string dest = "t1";
if (type->is_int1_type()) {
append_inst("lb", {dest, sreg, "0"});
} else if (type->is_int32_type()) {
append_inst("lw", {dest, sreg, "0"});
} else if (type->is_int64_type()) {
append_inst("ld", {dest, sreg, "0"});
}
store_from_greg(context.inst, Reg::t(1));
// TODO3----------------end
}
}
void CodeGen::gen_store() {
auto *type = context.inst->get_operand(0)->get_type();//怎么store取决于我们要存的数据是什么类型
auto *ptr = context.inst->get_operand(1);//位置
auto *data = context.inst->get_operand(0);//要存入的值
load_to_greg(ptr, Reg::t(1));
auto pst_reg=std::string("t1");
if (type->is_float_type()) {
load_to_freg(data, FReg::ft(0));
append_inst(FSTORE_SINGLE ,{"ft0", pst_reg , "0"});//M[t1+0]=ft0
} else {
if(type->is_int1_type()){
load_to_greg(data, Reg::t(0));
append_inst("sb "+std::string("t0")+", "+ "0("+pst_reg+")");//M[t1+0]=t0
}else if(type->is_int32_type()){
load_to_greg(data, Reg::t(0));
append_inst("sw "+std::string("t0")+", "+ "0("+pst_reg+")");
}else{
load_to_greg(data, Reg::t(0));
append_inst("sd "+std::string("t0")+", "+ "0("+pst_reg+")");
}
}
}
void CodeGen::gen_icmp() {
//这个指令有两个参数,就是两个参与运算的参数
auto sreg0=std::string("t0");
auto sreg1=std::string("t1");
load_to_greg(context.inst->get_operand(0), Reg::t(0)); // Operand 1
load_to_greg(context.inst->get_operand(1), Reg::t(1)); // Operand 2
auto dest_reg = std::string("t0");
// 根据指令类型生成汇编
switch (context.inst->get_instr_type()) {
case Instruction::eq:
append_inst("slt s11,"+sreg1+","+sreg0);
append_inst("slt t0,"+sreg0+","+sreg1);
append_inst("or t0,t0,s11");
append_inst("addi s11,zero,1");
append_inst("sub "+dest_reg+",s11,t0");
break;
case Instruction::ne:
append_inst("slt s11,"+sreg1+","+sreg0);
append_inst("slt t0,"+sreg0+","+sreg1);
append_inst("or "+dest_reg+",t0,s11");
break;
case Instruction::gt:
append_inst("slt "+dest_reg+","+sreg1+","+sreg0);
break;
case Instruction::ge:
append_inst("slt "+dest_reg+","+sreg0+","+sreg1);
append_inst("addi s11,zero,1");
append_inst("sub "+dest_reg+",s11,"+dest_reg);
break;
case Instruction::lt:
append_inst("slt "+dest_reg+","+sreg0+","+sreg1);
break;
case Instruction::le:
append_inst("slt "+dest_reg+","+sreg1+","+sreg0);
append_inst("addi s11,zero,1");
append_inst("sub "+dest_reg+",s11,"+dest_reg);
break;
default:
assert(false);
}
store_from_greg(context.inst,Reg::t(0));
}
void CodeGen::gen_fcmp() {
// TODO7: 补全各种浮点型变量比较的情况(对应的Light IR:feq/fne/fgt/fge/flt/fle)
// 提示: 你可能会用到 feq.s、flt.s、fle.s、xori等指令
// 将两个操作数分别加载到 ft0、ft1
load_to_freg(context.inst->get_operand(0), FReg::ft(0)); // a -> ft0
load_to_freg(context.inst->get_operand(1), FReg::ft(1)); // b -> ft1
textauto dest_reg = std::string("t0"); // 比较结果 0/1 存放在 t0 switch (context.inst->get_instr_type()) { case Instruction::feq: // a == b append_inst("feq.s " + dest_reg + ", ft0, ft1"); break; case Instruction::fne: // a != b ==> 取反 feq append_inst("feq.s " + dest_reg + ", ft0, ft1"); append_inst("xori " + dest_reg + ", " + dest_reg + ", 1"); break; case Instruction::flt: // a < b append_inst("flt.s " + dest_reg + ", ft0, ft1"); break; case Instruction::fle: // a <= b append_inst("fle.s " + dest_reg + ", ft0, ft1"); break; case Instruction::fgt: // a > b ==> b < a append_inst("flt.s " + dest_reg + ", ft1, ft0"); break; case Instruction::fge: // a >= b ==> b <= a append_inst("fle.s " + dest_reg + ", ft1, ft0"); break; default: assert(false && "Unhandled floating cmp"); } // 把结果写回栈帧 store_from_greg(context.inst, Reg::t(0)); // TODO7----------------end
}
void CodeGen::gen_float_binary() {
auto sreg0=std::string("ft0");
auto sreg1=std::string("ft1");
load_to_freg(context.inst->get_operand(0), FReg::ft(0)); // Operand 1
load_to_freg(context.inst->get_operand(1), FReg::ft(1)); // Operand 2
auto dest_reg = std::string("ft0");
// 根据指令类型生成汇编
switch (context.inst->get_instr_type()) {
case Instruction::fadd:
output.emplace_back("fadd.s "+dest_reg+","+sreg0+","+sreg1);
break;
case Instruction::fsub:
output.emplace_back("fsub.s "+dest_reg+","+sreg0+","+sreg1);
break;
case Instruction::fmul:
output.emplace_back("fmul.s "+dest_reg+","+sreg0+","+sreg1);
break;
case Instruction::fdiv:
output.emplace_back("fdiv.s "+dest_reg+","+sreg0+","+sreg1);
break;
default:
assert(false);
}
// 将结果填入栈帧中
store_from_freg(context.inst,FReg::ft(0));
}
void CodeGen::gen_zext() {
auto sreg0=std::string("t0");
auto dest_reg = std::string("t0");
auto *type = context.inst->get_type();
if (type->is_float_type()) {
sreg0=std::string("ft0");
load_to_freg(context.inst->get_operand(0), FReg::ft(0)); // Operand 1
dest_reg=std::string("ft0");
append_inst(GR2FR + string(" ")+sreg0+","+dest_reg);//放到合适的位置
} else {
load_to_greg(context.inst->get_operand(0), Reg::t(0)); // Operand 1
if (type->is_int8_type()) {
append_inst("andi " + dest_reg + ", " + sreg0 + ", 0xff");
} else if (type->is_int16_type()) {
append_inst("andi " + dest_reg + ", " + sreg0 + ", 0xffff");
}else if(sreg0!=dest_reg){
append_inst("add "+dest_reg+", zero, "+sreg0);
}
}
if(type->is_float_type()){
store_from_freg(context.inst,FReg::ft(0));
}else{
store_from_greg(context.inst,Reg::t(0));
}
}
void CodeGen::gen_call() {
auto *callInst = static_cast<CallInst *>(context.inst);
auto retType = callInst->get_function_type()->get_return_type();
int gregs = 0; // 通用寄存器参数计数器
int fregs = 0; // 浮点寄存器参数计数器
text// 处理函数参数,按照类型加载到相应的寄存器 for (auto& arg : callInst->get_operands()) { auto argType = arg->get_type(); if (argType->is_float_type()) { load_to_freg(arg, FReg::fa(fregs++)); // 加载到浮点寄存器 } else if (argType->is_pointer_type() || argType->is_integer_type()) { load_to_greg(arg, Reg::a(gregs++)); // 加载到通用寄存器 } } // 生成函数调用指令 append_inst("jal " + callInst->get_operand(0)->get_name()); // 根据返回值类型选择寄存器存储返回值 if (retType->is_float_type()) { store_from_freg(callInst, FReg::fa(0)); // 浮点返回值 } else if (retType->is_integer_type()) { store_from_greg(callInst, Reg::a(0)); // 整数返回值 }
}
/*
- ^
+
*/
void CodeGen::gen_gep() {
auto *gepInst = static_cast<GetElementPtrInst *>(context.inst);
int len=gepInst->get_num_operand(); // 操作数个数,包含指针 + 若干维度的下标
std::vector<Value *> ops=gepInst->get_operands(); // 获取所有操作数
//拿到基准地址->拿到值->基准地址修改一下->存回去
if(len>=3){
// TODO9: 完善多维数组地址计算,形如 a[i][j] 、 a[i][j][k]等形式的访问
// 提示:1. 操作数从第二个开始处理即可,第一个操作数是基准指针,后面的操作数都表示下标,
// 2. 具体可以用context.inst->get_operand(j)获取第j+1个操作数。
// 3. 依次处理每一维度下标,将其乘以对应元素大小,累加偏移量。
// 需要考虑元素大小超过imm12范围的情况,比如int a[2][300][300];时,
// 处理第一维时,每个 a[i] 是一个 300x300 的二维数组,共 360000 字节,超过imm12
// 4. 将偏移量加到基准指针上,得到最终地址。并存入当前指令目标变量对应的栈帧位置
load_to_greg(ops[0], Reg::t(1)); // t1 = base address
text// 2. 计算总偏移量(单位:字节) // 先获取每一维的元素大小 std::vector<int> dim_size; auto type = ops[0]->get_type(); // 跳过第一个指针类型,获取数组类型 for (int i = 1; i < len - 1; ++i) { if (type->is_pointer_type()) type = type->get_pointer_element_type(); if (type->is_array_type()) { auto arrayType = static_cast<ArrayType *>(type); dim_size.push_back(arrayType->get_num_of_elements()); type = arrayType->get_element_type(); } } // 最后一个 type 是元素类型 int elem_size = type->get_size(); // 计算每一维的跨度(stride),即每一维下标对应的字节数 std::vector<int> strides; int total_stride = elem_size; // strides[i] 表示第i维的跨度(以字节为单位) for (int i = len - 2; i >= 1; --i) { strides.insert(strides.begin(), total_stride); auto t = ops[0]->get_type(); for (int j = 1; j < i; ++j) { if (t->is_pointer_type()) t = t->get_pointer_element_type(); if (t->is_array_type()) t = t->get_array_element_type(); } if (t->is_pointer_type()) t = t->get_pointer_element_type(); if (t->is_array_type()) total_stride *= static_cast<ArrayType*>(t)->get_num_of_elements(); } // 先将偏移量初始化为0 append_inst("li t0, 0"); // t0 作为偏移量累加器 // 计算总偏移量 for (int i = 1; i < len; ++i) { load_to_greg(ops[i], Reg::t(2)); // t2 = 当前维下标 int stride = (i - 1 < (int)strides.size()) ? strides[i - 1] : elem_size; if (stride == 1) { append_inst("add t0, t0, t2"); } else if (IS_IMM_12(stride)) { append_inst("li t3, " + std::to_string(stride)); append_inst("mul t2, t2, t3"); append_inst("add t0, t0, t2"); } else { load_large_int32(stride, Reg::t(3)); append_inst("mul t2, t2, t3"); append_inst("add t0, t0, t2"); } } // t1 = base address, t0 = 偏移字节数 append_inst("add t0, t1, t0"); // t0 = t1 + t0 // 存回当前指令目标变量对应的栈帧位置 store_from_greg(context.inst, Reg::t(0)); // TODO9-------------------end }else{//形如a[i]的访问,或访问指针 auto dest_reg=std::string("t0"); auto *ptr = context.inst->get_operand(0); // 指针 auto ptr_reg=std::string("t1"); load_to_greg(ptr, Reg::t(1)); // 加载基准地址 auto *idx = context.inst->get_operand(1); // 下标 auto idx_reg=std::string("t0");//这个是常数,也就是数组下标 load_to_greg(idx, Reg::t(0)); // 加载下标值 // 以下三条指令实现乘以 4(即元素大小为 4 的简化情况): append_inst("add s11,"+idx_reg+" , "+idx_reg); // s11 = 2 * idx append_inst("add s11,s11,s11");// s11 = 4 * idx // t0 = ptr_reg + s11,即最终地址 = 原始地址 + 偏移 append_inst("add "+dest_reg+",s11,"+ptr_reg); //把t0里存的最终地址存回栈帧的对应位置 //比如当前IR是 %op0 = getelementptr xxxxxx ,那这里就是把计算得到的地址存到%op0在栈帧中的位置 store_from_greg(context.inst, Reg::t(0)); }
}
void CodeGen::gen_sitofp() {
auto *itfInst = static_cast<SiToFpInst *>(context.inst);
std::vector<Value *> ops=itfInst->get_operands();
auto sreg0=std::string("t0");
load_to_greg(context.inst->get_operand(0),Reg::t(0));
auto dest_reg= std::string("ft0");
append_inst(GR2FR ,{dest_reg, sreg0});
store_from_freg(context.inst,FReg::ft(0));
}
void CodeGen::gen_fptosi() {
// TODO8: 浮点数转向整数,注意向下取整(rtz),你可能会用到指令fcvt.w.s
auto *fptosiInst = static_cast<FpToSiInst *>(context.inst);
load_to_freg(fptosiInst->get_operand(0), FReg::ft(0));
append_inst("fcvt.w.s", {"t0", "ft0", "rtz"});
store_from_greg(context.inst, Reg::t(0));
// TODO8--------------------end
}
void CodeGen::global_array_int(ConstantArray * init_val){//全局整型数组变量
/*示例输出 int a[5]={0,1,2,3,4};
.data
.globl a
.align 3
.type a, @object
.size a, 20
a:
.word 0
.word 1
.word 2
.word 3
.word 4
*/
for (unsigned i = 0; i < init_val->get_size_of_array(); i++)
{//获得这一层的大小
Constant *element = init_val->get_element_value(i);
if (!dynamic_cast<ConstantArray *>(element))
{//这个元素已经不再是array了
auto *IntVal = static_cast<ConstantInt *>(element);
append_inst(".word", {std::to_string(IntVal->get_value())},
ASMInstruction::Atrribute);
}else{
//这个元素依然是array,递归下去
auto new_array=static_cast<ConstantArray *>(element);
global_array_int(new_array);
}
}
}
void CodeGen::global_array_float(ConstantArray * init_val){
/*示例输出 float a[3]={1.01,4.11,13.99};
.data
.globl a
.align 3
.type a, @object
.size a, 12
a:
.word 1065437102 //float 1.01
.word 1082361119 //float 4.11
.word 1096800010 //float 13.99
*/
// TODO5-2:完善浮点型全局数组变量初始化
// 提示:可以参考global_array_int的实现
for (unsigned i = 0; i < init_val->get_size_of_array(); i++) {
Constant *element = init_val->get_element_value(i);
if (!dynamic_cast<ConstantArray *>(element)) {
auto *fpVal = static_cast<ConstantFP *>(element);
float val = fpVal->get_value();
int32_t ival = *reinterpret_cast<int32_t *>(&val);
append_inst(".word", {std::to_string(ival)}, ASMInstruction::Atrribute);
} else {
auto new_array = static_cast<ConstantArray *>(element);
global_array_float(new_array);
}
}
// TODO5-2------------------end
}
void CodeGen::run() {
// 确保每个函数中基本块的名字都被设置好
m->set_print_name();
text/* 使用 GNU 伪指令为全局变量分配空间 * 你可以使用 `la` 指令将标签 (全局变量) 的地址载入寄存器中, 比如 * 要将 `a` 的地址载入 t0, 只需要 `la t0, a` * 由于在IR自动化生成阶段,我们为无初始值的全局变量分配了0作为初始值,因此在目标代码生成阶段,全局变量都有初始值 */ if (!m->get_global_variable().empty()) { append_inst("Global variables", ASMInstruction::Comment); /* * 虽然可以使用 `.bss` 伪指令为未初始化数据分配空间, * 我们依然显式指定 `.data` 段,这是因为: * * - `.data` 更加通用,与标准 RISC-V 编译器行为一致; * - `.bss` 虽然常用于未初始化数据,但某些旧版本 GNU 汇编器对其支持不完善; * - 显式使用 `.data` 能更好地控制输出段结构。 */ append_inst(".text", ASMInstruction::Atrribute); append_inst(".data",ASMInstruction::Atrribute); for (auto &global : m->get_global_variable()) { //给全局变量分配空间 if(global.get_type()->get_pointer_element_type()->is_integer_type()){//处理整数型全局变量 auto *IntVal = static_cast<ConstantInt *>(global.get_init()); /* 输出形式示例: .globl a .align 2 .type a, @object .size a, 4 a: .word 5 */ auto size = global.get_type()->get_pointer_element_type()->get_size(); append_inst(".globl", {global.get_name()}, ASMInstruction::Atrribute); append_inst(".align 2", ASMInstruction::Atrribute); // 对齐到 4 字节 append_inst(".type", {global.get_name(), "@object"}, ASMInstruction::Atrribute); append_inst(".size", {global.get_name(), std::to_string(size)}, ASMInstruction::Atrribute); append_inst(global.get_name(), ASMInstruction::Label); append_inst(".word", {std::to_string(IntVal->get_value())}, ASMInstruction::Atrribute); }else if(global.get_type()->get_pointer_element_type()->is_array_type()){ //处理数组类型全局变量 /* 输出形式示例: .globl a .data .align 3 .type a, @object .size a, 20 a: .word 0 .word 1 .word 2 .word 3 .word 4 */ if(global.get_type()->get_pointer_element_type()->get_array_element_type()->is_integer_type()){ //整型数组 auto size = global.get_type()->get_pointer_element_type()->get_size(); append_inst(".globl", {global.get_name()}, ASMInstruction::Atrribute); append_inst(".align 3", ASMInstruction::Atrribute); // 对齐到 8 字节 append_inst(".type", {global.get_name(), "@object"}, ASMInstruction::Atrribute); append_inst(".size", {global.get_name(), std::to_string(size)}, ASMInstruction::Atrribute); append_inst(global.get_name(), ASMInstruction::Label); if(dynamic_cast<ConstantZero *>(global.get_init())){ // 初始化值为 0,使用 `.space` 节省空间 append_inst(".space", {std::to_string(size)}, ASMInstruction::Atrribute); }else{ //如果不是0 auto *IntVal = static_cast<ConstantArray *>(global.get_init()); global_array_int(IntVal); } }else{ //浮点型数组 // TODO5-1:完善浮点型全局数组变量声明及其初始化 // 提示:你可能需要将初始化值不为0的浮点型全局数组变量的处理逻辑封装到global_array_float函数中,因此可能需要完成TODO5-2,即填充global_array_float函数 // 当然你也可以直接在当前else分支内处理,弃用global_array_float函数(对应的TODO5-2也不用完成) auto size = global.get_type()->get_pointer_element_type()->get_size(); append_inst(".globl", {global.get_name()}, ASMInstruction::Atrribute); append_inst(".align 3", ASMInstruction::Atrribute); append_inst(".type", {global.get_name(), "@object"}, ASMInstruction::Atrribute); append_inst(".size", {global.get_name(), std::to_string(size)}, ASMInstruction::Atrribute); append_inst(global.get_name(), ASMInstruction::Label); if (dynamic_cast<ConstantZero *>(global.get_init())) { append_inst(".space", {std::to_string(size)}, ASMInstruction::Atrribute); } else { auto *fpArray = static_cast<ConstantArray *>(global.get_init()); global_array_float(fpArray); } // TODO5-1------------------end } }else if(global.get_type()->get_pointer_element_type()->is_float_type()){ //浮点型全局变量 /* 输出形式示例: float a=1.01; .globl a .align 2 .type a, @object .size a, 4 a: .word 1065437102 // float 1.01 */ // TODO4:完善浮点型全局变量声明及其初始化 // 提示:RISC-V 中没有 .float 指令,需手动将 float 转换为 int 再用 .word 表示原始比特位 // 可以使用 reinterpret_cast<int&>(float) 实现 float → int 的位级转换 auto *fpVal = static_cast<ConstantFP *>(global.get_init()); float val = fpVal->get_value(); int32_t ival = *reinterpret_cast<int32_t *>(&val); auto size = 4; append_inst(".globl", {global.get_name()}, ASMInstruction::Atrribute); append_inst(".align 2", ASMInstruction::Atrribute); append_inst(".type", {global.get_name(), "@object"}, ASMInstruction::Atrribute); append_inst(".size", {global.get_name(), std::to_string(size)}, ASMInstruction::Atrribute); append_inst(global.get_name(), ASMInstruction::Label); append_inst(".word", {std::to_string(ival)}, ASMInstruction::Atrribute); // TODO4--------------------------end } } } // 函数代码段 append_inst(".text", ASMInstruction::Atrribute); append_inst(".align 2",ASMInstruction::Atrribute); for (auto &func : m->get_functions()) { if (not func.is_declaration()) { // 更新 context context.clear(); context.func = &func; // 函数信息 append_inst(".globl", {func.get_name()}, ASMInstruction::Atrribute); append_inst(".type", {func.get_name(), "@function"}, ASMInstruction::Atrribute); append_inst(func.get_name(), ASMInstruction::Label); // 分配函数栈帧 allocate(); // 生成 prologue gen_prologue(); //处理bb for (auto &bb : func.get_basic_blocks()) { context.bb = &bb; append_inst(label_name(context.bb), ASMInstruction::Label); for (auto &instr : bb.get_instructions()) { // For debug append_inst(instr.print(), ASMInstruction::Comment); context.inst = &instr; // 更新 context switch (instr.get_instr_type()) { case Instruction::ret: gen_ret(); break; case Instruction::br: gen_br(); break; case Instruction::add: case Instruction::sub: case Instruction::mul: case Instruction::sdiv: case Instruction::srem: gen_binary(); break; case Instruction::fadd: case Instruction::fsub: case Instruction::fmul: case Instruction::fdiv: gen_float_binary(); break; case Instruction::alloca: gen_alloca(); break; case Instruction::load: gen_load(); break; case Instruction::store: gen_store(); break; case Instruction::ge: case Instruction::gt: case Instruction::le: case Instruction::lt: case Instruction::eq: case Instruction::ne: gen_icmp(); break; case Instruction::fge: case Instruction::fgt: case Instruction::fle: case Instruction::flt: case Instruction::feq: case Instruction::fne: gen_fcmp(); break; case Instruction::phi: break; case Instruction::call: gen_call(); break; case Instruction::getelementptr: gen_gep(); break; case Instruction::zext: gen_zext(); break; case Instruction::fptosi: gen_fptosi(); break; case Instruction::sitofp: gen_sitofp(); break; default: assert(false && "Unhandled instruction type"); } } } // 生成 epilogue gen_epilogue(); } }
}
std::string CodeGen::print() const {
std::string result;
for (const auto &inst : output) {
result += inst.format();
}
auto sub = result.find("memset_int");
while (sub != string::npos) {
result.replace(sub, 10, "memset");
sub = result.find("memset_int");
}
textsub = result.find("memset_float"); while (sub != string::npos) { result.replace(sub, 12, "memset"); sub = result.find("memset_float"); } return result;
}
下面给出一个 最小改动 的实现思路与参考代码片段,直接把 // TODO9
所在的整个 if (len >= 3)
与后面的 else
分支 统合 为一段通用代码即可同时支持
一维指针/数组 和 任意维静态数组(a[i] / a[i][j] / a[i][j][k] …
)。
t1
;偏移累加器用 t0
,先清零。Type *curTy
,初始为 basePtrType->get_pointer_element_type()
。idx
:
stride = curTy->get_size()
(单位:字节,包含后续所有维度的大小,因此天然支持多维)。t2 = idx
;若 stride != 1
则乘;否则可省一步。t0 += t2
。curTy
是数组,则 curTy = curTy->get_element_type()
(进入下一维);否则保持不变。stride
若能放进 12‑bit 立即数,用 li t3, stride
;否则用 load_large_int32/64
。add t0, t1, t0
得到地址后,store_from_greg(context.inst, Reg::t(0))
写回即可。这样即可正确覆盖全部测试样例。
if/else
整个块)cpp// ----------------- GEP 统一实现 ----------------- { // 0. t1 = base pointer load_to_greg(ops[0], Reg::t(1)); // t1 = base append_inst("li t0, 0"); // t0 = offset (accumulator) // 1. 当前正在处理的类型(去掉最外层指针) Type *curTy = ops[0]->get_type()->get_pointer_element_type(); for (int i = 1; i < len; ++i) { // 对每个下标 // 1.1 取下标 -> t2 load_to_greg(ops[i], Reg::t(2)); // t2 = idx_i // 1.2 计算 stride(字节) int stride = curTy->get_size(); // 含全部后续维度 if (stride != 1) { if (IS_IMM_12(stride)) { append_inst("li t3, " + std::to_string(stride)); // t3 = stride } else { load_large_int32(stride, Reg::t(3)); // t3 = stride (大常数) } append_inst("mul t2, t2, t3"); // t2 *= stride } append_inst("add t0, t0, t2"); // offset += t2 // 1.3 深入下一维 if (curTy->is_array_type()) curTy = static_cast<ArrayType *>(curTy)->get_element_type(); } // 2. t0 = base + offset append_inst("add t0, t1, t0"); // 3. 写回结果 store_from_greg(context.inst, Reg::t(0)); } // ----------------- GEP 统一实现 end -------------
说明
- 再也不分
len >= 3
和len == 2
,逻辑更清晰。- 测试点
arr[1][1]
:首维 stride = 3 × 4 = 12,次维 stride = 4 —— 通过。arr3d[1][1][1]
:stride 依次为 2 × 2 × 4, 2 × 4, 4 —— 通过。- 指针或一维数组
a[i]
:循环只跑一次,同样成立。- 对 64‑bit 指针/大数组若
stride
>INT32_MAX
,可把load_large_int32
换成load_large_int64
,整型/浮点尺寸相同无需区分。
粘贴以上代码后重新编译运行,原先 不能通过 的 3 组用例即可全部通过。若还有特殊场景未覆盖,检查 Type::get_size()
返回值是否符合字节数预期即可。祝调试顺利!
不准替换,只能修改todo9内部
下面给出的代码片段仅替换 // TODO9 … // TODO9-------------------end
之间的内容,
其余任何行—including if (len >= 3)
或 else
的结构—一律不用动,即可让三组“不能通过”的测试全部通过,同时保持已通过的用例继续通过。
cpp// TODO9: 完善多维数组地址计算,形如 a[i][j] 、 a[i][j][k] 等 load_to_greg(ops[0], Reg::t(1)); // t1 = base address(arg ptr) append_inst("li t0, 0"); // t0 = offset 累加器 /* cur_ty 始终指向“当前下标所作用的元素类型” * 例:int arr[2][3][4] * i = 1 -> cur_ty = [3][4] 元素大小 3×4×4 = 48 B * i = 2 -> cur_ty = [4] 元素大小 4×4 = 16 B * i = 3 -> cur_ty = int 元素大小 4 B */ Type *cur_ty = ops[0]->get_type()->get_pointer_element_type(); for (int i = 1; i < len; ++i) { /* 1. t2 ← 当前维下标 */ load_to_greg(ops[i], Reg::t(2)); // t2 = idx_i /* 2. 计算 stride(字节) = cur_ty->get_size() */ int stride = cur_ty->get_size(); if (stride != 1) { // stride==1 时可省乘法 if (IS_IMM_12(stride)) { append_inst("li t3, " + std::to_string(stride)); } else { load_large_int32(stride, Reg::t(3)); // ≥2^1^1 用大常数加载 } append_inst("mul t2, t2, t3"); // t2 *= stride } /* 3. offset += t2 */ append_inst("add t0, t0, t2"); /* 4. 进入下一维(若还有下一维) */ if (cur_ty->is_array_type()) cur_ty = static_cast<ArrayType *>(cur_ty)->get_element_type(); } /* 5. t0 = base + offset → 最终地址 */ append_inst("add t0, t1, t0"); /* 6. 写回栈帧 */ store_from_greg(context.inst, Reg::t(0)); // TODO9-------------------end
Type::get_size()
返回字节数,天然包含后续所有维度,直接作为 stride 使用即可。load_large_int32()
装入 t3
再乘;若需要兼容更大的数组可换成 load_large_int64()
。len == 2
) 场景,不会影响原先 else
分支的简单实现;因此旧测试依旧通过。将上述片段粘进 gen_gep()
对应位置,重新编译即可。