|
|
// IR 文本输出:
|
|
|
// - 将 IR 打印为 .ll 风格的文本
|
|
|
// - 支撑调试与测试对比(diff)
|
|
|
|
|
|
#include "ir/IR.h"
|
|
|
|
|
|
#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::Int1:
|
|
|
return "i1";
|
|
|
case Type::Kind::Int32:
|
|
|
return "i32";
|
|
|
case Type::Kind::PtrInt32:
|
|
|
return "i32*";
|
|
|
}
|
|
|
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::Neg:
|
|
|
return "neg";
|
|
|
case Opcode::Alloca:
|
|
|
return "alloca";
|
|
|
case Opcode::Load:
|
|
|
return "load";
|
|
|
case Opcode::Store:
|
|
|
return "store";
|
|
|
case Opcode::Ret:
|
|
|
return "ret";
|
|
|
case Opcode::Cmp:
|
|
|
return "icmp";
|
|
|
case Opcode::Zext:
|
|
|
return "zext";
|
|
|
case Opcode::Br:
|
|
|
case Opcode::CondBr:
|
|
|
return "br";
|
|
|
}
|
|
|
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::Gt:
|
|
|
return "sgt";
|
|
|
case CmpOp::Le:
|
|
|
return "sle";
|
|
|
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());
|
|
|
}
|
|
|
return v ? v->GetName() : "<null>";
|
|
|
}
|
|
|
|
|
|
static std::string PrintLabel(const Value* bb) {
|
|
|
if (!bb) return "<null>";
|
|
|
std::string name = bb->GetName();
|
|
|
if (name.empty()) return "<empty>";
|
|
|
if (name[0] == '%') return name;
|
|
|
return "%" + name;
|
|
|
}
|
|
|
|
|
|
static std::string PrintLabelDef(const Value* bb) {
|
|
|
if (!bb) return "<null>";
|
|
|
std::string name = bb->GetName();
|
|
|
if (!name.empty() && name[0] == '%') return name.substr(1);
|
|
|
return name;
|
|
|
}
|
|
|
|
|
|
void IRPrinter::Print(const Module& module, std::ostream& os) {
|
|
|
for (const auto& func : module.GetFunctions()) {
|
|
|
os << "define " << TypeToString(*func->GetType()) << " @" << func->GetName()
|
|
|
<< "() {\n";
|
|
|
for (const auto& bb : func->GetBlocks()) {
|
|
|
if (!bb) {
|
|
|
continue;
|
|
|
}
|
|
|
os << PrintLabelDef(bb.get()) << ":\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::Neg: {
|
|
|
auto* unary = static_cast<const UnaryInst*>(inst);
|
|
|
os << " " << unary->GetName() << " = "
|
|
|
<< OpcodeToString(unary->GetOpcode()) << " "
|
|
|
<< TypeToString(*unary->GetUnaryOperand()->GetType()) << " "
|
|
|
<< ValueToString(unary->GetUnaryOperand()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Alloca: {
|
|
|
auto* alloca = static_cast<const AllocaInst*>(inst);
|
|
|
os << " " << alloca->GetName() << " = alloca i32\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Load: {
|
|
|
auto* load = static_cast<const LoadInst*>(inst);
|
|
|
os << " " << load->GetName() << " = load i32, i32* "
|
|
|
<< ValueToString(load->GetPtr()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Store: {
|
|
|
auto* store = static_cast<const StoreInst*>(inst);
|
|
|
os << " store i32 " << ValueToString(store->GetValue())
|
|
|
<< ", i32* " << ValueToString(store->GetPtr()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Ret: {
|
|
|
auto* ret = static_cast<const ReturnInst*>(inst);
|
|
|
os << " ret " << TypeToString(*ret->GetValue()->GetType()) << " "
|
|
|
<< ValueToString(ret->GetValue()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Cmp: {
|
|
|
auto* cmp = static_cast<const CmpInst*>(inst);
|
|
|
os << " " << cmp->GetName() << " = icmp "
|
|
|
<< CmpOpToString(cmp->GetCmpOp()) << " "
|
|
|
<< TypeToString(*cmp->GetLhs()->GetType()) << " "
|
|
|
<< ValueToString(cmp->GetLhs()) << ", "
|
|
|
<< ValueToString(cmp->GetRhs()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Zext: {
|
|
|
auto* zext = static_cast<const ZextInst*>(inst);
|
|
|
os << " " << zext->GetName() << " = zext "
|
|
|
<< TypeToString(*zext->GetOperand(0)->GetType()) << " "
|
|
|
<< ValueToString(zext->GetOperand(0)) << " to "
|
|
|
<< TypeToString(*zext->GetType()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::Br: {
|
|
|
auto* br = static_cast<const BranchInst*>(inst);
|
|
|
os << " br label " << PrintLabel(br->GetDest()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
case Opcode::CondBr: {
|
|
|
auto* cbr = static_cast<const CondBranchInst*>(inst);
|
|
|
os << " br i1 " << ValueToString(cbr->GetCond())
|
|
|
<< ", label " << PrintLabel(cbr->GetTrueBlock())
|
|
|
<< ", label " << PrintLabel(cbr->GetFalseBlock()) << "\n";
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
os << "}\n";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} // namespace ir
|