1 /* 2 * Copyright 2020 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SKSL_INLINER 9 #define SKSL_INLINER 10 11 #ifndef SK_ENABLE_OPTIMIZE_SIZE 12 13 #include "src/core/SkTHash.h" 14 #include "src/sksl/SkSLContext.h" 15 #include "src/sksl/SkSLMangler.h" 16 #include "src/sksl/SkSLProgramSettings.h" 17 #include "src/sksl/ir/SkSLBlock.h" 18 #include "src/sksl/ir/SkSLExpression.h" 19 20 #include <memory> 21 #include <vector> 22 23 namespace SkSL { 24 25 class FunctionCall; 26 class FunctionDeclaration; 27 class FunctionDefinition; 28 class Position; 29 class ProgramElement; 30 class ProgramUsage; 31 class Statement; 32 class SymbolTable; 33 class Variable; 34 struct InlineCandidate; 35 struct InlineCandidateList; 36 namespace Analysis { enum class ReturnComplexity; } 37 38 /** 39 * Converts a FunctionCall in the IR to a set of statements to be injected ahead of the function 40 * call, and a replacement expression. Can also detect cases where inlining isn't cleanly possible 41 * (e.g. return statements nested inside of a loop construct). The inliner isn't able to guarantee 42 * identical-to-GLSL execution order if the inlined function has visible side effects. 43 */ 44 class Inliner { 45 public: Inliner(const Context * context)46 Inliner(const Context* context) : fContext(context) {} 47 48 /** Inlines any eligible functions that are found. Returns true if any changes are made. */ 49 bool analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements, 50 SymbolTable* symbols, 51 ProgramUsage* usage); 52 53 private: 54 using VariableRewriteMap = skia_private::THashMap<const Variable*, std::unique_ptr<Expression>>; 55 settings()56 const ProgramSettings& settings() const { return fContext->fConfig->fSettings; } 57 58 void buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements, 59 SymbolTable* symbols, 60 ProgramUsage* usage, 61 InlineCandidateList* candidateList); 62 63 std::unique_ptr<Expression> inlineExpression(Position pos, 64 VariableRewriteMap* varMap, 65 SymbolTable* symbolTableForExpression, 66 const Expression& expression); 67 std::unique_ptr<Statement> inlineStatement(Position pos, 68 VariableRewriteMap* varMap, 69 SymbolTable* symbolTableForStatement, 70 std::unique_ptr<Expression>* resultExpr, 71 Analysis::ReturnComplexity returnComplexity, 72 const Statement& statement, 73 const ProgramUsage& usage, 74 bool isBuiltinCode); 75 76 /** 77 * Searches the rewrite map for an rewritten Variable* for the passed-in one. Asserts if the 78 * rewrite map doesn't contain the variable, or contains a different type of expression. 79 */ 80 static const Variable* RemapVariable(const Variable* variable, 81 const VariableRewriteMap* varMap); 82 83 using InlinabilityCache = skia_private::THashMap<const FunctionDeclaration*, bool>; 84 bool candidateCanBeInlined(const InlineCandidate& candidate, 85 const ProgramUsage& usage, 86 InlinabilityCache* cache); 87 88 bool functionCanBeInlined(const FunctionDeclaration& funcDecl, 89 const ProgramUsage& usage, 90 InlinabilityCache* cache); 91 92 using FunctionSizeCache = skia_private::THashMap<const FunctionDeclaration*, int>; 93 int getFunctionSize(const FunctionDeclaration& fnDecl, FunctionSizeCache* cache); 94 95 /** 96 * Processes the passed-in FunctionCall expression. The FunctionCall expression should be 97 * replaced with `fReplacementExpr`. If non-null, `fInlinedBody` should be inserted immediately 98 * above the statement containing the inlined expression. 99 */ 100 struct InlinedCall { 101 std::unique_ptr<Block> fInlinedBody; 102 std::unique_ptr<Expression> fReplacementExpr; 103 }; 104 InlinedCall inlineCall(const FunctionCall&, 105 SymbolTable*, 106 const ProgramUsage&, 107 const FunctionDeclaration* caller); 108 109 /** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */ 110 void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt); 111 112 /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */ 113 bool isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage); 114 115 const Context* fContext = nullptr; 116 Mangler fMangler; 117 int fInlinedStatementCounter = 0; 118 }; 119 120 } // namespace SkSL 121 122 #endif // SK_ENABLE_OPTIMIZE_SIZE 123 124 #endif // SKSL_INLINER 125