xref: /freebsd/contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMacroFusion.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
15ffd83dbSDimitry Andric //===- PPCMacroFusion.cpp - PowerPC Macro Fusion --------------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric /// \file This file contains the PowerPC implementation of the DAG scheduling
105ffd83dbSDimitry Andric ///  mutation to pair instructions back to back.
115ffd83dbSDimitry Andric //
125ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
135ffd83dbSDimitry Andric 
145ffd83dbSDimitry Andric #include "PPC.h"
155ffd83dbSDimitry Andric #include "PPCSubtarget.h"
165ffd83dbSDimitry Andric #include "llvm/ADT/DenseSet.h"
175ffd83dbSDimitry Andric #include "llvm/CodeGen/MacroFusion.h"
1881ad6265SDimitry Andric #include "llvm/CodeGen/ScheduleDAGMutation.h"
19*bdd1243dSDimitry Andric #include <optional>
205ffd83dbSDimitry Andric 
215ffd83dbSDimitry Andric using namespace llvm;
225ffd83dbSDimitry Andric namespace {
235ffd83dbSDimitry Andric 
245ffd83dbSDimitry Andric class FusionFeature {
255ffd83dbSDimitry Andric public:
265ffd83dbSDimitry Andric   typedef SmallDenseSet<unsigned> FusionOpSet;
275ffd83dbSDimitry Andric 
285ffd83dbSDimitry Andric   enum FusionKind {
295ffd83dbSDimitry Andric   #define FUSION_KIND(KIND) FK_##KIND
305ffd83dbSDimitry Andric   #define FUSION_FEATURE(KIND, HAS_FEATURE, DEP_OP_IDX, OPSET1, OPSET2) \
315ffd83dbSDimitry Andric     FUSION_KIND(KIND),
325ffd83dbSDimitry Andric   #include "PPCMacroFusion.def"
335ffd83dbSDimitry Andric   FUSION_KIND(END)
345ffd83dbSDimitry Andric   };
355ffd83dbSDimitry Andric private:
365ffd83dbSDimitry Andric   // Each fusion feature is assigned with one fusion kind. All the
375ffd83dbSDimitry Andric   // instructions with the same fusion kind have the same fusion characteristic.
385ffd83dbSDimitry Andric   FusionKind Kd;
395ffd83dbSDimitry Andric   // True if this feature is enabled.
405ffd83dbSDimitry Andric   bool Supported;
415ffd83dbSDimitry Andric   // li rx, si
425ffd83dbSDimitry Andric   // load rt, ra, rx
435ffd83dbSDimitry Andric   // The dependent operand index in the second op(load). And the negative means
445ffd83dbSDimitry Andric   // it could be any one.
455ffd83dbSDimitry Andric   int DepOpIdx;
465ffd83dbSDimitry Andric   // The first fusion op set.
475ffd83dbSDimitry Andric   FusionOpSet OpSet1;
485ffd83dbSDimitry Andric   // The second fusion op set.
495ffd83dbSDimitry Andric   FusionOpSet OpSet2;
505ffd83dbSDimitry Andric public:
515ffd83dbSDimitry Andric   FusionFeature(FusionKind Kind, bool HasFeature, int Index,
525ffd83dbSDimitry Andric                 const FusionOpSet &First, const FusionOpSet &Second) :
535ffd83dbSDimitry Andric     Kd(Kind), Supported(HasFeature), DepOpIdx(Index), OpSet1(First),
545ffd83dbSDimitry Andric     OpSet2(Second) {}
555ffd83dbSDimitry Andric 
56e8d8bef9SDimitry Andric   bool hasOp1(unsigned Opc) const { return OpSet1.contains(Opc); }
57e8d8bef9SDimitry Andric   bool hasOp2(unsigned Opc) const { return OpSet2.contains(Opc); }
585ffd83dbSDimitry Andric   bool isSupported() const { return Supported; }
59*bdd1243dSDimitry Andric   std::optional<unsigned> depOpIdx() const {
605ffd83dbSDimitry Andric     if (DepOpIdx < 0)
61*bdd1243dSDimitry Andric       return std::nullopt;
625ffd83dbSDimitry Andric     return DepOpIdx;
635ffd83dbSDimitry Andric   }
645ffd83dbSDimitry Andric 
655ffd83dbSDimitry Andric   FusionKind getKind() const { return Kd; }
665ffd83dbSDimitry Andric };
675ffd83dbSDimitry Andric 
685ffd83dbSDimitry Andric static bool matchingRegOps(const MachineInstr &FirstMI,
695ffd83dbSDimitry Andric                            int FirstMIOpIndex,
705ffd83dbSDimitry Andric                            const MachineInstr &SecondMI,
715ffd83dbSDimitry Andric                            int SecondMIOpIndex) {
725ffd83dbSDimitry Andric   const MachineOperand &Op1 = FirstMI.getOperand(FirstMIOpIndex);
735ffd83dbSDimitry Andric   const MachineOperand &Op2 = SecondMI.getOperand(SecondMIOpIndex);
745ffd83dbSDimitry Andric   if (!Op1.isReg() || !Op2.isReg())
755ffd83dbSDimitry Andric     return false;
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric   return Op1.getReg() == Op2.getReg();
785ffd83dbSDimitry Andric }
795ffd83dbSDimitry Andric 
80349cc55cSDimitry Andric static bool matchingImmOps(const MachineInstr &MI,
81349cc55cSDimitry Andric                            int MIOpIndex,
82349cc55cSDimitry Andric                            int64_t Expect,
83349cc55cSDimitry Andric                            unsigned ExtendFrom = 64) {
84349cc55cSDimitry Andric   const MachineOperand &Op = MI.getOperand(MIOpIndex);
85349cc55cSDimitry Andric   if (!Op.isImm())
86349cc55cSDimitry Andric     return false;
87349cc55cSDimitry Andric   int64_t Imm = Op.getImm();
88349cc55cSDimitry Andric   if (ExtendFrom < 64)
89349cc55cSDimitry Andric     Imm = SignExtend64(Imm, ExtendFrom);
90349cc55cSDimitry Andric   return Imm == Expect;
91349cc55cSDimitry Andric }
92349cc55cSDimitry Andric 
935ffd83dbSDimitry Andric // Return true if the FirstMI meets the constraints of SecondMI according to
945ffd83dbSDimitry Andric // fusion specification.
955ffd83dbSDimitry Andric static bool checkOpConstraints(FusionFeature::FusionKind Kd,
965ffd83dbSDimitry Andric                                const MachineInstr &FirstMI,
975ffd83dbSDimitry Andric                                const MachineInstr &SecondMI) {
985ffd83dbSDimitry Andric   switch (Kd) {
995ffd83dbSDimitry Andric   // The hardware didn't require any specific check for the fused instructions'
1005ffd83dbSDimitry Andric   // operands. Therefore, return true to indicate that, it is fusable.
1015ffd83dbSDimitry Andric   default: return true;
1025ffd83dbSDimitry Andric   // [addi rt,ra,si - lxvd2x xt,ra,rb] etc.
1035ffd83dbSDimitry Andric   case FusionFeature::FK_AddiLoad: {
1045ffd83dbSDimitry Andric     // lxvd2x(ra) cannot be zero
1055ffd83dbSDimitry Andric     const MachineOperand &RA = SecondMI.getOperand(1);
1065ffd83dbSDimitry Andric     if (!RA.isReg())
1075ffd83dbSDimitry Andric       return true;
1085ffd83dbSDimitry Andric 
109*bdd1243dSDimitry Andric     return RA.getReg().isVirtual() ||
1105ffd83dbSDimitry Andric            (RA.getReg() != PPC::ZERO && RA.getReg() != PPC::ZERO8);
1115ffd83dbSDimitry Andric   }
1125ffd83dbSDimitry Andric   // [addis rt,ra,si - ld rt,ds(ra)] etc.
1135ffd83dbSDimitry Andric   case FusionFeature::FK_AddisLoad: {
1145ffd83dbSDimitry Andric     const MachineOperand &RT = SecondMI.getOperand(0);
1155ffd83dbSDimitry Andric     if (!RT.isReg())
1165ffd83dbSDimitry Andric       return true;
1175ffd83dbSDimitry Andric 
1185ffd83dbSDimitry Andric     // Only check it for non-virtual register.
119*bdd1243dSDimitry Andric     if (!RT.getReg().isVirtual())
1205ffd83dbSDimitry Andric       // addis(rt) = ld(ra) = ld(rt)
1215ffd83dbSDimitry Andric       // ld(rt) cannot be zero
1225ffd83dbSDimitry Andric       if (!matchingRegOps(SecondMI, 0, SecondMI, 2) ||
1235ffd83dbSDimitry Andric           (RT.getReg() == PPC::ZERO || RT.getReg() == PPC::ZERO8))
1245ffd83dbSDimitry Andric           return false;
1255ffd83dbSDimitry Andric 
1265ffd83dbSDimitry Andric     // addis(si) first 12 bits must be all 1s or all 0s
1275ffd83dbSDimitry Andric     const MachineOperand &SI = FirstMI.getOperand(2);
1285ffd83dbSDimitry Andric     if (!SI.isImm())
1295ffd83dbSDimitry Andric       return true;
1305ffd83dbSDimitry Andric     int64_t Imm = SI.getImm();
1315ffd83dbSDimitry Andric     if (((Imm & 0xFFF0) != 0) && ((Imm & 0xFFF0) != 0xFFF0))
1325ffd83dbSDimitry Andric       return false;
1335ffd83dbSDimitry Andric 
1345ffd83dbSDimitry Andric     // If si = 1111111111110000 and the msb of the d/ds field of the load equals
1355ffd83dbSDimitry Andric     // 1, then fusion does not occur.
1365ffd83dbSDimitry Andric     if ((Imm & 0xFFF0) == 0xFFF0) {
1375ffd83dbSDimitry Andric       const MachineOperand &D = SecondMI.getOperand(1);
1385ffd83dbSDimitry Andric       if (!D.isImm())
1395ffd83dbSDimitry Andric         return true;
1405ffd83dbSDimitry Andric 
1415ffd83dbSDimitry Andric       // 14 bit for DS field, while 16 bit for D field.
1425ffd83dbSDimitry Andric       int MSB = 15;
1435ffd83dbSDimitry Andric       if (SecondMI.getOpcode() == PPC::LD)
1445ffd83dbSDimitry Andric         MSB = 13;
1455ffd83dbSDimitry Andric 
1465ffd83dbSDimitry Andric       return (D.getImm() & (1ULL << MSB)) == 0;
1475ffd83dbSDimitry Andric     }
1485ffd83dbSDimitry Andric     return true;
1495ffd83dbSDimitry Andric   }
150349cc55cSDimitry Andric 
151349cc55cSDimitry Andric   case FusionFeature::FK_SldiAdd:
152349cc55cSDimitry Andric     return (matchingImmOps(FirstMI, 2, 3) && matchingImmOps(FirstMI, 3, 60)) ||
153349cc55cSDimitry Andric            (matchingImmOps(FirstMI, 2, 6) && matchingImmOps(FirstMI, 3, 57));
1544824e7fdSDimitry Andric 
1554824e7fdSDimitry Andric   // rldicl rx, ra, 1, 0  - xor
1564824e7fdSDimitry Andric   case FusionFeature::FK_RotateLeftXor:
1574824e7fdSDimitry Andric     return matchingImmOps(FirstMI, 2, 1) && matchingImmOps(FirstMI, 3, 0);
1584824e7fdSDimitry Andric 
1594824e7fdSDimitry Andric   // rldicr rx, ra, 1, 63 - xor
1604824e7fdSDimitry Andric   case FusionFeature::FK_RotateRightXor:
1614824e7fdSDimitry Andric     return matchingImmOps(FirstMI, 2, 1) && matchingImmOps(FirstMI, 3, 63);
1624824e7fdSDimitry Andric 
1634824e7fdSDimitry Andric   // We actually use CMPW* and CMPD*, 'l' doesn't exist as an operand in instr.
1644824e7fdSDimitry Andric 
1654824e7fdSDimitry Andric   // { lbz,lbzx,lhz,lhzx,lwz,lwzx } - cmpi 0,1,rx,{ 0,1,-1 }
1664824e7fdSDimitry Andric   // { lbz,lbzx,lhz,lhzx,lwz,lwzx } - cmpli 0,L,rx,{ 0,1 }
1674824e7fdSDimitry Andric   case FusionFeature::FK_LoadCmp1:
1684824e7fdSDimitry Andric   // { ld,ldx } - cmpi 0,1,rx,{ 0,1,-1 }
1694824e7fdSDimitry Andric   // { ld,ldx } - cmpli 0,1,rx,{ 0,1 }
1704824e7fdSDimitry Andric   case FusionFeature::FK_LoadCmp2: {
1714824e7fdSDimitry Andric     const MachineOperand &BT = SecondMI.getOperand(0);
172*bdd1243dSDimitry Andric     if (!BT.isReg() || (!BT.getReg().isVirtual() && BT.getReg() != PPC::CR0))
1734824e7fdSDimitry Andric       return false;
1744824e7fdSDimitry Andric     if (SecondMI.getOpcode() == PPC::CMPDI &&
1754824e7fdSDimitry Andric         matchingImmOps(SecondMI, 2, -1, 16))
1764824e7fdSDimitry Andric       return true;
1774824e7fdSDimitry Andric     return matchingImmOps(SecondMI, 2, 0) || matchingImmOps(SecondMI, 2, 1);
1784824e7fdSDimitry Andric   }
1794824e7fdSDimitry Andric 
1804824e7fdSDimitry Andric   // { lha,lhax,lwa,lwax } - cmpi 0,L,rx,{ 0,1,-1 }
1814824e7fdSDimitry Andric   case FusionFeature::FK_LoadCmp3: {
1824824e7fdSDimitry Andric     const MachineOperand &BT = SecondMI.getOperand(0);
183*bdd1243dSDimitry Andric     if (!BT.isReg() || (!BT.getReg().isVirtual() && BT.getReg() != PPC::CR0))
1844824e7fdSDimitry Andric       return false;
1854824e7fdSDimitry Andric     return matchingImmOps(SecondMI, 2, 0) || matchingImmOps(SecondMI, 2, 1) ||
1864824e7fdSDimitry Andric            matchingImmOps(SecondMI, 2, -1, 16);
1874824e7fdSDimitry Andric   }
1884824e7fdSDimitry Andric 
1894824e7fdSDimitry Andric   // mtctr - { bcctr,bcctrl }
1904824e7fdSDimitry Andric   case FusionFeature::FK_ZeroMoveCTR:
1914824e7fdSDimitry Andric     // ( mtctr rx ) is alias of ( mtspr 9, rx )
1924824e7fdSDimitry Andric     return (FirstMI.getOpcode() != PPC::MTSPR &&
1934824e7fdSDimitry Andric             FirstMI.getOpcode() != PPC::MTSPR8) ||
1944824e7fdSDimitry Andric            matchingImmOps(FirstMI, 0, 9);
1954824e7fdSDimitry Andric 
1964824e7fdSDimitry Andric   // mtlr - { bclr,bclrl }
1974824e7fdSDimitry Andric   case FusionFeature::FK_ZeroMoveLR:
1984824e7fdSDimitry Andric     // ( mtlr rx ) is alias of ( mtspr 8, rx )
1994824e7fdSDimitry Andric     return (FirstMI.getOpcode() != PPC::MTSPR &&
2004824e7fdSDimitry Andric             FirstMI.getOpcode() != PPC::MTSPR8) ||
2014824e7fdSDimitry Andric            matchingImmOps(FirstMI, 0, 8);
2024824e7fdSDimitry Andric 
2034824e7fdSDimitry Andric   // addis rx,ra,si - addi rt,rx,SI, SI >= 0
2044824e7fdSDimitry Andric   case FusionFeature::FK_AddisAddi: {
2054824e7fdSDimitry Andric     const MachineOperand &RA = FirstMI.getOperand(1);
2064824e7fdSDimitry Andric     const MachineOperand &SI = SecondMI.getOperand(2);
2074824e7fdSDimitry Andric     if (!SI.isImm() || !RA.isReg())
2084824e7fdSDimitry Andric       return false;
2094824e7fdSDimitry Andric     if (RA.getReg() == PPC::ZERO || RA.getReg() == PPC::ZERO8)
2104824e7fdSDimitry Andric       return false;
2114824e7fdSDimitry Andric     return SignExtend64(SI.getImm(), 16) >= 0;
2124824e7fdSDimitry Andric   }
2134824e7fdSDimitry Andric 
2144824e7fdSDimitry Andric   // addi rx,ra,si - addis rt,rx,SI, ra > 0, SI >= 2
2154824e7fdSDimitry Andric   case FusionFeature::FK_AddiAddis: {
2164824e7fdSDimitry Andric     const MachineOperand &RA = FirstMI.getOperand(1);
2174824e7fdSDimitry Andric     const MachineOperand &SI = FirstMI.getOperand(2);
2184824e7fdSDimitry Andric     if (!SI.isImm() || !RA.isReg())
2194824e7fdSDimitry Andric       return false;
2204824e7fdSDimitry Andric     if (RA.getReg() == PPC::ZERO || RA.getReg() == PPC::ZERO8)
2214824e7fdSDimitry Andric       return false;
2224824e7fdSDimitry Andric     int64_t ExtendedSI = SignExtend64(SI.getImm(), 16);
2234824e7fdSDimitry Andric     return ExtendedSI >= 2;
2244824e7fdSDimitry Andric   }
2255ffd83dbSDimitry Andric   }
2265ffd83dbSDimitry Andric 
2275ffd83dbSDimitry Andric   llvm_unreachable("All the cases should have been handled");
2285ffd83dbSDimitry Andric   return true;
2295ffd83dbSDimitry Andric }
2305ffd83dbSDimitry Andric 
2315ffd83dbSDimitry Andric /// Check if the instr pair, FirstMI and SecondMI, should be fused together.
2325ffd83dbSDimitry Andric /// Given SecondMI, when FirstMI is unspecified, then check if SecondMI may be
2335ffd83dbSDimitry Andric /// part of a fused pair at all.
2345ffd83dbSDimitry Andric static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
2355ffd83dbSDimitry Andric                                    const TargetSubtargetInfo &TSI,
2365ffd83dbSDimitry Andric                                    const MachineInstr *FirstMI,
2375ffd83dbSDimitry Andric                                    const MachineInstr &SecondMI) {
2385ffd83dbSDimitry Andric   // We use the PPC namespace to avoid the need to prefix opcodes with PPC:: in
2395ffd83dbSDimitry Andric   // the def file.
2405ffd83dbSDimitry Andric   using namespace PPC;
2415ffd83dbSDimitry Andric 
2425ffd83dbSDimitry Andric   const PPCSubtarget &ST = static_cast<const PPCSubtarget&>(TSI);
2435ffd83dbSDimitry Andric   static const FusionFeature FusionFeatures[] = {
2445ffd83dbSDimitry Andric   #define FUSION_FEATURE(KIND, HAS_FEATURE, DEP_OP_IDX, OPSET1, OPSET2) { \
2455ffd83dbSDimitry Andric     FusionFeature::FUSION_KIND(KIND), ST.HAS_FEATURE(), DEP_OP_IDX, { OPSET1 },\
2465ffd83dbSDimitry Andric     { OPSET2 } },
2475ffd83dbSDimitry Andric    #include "PPCMacroFusion.def"
2485ffd83dbSDimitry Andric   };
2495ffd83dbSDimitry Andric   #undef FUSION_KIND
2505ffd83dbSDimitry Andric 
2515ffd83dbSDimitry Andric   for (auto &Feature : FusionFeatures) {
2525ffd83dbSDimitry Andric     // Skip if the feature is not supported.
2535ffd83dbSDimitry Andric     if (!Feature.isSupported())
2545ffd83dbSDimitry Andric       continue;
2555ffd83dbSDimitry Andric 
2565ffd83dbSDimitry Andric     // Only when the SecondMI is fusable, we are starting to look for the
2575ffd83dbSDimitry Andric     // fusable FirstMI.
2585ffd83dbSDimitry Andric     if (Feature.hasOp2(SecondMI.getOpcode())) {
2595ffd83dbSDimitry Andric       // If FirstMI == nullptr, that means, we're only checking whether SecondMI
2605ffd83dbSDimitry Andric       // can be fused at all.
2615ffd83dbSDimitry Andric       if (!FirstMI)
2625ffd83dbSDimitry Andric         return true;
2635ffd83dbSDimitry Andric 
2645ffd83dbSDimitry Andric       // Checking if the FirstMI is fusable with the SecondMI.
2655ffd83dbSDimitry Andric       if (!Feature.hasOp1(FirstMI->getOpcode()))
2665ffd83dbSDimitry Andric         continue;
2675ffd83dbSDimitry Andric 
2685ffd83dbSDimitry Andric       auto DepOpIdx = Feature.depOpIdx();
26981ad6265SDimitry Andric       if (DepOpIdx) {
2705ffd83dbSDimitry Andric         // Checking if the result of the FirstMI is the desired operand of the
2715ffd83dbSDimitry Andric         // SecondMI if the DepOpIdx is set. Otherwise, ignore it.
2725ffd83dbSDimitry Andric         if (!matchingRegOps(*FirstMI, 0, SecondMI, *DepOpIdx))
2735ffd83dbSDimitry Andric           return false;
2745ffd83dbSDimitry Andric       }
2755ffd83dbSDimitry Andric 
2765ffd83dbSDimitry Andric       // Checking more on the instruction operands.
2775ffd83dbSDimitry Andric       if (checkOpConstraints(Feature.getKind(), *FirstMI, SecondMI))
2785ffd83dbSDimitry Andric         return true;
2795ffd83dbSDimitry Andric     }
2805ffd83dbSDimitry Andric   }
2815ffd83dbSDimitry Andric 
2825ffd83dbSDimitry Andric   return false;
2835ffd83dbSDimitry Andric }
2845ffd83dbSDimitry Andric 
2855ffd83dbSDimitry Andric } // end anonymous namespace
2865ffd83dbSDimitry Andric 
2875ffd83dbSDimitry Andric namespace llvm {
2885ffd83dbSDimitry Andric 
2895ffd83dbSDimitry Andric std::unique_ptr<ScheduleDAGMutation> createPowerPCMacroFusionDAGMutation() {
2905ffd83dbSDimitry Andric   return createMacroFusionDAGMutation(shouldScheduleAdjacent);
2915ffd83dbSDimitry Andric }
2925ffd83dbSDimitry Andric 
2935ffd83dbSDimitry Andric } // end namespace llvm
294