xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLTransform.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 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_TRANSFORM
9 #define SKSL_TRANSFORM
10 
11 #include "src/sksl/SkSLDefines.h"
12 #include "src/sksl/ir/SkSLModifierFlags.h"
13 
14 #include <cstdint>
15 #include <memory>
16 
17 namespace SkSL {
18 
19 class Block;
20 class Context;
21 class Expression;
22 class IndexExpression;
23 class Position;
24 class ProgramUsage;
25 class SymbolTable;
26 class Variable;
27 enum class ProgramKind : int8_t;
28 struct Module;
29 struct Program;
30 
31 namespace Transform {
32 
33 /**
34  * Checks to see if it would be safe to add `const` to the modifier flags of a variable. If so,
35  * returns the modifiers with `const` applied; if not, returns the existing modifiers as-is. Adding
36  * `const` allows the inliner to fold away more values and generate tighter code.
37  */
38 ModifierFlags AddConstToVarModifiers(const Variable& var,
39                                      const Expression* initialValue,
40                                      const ProgramUsage* usage);
41 
42 /**
43  * Rewrites indexed swizzles of the form `myVec.zyx[i]` by replacing the swizzle with a lookup into
44  * a constant vector. e.g., the above expression would be rewritten as `myVec[vec3(2, 1, 0)[i]]`.
45  * This roughly matches glslang's handling of the code.
46  */
47 std::unique_ptr<Expression> RewriteIndexedSwizzle(const Context& context,
48                                                   const IndexExpression& swizzle);
49 
50 /**
51  * Copies built-in functions from modules into the program. Relies on ProgramUsage to determine
52  * which functions are necessary.
53  */
54 void FindAndDeclareBuiltinFunctions(Program& program);
55 
56 /**
57  * Copies built-in structs from modules into the program. Relies on ProgramUsage to determine
58  * which structs are necessary.
59  */
60 void FindAndDeclareBuiltinStructs(Program& program);
61 
62 /**
63  * Scans the finished program for built-in variables like `sk_FragColor` and adds them to the
64  * program's shared elements.
65  */
66 void FindAndDeclareBuiltinVariables(Program& program);
67 
68 /**
69  * Eliminates statements in a block which cannot be reached; for example, a statement
70  * immediately after a `return` or `continue` can safely be eliminated.
71  */
72 void EliminateUnreachableCode(Module& module, ProgramUsage* usage);
73 void EliminateUnreachableCode(Program& program);
74 
75 /**
76  * Eliminates empty statements in a module (Nops, or blocks holding only Nops). Not implemented for
77  * Programs because Nops are harmless, but they waste space in long-lived module IR.
78  */
79 void EliminateEmptyStatements(Module& module);
80 
81 /**
82  * Eliminates unnecessary braces in a module (e.g., single-statement child blocks). Not implemented
83  * for Programs because extra braces are harmless, but they waste space in long-lived module IR.
84  */
85 void EliminateUnnecessaryBraces(const Context& context, Module& module);
86 
87 /**
88  * Replaces splat-casts like `float4(myFloat)` with `myFloat.xxxx`. This should be slightly smaller
89  * in textual form, and will be optimized back to the splat-cast at load time.
90  */
91 void ReplaceSplatCastsWithSwizzles(const Context& context, Module& module);
92 
93 /**
94  * Eliminates functions in a program which are never called. Returns true if any changes were made.
95  */
96 bool EliminateDeadFunctions(const Context& context, Module& module, ProgramUsage* usage);
97 bool EliminateDeadFunctions(Program& program);
98 
99 /**
100  * Eliminates variables in a program which are never read or written (past their initializer).
101  * Preserves side effects from initializers, if any. Returns true if any changes were made.
102  */
103 bool EliminateDeadLocalVariables(const Context& context,
104                                  Module& module,
105                                  ProgramUsage* usage);
106 bool EliminateDeadLocalVariables(Program& program);
107 bool EliminateDeadGlobalVariables(const Context& context,
108                                   Module& module,
109                                   ProgramUsage* usage,
110                                   bool onlyPrivateGlobals);
111 bool EliminateDeadGlobalVariables(Program& program);
112 
113 /** Renames private functions and function-local variables to minimize code size. */
114 void RenamePrivateSymbols(Context& context, Module& module, ProgramUsage* usage, ProgramKind kind);
115 
116 /** Replaces constant variables in a program with their equivalent values. */
117 void ReplaceConstVarsWithLiterals(Module& module, ProgramUsage* usage);
118 
119 /**
120  * Looks for variables inside of the top-level of switch-cases, such as:
121  *
122  *    case 1: int i;         // `i` is at top-level
123  *    case 2: float f = 5.0; // `f` is at top-level, and has an initial-value assignment
124  *    case 3: { bool b; }    // `b` is not at top-level; it has an additional scope
125  *
126  * If any top-level variables are found, a scoped block is created and returned which holds the
127  * variable declarations from the switch-cases and into the outer scope. (Variables with additional
128  * scoping are left as-is.) Then, we replace the declarations with nops or assignment statements.
129  * That is, we would return a Block like this:
130  *
131  *    {
132  *        int i;
133  *        float f;
134  *    }
135  *
136  * And we would also mutate the passed-in case statements to eliminate the variable decarations:
137  *
138  *    case 1: Nop;         // `i` is declared in the returned block and needs no initialization
139  *    case 2: f = 5.0;     // `f` is declared in the returned block and initialized here
140  *    case 3: { bool b; }  // `b` is left as-is because it has a block-scope
141  *
142  * This doesn't change the meaning or correctness of the code. If the switch needs to be rewriten
143  * (e.g. due to the restrictions of ES2 or WGSL), this transformation prevents scoping issues with
144  * variables falling out of scope between switch-cases when we fall through.
145  *
146  * If there are no variables at the top-level, null is returned.
147  */
148 std::unique_ptr<Block> HoistSwitchVarDeclarationsAtTopLevel(const Context&, StatementArray& cases,
149                                                             SymbolTable& symbolTable, Position pos);
150 
151 } // namespace Transform
152 } // namespace SkSL
153 
154 #endif
155