1*0b57cec5SDimitry Andric //=- AArch64ConditionOptimizer.cpp - Remove useless comparisons for AArch64 -=// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This pass tries to make consecutive compares of values use same operands to 10*0b57cec5SDimitry Andric // allow CSE pass to remove duplicated instructions. For this it analyzes 11*0b57cec5SDimitry Andric // branches and adjusts comparisons with immediate values by converting: 12*0b57cec5SDimitry Andric // * GE -> GT 13*0b57cec5SDimitry Andric // * GT -> GE 14*0b57cec5SDimitry Andric // * LT -> LE 15*0b57cec5SDimitry Andric // * LE -> LT 16*0b57cec5SDimitry Andric // and adjusting immediate values appropriately. It basically corrects two 17*0b57cec5SDimitry Andric // immediate values towards each other to make them equal. 18*0b57cec5SDimitry Andric // 19*0b57cec5SDimitry Andric // Consider the following example in C: 20*0b57cec5SDimitry Andric // 21*0b57cec5SDimitry Andric // if ((a < 5 && ...) || (a > 5 && ...)) { 22*0b57cec5SDimitry Andric // ~~~~~ ~~~~~ 23*0b57cec5SDimitry Andric // ^ ^ 24*0b57cec5SDimitry Andric // x y 25*0b57cec5SDimitry Andric // 26*0b57cec5SDimitry Andric // Here both "x" and "y" expressions compare "a" with "5". When "x" evaluates 27*0b57cec5SDimitry Andric // to "false", "y" can just check flags set by the first comparison. As a 28*0b57cec5SDimitry Andric // result of the canonicalization employed by 29*0b57cec5SDimitry Andric // SelectionDAGBuilder::visitSwitchCase, DAGCombine, and other target-specific 30*0b57cec5SDimitry Andric // code, assembly ends up in the form that is not CSE friendly: 31*0b57cec5SDimitry Andric // 32*0b57cec5SDimitry Andric // ... 33*0b57cec5SDimitry Andric // cmp w8, #4 34*0b57cec5SDimitry Andric // b.gt .LBB0_3 35*0b57cec5SDimitry Andric // ... 36*0b57cec5SDimitry Andric // .LBB0_3: 37*0b57cec5SDimitry Andric // cmp w8, #6 38*0b57cec5SDimitry Andric // b.lt .LBB0_6 39*0b57cec5SDimitry Andric // ... 40*0b57cec5SDimitry Andric // 41*0b57cec5SDimitry Andric // Same assembly after the pass: 42*0b57cec5SDimitry Andric // 43*0b57cec5SDimitry Andric // ... 44*0b57cec5SDimitry Andric // cmp w8, #5 45*0b57cec5SDimitry Andric // b.ge .LBB0_3 46*0b57cec5SDimitry Andric // ... 47*0b57cec5SDimitry Andric // .LBB0_3: 48*0b57cec5SDimitry Andric // cmp w8, #5 // <-- CSE pass removes this instruction 49*0b57cec5SDimitry Andric // b.le .LBB0_6 50*0b57cec5SDimitry Andric // ... 51*0b57cec5SDimitry Andric // 52*0b57cec5SDimitry Andric // Currently only SUBS and ADDS followed by b.?? are supported. 53*0b57cec5SDimitry Andric // 54*0b57cec5SDimitry Andric // TODO: maybe handle TBNZ/TBZ the same way as CMP when used instead for "a < 0" 55*0b57cec5SDimitry Andric // TODO: handle other conditional instructions (e.g. CSET) 56*0b57cec5SDimitry Andric // TODO: allow second branching to be anything if it doesn't require adjusting 57*0b57cec5SDimitry Andric // 58*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric #include "AArch64.h" 61*0b57cec5SDimitry Andric #include "MCTargetDesc/AArch64AddressingModes.h" 62*0b57cec5SDimitry Andric #include "Utils/AArch64BaseInfo.h" 63*0b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 64*0b57cec5SDimitry Andric #include "llvm/ADT/DepthFirstIterator.h" 65*0b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 66*0b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 67*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 68*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 69*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 70*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 71*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 72*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 73*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineOperand.h" 74*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 75*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h" 76*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h" 77*0b57cec5SDimitry Andric #include "llvm/Pass.h" 78*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 79*0b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 80*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 81*0b57cec5SDimitry Andric #include <cassert> 82*0b57cec5SDimitry Andric #include <cstdlib> 83*0b57cec5SDimitry Andric #include <tuple> 84*0b57cec5SDimitry Andric 85*0b57cec5SDimitry Andric using namespace llvm; 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric #define DEBUG_TYPE "aarch64-condopt" 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric STATISTIC(NumConditionsAdjusted, "Number of conditions adjusted"); 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric namespace { 92*0b57cec5SDimitry Andric 93*0b57cec5SDimitry Andric class AArch64ConditionOptimizer : public MachineFunctionPass { 94*0b57cec5SDimitry Andric const TargetInstrInfo *TII; 95*0b57cec5SDimitry Andric MachineDominatorTree *DomTree; 96*0b57cec5SDimitry Andric const MachineRegisterInfo *MRI; 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric public: 99*0b57cec5SDimitry Andric // Stores immediate, compare instruction opcode and branch condition (in this 100*0b57cec5SDimitry Andric // order) of adjusted comparison. 101*0b57cec5SDimitry Andric using CmpInfo = std::tuple<int, unsigned, AArch64CC::CondCode>; 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric static char ID; 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric AArch64ConditionOptimizer() : MachineFunctionPass(ID) { 106*0b57cec5SDimitry Andric initializeAArch64ConditionOptimizerPass(*PassRegistry::getPassRegistry()); 107*0b57cec5SDimitry Andric } 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 110*0b57cec5SDimitry Andric MachineInstr *findSuitableCompare(MachineBasicBlock *MBB); 111*0b57cec5SDimitry Andric CmpInfo adjustCmp(MachineInstr *CmpMI, AArch64CC::CondCode Cmp); 112*0b57cec5SDimitry Andric void modifyCmp(MachineInstr *CmpMI, const CmpInfo &Info); 113*0b57cec5SDimitry Andric bool adjustTo(MachineInstr *CmpMI, AArch64CC::CondCode Cmp, MachineInstr *To, 114*0b57cec5SDimitry Andric int ToImm); 115*0b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric StringRef getPassName() const override { 118*0b57cec5SDimitry Andric return "AArch64 Condition Optimizer"; 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric }; 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric } // end anonymous namespace 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric char AArch64ConditionOptimizer::ID = 0; 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(AArch64ConditionOptimizer, "aarch64-condopt", 127*0b57cec5SDimitry Andric "AArch64 CondOpt Pass", false, false) 128*0b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) 129*0b57cec5SDimitry Andric INITIALIZE_PASS_END(AArch64ConditionOptimizer, "aarch64-condopt", 130*0b57cec5SDimitry Andric "AArch64 CondOpt Pass", false, false) 131*0b57cec5SDimitry Andric 132*0b57cec5SDimitry Andric FunctionPass *llvm::createAArch64ConditionOptimizerPass() { 133*0b57cec5SDimitry Andric return new AArch64ConditionOptimizer(); 134*0b57cec5SDimitry Andric } 135*0b57cec5SDimitry Andric 136*0b57cec5SDimitry Andric void AArch64ConditionOptimizer::getAnalysisUsage(AnalysisUsage &AU) const { 137*0b57cec5SDimitry Andric AU.addRequired<MachineDominatorTree>(); 138*0b57cec5SDimitry Andric AU.addPreserved<MachineDominatorTree>(); 139*0b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric 142*0b57cec5SDimitry Andric // Finds compare instruction that corresponds to supported types of branching. 143*0b57cec5SDimitry Andric // Returns the instruction or nullptr on failures or detecting unsupported 144*0b57cec5SDimitry Andric // instructions. 145*0b57cec5SDimitry Andric MachineInstr *AArch64ConditionOptimizer::findSuitableCompare( 146*0b57cec5SDimitry Andric MachineBasicBlock *MBB) { 147*0b57cec5SDimitry Andric MachineBasicBlock::iterator I = MBB->getFirstTerminator(); 148*0b57cec5SDimitry Andric if (I == MBB->end()) 149*0b57cec5SDimitry Andric return nullptr; 150*0b57cec5SDimitry Andric 151*0b57cec5SDimitry Andric if (I->getOpcode() != AArch64::Bcc) 152*0b57cec5SDimitry Andric return nullptr; 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric // Since we may modify cmp of this MBB, make sure NZCV does not live out. 155*0b57cec5SDimitry Andric for (auto SuccBB : MBB->successors()) 156*0b57cec5SDimitry Andric if (SuccBB->isLiveIn(AArch64::NZCV)) 157*0b57cec5SDimitry Andric return nullptr; 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric // Now find the instruction controlling the terminator. 160*0b57cec5SDimitry Andric for (MachineBasicBlock::iterator B = MBB->begin(); I != B;) { 161*0b57cec5SDimitry Andric --I; 162*0b57cec5SDimitry Andric assert(!I->isTerminator() && "Spurious terminator"); 163*0b57cec5SDimitry Andric // Check if there is any use of NZCV between CMP and Bcc. 164*0b57cec5SDimitry Andric if (I->readsRegister(AArch64::NZCV)) 165*0b57cec5SDimitry Andric return nullptr; 166*0b57cec5SDimitry Andric switch (I->getOpcode()) { 167*0b57cec5SDimitry Andric // cmp is an alias for subs with a dead destination register. 168*0b57cec5SDimitry Andric case AArch64::SUBSWri: 169*0b57cec5SDimitry Andric case AArch64::SUBSXri: 170*0b57cec5SDimitry Andric // cmn is an alias for adds with a dead destination register. 171*0b57cec5SDimitry Andric case AArch64::ADDSWri: 172*0b57cec5SDimitry Andric case AArch64::ADDSXri: { 173*0b57cec5SDimitry Andric unsigned ShiftAmt = AArch64_AM::getShiftValue(I->getOperand(3).getImm()); 174*0b57cec5SDimitry Andric if (!I->getOperand(2).isImm()) { 175*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Immediate of cmp is symbolic, " << *I << '\n'); 176*0b57cec5SDimitry Andric return nullptr; 177*0b57cec5SDimitry Andric } else if (I->getOperand(2).getImm() << ShiftAmt >= 0xfff) { 178*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Immediate of cmp may be out of range, " << *I 179*0b57cec5SDimitry Andric << '\n'); 180*0b57cec5SDimitry Andric return nullptr; 181*0b57cec5SDimitry Andric } else if (!MRI->use_empty(I->getOperand(0).getReg())) { 182*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Destination of cmp is not dead, " << *I << '\n'); 183*0b57cec5SDimitry Andric return nullptr; 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric return &*I; 186*0b57cec5SDimitry Andric } 187*0b57cec5SDimitry Andric // Prevent false positive case like: 188*0b57cec5SDimitry Andric // cmp w19, #0 189*0b57cec5SDimitry Andric // cinc w0, w19, gt 190*0b57cec5SDimitry Andric // ... 191*0b57cec5SDimitry Andric // fcmp d8, #0.0 192*0b57cec5SDimitry Andric // b.gt .LBB0_5 193*0b57cec5SDimitry Andric case AArch64::FCMPDri: 194*0b57cec5SDimitry Andric case AArch64::FCMPSri: 195*0b57cec5SDimitry Andric case AArch64::FCMPESri: 196*0b57cec5SDimitry Andric case AArch64::FCMPEDri: 197*0b57cec5SDimitry Andric 198*0b57cec5SDimitry Andric case AArch64::SUBSWrr: 199*0b57cec5SDimitry Andric case AArch64::SUBSXrr: 200*0b57cec5SDimitry Andric case AArch64::ADDSWrr: 201*0b57cec5SDimitry Andric case AArch64::ADDSXrr: 202*0b57cec5SDimitry Andric case AArch64::FCMPSrr: 203*0b57cec5SDimitry Andric case AArch64::FCMPDrr: 204*0b57cec5SDimitry Andric case AArch64::FCMPESrr: 205*0b57cec5SDimitry Andric case AArch64::FCMPEDrr: 206*0b57cec5SDimitry Andric // Skip comparison instructions without immediate operands. 207*0b57cec5SDimitry Andric return nullptr; 208*0b57cec5SDimitry Andric } 209*0b57cec5SDimitry Andric } 210*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Flags not defined in " << printMBBReference(*MBB) 211*0b57cec5SDimitry Andric << '\n'); 212*0b57cec5SDimitry Andric return nullptr; 213*0b57cec5SDimitry Andric } 214*0b57cec5SDimitry Andric 215*0b57cec5SDimitry Andric // Changes opcode adds <-> subs considering register operand width. 216*0b57cec5SDimitry Andric static int getComplementOpc(int Opc) { 217*0b57cec5SDimitry Andric switch (Opc) { 218*0b57cec5SDimitry Andric case AArch64::ADDSWri: return AArch64::SUBSWri; 219*0b57cec5SDimitry Andric case AArch64::ADDSXri: return AArch64::SUBSXri; 220*0b57cec5SDimitry Andric case AArch64::SUBSWri: return AArch64::ADDSWri; 221*0b57cec5SDimitry Andric case AArch64::SUBSXri: return AArch64::ADDSXri; 222*0b57cec5SDimitry Andric default: 223*0b57cec5SDimitry Andric llvm_unreachable("Unexpected opcode"); 224*0b57cec5SDimitry Andric } 225*0b57cec5SDimitry Andric } 226*0b57cec5SDimitry Andric 227*0b57cec5SDimitry Andric // Changes form of comparison inclusive <-> exclusive. 228*0b57cec5SDimitry Andric static AArch64CC::CondCode getAdjustedCmp(AArch64CC::CondCode Cmp) { 229*0b57cec5SDimitry Andric switch (Cmp) { 230*0b57cec5SDimitry Andric case AArch64CC::GT: return AArch64CC::GE; 231*0b57cec5SDimitry Andric case AArch64CC::GE: return AArch64CC::GT; 232*0b57cec5SDimitry Andric case AArch64CC::LT: return AArch64CC::LE; 233*0b57cec5SDimitry Andric case AArch64CC::LE: return AArch64CC::LT; 234*0b57cec5SDimitry Andric default: 235*0b57cec5SDimitry Andric llvm_unreachable("Unexpected condition code"); 236*0b57cec5SDimitry Andric } 237*0b57cec5SDimitry Andric } 238*0b57cec5SDimitry Andric 239*0b57cec5SDimitry Andric // Transforms GT -> GE, GE -> GT, LT -> LE, LE -> LT by updating comparison 240*0b57cec5SDimitry Andric // operator and condition code. 241*0b57cec5SDimitry Andric AArch64ConditionOptimizer::CmpInfo AArch64ConditionOptimizer::adjustCmp( 242*0b57cec5SDimitry Andric MachineInstr *CmpMI, AArch64CC::CondCode Cmp) { 243*0b57cec5SDimitry Andric unsigned Opc = CmpMI->getOpcode(); 244*0b57cec5SDimitry Andric 245*0b57cec5SDimitry Andric // CMN (compare with negative immediate) is an alias to ADDS (as 246*0b57cec5SDimitry Andric // "operand - negative" == "operand + positive") 247*0b57cec5SDimitry Andric bool Negative = (Opc == AArch64::ADDSWri || Opc == AArch64::ADDSXri); 248*0b57cec5SDimitry Andric 249*0b57cec5SDimitry Andric int Correction = (Cmp == AArch64CC::GT) ? 1 : -1; 250*0b57cec5SDimitry Andric // Negate Correction value for comparison with negative immediate (CMN). 251*0b57cec5SDimitry Andric if (Negative) { 252*0b57cec5SDimitry Andric Correction = -Correction; 253*0b57cec5SDimitry Andric } 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric const int OldImm = (int)CmpMI->getOperand(2).getImm(); 256*0b57cec5SDimitry Andric const int NewImm = std::abs(OldImm + Correction); 257*0b57cec5SDimitry Andric 258*0b57cec5SDimitry Andric // Handle +0 -> -1 and -0 -> +1 (CMN with 0 immediate) transitions by 259*0b57cec5SDimitry Andric // adjusting compare instruction opcode. 260*0b57cec5SDimitry Andric if (OldImm == 0 && ((Negative && Correction == 1) || 261*0b57cec5SDimitry Andric (!Negative && Correction == -1))) { 262*0b57cec5SDimitry Andric Opc = getComplementOpc(Opc); 263*0b57cec5SDimitry Andric } 264*0b57cec5SDimitry Andric 265*0b57cec5SDimitry Andric return CmpInfo(NewImm, Opc, getAdjustedCmp(Cmp)); 266*0b57cec5SDimitry Andric } 267*0b57cec5SDimitry Andric 268*0b57cec5SDimitry Andric // Applies changes to comparison instruction suggested by adjustCmp(). 269*0b57cec5SDimitry Andric void AArch64ConditionOptimizer::modifyCmp(MachineInstr *CmpMI, 270*0b57cec5SDimitry Andric const CmpInfo &Info) { 271*0b57cec5SDimitry Andric int Imm; 272*0b57cec5SDimitry Andric unsigned Opc; 273*0b57cec5SDimitry Andric AArch64CC::CondCode Cmp; 274*0b57cec5SDimitry Andric std::tie(Imm, Opc, Cmp) = Info; 275*0b57cec5SDimitry Andric 276*0b57cec5SDimitry Andric MachineBasicBlock *const MBB = CmpMI->getParent(); 277*0b57cec5SDimitry Andric 278*0b57cec5SDimitry Andric // Change immediate in comparison instruction (ADDS or SUBS). 279*0b57cec5SDimitry Andric BuildMI(*MBB, CmpMI, CmpMI->getDebugLoc(), TII->get(Opc)) 280*0b57cec5SDimitry Andric .add(CmpMI->getOperand(0)) 281*0b57cec5SDimitry Andric .add(CmpMI->getOperand(1)) 282*0b57cec5SDimitry Andric .addImm(Imm) 283*0b57cec5SDimitry Andric .add(CmpMI->getOperand(3)); 284*0b57cec5SDimitry Andric CmpMI->eraseFromParent(); 285*0b57cec5SDimitry Andric 286*0b57cec5SDimitry Andric // The fact that this comparison was picked ensures that it's related to the 287*0b57cec5SDimitry Andric // first terminator instruction. 288*0b57cec5SDimitry Andric MachineInstr &BrMI = *MBB->getFirstTerminator(); 289*0b57cec5SDimitry Andric 290*0b57cec5SDimitry Andric // Change condition in branch instruction. 291*0b57cec5SDimitry Andric BuildMI(*MBB, BrMI, BrMI.getDebugLoc(), TII->get(AArch64::Bcc)) 292*0b57cec5SDimitry Andric .addImm(Cmp) 293*0b57cec5SDimitry Andric .add(BrMI.getOperand(1)); 294*0b57cec5SDimitry Andric BrMI.eraseFromParent(); 295*0b57cec5SDimitry Andric 296*0b57cec5SDimitry Andric MBB->updateTerminator(); 297*0b57cec5SDimitry Andric 298*0b57cec5SDimitry Andric ++NumConditionsAdjusted; 299*0b57cec5SDimitry Andric } 300*0b57cec5SDimitry Andric 301*0b57cec5SDimitry Andric // Parse a condition code returned by AnalyzeBranch, and compute the CondCode 302*0b57cec5SDimitry Andric // corresponding to TBB. 303*0b57cec5SDimitry Andric // Returns true if parsing was successful, otherwise false is returned. 304*0b57cec5SDimitry Andric static bool parseCond(ArrayRef<MachineOperand> Cond, AArch64CC::CondCode &CC) { 305*0b57cec5SDimitry Andric // A normal br.cond simply has the condition code. 306*0b57cec5SDimitry Andric if (Cond[0].getImm() != -1) { 307*0b57cec5SDimitry Andric assert(Cond.size() == 1 && "Unknown Cond array format"); 308*0b57cec5SDimitry Andric CC = (AArch64CC::CondCode)(int)Cond[0].getImm(); 309*0b57cec5SDimitry Andric return true; 310*0b57cec5SDimitry Andric } 311*0b57cec5SDimitry Andric return false; 312*0b57cec5SDimitry Andric } 313*0b57cec5SDimitry Andric 314*0b57cec5SDimitry Andric // Adjusts one cmp instruction to another one if result of adjustment will allow 315*0b57cec5SDimitry Andric // CSE. Returns true if compare instruction was changed, otherwise false is 316*0b57cec5SDimitry Andric // returned. 317*0b57cec5SDimitry Andric bool AArch64ConditionOptimizer::adjustTo(MachineInstr *CmpMI, 318*0b57cec5SDimitry Andric AArch64CC::CondCode Cmp, MachineInstr *To, int ToImm) 319*0b57cec5SDimitry Andric { 320*0b57cec5SDimitry Andric CmpInfo Info = adjustCmp(CmpMI, Cmp); 321*0b57cec5SDimitry Andric if (std::get<0>(Info) == ToImm && std::get<1>(Info) == To->getOpcode()) { 322*0b57cec5SDimitry Andric modifyCmp(CmpMI, Info); 323*0b57cec5SDimitry Andric return true; 324*0b57cec5SDimitry Andric } 325*0b57cec5SDimitry Andric return false; 326*0b57cec5SDimitry Andric } 327*0b57cec5SDimitry Andric 328*0b57cec5SDimitry Andric bool AArch64ConditionOptimizer::runOnMachineFunction(MachineFunction &MF) { 329*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** AArch64 Conditional Compares **********\n" 330*0b57cec5SDimitry Andric << "********** Function: " << MF.getName() << '\n'); 331*0b57cec5SDimitry Andric if (skipFunction(MF.getFunction())) 332*0b57cec5SDimitry Andric return false; 333*0b57cec5SDimitry Andric 334*0b57cec5SDimitry Andric TII = MF.getSubtarget().getInstrInfo(); 335*0b57cec5SDimitry Andric DomTree = &getAnalysis<MachineDominatorTree>(); 336*0b57cec5SDimitry Andric MRI = &MF.getRegInfo(); 337*0b57cec5SDimitry Andric 338*0b57cec5SDimitry Andric bool Changed = false; 339*0b57cec5SDimitry Andric 340*0b57cec5SDimitry Andric // Visit blocks in dominator tree pre-order. The pre-order enables multiple 341*0b57cec5SDimitry Andric // cmp-conversions from the same head block. 342*0b57cec5SDimitry Andric // Note that updateDomTree() modifies the children of the DomTree node 343*0b57cec5SDimitry Andric // currently being visited. The df_iterator supports that; it doesn't look at 344*0b57cec5SDimitry Andric // child_begin() / child_end() until after a node has been visited. 345*0b57cec5SDimitry Andric for (MachineDomTreeNode *I : depth_first(DomTree)) { 346*0b57cec5SDimitry Andric MachineBasicBlock *HBB = I->getBlock(); 347*0b57cec5SDimitry Andric 348*0b57cec5SDimitry Andric SmallVector<MachineOperand, 4> HeadCond; 349*0b57cec5SDimitry Andric MachineBasicBlock *TBB = nullptr, *FBB = nullptr; 350*0b57cec5SDimitry Andric if (TII->analyzeBranch(*HBB, TBB, FBB, HeadCond)) { 351*0b57cec5SDimitry Andric continue; 352*0b57cec5SDimitry Andric } 353*0b57cec5SDimitry Andric 354*0b57cec5SDimitry Andric // Equivalence check is to skip loops. 355*0b57cec5SDimitry Andric if (!TBB || TBB == HBB) { 356*0b57cec5SDimitry Andric continue; 357*0b57cec5SDimitry Andric } 358*0b57cec5SDimitry Andric 359*0b57cec5SDimitry Andric SmallVector<MachineOperand, 4> TrueCond; 360*0b57cec5SDimitry Andric MachineBasicBlock *TBB_TBB = nullptr, *TBB_FBB = nullptr; 361*0b57cec5SDimitry Andric if (TII->analyzeBranch(*TBB, TBB_TBB, TBB_FBB, TrueCond)) { 362*0b57cec5SDimitry Andric continue; 363*0b57cec5SDimitry Andric } 364*0b57cec5SDimitry Andric 365*0b57cec5SDimitry Andric MachineInstr *HeadCmpMI = findSuitableCompare(HBB); 366*0b57cec5SDimitry Andric if (!HeadCmpMI) { 367*0b57cec5SDimitry Andric continue; 368*0b57cec5SDimitry Andric } 369*0b57cec5SDimitry Andric 370*0b57cec5SDimitry Andric MachineInstr *TrueCmpMI = findSuitableCompare(TBB); 371*0b57cec5SDimitry Andric if (!TrueCmpMI) { 372*0b57cec5SDimitry Andric continue; 373*0b57cec5SDimitry Andric } 374*0b57cec5SDimitry Andric 375*0b57cec5SDimitry Andric AArch64CC::CondCode HeadCmp; 376*0b57cec5SDimitry Andric if (HeadCond.empty() || !parseCond(HeadCond, HeadCmp)) { 377*0b57cec5SDimitry Andric continue; 378*0b57cec5SDimitry Andric } 379*0b57cec5SDimitry Andric 380*0b57cec5SDimitry Andric AArch64CC::CondCode TrueCmp; 381*0b57cec5SDimitry Andric if (TrueCond.empty() || !parseCond(TrueCond, TrueCmp)) { 382*0b57cec5SDimitry Andric continue; 383*0b57cec5SDimitry Andric } 384*0b57cec5SDimitry Andric 385*0b57cec5SDimitry Andric const int HeadImm = (int)HeadCmpMI->getOperand(2).getImm(); 386*0b57cec5SDimitry Andric const int TrueImm = (int)TrueCmpMI->getOperand(2).getImm(); 387*0b57cec5SDimitry Andric 388*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Head branch:\n"); 389*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(HeadCmp) 390*0b57cec5SDimitry Andric << '\n'); 391*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\timmediate: " << HeadImm << '\n'); 392*0b57cec5SDimitry Andric 393*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "True branch:\n"); 394*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(TrueCmp) 395*0b57cec5SDimitry Andric << '\n'); 396*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\timmediate: " << TrueImm << '\n'); 397*0b57cec5SDimitry Andric 398*0b57cec5SDimitry Andric if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::LT) || 399*0b57cec5SDimitry Andric (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::GT)) && 400*0b57cec5SDimitry Andric std::abs(TrueImm - HeadImm) == 2) { 401*0b57cec5SDimitry Andric // This branch transforms machine instructions that correspond to 402*0b57cec5SDimitry Andric // 403*0b57cec5SDimitry Andric // 1) (a > {TrueImm} && ...) || (a < {HeadImm} && ...) 404*0b57cec5SDimitry Andric // 2) (a < {TrueImm} && ...) || (a > {HeadImm} && ...) 405*0b57cec5SDimitry Andric // 406*0b57cec5SDimitry Andric // into 407*0b57cec5SDimitry Andric // 408*0b57cec5SDimitry Andric // 1) (a >= {NewImm} && ...) || (a <= {NewImm} && ...) 409*0b57cec5SDimitry Andric // 2) (a <= {NewImm} && ...) || (a >= {NewImm} && ...) 410*0b57cec5SDimitry Andric 411*0b57cec5SDimitry Andric CmpInfo HeadCmpInfo = adjustCmp(HeadCmpMI, HeadCmp); 412*0b57cec5SDimitry Andric CmpInfo TrueCmpInfo = adjustCmp(TrueCmpMI, TrueCmp); 413*0b57cec5SDimitry Andric if (std::get<0>(HeadCmpInfo) == std::get<0>(TrueCmpInfo) && 414*0b57cec5SDimitry Andric std::get<1>(HeadCmpInfo) == std::get<1>(TrueCmpInfo)) { 415*0b57cec5SDimitry Andric modifyCmp(HeadCmpMI, HeadCmpInfo); 416*0b57cec5SDimitry Andric modifyCmp(TrueCmpMI, TrueCmpInfo); 417*0b57cec5SDimitry Andric Changed = true; 418*0b57cec5SDimitry Andric } 419*0b57cec5SDimitry Andric } else if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::GT) || 420*0b57cec5SDimitry Andric (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::LT)) && 421*0b57cec5SDimitry Andric std::abs(TrueImm - HeadImm) == 1) { 422*0b57cec5SDimitry Andric // This branch transforms machine instructions that correspond to 423*0b57cec5SDimitry Andric // 424*0b57cec5SDimitry Andric // 1) (a > {TrueImm} && ...) || (a > {HeadImm} && ...) 425*0b57cec5SDimitry Andric // 2) (a < {TrueImm} && ...) || (a < {HeadImm} && ...) 426*0b57cec5SDimitry Andric // 427*0b57cec5SDimitry Andric // into 428*0b57cec5SDimitry Andric // 429*0b57cec5SDimitry Andric // 1) (a <= {NewImm} && ...) || (a > {NewImm} && ...) 430*0b57cec5SDimitry Andric // 2) (a < {NewImm} && ...) || (a >= {NewImm} && ...) 431*0b57cec5SDimitry Andric 432*0b57cec5SDimitry Andric // GT -> GE transformation increases immediate value, so picking the 433*0b57cec5SDimitry Andric // smaller one; LT -> LE decreases immediate value so invert the choice. 434*0b57cec5SDimitry Andric bool adjustHeadCond = (HeadImm < TrueImm); 435*0b57cec5SDimitry Andric if (HeadCmp == AArch64CC::LT) { 436*0b57cec5SDimitry Andric adjustHeadCond = !adjustHeadCond; 437*0b57cec5SDimitry Andric } 438*0b57cec5SDimitry Andric 439*0b57cec5SDimitry Andric if (adjustHeadCond) { 440*0b57cec5SDimitry Andric Changed |= adjustTo(HeadCmpMI, HeadCmp, TrueCmpMI, TrueImm); 441*0b57cec5SDimitry Andric } else { 442*0b57cec5SDimitry Andric Changed |= adjustTo(TrueCmpMI, TrueCmp, HeadCmpMI, HeadImm); 443*0b57cec5SDimitry Andric } 444*0b57cec5SDimitry Andric } 445*0b57cec5SDimitry Andric // Other transformation cases almost never occur due to generation of < or > 446*0b57cec5SDimitry Andric // comparisons instead of <= and >=. 447*0b57cec5SDimitry Andric } 448*0b57cec5SDimitry Andric 449*0b57cec5SDimitry Andric return Changed; 450*0b57cec5SDimitry Andric } 451