diff --git a/llvm/lib/Target/PowerPC/CMakeLists.txt b/llvm/lib/Target/PowerPC/CMakeLists.txt --- a/llvm/lib/Target/PowerPC/CMakeLists.txt +++ b/llvm/lib/Target/PowerPC/CMakeLists.txt @@ -26,6 +26,7 @@ PPCBranchCoalescing.cpp PPCCallingConv.cpp PPCCCState.cpp + PPCCTRLoops.cpp PPCCTRLoopsVerify.cpp PPCExpandAtomicPseudoInsts.cpp PPCHazardRecognizers.cpp diff --git a/llvm/lib/Target/PowerPC/PPC.h b/llvm/lib/Target/PowerPC/PPC.h --- a/llvm/lib/Target/PowerPC/PPC.h +++ b/llvm/lib/Target/PowerPC/PPC.h @@ -52,6 +52,7 @@ FunctionPass *createPPCExpandISELPass(); FunctionPass *createPPCPreEmitPeepholePass(); FunctionPass *createPPCExpandAtomicPseudoPass(); + FunctionPass *createPPCCTRLoopsPass(); void LowerPPCMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, AsmPrinter &AP); bool LowerPPCMachineOperandToMCOperand(const MachineOperand &MO, @@ -75,6 +76,7 @@ void initializePPCTLSDynamicCallPass(PassRegistry &); void initializePPCMIPeepholePass(PassRegistry&); void initializePPCExpandAtomicPseudoPass(PassRegistry &); + void initializePPCCTRLoopsPass(PassRegistry &); extern char &PPCVSXFMAMutateID; diff --git a/llvm/lib/Target/PowerPC/PPCCTRLoops.cpp b/llvm/lib/Target/PowerPC/PPCCTRLoops.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/PPCCTRLoops.cpp @@ -0,0 +1,296 @@ +//===-- PPCCTRLoops.cpp - Generate CTR loops ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass generates machine instructions for the CTR loops related pseudos: +// 1: MTCTRPseudo/DecreaseCTRPseudo +// 2: MTCTR8Pseudo/DecreaseCTR8Pseudo +// +// If a CTR loop can be generated: +// 1: MTCTRPseudo/MTCTR8Pseudo will be converted to "mtctr" +// 2: DecreaseCTRPseudo/DecreaseCTR8Pseudo will be converted to "bdnz" and its +// user branch instruction can be deleted. +// +// If a CTR loop can not be generated due to clobber of CTR in the loop body: +// 1: MTCTRPseudo/MTCTR8Pseudo can be deleted. +// 2: DecreaseCTRPseudo/DecreaseCTR8Pseudo will be converted to "addi -1" and +// a "cmpl/cmpli". +// +// This pass runs just before register allocation, because we don't want +// register allocator to allocate register for DecreaseCTRPseudo if a CTR can be +// generated or if a CTR loop can not be generated, we don't have any condition +// register for the new added "cmpl/cmpli". +// +//===----------------------------------------------------------------------===// + +#include "PPC.h" +#include "PPCInstrInfo.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Register.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +#define DEBUG_TYPE "ppc-ctrloops" + +namespace { +class PPCCTRLoops : public MachineFunctionPass { +public: + static char ID; + + PPCCTRLoops() : MachineFunctionPass(ID) { + initializePPCCTRLoopsPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + const PPCInstrInfo *TII = nullptr; + MachineRegisterInfo *MRI = nullptr; + + bool processLoop(MachineLoop *ML); + bool isCTRClobber(MachineInstr *MI, bool CheckReads) const; + void expandNormalLoops(MachineLoop *ML, MachineInstr *Start, + MachineInstr *Dec); + void expandCTRLoops(MachineInstr *Start, MachineInstr *Dec); +}; +} // namespace + +char PPCCTRLoops::ID = 0; + +INITIALIZE_PASS(PPCCTRLoops, DEBUG_TYPE, "PowerPC CTR loops generation", false, + false) + +FunctionPass *llvm::createPPCCTRLoopsPass() { return new PPCCTRLoops(); } + +bool PPCCTRLoops::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + + auto &MLI = getAnalysis(); + TII = static_cast(MF.getSubtarget().getInstrInfo()); + MRI = &MF.getRegInfo(); + + for (auto ML : MLI) { + if (!ML->getParentLoop()) + Changed |= processLoop(ML); + } + + return Changed; +} + +bool PPCCTRLoops::isCTRClobber(MachineInstr *MI, bool CheckReads) const { + if ((MI->definesRegister(PPC::CTR) && !MI->registerDefIsDead(PPC::CTR)) || + (MI->definesRegister(PPC::CTR8) && !MI->registerDefIsDead(PPC::CTR8))) + return true; + + if (!CheckReads) + return false; + + if (MI->getDesc().isCall()) + return true; + + // We define the CTR in the loop preheader, so if there is any CTR reader in + // the loop, we also can not use CTR loop form. + if (MI->readsRegister(PPC::CTR) || MI->readsRegister(PPC::CTR8)) + return true; + + return false; +} + +bool PPCCTRLoops::processLoop(MachineLoop *ML) { + bool Changed = false; + + // Align with HardwareLoop pass, process inner loops first. + for (auto I = ML->begin(), E = ML->end(); I != E; ++I) + Changed |= processLoop(*I); + + auto IsLoopStart = [](MachineInstr &MI) { + return MI.getOpcode() == PPC::MTCTRPseudo || + MI.getOpcode() == PPC::MTCTR8Pseudo; + }; + + auto SearchForStart = + [&IsLoopStart](MachineBasicBlock *MBB) -> MachineInstr * { + for (auto &MI : *MBB) { + if (IsLoopStart(MI)) + return &MI; + } + return nullptr; + }; + + MachineInstr *Start = nullptr; + MachineInstr *Dec = nullptr; + bool Revert = false; + + MachineBasicBlock *Preheader = ML->getLoopPreheader(); + // If there is no preheader for this loop, there must be no MTCTRPseudo + // eigher. + if (!Preheader) + return false; + + Start = SearchForStart(Preheader); + // This is not a CTR loop candidate. + if (!Start) + return false; + + // If CTR is live to the preheader, we can not redefine the CTR register. + if (Preheader->isLiveIn(PPC::CTR) || Preheader->isLiveIn(PPC::CTR8)) + Revert = true; + + // Make sure there is also no CTR clobber in the block preheader between the + // begin and MTCTR. + for (MachineBasicBlock::reverse_instr_iterator I = + std::next(Start->getReverseIterator()); + I != Preheader->instr_rend(); ++I) + // Only check the definitions of CTR. If there is non-dead definition for + // the CTR, we conservatively don't generate a CTR loop. + if (isCTRClobber(&*I, false /* CheckReads */)) + Revert = true; + + // Make sure there is also no CTR clobber/user in the block preheader between + // MTCTR and the end. + for (MachineBasicBlock::instr_iterator I = std::next(Start->getIterator()); + I != Preheader->instr_end(); ++I) + if (isCTRClobber(&*I, true /* CheckReads */)) + Revert = true; + + // Find the CTR loop components and decide whether or not to fall back to a + // normal loop. + for (auto *MBB : reverse(ML->getBlocks())) { + for (auto &MI : *MBB) { + if (MI.getOpcode() == PPC::DecreaseCTRPseudo || + MI.getOpcode() == PPC::DecreaseCTR8Pseudo) + Dec = &MI; + else if (!Revert) + // If any instruction clobber CTR, then we can not generate a CTR loop. + Revert |= isCTRClobber(&MI, true /* CheckReads */); + } + if (Dec && Revert) + break; + } + + assert(Dec && "CTR loop is not complete!\n"); + + if (Revert) + expandNormalLoops(ML, Start, Dec); + else + expandCTRLoops(Start, Dec); + return true; +} + +void PPCCTRLoops::expandNormalLoops(MachineLoop *ML, MachineInstr *Start, + MachineInstr *Dec) { + bool is32Bit = (Start->getOpcode() == PPC::MTCTRPseudo); + + MachineBasicBlock *Preheader = Start->getParent(); + MachineBasicBlock *Exiting = Dec->getParent(); + assert((Preheader && Exiting) && + "Preheader and exiting should exist for CTR loop!\n"); + + assert(Dec->getOperand(1).getImm() == 1 && + "Loop decrement stride must be 1\n"); + + unsigned ADDIOpcode = is32Bit ? PPC::ADDI : PPC::ADDI8; + unsigned CMPOpcode = is32Bit ? PPC::CMPWI : PPC::CMPDI; + + Register PHIDef = + MRI->createVirtualRegister(is32Bit ? &PPC::GPRC_and_GPRC_NOR0RegClass + : &PPC::G8RC_and_G8RC_NOX0RegClass); + + Start->getParent()->getParent()->getProperties().reset( + MachineFunctionProperties::Property::NoPHIs); + + // Generate "PHI" in the header block. + auto PHIMIB = BuildMI(*ML->getHeader(), ML->getHeader()->getFirstNonPHI(), + DebugLoc(), TII->get(TargetOpcode::PHI), PHIDef); + PHIMIB.addReg(Start->getOperand(0).getReg()).addMBB(Preheader); + + Register ADDIDef = + MRI->createVirtualRegister(is32Bit ? &PPC::GPRC_and_GPRC_NOR0RegClass + : &PPC::G8RC_and_G8RC_NOX0RegClass); + // Generate "addi -1" in the exiting block. + auto ADDIMIB = + BuildMI(*Exiting, Dec, Dec->getDebugLoc(), TII->get(ADDIOpcode), ADDIDef); + ADDIMIB.addReg(PHIDef); + ADDIMIB.addImm(-1); + + // Add another input for the PHI node. + PHIMIB.addReg(ADDIDef).addMBB(Exiting); + + // Generate the compare in the exiting block. + Register CMPDef = MRI->createVirtualRegister(&PPC::CRRCRegClass); + auto CMPMIB = + BuildMI(*Exiting, Dec, Dec->getDebugLoc(), TII->get(CMPOpcode), CMPDef); + CMPMIB.addReg(ADDIDef).addImm(0); + + BuildMI(*Exiting, Dec, Dec->getDebugLoc(), TII->get(TargetOpcode::COPY), + Dec->getOperand(0).getReg()) + .addReg(CMPMIB->getOperand(0).getReg(), 0, PPC::sub_gt); + + // Remove the pseudo instructions. + Start->eraseFromParent(); + Dec->eraseFromParent(); +} + +void PPCCTRLoops::expandCTRLoops(MachineInstr *Start, MachineInstr *Dec) { + bool is32Bit = (Start->getOpcode() == PPC::MTCTRPseudo); + + MachineBasicBlock *Preheader = Start->getParent(); + MachineBasicBlock *Exiting = Dec->getParent(); + assert((Preheader && Exiting) && + "Preheader and exiting should exist for CTR loop!\n"); + + assert(Dec->getOperand(1).getImm() == 1 && "Loop decrement must be 1\n"); + + unsigned BDNZOpcode = is32Bit ? PPC::BDNZ : PPC::BDNZ8; + unsigned BDZOpcode = is32Bit ? PPC::BDZ : PPC::BDZ8; + auto BrInstr = MRI->use_instr_begin(Dec->getOperand(0).getReg()); + assert(MRI->hasOneUse(Dec->getOperand(0).getReg()) && + "There should be only one user for loop decrement pseudo!\n"); + unsigned Opcode = 0; + switch (BrInstr->getOpcode()) { + case PPC::BC: + Opcode = BDNZOpcode; + break; + case PPC::BCn: + Opcode = BDZOpcode; + break; + default: + assert(true && "Unhandled branch user for DecreaseCTRPseudo."); + } + unsigned MTCTROpcode = is32Bit ? PPC::MTCTR : PPC::MTCTR8; + + // Generate "mtctr" in the loop preheader. + auto MTCTRMIB = + BuildMI(*Preheader, Start, Start->getDebugLoc(), TII->get(MTCTROpcode)); + MTCTRMIB.addReg(Start->getOperand(0).getReg()); + + // Generate "bdnz/bdz" in the exiting block. + auto BDZMIB = BuildMI(*Exiting, Dec, Dec->getDebugLoc(), TII->get(Opcode)); + BDZMIB.addMBB(BrInstr->getOperand(1).getMBB()); + + // Remove the pseudo instructions. + Start->eraseFromParent(); + BrInstr->eraseFromParent(); + Dec->eraseFromParent(); +} diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -580,6 +580,14 @@ PPC970_DGroup_First, PPC970_Unit_FXU; } + +let Defs = [CTR] in +def MTCTR8Pseudo : PPCEmitTimePseudo<(outs), (ins g8rc:$rS), "#MTCTR8Pseudo", []>; + +let Uses = [CTR], Defs = [CTR] in +def DecreaseCTR8Pseudo : PPCEmitTimePseudo<(outs crbitrc:$rT), (ins i64imm:$stride), + "#DecreaseCTR8Pseudo", []>; + let Pattern = [(set i64:$rT, readcyclecounter)] in def MFTB8 : XFXForm_1_ext<31, 339, 268, (outs g8rc:$rT), (ins), "mfspr $rT, 268", IIC_SprMFTB>, diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -3056,6 +3056,13 @@ PPC970_DGroup_First, PPC970_Unit_FXU; } +let Defs = [CTR] in +def MTCTRPseudo : PPCEmitTimePseudo<(outs), (ins gprc:$rS), "#MTCTRPseudo", []>; + +let Uses = [CTR], Defs = [CTR] in +def DecreaseCTRPseudo : PPCEmitTimePseudo<(outs crbitrc:$rT), (ins i32imm:$stride), + "#DecreaseCTRPseudo", []>; + let hasSideEffects = 0 in { let Defs = [LR] in { def MTLR : XFXForm_7_ext<31, 467, 8, (outs), (ins gprc:$rS), diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp --- a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp +++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp @@ -134,6 +134,7 @@ initializePPCGenScalarMASSEntriesPass(PR); initializePPCExpandAtomicPseudoPass(PR); initializeGlobalISel(PR); + initializePPCCTRLoopsPass(PR); } static bool isLittleEndianTriple(const Triple &T) { @@ -541,6 +542,12 @@ if (getOptLevel() != CodeGenOpt::None) addPass(&MachinePipelinerID); + + // Must run CTR loops pass after MachinePipeliner pass. + // MachinePipeliner needs to know if a MTCTR is for a CTR loop, so we must + // keep MTCTRPseudo unchanged. + if (getOptLevel() != CodeGenOpt::None) + addPass(createPPCCTRLoopsPass()); } void PPCPassConfig::addPreSched2() { diff --git a/llvm/test/CodeGen/PowerPC/O3-pipeline.ll b/llvm/test/CodeGen/PowerPC/O3-pipeline.ll --- a/llvm/test/CodeGen/PowerPC/O3-pipeline.ll +++ b/llvm/test/CodeGen/PowerPC/O3-pipeline.ll @@ -135,6 +135,9 @@ ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: Modulo Software Pipelining +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: PowerPC CTR loops generation ; CHECK-NEXT: Detect Dead Lanes ; CHECK-NEXT: Process Implicit Definitions ; CHECK-NEXT: Remove unreachable machine basic blocks diff --git a/llvm/test/CodeGen/PowerPC/ctrloops.mir b/llvm/test/CodeGen/PowerPC/ctrloops.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/ctrloops.mir @@ -0,0 +1,242 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -ppc-asm-full-reg-names -mtriple=powerpc64le-unknown-linux-gnu \ +# RUN: -run-pass=ppc-ctrloops %s -o - -verify-machineinstrs | FileCheck %s +# RUN: llc -ppc-asm-full-reg-names -mtriple=powerpc64-ibm-aix-xcoff \ +# RUN: -run-pass=ppc-ctrloops %s -o - -verify-machineinstrs | FileCheck %s +# RUN: llc -ppc-asm-full-reg-names -mtriple=powerpc-ibm-aix-xcoff \ +# RUN: -run-pass=ppc-ctrloops %s -o - -verify-machineinstrs | FileCheck %s + +--- +name: test_success1 +# CHECK: test_success1 + +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK: MTCTR + ; CHECK: BDNZ + ; CHECK-NOT: ADDI + ; CHECK-NOT: CMPWI + ; CHECK-NOT: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + %1:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %1:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_clobber +# CHECK: test_fail_clobber +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + INLINEASM &"", 1 /* sideeffect attdialect */, 12 /* clobber */, implicit-def early-clobber $ctr + %1:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %1:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_use_in_loop +# CHECK: test_fail_use_in_loop +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + %1:gprc = MFCTR implicit $ctr + %2:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %2:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_livein_preheader +# CHECK: test_fail_livein_preheader +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $ctr + + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + %1:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %1:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_def_preheader +# CHECK: test_fail_def_preheader +tracksRegLiveness: true +body: | + bb.0.entry: + + INLINEASM &"", 1 /* sideeffect attdialect */, 12 /* clobber */, implicit-def early-clobber $ctr + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + %1:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %1:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_success_only_use_preheader +# CHECK: test_success_only_use_preheader +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = MFCTR implicit $ctr + %1:gprc = LI 2048 + ; CHECK: MTCTR + ; CHECK: BDNZ + ; CHECK-NOT: ADDI + ; CHECK-NOT: CMPWI + ; CHECK-NOT: BC + MTCTRPseudo killed %1:gprc, implicit-def dead $ctr + + bb.1: + + %2:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %2:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_use_after_mtctr +# CHECK: test_fail_use_after_mtctr +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + %1:gprc = MFCTR implicit $ctr + + bb.1: + + %2:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %2:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_fail_def_after_mtctr +# CHECK: test_fail_def_after_mtctr +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK-NOT: MTCTR + ; CHECK-NOT: BDNZ + ; CHECK: ADDI + ; CHECK: CMPWI + ; CHECK: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + INLINEASM &"", 1 /* sideeffect attdialect */, 12 /* clobber */, implicit-def early-clobber $ctr + + bb.1: + + %2:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %2:crbitrc, %bb.1 + B %bb.2 + + bb.2: + + BLR implicit $lr, implicit $rm +... +--- +name: test_success_def_after_loop +# CHECK: test_success_def_after_loop +tracksRegLiveness: true +body: | + bb.0.entry: + + %0:gprc = LI 2048 + ; CHECK: MTCTR + ; CHECK: BDNZ + ; CHECK-NOT: ADDI + ; CHECK-NOT: CMPWI + ; CHECK-NOT: BC + MTCTRPseudo killed %0:gprc, implicit-def dead $ctr + + bb.1: + + %2:crbitrc = DecreaseCTRPseudo 1, implicit-def dead $ctr, implicit $ctr + BC killed %2:crbitrc, %bb.1 + B %bb.2 + + bb.2: + INLINEASM &"", 1 /* sideeffect attdialect */, 12 /* clobber */, implicit-def early-clobber $ctr + + BLR implicit $lr, implicit $rm +...