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