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.
nudt-compiler-cpp/src/mir/Lowering.cpp

2084 lines
76 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "mir/MIR.h"
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <unordered_map>
#include "ir/IR.h"
#include "utils/Log.h"
namespace mir
{
namespace
{
using ValueVRegMap = std::unordered_map<const ir::Value *, int>;
using LocalScalarMap = std::unordered_map<const ir::Value *, int>;
using LocalArrayMap = std::unordered_map<const ir::Value *, int>;
using BlockMap = std::unordered_map<const ir::BasicBlock *, MachineBasicBlock *>;
static bool TryGetConstantInt(const ir::Value *value, int &out);
static int GetTypeSize(const std::shared_ptr<ir::Type> &type)
{
if (!type)
return 4;
if (type->IsPtrInt32() || type->IsPtrFloat32())
return 8;
return 4;
}
static int AlignTo(int value, int align)
{
return ((value + align - 1) / align) * align;
}
static bool IsPointerValue(const ir::Value *value)
{
if (!value)
return false;
auto type = value->GetType();
return type && (type->IsPtrInt32() || type->IsPtrFloat32());
}
static bool IsPointerType(const std::shared_ptr<ir::Type> &type)
{
return type && (type->IsPtrInt32() || type->IsPtrFloat32());
}
static bool IsFloatType(const std::shared_ptr<ir::Type> &type)
{
return type && type->IsFloat32();
}
static bool IsFloatValue(const ir::Value *value)
{
return value && IsFloatType(value->GetType());
}
static bool IsIntegerCompareOpcode(ir::Opcode opcode)
{
switch (opcode)
{
case ir::Opcode::Eq:
case ir::Opcode::Ne:
case ir::Opcode::Lt:
case ir::Opcode::Le:
case ir::Opcode::Gt:
case ir::Opcode::Ge:
return true;
default:
return false;
}
}
static CondCode GetCondCodeForCompareOpcode(ir::Opcode opcode)
{
switch (opcode)
{
case ir::Opcode::Eq:
return CondCode::EQ;
case ir::Opcode::Ne:
return CondCode::NE;
case ir::Opcode::Lt:
return CondCode::LT;
case ir::Opcode::Le:
return CondCode::LE;
case ir::Opcode::Gt:
return CondCode::GT;
case ir::Opcode::Ge:
return CondCode::GE;
default:
throw std::runtime_error(FormatError("mir", "不支持的比较 opcode"));
}
}
static CondCode NegateCondCode(CondCode cond)
{
switch (cond)
{
case CondCode::EQ:
return CondCode::NE;
case CondCode::NE:
return CondCode::EQ;
case CondCode::LT:
return CondCode::GE;
case CondCode::LE:
return CondCode::GT;
case CondCode::GT:
return CondCode::LE;
case CondCode::GE:
return CondCode::LT;
default:
return CondCode::NE;
}
}
static PhysReg GetArgWReg(size_t index)
{
static const PhysReg regs[] = {
PhysReg::W0, PhysReg::W1, PhysReg::W2, PhysReg::W3,
PhysReg::W4, PhysReg::W5, PhysReg::W6, PhysReg::W7};
return index < 8 ? regs[index] : PhysReg::W0;
}
static PhysReg GetArgXReg(size_t index)
{
static const PhysReg regs[] = {
PhysReg::X0, PhysReg::X1, PhysReg::X2, PhysReg::X3,
PhysReg::X4, PhysReg::X5, PhysReg::X6, PhysReg::X7};
return index < 8 ? regs[index] : PhysReg::X0;
}
static PhysReg GetArgSReg(size_t index)
{
static const PhysReg regs[] = {
PhysReg::S0, PhysReg::S1, PhysReg::S2, PhysReg::S3,
PhysReg::S4, PhysReg::S5, PhysReg::S6, PhysReg::S7};
return index < 8 ? regs[index] : PhysReg::S0;
}
static bool TryGetConstantInt(const ir::Value *value, int &out)
{
if (auto *constant = dynamic_cast<const ir::ConstantInt *>(value))
{
out = constant->GetValue();
return true;
}
return false;
}
static int FloatToBits(float value)
{
int bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
return bits;
}
static bool TryGetConstantFloatBits(const ir::Value *value, int &out)
{
if (auto *constant = dynamic_cast<const ir::ConstantFloat *>(value))
{
out = FloatToBits(static_cast<float>(constant->GetValue()));
return true;
}
return false;
}
static const ir::GlobalVariable *AsGlobalScalarObject(const ir::Value *value)
{
auto *global = dynamic_cast<const ir::GlobalVariable *>(value);
if (!global)
return nullptr;
if (global->IsArray())
return nullptr;
if (!IsPointerValue(global))
return nullptr;
return global;
}
static const ir::GlobalVariable *AsGlobalArrayObject(const ir::Value *value)
{
auto *global = dynamic_cast<const ir::GlobalVariable *>(value);
if (!global)
return nullptr;
if (!global->IsArray())
return nullptr;
if (!IsPointerValue(global))
return nullptr;
return global;
}
static bool IsZeroIntConstant(const ir::Value *value)
{
int imm = 0;
return TryGetConstantInt(value, imm) && imm == 0;
}
[[maybe_unused]] static bool IsSolelyConsumedByCondBr(const ir::Instruction &inst)
{
const auto &uses = inst.GetUses();
if (uses.size() != 1)
return false;
auto *user = uses.front().GetUser();
return dynamic_cast<const ir::CondBranchInst *>(user) != nullptr;
}
static bool IsSolelyConsumedByCanonicalBoolUse(const ir::Instruction &inst)
{
const auto &uses = inst.GetUses();
if (uses.size() != 1)
return false;
auto *user_inst = dynamic_cast<const ir::Instruction *>(uses.front().GetUser());
if (!user_inst)
return false;
if (dynamic_cast<const ir::CondBranchInst *>(user_inst))
return true;
if (auto *cast = dynamic_cast<const ir::CastInst *>(user_inst))
return cast->GetOpcode() == ir::Opcode::ZExt;
auto *bin = dynamic_cast<const ir::BinaryInst *>(user_inst);
if (!bin)
return false;
if (bin->GetOpcode() != ir::Opcode::Eq && bin->GetOpcode() != ir::Opcode::Ne)
return false;
return (bin->GetLhs() == &inst && IsZeroIntConstant(bin->GetRhs())) ||
(bin->GetRhs() == &inst && IsZeroIntConstant(bin->GetLhs()));
}
static bool TryResolveDirectScalarSlot(const ir::Value *ptr,
const LocalScalarMap &scalar_slots,
int &out_slot)
{
auto it = scalar_slots.find(ptr);
if (it != scalar_slots.end())
{
out_slot = it->second;
return true;
}
auto *gep = dynamic_cast<const ir::GetElementPtrInst *>(ptr);
if (!gep)
return false;
int idx = 0;
if (!TryGetConstantInt(gep->GetIndex(), idx))
return false;
if (idx != 0)
return false;
auto base_it = scalar_slots.find(gep->GetBasePtr());
if (base_it != scalar_slots.end())
{
out_slot = base_it->second;
return true;
}
return false;
}
static int EmitIntValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots, MachineBasicBlock &block);
static int EmitFloatValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, MachineBasicBlock &block);
static int EmitPtrValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots, MachineBasicBlock &block);
static int EmitIntValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots, MachineBasicBlock &block)
{
if (!value)
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
return vreg;
}
auto it = value_vregs.find(value);
if (it != value_vregs.end())
{
if (function.GetVRegClass(it->second) == VRegClass::Float)
{
int dst = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::FCvtzs,
{Operand::VReg(dst, VRegClass::Int), Operand::VReg(it->second, VRegClass::Float)});
return dst;
}
return it->second;
}
int imm = 0;
if (TryGetConstantInt(value, imm))
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Int), Operand::Imm(imm)}).SetRematerializable(true).SetRematImm(imm);
value_vregs[value] = vreg;
return vreg;
}
if (TryGetConstantFloatBits(value, imm))
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Int), Operand::Imm(imm)}).SetRematerializable(true).SetRematImm(imm);
value_vregs[value] = vreg;
return vreg;
}
if (auto *global = AsGlobalScalarObject(value))
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadGlobal,
{Operand::VReg(vreg, VRegClass::Int), Operand::Symbol(global->GetName())});
value_vregs[value] = vreg;
return vreg;
}
if (auto *cast = dynamic_cast<const ir::CastInst *>(value))
{
if (cast->GetOpcode() == ir::Opcode::ZExt)
{
int src = EmitIntValue(cast->GetOperandValue(), function, value_vregs,
scalar_slots, array_slots, block);
value_vregs[value] = src;
return src;
}
if (cast->GetOpcode() == ir::Opcode::FPToSI)
{
int src = EmitFloatValue(cast->GetOperandValue(), function, value_vregs, block);
int dst = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::FCvtzs,
{Operand::VReg(dst, VRegClass::Int), Operand::VReg(src, VRegClass::Float)});
value_vregs[value] = dst;
return dst;
}
}
if (auto *bin = dynamic_cast<const ir::BinaryInst *>(value))
{
if (IsIntegerCompareOpcode(bin->GetOpcode()))
{
int lhs = EmitIntValue(bin->GetLhs(), function, value_vregs,
scalar_slots, array_slots, block);
int rhs = EmitIntValue(bin->GetRhs(), function, value_vregs,
scalar_slots, array_slots, block);
// 立即数折叠:常量 rhs 直接用 CmpImm
// 常量 lhs 则交换并翻转条件
int cmp_imm = 0;
CondCode cond = GetCondCodeForCompareOpcode(bin->GetOpcode());
if (TryGetConstantInt(bin->GetRhs(), cmp_imm) && static_cast<unsigned int>(cmp_imm) <= 4095)
{
block.Append(Opcode::CmpImm,
{Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(cmp_imm)});
}
else if (TryGetConstantInt(bin->GetLhs(), cmp_imm) && static_cast<unsigned int>(cmp_imm) <= 4095)
{
// 交换cmp rhs, #lhs 并翻转条件
block.Append(Opcode::CmpImm,
{Operand::VReg(rhs, VRegClass::Int),
Operand::Imm(cmp_imm)});
switch (cond)
{
case CondCode::LT: cond = CondCode::GT; break;
case CondCode::LE: cond = CondCode::GE; break;
case CondCode::GT: cond = CondCode::LT; break;
case CondCode::GE: cond = CondCode::LE; break;
default: break;
}
}
else
{
block.Append(Opcode::CmpRR,
{Operand::VReg(lhs, VRegClass::Int), Operand::VReg(rhs, VRegClass::Int)});
}
int dst = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::CSet,
{Operand::VReg(dst, VRegClass::Int),
Operand::Imm(static_cast<int>(cond))});
value_vregs[value] = dst;
return dst;
}
if (IsFloatType(bin->GetType()))
{
return EmitFloatValue(value, function, value_vregs, block);
}
Opcode opcode = Opcode::AddRR;
switch (bin->GetOpcode())
{
case ir::Opcode::Add:
opcode = Opcode::AddRR;
break;
case ir::Opcode::Sub:
opcode = Opcode::SubRR;
break;
case ir::Opcode::Mul:
opcode = Opcode::MulRR;
break;
case ir::Opcode::Div:
opcode = Opcode::DivRR;
break;
case ir::Opcode::Mod:
opcode = Opcode::ModRR;
break;
default:
break;
}
int dst = function.CreateVReg(VRegClass::Int);
// Madd 折叠sum + (a * b) → madd sum, a, b, sum必须在 EmitIntValue 之前)
if (opcode == Opcode::AddRR)
{
auto *mul_rhs = dynamic_cast<const ir::BinaryInst *>(bin->GetRhs());
auto *mul_lhs = dynamic_cast<const ir::BinaryInst *>(bin->GetLhs());
const ir::BinaryInst *mul_op = nullptr;
bool mul_is_rhs = true;
if (mul_rhs && mul_rhs->GetOpcode() == ir::Opcode::Mul)
mul_op = mul_rhs;
else if (mul_lhs && mul_lhs->GetOpcode() == ir::Opcode::Mul)
{ mul_op = mul_lhs; mul_is_rhs = false; }
if (mul_op)
{
// 仅当 Mul 两端都不是常量时才折叠(常量有 shift/强度削减更优)
int dummy;
if (!TryGetConstantInt(mul_op->GetRhs(), dummy) &&
!TryGetConstantInt(mul_op->GetLhs(), dummy))
{
int a = EmitIntValue(mul_op->GetLhs(), function, value_vregs,
scalar_slots, array_slots, block);
int b = EmitIntValue(mul_op->GetRhs(), function, value_vregs,
scalar_slots, array_slots, block);
int acc = EmitIntValue(mul_is_rhs ? bin->GetLhs() : bin->GetRhs(),
function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::Madd,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(a, VRegClass::Int),
Operand::VReg(b, VRegClass::Int),
Operand::VReg(acc, VRegClass::Int)});
value_vregs[value] = dst;
value_vregs[mul_op] = dst;
return dst;
}
}
}
int lhs = EmitIntValue(bin->GetLhs(), function, value_vregs,
scalar_slots, array_slots, block);
int rhs = EmitIntValue(bin->GetRhs(), function, value_vregs,
scalar_slots, array_slots, block);
if (opcode == Opcode::MulRR)
{
auto *rhs_const = dynamic_cast<const ir::ConstantInt *>(bin->GetRhs());
if (rhs_const)
{
int val = rhs_const->GetValue();
if (val > 0 && (val & (val - 1)) == 0)
{
int shift = 0;
while (val > 1)
{
val >>= 1;
++shift;
}
block.Append(Opcode::ShlRR,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(shift)});
value_vregs[value] = dst;
return dst;
}
}
}
if (opcode == Opcode::DivRR)
{
auto *rhs_const = dynamic_cast<const ir::ConstantInt *>(bin->GetRhs());
if (rhs_const)
{
int val = rhs_const->GetValue();
if (val == 1)
{
value_vregs[value] = lhs;
return lhs;
}
if (val == -1)
{
block.Append(Opcode::NegRR,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int)});
value_vregs[value] = dst;
return dst;
}
if (val > 0 && (val & (val - 1)) == 0)
{
int shift = 0;
int tmp = val;
while (tmp > 1)
{
tmp >>= 1;
++shift;
}
int bias = (1 << shift) - 1;
int biased = function.CreateVReg(VRegClass::Int);
if (bias <= 4095)
{
block.Append(Opcode::AddRR,
{Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(bias)});
}
else
{
int bias_reg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm,
{Operand::VReg(bias_reg, VRegClass::Int),
Operand::Imm(bias)}).SetRematerializable(true).SetRematImm(bias);
block.Append(Opcode::AddRR,
{Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::VReg(bias_reg, VRegClass::Int)});
}
block.Append(Opcode::CmpImm,
{Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(0)});
int selected = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::Csel,
{Operand::VReg(selected, VRegClass::Int),
Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(static_cast<int>(CondCode::LT))});
block.Append(Opcode::AsrRR,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(selected, VRegClass::Int),
Operand::Imm(shift)});
value_vregs[value] = dst;
return dst;
}
if (val < 0 && (-val & (-val - 1)) == 0 && val != -1)
{
int abs_val = -val;
int shift = 0;
int tmp = abs_val;
while (tmp > 1)
{
tmp >>= 1;
++shift;
}
int bias = (1 << shift) - 1;
int biased = function.CreateVReg(VRegClass::Int);
if (bias <= 4095)
{
block.Append(Opcode::AddRR,
{Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(bias)});
}
else
{
int bias_reg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm,
{Operand::VReg(bias_reg, VRegClass::Int),
Operand::Imm(bias)}).SetRematerializable(true).SetRematImm(bias);
block.Append(Opcode::AddRR,
{Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::VReg(bias_reg, VRegClass::Int)});
}
block.Append(Opcode::CmpImm,
{Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(0)});
int selected = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::Csel,
{Operand::VReg(selected, VRegClass::Int),
Operand::VReg(biased, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(static_cast<int>(CondCode::LT))});
int pos_q = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::AsrRR,
{Operand::VReg(pos_q, VRegClass::Int),
Operand::VReg(selected, VRegClass::Int),
Operand::Imm(shift)});
block.Append(Opcode::NegRR,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(pos_q, VRegClass::Int)});
value_vregs[value] = dst;
return dst;
}
}
}
if (opcode == Opcode::ModRR)
{
auto *rhs_const = dynamic_cast<const ir::ConstantInt *>(bin->GetRhs());
if (rhs_const)
{
int val = rhs_const->GetValue();
if (val > 0 && (val & (val - 1)) == 0)
{
// x % 2^n -> negs+and+and+csneg4 指令,含零值正确语义)
int mask = val - 1;
int neg = function.CreateVReg(VRegClass::Int);
int pos = function.CreateVReg(VRegClass::Int);
int neg_masked = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::NegRR,
{Operand::VReg(neg, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int)});
if (static_cast<unsigned int>(mask) <= 4095) {
block.Append(Opcode::AndRR,
{Operand::VReg(pos, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(mask)});
block.Append(Opcode::AndRR,
{Operand::VReg(neg_masked, VRegClass::Int),
Operand::VReg(neg, VRegClass::Int),
Operand::Imm(mask)});
} else {
int mask_reg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm,
{Operand::VReg(mask_reg, VRegClass::Int),
Operand::Imm(mask)}).SetRematerializable(true).SetRematImm(mask);
block.Append(Opcode::AndRR,
{Operand::VReg(pos, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::VReg(mask_reg, VRegClass::Int)});
block.Append(Opcode::AndRR,
{Operand::VReg(neg_masked, VRegClass::Int),
Operand::VReg(neg, VRegClass::Int),
Operand::VReg(mask_reg, VRegClass::Int)});
}
block.Append(Opcode::Csneg,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(pos, VRegClass::Int),
Operand::VReg(neg_masked, VRegClass::Int),
Operand::Imm(static_cast<int>(CondCode::MI))});
value_vregs[value] = dst;
return dst;
}
if (val < 0 && (-val & (-val - 1)) == 0 && val != -1)
{
// x % -2^n -> 同 x % 2^nnegs+and+and+csneg
int mask = (-val) - 1;
int neg = function.CreateVReg(VRegClass::Int);
int pos = function.CreateVReg(VRegClass::Int);
int neg_masked = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::NegRR,
{Operand::VReg(neg, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int)});
if (static_cast<unsigned int>(mask) <= 4095) {
block.Append(Opcode::AndRR,
{Operand::VReg(pos, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(mask)});
block.Append(Opcode::AndRR,
{Operand::VReg(neg_masked, VRegClass::Int),
Operand::VReg(neg, VRegClass::Int),
Operand::Imm(mask)});
} else {
int mask_reg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm,
{Operand::VReg(mask_reg, VRegClass::Int),
Operand::Imm(mask)}).SetRematerializable(true).SetRematImm(mask);
block.Append(Opcode::AndRR,
{Operand::VReg(pos, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::VReg(mask_reg, VRegClass::Int)});
block.Append(Opcode::AndRR,
{Operand::VReg(neg_masked, VRegClass::Int),
Operand::VReg(neg, VRegClass::Int),
Operand::VReg(mask_reg, VRegClass::Int)});
}
block.Append(Opcode::Csneg,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(pos, VRegClass::Int),
Operand::VReg(neg_masked, VRegClass::Int),
Operand::Imm(static_cast<int>(CondCode::MI))});
value_vregs[value] = dst;
return dst;
}
}
}
// 立即数折叠AddRR/SubRR 的操作数 2 如果是常量且 <=4095直接用 Imm
int imm_val = 0;
if ((opcode == Opcode::AddRR || opcode == Opcode::SubRR) &&
TryGetConstantInt(bin->GetRhs(), imm_val))
{
int abs_val = imm_val >= 0 ? imm_val : -imm_val;
if (abs_val <= 4095)
{
block.Append(opcode,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::Imm(imm_val)});
value_vregs[value] = dst;
return dst;
}
}
block.Append(opcode,
{Operand::VReg(dst, VRegClass::Int),
Operand::VReg(lhs, VRegClass::Int),
Operand::VReg(rhs, VRegClass::Int)});
value_vregs[value] = dst;
return dst;
}
if (auto *phi = dynamic_cast<const ir::PhiInst *>(value))
{
auto phi_it = value_vregs.find(value);
if (phi_it != value_vregs.end())
return phi_it->second;
}
if (auto *load = dynamic_cast<const ir::LoadInst *>(value))
{
int scalar_slot = -1;
if (TryResolveDirectScalarSlot(load->GetPtr(), scalar_slots, scalar_slot))
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Int), Operand::FrameIndex(scalar_slot)});
value_vregs[value] = vreg;
return vreg;
}
if (auto *global = AsGlobalScalarObject(load->GetPtr()))
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadGlobal,
{Operand::VReg(vreg, VRegClass::Int), Operand::Symbol(global->GetName())});
value_vregs[value] = vreg;
return vreg;
}
// 缩放寻址GEP + Load → ldr [base, idx, uxtw #2]
if (auto *gep = dynamic_cast<const ir::GetElementPtrInst *>(load->GetPtr()))
{
int idx_imm = 0;
if (!TryGetConstantInt(gep->GetIndex(), idx_imm))
{
int base_addr = EmitPtrValue(gep->GetBasePtr(), function, value_vregs,
scalar_slots, array_slots, block);
int idx_vreg = EmitIntValue(gep->GetIndex(), function, value_vregs,
scalar_slots, array_slots, block);
int data_vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadMem,
{Operand::VReg(data_vreg, VRegClass::Int),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
value_vregs[value] = data_vreg;
return data_vreg;
}
}
int addr = EmitPtrValue(load->GetPtr(), function, value_vregs,
scalar_slots, array_slots, block);
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Int), Operand::VReg(addr, VRegClass::Ptr)});
value_vregs[value] = vreg;
return vreg;
}
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
value_vregs[value] = vreg;
return vreg;
}
static int EmitFloatValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, MachineBasicBlock &block)
{
if (!value)
{
int vreg = function.CreateVReg(VRegClass::Float);
int wvreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(wvreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
block.Append(Opcode::FMovWS,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(wvreg, VRegClass::Int)});
return vreg;
}
auto it = value_vregs.find(value);
if (it != value_vregs.end())
{
if (function.GetVRegClass(it->second) != VRegClass::Float)
{
int dst = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::Scvtf,
{Operand::VReg(dst, VRegClass::Float), Operand::VReg(it->second, VRegClass::Int)});
return dst;
}
return it->second;
}
int bits = 0;
if (TryGetConstantFloatBits(value, bits))
{
int wvreg = function.CreateVReg(VRegClass::Int);
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::MovImm, {Operand::VReg(wvreg, VRegClass::Int), Operand::Imm(bits)}).SetRematerializable(true).SetRematImm(bits);
block.Append(Opcode::FMovWS,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(wvreg, VRegClass::Int)});
value_vregs[value] = vreg;
return vreg;
}
if (auto *global = AsGlobalScalarObject(value))
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadGlobal,
{Operand::VReg(vreg, VRegClass::Float), Operand::Symbol(global->GetName())});
value_vregs[value] = vreg;
return vreg;
}
if (auto *cast = dynamic_cast<const ir::CastInst *>(value))
{
if (cast->GetOpcode() == ir::Opcode::SIToFP)
{
int src = EmitIntValue(cast->GetOperandValue(), function, value_vregs,
LocalScalarMap(), LocalArrayMap(), block);
int dst = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::Scvtf,
{Operand::VReg(dst, VRegClass::Float), Operand::VReg(src, VRegClass::Int)});
value_vregs[value] = dst;
return dst;
}
}
if (auto *bin = dynamic_cast<const ir::BinaryInst *>(value))
{
if (IsFloatType(bin->GetType()))
{
Opcode opcode = Opcode::FAddRR;
switch (bin->GetOpcode())
{
case ir::Opcode::Add:
opcode = Opcode::FAddRR;
break;
case ir::Opcode::Sub:
opcode = Opcode::FSubRR;
break;
case ir::Opcode::Mul:
opcode = Opcode::FMulRR;
break;
case ir::Opcode::Div:
opcode = Opcode::FDivRR;
break;
default:
break;
}
int lhs = EmitFloatValue(bin->GetLhs(), function, value_vregs, block);
int rhs = EmitFloatValue(bin->GetRhs(), function, value_vregs, block);
int dst = function.CreateVReg(VRegClass::Float);
block.Append(opcode,
{Operand::VReg(dst, VRegClass::Float),
Operand::VReg(lhs, VRegClass::Float),
Operand::VReg(rhs, VRegClass::Float)});
value_vregs[value] = dst;
return dst;
}
}
if (auto *phi = dynamic_cast<const ir::PhiInst *>(value))
{
auto phi_it = value_vregs.find(value);
if (phi_it != value_vregs.end())
return phi_it->second;
}
if (auto *load = dynamic_cast<const ir::LoadInst *>(value))
{
int scalar_slot = -1;
LocalScalarMap dummy_scalar;
if (TryResolveDirectScalarSlot(load->GetPtr(), dummy_scalar, scalar_slot))
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Float), Operand::FrameIndex(scalar_slot)});
value_vregs[value] = vreg;
return vreg;
}
}
int vreg = function.CreateVReg(VRegClass::Float);
int wvreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(wvreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
block.Append(Opcode::FMovWS,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(wvreg, VRegClass::Int)});
value_vregs[value] = vreg;
return vreg;
}
static int EmitPtrValue(const ir::Value *value, MachineFunction &function,
ValueVRegMap &value_vregs, const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots, MachineBasicBlock &block)
{
auto it = value_vregs.find(value);
if (it != value_vregs.end())
return it->second;
if (auto *global_scalar = AsGlobalScalarObject(value))
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadGlobalAddr,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::Symbol(global_scalar->GetName())});
value_vregs[value] = vreg;
return vreg;
}
if (auto *global_array = AsGlobalArrayObject(value))
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadGlobalAddr,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::Symbol(global_array->GetName())});
value_vregs[value] = vreg;
return vreg;
}
auto scalar_it = scalar_slots.find(value);
if (scalar_it != scalar_slots.end())
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadStackAddr,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(scalar_it->second)});
value_vregs[value] = vreg;
return vreg;
}
auto array_it = array_slots.find(value);
if (array_it != array_slots.end())
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadStackAddr,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(array_it->second)});
value_vregs[value] = vreg;
return vreg;
}
auto *gep = dynamic_cast<const ir::GetElementPtrInst *>(value);
if (gep)
{
int base = EmitPtrValue(gep->GetBasePtr(), function, value_vregs,
scalar_slots, array_slots, block);
int idx_imm = 0;
if (TryGetConstantInt(gep->GetIndex(), idx_imm))
{
const int byte_offset = static_cast<int>(static_cast<unsigned int>(idx_imm) * 4u);
if (byte_offset == 0)
{
value_vregs[value] = base;
return base;
}
int dst = function.CreateVReg(VRegClass::Ptr);
int offset_vreg = function.CreateVReg(VRegClass::Ptr);
int abs_off = byte_offset > 0 ? byte_offset : -byte_offset;
block.Append(Opcode::MovImm, {Operand::VReg(offset_vreg, VRegClass::Ptr), Operand::Imm(abs_off)}).SetRematerializable(true).SetRematImm(abs_off);
if (byte_offset > 0)
{
block.Append(Opcode::AddRR,
{Operand::VReg(dst, VRegClass::Ptr),
Operand::VReg(base, VRegClass::Ptr),
Operand::VReg(offset_vreg, VRegClass::Ptr)});
}
else
{
block.Append(Opcode::SubRR,
{Operand::VReg(dst, VRegClass::Ptr),
Operand::VReg(base, VRegClass::Ptr),
Operand::VReg(offset_vreg, VRegClass::Ptr)});
}
value_vregs[value] = dst;
return dst;
}
int idx = EmitIntValue(gep->GetIndex(), function, value_vregs,
scalar_slots, array_slots, block);
int sext = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::Sxtw,
{Operand::VReg(sext, VRegClass::Ptr), Operand::VReg(idx, VRegClass::Int)});
int shifted = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::ShlRR,
{Operand::VReg(shifted, VRegClass::Ptr),
Operand::VReg(sext, VRegClass::Ptr),
Operand::Imm(2)});
int dst = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::AddRR,
{Operand::VReg(dst, VRegClass::Ptr),
Operand::VReg(base, VRegClass::Ptr),
Operand::VReg(shifted, VRegClass::Ptr)});
value_vregs[value] = dst;
return dst;
}
if (IsPointerValue(value))
{
auto vreg_it = value_vregs.find(value);
if (vreg_it != value_vregs.end())
return vreg_it->second;
}
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Ptr), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
value_vregs[value] = vreg;
return vreg;
}
static void EmitCompareToFlags(const ir::BinaryInst &bin,
MachineFunction &function,
ValueVRegMap &value_vregs,
const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots,
MachineBasicBlock &block)
{
if (IsFloatValue(bin.GetLhs()) || IsFloatValue(bin.GetRhs()))
{
int lhs = EmitFloatValue(bin.GetLhs(), function, value_vregs, block);
int rhs = EmitFloatValue(bin.GetRhs(), function, value_vregs, block);
block.Append(Opcode::FCmpRR,
{Operand::VReg(lhs, VRegClass::Float), Operand::VReg(rhs, VRegClass::Float)});
return;
}
int lhs = EmitIntValue(bin.GetLhs(), function, value_vregs,
scalar_slots, array_slots, block);
int rhs = EmitIntValue(bin.GetRhs(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::CmpRR,
{Operand::VReg(lhs, VRegClass::Int), Operand::VReg(rhs, VRegClass::Int)});
}
static bool TryEmitCondValueToFlags(const ir::Value *value,
MachineFunction &function,
ValueVRegMap &value_vregs,
const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots,
MachineBasicBlock &block,
CondCode &true_cond, int depth = 0)
{
if (!value || depth > 8)
return false;
if (auto *cast = dynamic_cast<const ir::CastInst *>(value))
{
if (cast->GetOpcode() == ir::Opcode::ZExt)
{
return TryEmitCondValueToFlags(cast->GetOperandValue(),
function, value_vregs, scalar_slots, array_slots,
block, true_cond, depth + 1);
}
}
if (auto *bin = dynamic_cast<const ir::BinaryInst *>(value))
{
if (IsIntegerCompareOpcode(bin->GetOpcode()))
{
if (bin->GetOpcode() == ir::Opcode::Eq || bin->GetOpcode() == ir::Opcode::Ne)
{
const ir::Value *inner = nullptr;
if (IsZeroIntConstant(bin->GetLhs()))
inner = bin->GetRhs();
else if (IsZeroIntConstant(bin->GetRhs()))
inner = bin->GetLhs();
if (inner)
{
CondCode inner_cond = CondCode::NE;
if (TryEmitCondValueToFlags(inner, function, value_vregs,
scalar_slots, array_slots,
block, inner_cond, depth + 1))
{
true_cond = (bin->GetOpcode() == ir::Opcode::Eq)
? NegateCondCode(inner_cond)
: inner_cond;
return true;
}
}
}
EmitCompareToFlags(*bin, function, value_vregs, scalar_slots, array_slots, block);
true_cond = GetCondCodeForCompareOpcode(bin->GetOpcode());
return true;
}
}
if (IsFloatValue(value))
{
int vreg = EmitFloatValue(value, function, value_vregs, block);
int zero_w = function.CreateVReg(VRegClass::Int);
int zero_s = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::MovImm, {Operand::VReg(zero_w, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
block.Append(Opcode::FMovWS,
{Operand::VReg(zero_s, VRegClass::Float), Operand::VReg(zero_w, VRegClass::Int)});
block.Append(Opcode::FCmpRR,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(zero_s, VRegClass::Float)});
true_cond = CondCode::NE;
return true;
}
int vreg = EmitIntValue(value, function, value_vregs, scalar_slots, array_slots, block);
int zero = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(zero, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
block.Append(Opcode::CmpRR,
{Operand::VReg(vreg, VRegClass::Int), Operand::VReg(zero, VRegClass::Int)});
true_cond = CondCode::NE;
return true;
}
static void EmitStackPointerAdjust(MachineBasicBlock &block, Opcode opcode, int amount)
{
if (amount <= 0)
return;
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::X14), Operand::Imm(amount)});
block.Append(opcode, {Operand::Reg(PhysReg::SP), Operand::Reg(PhysReg::SP), Operand::Reg(PhysReg::X14)});
}
static int ComputeStackArgumentBytes(const ir::CallInst &call)
{
int total = 0;
size_t gp_idx = 0;
size_t fp_idx = 0;
for (size_t i = 0; i < call.GetNumArgs(); ++i)
{
auto *arg = call.GetArg(i);
auto type = arg ? arg->GetType() : nullptr;
if (IsFloatType(type))
{
if (fp_idx < 8)
++fp_idx;
else
total += 8;
}
else
{
if (gp_idx < 8)
++gp_idx;
else
total += 8;
}
}
return total;
}
static void LowerFunctionParams(const ir::Function &function,
MachineFunction &machine_func,
ValueVRegMap &value_vregs)
{
if (!machine_func.GetEntryPtr())
return;
auto &entry = machine_func.GetEntry();
const auto &params = function.GetParams();
size_t gp_idx = 0;
size_t fp_idx = 0;
int callee_stack_offset = 0;
for (const auto &param : params)
{
if (!param)
continue;
if (IsFloatType(param->GetType()))
{
if (fp_idx < 8)
{
int vreg = machine_func.CreateVReg(VRegClass::Float);
entry.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Float),
Operand::Reg(GetArgSReg(fp_idx))});
value_vregs[param.get()] = vreg;
++fp_idx;
}
else
{
const int arg_slot = machine_func.CreateCalleeStackArgFrameIndex(GetTypeSize(param->GetType()));
machine_func.GetFrameSlot(arg_slot).offset = callee_stack_offset;
callee_stack_offset += 8;
int vreg = machine_func.CreateVReg(VRegClass::Float);
entry.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Float), Operand::FrameIndex(arg_slot)});
value_vregs[param.get()] = vreg;
}
}
else if (IsPointerType(param->GetType()))
{
if (gp_idx < 8)
{
int vreg = machine_func.CreateVReg(VRegClass::Ptr);
entry.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Ptr),
Operand::Reg(GetArgXReg(gp_idx))});
value_vregs[param.get()] = vreg;
++gp_idx;
}
else
{
const int arg_slot = machine_func.CreateCalleeStackArgFrameIndex(GetTypeSize(param->GetType()));
machine_func.GetFrameSlot(arg_slot).offset = callee_stack_offset;
callee_stack_offset += 8;
int vreg = machine_func.CreateVReg(VRegClass::Ptr);
entry.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(arg_slot)});
value_vregs[param.get()] = vreg;
}
}
else
{
if (gp_idx < 8)
{
int vreg = machine_func.CreateVReg(VRegClass::Int);
entry.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Int),
Operand::Reg(GetArgWReg(gp_idx))});
value_vregs[param.get()] = vreg;
++gp_idx;
}
else
{
const int arg_slot = machine_func.CreateCalleeStackArgFrameIndex(GetTypeSize(param->GetType()));
machine_func.GetFrameSlot(arg_slot).offset = callee_stack_offset;
callee_stack_offset += 8;
int vreg = machine_func.CreateVReg(VRegClass::Int);
entry.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Int), Operand::FrameIndex(arg_slot)});
value_vregs[param.get()] = vreg;
}
}
}
}
static void EmitPhiValueStores(const ir::BasicBlock *src_bb,
const ir::BasicBlock *dst_bb,
MachineFunction &function,
const ValueVRegMap &value_vregs,
const LocalScalarMap &scalar_slots,
const LocalArrayMap &array_slots,
MachineBasicBlock &mir_block)
{
if (!src_bb || !dst_bb)
return;
for (const auto &inst_ptr : dst_bb->GetInstructions())
{
auto *phi = dynamic_cast<const ir::PhiInst *>(inst_ptr.get());
if (!phi)
break;
auto phi_it = value_vregs.find(phi);
if (phi_it == value_vregs.end())
continue;
int phi_vreg = phi_it->second;
const ir::Value *incoming_value = nullptr;
size_t num_ops = phi->GetNumOperands();
for (size_t i = 0; i + 1 < num_ops; i += 2)
{
auto *val = phi->GetOperand(i);
auto *bb_ptr = dynamic_cast<ir::BasicBlock *>(phi->GetOperand(i + 1));
if (bb_ptr && bb_ptr == src_bb)
{
incoming_value = val;
break;
}
}
if (!incoming_value)
continue;
VRegClass phi_class = function.GetVRegClass(phi_vreg);
if (phi_class == VRegClass::Float)
{
int src = EmitFloatValue(incoming_value, function,
const_cast<ValueVRegMap &>(value_vregs), mir_block);
if (phi_vreg == src) {
// self-referencing PHI, skip
} else {
mir_block.Append(Opcode::MovReg,
{Operand::VReg(phi_vreg, VRegClass::Float),
Operand::VReg(src, VRegClass::Float)});
}
}
else if (phi_class == VRegClass::Ptr)
{
int src = EmitPtrValue(incoming_value, function,
const_cast<ValueVRegMap &>(value_vregs),
scalar_slots, array_slots, mir_block);
if (phi_vreg == src) {
// self-referencing PHI, skip
} else {
mir_block.Append(Opcode::MovReg,
{Operand::VReg(phi_vreg, VRegClass::Ptr),
Operand::VReg(src, VRegClass::Ptr)});
}
}
else
{
int src = EmitIntValue(incoming_value, function,
const_cast<ValueVRegMap &>(value_vregs),
scalar_slots, array_slots, mir_block);
if (phi_vreg == src) {
// self-referencing PHI, skip
} else {
mir_block.Append(Opcode::MovReg,
{Operand::VReg(phi_vreg, VRegClass::Int),
Operand::VReg(src, VRegClass::Int)});
}
}
}
}
static void LowerInstruction(const ir::Instruction &inst,
MachineFunction &function,
ValueVRegMap &value_vregs,
LocalScalarMap &scalar_slots,
LocalArrayMap &array_slots,
const BlockMap &block_map,
MachineBasicBlock &block)
{
switch (inst.GetOpcode())
{
case ir::Opcode::Alloca:
{
auto &alloca = static_cast<const ir::AllocaInst &>(inst);
const int elem_size = GetTypeSize(alloca.GetElementType());
if (alloca.IsArrayAlloca())
{
int count = 0;
if (TryGetConstantInt(alloca.GetCount(), count) && count > 0)
array_slots[&inst] = function.CreateFrameIndex(elem_size * count);
else
array_slots[&inst] = function.CreateFrameIndex(elem_size);
}
else
{
const int slot = function.CreateFrameIndex(elem_size);
scalar_slots[&inst] = slot;
}
return;
}
case ir::Opcode::Load:
{
auto &load = static_cast<const ir::LoadInst &>(inst);
const bool is_ptr = IsPointerType(load.GetType());
const bool is_float = IsFloatType(load.GetType());
int scalar_slot = -1;
if (TryResolveDirectScalarSlot(load.GetPtr(), scalar_slots, scalar_slot))
{
if (is_ptr)
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(scalar_slot)});
value_vregs[&load] = vreg;
}
else if (is_float)
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Float), Operand::FrameIndex(scalar_slot)});
value_vregs[&load] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadStack,
{Operand::VReg(vreg, VRegClass::Int), Operand::FrameIndex(scalar_slot)});
value_vregs[&load] = vreg;
}
return;
}
if (!is_ptr)
{
if (auto *global = AsGlobalScalarObject(load.GetPtr()))
{
if (is_float)
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadGlobal,
{Operand::VReg(vreg, VRegClass::Float), Operand::Symbol(global->GetName())});
value_vregs[&load] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadGlobal,
{Operand::VReg(vreg, VRegClass::Int), Operand::Symbol(global->GetName())});
value_vregs[&load] = vreg;
}
return;
}
}
// 缩放寻址GEP + Load → ldr [base, idx, uxtw #2]
bool scaled_load = false;
if (auto *gep = dynamic_cast<const ir::GetElementPtrInst *>(load.GetPtr()))
{
int idx_imm = 0;
if (!TryGetConstantInt(gep->GetIndex(), idx_imm))
{
scaled_load = true;
int base_addr = EmitPtrValue(gep->GetBasePtr(), function, value_vregs,
scalar_slots, array_slots, block);
int idx_vreg = EmitIntValue(gep->GetIndex(), function, value_vregs,
scalar_slots, array_slots, block);
if (is_ptr)
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Ptr),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
value_vregs[&load] = vreg;
}
else if (is_float)
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Float),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
value_vregs[&load] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Int),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
value_vregs[&load] = vreg;
}
}
}
if (!scaled_load)
{
int addr = EmitPtrValue(load.GetPtr(), function, value_vregs,
scalar_slots, array_slots, block);
if (is_ptr)
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::VReg(addr, VRegClass::Ptr)});
value_vregs[&load] = vreg;
}
else if (is_float)
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(addr, VRegClass::Ptr)});
value_vregs[&load] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::LoadMem,
{Operand::VReg(vreg, VRegClass::Int), Operand::VReg(addr, VRegClass::Ptr)});
value_vregs[&load] = vreg;
}
}
return;
}
case ir::Opcode::Store:
{
auto &store = static_cast<const ir::StoreInst &>(inst);
const bool value_is_ptr = IsPointerType(store.GetValue()->GetType());
const bool value_is_float = IsFloatType(store.GetValue()->GetType());
int scalar_slot = -1;
if (TryResolveDirectScalarSlot(store.GetPtr(), scalar_slots, scalar_slot))
{
if (value_is_ptr)
{
int vreg = EmitPtrValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(scalar_slot)});
}
else if (value_is_float)
{
int vreg = EmitFloatValue(store.GetValue(), function, value_vregs, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Float), Operand::FrameIndex(scalar_slot)});
}
else
{
int vreg = EmitIntValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Int), Operand::FrameIndex(scalar_slot)});
}
return;
}
if (!value_is_ptr)
{
if (auto *global = AsGlobalScalarObject(store.GetPtr()))
{
if (value_is_float)
{
int vreg = EmitFloatValue(store.GetValue(), function, value_vregs, block);
block.Append(Opcode::StoreGlobal,
{Operand::VReg(vreg, VRegClass::Float), Operand::Symbol(global->GetName())});
}
else
{
int vreg = EmitIntValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreGlobal,
{Operand::VReg(vreg, VRegClass::Int), Operand::Symbol(global->GetName())});
}
return;
}
}
// 缩放寻址GEP + Store → str [base, idx, uxtw #2]
bool scaled_store = false;
if (auto *gep = dynamic_cast<const ir::GetElementPtrInst *>(store.GetPtr()))
{
int idx_imm = 0;
if (!TryGetConstantInt(gep->GetIndex(), idx_imm))
{
scaled_store = true;
int base_addr = EmitPtrValue(gep->GetBasePtr(), function, value_vregs,
scalar_slots, array_slots, block);
int idx_vreg = EmitIntValue(gep->GetIndex(), function, value_vregs,
scalar_slots, array_slots, block);
if (value_is_ptr)
{
int val = EmitPtrValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Ptr),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
}
else if (value_is_float)
{
int val = EmitFloatValue(store.GetValue(), function, value_vregs, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Float),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
}
else
{
int val = EmitIntValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Int),
Operand::VReg(base_addr, VRegClass::Ptr),
Operand::VReg(idx_vreg, VRegClass::Int)});
}
}
}
if (!scaled_store)
{
int addr = EmitPtrValue(store.GetPtr(), function, value_vregs,
scalar_slots, array_slots, block);
if (value_is_ptr)
{
int val = EmitPtrValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Ptr), Operand::VReg(addr, VRegClass::Ptr)});
}
else if (value_is_float)
{
int val = EmitFloatValue(store.GetValue(), function, value_vregs, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Float), Operand::VReg(addr, VRegClass::Ptr)});
}
else
{
int val = EmitIntValue(store.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreMem,
{Operand::VReg(val, VRegClass::Int), Operand::VReg(addr, VRegClass::Ptr)});
}
}
return;
}
case ir::Opcode::GEP:
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);
if (IsFloatType(bin.GetType()))
{
EmitFloatValue(&bin, function, value_vregs, block);
return;
}
EmitIntValue(&bin, function, value_vregs, scalar_slots, array_slots, block);
return;
}
case ir::Opcode::Eq:
case ir::Opcode::Ne:
case ir::Opcode::Lt:
case ir::Opcode::Le:
case ir::Opcode::Gt:
case ir::Opcode::Ge:
{
auto &bin = static_cast<const ir::BinaryInst &>(inst);
if (IsSolelyConsumedByCanonicalBoolUse(bin))
return;
EmitIntValue(&bin, function, value_vregs, scalar_slots, array_slots, block);
return;
}
case ir::Opcode::SIToFP:
case ir::Opcode::FPToSI:
case ir::Opcode::ZExt:
{
auto &cast = static_cast<const ir::CastInst &>(inst);
if (inst.GetOpcode() == ir::Opcode::ZExt)
{
if (IsSolelyConsumedByCanonicalBoolUse(cast))
return;
}
if (inst.GetOpcode() == ir::Opcode::SIToFP)
{
EmitFloatValue(&inst, function, value_vregs, block);
return;
}
if (inst.GetOpcode() == ir::Opcode::FPToSI)
{
EmitIntValue(&inst, function, value_vregs, scalar_slots, array_slots, block);
return;
}
if (inst.GetOpcode() == ir::Opcode::ZExt)
{
EmitIntValue(&inst, function, value_vregs, scalar_slots, array_slots, block);
return;
}
return;
}
case ir::Opcode::Phi:
return;
case ir::Opcode::Br:
{
auto &br = static_cast<const ir::BranchInst &>(inst);
auto it = block_map.find(br.GetTarget());
if (it != block_map.end() && it->second)
{
EmitPhiValueStores(inst.GetParent(), br.GetTarget(), function,
value_vregs, scalar_slots, array_slots, block);
block.Append(Opcode::Br, {Operand::Label(it->second->GetLabelId())});
}
return;
}
case ir::Opcode::CondBr:
{
auto &br = static_cast<const ir::CondBranchInst &>(inst);
CondCode true_cond = CondCode::NE;
TryEmitCondValueToFlags(br.GetCond(), function, value_vregs,
scalar_slots, array_slots, block, true_cond);
auto true_it = block_map.find(br.GetTrueTarget());
if (true_it != block_map.end() && true_it->second)
{
EmitPhiValueStores(inst.GetParent(), br.GetTrueTarget(), function,
value_vregs, scalar_slots, array_slots, block);
block.Append(Opcode::CondBr,
{Operand::Imm(static_cast<int>(true_cond)),
Operand::Label(true_it->second->GetLabelId())});
}
auto false_it = block_map.find(br.GetFalseTarget());
if (false_it != block_map.end() && false_it->second)
{
EmitPhiValueStores(inst.GetParent(), br.GetFalseTarget(), function,
value_vregs, scalar_slots, array_slots, block);
block.Append(Opcode::Br,
{Operand::Label(false_it->second->GetLabelId())});
}
return;
}
case ir::Opcode::Call:
{
auto &call = static_cast<const ir::CallInst &>(inst);
auto *callee = call.GetCallee();
if (!callee)
{
if (!call.GetType()->IsVoid())
{
if (IsPointerType(call.GetType()))
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Ptr), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
value_vregs[&call] = vreg;
}
else if (IsFloatType(call.GetType()))
{
int vreg = function.CreateVReg(VRegClass::Float);
int wvreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(wvreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
block.Append(Opcode::FMovWS,
{Operand::VReg(vreg, VRegClass::Float), Operand::VReg(wvreg, VRegClass::Int)});
value_vregs[&call] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovImm, {Operand::VReg(vreg, VRegClass::Int), Operand::Imm(0)}).SetRematerializable(true).SetRematImm(0);
value_vregs[&call] = vreg;
}
}
return;
}
std::vector<size_t> stack_arg_indices;
size_t gp_idx = 0;
size_t fp_idx = 0;
for (size_t i = 0; i < call.GetNumArgs(); ++i)
{
auto *arg = call.GetArg(i);
if (!arg)
continue;
if (IsFloatType(arg->GetType()))
{
if (fp_idx < 8)
{
int vreg = EmitFloatValue(arg, function, value_vregs, block);
block.Append(Opcode::MovReg,
{Operand::Reg(GetArgSReg(fp_idx)),
Operand::VReg(vreg, VRegClass::Float)});
++fp_idx;
}
else
{
stack_arg_indices.push_back(i);
}
}
else if (IsPointerValue(arg))
{
if (gp_idx < 8)
{
int vreg = EmitPtrValue(arg, function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::MovReg,
{Operand::Reg(GetArgXReg(gp_idx)),
Operand::VReg(vreg, VRegClass::Ptr)});
++gp_idx;
}
else
{
stack_arg_indices.push_back(i);
}
}
else
{
if (gp_idx < 8)
{
int vreg = EmitIntValue(arg, function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::MovReg,
{Operand::Reg(GetArgWReg(gp_idx)),
Operand::VReg(vreg, VRegClass::Int)});
++gp_idx;
}
else
{
stack_arg_indices.push_back(i);
}
}
}
const int raw_stack_arg_bytes = ComputeStackArgumentBytes(call);
const int aligned_stack_arg_bytes = AlignTo(raw_stack_arg_bytes, 16);
if (aligned_stack_arg_bytes > 0)
{
EmitStackPointerAdjust(block, Opcode::SubRR, aligned_stack_arg_bytes);
int offset = 0;
for (size_t idx : stack_arg_indices)
{
auto *arg = call.GetArg(idx);
const int arg_size = GetTypeSize(arg ? arg->GetType() : nullptr);
if (!arg)
{
offset += 8;
continue;
}
const int slot = function.CreateStackArgFrameIndex(arg_size);
function.GetFrameSlot(slot).offset = offset;
if (IsFloatType(arg->GetType()))
{
int vreg = EmitFloatValue(arg, function, value_vregs, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Float), Operand::FrameIndex(slot)});
}
else if (IsPointerValue(arg))
{
int vreg = EmitPtrValue(arg, function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::FrameIndex(slot)});
}
else
{
int vreg = EmitIntValue(arg, function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::StoreStack,
{Operand::VReg(vreg, VRegClass::Int), Operand::FrameIndex(slot)});
}
offset += 8;
}
}
block.Append(Opcode::Call, {Operand::Symbol(callee->GetName())});
if (aligned_stack_arg_bytes > 0)
{
EmitStackPointerAdjust(block, Opcode::AddRR, aligned_stack_arg_bytes);
}
if (!call.GetType()->IsVoid())
{
if (IsPointerType(call.GetType()))
{
int vreg = function.CreateVReg(VRegClass::Ptr);
block.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Ptr), Operand::Reg(PhysReg::X0)});
value_vregs[&call] = vreg;
}
else if (IsFloatType(call.GetType()))
{
int vreg = function.CreateVReg(VRegClass::Float);
block.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Float), Operand::Reg(PhysReg::S0)});
value_vregs[&call] = vreg;
}
else
{
int vreg = function.CreateVReg(VRegClass::Int);
block.Append(Opcode::MovReg,
{Operand::VReg(vreg, VRegClass::Int), Operand::Reg(PhysReg::W0)});
value_vregs[&call] = vreg;
}
}
return;
}
case ir::Opcode::Ret:
{
auto &ret = static_cast<const ir::ReturnInst &>(inst);
if (ret.HasValue())
{
if (ret.GetValue()->GetType()->IsPtrInt32() ||
ret.GetValue()->GetType()->IsPtrFloat32())
{
int vreg = EmitPtrValue(ret.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::MovReg,
{Operand::Reg(PhysReg::X0), Operand::VReg(vreg, VRegClass::Ptr)});
}
else if (IsFloatValue(ret.GetValue()))
{
int vreg = EmitFloatValue(ret.GetValue(), function, value_vregs, block);
block.Append(Opcode::MovReg,
{Operand::Reg(PhysReg::S0), Operand::VReg(vreg, VRegClass::Float)});
}
else
{
int vreg = EmitIntValue(ret.GetValue(), function, value_vregs,
scalar_slots, array_slots, block);
block.Append(Opcode::MovReg,
{Operand::Reg(PhysReg::W0), Operand::VReg(vreg, VRegClass::Int)});
}
}
block.Append(Opcode::Ret);
return;
}
default:
return;
}
}
static void LowerOneFunction(const ir::Function &ir_function,
MachineFunction &machine_func)
{
ValueVRegMap value_vregs;
LocalScalarMap scalar_slots;
LocalArrayMap array_slots;
BlockMap block_map;
const auto *entry = ir_function.GetEntry();
if (!entry)
{
throw std::runtime_error(
FormatError("mir", "IR 函数缺少入口基本块: " + ir_function.GetName()));
}
block_map.emplace(entry, &machine_func.GetEntry());
for (const auto &bb : ir_function.GetBlocks())
{
if (!bb || bb.get() == entry)
continue;
block_map.emplace(bb.get(), &machine_func.CreateBlock(bb->GetName()));
}
LowerFunctionParams(ir_function, machine_func, value_vregs);
for (const auto &bb : ir_function.GetBlocks())
{
if (!bb)
continue;
for (const auto &inst : bb->GetInstructions())
{
auto *phi = dynamic_cast<const ir::PhiInst *>(inst.get());
if (!phi)
break;
VRegClass vc = VRegClass::Int;
if (IsFloatType(phi->GetType()))
vc = VRegClass::Float;
else if (IsPointerType(phi->GetType()))
vc = VRegClass::Ptr;
int phi_vreg = machine_func.CreateVReg(vc);
value_vregs[phi] = phi_vreg;
}
}
for (const auto &bb : ir_function.GetBlocks())
{
if (!bb)
continue;
auto it = block_map.find(bb.get());
if (it == block_map.end() || !it->second)
continue;
auto &mir_block = *it->second;
std::vector<const ir::Value *> to_remove;
for (auto &pair : value_vregs)
{
if (dynamic_cast<const ir::ConstantInt *>(pair.first) ||
dynamic_cast<const ir::ConstantFloat *>(pair.first) ||
AsGlobalScalarObject(pair.first) ||
AsGlobalArrayObject(pair.first) ||
dynamic_cast<const ir::AllocaInst *>(pair.first) ||
dynamic_cast<const ir::GetElementPtrInst *>(pair.first))
{
to_remove.push_back(pair.first);
}
}
for (auto *v : to_remove)
value_vregs.erase(v);
for (const auto &inst : bb->GetInstructions())
{
LowerInstruction(*inst, machine_func,
value_vregs, scalar_slots, array_slots,
block_map, mir_block);
}
}
}
static void LowerGlobals(const ir::Module &module,
MachineModule &machine_module)
{
for (const auto &global : module.GetGlobals())
{
if (!global)
continue;
if (!IsPointerValue(global.get()))
continue;
if (global->IsArray())
{
if (global->IsPtrFloat32())
{
std::vector<int> init_bits;
if (global->HasInitValues())
{
const auto &init_values = global->GetInitFloatValues();
init_bits.reserve(init_values.size());
for (double v : init_values)
{
init_bits.push_back(FloatToBits(static_cast<float>(v)));
}
}
machine_module.AddGlobalArrayI32(global->GetName(),
global->GetArraySize(),
std::move(init_bits));
}
else if (global->HasInitValues())
{
machine_module.AddGlobalArrayI32(global->GetName(),
global->GetArraySize(),
global->GetInitValues());
}
else
{
machine_module.AddGlobalArrayI32(global->GetName(),
global->GetArraySize());
}
continue;
}
if (global->IsPtrFloat32())
{
machine_module.AddGlobalI32(global->GetName(),
FloatToBits(static_cast<float>(global->GetInitFloatValue())));
}
else
{
machine_module.AddGlobalI32(global->GetName(), global->GetInitValue());
}
}
}
} // namespace
std::unique_ptr<MachineModule> LowerModuleToMIR(const ir::Module &module)
{
DefaultContext();
auto machine_module = std::make_unique<MachineModule>();
LowerGlobals(module, *machine_module);
for (const auto &func : module.GetFunctions())
{
if (!func || func->IsExternal())
continue;
auto &machine_func = machine_module->CreateFunction(func->GetName());
LowerOneFunction(*func, machine_func);
}
return machine_module;
}
std::unique_ptr<MachineFunction> LowerToMIR(const ir::Module &module)
{
auto machine_module = LowerModuleToMIR(module);
if (!machine_module)
{
throw std::runtime_error(FormatError("mir", "LowerModuleToMIR 失败"));
}
auto &functions = machine_module->GetFunctions();
for (auto &func : functions)
{
if (func && func->GetName() == "main")
{
return std::move(func);
}
}
throw std::runtime_error(FormatError("mir", "未找到 main 函数对应的 MIR"));
}
} // namespace mir