Index: lib/Target/PowerPC/CMakeLists.txt =================================================================== --- lib/Target/PowerPC/CMakeLists.txt +++ lib/Target/PowerPC/CMakeLists.txt @@ -40,6 +40,7 @@ PPCVSXCopy.cpp PPCVSXFMAMutate.cpp PPCVSXSwapRemoval.cpp + PPCExpandISEL.cpp ) add_subdirectory(AsmParser) Index: lib/Target/PowerPC/PPC.h =================================================================== --- lib/Target/PowerPC/PPC.h +++ lib/Target/PowerPC/PPC.h @@ -45,6 +45,7 @@ FunctionPass *createPPCISelDag(PPCTargetMachine &TM); FunctionPass *createPPCTLSDynamicCallPass(); FunctionPass *createPPCBoolRetToIntPass(); + FunctionPass *createPPCExpandISELPass(); void LowerPPCMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, AsmPrinter &AP, bool isDarwin); Index: lib/Target/PowerPC/PPCExpandISEL.cpp =================================================================== --- /dev/null +++ lib/Target/PowerPC/PPCExpandISEL.cpp @@ -0,0 +1,350 @@ +//===------------- PPCExpandISEL.cpp - Expand ISEL instruction ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A pass that expands the ISEL instruction into an if-then-else sequence +// +//===----------------------------------------------------------------------===// + +#include "PPC.h" +#include "PPCInstrInfo.h" +#include "PPCSubtarget.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "ppc-expand-isel" + +STATISTIC(NumExpanded, "Number of ISEL instructions expanded"); + +static cl::opt + GenerateISEL("ppc-gen-isel", + cl::desc("Disable generating the ISEL instruction on 64-bit non-Darwin PPC targets."), + cl::init(true), cl::Hidden); + +namespace llvm { + void initializePPCExpandISELPass(PassRegistry &); +} + +class PPCExpandISEL : public MachineFunctionPass { + + typedef SmallVector BlockISELList; + typedef SmallDenseMap ISELInstructionList; + + // List of ISEL instructions, sorted by MBB. + ISELInstructionList ISELInstructions; + MachineFunction *MF; + + /// + /// \brief Initialize the object + /// + void initialize(MachineFunction & MFParam); + + void expandISELInstruction(MachineInstr & MI); + void expandISELInstructions(); + + /// + /// \brief Is this instruction an ISEL or ISEL8 + /// + bool isISEL(const MachineInstr &MI) const { + return (MI.getOpcode() == PPC::ISEL || MI.getOpcode() == PPC::ISEL8); + } + + /// + /// \brief Collect all ISEL instructions from the current function + /// + /// Walk the current function and collect all the ISEL instructions that are + /// found. The instructions are placed in the ISELInstructions vector. + /// + /// \return true if any ISEL instructions were found, false otherwise + /// + bool collectISELInstructions(); + +public: + static char ID; + PPCExpandISEL() : MachineFunctionPass(ID) { + initializePPCExpandISELPass(*PassRegistry::getPassRegistry()); + } + + const TargetInstrInfo *TII; + + /// + /// \brief Determine whether to generate the ISEL instruction or expand it + /// + /// Expand ISEL instructions into if-then-else sequence only when both of + /// the following two conditions hold: + /// (1) 64-bit non-Darwin PPC targets. + /// (2) -ppc-gen-isel=false + /// Otherwise, still generate ISEL instructions. + /// The -ppc-gen-isel option is set to true by default. which means the + /// ISEL insturctions are still generated by default on all platforms. + /// instruction, regardless of the architecture. + /// + /// \return true if ISEL should be expanded into if-then-else code sequence; + /// false if ISEL instruction should be generated, i.e. not expaned. + /// + static bool isExpandISELEnabled(const MachineFunction &MF); + +#ifndef NDEBUG + void DumpISELInstructions() const; +#endif + + bool runOnMachineFunction(MachineFunction & MF) override { + DEBUG(dbgs() << "Function: "; MF.dump(); dbgs() << "\n"); + if (!isExpandISELEnabled(MF)) + return false; + + initialize(MF); + + if (!collectISELInstructions()) { + DEBUG(dbgs() << "No ISEL instructions in this function\n"); + return false; + } + +#ifndef NDEBUG + DumpISELInstructions(); +#endif + + expandISELInstructions(); + + return true; + } +}; + +void PPCExpandISEL::initialize(MachineFunction &MFParam) { + MF = &MFParam; + TII = MF->getSubtarget().getInstrInfo(); + ISELInstructions.clear(); +} + +bool PPCExpandISEL::isExpandISELEnabled(const MachineFunction &MF) { + if (GenerateISEL) + return false; + + if (MF.getSubtarget().isSVR4ABI() && + MF.getSubtarget().isPPC64()) + return true; + + return false; +} + +bool PPCExpandISEL::collectISELInstructions() { + for (MachineBasicBlock &MBB : *MF) { + BlockISELList thisBlockISELs; + for (MachineInstr &MI : MBB) + if (isISEL(MI)) + thisBlockISELs.push_back(&MI); + if (!thisBlockISELs.empty()) + ISELInstructions.insert(std::make_pair(MBB.getNumber(), thisBlockISELs)); + } + return !ISELInstructions.empty(); +} + +#ifndef NDEBUG +void PPCExpandISEL::DumpISELInstructions() const { + for (ISELInstructionList::const_iterator I = ISELInstructions.begin(), + Iend = ISELInstructions.end(); + I != Iend; ++I) { + DEBUG(dbgs() << "BB#" << I->first << ":\n"); + for (BlockISELList::const_iterator VI = I->second.begin(), + Vend = I->second.end(); + VI != Vend; ++VI) + DEBUG(dbgs() << " "; (*VI)->print(dbgs())); + } +} +#endif + +void PPCExpandISEL::expandISELInstructions() { + for (ISELInstructionList::iterator BlockList = ISELInstructions.begin(), + ListEnd = ISELInstructions.end(); + BlockList != ListEnd; ++BlockList) { + DEBUG(dbgs() << "Expanding ISEL instructions in BB#" << BlockList->first + << "\n"); + BlockISELList &CurrentISELList = BlockList->second; + for (auto I = CurrentISELList.begin(), E = CurrentISELList.end(); I != E; + ++I) { + expandISELInstruction(**I); + } + } +} + + +void PPCExpandISEL::expandISELInstruction(MachineInstr &MI) { + assert(isISEL(MI) && "Expecting an ISEL instruction"); + DEBUG(dbgs() << "MI: " << MI << "\n"); + + MachineOperand &Dest = MI.getOperand(0); // location to store to + MachineOperand &TrueValue = MI.getOperand(1); // Value to store if + // condition is true + MachineOperand &FalseValue = MI.getOperand(2); // Value to store if + // condition is false + MachineOperand &ConditionRegister = MI.getOperand(3); // Condition + + bool IsTrueBlockRequired = (Dest.getReg() != TrueValue.getReg()); + bool IsFalseBlockRequired = (Dest.getReg() != FalseValue.getReg()); + + // Special case 1, all the registers used by ISEL are the same. + if (!IsTrueBlockRequired && !IsFalseBlockRequired) { + DEBUG(dbgs() << "Remove redudant ISEL instruction."); + MI.eraseFromParent(); // remove the ISEL instruction + return; + } + + DEBUG(dbgs() << "Dest: " << Dest << "\n"); + DEBUG(dbgs() << "TrueValue: " << TrueValue << "\n"); + DEBUG(dbgs() << "FalseValue: " << FalseValue << "\n"); + DEBUG(dbgs() << "ConditionRegister: " << ConditionRegister << "\n"); + + MachineBasicBlock *MBB = MI.getParent(); + MachineFunction *Fn = MBB->getParent(); + MachineFunction::iterator It = MBB->getIterator(); + ++It; // Point to the successor block of MBB + + const BasicBlock *LLVM_BB = MBB->getBasicBlock(); + MachineBasicBlock *TrueBlock = nullptr; + MachineBasicBlock *FalseBlock = nullptr; + MachineBasicBlock::iterator MBBI = MI; + MachineBasicBlock *NewSuccessor = MBBI != MBB->getLastNonDebugInstr() + ? Fn->CreateMachineBasicBlock(LLVM_BB) + : nullptr; + MachineBasicBlock *Successor = nullptr; + DebugLoc dl; + + // Special case 2, the two input registers used by ISEL are the same. + if (TrueValue.getReg() == FalseValue.getReg()) { + DEBUG(dbgs() << "Fold the ISEL insturction to an unconditonal copy."); + BuildMI(*MBB, &MI, dl, TII->get(PPC::ADDI8)) + .addOperand(Dest) + .addOperand(TrueValue) + .addOperand(MachineOperand::CreateImm(0)); + MI.eraseFromParent(); // remove the ISEL instruction + return; + } + // If NewSuccessor is NULL then the ISEL was the last non-debug instruction + // in this block. Find the fall-through successor of this block to use when + // updating the CFG below + if (!NewSuccessor) { + assert(MBB->canFallThrough() && "This block should fall through!\n"); + for (MachineBasicBlock::succ_iterator SuccIt = MBB->succ_begin(); + SuccIt != MBB->succ_end(); SuccIt++) { + if (MBB->isLayoutSuccessor(*SuccIt)) { + Successor = *SuccIt; + break; + } + } + } else + Successor = NewSuccessor; + + // The FalseBlock and TrueBlock are inserted after the MBB block but before + // its successor conditionally. + // Note this need to be done *after* the above setting the Successor code, + // otherwise the canFallThrough assert will fail. + if (IsFalseBlockRequired) { + FalseBlock = Fn->CreateMachineBasicBlock(LLVM_BB); + Fn->insert(It, FalseBlock); + } + + if (IsTrueBlockRequired) { + TrueBlock = Fn->CreateMachineBasicBlock(LLVM_BB); + Fn->insert(It, TrueBlock); + } + + if (NewSuccessor) { + Fn->insert(It, NewSuccessor); + + // Transfer the rest of this block into the new successor block + NewSuccessor->splice(NewSuccessor->end(), MBB, + std::next(MachineBasicBlock::iterator(MI)), + MBB->end()); + NewSuccessor->transferSuccessorsAndUpdatePHIs(MBB); + + // Copy the liveIn to new successor + for (MachineBasicBlock::livein_iterator LIT = MBB->livein_begin(); + LIT != MBB->livein_end(); LIT++) + NewSuccessor->addLiveIn(*LIT); + } else { + // Remove successor from MBB; + MBB->removeSuccessor(Successor); + } + + // Note that this needs to be done *after* transfering the successors from MBB + // to the NewSuccessor block, otherwise these blocks will also be transferred + // as successors! + MBB->addSuccessor(IsTrueBlockRequired ? TrueBlock : Successor); + MBB->addSuccessor(IsFalseBlockRequired ? FalseBlock : Successor); + + MachineBasicBlock::iterator TrueBlockI; + MachineBasicBlock::iterator FalseBlockI; + + if (IsTrueBlockRequired) { + TrueBlockI = TrueBlock->begin(); + TrueBlock->addSuccessor(Successor); + } + + if (IsFalseBlockRequired) { + FalseBlockI = FalseBlock->begin(); + FalseBlock->addSuccessor(Successor); + } + + // Copy the result into the destination if the condition is true + if (IsTrueBlockRequired) + BuildMI(*TrueBlock, TrueBlockI, dl, TII->get(PPC::ADDI8)) + .addOperand(Dest) + .addOperand(TrueValue) + .addOperand(MachineOperand::CreateImm(0)); + + if (IsTrueBlockRequired) { + TrueBlock->addLiveIn(Dest.getReg()); + TrueBlock->addLiveIn(TrueValue.getReg()); + } + + if (IsFalseBlockRequired) { + FalseBlock->addLiveIn(Dest.getReg()); + FalseBlock->addLiveIn(FalseValue.getReg()); + } + + if (NewSuccessor) { + NewSuccessor->addLiveIn(Dest.getReg()); + NewSuccessor->addLiveIn(TrueValue.getReg()); + NewSuccessor->addLiveIn(FalseValue.getReg()); + } + + // Conditional branch to the TrueBlock or Successor + BuildMI(*MBB, &MI, dl, TII->get(PPC::BC)) + .addOperand(ConditionRegister) + .addMBB(IsTrueBlockRequired ? TrueBlock : Successor); + + // Copy the value into the destination if the condition is false + if (IsFalseBlockRequired) + BuildMI(*FalseBlock, FalseBlockI, dl, TII->get(PPC::ADDI8)) + .addOperand(Dest) + .addOperand(FalseValue) + .addOperand(MachineOperand::CreateImm(0)); + + // Jump over the true block to the new successor if the condition is false + BuildMI(*(IsFalseBlockRequired ? FalseBlock : MBB), + (IsFalseBlockRequired ? FalseBlockI : MI), dl, TII->get(PPC::B)) + .addMBB(Successor); + + MI.eraseFromParent(); // remove the ISEL instruction + + NumExpanded++; +} + +INITIALIZE_PASS(PPCExpandISEL, DEBUG_TYPE, "PowerPC Expand ISEL Generation", + false, false) + +char PPCExpandISEL::ID = 0; +FunctionPass *llvm::createPPCExpandISELPass() { + return new PPCExpandISEL(); } Index: lib/Target/PowerPC/PPCTargetMachine.cpp =================================================================== --- lib/Target/PowerPC/PPCTargetMachine.cpp +++ lib/Target/PowerPC/PPCTargetMachine.cpp @@ -412,6 +412,8 @@ } void PPCPassConfig::addPreEmitPass() { + addPass(createPPCExpandISELPass(), false); + if (getOptLevel() != CodeGenOpt::None) addPass(createPPCEarlyReturnPass(), false); // Must run branch selection immediately preceding the asm printer. Index: test/CodeGen/PowerPC/expand-isel.ll =================================================================== --- /dev/null +++ test/CodeGen/PowerPC/expand-isel.ll @@ -0,0 +1,100 @@ +; RUN: llc -verify-machineinstrs -O2 -ppc-asm-full-reg-names -ppc-gen-isel=false < %s | FileCheck %s -implicit-check-not isel +; RUN: llc -verify-machineinstrs -O2 -ppc-asm-full-reg-names -ppc-gen-isel=false < %s | FileCheck %s -implicit-check-not isel +; Function Attrs: norecurse nounwind readnone +define signext i32 @testExpandISELToIfElse(i32 signext %i, i32 signext %j) { +entry: + %cmp = icmp sgt i32 %i, 0 + %add = add nsw i32 %i, 1 + %cond = select i1 %cmp, i32 %add, i32 %j + ret i32 %cond + +; CHECK: @testExpandISELToIfElse +; CHECK: addi r5, r3, 1 +; CHECK: cmpwi cr0, r3, 0 +; CHECK: bc 12, 1, .LBB0_2 +; CHECK: addi r3, r4, 0 +; CHECK: b .LBB0_3 +; CHECK: .LBB0_2: +; CHECK: addi r3, r5, 0 +; CHECK: .LBB0_3: +; CHECK: extsw r3, r3 +; CHECK: blr +} + + +; Function Attrs: norecurse nounwind readnone +define signext i32 @testExpandISELToIf(i32 signext %i, i32 signext %j) { +entry: + %cmp = icmp sgt i32 %i, 0 + %cond = select i1 %cmp, i32 %j, i32 %i + ret i32 %cond + +; CHECK: @testExpandISELToIf +; CHECK: cmpwi r3, 0 +; CHECK: bc 12, 1, .LBB1_1 +; CHECK: blr +; CHECK: .LBB1_1: +; CHECK: addi r3, r4, 0 +; CHECK: blr +} + +; Function Attrs: norecurse nounwind readnone +define signext i32 @testExpandISELToElse(i32 signext %i, i32 signext %j) { +entry: + %cmp = icmp sgt i32 %i, 0 + %cond = select i1 %cmp, i32 %i, i32 %j + ret i32 %cond + +; CHECK: @testExpandISELToElse +; CHECK: cmpwi r3, 0 +; CHECK: bclr 12, 1, 0 +; CHECK: addi r3, r4, 0 +; CHECK: blr +} + +; Function Attrs: norecurse nounwind readnone +define signext i32 @testExpandISELToNull(i32 signext %i, i32 signext %j) { +entry: + %cmp = icmp sgt i32 %i, 0 + %cond = select i1 %cmp, i32 %i, i32 %i + ret i32 %cond + +; CHECK: @testExpandISELToNull +; CHECK-NOT: b .LBB +; CHECK-NOT: bc +; CHECK: blr +} + +@b = common local_unnamed_addr global i32 0, align 4 +@a = common local_unnamed_addr global i32 0, align 4 +; Function Attrs: norecurse nounwind readonly +define signext i32 @testComplexISEL() #0 { +entry: + %0 = load i32, i32* @b, align 4, !tbaa !1 + %tobool = icmp eq i32 %0, 0 + br i1 %tobool, label %if.end, label %cleanup + +if.end: + %1 = load i32, i32* @a, align 4, !tbaa !1 + %conv = sext i32 %1 to i64 + %2 = inttoptr i64 %conv to i32 (...)* + %cmp = icmp eq i32 (...)* %2, bitcast (i32 ()* @testComplexISEL to i32 (...)*) + %conv3 = zext i1 %cmp to i32 + br label %cleanup + +cleanup: + %retval.0 = phi i32 [ %conv3, %if.end ], [ 1, %entry ] + ret i32 %retval.0 + +; CHECK: @testComplexISEL +; CHECK: bc 12, 2, .LBB +; CHECK-NEXT: b .LBB +; CHECK: addi r3, r12, 0 +; CHECK: clrldi r3, r3, 32 +; CHECK-NEXT: blr +} + +!1 = !{!2, !2, i64 0} +!2 = !{!"int", !3, i64 0} +!3 = !{!"omnipotent char", !4, i64 0} +!4 = !{!"Simple C/C++ TBAA"}