10b57cec5SDimitry Andric //===- PPCBoolRetToInt.cpp ------------------------------------------------===// 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 file implements converting i1 values to i32/i64 if they could be more 100b57cec5SDimitry Andric // profitably allocated as GPRs rather than CRs. This pass will become totally 110b57cec5SDimitry Andric // unnecessary if Register Bank Allocation and Global Instruction Selection ever 120b57cec5SDimitry Andric // go upstream. 130b57cec5SDimitry Andric // 140b57cec5SDimitry Andric // Presently, the pass converts i1 Constants, and Arguments to i32/i64 if the 150b57cec5SDimitry Andric // transitive closure of their uses includes only PHINodes, CallInsts, and 160b57cec5SDimitry Andric // ReturnInsts. The rational is that arguments are generally passed and returned 170b57cec5SDimitry Andric // in GPRs rather than CRs, so casting them to i32/i64 at the LLVM IR level will 180b57cec5SDimitry Andric // actually save casts at the Machine Instruction level. 190b57cec5SDimitry Andric // 200b57cec5SDimitry Andric // It might be useful to expand this pass to add bit-wise operations to the list 210b57cec5SDimitry Andric // of safe transitive closure types. Also, we miss some opportunities when LLVM 220b57cec5SDimitry Andric // represents logical AND and OR operations with control flow rather than data 230b57cec5SDimitry Andric // flow. For example by lowering the expression: return (A && B && C) 240b57cec5SDimitry Andric // 250b57cec5SDimitry Andric // as: return A ? true : B && C. 260b57cec5SDimitry Andric // 270b57cec5SDimitry Andric // There's code in SimplifyCFG that code be used to turn control flow in data 280b57cec5SDimitry Andric // flow using SelectInsts. Selects are slow on some architectures (P7/P8), so 290b57cec5SDimitry Andric // this probably isn't good in general, but for the special case of i1, the 300b57cec5SDimitry Andric // Selects could be further lowered to bit operations that are fast everywhere. 310b57cec5SDimitry Andric // 320b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric #include "PPC.h" 350b57cec5SDimitry Andric #include "PPCTargetMachine.h" 360b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 370b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 380b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h" 390b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 400b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 410b57cec5SDimitry Andric #include "llvm/IR/Argument.h" 420b57cec5SDimitry Andric #include "llvm/IR/Constants.h" 430b57cec5SDimitry Andric #include "llvm/IR/Dominators.h" 440b57cec5SDimitry Andric #include "llvm/IR/Function.h" 450b57cec5SDimitry Andric #include "llvm/IR/Instruction.h" 460b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 470b57cec5SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 485f757f3fSDimitry Andric #include "llvm/IR/IRBuilder.h" 490b57cec5SDimitry Andric #include "llvm/IR/OperandTraits.h" 500b57cec5SDimitry Andric #include "llvm/IR/Type.h" 510b57cec5SDimitry Andric #include "llvm/IR/Use.h" 520b57cec5SDimitry Andric #include "llvm/IR/User.h" 530b57cec5SDimitry Andric #include "llvm/IR/Value.h" 540b57cec5SDimitry Andric #include "llvm/Pass.h" 550b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 560b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 570b57cec5SDimitry Andric #include <cassert> 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric using namespace llvm; 600b57cec5SDimitry Andric 610b57cec5SDimitry Andric namespace { 620b57cec5SDimitry Andric 63e8d8bef9SDimitry Andric #define DEBUG_TYPE "ppc-bool-ret-to-int" 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric STATISTIC(NumBoolRetPromotion, 660b57cec5SDimitry Andric "Number of times a bool feeding a RetInst was promoted to an int"); 670b57cec5SDimitry Andric STATISTIC(NumBoolCallPromotion, 680b57cec5SDimitry Andric "Number of times a bool feeding a CallInst was promoted to an int"); 690b57cec5SDimitry Andric STATISTIC(NumBoolToIntPromotion, 700b57cec5SDimitry Andric "Total number of times a bool was promoted to an int"); 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric class PPCBoolRetToInt : public FunctionPass { 730b57cec5SDimitry Andric static SmallPtrSet<Value *, 8> findAllDefs(Value *V) { 740b57cec5SDimitry Andric SmallPtrSet<Value *, 8> Defs; 750b57cec5SDimitry Andric SmallVector<Value *, 8> WorkList; 760b57cec5SDimitry Andric WorkList.push_back(V); 770b57cec5SDimitry Andric Defs.insert(V); 780b57cec5SDimitry Andric while (!WorkList.empty()) { 79e8d8bef9SDimitry Andric Value *Curr = WorkList.pop_back_val(); 800b57cec5SDimitry Andric auto *CurrUser = dyn_cast<User>(Curr); 8116d6b3b3SDimitry Andric // Operands of CallInst/Constant are skipped because they may not be Bool 8216d6b3b3SDimitry Andric // type. For CallInst, their positions are defined by ABI. 8316d6b3b3SDimitry Andric if (CurrUser && !isa<CallInst>(Curr) && !isa<Constant>(Curr)) 840b57cec5SDimitry Andric for (auto &Op : CurrUser->operands()) 850b57cec5SDimitry Andric if (Defs.insert(Op).second) 860b57cec5SDimitry Andric WorkList.push_back(Op); 870b57cec5SDimitry Andric } 880b57cec5SDimitry Andric return Defs; 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric // Translate a i1 value to an equivalent i32/i64 value: 920b57cec5SDimitry Andric Value *translate(Value *V) { 9316d6b3b3SDimitry Andric assert(V->getType() == Type::getInt1Ty(V->getContext()) && 9416d6b3b3SDimitry Andric "Expect an i1 value"); 9516d6b3b3SDimitry Andric 960b57cec5SDimitry Andric Type *IntTy = ST->isPPC64() ? Type::getInt64Ty(V->getContext()) 970b57cec5SDimitry Andric : Type::getInt32Ty(V->getContext()); 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric if (auto *P = dyn_cast<PHINode>(V)) { 1000b57cec5SDimitry Andric // Temporarily set the operands to 0. We'll fix this later in 1010b57cec5SDimitry Andric // runOnUse. 1020b57cec5SDimitry Andric Value *Zero = Constant::getNullValue(IntTy); 1030b57cec5SDimitry Andric PHINode *Q = 104*0fca6ea1SDimitry Andric PHINode::Create(IntTy, P->getNumIncomingValues(), P->getName(), P->getIterator()); 1050b57cec5SDimitry Andric for (unsigned i = 0; i < P->getNumOperands(); ++i) 1060b57cec5SDimitry Andric Q->addIncoming(Zero, P->getIncomingBlock(i)); 1070b57cec5SDimitry Andric return Q; 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1105f757f3fSDimitry Andric IRBuilder IRB(V->getContext()); 1115f757f3fSDimitry Andric if (auto *I = dyn_cast<Instruction>(V)) 1125f757f3fSDimitry Andric IRB.SetInsertPoint(I->getNextNode()); 1135f757f3fSDimitry Andric else 1145f757f3fSDimitry Andric IRB.SetInsertPoint(&Func->getEntryBlock(), Func->getEntryBlock().begin()); 1155f757f3fSDimitry Andric return IRB.CreateZExt(V, IntTy); 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric typedef SmallPtrSet<const PHINode *, 8> PHINodeSet; 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric // A PHINode is Promotable if: 1210b57cec5SDimitry Andric // 1. Its type is i1 AND 1220b57cec5SDimitry Andric // 2. All of its uses are ReturnInt, CallInst, PHINode, or DbgInfoIntrinsic 1230b57cec5SDimitry Andric // AND 1240b57cec5SDimitry Andric // 3. All of its operands are Constant or Argument or 1250b57cec5SDimitry Andric // CallInst or PHINode AND 1260b57cec5SDimitry Andric // 4. All of its PHINode uses are Promotable AND 1270b57cec5SDimitry Andric // 5. All of its PHINode operands are Promotable 1280b57cec5SDimitry Andric static PHINodeSet getPromotablePHINodes(const Function &F) { 1290b57cec5SDimitry Andric PHINodeSet Promotable; 1300b57cec5SDimitry Andric // Condition 1 1310b57cec5SDimitry Andric for (auto &BB : F) 1320b57cec5SDimitry Andric for (auto &I : BB) 1330b57cec5SDimitry Andric if (const auto *P = dyn_cast<PHINode>(&I)) 1340b57cec5SDimitry Andric if (P->getType()->isIntegerTy(1)) 1350b57cec5SDimitry Andric Promotable.insert(P); 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric SmallVector<const PHINode *, 8> ToRemove; 1380b57cec5SDimitry Andric for (const PHINode *P : Promotable) { 1390b57cec5SDimitry Andric // Condition 2 and 3 1400b57cec5SDimitry Andric auto IsValidUser = [] (const Value *V) -> bool { 1410b57cec5SDimitry Andric return isa<ReturnInst>(V) || isa<CallInst>(V) || isa<PHINode>(V) || 1420b57cec5SDimitry Andric isa<DbgInfoIntrinsic>(V); 1430b57cec5SDimitry Andric }; 1440b57cec5SDimitry Andric auto IsValidOperand = [] (const Value *V) -> bool { 1450b57cec5SDimitry Andric return isa<Constant>(V) || isa<Argument>(V) || isa<CallInst>(V) || 1460b57cec5SDimitry Andric isa<PHINode>(V); 1470b57cec5SDimitry Andric }; 1480b57cec5SDimitry Andric const auto &Users = P->users(); 1490b57cec5SDimitry Andric const auto &Operands = P->operands(); 1500b57cec5SDimitry Andric if (!llvm::all_of(Users, IsValidUser) || 1510b57cec5SDimitry Andric !llvm::all_of(Operands, IsValidOperand)) 1520b57cec5SDimitry Andric ToRemove.push_back(P); 1530b57cec5SDimitry Andric } 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric // Iterate to convergence 1560b57cec5SDimitry Andric auto IsPromotable = [&Promotable] (const Value *V) -> bool { 1570b57cec5SDimitry Andric const auto *Phi = dyn_cast<PHINode>(V); 1580b57cec5SDimitry Andric return !Phi || Promotable.count(Phi); 1590b57cec5SDimitry Andric }; 1600b57cec5SDimitry Andric while (!ToRemove.empty()) { 1610b57cec5SDimitry Andric for (auto &User : ToRemove) 1620b57cec5SDimitry Andric Promotable.erase(User); 1630b57cec5SDimitry Andric ToRemove.clear(); 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric for (const PHINode *P : Promotable) { 1660b57cec5SDimitry Andric // Condition 4 and 5 1670b57cec5SDimitry Andric const auto &Users = P->users(); 1680b57cec5SDimitry Andric const auto &Operands = P->operands(); 1690b57cec5SDimitry Andric if (!llvm::all_of(Users, IsPromotable) || 1700b57cec5SDimitry Andric !llvm::all_of(Operands, IsPromotable)) 1710b57cec5SDimitry Andric ToRemove.push_back(P); 1720b57cec5SDimitry Andric } 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric return Promotable; 1760b57cec5SDimitry Andric } 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric typedef DenseMap<Value *, Value *> B2IMap; 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric public: 1810b57cec5SDimitry Andric static char ID; 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric PPCBoolRetToInt() : FunctionPass(ID) { 1840b57cec5SDimitry Andric initializePPCBoolRetToIntPass(*PassRegistry::getPassRegistry()); 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric bool runOnFunction(Function &F) override { 1880b57cec5SDimitry Andric if (skipFunction(F)) 1890b57cec5SDimitry Andric return false; 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric auto *TPC = getAnalysisIfAvailable<TargetPassConfig>(); 1920b57cec5SDimitry Andric if (!TPC) 1930b57cec5SDimitry Andric return false; 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric auto &TM = TPC->getTM<PPCTargetMachine>(); 1960b57cec5SDimitry Andric ST = TM.getSubtargetImpl(F); 1975f757f3fSDimitry Andric Func = &F; 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric PHINodeSet PromotablePHINodes = getPromotablePHINodes(F); 2000b57cec5SDimitry Andric B2IMap Bool2IntMap; 2010b57cec5SDimitry Andric bool Changed = false; 2020b57cec5SDimitry Andric for (auto &BB : F) { 2030b57cec5SDimitry Andric for (auto &I : BB) { 2040b57cec5SDimitry Andric if (auto *R = dyn_cast<ReturnInst>(&I)) 2050b57cec5SDimitry Andric if (F.getReturnType()->isIntegerTy(1)) 2060b57cec5SDimitry Andric Changed |= 2070b57cec5SDimitry Andric runOnUse(R->getOperandUse(0), PromotablePHINodes, Bool2IntMap); 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric if (auto *CI = dyn_cast<CallInst>(&I)) 2100b57cec5SDimitry Andric for (auto &U : CI->operands()) 2110b57cec5SDimitry Andric if (U->getType()->isIntegerTy(1)) 2120b57cec5SDimitry Andric Changed |= runOnUse(U, PromotablePHINodes, Bool2IntMap); 2130b57cec5SDimitry Andric } 2140b57cec5SDimitry Andric } 2150b57cec5SDimitry Andric 2160b57cec5SDimitry Andric return Changed; 2170b57cec5SDimitry Andric } 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric bool runOnUse(Use &U, const PHINodeSet &PromotablePHINodes, 2200b57cec5SDimitry Andric B2IMap &BoolToIntMap) { 2210b57cec5SDimitry Andric auto Defs = findAllDefs(U); 2220b57cec5SDimitry Andric 2230b57cec5SDimitry Andric // If the values are all Constants or Arguments, don't bother 2245ffd83dbSDimitry Andric if (llvm::none_of(Defs, [](Value *V) { return isa<Instruction>(V); })) 2250b57cec5SDimitry Andric return false; 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric // Presently, we only know how to handle PHINode, Constant, Arguments and 2280b57cec5SDimitry Andric // CallInst. Potentially, bitwise operations (AND, OR, XOR, NOT) and sign 2290b57cec5SDimitry Andric // extension could also be handled in the future. 2300b57cec5SDimitry Andric for (Value *V : Defs) 2310b57cec5SDimitry Andric if (!isa<PHINode>(V) && !isa<Constant>(V) && 2320b57cec5SDimitry Andric !isa<Argument>(V) && !isa<CallInst>(V)) 2330b57cec5SDimitry Andric return false; 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric for (Value *V : Defs) 2360b57cec5SDimitry Andric if (const auto *P = dyn_cast<PHINode>(V)) 2370b57cec5SDimitry Andric if (!PromotablePHINodes.count(P)) 2380b57cec5SDimitry Andric return false; 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric if (isa<ReturnInst>(U.getUser())) 2410b57cec5SDimitry Andric ++NumBoolRetPromotion; 2420b57cec5SDimitry Andric if (isa<CallInst>(U.getUser())) 2430b57cec5SDimitry Andric ++NumBoolCallPromotion; 2440b57cec5SDimitry Andric ++NumBoolToIntPromotion; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric for (Value *V : Defs) 2470b57cec5SDimitry Andric if (!BoolToIntMap.count(V)) 2480b57cec5SDimitry Andric BoolToIntMap[V] = translate(V); 2490b57cec5SDimitry Andric 2500b57cec5SDimitry Andric // Replace the operands of the translated instructions. They were set to 2510b57cec5SDimitry Andric // zero in the translate function. 2520b57cec5SDimitry Andric for (auto &Pair : BoolToIntMap) { 2530b57cec5SDimitry Andric auto *First = dyn_cast<User>(Pair.first); 2540b57cec5SDimitry Andric auto *Second = dyn_cast<User>(Pair.second); 2550b57cec5SDimitry Andric assert((!First || Second) && "translated from user to non-user!?"); 25616d6b3b3SDimitry Andric // Operands of CallInst/Constant are skipped because they may not be Bool 25716d6b3b3SDimitry Andric // type. For CallInst, their positions are defined by ABI. 25816d6b3b3SDimitry Andric if (First && !isa<CallInst>(First) && !isa<Constant>(First)) 2590b57cec5SDimitry Andric for (unsigned i = 0; i < First->getNumOperands(); ++i) 2600b57cec5SDimitry Andric Second->setOperand(i, BoolToIntMap[First->getOperand(i)]); 2610b57cec5SDimitry Andric } 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric Value *IntRetVal = BoolToIntMap[U]; 2640b57cec5SDimitry Andric Type *Int1Ty = Type::getInt1Ty(U->getContext()); 2650b57cec5SDimitry Andric auto *I = cast<Instruction>(U.getUser()); 266*0fca6ea1SDimitry Andric Value *BackToBool = 267*0fca6ea1SDimitry Andric new TruncInst(IntRetVal, Int1Ty, "backToBool", I->getIterator()); 2680b57cec5SDimitry Andric U.set(BackToBool); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric return true; 2710b57cec5SDimitry Andric } 2720b57cec5SDimitry Andric 2730b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 2740b57cec5SDimitry Andric AU.addPreserved<DominatorTreeWrapperPass>(); 2750b57cec5SDimitry Andric FunctionPass::getAnalysisUsage(AU); 2760b57cec5SDimitry Andric } 2770b57cec5SDimitry Andric 2780b57cec5SDimitry Andric private: 2790b57cec5SDimitry Andric const PPCSubtarget *ST; 2805f757f3fSDimitry Andric Function *Func; 2810b57cec5SDimitry Andric }; 2820b57cec5SDimitry Andric 2830b57cec5SDimitry Andric } // end anonymous namespace 2840b57cec5SDimitry Andric 2850b57cec5SDimitry Andric char PPCBoolRetToInt::ID = 0; 286e8d8bef9SDimitry Andric INITIALIZE_PASS(PPCBoolRetToInt, "ppc-bool-ret-to-int", 287e8d8bef9SDimitry Andric "Convert i1 constants to i32/i64 if they are returned", false, 288e8d8bef9SDimitry Andric false) 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric FunctionPass *llvm::createPPCBoolRetToIntPass() { return new PPCBoolRetToInt(); } 291