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/SkSLDefines.h" 12 #include "include/private/SkSLSampleUsage.h" 13 14 #include <memory> 15 16 namespace SkSL { 17 18 class ErrorReporter; 19 class Expression; 20 class ForStatement; 21 class FunctionDeclaration; 22 class FunctionDefinition; 23 struct LoadedModule; 24 struct Program; 25 class ProgramElement; 26 class ProgramUsage; 27 class Statement; 28 class Variable; 29 class VariableReference; 30 enum class VariableRefKind : int8_t; 31 32 /** 33 * Provides utilities for analyzing SkSL statically before it's composed into a full program. 34 */ 35 struct Analysis { 36 /** 37 * Determines how `program` samples `fp`. By default, assumes that the sample coords 38 * (SK_MAIN_COORDS_BUILTIN) might be modified, so `sample(fp, sampleCoords)` is treated as 39 * Explicit. If writesToSampleCoords is false, treats that as PassThrough, instead. 40 */ 41 static SampleUsage GetSampleUsage(const Program& program, 42 const Variable& fp, 43 bool writesToSampleCoords = true); 44 45 static bool ReferencesBuiltin(const Program& program, int builtin); 46 47 static bool ReferencesSampleCoords(const Program& program); 48 static bool ReferencesFragCoords(const Program& program); 49 50 static int NodeCountUpToLimit(const FunctionDefinition& function, int limit); 51 52 /** 53 * Finds unconditional exits from a switch-case. Returns true if this statement unconditionally 54 * causes an exit from this switch (via continue, break or return). 55 */ 56 static bool SwitchCaseContainsUnconditionalExit(Statement& stmt); 57 58 /** 59 * A switch-case "falls through" when it doesn't have an unconditional exit. 60 */ SwitchCaseFallsThroughAnalysis61 static bool SwitchCaseFallsThrough(Statement& stmt) { 62 return !SwitchCaseContainsUnconditionalExit(stmt); 63 } 64 65 /** 66 * Finds conditional exits from a switch-case. Returns true if this statement contains a 67 * conditional that wraps a potential exit from the switch (via continue, break or return). 68 */ 69 static bool SwitchCaseContainsConditionalExit(Statement& stmt); 70 71 static std::unique_ptr<ProgramUsage> GetUsage(const Program& program); 72 static std::unique_ptr<ProgramUsage> GetUsage(const LoadedModule& module); 73 74 static bool StatementWritesToVariable(const Statement& stmt, const Variable& var); 75 76 struct AssignmentInfo { 77 VariableReference* fAssignedVar = nullptr; 78 }; 79 static bool IsAssignable(Expression& expr, AssignmentInfo* info = nullptr, 80 ErrorReporter* errors = nullptr); 81 82 // Updates the `refKind` field of exactly one VariableReference inside `expr`. 83 // `expr` must be `IsAssignable`; returns an error otherwise. 84 static bool MakeAssignmentExpr(Expression* expr, VariableRefKind kind, ErrorReporter* errors); 85 86 // Updates the `refKind` field of every VariableReference found within `expr`. 87 // `expr` is allowed to have zero, one, or multiple VariableReferences. 88 static void UpdateRefKind(Expression* expr, VariableRefKind refKind); 89 90 // A "trivial" expression is one where we'd feel comfortable cloning it multiple times in 91 // the code, without worrying about incurring a performance penalty. Examples: 92 // - true 93 // - 3.14159265 94 // - myIntVariable 95 // - myColor.rgb 96 // - myArray[123] 97 // - myStruct.myField 98 // - half4(0) 99 // 100 // Trivial-ness is stackable. Somewhat large expressions can occasionally make the cut: 101 // - half4(myColor.a) 102 // - myStruct.myArrayField[7].xyz 103 static bool IsTrivialExpression(const Expression& expr); 104 105 // Returns true if both expression trees are the same. Used by the optimizer to look for self- 106 // assignment or self-comparison; won't necessarily catch complex cases. Rejects expressions 107 // that may cause side effects. 108 static bool IsSameExpressionTree(const Expression& left, const Expression& right); 109 110 // Is 'expr' a constant-expression, as defined by GLSL 1.0, section 5.10? A constant expression 111 // is one of: 112 // 113 // - A literal value 114 // - A global or local variable qualified as 'const', excluding function parameters 115 // - An expression formed by an operator on operands that are constant expressions, including 116 // getting an element of a constant vector or a constant matrix, or a field of a constant 117 // structure 118 // - A constructor whose arguments are all constant expressions 119 // 120 // GLSL (but not SkSL, yet - skbug.com/10835) also provides: 121 // - A built-in function call whose arguments are all constant expressions, with the exception 122 // of the texture lookup functions 123 static bool IsConstantExpression(const Expression& expr); 124 125 struct UnrollableLoopInfo { 126 const Variable* fIndex; 127 double fStart; 128 double fDelta; 129 int fCount; 130 }; 131 132 // Ensures that a for-loop meets the strict requirements of The OpenGL ES Shading Language 1.00, 133 // Appendix A, Section 4. 134 // Information about the loop's structure are placed in outLoopInfo (if not nullptr). 135 // If the function returns false, specific reasons are reported via errors (if not nullptr). 136 static bool ForLoopIsValidForES2(int offset, 137 const Statement* loopInitializer, 138 const Expression* loopTest, 139 const Expression* loopNext, 140 const Statement* loopStatement, 141 UnrollableLoopInfo* outLoopInfo, 142 ErrorReporter* errors); 143 144 static void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors); 145 146 // Detects functions that fail to return a value on at least one path. 147 static bool CanExitWithoutReturningValue(const FunctionDeclaration& funcDecl, 148 const Statement& body); 149 }; 150 151 /** 152 * Utility class to visit every element, statement, and expression in an SkSL program IR. 153 * This is intended for simple analysis and accumulation, where custom visitation behavior is only 154 * needed for a limited set of expression kinds. 155 * 156 * Subclasses should override visitExpression/visitStatement/visitProgramElement as needed and 157 * intercept elements of interest. They can then invoke the base class's function to visit all 158 * sub expressions. They can also choose not to call the base function to arrest recursion, or 159 * implement custom recursion. 160 * 161 * The visit functions return a bool that determines how the default implementation recurses. Once 162 * any visit call returns true, the default behavior stops recursing and propagates true up the 163 * stack. 164 */ 165 template <typename T> 166 class TProgramVisitor { 167 public: 168 virtual ~TProgramVisitor() = default; 169 170 protected: 171 virtual bool visitExpression(typename T::Expression& expression); 172 virtual bool visitStatement(typename T::Statement& statement); 173 virtual bool visitProgramElement(typename T::ProgramElement& programElement); 174 175 virtual bool visitExpressionPtr(typename T::UniquePtrExpression& expr) = 0; 176 virtual bool visitStatementPtr(typename T::UniquePtrStatement& stmt) = 0; 177 }; 178 179 // ProgramVisitors take const types; ProgramWriters do not. 180 struct ProgramVisitorTypes { 181 using Program = const SkSL::Program; 182 using Expression = const SkSL::Expression; 183 using Statement = const SkSL::Statement; 184 using ProgramElement = const SkSL::ProgramElement; 185 using UniquePtrExpression = const std::unique_ptr<SkSL::Expression>; 186 using UniquePtrStatement = const std::unique_ptr<SkSL::Statement>; 187 }; 188 189 struct ProgramWriterTypes { 190 using Program = SkSL::Program; 191 using Expression = SkSL::Expression; 192 using Statement = SkSL::Statement; 193 using ProgramElement = SkSL::ProgramElement; 194 using UniquePtrExpression = std::unique_ptr<SkSL::Expression>; 195 using UniquePtrStatement = std::unique_ptr<SkSL::Statement>; 196 }; 197 198 // Squelch bogus Clang warning about template vtables: https://bugs.llvm.org/show_bug.cgi?id=18733 199 #if defined(__clang__) 200 #pragma clang diagnostic push 201 #pragma clang diagnostic ignored "-Wweak-template-vtables" 202 #endif 203 extern template class TProgramVisitor<ProgramVisitorTypes>; 204 extern template class TProgramVisitor<ProgramWriterTypes>; 205 #if defined(__clang__) 206 #pragma clang diagnostic pop 207 #endif 208 209 class ProgramVisitor : public TProgramVisitor<ProgramVisitorTypes> { 210 public: 211 bool visit(const Program& program); 212 213 private: 214 // ProgramVisitors shouldn't need access to unique_ptrs, and marking these as final should help 215 // these accessors inline away. Use ProgramWriter if you need the unique_ptrs. visitExpressionPtr(const std::unique_ptr<Expression> & e)216 bool visitExpressionPtr(const std::unique_ptr<Expression>& e) final { 217 return this->visitExpression(*e); 218 } visitStatementPtr(const std::unique_ptr<Statement> & s)219 bool visitStatementPtr(const std::unique_ptr<Statement>& s) final { 220 return this->visitStatement(*s); 221 } 222 }; 223 224 class ProgramWriter : public TProgramVisitor<ProgramWriterTypes> { 225 public: 226 // Subclass these methods if you want access to the unique_ptrs of IRNodes in a program. 227 // This will allow statements or expressions to be replaced during a visit. visitExpressionPtr(std::unique_ptr<Expression> & e)228 bool visitExpressionPtr(std::unique_ptr<Expression>& e) override { 229 return this->visitExpression(*e); 230 } visitStatementPtr(std::unique_ptr<Statement> & s)231 bool visitStatementPtr(std::unique_ptr<Statement>& s) override { 232 return this->visitStatement(*s); 233 } 234 }; 235 236 } // namespace SkSL 237 238 #endif 239