[llvm] a682a9c - Revert "Port Swift's merge function pass to llvm: merging functions that differ in constants (#68235)"
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 3 13:16:45 PDT 2023
Author: Nikita Popov
Date: 2023-11-03T21:15:46+01:00
New Revision: a682a9cfd006c52559387f80398b720d529595d1
URL: https://github.com/llvm/llvm-project/commit/a682a9cfd006c52559387f80398b720d529595d1
DIFF: https://github.com/llvm/llvm-project/commit/a682a9cfd006c52559387f80398b720d529595d1.diff
LOG: Revert "Port Swift's merge function pass to llvm: merging functions that differ in constants (#68235)"
This reverts commit 19b5495b653a00da7a250f48b4f739fcf2bbe82f.
PR landed without approval, with severe quality issues.
Added:
Modified:
llvm/include/llvm/Transforms/Utils/FunctionComparator.h
llvm/lib/Passes/PassBuilder.cpp
llvm/lib/Passes/PassBuilderPipelines.cpp
llvm/lib/Passes/PassRegistry.def
llvm/lib/Transforms/IPO/CMakeLists.txt
llvm/lib/Transforms/Utils/CMakeLists.txt
Removed:
llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h
llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll
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
deleted file mode 100644
index 638d009abf2bffc..000000000000000
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctionsIgnoringConst.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//===- 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
diff er 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);
-};
-
-} // 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 1a314b481c72c61..c28f868039a1f7b 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
@@ -379,7 +379,6 @@ 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
deleted file mode 100644
index 9c7fe3baf2fa0db..000000000000000
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparatorIgnoringConst.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//===- 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 match when certain constants are ignored.
-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
deleted file mode 100644
index e63afbb6bbf1718..000000000000000
--- a/llvm/include/llvm/Transforms/Utils/MergeFunctionsIgnoringConst.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===- 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 789ddfcbf529879..0d7cac19d44c3a8 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -123,7 +123,6 @@
#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 20dbd3952beb60f..baea2913338cda7 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -60,7 +60,6 @@
#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"
@@ -177,10 +176,6 @@ 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"));
@@ -1638,9 +1633,6 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline(
MPM.addPass(buildModuleOptimizationPipeline(
Level, ThinOrFullLTOPhase::ThinLTOPostLink));
- if (EnableMergeFuncIgnoringConst)
- MPM.addPass(MergeFuncIgnoringConstPass());
-
// Emit annotation remarks.
addAnnotationRemarksPass(MPM);
@@ -1966,9 +1958,6 @@ 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 ba32c64d18423b9..eb51ccef68c827d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -87,7 +87,6 @@ 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 4dac04d3369950f..034f1587ae8df44 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -30,7 +30,6 @@ 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
deleted file mode 100644
index d6ae788ddb9e1a1..000000000000000
--- a/llvm/lib/Transforms/IPO/MergeFunctionsIgnoringConst.cpp
+++ /dev/null
@@ -1,1399 +0,0 @@
-//===--- 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
diff er by a few
-// constants in certain instructions.
-// This is copied from 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/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/Analysis/ObjCARCUtil.h"
-#include "llvm/IR/Attributes.h"
-#include "llvm/IR/Constants.h"
-#include "llvm/IR/DataLayout.h"
-#include "llvm/IR/DebugInfoMetadata.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 "llvm/Transforms/Utils/FunctionComparatorIgnoringConst.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> 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",
- 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 (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);
-}
-
-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
diff er by constants in
-/// certain instructions, e.g. resulting from specialized functions of layout
-/// compatible types.
-/// Such functions are merged by replacing the
diff ering constants by a
-/// parameter. The original functions are replaced by thunks which call the
-/// merged function with the specific argument constants.
-///
-class MergeFuncIgnoringConstImpl {
-public:
- MergeFuncIgnoringConstImpl(bool PtrAuthEnabled, unsigned PtrAuthKey,
- std::string Suffix)
- : FnTree(FunctionNodeCmp(&GlobalNumbers)), PtrAuthEnabled(PtrAuthEnabled),
- PtrAuthKey(PtrAuthKey), MergeFuncSuffix(Suffix) {}
-
- bool runImpl(Module &M);
-
-private:
- struct FunctionEntry;
-
- /// Describes the set of functions which are considered as "equivalent" (i.e.
- /// only
diff ering 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 *CurrentModule = nullptr;
-
- 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;
-
- /// True if the architecture has pointer authentication enabled.
- bool PtrAuthEnabled = false;
-
- /// 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 {
- 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
- return PtrAuthEnabled;
- }
-
- ConstantInt *getPtrAuthKey() {
- // TODO: fix pointer authentication
- 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)
- return value;
-
- auto lookupKey = std::make_pair(value, PI.Discriminator);
- Constant *&ptrAuthGlobal = PtrAuthGlobals[lookupKey];
- if (!ptrAuthGlobal) {
- // TODO: fix pointer authentication
- }
- 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);
-};
-
-} // end anonymous namespace
-
-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;
-}
-
-bool MergeFuncIgnoringConstImpl::runImpl(Module &M) {
- if (IgnoringConstMergeThreshold == 0)
- return false;
-
- CurrentModule = &M;
-
- // TODO: fix pointer authentication
-
- 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
diff er 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
diff er
- // 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
- //
diff ers 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
diff er 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
diff er 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
diff ers.
-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
diff er 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 (EnableAggressiveMergeFunc &&
- isa<ConstantPointerNull>(CommonConst) &&
- isa<ConstantPointerNull>(C)) {
- // if both are null pointer, and if they are
diff erent constants
- // due to type, still treat them as the same.
- } else if (C != CommonConst) {
- return true;
- }
- }
- }
- return false;
-}
-
-/// Create a new parameter for
diff ering 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
diff ering
- // 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() + MergeFuncSuffix);
- 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
diff ering 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
diff ering 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, ArrayRef(I)),
- DestTy->getStructElementType(I));
-
- Result = Builder.CreateInsertValue(Result, Element, ArrayRef(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, ArrayRef(I)),
- DestAT->getElementType());
-
- Result = Builder.CreateInsertValue(Result, Element, ArrayRef(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;
-}
-
-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,
- 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.
- 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()
- << "\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 (MergeFuncIgnoringConstImpl(PtrAuthEnabled, PtrAuthKey, MergeFuncSuffix)
- .runImpl(M))
- return PreservedAnalyses::none();
- return PreservedAnalyses::all();
-}
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 9c320beb09711af..51e8821773c3af3 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -27,7 +27,6 @@ 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
deleted file mode 100644
index 9cfd95345598083..000000000000000
--- a/llvm/lib/Transforms/Utils/FunctionComparatorIgnoringConst.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-//===--- 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
deleted file mode 100644
index 1d84340da417235..000000000000000
--- a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_func.ll
+++ /dev/null
@@ -1,532 +0,0 @@
-; 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
diff ering 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
deleted file mode 100644
index c5c8b898c046e51..000000000000000
--- a/llvm/test/Transforms/MergeFuncIgnoringConst/merge_with_exception.ll
+++ /dev/null
@@ -1,190 +0,0 @@
-; RUN: opt -S -enable-aggressive-mergefunc-ignoringconst -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 = !{}
More information about the llvm-commits
mailing list