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/passes/Peephole.cpp

586 lines
19 KiB

#include "mir/MIR.h"
#include <vector>
#include "utils/Log.h"
namespace mir
{
namespace
{
static bool IsSamePhysReg(PhysReg a, PhysReg b)
{
int an = static_cast<int>(a);
int bn = static_cast<int>(b);
if (an == bn)
return true;
int aw = static_cast<int>(PhysReg::W0);
int ax = static_cast<int>(PhysReg::X0);
if (an >= aw && an <= static_cast<int>(PhysReg::W30) &&
bn >= ax && bn <= static_cast<int>(PhysReg::X30))
{
return (an - aw) == (bn - ax);
}
if (an >= ax && an <= static_cast<int>(PhysReg::X30) &&
bn >= aw && bn <= static_cast<int>(PhysReg::W30))
{
return (an - ax) == (bn - aw);
}
return false;
}
static bool IsWReg(PhysReg r)
{
int n = static_cast<int>(r);
return n >= static_cast<int>(PhysReg::W0) && n <= static_cast<int>(PhysReg::W30);
}
static bool IsXReg(PhysReg r)
{
int n = static_cast<int>(r);
return n >= static_cast<int>(PhysReg::X0) && n <= static_cast<int>(PhysReg::X30);
}
static bool IsSameRegClass(PhysReg a, PhysReg b)
{
return (IsWReg(a) && IsWReg(b)) || (IsXReg(a) && IsXReg(b));
}
static bool IsRedundantMovReg(const MachineInstr &inst)
{
if (inst.GetOpcode() != Opcode::MovReg)
return false;
const auto &ops = inst.GetOperands();
if (ops.size() < 2)
return false;
if (ops[0].GetKind() != Operand::Kind::Reg || ops[1].GetKind() != Operand::Kind::Reg)
return false;
return ops[0].GetReg() == ops[1].GetReg();
}
static bool IsIdentityAddSub(const MachineInstr &inst)
{
if (inst.GetOpcode() != Opcode::AddRR && inst.GetOpcode() != Opcode::SubRR)
return false;
const auto &ops = inst.GetOperands();
if (ops.size() < 3)
return false;
if (ops[2].GetKind() != Operand::Kind::Imm)
return false;
return ops[2].GetImm() == 0;
}
static bool IsRedundantStoreLoad(const MachineInstr &store,
const MachineInstr &load)
{
if (store.GetOpcode() != Opcode::StoreStack || load.GetOpcode() != Opcode::LoadStack)
return false;
const auto &s_ops = store.GetOperands();
const auto &l_ops = load.GetOperands();
if (s_ops.size() < 2 || l_ops.size() < 2)
return false;
if (s_ops[1].GetKind() != Operand::Kind::FrameIndex ||
l_ops[1].GetKind() != Operand::Kind::FrameIndex)
return false;
return s_ops[1].GetFrameIndex() == l_ops[1].GetFrameIndex();
}
static bool InstDefinesReg(const MachineInstr &inst, PhysReg reg)
{
const auto opcode = inst.GetOpcode();
const auto &ops = inst.GetOperands();
switch (opcode)
{
case Opcode::Call:
case Opcode::Prologue:
case Opcode::Epilogue:
return true;
case Opcode::StoreStack:
case Opcode::StoreMem:
case Opcode::StoreGlobal:
case Opcode::CmpRR:
case Opcode::CmpImm:
case Opcode::FCmpRR:
case Opcode::Br:
case Opcode::CondBr:
case Opcode::Ret:
return false;
default:
if (!ops.empty() && ops[0].GetKind() == Operand::Kind::Reg)
return IsSamePhysReg(ops[0].GetReg(), reg);
return false;
}
}
static bool InstUsesReg(const MachineInstr &inst, PhysReg reg)
{
const auto opcode = inst.GetOpcode();
const auto &ops = inst.GetOperands();
switch (opcode)
{
case Opcode::Call:
case Opcode::Prologue:
case Opcode::Epilogue:
return true;
case Opcode::MovImm:
case Opcode::Br:
case Opcode::CondBr:
case Opcode::Ret:
return false;
default:
break;
}
size_t start = 1;
if (opcode == Opcode::StoreStack || opcode == Opcode::StoreMem ||
opcode == Opcode::StoreGlobal || opcode == Opcode::CmpRR ||
opcode == Opcode::CmpImm || opcode == Opcode::FCmpRR)
{
start = 0;
}
for (size_t i = start; i < ops.size(); ++i)
{
if (ops[i].GetKind() == Operand::Kind::Reg && IsSamePhysReg(ops[i].GetReg(), reg))
return true;
}
return false;
}
static void RunPeepholeOnBlock(MachineBasicBlock &block)
{
auto &insts = block.GetInstructions();
bool changed = true;
while (changed)
{
changed = false;
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (IsRedundantMovReg(*it))
{
it = insts.erase(it);
changed = true;
break;
}
if (IsIdentityAddSub(*it))
{
const auto &ops = it->GetOperands();
if (ops[0].GetKind() == Operand::Kind::Reg &&
ops[1].GetKind() == Operand::Kind::Reg &&
ops[0].GetReg() == ops[1].GetReg())
{
it = insts.erase(it);
changed = true;
break;
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::StoreStack)
{
auto next = std::next(it);
if (next != insts.end() && IsRedundantStoreLoad(*it, *next))
{
const auto &s_ops = it->GetOperands();
const auto &l_ops = next->GetOperands();
if (s_ops[0].GetKind() == Operand::Kind::Reg &&
l_ops[0].GetKind() == Operand::Kind::Reg &&
s_ops[0].GetReg() == l_ops[0].GetReg())
{
next = insts.erase(next);
changed = true;
break;
}
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::MovReg)
{
const auto &ops = it->GetOperands();
if (ops.size() >= 2 &&
ops[0].GetKind() == Operand::Kind::Reg &&
ops[1].GetKind() == Operand::Kind::Reg)
{
PhysReg dst = ops[0].GetReg();
PhysReg src = ops[1].GetReg();
for (auto pit = insts.begin(); pit != it; ++pit)
{
if (pit->GetOpcode() == Opcode::MovImm)
{
const auto &pops = pit->GetOperands();
if (pops.size() >= 2 &&
pops[0].GetKind() == Operand::Kind::Reg &&
IsSamePhysReg(pops[0].GetReg(), src) &&
IsSameRegClass(pops[0].GetReg(), src) &&
pops[1].GetKind() == Operand::Kind::Imm)
{
bool src_redefined = false;
for (auto mid = std::next(pit); mid != it; ++mid)
{
if (InstDefinesReg(*mid, src))
{
src_redefined = true;
break;
}
}
if (!src_redefined)
{
int imm = pops[1].GetImm();
*it = MachineInstr(Opcode::MovImm, {Operand::Reg(dst), Operand::Imm(imm)});
changed = true;
break;
}
}
}
}
if (changed)
break;
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::AddRR || it->GetOpcode() == Opcode::SubRR)
{
const auto &ops = it->GetOperands();
if (ops.size() >= 3 &&
ops[0].GetKind() == Operand::Kind::Reg &&
ops[1].GetKind() == Operand::Kind::Reg &&
ops[2].GetKind() == Operand::Kind::Reg)
{
PhysReg add_dst = ops[0].GetReg();
PhysReg add_lhs = ops[1].GetReg();
PhysReg add_rhs = ops[2].GetReg();
for (auto pit = insts.begin(); pit != it; ++pit)
{
if (pit->GetOpcode() == Opcode::MulRR)
{
const auto &mops = pit->GetOperands();
if (mops.size() >= 3 &&
mops[0].GetKind() == Operand::Kind::Reg &&
mops[1].GetKind() == Operand::Kind::Reg &&
mops[2].GetKind() == Operand::Kind::Reg)
{
PhysReg mul_dst = mops[0].GetReg();
PhysReg mul_lhs = mops[1].GetReg();
PhysReg mul_rhs = mops[2].GetReg();
if (IsSamePhysReg(mul_dst, add_lhs) || IsSamePhysReg(mul_dst, add_rhs))
{
bool mul_dst_is_lhs = IsSamePhysReg(mul_dst, add_lhs);
PhysReg acc_reg = mul_dst_is_lhs ? add_rhs : add_lhs;
bool valid = true;
for (auto mid = std::next(pit); mid != it; ++mid)
{
if (InstDefinesReg(*mid, mul_dst) || InstDefinesReg(*mid, acc_reg))
{
valid = false;
break;
}
if (InstUsesReg(*mid, mul_dst))
{
valid = false;
break;
}
}
if (valid)
{
for (auto after = std::next(it); after != insts.end(); ++after)
{
if (InstUsesReg(*after, mul_dst))
{
valid = false;
break;
}
}
}
if (valid)
{
if (it->GetOpcode() == Opcode::AddRR)
{
*it = MachineInstr(Opcode::Madd,
{Operand::Reg(add_dst),
Operand::Reg(mul_lhs),
Operand::Reg(mul_rhs),
Operand::Reg(acc_reg)});
}
else
{
*it = MachineInstr(Opcode::Msub,
{Operand::Reg(add_dst),
Operand::Reg(mul_lhs),
Operand::Reg(mul_rhs),
Operand::Reg(acc_reg)});
}
it = insts.erase(pit);
changed = true;
break;
}
}
}
}
if (changed)
break;
}
if (changed)
break;
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::MovImm)
{
const auto &ops = it->GetOperands();
if (ops.size() >= 1 &&
ops[0].GetKind() == Operand::Kind::Reg)
{
PhysReg dst = ops[0].GetReg();
bool dead = false;
for (auto fit = std::next(it); fit != insts.end(); ++fit)
{
if (InstUsesReg(*fit, dst))
{
break;
}
if (InstDefinesReg(*fit, dst))
{
dead = true;
break;
}
}
if (dead)
{
it = insts.erase(it);
changed = true;
break;
}
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::LoadStack)
{
const auto &l_ops = it->GetOperands();
if (l_ops.size() >= 2 &&
l_ops[0].GetKind() == Operand::Kind::Reg &&
l_ops[1].GetKind() == Operand::Kind::FrameIndex)
{
PhysReg ld = l_ops[0].GetReg();
int fi = l_ops[1].GetFrameIndex();
for (auto pit = insts.begin(); pit != it; ++pit)
{
if (pit->GetOpcode() == Opcode::StoreStack)
{
const auto &pops = pit->GetOperands();
if (pops.size() >= 2 &&
pops[0].GetKind() == Operand::Kind::Reg &&
pops[1].GetKind() == Operand::Kind::FrameIndex &&
pops[1].GetFrameIndex() == fi)
{
PhysReg rs = pops[0].GetReg();
bool valid = true;
for (auto mid = std::next(pit); mid != it; ++mid)
{
if (mid->GetOpcode() == Opcode::StoreStack)
{
const auto &mops = mid->GetOperands();
if (mops.size() >= 2 &&
mops[1].GetKind() == Operand::Kind::FrameIndex &&
mops[1].GetFrameIndex() == fi)
{
valid = false;
break;
}
}
if (InstDefinesReg(*mid, rs))
{
valid = false;
break;
}
}
if (valid && IsSameRegClass(ld, rs))
{
*it = MachineInstr(Opcode::MovReg, {Operand::Reg(ld), Operand::Reg(rs)});
changed = true;
break;
}
}
}
}
if (changed)
break;
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::MovReg)
{
const auto &ops = it->GetOperands();
if (ops.size() >= 2 &&
ops[0].GetKind() == Operand::Kind::Reg)
{
PhysReg dst = ops[0].GetReg();
bool dead = false;
for (auto fit = std::next(it); fit != insts.end(); ++fit)
{
if (InstUsesReg(*fit, dst))
{
break;
}
if (InstDefinesReg(*fit, dst))
{
dead = true;
break;
}
}
if (dead)
{
it = insts.erase(it);
changed = true;
break;
}
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::LoadStack)
{
const auto &l_ops = it->GetOperands();
if (l_ops.size() >= 2 &&
l_ops[0].GetKind() == Operand::Kind::Reg &&
l_ops[1].GetKind() == Operand::Kind::FrameIndex)
{
PhysReg rd = l_ops[0].GetReg();
int fi = l_ops[1].GetFrameIndex();
for (auto pit = insts.begin(); pit != it; ++pit)
{
if (pit->GetOpcode() == Opcode::LoadStack)
{
const auto &pops = pit->GetOperands();
if (pops.size() >= 2 &&
pops[0].GetKind() == Operand::Kind::Reg &&
IsSamePhysReg(pops[0].GetReg(), rd) &&
pops[1].GetKind() == Operand::Kind::FrameIndex &&
pops[1].GetFrameIndex() == fi)
{
bool valid = true;
for (auto mid = std::next(pit); mid != it; ++mid)
{
if (mid->GetOpcode() == Opcode::StoreStack)
{
const auto &mops = mid->GetOperands();
if (mops.size() >= 2 &&
mops[1].GetKind() == Operand::Kind::FrameIndex &&
mops[1].GetFrameIndex() == fi)
{
valid = false;
break;
}
}
if (InstDefinesReg(*mid, rd))
{
valid = false;
break;
}
}
if (valid)
{
it = insts.erase(it);
changed = true;
break;
}
}
}
}
if (changed)
break;
}
}
}
}
if (!changed)
{
for (auto it = insts.begin(); it != insts.end(); ++it)
{
if (it->GetOpcode() == Opcode::MovReg)
{
const auto &ops = it->GetOperands();
if (ops.size() >= 2 &&
ops[0].GetKind() == Operand::Kind::Reg &&
ops[1].GetKind() == Operand::Kind::Reg &&
IsSamePhysReg(ops[0].GetReg(), ops[1].GetReg()))
{
it = insts.erase(it);
changed = true;
break;
}
}
}
}
}
}
} // namespace
void RunPeephole(MachineFunction &function)
{
for (auto &block : function.GetBlocks())
{
if (block)
RunPeepholeOnBlock(*block);
}
}
void RunPeephole(MachineModule &module)
{
for (auto &function : module.GetFunctions())
{
if (function)
RunPeephole(*function);
}
}
} // namespace mir