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 #include <memory> 12 #include <unordered_map> 13 14 #include "src/sksl/SkSLMangler.h" 15 #include "src/sksl/ir/SkSLProgram.h" 16 #include "src/sksl/ir/SkSLVariableReference.h" 17 18 namespace SkSL { 19 20 class Block; 21 class Context; 22 class Expression; 23 class FunctionCall; 24 class FunctionDefinition; 25 struct InlineCandidate; 26 struct InlineCandidateList; 27 class ModifiersPool; 28 class Statement; 29 class SymbolTable; 30 class Variable; 31 32 /** 33 * Converts a FunctionCall in the IR to a set of statements to be injected ahead of the function 34 * call, and a replacement expression. Can also detect cases where inlining isn't cleanly possible 35 * (e.g. return statements nested inside of a loop construct). The inliner isn't able to guarantee 36 * identical-to-GLSL execution order if the inlined function has visible side effects. 37 */ 38 class Inliner { 39 public: Inliner(const Context * context)40 Inliner(const Context* context) : fContext(context) {} 41 42 void reset(); 43 44 /** Inlines any eligible functions that are found. Returns true if any changes are made. */ 45 bool analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements, 46 std::shared_ptr<SymbolTable> symbols, 47 ProgramUsage* usage); 48 49 private: 50 using VariableRewriteMap = std::unordered_map<const Variable*, std::unique_ptr<Expression>>; 51 52 enum class ReturnComplexity { 53 kSingleSafeReturn, 54 kScopedReturns, 55 kEarlyReturns, 56 }; 57 settings()58 const Program::Settings& settings() const { return fContext->fConfig->fSettings; } 59 60 void buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements, 61 std::shared_ptr<SymbolTable> symbols, ProgramUsage* usage, 62 InlineCandidateList* candidateList); 63 64 std::unique_ptr<Expression> inlineExpression(int line, 65 VariableRewriteMap* varMap, 66 SymbolTable* symbolTableForExpression, 67 const Expression& expression); 68 std::unique_ptr<Statement> inlineStatement(int line, 69 VariableRewriteMap* varMap, 70 SymbolTable* symbolTableForStatement, 71 std::unique_ptr<Expression>* resultExpr, 72 ReturnComplexity returnComplexity, 73 const Statement& statement, 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 /** Determines if a given function has multiple and/or early returns. */ 84 static ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef); 85 86 using InlinabilityCache = std::unordered_map<const FunctionDeclaration*, bool>; 87 bool candidateCanBeInlined(const InlineCandidate& candidate, 88 const ProgramUsage& usage, 89 InlinabilityCache* cache); 90 91 using FunctionSizeCache = std::unordered_map<const FunctionDeclaration*, int>; 92 int getFunctionSize(const FunctionDeclaration& fnDecl, FunctionSizeCache* cache); 93 94 /** 95 * Processes the passed-in FunctionCall expression. The FunctionCall expression should be 96 * replaced with `fReplacementExpr`. If non-null, `fInlinedBody` should be inserted immediately 97 * above the statement containing the inlined expression. 98 */ 99 struct InlinedCall { 100 std::unique_ptr<Block> fInlinedBody; 101 std::unique_ptr<Expression> fReplacementExpr; 102 }; 103 InlinedCall inlineCall(FunctionCall*, 104 std::shared_ptr<SymbolTable>, 105 const ProgramUsage&, 106 const FunctionDeclaration* caller); 107 108 /** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */ 109 void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt); 110 111 /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */ 112 bool isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage); 113 114 const Context* fContext = nullptr; 115 int fInlinedStatementCounter = 0; 116 }; 117 118 } // namespace SkSL 119 120 #endif // SKSL_INLINER 121