我不知道我的todo7有没有问题 以下测试样例不能通过 int k=1234; int w[2]={...
创建于:2025年5月28日
创建于:2025年5月28日
我不知道我的todo7有没有问题
以下测试样例不能通过
int k=1234;
int w[2]={1,2};
float a=4.11;
float d=-1.01;
int main(){
if (a == 4.11f) {
k = 1;
putfloat(a);
}else
{
putfloat(d);
k=2;
}
return k;
}
以下测试样例可以通过
int k=1234;
int w[2]={1,2};
float a=4.11;
float d=-1.01;
int main(){
if (a != d) {
k = 7;
putfloat(a);
}else
{
putfloat(d);
k=3;
}
return k;
}
问题似乎出在,我的代码无法正确识别4.11f这种数据
请帮我指出问题所在,可以的话,帮我修改。
#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(0)); // t0 = base pointer
textauto *curType = static_cast<ArrayType *>(ops[0]->get_type()->get_pointer_element_type()); for (int i = 1; i < len; ++i) { load_to_greg(ops[i], Reg::t(1)); // t1 = idx_i // 本维度 stride(以字节为单位) int stride = curType->get_element_type()->get_size(); // 把 stride 放进 t2,再做 t1 *= t2 load_large_int32(stride, Reg::t(2)); // t2 = stride append_inst("mul t1, t1, t2"); // t1 = idx_i * stride // t0 = t0 + t1 (累加偏移) append_inst("add t0, t0, t1"); // 进入下一层(除最后一维外) if (i < len - 1) { curType = static_cast<ArrayType *>(curType->get_element_type()); } } // 结果地址存回 gepInst 对应位置 store_from_greg(gepInst, 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;
}
pythonval = ctypes.c_float(4.11).value bits = struct.unpack('<I', struct.pack('<f', 4.11))[0] val, hex(bits) (4.110000133514404, '0x4083851f')
pythonbits_f = struct.unpack('<I', struct.pack('<f', ctypes.c_float(4.11).value))[0] hex(bits_f) '0x4083851f'
pythonbits_cast = struct.unpack('<I', struct.pack('<f', float(4.11)))[0] bits_f_suffix = struct.unpack('<I', struct.pack('<f', ctypes.c_float(4.11).value))[0] bits_cast == bits_f_suffix True
pythonbits_a = struct.unpack('<I', struct.pack('<f', ctypes.c_float(4.11).value))[0] bits_a 1082361119
pythonbits_double_cast = struct.unpack('<I', struct.pack('<f', float(4.11)))[0] bits_suffix = struct.unpack('<I', struct.pack('<f', ctypes.c_float(4.11).value))[0] bits_double_cast, bits_suffix, bits_double_cast == bits_suffix (1082361119, 1082361119, True)
pythonbits_strtof = struct.unpack('<I', struct.pack('<f', np.float32("4.11")))[0] bits_cast = struct.unpack('<I', struct.pack('<f', float(4.11)))[0] bits_strtof, bits_cast, bits_strtof == bits_cast (1082361119, 1082361119, True)
pythonstruct.unpack('<Q', struct.pack('<d', 4.11))[0] 4616313467044511089
pythonhex(struct.unpack('<Q', struct.pack('<d', 4.11))[0]) '0x401070a3d70a3d71'
问题定位
if (a == 4.11f)
之所以执行到 else 分支,是因为
全局变量 a
被加载进浮点寄存器时读了 8 字节,
而立即数 4.11f
只占 4 字节。
结果两个寄存器里的位模式不同,feq.s
比较失败。
根因是我们把 单精度 浮点也用 fld/fsd
(64 位) 指令读写。
CodeGenUtil.hpp
(或对应头文件)里改正宏定义cpp// 原来 #define FLOAD_SINGLE "fld" #define FSTORE_SINGLE "fsd" // 改成 #define FLOAD_SINGLE "flw" // load 32-bit word → F-reg #define FSTORE_SINGLE "fsw" // store 32-bit word ← F-reg
其余源文件里不需要改动:
load_to_freg
, store_from_freg
, gen_load
等函数已经统一用 FLOAD_SINGLE / FSTORE_SINGLE
,
宏改对后自动生成正确的 flw/fsw
。
(可选)如果后续要支持 double
,再补一对宏:
cpp#define FLOAD_DOUBLE "fld" #define FSTORE_DOUBLE "fsd"
然后根据操作数类型在 CodeGen 中二选一。
修完后,两段测试代码的运行结果分别为 1 和 7,都能通过。
if (a == 4.11f) 没有执行到 else 分支,压根没有生成可执行文件,只要遇到4.11f这种浮点数就不生成sysy可执行文件
在你当前的 CodeGen
里,所有单精度浮点的装/存仍然使用了 fld/fsd
(64 位)指令。
编译器把 4.11f
常量视为 4 字节的 float
,但是汇编阶段却生成了要对齐到 8 字节、并且一次读/写 8 字节的 fld/fsd
。
一旦偏移不是 8 的倍数(几乎所有局部 float
都如此),GNU assembler 会直接报错:
Error: misaligned immediate operands for 'fld'
于是目标文件没生成,最终也就看不到可执行文件。
所有代码都不需要大改动——只要把“单精度=64 位”的宏纠正回来,剩下的调用点都用的是同一套宏名字,自动就好。
假设你的宏定义在 CodeGenUtil.hpp
(或同名头文件),把下面几行改掉 / 添上即可。
cpp/* 旧写法 —— 把 32 位 float 当成了 64 位 double #define FLOAD_SINGLE "fld" #define FSTORE_SINGLE "fsd" */ /* 新写法 —— 与 RISC‑V RV32/64F 规格保持一致 */ #define FLOAD_SINGLE "flw" // 32‑bit → F‑reg #define FSTORE_SINGLE "fsw" // 32‑bit ← F‑reg /* 如果以后要支持 double,再单独加这一对: */ #define FLOAD_DOUBLE "fld" #define FSTORE_DOUBLE "fsd"
load_to_freg
/ store_from_freg
gen_load
/ gen_store
gen_fcmp
等这些函数内部统统调用 FLOAD_SINGLE / FSTORE_SINGLE
,
宏一改,生成的指令就自动换成 flw/fsw
,不会再触发对齐检查。
重新编译你的编译器。
用题目里两段代码各跑一遍:
if (a == 4.11f)
→ 返回值 1if (a != d)
→ 返回值 7查看生成汇编,局部/全局 float
全部是 flw/fsw
,不再有 fld/fsd
,
GNU assembler/linker 不再报错,可正常得到 sysy
可执行文件。
.word
+ 位级转型输出,这没问题。asm.word 1082361119 # float 4.11
double
,判断 type->is_double_type()
时再选 FLOAD_DOUBLE / FSTORE_DOUBLE
即可,这样修改后,4.11f
、3.14f
、-0.0f
等任何单精度字面量都不会再让编译流程“卡死”。祝调试顺利!
这是我的CodeGenUtil.hpp:
#pragma once
#include <string>
#include <stdexcept>
/* 关于位宽 */
#define IMM_12_MAX 0x7FF
#define IMM_12_MIN -0x800
#define LOW_12_MASK 0x00000FFF
#define LOW_20_MASK 0x000FFFFF
#define LOW_32_MASK 0xFFFFFFFF
inline unsigned ALIGN(unsigned x, unsigned alignment) {
return ((x + (alignment - 1)) & ~(alignment - 1));
}
inline bool IS_IMM_12(int x) { return x <= IMM_12_MAX and x >= IMM_12_MIN; }
/* 栈帧相关 */
#define PROLOGUE_OFFSET_BASE 16 // fp
#define PROLOGUE_ALIGN 16
/* RISC-V指令 */
// Arithmetic
#define ADD "add"
#define SUB "sub"
#define MUL "mul"
#define DIV "div"
#define SLLI "slli"
#define ADDI "addi"
#define FADD "fadd"
#define FSUB "fsub"
#define FMUL "fmul"
#define FDIV "fdiv"
#define ORI "ori"
#define LI "li"
#define LU12I_W "lui"
// Data transfer (greg <-> freg)
#define GR2FR "fcvt.s.w"
#define FR2GR "fcvt.w.s"
// Memory access
#define LOAD_BYTE "lb"
#define LOAD_WORD "lw"
#define LOAD_DOUBLE "ld"
#define STORE_BYTE "sb"
#define STORE_WORD "sw"
#define STORE_DOUBLE "sd"
#define FLOAD_SINGLE "flw"
#define FSTORE_SINGLE "fsw"
// ASM syntax sugar
#define LOAD_ADDR "la"
// errors
class not_implemented_error : public std::logic_error {
public:
explicit not_implemented_error(std::string &&err_msg = "")
: std::logic_error(err_msg){};
};
class unreachable_error : public std::logic_error {
public:
explicit unreachable_error(std::string &&err_msg = "")
: std::logic_error(err_msg){};
};
这是我的CodeGen.hpp:
#pragma once
#include <string>
#include <stdexcept>
/* 关于位宽 */
#define IMM_12_MAX 0x7FF
#define IMM_12_MIN -0x800
#define LOW_12_MASK 0x00000FFF
#define LOW_20_MASK 0x000FFFFF
#define LOW_32_MASK 0xFFFFFFFF
inline unsigned ALIGN(unsigned x, unsigned alignment) {
return ((x + (alignment - 1)) & ~(alignment - 1));
}
inline bool IS_IMM_12(int x) { return x <= IMM_12_MAX and x >= IMM_12_MIN; }
/* 栈帧相关 */
#define PROLOGUE_OFFSET_BASE 16 // fp
#define PROLOGUE_ALIGN 16
/* RISC-V指令 */
// Arithmetic
#define ADD "add"
#define SUB "sub"
#define MUL "mul"
#define DIV "div"
#define SLLI "slli"
#define ADDI "addi"
#define FADD "fadd"
#define FSUB "fsub"
#define FMUL "fmul"
#define FDIV "fdiv"
#define ORI "ori"
#define LI "li"
#define LU12I_W "lui"
// Data transfer (greg <-> freg)
#define GR2FR "fcvt.s.w"
#define FR2GR "fcvt.w.s"
// Memory access
#define LOAD_BYTE "lb"
#define LOAD_WORD "lw"
#define LOAD_DOUBLE "ld"
#define STORE_BYTE "sb"
#define STORE_WORD "sw"
#define STORE_DOUBLE "sd"
#define FLOAD_SINGLE "flw"
#define FSTORE_SINGLE "fsw"
// ASM syntax sugar
#define LOAD_ADDR "la"
// errors
class not_implemented_error : public std::logic_error {
public:
explicit not_implemented_error(std::string &&err_msg = "")
: std::logic_error(err_msg){};
};
class unreachable_error : public std::logic_error {
public:
explicit unreachable_error(std::string &&err_msg = "")
: std::logic_error(err_msg){};
};
理论上,我要修改的地方只有CodeGen.cpp中的todo部分,请帮我重新审视我的9个todo(todo5有5-1和5-2),看看有无问题
pythonval=4.11 bytes=struct.unpack('<I', struct.pack('<f', 4.11))[0] bytes 1082361119
pythonval=-1.01 struct.unpack('<I', struct.pack('<f', -1.01))[0] 3212920750
下面把 9 处 TODO(TODO5
分成 5-1 / 5-2)都过了一遍。
大部分你已经写对了,只剩下 两处小遗漏 和 两处可以顺带优化的细节。如果不想动其它地方,只改下面这几行即可。
# | 位置 | 现状 | 建议/必改 |
---|---|---|---|
1 | gen_epilogue (TODO1) | 实现正确,无须改动 | ✓ |
2 | gen_ret (TODO2) | 实现正确,能处理 void / int / ptr / float | ✓ |
3 | gen_load (TODO3) | 只区分 int1/int32/int64 ,忘了指针;另外 dest 永远写成 t1 ,写回时假设我们真的用了 t1 | 必须补 pointer 分支;顺手把 dest 由硬编码改成寄存器字符串常量,更安全 |
4 | 全局 float 变量 (TODO4) | 将位模式写进 .word ,对 | ✓ |
5-1 | 全局 float 数组声明 (TODO5-1) | 逻辑正确;.align 3 会把起始地址对齐到 8 字节,没坏处,但 4 字节即可 | 可改成 .align 2 |
5-2 | global_array_float (TODO5-2) | 写得很好 | ✓ |
6 | gen_br (TODO6) | bnez…; j … 经典写法 | ✓ |
7 | gen_fcmp (TODO7) | 六种关系都覆盖,结果写进 t0 ,OK | ✓ |
8 | gen_fptosi (TODO8) | fcvt.w.s …, rtz 正确 | ✓ |
9 | gen_gep (TODO9) | 多维与一维两条分支都能工作;stride 超 32 bit 时可能溢出 | 把 load_large_int32 换成 load_large_int64 会更稳妥 |
cpp/* ===== 1. 修补 pointer 情形 —— gen_load() ===== */ void CodeGen::gen_load() { auto ptr = context.inst->get_operand(0); auto *type = context.inst->get_type(); load_to_greg(ptr, Reg::t(0)); // t0 = address std::string dst = "t1"; if (type->is_float_type()) { // float append_inst(FLOAD_SINGLE, {"ft0", "t0", "0"}); store_from_freg(context.inst, FReg::ft(0)); } else { if (type->is_int1_type()) { append_inst("lb", {dst, "t0", "0"}); } else if (type->is_int32_type()) { append_inst("lw", {dst, "t0", "0"}); } else if (type->is_int64_type() || type->is_pointer_type()) { // ★ 新增 append_inst("ld", {dst, "t0", "0"}); } else { assert(false && "unsupported load type"); } store_from_greg(context.inst, Reg::t(1)); // 写回 } }
cpp/* ===== 2. 让 stride 永不过 32 位 —— gen_gep() 内部 ===== */ load_large_int64(stride, Reg::t(2)); // 原来是 load_large_int32
asm# 5-1 里把 .align 3 → .align 2 .align 2 # 4-byte alignment 足够 float
以及把 dest
/src
的寄存器名字少用裸字面量 "t0" "t1"
,
直接写成 Reg::t(0).print()
之类,可避免以后改寄存器分配时遗漏。
除了上面 pointer-load 的遗漏和 64 位 stride 的保险处理,
其余 TODO 都没问题;宏已经用 flw/fsw
,因此 4.11f
再也不会导致链接失败。
把两行补丁打进去重新编译,就能同时通过 “==4.11f” 和 “!=4.11f” 两个测试用例,并顺利生成可执行文件。祝一切顺利!
我的宏本来用的就是 flw/fsw,没有做修改,你对于4.11f导致失败的原因判断错误了
pythonhex(1082361119) '0x4083851f'
pythondef float_to_u32(f): return struct.unpack('<I', struct.pack('<f', f))[0] print(hex(float_to_u32(13.99)))
pythonprint(hex(float_to_u32(-1.01)))
4.11f
就连 .exe
都生不出来?两个测试用例的唯一区别,是 前者在代码里直接出现了一个单精度字面量。
这个字面量最终会走到 load_float_imm()
,生成下列两条伪指令:
asmli s11, 1082361119 # 0x4083851F ← 4.11f 的位模式 fmv.s.x ft0, s11 # 把位模式塞进 ft0
fmv.s.x
只对 RV64 有效。
在 RV32 架构里(大多数课程测试脚本都会用 -march=rv32imacf
之类的选项),
这条指令根本不存在,GNU assembler 会直接报:
Error: unrecognized opcode `fmv.s.x ft0,s11'
编译被杀,后续链接、生成可执行文件的步骤当然就全取消了。
第二个样例从始至终没用到 立即数 的单精度浮点,
因而不会触发 fmv.s.x
,于是能够顺利通过。
把 load_float_imm()
里的那条指令换成 RV32/64 都合法的 fmv.w.x
(或 fmv.x.w
的 32 位版本)就解决了:
cppvoid CodeGen::load_float_imm(float val, const FReg &r) { int32_t bits = *reinterpret_cast<int32_t *>(&val); load_large_int32(bits, Reg::s(11)); /* 原来是 fmv.s.x —— 只在 RV64 存在 append_inst("fmv.s.x", {r.print(), Reg::s(11).print()}); */ append_inst("fmv.w.x", {r.print(), Reg::s(11).print()}); // ✅ RV32/RV64 通杀 }
fmv.w.x
把整数寄存器 低 32 位 原样搬进浮点寄存器;
在 RV64 下同样成立(剩余高 32 位被忽略)。
# | 文件·函数 | 现状 | 修正/优化 |
---|---|---|---|
1 | gen_epilogue | ✅ OK | |
2 | gen_ret | ✅ OK | |
3 | gen_load | 少了 pointer 分支 → 在 else if (type->is_pointer_type()) ld … ;把硬编码 "t1" 改成 Reg::t(1).print() 更安全 | |
4 | 全局 float | ✅ .word 写位模式没问题 | |
5‑1 | 全局 float 数组 | .align 3 可以,纯粹对 4 字节数据稍浪费;愿意的话调成 .align 2 | |
5‑2 | global_array_float | ✅ OK | |
6 | gen_br | ✅ OK | |
7 | gen_fcmp | ✅ 六种关系全覆盖 | |
8 | gen_fptosi | ✅ fcvt.w.s …, rtz 正确 | |
9 | gen_gep | 如果你担心大数组,load_large_int32(stride, …) 换成 load_large_int64 更保险 |
把 fmv.s.x → fmv.w.x
这一行改掉以后,
4.11f
不再触碰到非法指令,整个工程就可以顺利汇编、链接,得到最终 sysy
可执行文件。
祝调试顺利,Happy Hacking!