xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //=- AArch64ConditionOptimizer.cpp - Remove useless comparisons for AArch64 -=//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This pass tries to make consecutive compares of values use same operands to
100b57cec5SDimitry Andric // allow CSE pass to remove duplicated instructions.  For this it analyzes
110b57cec5SDimitry Andric // branches and adjusts comparisons with immediate values by converting:
120b57cec5SDimitry Andric //  * GE -> GT
130b57cec5SDimitry Andric //  * GT -> GE
140b57cec5SDimitry Andric //  * LT -> LE
150b57cec5SDimitry Andric //  * LE -> LT
160b57cec5SDimitry Andric // and adjusting immediate values appropriately.  It basically corrects two
170b57cec5SDimitry Andric // immediate values towards each other to make them equal.
180b57cec5SDimitry Andric //
190b57cec5SDimitry Andric // Consider the following example in C:
200b57cec5SDimitry Andric //
210b57cec5SDimitry Andric //   if ((a < 5 && ...) || (a > 5 && ...)) {
220b57cec5SDimitry Andric //        ~~~~~             ~~~~~
230b57cec5SDimitry Andric //          ^                 ^
240b57cec5SDimitry Andric //          x                 y
250b57cec5SDimitry Andric //
260b57cec5SDimitry Andric // Here both "x" and "y" expressions compare "a" with "5".  When "x" evaluates
270b57cec5SDimitry Andric // to "false", "y" can just check flags set by the first comparison.  As a
280b57cec5SDimitry Andric // result of the canonicalization employed by
290b57cec5SDimitry Andric // SelectionDAGBuilder::visitSwitchCase, DAGCombine, and other target-specific
300b57cec5SDimitry Andric // code, assembly ends up in the form that is not CSE friendly:
310b57cec5SDimitry Andric //
320b57cec5SDimitry Andric //     ...
330b57cec5SDimitry Andric //     cmp      w8, #4
340b57cec5SDimitry Andric //     b.gt     .LBB0_3
350b57cec5SDimitry Andric //     ...
360b57cec5SDimitry Andric //   .LBB0_3:
370b57cec5SDimitry Andric //     cmp      w8, #6
380b57cec5SDimitry Andric //     b.lt     .LBB0_6
390b57cec5SDimitry Andric //     ...
400b57cec5SDimitry Andric //
410b57cec5SDimitry Andric // Same assembly after the pass:
420b57cec5SDimitry Andric //
430b57cec5SDimitry Andric //     ...
440b57cec5SDimitry Andric //     cmp      w8, #5
450b57cec5SDimitry Andric //     b.ge     .LBB0_3
460b57cec5SDimitry Andric //     ...
470b57cec5SDimitry Andric //   .LBB0_3:
480b57cec5SDimitry Andric //     cmp      w8, #5     // <-- CSE pass removes this instruction
490b57cec5SDimitry Andric //     b.le     .LBB0_6
500b57cec5SDimitry Andric //     ...
510b57cec5SDimitry Andric //
520b57cec5SDimitry Andric // Currently only SUBS and ADDS followed by b.?? are supported.
530b57cec5SDimitry Andric //
540b57cec5SDimitry Andric // TODO: maybe handle TBNZ/TBZ the same way as CMP when used instead for "a < 0"
550b57cec5SDimitry Andric // TODO: handle other conditional instructions (e.g. CSET)
560b57cec5SDimitry Andric // TODO: allow second branching to be anything if it doesn't require adjusting
570b57cec5SDimitry Andric //
580b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric #include "AArch64.h"
610b57cec5SDimitry Andric #include "MCTargetDesc/AArch64AddressingModes.h"
620b57cec5SDimitry Andric #include "Utils/AArch64BaseInfo.h"
630b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
640b57cec5SDimitry Andric #include "llvm/ADT/DepthFirstIterator.h"
650b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
660b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
670b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
680b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h"
690b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
700b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
710b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstr.h"
720b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
730b57cec5SDimitry Andric #include "llvm/CodeGen/MachineOperand.h"
740b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
750b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
760b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
77480093f4SDimitry Andric #include "llvm/InitializePasses.h"
780b57cec5SDimitry Andric #include "llvm/Pass.h"
790b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
800b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
810b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
820b57cec5SDimitry Andric #include <cassert>
830b57cec5SDimitry Andric #include <cstdlib>
840b57cec5SDimitry Andric #include <tuple>
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric using namespace llvm;
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric #define DEBUG_TYPE "aarch64-condopt"
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric STATISTIC(NumConditionsAdjusted, "Number of conditions adjusted");
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric namespace {
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric class AArch64ConditionOptimizer : public MachineFunctionPass {
950b57cec5SDimitry Andric   const TargetInstrInfo *TII;
960b57cec5SDimitry Andric   MachineDominatorTree *DomTree;
970b57cec5SDimitry Andric   const MachineRegisterInfo *MRI;
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric public:
1000b57cec5SDimitry Andric   // Stores immediate, compare instruction opcode and branch condition (in this
1010b57cec5SDimitry Andric   // order) of adjusted comparison.
1020b57cec5SDimitry Andric   using CmpInfo = std::tuple<int, unsigned, AArch64CC::CondCode>;
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   static char ID;
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric   AArch64ConditionOptimizer() : MachineFunctionPass(ID) {
1070b57cec5SDimitry Andric     initializeAArch64ConditionOptimizerPass(*PassRegistry::getPassRegistry());
1080b57cec5SDimitry Andric   }
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override;
1110b57cec5SDimitry Andric   MachineInstr *findSuitableCompare(MachineBasicBlock *MBB);
1120b57cec5SDimitry Andric   CmpInfo adjustCmp(MachineInstr *CmpMI, AArch64CC::CondCode Cmp);
1130b57cec5SDimitry Andric   void modifyCmp(MachineInstr *CmpMI, const CmpInfo &Info);
1140b57cec5SDimitry Andric   bool adjustTo(MachineInstr *CmpMI, AArch64CC::CondCode Cmp, MachineInstr *To,
1150b57cec5SDimitry Andric                 int ToImm);
1160b57cec5SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   StringRef getPassName() const override {
1190b57cec5SDimitry Andric     return "AArch64 Condition Optimizer";
1200b57cec5SDimitry Andric   }
1210b57cec5SDimitry Andric };
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric } // end anonymous namespace
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric char AArch64ConditionOptimizer::ID = 0;
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(AArch64ConditionOptimizer, "aarch64-condopt",
1280b57cec5SDimitry Andric                       "AArch64 CondOpt Pass", false, false)
129*0fca6ea1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
1300b57cec5SDimitry Andric INITIALIZE_PASS_END(AArch64ConditionOptimizer, "aarch64-condopt",
1310b57cec5SDimitry Andric                     "AArch64 CondOpt Pass", false, false)
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric FunctionPass *llvm::createAArch64ConditionOptimizerPass() {
1340b57cec5SDimitry Andric   return new AArch64ConditionOptimizer();
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric void AArch64ConditionOptimizer::getAnalysisUsage(AnalysisUsage &AU) const {
138*0fca6ea1SDimitry Andric   AU.addRequired<MachineDominatorTreeWrapperPass>();
139*0fca6ea1SDimitry Andric   AU.addPreserved<MachineDominatorTreeWrapperPass>();
1400b57cec5SDimitry Andric   MachineFunctionPass::getAnalysisUsage(AU);
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric // Finds compare instruction that corresponds to supported types of branching.
1440b57cec5SDimitry Andric // Returns the instruction or nullptr on failures or detecting unsupported
1450b57cec5SDimitry Andric // instructions.
1460b57cec5SDimitry Andric MachineInstr *AArch64ConditionOptimizer::findSuitableCompare(
1470b57cec5SDimitry Andric     MachineBasicBlock *MBB) {
1485ffd83dbSDimitry Andric   MachineBasicBlock::iterator Term = MBB->getFirstTerminator();
1495ffd83dbSDimitry Andric   if (Term == MBB->end())
1500b57cec5SDimitry Andric     return nullptr;
1510b57cec5SDimitry Andric 
1525ffd83dbSDimitry Andric   if (Term->getOpcode() != AArch64::Bcc)
1530b57cec5SDimitry Andric     return nullptr;
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric   // Since we may modify cmp of this MBB, make sure NZCV does not live out.
156bdd1243dSDimitry Andric   for (auto *SuccBB : MBB->successors())
1570b57cec5SDimitry Andric     if (SuccBB->isLiveIn(AArch64::NZCV))
1580b57cec5SDimitry Andric       return nullptr;
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric   // Now find the instruction controlling the terminator.
1615ffd83dbSDimitry Andric   for (MachineBasicBlock::iterator B = MBB->begin(), It = Term; It != B;) {
1625ffd83dbSDimitry Andric     It = prev_nodbg(It, B);
1635ffd83dbSDimitry Andric     MachineInstr &I = *It;
1645ffd83dbSDimitry Andric     assert(!I.isTerminator() && "Spurious terminator");
1650b57cec5SDimitry Andric     // Check if there is any use of NZCV between CMP and Bcc.
166*0fca6ea1SDimitry Andric     if (I.readsRegister(AArch64::NZCV, /*TRI=*/nullptr))
1670b57cec5SDimitry Andric       return nullptr;
1685ffd83dbSDimitry Andric     switch (I.getOpcode()) {
1690b57cec5SDimitry Andric     // cmp is an alias for subs with a dead destination register.
1700b57cec5SDimitry Andric     case AArch64::SUBSWri:
1710b57cec5SDimitry Andric     case AArch64::SUBSXri:
1720b57cec5SDimitry Andric     // cmn is an alias for adds with a dead destination register.
1730b57cec5SDimitry Andric     case AArch64::ADDSWri:
1740b57cec5SDimitry Andric     case AArch64::ADDSXri: {
1755ffd83dbSDimitry Andric       unsigned ShiftAmt = AArch64_AM::getShiftValue(I.getOperand(3).getImm());
1765ffd83dbSDimitry Andric       if (!I.getOperand(2).isImm()) {
1775ffd83dbSDimitry Andric         LLVM_DEBUG(dbgs() << "Immediate of cmp is symbolic, " << I << '\n');
1780b57cec5SDimitry Andric         return nullptr;
1795ffd83dbSDimitry Andric       } else if (I.getOperand(2).getImm() << ShiftAmt >= 0xfff) {
1805ffd83dbSDimitry Andric         LLVM_DEBUG(dbgs() << "Immediate of cmp may be out of range, " << I
1810b57cec5SDimitry Andric                           << '\n');
1820b57cec5SDimitry Andric         return nullptr;
1835ffd83dbSDimitry Andric       } else if (!MRI->use_nodbg_empty(I.getOperand(0).getReg())) {
1845ffd83dbSDimitry Andric         LLVM_DEBUG(dbgs() << "Destination of cmp is not dead, " << I << '\n');
1850b57cec5SDimitry Andric         return nullptr;
1860b57cec5SDimitry Andric       }
1875ffd83dbSDimitry Andric       return &I;
1880b57cec5SDimitry Andric     }
1890b57cec5SDimitry Andric     // Prevent false positive case like:
1900b57cec5SDimitry Andric     // cmp      w19, #0
1910b57cec5SDimitry Andric     // cinc     w0, w19, gt
1920b57cec5SDimitry Andric     // ...
1930b57cec5SDimitry Andric     // fcmp     d8, #0.0
1940b57cec5SDimitry Andric     // b.gt     .LBB0_5
1950b57cec5SDimitry Andric     case AArch64::FCMPDri:
1960b57cec5SDimitry Andric     case AArch64::FCMPSri:
1970b57cec5SDimitry Andric     case AArch64::FCMPESri:
1980b57cec5SDimitry Andric     case AArch64::FCMPEDri:
1990b57cec5SDimitry Andric 
2000b57cec5SDimitry Andric     case AArch64::SUBSWrr:
2010b57cec5SDimitry Andric     case AArch64::SUBSXrr:
2020b57cec5SDimitry Andric     case AArch64::ADDSWrr:
2030b57cec5SDimitry Andric     case AArch64::ADDSXrr:
2040b57cec5SDimitry Andric     case AArch64::FCMPSrr:
2050b57cec5SDimitry Andric     case AArch64::FCMPDrr:
2060b57cec5SDimitry Andric     case AArch64::FCMPESrr:
2070b57cec5SDimitry Andric     case AArch64::FCMPEDrr:
2080b57cec5SDimitry Andric       // Skip comparison instructions without immediate operands.
2090b57cec5SDimitry Andric       return nullptr;
2100b57cec5SDimitry Andric     }
2110b57cec5SDimitry Andric   }
2120b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "Flags not defined in " << printMBBReference(*MBB)
2130b57cec5SDimitry Andric                     << '\n');
2140b57cec5SDimitry Andric   return nullptr;
2150b57cec5SDimitry Andric }
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric // Changes opcode adds <-> subs considering register operand width.
2180b57cec5SDimitry Andric static int getComplementOpc(int Opc) {
2190b57cec5SDimitry Andric   switch (Opc) {
2200b57cec5SDimitry Andric   case AArch64::ADDSWri: return AArch64::SUBSWri;
2210b57cec5SDimitry Andric   case AArch64::ADDSXri: return AArch64::SUBSXri;
2220b57cec5SDimitry Andric   case AArch64::SUBSWri: return AArch64::ADDSWri;
2230b57cec5SDimitry Andric   case AArch64::SUBSXri: return AArch64::ADDSXri;
2240b57cec5SDimitry Andric   default:
2250b57cec5SDimitry Andric     llvm_unreachable("Unexpected opcode");
2260b57cec5SDimitry Andric   }
2270b57cec5SDimitry Andric }
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric // Changes form of comparison inclusive <-> exclusive.
2300b57cec5SDimitry Andric static AArch64CC::CondCode getAdjustedCmp(AArch64CC::CondCode Cmp) {
2310b57cec5SDimitry Andric   switch (Cmp) {
2320b57cec5SDimitry Andric   case AArch64CC::GT: return AArch64CC::GE;
2330b57cec5SDimitry Andric   case AArch64CC::GE: return AArch64CC::GT;
2340b57cec5SDimitry Andric   case AArch64CC::LT: return AArch64CC::LE;
2350b57cec5SDimitry Andric   case AArch64CC::LE: return AArch64CC::LT;
2360b57cec5SDimitry Andric   default:
2370b57cec5SDimitry Andric     llvm_unreachable("Unexpected condition code");
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric // Transforms GT -> GE, GE -> GT, LT -> LE, LE -> LT by updating comparison
2420b57cec5SDimitry Andric // operator and condition code.
2430b57cec5SDimitry Andric AArch64ConditionOptimizer::CmpInfo AArch64ConditionOptimizer::adjustCmp(
2440b57cec5SDimitry Andric     MachineInstr *CmpMI, AArch64CC::CondCode Cmp) {
2450b57cec5SDimitry Andric   unsigned Opc = CmpMI->getOpcode();
2460b57cec5SDimitry Andric 
2470b57cec5SDimitry Andric   // CMN (compare with negative immediate) is an alias to ADDS (as
2480b57cec5SDimitry Andric   // "operand - negative" == "operand + positive")
2490b57cec5SDimitry Andric   bool Negative = (Opc == AArch64::ADDSWri || Opc == AArch64::ADDSXri);
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric   int Correction = (Cmp == AArch64CC::GT) ? 1 : -1;
2520b57cec5SDimitry Andric   // Negate Correction value for comparison with negative immediate (CMN).
2530b57cec5SDimitry Andric   if (Negative) {
2540b57cec5SDimitry Andric     Correction = -Correction;
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   const int OldImm = (int)CmpMI->getOperand(2).getImm();
2580b57cec5SDimitry Andric   const int NewImm = std::abs(OldImm + Correction);
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric   // Handle +0 -> -1 and -0 -> +1 (CMN with 0 immediate) transitions by
2610b57cec5SDimitry Andric   // adjusting compare instruction opcode.
2620b57cec5SDimitry Andric   if (OldImm == 0 && ((Negative && Correction == 1) ||
2630b57cec5SDimitry Andric                       (!Negative && Correction == -1))) {
2640b57cec5SDimitry Andric     Opc = getComplementOpc(Opc);
2650b57cec5SDimitry Andric   }
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric   return CmpInfo(NewImm, Opc, getAdjustedCmp(Cmp));
2680b57cec5SDimitry Andric }
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric // Applies changes to comparison instruction suggested by adjustCmp().
2710b57cec5SDimitry Andric void AArch64ConditionOptimizer::modifyCmp(MachineInstr *CmpMI,
2720b57cec5SDimitry Andric     const CmpInfo &Info) {
2730b57cec5SDimitry Andric   int Imm;
2740b57cec5SDimitry Andric   unsigned Opc;
2750b57cec5SDimitry Andric   AArch64CC::CondCode Cmp;
2760b57cec5SDimitry Andric   std::tie(Imm, Opc, Cmp) = Info;
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric   MachineBasicBlock *const MBB = CmpMI->getParent();
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   // Change immediate in comparison instruction (ADDS or SUBS).
2810b57cec5SDimitry Andric   BuildMI(*MBB, CmpMI, CmpMI->getDebugLoc(), TII->get(Opc))
2820b57cec5SDimitry Andric       .add(CmpMI->getOperand(0))
2830b57cec5SDimitry Andric       .add(CmpMI->getOperand(1))
2840b57cec5SDimitry Andric       .addImm(Imm)
2850b57cec5SDimitry Andric       .add(CmpMI->getOperand(3));
2860b57cec5SDimitry Andric   CmpMI->eraseFromParent();
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric   // The fact that this comparison was picked ensures that it's related to the
2890b57cec5SDimitry Andric   // first terminator instruction.
2900b57cec5SDimitry Andric   MachineInstr &BrMI = *MBB->getFirstTerminator();
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric   // Change condition in branch instruction.
2930b57cec5SDimitry Andric   BuildMI(*MBB, BrMI, BrMI.getDebugLoc(), TII->get(AArch64::Bcc))
2940b57cec5SDimitry Andric       .addImm(Cmp)
2950b57cec5SDimitry Andric       .add(BrMI.getOperand(1));
2960b57cec5SDimitry Andric   BrMI.eraseFromParent();
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric   ++NumConditionsAdjusted;
2990b57cec5SDimitry Andric }
3000b57cec5SDimitry Andric 
3015ffd83dbSDimitry Andric // Parse a condition code returned by analyzeBranch, and compute the CondCode
3020b57cec5SDimitry Andric // corresponding to TBB.
3030b57cec5SDimitry Andric // Returns true if parsing was successful, otherwise false is returned.
3040b57cec5SDimitry Andric static bool parseCond(ArrayRef<MachineOperand> Cond, AArch64CC::CondCode &CC) {
3050b57cec5SDimitry Andric   // A normal br.cond simply has the condition code.
3060b57cec5SDimitry Andric   if (Cond[0].getImm() != -1) {
3070b57cec5SDimitry Andric     assert(Cond.size() == 1 && "Unknown Cond array format");
3080b57cec5SDimitry Andric     CC = (AArch64CC::CondCode)(int)Cond[0].getImm();
3090b57cec5SDimitry Andric     return true;
3100b57cec5SDimitry Andric   }
3110b57cec5SDimitry Andric   return false;
3120b57cec5SDimitry Andric }
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric // Adjusts one cmp instruction to another one if result of adjustment will allow
3150b57cec5SDimitry Andric // CSE.  Returns true if compare instruction was changed, otherwise false is
3160b57cec5SDimitry Andric // returned.
3170b57cec5SDimitry Andric bool AArch64ConditionOptimizer::adjustTo(MachineInstr *CmpMI,
3180b57cec5SDimitry Andric   AArch64CC::CondCode Cmp, MachineInstr *To, int ToImm)
3190b57cec5SDimitry Andric {
3200b57cec5SDimitry Andric   CmpInfo Info = adjustCmp(CmpMI, Cmp);
3210b57cec5SDimitry Andric   if (std::get<0>(Info) == ToImm && std::get<1>(Info) == To->getOpcode()) {
3220b57cec5SDimitry Andric     modifyCmp(CmpMI, Info);
3230b57cec5SDimitry Andric     return true;
3240b57cec5SDimitry Andric   }
3250b57cec5SDimitry Andric   return false;
3260b57cec5SDimitry Andric }
3270b57cec5SDimitry Andric 
3280b57cec5SDimitry Andric bool AArch64ConditionOptimizer::runOnMachineFunction(MachineFunction &MF) {
3290b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "********** AArch64 Conditional Compares **********\n"
3300b57cec5SDimitry Andric                     << "********** Function: " << MF.getName() << '\n');
3310b57cec5SDimitry Andric   if (skipFunction(MF.getFunction()))
3320b57cec5SDimitry Andric     return false;
3330b57cec5SDimitry Andric 
3340b57cec5SDimitry Andric   TII = MF.getSubtarget().getInstrInfo();
335*0fca6ea1SDimitry Andric   DomTree = &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
3360b57cec5SDimitry Andric   MRI = &MF.getRegInfo();
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric   bool Changed = false;
3390b57cec5SDimitry Andric 
3400b57cec5SDimitry Andric   // Visit blocks in dominator tree pre-order. The pre-order enables multiple
3410b57cec5SDimitry Andric   // cmp-conversions from the same head block.
3420b57cec5SDimitry Andric   // Note that updateDomTree() modifies the children of the DomTree node
3430b57cec5SDimitry Andric   // currently being visited. The df_iterator supports that; it doesn't look at
3440b57cec5SDimitry Andric   // child_begin() / child_end() until after a node has been visited.
3450b57cec5SDimitry Andric   for (MachineDomTreeNode *I : depth_first(DomTree)) {
3460b57cec5SDimitry Andric     MachineBasicBlock *HBB = I->getBlock();
3470b57cec5SDimitry Andric 
3480b57cec5SDimitry Andric     SmallVector<MachineOperand, 4> HeadCond;
3490b57cec5SDimitry Andric     MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
3500b57cec5SDimitry Andric     if (TII->analyzeBranch(*HBB, TBB, FBB, HeadCond)) {
3510b57cec5SDimitry Andric       continue;
3520b57cec5SDimitry Andric     }
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric     // Equivalence check is to skip loops.
3550b57cec5SDimitry Andric     if (!TBB || TBB == HBB) {
3560b57cec5SDimitry Andric       continue;
3570b57cec5SDimitry Andric     }
3580b57cec5SDimitry Andric 
3590b57cec5SDimitry Andric     SmallVector<MachineOperand, 4> TrueCond;
3600b57cec5SDimitry Andric     MachineBasicBlock *TBB_TBB = nullptr, *TBB_FBB = nullptr;
3610b57cec5SDimitry Andric     if (TII->analyzeBranch(*TBB, TBB_TBB, TBB_FBB, TrueCond)) {
3620b57cec5SDimitry Andric       continue;
3630b57cec5SDimitry Andric     }
3640b57cec5SDimitry Andric 
3650b57cec5SDimitry Andric     MachineInstr *HeadCmpMI = findSuitableCompare(HBB);
3660b57cec5SDimitry Andric     if (!HeadCmpMI) {
3670b57cec5SDimitry Andric       continue;
3680b57cec5SDimitry Andric     }
3690b57cec5SDimitry Andric 
3700b57cec5SDimitry Andric     MachineInstr *TrueCmpMI = findSuitableCompare(TBB);
3710b57cec5SDimitry Andric     if (!TrueCmpMI) {
3720b57cec5SDimitry Andric       continue;
3730b57cec5SDimitry Andric     }
3740b57cec5SDimitry Andric 
3750b57cec5SDimitry Andric     AArch64CC::CondCode HeadCmp;
3760b57cec5SDimitry Andric     if (HeadCond.empty() || !parseCond(HeadCond, HeadCmp)) {
3770b57cec5SDimitry Andric       continue;
3780b57cec5SDimitry Andric     }
3790b57cec5SDimitry Andric 
3800b57cec5SDimitry Andric     AArch64CC::CondCode TrueCmp;
3810b57cec5SDimitry Andric     if (TrueCond.empty() || !parseCond(TrueCond, TrueCmp)) {
3820b57cec5SDimitry Andric       continue;
3830b57cec5SDimitry Andric     }
3840b57cec5SDimitry Andric 
3850b57cec5SDimitry Andric     const int HeadImm = (int)HeadCmpMI->getOperand(2).getImm();
3860b57cec5SDimitry Andric     const int TrueImm = (int)TrueCmpMI->getOperand(2).getImm();
3870b57cec5SDimitry Andric 
3880b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Head branch:\n");
3890b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(HeadCmp)
3900b57cec5SDimitry Andric                       << '\n');
3910b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\timmediate: " << HeadImm << '\n');
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "True branch:\n");
3940b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(TrueCmp)
3950b57cec5SDimitry Andric                       << '\n');
3960b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\timmediate: " << TrueImm << '\n');
3970b57cec5SDimitry Andric 
3980b57cec5SDimitry Andric     if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::LT) ||
3990b57cec5SDimitry Andric          (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::GT)) &&
4000b57cec5SDimitry Andric         std::abs(TrueImm - HeadImm) == 2) {
4010b57cec5SDimitry Andric       // This branch transforms machine instructions that correspond to
4020b57cec5SDimitry Andric       //
4030b57cec5SDimitry Andric       // 1) (a > {TrueImm} && ...) || (a < {HeadImm} && ...)
4040b57cec5SDimitry Andric       // 2) (a < {TrueImm} && ...) || (a > {HeadImm} && ...)
4050b57cec5SDimitry Andric       //
4060b57cec5SDimitry Andric       // into
4070b57cec5SDimitry Andric       //
4080b57cec5SDimitry Andric       // 1) (a >= {NewImm} && ...) || (a <= {NewImm} && ...)
4090b57cec5SDimitry Andric       // 2) (a <= {NewImm} && ...) || (a >= {NewImm} && ...)
4100b57cec5SDimitry Andric 
4110b57cec5SDimitry Andric       CmpInfo HeadCmpInfo = adjustCmp(HeadCmpMI, HeadCmp);
4120b57cec5SDimitry Andric       CmpInfo TrueCmpInfo = adjustCmp(TrueCmpMI, TrueCmp);
4130b57cec5SDimitry Andric       if (std::get<0>(HeadCmpInfo) == std::get<0>(TrueCmpInfo) &&
4140b57cec5SDimitry Andric           std::get<1>(HeadCmpInfo) == std::get<1>(TrueCmpInfo)) {
4150b57cec5SDimitry Andric         modifyCmp(HeadCmpMI, HeadCmpInfo);
4160b57cec5SDimitry Andric         modifyCmp(TrueCmpMI, TrueCmpInfo);
4170b57cec5SDimitry Andric         Changed = true;
4180b57cec5SDimitry Andric       }
4190b57cec5SDimitry Andric     } else if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::GT) ||
4200b57cec5SDimitry Andric                 (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::LT)) &&
4210b57cec5SDimitry Andric                 std::abs(TrueImm - HeadImm) == 1) {
4220b57cec5SDimitry Andric       // This branch transforms machine instructions that correspond to
4230b57cec5SDimitry Andric       //
4240b57cec5SDimitry Andric       // 1) (a > {TrueImm} && ...) || (a > {HeadImm} && ...)
4250b57cec5SDimitry Andric       // 2) (a < {TrueImm} && ...) || (a < {HeadImm} && ...)
4260b57cec5SDimitry Andric       //
4270b57cec5SDimitry Andric       // into
4280b57cec5SDimitry Andric       //
4290b57cec5SDimitry Andric       // 1) (a <= {NewImm} && ...) || (a >  {NewImm} && ...)
4300b57cec5SDimitry Andric       // 2) (a <  {NewImm} && ...) || (a >= {NewImm} && ...)
4310b57cec5SDimitry Andric 
4320b57cec5SDimitry Andric       // GT -> GE transformation increases immediate value, so picking the
4330b57cec5SDimitry Andric       // smaller one; LT -> LE decreases immediate value so invert the choice.
4340b57cec5SDimitry Andric       bool adjustHeadCond = (HeadImm < TrueImm);
4350b57cec5SDimitry Andric       if (HeadCmp == AArch64CC::LT) {
4360b57cec5SDimitry Andric           adjustHeadCond = !adjustHeadCond;
4370b57cec5SDimitry Andric       }
4380b57cec5SDimitry Andric 
4390b57cec5SDimitry Andric       if (adjustHeadCond) {
4400b57cec5SDimitry Andric         Changed |= adjustTo(HeadCmpMI, HeadCmp, TrueCmpMI, TrueImm);
4410b57cec5SDimitry Andric       } else {
4420b57cec5SDimitry Andric         Changed |= adjustTo(TrueCmpMI, TrueCmp, HeadCmpMI, HeadImm);
4430b57cec5SDimitry Andric       }
4440b57cec5SDimitry Andric     }
4450b57cec5SDimitry Andric     // Other transformation cases almost never occur due to generation of < or >
4460b57cec5SDimitry Andric     // comparisons instead of <= and >=.
4470b57cec5SDimitry Andric   }
4480b57cec5SDimitry Andric 
4490b57cec5SDimitry Andric   return Changed;
4500b57cec5SDimitry Andric }
451