1 //===- FunctionSpecialization.h - Function Specialization -----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This specialises functions with constant parameters. Constant parameters
10 // like function pointers and constant globals are propagated to the callee by
11 // specializing the function. The main benefit of this pass at the moment is
12 // that indirect calls are transformed into direct calls, which provides inline
13 // opportunities that the inliner would not have been able to achieve. That's
14 // why function specialisation is run before the inliner in the optimisation
15 // pipeline; that is by design. Otherwise, we would only benefit from constant
16 // passing, which is a valid use-case too, but hasn't been explored much in
17 // terms of performance uplifts, cost-model and compile-time impact.
18 //
19 // Current limitations:
20 // - It does not yet handle integer ranges. We do support "literal constants",
21 //   but that's off by default under an option.
22 // - The cost-model could be further looked into (it mainly focuses on inlining
23 //   benefits),
24 //
25 // Ideas:
26 // - With a function specialization attribute for arguments, we could have
27 //   a direct way to steer function specialization, avoiding the cost-model,
28 //   and thus control compile-times / code-size.
29 //
30 // Todos:
31 // - Specializing recursive functions relies on running the transformation a
32 //   number of times, which is controlled by option
33 //   `func-specialization-max-iters`. Thus, increasing this value and the
34 //   number of iterations, will linearly increase the number of times recursive
35 //   functions get specialized, see also the discussion in
36 //   https://reviews.llvm.org/D106426 for details. Perhaps there is a
37 //   compile-time friendlier way to control/limit the number of specialisations
38 //   for recursive functions.
39 // - Don't transform the function if function specialization does not trigger;
40 //   the SCCPSolver may make IR changes.
41 //
42 // References:
43 // - 2021 LLVM Dev Mtg “Introducing function specialisation, and can we enable
44 //   it by default?”, https://www.youtube.com/watch?v=zJiCjeXgV5Q
45 //
46 //===----------------------------------------------------------------------===//
47 
48 #ifndef LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
49 #define LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
50 
51 #include "llvm/Analysis/BlockFrequencyInfo.h"
52 #include "llvm/Analysis/CodeMetrics.h"
53 #include "llvm/Analysis/InlineCost.h"
54 #include "llvm/Analysis/TargetTransformInfo.h"
55 #include "llvm/IR/InstVisitor.h"
56 #include "llvm/Transforms/Scalar/SCCP.h"
57 #include "llvm/Transforms/Utils/Cloning.h"
58 #include "llvm/Transforms/Utils/SCCPSolver.h"
59 #include "llvm/Transforms/Utils/SizeOpts.h"
60 
61 using namespace llvm;
62 
63 namespace llvm {
64 // Map of potential specializations for each function. The FunctionSpecializer
65 // keeps the discovered specialisation opportunities for the module in a single
66 // vector, where the specialisations of each function form a contiguous range.
67 // This map's value is the beginning and the end of that range.
68 using SpecMap = DenseMap<Function *, std::pair<unsigned, unsigned>>;
69 
70 // Just a shorter abbreviation to improve indentation.
71 using Cost = InstructionCost;
72 
73 // Map of known constants found during the specialization bonus estimation.
74 using ConstMap = DenseMap<Value *, Constant *>;
75 
76 // Specialization signature, used to uniquely designate a specialization within
77 // a function.
78 struct SpecSig {
79   // Hashing support, used to distinguish between ordinary, empty, or tombstone
80   // keys.
81   unsigned Key = 0;
82   SmallVector<ArgInfo, 4> Args;
83 
84   bool operator==(const SpecSig &Other) const {
85     if (Key != Other.Key || Args.size() != Other.Args.size())
86       return false;
87     for (size_t I = 0; I < Args.size(); ++I)
88       if (Args[I] != Other.Args[I])
89         return false;
90     return true;
91   }
92 
93   friend hash_code hash_value(const SpecSig &S) {
94     return hash_combine(hash_value(S.Key),
95                         hash_combine_range(S.Args.begin(), S.Args.end()));
96   }
97 };
98 
99 // Specialization instance.
100 struct Spec {
101   // Original function.
102   Function *F;
103 
104   // Cloned function, a specialized version of the original one.
105   Function *Clone = nullptr;
106 
107   // Specialization signature.
108   SpecSig Sig;
109 
110   // Profitability of the specialization.
111   Cost Score;
112 
113   // List of call sites, matching this specialization.
114   SmallVector<CallBase *> CallSites;
115 
116   Spec(Function *F, const SpecSig &S, Cost Score)
117       : F(F), Sig(S), Score(Score) {}
118   Spec(Function *F, const SpecSig &&S, Cost Score)
119       : F(F), Sig(S), Score(Score) {}
120 };
121 
122 class InstCostVisitor : public InstVisitor<InstCostVisitor, Constant *> {
123   const DataLayout &DL;
124   BlockFrequencyInfo &BFI;
125   TargetTransformInfo &TTI;
126   SCCPSolver &Solver;
127 
128   ConstMap KnownConstants;
129 
130   ConstMap::iterator LastVisited;
131 
132 public:
133   InstCostVisitor(const DataLayout &DL, BlockFrequencyInfo &BFI,
134                   TargetTransformInfo &TTI, SCCPSolver &Solver)
135       : DL(DL), BFI(BFI), TTI(TTI), Solver(Solver) {}
136 
137   Cost getUserBonus(Instruction *User, Value *Use, Constant *C);
138 
139 private:
140   friend class InstVisitor<InstCostVisitor, Constant *>;
141 
142   Cost estimateSwitchInst(SwitchInst &I);
143   Cost estimateBranchInst(BranchInst &I);
144 
145   Constant *visitInstruction(Instruction &I) { return nullptr; }
146   Constant *visitFreezeInst(FreezeInst &I);
147   Constant *visitCallBase(CallBase &I);
148   Constant *visitLoadInst(LoadInst &I);
149   Constant *visitGetElementPtrInst(GetElementPtrInst &I);
150   Constant *visitSelectInst(SelectInst &I);
151   Constant *visitCastInst(CastInst &I);
152   Constant *visitCmpInst(CmpInst &I);
153   Constant *visitUnaryOperator(UnaryOperator &I);
154   Constant *visitBinaryOperator(BinaryOperator &I);
155 };
156 
157 class FunctionSpecializer {
158 
159   /// The IPSCCP Solver.
160   SCCPSolver &Solver;
161 
162   Module &M;
163 
164   /// Analysis manager, needed to invalidate analyses.
165   FunctionAnalysisManager *FAM;
166 
167   /// Analyses used to help determine if a function should be specialized.
168   std::function<BlockFrequencyInfo &(Function &)> GetBFI;
169   std::function<const TargetLibraryInfo &(Function &)> GetTLI;
170   std::function<TargetTransformInfo &(Function &)> GetTTI;
171   std::function<AssumptionCache &(Function &)> GetAC;
172 
173   SmallPtrSet<Function *, 32> Specializations;
174   SmallPtrSet<Function *, 32> FullySpecialized;
175   DenseMap<Function *, CodeMetrics> FunctionMetrics;
176 
177 public:
178   FunctionSpecializer(
179       SCCPSolver &Solver, Module &M, FunctionAnalysisManager *FAM,
180       std::function<BlockFrequencyInfo &(Function &)> GetBFI,
181       std::function<const TargetLibraryInfo &(Function &)> GetTLI,
182       std::function<TargetTransformInfo &(Function &)> GetTTI,
183       std::function<AssumptionCache &(Function &)> GetAC)
184       : Solver(Solver), M(M), FAM(FAM), GetBFI(GetBFI), GetTLI(GetTLI),
185         GetTTI(GetTTI), GetAC(GetAC) {}
186 
187   ~FunctionSpecializer();
188 
189   bool run();
190 
191   InstCostVisitor getInstCostVisitorFor(Function *F) {
192     auto &BFI = GetBFI(*F);
193     auto &TTI = GetTTI(*F);
194     return InstCostVisitor(M.getDataLayout(), BFI, TTI, Solver);
195   }
196 
197   /// Compute a bonus for replacing argument \p A with constant \p C.
198   Cost getSpecializationBonus(Argument *A, Constant *C,
199                               InstCostVisitor &Visitor);
200 
201 private:
202   Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call);
203 
204   /// A constant stack value is an AllocaInst that has a single constant
205   /// value stored to it. Return this constant if such an alloca stack value
206   /// is a function argument.
207   Constant *getConstantStackValue(CallInst *Call, Value *Val);
208 
209   /// See if there are any new constant values for the callers of \p F via
210   /// stack variables and promote them to global variables.
211   void promoteConstantStackValues(Function *F);
212 
213   /// Clean up fully specialized functions.
214   void removeDeadFunctions();
215 
216   /// Remove any ssa_copy intrinsics that may have been introduced.
217   void cleanUpSSA();
218 
219   /// @brief  Find potential specialization opportunities.
220   /// @param F Function to specialize
221   /// @param SpecCost Cost of specializing a function. Final score is benefit
222   /// minus this cost.
223   /// @param AllSpecs A vector to add potential specializations to.
224   /// @param SM  A map for a function's specialisation range
225   /// @return True, if any potential specializations were found
226   bool findSpecializations(Function *F, Cost SpecCost,
227                            SmallVectorImpl<Spec> &AllSpecs, SpecMap &SM);
228 
229   bool isCandidateFunction(Function *F);
230 
231   /// @brief Create a specialization of \p F and prime the SCCPSolver
232   /// @param F Function to specialize
233   /// @param S Which specialization to create
234   /// @return The new, cloned function
235   Function *createSpecialization(Function *F, const SpecSig &S);
236 
237   /// Determine if it is possible to specialise the function for constant values
238   /// of the formal parameter \p A.
239   bool isArgumentInteresting(Argument *A);
240 
241   /// Check if the value \p V  (an actual argument) is a constant or can only
242   /// have a constant value. Return that constant.
243   Constant *getCandidateConstant(Value *V);
244 
245   /// @brief Find and update calls to \p F, which match a specialization
246   /// @param F Orginal function
247   /// @param Begin Start of a range of possibly matching specialisations
248   /// @param End End of a range (exclusive) of possibly matching specialisations
249   void updateCallSites(Function *F, const Spec *Begin, const Spec *End);
250 };
251 } // namespace llvm
252 
253 #endif // LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
254