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