[llvm] Port Swift's merge function pass to llvm: merging functions that differ in constants (PR #68235)

Manman Ren via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 30 14:55:25 PDT 2023


https://github.com/manman-ren updated https://github.com/llvm/llvm-project/pull/68235

>From b396809b77d0a96ce5393c94c03e3867063939d8 Mon Sep 17 00:00:00 2001
From: Manman Ren <mren at fb.com>
Date: Mon, 2 Oct 2023 11:16:58 -0700
Subject: [PATCH 1/3] Preliminary patch for merging functions that differ in
 constants

---
 .../IPO/MergeFunctionsIgnoringConst.h         |   34 +
 .../Transforms/Utils/FunctionComparator.h     |    1 +
 .../Utils/FunctionComparatorIgnoringConst.h   |   58 +
 .../Utils/MergeFunctionsIgnoringConst.h       |   29 +
 llvm/lib/Passes/PassBuilder.cpp               |    1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |   11 +
 llvm/lib/Passes/PassRegistry.def              |    1 +
 llvm/lib/Transforms/IPO/CMakeLists.txt        |    1 +
 .../IPO/MergeFunctionsIgnoringConst.cpp       | 1430 +++++++++++++++++
 llvm/lib/Transforms/Utils/CMakeLists.txt      |    1 +
 .../Utils/FunctionComparatorIgnoringConst.cpp |  107 ++
 .../MergeFuncIgnoringConst/merge_func.ll      |  532 ++++++
 .../merge_with_exception.ll                   |  190 +++
 13 files changed, 2396 insertions(+)
 create mode 100644 llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
 create mode 100644 llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
 create mode 100644 llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h
 create mode 100644 llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
 create mode 100644 llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
 create mode 100644 llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll
 create mode 100644 llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll

diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
new file mode 100644
index 000000000000000..f9d55cc40873adc
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
@@ -0,0 +1,34 @@
+//===- MergeFunctionsIgnoringConst.h - Merge Functions ----------*- C++ -*-===//
+//
+// 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 pass transforms simple global variables that never have their address
+// taken.  If obviously true, it marks read/write globals as constant, deletes
+// variables only stored to, etc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_MERGEFUNCTIONSIGNORINGCONST_H
+#define LLVM_TRANSFORMS_IPO_MERGEFUNCTIONSIGNORINGCONST_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Module;
+
+/// Merge functions that differ by constants.
+class MergeFuncIgnoringConstPass
+    : public PassInfoMixin<MergeFuncIgnoringConstPass> {
+public:
+  MergeFuncIgnoringConstPass() {}
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_MERGEFUNCTIONSIGNORINGCONST_H
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
index c28f868039a1f7b..1a314b481c72c61 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
@@ -379,6 +379,7 @@ class FunctionComparator {
   /// But, we are still not able to compare operands of PHI nodes, since those
   /// could be operands from further BBs we didn't scan yet.
   /// So it's impossible to use dominance properties in general.
+protected:
   mutable DenseMap<const Value*, int> sn_mapL, sn_mapR;
 
   // The global state we will use
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h b/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
new file mode 100644
index 000000000000000..a61e02fa41db762
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
@@ -0,0 +1,58 @@
+//===- FunctionComparatorIgnoringConst.h - Function Comparator --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the FunctionComparatorIgnoringConst class which is used by
+// the MergeFuncIgnoringConst pass for comparing functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATORIGNORINGCONST_H
+#define LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATORIGNORINGCONST_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/IR/ValueMap.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Transforms/Utils/FunctionComparator.h"
+#include <set>
+
+namespace llvm {
+
+/// FunctionComparatorIgnoringConst - Compares two functions to determine
+/// whether or not they will generate machine code with the same behavior.
+class FunctionComparatorIgnoringConst : public FunctionComparator {
+public:
+  FunctionComparatorIgnoringConst(const Function *F1, const Function *F2,
+                                  GlobalNumberState *GN)
+      : FunctionComparator(F1, F2, GN) {}
+
+  int cmpOperandsIgnoringConsts(const Instruction *L, const Instruction *R,
+                                unsigned opIdx);
+
+  int cmpBasicBlocksIgnoringConsts(
+      const BasicBlock *BBL, const BasicBlock *BBR,
+      const std::set<std::pair<int, int>> *InstOpndIndex = nullptr);
+
+  int compareIgnoringConsts(
+      const std::set<std::pair<int, int>> *InstOpndIndex = nullptr);
+
+  int compareConstants(const Constant *L, const Constant *R) const {
+    return cmpConstants(L, R);
+  }
+
+private:
+  // Scratch index for instruction in order during cmpOperandsIgnoringConsts.
+  int index = 0;
+};
+
+} // end namespace llvm
+#endif // LLVM_TRANSFORMS_UTILS_FUNCTIONCOMPARATORIGNORINGCONST_H
diff --git a/llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h b/llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h
new file mode 100644
index 000000000000000..e63afbb6bbf1718
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h
@@ -0,0 +1,29 @@
+//===- MergeFunctionsIgnoringConst.h - Merge Functions ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines helpers used in the MergeFunctionsIgnoringConst.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_MERGEFUNCTIONSIGNORINGCONST_H
+#define LLVM_TRANSFORMS_UTILS_MERGEFUNCTIONSIGNORINGCONST_H
+
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Operator.h"
+
+using namespace llvm;
+
+bool isEligibleInstrunctionForConstantSharing(const Instruction *I);
+
+bool isEligibleOperandForConstantSharing(const Instruction *I, unsigned OpIdx);
+
+bool isEligibleFunction(Function *F);
+
+Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy);
+#endif // LLVM_TRANSFORMS_UTILS_MERGEFUNCTIONSIGNORINGCONST_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index fde759026e5d780..04e4c692ed0b23c 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -121,6 +121,7 @@
 #include "llvm/Transforms/IPO/LowerTypeTests.h"
 #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h"
 #include "llvm/Transforms/IPO/MergeFunctions.h"
+#include "llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h"
 #include "llvm/Transforms/IPO/ModuleInliner.h"
 #include "llvm/Transforms/IPO/OpenMPOpt.h"
 #include "llvm/Transforms/IPO/PartialInlining.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 600f8d43caaf216..6da458ce037dee1 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -60,6 +60,7 @@
 #include "llvm/Transforms/IPO/LowerTypeTests.h"
 #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h"
 #include "llvm/Transforms/IPO/MergeFunctions.h"
+#include "llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h"
 #include "llvm/Transforms/IPO/ModuleInliner.h"
 #include "llvm/Transforms/IPO/OpenMPOpt.h"
 #include "llvm/Transforms/IPO/PartialInlining.h"
@@ -176,6 +177,10 @@ static cl::opt<bool> EnableMergeFunctions(
     "enable-merge-functions", cl::init(false), cl::Hidden,
     cl::desc("Enable function merging as part of the optimization pipeline"));
 
+static cl::opt<bool> EnableMergeFuncIgnoringConst(
+    "enable-merge-func-ignoring-const", cl::init(false), cl::Hidden,
+    cl::desc("Enable function merger that ignores constants"));
+
 static cl::opt<bool> EnablePostPGOLoopRotation(
     "enable-post-pgo-loop-rotation", cl::init(true), cl::Hidden,
     cl::desc("Run the loop rotation transformation after PGO instrumentation"));
@@ -1631,6 +1636,9 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline(
   MPM.addPass(buildModuleOptimizationPipeline(
       Level, ThinOrFullLTOPhase::ThinLTOPostLink));
 
+  if (EnableMergeFuncIgnoringConst)
+    MPM.addPass(MergeFuncIgnoringConstPass());
+
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
@@ -1956,6 +1964,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
 
   invokeFullLinkTimeOptimizationLastEPCallbacks(MPM, Level);
 
+  if (EnableMergeFuncIgnoringConst)
+    MPM.addPass(MergeFuncIgnoringConstPass());
+
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 91782d661ddd7b7..73bb21d36364eda 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -87,6 +87,7 @@ MODULE_PASS("lower-ifunc", LowerIFuncPass())
 MODULE_PASS("lowertypetests", LowerTypeTestsPass())
 MODULE_PASS("metarenamer", MetaRenamerPass())
 MODULE_PASS("mergefunc", MergeFunctionsPass())
+MODULE_PASS("mergefunc-ignoring-const", MergeFuncIgnoringConstPass())
 MODULE_PASS("name-anon-globals", NameAnonGlobalPass())
 MODULE_PASS("no-op-module", NoOpModulePass())
 MODULE_PASS("objc-arc-apelim", ObjCARCAPElimPass())
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt
index 034f1587ae8df44..4dac04d3369950f 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -30,6 +30,7 @@ add_llvm_component_library(LLVMipo
   LowerTypeTests.cpp
   MemProfContextDisambiguation.cpp
   MergeFunctions.cpp
+  MergeFunctionsIgnoringConst.cpp
   ModuleInliner.cpp
   OpenMPOpt.cpp
   PartialInlining.cpp
diff --git a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
new file mode 100644
index 000000000000000..e60acf1d8279323
--- /dev/null
+++ b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
@@ -0,0 +1,1430 @@
+//===--- MergeFunctionsIgnoringConst.cpp - Merge functions ----------------===//
+//
+// 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 pass looks for similar functions that are mergeable and folds them.
+// The implementation is similar to LLVM's MergeFunctions pass. Instead of
+// merging identical functions, it merges functions which only differ by a few
+// constants in certain instructions.
+// This is copied from Swift's implementation.
+// TODO: We should generalize this pass and share it with Swift's
+// implementation.
+//
+// This pass should run after LLVM's MergeFunctions pass, because it works best
+// if there are no _identical_ functions in the module.
+// Note: it would also work for identical functions but could produce more
+// code overhead than the LLVM pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h"
+// #include "llvm/Transforms/Utils/GlobalMergeFunctions.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h"
+// #include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/ObjCARCUtil.h"
+#include "llvm/ADT/StableHashing.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+// #include "llvm/IR/GlobalPtrAuthInfo.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/IR/StructuralHash.h"
+#include "llvm/IR/ValueHandle.h"
+#include "llvm/IR/ValueMap.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+#include <vector>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "mergefunc-ignoring-const"
+
+STATISTIC(NumFunctionsMergedIgnoringConst, "Number of functions merged");
+STATISTIC(NumThunksWrittenIgnoringConst, "Number of thunks generated");
+
+static cl::opt<bool>
+    EnableMergeFunc2("enable-merge-func2", cl::init(false), cl::Hidden,
+                     cl::desc("Enable more aggressive function merger"));
+
+static cl::opt<unsigned> NumFunctionsIgnoringConstForSanityCheck(
+    "mergefunc-ignoringconst-sanity",
+    cl::desc("How many functions in module could be used for "
+             "MergeFunctionsIgnoringConst pass sanity check. "
+             "'0' disables this check. Works only with '-debug' key."),
+    cl::init(0), cl::Hidden);
+
+static cl::opt<unsigned> IgnoringConstMergeThreshold(
+    "mergefunc-ignoringconst-threshold",
+    cl::desc("Functions larger than the threshold are considered for merging."
+             "'0' disables function merging at all."),
+    cl::init(15), cl::Hidden);
+
+cl::opt<bool> UseLinkOnceODRLinkageMerging(
+    "use-linkonceodr-linkage-merging", cl::init(false), cl::Hidden,
+    cl::desc(
+        "Use LinkeOnceODR linkage to deduplicate the identical merged function "
+        "(default = off)"));
+
+cl::opt<bool> NoInlineForMergedFunction(
+    "no-inline-merged-function", cl::init(false), cl::Hidden,
+    cl::desc("set noinline for merged function (default = off)"));
+
+static cl::opt<bool>
+    CastArrayType("merge-cast-array-type", cl::init(false), cl::Hidden,
+                  cl::desc("support for casting array type (default = off)"));
+
+static cl::opt<bool> IgnoreMusttailFunction(
+    "ignore-musttail-function", cl::init(false), cl::Hidden,
+    cl::desc(
+        "ignore functions containing callsites with musttail (default = off)"));
+
+static cl::opt<bool> AlwaysCallThunk(
+    "merge-always-call-thunk", cl::init(false), cl::Hidden,
+    cl::desc(
+        "do not replace callsites and always emit a thunk (default = off)"));
+
+static cl::list<std::string> MergeBlockRegexFilters(
+    "merge-block-regex", cl::Optional,
+    cl::desc("Block functions from merging if they match the given "
+             "regular expression"),
+    cl::ZeroOrMore);
+
+static cl::list<std::string> MergeAllowRegexFilters(
+    "merge-allow-regex", cl::Optional,
+    cl::desc("Allow functions from merging if they match the given "
+             "regular expression"),
+    cl::ZeroOrMore);
+
+bool isEligibleInstrunctionForConstantSharing(const Instruction *I) {
+  switch (I->getOpcode()) {
+  case Instruction::Load:
+  case Instruction::Store:
+  case Instruction::Call:
+    return true;
+  default: {
+    if (EnableMergeFunc2 && I->getOpcode() == Instruction::Invoke)
+      return true;
+    return false;
+  }
+  }
+}
+
+/// Returns true if the \opIdx operand of \p CI is the callee operand.
+static bool isCalleeOperand(const CallBase *CI, unsigned opIdx) {
+  return &CI->getCalledOperandUse() == &CI->getOperandUse(opIdx);
+}
+
+static bool canParameterizeCallOperand(const CallBase *CI, unsigned opIdx) {
+  if (CI->isInlineAsm())
+    return false;
+  Function *Callee = CI->getCalledOperand()
+                         ? dyn_cast_or_null<Function>(
+                               CI->getCalledOperand()->stripPointerCasts())
+                         : nullptr;
+  if (Callee) {
+    if (Callee->isIntrinsic())
+      return false;
+    // objc_msgSend stubs must be called, and can't have their address taken.
+    if (Callee->getName().startswith("objc_msgSend$"))
+      return false;
+  }
+  if (isCalleeOperand(CI, opIdx) &&
+      CI->getOperandBundle(LLVMContext::OB_ptrauth).has_value()) {
+    // The operand is the callee and it has already been signed. Ignore this
+    // because we cannot add another ptrauth bundle to the call instruction.
+    return false;
+  }
+  return true;
+}
+
+bool isEligibleOperandForConstantSharing(const Instruction *I, unsigned OpIdx) {
+  assert(OpIdx < I->getNumOperands() && "Invalid operand index");
+
+  if (!isEligibleInstrunctionForConstantSharing(I))
+    return false;
+
+  auto Opnd = I->getOperand(OpIdx);
+  if (!isa<Constant>(Opnd))
+    return false;
+
+  if (const auto *CI = dyn_cast<CallBase>(I))
+    return canParameterizeCallOperand(CI, OpIdx);
+
+  return true;
+}
+
+namespace {
+
+/// MergeFuncIgnoringConst finds functions which only differ by constants in
+/// certain instructions, e.g. resulting from specialized functions of layout
+/// compatible types.
+/// Such functions are merged by replacing the differing constants by a
+/// parameter. The original functions are replaced by thunks which call the
+/// merged function with the specific argument constants.
+///
+class MergeFuncIgnoringConstImpl { // : public ModulePass {
+public:
+  MergeFuncIgnoringConstImpl() : FnTree(FunctionNodeCmp(&GlobalNumbers)) {}
+
+  MergeFuncIgnoringConstImpl(bool ptrAuthEnabled, unsigned ptrAuthKey)
+      : FnTree(FunctionNodeCmp(&GlobalNumbers)), ptrAuthOptionsSet(true),
+        ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey) {}
+
+  bool runImpl(Module &M);
+
+private:
+  struct FunctionEntry;
+
+  /// Describes the set of functions which are considered as "equivalent" (i.e.
+  /// only differing by some constants).
+  struct EquivalenceClass {
+    /// The single-linked list of all functions which are a member of this
+    /// equivalence class.
+    FunctionEntry *First;
+
+    /// A very cheap hash, used to early exit if functions do not match.
+    llvm::IRHash Hash;
+
+  public:
+    // Note the hash is recalculated potentially multiple times, but it is
+    // cheap.
+    EquivalenceClass(FunctionEntry *First)
+        : First(First), Hash(StructuralHash(*First->F)) {
+      assert(!First->Next);
+    }
+  };
+
+  /// The function comparison operator is provided here so that FunctionNodes do
+  /// not need to become larger with another pointer.
+  class FunctionNodeCmp {
+    GlobalNumberState *GlobalNumbers;
+
+  public:
+    FunctionNodeCmp(GlobalNumberState *GN) : GlobalNumbers(GN) {}
+    bool operator()(const EquivalenceClass &LHS,
+                    const EquivalenceClass &RHS) const {
+      // Order first by hashes, then full function comparison.
+      if (LHS.Hash != RHS.Hash)
+        return LHS.Hash < RHS.Hash;
+      FunctionComparatorIgnoringConst FCmp(LHS.First->F, RHS.First->F,
+                                           GlobalNumbers);
+      return FCmp.compareIgnoringConsts() == -1;
+    }
+  };
+  using FnTreeType = std::set<EquivalenceClass, FunctionNodeCmp>;
+
+  ///
+  struct FunctionEntry {
+    FunctionEntry(Function *F, FnTreeType::iterator I)
+        : F(F), Next(nullptr), numUnhandledCallees(0), TreeIter(I),
+          isMerged(false) {}
+
+    /// Back-link to the function.
+    AssertingVH<Function> F;
+
+    /// The next function in its equivalence class.
+    FunctionEntry *Next;
+
+    /// The number of not-yet merged callees. Used to process the merging in
+    /// bottom-up call order.
+    /// This is only valid in the first entry of an equivalence class. The
+    /// counts of all functions in an equivalence class are accumulated in the
+    /// first entry.
+    int numUnhandledCallees;
+
+    /// The iterator of the function's equivalence class in the FnTree.
+    /// It's FnTree.end() if the function is not in an equivalence class.
+    FnTreeType::iterator TreeIter;
+
+    /// True if this function is already a thunk, calling the merged function.
+    bool isMerged;
+  };
+
+  /// Describes an operator of a specific instruction.
+  struct OpLocation {
+    Instruction *I;
+    unsigned OpIndex;
+  };
+
+  /// Information for a function. Used during merging.
+  struct FunctionInfo {
+
+    FunctionInfo(Function *F)
+        : F(F), CurrentInst(nullptr), NumParamsNeeded(0) {}
+
+    void init() {
+      CurrentInst = &*F->begin()->begin();
+      NumParamsNeeded = 0;
+    }
+
+    /// Advances the current instruction to the next instruction.
+    void nextInst() {
+      assert(CurrentInst);
+      if (CurrentInst->isTerminator()) {
+        auto BlockIter = std::next(CurrentInst->getParent()->getIterator());
+        if (BlockIter == F->end()) {
+          CurrentInst = nullptr;
+          return;
+        }
+        CurrentInst = &*BlockIter->begin();
+        return;
+      }
+      CurrentInst = &*std::next(CurrentInst->getIterator());
+    }
+
+    /// Returns true if the operand \p OpIdx of the current instruction is the
+    /// callee of a call, which needs to be signed if passed as a parameter.
+    bool needsPointerSigning(unsigned OpIdx) const {
+      if (auto *CI = dyn_cast<CallInst>(CurrentInst))
+        return isCalleeOperand(CI, OpIdx);
+      return false;
+    }
+
+    Function *F;
+
+    /// The current instruction while iterating over all instructions.
+    Instruction *CurrentInst;
+
+    /// Roughly the number of parameters needed if this function would be
+    /// merged with the first function of the equivalence class.
+    int NumParamsNeeded;
+  };
+
+  using FunctionInfos = SmallVector<FunctionInfo, 8>;
+
+  /// Describes a parameter which we create to parameterize the merged function.
+  struct ParamInfo {
+    /// The value of the parameter for all the functions in the equivalence
+    /// class.
+    SmallVector<Constant *, 8> Values;
+
+    /// All uses of the parameter in the merged function.
+    SmallVector<OpLocation, 16> Uses;
+
+    /// The discriminator for pointer signing.
+    /// Only not null if needsPointerSigning is true.
+    ConstantInt *discriminator = nullptr;
+
+    /// True if the value is a callee function, which needs to be signed if
+    /// passed as a parameter.
+    bool needsPointerSigning = false;
+
+    /// Checks if this parameter can be used to describe an operand in all
+    /// functions of the equivalence class. Returns true if all values match
+    /// the specific instruction operands in all functions.
+    bool matches(const FunctionInfos &FInfos, unsigned OpIdx,
+                 bool ptrAuthEnabled) const {
+      unsigned NumFuncs = FInfos.size();
+      assert(Values.size() == NumFuncs);
+      if (ptrAuthEnabled &&
+          needsPointerSigning != FInfos[0].needsPointerSigning(OpIdx)) {
+        return false;
+      }
+      for (unsigned Idx = 0; Idx < NumFuncs; ++Idx) {
+        const FunctionInfo &FI = FInfos[Idx];
+        Constant *C = cast<Constant>(FI.CurrentInst->getOperand(OpIdx));
+        if (Values[Idx] != C)
+          return false;
+      }
+      return true;
+    }
+
+    /// Computes the discriminator for pointer signing.
+    void computeDiscriminator(LLVMContext &Context) {
+      assert(needsPointerSigning);
+      assert(!discriminator);
+
+      /// Get a hash from the concatenated function names.
+      /// The hash is deterministic, because the order of values depends on the
+      /// order of functions in the module, which is itself deterministic.
+      /// Note that the hash is not part of the ABI, because it's purly used
+      /// for pointer authentication between a module-private caller-callee
+      /// pair.
+      std::string concatenatedCalleeNames;
+      for (Constant *value : Values) {
+        if (auto *GO = dyn_cast<GlobalObject>(value))
+          concatenatedCalleeNames += GO->getName();
+      }
+      uint64_t rawHash = stable_hash_combine_string(concatenatedCalleeNames);
+      IntegerType *discrTy = Type::getInt64Ty(Context);
+      discriminator = ConstantInt::get(discrTy, (rawHash % 0xFFFF) + 1);
+    }
+  };
+
+  using ParamInfos = SmallVector<ParamInfo, 16>;
+
+  Module *module = nullptr;
+  ModuleSummaryIndex *ExportSummary;
+  const ModuleSummaryIndex *ImportSummary;
+
+  GlobalNumberState GlobalNumbers;
+
+  /// A work queue of functions that may have been modified and should be
+  /// analyzed again.
+  std::vector<WeakTrackingVH> Deferred;
+
+  /// The set of all distinct functions. Use the insert() and remove() methods
+  /// to modify it. The map allows efficient lookup and deferring of Functions.
+  FnTreeType FnTree;
+
+  ValueMap<Function *, FunctionEntry *> FuncEntries;
+
+  // Maps a function-pointer / discriminator pair to a corresponding global in
+  // the llvm.ptrauth section.
+  // This map is used as a cache to not create ptrauth globals twice.
+  DenseMap<std::pair<Constant *, ConstantInt *>, Constant *> ptrAuthGlobals;
+
+  /// If true, ptrAuthEnabled and ptrAuthKey are valid.
+  bool ptrAuthOptionsSet = false;
+
+  /// True if the architecture has pointer authentication enabled.
+  bool ptrAuthEnabled = false;
+
+  /// The key for pointer authentication.
+  unsigned ptrAuthKey = 0;
+
+  FunctionEntry *getEntry(Function *F) const { return FuncEntries.lookup(F); }
+
+  bool isInEquivalenceClass(FunctionEntry *FE) const {
+    if (FE->TreeIter != FnTree.end()) {
+      return true;
+    }
+    assert(!FE->Next);
+    assert(FE->numUnhandledCallees == 0);
+    return false;
+  }
+
+  /// Checks the rules of order relation introduced among functions set.
+  /// Returns true, if sanity check has been passed, and false if failed.
+  bool doSanityCheck(std::vector<WeakTrackingVH> &Worklist);
+
+  /// Updates the numUnhandledCallees of all user functions of the equivalence
+  /// class containing \p FE by \p Delta.
+  void updateUnhandledCalleeCount(FunctionEntry *FE, int Delta);
+
+  bool tryMergeEquivalenceClass(FunctionEntry *FirstInClass);
+
+  FunctionInfo removeFuncWithMostParams(FunctionInfos &FInfos);
+
+  bool deriveParams(ParamInfos &Params, FunctionInfos &FInfos,
+                    unsigned maxParams);
+
+  bool numOperandsDiffer(FunctionInfos &FInfos);
+
+  bool constsDiffer(const FunctionInfos &FInfos, unsigned OpIdx);
+
+  bool tryMapToParameter(FunctionInfos &FInfos, unsigned OpIdx,
+                         ParamInfos &Params, unsigned maxParams);
+
+  void replaceCallWithAddedPtrAuth(CallInst *origCall, Value *newCallee,
+                                   ConstantInt *discriminator);
+
+  void mergeWithParams(const FunctionInfos &FInfos, ParamInfos &Params);
+  static void dumpMergeInfo(const FunctionInfos &FInfos, unsigned);
+
+  void removeEquivalenceClassFromTree(FunctionEntry *FE);
+
+  void writeThunk(Function *ToFunc, Function *Thunk, const ParamInfos &Params,
+                  unsigned FuncIdx);
+
+  bool isPtrAuthEnabled() const {
+    // TODO: fix pointer authentication
+    // assert(ptrAuthOptionsSet);
+    return ptrAuthEnabled;
+  }
+
+  ConstantInt *getPtrAuthKey() {
+    // TODO: fix pointer authentication
+    // assert(isPtrAuthEnabled());
+    return ConstantInt::get(Type::getInt32Ty(module->getContext()), ptrAuthKey);
+  }
+
+  /// Returns the value of function \p FuncIdx, and signes it if required.
+  Constant *getSignedValue(const ParamInfo &PI, unsigned FuncIdx) {
+    Constant *value = PI.Values[FuncIdx];
+    if (!PI.needsPointerSigning)
+      return value;
+
+    auto lookupKey = std::make_pair(value, PI.discriminator);
+    Constant *&ptrAuthGlobal = ptrAuthGlobals[lookupKey];
+    if (!ptrAuthGlobal) {
+#if 0
+      ptrAuthGlobal = GlobalPtrAuthInfo::create(
+          *module, value, getPtrAuthKey(),
+          ConstantInt::get(PI.discriminator->getType(), 0), PI.discriminator);
+#endif
+    }
+    return ptrAuthGlobal;
+  }
+
+  /// Replace all direct calls of Old with calls of New. Will bitcast New if
+  /// necessary to make types match.
+  bool replaceDirectCallers(Function *Old, Function *New,
+                            const ParamInfos &Params, unsigned FuncIdx);
+};
+
+#if 0
+class MergeFuncIgnoringConst : public ModulePass {
+public:
+  static char ID;
+  /// True if the architecture has pointer authentication enabled.
+  bool ptrAuthEnabled = false;
+
+  /// The key for pointer authentication.
+  unsigned ptrAuthKey = 0;
+  ModuleSummaryIndex *ExportSummary;
+  const ModuleSummaryIndex *ImportSummary;
+
+  MergeFuncIgnoringConst() : ModulePass(ID) {
+    initializeMergeFuncIgnoringConstPass(*llvm::PassRegistry::getPassRegistry());
+  }
+  MergeFuncIgnoringConst(bool ptrAuthEnabled, unsigned ptrAuthKey)
+      : ModulePass(ID), ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey) {
+    initializeMergeFuncIgnoringConstPass(*llvm::PassRegistry::getPassRegistry());
+  }
+  bool runOnModule(Module &M) override;
+};
+#endif
+
+} // end anonymous namespace
+
+#if 0
+char MergeFuncIgnoringConst::ID = 0;
+INITIALIZE_PASS_BEGIN(MergeFuncIgnoringConst, "merge-func-ignoring-const",
+                      "merge function pass ignoring const", false, false)
+INITIALIZE_PASS_END(MergeFuncIgnoringConst, "merge-func-ignoring-const",
+                    "merge function pass ignoring const", false, false)
+#endif
+bool MergeFuncIgnoringConstImpl::doSanityCheck(
+    std::vector<WeakTrackingVH> &Worklist) {
+  if (const unsigned Max = NumFunctionsIgnoringConstForSanityCheck) {
+    unsigned TripleNumber = 0;
+    bool Valid = true;
+
+    dbgs() << "MERGEFUNC-SANITY: Started for first " << Max << " functions.\n";
+
+    unsigned i = 0;
+    for (std::vector<WeakTrackingVH>::iterator I = Worklist.begin(),
+                                               E = Worklist.end();
+         I != E && i < Max; ++I, ++i) {
+      unsigned j = i;
+      for (std::vector<WeakTrackingVH>::iterator J = I; J != E && j < Max;
+           ++J, ++j) {
+        Function *F1 = cast<Function>(*I);
+        Function *F2 = cast<Function>(*J);
+        int Res1 = FunctionComparatorIgnoringConst(F1, F2, &GlobalNumbers)
+                       .compareIgnoringConsts();
+        int Res2 = FunctionComparatorIgnoringConst(F2, F1, &GlobalNumbers)
+                       .compareIgnoringConsts();
+
+        // If F1 <= F2, then F2 >= F1, otherwise report failure.
+        if (Res1 != -Res2) {
+          dbgs() << "MERGEFUNC-SANITY: Non-symmetric; triple: " << TripleNumber
+                 << "\n";
+          LLVM_DEBUG(F1->dump());
+          LLVM_DEBUG(F2->dump());
+          Valid = false;
+        }
+
+        if (Res1 == 0)
+          continue;
+
+        unsigned k = j;
+        for (std::vector<WeakTrackingVH>::iterator K = J; K != E && k < Max;
+             ++k, ++K, ++TripleNumber) {
+          if (K == J)
+            continue;
+
+          Function *F3 = cast<Function>(*K);
+          int Res3 = FunctionComparatorIgnoringConst(F1, F3, &GlobalNumbers)
+                         .compareIgnoringConsts();
+          int Res4 = FunctionComparatorIgnoringConst(F2, F3, &GlobalNumbers)
+                         .compareIgnoringConsts();
+
+          bool Transitive = true;
+
+          if (Res1 != 0 && Res1 == Res4) {
+            // F1 > F2, F2 > F3 => F1 > F3
+            Transitive = Res3 == Res1;
+          } else if (Res3 != 0 && Res3 == -Res4) {
+            // F1 > F3, F3 > F2 => F1 > F2
+            Transitive = Res3 == Res1;
+          } else if (Res4 != 0 && -Res3 == Res4) {
+            // F2 > F3, F3 > F1 => F2 > F1
+            Transitive = Res4 == -Res1;
+          }
+
+          if (!Transitive) {
+            dbgs() << "MERGEFUNC-SANITY: Non-transitive; triple: "
+                   << TripleNumber << "\n";
+            dbgs() << "Res1, Res3, Res4: " << Res1 << ", " << Res3 << ", "
+                   << Res4 << "\n";
+            LLVM_DEBUG(F1->dump());
+            LLVM_DEBUG(F2->dump());
+            LLVM_DEBUG(F3->dump());
+            Valid = false;
+          }
+        }
+      }
+    }
+
+    dbgs() << "MERGEFUNC-SANITY: " << (Valid ? "Passed." : "Failed.") << "\n";
+    return Valid;
+  }
+  return true;
+}
+
+/// Returns true if functions containing calls to \p F may be merged together.
+static bool mayMergeCallsToFunction(Function &F) {
+  StringRef Name = F.getName();
+
+  // Calls to dtrace probes must generate unique patchpoints.
+  if (Name.startswith("__dtrace"))
+    return false;
+
+  return true;
+}
+
+/// Returns the benefit, which is approximately the size of the function.
+/// Return 0, if the function should not be merged.
+static unsigned getBenefit(Function *F) {
+  unsigned Benefit = 0;
+
+  // We don't want to merge very small functions, because the overhead of
+  // adding creating thunks and/or adding parameters to the call sites
+  // outweighs the benefit.
+  for (BasicBlock &BB : *F) {
+    for (Instruction &I : BB) {
+      if (CallBase *CB = dyn_cast<CallBase>(&I)) {
+        Function *Callee = CB->getCalledFunction();
+        if (Callee && !mayMergeCallsToFunction(*Callee))
+          return 0;
+        if (!Callee || !Callee->isIntrinsic()) {
+          Benefit += 5;
+          continue;
+        }
+      }
+      Benefit += 1;
+    }
+  }
+  return Benefit;
+}
+
+/// Returns true if function \p F is eligible for merging.
+bool isEligibleFunction(Function *F) {
+  if (F->isDeclaration())
+    return false;
+
+  if (F->hasFnAttribute(llvm::Attribute::NoMerge))
+    return false;
+
+  if (F->hasAvailableExternallyLinkage()) {
+    return false;
+  }
+
+  if (F->getFunctionType()->isVarArg()) {
+    return false;
+  }
+
+  // Check against blocklist.
+  if (!MergeBlockRegexFilters.empty()) {
+    StringRef FuncName = F->getName();
+    for (const auto &tRegex : MergeBlockRegexFilters)
+      if (Regex(tRegex).match(FuncName)) {
+        return false;
+      }
+  }
+  // Check against allowlist
+  if (!MergeAllowRegexFilters.empty()) {
+    StringRef FuncName = F->getName();
+    bool found = false;
+    for (const auto &tRegex : MergeAllowRegexFilters)
+      if (Regex(tRegex).match(FuncName)) {
+        found = true;
+        break;
+      }
+    if (!found)
+      return false;
+  }
+
+  if (F->getCallingConv() == CallingConv::SwiftTail)
+    return false;
+
+  // if function contains callsites with musttail, if we merge
+  // it, the merged function will have the musttail callsite, but
+  // the number of parameters can change, thus the parameter count
+  // of the callsite will mismatch with the function itself.
+  if (IgnoreMusttailFunction) {
+    for (const BasicBlock &BB : *F) {
+      for (const Instruction &I : BB) {
+        const auto *CB = dyn_cast<CallBase>(&I);
+        if (CB && CB->isMustTailCall())
+          return false;
+      }
+    }
+  }
+
+  unsigned Benefit = getBenefit(F);
+  if (Benefit < IgnoringConstMergeThreshold) {
+    return false;
+  }
+
+  return true;
+}
+
+static bool runInternal(Module &M) {
+  return MergeFuncIgnoringConstImpl().runImpl(M);
+}
+
+// bool MergeFuncIgnoringConst::runOnModule(Module &M) { return runInternal(M);
+// }
+
+bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
+  if (IgnoringConstMergeThreshold == 0)
+    return false;
+
+  module = &M;
+
+#if 0
+  // TODO: fix pointer authentication
+  if (!ptrAuthOptionsSet) {
+    // If invoked from IRGen in the compiler, those options are already set.
+    // If invoked from swift-llvm-opt, derive the options from the target triple.
+    Triple triple(M.getTargetTriple());
+    ptrAuthEnabled = (triple.getSubArch() == Triple::AArch64SubArch_arm64e);
+    ptrAuthKey = (unsigned)clang::PointerAuthSchema::ARM8_3Key::ASIA;
+    ptrAuthOptionsSet = true;
+  }
+#endif
+
+  bool Changed = false;
+
+  // All functions in the module, ordered by hash. Functions with a unique
+  // hash value are easily eliminated.
+  std::vector<std::pair<llvm::IRHash, Function *>> HashedFuncs;
+
+  for (Function &Func : M) {
+    if (isEligibleFunction(&Func)) {
+      HashedFuncs.push_back({StructuralHash(Func), &Func});
+    }
+  }
+
+  std::stable_sort(HashedFuncs.begin(), HashedFuncs.end(),
+                   [](const std::pair<llvm::IRHash, Function *> &a,
+                      const std::pair<llvm::IRHash, Function *> &b) {
+                     return a.first < b.first;
+                   });
+
+  std::vector<FunctionEntry> FuncEntryStorage;
+  FuncEntryStorage.reserve(HashedFuncs.size());
+
+  auto S = HashedFuncs.begin();
+  for (auto I = HashedFuncs.begin(), IE = HashedFuncs.end(); I != IE; ++I) {
+
+    Function *F = I->second;
+    FuncEntryStorage.push_back(FunctionEntry(F, FnTree.end()));
+    FunctionEntry &FE = FuncEntryStorage.back();
+    FuncEntries[F] = &FE;
+
+    // If the hash value matches the previous value or the next one, we must
+    // consider merging it. Otherwise it is dropped and never considered again.
+    if ((I != S && std::prev(I)->first == I->first) ||
+        (std::next(I) != IE && std::next(I)->first == I->first)) {
+      Deferred.push_back(WeakTrackingVH(F));
+    }
+  }
+
+  do {
+    std::vector<WeakTrackingVH> Worklist;
+    Deferred.swap(Worklist);
+
+    LLVM_DEBUG(dbgs() << "======\nbuild tree: worklist-size=" << Worklist.size()
+                      << '\n');
+    LLVM_DEBUG(doSanityCheck(Worklist));
+
+    SmallVector<FunctionEntry *, 8> FuncsToMerge;
+
+    // Insert all candidates into the Worklist.
+    for (WeakTrackingVH &I : Worklist) {
+      if (!I)
+        continue;
+      Function *F = cast<Function>(I);
+      FunctionEntry *FE = getEntry(F);
+      assert(!isInEquivalenceClass(FE));
+
+      std::pair<FnTreeType::iterator, bool> Result = FnTree.insert(FE);
+
+      FE->TreeIter = Result.first;
+      const EquivalenceClass &Eq = *Result.first;
+
+      if (Result.second) {
+        assert(Eq.First == FE);
+        LLVM_DEBUG(dbgs() << "  new in tree: " << F->getName() << '\n');
+      } else {
+        assert(Eq.First != FE);
+        LLVM_DEBUG(dbgs() << "  add to existing: " << F->getName() << '\n');
+        // Add the function to the existing equivalence class.
+        FE->Next = Eq.First->Next;
+        Eq.First->Next = FE;
+        // Schedule for merging if the function's equivalence class reaches the
+        // size of 2.
+        if (!FE->Next)
+          FuncsToMerge.push_back(Eq.First);
+      }
+    }
+    LLVM_DEBUG(dbgs() << "merge functions: tree-size=" << FnTree.size()
+                      << '\n');
+
+    // Figure out the leaf functions. We want to do the merging in bottom-up
+    // call order. This ensures that we don't parameterize on callee function
+    // names if we don't have to (because the callee may be merged).
+    // Note that "leaf functions" refer to the sub-call-graph of functions which
+    // are in the FnTree.
+    for (FunctionEntry *ToMerge : FuncsToMerge) {
+      assert(isInEquivalenceClass(ToMerge));
+      updateUnhandledCalleeCount(ToMerge, 1);
+    }
+
+    // Check if there are any leaf functions at all.
+    bool LeafFound = false;
+    for (FunctionEntry *ToMerge : FuncsToMerge) {
+      if (ToMerge->numUnhandledCallees == 0)
+        LeafFound = true;
+    }
+    for (FunctionEntry *ToMerge : FuncsToMerge) {
+      if (isInEquivalenceClass(ToMerge)) {
+        // Only merge leaf functions (or all functions if all functions are in
+        // a call cycle).
+        if (ToMerge->numUnhandledCallees == 0 || !LeafFound) {
+          updateUnhandledCalleeCount(ToMerge, -1);
+          Changed |= tryMergeEquivalenceClass(ToMerge);
+        } else {
+          // Non-leaf functions (i.e. functions in a call cycle) may become
+          // leaf functions in the next iteration.
+          removeEquivalenceClassFromTree(ToMerge);
+        }
+      }
+    }
+  } while (!Deferred.empty());
+
+  FnTree.clear();
+  GlobalNumbers.clear();
+  FuncEntries.clear();
+  ptrAuthGlobals.clear();
+
+  return Changed;
+}
+
+void MergeFuncIgnoringConstImpl::updateUnhandledCalleeCount(FunctionEntry *FE,
+                                                            int Delta) {
+  // Iterate over all functions of FE's equivalence class.
+  do {
+    for (Use &U : FE->F->uses()) {
+      if (auto *I = dyn_cast<Instruction>(U.getUser())) {
+        FunctionEntry *CallerFE = getEntry(I->getFunction());
+        if (CallerFE && CallerFE->TreeIter != FnTree.end()) {
+          // Accumulate the count in the first entry of the equivalence class.
+          FunctionEntry *Head = CallerFE->TreeIter->First;
+          Head->numUnhandledCallees += Delta;
+        }
+      }
+    }
+    FE = FE->Next;
+  } while (FE);
+}
+
+bool MergeFuncIgnoringConstImpl::tryMergeEquivalenceClass(
+    FunctionEntry *FirstInClass) {
+  // Build the FInfos vector from all functions in the equivalence class.
+  FunctionInfos FInfos;
+  FunctionEntry *FE = FirstInClass;
+  do {
+    FInfos.push_back(FunctionInfo(FE->F));
+    FE->isMerged = true;
+    FE = FE->Next;
+  } while (FE);
+  assert(FInfos.size() >= 2);
+
+  // Merged or not: in any case we remove the equivalence class from the FnTree.
+  removeEquivalenceClassFromTree(FirstInClass);
+
+  // Contains functions which differ too much from the first function (i.e.
+  // would need too many parameters).
+  FunctionInfos Removed;
+
+  bool Changed = false;
+  int Try = 0;
+
+  unsigned Benefit = getBenefit(FirstInClass->F);
+
+  // The bigger the function, the more parameters are allowed.
+  unsigned maxParams = std::max(4u, Benefit / 100);
+
+  // We need multiple tries if there are some functions in FInfos which differ
+  // too much from the first function in FInfos. But we limit the number of
+  // tries to a small number, because this is quadratic.
+  while (FInfos.size() >= 2 && Try++ < 4) {
+    ParamInfos Params;
+    bool Merged = deriveParams(Params, FInfos, maxParams);
+    if (Merged) {
+      mergeWithParams(FInfos, Params);
+      Changed = true;
+    } else {
+      // We ran out of parameters. Remove the function from the set which
+      // differs most from the first function.
+      Removed.push_back(removeFuncWithMostParams(FInfos));
+    }
+    if (Merged || FInfos.size() < 2) {
+      // Try again with the functions which were removed from the original set.
+      FInfos.swap(Removed);
+      Removed.clear();
+    }
+  }
+  return Changed;
+}
+
+/// Remove the function from \p FInfos which needs the most parameters. Add the
+/// removed function to
+MergeFuncIgnoringConstImpl::FunctionInfo
+MergeFuncIgnoringConstImpl::removeFuncWithMostParams(FunctionInfos &FInfos) {
+  FunctionInfos::iterator MaxIter = FInfos.end();
+  for (auto Iter = FInfos.begin(), End = FInfos.end(); Iter != End; ++Iter) {
+    if (MaxIter == FInfos.end() ||
+        Iter->NumParamsNeeded > MaxIter->NumParamsNeeded) {
+      MaxIter = Iter;
+    }
+  }
+  FunctionInfo Removed = *MaxIter;
+  FInfos.erase(MaxIter);
+  return Removed;
+}
+
+/// Finds the set of parameters which are required to merge the functions in
+/// \p FInfos.
+/// Returns true on success, i.e. the functions in \p FInfos can be merged with
+/// the parameters returned in \p Params.
+bool MergeFuncIgnoringConstImpl::deriveParams(ParamInfos &Params,
+                                              FunctionInfos &FInfos,
+                                              unsigned maxParams) {
+  for (FunctionInfo &FI : FInfos)
+    FI.init();
+
+  FunctionInfo &FirstFI = FInfos.front();
+
+  // Iterate over all instructions synchronously in all functions.
+  do {
+    if (isEligibleInstrunctionForConstantSharing(FirstFI.CurrentInst)) {
+
+      // Here we handle a rare corner case which needs to be explained:
+      // Usually the number of operands match, because otherwise the functions
+      // in FInfos would not be in the same equivalence class. There is only one
+      // exception to that: If the current instruction is a call to a function,
+      // which was merged in the previous iteration (in
+      // tryMergeEquivalenceClass) then the call could be replaced and has more
+      // arguments than the original call.
+      if (numOperandsDiffer(FInfos)) {
+        assert(isa<CallInst>(FirstFI.CurrentInst) &&
+               "only calls are expected to differ in number of operands");
+        return false;
+      }
+
+      for (unsigned OpIdx = 0, NumOps = FirstFI.CurrentInst->getNumOperands();
+           OpIdx != NumOps; ++OpIdx) {
+
+        if (constsDiffer(FInfos, OpIdx)) {
+          // This instruction has operands which differ in at least some
+          // functions. So we need to parameterize it.
+          if (!tryMapToParameter(FInfos, OpIdx, Params, maxParams)) {
+            // We ran out of parameters.
+            return false;
+          }
+        }
+      }
+    }
+    // Go to the next instruction in all functions.
+    for (FunctionInfo &FI : FInfos)
+      FI.nextInst();
+  } while (FirstFI.CurrentInst);
+
+  return true;
+}
+
+/// Returns true if the number of operands of the current instruction differs.
+bool MergeFuncIgnoringConstImpl::numOperandsDiffer(FunctionInfos &FInfos) {
+  unsigned numOps = FInfos[0].CurrentInst->getNumOperands();
+  for (const FunctionInfo &FI : ArrayRef<FunctionInfo>(FInfos).drop_front(1)) {
+    if (FI.CurrentInst->getNumOperands() != numOps)
+      return true;
+  }
+  return false;
+}
+
+/// Returns true if the \p OpIdx's constant operand in the current instruction
+/// does differ in any of the functions in \p FInfos.
+bool MergeFuncIgnoringConstImpl::constsDiffer(const FunctionInfos &FInfos,
+                                              unsigned OpIdx) {
+  Constant *CommonConst = nullptr;
+
+  for (const FunctionInfo &FI : FInfos) {
+    Value *Op = FI.CurrentInst->getOperand(OpIdx);
+    if (auto *C = dyn_cast<Constant>(Op)) {
+      if (!CommonConst) {
+        CommonConst = C;
+      } else if (EnableMergeFunc2 && isa<ConstantPointerNull>(CommonConst) &&
+                 isa<ConstantPointerNull>(C)) {
+        // if both are null pointer, and if they are different constants
+        // due to type, still treat them as the same.
+      } else if (C != CommonConst) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/// Create a new parameter for differing operands or try to reuse an existing
+/// parameter.
+/// Returns true if a parameter could be created or found without exceeding the
+/// maximum number of parameters.
+bool MergeFuncIgnoringConstImpl::tryMapToParameter(FunctionInfos &FInfos,
+                                                   unsigned OpIdx,
+                                                   ParamInfos &Params,
+                                                   unsigned maxParams) {
+  ParamInfo *Matching = nullptr;
+  // Try to find an existing parameter which exactly matches the differing
+  // operands of the current instruction.
+  for (ParamInfo &PI : Params) {
+    if (PI.matches(FInfos, OpIdx, isPtrAuthEnabled())) {
+      Matching = &PI;
+      break;
+    }
+  }
+  if (!Matching) {
+    // We need a new parameter.
+    // Check if we are within the limit.
+    if (Params.size() >= maxParams)
+      return false;
+
+    Params.resize(Params.size() + 1);
+    Matching = &Params.back();
+    // Store the constant values into the new parameter.
+    Constant *FirstC = cast<Constant>(FInfos[0].CurrentInst->getOperand(OpIdx));
+    for (FunctionInfo &FI : FInfos) {
+      Constant *C = cast<Constant>(FI.CurrentInst->getOperand(OpIdx));
+      Matching->Values.push_back(C);
+      if (C != FirstC)
+        FI.NumParamsNeeded += 1;
+    }
+    if (isPtrAuthEnabled())
+      Matching->needsPointerSigning = FInfos[0].needsPointerSigning(OpIdx);
+  }
+  /// Remember where the parameter is needed when we build our merged function.
+  Matching->Uses.push_back({FInfos[0].CurrentInst, OpIdx});
+  return true;
+}
+
+/// Copy \p origCall with a \p newCalle and add a ptrauth bundle with \p
+/// discriminator.
+void MergeFuncIgnoringConstImpl::replaceCallWithAddedPtrAuth(
+    CallInst *origCall, Value *newCallee, ConstantInt *discriminator) {
+  SmallVector<llvm::OperandBundleDef, 4> bundles;
+  origCall->getOperandBundlesAsDefs(bundles);
+  ConstantInt *key = getPtrAuthKey();
+  llvm::Value *bundleArgs[] = {key, discriminator};
+  bundles.emplace_back("ptrauth", bundleArgs);
+
+  SmallVector<llvm::Value *, 4> copiedArgs;
+  for (Value *op : origCall->args()) {
+    copiedArgs.push_back(op);
+  }
+
+  auto *newCall =
+      CallInst::Create(origCall->getFunctionType(), newCallee, copiedArgs,
+                       bundles, origCall->getName(), origCall);
+  newCall->setAttributes(origCall->getAttributes());
+  newCall->setTailCallKind(origCall->getTailCallKind());
+  newCall->setCallingConv(origCall->getCallingConv());
+  origCall->replaceAllUsesWith(newCall);
+  origCall->eraseFromParent();
+}
+
+void MergeFuncIgnoringConstImpl::dumpMergeInfo(const FunctionInfos &FInfos,
+                                               unsigned paramSize) {
+  std::set<llvm::IRHash> oHashes;
+  std::vector<std::string> funcLocs;
+  Function *OrigFunc = nullptr;
+  for (const auto &FInfo : FInfos) {
+    OrigFunc = FInfo.F;
+
+    llvm::IRHash origHash = StructuralHash(*OrigFunc);
+    oHashes.insert(origHash);
+
+    // Print debug location.
+    std::string Result;
+    raw_string_ostream DbgLocOS(Result);
+    if (DISubprogram *DIS = OrigFunc->getSubprogram()) {
+      DebugLoc FuncDbgLoc =
+          DILocation::get(DIS->getContext(), DIS->getScopeLine(), 0, DIS);
+      FuncDbgLoc.print(DbgLocOS);
+      DbgLocOS.flush();
+    }
+    std::string singleLine =
+        "# functionLoc " +
+        std::to_string(GlobalValue::getGUID(OrigFunc->getName())) + " " +
+        Result + " " + std::string(OrigFunc->getName()) + "\n";
+    funcLocs.push_back(singleLine);
+  }
+}
+
+/// Merge all functions in \p FInfos by creating thunks which call the single
+/// merged function with additional parameters.
+void MergeFuncIgnoringConstImpl::mergeWithParams(const FunctionInfos &FInfos,
+                                                 ParamInfos &Params) {
+  // We reuse the body of the first function for the new merged function.
+  Function *FirstF = FInfos.front().F;
+
+  // Build the type for the merged function. This will be the type of the
+  // original function (FirstF) but with the additional parameter which are
+  // needed to parameterize the merged function.
+  FunctionType *OrigTy = FirstF->getFunctionType();
+  SmallVector<Type *, 8> ParamTypes(OrigTy->param_begin(), OrigTy->param_end());
+
+  for (const ParamInfo &PI : Params) {
+    ParamTypes.push_back(PI.Values[0]->getType());
+  }
+
+  FunctionType *funcType =
+      FunctionType::get(OrigTy->getReturnType(), ParamTypes, false);
+
+  // Create the new function.
+  Function *NewFunction = Function::Create(funcType, FirstF->getLinkage(),
+                                           FirstF->getName() + ".Tm");
+  if (auto *SP = FirstF->getSubprogram())
+    NewFunction->setSubprogram(SP);
+  NewFunction->copyAttributesFrom(FirstF);
+  // NOTE: this function is not externally available, do ensure that we reset
+  // the DLL storage
+  NewFunction->setDLLStorageClass(GlobalValue::DefaultStorageClass);
+  if (UseLinkOnceODRLinkageMerging)
+    NewFunction->setLinkage(GlobalValue::LinkOnceODRLinkage);
+  else
+    NewFunction->setLinkage(GlobalValue::InternalLinkage);
+  if (NoInlineForMergedFunction)
+    NewFunction->addFnAttr(Attribute::NoInline);
+
+  // Insert the new function after the last function in the equivalence class.
+  FirstF->getParent()->getFunctionList().insert(
+      std::next(FInfos[1].F->getIterator()), NewFunction);
+
+  LLVM_DEBUG(dbgs() << "  Merge into " << NewFunction->getName() << '\n');
+
+  // Move the body of FirstF into the NewFunction.
+  NewFunction->splice(NewFunction->begin(), FirstF);
+
+  auto NewArgIter = NewFunction->arg_begin();
+  for (Argument &OrigArg : FirstF->args()) {
+    Argument &NewArg = *NewArgIter++;
+    OrigArg.replaceAllUsesWith(&NewArg);
+  }
+  unsigned numOrigArgs = FirstF->arg_size();
+
+  SmallPtrSet<Function *, 8> SelfReferencingFunctions;
+
+  // Replace all differing operands with a parameter.
+  for (unsigned paramIdx = 0; paramIdx < Params.size(); ++paramIdx) {
+    const ParamInfo &PI = Params[paramIdx];
+    Argument *NewArg = NewFunction->getArg(numOrigArgs + paramIdx);
+
+    if (!PI.needsPointerSigning) {
+      for (const OpLocation &OL : PI.Uses) {
+        OL.I->setOperand(OL.OpIndex, NewArg);
+      }
+    }
+    // Collect all functions which are referenced by any parameter.
+    for (Value *V : PI.Values) {
+      if (auto *F = dyn_cast<Function>(V))
+        SelfReferencingFunctions.insert(F);
+    }
+  }
+
+  // Replace all differing operands, which need pointer signing, with a
+  // parameter.
+  // We need to do that after all other parameters, because here we replace
+  // call instructions, which must be live in case it has another constant to
+  // be replaced.
+  for (unsigned paramIdx = 0; paramIdx < Params.size(); ++paramIdx) {
+    ParamInfo &PI = Params[paramIdx];
+    if (PI.needsPointerSigning) {
+      PI.computeDiscriminator(NewFunction->getContext());
+      for (const OpLocation &OL : PI.Uses) {
+        auto *origCall = cast<CallInst>(OL.I);
+        Argument *newCallee = NewFunction->getArg(numOrigArgs + paramIdx);
+        replaceCallWithAddedPtrAuth(origCall, newCallee, PI.discriminator);
+      }
+    }
+  }
+
+  for (unsigned FIdx = 0, NumFuncs = FInfos.size(); FIdx < NumFuncs; ++FIdx) {
+    Function *OrigFunc = FInfos[FIdx].F;
+    // Don't try to replace all callers of functions which are used as
+    // parameters because we must not delete such functions.
+    if (SelfReferencingFunctions.count(OrigFunc) == 0 &&
+        replaceDirectCallers(OrigFunc, NewFunction, Params, FIdx)) {
+      // We could replace all uses (and the function is not externally visible),
+      // so we can delete the original function.
+      auto Iter = FuncEntries.find(OrigFunc);
+      assert(Iter != FuncEntries.end());
+      assert(!isInEquivalenceClass(&*Iter->second));
+      Iter->second->F = nullptr;
+      FuncEntries.erase(Iter);
+      LLVM_DEBUG(dbgs() << "    Erase " << OrigFunc->getName() << '\n');
+      OrigFunc->eraseFromParent();
+    } else {
+      // Otherwise we need a thunk which calls the merged function.
+      writeThunk(NewFunction, OrigFunc, Params, FIdx);
+    }
+    ++NumFunctionsMergedIgnoringConst;
+  }
+}
+
+/// Remove all functions of \p FE's equivalence class from FnTree. Add them to
+/// Deferred so that we'll look at them in the next round.
+void MergeFuncIgnoringConstImpl::removeEquivalenceClassFromTree(
+    FunctionEntry *FE) {
+  if (!isInEquivalenceClass(FE))
+    return;
+
+  FnTreeType::iterator Iter = FE->TreeIter;
+  FunctionEntry *Unlink = Iter->First;
+  Unlink->numUnhandledCallees = 0;
+  while (Unlink) {
+    LLVM_DEBUG(dbgs() << "    remove from tree: " << Unlink->F->getName()
+                      << '\n');
+    if (!Unlink->isMerged)
+      Deferred.emplace_back(Unlink->F);
+    Unlink->TreeIter = FnTree.end();
+    assert(Unlink->numUnhandledCallees == 0);
+    FunctionEntry *NextEntry = Unlink->Next;
+    Unlink->Next = nullptr;
+    Unlink = NextEntry;
+  }
+  FnTree.erase(Iter);
+}
+
+// Helper for writeThunk,
+// Selects proper bitcast operation,
+// but a bit simpler then CastInst::getCastOpcode.
+Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy) {
+  Type *SrcTy = V->getType();
+  if (SrcTy->isStructTy()) {
+    assert(DestTy->isStructTy());
+    assert(SrcTy->getStructNumElements() == DestTy->getStructNumElements());
+    Value *Result = UndefValue::get(DestTy);
+    for (unsigned int I = 0, E = SrcTy->getStructNumElements(); I < E; ++I) {
+      Value *Element =
+          createCast(Builder, Builder.CreateExtractValue(V, makeArrayRef(I)),
+                     DestTy->getStructElementType(I));
+
+      Result = Builder.CreateInsertValue(Result, Element, makeArrayRef(I));
+    }
+    return Result;
+  }
+  assert(!DestTy->isStructTy());
+  if (CastArrayType) {
+    if (auto *SrcAT = dyn_cast<ArrayType>(SrcTy)) {
+      auto *DestAT = dyn_cast<ArrayType>(DestTy);
+      assert(DestAT);
+      assert(SrcAT->getNumElements() == DestAT->getNumElements());
+      Value *Result = UndefValue::get(DestTy);
+      for (unsigned int I = 0, E = SrcAT->getNumElements(); I < E; ++I) {
+        Value *Element =
+            createCast(Builder, Builder.CreateExtractValue(V, makeArrayRef(I)),
+                       DestAT->getElementType());
+
+        Result = Builder.CreateInsertValue(Result, Element, makeArrayRef(I));
+      }
+      return Result;
+    }
+    assert(!DestTy->isArrayTy());
+  }
+  if (SrcTy->isIntegerTy() && DestTy->isPointerTy())
+    return Builder.CreateIntToPtr(V, DestTy);
+  else if (SrcTy->isPointerTy() && DestTy->isIntegerTy())
+    return Builder.CreatePtrToInt(V, DestTy);
+  else
+    return Builder.CreateBitCast(V, DestTy);
+}
+
+/// Replace \p Thunk with a simple tail call to \p ToFunc. Also add parameters
+/// to the call to \p ToFunc, which are defined by the FuncIdx's value in
+/// \p Params.
+void MergeFuncIgnoringConstImpl::writeThunk(Function *ToFunc, Function *Thunk,
+                                            const ParamInfos &Params,
+                                            unsigned FuncIdx) {
+  // Delete the existing content of Thunk.
+  Thunk->dropAllReferences();
+
+  BasicBlock *BB = BasicBlock::Create(Thunk->getContext(), "", Thunk);
+  IRBuilder<> Builder(BB);
+
+  SmallVector<Value *, 16> Args;
+  unsigned ParamIdx = 0;
+  FunctionType *ToFuncTy = ToFunc->getFunctionType();
+
+  // Add arguments which are passed through Thunk.
+  for (Argument &AI : Thunk->args()) {
+    Args.push_back(createCast(Builder, &AI, ToFuncTy->getParamType(ParamIdx)));
+    ++ParamIdx;
+  }
+  // Add new arguments defined by Params.
+  for (const ParamInfo &PI : Params) {
+    assert(ParamIdx < ToFuncTy->getNumParams());
+    Constant *param = getSignedValue(PI, FuncIdx);
+    Args.push_back(
+        createCast(Builder, param, ToFuncTy->getParamType(ParamIdx)));
+    ++ParamIdx;
+  }
+
+  CallInst *CI = Builder.CreateCall(ToFunc, Args);
+  bool isSwiftTailCall = ToFunc->getCallingConv() == CallingConv::SwiftTail &&
+                         Thunk->getCallingConv() == CallingConv::SwiftTail;
+  CI->setTailCallKind(isSwiftTailCall ? llvm::CallInst::TCK_MustTail
+                                      : llvm::CallInst::TCK_Tail);
+  CI->setCallingConv(ToFunc->getCallingConv());
+  CI->setAttributes(ToFunc->getAttributes());
+  if (Thunk->getReturnType()->isVoidTy()) {
+    Builder.CreateRetVoid();
+  } else {
+    Builder.CreateRet(createCast(Builder, CI, Thunk->getReturnType()));
+  }
+
+  LLVM_DEBUG(dbgs() << "    writeThunk: " << Thunk->getName() << '\n');
+  ++NumThunksWrittenIgnoringConst;
+}
+
+/// Replace direct callers of Old with New. Also add parameters to the call to
+/// \p New, which are defined by the FuncIdx's value in \p Params.
+bool MergeFuncIgnoringConstImpl::replaceDirectCallers(Function *Old,
+                                                      Function *New,
+                                                      const ParamInfos &Params,
+                                                      unsigned FuncIdx) {
+  bool AllReplaced = true;
+
+  SmallVector<CallInst *, 8> Callers;
+
+  for (Use &U : Old->uses()) {
+    auto *I = dyn_cast<Instruction>(U.getUser());
+    if (!I) {
+      AllReplaced = false;
+      continue;
+    }
+    FunctionEntry *FE = getEntry(I->getFunction());
+    if (FE)
+      removeEquivalenceClassFromTree(FE);
+
+    auto *CI = dyn_cast<CallInst>(I);
+    if (!CI || CI->getCalledOperand() != Old) {
+      AllReplaced = false;
+      continue;
+    }
+    Callers.push_back(CI);
+  }
+  if (!AllReplaced)
+    return false;
+
+  // When AlwaysCallThunk is true, return false so a thunk will be emitted, also
+  // do not replace callsites.
+  if (AlwaysCallThunk)
+    return false;
+
+  for (CallInst *CI : Callers) {
+    auto &Context = New->getContext();
+    auto NewPAL = New->getAttributes();
+
+    SmallVector<Type *, 8> OldParamTypes;
+    SmallVector<Value *, 16> NewArgs;
+    SmallVector<AttributeSet, 8> NewArgAttrs;
+    IRBuilder<> Builder(CI);
+
+    FunctionType *NewFuncTy = New->getFunctionType();
+    (void)NewFuncTy;
+    unsigned ParamIdx = 0;
+
+    // Add the existing parameters.
+    for (Value *OldArg : CI->args()) {
+      NewArgAttrs.push_back(NewPAL.getParamAttrs(ParamIdx));
+      NewArgs.push_back(OldArg);
+      OldParamTypes.push_back(OldArg->getType());
+      ++ParamIdx;
+    }
+    // Add the new parameters.
+    for (const ParamInfo &PI : Params) {
+      assert(ParamIdx < NewFuncTy->getNumParams());
+      Constant *ArgValue = getSignedValue(PI, FuncIdx);
+      assert(ArgValue != Old && "should not try to replace all callers of self "
+                                "referencing functions");
+      NewArgs.push_back(ArgValue);
+      OldParamTypes.push_back(ArgValue->getType());
+      ++ParamIdx;
+    }
+
+    auto *FType = FunctionType::get(Old->getFunctionType()->getReturnType(),
+                                    OldParamTypes, false);
+    auto *FPtrType = PointerType::get(
+        FType, cast<PointerType>(New->getType())->getAddressSpace());
+
+    Value *Callee = ConstantExpr::getBitCast(New, FPtrType);
+    CallInst *NewCI;
+    if (objcarc::hasAttachedCallOpBundle(CI)) {
+      Value *BundleArgs[] = {*objcarc::getAttachedARCFunction(CI)};
+      OperandBundleDef OB("clang.arc.attachedcall", BundleArgs);
+      NewCI = Builder.CreateCall(FType, Callee, NewArgs, {OB});
+    } else {
+      NewCI = Builder.CreateCall(FType, Callee, NewArgs);
+    }
+    NewCI->setCallingConv(CI->getCallingConv());
+    // Don't transfer attributes from the function to the callee. Function
+    // attributes typically aren't relevant to the calling convention or ABI.
+    NewCI->setAttributes(AttributeList::get(Context, /*FnAttrs=*/AttributeSet(),
+                                            NewPAL.getRetAttrs(), NewArgAttrs));
+    if (IgnoreMusttailFunction && CI->isMustTailCall()) {
+      // replace a callsite with musttail.
+      llvm::errs() << "callsite has musttail in newF " << New->getName()
+                   << "\n";
+    }
+    NewCI->copyMetadata(*CI);
+    CI->replaceAllUsesWith(NewCI);
+    CI->eraseFromParent();
+  }
+  assert(Old->use_empty() && "should have replaced all uses of old function");
+  return Old->hasLocalLinkage();
+}
+
+PreservedAnalyses MergeFuncIgnoringConstPass::run(Module &M,
+                                                  ModuleAnalysisManager &MAM) {
+  if (runInternal(M))
+    return PreservedAnalyses::none();
+  return PreservedAnalyses::all();
+}
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index e971c638327bf05..082f9c1110bbc5e 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -27,6 +27,7 @@ add_llvm_component_library(LLVMTransformUtils
   FixIrreducible.cpp
   FlattenCFG.cpp
   FunctionComparator.cpp
+  FunctionComparatorIgnoringConst.cpp
   FunctionImportUtils.cpp
   GlobalStatus.cpp
   GuardUtils.cpp
diff --git a/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp b/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
new file mode 100644
index 000000000000000..3b3567111f43034
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
@@ -0,0 +1,107 @@
+//===--- FunctionComparatorIgnoringConst.cpp - Function Comparator --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h"
+
+using namespace llvm;
+
+int FunctionComparatorIgnoringConst::cmpOperandsIgnoringConsts(
+    const Instruction *L, const Instruction *R, unsigned opIdx) {
+  Value *OpL = L->getOperand(opIdx);
+  Value *OpR = R->getOperand(opIdx);
+
+  int Res = cmpValues(OpL, OpR);
+  if (Res == 0)
+    return Res;
+
+  if (!isa<Constant>(OpL) || !isa<Constant>(OpR))
+    return Res;
+
+  if (!isEligibleOperandForConstantSharing(L, opIdx) ||
+      !isEligibleOperandForConstantSharing(R, opIdx))
+    return Res;
+
+  if (cmpTypes(OpL->getType(), OpR->getType()))
+    return Res;
+
+  return 0;
+}
+
+// Test whether two basic blocks have equivalent behavior.
+int FunctionComparatorIgnoringConst::cmpBasicBlocksIgnoringConsts(
+    const BasicBlock *BBL, const BasicBlock *BBR,
+    const std::set<std::pair<int, int>> *InstOpndIndex) {
+  BasicBlock::const_iterator InstL = BBL->begin(), InstLE = BBL->end();
+  BasicBlock::const_iterator InstR = BBR->begin(), InstRE = BBR->end();
+
+  do {
+    bool needToCmpOperands = true;
+    if (int Res = cmpOperations(&*InstL, &*InstR, needToCmpOperands))
+      return Res;
+    if (needToCmpOperands) {
+      assert(InstL->getNumOperands() == InstR->getNumOperands());
+
+      for (unsigned i = 0, e = InstL->getNumOperands(); i != e; ++i) {
+        // When a set for (instruction, operand) index pairs is given, we only
+        // ignore constants located at such indices. Otherwise, we precisely
+        // compare the operands.
+        if (InstOpndIndex && !InstOpndIndex->count(std::make_pair(index, i))) {
+          Value *OpL = InstL->getOperand(i);
+          Value *OpR = InstR->getOperand(i);
+          if (int Res = cmpValues(OpL, OpR))
+            return Res;
+        }
+        if (int Res = cmpOperandsIgnoringConsts(&*InstL, &*InstR, i))
+          return Res;
+        // cmpValues should ensure this is true.
+        assert(cmpTypes(InstL->getOperand(i)->getType(),
+                        InstR->getOperand(i)->getType()) == 0);
+      }
+    }
+    ++index;
+    ++InstL, ++InstR;
+  } while (InstL != InstLE && InstR != InstRE);
+
+  if (InstL != InstLE && InstR == InstRE)
+    return 1;
+  if (InstL == InstLE && InstR != InstRE)
+    return -1;
+  return 0;
+}
+
+// Test whether the two functions have equivalent behavior.
+int FunctionComparatorIgnoringConst::compareIgnoringConsts(
+    const std::set<std::pair<int, int>> *InstOpndIndex) {
+  beginCompare();
+  index = 0;
+
+  if (int Res = compareSignature())
+    return Res;
+
+  Function::const_iterator LIter = FnL->begin(), LEnd = FnL->end();
+  Function::const_iterator RIter = FnR->begin(), REnd = FnR->end();
+
+  do {
+    const BasicBlock *BBL = &*LIter;
+    const BasicBlock *BBR = &*RIter;
+
+    if (int Res = cmpValues(BBL, BBR))
+      return Res;
+
+    if (int Res = cmpBasicBlocksIgnoringConsts(BBL, BBR, InstOpndIndex))
+      return Res;
+
+    ++LIter, ++RIter;
+  } while (LIter != LEnd && RIter != REnd);
+
+  return 0;
+}
diff --git a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll
new file mode 100644
index 000000000000000..1d84340da417235
--- /dev/null
+++ b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll
@@ -0,0 +1,532 @@
+; RUN: opt -S -mergefunc-ignoringconst-threshold=4 -passes=mergefunc-ignoring-const %s | FileCheck %s
+
+ at g1 = external global i32
+ at g2 = external global i32
+ at g3 = external global i32
+ at g4 = external global i32
+ at g5 = external global i32
+
+; Test the most trivial example.
+
+; CHECK-LABEL: define i32 @simple_func1(i32 %x, i32 %y)
+; CHECK: %1 = tail call i32 @simple_func1.Tm(i32 %x, i32 %y, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @simple_func1(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %l = load i32, i32* @g1, align 4
+  %sum3 = add i32 %sum2, %y
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define i32 @simple_func2(i32 %x, i32 %y)
+; CHECK: %1 = tail call i32 @simple_func1.Tm(i32 %x, i32 %y, ptr @g2)
+; CHECK: ret i32 %1
+define i32 @simple_func2(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %l = load i32, i32* @g2, align 4
+  %sum3 = add i32 %sum2, %y
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define internal i32 @simple_func1.Tm(i32 %0, i32 %1, ptr %2)
+; CHECK: %l = load i32, ptr %2
+; CHECK: ret
+
+
+; Merge 3 functions with 3 types of differing instructions: load, store and call.
+
+; CHECK-LABEL: define i32 @func1_of_3(i32 %x)
+; CHECK: %1 = tail call i32 @func1_of_3.Tm(i32 %x, ptr @g1, ptr @g1, ptr @callee1)
+; CHECK: ret i32 %1
+define i32 @func1_of_3(i32 %x) {
+  %l1 = load i32, i32* @g1, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g1, align 4
+  %sum2 = add i32 %sum, %l2
+  store i32 %sum2, i32 *@g1, align 4
+  call void @callee1(i32 %sum2)
+  %sum3 = add i32 %sum2, %l2
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define i32 @func2_of_3(i32 %x)
+; CHECK: %1 = tail call i32 @func1_of_3.Tm(i32 %x, ptr @g2, ptr @g2, ptr @callee2)
+; CHECK: ret i32 %1
+define i32 @func2_of_3(i32 %x) {
+  %l1 = load i32, i32* @g2, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l2
+  store i32 %sum2, i32 *@g2, align 4
+  call void @callee2(i32 %sum2)
+  %sum3 = add i32 %sum2, %l2
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define i32 @func3_of_3(i32 %x)
+; CHECK: %1 = tail call i32 @func1_of_3.Tm(i32 %x, ptr @g3, ptr @g1, ptr @callee3)
+; CHECK: ret i32 %1
+define i32 @func3_of_3(i32 %x) {
+  %l1 = load i32, i32* @g3, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g1, align 4
+  %sum2 = add i32 %sum, %l2
+  store i32 %sum2, i32 *@g3, align 4
+  call void @callee3(i32 %sum2)
+  %sum3 = add i32 %sum2, %l2
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define internal i32 @func1_of_3.Tm(i32 %0, ptr %1, ptr %2, ptr %3)
+; CHECK: %l1 = load i32, ptr %1
+; CHECK: %l2 = load i32, ptr %2
+; CHECK: store i32 %sum2, ptr %1
+; CHECK: call void %3(i32 %sum2)
+; CHECK: ret
+
+declare void @callee1(i32 %x)
+declare void @callee2(i32 %x)
+declare void @callee3(i32 %x)
+
+; Preserve attributes
+
+; CHECK-LABEL: define void @sret_func1(ptr sret(i32) %p, i32 %x, i32 %y)
+; CHECK: tail call void @sret_func1.Tm(ptr sret(i32) %p, i32 %x, i32 %y, ptr @g1)
+; CHECK: ret void
+define void @sret_func1(i32* sret(i32) %p, i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %l = load i32, i32* @g1, align 4
+  %sum2 = add i32 %sum, %l
+  store i32 %sum2, i32* %p
+  ret void
+}
+
+; CHECK-LABEL: define void @sret_func2(ptr sret(i32) %p, i32 %x, i32 %y)
+; CHECK: tail call void @sret_func1.Tm(ptr sret(i32) %p, i32 %x, i32 %y, ptr @g2)
+; CHECK: ret void
+define void @sret_func2(i32* sret(i32) %p, i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %l = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l
+  store i32 %sum2, i32* %p
+  ret void
+}
+
+; CHECK-LABEL: define internal void @sret_func1.Tm(ptr sret(i32) %0, i32 %1, i32 %2, ptr %3)
+; CHECK: %l = load i32, ptr %3, align 4
+; CHECK: store i32 %sum2, ptr %0
+; CHECK: ret
+
+
+; Don't merge all functions, because we would generate too many parameters.
+; Instead merge those functions which match best.
+
+; CHECK-LABEL: define i32 @func1_merged_with3(i32 %x)
+; CHECK: %1 = tail call i32 @func1_merged_with3.Tm(i32 %x, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @func1_merged_with3(i32 %x) {
+  %l1 = load i32, i32* @g1, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g3, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g4, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g5, align 4
+  %sum5 = add i32 %sum4, %l2
+  ret i32 %sum5
+}
+
+; CHECK-LABEL: define i32 @func2_merged_with4(i32 %x)
+; CHECK: %1 = tail call i32 @func2_merged_with4.Tm(i32 %x, ptr @g2)
+; CHECK: ret i32 %1
+define i32 @func2_merged_with4(i32 %x) {
+  %l1 = load i32, i32* @g2, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g3, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g4, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g5, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g1, align 4
+  %sum5 = add i32 %sum4, %l2
+  ret i32 %sum5
+}
+
+; CHECK-LABEL: define i32 @func3_merged_with1(i32 %x)
+; CHECK: %1 = tail call i32 @func1_merged_with3.Tm(i32 %x, ptr @g2)
+; CHECK: ret i32 %1
+define i32 @func3_merged_with1(i32 %x) {
+  %l1 = load i32, i32* @g2, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g3, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g4, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g5, align 4
+  %sum5 = add i32 %sum4, %l2
+  ret i32 %sum5
+}
+
+; CHECK-LABEL: define internal i32 @func1_merged_with3.Tm(i32 %0, ptr %1)
+; CHECK: load i32, ptr %1, align 4
+; CHECK: load i32, ptr @g2, align 4
+; CHECK: load i32, ptr @g3, align 4
+; CHECK: load i32, ptr @g4, align 4
+; CHECK: load i32, ptr @g5, align 4
+; CHECK: ret i32
+
+; CHECK-LABEL: define i32 @func4_merged_with2(i32 %x) {
+; CHECK: %1 = tail call i32 @func2_merged_with4.Tm(i32 %x, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @func4_merged_with2(i32 %x) {
+  %l1 = load i32, i32* @g1, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g3, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g4, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g5, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g1, align 4
+  %sum5 = add i32 %sum4, %l2
+  ret i32 %sum5
+}
+
+
+; The same example as above, but we cannot merge func2 with func4, because
+; func4 calls func1 (which is merged with func2 in the first iteration).
+
+declare i32 @get_int(i32 %x)
+
+; CHECK-LABEL: define i32 @Function1_merged_with_3(i32 %x)
+; CHECK: %1 = tail call i32 @Function1_merged_with_3.Tm(i32 %x, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @Function1_merged_with_3(i32 %x) {
+  %l1 = load i32, i32* @g1, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g3, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g4, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g5, align 4
+  %sum5 = add i32 %sum4, %l2
+  %c = call fastcc i32 @get_int(i32 %sum5)
+  ret i32 %c
+}
+
+; CHECK-LABEL: define i32 @Function2_not_merged(i32 %x)
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: %c = call fastcc i32 @get_int
+; CHECK: ret i32 %c
+define i32 @Function2_not_merged(i32 %x) {
+  %l1 = load i32, i32* @g2, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g3, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g4, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g5, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g1, align 4
+  %sum5 = add i32 %sum4, %l2
+  %c = call fastcc i32 @get_int(i32 %sum5)
+  ret i32 %c
+}
+
+; CHECK-LABEL: define i32 @Function3_merged_with_1(i32 %x)
+; CHECK: %1 = tail call i32 @Function1_merged_with_3.Tm(i32 %x, ptr @g2)
+; CHECK: ret i32 %1
+define i32 @Function3_merged_with_1(i32 %x) {
+  %l1 = load i32, i32* @g2, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g2, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g3, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g4, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g5, align 4
+  %sum5 = add i32 %sum4, %l2
+  %c = call fastcc i32 @get_int(i32 %sum5)
+  ret i32 %c
+}
+
+; CHECK-LABEL: define internal i32 @Function1_merged_with_3.Tm(i32 %0, ptr %1)
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: %c = call fastcc i32 @get_int
+; CHECK: ret i32 %c
+
+; CHECK-LABEL: define i32 @Function4_not_merged(i32 %x) {
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: load
+; CHECK: %1 = call fastcc i32 @Function1_merged_with_3.Tm(i32 %sum5, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @Function4_not_merged(i32 %x) {
+  %l1 = load i32, i32* @g1, align 4
+  %sum = add i32 %x, %l1
+  %l2 = load i32, i32* @g3, align 4
+  %sum2 = add i32 %sum, %l2
+  %l3 = load i32, i32* @g4, align 4
+  %sum3 = add i32 %sum2, %l2
+  %l4 = load i32, i32* @g5, align 4
+  %sum4 = add i32 %sum3, %l2
+  %l5 = load i32, i32* @g1, align 4
+  %sum5 = add i32 %sum4, %l2
+  %c = call fastcc i32 @Function1_merged_with_3(i32 %sum5)
+  ret i32 %c
+}
+
+
+; Test a call chain: caller -> callee1 -> callee2.
+; Functions should be merged in bottom-up order: callee2, callee1, caller.
+; Also check that the calling convention is preserved.
+
+; CHECK-LABEL: define fastcc i32 @callee1_a(i32 %x, i32 %y)
+; CHECK: %1 = tail call fastcc i32 @callee1_a.Tm(i32 %x, i32 %y, ptr @g1)
+; CHECK: ret i32 %1
+define fastcc i32 @callee1_a(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %c = call i32 @callee2_a(i32 %sum2, i32 %y)
+  %sum3 = add i32 %sum2, %c
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define fastcc i32 @callee1_b(i32 %x, i32 %y)
+; CHECK: %1 = tail call fastcc i32 @callee1_a.Tm(i32 %x, i32 %y, ptr @g2)
+; CHECK: ret i32 %1
+define fastcc i32 @callee1_b(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %c = call i32 @callee2_b(i32 %sum2, i32 %y)
+  %sum3 = add i32 %sum2, %c
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define internal fastcc i32 @callee1_a.Tm(i32 %0, i32 %1, ptr %2)
+; CHECK: call i32 @callee2_a.Tm(i32 %sum2, i32 %1, ptr %2)
+; CHECK: ret
+
+; CHECK-NOT: @callee2_a(
+define internal i32 @callee2_a(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = sub i32 %sum, %y
+  %l = load i32, i32* @g1, align 4
+  %sum3 = add i32 %sum2, %y
+  ret i32 %sum3
+}
+
+; CHECK-NOT: @callee2_b(
+define internal i32 @callee2_b(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = sub i32 %sum, %y
+  %l = load i32, i32* @g2, align 4
+  %sum3 = add i32 %sum2, %y
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define i32 @caller_a(i32 %x, i32 %y)
+; CHECK: %1 = tail call i32 @caller_a.Tm(i32 %x, i32 %y, ptr @g1)
+; CHECK: ret i32 %1
+define i32 @caller_a(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %c = call fastcc i32 @callee1_a(i32 %sum2, i32 %y)
+  %sum3 = add i32 %sum2, %c
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define i32 @caller_b(i32 %x, i32 %y)
+; CHECK: %1 = tail call i32 @caller_a.Tm(i32 %x, i32 %y, ptr @g2)
+; CHECK: ret i32 %1
+define i32 @caller_b(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %c = call fastcc i32 @callee1_b(i32 %sum2, i32 %y)
+  %sum3 = add i32 %sum2, %c
+  ret i32 %sum3
+}
+
+; CHECK-LABEL: define internal i32 @caller_a.Tm(i32 %0, i32 %1, ptr %2)
+; CHECK: call fastcc i32 @callee1_a.Tm(i32 %sum2, i32 %1, ptr %2)
+; CHECK: ret
+
+
+; Ensure that we do not merge functions that are identical with the
+; exception of the order of the incoming blocks to a phi.
+
+; CHECK-LABEL: define linkonce_odr hidden i1 @first(i2 %0)
+define linkonce_odr hidden i1 @first(i2) {
+entry:
+; CHECK: switch i2
+  switch i2 %0, label %default [
+    i2 0, label %L1
+    i2 1, label %L2
+    i2 -2, label %L3
+  ]
+default:
+  unreachable
+L1:
+  br label %done
+L2:
+  br label %done
+L3:
+  br label %done
+done:
+  %result = phi i1 [ true, %L1 ], [ false, %L2 ], [ false, %L3 ]
+; CHECK: ret i1
+  ret i1 %result
+}
+
+; CHECK-LABEL: define linkonce_odr hidden i1 @second(i2 %0)
+define linkonce_odr hidden i1 @second(i2) {
+entry:
+; CHECK: switch i2
+  switch i2 %0, label %default [
+    i2 0, label %L1
+    i2 1, label %L2
+    i2 -2, label %L3
+  ]
+default:
+  unreachable
+L1:
+  br label %done
+L2:
+  br label %done
+L3:
+  br label %done
+done:
+  %result = phi i1 [ true, %L3 ], [ false, %L2 ], [ false, %L1 ]
+; CHECK: ret i1
+  ret i1 %result
+}
+
+; Check self recursive functions
+
+; CHECK-LABEL: define internal void @recursive1(i32 %x, i32 %y)
+; CHECK: tail call void @recursive1.Tm(i32 %x, i32 %y, ptr @g1, ptr @recursive1)
+; CHECK: ret void
+define internal void @recursive1(i32 %x, i32 %y) {
+  br i1 undef, label %bb1, label %bb2
+
+bb1:
+  %l = load i32, i32* @g1, align 4
+  call void @recursive1(i32 %x, i32 %y)
+  br label %bb2
+
+bb2:
+  ret void
+}
+
+; CHECK-LABEL: define internal void @recursive2(i32 %x, i32 %y)
+; CHECK: tail call void @recursive1.Tm(i32 %x, i32 %y, ptr @g2, ptr @recursive2)
+; CHECK: ret void
+define internal void @recursive2(i32 %x, i32 %y) {
+  br i1 undef, label %bb1, label %bb2
+
+bb1:
+  %l = load i32, i32* @g2, align 4
+  call void @recursive2(i32 %x, i32 %y)
+  br label %bb2
+
+bb2:
+  ret void
+}
+; CHECK-LABEL: define internal void @recursive1.Tm(i32 %0, i32 %1, ptr %2, ptr %3)
+; CHECK: load i32, ptr %2
+; CHECK: call void %3(i32 %0, i32 %1)
+; CHECK: ret void
+
+
+; CHECK-LABEL: define internal void @another_recursive_func(i32 %x)
+; CHECK: tail call void @another_recursive_func.Tm(i32 %x, ptr @g1, ptr @another_recursive_func)
+; CHECK: ret void
+define internal void @another_recursive_func(i32 %x) {
+  br i1 undef, label %bb1, label %bb2
+
+bb1:
+  store i32 %x, i32 *@g1, align 4
+  call void @another_recursive_func(i32 %x)
+  br label %bb2
+
+bb2:
+  ret void
+}
+; CHECK-NOT: @not_really_recursive(
+
+; CHECK-LABEL: define internal void @another_recursive_func.Tm(i32 %0, ptr %1, ptr %2)
+; CHECK: store i32 %0, ptr %1
+; CHECK: call void %2(i32 %0)
+; CHECK: ret void
+define internal void @not_really_recursive(i32 %x) {
+  br i1 undef, label %bb1, label %bb2
+
+bb1:
+  store i32 %x, i32 *@g2, align 4
+  call void @callee1(i32 %x)
+  br label %bb2
+
+bb2:
+  ret void
+}
+; CHECK-NOT: @not_really_recursive(
+
+; CHECK-LABEL: define void @call_recursive_funcs(i32 %x)
+; CHECK: call void @recursive1(i32 %x, i32 %x)
+; CHECK: call void @recursive2(i32 %x, i32 %x)
+; CHECK: call void @another_recursive_func(i32 %x)
+; CHECK: call void @another_recursive_func.Tm(i32 %x, ptr @g2, ptr @callee1)
+; CHECK: ret void
+define void @call_recursive_funcs(i32 %x) {
+  call void @recursive1(i32 %x, i32 %x)
+  call void @recursive2(i32 %x, i32 %x)
+  call void @another_recursive_func(i32 %x)
+  call void @not_really_recursive(i32 %x)
+  ret void
+}
+
+; Ensure that we do not merge functions which make use of distinct dtrace
+; probes. Each call to a dtrace probe must resolve to a unique patchpoint.
+
+declare void @"__dtrace_probe$Apple$Probe1$v1$696e74"(i32) local_unnamed_addr
+
+; CHECK-LABEL: define i32 @use_dtrace_probe1
+; CHECK: call void @"__dtrace_probe$Apple$Probe1$v1$696e74"
+define i32 @use_dtrace_probe1(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %l = load i32, i32* @g1, align 4
+  %sum3 = add i32 %sum2, %y
+  tail call void @"__dtrace_probe$Apple$Probe1$v1$696e74"(i32 undef)
+  ret i32 %sum3
+}
+
+declare void @"__dtrace_probe$Apple$Probe2$v1$696e74"(i32) local_unnamed_addr
+
+; CHECK-LABEL: define i32 @use_dtrace_probe2
+; CHECK: call void @"__dtrace_probe$Apple$Probe2$v1$696e74"
+define i32 @use_dtrace_probe2(i32 %x, i32 %y) {
+  %sum = add i32 %x, %y
+  %sum2 = add i32 %sum, %y
+  %l = load i32, i32* @g2, align 4
+  %sum3 = add i32 %sum2, %y
+  tail call void @"__dtrace_probe$Apple$Probe2$v1$696e74"(i32 undef)
+  ret i32 %sum3
+}
diff --git a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
new file mode 100644
index 000000000000000..14f026f94a8bbaa
--- /dev/null
+++ b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
@@ -0,0 +1,190 @@
+; RUN: opt -S -enable-merge-func2 -passes=mergefunc-ignoring-const %s -o - | FileCheck %s
+
+%4 = type opaque
+%10 = type opaque
+%"struct.SearchSpec::State" = type { %4* }
+%"struct.PointerList" = type { i8*, i8*, i8*, i8*, i8* }
+%"struct.DynamicCallback" = type { %10* }
+
+; CHECK: define ptr @invoke_foo(ptr nocapture readonly %.block_descriptor, ptr %stateWrapper)
+; CHECK: %1 = {{.*}}call ptr @invoke_foo.Tm
+; CHECK: define ptr @invoke_bar(ptr nocapture readonly %.block_descriptor, ptr %stateWrapper) {
+; CHECK: %1 = {{.*}}call ptr @invoke_foo.Tm
+; CHECK: define {{.*}}.Tm(ptr nocapture readonly %0, ptr %1, ptr %2, ptr %3)
+
+; Function Attrs: minsize optsize ssp uwtable
+define i8* @invoke_foo(i8* nocapture readonly %.block_descriptor, i8* %stateWrapper) #1 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+entry:
+  %state = alloca %"struct.SearchSpec::State", align 8
+  %agg.tmp = alloca %"struct.PointerList", align 8
+  %0 = tail call i8* @llvm.objc.retain(i8* %stateWrapper) #2
+  %1 = bitcast %"struct.SearchSpec::State"* %state to i8*
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) #2
+  %2 = getelementptr inbounds i8, i8* %stateWrapper, i64 16
+  %3 = bitcast i8* %2 to %"struct.SearchSpec::State"* (i8*)**
+  %4 = load %"struct.SearchSpec::State"* (i8*)*, %"struct.SearchSpec::State"* (i8*)** %3, align 8
+  %call.i4 = invoke nonnull align 8 dereferenceable(8) %"struct.SearchSpec::State"* %4(i8* nonnull %stateWrapper) #31
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:                                      ; preds = %entry
+  %initialText.i.i = getelementptr inbounds %"struct.SearchSpec::State", %"struct.SearchSpec::State"* %state, i64 0, i32 0
+  %initialText2.i.i = getelementptr inbounds %"struct.SearchSpec::State", %"struct.SearchSpec::State"* %call.i4, i64 0, i32 0
+  %5 = load %4*, %4** %initialText2.i.i, align 8
+  %6 = bitcast %4* %5 to i8*
+  %7 = tail call i8* @llvm.objc.retain(i8* %6) #2
+  store %4* %5, %4** %initialText.i.i, align 8
+  %block.capture.addr = getelementptr inbounds i8, i8* %.block_descriptor, i64 32
+  %8 = bitcast i8* %block.capture.addr to i8**
+  %9 = load i8*, i8** %8, align 8
+  invoke void @callee2(%"struct.PointerList"* nonnull sret(%"struct.PointerList") align 8 %agg.tmp, i8* %9, i1 zeroext false) #31
+          to label %invoke.cont2 unwind label %lpad1
+
+invoke.cont2:                                     ; preds = %invoke.cont
+  %block.capture.addr3 = getelementptr inbounds i8, i8* %.block_descriptor, i64 40
+  %10 = bitcast i8* %block.capture.addr3 to %4**
+  %agg.tmp6.sroa.3.0..sroa_idx12 = getelementptr inbounds %"struct.PointerList", %"struct.PointerList"* %agg.tmp, i64 0, i32 3
+  %agg.tmp6.sroa.3.0.copyload = load i8*, i8** %agg.tmp6.sroa.3.0..sroa_idx12, align 8
+  %11 = load %4*, %4** %10, align 8
+  invoke void @callee1(%"struct.SearchSpec::State"* nonnull align 8 dereferenceable(8) %state, %4* %11) #31
+          to label %invoke.cont4 unwind label %lpad.i
+
+lpad.i:                                           ; preds = %invoke.cont2
+  %12 = landingpad { i8*, i32 }
+          cleanup
+  call void @llvm.objc.release(i8* %agg.tmp6.sroa.3.0.copyload) #2
+  %.phi.trans.insert = bitcast %"struct.SearchSpec::State"* %state to i8**
+  %.pre = load i8*, i8** %.phi.trans.insert, align 8
+  br label %lpad1.body
+
+invoke.cont4:                                     ; preds = %invoke.cont2
+  call void @llvm.objc.release(i8* %agg.tmp6.sroa.3.0.copyload) #2
+  %13 = load %4*, %4** %initialText.i.i, align 8
+  store %4* null, %4** %initialText.i.i, align 8
+  %call78 = call fastcc i8* @callee3(%4* %13) #31 [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
+  call void (...) @llvm.objc.clang.arc.noop.use(i8* %call78) #2
+  %14 = bitcast %"struct.SearchSpec::State"* %state to i8**
+  %15 = load i8*, i8** %14, align 8
+  call void @llvm.objc.release(i8* %15) #2
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #2
+  call void @llvm.objc.release(i8* nonnull %stateWrapper) #2, !clang.imprecise_release !1
+  %16 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call78) #2
+  ret i8* %call78
+
+lpad:                                             ; preds = %entry
+  %17 = landingpad { i8*, i32 }
+          cleanup
+  br label %ehcleanup
+
+lpad1:                                            ; preds = %invoke.cont
+  %18 = landingpad { i8*, i32 }
+          cleanup
+  br label %lpad1.body
+
+lpad1.body:                                       ; preds = %lpad1, %lpad.i
+  %19 = phi i8* [ %6, %lpad1 ], [ %.pre, %lpad.i ]
+  %eh.lpad-body = phi { i8*, i32 } [ %18, %lpad1 ], [ %12, %lpad.i ]
+  call void @llvm.objc.release(i8* %19) #2
+  br label %ehcleanup
+
+ehcleanup:                                        ; preds = %lpad1.body, %lpad
+  %.pn = phi { i8*, i32 } [ %eh.lpad-body, %lpad1.body ], [ %17, %lpad ]
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #2
+  call void @llvm.objc.release(i8* nonnull %stateWrapper) #2, !clang.imprecise_release !1
+  resume { i8*, i32 } %.pn
+}
+
+; Function Attrs: minsize optsize ssp uwtable
+define i8* @invoke_bar(i8* nocapture readonly %.block_descriptor, i8* %stateWrapper) #1 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+entry:
+  %state = alloca %"struct.DynamicCallback", align 8
+  %agg.tmp = alloca %"struct.PointerList", align 8
+  %0 = tail call i8* @llvm.objc.retain(i8* %stateWrapper) #2
+  %1 = bitcast %"struct.DynamicCallback"* %state to i8*
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) #2
+  %2 = getelementptr inbounds i8, i8* %stateWrapper, i64 16
+  %3 = bitcast i8* %2 to %"struct.DynamicCallback"* (i8*)**
+  %4 = load %"struct.DynamicCallback"* (i8*)*, %"struct.DynamicCallback"* (i8*)** %3, align 8
+  %call.i4 = invoke nonnull align 8 dereferenceable(8) %"struct.DynamicCallback"* %4(i8* nonnull %stateWrapper) #31
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:                                      ; preds = %entry
+  %call.i.i = getelementptr inbounds %"struct.DynamicCallback", %"struct.DynamicCallback"* %state, i64 0, i32 0
+  %call2.i.i = getelementptr inbounds %"struct.DynamicCallback", %"struct.DynamicCallback"* %call.i4, i64 0, i32 0
+  %5 = load %10*, %10** %call2.i.i, align 8
+  %6 = bitcast %10* %5 to i8*
+  %7 = tail call i8* @llvm.objc.retain(i8* %6) #2
+  store %10* %5, %10** %call.i.i, align 8
+  %block.capture.addr = getelementptr inbounds i8, i8* %.block_descriptor, i64 32
+  %8 = bitcast i8* %block.capture.addr to i8**
+  %9 = load i8*, i8** %8, align 8
+  invoke void @callee2(%"struct.PointerList"* nonnull sret(%"struct.PointerList") align 8 %agg.tmp, i8* %9, i1 zeroext false) #31
+          to label %invoke.cont2 unwind label %lpad1
+
+invoke.cont2:                                     ; preds = %invoke.cont
+  %block.capture.addr3 = getelementptr inbounds i8, i8* %.block_descriptor, i64 40
+  %10 = bitcast i8* %block.capture.addr3 to %10**
+  %agg.tmp6.sroa.3.0..sroa_idx12 = getelementptr inbounds %"struct.PointerList", %"struct.PointerList"* %agg.tmp, i64 0, i32 3
+  %agg.tmp6.sroa.3.0.copyload = load i8*, i8** %agg.tmp6.sroa.3.0..sroa_idx12, align 8
+  %11 = load %10*, %10** %10, align 8
+  invoke void @callee5(%"struct.DynamicCallback"* nonnull align 8 dereferenceable(8) %state, %10* %11) #31
+          to label %invoke.cont4 unwind label %lpad.i
+
+lpad.i:                                           ; preds = %invoke.cont2
+  %12 = landingpad { i8*, i32 }
+          cleanup
+  call void @llvm.objc.release(i8* %agg.tmp6.sroa.3.0.copyload) #2
+  %.phi.trans.insert = bitcast %"struct.DynamicCallback"* %state to i8**
+  %.pre = load i8*, i8** %.phi.trans.insert, align 8
+  br label %lpad1.body
+
+invoke.cont4:                                     ; preds = %invoke.cont2
+  call void @llvm.objc.release(i8* %agg.tmp6.sroa.3.0.copyload) #2
+  %13 = load %10*, %10** %call.i.i, align 8
+  store %10* null, %10** %call.i.i, align 8
+  %call78 = call fastcc i8* @callee4(%10* %13) #31 [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
+  call void (...) @llvm.objc.clang.arc.noop.use(i8* %call78) #2
+  %14 = bitcast %"struct.DynamicCallback"* %state to i8**
+  %15 = load i8*, i8** %14, align 8
+  call void @llvm.objc.release(i8* %15) #2
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #2
+  call void @llvm.objc.release(i8* nonnull %stateWrapper) #2, !clang.imprecise_release !1
+  %16 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call78) #2
+  ret i8* %call78
+
+lpad:                                             ; preds = %entry
+  %17 = landingpad { i8*, i32 }
+          cleanup
+  br label %ehcleanup
+
+lpad1:                                            ; preds = %invoke.cont
+  %18 = landingpad { i8*, i32 }
+          cleanup
+  br label %lpad1.body
+
+lpad1.body:                                       ; preds = %lpad1, %lpad.i
+  %19 = phi i8* [ %6, %lpad1 ], [ %.pre, %lpad.i ]
+  %eh.lpad-body = phi { i8*, i32 } [ %18, %lpad1 ], [ %12, %lpad.i ]
+  call void @llvm.objc.release(i8* %19) #2
+  br label %ehcleanup
+
+ehcleanup:                                        ; preds = %lpad1.body, %lpad
+  %.pn = phi { i8*, i32 } [ %eh.lpad-body, %lpad1.body ], [ %17, %lpad ]
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #2
+  call void @llvm.objc.release(i8* nonnull %stateWrapper) #2, !clang.imprecise_release !1
+  resume { i8*, i32 } %.pn
+}
+declare void @callee1(%"struct.SearchSpec::State"* nonnull align 8 dereferenceable(8), %4*)
+declare void @callee2(%"struct.PointerList"* sret(%"struct.PointerList") align 8, i8*, i1 zeroext)
+declare i8* @callee3(%4* %state.coerce)
+declare i8* @callee4(%10* %state.coerce)
+declare void @callee5(%"struct.DynamicCallback"* nonnull align 8 dereferenceable(8), %10*)
+declare i32 @__gxx_personality_v0(...)
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
+declare i8* @llvm.objc.autoreleaseReturnValue(i8*)
+declare void @llvm.objc.clang.arc.noop.use(...)
+declare void @llvm.objc.release(i8*)
+declare i8* @llvm.objc.retain(i8*)
+declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*)
+
+!1 = !{}

>From 1e04c5da7deb3463ae0781575cc3377d0f8d1ff7 Mon Sep 17 00:00:00 2001
From: Manman Ren <mren at fb.com>
Date: Fri, 20 Oct 2023 17:44:40 -0700
Subject: [PATCH 2/3] address review comments

add a string for suffix of the merged function
---
 .../IPO/MergeFunctionsIgnoringConst.h         |  5 ++
 .../IPO/MergeFunctionsIgnoringConst.cpp       | 84 ++++++++-----------
 2 files changed, 40 insertions(+), 49 deletions(-)

diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
index f9d55cc40873adc..baf24a53032a987 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
@@ -24,8 +24,13 @@ class Module;
 /// Merge functions that differ by constants.
 class MergeFuncIgnoringConstPass
     : public PassInfoMixin<MergeFuncIgnoringConstPass> {
+  bool ptrAuthEnabled = false;
+  unsigned ptrAuthKey = 0;
+  std::string mergeFuncSuffix = ".Tm";
 public:
   MergeFuncIgnoringConstPass() {}
+  MergeFuncIgnoringConstPass(bool ptrAuthEnabled, unsigned ptrAuthKey, std::string suffix)
+      : ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey), mergeFuncSuffix(suffix) {}
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
 };
 
diff --git a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
index e60acf1d8279323..3aa58f6fe07d193 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
@@ -11,8 +11,6 @@
 // merging identical functions, it merges functions which only differ by a few
 // constants in certain instructions.
 // This is copied from Swift's implementation.
-// TODO: We should generalize this pass and share it with Swift's
-// implementation.
 //
 // This pass should run after LLVM's MergeFunctions pass, because it works best
 // if there are no _identical_ functions in the module.
@@ -186,11 +184,9 @@ namespace {
 ///
 class MergeFuncIgnoringConstImpl { // : public ModulePass {
 public:
-  MergeFuncIgnoringConstImpl() : FnTree(FunctionNodeCmp(&GlobalNumbers)) {}
-
-  MergeFuncIgnoringConstImpl(bool ptrAuthEnabled, unsigned ptrAuthKey)
+  MergeFuncIgnoringConstImpl(bool ptrAuthEnabled, unsigned ptrAuthKey, std::string suffix)
       : FnTree(FunctionNodeCmp(&GlobalNumbers)), ptrAuthOptionsSet(true),
-        ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey) {}
+        ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey), mergeFuncSuffix(suffix) {}
 
   bool runImpl(Module &M);
 
@@ -376,8 +372,6 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
   using ParamInfos = SmallVector<ParamInfo, 16>;
 
   Module *module = nullptr;
-  ModuleSummaryIndex *ExportSummary;
-  const ModuleSummaryIndex *ImportSummary;
 
   GlobalNumberState GlobalNumbers;
 
@@ -405,6 +399,8 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
   /// The key for pointer authentication.
   unsigned ptrAuthKey = 0;
 
+  std::string mergeFuncSuffix = ".Tm";
+
   FunctionEntry *getEntry(Function *F) const { return FuncEntries.lookup(F); }
 
   bool isInEquivalenceClass(FunctionEntry *FE) const {
@@ -485,38 +481,8 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
                             const ParamInfos &Params, unsigned FuncIdx);
 };
 
-#if 0
-class MergeFuncIgnoringConst : public ModulePass {
-public:
-  static char ID;
-  /// True if the architecture has pointer authentication enabled.
-  bool ptrAuthEnabled = false;
-
-  /// The key for pointer authentication.
-  unsigned ptrAuthKey = 0;
-  ModuleSummaryIndex *ExportSummary;
-  const ModuleSummaryIndex *ImportSummary;
-
-  MergeFuncIgnoringConst() : ModulePass(ID) {
-    initializeMergeFuncIgnoringConstPass(*llvm::PassRegistry::getPassRegistry());
-  }
-  MergeFuncIgnoringConst(bool ptrAuthEnabled, unsigned ptrAuthKey)
-      : ModulePass(ID), ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey) {
-    initializeMergeFuncIgnoringConstPass(*llvm::PassRegistry::getPassRegistry());
-  }
-  bool runOnModule(Module &M) override;
-};
-#endif
-
 } // end anonymous namespace
 
-#if 0
-char MergeFuncIgnoringConst::ID = 0;
-INITIALIZE_PASS_BEGIN(MergeFuncIgnoringConst, "merge-func-ignoring-const",
-                      "merge function pass ignoring const", false, false)
-INITIALIZE_PASS_END(MergeFuncIgnoringConst, "merge-func-ignoring-const",
-                    "merge function pass ignoring const", false, false)
-#endif
 bool MergeFuncIgnoringConstImpl::doSanityCheck(
     std::vector<WeakTrackingVH> &Worklist) {
   if (const unsigned Max = NumFunctionsIgnoringConstForSanityCheck) {
@@ -694,13 +660,6 @@ bool isEligibleFunction(Function *F) {
   return true;
 }
 
-static bool runInternal(Module &M) {
-  return MergeFuncIgnoringConstImpl().runImpl(M);
-}
-
-// bool MergeFuncIgnoringConst::runOnModule(Module &M) { return runInternal(M);
-// }
-
 bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
   if (IgnoringConstMergeThreshold == 0)
     return false;
@@ -1120,7 +1079,7 @@ void MergeFuncIgnoringConstImpl::mergeWithParams(const FunctionInfos &FInfos,
 
   // Create the new function.
   Function *NewFunction = Function::Create(funcType, FirstF->getLinkage(),
-                                           FirstF->getName() + ".Tm");
+                                           FirstF->getName() + mergeFuncSuffix);
   if (auto *SP = FirstF->getSubprogram())
     NewFunction->setSubprogram(SP);
   NewFunction->copyAttributesFrom(FirstF);
@@ -1324,6 +1283,30 @@ void MergeFuncIgnoringConstImpl::writeThunk(Function *ToFunc, Function *Thunk,
   ++NumThunksWrittenIgnoringConst;
 }
 
+static llvm::AttributeList
+fixUpTypesInByValAndStructRetAttributes(llvm::FunctionType *fnType,
+                                        llvm::AttributeList attrList) {
+  auto &context = fnType->getContext();
+  if (!context.supportsTypedPointers())
+    return attrList;
+
+  for (unsigned i = 0; i < fnType->getNumParams(); ++i) {
+    auto paramTy = fnType->getParamType(i);
+    auto attrListIndex = llvm::AttributeList::FirstArgIndex + i;
+    if (attrList.hasParamAttr(i, llvm::Attribute::StructRet) &&
+        paramTy->getNonOpaquePointerElementType() != attrList.getParamStructRetType(i))
+      attrList = attrList.replaceAttributeTypeAtIndex(
+          context, attrListIndex, llvm::Attribute::StructRet,
+          paramTy->getNonOpaquePointerElementType());
+    if (attrList.hasParamAttr(i, llvm::Attribute::ByVal) &&
+        paramTy->getNonOpaquePointerElementType() != attrList.getParamByValType(i))
+      attrList = attrList.replaceAttributeTypeAtIndex(
+          context, attrListIndex, llvm::Attribute::ByVal,
+          paramTy->getNonOpaquePointerElementType());
+  }
+  return attrList;
+}
+
 /// Replace direct callers of Old with New. Also add parameters to the call to
 /// \p New, which are defined by the FuncIdx's value in \p Params.
 bool MergeFuncIgnoringConstImpl::replaceDirectCallers(Function *Old,
@@ -1407,8 +1390,11 @@ bool MergeFuncIgnoringConstImpl::replaceDirectCallers(Function *Old,
     NewCI->setCallingConv(CI->getCallingConv());
     // Don't transfer attributes from the function to the callee. Function
     // attributes typically aren't relevant to the calling convention or ABI.
-    NewCI->setAttributes(AttributeList::get(Context, /*FnAttrs=*/AttributeSet(),
-                                            NewPAL.getRetAttrs(), NewArgAttrs));
+    auto newAttrList = AttributeList::get(Context, /*FnAttrs=*/AttributeSet(),
+                                            NewPAL.getRetAttrs(),
+                                            NewArgAttrs);
+    newAttrList = fixUpTypesInByValAndStructRetAttributes(FType, newAttrList);
+    NewCI->setAttributes(newAttrList);
     if (IgnoreMusttailFunction && CI->isMustTailCall()) {
       // replace a callsite with musttail.
       llvm::errs() << "callsite has musttail in newF " << New->getName()
@@ -1424,7 +1410,7 @@ bool MergeFuncIgnoringConstImpl::replaceDirectCallers(Function *Old,
 
 PreservedAnalyses MergeFuncIgnoringConstPass::run(Module &M,
                                                   ModuleAnalysisManager &MAM) {
-  if (runInternal(M))
+  if (MergeFuncIgnoringConstImpl(ptrAuthEnabled, ptrAuthKey, mergeFuncSuffix).runImpl(M))
     return PreservedAnalyses::none();
   return PreservedAnalyses::all();
 }

>From 5fd3518cd3cc25d6156d68d6ee04ec8215caed98 Mon Sep 17 00:00:00 2001
From: Manman Ren <mren at fb.com>
Date: Mon, 30 Oct 2023 14:28:49 -0700
Subject: [PATCH 3/3] Address review comments on variable names, cleanup

---
 .../IPO/MergeFunctionsIgnoringConst.h         |  13 +-
 .../Utils/FunctionComparatorIgnoringConst.h   |   6 +-
 .../IPO/MergeFunctionsIgnoringConst.cpp       | 160 ++++++++----------
 .../Utils/FunctionComparatorIgnoringConst.cpp |   6 +-
 .../merge_with_exception.ll                   |   2 +-
 5 files changed, 86 insertions(+), 101 deletions(-)

diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
index baf24a53032a987..638d009abf2bffc 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
@@ -24,13 +24,16 @@ class Module;
 /// Merge functions that differ by constants.
 class MergeFuncIgnoringConstPass
     : public PassInfoMixin<MergeFuncIgnoringConstPass> {
-  bool ptrAuthEnabled = false;
-  unsigned ptrAuthKey = 0;
-  std::string mergeFuncSuffix = ".Tm";
+  bool PtrAuthEnabled = false;
+  unsigned PtrAuthKey = 0;
+  std::string MergeFuncSuffix = ".Tm";
+
 public:
   MergeFuncIgnoringConstPass() {}
-  MergeFuncIgnoringConstPass(bool ptrAuthEnabled, unsigned ptrAuthKey, std::string suffix)
-      : ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey), mergeFuncSuffix(suffix) {}
+  MergeFuncIgnoringConstPass(bool PtrAuthEnabled, unsigned PtrAuthKey,
+                             std::string Suffix)
+      : PtrAuthEnabled(PtrAuthEnabled), PtrAuthKey(PtrAuthKey),
+        MergeFuncSuffix(Suffix) {}
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
 };
 
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h b/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
index a61e02fa41db762..9c7fe3baf2fa0db 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
@@ -28,7 +28,7 @@
 namespace llvm {
 
 /// FunctionComparatorIgnoringConst - Compares two functions to determine
-/// whether or not they will generate machine code with the same behavior.
+/// whether or not they match when certain constants are ignored.
 class FunctionComparatorIgnoringConst : public FunctionComparator {
 public:
   FunctionComparatorIgnoringConst(const Function *F1, const Function *F2,
@@ -50,8 +50,8 @@ class FunctionComparatorIgnoringConst : public FunctionComparator {
   }
 
 private:
-  // Scratch index for instruction in order during cmpOperandsIgnoringConsts.
-  int index = 0;
+  /// Scratch index for instruction in order during cmpOperandsIgnoringConsts.
+  int Index = 0;
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
index 3aa58f6fe07d193..a3f734e0987764d 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
@@ -20,22 +20,18 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h"
-// #include "llvm/Transforms/Utils/GlobalMergeFunctions.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StableHashing.h"
 #include "llvm/ADT/Statistic.h"
-#include "llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h"
-// #include "llvm/ADT/Triple.h"
 #include "llvm/Analysis/ObjCARCUtil.h"
-#include "llvm/ADT/StableHashing.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfoMetadata.h"
-// #include "llvm/IR/GlobalPtrAuthInfo.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/Instructions.h"
@@ -53,6 +49,7 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h"
 #include <vector>
 
 using namespace llvm;
@@ -62,9 +59,9 @@ using namespace llvm;
 STATISTIC(NumFunctionsMergedIgnoringConst, "Number of functions merged");
 STATISTIC(NumThunksWrittenIgnoringConst, "Number of thunks generated");
 
-static cl::opt<bool>
-    EnableMergeFunc2("enable-merge-func2", cl::init(false), cl::Hidden,
-                     cl::desc("Enable more aggressive function merger"));
+static cl::opt<bool> EnableAggressiveMergeFunc(
+    "enable-aggressive-mergefunc-ignoringconst", cl::init(false), cl::Hidden,
+    cl::desc("Enable more aggressive function merger"));
 
 static cl::opt<unsigned> NumFunctionsIgnoringConstForSanityCheck(
     "mergefunc-ignoringconst-sanity",
@@ -122,19 +119,19 @@ bool isEligibleInstrunctionForConstantSharing(const Instruction *I) {
   case Instruction::Call:
     return true;
   default: {
-    if (EnableMergeFunc2 && I->getOpcode() == Instruction::Invoke)
+    if (EnableAggressiveMergeFunc && I->getOpcode() == Instruction::Invoke)
       return true;
     return false;
   }
   }
 }
 
-/// Returns true if the \opIdx operand of \p CI is the callee operand.
-static bool isCalleeOperand(const CallBase *CI, unsigned opIdx) {
-  return &CI->getCalledOperandUse() == &CI->getOperandUse(opIdx);
+/// Returns true if the \OpIdx operand of \p CI is the callee operand.
+static bool isCalleeOperand(const CallBase *CI, unsigned OpIdx) {
+  return &CI->getCalledOperandUse() == &CI->getOperandUse(OpIdx);
 }
 
-static bool canParameterizeCallOperand(const CallBase *CI, unsigned opIdx) {
+static bool canParameterizeCallOperand(const CallBase *CI, unsigned OpIdx) {
   if (CI->isInlineAsm())
     return false;
   Function *Callee = CI->getCalledOperand()
@@ -148,7 +145,7 @@ static bool canParameterizeCallOperand(const CallBase *CI, unsigned opIdx) {
     if (Callee->getName().startswith("objc_msgSend$"))
       return false;
   }
-  if (isCalleeOperand(CI, opIdx) &&
+  if (isCalleeOperand(CI, OpIdx) &&
       CI->getOperandBundle(LLVMContext::OB_ptrauth).has_value()) {
     // The operand is the callee and it has already been signed. Ignore this
     // because we cannot add another ptrauth bundle to the call instruction.
@@ -182,11 +179,12 @@ namespace {
 /// parameter. The original functions are replaced by thunks which call the
 /// merged function with the specific argument constants.
 ///
-class MergeFuncIgnoringConstImpl { // : public ModulePass {
+class MergeFuncIgnoringConstImpl {
 public:
-  MergeFuncIgnoringConstImpl(bool ptrAuthEnabled, unsigned ptrAuthKey, std::string suffix)
-      : FnTree(FunctionNodeCmp(&GlobalNumbers)), ptrAuthOptionsSet(true),
-        ptrAuthEnabled(ptrAuthEnabled), ptrAuthKey(ptrAuthKey), mergeFuncSuffix(suffix) {}
+  MergeFuncIgnoringConstImpl(bool PtrAuthEnabled, unsigned PtrAuthKey,
+                             std::string Suffix)
+      : FnTree(FunctionNodeCmp(&GlobalNumbers)), PtrAuthEnabled(PtrAuthEnabled),
+        PtrAuthKey(PtrAuthKey), MergeFuncSuffix(Suffix) {}
 
   bool runImpl(Module &M);
 
@@ -234,8 +232,8 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
   ///
   struct FunctionEntry {
     FunctionEntry(Function *F, FnTreeType::iterator I)
-        : F(F), Next(nullptr), numUnhandledCallees(0), TreeIter(I),
-          isMerged(false) {}
+        : F(F), Next(nullptr), NumUnhandledCallees(0), TreeIter(I),
+          IsMerged(false) {}
 
     /// Back-link to the function.
     AssertingVH<Function> F;
@@ -248,14 +246,14 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
     /// This is only valid in the first entry of an equivalence class. The
     /// counts of all functions in an equivalence class are accumulated in the
     /// first entry.
-    int numUnhandledCallees;
+    int NumUnhandledCallees;
 
     /// The iterator of the function's equivalence class in the FnTree.
     /// It's FnTree.end() if the function is not in an equivalence class.
     FnTreeType::iterator TreeIter;
 
     /// True if this function is already a thunk, calling the merged function.
-    bool isMerged;
+    bool IsMerged;
   };
 
   /// Describes an operator of a specific instruction.
@@ -319,23 +317,23 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
     /// All uses of the parameter in the merged function.
     SmallVector<OpLocation, 16> Uses;
 
-    /// The discriminator for pointer signing.
+    /// The Discriminator for pointer signing.
     /// Only not null if needsPointerSigning is true.
-    ConstantInt *discriminator = nullptr;
+    ConstantInt *Discriminator = nullptr;
 
     /// True if the value is a callee function, which needs to be signed if
     /// passed as a parameter.
-    bool needsPointerSigning = false;
+    bool NeedsPointerSigning = false;
 
     /// Checks if this parameter can be used to describe an operand in all
     /// functions of the equivalence class. Returns true if all values match
     /// the specific instruction operands in all functions.
     bool matches(const FunctionInfos &FInfos, unsigned OpIdx,
-                 bool ptrAuthEnabled) const {
+                 bool PtrAuthEnabled) const {
       unsigned NumFuncs = FInfos.size();
       assert(Values.size() == NumFuncs);
-      if (ptrAuthEnabled &&
-          needsPointerSigning != FInfos[0].needsPointerSigning(OpIdx)) {
+      if (PtrAuthEnabled &&
+          NeedsPointerSigning != FInfos[0].needsPointerSigning(OpIdx)) {
         return false;
       }
       for (unsigned Idx = 0; Idx < NumFuncs; ++Idx) {
@@ -347,10 +345,10 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
       return true;
     }
 
-    /// Computes the discriminator for pointer signing.
+    /// Computes the Discriminator for pointer signing.
     void computeDiscriminator(LLVMContext &Context) {
-      assert(needsPointerSigning);
-      assert(!discriminator);
+      assert(NeedsPointerSigning);
+      assert(!Discriminator);
 
       /// Get a hash from the concatenated function names.
       /// The hash is deterministic, because the order of values depends on the
@@ -365,13 +363,13 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
       }
       uint64_t rawHash = stable_hash_combine_string(concatenatedCalleeNames);
       IntegerType *discrTy = Type::getInt64Ty(Context);
-      discriminator = ConstantInt::get(discrTy, (rawHash % 0xFFFF) + 1);
+      Discriminator = ConstantInt::get(discrTy, (rawHash % 0xFFFF) + 1);
     }
   };
 
   using ParamInfos = SmallVector<ParamInfo, 16>;
 
-  Module *module = nullptr;
+  Module *CurrentModule = nullptr;
 
   GlobalNumberState GlobalNumbers;
 
@@ -385,21 +383,18 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
 
   ValueMap<Function *, FunctionEntry *> FuncEntries;
 
-  // Maps a function-pointer / discriminator pair to a corresponding global in
+  // Maps a function-pointer / Discriminator pair to a corresponding global in
   // the llvm.ptrauth section.
   // This map is used as a cache to not create ptrauth globals twice.
-  DenseMap<std::pair<Constant *, ConstantInt *>, Constant *> ptrAuthGlobals;
-
-  /// If true, ptrAuthEnabled and ptrAuthKey are valid.
-  bool ptrAuthOptionsSet = false;
+  DenseMap<std::pair<Constant *, ConstantInt *>, Constant *> PtrAuthGlobals;
 
   /// True if the architecture has pointer authentication enabled.
-  bool ptrAuthEnabled = false;
+  bool PtrAuthEnabled = false;
 
   /// The key for pointer authentication.
-  unsigned ptrAuthKey = 0;
+  unsigned PtrAuthKey = 0;
 
-  std::string mergeFuncSuffix = ".Tm";
+  std::string MergeFuncSuffix = ".Tm";
 
   FunctionEntry *getEntry(Function *F) const { return FuncEntries.lookup(F); }
 
@@ -408,7 +403,7 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
       return true;
     }
     assert(!FE->Next);
-    assert(FE->numUnhandledCallees == 0);
+    assert(FE->NumUnhandledCallees == 0);
     return false;
   }
 
@@ -416,7 +411,7 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
   /// Returns true, if sanity check has been passed, and false if failed.
   bool doSanityCheck(std::vector<WeakTrackingVH> &Worklist);
 
-  /// Updates the numUnhandledCallees of all user functions of the equivalence
+  /// Updates the NumUnhandledCallees of all user functions of the equivalence
   /// class containing \p FE by \p Delta.
   void updateUnhandledCalleeCount(FunctionEntry *FE, int Delta);
 
@@ -435,7 +430,7 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
                          ParamInfos &Params, unsigned maxParams);
 
   void replaceCallWithAddedPtrAuth(CallInst *origCall, Value *newCallee,
-                                   ConstantInt *discriminator);
+                                   ConstantInt *Discriminator);
 
   void mergeWithParams(const FunctionInfos &FInfos, ParamInfos &Params);
   static void dumpMergeInfo(const FunctionInfos &FInfos, unsigned);
@@ -447,30 +442,25 @@ class MergeFuncIgnoringConstImpl { // : public ModulePass {
 
   bool isPtrAuthEnabled() const {
     // TODO: fix pointer authentication
-    // assert(ptrAuthOptionsSet);
-    return ptrAuthEnabled;
+    return PtrAuthEnabled;
   }
 
   ConstantInt *getPtrAuthKey() {
     // TODO: fix pointer authentication
-    // assert(isPtrAuthEnabled());
-    return ConstantInt::get(Type::getInt32Ty(module->getContext()), ptrAuthKey);
+    return ConstantInt::get(Type::getInt32Ty(CurrentModule->getContext()),
+                            PtrAuthKey);
   }
 
   /// Returns the value of function \p FuncIdx, and signes it if required.
   Constant *getSignedValue(const ParamInfo &PI, unsigned FuncIdx) {
     Constant *value = PI.Values[FuncIdx];
-    if (!PI.needsPointerSigning)
+    if (!PI.NeedsPointerSigning)
       return value;
 
-    auto lookupKey = std::make_pair(value, PI.discriminator);
-    Constant *&ptrAuthGlobal = ptrAuthGlobals[lookupKey];
+    auto lookupKey = std::make_pair(value, PI.Discriminator);
+    Constant *&ptrAuthGlobal = PtrAuthGlobals[lookupKey];
     if (!ptrAuthGlobal) {
-#if 0
-      ptrAuthGlobal = GlobalPtrAuthInfo::create(
-          *module, value, getPtrAuthKey(),
-          ConstantInt::get(PI.discriminator->getType(), 0), PI.discriminator);
-#endif
+      // TODO: fix pointer authentication
     }
     return ptrAuthGlobal;
   }
@@ -664,19 +654,9 @@ bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
   if (IgnoringConstMergeThreshold == 0)
     return false;
 
-  module = &M;
+  CurrentModule = &M;
 
-#if 0
   // TODO: fix pointer authentication
-  if (!ptrAuthOptionsSet) {
-    // If invoked from IRGen in the compiler, those options are already set.
-    // If invoked from swift-llvm-opt, derive the options from the target triple.
-    Triple triple(M.getTargetTriple());
-    ptrAuthEnabled = (triple.getSubArch() == Triple::AArch64SubArch_arm64e);
-    ptrAuthKey = (unsigned)clang::PointerAuthSchema::ARM8_3Key::ASIA;
-    ptrAuthOptionsSet = true;
-  }
-#endif
 
   bool Changed = false;
 
@@ -769,14 +749,14 @@ bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
     // Check if there are any leaf functions at all.
     bool LeafFound = false;
     for (FunctionEntry *ToMerge : FuncsToMerge) {
-      if (ToMerge->numUnhandledCallees == 0)
+      if (ToMerge->NumUnhandledCallees == 0)
         LeafFound = true;
     }
     for (FunctionEntry *ToMerge : FuncsToMerge) {
       if (isInEquivalenceClass(ToMerge)) {
         // Only merge leaf functions (or all functions if all functions are in
         // a call cycle).
-        if (ToMerge->numUnhandledCallees == 0 || !LeafFound) {
+        if (ToMerge->NumUnhandledCallees == 0 || !LeafFound) {
           updateUnhandledCalleeCount(ToMerge, -1);
           Changed |= tryMergeEquivalenceClass(ToMerge);
         } else {
@@ -791,7 +771,7 @@ bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
   FnTree.clear();
   GlobalNumbers.clear();
   FuncEntries.clear();
-  ptrAuthGlobals.clear();
+  PtrAuthGlobals.clear();
 
   return Changed;
 }
@@ -806,7 +786,7 @@ void MergeFuncIgnoringConstImpl::updateUnhandledCalleeCount(FunctionEntry *FE,
         if (CallerFE && CallerFE->TreeIter != FnTree.end()) {
           // Accumulate the count in the first entry of the equivalence class.
           FunctionEntry *Head = CallerFE->TreeIter->First;
-          Head->numUnhandledCallees += Delta;
+          Head->NumUnhandledCallees += Delta;
         }
       }
     }
@@ -821,7 +801,7 @@ bool MergeFuncIgnoringConstImpl::tryMergeEquivalenceClass(
   FunctionEntry *FE = FirstInClass;
   do {
     FInfos.push_back(FunctionInfo(FE->F));
-    FE->isMerged = true;
+    FE->IsMerged = true;
     FE = FE->Next;
   } while (FE);
   assert(FInfos.size() >= 2);
@@ -951,7 +931,8 @@ bool MergeFuncIgnoringConstImpl::constsDiffer(const FunctionInfos &FInfos,
     if (auto *C = dyn_cast<Constant>(Op)) {
       if (!CommonConst) {
         CommonConst = C;
-      } else if (EnableMergeFunc2 && isa<ConstantPointerNull>(CommonConst) &&
+      } else if (EnableAggressiveMergeFunc &&
+                 isa<ConstantPointerNull>(CommonConst) &&
                  isa<ConstantPointerNull>(C)) {
         // if both are null pointer, and if they are different constants
         // due to type, still treat them as the same.
@@ -997,7 +978,7 @@ bool MergeFuncIgnoringConstImpl::tryMapToParameter(FunctionInfos &FInfos,
         FI.NumParamsNeeded += 1;
     }
     if (isPtrAuthEnabled())
-      Matching->needsPointerSigning = FInfos[0].needsPointerSigning(OpIdx);
+      Matching->NeedsPointerSigning = FInfos[0].needsPointerSigning(OpIdx);
   }
   /// Remember where the parameter is needed when we build our merged function.
   Matching->Uses.push_back({FInfos[0].CurrentInst, OpIdx});
@@ -1005,13 +986,13 @@ bool MergeFuncIgnoringConstImpl::tryMapToParameter(FunctionInfos &FInfos,
 }
 
 /// Copy \p origCall with a \p newCalle and add a ptrauth bundle with \p
-/// discriminator.
+/// Discriminator.
 void MergeFuncIgnoringConstImpl::replaceCallWithAddedPtrAuth(
-    CallInst *origCall, Value *newCallee, ConstantInt *discriminator) {
+    CallInst *origCall, Value *newCallee, ConstantInt *Discriminator) {
   SmallVector<llvm::OperandBundleDef, 4> bundles;
   origCall->getOperandBundlesAsDefs(bundles);
   ConstantInt *key = getPtrAuthKey();
-  llvm::Value *bundleArgs[] = {key, discriminator};
+  llvm::Value *bundleArgs[] = {key, Discriminator};
   bundles.emplace_back("ptrauth", bundleArgs);
 
   SmallVector<llvm::Value *, 4> copiedArgs;
@@ -1079,7 +1060,7 @@ void MergeFuncIgnoringConstImpl::mergeWithParams(const FunctionInfos &FInfos,
 
   // Create the new function.
   Function *NewFunction = Function::Create(funcType, FirstF->getLinkage(),
-                                           FirstF->getName() + mergeFuncSuffix);
+                                           FirstF->getName() + MergeFuncSuffix);
   if (auto *SP = FirstF->getSubprogram())
     NewFunction->setSubprogram(SP);
   NewFunction->copyAttributesFrom(FirstF);
@@ -1116,7 +1097,7 @@ void MergeFuncIgnoringConstImpl::mergeWithParams(const FunctionInfos &FInfos,
     const ParamInfo &PI = Params[paramIdx];
     Argument *NewArg = NewFunction->getArg(numOrigArgs + paramIdx);
 
-    if (!PI.needsPointerSigning) {
+    if (!PI.NeedsPointerSigning) {
       for (const OpLocation &OL : PI.Uses) {
         OL.I->setOperand(OL.OpIndex, NewArg);
       }
@@ -1135,12 +1116,12 @@ void MergeFuncIgnoringConstImpl::mergeWithParams(const FunctionInfos &FInfos,
   // be replaced.
   for (unsigned paramIdx = 0; paramIdx < Params.size(); ++paramIdx) {
     ParamInfo &PI = Params[paramIdx];
-    if (PI.needsPointerSigning) {
+    if (PI.NeedsPointerSigning) {
       PI.computeDiscriminator(NewFunction->getContext());
       for (const OpLocation &OL : PI.Uses) {
         auto *origCall = cast<CallInst>(OL.I);
         Argument *newCallee = NewFunction->getArg(numOrigArgs + paramIdx);
-        replaceCallWithAddedPtrAuth(origCall, newCallee, PI.discriminator);
+        replaceCallWithAddedPtrAuth(origCall, newCallee, PI.Discriminator);
       }
     }
   }
@@ -1177,14 +1158,14 @@ void MergeFuncIgnoringConstImpl::removeEquivalenceClassFromTree(
 
   FnTreeType::iterator Iter = FE->TreeIter;
   FunctionEntry *Unlink = Iter->First;
-  Unlink->numUnhandledCallees = 0;
+  Unlink->NumUnhandledCallees = 0;
   while (Unlink) {
     LLVM_DEBUG(dbgs() << "    remove from tree: " << Unlink->F->getName()
                       << '\n');
-    if (!Unlink->isMerged)
+    if (!Unlink->IsMerged)
       Deferred.emplace_back(Unlink->F);
     Unlink->TreeIter = FnTree.end();
-    assert(Unlink->numUnhandledCallees == 0);
+    assert(Unlink->NumUnhandledCallees == 0);
     FunctionEntry *NextEntry = Unlink->Next;
     Unlink->Next = nullptr;
     Unlink = NextEntry;
@@ -1203,10 +1184,10 @@ Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy) {
     Value *Result = UndefValue::get(DestTy);
     for (unsigned int I = 0, E = SrcTy->getStructNumElements(); I < E; ++I) {
       Value *Element =
-          createCast(Builder, Builder.CreateExtractValue(V, makeArrayRef(I)),
+          createCast(Builder, Builder.CreateExtractValue(V, ArrayRef(I)),
                      DestTy->getStructElementType(I));
 
-      Result = Builder.CreateInsertValue(Result, Element, makeArrayRef(I));
+      Result = Builder.CreateInsertValue(Result, Element, ArrayRef(I));
     }
     return Result;
   }
@@ -1219,10 +1200,10 @@ Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy) {
       Value *Result = UndefValue::get(DestTy);
       for (unsigned int I = 0, E = SrcAT->getNumElements(); I < E; ++I) {
         Value *Element =
-            createCast(Builder, Builder.CreateExtractValue(V, makeArrayRef(I)),
+            createCast(Builder, Builder.CreateExtractValue(V, ArrayRef(I)),
                        DestAT->getElementType());
 
-        Result = Builder.CreateInsertValue(Result, Element, makeArrayRef(I));
+        Result = Builder.CreateInsertValue(Result, Element, ArrayRef(I));
       }
       return Result;
     }
@@ -1410,7 +1391,8 @@ bool MergeFuncIgnoringConstImpl::replaceDirectCallers(Function *Old,
 
 PreservedAnalyses MergeFuncIgnoringConstPass::run(Module &M,
                                                   ModuleAnalysisManager &MAM) {
-  if (MergeFuncIgnoringConstImpl(ptrAuthEnabled, ptrAuthKey, mergeFuncSuffix).runImpl(M))
+  if (MergeFuncIgnoringConstImpl(PtrAuthEnabled, PtrAuthKey, MergeFuncSuffix)
+          .runImpl(M))
     return PreservedAnalyses::none();
   return PreservedAnalyses::all();
 }
diff --git a/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp b/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
index 3b3567111f43034..9cfd95345598083 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
@@ -54,7 +54,7 @@ int FunctionComparatorIgnoringConst::cmpBasicBlocksIgnoringConsts(
         // When a set for (instruction, operand) index pairs is given, we only
         // ignore constants located at such indices. Otherwise, we precisely
         // compare the operands.
-        if (InstOpndIndex && !InstOpndIndex->count(std::make_pair(index, i))) {
+        if (InstOpndIndex && !InstOpndIndex->count(std::make_pair(Index, i))) {
           Value *OpL = InstL->getOperand(i);
           Value *OpR = InstR->getOperand(i);
           if (int Res = cmpValues(OpL, OpR))
@@ -67,7 +67,7 @@ int FunctionComparatorIgnoringConst::cmpBasicBlocksIgnoringConsts(
                         InstR->getOperand(i)->getType()) == 0);
       }
     }
-    ++index;
+    ++Index;
     ++InstL, ++InstR;
   } while (InstL != InstLE && InstR != InstRE);
 
@@ -82,7 +82,7 @@ int FunctionComparatorIgnoringConst::cmpBasicBlocksIgnoringConsts(
 int FunctionComparatorIgnoringConst::compareIgnoringConsts(
     const std::set<std::pair<int, int>> *InstOpndIndex) {
   beginCompare();
-  index = 0;
+  Index = 0;
 
   if (int Res = compareSignature())
     return Res;
diff --git a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
index 14f026f94a8bbaa..c5c8b898c046e51 100644
--- a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
+++ b/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S -enable-merge-func2 -passes=mergefunc-ignoring-const %s -o - | FileCheck %s
+; RUN: opt -S -enable-aggressive-mergefunc-ignoringconst -passes=mergefunc-ignoring-const %s -o - | FileCheck %s
 
 %4 = type opaque
 %10 = type opaque



More information about the llvm-commits mailing list