| //===- PPCBoolRetToInt.cpp ------------------------------------------------===// | |
| // | |
| // 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 file implements converting i1 values to i32/i64 if they could be more | |
| // profitably allocated as GPRs rather than CRs. This pass will become totally | |
| // unnecessary if Register Bank Allocation and Global Instruction Selection ever | |
| // go upstream. | |
| // | |
| // Presently, the pass converts i1 Constants, and Arguments to i32/i64 if the | |
| // transitive closure of their uses includes only PHINodes, CallInsts, and | |
| // ReturnInsts. The rational is that arguments are generally passed and returned | |
| // in GPRs rather than CRs, so casting them to i32/i64 at the LLVM IR level will | |
| // actually save casts at the Machine Instruction level. | |
| // | |
| // It might be useful to expand this pass to add bit-wise operations to the list | |
| // of safe transitive closure types. Also, we miss some opportunities when LLVM | |
| // represents logical AND and OR operations with control flow rather than data | |
| // flow. For example by lowering the expression: return (A && B && C) | |
| // | |
| // as: return A ? true : B && C. | |
| // | |
| // There's code in SimplifyCFG that code be used to turn control flow in data | |
| // flow using SelectInsts. Selects are slow on some architectures (P7/P8), so | |
| // this probably isn't good in general, but for the special case of i1, the | |
| // Selects could be further lowered to bit operations that are fast everywhere. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "PPC.h" | |
| #include "PPCTargetMachine.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallPtrSet.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/CodeGen/TargetPassConfig.h" | |
| #include "llvm/IR/Argument.h" | |
| #include "llvm/IR/Dominators.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/Instruction.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/Type.h" | |
| #include "llvm/IR/Use.h" | |
| #include "llvm/IR/User.h" | |
| #include "llvm/IR/Value.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Support/Casting.h" | |
| #include <cassert> | |
| using namespace llvm; | |
| namespace { | |
| #define DEBUG_TYPE "ppc-bool-ret-to-int" | |
| STATISTIC(NumBoolRetPromotion, | |
| "Number of times a bool feeding a RetInst was promoted to an int"); | |
| STATISTIC(NumBoolCallPromotion, | |
| "Number of times a bool feeding a CallInst was promoted to an int"); | |
| STATISTIC(NumBoolToIntPromotion, | |
| "Total number of times a bool was promoted to an int"); | |
| class PPCBoolRetToInt : public FunctionPass { | |
| static SmallPtrSet<Value *, 8> findAllDefs(Value *V) { | |
| SmallPtrSet<Value *, 8> Defs; | |
| SmallVector<Value *, 8> WorkList; | |
| WorkList.push_back(V); | |
| Defs.insert(V); | |
| while (!WorkList.empty()) { | |
| Value *Curr = WorkList.pop_back_val(); | |
| auto *CurrUser = dyn_cast<User>(Curr); | |
| // Operands of CallInst/Constant are skipped because they may not be Bool | |
| // type. For CallInst, their positions are defined by ABI. | |
| if (CurrUser && !isa<CallInst>(Curr) && !isa<Constant>(Curr)) | |
| for (auto &Op : CurrUser->operands()) | |
| if (Defs.insert(Op).second) | |
| WorkList.push_back(Op); | |
| } | |
| return Defs; | |
| } | |
| // Translate a i1 value to an equivalent i32/i64 value: | |
| Value *translate(Value *V) { | |
| assert(V->getType() == Type::getInt1Ty(V->getContext()) && | |
| "Expect an i1 value"); | |
| Type *IntTy = ST->isPPC64() ? Type::getInt64Ty(V->getContext()) | |
| : Type::getInt32Ty(V->getContext()); | |
| if (auto *P = dyn_cast<PHINode>(V)) { | |
| // Temporarily set the operands to 0. We'll fix this later in | |
| // runOnUse. | |
| Value *Zero = Constant::getNullValue(IntTy); | |
| PHINode *Q = | |
| PHINode::Create(IntTy, P->getNumIncomingValues(), P->getName(), P->getIterator()); | |
| for (unsigned I = 0; I < P->getNumOperands(); ++I) | |
| Q->addIncoming(Zero, P->getIncomingBlock(I)); | |
| return Q; | |
| } | |
| IRBuilder IRB(*Func->getParent()); | |
| if (auto *I = dyn_cast<Instruction>(V)) | |
| IRB.SetInsertPoint(I->getNextNode()); | |
| else | |
| IRB.SetInsertPoint(Func->getEntryBlock().begin()); | |
| return IRB.CreateZExt(V, IntTy); | |
| } | |
| typedef SmallPtrSet<const PHINode *, 8> PHINodeSet; | |
| // A PHINode is Promotable if: | |
| // 1. Its type is i1 AND | |
| // 2. All of its uses are ReturnInt, CallInst, or PHINode | |
| // AND | |
| // 3. All of its operands are Constant or Argument or | |
| // CallInst or PHINode AND | |
| // 4. All of its PHINode uses are Promotable AND | |
| // 5. All of its PHINode operands are Promotable | |
| static PHINodeSet getPromotablePHINodes(const Function &F) { | |
| PHINodeSet Promotable; | |
| // Condition 1 | |
| for (auto &BB : F) | |
| for (auto &I : BB) | |
| if (const auto *P = dyn_cast<PHINode>(&I)) | |
| if (P->getType()->isIntegerTy(1)) | |
| Promotable.insert(P); | |
| SmallVector<const PHINode *, 8> ToRemove; | |
| for (const PHINode *P : Promotable) { | |
| // Condition 2 and 3 | |
| auto IsValidUser = [] (const Value *V) -> bool { | |
| return isa<ReturnInst>(V) || isa<CallInst>(V) || isa<PHINode>(V); | |
| }; | |
| auto IsValidOperand = [] (const Value *V) -> bool { | |
| return isa<Constant>(V) || isa<Argument>(V) || isa<CallInst>(V) || | |
| isa<PHINode>(V); | |
| }; | |
| const auto &Users = P->users(); | |
| const auto &Operands = P->operands(); | |
| if (!llvm::all_of(Users, IsValidUser) || | |
| !llvm::all_of(Operands, IsValidOperand)) | |
| ToRemove.push_back(P); | |
| } | |
| // Iterate to convergence | |
| auto IsPromotable = [&Promotable] (const Value *V) -> bool { | |
| const auto *Phi = dyn_cast<PHINode>(V); | |
| return !Phi || Promotable.count(Phi); | |
| }; | |
| while (!ToRemove.empty()) { | |
| for (auto &User : ToRemove) | |
| Promotable.erase(User); | |
| ToRemove.clear(); | |
| for (const PHINode *P : Promotable) { | |
| // Condition 4 and 5 | |
| const auto &Users = P->users(); | |
| const auto &Operands = P->operands(); | |
| if (!llvm::all_of(Users, IsPromotable) || | |
| !llvm::all_of(Operands, IsPromotable)) | |
| ToRemove.push_back(P); | |
| } | |
| } | |
| return Promotable; | |
| } | |
| typedef DenseMap<Value *, Value *> B2IMap; | |
| public: | |
| static char ID; | |
| PPCBoolRetToInt() : FunctionPass(ID) {} | |
| bool runOnFunction(Function &F) override { | |
| if (skipFunction(F)) | |
| return false; | |
| auto *TPC = getAnalysisIfAvailable<TargetPassConfig>(); | |
| if (!TPC) | |
| return false; | |
| auto &TM = TPC->getTM<PPCTargetMachine>(); | |
| ST = TM.getSubtargetImpl(F); | |
| Func = &F; | |
| PHINodeSet PromotablePHINodes = getPromotablePHINodes(F); | |
| B2IMap Bool2IntMap; | |
| bool Changed = false; | |
| for (auto &BB : F) { | |
| for (auto &I : BB) { | |
| if (auto *R = dyn_cast<ReturnInst>(&I)) | |
| if (F.getReturnType()->isIntegerTy(1)) | |
| Changed |= | |
| runOnUse(R->getOperandUse(0), PromotablePHINodes, Bool2IntMap); | |
| if (auto *CI = dyn_cast<CallInst>(&I)) | |
| for (auto &U : CI->operands()) | |
| if (U->getType()->isIntegerTy(1)) | |
| Changed |= runOnUse(U, PromotablePHINodes, Bool2IntMap); | |
| } | |
| } | |
| return Changed; | |
| } | |
| bool runOnUse(Use &U, const PHINodeSet &PromotablePHINodes, | |
| B2IMap &BoolToIntMap) { | |
| auto Defs = findAllDefs(U); | |
| // If the values are all Constants or Arguments, don't bother | |
| if (llvm::none_of(Defs, [](Value *V) { return isa<Instruction>(V); })) | |
| return false; | |
| // Presently, we only know how to handle PHINode, Constant, Arguments and | |
| // CallInst. Potentially, bitwise operations (AND, OR, XOR, NOT) and sign | |
| // extension could also be handled in the future. | |
| for (Value *V : Defs) | |
| if (!isa<PHINode>(V) && !isa<Constant>(V) && | |
| !isa<Argument>(V) && !isa<CallInst>(V)) | |
| return false; | |
| for (Value *V : Defs) | |
| if (const auto *P = dyn_cast<PHINode>(V)) | |
| if (!PromotablePHINodes.count(P)) | |
| return false; | |
| if (isa<ReturnInst>(U.getUser())) | |
| ++NumBoolRetPromotion; | |
| if (isa<CallInst>(U.getUser())) | |
| ++NumBoolCallPromotion; | |
| ++NumBoolToIntPromotion; | |
| for (Value *V : Defs) { | |
| auto [It, Inserted] = BoolToIntMap.try_emplace(V); | |
| if (Inserted) | |
| It->second = translate(V); | |
| } | |
| // Replace the operands of the translated instructions. They were set to | |
| // zero in the translate function. | |
| for (auto &Pair : BoolToIntMap) { | |
| auto *First = dyn_cast<User>(Pair.first); | |
| auto *Second = dyn_cast<User>(Pair.second); | |
| assert((!First || Second) && "translated from user to non-user!?"); | |
| // Operands of CallInst/Constant are skipped because they may not be Bool | |
| // type. For CallInst, their positions are defined by ABI. | |
| if (First && !isa<CallInst>(First) && !isa<Constant>(First)) | |
| for (unsigned I = 0; I < First->getNumOperands(); ++I) | |
| Second->setOperand(I, BoolToIntMap[First->getOperand(I)]); | |
| } | |
| Value *IntRetVal = BoolToIntMap[U]; | |
| Type *Int1Ty = Type::getInt1Ty(U->getContext()); | |
| auto *I = cast<Instruction>(U.getUser()); | |
| Value *BackToBool = | |
| new TruncInst(IntRetVal, Int1Ty, "backToBool", I->getIterator()); | |
| U.set(BackToBool); | |
| return true; | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.addPreserved<DominatorTreeWrapperPass>(); | |
| FunctionPass::getAnalysisUsage(AU); | |
| } | |
| private: | |
| const PPCSubtarget *ST; | |
| Function *Func; | |
| }; | |
| } // end anonymous namespace | |
| char PPCBoolRetToInt::ID = 0; | |
| INITIALIZE_PASS(PPCBoolRetToInt, "ppc-bool-ret-to-int", | |
| "Convert i1 constants to i32/i64 if they are returned", false, | |
| false) | |
| FunctionPass *llvm::createPPCBoolRetToIntPass() { return new PPCBoolRetToInt(); } |