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