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" 77*480093f4SDimitry 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) 1290b57cec5SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) 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 { 1380b57cec5SDimitry Andric AU.addRequired<MachineDominatorTree>(); 1390b57cec5SDimitry Andric AU.addPreserved<MachineDominatorTree>(); 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) { 1480b57cec5SDimitry Andric MachineBasicBlock::iterator I = MBB->getFirstTerminator(); 1490b57cec5SDimitry Andric if (I == MBB->end()) 1500b57cec5SDimitry Andric return nullptr; 1510b57cec5SDimitry Andric 1520b57cec5SDimitry Andric if (I->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. 1560b57cec5SDimitry 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. 1610b57cec5SDimitry Andric for (MachineBasicBlock::iterator B = MBB->begin(); I != B;) { 1620b57cec5SDimitry Andric --I; 1630b57cec5SDimitry Andric assert(!I->isTerminator() && "Spurious terminator"); 1640b57cec5SDimitry Andric // Check if there is any use of NZCV between CMP and Bcc. 1650b57cec5SDimitry Andric if (I->readsRegister(AArch64::NZCV)) 1660b57cec5SDimitry Andric return nullptr; 1670b57cec5SDimitry Andric switch (I->getOpcode()) { 1680b57cec5SDimitry Andric // cmp is an alias for subs with a dead destination register. 1690b57cec5SDimitry Andric case AArch64::SUBSWri: 1700b57cec5SDimitry Andric case AArch64::SUBSXri: 1710b57cec5SDimitry Andric // cmn is an alias for adds with a dead destination register. 1720b57cec5SDimitry Andric case AArch64::ADDSWri: 1730b57cec5SDimitry Andric case AArch64::ADDSXri: { 1740b57cec5SDimitry Andric unsigned ShiftAmt = AArch64_AM::getShiftValue(I->getOperand(3).getImm()); 1750b57cec5SDimitry Andric if (!I->getOperand(2).isImm()) { 1760b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Immediate of cmp is symbolic, " << *I << '\n'); 1770b57cec5SDimitry Andric return nullptr; 1780b57cec5SDimitry Andric } else if (I->getOperand(2).getImm() << ShiftAmt >= 0xfff) { 1790b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Immediate of cmp may be out of range, " << *I 1800b57cec5SDimitry Andric << '\n'); 1810b57cec5SDimitry Andric return nullptr; 1820b57cec5SDimitry Andric } else if (!MRI->use_empty(I->getOperand(0).getReg())) { 1830b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Destination of cmp is not dead, " << *I << '\n'); 1840b57cec5SDimitry Andric return nullptr; 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric return &*I; 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric // Prevent false positive case like: 1890b57cec5SDimitry Andric // cmp w19, #0 1900b57cec5SDimitry Andric // cinc w0, w19, gt 1910b57cec5SDimitry Andric // ... 1920b57cec5SDimitry Andric // fcmp d8, #0.0 1930b57cec5SDimitry Andric // b.gt .LBB0_5 1940b57cec5SDimitry Andric case AArch64::FCMPDri: 1950b57cec5SDimitry Andric case AArch64::FCMPSri: 1960b57cec5SDimitry Andric case AArch64::FCMPESri: 1970b57cec5SDimitry Andric case AArch64::FCMPEDri: 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric case AArch64::SUBSWrr: 2000b57cec5SDimitry Andric case AArch64::SUBSXrr: 2010b57cec5SDimitry Andric case AArch64::ADDSWrr: 2020b57cec5SDimitry Andric case AArch64::ADDSXrr: 2030b57cec5SDimitry Andric case AArch64::FCMPSrr: 2040b57cec5SDimitry Andric case AArch64::FCMPDrr: 2050b57cec5SDimitry Andric case AArch64::FCMPESrr: 2060b57cec5SDimitry Andric case AArch64::FCMPEDrr: 2070b57cec5SDimitry Andric // Skip comparison instructions without immediate operands. 2080b57cec5SDimitry Andric return nullptr; 2090b57cec5SDimitry Andric } 2100b57cec5SDimitry Andric } 2110b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Flags not defined in " << printMBBReference(*MBB) 2120b57cec5SDimitry Andric << '\n'); 2130b57cec5SDimitry Andric return nullptr; 2140b57cec5SDimitry Andric } 2150b57cec5SDimitry Andric 2160b57cec5SDimitry Andric // Changes opcode adds <-> subs considering register operand width. 2170b57cec5SDimitry Andric static int getComplementOpc(int Opc) { 2180b57cec5SDimitry Andric switch (Opc) { 2190b57cec5SDimitry Andric case AArch64::ADDSWri: return AArch64::SUBSWri; 2200b57cec5SDimitry Andric case AArch64::ADDSXri: return AArch64::SUBSXri; 2210b57cec5SDimitry Andric case AArch64::SUBSWri: return AArch64::ADDSWri; 2220b57cec5SDimitry Andric case AArch64::SUBSXri: return AArch64::ADDSXri; 2230b57cec5SDimitry Andric default: 2240b57cec5SDimitry Andric llvm_unreachable("Unexpected opcode"); 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric // Changes form of comparison inclusive <-> exclusive. 2290b57cec5SDimitry Andric static AArch64CC::CondCode getAdjustedCmp(AArch64CC::CondCode Cmp) { 2300b57cec5SDimitry Andric switch (Cmp) { 2310b57cec5SDimitry Andric case AArch64CC::GT: return AArch64CC::GE; 2320b57cec5SDimitry Andric case AArch64CC::GE: return AArch64CC::GT; 2330b57cec5SDimitry Andric case AArch64CC::LT: return AArch64CC::LE; 2340b57cec5SDimitry Andric case AArch64CC::LE: return AArch64CC::LT; 2350b57cec5SDimitry Andric default: 2360b57cec5SDimitry Andric llvm_unreachable("Unexpected condition code"); 2370b57cec5SDimitry Andric } 2380b57cec5SDimitry Andric } 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric // Transforms GT -> GE, GE -> GT, LT -> LE, LE -> LT by updating comparison 2410b57cec5SDimitry Andric // operator and condition code. 2420b57cec5SDimitry Andric AArch64ConditionOptimizer::CmpInfo AArch64ConditionOptimizer::adjustCmp( 2430b57cec5SDimitry Andric MachineInstr *CmpMI, AArch64CC::CondCode Cmp) { 2440b57cec5SDimitry Andric unsigned Opc = CmpMI->getOpcode(); 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric // CMN (compare with negative immediate) is an alias to ADDS (as 2470b57cec5SDimitry Andric // "operand - negative" == "operand + positive") 2480b57cec5SDimitry Andric bool Negative = (Opc == AArch64::ADDSWri || Opc == AArch64::ADDSXri); 2490b57cec5SDimitry Andric 2500b57cec5SDimitry Andric int Correction = (Cmp == AArch64CC::GT) ? 1 : -1; 2510b57cec5SDimitry Andric // Negate Correction value for comparison with negative immediate (CMN). 2520b57cec5SDimitry Andric if (Negative) { 2530b57cec5SDimitry Andric Correction = -Correction; 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric const int OldImm = (int)CmpMI->getOperand(2).getImm(); 2570b57cec5SDimitry Andric const int NewImm = std::abs(OldImm + Correction); 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric // Handle +0 -> -1 and -0 -> +1 (CMN with 0 immediate) transitions by 2600b57cec5SDimitry Andric // adjusting compare instruction opcode. 2610b57cec5SDimitry Andric if (OldImm == 0 && ((Negative && Correction == 1) || 2620b57cec5SDimitry Andric (!Negative && Correction == -1))) { 2630b57cec5SDimitry Andric Opc = getComplementOpc(Opc); 2640b57cec5SDimitry Andric } 2650b57cec5SDimitry Andric 2660b57cec5SDimitry Andric return CmpInfo(NewImm, Opc, getAdjustedCmp(Cmp)); 2670b57cec5SDimitry Andric } 2680b57cec5SDimitry Andric 2690b57cec5SDimitry Andric // Applies changes to comparison instruction suggested by adjustCmp(). 2700b57cec5SDimitry Andric void AArch64ConditionOptimizer::modifyCmp(MachineInstr *CmpMI, 2710b57cec5SDimitry Andric const CmpInfo &Info) { 2720b57cec5SDimitry Andric int Imm; 2730b57cec5SDimitry Andric unsigned Opc; 2740b57cec5SDimitry Andric AArch64CC::CondCode Cmp; 2750b57cec5SDimitry Andric std::tie(Imm, Opc, Cmp) = Info; 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric MachineBasicBlock *const MBB = CmpMI->getParent(); 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric // Change immediate in comparison instruction (ADDS or SUBS). 2800b57cec5SDimitry Andric BuildMI(*MBB, CmpMI, CmpMI->getDebugLoc(), TII->get(Opc)) 2810b57cec5SDimitry Andric .add(CmpMI->getOperand(0)) 2820b57cec5SDimitry Andric .add(CmpMI->getOperand(1)) 2830b57cec5SDimitry Andric .addImm(Imm) 2840b57cec5SDimitry Andric .add(CmpMI->getOperand(3)); 2850b57cec5SDimitry Andric CmpMI->eraseFromParent(); 2860b57cec5SDimitry Andric 2870b57cec5SDimitry Andric // The fact that this comparison was picked ensures that it's related to the 2880b57cec5SDimitry Andric // first terminator instruction. 2890b57cec5SDimitry Andric MachineInstr &BrMI = *MBB->getFirstTerminator(); 2900b57cec5SDimitry Andric 2910b57cec5SDimitry Andric // Change condition in branch instruction. 2920b57cec5SDimitry Andric BuildMI(*MBB, BrMI, BrMI.getDebugLoc(), TII->get(AArch64::Bcc)) 2930b57cec5SDimitry Andric .addImm(Cmp) 2940b57cec5SDimitry Andric .add(BrMI.getOperand(1)); 2950b57cec5SDimitry Andric BrMI.eraseFromParent(); 2960b57cec5SDimitry Andric 2970b57cec5SDimitry Andric MBB->updateTerminator(); 2980b57cec5SDimitry Andric 2990b57cec5SDimitry Andric ++NumConditionsAdjusted; 3000b57cec5SDimitry Andric } 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric // Parse a condition code returned by AnalyzeBranch, and compute the CondCode 3030b57cec5SDimitry Andric // corresponding to TBB. 3040b57cec5SDimitry Andric // Returns true if parsing was successful, otherwise false is returned. 3050b57cec5SDimitry Andric static bool parseCond(ArrayRef<MachineOperand> Cond, AArch64CC::CondCode &CC) { 3060b57cec5SDimitry Andric // A normal br.cond simply has the condition code. 3070b57cec5SDimitry Andric if (Cond[0].getImm() != -1) { 3080b57cec5SDimitry Andric assert(Cond.size() == 1 && "Unknown Cond array format"); 3090b57cec5SDimitry Andric CC = (AArch64CC::CondCode)(int)Cond[0].getImm(); 3100b57cec5SDimitry Andric return true; 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric return false; 3130b57cec5SDimitry Andric } 3140b57cec5SDimitry Andric 3150b57cec5SDimitry Andric // Adjusts one cmp instruction to another one if result of adjustment will allow 3160b57cec5SDimitry Andric // CSE. Returns true if compare instruction was changed, otherwise false is 3170b57cec5SDimitry Andric // returned. 3180b57cec5SDimitry Andric bool AArch64ConditionOptimizer::adjustTo(MachineInstr *CmpMI, 3190b57cec5SDimitry Andric AArch64CC::CondCode Cmp, MachineInstr *To, int ToImm) 3200b57cec5SDimitry Andric { 3210b57cec5SDimitry Andric CmpInfo Info = adjustCmp(CmpMI, Cmp); 3220b57cec5SDimitry Andric if (std::get<0>(Info) == ToImm && std::get<1>(Info) == To->getOpcode()) { 3230b57cec5SDimitry Andric modifyCmp(CmpMI, Info); 3240b57cec5SDimitry Andric return true; 3250b57cec5SDimitry Andric } 3260b57cec5SDimitry Andric return false; 3270b57cec5SDimitry Andric } 3280b57cec5SDimitry Andric 3290b57cec5SDimitry Andric bool AArch64ConditionOptimizer::runOnMachineFunction(MachineFunction &MF) { 3300b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** AArch64 Conditional Compares **********\n" 3310b57cec5SDimitry Andric << "********** Function: " << MF.getName() << '\n'); 3320b57cec5SDimitry Andric if (skipFunction(MF.getFunction())) 3330b57cec5SDimitry Andric return false; 3340b57cec5SDimitry Andric 3350b57cec5SDimitry Andric TII = MF.getSubtarget().getInstrInfo(); 3360b57cec5SDimitry Andric DomTree = &getAnalysis<MachineDominatorTree>(); 3370b57cec5SDimitry Andric MRI = &MF.getRegInfo(); 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric bool Changed = false; 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric // Visit blocks in dominator tree pre-order. The pre-order enables multiple 3420b57cec5SDimitry Andric // cmp-conversions from the same head block. 3430b57cec5SDimitry Andric // Note that updateDomTree() modifies the children of the DomTree node 3440b57cec5SDimitry Andric // currently being visited. The df_iterator supports that; it doesn't look at 3450b57cec5SDimitry Andric // child_begin() / child_end() until after a node has been visited. 3460b57cec5SDimitry Andric for (MachineDomTreeNode *I : depth_first(DomTree)) { 3470b57cec5SDimitry Andric MachineBasicBlock *HBB = I->getBlock(); 3480b57cec5SDimitry Andric 3490b57cec5SDimitry Andric SmallVector<MachineOperand, 4> HeadCond; 3500b57cec5SDimitry Andric MachineBasicBlock *TBB = nullptr, *FBB = nullptr; 3510b57cec5SDimitry Andric if (TII->analyzeBranch(*HBB, TBB, FBB, HeadCond)) { 3520b57cec5SDimitry Andric continue; 3530b57cec5SDimitry Andric } 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric // Equivalence check is to skip loops. 3560b57cec5SDimitry Andric if (!TBB || TBB == HBB) { 3570b57cec5SDimitry Andric continue; 3580b57cec5SDimitry Andric } 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric SmallVector<MachineOperand, 4> TrueCond; 3610b57cec5SDimitry Andric MachineBasicBlock *TBB_TBB = nullptr, *TBB_FBB = nullptr; 3620b57cec5SDimitry Andric if (TII->analyzeBranch(*TBB, TBB_TBB, TBB_FBB, TrueCond)) { 3630b57cec5SDimitry Andric continue; 3640b57cec5SDimitry Andric } 3650b57cec5SDimitry Andric 3660b57cec5SDimitry Andric MachineInstr *HeadCmpMI = findSuitableCompare(HBB); 3670b57cec5SDimitry Andric if (!HeadCmpMI) { 3680b57cec5SDimitry Andric continue; 3690b57cec5SDimitry Andric } 3700b57cec5SDimitry Andric 3710b57cec5SDimitry Andric MachineInstr *TrueCmpMI = findSuitableCompare(TBB); 3720b57cec5SDimitry Andric if (!TrueCmpMI) { 3730b57cec5SDimitry Andric continue; 3740b57cec5SDimitry Andric } 3750b57cec5SDimitry Andric 3760b57cec5SDimitry Andric AArch64CC::CondCode HeadCmp; 3770b57cec5SDimitry Andric if (HeadCond.empty() || !parseCond(HeadCond, HeadCmp)) { 3780b57cec5SDimitry Andric continue; 3790b57cec5SDimitry Andric } 3800b57cec5SDimitry Andric 3810b57cec5SDimitry Andric AArch64CC::CondCode TrueCmp; 3820b57cec5SDimitry Andric if (TrueCond.empty() || !parseCond(TrueCond, TrueCmp)) { 3830b57cec5SDimitry Andric continue; 3840b57cec5SDimitry Andric } 3850b57cec5SDimitry Andric 3860b57cec5SDimitry Andric const int HeadImm = (int)HeadCmpMI->getOperand(2).getImm(); 3870b57cec5SDimitry Andric const int TrueImm = (int)TrueCmpMI->getOperand(2).getImm(); 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Head branch:\n"); 3900b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(HeadCmp) 3910b57cec5SDimitry Andric << '\n'); 3920b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\timmediate: " << HeadImm << '\n'); 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "True branch:\n"); 3950b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\tcondition: " << AArch64CC::getCondCodeName(TrueCmp) 3960b57cec5SDimitry Andric << '\n'); 3970b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\timmediate: " << TrueImm << '\n'); 3980b57cec5SDimitry Andric 3990b57cec5SDimitry Andric if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::LT) || 4000b57cec5SDimitry Andric (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::GT)) && 4010b57cec5SDimitry Andric std::abs(TrueImm - HeadImm) == 2) { 4020b57cec5SDimitry Andric // This branch transforms machine instructions that correspond to 4030b57cec5SDimitry Andric // 4040b57cec5SDimitry Andric // 1) (a > {TrueImm} && ...) || (a < {HeadImm} && ...) 4050b57cec5SDimitry Andric // 2) (a < {TrueImm} && ...) || (a > {HeadImm} && ...) 4060b57cec5SDimitry Andric // 4070b57cec5SDimitry Andric // into 4080b57cec5SDimitry Andric // 4090b57cec5SDimitry Andric // 1) (a >= {NewImm} && ...) || (a <= {NewImm} && ...) 4100b57cec5SDimitry Andric // 2) (a <= {NewImm} && ...) || (a >= {NewImm} && ...) 4110b57cec5SDimitry Andric 4120b57cec5SDimitry Andric CmpInfo HeadCmpInfo = adjustCmp(HeadCmpMI, HeadCmp); 4130b57cec5SDimitry Andric CmpInfo TrueCmpInfo = adjustCmp(TrueCmpMI, TrueCmp); 4140b57cec5SDimitry Andric if (std::get<0>(HeadCmpInfo) == std::get<0>(TrueCmpInfo) && 4150b57cec5SDimitry Andric std::get<1>(HeadCmpInfo) == std::get<1>(TrueCmpInfo)) { 4160b57cec5SDimitry Andric modifyCmp(HeadCmpMI, HeadCmpInfo); 4170b57cec5SDimitry Andric modifyCmp(TrueCmpMI, TrueCmpInfo); 4180b57cec5SDimitry Andric Changed = true; 4190b57cec5SDimitry Andric } 4200b57cec5SDimitry Andric } else if (((HeadCmp == AArch64CC::GT && TrueCmp == AArch64CC::GT) || 4210b57cec5SDimitry Andric (HeadCmp == AArch64CC::LT && TrueCmp == AArch64CC::LT)) && 4220b57cec5SDimitry Andric std::abs(TrueImm - HeadImm) == 1) { 4230b57cec5SDimitry Andric // This branch transforms machine instructions that correspond to 4240b57cec5SDimitry Andric // 4250b57cec5SDimitry Andric // 1) (a > {TrueImm} && ...) || (a > {HeadImm} && ...) 4260b57cec5SDimitry Andric // 2) (a < {TrueImm} && ...) || (a < {HeadImm} && ...) 4270b57cec5SDimitry Andric // 4280b57cec5SDimitry Andric // into 4290b57cec5SDimitry Andric // 4300b57cec5SDimitry Andric // 1) (a <= {NewImm} && ...) || (a > {NewImm} && ...) 4310b57cec5SDimitry Andric // 2) (a < {NewImm} && ...) || (a >= {NewImm} && ...) 4320b57cec5SDimitry Andric 4330b57cec5SDimitry Andric // GT -> GE transformation increases immediate value, so picking the 4340b57cec5SDimitry Andric // smaller one; LT -> LE decreases immediate value so invert the choice. 4350b57cec5SDimitry Andric bool adjustHeadCond = (HeadImm < TrueImm); 4360b57cec5SDimitry Andric if (HeadCmp == AArch64CC::LT) { 4370b57cec5SDimitry Andric adjustHeadCond = !adjustHeadCond; 4380b57cec5SDimitry Andric } 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric if (adjustHeadCond) { 4410b57cec5SDimitry Andric Changed |= adjustTo(HeadCmpMI, HeadCmp, TrueCmpMI, TrueImm); 4420b57cec5SDimitry Andric } else { 4430b57cec5SDimitry Andric Changed |= adjustTo(TrueCmpMI, TrueCmp, HeadCmpMI, HeadImm); 4440b57cec5SDimitry Andric } 4450b57cec5SDimitry Andric } 4460b57cec5SDimitry Andric // Other transformation cases almost never occur due to generation of < or > 4470b57cec5SDimitry Andric // comparisons instead of <= and >=. 4480b57cec5SDimitry Andric } 4490b57cec5SDimitry Andric 4500b57cec5SDimitry Andric return Changed; 4510b57cec5SDimitry Andric } 452