• 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/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