|
|
// IR 文本输出:
|
|
|
// - 将 IR 打印为 .ll 风格的文本
|
|
|
// - 支撑调试与测试对比(diff)
|
|
|
|
|
|
#include "ir/IR.h"
|
|
|
|
|
|
#include <cstdint>
|
|
|
#include <cstring>
|
|
|
#include <ostream>
|
|
|
#include <stdexcept>
|
|
|
#include <string>
|
|
|
|
|
|
#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<const ConstantInt*>(v)) {
|
|
|
return std::to_string(ci->GetValue());
|
|
|
}
|
|
|
if (auto* gv = dynamic_cast<const GlobalVariable*>(v)) {
|
|
|
return "@" + gv->GetName();
|
|
|
}
|
|
|
if (auto* func = dynamic_cast<const Function*>(v)) {
|
|
|
return "@" + func->GetName();
|
|
|
}
|
|
|
if (auto* arg = dynamic_cast<const Argument*>(v)) {
|
|
|
return arg->GetName();
|
|
|
}
|
|
|
return v ? v->GetName() : "<null>";
|
|
|
}
|
|
|
|
|
|
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<std::int32_t>(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<const BinaryInst*>(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<const CmpInst*>(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<const CastInst*>(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<const AllocaInst*>(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<const LoadInst*>(inst);
|
|
|
os << " " << load->GetName() << " = load "
|
|
|
<< TypeToString(*load->GetType()) << ", "
|
|
|
<< TypeToString(*load->GetPtr()->GetType()) << " "
|
|
|
<< ValueToString(load->GetPtr()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Store: {
|
|
|
auto* store = static_cast<const StoreInst*>(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<const BranchInst*>(inst);
|
|
|
os << " br label %" << br->GetTarget()->GetName() << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::CondBr: {
|
|
|
auto* cbr = static_cast<const CondBranchInst*>(inst);
|
|
|
os << " br i1 " << ValueToString(cbr->GetCond()) << ", label %"
|
|
|
<< cbr->GetTrueBlock()->GetName() << ", label %"
|
|
|
<< cbr->GetFalseBlock()->GetName() << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Call: {
|
|
|
auto* call = static_cast<const CallInst*>(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<const GepInst*>(inst);
|
|
|
auto* base = gep->GetBase();
|
|
|
const char* elem_ty = base->GetType()->IsPtrFloat32() ? "float" : "i32";
|
|
|
// 全局数组用双下标 GEP,局部 alloca 用平坦 GEP。
|
|
|
if (auto* gv = dynamic_cast<const GlobalVariable*>(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<const ReturnInst*>(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
|