• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 SkSLAnalysis_DEFINED
9 #define SkSLAnalysis_DEFINED
10 
11 #include "include/private/SkSLSampleUsage.h"
12 #include "include/private/base/SkTArray.h"
13 
14 #include <cstdint>
15 #include <memory>
16 #include <vector>
17 
18 namespace SkSL {
19 
20 class Context;
21 class ErrorReporter;
22 class Expression;
23 class FunctionDeclaration;
24 class FunctionDefinition;
25 class Position;
26 class ProgramElement;
27 class ProgramUsage;
28 class Statement;
29 class SymbolTable;
30 class Variable;
31 class VariableReference;
32 enum class VariableRefKind : int8_t;
33 struct ForLoopPositions;
34 struct LoopUnrollInfo;
35 struct Module;
36 struct Program;
37 
38 /**
39  * Provides utilities for analyzing SkSL statically before it's composed into a full program.
40  */
41 namespace Analysis {
42 
43 /**
44  * Determines how `program` samples `child`. By default, assumes that the sample coords might be
45  * modified, so `child.eval(sampleCoords)` is treated as Explicit. If writesToSampleCoords is false,
46  * treats that as PassThrough, instead. If elidedSampleCoordCount is provided, the pointed to value
47  * will be incremented by the number of sample calls where the above rewrite was performed.
48  */
49 SampleUsage GetSampleUsage(const Program& program,
50                            const Variable& child,
51                            bool writesToSampleCoords = true,
52                            int* elidedSampleCoordCount = nullptr);
53 
54 bool ReferencesBuiltin(const Program& program, int builtin);
55 
56 bool ReferencesSampleCoords(const Program& program);
57 bool ReferencesFragCoords(const Program& program);
58 
59 bool CallsSampleOutsideMain(const Program& program);
60 
61 bool CallsColorTransformIntrinsics(const Program& program);
62 
63 /**
64  * Determines if `function` always returns an opaque color (a vec4 where the last component is known
65  * to be 1). This is conservative, and based on constant expression analysis.
66  */
67 bool ReturnsOpaqueColor(const FunctionDefinition& function);
68 
69 /**
70  * Determines if `function` is a color filter which returns the alpha component of the input color
71  * unchanged. This is a very conservative analysis, and only supports returning a swizzle of the
72  * input color, or returning a constructor that ends with `input.a`.
73  */
74 bool ReturnsInputAlpha(const FunctionDefinition& function, const ProgramUsage& usage);
75 
76 /**
77  * Checks for recursion or overly-deep function-call chains, and rejects programs which have them.
78  * Also, computes the size of the program in a completely flattened state--loops fully unrolled,
79  * function calls inlined--and rejects programs that exceed an arbitrary upper bound.
80  */
81 bool CheckProgramStructure(const Program& program, bool enforceSizeLimit);
82 
83 /** Determines if `expr` contains a reference to the variable sk_RTAdjust. */
84 bool ContainsRTAdjust(const Expression& expr);
85 
86 /** Determines if `expr` contains a reference to variable `var`. */
87 bool ContainsVariable(const Expression& expr, const Variable& var);
88 
89 /** Determines if `expr` has any side effects. (Is the expression state-altering or pure?) */
90 bool HasSideEffects(const Expression& expr);
91 
92 /** Determines if `expr` is a compile-time constant (composed of just constructors and literals). */
93 bool IsCompileTimeConstant(const Expression& expr);
94 
95 /**
96  * Determines if `expr` is a dynamically-uniform expression; this returns true if the expression
97  * could be evaluated at compile time if uniform values were known.
98  */
99 bool IsDynamicallyUniformExpression(const Expression& expr);
100 
101 /**
102  * Detect an orphaned variable declaration outside of a scope, e.g. if (true) int a;. Returns
103  * true if an error was reported.
104  */
105 bool DetectVarDeclarationWithoutScope(const Statement& stmt, ErrorReporter* errors = nullptr);
106 
107 int NodeCountUpToLimit(const FunctionDefinition& function, int limit);
108 
109 /**
110  * Finds unconditional exits from a switch-case. Returns true if this statement unconditionally
111  * causes an exit from this switch (via continue, break or return).
112  */
113 bool SwitchCaseContainsUnconditionalExit(const Statement& stmt);
114 
115 /**
116  * Finds conditional exits from a switch-case. Returns true if this statement contains a
117  * conditional that wraps a potential exit from the switch (via continue, break or return).
118  */
119 bool SwitchCaseContainsConditionalExit(const Statement& stmt);
120 
121 std::unique_ptr<ProgramUsage> GetUsage(const Program& program);
122 std::unique_ptr<ProgramUsage> GetUsage(const Module& module);
123 
124 /** Returns true if the passed-in statement might alter `var`. */
125 bool StatementWritesToVariable(const Statement& stmt, const Variable& var);
126 
127 /**
128  * Detects if the passed-in block contains a `continue`, `break` or `return` that could directly
129  * affect its control flow. (A `continue` or `break` nested inside an inner loop/switch will not
130  * affect the loop, but a `return` will.)
131  */
132 struct LoopControlFlowInfo {
133     bool fHasContinue = false;
134     bool fHasBreak = false;
135     bool fHasReturn = false;
136 };
137 LoopControlFlowInfo GetLoopControlFlowInfo(const Statement& stmt);
138 
139 /**
140  * Returns true if the expression can be assigned-into. Pass `info` if you want to know the
141  * VariableReference that will be written to. Pass `errors` to report an error for expressions that
142  * are not actually writable.
143  */
144 struct AssignmentInfo {
145     VariableReference* fAssignedVar = nullptr;
146 };
147 bool IsAssignable(Expression& expr, AssignmentInfo* info = nullptr,
148                   ErrorReporter* errors = nullptr);
149 
150 /**
151  * Updates the `refKind` field of the VariableReference at the top level of `expr`.
152  * If `expr` can be assigned to (`IsAssignable`), true is returned and no errors are reported.
153  * If not, false is returned. and an error is reported if `errors` is non-null.
154  */
155 bool UpdateVariableRefKind(Expression* expr, VariableRefKind kind, ErrorReporter* errors = nullptr);
156 
157 /**
158  * A "trivial" expression is one where we'd feel comfortable cloning it multiple times in
159  * the code, without worrying about incurring a performance penalty. Examples:
160  * - true
161  * - 3.14159265
162  * - myIntVariable
163  * - myColor.rgb
164  * - myArray[123]
165  * - myStruct.myField
166  * - half4(0)
167  * - !myBoolean
168  * - +myValue
169  * - -myValue
170  * - ~myInteger
171  *
172  * Trivial-ness is stackable. Somewhat large expressions can occasionally make the cut:
173  * - half4(myColor.a)
174  * - myStruct.myArrayField[7].xzy
175  */
176 bool IsTrivialExpression(const Expression& expr);
177 
178 /**
179  * Returns true if both expression trees are the same. Used by the optimizer to look for self-
180  * assignment or self-comparison; won't necessarily catch complex cases. Rejects expressions
181  * that may cause side effects.
182  */
183 bool IsSameExpressionTree(const Expression& left, const Expression& right);
184 
185 /**
186  * Returns true if expr is a constant-expression, as defined by GLSL 1.0, section 5.10.
187  * A constant expression is one of:
188  * - A literal value
189  * - A global or local variable qualified as 'const', excluding function parameters
190  * - An expression formed by an operator on operands that are constant expressions, including
191  *   getting an element of a constant vector or a constant matrix, or a field of a constant
192  *   structure
193  * - A constructor whose arguments are all constant expressions
194  * - A built-in function call whose arguments are all constant expressions, with the exception
195  *   of the texture lookup functions
196  */
197 bool IsConstantExpression(const Expression& expr);
198 
199 /**
200  * Ensures that any index-expressions inside of for-loops qualify as 'constant-index-expressions' as
201  * defined by GLSL 1.0, Appendix A, Section 5. A constant-index-expression is:
202  * - A constant-expression
203  * - Loop indices (as defined in Appendix A, Section 4)
204  * - Expressions composed of both of the above
205  */
206 void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors);
207 
208 /**
209  * Emits an internal error if a VarDeclaration exists without a matching entry in the nearest
210  * SymbolTable.
211  */
212 void CheckSymbolTableCorrectness(const Program& program);
213 
214 /**
215  * Ensures that a for-loop meets the strict requirements of The OpenGL ES Shading Language 1.00,
216  * Appendix A, Section 4.
217  * If the requirements are met, information about the loop's structure is returned.
218  * If the requirements are not met, the problem is reported via `errors` (if not nullptr), and
219  * null is returned.
220  * The loop test-expression may be altered by this check. For example, a loop like this:
221  *     for (float x = 1.0; x != 0.0; x -= 0.01) {...}
222  * appears to be ES2-safe, but due to floating-point rounding error, it may not actually terminate.
223  * We rewrite the test condition to `x > 0.0` in order to ensure loop termination.
224  */
225 std::unique_ptr<LoopUnrollInfo> GetLoopUnrollInfo(const Context& context,
226                                                   Position pos,
227                                                   const ForLoopPositions& positions,
228                                                   const Statement* loopInitializer,
229                                                   std::unique_ptr<Expression>* loopTestPtr,
230                                                   const Expression* loopNext,
231                                                   const Statement* loopStatement,
232                                                   ErrorReporter* errors);
233 
234 /** Detects functions that fail to return a value on at least one path. */
235 bool CanExitWithoutReturningValue(const FunctionDeclaration& funcDecl, const Statement& body);
236 
237 /** Determines if a given function has multiple and/or early returns. */
238 enum class ReturnComplexity {
239     kSingleSafeReturn,
240     kScopedReturns,
241     kEarlyReturns,
242 };
243 ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef);
244 
245 /**
246  * Runs at finalization time to perform any last-minute correctness checks:
247  * - Reports dangling FunctionReference or TypeReference expressions
248  * - Reports function `out` params which are never written to (structs are currently exempt)
249  */
250 void DoFinalizationChecks(const Program& program);
251 
252 /**
253  * Error checks compute shader in/outs and returns a vector containing them ordered by location.
254  */
255 skia_private::TArray<const SkSL::Variable*> GetComputeShaderMainParams(const Context& context,
256                                                                        const Program& program);
257 
258 /**
259  * Tracks the symbol table stack, in conjunction with a ProgramVisitor. Inside `visitStatement`,
260  * pass the current statement and a symbol-table vector to a SymbolTableStackBuilder and the symbol
261  * table stack will be maintained automatically.
262  */
263 class SymbolTableStackBuilder {
264 public:
265     // If the passed-in statement holds a symbol table, adds it to the stack.
266     SymbolTableStackBuilder(const Statement* stmt, std::vector<SymbolTable*>* stack);
267 
268     // If a symbol table was added to the stack earlier, removes it from the stack.
269     ~SymbolTableStackBuilder();
270 
271     // Returns true if an entry was added to the symbol-table stack.
foundSymbolTable()272     bool foundSymbolTable() {
273         return fStackToPop != nullptr;
274     }
275 
276 private:
277     std::vector<SymbolTable*>* fStackToPop = nullptr;
278 };
279 
280 }  // namespace Analysis
281 }  // namespace SkSL
282 
283 #endif
284