You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
25 KiB

#include "mir/MIR.h"
#include <cstring>
#include <stdexcept>
#include <unordered_map>
#include "ir/IR.h"
#include "utils/Log.h"
namespace mir {
namespace {
using ValueSlotMap = std::unordered_map<const ir::Value*, int>;
int AlignTo(int value, int align) {
return ((value + align - 1) / align) * align;
}
bool IsPointerLike(const ir::Type& ty) {
return ty.IsPointer() || ty.IsPtrInt32() || ty.IsPtrFloat();
}
bool IsFloatLike(const ir::Type& ty) { return ty.IsFloat(); }
PhysReg ToXReg(PhysReg reg) {
if ((int)reg >= (int)PhysReg::W0 && (int)reg <= (int)PhysReg::W15) {
return static_cast<PhysReg>((int)reg - (int)PhysReg::W0 + (int)PhysReg::X0);
}
return reg;
}
PhysReg ToSReg(PhysReg reg) {
if ((int)reg >= (int)PhysReg::W0 && (int)reg <= (int)PhysReg::W15) {
return static_cast<PhysReg>((int)reg - (int)PhysReg::W0 + (int)PhysReg::S0);
}
return reg;
}
struct ArgLoc {
bool in_reg = false;
PhysReg reg = PhysReg::W0;
int stack_offset = 0; // bytes from stack-args base
};
ArgLoc GetFunctionArgLoc(const ir::Function& func, size_t arg_no) {
int gpr_idx = 0;
int fpr_idx = 0;
int stack_slots = 0;
const auto& args = func.GetArgs();
for (size_t i = 0; i < args.size(); ++i) {
const auto& ty = *args[i]->GetType();
const bool is_float = IsFloatLike(ty);
const bool is_ptr = IsPointerLike(ty);
ArgLoc loc;
if (is_float && fpr_idx < 8) {
loc.in_reg = true;
loc.reg = static_cast<PhysReg>((int)PhysReg::S0 + fpr_idx);
++fpr_idx;
} else if (!is_float && gpr_idx < 8) {
loc.in_reg = true;
loc.reg = is_ptr ? static_cast<PhysReg>((int)PhysReg::X0 + gpr_idx)
: static_cast<PhysReg>((int)PhysReg::W0 + gpr_idx);
++gpr_idx;
} else {
loc.in_reg = false;
loc.stack_offset = stack_slots * 8;
++stack_slots;
}
if (i == arg_no) return loc;
}
throw std::runtime_error(
FormatError("mir", "函数参数索引越界: " + std::to_string(arg_no)));
}
void EmitValueToReg(const ir::Value* value, PhysReg target,
const ValueSlotMap& slots, MachineBasicBlock& block) {
bool is_ptr = IsPointerLike(*value->GetType());
bool is_float = IsFloatLike(*value->GetType());
if (is_ptr) {
target = ToXReg(target);
} else if (is_float) {
target = ToSReg(target);
}
if (auto* constant = dynamic_cast<const ir::ConstantInt*>(value)) {
block.Append(Opcode::MovImm,
{Operand::Reg(target), Operand::Imm(constant->GetValue())});
return;
}
if (auto* cf = dynamic_cast<const ir::ConstantFloat*>(value)) {
float f = cf->GetValue();
uint32_t bits;
std::memcpy(&bits, &f, 4);
// mov w10, #bits; fmov target, w10
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::W10), Operand::Imm((int)bits)});
block.Append(Opcode::MovRR, {Operand::Reg(target), Operand::Reg(PhysReg::W10)});
return;
}
if (auto* gv = dynamic_cast<const ir::GlobalVariable*>(value)) {
// This loads the VALUE of the global, not its address
block.Append(Opcode::LoadGlobal,
{Operand::Reg(target), Operand::Global(gv->GetName())});
return;
}
if (auto* arg = dynamic_cast<const ir::Argument*>(value)) {
const auto* parent = arg->GetParent();
if (!parent) {
throw std::runtime_error(FormatError("mir", "参数未绑定到函数"));
}
const ArgLoc loc = GetFunctionArgLoc(*parent, arg->GetArgNo());
if (loc.in_reg) {
block.Append(Opcode::MovRR, {Operand::Reg(target), Operand::Reg(loc.reg)});
} else {
// Incoming stack args are at [old_sp + offset]. After prologue:
// x29 = old_sp - 16, so address is [x29 + 16 + offset].
const int fp_offset = 16 + loc.stack_offset;
if (fp_offset <= 4095) {
block.Append(Opcode::AddRRI, {Operand::Reg(PhysReg::X10),
Operand::Reg(PhysReg::X29),
Operand::Imm(fp_offset)});
} else {
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::X11),
Operand::Imm(fp_offset)});
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::X10),
Operand::Reg(PhysReg::X29),
Operand::Reg(PhysReg::X11)});
}
block.Append(Opcode::LoadR, {Operand::Reg(target), Operand::Reg(PhysReg::X10)});
}
return;
}
auto it = slots.find(value);
if (it == slots.end()) {
throw std::runtime_error(
FormatError("mir", "找不到值对应的栈槽: " + value->GetName()));
}
block.Append(Opcode::LoadStack, {Operand::Reg(target), Operand::FrameIndex(it->second)});
}
void EmitAddrToReg(const ir::Value* value, PhysReg target,
const MachineFunction& function,
const ValueSlotMap& slots, MachineBasicBlock& block) {
if (auto* gv = dynamic_cast<const ir::GlobalVariable*>(value)) {
// adrp x10, gv; add x10, x10, :lo12:gv
block.Append(Opcode::MovImm, {Operand::Reg(target), Operand::Global(gv->GetName())}); // Special case for address
return;
}
if (auto* arg = dynamic_cast<const ir::Argument*>(value)) {
// Argument is already an address (pointer)
EmitValueToReg(arg, target, slots, block);
return;
}
auto it = slots.find(value);
if (it != slots.end()) {
// Check if it's an alloca (frame index) or a stored address
// For alloca, we want the address: add x10, x29, #offset
// For stored address, we want to load it: ldr x10, [x29, #offset]
// In our simple lowering, alloca's value in 'slots' is the frame index.
// If 'value' is an AllocaInst, we compute its address.
if (dynamic_cast<const ir::AllocaInst*>(value)) {
block.Append(Opcode::AddrStack, {Operand::Reg(target), Operand::FrameIndex(it->second)});
return;
}
// Otherwise it's a stored address (from a GEP)
block.Append(Opcode::LoadStack, {Operand::Reg(target), Operand::FrameIndex(it->second)});
return;
}
throw std::runtime_error(FormatError("mir", "无法获取地址: " + value->GetName()));
}
size_t GetTypeSize(const ir::Type& ty) {
if (ty.IsInt32() || ty.IsFloat()) return 4;
if (ty.IsPointer() || ty.IsPtrInt32() || ty.IsPtrFloat()) return 8;
if (ty.IsArray()) {
return ty.GetNumElements() * GetTypeSize(*ty.GetElementType());
}
return 0;
}
void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
MachineBasicBlock& block, ValueSlotMap& slots) {
switch (inst.GetOpcode()) {
case ir::Opcode::Alloca: {
auto& alloca = static_cast<const ir::AllocaInst&>(inst);
// AllocaInst's type is PointerType. We want the size of the pointed type.
size_t size = GetTypeSize(*alloca.GetType()->GetPointedType());
slots.emplace(&inst, function.CreateFrameIndex(static_cast<int>(size)));
return;
}
case ir::Opcode::Store: {
auto& store = static_cast<const ir::StoreInst&>(inst);
PhysReg val_reg = PhysReg::W8;
EmitValueToReg(store.GetValue(), val_reg, slots, block);
if (IsPointerLike(*store.GetValue()->GetType())) {
val_reg = ToXReg(val_reg);
} else if (IsFloatLike(*store.GetValue()->GetType())) {
val_reg = ToSReg(val_reg);
}
// If ptr is a global or stored address (GEP result), we use LoadR/StoreR logic
if (auto* gv = dynamic_cast<const ir::GlobalVariable*>(store.GetPtr())) {
block.Append(Opcode::StoreGlobal, {Operand::Reg(val_reg), Operand::Global(gv->GetName())});
} else if (auto* alloca = dynamic_cast<const ir::AllocaInst*>(store.GetPtr())) {
auto it = slots.find(alloca);
if (it == slots.end()) throw std::runtime_error("Alloca not found");
block.Append(Opcode::StoreStack, {Operand::Reg(val_reg), Operand::FrameIndex(it->second)});
} else {
// Pointer is in a register (from GEP)
EmitAddrToReg(store.GetPtr(), PhysReg::X10, function, slots, block);
block.Append(Opcode::StoreR, {Operand::Reg(val_reg), Operand::Reg(PhysReg::X10)});
}
return;
}
case ir::Opcode::Load: {
auto& load = static_cast<const ir::LoadInst&>(inst);
int dst_slot = function.CreateFrameIndex(static_cast<int>(GetTypeSize(*load.GetType())));
PhysReg dst_reg = PhysReg::W8;
if (IsPointerLike(*load.GetType())) {
dst_reg = ToXReg(dst_reg);
} else if (IsFloatLike(*load.GetType())) {
dst_reg = ToSReg(dst_reg);
}
if (auto* gv = dynamic_cast<const ir::GlobalVariable*>(load.GetPtr())) {
block.Append(Opcode::LoadGlobal, {Operand::Reg(dst_reg), Operand::Global(gv->GetName())});
} else if (auto* alloca = dynamic_cast<const ir::AllocaInst*>(load.GetPtr())) {
auto it = slots.find(alloca);
if (it == slots.end()) throw std::runtime_error("Alloca not found");
block.Append(Opcode::LoadStack, {Operand::Reg(dst_reg), Operand::FrameIndex(it->second)});
} else {
// Pointer is in a register (from GEP)
EmitAddrToReg(load.GetPtr(), PhysReg::X10, function, slots, block);
block.Append(Opcode::LoadR, {Operand::Reg(dst_reg), Operand::Reg(PhysReg::X10)});
}
block.Append(Opcode::StoreStack, {Operand::Reg(dst_reg), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::GEP: {
auto& gep = static_cast<const ir::GEPInst&>(inst);
int dst_slot = function.CreateFrameIndex(8); // Address is 8 bytes
EmitAddrToReg(gep.GetPtr(), PhysReg::X10, function, slots, block);
// Initial type is the pointed type of the base pointer
std::shared_ptr<ir::Type> cur_ty = gep.GetPtr()->GetType()->GetPointedType();
for (size_t i = 0; i < gep.GetIndices().size(); ++i) {
ir::Value* index_val = gep.GetIndices()[i];
// Skip index 0 if it's the first index and we're starting from a pointer
if (i == 0) {
if (auto* ci = dynamic_cast<ir::ConstantInt*>(index_val)) {
if (ci->GetValue() == 0) {
continue;
}
}
EmitValueToReg(index_val, PhysReg::W8, slots, block);
size_t element_size = GetTypeSize(*cur_ty);
// Use X8 for 64-bit multiplication if element_size is large,
// but for simple cases we can use AddRRR_LSL with W8 for auto sxtw
if (element_size == 4) {
block.Append(Opcode::AddRRR_LSL, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::W8), Operand::Imm(2)});
} else if (element_size == 8) {
block.Append(Opcode::AddRRR_LSL, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::W8), Operand::Imm(3)});
} else {
block.Append(Opcode::Sxtw, {Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::W8)});
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::X9), Operand::Imm(static_cast<int>(element_size))});
block.Append(Opcode::MulRR, {Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X9)});
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X8)});
}
continue;
}
if (cur_ty->IsArray()) {
size_t element_size = GetTypeSize(*cur_ty->GetElementType());
EmitValueToReg(index_val, PhysReg::W8, slots, block);
if (element_size == 4) {
block.Append(Opcode::AddRRR_LSL, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::W8), Operand::Imm(2)});
} else if (element_size == 8) {
block.Append(Opcode::AddRRR_LSL, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::W8), Operand::Imm(3)});
} else {
block.Append(Opcode::Sxtw, {Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::W8)});
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::X9), Operand::Imm(static_cast<int>(element_size))});
block.Append(Opcode::MulRR, {Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X9)});
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::X8)});
}
cur_ty = cur_ty->GetElementType();
} else {
throw std::runtime_error(FormatError("mir", "GEP 索引超出范围或类型不是数组"));
}
}
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::X10), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::Call: {
auto& call = static_cast<const ir::CallInst&>(inst);
const auto& args = call.GetArgs();
std::vector<ArgLoc> arg_locs(args.size());
int gpr_idx = 0;
int fpr_idx = 0;
int stack_slots = 0;
for (size_t i = 0; i < args.size(); ++i) {
const auto& ty = *args[i]->GetType();
const bool is_float = IsFloatLike(ty);
const bool is_ptr = IsPointerLike(ty);
if (is_float && fpr_idx < 8) {
arg_locs[i] = ArgLoc{true, static_cast<PhysReg>((int)PhysReg::S0 + fpr_idx), 0};
++fpr_idx;
} else if (!is_float && gpr_idx < 8) {
arg_locs[i] = ArgLoc{
true,
is_ptr ? static_cast<PhysReg>((int)PhysReg::X0 + gpr_idx)
: static_cast<PhysReg>((int)PhysReg::W0 + gpr_idx),
0};
++gpr_idx;
} else {
arg_locs[i] = ArgLoc{false, PhysReg::W0, stack_slots * 8};
++stack_slots;
}
}
int stack_arg_size = 0;
if (stack_slots > 0) {
stack_arg_size = AlignTo(stack_slots * 8, 16);
block.Append(Opcode::MovImm,
{Operand::Reg(PhysReg::X11), Operand::Imm(stack_arg_size)});
block.Append(Opcode::SubRR, {Operand::Reg(PhysReg::SP),
Operand::Reg(PhysReg::SP),
Operand::Reg(PhysReg::X11)});
}
for (size_t i = 0; i < args.size(); ++i) {
const ArgLoc& loc = arg_locs[i];
if (loc.in_reg) {
EmitValueToReg(args[i], loc.reg, slots, block);
continue;
}
PhysReg val_reg = PhysReg::W8;
if (IsPointerLike(*args[i]->GetType())) {
val_reg = ToXReg(val_reg);
} else if (IsFloatLike(*args[i]->GetType())) {
val_reg = ToSReg(val_reg);
}
EmitValueToReg(args[i], val_reg, slots, block);
if (loc.stack_offset == 0) {
block.Append(Opcode::MovRR,
{Operand::Reg(PhysReg::X10), Operand::Reg(PhysReg::SP)});
} else if (loc.stack_offset <= 4095) {
block.Append(Opcode::AddRRI, {Operand::Reg(PhysReg::X10),
Operand::Reg(PhysReg::SP),
Operand::Imm(loc.stack_offset)});
} else {
block.Append(Opcode::MovImm,
{Operand::Reg(PhysReg::X11), Operand::Imm(loc.stack_offset)});
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::X10),
Operand::Reg(PhysReg::SP),
Operand::Reg(PhysReg::X11)});
}
block.Append(Opcode::StoreR,
{Operand::Reg(val_reg), Operand::Reg(PhysReg::X10)});
}
block.Append(Opcode::Call, {Operand::Label(call.GetFunc()->GetName())});
if (stack_arg_size > 0) {
block.Append(Opcode::MovImm,
{Operand::Reg(PhysReg::X11), Operand::Imm(stack_arg_size)});
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::SP),
Operand::Reg(PhysReg::SP),
Operand::Reg(PhysReg::X11)});
}
if (!call.GetType()->IsVoid()) {
int dst_slot = function.CreateFrameIndex(static_cast<int>(GetTypeSize(*call.GetType())));
PhysReg ret_reg = PhysReg::W0;
if (IsFloatLike(*call.GetType())) {
ret_reg = ToSReg(ret_reg);
} else if (IsPointerLike(*call.GetType())) {
ret_reg = ToXReg(ret_reg);
}
block.Append(Opcode::StoreStack, {Operand::Reg(ret_reg), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
}
return;
}
case ir::Opcode::Add:
case ir::Opcode::Sub:
case ir::Opcode::Mul:
case ir::Opcode::Div:
case ir::Opcode::Mod: {
auto& bin = static_cast<const ir::BinaryInst&>(inst);
int dst_slot = function.CreateFrameIndex();
if (bin.GetType()->IsFloat()) {
PhysReg lhs_reg = PhysReg::W8;
PhysReg rhs_reg = PhysReg::W9;
EmitValueToReg(bin.GetLhs(), lhs_reg, slots, block);
EmitValueToReg(bin.GetRhs(), rhs_reg, slots, block);
lhs_reg = ToSReg(lhs_reg);
rhs_reg = ToSReg(rhs_reg);
Opcode op;
if (inst.GetOpcode() == ir::Opcode::Add) op = Opcode::FAdd;
else if (inst.GetOpcode() == ir::Opcode::Sub) op = Opcode::FSub;
else if (inst.GetOpcode() == ir::Opcode::Mul) op = Opcode::FMUL;
else if (inst.GetOpcode() == ir::Opcode::Div) op = Opcode::FDiv;
else throw std::runtime_error("Float mod not supported");
block.Append(op, {Operand::Reg(PhysReg::S0), Operand::Reg(lhs_reg), Operand::Reg(rhs_reg)});
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::S0), Operand::FrameIndex(dst_slot)});
} else {
EmitValueToReg(bin.GetLhs(), PhysReg::W8, slots, block);
EmitValueToReg(bin.GetRhs(), PhysReg::W9, slots, block);
if (inst.GetOpcode() == ir::Opcode::Add) {
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
} else if (inst.GetOpcode() == ir::Opcode::Sub) {
block.Append(Opcode::SubRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
} else if (inst.GetOpcode() == ir::Opcode::Mul) {
block.Append(Opcode::MulRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
} else if (inst.GetOpcode() == ir::Opcode::Div) {
block.Append(Opcode::SDivRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
} else if (inst.GetOpcode() == ir::Opcode::Mod) {
// srem w10, w8, w9 => sdiv w10, w8, w9; msub w8, w10, w9, w8
block.Append(Opcode::SDivRR, {Operand::Reg(PhysReg::W10), Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
block.Append(Opcode::MSubRRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W10), Operand::Reg(PhysReg::W9), Operand::Reg(PhysReg::W8)});
}
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
}
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::SIToFP: {
auto& fcvt = static_cast<const ir::UnaryInst&>(inst);
int dst_slot = function.CreateFrameIndex();
EmitValueToReg(fcvt.GetUnaryOperand(), PhysReg::W8, slots, block);
block.Append(Opcode::FCvtSI2FP, {Operand::Reg(PhysReg::S0), Operand::Reg(PhysReg::W8)});
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::S0), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::FPToSI: {
auto& fcvt = static_cast<const ir::UnaryInst&>(inst);
int dst_slot = function.CreateFrameIndex();
EmitValueToReg(fcvt.GetUnaryOperand(), PhysReg::W8, slots, block);
block.Append(Opcode::FCvtFP2SI, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::S8)});
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::Cmp:
case ir::Opcode::FCmp: {
int dst_slot = function.CreateFrameIndex();
ir::CmpOp ir_cc;
if (inst.GetOpcode() == ir::Opcode::Cmp) {
auto& cmp = static_cast<const ir::CmpInst&>(inst);
EmitValueToReg(cmp.GetLhs(), PhysReg::W8, slots, block);
EmitValueToReg(cmp.GetRhs(), PhysReg::W9, slots, block);
block.Append(Opcode::CmpRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9)});
ir_cc = cmp.GetCmpOp();
} else {
auto& cmp = static_cast<const ir::FCmpInst&>(inst);
EmitValueToReg(cmp.GetLhs(), PhysReg::W8, slots, block);
EmitValueToReg(cmp.GetRhs(), PhysReg::W9, slots, block);
block.Append(Opcode::FCmp, {Operand::Reg(PhysReg::S8), Operand::Reg(PhysReg::S9)});
ir_cc = cmp.GetCmpOp();
}
CondCode cc = CondCode::EQ;
switch (ir_cc) {
case ir::CmpOp::Eq: cc = CondCode::EQ; break;
case ir::CmpOp::Ne: cc = CondCode::NE; break;
case ir::CmpOp::Lt: cc = CondCode::LT; break;
case ir::CmpOp::Le: cc = CondCode::LE; break;
case ir::CmpOp::Gt: cc = CondCode::GT; break;
case ir::CmpOp::Ge: cc = CondCode::GE; break;
}
block.Append(Opcode::CSet, {Operand::Reg(PhysReg::W8), Operand::Cond(cc)});
block.Append(Opcode::StoreStack,
{Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::Zext: {
auto& zext = static_cast<const ir::ZextInst&>(inst);
int dst_slot = function.CreateFrameIndex();
EmitValueToReg(zext.GetValue(), PhysReg::W8, slots, block);
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::Neg: {
auto& unary = static_cast<const ir::UnaryInst&>(inst);
int dst_slot = function.CreateFrameIndex();
if (unary.GetType()->IsFloat()) {
EmitValueToReg(unary.GetUnaryOperand(), PhysReg::W8, slots, block);
block.Append(Opcode::FNeg, {Operand::Reg(PhysReg::S0), Operand::Reg(PhysReg::S8)});
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::S0), Operand::FrameIndex(dst_slot)});
} else {
EmitValueToReg(unary.GetUnaryOperand(), PhysReg::W8, slots, block);
block.Append(Opcode::NegR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8)});
block.Append(Opcode::StoreStack,
{Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
}
slots.emplace(&inst, dst_slot);
return;
}
case ir::Opcode::Br: {
auto& br = static_cast<const ir::BranchInst&>(inst);
block.Append(Opcode::B, {Operand::Label(br.GetDest()->GetName())});
return;
}
case ir::Opcode::CondBr: {
auto& cbr = static_cast<const ir::CondBranchInst&>(inst);
EmitValueToReg(cbr.GetCond(), PhysReg::W8, slots, block);
// SysY IR CondBr uses i1. In MIR, we compare with 0.
block.Append(Opcode::BCond, {Operand::Cond(CondCode::NE),
Operand::Reg(PhysReg::W8),
Operand::Label(cbr.GetTrueBlock()->GetName())});
block.Append(Opcode::B, {Operand::Label(cbr.GetFalseBlock()->GetName())});
return;
}
case ir::Opcode::Ret: {
auto& ret = static_cast<const ir::ReturnInst&>(inst);
if (auto* val = ret.GetValue()) {
EmitValueToReg(val, PhysReg::W0, slots, block);
}
block.Append(Opcode::Ret);
return;
}
default:
throw std::runtime_error(FormatError("mir", "暂不支持该 IR 指令: " + std::to_string((int)inst.GetOpcode())));
}
}
} // namespace
std::unique_ptr<MachineModule> LowerToMIR(const ir::Module& module) {
DefaultContext();
auto machine_module = std::make_unique<MachineModule>();
// Lower global variables
for (const auto& gv : module.GetGlobalVariables()) {
GlobalVariable mir_gv;
mir_gv.name = gv->GetName();
mir_gv.size = GetTypeSize(*gv->GetType()->GetPointedType());
if (auto* init = gv->GetInitializer()) {
if (auto* ci = dynamic_cast<ir::ConstantInt*>(init)) {
mir_gv.init_value = ci->GetValue();
} else if (auto* cf = dynamic_cast<ir::ConstantFloat*>(init)) {
float f = cf->GetValue();
uint32_t bits;
std::memcpy(&bits, &f, 4);
mir_gv.init_value = static_cast<int>(bits);
}
}
machine_module->GetGlobals().push_back(mir_gv);
}
// Lower functions
for (const auto& ir_func : module.GetFunctions()) {
if (ir_func->GetBlocks().empty()) continue; // Skip declarations
auto machine_func = std::make_unique<MachineFunction>(ir_func->GetName());
ValueSlotMap slots;
// Create all blocks first to handle forward references in branches
std::unordered_map<const ir::BasicBlock*, MachineBasicBlock*> block_map;
for (const auto& ir_bb : ir_func->GetBlocks()) {
block_map[ir_bb.get()] = &machine_func->CreateBlock(ir_bb->GetName());
}
// Lower instructions in each block
for (const auto& ir_bb : ir_func->GetBlocks()) {
auto& machine_bb = *block_map.at(ir_bb.get());
for (const auto& inst : ir_bb->GetInstructions()) {
LowerInstruction(*inst, *machine_func, machine_bb, slots);
}
}
machine_module->GetFunctions().push_back(std::move(machine_func));
}
return machine_module;
}
} // namespace mir