xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLIsConstantExpression.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 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 #include "include/core/SkTypes.h"
9 #include "src/core/SkTHash.h"
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLErrorReporter.h"
12 #include "src/sksl/SkSLOperator.h"
13 #include "src/sksl/analysis/SkSLProgramVisitor.h"
14 #include "src/sksl/ir/SkSLBinaryExpression.h"
15 #include "src/sksl/ir/SkSLExpression.h"
16 #include "src/sksl/ir/SkSLForStatement.h"
17 #include "src/sksl/ir/SkSLIRNode.h"
18 #include "src/sksl/ir/SkSLIndexExpression.h"
19 #include "src/sksl/ir/SkSLModifierFlags.h"
20 #include "src/sksl/ir/SkSLStatement.h"
21 #include "src/sksl/ir/SkSLVarDeclarations.h"
22 #include "src/sksl/ir/SkSLVariable.h"
23 #include "src/sksl/ir/SkSLVariableReference.h"
24 
25 #include <memory>
26 
27 using namespace skia_private;
28 
29 namespace SkSL {
30 
31 class ProgramElement;
32 
33 namespace {
34 
35 // Checks for ES2 constant-expression rules, and (optionally) constant-index-expression rules
36 // (if loopIndices is non-nullptr)
37 class ConstantExpressionVisitor : public ProgramVisitor {
38 public:
ConstantExpressionVisitor(const THashSet<const Variable * > * loopIndices)39     ConstantExpressionVisitor(const THashSet<const Variable*>* loopIndices)
40             : fLoopIndices(loopIndices) {}
41 
visitExpression(const Expression & e)42     bool visitExpression(const Expression& e) override {
43         // A constant-(index)-expression is one of...
44         switch (e.kind()) {
45             // ... a literal value
46             case Expression::Kind::kLiteral:
47                 return false;
48 
49             // ... settings can appear in fragment processors; they will resolve when compiled
50             case Expression::Kind::kSetting:
51                 return false;
52 
53             // ... a global or local variable qualified as 'const', excluding function parameters.
54             // ... loop indices as defined in section 4. [constant-index-expression]
55             case Expression::Kind::kVariableReference: {
56                 const Variable* v = e.as<VariableReference>().variable();
57                 if (v->modifierFlags().isConst() && (v->storage() == Variable::Storage::kGlobal ||
58                                                      v->storage() == Variable::Storage::kLocal)) {
59                     return false;
60                 }
61                 return !fLoopIndices || !fLoopIndices->contains(v);
62             }
63 
64             // ... not a sequence expression (skia:13311)...
65             case Expression::Kind::kBinary:
66                 if (e.as<BinaryExpression>().getOperator().kind() == Operator::Kind::COMMA) {
67                     return true;
68                 }
69                 [[fallthrough]];
70 
71             // ... expressions composed of both of the above
72             case Expression::Kind::kConstructorArray:
73             case Expression::Kind::kConstructorArrayCast:
74             case Expression::Kind::kConstructorCompound:
75             case Expression::Kind::kConstructorCompoundCast:
76             case Expression::Kind::kConstructorDiagonalMatrix:
77             case Expression::Kind::kConstructorMatrixResize:
78             case Expression::Kind::kConstructorScalarCast:
79             case Expression::Kind::kConstructorSplat:
80             case Expression::Kind::kConstructorStruct:
81             case Expression::Kind::kFieldAccess:
82             case Expression::Kind::kIndex:
83             case Expression::Kind::kPrefix:
84             case Expression::Kind::kPostfix:
85             case Expression::Kind::kSwizzle:
86             case Expression::Kind::kTernary:
87                 return INHERITED::visitExpression(e);
88 
89             // Function calls are completely disallowed in SkSL constant-(index)-expressions.
90             // GLSL does mandate that calling a built-in function where the arguments are all
91             // constant-expressions should result in a constant-expression. SkSL handles this by
92             // optimizing fully-constant function calls into literals in FunctionCall::Make.
93             case Expression::Kind::kFunctionCall:
94             case Expression::Kind::kChildCall:
95 
96             // These shouldn't appear in a valid program at all, and definitely aren't
97             // constant-(index)-expressions.
98             case Expression::Kind::kPoison:
99             case Expression::Kind::kFunctionReference:
100             case Expression::Kind::kMethodReference:
101             case Expression::Kind::kTypeReference:
102             case Expression::Kind::kEmpty:
103                 return true;
104 
105             default:
106                 SkDEBUGFAIL("Unexpected expression type");
107                 return true;
108         }
109     }
110 
111 private:
112     const THashSet<const Variable*>* fLoopIndices;
113     using INHERITED = ProgramVisitor;
114 };
115 
116 // Visits a function, tracks its loop indices, and verifies that every index-expression in the
117 // function qualifies as a constant-index-expression.
118 class ES2IndexingVisitor : public ProgramVisitor {
119 public:
ES2IndexingVisitor(ErrorReporter & errors)120     ES2IndexingVisitor(ErrorReporter& errors) : fErrors(errors) {}
121 
visitStatement(const Statement & s)122     bool visitStatement(const Statement& s) override {
123         if (s.is<ForStatement>()) {
124             const ForStatement& f = s.as<ForStatement>();
125             SkASSERT(f.initializer() && f.initializer()->is<VarDeclaration>());
126             const Variable* var = f.initializer()->as<VarDeclaration>().var();
127             SkASSERT(!fLoopIndices.contains(var));
128             fLoopIndices.add(var);
129             bool result = this->visitStatement(*f.statement());
130             fLoopIndices.remove(var);
131             return result;
132         }
133         return INHERITED::visitStatement(s);
134     }
135 
visitExpression(const Expression & e)136     bool visitExpression(const Expression& e) override {
137         if (e.is<IndexExpression>()) {
138             const IndexExpression& i = e.as<IndexExpression>();
139             if (ConstantExpressionVisitor{&fLoopIndices}.visitExpression(*i.index())) {
140                 fErrors.error(i.fPosition, "index expression must be constant");
141                 return true;
142             }
143         }
144         return INHERITED::visitExpression(e);
145     }
146 
147     using ProgramVisitor::visitProgramElement;
148 
149 private:
150     ErrorReporter& fErrors;
151     THashSet<const Variable*> fLoopIndices;
152     using INHERITED = ProgramVisitor;
153 };
154 
155 }  // namespace
156 
IsConstantExpression(const Expression & expr)157 bool Analysis::IsConstantExpression(const Expression& expr) {
158     return !ConstantExpressionVisitor{/*loopIndices=*/nullptr}.visitExpression(expr);
159 }
160 
ValidateIndexingForES2(const ProgramElement & pe,ErrorReporter & errors)161 void Analysis::ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors) {
162     ES2IndexingVisitor visitor(errors);
163     visitor.visitProgramElement(pe);
164 }
165 
166 }  // namespace SkSL
167