[llvm] 8136a01 - [FuncSpec] Make the Function Specializer part of the IPSCCP pass.

Alexandros Lamprineas via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 10 06:50:39 PST 2022


Author: Alexandros Lamprineas
Date: 2022-12-10T14:39:49Z
New Revision: 8136a0172b3ce04975650732cc3cc9da89908211

URL: https://github.com/llvm/llvm-project/commit/8136a0172b3ce04975650732cc3cc9da89908211
DIFF: https://github.com/llvm/llvm-project/commit/8136a0172b3ce04975650732cc3cc9da89908211.diff

LOG: [FuncSpec] Make the Function Specializer part of the IPSCCP pass.

Reland 877a9f9abec61f06e39f1cd872e37b828139c2d1 since D138654 (parent)
has been fixed with 9ebaf4fef4aac89d4eff08e48185d61bc893f14e and with
8f1e11c5a7d70f96943a72649daa69f152d73e90.

Differential Revision: https://reviews.llvm.org/D126455

Added: 
    llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h

Modified: 
    llvm/include/llvm/InitializePasses.h
    llvm/include/llvm/LinkAllPasses.h
    llvm/include/llvm/Transforms/IPO.h
    llvm/include/llvm/Transforms/IPO/SCCP.h
    llvm/include/llvm/Transforms/Scalar/SCCP.h
    llvm/include/llvm/Transforms/Utils/SCCPSolver.h
    llvm/lib/Passes/PassBuilderPipelines.cpp
    llvm/lib/Passes/PassRegistry.def
    llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
    llvm/lib/Transforms/IPO/IPO.cpp
    llvm/lib/Transforms/IPO/SCCP.cpp
    llvm/lib/Transforms/Utils/SCCPSolver.cpp
    llvm/test/Transforms/FunctionSpecialization/bug52821-use-after-free.ll
    llvm/test/Transforms/FunctionSpecialization/bug55000-read-uninitialized-value.ll
    llvm/test/Transforms/FunctionSpecialization/compiler-crash-58759.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-always-inline.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression2.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression3.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression4.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression5.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-integers.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-loop.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize2.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize3.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup2.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-noexec.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-nonconst-glob.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-poison.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive2.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive3.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive4.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization-stats.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization2.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization3.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization4.ll
    llvm/test/Transforms/FunctionSpecialization/function-specialization5.ll
    llvm/test/Transforms/FunctionSpecialization/get-possible-constants.ll
    llvm/test/Transforms/FunctionSpecialization/identical-specializations.ll
    llvm/test/Transforms/FunctionSpecialization/literal-const.ll
    llvm/test/Transforms/FunctionSpecialization/no-spec-unused-arg.ll
    llvm/test/Transforms/FunctionSpecialization/noinline.ll
    llvm/test/Transforms/FunctionSpecialization/remove-dead-recursive-function.ll
    llvm/test/Transforms/FunctionSpecialization/specialization-order.ll
    llvm/test/Transforms/FunctionSpecialization/specialize-multiple-arguments.ll

Removed: 
    llvm/test/Transforms/FunctionSpecialization/function-specialization-nothing-todo.ll


################################################################################
diff  --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index a009183aef6a..d6376e147556 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -147,7 +147,6 @@ void initializeFlattenCFGLegacyPassPass(PassRegistry &);
 void initializeFloat2IntLegacyPassPass(PassRegistry&);
 void initializeForceFunctionAttrsLegacyPassPass(PassRegistry&);
 void initializeFuncletLayoutPass(PassRegistry&);
-void initializeFunctionSpecializationLegacyPassPass(PassRegistry &);
 void initializeGCMachineCodeAnalysisPass(PassRegistry&);
 void initializeGCModuleInfoPass(PassRegistry&);
 void initializeGVNHoistLegacyPassPass(PassRegistry&);

diff  --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h
index d5c3fb9d7675..e0706395e2b1 100644
--- a/llvm/include/llvm/LinkAllPasses.h
+++ b/llvm/include/llvm/LinkAllPasses.h
@@ -217,7 +217,6 @@ namespace {
       (void) llvm::createInjectTLIMappingsLegacyPass();
       (void) llvm::createUnifyLoopExitsPass();
       (void) llvm::createFixIrreduciblePass();
-      (void)llvm::createFunctionSpecializationPass();
       (void)llvm::createSelectOptimizePass();
 
       (void)new llvm::IntervalPartition();

diff  --git a/llvm/include/llvm/Transforms/IPO.h b/llvm/include/llvm/Transforms/IPO.h
index c979c41d74b7..fe1e4d6c2dc9 100644
--- a/llvm/include/llvm/Transforms/IPO.h
+++ b/llvm/include/llvm/Transforms/IPO.h
@@ -146,11 +146,6 @@ ModulePass *createDeadArgHackingPass();
 ///
 ModulePass *createIPSCCPPass();
 
-//===----------------------------------------------------------------------===//
-/// createFunctionSpecializationPass - This pass propagates constants from call
-/// sites to the specialized version of the callee function.
-ModulePass *createFunctionSpecializationPass();
-
 //===----------------------------------------------------------------------===//
 //
 /// createLoopExtractorPass - This pass extracts all natural loops from the

diff  --git a/llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h b/llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h
new file mode 100644
index 000000000000..bd278257f931
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/FunctionSpecialization.h
@@ -0,0 +1,174 @@
+//===- FunctionSpecialization.h - Function Specialization -----------------===//
+//
+// 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 specialises functions with constant parameters. Constant parameters
+// like function pointers and constant globals are propagated to the callee by
+// specializing the function. The main benefit of this pass at the moment is
+// that indirect calls are transformed into direct calls, which provides inline
+// opportunities that the inliner would not have been able to achieve. That's
+// why function specialisation is run before the inliner in the optimisation
+// pipeline; that is by design. Otherwise, we would only benefit from constant
+// passing, which is a valid use-case too, but hasn't been explored much in
+// terms of performance uplifts, cost-model and compile-time impact.
+//
+// Current limitations:
+// - It does not yet handle integer ranges. We do support "literal constants",
+//   but that's off by default under an option.
+// - The cost-model could be further looked into (it mainly focuses on inlining
+//   benefits),
+//
+// Ideas:
+// - With a function specialization attribute for arguments, we could have
+//   a direct way to steer function specialization, avoiding the cost-model,
+//   and thus control compile-times / code-size.
+//
+// Todos:
+// - Specializing recursive functions relies on running the transformation a
+//   number of times, which is controlled by option
+//   `func-specialization-max-iters`. Thus, increasing this value and the
+//   number of iterations, will linearly increase the number of times recursive
+//   functions get specialized, see also the discussion in
+//   https://reviews.llvm.org/D106426 for details. Perhaps there is a
+//   compile-time friendlier way to control/limit the number of specialisations
+//   for recursive functions.
+// - Don't transform the function if function specialization does not trigger;
+//   the SCCPSolver may make IR changes.
+//
+// References:
+// - 2021 LLVM Dev Mtg “Introducing function specialisation, and can we enable
+//   it by default?”, https://www.youtube.com/watch?v=zJiCjeXgV5Q
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
+#define LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
+
+#include "llvm/Analysis/CodeMetrics.h"
+#include "llvm/Analysis/InlineCost.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Transforms/Scalar/SCCP.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/SCCPSolver.h"
+#include "llvm/Transforms/Utils/SizeOpts.h"
+
+using namespace llvm;
+
+namespace llvm {
+// Bookkeeping struct to pass data from the analysis and profitability phase
+// to the actual transform helper functions.
+struct SpecializationInfo {
+  SmallVector<ArgInfo, 8> Args; // Stores the {formal,actual} argument pairs.
+  InstructionCost Gain;         // Profitability: Gain = Bonus - Cost.
+  Function *Clone;              // The definition of the specialized function.
+};
+
+using CallSpecBinding = std::pair<CallBase *, SpecializationInfo>;
+// We are using MapVector because it guarantees deterministic iteration
+// order across executions.
+using SpecializationMap = SmallMapVector<CallBase *, SpecializationInfo, 8>;
+
+class FunctionSpecializer {
+
+  /// The IPSCCP Solver.
+  SCCPSolver &Solver;
+
+  Module &M;
+
+  /// Analysis manager, needed to invalidate analyses.
+  FunctionAnalysisManager *FAM;
+
+  /// Analyses used to help determine if a function should be specialized.
+  std::function<const TargetLibraryInfo &(Function &)> GetTLI;
+  std::function<TargetTransformInfo &(Function &)> GetTTI;
+  std::function<AssumptionCache &(Function &)> GetAC;
+
+  // The number of functions specialised, used for collecting statistics and
+  // also in the cost model.
+  unsigned NbFunctionsSpecialized = 0;
+
+  SmallPtrSet<Function *, 32> SpecializedFuncs;
+  SmallPtrSet<Function *, 32> FullySpecialized;
+  DenseMap<Function *, CodeMetrics> FunctionMetrics;
+
+public:
+  FunctionSpecializer(
+      SCCPSolver &Solver, Module &M, FunctionAnalysisManager *FAM,
+      std::function<const TargetLibraryInfo &(Function &)> GetTLI,
+      std::function<TargetTransformInfo &(Function &)> GetTTI,
+      std::function<AssumptionCache &(Function &)> GetAC)
+      : Solver(Solver), M(M), FAM(FAM), GetTLI(GetTLI), GetTTI(GetTTI),
+        GetAC(GetAC) {}
+
+  ~FunctionSpecializer() {
+    // Eliminate dead code.
+    removeDeadFunctions();
+    cleanUpSSA();
+  }
+
+  bool isClonedFunction(Function *F) { return SpecializedFuncs.count(F); }
+
+  bool run();
+
+private:
+  Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call);
+
+  /// A constant stack value is an AllocaInst that has a single constant
+  /// value stored to it. Return this constant if such an alloca stack value
+  /// is a function argument.
+  Constant *getConstantStackValue(CallInst *Call, Value *Val);
+
+  /// Iterate over the argument tracked functions see if there
+  /// are any new constant values for the call instruction via
+  /// stack variables.
+  void promoteConstantStackValues();
+
+  /// Clean up fully specialized functions.
+  void removeDeadFunctions();
+
+  /// Remove any ssa_copy intrinsics that may have been introduced.
+  void cleanUpSSA();
+
+  // Compute the code metrics for function \p F.
+  CodeMetrics &analyzeFunction(Function *F);
+
+  /// This function decides whether it's worthwhile to specialize function
+  /// \p F based on the known constant values its arguments can take on. It
+  /// only discovers potential specialization opportunities without actually
+  /// applying them.
+  ///
+  /// \returns true if any specializations have been found.
+  bool findSpecializations(Function *F, InstructionCost Cost,
+                           SmallVectorImpl<CallSpecBinding> &WorkList);
+
+  bool isCandidateFunction(Function *F);
+
+  Function *createSpecialization(Function *F, CallSpecBinding &Specialization);
+
+  /// Compute and return the cost of specializing function \p F.
+  InstructionCost getSpecializationCost(Function *F);
+
+  /// Compute a bonus for replacing argument \p A with constant \p C.
+  InstructionCost getSpecializationBonus(Argument *A, Constant *C,
+                                         const LoopInfo &LI);
+
+  /// Determine if it is possible to specialise the function for constant values
+  /// of the formal parameter \p A.
+  bool isArgumentInteresting(Argument *A);
+
+  /// Check if the value \p V  (an actual argument) is a constant or can only
+  /// have a constant value. Return that constant.
+  Constant *getCandidateConstant(Value *V);
+
+  /// Redirects callsites of function \p F to its specialized copies.
+  void updateCallSites(Function *F,
+                       SmallVectorImpl<CallSpecBinding> &Specializations);
+};
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H

diff  --git a/llvm/include/llvm/Transforms/IPO/SCCP.h b/llvm/include/llvm/Transforms/IPO/SCCP.h
index 0f85b51bcb9b..3c40d44ca9de 100644
--- a/llvm/include/llvm/Transforms/IPO/SCCP.h
+++ b/llvm/include/llvm/Transforms/IPO/SCCP.h
@@ -32,14 +32,6 @@ class IPSCCPPass : public PassInfoMixin<IPSCCPPass> {
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
 };
 
-/// Pass to perform interprocedural constant propagation by specializing
-/// functions
-class FunctionSpecializationPass
-    : public PassInfoMixin<FunctionSpecializationPass> {
-public:
-  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
-};
-
 } // end namespace llvm
 
 #endif // LLVM_TRANSFORMS_IPO_SCCP_H

diff  --git a/llvm/include/llvm/Transforms/Scalar/SCCP.h b/llvm/include/llvm/Transforms/Scalar/SCCP.h
index da03216d9621..9d5441a799f9 100644
--- a/llvm/include/llvm/Transforms/Scalar/SCCP.h
+++ b/llvm/include/llvm/Transforms/Scalar/SCCP.h
@@ -40,12 +40,6 @@ class SCCPPass : public PassInfoMixin<SCCPPass> {
   PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
 };
 
-bool runFunctionSpecialization(
-    Module &M, FunctionAnalysisManager *FAM, const DataLayout &DL,
-    std::function<TargetLibraryInfo &(Function &)> GetTLI,
-    std::function<TargetTransformInfo &(Function &)> GetTTI,
-    std::function<AssumptionCache &(Function &)> GetAC,
-    function_ref<AnalysisResultsForFn(Function &)> GetAnalysis);
 } // end namespace llvm
 
 #endif // LLVM_TRANSFORMS_SCALAR_SCCP_H

diff  --git a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
index e39333b153b5..9e73425b07fb 100644
--- a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
+++ b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
@@ -118,6 +118,10 @@ class SCCPSolver {
   /// should be rerun.
   bool resolvedUndefsIn(Function &F);
 
+  void solveWhileResolvedUndefsIn(Module &M);
+
+  void solveWhileResolvedUndefsIn(SmallVectorImpl<Function *> &WorkList);
+
   bool isBlockExecutable(BasicBlock *BB) const;
 
   // isEdgeFeasible - Return true if the control flow edge from the 'From' basic

diff  --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 1b5808467782..6aae40431862 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -267,10 +267,6 @@ static cl::opt<bool> EnableConstraintElimination(
     cl::desc(
         "Enable pass to eliminate conditions based on linear constraints"));
 
-static cl::opt<bool> EnableFunctionSpecialization(
-    "enable-function-specialization", cl::init(false), cl::Hidden,
-    cl::desc("Enable Function Specialization pass"));
-
 static cl::opt<AttributorRunOption> AttributorRun(
     "attributor-enable", cl::Hidden, cl::init(AttributorRunOption::NONE),
     cl::desc("Enable the attributor inter-procedural deduction pass"),
@@ -1016,10 +1012,6 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   for (auto &C : PipelineEarlySimplificationEPCallbacks)
     C(MPM, Level);
 
-  // Specialize functions with IPSCCP.
-  if (EnableFunctionSpecialization && Level == OptimizationLevel::O3)
-    MPM.addPass(FunctionSpecializationPass());
-
   // Interprocedural constant propagation now that basic cleanup has occurred
   // and prior to optimizing globals.
   // FIXME: This position in the pipeline hasn't been carefully considered in
@@ -1634,8 +1626,6 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
     MPM.addPass(PGOIndirectCallPromotion(
         true /* InLTO */, PGOOpt && PGOOpt->Action == PGOOptions::SampleUse));
 
-    if (EnableFunctionSpecialization && Level == OptimizationLevel::O3)
-      MPM.addPass(FunctionSpecializationPass());
     // Propagate constants at call sites into the functions they call.  This
     // opens opportunities for globalopt (and inlining) by substituting function
     // pointers passed as arguments to direct uses of functions.

diff  --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index dc425569c31d..f4686e33de73 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -59,7 +59,6 @@ MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass())
 MODULE_PASS("extract-blocks", BlockExtractorPass())
 MODULE_PASS("forceattrs", ForceFunctionAttrsPass())
 MODULE_PASS("function-import", FunctionImportPass())
-MODULE_PASS("function-specialization", FunctionSpecializationPass())
 MODULE_PASS("globaldce", GlobalDCEPass())
 MODULE_PASS("globalopt", GlobalOptPass())
 MODULE_PASS("globalsplit", GlobalSplitPass())

diff  --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index 0554499b2e48..84ddeebde853 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -45,6 +45,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/Transforms/IPO/FunctionSpecialization.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/CodeMetrics.h"
 #include "llvm/Analysis/InlineCost.h"
@@ -70,11 +71,6 @@ static cl::opt<bool> ForceFunctionSpecialization(
     cl::desc("Force function specialization for every call site with a "
              "constant argument"));
 
-static cl::opt<unsigned> FuncSpecializationMaxIters(
-    "func-specialization-max-iters", cl::Hidden,
-    cl::desc("The maximum number of iterations function specialization is run"),
-    cl::init(1));
-
 static cl::opt<unsigned> MaxClonesThreshold(
     "func-specialization-max-clones", cl::Hidden,
     cl::desc("The maximum number of clones allowed for a single function "
@@ -97,9 +93,6 @@ static cl::opt<bool> SpecializeOnAddresses(
     cl::desc("Enable function specialization on the address of global values"));
 
 // Disabled by default as it can significantly increase compilation times.
-// Running nikic's compile time tracker on x86 with instruction count as the
-// metric shows 3-4% regression for SPASS while being neutral for all other
-// benchmarks of the llvm test suite.
 //
 // https://llvm-compile-time-tracker.com
 // https://github.com/nikic/llvm-compile-time-tracker
@@ -108,23 +101,8 @@ static cl::opt<bool> EnableSpecializationForLiteralConstant(
     cl::desc("Enable specialization of functions that take a literal constant "
              "as an argument."));
 
-namespace {
-// Bookkeeping struct to pass data from the analysis and profitability phase
-// to the actual transform helper functions.
-struct SpecializationInfo {
-  SmallVector<ArgInfo, 8> Args; // Stores the {formal,actual} argument pairs.
-  InstructionCost Gain;         // Profitability: Gain = Bonus - Cost.
-};
-} // Anonymous namespace
-
-using FuncList = SmallVectorImpl<Function *>;
-using CallArgBinding = std::pair<CallBase *, Constant *>;
-using CallSpecBinding = std::pair<CallBase *, SpecializationInfo>;
-// We are using MapVector because it guarantees deterministic iteration
-// order across executions.
-using SpecializationMap = SmallMapVector<CallBase *, SpecializationInfo, 8>;
-
-static Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call) {
+Constant *FunctionSpecializer::getPromotableAlloca(AllocaInst *Alloca,
+                                                   CallInst *Call) {
   Value *StoreValue = nullptr;
   for (auto *User : Alloca->users()) {
     // We can't use llvm::isAllocaPromotable() as that would fail because of
@@ -147,14 +125,14 @@ static Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call) {
     // Bail if there is any other unknown usage.
     return nullptr;
   }
-  return dyn_cast_or_null<Constant>(StoreValue);
+  return getCandidateConstant(StoreValue);
 }
 
 // A constant stack value is an AllocaInst that has a single constant
 // value stored to it. Return this constant if such an alloca stack value
 // is a function argument.
-static Constant *getConstantStackValue(CallInst *Call, Value *Val,
-                                       SCCPSolver &Solver) {
+Constant *FunctionSpecializer::getConstantStackValue(CallInst *Call,
+                                                     Value *Val) {
   if (!Val)
     return nullptr;
   Val = Val->stripPointerCasts();
@@ -187,19 +165,23 @@ static Constant *getConstantStackValue(CallInst *Call, Value *Val,
 //       ret void
 //     }
 //
-static void constantArgPropagation(FuncList &WorkList, Module &M,
-                                   SCCPSolver &Solver) {
+void FunctionSpecializer::promoteConstantStackValues() {
   // Iterate over the argument tracked functions see if there
   // are any new constant values for the call instruction via
   // stack variables.
-  for (auto *F : WorkList) {
+  for (Function &F : M) {
+    if (!Solver.isArgumentTrackedFunction(&F))
+      continue;
 
-    for (auto *User : F->users()) {
+    for (auto *User : F.users()) {
 
       auto *Call = dyn_cast<CallInst>(User);
       if (!Call)
         continue;
 
+      if (!Solver.isBlockExecutable(Call->getParent()))
+        continue;
+
       bool Changed = false;
       for (const Use &U : Call->args()) {
         unsigned Idx = Call->getArgOperandNo(&U);
@@ -209,7 +191,7 @@ static void constantArgPropagation(FuncList &WorkList, Module &M,
         if (!Call->onlyReadsMemory(Idx) || !ArgOpType->isPointerTy())
           continue;
 
-        auto *ConstVal = getConstantStackValue(Call, ArgOp, Solver);
+        auto *ConstVal = getConstantStackValue(Call, ArgOp);
         if (!ConstVal)
           continue;
 
@@ -231,7 +213,7 @@ static void constantArgPropagation(FuncList &WorkList, Module &M,
 }
 
 // ssa_copy intrinsics are introduced by the SCCP solver. These intrinsics
-// interfere with the constantArgPropagation optimization.
+// interfere with the promoteConstantStackValues() optimization.
 static void removeSSACopy(Function &F) {
   for (BasicBlock &BB : F) {
     for (Instruction &Inst : llvm::make_early_inc_range(BB)) {
@@ -246,691 +228,475 @@ static void removeSSACopy(Function &F) {
   }
 }
 
-static void removeSSACopy(Module &M) {
-  for (Function &F : M)
-    removeSSACopy(F);
+/// Remove any ssa_copy intrinsics that may have been introduced.
+void FunctionSpecializer::cleanUpSSA() {
+  for (Function *F : SpecializedFuncs)
+    removeSSACopy(*F);
 }
 
-namespace {
-class FunctionSpecializer {
-
-  /// The IPSCCP Solver.
-  SCCPSolver &Solver;
-
-  /// Analysis manager, needed to invalidate analyses.
-  FunctionAnalysisManager *FAM;
+/// Attempt to specialize functions in the module to enable constant
+/// propagation across function boundaries.
+///
+/// \returns true if at least one function is specialized.
+bool FunctionSpecializer::run() {
+  bool Changed = false;
 
-  /// Analyses used to help determine if a function should be specialized.
-  std::function<AssumptionCache &(Function &)> GetAC;
-  std::function<TargetTransformInfo &(Function &)> GetTTI;
-  std::function<TargetLibraryInfo &(Function &)> GetTLI;
+  for (Function &F : M) {
+    if (!isCandidateFunction(&F))
+      continue;
 
-  SmallPtrSet<Function *, 4> SpecializedFuncs;
-  SmallPtrSet<Function *, 4> FullySpecialized;
-  SmallVector<Instruction *> ReplacedWithConstant;
-  DenseMap<Function *, CodeMetrics> FunctionMetrics;
+    auto Cost = getSpecializationCost(&F);
+    if (!Cost.isValid()) {
+      LLVM_DEBUG(dbgs() << "FnSpecialization: Invalid specialization cost.\n");
+      continue;
+    }
 
-public:
-  FunctionSpecializer(SCCPSolver &Solver, FunctionAnalysisManager *FAM,
-                      std::function<AssumptionCache &(Function &)> GetAC,
-                      std::function<TargetTransformInfo &(Function &)> GetTTI,
-                      std::function<TargetLibraryInfo &(Function &)> GetTLI)
-      : Solver(Solver), FAM(FAM), GetAC(GetAC), GetTTI(GetTTI), GetTLI(GetTLI) {
-  }
+    LLVM_DEBUG(dbgs() << "FnSpecialization: Specialization cost for "
+                      << F.getName() << " is " << Cost << "\n");
 
-  ~FunctionSpecializer() {
-    // Eliminate dead code.
-    removeDeadInstructions();
-    removeDeadFunctions();
-  }
+    SmallVector<CallSpecBinding, 8> Specializations;
+    if (!findSpecializations(&F, Cost, Specializations)) {
+      LLVM_DEBUG(
+          dbgs() << "FnSpecialization: No possible specializations found\n");
+      continue;
+    }
 
-  /// 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(FuncList &Candidates, FuncList &WorkList) {
-    bool Changed = false;
-    for (auto *F : Candidates) {
-      if (!isCandidateFunction(F))
-        continue;
+    Changed = true;
 
-      auto Cost = getSpecializationCost(F);
-      if (!Cost.isValid()) {
-        LLVM_DEBUG(
-            dbgs() << "FnSpecialization: Invalid specialization cost.\n");
-        continue;
-      }
+    SmallVector<Function *, 4> Clones;
+    for (CallSpecBinding &Specialization : Specializations)
+      Clones.push_back(createSpecialization(&F, Specialization));
 
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Specialization cost for "
-                        << F->getName() << " is " << Cost << "\n");
+    Solver.solveWhileResolvedUndefsIn(Clones);
+    updateCallSites(&F, Specializations);
+  }
 
-      SmallVector<CallSpecBinding, 8> Specializations;
-      if (!findSpecializations(F, Cost, Specializations)) {
-        LLVM_DEBUG(
-            dbgs() << "FnSpecialization: No possible specializations found\n");
-        continue;
-      }
+  promoteConstantStackValues();
 
-      Changed = true;
-      for (auto &Entry : Specializations)
-        specializeFunction(F, Entry.second, WorkList);
-    }
+  LLVM_DEBUG(if (NbFunctionsSpecialized) dbgs()
+             << "FnSpecialization: Specialized " << NbFunctionsSpecialized
+             << " functions in module " << M.getName() << "\n");
 
-    updateSpecializedFuncs(Candidates, WorkList);
-    NumFuncSpecialized += NbFunctionsSpecialized;
-    return Changed;
-  }
+  NumFuncSpecialized += NbFunctionsSpecialized;
+  return Changed;
+}
 
-  void removeDeadInstructions() {
-    for (auto *I : ReplacedWithConstant) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Removing dead instruction " << *I
-                        << "\n");
-      I->eraseFromParent();
-    }
-    ReplacedWithConstant.clear();
+void FunctionSpecializer::removeDeadFunctions() {
+  for (Function *F : FullySpecialized) {
+    LLVM_DEBUG(dbgs() << "FnSpecialization: Removing dead function "
+                      << F->getName() << "\n");
+    if (FAM)
+      FAM->clear(*F, F->getName());
+    F->eraseFromParent();
   }
+  FullySpecialized.clear();
+}
 
-  void removeDeadFunctions() {
-    for (auto *F : FullySpecialized) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Removing dead function "
-                        << F->getName() << "\n");
-      if (FAM)
-        FAM->clear(*F, F->getName());
-      F->eraseFromParent();
-    }
-    FullySpecialized.clear();
+// Compute the code metrics for function \p F.
+CodeMetrics &FunctionSpecializer::analyzeFunction(Function *F) {
+  auto I = FunctionMetrics.insert({F, CodeMetrics()});
+  CodeMetrics &Metrics = I.first->second;
+  if (I.second) {
+    // The code metrics were not cached.
+    SmallPtrSet<const Value *, 32> EphValues;
+    CodeMetrics::collectEphemeralValues(F, &(GetAC)(*F), EphValues);
+    for (BasicBlock &BB : *F)
+      Metrics.analyzeBasicBlock(&BB, (GetTTI)(*F), EphValues);
+
+    LLVM_DEBUG(dbgs() << "FnSpecialization: Code size of function "
+                      << F->getName() << " is " << Metrics.NumInsts
+                      << " instructions\n");
   }
+  return Metrics;
+}
 
-  bool tryToReplaceWithConstant(Value *V) {
-    if (!V->getType()->isSingleValueType() || isa<CallBase>(V) ||
-        V->user_empty())
-      return false;
-
-    const ValueLatticeElement &IV = Solver.getLatticeValueFor(V);
-    if (isOverdefined(IV))
-      return false;
-    auto *Const =
-        isConstant(IV) ? Solver.getConstant(IV) : UndefValue::get(V->getType());
-
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Replacing " << *V
-                      << "\nFnSpecialization: with " << *Const << "\n");
-
-    // Record uses of V to avoid visiting irrelevant uses of const later.
-    SmallVector<Instruction *> UseInsts;
-    for (auto *U : V->users())
-      if (auto *I = dyn_cast<Instruction>(U))
-        if (Solver.isBlockExecutable(I->getParent()))
-          UseInsts.push_back(I);
-
-    V->replaceAllUsesWith(Const);
-
-    for (auto *I : UseInsts)
-      Solver.visit(I);
-
-    // Remove the instruction from Block and Solver.
-    if (auto *I = dyn_cast<Instruction>(V)) {
-      if (I->isSafeToRemove()) {
-        ReplacedWithConstant.push_back(I);
-        Solver.removeLatticeValueFor(I);
-      }
-    }
-    return true;
-  }
+/// Clone the function \p F and remove the ssa_copy intrinsics added by
+/// the SCCPSolver in the cloned version.
+static Function *cloneCandidateFunction(Function *F) {
+  ValueToValueMapTy Mappings;
+  Function *Clone = CloneFunction(F, Mappings);
+  removeSSACopy(*Clone);
+  return Clone;
+}
 
-private:
-  // The number of functions specialised, used for collecting statistics and
-  // also in the cost model.
-  unsigned NbFunctionsSpecialized = 0;
-
-  // Compute the code metrics for function \p F.
-  CodeMetrics &analyzeFunction(Function *F) {
-    auto I = FunctionMetrics.insert({F, CodeMetrics()});
-    CodeMetrics &Metrics = I.first->second;
-    if (I.second) {
-      // The code metrics were not cached.
-      SmallPtrSet<const Value *, 32> EphValues;
-      CodeMetrics::collectEphemeralValues(F, &(GetAC)(*F), EphValues);
-      for (BasicBlock &BB : *F)
-        Metrics.analyzeBasicBlock(&BB, (GetTTI)(*F), EphValues);
-
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Code size of function "
-                        << F->getName() << " is " << Metrics.NumInsts
-                        << " instructions\n");
-    }
-    return Metrics;
-  }
+/// This function decides whether it's worthwhile to specialize function
+/// \p F based on the known constant values its arguments can take on. It
+/// only discovers potential specialization opportunities without actually
+/// applying them.
+///
+/// \returns true if any specializations have been found.
+bool FunctionSpecializer::findSpecializations(
+    Function *F, InstructionCost Cost,
+    SmallVectorImpl<CallSpecBinding> &WorkList) {
+  // Get a list of interesting arguments.
+  SmallVector<Argument *, 4> Args;
+  for (Argument &Arg : F->args())
+    if (isArgumentInteresting(&Arg))
+      Args.push_back(&Arg);
+
+  if (!Args.size())
+    return false;
 
-  /// Clone the function \p F and remove the ssa_copy intrinsics added by
-  /// the SCCPSolver in the cloned version.
-  Function *cloneCandidateFunction(Function *F, ValueToValueMapTy &Mappings) {
-    Function *Clone = CloneFunction(F, Mappings);
-    removeSSACopy(*Clone);
-    return Clone;
-  }
+  // Find all the call sites for the function.
+  SpecializationMap Specializations;
+  for (User *U : F->users()) {
+    if (!isa<CallInst>(U) && !isa<InvokeInst>(U))
+      continue;
+    auto &CS = *cast<CallBase>(U);
 
-  /// This function decides whether it's worthwhile to specialize function
-  /// \p F based on the known constant values its arguments can take on. It
-  /// only discovers potential specialization opportunities without actually
-  /// applying them.
-  ///
-  /// \returns true if any specializations have been found.
-  bool findSpecializations(Function *F, InstructionCost Cost,
-                           SmallVectorImpl<CallSpecBinding> &WorkList) {
-    // Get a list of interesting arguments.
-    SmallVector<Argument *, 4> Args;
-    for (Argument &Arg : F->args())
-      if (isArgumentInteresting(&Arg))
-        Args.push_back(&Arg);
-
-    if (!Args.size())
-      return false;
-
-    // Find all the call sites for the function.
-    SpecializationMap Specializations;
-    for (User *U : F->users()) {
-      if (!isa<CallInst>(U) && !isa<InvokeInst>(U))
-        continue;
-      auto &CS = *cast<CallBase>(U);
-      // If the call site has attribute minsize set, that callsite won't be
-      // specialized.
-      if (CS.hasFnAttr(Attribute::MinSize))
-        continue;
+    // Skip irrelevant users.
+    if (CS.getCalledFunction() != F)
+      continue;
 
-      // 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.getParent()))
-        continue;
+    // If the call site has attribute minsize set, that callsite won't be
+    // specialized.
+    if (CS.hasFnAttr(Attribute::MinSize))
+      continue;
 
-      // Examine arguments and create specialization candidates from call sites
-      // with constant arguments.
-      bool Added = false;
-      for (Argument *A : Args) {
-        Constant *C = getCandidateConstant(CS.getArgOperand(A->getArgNo()));
-        if (!C)
-          continue;
+    // 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.getParent()))
+      continue;
 
-        if (!Added) {
-          Specializations[&CS] = {{}, 0 - Cost};
-          Added = true;
-        }
+    // Examine arguments and create specialization candidates from call sites
+    // with constant arguments.
+    bool Added = false;
+    for (Argument *A : Args) {
+      Constant *C = getCandidateConstant(CS.getArgOperand(A->getArgNo()));
+      if (!C)
+        continue;
 
-        SpecializationInfo &S = Specializations.back().second;
-        S.Gain += getSpecializationBonus(A, C, Solver.getLoopInfo(*F));
-        S.Args.push_back({A, C});
+      if (!Added) {
+        Specializations[&CS] = {{}, 0 - Cost, nullptr};
+        Added = true;
       }
-      Added = false;
-    }
 
-    // Remove unprofitable specializations.
-    if (!ForceFunctionSpecialization)
-      Specializations.remove_if(
-          [](const auto &Entry) { return Entry.second.Gain <= 0; });
-
-    // Clear the MapVector and return the underlying vector.
-    WorkList = Specializations.takeVector();
-
-    // Sort the candidates in descending order.
-    llvm::stable_sort(WorkList, [](const auto &L, const auto &R) {
-      return L.second.Gain > R.second.Gain;
-    });
-
-    // Truncate the worklist to 'MaxClonesThreshold' candidates if necessary.
-    if (WorkList.size() > MaxClonesThreshold) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Number of candidates exceed "
-                        << "the maximum number of clones threshold.\n"
-                        << "FnSpecialization: Truncating worklist to "
-                        << MaxClonesThreshold << " candidates.\n");
-      WorkList.erase(WorkList.begin() + MaxClonesThreshold, WorkList.end());
+      SpecializationInfo &S = Specializations.back().second;
+      S.Gain += getSpecializationBonus(A, C, Solver.getLoopInfo(*F));
+      S.Args.push_back({A, C});
     }
-
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Specializations for function "
-                      << F->getName() << "\n";
-               for (const auto &Entry
-                    : WorkList) {
-                 dbgs() << "FnSpecialization:   Gain = " << Entry.second.Gain
-                        << "\n";
-                 for (const ArgInfo &Arg : Entry.second.Args)
-                   dbgs() << "FnSpecialization:   FormalArg = "
-                          << Arg.Formal->getNameOrAsOperand()
-                          << ", ActualArg = "
-                          << Arg.Actual->getNameOrAsOperand() << "\n";
-               });
-
-    return !WorkList.empty();
+    Added = false;
   }
 
-  bool isCandidateFunction(Function *F) {
-    // Do not specialize the cloned function again.
-    if (SpecializedFuncs.contains(F))
-      return false;
-
-    // If we're optimizing the function for size, we shouldn't specialize it.
-    if (F->hasOptSize() ||
-        shouldOptimizeForSize(F, nullptr, nullptr, PGSOQueryType::IRPass))
-      return false;
+  // Remove unprofitable specializations.
+  if (!ForceFunctionSpecialization)
+    Specializations.remove_if(
+        [](const auto &Entry) { return Entry.second.Gain <= 0; });
+
+  // Clear the MapVector and return the underlying vector.
+  WorkList = Specializations.takeVector();
+
+  // Sort the candidates in descending order.
+  llvm::stable_sort(WorkList, [](const auto &L, const auto &R) {
+    return L.second.Gain > R.second.Gain;
+  });
+
+  // Truncate the worklist to 'MaxClonesThreshold' candidates if necessary.
+  if (WorkList.size() > MaxClonesThreshold) {
+    LLVM_DEBUG(dbgs() << "FnSpecialization: Number of candidates exceed "
+                      << "the maximum number of clones threshold.\n"
+                      << "FnSpecialization: Truncating worklist to "
+                      << MaxClonesThreshold << " candidates.\n");
+    WorkList.erase(WorkList.begin() + MaxClonesThreshold, WorkList.end());
+  }
 
-    // Exit if the function is not executable. There's no point in specializing
-    // a dead function.
-    if (!Solver.isBlockExecutable(&F->getEntryBlock()))
-      return false;
+  LLVM_DEBUG(dbgs() << "FnSpecialization: Specializations for function "
+                    << F->getName() << "\n";
+             for (const auto &Entry
+                  : WorkList) {
+               dbgs() << "FnSpecialization:   Gain = " << Entry.second.Gain
+                      << "\n";
+               for (const ArgInfo &Arg : Entry.second.Args)
+                 dbgs() << "FnSpecialization:   FormalArg = "
+                        << Arg.Formal->getNameOrAsOperand()
+                        << ", ActualArg = " << Arg.Actual->getNameOrAsOperand()
+                        << "\n";
+             });
 
-    // It wastes time to specialize a function which would get inlined finally.
-    if (F->hasFnAttribute(Attribute::AlwaysInline))
-      return false;
+  return !WorkList.empty();
+}
 
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Try function: " << F->getName()
-                      << "\n");
-    return true;
-  }
+bool FunctionSpecializer::isCandidateFunction(Function *F) {
+  if (F->isDeclaration())
+    return false;
 
-  void specializeFunction(Function *F, SpecializationInfo &S,
-                          FuncList &WorkList) {
-    ValueToValueMapTy Mappings;
-    Function *Clone = cloneCandidateFunction(F, Mappings);
-
-    // Rewrite calls to the function so that they call the clone instead.
-    rewriteCallSites(Clone, S.Args, Mappings);
-
-    // 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(Clone, S.Args);
-
-    // Mark all the specialized functions
-    WorkList.push_back(Clone);
-    NbFunctionsSpecialized++;
-
-    // If the function has been completely specialized, the original function
-    // is no longer needed. Mark it unreachable.
-    if (F->getNumUses() == 0 || all_of(F->users(), [F](User *U) {
-          if (auto *CS = dyn_cast<CallBase>(U))
-            return CS->getFunction() == F;
-          return false;
-        })) {
-      Solver.markFunctionUnreachable(F);
-      FullySpecialized.insert(F);
-    }
-  }
+  if (F->hasFnAttribute(Attribute::NoDuplicate))
+    return false;
 
-  /// Compute and return the cost of specializing function \p F.
-  InstructionCost getSpecializationCost(Function *F) {
-    CodeMetrics &Metrics = analyzeFunction(F);
-    // If the code metrics reveal that we shouldn't duplicate the function, we
-    // shouldn't specialize it. Set the specialization cost to Invalid.
-    // Or if the lines of codes implies that this function is easy to get
-    // inlined so that we shouldn't specialize it.
-    if (Metrics.notDuplicatable || !Metrics.NumInsts.isValid() ||
-        (!ForceFunctionSpecialization &&
-         !F->hasFnAttribute(Attribute::NoInline) &&
-         Metrics.NumInsts < SmallFunctionThreshold))
-      return InstructionCost::getInvalid();
-
-    // Otherwise, set the specialization cost to be the cost of all the
-    // instructions in the function and penalty for specializing more functions.
-    unsigned Penalty = NbFunctionsSpecialized + 1;
-    return Metrics.NumInsts * InlineConstants::getInstrCost() * Penalty;
-  }
+  if (!Solver.isArgumentTrackedFunction(F))
+    return false;
 
-  InstructionCost getUserBonus(User *U, llvm::TargetTransformInfo &TTI,
-                               const LoopInfo &LI) {
-    auto *I = dyn_cast_or_null<Instruction>(U);
-    // If not an instruction we do not know how to evaluate.
-    // Keep minimum possible cost for now so that it doesnt affect
-    // specialization.
-    if (!I)
-      return std::numeric_limits<unsigned>::min();
-
-    InstructionCost Cost =
-        TTI.getInstructionCost(U, TargetTransformInfo::TCK_SizeAndLatency);
-
-    // Increase the cost if it is inside the loop.
-    unsigned LoopDepth = LI.getLoopDepth(I->getParent());
-    Cost *= std::pow((double)AvgLoopIterationCount, LoopDepth);
-
-    // Traverse recursively if there are more uses.
-    // TODO: Any other instructions to be added here?
-    if (I->mayReadFromMemory() || I->isCast())
-      for (auto *User : I->users())
-        Cost += getUserBonus(User, TTI, LI);
-
-    return Cost;
-  }
+  // Do not specialize the cloned function again.
+  if (SpecializedFuncs.contains(F))
+    return false;
 
-  /// Compute a bonus for replacing argument \p A with constant \p C.
-  InstructionCost getSpecializationBonus(Argument *A, Constant *C,
-                                         const LoopInfo &LI) {
-    Function *F = A->getParent();
-    auto &TTI = (GetTTI)(*F);
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Analysing bonus for constant: "
-                      << C->getNameOrAsOperand() << "\n");
-
-    InstructionCost TotalCost = 0;
-    for (auto *U : A->users()) {
-      TotalCost += getUserBonus(U, TTI, LI);
-      LLVM_DEBUG(dbgs() << "FnSpecialization:   User cost ";
-                 TotalCost.print(dbgs()); dbgs() << " for: " << *U << "\n");
-    }
+  // If we're optimizing the function for size, we shouldn't specialize it.
+  if (F->hasOptSize() ||
+      shouldOptimizeForSize(F, nullptr, nullptr, PGSOQueryType::IRPass))
+    return false;
 
-    // The below heuristic is only concerned with exposing inlining
-    // opportunities via indirect call promotion. If the argument is not a
-    // (potentially casted) function pointer, give up.
-    Function *CalledFunction = dyn_cast<Function>(C->stripPointerCasts());
-    if (!CalledFunction)
-      return TotalCost;
-
-    // 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<CallInst>(U) && !isa<InvokeInst>(U))
-        continue;
-      auto *CS = cast<CallBase>(U);
-      if (CS->getCalledOperand() != A)
-        continue;
+  // Exit if the function is not executable. There's no point in specializing
+  // a dead function.
+  if (!Solver.isBlockExecutable(&F->getEntryBlock()))
+    return false;
 
-      // 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, GetTLI);
-
-      // 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();
-
-      LLVM_DEBUG(dbgs() << "FnSpecialization:   Inlining bonus " << Bonus
-                        << " for user " << *U << "\n");
-    }
+  // It wastes time to specialize a function which would get inlined finally.
+  if (F->hasFnAttribute(Attribute::AlwaysInline))
+    return false;
 
-    return TotalCost + Bonus;
-  }
+  LLVM_DEBUG(dbgs() << "FnSpecialization: Try function: " << F->getName()
+                    << "\n");
+  return true;
+}
 
-  /// Determine if it is possible to specialise the function for constant values
-  /// of the formal parameter \p A.
-  bool isArgumentInteresting(Argument *A) {
-    // No point in specialization if the argument is unused.
-    if (A->user_empty())
-      return false;
-
-    // For now, don't attempt to specialize functions based on the values of
-    // composite types.
-    Type *ArgTy = A->getType();
-    if (!ArgTy->isSingleValueType())
-      return false;
-
-    // Specialization of integer and floating point types needs to be explicitly
-    // enabled.
-    if (!EnableSpecializationForLiteralConstant &&
-        (ArgTy->isIntegerTy() || ArgTy->isFloatingPointTy()))
-      return false;
-
-    // SCCP solver does not record an argument that will be constructed on
-    // stack.
-    if (A->hasByValAttr() && !A->getParent()->onlyReadsMemory())
-      return false;
-
-    // Check the lattice value and decide if we should attemt to specialize,
-    // based on this argument. No point in specialization, if the lattice value
-    // is already a constant.
-    const ValueLatticeElement &LV = Solver.getLatticeValueFor(A);
-    if (LV.isUnknownOrUndef() || LV.isConstant() ||
-        (LV.isConstantRange() && LV.getConstantRange().isSingleElement())) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Nothing to do, argument "
-                        << A->getNameOrAsOperand() << " is already constant\n");
-      return false;
-    }
+Function *
+FunctionSpecializer::createSpecialization(Function *F,
+                                          CallSpecBinding &Specialization) {
+  Function *Clone = cloneCandidateFunction(F);
+  Specialization.second.Clone = Clone;
 
-    return true;
-  }
+  // 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(Clone, Specialization.second.Args);
 
-  /// Check if the valuy \p V  (an actual argument) is a constant or can only
-  /// have a constant value. Return that constant.
-  Constant *getCandidateConstant(Value *V) {
-    if (isa<PoisonValue>(V))
-      return nullptr;
+  Solver.addArgumentTrackedFunction(Clone);
+  Solver.markBlockExecutable(&Clone->front());
 
-    // TrackValueOfGlobalVariable only tracks scalar global variables.
-    if (auto *GV = dyn_cast<GlobalVariable>(V)) {
-      // Check if we want to specialize on the address of non-constant
-      // global values.
-      if (!GV->isConstant() && !SpecializeOnAddresses)
-        return nullptr;
+  // Mark all the specialized functions
+  SpecializedFuncs.insert(Clone);
+  NbFunctionsSpecialized++;
 
-      if (!GV->getValueType()->isSingleValueType())
-        return nullptr;
-    }
+  return Clone;
+}
 
-    // Select for possible specialisation values that are constants or
-    // are deduced to be constants or constant ranges with a single element.
-    Constant *C = dyn_cast<Constant>(V);
-    if (!C) {
-      const ValueLatticeElement &LV = Solver.getLatticeValueFor(V);
-      if (LV.isConstant())
-        C = LV.getConstant();
-      else if (LV.isConstantRange() &&
-               LV.getConstantRange().isSingleElement()) {
-        assert(V->getType()->isIntegerTy() && "Non-integral constant range");
-        C = Constant::getIntegerValue(
-            V->getType(), *LV.getConstantRange().getSingleElement());
-      } else
-        return nullptr;
-    }
+/// Compute and return the cost of specializing function \p F.
+InstructionCost FunctionSpecializer::getSpecializationCost(Function *F) {
+  CodeMetrics &Metrics = analyzeFunction(F);
+  // If the code metrics reveal that we shouldn't duplicate the function, we
+  // shouldn't specialize it. Set the specialization cost to Invalid.
+  // Or if the lines of codes implies that this function is easy to get
+  // inlined so that we shouldn't specialize it.
+  if (Metrics.notDuplicatable || !Metrics.NumInsts.isValid() ||
+      (!ForceFunctionSpecialization &&
+       !F->hasFnAttribute(Attribute::NoInline) &&
+       Metrics.NumInsts < SmallFunctionThreshold))
+    return InstructionCost::getInvalid();
+
+  // Otherwise, set the specialization cost to be the cost of all the
+  // instructions in the function and penalty for specializing more functions.
+  unsigned Penalty = NbFunctionsSpecialized + 1;
+  return Metrics.NumInsts * InlineConstants::getInstrCost() * Penalty;
+}
 
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Found interesting argument "
-                      << V->getNameOrAsOperand() << "\n");
+static InstructionCost getUserBonus(User *U, llvm::TargetTransformInfo &TTI,
+                                    const LoopInfo &LI) {
+  auto *I = dyn_cast_or_null<Instruction>(U);
+  // If not an instruction we do not know how to evaluate.
+  // Keep minimum possible cost for now so that it doesnt affect
+  // specialization.
+  if (!I)
+    return std::numeric_limits<unsigned>::min();
+
+  InstructionCost Cost =
+      TTI.getInstructionCost(U, TargetTransformInfo::TCK_SizeAndLatency);
+
+  // Increase the cost if it is inside the loop.
+  unsigned LoopDepth = LI.getLoopDepth(I->getParent());
+  Cost *= std::pow((double)AvgLoopIterationCount, LoopDepth);
+
+  // Traverse recursively if there are more uses.
+  // TODO: Any other instructions to be added here?
+  if (I->mayReadFromMemory() || I->isCast())
+    for (auto *User : I->users())
+      Cost += getUserBonus(User, TTI, LI);
+
+  return Cost;
+}
 
-    return C;
+/// Compute a bonus for replacing argument \p A with constant \p C.
+InstructionCost
+FunctionSpecializer::getSpecializationBonus(Argument *A, Constant *C,
+                                            const LoopInfo &LI) {
+  Function *F = A->getParent();
+  auto &TTI = (GetTTI)(*F);
+  LLVM_DEBUG(dbgs() << "FnSpecialization: Analysing bonus for constant: "
+                    << C->getNameOrAsOperand() << "\n");
+
+  InstructionCost TotalCost = 0;
+  for (auto *U : A->users()) {
+    TotalCost += getUserBonus(U, TTI, LI);
+    LLVM_DEBUG(dbgs() << "FnSpecialization:   User cost ";
+               TotalCost.print(dbgs()); dbgs() << " for: " << *U << "\n");
   }
 
-  /// Rewrite calls to function \p F to call function \p Clone instead.
-  ///
-  /// This function modifies calls to function \p F as long as the actual
-  /// arguments match those in \p Args. Note that for recursive calls we
-  /// need to compare against the cloned formal arguments.
-  ///
-  /// Callsites that have been marked with the MinSize function attribute won't
-  /// be specialized and rewritten.
-  void rewriteCallSites(Function *Clone, const SmallVectorImpl<ArgInfo> &Args,
-                        ValueToValueMapTy &Mappings) {
-    assert(!Args.empty() && "Specialization without arguments");
-    Function *F = Args[0].Formal->getParent();
-
-    SmallVector<CallBase *, 8> CallSitesToRewrite;
-    for (auto *U : F->users()) {
-      if (!isa<CallInst>(U) && !isa<InvokeInst>(U))
-        continue;
-      auto &CS = *cast<CallBase>(U);
-      if (!CS.getCalledFunction() || CS.getCalledFunction() != F)
-        continue;
-      CallSitesToRewrite.push_back(&CS);
-    }
+  // The below heuristic is only concerned with exposing inlining
+  // opportunities via indirect call promotion. If the argument is not a
+  // (potentially casted) function pointer, give up.
+  Function *CalledFunction = dyn_cast<Function>(C->stripPointerCasts());
+  if (!CalledFunction)
+    return TotalCost;
+
+  // 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<CallInst>(U) && !isa<InvokeInst>(U))
+      continue;
+    auto *CS = cast<CallBase>(U);
+    if (CS->getCalledOperand() != A)
+      continue;
 
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Replacing call sites of "
-                      << F->getName() << " with " << Clone->getName() << "\n");
-
-    for (auto *CS : CallSitesToRewrite) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization:   "
-                        << CS->getFunction()->getName() << " ->" << *CS
-                        << "\n");
-      if (/* recursive call */
-          (CS->getFunction() == Clone &&
-           all_of(Args,
-                  [CS, &Mappings](const ArgInfo &Arg) {
-                    unsigned ArgNo = Arg.Formal->getArgNo();
-                    return CS->getArgOperand(ArgNo) == Mappings[Arg.Formal];
-                  })) ||
-          /* normal call */
-          all_of(Args, [CS](const ArgInfo &Arg) {
-            unsigned ArgNo = Arg.Formal->getArgNo();
-            return CS->getArgOperand(ArgNo) == Arg.Actual;
-          })) {
-        CS->setCalledFunction(Clone);
-        Solver.markOverdefined(CS);
-      }
-    }
+    // 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, GetTLI);
+
+    // 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();
+
+    LLVM_DEBUG(dbgs() << "FnSpecialization:   Inlining bonus " << Bonus
+                      << " for user " << *U << "\n");
   }
 
-  void updateSpecializedFuncs(FuncList &Candidates, FuncList &WorkList) {
-    for (auto *F : WorkList) {
-      SpecializedFuncs.insert(F);
+  return TotalCost + Bonus;
+}
 
-      // Initialize the state of the newly created functions, marking them
-      // argument-tracked and executable.
-      if (F->hasExactDefinition() && !F->hasFnAttribute(Attribute::Naked))
-        Solver.addTrackedFunction(F);
+/// Determine if it is possible to specialise the function for constant values
+/// of the formal parameter \p A.
+bool FunctionSpecializer::isArgumentInteresting(Argument *A) {
+  // No point in specialization if the argument is unused.
+  if (A->user_empty())
+    return false;
 
-      Solver.addArgumentTrackedFunction(F);
-      Candidates.push_back(F);
-      Solver.markBlockExecutable(&F->front());
+  // For now, don't attempt to specialize functions based on the values of
+  // composite types.
+  Type *ArgTy = A->getType();
+  if (!ArgTy->isSingleValueType())
+    return false;
 
-      // Replace the function arguments for the specialized functions.
-      for (Argument &Arg : F->args())
-        if (!Arg.use_empty() && tryToReplaceWithConstant(&Arg))
-          LLVM_DEBUG(dbgs() << "FnSpecialization: Replaced constant argument: "
-                            << Arg.getNameOrAsOperand() << "\n");
-    }
-  }
-};
-} // namespace
-
-bool llvm::runFunctionSpecialization(
-    Module &M, FunctionAnalysisManager *FAM, const DataLayout &DL,
-    std::function<TargetLibraryInfo &(Function &)> GetTLI,
-    std::function<TargetTransformInfo &(Function &)> GetTTI,
-    std::function<AssumptionCache &(Function &)> GetAC,
-    function_ref<AnalysisResultsForFn(Function &)> GetAnalysis) {
-  SCCPSolver Solver(DL, GetTLI, M.getContext());
-  FunctionSpecializer FS(Solver, FAM, GetAC, GetTTI, GetTLI);
-  bool Changed = false;
+  // Specialization of integer and floating point types needs to be explicitly
+  // enabled.
+  if (!EnableSpecializationForLiteralConstant &&
+      (ArgTy->isIntegerTy() || ArgTy->isFloatingPointTy()))
+    return false;
 
-  // Loop over all functions, marking arguments to those with their addresses
-  // taken or that are external as overdefined.
-  for (Function &F : M) {
-    if (F.isDeclaration())
-      continue;
-    if (F.hasFnAttribute(Attribute::NoDuplicate))
-      continue;
+  // SCCP solver does not record an argument that will be constructed on
+  // stack.
+  if (A->hasByValAttr() && !A->getParent()->onlyReadsMemory())
+    return false;
 
-    LLVM_DEBUG(dbgs() << "\nFnSpecialization: Analysing decl: " << F.getName()
-                      << "\n");
-    Solver.addAnalysis(F, GetAnalysis(F));
+  // Check the lattice value and decide if we should attemt to specialize,
+  // based on this argument. No point in specialization, if the lattice value
+  // is already a constant.
+  const ValueLatticeElement &LV = Solver.getLatticeValueFor(A);
+  if (LV.isUnknownOrUndef() || LV.isConstant() ||
+      (LV.isConstantRange() && LV.getConstantRange().isSingleElement())) {
+    LLVM_DEBUG(dbgs() << "FnSpecialization: Nothing to do, argument "
+                      << A->getNameOrAsOperand() << " is already constant\n");
+    return false;
+  }
 
-    // Determine if we can track the function's arguments. If so, add the
-    // function to the solver's set of argument-tracked functions.
-    if (canTrackArgumentsInterprocedurally(&F)) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Can track arguments\n");
-      Solver.addArgumentTrackedFunction(&F);
-      continue;
-    } else {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Can't track arguments!\n"
-                        << "FnSpecialization: Doesn't have local linkage, or "
-                        << "has its address taken\n");
-    }
+  return true;
+}
 
-    // Assume the function is called.
-    Solver.markBlockExecutable(&F.front());
+/// Check if the valuy \p V  (an actual argument) is a constant or can only
+/// have a constant value. Return that constant.
+Constant *FunctionSpecializer::getCandidateConstant(Value *V) {
+  if (isa<PoisonValue>(V))
+    return nullptr;
 
-    // Assume nothing about the incoming arguments.
-    for (Argument &AI : F.args())
-      Solver.markOverdefined(&AI);
-  }
+  // TrackValueOfGlobalVariable only tracks scalar global variables.
+  if (auto *GV = dyn_cast<GlobalVariable>(V)) {
+    // Check if we want to specialize on the address of non-constant
+    // global values.
+    if (!GV->isConstant() && !SpecializeOnAddresses)
+      return nullptr;
 
-  // Determine if we can track any of the module's global variables. If so, add
-  // the global variables we can track to the solver's set of tracked global
-  // variables.
-  for (GlobalVariable &G : M.globals()) {
-    G.removeDeadConstantUsers();
-    if (canTrackGlobalVariableInterprocedurally(&G))
-      Solver.trackValueOfGlobalVariable(&G);
+    if (!GV->getValueType()->isSingleValueType())
+      return nullptr;
   }
 
-  auto &TrackedFuncs = Solver.getArgumentTrackedFunctions();
-  SmallVector<Function *, 16> FuncDecls(TrackedFuncs.begin(),
-                                        TrackedFuncs.end());
-
-  // No tracked functions, so nothing to do: don't run the solver and remove
-  // the ssa_copy intrinsics that may have been introduced.
-  if (TrackedFuncs.empty()) {
-    removeSSACopy(M);
-    return false;
+  // Select for possible specialisation values that are constants or
+  // are deduced to be constants or constant ranges with a single element.
+  Constant *C = dyn_cast<Constant>(V);
+  if (!C) {
+    const ValueLatticeElement &LV = Solver.getLatticeValueFor(V);
+    if (LV.isConstant())
+      C = LV.getConstant();
+    else if (LV.isConstantRange() && LV.getConstantRange().isSingleElement()) {
+      assert(V->getType()->isIntegerTy() && "Non-integral constant range");
+      C = Constant::getIntegerValue(V->getType(),
+                                    *LV.getConstantRange().getSingleElement());
+    } else
+      return nullptr;
   }
 
-  // Solve for constants.
-  auto RunSCCPSolver = [&](auto &WorkList) {
-    bool ResolvedUndefs = true;
-
-    while (ResolvedUndefs) {
-      // Not running the solver unnecessary is checked in regression test
-      // nothing-to-do.ll, so if this debug message is changed, this regression
-      // test needs updating too.
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Running solver\n");
-
-      Solver.solve();
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Resolving undefs\n");
-      ResolvedUndefs = false;
-      for (Function *F : WorkList)
-        if (Solver.resolvedUndefsIn(*F))
-          ResolvedUndefs = true;
-    }
-
-    for (auto *F : WorkList) {
-      for (BasicBlock &BB : *F) {
-        if (!Solver.isBlockExecutable(&BB))
-          continue;
-        // FIXME: The solver may make changes to the function here, so set
-        // Changed, even if later function specialization does not trigger.
-        for (auto &I : make_early_inc_range(BB))
-          Changed |= FS.tryToReplaceWithConstant(&I);
-      }
-    }
-  };
+  LLVM_DEBUG(dbgs() << "FnSpecialization: Found interesting argument "
+                    << V->getNameOrAsOperand() << "\n");
 
-#ifndef NDEBUG
-  LLVM_DEBUG(dbgs() << "FnSpecialization: Worklist fn decls:\n");
-  for (auto *F : FuncDecls)
-    LLVM_DEBUG(dbgs() << "FnSpecialization: *) " << F->getName() << "\n");
-#endif
+  return C;
+}
 
-  // Initially resolve the constants in all the argument tracked functions.
-  RunSCCPSolver(FuncDecls);
+/// Redirects callsites of function \p F to its specialized copies.
+void FunctionSpecializer::updateCallSites(
+    Function *F, SmallVectorImpl<CallSpecBinding> &Specializations) {
+  SmallVector<CallBase *, 8> ToUpdate;
+  for (User *U : F->users()) {
+    if (auto *CS = dyn_cast<CallBase>(U))
+      if (CS->getCalledFunction() == F &&
+          Solver.isBlockExecutable(CS->getParent()))
+        ToUpdate.push_back(CS);
+  }
 
-  SmallVector<Function *, 8> WorkList;
-  unsigned I = 0;
-  while (FuncSpecializationMaxIters != I++ &&
-         FS.specializeFunctions(FuncDecls, WorkList)) {
-    LLVM_DEBUG(dbgs() << "FnSpecialization: Finished iteration " << I << "\n");
+  unsigned NCallsLeft = ToUpdate.size();
+  for (CallBase *CS : ToUpdate) {
+    // Decrement the counter if the callsite is either recursive or updated.
+    bool ShouldDecrementCount = CS->getFunction() == F;
+    for (CallSpecBinding &Specialization : Specializations) {
+      Function *Clone = Specialization.second.Clone;
+      SmallVectorImpl<ArgInfo> &Args = Specialization.second.Args;
 
-    // Run the solver for the specialized functions.
-    RunSCCPSolver(WorkList);
+      if (any_of(Args, [CS, this](const ArgInfo &Arg) {
+            unsigned ArgNo = Arg.Formal->getArgNo();
+            return getCandidateConstant(CS->getArgOperand(ArgNo)) != Arg.Actual;
+          }))
+        continue;
 
-    // Replace some unresolved constant arguments.
-    constantArgPropagation(FuncDecls, M, Solver);
+      LLVM_DEBUG(dbgs() << "FnSpecialization: Replacing call site " << *CS
+                        << " with " << Clone->getName() << "\n");
 
-    WorkList.clear();
-    Changed = true;
+      CS->setCalledFunction(Clone);
+      ShouldDecrementCount = true;
+      break;
+    }
+    if (ShouldDecrementCount)
+      --NCallsLeft;
   }
 
-  LLVM_DEBUG(dbgs() << "FnSpecialization: Number of specializations = "
-                    << NumFuncSpecialized << "\n");
-
-  // Remove any ssa_copy intrinsics that may have been introduced.
-  removeSSACopy(M);
-  return Changed;
+  // If the function has been completely specialized, the original function
+  // is no longer needed. Mark it unreachable.
+  if (NCallsLeft == 0) {
+    Solver.markFunctionUnreachable(F);
+    FullySpecialized.insert(F);
+  }
 }

diff  --git a/llvm/lib/Transforms/IPO/IPO.cpp b/llvm/lib/Transforms/IPO/IPO.cpp
index cad6a2650f09..8bd92b53a6ce 100644
--- a/llvm/lib/Transforms/IPO/IPO.cpp
+++ b/llvm/lib/Transforms/IPO/IPO.cpp
@@ -30,7 +30,6 @@ void llvm::initializeIPO(PassRegistry &Registry) {
   initializeDAEPass(Registry);
   initializeDAHPass(Registry);
   initializeForceFunctionAttrsLegacyPassPass(Registry);
-  initializeFunctionSpecializationLegacyPassPass(Registry);
   initializeGlobalDCELegacyPassPass(Registry);
   initializeGlobalOptLegacyPassPass(Registry);
   initializeGlobalSplitPass(Registry);

diff  --git a/llvm/lib/Transforms/IPO/SCCP.cpp b/llvm/lib/Transforms/IPO/SCCP.cpp
index 868f62be929f..19659f473a13 100644
--- a/llvm/lib/Transforms/IPO/SCCP.cpp
+++ b/llvm/lib/Transforms/IPO/SCCP.cpp
@@ -23,8 +23,10 @@
 #include "llvm/InitializePasses.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ModRef.h"
 #include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FunctionSpecialization.h"
 #include "llvm/Transforms/Scalar/SCCP.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/SCCPSolver.h"
@@ -40,6 +42,13 @@ STATISTIC(NumDeadBlocks , "Number of basic blocks unreachable");
 STATISTIC(NumInstReplaced,
           "Number of instructions replaced with (simpler) instruction");
 
+static cl::opt<bool> SpecializeFunctions("specialize-functions",
+    cl::init(false), cl::Hidden, cl::desc("Enable function specialization"));
+
+static cl::opt<unsigned> FuncSpecializationMaxIters(
+    "func-specialization-max-iters", cl::init(1), cl::Hidden, cl::desc(
+    "The maximum number of iterations function specialization is run"));
+
 static void findReturnsToZap(Function &F,
                              SmallVector<ReturnInst *, 8> &ReturnsToZap,
                              SCCPSolver &Solver) {
@@ -93,10 +102,13 @@ static void findReturnsToZap(Function &F,
 }
 
 static bool runIPSCCP(
-    Module &M, const DataLayout &DL,
+    Module &M, const DataLayout &DL, FunctionAnalysisManager *FAM,
     std::function<const TargetLibraryInfo &(Function &)> GetTLI,
+    std::function<TargetTransformInfo &(Function &)> GetTTI,
+    std::function<AssumptionCache &(Function &)> GetAC,
     function_ref<AnalysisResultsForFn(Function &)> getAnalysis) {
   SCCPSolver Solver(DL, GetTLI, M.getContext());
+  FunctionSpecializer Specializer(Solver, M, FAM, GetTLI, GetTTI, GetAC);
 
   // Loop over all functions, marking arguments to those with their addresses
   // taken or that are external as overdefined.
@@ -136,24 +148,16 @@ static bool runIPSCCP(
   }
 
   // Solve for constants.
-  bool ResolvedUndefs = true;
-  Solver.solve();
-  while (ResolvedUndefs) {
-    LLVM_DEBUG(dbgs() << "RESOLVING UNDEFS\n");
-    ResolvedUndefs = false;
-    for (Function &F : M) {
-      if (Solver.resolvedUndefsIn(F))
-        ResolvedUndefs = true;
-    }
-    if (ResolvedUndefs)
-      Solver.solve();
-  }
+  Solver.solveWhileResolvedUndefsIn(M);
 
-  bool MadeChanges = false;
+  if (SpecializeFunctions) {
+    unsigned Iters = 0;
+    while (Iters++ < FuncSpecializationMaxIters && Specializer.run());
+  }
 
   // Iterate over all of the instructions in the module, replacing them with
   // constants if we have found them to be of constant values.
-
+  bool MadeChanges = false;
   for (Function &F : M) {
     if (F.isDeclaration())
       continue;
@@ -213,7 +217,10 @@ static bool runIPSCCP(
                                           NumInstRemoved, NumInstReplaced);
     }
 
-    DomTreeUpdater DTU = Solver.getDTU(F);
+    DomTreeUpdater DTU = SpecializeFunctions && Specializer.isClonedFunction(&F)
+        ? DomTreeUpdater(DomTreeUpdater::UpdateStrategy::Lazy)
+        : Solver.getDTU(F);
+
     // Change dead blocks to unreachable. We do it after replacing constants
     // in all executable blocks, because changeToUnreachable may remove PHI
     // nodes in executable blocks we found values for. The function's entry
@@ -364,15 +371,21 @@ PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) {
   auto GetTLI = [&FAM](Function &F) -> const TargetLibraryInfo & {
     return FAM.getResult<TargetLibraryAnalysis>(F);
   };
+  auto GetTTI = [&FAM](Function &F) -> TargetTransformInfo & {
+    return FAM.getResult<TargetIRAnalysis>(F);
+  };
+  auto GetAC = [&FAM](Function &F) -> AssumptionCache & {
+    return FAM.getResult<AssumptionAnalysis>(F);
+  };
   auto getAnalysis = [&FAM](Function &F) -> AnalysisResultsForFn {
     DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
     return {
         std::make_unique<PredicateInfo>(F, DT, FAM.getResult<AssumptionAnalysis>(F)),
         &DT, FAM.getCachedResult<PostDominatorTreeAnalysis>(F),
-        nullptr};
+        SpecializeFunctions ? &FAM.getResult<LoopAnalysis>(F) : nullptr };
   };
 
-  if (!runIPSCCP(M, DL, GetTLI, getAnalysis))
+  if (!runIPSCCP(M, DL, &FAM, GetTLI, GetTTI, GetAC, getAnalysis))
     return PreservedAnalyses::all();
 
   PreservedAnalyses PA;
@@ -404,6 +417,12 @@ class IPSCCPLegacyPass : public ModulePass {
     auto GetTLI = [this](Function &F) -> const TargetLibraryInfo & {
       return this->getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
     };
+    auto GetTTI = [this](Function &F) -> TargetTransformInfo & {
+      return this->getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
+    };
+    auto GetAC = [this](Function &F) -> AssumptionCache & {
+      return this->getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
+    };
     auto getAnalysis = [this](Function &F) -> AnalysisResultsForFn {
       DominatorTree &DT =
           this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
@@ -417,13 +436,14 @@ class IPSCCPLegacyPass : public ModulePass {
           nullptr};
     };
 
-    return runIPSCCP(M, DL, GetTLI, getAnalysis);
+    return runIPSCCP(M, DL, nullptr, GetTLI, GetTTI, GetAC, getAnalysis);
   }
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.addRequired<AssumptionCacheTracker>();
     AU.addRequired<DominatorTreeWrapperPass>();
     AU.addRequired<TargetLibraryInfoWrapperPass>();
+    AU.addRequired<TargetTransformInfoWrapperPass>();
   }
 };
 
@@ -444,95 +464,3 @@ INITIALIZE_PASS_END(IPSCCPLegacyPass, "ipsccp",
 // createIPSCCPPass - This is the public interface to this file.
 ModulePass *llvm::createIPSCCPPass() { return new IPSCCPLegacyPass(); }
 
-PreservedAnalyses FunctionSpecializationPass::run(Module &M,
-                                                  ModuleAnalysisManager &AM) {
-  const DataLayout &DL = M.getDataLayout();
-  auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
-  auto GetTLI = [&FAM](Function &F) -> TargetLibraryInfo & {
-    return FAM.getResult<TargetLibraryAnalysis>(F);
-  };
-  auto GetTTI = [&FAM](Function &F) -> TargetTransformInfo & {
-    return FAM.getResult<TargetIRAnalysis>(F);
-  };
-  auto GetAC = [&FAM](Function &F) -> AssumptionCache & {
-    return FAM.getResult<AssumptionAnalysis>(F);
-  };
-  auto GetAnalysis = [&FAM](Function &F) -> AnalysisResultsForFn {
-    DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
-    return {std::make_unique<PredicateInfo>(
-                F, DT, FAM.getResult<AssumptionAnalysis>(F)),
-            &DT, FAM.getCachedResult<PostDominatorTreeAnalysis>(F),
-            &FAM.getResult<LoopAnalysis>(F)};
-  };
-
-  if (!runFunctionSpecialization(M, &FAM, DL, GetTLI, GetTTI, GetAC, GetAnalysis))
-    return PreservedAnalyses::all();
-
-  PreservedAnalyses PA;
-  PA.preserve<DominatorTreeAnalysis>();
-  PA.preserve<PostDominatorTreeAnalysis>();
-  PA.preserve<FunctionAnalysisManagerModuleProxy>();
-  return PA;
-}
-
-namespace {
-struct FunctionSpecializationLegacyPass : public ModulePass {
-  static char ID; // Pass identification, replacement for typeid
-  FunctionSpecializationLegacyPass() : ModulePass(ID) {}
-
-  void getAnalysisUsage(AnalysisUsage &AU) const override {
-    AU.addRequired<AssumptionCacheTracker>();
-    AU.addRequired<DominatorTreeWrapperPass>();
-    AU.addRequired<TargetLibraryInfoWrapperPass>();
-    AU.addRequired<TargetTransformInfoWrapperPass>();
-  }
-
-  bool runOnModule(Module &M) override {
-    if (skipModule(M))
-      return false;
-
-    const DataLayout &DL = M.getDataLayout();
-    auto GetTLI = [this](Function &F) -> TargetLibraryInfo & {
-      return this->getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
-    };
-    auto GetTTI = [this](Function &F) -> TargetTransformInfo & {
-      return this->getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
-    };
-    auto GetAC = [this](Function &F) -> AssumptionCache & {
-      return this->getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
-    };
-
-    auto GetAnalysis = [this](Function &F) -> AnalysisResultsForFn {
-      DominatorTree &DT =
-          this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
-      return {
-          std::make_unique<PredicateInfo>(
-              F, DT,
-              this->getAnalysis<AssumptionCacheTracker>().getAssumptionCache(
-                  F)),
-          nullptr, // We cannot preserve the LI, DT, or PDT with the legacy pass
-          nullptr, // manager, so set them to nullptr.
-          nullptr};
-    };
-    return runFunctionSpecialization(M, nullptr, DL, GetTLI, GetTTI, GetAC, GetAnalysis);
-  }
-};
-} // namespace
-
-char FunctionSpecializationLegacyPass::ID = 0;
-
-INITIALIZE_PASS_BEGIN(
-    FunctionSpecializationLegacyPass, "function-specialization",
-    "Propagate constant arguments by specializing the function", false, false)
-
-INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
-INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
-INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
-INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
-INITIALIZE_PASS_END(FunctionSpecializationLegacyPass, "function-specialization",
-                    "Propagate constant arguments by specializing the function",
-                    false, false)
-
-ModulePass *llvm::createFunctionSpecializationPass() {
-  return new FunctionSpecializationLegacyPass();
-}

diff  --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 3c2d220daa64..eed366c41deb 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -704,6 +704,26 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
     for (auto &BB : *F)
       BBExecutable.erase(&BB);
   }
+
+  void solveWhileResolvedUndefsIn(Module &M) {
+    bool ResolvedUndefs = true;
+    while (ResolvedUndefs) {
+      solve();
+      ResolvedUndefs = false;
+      for (Function &F : M)
+        ResolvedUndefs |= resolvedUndefsIn(F);
+    }
+  }
+
+  void solveWhileResolvedUndefsIn(SmallVectorImpl<Function *> &WorkList) {
+    bool ResolvedUndefs = true;
+    while (ResolvedUndefs) {
+      solve();
+      ResolvedUndefs = false;
+      for (Function *F : WorkList)
+        ResolvedUndefs |= resolvedUndefsIn(*F);
+    }
+  }
 };
 
 } // namespace llvm
@@ -1771,6 +1791,9 @@ bool SCCPInstVisitor::resolvedUndefsIn(Function &F) {
     }
   }
 
+  LLVM_DEBUG(if (MadeChange) dbgs()
+             << "\nResolved undefs in " << F.getName() << '\n');
+
   return MadeChange;
 }
 
@@ -1834,6 +1857,15 @@ bool SCCPSolver::resolvedUndefsIn(Function &F) {
   return Visitor->resolvedUndefsIn(F);
 }
 
+void SCCPSolver::solveWhileResolvedUndefsIn(Module &M) {
+  Visitor->solveWhileResolvedUndefsIn(M);
+}
+
+void
+SCCPSolver::solveWhileResolvedUndefsIn(SmallVectorImpl<Function *> &WorkList) {
+  Visitor->solveWhileResolvedUndefsIn(WorkList);
+}
+
 bool SCCPSolver::isBlockExecutable(BasicBlock *BB) const {
   return Visitor->isBlockExecutable(BB);
 }

diff  --git a/llvm/test/Transforms/FunctionSpecialization/bug52821-use-after-free.ll b/llvm/test/Transforms/FunctionSpecialization/bug52821-use-after-free.ll
index 4b10fa1b12ff..b2f284653b02 100644
--- a/llvm/test/Transforms/FunctionSpecialization/bug52821-use-after-free.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/bug52821-use-after-free.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -S < %s | FileCheck %s
 
 %mystruct = type { i32, [2 x i64] }
 
@@ -8,17 +8,11 @@ define internal ptr @myfunc(ptr %arg) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    br label [[FOR_COND:%.*]]
 ; CHECK:       for.cond:
-; CHECK-NEXT:    br i1 true, label [[FOR_COND2:%.*]], label [[FOR_BODY:%.*]]
-; CHECK:       for.body:
-; CHECK-NEXT:    call void @callee(ptr nonnull null)
-; CHECK-NEXT:    br label [[FOR_COND]]
+; CHECK-NEXT:    br label [[FOR_COND2:%.*]]
 ; CHECK:       for.cond2:
-; CHECK-NEXT:    br i1 false, label [[FOR_END:%.*]], label [[FOR_BODY2:%.*]]
+; CHECK-NEXT:    br label [[FOR_BODY2:%.*]]
 ; CHECK:       for.body2:
-; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [[MYSTRUCT:%.*]], ptr null, i64 0, i32 1, i64 3
 ; CHECK-NEXT:    br label [[FOR_COND2]]
-; CHECK:       for.end:
-; CHECK-NEXT:    ret ptr [[ARG:%.*]]
 ;
 entry:
   br label %for.cond
@@ -48,7 +42,7 @@ define ptr @caller() {
 ; CHECK-LABEL: @caller(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[CALL:%.*]] = call ptr @myfunc(ptr undef)
-; CHECK-NEXT:    ret ptr [[CALL]]
+; CHECK-NEXT:    ret ptr undef
 ;
 entry:
   %call = call ptr @myfunc(ptr undef)
@@ -56,3 +50,4 @@ entry:
 }
 
 declare void @callee(ptr)
+

diff  --git a/llvm/test/Transforms/FunctionSpecialization/bug55000-read-uninitialized-value.ll b/llvm/test/Transforms/FunctionSpecialization/bug55000-read-uninitialized-value.ll
index 1b127b1a1447..664c152203be 100644
--- a/llvm/test/Transforms/FunctionSpecialization/bug55000-read-uninitialized-value.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/bug55000-read-uninitialized-value.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-max-iters=2 -func-specialization-size-threshold=20 -func-specialization-avg-iters-cost=20 -function-specialization-for-literal-constant=true -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-max-iters=2 -func-specialization-max-clones=1 -function-specialization-for-literal-constant=true -S < %s | FileCheck %s
 
 declare hidden i1 @compare(ptr) align 2
 declare hidden { i8, ptr } @getType(ptr) align 2

diff  --git a/llvm/test/Transforms/FunctionSpecialization/compiler-crash-58759.ll b/llvm/test/Transforms/FunctionSpecialization/compiler-crash-58759.ll
index 5cf3f42e508e..fa04d15e1b83 100644
--- a/llvm/test/Transforms/FunctionSpecialization/compiler-crash-58759.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/compiler-crash-58759.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S --passes='default<O3>' -enable-function-specialization < %s | FileCheck %s
+; RUN: opt -S --passes='default<O3>' -specialize-functions < %s | FileCheck %s
 
 define dso_local i32 @g0(i32 noundef %x) local_unnamed_addr {
 entry:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-always-inline.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-always-inline.ll
index d8a9ca851fec..2d5b3bf1a5f4 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-always-inline.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-always-inline.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-avg-iters-cost=3 -func-specialization-size-threshold=10 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-avg-iters-cost=3 -func-specialization-size-threshold=10 -S < %s | FileCheck %s
 
 ; CHECK-NOT: foo.{{[0-9]+}}
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression.ll
index 5483b5862905..5f57cfddf153 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression.ll
@@ -4,7 +4,7 @@
 ; Note that this test case shows that function specialization pass would
 ; transform the function even if no specialization happened.
 
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 %struct = type { i8, i16, i32, i64, i64}
 @Global = internal constant %struct {i8 0, i16 1, i32 2, i64 3, i64 4}
@@ -18,8 +18,7 @@ entry:
 define internal i64 @func(ptr %x, ptr %binop) {
 ; CHECK-LABEL: @func(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = call i64 [[BINOP:%.*]](ptr [[X:%.*]])
-; CHECK-NEXT:    ret i64 [[TMP0]]
+; CHECK-NEXT:    unreachable
 ;
 entry:
   %tmp0 = call i64 %binop(ptr %x)

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression2.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression2.ll
index d4cfa0e0b6cd..726d8dfc664f 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression2.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression2.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; Check that we don't crash and specialise on a constant expression.
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression3.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression3.ll
index 390c58dc0863..f3c63ec8844d 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression3.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression3.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 define i32 @main() {
 ; CHECK-LABEL: @main(

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression4.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression4.ll
index 02ff84d61436..7f5afd40f720 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression4.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression4.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; Check that we don't crash and specialise on a function call with byval attribute.
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression5.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression5.ll
index eeb5983e14e6..2c555abf3ed1 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression5.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-expression5.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-on-address -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-on-address -S < %s | FileCheck %s
 
 ; Check that we don't crash and specialise on a scalar global variable with byval attribute.
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-integers.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-integers.ll
index 2689eeb31dcf..bda21178a307 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-integers.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-constant-integers.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -function-specialization-for-literal-constant=true -func-specialization-size-threshold=10 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -function-specialization-for-literal-constant=true -func-specialization-size-threshold=10 -S < %s | FileCheck %s
 
 ; Check that the literal constant parameter could be specialized.
 ; CHECK: @foo.1(

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-loop.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-loop.ll
index ab454abf9420..c4f905edc28a 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-loop.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-loop.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-avg-iters-cost=5 -func-specialization-size-threshold=10 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-avg-iters-cost=5 -func-specialization-size-threshold=10 -S < %s | FileCheck %s
 
 ; Check that the loop depth results in a larger specialization bonus.
 ; CHECK: @foo.1(

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize.ll
index fb03453781b6..4e84a843bde9 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -S < %s | FileCheck %s
 
 ; CHECK-NOT: @compute.1
 ; CHECK-NOT: @compute.2

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize2.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize2.ll
index bb6d1cfd6406..9430d4fd3ac4 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize2.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize2.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-size-threshold=3 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-size-threshold=3 -S < %s | FileCheck %s
 
 ; Checks for callsites that have been annotated with MinSize. No specialisation
 ; expected here:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize3.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize3.ll
index e02558db20e8..89f55d567c95 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize3.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-minsize3.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-size-threshold=3 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-size-threshold=3 -S < %s | FileCheck %s
 
 ; Checks for callsites that have been annotated with MinSize. We only expect
 ; specialisation for the call that does not have the attribute:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup.ll
index 6fa96078838a..c6af1ec87f3d 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; Function @foo has function attribute 'noduplicate', so check that we don't
 ; specialize it:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup2.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup2.ll
index 44fa3d8446c6..26115c5e8727 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup2.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nodup2.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; Check that function foo does not gets specialised as it contains an intrinsic
 ; that is marked as NoDuplicate.

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-noexec.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-noexec.ll
index e86b6b0a4bf0..e399d9eff303 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-noexec.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-noexec.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; The if.then block is not executed, so check that we don't specialise here.
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nonconst-glob.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nonconst-glob.ll
index c09210c8ed11..51cefe4c32d2 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nonconst-glob.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nonconst-glob.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-on-address=0 -S < %s | FileCheck %s
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-on-address=1 -S < %s | FileCheck %s --check-prefix=ON-ADDRESS
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-on-address=0 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-on-address=1 -S < %s | FileCheck %s --check-prefix=ON-ADDRESS
 
 ; Global B is not constant. We do not specialise on addresses unless we
 ; enable that:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nothing-todo.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-nothing-todo.ll
deleted file mode 100644
index dfa31b154225..000000000000
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-nothing-todo.ll
+++ /dev/null
@@ -1,51 +0,0 @@
-; REQUIRES: asserts
-; RUN: opt -passes=function-specialization -debug -S < %s 2>&1 | FileCheck %s
-
-; The purpose of this test is to check that we don't run the solver as there's
-; nothing to do here. For a test that doesn't trigger function specialisation,
-; it is intentionally 'big' because we also want to check that the ssa.copy
-; intrinsics that are introduced by the solver are cleaned up if we bail
-; early. Thus, first check the debug messages for the introduction of these
-; intrinsics:
-
-; CHECK: FnSpecialization: Analysing decl: foo
-; CHECK: Found replacement{{.*}} call i32 @llvm.ssa.copy.i32
-; CHECK: Found replacement{{.*}} call i32 @llvm.ssa.copy.i32
-
-; Then, make sure the solver didn't run:
-
-; CHECK-NOT: Running solver
-
-; Finally, check the absence and thus removal of these intrinsics:
-
-; CHECK-LABEL: @foo
-; CHECK-NOT:   call i32 @llvm.ssa.copy.i32
-
- at N = external dso_local global i32, align 4
- at B = external dso_local global ptr, align 8
- at A = external dso_local global ptr, align 8
-
-define dso_local i32 @foo() {
-entry:
-  br label %for.cond
-
-for.cond:
-  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
-  %0 = load i32, ptr @N, align 4
-  %cmp = icmp slt i32 %i.0, %0
-  br i1 %cmp, label %for.body, label %for.cond.cleanup
-
-for.cond.cleanup:
-  ret i32 undef
-
-for.body:
-  %1 = load ptr, ptr @B, align 8
-  %idxprom = sext i32 %i.0 to i64
-  %arrayidx = getelementptr inbounds i32, ptr %1, i64 %idxprom
-  %2 = load i32, ptr %arrayidx, align 4
-  %3 = load ptr, ptr @A, align 8
-  %arrayidx2 = getelementptr inbounds i32, ptr %3, i64 %idxprom
-  store i32 %2, ptr %arrayidx2, align 4
-  %inc = add nsw i32 %i.0, 1
-  br label %for.cond
-}

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-poison.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-poison.ll
index 1990a9b8b040..7f2711ddac2a 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-poison.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-poison.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; Check that we don't crash and specialise on a poison value.
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive.ll
index db84fbcfe949..64d7a9c0e87a 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive.ll
@@ -1,6 +1,6 @@
-; RUN: opt -passes=function-specialization,inline,instcombine -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s --check-prefix=ITERS2
-; RUN: opt -passes=function-specialization,inline,instcombine -force-function-specialization -func-specialization-max-iters=3 -S < %s | FileCheck %s --check-prefix=ITERS3
-; RUN: opt -passes=function-specialization,inline,instcombine -force-function-specialization -func-specialization-max-iters=4 -S < %s | FileCheck %s --check-prefix=ITERS4
+; RUN: opt -passes=ipsccp,inline,instcombine -specialize-functions -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s --check-prefix=ITERS2
+; RUN: opt -passes=ipsccp,inline,instcombine -specialize-functions -force-function-specialization -func-specialization-max-iters=3 -S < %s | FileCheck %s --check-prefix=ITERS3
+; RUN: opt -passes=ipsccp,inline,instcombine -specialize-functions -force-function-specialization -func-specialization-max-iters=4 -S < %s | FileCheck %s --check-prefix=ITERS4
 
 @low = internal constant i32 0, align 4
 @high = internal constant i32 6, align 4

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive2.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive2.ll
index c01e76b1e7d6..c28b029728b0 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive2.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive2.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
 
 ; Volatile store preventing recursive specialisation:
 ;

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive3.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive3.ll
index 4ad02bc16f53..6e6a64178e86 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive3.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive3.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
 
 ; Duplicate store preventing recursive specialisation:
 ;

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive4.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive4.ll
index b9a7ded3a7a3..53509f128f0b 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive4.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-recursive4.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -func-specialization-max-iters=2 -S < %s | FileCheck %s
 
 ; Alloca is not an integer type:
 ;

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-stats.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-stats.ll
index 5c9111093a07..d380e38cfa75 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization-stats.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-stats.ll
@@ -1,5 +1,5 @@
 ; REQUIRES: asserts
-; RUN: opt -stats -passes=function-specialization -S -force-function-specialization < %s 2>&1 | FileCheck %s
+; RUN: opt -stats -passes=ipsccp -specialize-functions -S -force-function-specialization < %s 2>&1 | FileCheck %s
 
 ; CHECK: 2 function-specialization - Number of functions specialized
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization.ll
index 02b0a933bb93..afd1334fdd64 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-size-threshold=3 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-size-threshold=3 -S < %s | FileCheck %s
 
 define i64 @main(i64 %x, i1 %flag) {
 ;

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization2.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization2.ll
index feeae34f1bac..bf50e8501a91 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization2.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization2.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization,deadargelim -force-function-specialization -S < %s | FileCheck %s
-; RUN: opt -passes=function-specialization,deadargelim -func-specialization-max-iters=1 -force-function-specialization -S < %s | FileCheck %s
-; RUN: opt -passes=function-specialization,deadargelim -func-specialization-max-iters=0 -force-function-specialization -S < %s | FileCheck %s --check-prefix=DISABLED
-; RUN: opt -passes=function-specialization,deadargelim -func-specialization-avg-iters-cost=1 -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp,deadargelim -specialize-functions -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp,deadargelim -specialize-functions -func-specialization-max-iters=1 -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp,deadargelim -specialize-functions -func-specialization-max-iters=0 -force-function-specialization -S < %s | FileCheck %s --check-prefix=DISABLED
+; RUN: opt -passes=ipsccp,deadargelim -specialize-functions -func-specialization-avg-iters-cost=1 -force-function-specialization -S < %s | FileCheck %s
 
 ; DISABLED-NOT: @func.1(
 ; DISABLED-NOT: @func.2(
@@ -43,10 +43,11 @@ define internal void @decrement(ptr nocapture %0) {
 }
 
 define i32 @main(ptr %0, i32 %1) {
-; CHECK:    [[TMP3:%.*]] = call i32 @func.2(ptr [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK:    call void @func.2(ptr [[TMP0:%.*]], i32 [[TMP1:%.*]])
   %3 = call i32 @func(ptr %0, i32 %1, ptr nonnull @increment)
-; CHECK:    [[TMP4:%.*]] = call i32 @func.1(ptr [[TMP0]], i32 [[TMP3]])
+; CHECK:    call void @func.1(ptr [[TMP0]], i32 0)
   %4 = call i32 @func(ptr %0, i32 %3, ptr nonnull @decrement)
+; CHECK:    ret i32 0
   ret i32 %4
 }
 
@@ -63,10 +64,10 @@ define i32 @main(ptr %0, i32 %1) {
 ; CHECK:    call void @decrement(ptr [[TMP9]])
 ; CHECK:    [[TMP10:%.*]] = load i32, ptr [[TMP3]], align 4
 ; CHECK:    [[TMP11:%.*]] = add nsw i32 [[TMP10]], -1
-; CHECK:    [[TMP12:%.*]] = call i32 @func.1(ptr [[TMP0]], i32 [[TMP11]])
-; CHECK:    br label [[TMP13]]
-; CHECK:       13:
-; CHECK:    ret i32 0
+; CHECK:    call void @func.1(ptr [[TMP0]], i32 [[TMP11]])
+; CHECK:    br label [[TMP12:%.*]]
+; CHECK:       12:
+; CHECK:    ret void
 ;
 ;
 ; CHECK: @func.2(
@@ -82,6 +83,7 @@ define i32 @main(ptr %0, i32 %1) {
 ; CHECK:    call void @increment(ptr [[TMP9]])
 ; CHECK:    [[TMP10:%.*]] = load i32, ptr [[TMP3]], align 4
 ; CHECK:    [[TMP11:%.*]] = add nsw i32 [[TMP10]], -1
-; CHECK:    [[TMP12:%.*]] = call i32 @func.2(ptr [[TMP0]], i32 [[TMP11]])
-; CHECK:    br label [[TMP13]]
-; CHECK:    ret i32 0
+; CHECK:    call void @func.2(ptr [[TMP0]], i32 [[TMP11]])
+; CHECK:    br label [[TMP12:%.*]]
+; CHECK:       12:
+; CHECK:    ret void

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization3.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization3.ll
index 300def79fd9a..1ed1a8bf90b3 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization3.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization3.ll
@@ -1,8 +1,8 @@
-; RUN: opt -passes=function-specialization -func-specialization-avg-iters-cost=3 -S < %s | \
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-avg-iters-cost=3 -S < %s | \
 ; RUN:   FileCheck %s --check-prefixes=COMMON,DISABLED
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | \
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | \
 ; RUN:   FileCheck %s --check-prefixes=COMMON,FORCE
-; RUN: opt -passes=function-specialization -func-specialization-avg-iters-cost=3 -force-function-specialization -S < %s | \
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-avg-iters-cost=3 -force-function-specialization -S < %s | \
 ; RUN:   FileCheck %s --check-prefixes=COMMON,FORCE
 
 ; Test for specializing a constant global.

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization4.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization4.ll
index bfaa90b20533..5d388e813855 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization4.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization4.ll
@@ -1,7 +1,7 @@
-; RUN: opt -passes=function-specialization -force-function-specialization \
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization \
 ; RUN:   -func-specialization-max-clones=2 -S < %s | FileCheck %s
 
-; RUN: opt -passes=function-specialization -force-function-specialization \
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization \
 ; RUN:   -func-specialization-max-clones=1 -S < %s | FileCheck %s --check-prefix=CONST1
 
 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"

diff  --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization5.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization5.ll
index 0f828bd62e57..c4c4212d76b7 100644
--- a/llvm/test/Transforms/FunctionSpecialization/function-specialization5.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization5.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 ; There's nothing to specialize here as both calls are the same, so check that:
 ;

diff  --git a/llvm/test/Transforms/FunctionSpecialization/get-possible-constants.ll b/llvm/test/Transforms/FunctionSpecialization/get-possible-constants.ll
index a6e16ad48c72..0d817f4e1cb7 100644
--- a/llvm/test/Transforms/FunctionSpecialization/get-possible-constants.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/get-possible-constants.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S --passes=function-specialization < %s | FileCheck %s
+; RUN: opt -S --passes=ipsccp -specialize-functions < %s | FileCheck %s
 define dso_local i32 @p0(i32 noundef %x) {
 entry:
   %add = add nsw i32 %x, 1
@@ -11,6 +11,37 @@ entry:
   ret i32 %sub
 }
 
+; CHECK-LABEL: define dso_local i32 @f0
+; CHECK:       tail call fastcc i32 @g.[[#A:]]({{.*}}@p0)
+;
+define dso_local i32 @f0(i32 noundef %x) {
+entry:
+  %call = tail call fastcc i32 @g(i32 noundef %x, ptr noundef nonnull @p0)
+  ret i32 %call
+}
+
+; CHECK-LABEL: define dso_local i32 @f1
+; CHECK:       tail call fastcc i32 @g.[[#B:]]({{.*}}@p1)
+;
+define dso_local i32 @f1(i32 noundef %x) {
+entry:
+  %call = tail call fastcc i32 @g(i32 noundef %x, ptr noundef nonnull @p1)
+  ret i32 %call
+}
+
+; @g gets fully specialized
+; CHECK-NOT: define internal fastcc i32 @g(
+
+define internal fastcc i32 @g(i32 noundef %x, ptr nocapture noundef readonly %p) noinline  {
+entry:
+  %pcall = tail call i32 %p(i32 noundef %x)
+  %fcall = tail call fastcc i32 @f(i32 noundef %pcall, ptr noundef nonnull %p)
+  ret i32 %fcall
+}
+
+; CHECK-LABEL: define dso_local i32 @g0
+; CHECK:       tail call fastcc i32 @f.[[#C:]]({{.*}}@p0)
+;
 define dso_local i32 @g0(i32 noundef %x) {
 entry:
   %call = tail call fastcc i32 @f(i32 noundef %x, ptr noundef nonnull @p0)
@@ -24,6 +55,9 @@ entry:
   ret i32 %add
 }
 
+; CHECK-LABEL: define dso_local i32 @g1
+; CHECK:       tail call fastcc i32 @f.[[#D:]]({{.*}}@p1)
+;
 define dso_local i32 @g1(i32 noundef %x) {
 entry:
   %call = tail call fastcc i32 @f(i32 noundef %x, ptr noundef nonnull @p1)
@@ -38,5 +72,11 @@ entry:
 
 ; Check that a single argument, that cannot be used for specialisation, does not
 ; prevent specialisation based on other arguments.
-; CHECK: @f.1
-; CHECK: @f.2
+;
+; Also check that for callsites which reside in the body of newly created
+; (specialized) functions, the lattice value of the arguments is known.
+;
+; CHECK-DAG: define internal fastcc i32 @g.[[#A]]
+; CHECK-DAG: define internal fastcc i32 @g.[[#B]]
+; CHECK-DAG: define internal fastcc i32 @f.[[#C]]
+; CHECK-DAG: define internal fastcc i32 @f.[[#D]]

diff  --git a/llvm/test/Transforms/FunctionSpecialization/identical-specializations.ll b/llvm/test/Transforms/FunctionSpecialization/identical-specializations.ll
index e67f9d68b7b6..8ceb251908dc 100644
--- a/llvm/test/Transforms/FunctionSpecialization/identical-specializations.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/identical-specializations.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -force-function-specialization -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -force-function-specialization -S < %s | FileCheck %s
 
 define i64 @main(i64 %x, i64 %y, i1 %flag) {
 ; CHECK-LABEL: @main(
@@ -70,7 +70,7 @@ entry:
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[CMP0:%.*]] = call i64 @minus(i64 [[X:%.*]], i64 [[Y:%.*]])
 ; CHECK-NEXT:    [[CMP1:%.*]] = call i64 @plus(i64 [[X]], i64 [[Y]])
-; CHECK-NEXT:    [[CMP2:%.*]] = call i64 @compute(i64 [[X]], i64 [[Y]], ptr @minus, ptr @plus)
+; CHECK-NEXT:    [[CMP2:%.*]] = call i64 @compute.2(i64 [[X]], i64 [[Y]], ptr @minus, ptr @plus)
 
 ; CHECK-LABEL: @compute.3
 ; CHECK-NEXT:  entry:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/literal-const.ll b/llvm/test/Transforms/FunctionSpecialization/literal-const.ll
index 72cb966653b3..fb64b857a65c 100644
--- a/llvm/test/Transforms/FunctionSpecialization/literal-const.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/literal-const.ll
@@ -1,6 +1,6 @@
-; RUN: opt -S --passes=function-specialization \
+; RUN: opt -S --passes=ipsccp -specialize-functions \
 ; RUN:        -force-function-specialization < %s | FileCheck %s -check-prefix CHECK-NOLIT
-; RUN: opt -S --passes=function-specialization \
+; RUN: opt -S --passes=ipsccp -specialize-functions \
 ; RUN:        -function-specialization-for-literal-constant \
 ; RUN:        -force-function-specialization < %s | FileCheck %s -check-prefix CHECK-LIT
 

diff  --git a/llvm/test/Transforms/FunctionSpecialization/no-spec-unused-arg.ll b/llvm/test/Transforms/FunctionSpecialization/no-spec-unused-arg.ll
index 3ecbd66c9729..a3698181e70f 100644
--- a/llvm/test/Transforms/FunctionSpecialization/no-spec-unused-arg.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/no-spec-unused-arg.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S --passes=function-specialization -force-function-specialization -function-specialization-for-literal-constant < %s | FileCheck %s
+; RUN: opt -S --passes=ipsccp -specialize-functions -force-function-specialization -function-specialization-for-literal-constant < %s | FileCheck %s
 define internal i32 @f(i32 %x, i32 %y) noinline {
     ret i32 %x
 }
@@ -17,4 +17,4 @@ define i32 @g1() {
 ; to be a constant without the need for function specialisation and
 ; the second parameter is unused.
 
-;  CHECK-NOT: @f.
\ No newline at end of file
+;  CHECK-NOT: @f.

diff  --git a/llvm/test/Transforms/FunctionSpecialization/noinline.ll b/llvm/test/Transforms/FunctionSpecialization/noinline.ll
index 6c566fcb5738..fae5db2dfa29 100644
--- a/llvm/test/Transforms/FunctionSpecialization/noinline.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/noinline.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S --passes=function-specialization < %s | FileCheck %s
+; RUN: opt -S --passes=ipsccp -specialize-functions < %s | FileCheck %s
 define dso_local i32 @p0(i32 noundef %x) {
 entry:
   %add = add nsw i32 %x, 1

diff  --git a/llvm/test/Transforms/FunctionSpecialization/remove-dead-recursive-function.ll b/llvm/test/Transforms/FunctionSpecialization/remove-dead-recursive-function.ll
index 2655a39aaade..2be6cd9d4085 100644
--- a/llvm/test/Transforms/FunctionSpecialization/remove-dead-recursive-function.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/remove-dead-recursive-function.ll
@@ -1,4 +1,4 @@
-; RUN: opt -passes=function-specialization -func-specialization-size-threshold=3 -S < %s | FileCheck %s
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-size-threshold=3 -S < %s | FileCheck %s
 
 define i64 @main(i64 %x, i1 %flag) {
 entry:

diff  --git a/llvm/test/Transforms/FunctionSpecialization/specialization-order.ll b/llvm/test/Transforms/FunctionSpecialization/specialization-order.ll
index 9dade3f370b7..83b1ca19c5e5 100644
--- a/llvm/test/Transforms/FunctionSpecialization/specialization-order.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/specialization-order.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S --passes=function-specialization,deadargelim -force-function-specialization < %s | FileCheck %s
+; RUN: opt -S --passes=ipsccp,deadargelim -specialize-functions -force-function-specialization < %s | FileCheck %s
 define dso_local i32 @add(i32 %x, i32 %y) {
 entry:
   %add = add nsw i32 %y, %x

diff  --git a/llvm/test/Transforms/FunctionSpecialization/specialize-multiple-arguments.ll b/llvm/test/Transforms/FunctionSpecialization/specialize-multiple-arguments.ll
index 9a847a44f29e..f576764281e4 100644
--- a/llvm/test/Transforms/FunctionSpecialization/specialize-multiple-arguments.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/specialize-multiple-arguments.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=function-specialization -func-specialization-max-clones=0 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=NONE
-; RUN: opt -passes=function-specialization -func-specialization-max-clones=1 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=ONE
-; RUN: opt -passes=function-specialization -func-specialization-max-clones=2 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=TWO
-; RUN: opt -passes=function-specialization -func-specialization-max-clones=3 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=THREE
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-max-clones=0 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=NONE
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-max-clones=1 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=ONE
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-max-clones=2 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=TWO
+; RUN: opt -passes=ipsccp -specialize-functions -func-specialization-max-clones=3 -func-specialization-size-threshold=14 -S < %s | FileCheck %s --check-prefix=THREE
 
 ; Make sure that we iterate correctly after sorting the specializations:
 ; FnSpecialization: Specializations for function compute


        


More information about the llvm-commits mailing list