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 offset, 65 VariableRewriteMap* varMap, 66 SymbolTable* symbolTableForExpression, 67 const Expression& expression); 68 std::unique_ptr<Statement> inlineStatement(int offset, 69 VariableRewriteMap* varMap, 70 SymbolTable* symbolTableForStatement, 71 std::unique_ptr<Expression>* resultExpr, 72 ReturnComplexity returnComplexity, 73 const Statement& statement, 74 bool isBuiltinCode); 75 76 /** Determines if a given function has multiple and/or early returns. */ 77 static ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef); 78 79 using InlinabilityCache = std::unordered_map<const FunctionDeclaration*, bool>; 80 bool candidateCanBeInlined(const InlineCandidate& candidate, InlinabilityCache* cache); 81 82 using FunctionSizeCache = std::unordered_map<const FunctionDeclaration*, int>; 83 int getFunctionSize(const FunctionDeclaration& fnDecl, FunctionSizeCache* cache); 84 85 /** 86 * Processes the passed-in FunctionCall expression. The FunctionCall expression should be 87 * replaced with `fReplacementExpr`. If non-null, `fInlinedBody` should be inserted immediately 88 * above the statement containing the inlined expression. 89 */ 90 struct InlinedCall { 91 std::unique_ptr<Block> fInlinedBody; 92 std::unique_ptr<Expression> fReplacementExpr; 93 }; 94 InlinedCall inlineCall(FunctionCall*, 95 std::shared_ptr<SymbolTable>, 96 const ProgramUsage&, 97 const FunctionDeclaration* caller); 98 99 /** Creates a scratch variable for the inliner to use. */ 100 struct InlineVariable { 101 const Variable* fVarSymbol; 102 std::unique_ptr<Statement> fVarDecl; 103 }; 104 InlineVariable makeInlineVariable(const String& baseName, 105 const Type* type, 106 SymbolTable* symbolTable, 107 Modifiers modifiers, 108 bool isBuiltinCode, 109 std::unique_ptr<Expression>* initialValue); 110 111 /** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */ 112 void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt); 113 114 /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */ 115 bool isSafeToInline(const FunctionDefinition* functionDef); 116 modifiersPool()117 ModifiersPool& modifiersPool() const { return *fContext->fModifiersPool; } 118 119 const Context* fContext = nullptr; 120 Mangler fMangler; 121 int fInlinedStatementCounter = 0; 122 }; 123 124 } // namespace SkSL 125 126 #endif // SKSL_INLINER 127