123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- FunctionSpecialization.h - Function Specialization -----------------===//
- //
- // 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 specialises functions with constant parameters. Constant parameters
- // like function pointers and constant globals are propagated to the callee by
- // specializing the function. The main benefit of this pass at the moment is
- // that indirect calls are transformed into direct calls, which provides inline
- // opportunities that the inliner would not have been able to achieve. That's
- // why function specialisation is run before the inliner in the optimisation
- // pipeline; that is by design. Otherwise, we would only benefit from constant
- // passing, which is a valid use-case too, but hasn't been explored much in
- // terms of performance uplifts, cost-model and compile-time impact.
- //
- // Current limitations:
- // - It does not yet handle integer ranges. We do support "literal constants",
- // but that's off by default under an option.
- // - The cost-model could be further looked into (it mainly focuses on inlining
- // benefits),
- //
- // Ideas:
- // - With a function specialization attribute for arguments, we could have
- // a direct way to steer function specialization, avoiding the cost-model,
- // and thus control compile-times / code-size.
- //
- // Todos:
- // - Specializing recursive functions relies on running the transformation a
- // number of times, which is controlled by option
- // `func-specialization-max-iters`. Thus, increasing this value and the
- // number of iterations, will linearly increase the number of times recursive
- // functions get specialized, see also the discussion in
- // https://reviews.llvm.org/D106426 for details. Perhaps there is a
- // compile-time friendlier way to control/limit the number of specialisations
- // for recursive functions.
- // - Don't transform the function if function specialization does not trigger;
- // the SCCPSolver may make IR changes.
- //
- // References:
- // - 2021 LLVM Dev Mtg “Introducing function specialisation, and can we enable
- // it by default?”, https://www.youtube.com/watch?v=zJiCjeXgV5Q
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
- #define LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
- #include "llvm/Analysis/CodeMetrics.h"
- #include "llvm/Analysis/InlineCost.h"
- #include "llvm/Analysis/LoopInfo.h"
- #include "llvm/Analysis/TargetTransformInfo.h"
- #include "llvm/Transforms/Scalar/SCCP.h"
- #include "llvm/Transforms/Utils/Cloning.h"
- #include "llvm/Transforms/Utils/SCCPSolver.h"
- #include "llvm/Transforms/Utils/SizeOpts.h"
- using namespace llvm;
- namespace llvm {
- // Specialization signature, used to uniquely designate a specialization within
- // a function.
- struct SpecSig {
- // Hashing support, used to distinguish between ordinary, empty, or tombstone
- // keys.
- unsigned Key = 0;
- SmallVector<ArgInfo, 4> Args;
- bool operator==(const SpecSig &Other) const {
- if (Key != Other.Key || Args.size() != Other.Args.size())
- return false;
- for (size_t I = 0; I < Args.size(); ++I)
- if (Args[I] != Other.Args[I])
- return false;
- return true;
- }
- friend hash_code hash_value(const SpecSig &S) {
- return hash_combine(hash_value(S.Key),
- hash_combine_range(S.Args.begin(), S.Args.end()));
- }
- };
- // Specialization instance.
- struct Spec {
- // Original function.
- Function *F;
- // Cloned function, a specialized version of the original one.
- Function *Clone = nullptr;
- // Specialization signature.
- SpecSig Sig;
- // Profitability of the specialization.
- InstructionCost Gain;
- // List of call sites, matching this specialization.
- SmallVector<CallBase *> CallSites;
- Spec(Function *F, const SpecSig &S, InstructionCost G)
- : F(F), Sig(S), Gain(G) {}
- Spec(Function *F, const SpecSig &&S, InstructionCost G)
- : F(F), Sig(S), Gain(G) {}
- };
- // Map of potential specializations for each function. The FunctionSpecializer
- // keeps the discovered specialisation opportunities for the module in a single
- // vector, where the specialisations of each function form a contiguous range.
- // This map's value is the beginning and the end of that range.
- using SpecMap = DenseMap<Function *, std::pair<unsigned, unsigned>>;
- class FunctionSpecializer {
- /// The IPSCCP Solver.
- SCCPSolver &Solver;
- Module &M;
- /// Analysis manager, needed to invalidate analyses.
- FunctionAnalysisManager *FAM;
- /// Analyses used to help determine if a function should be specialized.
- std::function<const TargetLibraryInfo &(Function &)> GetTLI;
- std::function<TargetTransformInfo &(Function &)> GetTTI;
- std::function<AssumptionCache &(Function &)> GetAC;
- // The number of functions specialised, used for collecting statistics and
- // also in the cost model.
- unsigned NbFunctionsSpecialized = 0;
- SmallPtrSet<Function *, 32> SpecializedFuncs;
- SmallPtrSet<Function *, 32> FullySpecialized;
- DenseMap<Function *, CodeMetrics> FunctionMetrics;
- public:
- FunctionSpecializer(
- SCCPSolver &Solver, Module &M, FunctionAnalysisManager *FAM,
- std::function<const TargetLibraryInfo &(Function &)> GetTLI,
- std::function<TargetTransformInfo &(Function &)> GetTTI,
- std::function<AssumptionCache &(Function &)> GetAC)
- : Solver(Solver), M(M), FAM(FAM), GetTLI(GetTLI), GetTTI(GetTTI),
- GetAC(GetAC) {}
- ~FunctionSpecializer() {
- // Eliminate dead code.
- removeDeadFunctions();
- cleanUpSSA();
- }
- bool isClonedFunction(Function *F) { return SpecializedFuncs.count(F); }
- bool run();
- private:
- Constant *getPromotableAlloca(AllocaInst *Alloca, CallInst *Call);
- /// A constant stack value is an AllocaInst that has a single constant
- /// value stored to it. Return this constant if such an alloca stack value
- /// is a function argument.
- Constant *getConstantStackValue(CallInst *Call, Value *Val);
- /// Iterate over the argument tracked functions see if there
- /// are any new constant values for the call instruction via
- /// stack variables.
- void promoteConstantStackValues();
- /// Clean up fully specialized functions.
- void removeDeadFunctions();
- /// Remove any ssa_copy intrinsics that may have been introduced.
- void cleanUpSSA();
- // Compute the code metrics for function \p F.
- CodeMetrics &analyzeFunction(Function *F);
- /// @brief Find potential specialization opportunities.
- /// @param F Function to specialize
- /// @param Cost Cost of specializing a function. Final gain is this cost
- /// minus benefit
- /// @param AllSpecs A vector to add potential specializations to.
- /// @param SM A map for a function's specialisation range
- /// @return True, if any potential specializations were found
- bool findSpecializations(Function *F, InstructionCost Cost,
- SmallVectorImpl<Spec> &AllSpecs, SpecMap &SM);
- bool isCandidateFunction(Function *F);
- /// @brief Create a specialization of \p F and prime the SCCPSolver
- /// @param F Function to specialize
- /// @param S Which specialization to create
- /// @return The new, cloned function
- Function *createSpecialization(Function *F, const SpecSig &S);
- /// Compute and return the cost of specializing function \p F.
- InstructionCost getSpecializationCost(Function *F);
- /// Compute a bonus for replacing argument \p A with constant \p C.
- InstructionCost getSpecializationBonus(Argument *A, Constant *C,
- const LoopInfo &LI);
- /// Determine if it is possible to specialise the function for constant values
- /// of the formal parameter \p A.
- bool isArgumentInteresting(Argument *A);
- /// Check if the value \p V (an actual argument) is a constant or can only
- /// have a constant value. Return that constant.
- Constant *getCandidateConstant(Value *V);
- /// @brief Find and update calls to \p F, which match a specialization
- /// @param F Orginal function
- /// @param Begin Start of a range of possibly matching specialisations
- /// @param End End of a range (exclusive) of possibly matching specialisations
- void updateCallSites(Function *F, const Spec *Begin, const Spec *End);
- };
- } // namespace llvm
- #endif // LLVM_TRANSFORMS_IPO_FUNCTIONSPECIALIZATION_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|