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