[llvm] 89bcfd1 - Recommit "[FuncSpec] Decouple cost/benefit analysis, allowing sorting of candidates."
Sjoerd Meijer via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 17 01:03:43 PST 2021
Author: Sjoerd Meijer
Date: 2021-12-17T09:02:51Z
New Revision: 89bcfd16325740ad4d0503a0f9854a70bb10867c
URL: https://github.com/llvm/llvm-project/commit/89bcfd16325740ad4d0503a0f9854a70bb10867c
DIFF: https://github.com/llvm/llvm-project/commit/89bcfd16325740ad4d0503a0f9854a70bb10867c.diff
LOG: Recommit "[FuncSpec] Decouple cost/benefit analysis, allowing sorting of candidates."
Replaced llvm:sort with llvm::stable_sort, this was failing on the bot with
expensive checks enabled.
Added:
Modified:
llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index fbd083bb9bbfd..cb31f62fceca7 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -92,6 +92,25 @@ static cl::opt<bool> EnableSpecializationForLiteralConstant(
cl::desc("Enable specialization of functions that take a literal constant "
"as an argument."));
+namespace {
+// Bookkeeping struct to pass data from the analysis and profitability phase
+// to the actual transform helper functions.
+struct ArgInfo {
+ Function *Fn; // The function to perform specialisation on.
+ Argument *Arg; // The Formal argument being analysed.
+ Constant *Const; // A corresponding actual constant argument.
+ InstructionCost Gain; // Profitability: Gain = Bonus - Cost.
+
+ // Flag if this will be a partial specialization, in which case we will need
+ // to keep the original function around in addition to the added
+ // specializations.
+ bool Partial = false;
+
+ ArgInfo(Function *F, Argument *A, Constant *C, InstructionCost G)
+ : Fn(F), Arg(A), Const(C), Gain(G){};
+};
+} // Anonymous namespace
+
// Helper to check if \p LV is either a constant or a constant
// range with a single element. This should cover exactly the same cases as the
// old ValueLatticeElement::isConstant() and is intended to be used in the
@@ -256,16 +275,27 @@ class FunctionSpecializer {
bool
specializeFunctions(SmallVectorImpl<Function *> &FuncDecls,
SmallVectorImpl<Function *> &CurrentSpecializations) {
-
- // Attempt to specialize the argument-tracked functions.
bool Changed = false;
for (auto *F : FuncDecls) {
- if (specializeFunction(F, CurrentSpecializations)) {
- Changed = true;
- LLVM_DEBUG(dbgs() << "FnSpecialization: Can specialize this func.\n");
- } else {
+ if (!isCandidateFunction(F, CurrentSpecializations))
+ continue;
+
+ auto Cost = getSpecializationCost(F);
+ if (!Cost.isValid()) {
LLVM_DEBUG(
- dbgs() << "FnSpecialization: Cannot specialize this func.\n");
+ dbgs() << "FnSpecialization: Invalid specialisation cost.\n");
+ continue;
+ }
+
+ auto ConstArgs = calculateGains(F, Cost);
+ if (ConstArgs.empty()) {
+ LLVM_DEBUG(dbgs() << "FnSpecialization: no possible constants found\n");
+ continue;
+ }
+
+ for (auto &CA : ConstArgs) {
+ specializeFunction(CA, CurrentSpecializations);
+ Changed = true;
}
}
@@ -333,15 +363,80 @@ class FunctionSpecializer {
return Clone;
}
- /// This function decides whether to specialize function \p F based on the
- /// known constant values its arguments can take on. Specialization is
- /// performed on the first interesting argument. Specializations based on
- /// additional arguments will be evaluated on following iterations of the
- /// main IPSCCP solve loop. \returns true if the function is specialized and
- /// false otherwise.
- bool specializeFunction(Function *F,
- SmallVectorImpl<Function *> &Specializations) {
+ /// This function decides whether it's worthwhile to specialize function \p F
+ /// based on the known constant values its arguments can take on, i.e. it
+ /// calculates a gain and returns a list of actual arguments that are deemed
+ /// profitable to specialize. Specialization is performed on the first
+ /// interesting argument. Specializations based on additional arguments will
+ /// be evaluated on following iterations of the main IPSCCP solve loop.
+ SmallVector<ArgInfo> calculateGains(Function *F, InstructionCost Cost) {
+ SmallVector<ArgInfo> Worklist;
+ // Determine if we should specialize the function based on the values the
+ // argument can take on. If specialization is not profitable, we continue
+ // on to the next argument.
+ for (Argument &FormalArg : F->args()) {
+ LLVM_DEBUG(dbgs() << "FnSpecialization: Analysing arg: "
+ << FormalArg.getName() << "\n");
+ // Determine if this argument is interesting. If we know the argument can
+ // take on any constant values, they are collected in Constants. If the
+ // argument can only ever equal a constant value in Constants, the
+ // function will be completely specialized, and the IsPartial flag will
+ // be set to false by isArgumentInteresting (that function only adds
+ // values to the Constants list that are deemed profitable).
+ bool IsPartial = true;
+ SmallVector<Constant *> ActualConstArg;
+ if (!isArgumentInteresting(&FormalArg, ActualConstArg, IsPartial)) {
+ LLVM_DEBUG(dbgs() << "FnSpecialization: Argument is not interesting\n");
+ continue;
+ }
+
+ for (auto *ActualArg : ActualConstArg) {
+ InstructionCost Gain =
+ ForceFunctionSpecialization
+ ? 1
+ : getSpecializationBonus(&FormalArg, ActualArg) - Cost;
+
+ if (Gain <= 0)
+ continue;
+ Worklist.push_back({F, &FormalArg, ActualArg, Gain});
+ }
+
+ if (Worklist.empty())
+ continue;
+
+ // Sort the candidates in descending order.
+ llvm::stable_sort(Worklist, [](const ArgInfo &L, const ArgInfo &R) {
+ return L.Gain > R.Gain;
+ });
+
+ // TODO: truncate the worklist to 'MaxConstantsThreshold' candidates if
+ // necessary.
+ if (Worklist.size() > MaxConstantsThreshold) {
+ Worklist.clear();
+ continue;
+ }
+
+ if (IsPartial || Worklist.size() < ActualConstArg.size())
+ for (auto &ActualArg : Worklist)
+ ActualArg.Partial = true;
+
+ LLVM_DEBUG(dbgs() << "Sorted list of candidates by gain:\n";
+ for (auto &C
+ : Worklist) {
+ dbgs() << "- Function = " << C.Fn->getName() << ", ";
+ dbgs() << "FormalArg = " << C.Arg->getName() << ", ";
+ dbgs() << "ActualArg = " << C.Const->getName() << ", ";
+ dbgs() << "Gain = " << C.Gain << "\n";
+ });
+
+ // FIXME: Only one argument per function.
+ break;
+ }
+ return Worklist;
+ }
+ bool isCandidateFunction(Function *F,
+ SmallVectorImpl<Function *> &Specializations) {
// Do not specialize the cloned function again.
if (SpecializedFuncs.contains(F))
return false;
@@ -362,84 +457,33 @@ class FunctionSpecializer {
LLVM_DEBUG(dbgs() << "FnSpecialization: Try function: " << F->getName()
<< "\n");
+ return true;
+ }
- // Determine if it would be profitable to create a specialization of the
- // function where the argument takes on the given constant value. If so,
- // add the constant to Constants.
- auto FnSpecCost = getSpecializationCost(F);
- if (!FnSpecCost.isValid()) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: Invalid specialisation cost.\n");
- return false;
- }
-
- LLVM_DEBUG(dbgs() << "FnSpecialization: func specialisation cost: ";
- FnSpecCost.print(dbgs()); dbgs() << "\n");
-
- // Determine if we should specialize the function based on the values the
- // argument can take on. If specialization is not profitable, we continue
- // on to the next argument.
- for (Argument &A : F->args()) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: Analysing arg: " << A.getName()
- << "\n");
- // True if this will be a partial specialization. We will need to keep
- // the original function around in addition to the added specializations.
- bool IsPartial = true;
-
- // Determine if this argument is interesting. If we know the argument can
- // take on any constant values, they are collected in Constants. If the
- // argument can only ever equal a constant value in Constants, the
- // function will be completely specialized, and the IsPartial flag will
- // be set to false by isArgumentInteresting (that function only adds
- // values to the Constants list that are deemed profitable).
- SmallVector<Constant *, 4> Constants;
- if (!isArgumentInteresting(&A, Constants, FnSpecCost, IsPartial)) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: Argument is not interesting\n");
- continue;
- }
+ void specializeFunction(ArgInfo &AI,
+ SmallVectorImpl<Function *> &Specializations) {
+ Function *Clone = cloneCandidateFunction(AI.Fn);
+ Argument *ClonedArg = Clone->getArg(AI.Arg->getArgNo());
- assert(!Constants.empty() && "No constants on which to specialize");
- LLVM_DEBUG(dbgs() << "FnSpecialization: Argument is interesting!\n"
- << "FnSpecialization: Specializing '" << F->getName()
- << "' on argument: " << A << "\n"
- << "FnSpecialization: Constants are:\n\n";
- for (unsigned I = 0; I < Constants.size(); ++I) dbgs()
- << *Constants[I] << "\n";
- dbgs() << "FnSpecialization: End of constants\n\n");
-
- // Create a version of the function in which the argument is marked
- // constant with the given value.
- for (auto *C : Constants) {
- // Clone the function. We leave the ValueToValueMap empty to allow
- // IPSCCP to propagate the constant arguments.
- Function *Clone = cloneCandidateFunction(F);
- Argument *ClonedArg = Clone->arg_begin() + A.getArgNo();
-
- // Rewrite calls to the function so that they call the clone instead.
- rewriteCallSites(F, Clone, *ClonedArg, C);
-
- // Initialize the lattice state of the arguments of the function clone,
- // marking the argument on which we specialized the function constant
- // with the given value.
- Solver.markArgInFuncSpecialization(F, ClonedArg, C);
-
- // Mark all the specialized functions
- Specializations.push_back(Clone);
- NbFunctionsSpecialized++;
- }
+ // Rewrite calls to the function so that they call the clone instead.
+ rewriteCallSites(AI.Fn, Clone, *ClonedArg, AI.Const);
- // If the function has been completely specialized, the original function
- // is no longer needed. Mark it unreachable.
- if (!IsPartial)
- Solver.markFunctionUnreachable(F);
+ // Initialize the lattice state of the arguments of the function clone,
+ // marking the argument on which we specialized the function constant
+ // with the given value.
+ Solver.markArgInFuncSpecialization(AI.Fn, ClonedArg, AI.Const);
- // FIXME: Only one argument per function.
- return true;
- }
+ // Mark all the specialized functions
+ Specializations.push_back(Clone);
+ NbFunctionsSpecialized++;
- return false;
+ // If the function has been completely specialized, the original function
+ // is no longer needed. Mark it unreachable.
+ if (!AI.Partial)
+ Solver.markFunctionUnreachable(AI.Fn);
}
- /// Compute the cost of specializing function \p F.
+ /// Compute and return the cost of specializing function \p F.
InstructionCost getSpecializationCost(Function *F) {
// Compute the code metrics for the function.
SmallPtrSet<const Value *, 32> EphValues;
@@ -580,7 +624,6 @@ class FunctionSpecializer {
/// argument.
bool isArgumentInteresting(Argument *A,
SmallVectorImpl<Constant *> &Constants,
- const InstructionCost &FnSpecCost,
bool &IsPartial) {
// For now, don't attempt to specialize functions based on the values of
// composite types.
@@ -608,42 +651,8 @@ class FunctionSpecializer {
//
// TODO 2: this currently does not support constants, i.e. integer ranges.
//
- SmallVector<Constant *, 4> PossibleConstants;
- bool AllConstant = getPossibleConstants(A, PossibleConstants);
- if (PossibleConstants.empty()) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: no possible constants found\n");
- return false;
- }
- if (PossibleConstants.size() > MaxConstantsThreshold) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: number of constants found exceed "
- << "the maximum number of constants threshold.\n");
- return false;
- }
-
- for (auto *C : PossibleConstants) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: Constant: " << *C << "\n");
- if (ForceFunctionSpecialization) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: Forced!\n");
- Constants.push_back(C);
- continue;
- }
- if (getSpecializationBonus(A, C) > FnSpecCost) {
- LLVM_DEBUG(dbgs() << "FnSpecialization: profitable!\n");
- Constants.push_back(C);
- } else {
- LLVM_DEBUG(dbgs() << "FnSpecialization: not profitable\n");
- }
- }
-
- // None of the constant values the argument can take on were deemed good
- // candidates on which to specialize the function.
- if (Constants.empty())
- return false;
-
- // This will be a partial specialization if some of the constants were
- // rejected due to their profitability.
- IsPartial = !AllConstant || PossibleConstants.size() != Constants.size();
-
+ IsPartial = !getPossibleConstants(A, Constants);
+ LLVM_DEBUG(dbgs() << "FnSpecialization: interesting arg: " << *A << "\n");
return true;
}
@@ -681,7 +690,7 @@ class FunctionSpecializer {
// For now, constant expressions are fine but only if they are function
// calls.
- if (auto *CE = dyn_cast<ConstantExpr>(V))
+ if (auto *CE = dyn_cast<ConstantExpr>(V))
if (!isa<Function>(CE->getOperand(0)))
return false;
More information about the llvm-commits
mailing list