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
AArch64ConditionOptimizer()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
getPassName() const1180b57cec5SDimitry 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)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)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
getAnalysisUsage(AnalysisUsage & AU) const1370b57cec5SDimitry 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.
findSuitableCompare(MachineBasicBlock * MBB)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.
getComplementOpc(int Opc)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.
getAdjustedCmp(AArch64CC::CondCode Cmp)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.
adjustCmp(MachineInstr * CmpMI,AArch64CC::CondCode Cmp)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().
modifyCmp(MachineInstr * CmpMI,const CmpInfo & Info)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.
parseCond(ArrayRef<MachineOperand> Cond,AArch64CC::CondCode & CC)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.
adjustTo(MachineInstr * CmpMI,AArch64CC::CondCode Cmp,MachineInstr * To,int ToImm)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
runOnMachineFunction(MachineFunction & MF)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