xref: /aosp_15_r20/external/skia/src/sksl/SkSLInliner.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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