// IR 文本输出: // - 将 IR 打印为 .ll 风格的文本 // - 支撑调试与测试对比(diff) #include "ir/IR.h" #include #include #include #include #include #include "utils/Log.h" namespace ir { static const char* TypeToString(const Type& ty) { switch (ty.GetKind()) { case Type::Kind::Void: return "void"; case Type::Kind::Int32: return "i32"; case Type::Kind::PtrInt32: return "i32*"; case Type::Kind::Float32: return "float"; case Type::Kind::PtrFloat32: return "float*"; } throw std::runtime_error(FormatError("ir", "未知类型")); } static const char* OpcodeToString(Opcode op) { switch (op) { case Opcode::Add: return "add"; case Opcode::Sub: return "sub"; case Opcode::Mul: return "mul"; case Opcode::Div: return "sdiv"; case Opcode::Mod: return "srem"; case Opcode::Cmp: return "icmp"; case Opcode::Cast: return "cast"; case Opcode::Br: return "br"; case Opcode::CondBr: return "br"; case Opcode::Call: return "call"; case Opcode::Alloca: return "alloca"; case Opcode::Load: return "load"; case Opcode::Store: return "store"; case Opcode::Ret: return "ret"; case Opcode::Gep: return "getelementptr"; } return "?"; } static const char* CmpOpToString(CmpOp op) { switch (op) { case CmpOp::Eq: return "eq"; case CmpOp::Ne: return "ne"; case CmpOp::Lt: return "slt"; case CmpOp::Le: return "sle"; case CmpOp::Gt: return "sgt"; case CmpOp::Ge: return "sge"; } return "?"; } static std::string ValueToString(const Value* v) { if (auto* ci = dynamic_cast(v)) { return std::to_string(ci->GetValue()); } if (auto* gv = dynamic_cast(v)) { return "@" + gv->GetName(); } if (auto* func = dynamic_cast(v)) { return "@" + func->GetName(); } if (auto* arg = dynamic_cast(v)) { return arg->GetName(); } return v ? v->GetName() : ""; } void IRPrinter::Print(const Module& module, std::ostream& os) { // 先打印全局变量 for (const auto& gv : module.GetGlobalVars()) { if (!gv) continue; const char* elem_ty = gv->IsFloat() ? "float" : "i32"; if (gv->IsArray()) { os << "@" << gv->GetName() << " = global [" << gv->GetCount() << " x " << elem_ty << "] zeroinitializer\n"; } else { if (gv->IsFloat()) { std::int32_t bits = static_cast(gv->GetInitValue()); float fval = 0.0f; std::memcpy(&fval, &bits, sizeof(fval)); os << "@" << gv->GetName() << " = global float " << fval << "\n"; } else { os << "@" << gv->GetName() << " = global i32 " << gv->GetInitValue() << "\n"; } } } if (!module.GetGlobalVars().empty()) os << "\n"; for (const auto& func : module.GetFunctions()) { if (func->IsExternal()) { // 外部函数声明:declare rettype @name(paramtypes) os << "declare " << TypeToString(*func->GetType()) << " @" << func->GetName() << "("; const auto& ptypes = func->GetParamTypes(); for (size_t i = 0; i < ptypes.size(); ++i) { if (i != 0) os << ", "; os << TypeToString(*ptypes[i]); } os << ")\n"; continue; } std::string params; const auto& param_types = func->GetParamTypes(); for (size_t i = 0; i < param_types.size(); ++i) { if (i != 0) { params += ", "; } params += TypeToString(*param_types[i]); params += " %arg" + std::to_string(i); } os << "define " << TypeToString(*func->GetType()) << " @" << func->GetName() << "(" << params << ") {\n"; for (const auto& bb : func->GetBlocks()) { if (!bb) { continue; } os << bb->GetName() << ":\n"; for (const auto& instPtr : bb->GetInstructions()) { const auto* inst = instPtr.get(); switch (inst->GetOpcode()) { case Opcode::Add: case Opcode::Sub: case Opcode::Mul: case Opcode::Div: case Opcode::Mod: { auto* bin = static_cast(inst); os << " " << bin->GetName() << " = " << OpcodeToString(bin->GetOpcode()) << " " << TypeToString(*bin->GetLhs()->GetType()) << " " << ValueToString(bin->GetLhs()) << ", " << ValueToString(bin->GetRhs()) << "\n"; break; } case Opcode::Cmp: { auto* cmp = static_cast(inst); os << " " << cmp->GetName() << " = " << OpcodeToString(cmp->GetOpcode()) << " " << CmpOpToString(cmp->GetCmpOp()) << " " << TypeToString(*cmp->GetLhs()->GetType()) << " " << ValueToString(cmp->GetLhs()) << ", " << ValueToString(cmp->GetRhs()) << "\n"; break; } case Opcode::Cast: { auto* cast = static_cast(inst); const char* cast_name = (cast->GetCastOp() == CastOp::IntToFloat) ? "sitofp" : "fptosi"; os << " " << cast->GetName() << " = " << cast_name << " " << TypeToString(*cast->GetValue()->GetType()) << " " << ValueToString(cast->GetValue()) << " to " << TypeToString(*cast->GetType()) << "\n"; break; } case Opcode::Alloca: { auto* alloca = static_cast(inst); const char* elem_ty = alloca->GetType()->IsPtrFloat32() ? "float" : "i32"; if (alloca->IsArray()) { os << " " << alloca->GetName() << " = alloca " << elem_ty << ", i32 " << alloca->GetCount() << "\n"; } else { os << " " << alloca->GetName() << " = alloca " << elem_ty << "\n"; } break; } case Opcode::Load: { auto* load = static_cast(inst); os << " " << load->GetName() << " = load " << TypeToString(*load->GetType()) << ", " << TypeToString(*load->GetPtr()->GetType()) << " " << ValueToString(load->GetPtr()) << "\n"; break; } case Opcode::Store: { auto* store = static_cast(inst); os << " store " << TypeToString(*store->GetValue()->GetType()) << " " << ValueToString(store->GetValue()) << ", " << TypeToString(*store->GetPtr()->GetType()) << " " << ValueToString(store->GetPtr()) << "\n"; break; } case Opcode::Br: { auto* br = static_cast(inst); os << " br label %" << br->GetTarget()->GetName() << "\n"; break; } case Opcode::CondBr: { auto* cbr = static_cast(inst); os << " br i1 " << ValueToString(cbr->GetCond()) << ", label %" << cbr->GetTrueBlock()->GetName() << ", label %" << cbr->GetFalseBlock()->GetName() << "\n"; break; } case Opcode::Call: { auto* call = static_cast(inst); if (!call->GetType()->IsVoid()) { os << " " << call->GetName() << " = "; } else { os << " "; } os << "call " << TypeToString(*call->GetType()) << " @" << call->GetCallee()->GetName() << "("; for (size_t i = 0; i < call->GetNumArgs(); ++i) { if (i != 0) { os << ", "; } auto* arg = call->GetArg(i); os << TypeToString(*arg->GetType()) << " " << ValueToString(arg); } os << ")\n"; break; } case Opcode::Gep: { auto* gep = static_cast(inst); auto* base = gep->GetBase(); const char* elem_ty = base->GetType()->IsPtrFloat32() ? "float" : "i32"; // 全局数组用双下标 GEP,局部 alloca 用平坦 GEP。 if (auto* gv = dynamic_cast(base)) { if (gv->IsArray()) { os << " " << gep->GetName() << " = getelementptr [" << gv->GetCount() << " x " << elem_ty << "], [" << gv->GetCount() << " x " << elem_ty << "]* @" << gv->GetName() << ", i32 0, i32 " << ValueToString(gep->GetIndex()) << "\n"; break; } } os << " " << gep->GetName() << " = getelementptr " << elem_ty << ", " << TypeToString(*base->GetType()) << " " << ValueToString(base) << ", i32 " << ValueToString(gep->GetIndex()) << "\n"; break; } case Opcode::Ret: { auto* ret = static_cast(inst); auto* retval = ret->GetValue(); if (!retval) { os << " ret void\n"; } else { os << " ret " << TypeToString(*retval->GetType()) << " " << ValueToString(retval) << "\n"; } break; } } } } os << "}\n"; } } } // namespace ir