John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 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 | |
Ethan Nicholas | 6f4eee2 | 2021-01-11 12:37:42 -0500 | [diff] [blame] | 14 | #include "src/sksl/SkSLMangler.h" |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 15 | #include "src/sksl/ir/SkSLProgram.h" |
John Stiles | a5f3c31 | 2020-09-22 12:05:16 -0400 | [diff] [blame] | 16 | #include "src/sksl/ir/SkSLVariableReference.h" |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 17 | |
| 18 | namespace SkSL { |
| 19 | |
Ethan Nicholas | 7bd6043 | 2020-09-25 14:31:59 -0400 | [diff] [blame] | 20 | class Block; |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 21 | class Context; |
Ethan Nicholas | 1e9f7f3 | 2020-10-08 05:28:32 -0400 | [diff] [blame] | 22 | class Expression; |
| 23 | class FunctionCall; |
Ethan Nicholas | 6f87de7 | 2020-10-26 15:06:46 -0400 | [diff] [blame] | 24 | class FunctionDefinition; |
John Stiles | 2d7973a | 2020-10-02 15:01:03 -0400 | [diff] [blame] | 25 | struct InlineCandidate; |
| 26 | struct InlineCandidateList; |
Ethan Nicholas | 041fd0a | 2020-10-07 16:42:04 -0400 | [diff] [blame] | 27 | class ModifiersPool; |
Ethan Nicholas | 1e9f7f3 | 2020-10-08 05:28:32 -0400 | [diff] [blame] | 28 | class Statement; |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 29 | class SymbolTable; |
Ethan Nicholas | 041fd0a | 2020-10-07 16:42:04 -0400 | [diff] [blame] | 30 | class Variable; |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 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: |
John Stiles | 7b92044 | 2020-12-17 10:43:41 -0500 | [diff] [blame] | 40 | Inliner(const Context* context) : fContext(context) {} |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 41 | |
John Stiles | 10d39d9 | 2021-05-04 16:13:14 -0400 | [diff] [blame] | 42 | void reset(); |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 43 | |
John Stiles | 915a38c | 2020-09-14 09:38:13 -0400 | [diff] [blame] | 44 | /** Inlines any eligible functions that are found. Returns true if any changes are made. */ |
Brian Osman | 0006ad0 | 2020-11-18 15:38:39 -0500 | [diff] [blame] | 45 | bool analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements, |
John Stiles | 7804758 | 2020-12-16 16:17:41 -0500 | [diff] [blame] | 46 | std::shared_ptr<SymbolTable> symbols, |
Brian Osman | 0006ad0 | 2020-11-18 15:38:39 -0500 | [diff] [blame] | 47 | ProgramUsage* usage); |
John Stiles | 9344262 | 2020-09-11 12:11:27 -0400 | [diff] [blame] | 48 | |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 49 | private: |
John Stiles | e41b4ee | 2020-09-28 12:28:16 -0400 | [diff] [blame] | 50 | using VariableRewriteMap = std::unordered_map<const Variable*, std::unique_ptr<Expression>>; |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 51 | |
John Stiles | 77702f1 | 2020-12-17 14:38:56 -0500 | [diff] [blame] | 52 | enum class ReturnComplexity { |
John Stiles | c5ff486 | 2020-12-22 13:47:05 -0500 | [diff] [blame] | 53 | kSingleSafeReturn, |
John Stiles | 77702f1 | 2020-12-17 14:38:56 -0500 | [diff] [blame] | 54 | kScopedReturns, |
| 55 | kEarlyReturns, |
| 56 | }; |
| 57 | |
John Stiles | d120464 | 2021-02-17 16:30:02 -0500 | [diff] [blame] | 58 | const Program::Settings& settings() const { return fContext->fConfig->fSettings; } |
| 59 | |
Brian Osman | 0006ad0 | 2020-11-18 15:38:39 -0500 | [diff] [blame] | 60 | void buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements, |
John Stiles | 7804758 | 2020-12-16 16:17:41 -0500 | [diff] [blame] | 61 | std::shared_ptr<SymbolTable> symbols, ProgramUsage* usage, |
Brian Osman | 0006ad0 | 2020-11-18 15:38:39 -0500 | [diff] [blame] | 62 | InlineCandidateList* candidateList); |
John Stiles | 2d7973a | 2020-10-02 15:01:03 -0400 | [diff] [blame] | 63 | |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 64 | std::unique_ptr<Expression> inlineExpression(int offset, |
| 65 | VariableRewriteMap* varMap, |
John Stiles | d7cc093 | 2020-11-30 12:24:27 -0500 | [diff] [blame] | 66 | SymbolTable* symbolTableForExpression, |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 67 | const Expression& expression); |
| 68 | std::unique_ptr<Statement> inlineStatement(int offset, |
| 69 | VariableRewriteMap* varMap, |
| 70 | SymbolTable* symbolTableForStatement, |
John Stiles | 77702f1 | 2020-12-17 14:38:56 -0500 | [diff] [blame] | 71 | std::unique_ptr<Expression>* resultExpr, |
| 72 | ReturnComplexity returnComplexity, |
Brian Osman | 3887a01 | 2020-09-30 13:22:27 -0400 | [diff] [blame] | 73 | const Statement& statement, |
| 74 | bool isBuiltinCode); |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 75 | |
John Stiles | 77702f1 | 2020-12-17 14:38:56 -0500 | [diff] [blame] | 76 | /** Determines if a given function has multiple and/or early returns. */ |
| 77 | static ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef); |
| 78 | |
John Stiles | 2d7973a | 2020-10-02 15:01:03 -0400 | [diff] [blame] | 79 | using InlinabilityCache = std::unordered_map<const FunctionDeclaration*, bool>; |
| 80 | bool candidateCanBeInlined(const InlineCandidate& candidate, InlinabilityCache* cache); |
| 81 | |
John Stiles | 9b9415e | 2020-11-23 14:48:06 -0500 | [diff] [blame] | 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 | }; |
John Stiles | 7804758 | 2020-12-16 16:17:41 -0500 | [diff] [blame] | 94 | InlinedCall inlineCall(FunctionCall*, |
| 95 | std::shared_ptr<SymbolTable>, |
John Stiles | 30fce9c | 2021-03-18 09:24:06 -0400 | [diff] [blame] | 96 | const ProgramUsage&, |
John Stiles | 7804758 | 2020-12-16 16:17:41 -0500 | [diff] [blame] | 97 | const FunctionDeclaration* caller); |
John Stiles | 9b9415e | 2020-11-23 14:48:06 -0500 | [diff] [blame] | 98 | |
John Stiles | 7b92044 | 2020-12-17 10:43:41 -0500 | [diff] [blame] | 99 | /** Creates a scratch variable for the inliner to use. */ |
| 100 | struct InlineVariable { |
| 101 | const Variable* fVarSymbol; |
| 102 | std::unique_ptr<Statement> fVarDecl; |
| 103 | }; |
John Stiles | 769146f | 2021-09-15 21:47:00 -0400 | [diff] [blame] | 104 | InlineVariable makeInlineVariable(skstd::string_view baseName, |
John Stiles | 7b92044 | 2020-12-17 10:43:41 -0500 | [diff] [blame] | 105 | const Type* type, |
| 106 | SymbolTable* symbolTable, |
| 107 | Modifiers modifiers, |
| 108 | bool isBuiltinCode, |
| 109 | std::unique_ptr<Expression>* initialValue); |
| 110 | |
John Stiles | 9b9415e | 2020-11-23 14:48:06 -0500 | [diff] [blame] | 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); |
John Stiles | 2d7973a | 2020-10-02 15:01:03 -0400 | [diff] [blame] | 116 | |
John Stiles | 10d39d9 | 2021-05-04 16:13:14 -0400 | [diff] [blame] | 117 | ModifiersPool& modifiersPool() const { return *fContext->fModifiersPool; } |
John Stiles | f2872e6 | 2021-05-04 11:38:43 -0400 | [diff] [blame] | 118 | |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 119 | const Context* fContext = nullptr; |
John Stiles | 031a767 | 2020-11-13 16:13:18 -0500 | [diff] [blame] | 120 | int fInlinedStatementCounter = 0; |
John Stiles | 44e96be | 2020-08-31 13:16:04 -0400 | [diff] [blame] | 121 | }; |
| 122 | |
| 123 | } // namespace SkSL |
| 124 | |
| 125 | #endif // SKSL_INLINER |