Index: lib/Transforms/Scalar/SCCP.cpp =================================================================== --- lib/Transforms/Scalar/SCCP.cpp +++ lib/Transforms/Scalar/SCCP.cpp @@ -24,9 +24,14 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/Analysis/CodeMetrics.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/InlineCost.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" @@ -40,7 +45,9 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar/SCCP.h" +#include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/ValueMapper.h" #include using namespace llvm; @@ -53,6 +60,13 @@ STATISTIC(IPNumArgsElimed ,"Number of arguments constant propagated by IPSCCP"); STATISTIC(IPNumGlobalConst, "Number of globals found to be constant by IPSCCP"); +STATISTIC(IPNumSpecializedFuncs, "Number of functions specialized by IPSCCP"); + +/// Enable IPSCCP function specialization. +static cl::opt EnableFunctionSpecialization( + "ipsccp-enable-function-specialization", cl::init(true), cl::Hidden, + cl::desc("Enable IPSCCP function specialization")); + namespace { /// LatticeVal class - This class represents the different lattice values that /// an LLVM value may occupy. It is a simple class with value semantics. @@ -232,6 +246,14 @@ return true; } + /// Mark all of the blocks in function \p F non-executable. Clients can used + /// this method to erase a function from the module (e.g., if it has been + /// completely specialized and is no longer needed). + void markFunctionUnreachable(Function *F) { + for (auto &BB : *F) + BBExecutable.erase(&BB); + } + /// TrackValueOfGlobalVariable - Clients can use this method to /// inform the SCCPSolver that it should track loads and stores to the /// specified global variable if it can. This is only legal to call if @@ -263,6 +285,11 @@ TrackingIncomingArguments.insert(F); } + /// Return a reference to the set of argument tracked functions. + SmallPtrSetImpl &getArgumentTrackedFunctions() { + return TrackingIncomingArguments; + } + /// Solve - Solve for constants and executable blocks. /// void Solve(); @@ -338,6 +365,28 @@ return true; } + /// Mark argument \p A constant with value \p C in a new function + /// specialization. The argument's parent function is a specialization of the + /// original function \p F. All other arguments of the specialization inherit + /// the lattice state of their corresponding values in the original function. + void markArgInFuncSpecialization(Function *F, Argument *A, Constant *C) { + assert(F->arg_size() == A->getParent()->arg_size() && + "Functions should have the same number of arguments"); + + // Mark the argument constant in the new function. + markConstant(A, C); + + // For the remaining arguments in the new function, copy the lattice state + // over from the old function. + for (auto I = F->arg_begin(), J = A->getParent()->arg_begin(), + E = F->arg_end(); + I != E; ++I, ++J) + if (J != A && ValueState.count(I)) { + ValueState[J] = ValueState[I]; + pushToWorkList(ValueState[J], J); + } + } + private: // pushToWorkList - Helper for markConstant/markForcedConstant/markOverdefined void pushToWorkList(LatticeVal &IV, Value *V) { @@ -1699,6 +1748,420 @@ // createSCCPPass - This is the public interface to this file. FunctionPass *llvm::createSCCPPass() { return new SCCPLegacyPass(); } +namespace { + +/// FunctionSpecializer is responsible for specializing functions based on the +/// values of their incoming arguments. +/// +/// After an initial IPSCCP data-flow is solved, we may find that a formal +/// argument of a function can take on a particular constant value in some +/// calling contexts, but not all. If this is the case, we can create a new +/// version of the function in which the argument is replaced by the constant +/// value and recompute the data-flow. This allows constant propagation across +/// function boundaries when an argument can take on more than one value. +/// +/// Specialization is controlled by a goal-oriented heuristic that seeks to +/// predict if replacing an argument with a constant value would result in any +/// significant optimization opportunities. The heuristic is currently limited +/// to revealing inlining opportunities via indirect call promotion. Inline +/// cost is used to weigh the expected benefit of specialization against the +/// cost of doing so. +class FunctionSpecializer { + + /// The IPSCCP Solver. + SCCPSolver &Solver; + + /// Analyses used to help determine if a function should be specialized. + ProfileSummaryInfo *PSI; + std::function *GetAC; + Optional> GetBFI; + std::function *GetTTI; + + /// Maps to save specialization costs and bonuses so we don't recompute them + /// unnecessarily. + DenseMap SpecializationCosts; + DenseMap, unsigned> SpecializationBonuses; + + /// A mapping from specialized functions to their corresponding functions + /// from the original module. + DenseMap OriginalFunctions; + + /// The set of functions from the original module that have been specialized. + /// This set is primarily used to count the number of functions that have + /// been specialized. + SmallPtrSet SpecializedFunctions; + +public: + FunctionSpecializer( + SCCPSolver &Solver, ProfileSummaryInfo *PSI, + std::function *GetAC, + Optional> GetBFI, + std::function *GetTTI) + : Solver(Solver), PSI(PSI), GetAC(GetAC), GetBFI(GetBFI), GetTTI(GetTTI) { + } + + /// Attempt to specialize functions in the module to enable constant + /// propagation across function boundaries. + /// + /// \returns true if at least one function is specialized. + bool specializeFunctions() { + + // Holds the newly created functions. We maintain a separate list of these + // functions to avoid iterator invalidation. + SmallVector Specializations; + + // Attempt to specialize the argument-tracked functions. + bool Changed = false; + for (auto *F : Solver.getArgumentTrackedFunctions()) { + + // If the function is not in the OriginalFunctions map, initialize it + // with an identity relationship. + if (!OriginalFunctions.count(F)) + OriginalFunctions[F] = F; + + Changed |= specializeFunction(F, Specializations); + } + + // Initialize the state of the newly created functions, marking them + // argument-tracked and executable. + for (auto *F : Specializations) { + if (F->hasExactDefinition() && !F->hasFnAttribute(Attribute::Naked)) + Solver.AddTrackedFunction(F); + Solver.AddArgumentTrackedFunction(F); + Solver.MarkBlockExecutable(&F->front()); + } + + return Changed; + } + +private: + /// Attempt to specialize function \p F. + /// + /// This function decides whether to specialize function \p F based on the + /// known constant values its arguments can take on. Specialization is + /// performed on the first interesting argument. Specializations based on + /// additional arguments will be evaluated on following iterations of the + /// main IPSCCP solve loop. + /// + /// \returns true if the function is specialized. + bool specializeFunction(Function *F, + SmallVectorImpl &Specializations) { + + // If we're optimizing the function for size, we shouldn't specialize it. + if (F->optForSize()) + return false; + + // Exit if the function is not executable. There's no point in specializing + // a dead function. + if (!Solver.isBlockExecutable(&F->getEntryBlock())) + return false; + + // Determine if we should specialize the function based on the values the + // argument can take on. If specialization is not profitable, we continue + // on to the next argument. + for (Argument &A : F->args()) { + + // True if this will be a partial specialization. We will need to keep + // the original function around in addition to the added specializations. + bool IsPartial = true; + + // Determine if this argument is interesting. If we know the argument can + // take on any constant values, they are collected in Constants. If the + // argument can only ever equal a constant value in Constants, the + // function will be completely specialized, and the IsPartial flag will + // be set to false by isArgumentInteresting (that function only adds + // values to the Constants list that are deemed profitable). + SmallVector Constants; + if (!isArgumentInteresting(&A, Constants, IsPartial)) + continue; + + assert(!Constants.empty() && "No constants on which to specialize"); + DEBUG(dbgs() << "Specializing function " << F->getName() << " on " << A + << ": (" << *Constants[0]; + for (unsigned I = 1; I < Constants.size(); ++I) dbgs() + << ", " << *Constants[I]; + dbgs() << ")\n"); + + // Create a version of the function in which the argument is marked + // constant with the given value. + for (auto *C : Constants) { + + // Clone the function. We leave the ValueToValueMap empty to allow + // IPSCCP to propagate the constant arguments. + ValueToValueMapTy EmptyMap; + Function *Clone = CloneFunction(F, EmptyMap); + + // Rewrite calls to the function so that they call the clone instead. + rewriteCallSites(F, Clone, A.getArgNo(), C); + + // Initialize the lattice state of the arguments of the function clone, + // marking the argument on which we specialized the function constant + // with the given value. + Solver.markArgInFuncSpecialization(F, Clone->arg_begin() + A.getArgNo(), + C); + Specializations.push_back(Clone); + + // Set the parent function of the clone. + OriginalFunctions[Clone] = OriginalFunctions[F]; + } + + // The function has been specialized. Increment the counter. + if (SpecializedFunctions.insert(OriginalFunctions[F]).second) + ++IPNumSpecializedFuncs; + + // If the function has been completely specialized, the original function + // is no longer needed. Mark it unreachable. + if (!IsPartial) + Solver.markFunctionUnreachable(F); + + return true; + } + + return false; + } + + /// Compute the cost of specializing function \p F. + /// + /// This function computes the cost of specializing the given function. + /// Specialization is performed for a particular argument if the bonus from + /// specializing on that argument is greater than the specialization cost + /// computed here. + /// + /// \returns the cost of specialization function \p F. + unsigned getSpecializationCost(Function *F) { + + // If we've already computed the specialization cost for the given + // function, just return it. + if (SpecializationCosts.count(F)) + return SpecializationCosts[F]; + + // Compute the code metrics for the function. + SmallPtrSet EphValues; + CodeMetrics::collectEphemeralValues(F, &(*GetAC)(*F), EphValues); + CodeMetrics Metrics; + for (BasicBlock &BB : *F) + Metrics.analyzeBasicBlock(&BB, (*GetTTI)(*F), EphValues); + + // If the code metrics reveal that we shouldn't duplicate the function, we + // shouldn't specialize it. Set the specialization cost to the maximum. + if (Metrics.notDuplicatable) + return SpecializationCosts[F] = std::numeric_limits::max(); + + // Otherwise, set the specialization cost to be the cost of all the + // instructions in the function. + return SpecializationCosts[F] = + Metrics.NumInsts * InlineConstants::InstrCost; + } + + /// Compute a bonus for replacing argument \p A with constant \p C. + /// + /// When specializing a function, we replace an argument with a constant. + /// This function computes the expected benefit from doing to. + /// + /// The current heuristic is limited to uncovering inlining opportunities. + /// That is, if the argument is a function pointer used for indirect calls + /// within the function, we can specialize the function and promote the + /// indirect calls to direct calls. If the called function is sufficiently + /// simple, it will be inlined into the caller. + /// + /// TODO: We should consider expanding the kinds of optimization + /// opportunities we look for. + /// + /// \returns a bonus for replacing argument \p A with constant \p C. + unsigned getSpecializationBonus(Argument *A, Constant *C) { + + // If we've already computed the specialization bonus for the given + // argument and constant, just return it. + if (SpecializationBonuses.count(std::make_pair(A, C))) + return SpecializationBonuses[std::make_pair(A, C)]; + + // The current heuristic is only concerned with exposing inlining + // opportunities via indirect call promotion. If the argument is not a + // function pointer, give up. + if (!isa(A->getType()) || + !isa(A->getType()->getPointerElementType())) + return SpecializationBonuses[std::make_pair(A, C)] = 0; + + // Since the argument is a function pointer, its incoming constant values + // should be functions or constant expressions. The code below attempts to + // look through cast expressions to find the function that will be called. + Value *CalledValue = C; + while (isa(CalledValue) && + cast(CalledValue)->isCast()) + CalledValue = cast(CalledValue)->getOperand(0); + Function *CalledFunction = dyn_cast(CalledValue); + if (!CalledFunction) + return SpecializationBonuses[std::make_pair(A, C)] = 0; + + // Get TTI for the called function (used for the inline cost). + auto &CalleeTTI = (*GetTTI)(*CalledFunction); + + // Look at all the call sites whose called value is the argument. + // Specializing the function on the argument would allow these indirect + // calls to be promoted to direct calls. If the indirect call promotion + // would likely enable the called function to be inlined, specializing is a + // good idea. + int Bonus = 0; + for (User *U : A->users()) { + if (!isa(U) && !isa(U)) + continue; + auto CS = CallSite(U); + if (CS.getCalledValue() != A) + continue; + + // Get the cost of inlining the called function at this call site. Note + // that this is only an estimate. The called function may eventually + // change in a way that leads to it not being inlined here, even though + // inlining looks profitable now. For example, one of its called + // functions may be inlined into it, making the called function too large + // to be inlined into this call site. + // + // We apply a boost for performing indirect call promotion by increasing + // the default threshold by the threshold for indirect calls. + auto Params = getInlineParams(); + Params.DefaultThreshold += InlineConstants::IndirectCallThreshold; + InlineCost IC = getInlineCost(CS, CalledFunction, Params, CalleeTTI, + *GetAC, GetBFI, PSI); + + // We clamp the bonus for this call to be between zero and the default + // threshold. + if (IC.isAlways()) + Bonus += Params.DefaultThreshold; + else if (IC.isVariable() && IC.getCostDelta() > 0) + Bonus += IC.getCostDelta(); + } + + assert(Bonus >= 0 && "Computed negative commulative bonus"); + + // Return the commulative bonus for replacing the argument with the given + // constant. + return SpecializationBonuses[std::make_pair(A, C)] = Bonus; + } + + /// Determine if we should specialize a function based on the incoming values + /// of the given argument. + /// + /// This function implements the goal-directed heuristic. It determines if + /// specializing the function based on the incoming values of argument \p A + /// would result in any significant optimization opportunities. If + /// optimization opportunities exist, the constant values of \p A on which to + /// specialize the function are collected in \p Constants. If the values in + /// \p Constants represent the complete set of values that \p A can take on, + /// the function will be completely specialized, and the \p IsPartial flag is + /// set to false. + /// + /// \returns true if the function should be specialized on the given + /// argument. + bool isArgumentInteresting(Argument *A, + SmallVectorImpl &Constants, + bool &IsPartial) { + Function *F = A->getParent(); + + // For now, don't attempt to specialize functions based on the values of + // struct types. + if (A->getType()->isStructTy()) + return false; + + // If the argument isn't overdefined, there's nothing to do. It should + // already be constant. + if (!Solver.getLatticeValueFor(A).isOverdefined()) + return false; + + // Collect the constant values that the argument can take on. If the + // argument can't take on any constant values, we aren't going to + // specialize the function. While it's possible to specialize the function + // based on non-constant arguments, there's likely not much benefit to + // constant propagation in doing so. + SmallPtrSet PossibleConstants; + bool AllConstant = getPossibleConstants(A, PossibleConstants); + if (PossibleConstants.empty()) + return false; + + // Determine if it would be profitable to create a specialization of the + // function where the argument takes on the given constant value. If so, + // add the constant to Constants. + for (auto *C : PossibleConstants) + if (getSpecializationBonus(A, C) > getSpecializationCost(F)) + Constants.push_back(C); + + // None of the constant values the argument can take on were deemed good + // candidates on which to specialize the function. + if (Constants.empty()) + return false; + + // This will be a partial specialization if some of the constants were + // rejected due to their profitability. + IsPartial = !AllConstant || PossibleConstants.size() != Constants.size(); + + return true; + } + + /// Collect in \p Constants all the constant values that argument \p A can + /// take on. + /// + /// \returns true if all of the values the argument can take on are constant + /// (e.g., the argument's parent function cannot be called with an + /// overdefined value). + bool getPossibleConstants(Argument *A, + SmallPtrSetImpl &Constants) { + Function *F = A->getParent(); + bool AllConstant = true; + + // Iterate over all the call sites of the argument's parent function. + for (User *U : F->users()) { + if (!isa(U) && !isa(U)) + continue; + auto CS = CallSite(U); + + // If the parent of the call site will never be executed, we don't need + // to worry about the passed value. + if (!Solver.isBlockExecutable(CS.getInstruction()->getParent())) + continue; + + // Get the lattice value for the value the call site passes to the + // argument. If this value is not constant, move on to the next call + // site. Additionally, set the AllConstant flag to false. + if (CS.getArgument(A->getArgNo()) != A && + !Solver.getLatticeValueFor(CS.getArgument(A->getArgNo())) + .isConstant()) { + AllConstant = false; + continue; + } + + // Add the constant to the set. + if (auto *C = dyn_cast(CS.getArgument(A->getArgNo()))) + Constants.insert(C); + } + + // If the argument can only take on constant values, AllConstant will be + // true. + return AllConstant; + } + + /// Rewrite calls to function \p F to call function \p Clone instead. + /// + /// This function modifies calls to function \p F whose argument at index \p + /// ArgNo is equal to constant \p C. The calls are rewritten to call function + /// \p Clone instead. + void rewriteCallSites(Function *F, Function *Clone, unsigned ArgNo, + Constant *C) { + SmallVector CallSitesToRewrite; + for (auto *U : F->users()) { + if (!isa(U) && !isa(U)) + continue; + CallSite CS(U); + if (!CS.getCalledFunction() || CS.getCalledFunction() != F) + continue; + CallSitesToRewrite.push_back(CS); + } + for (auto CS : CallSitesToRewrite) + if (CS.getInstruction()->getParent()->getParent() == Clone || + CS.getArgument(ArgNo) == C) + CS.setCalledFunction(Clone); + } +}; +} // namespace + static bool AddressIsTaken(const GlobalValue *GV) { // Delete any dead constantexpr klingons. GV->removeDeadConstantUsers(); @@ -1739,9 +2202,14 @@ ReturnsToZap.push_back(RI); } -static bool runIPSCCP(Module &M, const DataLayout &DL, - const TargetLibraryInfo *TLI) { +static bool +runIPSCCP(Module &M, const DataLayout &DL, const TargetLibraryInfo *TLI, + ProfileSummaryInfo *PSI, + std::function *GetAC, + Optional> GetBFI, + std::function *GetTTI) { SCCPSolver Solver(DL, TLI); + FunctionSpecializer FS(Solver, PSI, GetAC, GetBFI, GetTTI); // AddressTakenFunctions - This set keeps track of the address-taken functions // that are in the input. As IPSCCP runs through and simplifies code, @@ -1795,14 +2263,20 @@ Solver.TrackValueOfGlobalVariable(&G); // Solve for constants. - bool ResolvedUndefs = true; - while (ResolvedUndefs) { + bool SolveForConstants = true; + while (SolveForConstants) { Solver.Solve(); DEBUG(dbgs() << "RESOLVING UNDEFS\n"); - ResolvedUndefs = false; + SolveForConstants = false; for (Function &F : M) - ResolvedUndefs |= Solver.ResolvedUndefsIn(F); + SolveForConstants |= Solver.ResolvedUndefsIn(F); + + if (!EnableFunctionSpecialization) + continue; + + DEBUG(dbgs() << "SPECIALIZING FUNCTIONS\n"); + SolveForConstants |= FS.specializeFunctions(); } bool MadeChanges = false; @@ -1937,7 +2411,25 @@ PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) { const DataLayout &DL = M.getDataLayout(); auto &TLI = AM.getResult(M); - if (!runIPSCCP(M, DL, &TLI)) + auto &FAM = AM.getResult(M).getManager(); + auto &PSI = AM.getResult(M); + + std::function GetAC = + [&FAM](Function &F) -> AssumptionCache & { + return FAM.getResult(F); + }; + + std::function GetTTI = + [&FAM](Function &F) -> TargetTransformInfo & { + return FAM.getResult(F); + }; + + std::function GetBFI = + [&FAM](Function &F) -> BlockFrequencyInfo & { + return FAM.getResult(F); + }; + + if (!runIPSCCP(M, DL, &TLI, &PSI, &GetAC, {GetBFI}, &GetTTI)) return PreservedAnalyses::all(); return PreservedAnalyses::none(); } @@ -1962,11 +2454,32 @@ const DataLayout &DL = M.getDataLayout(); const TargetLibraryInfo *TLI = &getAnalysis().getTLI(); - return runIPSCCP(M, DL, TLI); + + ProfileSummaryInfo *PSI = + getAnalysis().getPSI(); + AssumptionCacheTracker *ACT = &getAnalysis(); + + TargetTransformInfoWrapperPass *TTIWP = + &getAnalysis(); + + std::function GetAC = + [&ACT](Function &F) -> AssumptionCache & { + return ACT->getAssumptionCache(F); + }; + + std::function GetTTI = + [&TTIWP](Function &F) -> TargetTransformInfo & { + return TTIWP->getTTI(F); + }; + + return runIPSCCP(M, DL, TLI, PSI, &GetAC, None, &GetTTI); } void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); AU.addRequired(); + AU.addRequired(); } }; } // end anonymous namespace @@ -1975,7 +2488,10 @@ INITIALIZE_PASS_BEGIN(IPSCCPLegacyPass, "ipsccp", "Interprocedural Sparse Conditional Constant Propagation", false, false) +INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) +INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_END(IPSCCPLegacyPass, "ipsccp", "Interprocedural Sparse Conditional Constant Propagation", false, false) Index: test/Other/new-pm-defaults.ll =================================================================== --- test/Other/new-pm-defaults.ll +++ test/Other/new-pm-defaults.ll @@ -78,6 +78,7 @@ ; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass ; CHECK-O-NEXT: Finished llvm::Function pass manager run. ; CHECK-O-NEXT: Running pass: IPSCCPPass +; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PromotePass> ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass @@ -92,7 +93,6 @@ ; CHECK-O-NEXT: Running analysis: GlobalsAA ; CHECK-O-NEXT: Running analysis: CallGraphAnalysis ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis -; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis ; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy ; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis Index: test/Other/new-pm-lto-defaults.ll =================================================================== --- test/Other/new-pm-lto-defaults.ll +++ test/Other/new-pm-lto-defaults.ll @@ -33,6 +33,7 @@ ; CHECK-O2-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function ; CHECK-O2-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis ; CHECK-O2-NEXT: Running pass: IPSCCPPass +; CHECK-O2-NEXT: Running analysis: ProfileSummaryAnalysis ; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}PostOrderFunctionAttrsPass> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}SCC ; CHECK-O1-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function Index: test/Other/new-pm-thinlto-defaults.ll =================================================================== --- test/Other/new-pm-thinlto-defaults.ll +++ test/Other/new-pm-thinlto-defaults.ll @@ -73,6 +73,7 @@ ; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass ; CHECK-O-NEXT: Finished llvm::Function pass manager run. ; CHECK-O-NEXT: Running pass: IPSCCPPass +; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PromotePass> ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass @@ -86,7 +87,6 @@ ; CHECK-O-NEXT: Running analysis: GlobalsAA ; CHECK-O-NEXT: Running analysis: CallGraphAnalysis ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis -; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis ; CHECK-O-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}LazyCallGraph{{.*}}> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy ; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis Index: test/Transforms/SCCP/ipsccp-specialization.ll =================================================================== --- /dev/null +++ test/Transforms/SCCP/ipsccp-specialization.ll @@ -0,0 +1,52 @@ +; RUN: opt -ipsccp -deadargelim -inline -S < %s | FileCheck %s + +target triple = "aarch64-unknown-linux-gnueabi" + +; CHECK-LABEL: @main(i64 %x, i1 %flag) { +; CHECK: entry: +; CHECK-NEXT: br i1 %flag, label %plus, label %minus +; CHECK: plus: +; CHECK-NEXT: [[TMP0:%.+]] = add i64 %x, 1 +; CHECH-NEXT: br label %merge +; CHECK: minus: +; CHECK-NEXT: [[TMP1:%.+]] = sub i64 %x, 1 +; CHECK-NEXT: br label %merge +; CHECK: merge: +; CHECK-NEXT: [[TMP2:%.+]] = phi i64 [ [[TMP0]], %plus ], [ [[TMP1]], %minus ] +; CHECK-NEXT: ret i64 [[TMP2]] +; CHECK-NEXT: } +; +define i64 @main(i64 %x, i1 %flag) { +entry: + br i1 %flag, label %plus, label %minus + +plus: + %tmp0 = call i64 @compute(i64 %x, i64 (i64)* @plus) + br label %merge + +minus: + %tmp1 = call i64 @compute(i64 %x, i64 (i64)* @minus) + br label %merge + +merge: + %tmp2 = phi i64 [ %tmp0, %plus ], [ %tmp1, %minus] + ret i64 %tmp2 +} + +define internal i64 @compute(i64 %x, i64 (i64)* %binop) { +entry: + %tmp0 = call i64 %binop(i64 %x) + ret i64 %tmp0 +} + +define internal i64 @plus(i64 %x) { +entry: + %tmp0 = add i64 %x, 1 + ret i64 %tmp0 +} + +define internal i64 @minus(i64 %x) { +entry: + %tmp0 = sub i64 %x, 1 + ret i64 %tmp0 +}