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,317 @@ +//===------------- 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("Enable generating the ISEL instruction"), + 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); + + bool expandISEL(MachineInstr &MI); + bool expandISEL(); + + /// + /// \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-the-else sequence on POWER7 and POWER8. + /// Generate ISEL instruction on other architectures. + /// The -ppc-gen-isel option can be used to force the generation of ISEL + /// instruction, regardless of the architecture. + /// + /// \return true if ISEL should be expanded into if-the-else code sequence; + /// false if ISEL instruction should be generated. + /// + static bool isExpandISELEnabled(const MachineFunction &MF); + + void DumpISELInstructions() const; + + bool runOnMachineFunction(MachineFunction &MF) override { + bool Changed = false; + + 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; + } + + DumpISELInstructions(); + + Changed = expandISEL(); + + return Changed; + } +}; + +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(); +} + +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())); + } +} + +bool PPCExpandISEL::expandISEL() { + for (ISELInstructionList::iterator BlockList = ISELInstructions.begin(), + ListEnd = ISELInstructions.end(); + BlockList != ListEnd; ++BlockList) { + DEBUG(dbgs() << "Expanding ISEL instructions in block " << BlockList->first + << "\n"); + BlockISELList &CurrentISELList = BlockList->second; + for (auto I = CurrentISELList.begin(), E = CurrentISELList.end(); I != E; + ++I) { + if (!expandISEL(**I)) { + DEBUG(dbgs() << "Unable to expand isel for " << I << "\n"); + return false; + } + } + } + return true; +} + +bool PPCExpandISEL::expandISEL(MachineInstr &MI) { + unsigned Opcode = MI.getOpcode(); + unsigned NumOperands = MI.getNumOperands(); + + assert((Opcode == PPC::ISEL || Opcode == PPC::ISEL8) && + "Expecting an ISEL instruction"); + + DEBUG(dbgs() << "MI: " << MI << "\n"); + + for (unsigned i = 0; i < NumOperands; i++) { + DEBUG(dbgs() << "Operand " << i << ": " << MI.getOperand(i) << "\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 + + DEBUG(dbgs() << "Dest: " << Dest << "\n"); + DEBUG(dbgs() << "TrueValue: " << TrueValue << "\n"); + DEBUG(dbgs() << "False: " << FalseValue << "\n"); + DEBUG(dbgs() << "ConditionRegister: " << ConditionRegister << "\n"); + + MachineBasicBlock *MBB = MI.getParent(); + MachineFunction *Fn = MBB->getParent(); + MachineFunction::iterator It = MBB->getIterator(); + ++It; + + const BasicBlock *LLVM_BB = MBB->getBasicBlock(); + MachineBasicBlock *TrueBlock = Fn->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *FalseBlock = Fn->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *NewSuccessor = + &MI != (MachineInstr *)MBB->getLastNonDebugInstr() + ? Fn->CreateMachineBasicBlock(LLVM_BB) + : nullptr; + MachineBasicBlock *Successor = nullptr; + + // 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 succ_it = MBB->succ_begin(); + succ_it != MBB->succ_end(); succ_it++) { + if (MBB->isLayoutSuccessor(*succ_it)) { + Successor = *succ_it; + break; + } + } + } else + Successor = NewSuccessor; + + Fn->insert(It, FalseBlock); + Fn->insert(It, TrueBlock); + + if (NewSuccessor) { + Fn->insert(It, NewSuccessor); + + // Copy 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); + + for (MachineBasicBlock::livein_iterator it = MBB->livein_begin(); + it != MBB->livein_end(); it++) + NewSuccessor->addLiveIn(*it); + } 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(TrueBlock); + MBB->addSuccessor(FalseBlock); + TrueBlock->addSuccessor(Successor); + FalseBlock->addSuccessor(Successor); + + MachineBasicBlock::iterator TrueBlockI = TrueBlock->begin(); + MachineBasicBlock::iterator FalseBlockI = FalseBlock->begin(); + DebugLoc dl; + + // Copy the result into the destination if the condition is true + // MachineInstr *i1= + BuildMI(*TrueBlock, TrueBlockI, dl, TII->get(PPC::ADDI8)) + .addOperand(Dest) + .addOperand(TrueValue) + .addOperand(MachineOperand::CreateImm(0)) + .getInstr(); + + TrueBlock->addLiveIn(Dest.getReg()); + TrueBlock->addLiveIn(TrueValue.getReg()); + 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 + // MachineInstr *i2 = + BuildMI(*MBB, &MI, dl, TII->get(PPC::BC)) + .addOperand(ConditionRegister) + .addMBB(TrueBlock) + .getInstr(); + + // Copy the value into the destination if the condition is false + // This goes in the current block, to replace the isel instruction + // MachineInstr *i3 = + BuildMI(*FalseBlock, FalseBlockI, dl, TII->get(PPC::ADDI8)) + .addOperand(Dest) + .addOperand(FalseValue) + .addOperand(MachineOperand::CreateImm(0)) + .getInstr(); + + // Jump around the true block to the new successor if the condition is false + // MachineInstr *i4 = + BuildMI(*FalseBlock, FalseBlockI, dl, TII->get(PPC::B)) + .addMBB(Successor) + .getInstr(); + + MI.eraseFromParent(); // remove the ISEL instruction + + NumExpanded++; + + return true; +} + +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 @@ -429,6 +429,8 @@ } void PPCPassConfig::addPreEmitPass() { + addPass(createPPCExpandISELPass(), false); + if (getOptLevel() != CodeGenOpt::None) addPass(createPPCEarlyReturnPass(), false); // Must run branch selection immediately preceding the asm printer.