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