FunctionSpecialization.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- FunctionSpecialization.h - Function Specialization -----------------===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This specialises functions with constant parameters. Constant parameters
  15. // like function pointers and constant globals are propagated to the callee by
  16. // specializing the function. The main benefit of this pass at the moment is
  17. // that indirect calls are transformed into direct calls, which provides inline
  18. // opportunities that the inliner would not have been able to achieve. That's
  19. // why function specialisation is run before the inliner in the optimisation
  20. // pipeline; that is by design. Otherwise, we would only benefit from constant
  21. // passing, which is a valid use-case too, but hasn't been explored much in
  22. // terms of performance uplifts, cost-model and compile-time impact.
  23. //
  24. // Current limitations:
  25. // - It does not yet handle integer ranges. We do support "literal constants",
  26. // but that's off by default under an option.
  27. // - The cost-model could be further looked into (it mainly focuses on inlining
  28. // benefits),
  29. //
  30. // Ideas:
  31. // - With a function specialization attribute for arguments, we could have
  32. // a direct way to steer function specialization, avoiding the cost-model,
  33. // and thus control compile-times / code-size.
  34. //
  35. // Todos:
  36. // - Specializing recursive functions relies on running the transformation a
  37. // number of times, which is controlled by option
  38. // `func-specialization-max-iters`. Thus, increasing this value and the
  39. // number of iterations, will linearly increase the number of times recursive
  40. // functions get specialized, see also the discussion in
  41. // https://reviews.llvm.org/D106426 for details. Perhaps there is a
  42. // compile-time friendlier way to control/limit the number of specialisations
  43. // for recursive functions.
  44. // - Don't transform the function if function specialization does not trigger;
  45. // the SCCPSolver may make IR changes.
  46. //
  47. // References:
  48. // - 2021 LLVM Dev Mtg “Introducing function specialisation, and can we enable
  49. // it by default?”, https://www.youtube.com/watch?v=zJiCjeXgV5Q
  50. //
  51. //===----------------------------------------------------------------------===//
  52. #ifndef LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
  53. #define LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
  54. #include "llvm/Analysis/CodeMetrics.h"
  55. #include "llvm/Analysis/InlineCost.h"
  56. #include "llvm/Analysis/LoopInfo.h"
  57. #include "llvm/Analysis/TargetTransformInfo.h"
  58. #include "llvm/Transforms/Scalar/SCCP.h"
  59. #include "llvm/Transforms/Utils/Cloning.h"
  60. #include "llvm/Transforms/Utils/SCCPSolver.h"
  61. #include "llvm/Transforms/Utils/SizeOpts.h"
  62. using namespace llvm;
  63. namespace llvm {
  64. // Specialization signature, used to uniquely designate a specialization within
  65. // a function.
  66. struct SpecSig {
  67. // Hashing support, used to distinguish between ordinary, empty, or tombstone
  68. // keys.
  69. unsigned Key = 0;
  70. SmallVector<ArgInfo, 4> Args;
  71. bool operator==(const SpecSig &Other) const {
  72. if (Key != Other.Key || Args.size() != Other.Args.size())
  73. return false;
  74. for (size_t I = 0; I < Args.size(); ++I)
  75. if (Args[I] != Other.Args[I])
  76. return false;
  77. return true;
  78. }
  79. friend hash_code hash_value(const SpecSig &S) {
  80. return hash_combine(hash_value(S.Key),
  81. hash_combine_range(S.Args.begin(), S.Args.end()));
  82. }
  83. };
  84. // Specialization instance.
  85. struct Spec {
  86. // Original function.
  87. Function *F;
  88. // Cloned function, a specialized version of the original one.
  89. Function *Clone = nullptr;
  90. // Specialization signature.
  91. SpecSig Sig;
  92. // Profitability of the specialization.
  93. InstructionCost Gain;
  94. // List of call sites, matching this specialization.
  95. SmallVector<CallBase *> CallSites;
  96. Spec(Function *F, const SpecSig &S, InstructionCost G)
  97. : F(F), Sig(S), Gain(G) {}
  98. Spec(Function *F, const SpecSig &&S, InstructionCost G)
  99. : F(F), Sig(S), Gain(G) {}
  100. };
  101. // Map of potential specializations for each function. The FunctionSpecializer
  102. // keeps the discovered specialisation opportunities for the module in a single
  103. // vector, where the specialisations of each function form a contiguous range.
  104. // This map's value is the beginning and the end of that range.
  105. using SpecMap = DenseMap<Function *, std::pair<unsigned, unsigned>>;
  106. class FunctionSpecializer {
  107. /// The IPSCCP Solver.
  108. SCCPSolver &Solver;
  109. Module &M;
  110. /// Analysis manager, needed to invalidate analyses.
  111. FunctionAnalysisManager *FAM;
  112. /// Analyses used to help determine if a function should be specialized.
  113. std::function<const TargetLibraryInfo &(Function &)> GetTLI;
  114. std::function<TargetTransformInfo &(Function &)> GetTTI;
  115. std::function<AssumptionCache &(Function &)> GetAC;
  116. // The number of functions specialised, used for collecting statistics and
  117. // also in the cost model.
  118. unsigned NbFunctionsSpecialized = 0;
  119. SmallPtrSet<Function *, 32> SpecializedFuncs;
  120. SmallPtrSet<Function *, 32> FullySpecialized;
  121. DenseMap<Function *, CodeMetrics> FunctionMetrics;
  122. public:
  123. FunctionSpecializer(
  124. SCCPSolver &Solver, Module &M, FunctionAnalysisManager *FAM,
  125. std::function<const TargetLibraryInfo &(Function &)> GetTLI,
  126. std::function<TargetTransformInfo &(Function &)> GetTTI,
  127. std::function<AssumptionCache &(Function &)> GetAC)
  128. : Solver(Solver), M(M), FAM(FAM), GetTLI(GetTLI), GetTTI(GetTTI),
  129. GetAC(GetAC) {}
  130. ~FunctionSpecializer() {
  131. // Eliminate dead code.
  132. removeDeadFunctions();
  133. cleanUpSSA();
  134. }
  135. bool isClonedFunction(Function *F) { return SpecializedFuncs.count(F); }
  136. bool run();
  137. private:
  138. Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call);
  139. /// A constant stack value is an AllocaInst that has a single constant
  140. /// value stored to it. Return this constant if such an alloca stack value
  141. /// is a function argument.
  142. Constant *getConstantStackValue(CallInst *Call, Value *Val);
  143. /// Iterate over the argument tracked functions see if there
  144. /// are any new constant values for the call instruction via
  145. /// stack variables.
  146. void promoteConstantStackValues();
  147. /// Clean up fully specialized functions.
  148. void removeDeadFunctions();
  149. /// Remove any ssa_copy intrinsics that may have been introduced.
  150. void cleanUpSSA();
  151. // Compute the code metrics for function \p F.
  152. CodeMetrics &analyzeFunction(Function *F);
  153. /// @brief Find potential specialization opportunities.
  154. /// @param F Function to specialize
  155. /// @param Cost Cost of specializing a function. Final gain is this cost
  156. /// minus benefit
  157. /// @param AllSpecs A vector to add potential specializations to.
  158. /// @param SM A map for a function's specialisation range
  159. /// @return True, if any potential specializations were found
  160. bool findSpecializations(Function *F, InstructionCost Cost,
  161. SmallVectorImpl<Spec> &AllSpecs, SpecMap &SM);
  162. bool isCandidateFunction(Function *F);
  163. /// @brief Create a specialization of \p F and prime the SCCPSolver
  164. /// @param F Function to specialize
  165. /// @param S Which specialization to create
  166. /// @return The new, cloned function
  167. Function *createSpecialization(Function *F, const SpecSig &S);
  168. /// Compute and return the cost of specializing function \p F.
  169. InstructionCost getSpecializationCost(Function *F);
  170. /// Compute a bonus for replacing argument \p A with constant \p C.
  171. InstructionCost getSpecializationBonus(Argument *A, Constant *C,
  172. const LoopInfo &LI);
  173. /// Determine if it is possible to specialise the function for constant values
  174. /// of the formal parameter \p A.
  175. bool isArgumentInteresting(Argument *A);
  176. /// Check if the value \p V (an actual argument) is a constant or can only
  177. /// have a constant value. Return that constant.
  178. Constant *getCandidateConstant(Value *V);
  179. /// @brief Find and update calls to \p F, which match a specialization
  180. /// @param F Orginal function
  181. /// @param Begin Start of a range of possibly matching specialisations
  182. /// @param End End of a range (exclusive) of possibly matching specialisations
  183. void updateCallSites(Function *F, const Spec *Begin, const Spec *End);
  184. };
  185. } // namespace llvm
  186. #endif // LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
  187. #ifdef __GNUC__
  188. #pragma GCC diagnostic pop
  189. #endif