80#define DEBUG_TYPE "aarch64-mi-peephole-opt"
84class AArch64MIPeepholeOptImpl {
96 using OpcodePair = std::pair<unsigned, unsigned>;
98 using SplitAndOpcFunc =
99 std::function<std::optional<OpcodePair>(
T,
unsigned,
T &,
T &)>;
101 std::function<void(
MachineInstr &, OpcodePair,
unsigned,
unsigned,
118 template <
typename T>
120 SplitAndOpcFunc<T> SplitAndOpc, BuildMIFunc BuildInstr);
125 template <
typename T>
126 bool visitADDSUB(
unsigned PosOpc,
unsigned NegOpc,
MachineInstr &
MI);
127 template <
typename T>
128 bool visitADDSSUBS(OpcodePair PosOpcs, OpcodePair NegOpcs,
MachineInstr &
MI);
131 enum class SplitStrategy {
135 template <
typename T>
137 SplitStrategy Strategy,
unsigned OtherOpc = 0);
156 return "AArch64 MI Peephole Optimization pass";
166char AArch64MIPeepholeOptLegacy::ID = 0;
171 "AArch64 MI Peephole Optimization",
false,
false)
175 T UImm =
static_cast<T>(
Imm);
176 assert(UImm && (UImm != ~
static_cast<T>(0)) &&
"Invalid immediate!");
207 assert(
Imm && (
Imm != ~
static_cast<T>(0)) &&
"Invalid immediate!");
214 unsigned LowestGapBitUnset =
218 assert(LowestGapBitUnset <
sizeof(
T) * CHAR_BIT &&
"Undefined behaviour!");
219 T NewImm1 = (
static_cast<T>(1) << LowestGapBitUnset) -
234bool AArch64MIPeepholeOptImpl::trySplitLogicalImm(
unsigned Opc,
236 SplitStrategy Strategy,
248 return splitTwoPartImm<T>(
251 T &Imm1) -> std::optional<OpcodePair> {
260 if (Insn.
size() == 1)
263 bool SplitSucc =
false;
265 case SplitStrategy::Intersect:
266 SplitSucc = splitBitmaskImm(
Imm,
RegSize, Imm0, Imm1);
268 case SplitStrategy::Disjoint:
273 return std::make_pair(
Opc, !OtherOpc ?
Opc : OtherOpc);
276 [&
TII =
TII](MachineInstr &
MI, OpcodePair Opcode,
unsigned Imm0,
290bool AArch64MIPeepholeOptImpl::visitORR(MachineInstr &
MI) {
295 if (
MI.getOperand(3).getImm() != 0)
298 if (
MI.getOperand(1).getReg() != AArch64::WZR)
301 if (
MI.getOperand(2).getSubReg())
318 if (SrcMI->
getOpcode() == TargetOpcode::COPY &&
325 if (RC != &AArch64::FPR32RegClass &&
326 ((RC != &AArch64::FPR64RegClass && RC != &AArch64::FPR128RegClass &&
327 RC != &AArch64::ZPRRegClass) ||
334 TII->get(TargetOpcode::COPY), CpySrc)
344 else if (SrcMI->
getOpcode() <= TargetOpcode::GENERIC_OP_END)
352 MI.eraseFromParent();
357bool AArch64MIPeepholeOptImpl::visitCSEL(MachineInstr &
MI) {
359 if (
MI.getOperand(1).getReg() !=
MI.getOperand(2).getReg())
363 MI.getOpcode() == AArch64::CSELXr ? AArch64::XZR : AArch64::WZR;
365 MI.getOpcode() == AArch64::CSELXr ? AArch64::ORRXrs : AArch64::ORRWrs;
368 .
addReg(
MI.getOperand(0).getReg(), RegState::Define)
373 MI.eraseFromParent();
377bool AArch64MIPeepholeOptImpl::visitINSERT(MachineInstr &
MI) {
385 if (!
MI.isRegTiedToDefOperand(1))
404 if ((SrcMI->
getOpcode() <= TargetOpcode::GENERIC_OP_END) ||
405 !AArch64::GPR64allRegClass.hasSubClassEq(RC))
409 MachineInstr *SubregMI =
411 TII->get(TargetOpcode::SUBREG_TO_REG), DstReg)
412 .
add(
MI.getOperand(2))
413 .
add(
MI.getOperand(3));
416 MI.eraseFromParent();
425 if ((
Imm & 0xfff000) == 0 || (
Imm & 0xfff) == 0 ||
426 (
Imm & ~
static_cast<T>(0xffffff)) != 0)
432 if (Insn.
size() == 1)
436 Imm0 = (
Imm >> 12) & 0xfff;
442bool AArch64MIPeepholeOptImpl::visitADDSUB(
unsigned PosOpc,
unsigned NegOpc,
460 if (
MI.getOperand(1).getReg() == AArch64::XZR ||
461 MI.getOperand(1).getReg() == AArch64::WZR)
464 return splitTwoPartImm<T>(
467 T &Imm1) -> std::optional<OpcodePair> {
469 return std::make_pair(PosOpc, PosOpc);
471 return std::make_pair(NegOpc, NegOpc);
474 [&
TII =
TII](MachineInstr &
MI, OpcodePair Opcode,
unsigned Imm0,
491bool AArch64MIPeepholeOptImpl::visitADDSSUBS(OpcodePair PosOpcs,
497 if (
MI.getOperand(1).getReg() == AArch64::XZR ||
498 MI.getOperand(1).getReg() == AArch64::WZR)
501 return splitTwoPartImm<T>(
505 T &Imm1) -> std::optional<OpcodePair> {
515 MachineInstr *SrcMI = MRI->
getVRegDef(
MI.getOperand(1).getReg());
519 if (!NZCVUsed || NZCVUsed->C || NZCVUsed->V)
523 [&
TII =
TII](MachineInstr &
MI, OpcodePair Opcode,
unsigned Imm0,
541bool AArch64MIPeepholeOptImpl::checkMovImmInstr(MachineInstr &
MI,
542 MachineInstr *&MovMI,
543 MachineInstr *&SubregToRegMI) {
547 if (L && !
L->isLoopInvariant(
MI))
556 SubregToRegMI =
nullptr;
557 if (MovMI->
getOpcode() == TargetOpcode::SUBREG_TO_REG) {
558 SubregToRegMI = MovMI;
564 if (MovMI->
getOpcode() != AArch64::MOVi32imm &&
565 MovMI->
getOpcode() != AArch64::MOVi64imm)
580bool AArch64MIPeepholeOptImpl::splitTwoPartImm(MachineInstr &
MI,
581 SplitAndOpcFunc<T> SplitAndOpc,
582 BuildMIFunc BuildInstr) {
585 "Invalid RegSize for legal immediate peephole optimization");
588 MachineInstr *MovMI, *SubregToRegMI;
589 if (!checkMovImmInstr(
MI, MovMI, SubregToRegMI))
601 if (
auto R = SplitAndOpc(
Imm,
RegSize, Imm0, Imm1))
613 TII->getRegClass(
TII->get(Opcode.first), 0);
615 TII->getRegClass(
TII->get(Opcode.first), 1);
617 (Opcode.first == Opcode.second)
621 (Opcode.first == Opcode.second)
622 ? FirstInstrOperandRC
623 :
TII->getRegClass(
TII->get(Opcode.second), 1);
638 if (DstReg != NewDstReg)
642 BuildInstr(
MI, Opcode, Imm0, Imm1, SrcReg, NewTmpReg, NewDstReg);
646 if (DstReg != NewDstReg) {
648 MI.getOperand(0).setReg(DstReg);
652 MI.eraseFromParent();
660bool AArch64MIPeepholeOptImpl::visitINSviGPR(MachineInstr &
MI,
unsigned Opc) {
677 if (!SrcMI || SrcMI->
getOpcode() != TargetOpcode::COPY)
684 &AArch64::FPR128RegClass) {
692 MachineInstr *INSvilaneMI =
694 .
add(
MI.getOperand(1))
695 .
add(
MI.getOperand(2))
701 MI.eraseFromParent();
711 if (!
MI->getOperand(0).isReg() || !
MI->getOperand(0).isDef())
714 if (RC != &AArch64::FPR64RegClass)
716 if (
MI->getOpcode() == TargetOpcode::COPY) {
720 if (
SrcOp.getSubReg())
723 auto IsGPR64Like = [&]() ->
bool {
725 return AArch64::GPR64allRegClass.hasSubClassEq(
727 return AArch64::GPR64allRegClass.contains(SrcReg);
731 assert(
TII &&
"Expected InstrInfo when materializing COPYs");
734 bool SrcKill = SrcMO.
isKill();
736 if (MRI->
getRegClass(SrcReg) != &AArch64::GPR64RegClass) {
741 TII->get(TargetOpcode::COPY), NewSrc)
746 }
else if (!AArch64::GPR64RegClass.
contains(SrcReg)) {
754 MI->setDesc(
TII->get(AArch64::FMOVXDr));
757 return MI->getOpcode() > TargetOpcode::GENERIC_OP_END;
760bool AArch64MIPeepholeOptImpl::visitINSvi64lane(MachineInstr &
MI) {
768 MachineInstr *Low64MI = MRI->
getVRegDef(
MI.getOperand(1).getReg());
769 if (!Low64MI || Low64MI->
getOpcode() != AArch64::INSERT_SUBREG)
789 if (!High64MI || High64MI->
getOpcode() != AArch64::INSERT_SUBREG)
792 if (High64MI && High64MI->
getOpcode() == TargetOpcode::COPY)
794 if (!High64MI || (High64MI->
getOpcode() != AArch64::MOVID &&
795 High64MI->
getOpcode() != AArch64::MOVIv2d_ns))
807 MI.eraseFromParent();
812bool AArch64MIPeepholeOptImpl::visitFMOVDr(MachineInstr &
MI) {
826 MI.eraseFromParent();
831bool AArch64MIPeepholeOptImpl::visitUBFMXri(MachineInstr &
MI) {
834 int64_t Immr =
MI.getOperand(2).getImm();
835 int64_t Imms =
MI.getOperand(3).getImm();
837 bool IsLSR = Imms == 31 && Immr <= Imms;
838 bool IsLSL = Immr == Imms + 33;
839 if (!IsLSR && !IsLSL)
847 TII->getRegClass(
TII->get(
MI.getOpcode()), 0);
849 TRI->getSubRegisterClass(DstRC64, AArch64::sub_32);
850 assert(DstRC32 &&
"Destination register class of UBFMXri doesn't have a "
851 "sub_32 subregister class");
854 TII->getRegClass(
TII->get(
MI.getOpcode()), 1);
856 TRI->getSubRegisterClass(SrcRC64, AArch64::sub_32);
857 assert(SrcRC32 &&
"Source register class of UBFMXri doesn't have a sub_32 "
858 "subregister class");
860 Register DstReg64 =
MI.getOperand(0).getReg();
862 Register SrcReg64 =
MI.getOperand(1).getReg();
867 .
addReg(SrcReg64, {}, AArch64::sub_32);
874 TII->get(AArch64::SUBREG_TO_REG), DstReg64)
877 MI.eraseFromParent();
884bool AArch64MIPeepholeOptImpl::visitCopy(MachineInstr &
MI) {
885 Register InputReg =
MI.getOperand(1).getReg();
886 if (
MI.getOperand(1).getSubReg() != AArch64::sub_32 ||
891 SmallPtrSet<MachineInstr *, 4> DeadInstrs;
903 auto getSXTWSrcReg = [](MachineInstr *SrcMI) ->
Register {
904 if (SrcMI->
getOpcode() != AArch64::SBFMXri ||
907 return AArch64::NoRegister;
911 auto getUXTWSrcReg = [&](MachineInstr *SrcMI) ->
Register {
912 if (SrcMI->
getOpcode() != AArch64::SUBREG_TO_REG ||
915 return AArch64::NoRegister;
917 if (!Orr || Orr->
getOpcode() != AArch64::ORRWrr ||
920 return AArch64::NoRegister;
922 if (!Cpy || Cpy->
getOpcode() != AArch64::COPY ||
924 return AArch64::NoRegister;
929 Register SrcReg = getSXTWSrcReg(SrcMI);
931 SrcReg = getUXTWSrcReg(SrcMI);
937 MI.getOperand(1).setReg(SrcReg);
939 for (
auto *DeadMI : DeadInstrs) {
941 DeadMI->eraseFromParent();
947 TII =
static_cast<const AArch64InstrInfo *
>(MF.
getSubtarget().getInstrInfo());
948 TRI =
static_cast<const AArch64RegisterInfo *
>(
952 assert(MRI->
isSSA() &&
"Expected to be run on SSA form!");
956 for (MachineBasicBlock &
MBB : MF) {
958 switch (
MI.getOpcode()) {
961 case AArch64::INSERT_SUBREG:
964 case AArch64::ANDWrr:
965 Changed |= trySplitLogicalImm<uint32_t>(AArch64::ANDWri,
MI,
966 SplitStrategy::Intersect);
968 case AArch64::ANDXrr:
969 Changed |= trySplitLogicalImm<uint64_t>(AArch64::ANDXri,
MI,
970 SplitStrategy::Intersect);
972 case AArch64::ANDSWrr:
973 Changed |= trySplitLogicalImm<uint32_t>(
974 AArch64::ANDWri,
MI, SplitStrategy::Intersect, AArch64::ANDSWri);
976 case AArch64::ANDSXrr:
977 Changed |= trySplitLogicalImm<uint64_t>(
978 AArch64::ANDXri,
MI, SplitStrategy::Intersect, AArch64::ANDSXri);
980 case AArch64::EORWrr:
981 Changed |= trySplitLogicalImm<uint32_t>(AArch64::EORWri,
MI,
982 SplitStrategy::Disjoint);
984 case AArch64::EORXrr:
985 Changed |= trySplitLogicalImm<uint64_t>(AArch64::EORXri,
MI,
986 SplitStrategy::Disjoint);
988 case AArch64::ORRWrr:
989 Changed |= trySplitLogicalImm<uint32_t>(AArch64::ORRWri,
MI,
990 SplitStrategy::Disjoint);
992 case AArch64::ORRXrr:
993 Changed |= trySplitLogicalImm<uint64_t>(AArch64::ORRXri,
MI,
994 SplitStrategy::Disjoint);
996 case AArch64::ORRWrs:
999 case AArch64::ADDWrr:
1000 Changed |= visitADDSUB<uint32_t>(AArch64::ADDWri, AArch64::SUBWri,
MI);
1002 case AArch64::SUBWrr:
1003 Changed |= visitADDSUB<uint32_t>(AArch64::SUBWri, AArch64::ADDWri,
MI);
1005 case AArch64::ADDXrr:
1006 Changed |= visitADDSUB<uint64_t>(AArch64::ADDXri, AArch64::SUBXri,
MI);
1008 case AArch64::SUBXrr:
1009 Changed |= visitADDSUB<uint64_t>(AArch64::SUBXri, AArch64::ADDXri,
MI);
1011 case AArch64::ADDSWrr:
1013 visitADDSSUBS<uint32_t>({AArch64::ADDWri, AArch64::ADDSWri},
1014 {AArch64::SUBWri, AArch64::SUBSWri},
MI);
1016 case AArch64::SUBSWrr:
1018 visitADDSSUBS<uint32_t>({AArch64::SUBWri, AArch64::SUBSWri},
1019 {AArch64::ADDWri, AArch64::ADDSWri},
MI);
1021 case AArch64::ADDSXrr:
1023 visitADDSSUBS<uint64_t>({AArch64::ADDXri, AArch64::ADDSXri},
1024 {AArch64::SUBXri, AArch64::SUBSXri},
MI);
1026 case AArch64::SUBSXrr:
1028 visitADDSSUBS<uint64_t>({AArch64::SUBXri, AArch64::SUBSXri},
1029 {AArch64::ADDXri, AArch64::ADDSXri},
MI);
1031 case AArch64::CSELWr:
1032 case AArch64::CSELXr:
1035 case AArch64::INSvi64gpr:
1036 Changed |= visitINSviGPR(
MI, AArch64::INSvi64lane);
1038 case AArch64::INSvi32gpr:
1039 Changed |= visitINSviGPR(
MI, AArch64::INSvi32lane);
1041 case AArch64::INSvi16gpr:
1042 Changed |= visitINSviGPR(
MI, AArch64::INSvi16lane);
1044 case AArch64::INSvi8gpr:
1045 Changed |= visitINSviGPR(
MI, AArch64::INSvi8lane);
1047 case AArch64::INSvi64lane:
1050 case AArch64::FMOVDr:
1053 case AArch64::UBFMXri:
1066bool AArch64MIPeepholeOptLegacy::runOnMachineFunction(
MachineFunction &MF) {
1070 MachineLoopInfo &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
1071 return AArch64MIPeepholeOptImpl(MLI).run(MF);
1075 return new AArch64MIPeepholeOptLegacy();
1082 const bool Changed = AArch64MIPeepholeOptImpl(MLI).run(MF);
static const TargetRegisterClass * getRegClass(const MachineInstr &MI, Register Reg)
static bool is64bitDefwithZeroHigh64bit(MachineInstr *MI, MachineRegisterInfo *MRI, const AArch64InstrInfo *TII)
static bool splitDisjointBitmaskImm(T Imm, unsigned RegSize, T &Imm1Enc, T &Imm2Enc)
static bool splitAddSubImm(T Imm, unsigned RegSize, T &Imm0, T &Imm1)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
const HexagonInstrInfo * TII
Register const TargetRegisterInfo * TRI
Promote Memory to Register
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Represents analyses that only rely on functions' control flow.
FunctionPass class - This class is used to implement most global optimizations.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineInstrBuilder & addUse(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
Analysis pass that exposes the MachineLoopInfo for a machine function.
MachineOperand class - Representation of each machine instruction operand.
void setSubReg(unsigned subReg)
unsigned getSubReg() const
LLVM_ABI void setReg(Register Reg)
Change the register this operand corresponds to.
void setIsKill(bool Val=true)
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool hasOneNonDBGUse(Register RegNo) const
hasOneNonDBGUse - Return true if there is exactly one non-Debug use of the specified register.
const TargetRegisterClass * getRegClass(Register Reg) const
Return the register class of the specified virtual register.
LLVM_ABI void clearKillFlags(Register Reg) const
clearKillFlags - Iterate over all the uses of the given register and clear the kill flag from the Mac...
LLVM_ABI LLVM_READONLY MachineInstr * getVRegDef(Register Reg) const
getVRegDef - Return the machine instr that defines the specified virtual register or null if none is ...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
bool hasOneUse(Register RegNo) const
hasOneUse - Return true if there is exactly one instruction using the specified register.
LLVM_ABI const TargetRegisterClass * constrainRegClass(Register Reg, const TargetRegisterClass *RC, unsigned MinNumRegs=0)
constrainRegClass - Constrain the register class of the specified virtual register to be a common sub...
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
LLVM_ABI LLVM_READONLY MachineInstr * getUniqueVRegDef(Register Reg) const
getUniqueVRegDef - Return the unique machine instr that defines the specified virtual register or nul...
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Wrapper class representing virtual and physical registers.
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
static bool isLogicalImmediate(uint64_t imm, unsigned regSize)
isLogicalImmediate - Return true if the immediate is valid for a logical immediate instruction of the...
static uint64_t encodeLogicalImmediate(uint64_t imm, unsigned regSize)
encodeLogicalImmediate - Return the encoded immediate value for a logical immediate instruction of th...
void expandMOVImm(uint64_t Imm, unsigned BitSize, SmallVectorImpl< ImmInsnModel > &Insn)
Expand a MOVi32imm or MOVi64imm pseudo instruction to one or more real move-immediate instructions to...
This is an optimization pass for GlobalISel generic memory operations.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
constexpr RegState getKillRegState(bool B)
int countr_one(T Value)
Count the number of ones from the least significant bit to the first zero bit.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
unsigned Log2_64(uint64_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
int countr_zero(T Val)
Count number of 0's from the least significant bit to the most stopping at the first 1.
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
FunctionPass * createAArch64MIPeepholeOptLegacyPass()
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
std::optional< UsedNZCV > examineCFlagsUse(MachineInstr &MI, MachineInstr &CmpInstr, const TargetRegisterInfo &TRI, SmallVectorImpl< MachineInstr * > *CCUseInstrs=nullptr)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
RegState getRegState(const MachineOperand &RegOp)
Get all register state flags from machine operand RegOp.
MCRegisterClass TargetRegisterClass