Flutter Engine
The Flutter Engine
SkSLIsConstantExpression.cpp
Go to the documentation of this file.
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
9#include "src/core/SkTHash.h"
24
25#include <memory>
26
27using namespace skia_private;
28
29namespace SkSL {
30
31class ProgramElement;
32
33namespace {
34
35// Checks for ES2 constant-expression rules, and (optionally) constant-index-expression rules
36// (if loopIndices is non-nullptr)
37class ConstantExpressionVisitor : public ProgramVisitor {
38public:
39 ConstantExpressionVisitor(const THashSet<const Variable*>* loopIndices)
40 : fLoopIndices(loopIndices) {}
41
42 bool visitExpression(const Expression& e) override {
43 // A constant-(index)-expression is one of...
44 switch (e.kind()) {
45 // ... a literal value
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:
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:
103 return true;
104
105 default:
106 SkDEBUGFAIL("Unexpected expression type");
107 return true;
108 }
109 }
110
111private:
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.
118class ES2IndexingVisitor : public ProgramVisitor {
119public:
120 ES2IndexingVisitor(ErrorReporter& errors) : fErrors(errors) {}
121
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
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
148
149private:
150 ErrorReporter& fErrors;
151 THashSet<const Variable*> fLoopIndices;
152 using INHERITED = ProgramVisitor;
153};
154
155} // namespace
156
158 return !ConstantExpressionVisitor{/*loopIndices=*/nullptr}.visitExpression(expr);
159}
160
162 ES2IndexingVisitor visitor(errors);
163 visitor.visitProgramElement(pe);
164}
165
166} // namespace SkSL
#define COMMA
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
struct MyStruct s
GAsyncResult * result
bool IsConstantExpression(const Expression &expr)
void ValidateIndexingForES2(const ProgramElement &pe, ErrorReporter &errors)