1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkSLSampleUsage.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLIntrinsicList.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLNoOpErrorReporter.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramUsage.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramVisitor.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBlock.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLChildCall.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructor.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLDoStatement.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpressionStatement.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldAccess.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLForStatement.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionCall.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIfStatement.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIndexExpression.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPostfixExpression.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLReturnStatement.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchCase.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchStatement.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbol.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLProgramWriter.h"
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
62*c8dee2aaSAndroid Build Coastguard Worker #include <string>
63*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker namespace {
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker // Visitor that determines the merged SampleUsage for a given child in the program.
70*c8dee2aaSAndroid Build Coastguard Worker class MergeSampleUsageVisitor : public ProgramVisitor {
71*c8dee2aaSAndroid Build Coastguard Worker public:
MergeSampleUsageVisitor(const Context & context,const Variable & child,bool writesToSampleCoords)72*c8dee2aaSAndroid Build Coastguard Worker MergeSampleUsageVisitor(const Context& context,
73*c8dee2aaSAndroid Build Coastguard Worker const Variable& child,
74*c8dee2aaSAndroid Build Coastguard Worker bool writesToSampleCoords)
75*c8dee2aaSAndroid Build Coastguard Worker : fContext(context), fChild(child), fWritesToSampleCoords(writesToSampleCoords) {}
76*c8dee2aaSAndroid Build Coastguard Worker
visit(const Program & program)77*c8dee2aaSAndroid Build Coastguard Worker SampleUsage visit(const Program& program) {
78*c8dee2aaSAndroid Build Coastguard Worker fUsage = SampleUsage(); // reset to none
79*c8dee2aaSAndroid Build Coastguard Worker INHERITED::visit(program);
80*c8dee2aaSAndroid Build Coastguard Worker return fUsage;
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker
elidedSampleCoordCount() const83*c8dee2aaSAndroid Build Coastguard Worker int elidedSampleCoordCount() const { return fElidedSampleCoordCount; }
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker protected:
86*c8dee2aaSAndroid Build Coastguard Worker const Context& fContext;
87*c8dee2aaSAndroid Build Coastguard Worker const Variable& fChild;
88*c8dee2aaSAndroid Build Coastguard Worker const Variable* fMainCoordsParam = nullptr;
89*c8dee2aaSAndroid Build Coastguard Worker const bool fWritesToSampleCoords;
90*c8dee2aaSAndroid Build Coastguard Worker SampleUsage fUsage;
91*c8dee2aaSAndroid Build Coastguard Worker int fElidedSampleCoordCount = 0;
92*c8dee2aaSAndroid Build Coastguard Worker
visitProgramElement(const ProgramElement & pe)93*c8dee2aaSAndroid Build Coastguard Worker bool visitProgramElement(const ProgramElement& pe) override {
94*c8dee2aaSAndroid Build Coastguard Worker fMainCoordsParam = pe.is<FunctionDefinition>()
95*c8dee2aaSAndroid Build Coastguard Worker ? pe.as<FunctionDefinition>().declaration().getMainCoordsParameter()
96*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
97*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitProgramElement(pe);
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & e)100*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& e) override {
101*c8dee2aaSAndroid Build Coastguard Worker switch (e.kind()) {
102*c8dee2aaSAndroid Build Coastguard Worker case ExpressionKind::kChildCall: {
103*c8dee2aaSAndroid Build Coastguard Worker const ChildCall& cc = e.as<ChildCall>();
104*c8dee2aaSAndroid Build Coastguard Worker if (&cc.child() == &fChild) {
105*c8dee2aaSAndroid Build Coastguard Worker // Determine the type of call at this site, and merge it with the accumulated
106*c8dee2aaSAndroid Build Coastguard Worker // state
107*c8dee2aaSAndroid Build Coastguard Worker const ExpressionArray& arguments = cc.arguments();
108*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!arguments.empty());
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker const Expression* maybeCoords = arguments[0].get();
111*c8dee2aaSAndroid Build Coastguard Worker if (maybeCoords->type().matches(*fContext.fTypes.fFloat2)) {
112*c8dee2aaSAndroid Build Coastguard Worker // If the coords are a direct reference to the program's sample-coords, and
113*c8dee2aaSAndroid Build Coastguard Worker // those coords are never modified, we can conservatively turn this into
114*c8dee2aaSAndroid Build Coastguard Worker // PassThrough sampling. In all other cases, we consider it Explicit.
115*c8dee2aaSAndroid Build Coastguard Worker if (!fWritesToSampleCoords && maybeCoords->is<VariableReference>() &&
116*c8dee2aaSAndroid Build Coastguard Worker maybeCoords->as<VariableReference>().variable() == fMainCoordsParam) {
117*c8dee2aaSAndroid Build Coastguard Worker fUsage.merge(SampleUsage::PassThrough());
118*c8dee2aaSAndroid Build Coastguard Worker ++fElidedSampleCoordCount;
119*c8dee2aaSAndroid Build Coastguard Worker } else {
120*c8dee2aaSAndroid Build Coastguard Worker fUsage.merge(SampleUsage::Explicit());
121*c8dee2aaSAndroid Build Coastguard Worker }
122*c8dee2aaSAndroid Build Coastguard Worker } else {
123*c8dee2aaSAndroid Build Coastguard Worker // child(inputColor) or child(srcColor, dstColor) -> PassThrough
124*c8dee2aaSAndroid Build Coastguard Worker fUsage.merge(SampleUsage::PassThrough());
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker }
127*c8dee2aaSAndroid Build Coastguard Worker break;
128*c8dee2aaSAndroid Build Coastguard Worker }
129*c8dee2aaSAndroid Build Coastguard Worker case ExpressionKind::kFunctionCall: {
130*c8dee2aaSAndroid Build Coastguard Worker // If this child effect is ever passed via a function call...
131*c8dee2aaSAndroid Build Coastguard Worker const FunctionCall& call = e.as<FunctionCall>();
132*c8dee2aaSAndroid Build Coastguard Worker for (const std::unique_ptr<Expression>& arg : call.arguments()) {
133*c8dee2aaSAndroid Build Coastguard Worker if (arg->is<VariableReference>() &&
134*c8dee2aaSAndroid Build Coastguard Worker arg->as<VariableReference>().variable() == &fChild) {
135*c8dee2aaSAndroid Build Coastguard Worker // ... we must treat it as explicitly sampled, since the program's
136*c8dee2aaSAndroid Build Coastguard Worker // sample-coords only exist as a parameter to `main`.
137*c8dee2aaSAndroid Build Coastguard Worker fUsage.merge(SampleUsage::Explicit());
138*c8dee2aaSAndroid Build Coastguard Worker break;
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker break;
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker default:
144*c8dee2aaSAndroid Build Coastguard Worker break;
145*c8dee2aaSAndroid Build Coastguard Worker }
146*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(e);
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
150*c8dee2aaSAndroid Build Coastguard Worker };
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker // Visitor that searches for child calls from a function other than main()
153*c8dee2aaSAndroid Build Coastguard Worker class SampleOutsideMainVisitor : public ProgramVisitor {
154*c8dee2aaSAndroid Build Coastguard Worker public:
SampleOutsideMainVisitor()155*c8dee2aaSAndroid Build Coastguard Worker SampleOutsideMainVisitor() {}
156*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & e)157*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& e) override {
158*c8dee2aaSAndroid Build Coastguard Worker if (e.is<ChildCall>()) {
159*c8dee2aaSAndroid Build Coastguard Worker return true;
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(e);
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker
visitProgramElement(const ProgramElement & p)164*c8dee2aaSAndroid Build Coastguard Worker bool visitProgramElement(const ProgramElement& p) override {
165*c8dee2aaSAndroid Build Coastguard Worker return p.is<FunctionDefinition>() &&
166*c8dee2aaSAndroid Build Coastguard Worker !p.as<FunctionDefinition>().declaration().isMain() &&
167*c8dee2aaSAndroid Build Coastguard Worker INHERITED::visitProgramElement(p);
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker
170*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
171*c8dee2aaSAndroid Build Coastguard Worker };
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker class ReturnsNonOpaqueColorVisitor : public ProgramVisitor {
174*c8dee2aaSAndroid Build Coastguard Worker public:
ReturnsNonOpaqueColorVisitor()175*c8dee2aaSAndroid Build Coastguard Worker ReturnsNonOpaqueColorVisitor() {}
176*c8dee2aaSAndroid Build Coastguard Worker
visitStatement(const Statement & s)177*c8dee2aaSAndroid Build Coastguard Worker bool visitStatement(const Statement& s) override {
178*c8dee2aaSAndroid Build Coastguard Worker if (s.is<ReturnStatement>()) {
179*c8dee2aaSAndroid Build Coastguard Worker const Expression* e = s.as<ReturnStatement>().expression().get();
180*c8dee2aaSAndroid Build Coastguard Worker bool knownOpaque = e && e->type().slotCount() == 4 &&
181*c8dee2aaSAndroid Build Coastguard Worker ConstantFolder::GetConstantValueForVariable(*e)
182*c8dee2aaSAndroid Build Coastguard Worker ->getConstantValue(/*n=*/3)
183*c8dee2aaSAndroid Build Coastguard Worker .value_or(0) == 1;
184*c8dee2aaSAndroid Build Coastguard Worker return !knownOpaque;
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitStatement(s);
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & e)189*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& e) override {
190*c8dee2aaSAndroid Build Coastguard Worker // No need to recurse into expressions, these can never contain return statements
191*c8dee2aaSAndroid Build Coastguard Worker return false;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker
194*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
195*c8dee2aaSAndroid Build Coastguard Worker using INHERITED::visitProgramElement;
196*c8dee2aaSAndroid Build Coastguard Worker };
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker // Visitor that counts the number of nodes visited
199*c8dee2aaSAndroid Build Coastguard Worker class NodeCountVisitor : public ProgramVisitor {
200*c8dee2aaSAndroid Build Coastguard Worker public:
NodeCountVisitor(int limit)201*c8dee2aaSAndroid Build Coastguard Worker NodeCountVisitor(int limit) : fLimit(limit) {}
202*c8dee2aaSAndroid Build Coastguard Worker
visit(const Statement & s)203*c8dee2aaSAndroid Build Coastguard Worker int visit(const Statement& s) {
204*c8dee2aaSAndroid Build Coastguard Worker this->visitStatement(s);
205*c8dee2aaSAndroid Build Coastguard Worker return fCount;
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & e)208*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& e) override {
209*c8dee2aaSAndroid Build Coastguard Worker ++fCount;
210*c8dee2aaSAndroid Build Coastguard Worker return (fCount >= fLimit) || INHERITED::visitExpression(e);
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker
visitProgramElement(const ProgramElement & p)213*c8dee2aaSAndroid Build Coastguard Worker bool visitProgramElement(const ProgramElement& p) override {
214*c8dee2aaSAndroid Build Coastguard Worker ++fCount;
215*c8dee2aaSAndroid Build Coastguard Worker return (fCount >= fLimit) || INHERITED::visitProgramElement(p);
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker
visitStatement(const Statement & s)218*c8dee2aaSAndroid Build Coastguard Worker bool visitStatement(const Statement& s) override {
219*c8dee2aaSAndroid Build Coastguard Worker ++fCount;
220*c8dee2aaSAndroid Build Coastguard Worker return (fCount >= fLimit) || INHERITED::visitStatement(s);
221*c8dee2aaSAndroid Build Coastguard Worker }
222*c8dee2aaSAndroid Build Coastguard Worker
223*c8dee2aaSAndroid Build Coastguard Worker private:
224*c8dee2aaSAndroid Build Coastguard Worker int fCount = 0;
225*c8dee2aaSAndroid Build Coastguard Worker int fLimit;
226*c8dee2aaSAndroid Build Coastguard Worker
227*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
228*c8dee2aaSAndroid Build Coastguard Worker };
229*c8dee2aaSAndroid Build Coastguard Worker
230*c8dee2aaSAndroid Build Coastguard Worker class VariableWriteVisitor : public ProgramVisitor {
231*c8dee2aaSAndroid Build Coastguard Worker public:
VariableWriteVisitor(const Variable * var)232*c8dee2aaSAndroid Build Coastguard Worker VariableWriteVisitor(const Variable* var)
233*c8dee2aaSAndroid Build Coastguard Worker : fVar(var) {}
234*c8dee2aaSAndroid Build Coastguard Worker
visit(const Statement & s)235*c8dee2aaSAndroid Build Coastguard Worker bool visit(const Statement& s) {
236*c8dee2aaSAndroid Build Coastguard Worker return this->visitStatement(s);
237*c8dee2aaSAndroid Build Coastguard Worker }
238*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & e)239*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& e) override {
240*c8dee2aaSAndroid Build Coastguard Worker if (e.is<VariableReference>()) {
241*c8dee2aaSAndroid Build Coastguard Worker const VariableReference& ref = e.as<VariableReference>();
242*c8dee2aaSAndroid Build Coastguard Worker if (ref.variable() == fVar &&
243*c8dee2aaSAndroid Build Coastguard Worker (ref.refKind() == VariableReference::RefKind::kWrite ||
244*c8dee2aaSAndroid Build Coastguard Worker ref.refKind() == VariableReference::RefKind::kReadWrite ||
245*c8dee2aaSAndroid Build Coastguard Worker ref.refKind() == VariableReference::RefKind::kPointer)) {
246*c8dee2aaSAndroid Build Coastguard Worker return true;
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(e);
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker
252*c8dee2aaSAndroid Build Coastguard Worker private:
253*c8dee2aaSAndroid Build Coastguard Worker const Variable* fVar;
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
256*c8dee2aaSAndroid Build Coastguard Worker };
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker // This isn't actually using ProgramVisitor, because it only considers a subset of the fields for
259*c8dee2aaSAndroid Build Coastguard Worker // any given expression kind. For instance, when indexing an array (e.g. `x[1]`), we only want to
260*c8dee2aaSAndroid Build Coastguard Worker // know if the base (`x`) is assignable; the index expression (`1`) doesn't need to be.
261*c8dee2aaSAndroid Build Coastguard Worker class IsAssignableVisitor {
262*c8dee2aaSAndroid Build Coastguard Worker public:
IsAssignableVisitor(ErrorReporter * errors)263*c8dee2aaSAndroid Build Coastguard Worker IsAssignableVisitor(ErrorReporter* errors) : fErrors(errors) {}
264*c8dee2aaSAndroid Build Coastguard Worker
visit(Expression & expr,Analysis::AssignmentInfo * info)265*c8dee2aaSAndroid Build Coastguard Worker bool visit(Expression& expr, Analysis::AssignmentInfo* info) {
266*c8dee2aaSAndroid Build Coastguard Worker int oldErrorCount = fErrors->errorCount();
267*c8dee2aaSAndroid Build Coastguard Worker this->visitExpression(expr);
268*c8dee2aaSAndroid Build Coastguard Worker if (info) {
269*c8dee2aaSAndroid Build Coastguard Worker info->fAssignedVar = fAssignedVar;
270*c8dee2aaSAndroid Build Coastguard Worker }
271*c8dee2aaSAndroid Build Coastguard Worker return fErrors->errorCount() == oldErrorCount;
272*c8dee2aaSAndroid Build Coastguard Worker }
273*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(Expression & expr,const FieldAccess * fieldAccess=nullptr)274*c8dee2aaSAndroid Build Coastguard Worker void visitExpression(Expression& expr, const FieldAccess* fieldAccess = nullptr) {
275*c8dee2aaSAndroid Build Coastguard Worker switch (expr.kind()) {
276*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kVariableReference: {
277*c8dee2aaSAndroid Build Coastguard Worker VariableReference& varRef = expr.as<VariableReference>();
278*c8dee2aaSAndroid Build Coastguard Worker const Variable* var = varRef.variable();
279*c8dee2aaSAndroid Build Coastguard Worker auto fieldName = [&] {
280*c8dee2aaSAndroid Build Coastguard Worker return fieldAccess ? fieldAccess->description(OperatorPrecedence::kExpression)
281*c8dee2aaSAndroid Build Coastguard Worker : std::string(var->name());
282*c8dee2aaSAndroid Build Coastguard Worker };
283*c8dee2aaSAndroid Build Coastguard Worker if (var->modifierFlags().isConst() || var->modifierFlags().isUniform()) {
284*c8dee2aaSAndroid Build Coastguard Worker fErrors->error(expr.fPosition,
285*c8dee2aaSAndroid Build Coastguard Worker "cannot modify immutable variable '" + fieldName() + "'");
286*c8dee2aaSAndroid Build Coastguard Worker } else if (var->storage() == Variable::Storage::kGlobal &&
287*c8dee2aaSAndroid Build Coastguard Worker (var->modifierFlags() & ModifierFlag::kIn)) {
288*c8dee2aaSAndroid Build Coastguard Worker fErrors->error(expr.fPosition,
289*c8dee2aaSAndroid Build Coastguard Worker "cannot modify pipeline input variable '" + fieldName() + "'");
290*c8dee2aaSAndroid Build Coastguard Worker } else {
291*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fAssignedVar == nullptr);
292*c8dee2aaSAndroid Build Coastguard Worker fAssignedVar = &varRef;
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker break;
295*c8dee2aaSAndroid Build Coastguard Worker }
296*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFieldAccess: {
297*c8dee2aaSAndroid Build Coastguard Worker const FieldAccess& f = expr.as<FieldAccess>();
298*c8dee2aaSAndroid Build Coastguard Worker this->visitExpression(*f.base(), &f);
299*c8dee2aaSAndroid Build Coastguard Worker break;
300*c8dee2aaSAndroid Build Coastguard Worker }
301*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kSwizzle: {
302*c8dee2aaSAndroid Build Coastguard Worker const Swizzle& swizzle = expr.as<Swizzle>();
303*c8dee2aaSAndroid Build Coastguard Worker this->checkSwizzleWrite(swizzle);
304*c8dee2aaSAndroid Build Coastguard Worker this->visitExpression(*swizzle.base(), fieldAccess);
305*c8dee2aaSAndroid Build Coastguard Worker break;
306*c8dee2aaSAndroid Build Coastguard Worker }
307*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kIndex:
308*c8dee2aaSAndroid Build Coastguard Worker this->visitExpression(*expr.as<IndexExpression>().base(), fieldAccess);
309*c8dee2aaSAndroid Build Coastguard Worker break;
310*c8dee2aaSAndroid Build Coastguard Worker
311*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kPoison:
312*c8dee2aaSAndroid Build Coastguard Worker break;
313*c8dee2aaSAndroid Build Coastguard Worker
314*c8dee2aaSAndroid Build Coastguard Worker default:
315*c8dee2aaSAndroid Build Coastguard Worker fErrors->error(expr.fPosition, "cannot assign to this expression");
316*c8dee2aaSAndroid Build Coastguard Worker break;
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker }
319*c8dee2aaSAndroid Build Coastguard Worker
320*c8dee2aaSAndroid Build Coastguard Worker private:
checkSwizzleWrite(const Swizzle & swizzle)321*c8dee2aaSAndroid Build Coastguard Worker void checkSwizzleWrite(const Swizzle& swizzle) {
322*c8dee2aaSAndroid Build Coastguard Worker int bits = 0;
323*c8dee2aaSAndroid Build Coastguard Worker for (int8_t idx : swizzle.components()) {
324*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(idx >= SwizzleComponent::X && idx <= SwizzleComponent::W);
325*c8dee2aaSAndroid Build Coastguard Worker int bit = 1 << idx;
326*c8dee2aaSAndroid Build Coastguard Worker if (bits & bit) {
327*c8dee2aaSAndroid Build Coastguard Worker fErrors->error(swizzle.fPosition,
328*c8dee2aaSAndroid Build Coastguard Worker "cannot write to the same swizzle field more than once");
329*c8dee2aaSAndroid Build Coastguard Worker break;
330*c8dee2aaSAndroid Build Coastguard Worker }
331*c8dee2aaSAndroid Build Coastguard Worker bits |= bit;
332*c8dee2aaSAndroid Build Coastguard Worker }
333*c8dee2aaSAndroid Build Coastguard Worker }
334*c8dee2aaSAndroid Build Coastguard Worker
335*c8dee2aaSAndroid Build Coastguard Worker ErrorReporter* fErrors;
336*c8dee2aaSAndroid Build Coastguard Worker VariableReference* fAssignedVar = nullptr;
337*c8dee2aaSAndroid Build Coastguard Worker
338*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
339*c8dee2aaSAndroid Build Coastguard Worker };
340*c8dee2aaSAndroid Build Coastguard Worker
341*c8dee2aaSAndroid Build Coastguard Worker } // namespace
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
344*c8dee2aaSAndroid Build Coastguard Worker // Analysis
345*c8dee2aaSAndroid Build Coastguard Worker
GetSampleUsage(const Program & program,const Variable & child,bool writesToSampleCoords,int * elidedSampleCoordCount)346*c8dee2aaSAndroid Build Coastguard Worker SampleUsage Analysis::GetSampleUsage(const Program& program,
347*c8dee2aaSAndroid Build Coastguard Worker const Variable& child,
348*c8dee2aaSAndroid Build Coastguard Worker bool writesToSampleCoords,
349*c8dee2aaSAndroid Build Coastguard Worker int* elidedSampleCoordCount) {
350*c8dee2aaSAndroid Build Coastguard Worker MergeSampleUsageVisitor visitor(*program.fContext, child, writesToSampleCoords);
351*c8dee2aaSAndroid Build Coastguard Worker SampleUsage result = visitor.visit(program);
352*c8dee2aaSAndroid Build Coastguard Worker if (elidedSampleCoordCount) {
353*c8dee2aaSAndroid Build Coastguard Worker *elidedSampleCoordCount += visitor.elidedSampleCoordCount();
354*c8dee2aaSAndroid Build Coastguard Worker }
355*c8dee2aaSAndroid Build Coastguard Worker return result;
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
ReferencesBuiltin(const Program & program,int builtin)358*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ReferencesBuiltin(const Program& program, int builtin) {
359*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(program.fUsage);
360*c8dee2aaSAndroid Build Coastguard Worker for (const auto& [variable, counts] : program.fUsage->fVariableCounts) {
361*c8dee2aaSAndroid Build Coastguard Worker if (counts.fRead > 0 && variable->layout().fBuiltin == builtin) {
362*c8dee2aaSAndroid Build Coastguard Worker return true;
363*c8dee2aaSAndroid Build Coastguard Worker }
364*c8dee2aaSAndroid Build Coastguard Worker }
365*c8dee2aaSAndroid Build Coastguard Worker return false;
366*c8dee2aaSAndroid Build Coastguard Worker }
367*c8dee2aaSAndroid Build Coastguard Worker
ReferencesSampleCoords(const Program & program)368*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ReferencesSampleCoords(const Program& program) {
369*c8dee2aaSAndroid Build Coastguard Worker // Look for main().
370*c8dee2aaSAndroid Build Coastguard Worker for (const std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
371*c8dee2aaSAndroid Build Coastguard Worker if (pe->is<FunctionDefinition>()) {
372*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration& func = pe->as<FunctionDefinition>().declaration();
373*c8dee2aaSAndroid Build Coastguard Worker if (func.isMain()) {
374*c8dee2aaSAndroid Build Coastguard Worker // See if main() has a coords parameter that is read from anywhere.
375*c8dee2aaSAndroid Build Coastguard Worker if (const Variable* coords = func.getMainCoordsParameter()) {
376*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage::VariableCounts counts = program.fUsage->get(*coords);
377*c8dee2aaSAndroid Build Coastguard Worker return counts.fRead > 0;
378*c8dee2aaSAndroid Build Coastguard Worker }
379*c8dee2aaSAndroid Build Coastguard Worker }
380*c8dee2aaSAndroid Build Coastguard Worker }
381*c8dee2aaSAndroid Build Coastguard Worker }
382*c8dee2aaSAndroid Build Coastguard Worker // The program is missing a main().
383*c8dee2aaSAndroid Build Coastguard Worker return false;
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker
ReferencesFragCoords(const Program & program)386*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ReferencesFragCoords(const Program& program) {
387*c8dee2aaSAndroid Build Coastguard Worker return Analysis::ReferencesBuiltin(program, SK_FRAGCOORD_BUILTIN);
388*c8dee2aaSAndroid Build Coastguard Worker }
389*c8dee2aaSAndroid Build Coastguard Worker
CallsSampleOutsideMain(const Program & program)390*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::CallsSampleOutsideMain(const Program& program) {
391*c8dee2aaSAndroid Build Coastguard Worker SampleOutsideMainVisitor visitor;
392*c8dee2aaSAndroid Build Coastguard Worker return visitor.visit(program);
393*c8dee2aaSAndroid Build Coastguard Worker }
394*c8dee2aaSAndroid Build Coastguard Worker
CallsColorTransformIntrinsics(const Program & program)395*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::CallsColorTransformIntrinsics(const Program& program) {
396*c8dee2aaSAndroid Build Coastguard Worker for (auto [symbol, count] : program.usage()->fCallCounts) {
397*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration& fn = symbol->as<FunctionDeclaration>();
398*c8dee2aaSAndroid Build Coastguard Worker if (count != 0 && (fn.intrinsicKind() == k_toLinearSrgb_IntrinsicKind ||
399*c8dee2aaSAndroid Build Coastguard Worker fn.intrinsicKind() == k_fromLinearSrgb_IntrinsicKind)) {
400*c8dee2aaSAndroid Build Coastguard Worker return true;
401*c8dee2aaSAndroid Build Coastguard Worker }
402*c8dee2aaSAndroid Build Coastguard Worker }
403*c8dee2aaSAndroid Build Coastguard Worker return false;
404*c8dee2aaSAndroid Build Coastguard Worker }
405*c8dee2aaSAndroid Build Coastguard Worker
ReturnsOpaqueColor(const FunctionDefinition & function)406*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ReturnsOpaqueColor(const FunctionDefinition& function) {
407*c8dee2aaSAndroid Build Coastguard Worker ReturnsNonOpaqueColorVisitor visitor;
408*c8dee2aaSAndroid Build Coastguard Worker return !visitor.visitProgramElement(function);
409*c8dee2aaSAndroid Build Coastguard Worker }
410*c8dee2aaSAndroid Build Coastguard Worker
ContainsRTAdjust(const Expression & expr)411*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ContainsRTAdjust(const Expression& expr) {
412*c8dee2aaSAndroid Build Coastguard Worker class ContainsRTAdjustVisitor : public ProgramVisitor {
413*c8dee2aaSAndroid Build Coastguard Worker public:
414*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& expr) override {
415*c8dee2aaSAndroid Build Coastguard Worker if (expr.is<VariableReference>() &&
416*c8dee2aaSAndroid Build Coastguard Worker expr.as<VariableReference>().variable()->name() == Compiler::RTADJUST_NAME) {
417*c8dee2aaSAndroid Build Coastguard Worker return true;
418*c8dee2aaSAndroid Build Coastguard Worker }
419*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(expr);
420*c8dee2aaSAndroid Build Coastguard Worker }
421*c8dee2aaSAndroid Build Coastguard Worker
422*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
423*c8dee2aaSAndroid Build Coastguard Worker };
424*c8dee2aaSAndroid Build Coastguard Worker
425*c8dee2aaSAndroid Build Coastguard Worker ContainsRTAdjustVisitor visitor;
426*c8dee2aaSAndroid Build Coastguard Worker return visitor.visitExpression(expr);
427*c8dee2aaSAndroid Build Coastguard Worker }
428*c8dee2aaSAndroid Build Coastguard Worker
ContainsVariable(const Expression & expr,const Variable & var)429*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::ContainsVariable(const Expression& expr, const Variable& var) {
430*c8dee2aaSAndroid Build Coastguard Worker class ContainsVariableVisitor : public ProgramVisitor {
431*c8dee2aaSAndroid Build Coastguard Worker public:
432*c8dee2aaSAndroid Build Coastguard Worker ContainsVariableVisitor(const Variable* v) : fVariable(v) {}
433*c8dee2aaSAndroid Build Coastguard Worker
434*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& expr) override {
435*c8dee2aaSAndroid Build Coastguard Worker if (expr.is<VariableReference>() &&
436*c8dee2aaSAndroid Build Coastguard Worker expr.as<VariableReference>().variable() == fVariable) {
437*c8dee2aaSAndroid Build Coastguard Worker return true;
438*c8dee2aaSAndroid Build Coastguard Worker }
439*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(expr);
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
443*c8dee2aaSAndroid Build Coastguard Worker const Variable* fVariable;
444*c8dee2aaSAndroid Build Coastguard Worker };
445*c8dee2aaSAndroid Build Coastguard Worker
446*c8dee2aaSAndroid Build Coastguard Worker ContainsVariableVisitor visitor{&var};
447*c8dee2aaSAndroid Build Coastguard Worker return visitor.visitExpression(expr);
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker
IsCompileTimeConstant(const Expression & expr)450*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::IsCompileTimeConstant(const Expression& expr) {
451*c8dee2aaSAndroid Build Coastguard Worker class IsCompileTimeConstantVisitor : public ProgramVisitor {
452*c8dee2aaSAndroid Build Coastguard Worker public:
453*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& expr) override {
454*c8dee2aaSAndroid Build Coastguard Worker switch (expr.kind()) {
455*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kLiteral:
456*c8dee2aaSAndroid Build Coastguard Worker // Literals are compile-time constants.
457*c8dee2aaSAndroid Build Coastguard Worker return false;
458*c8dee2aaSAndroid Build Coastguard Worker
459*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorArray:
460*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorCompound:
461*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorDiagonalMatrix:
462*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorMatrixResize:
463*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorSplat:
464*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorStruct:
465*c8dee2aaSAndroid Build Coastguard Worker // Constructors might be compile-time constants, if they are composed entirely
466*c8dee2aaSAndroid Build Coastguard Worker // of literals and constructors. (Casting constructors are intentionally omitted
467*c8dee2aaSAndroid Build Coastguard Worker // here. If the value inside was a compile-time constant, we would have not have
468*c8dee2aaSAndroid Build Coastguard Worker // generated a cast at all.)
469*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(expr);
470*c8dee2aaSAndroid Build Coastguard Worker
471*c8dee2aaSAndroid Build Coastguard Worker default:
472*c8dee2aaSAndroid Build Coastguard Worker // This expression isn't a compile-time constant.
473*c8dee2aaSAndroid Build Coastguard Worker fIsConstant = false;
474*c8dee2aaSAndroid Build Coastguard Worker return true;
475*c8dee2aaSAndroid Build Coastguard Worker }
476*c8dee2aaSAndroid Build Coastguard Worker }
477*c8dee2aaSAndroid Build Coastguard Worker
478*c8dee2aaSAndroid Build Coastguard Worker bool fIsConstant = true;
479*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
480*c8dee2aaSAndroid Build Coastguard Worker };
481*c8dee2aaSAndroid Build Coastguard Worker
482*c8dee2aaSAndroid Build Coastguard Worker IsCompileTimeConstantVisitor visitor;
483*c8dee2aaSAndroid Build Coastguard Worker visitor.visitExpression(expr);
484*c8dee2aaSAndroid Build Coastguard Worker return visitor.fIsConstant;
485*c8dee2aaSAndroid Build Coastguard Worker }
486*c8dee2aaSAndroid Build Coastguard Worker
DetectVarDeclarationWithoutScope(const Statement & stmt,ErrorReporter * errors)487*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::DetectVarDeclarationWithoutScope(const Statement& stmt, ErrorReporter* errors) {
488*c8dee2aaSAndroid Build Coastguard Worker // A variable declaration can create either a lone VarDeclaration or an unscoped Block
489*c8dee2aaSAndroid Build Coastguard Worker // containing multiple VarDeclaration statements. We need to detect either case.
490*c8dee2aaSAndroid Build Coastguard Worker const Variable* var;
491*c8dee2aaSAndroid Build Coastguard Worker if (stmt.is<VarDeclaration>()) {
492*c8dee2aaSAndroid Build Coastguard Worker // The single-variable case. No blocks at all.
493*c8dee2aaSAndroid Build Coastguard Worker var = stmt.as<VarDeclaration>().var();
494*c8dee2aaSAndroid Build Coastguard Worker } else if (stmt.is<Block>()) {
495*c8dee2aaSAndroid Build Coastguard Worker // The multiple-variable case: an unscoped, non-empty block...
496*c8dee2aaSAndroid Build Coastguard Worker const Block& block = stmt.as<Block>();
497*c8dee2aaSAndroid Build Coastguard Worker if (block.isScope() || block.children().empty()) {
498*c8dee2aaSAndroid Build Coastguard Worker return false;
499*c8dee2aaSAndroid Build Coastguard Worker }
500*c8dee2aaSAndroid Build Coastguard Worker // ... holding a variable declaration.
501*c8dee2aaSAndroid Build Coastguard Worker const Statement& innerStmt = *block.children().front();
502*c8dee2aaSAndroid Build Coastguard Worker if (!innerStmt.is<VarDeclaration>()) {
503*c8dee2aaSAndroid Build Coastguard Worker return false;
504*c8dee2aaSAndroid Build Coastguard Worker }
505*c8dee2aaSAndroid Build Coastguard Worker var = innerStmt.as<VarDeclaration>().var();
506*c8dee2aaSAndroid Build Coastguard Worker } else {
507*c8dee2aaSAndroid Build Coastguard Worker // This statement wasn't a variable declaration. No problem.
508*c8dee2aaSAndroid Build Coastguard Worker return false;
509*c8dee2aaSAndroid Build Coastguard Worker }
510*c8dee2aaSAndroid Build Coastguard Worker
511*c8dee2aaSAndroid Build Coastguard Worker // Report an error.
512*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(var);
513*c8dee2aaSAndroid Build Coastguard Worker if (errors) {
514*c8dee2aaSAndroid Build Coastguard Worker errors->error(var->fPosition,
515*c8dee2aaSAndroid Build Coastguard Worker "variable '" + std::string(var->name()) + "' must be created in a scope");
516*c8dee2aaSAndroid Build Coastguard Worker }
517*c8dee2aaSAndroid Build Coastguard Worker return true;
518*c8dee2aaSAndroid Build Coastguard Worker }
519*c8dee2aaSAndroid Build Coastguard Worker
NodeCountUpToLimit(const FunctionDefinition & function,int limit)520*c8dee2aaSAndroid Build Coastguard Worker int Analysis::NodeCountUpToLimit(const FunctionDefinition& function, int limit) {
521*c8dee2aaSAndroid Build Coastguard Worker return NodeCountVisitor{limit}.visit(*function.body());
522*c8dee2aaSAndroid Build Coastguard Worker }
523*c8dee2aaSAndroid Build Coastguard Worker
StatementWritesToVariable(const Statement & stmt,const Variable & var)524*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::StatementWritesToVariable(const Statement& stmt, const Variable& var) {
525*c8dee2aaSAndroid Build Coastguard Worker return VariableWriteVisitor(&var).visit(stmt);
526*c8dee2aaSAndroid Build Coastguard Worker }
527*c8dee2aaSAndroid Build Coastguard Worker
IsAssignable(Expression & expr,AssignmentInfo * info,ErrorReporter * errors)528*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::IsAssignable(Expression& expr, AssignmentInfo* info, ErrorReporter* errors) {
529*c8dee2aaSAndroid Build Coastguard Worker NoOpErrorReporter unusedErrors;
530*c8dee2aaSAndroid Build Coastguard Worker return IsAssignableVisitor{errors ? errors : &unusedErrors}.visit(expr, info);
531*c8dee2aaSAndroid Build Coastguard Worker }
532*c8dee2aaSAndroid Build Coastguard Worker
UpdateVariableRefKind(Expression * expr,VariableReference::RefKind kind,ErrorReporter * errors)533*c8dee2aaSAndroid Build Coastguard Worker bool Analysis::UpdateVariableRefKind(Expression* expr,
534*c8dee2aaSAndroid Build Coastguard Worker VariableReference::RefKind kind,
535*c8dee2aaSAndroid Build Coastguard Worker ErrorReporter* errors) {
536*c8dee2aaSAndroid Build Coastguard Worker Analysis::AssignmentInfo info;
537*c8dee2aaSAndroid Build Coastguard Worker if (!Analysis::IsAssignable(*expr, &info, errors)) {
538*c8dee2aaSAndroid Build Coastguard Worker return false;
539*c8dee2aaSAndroid Build Coastguard Worker }
540*c8dee2aaSAndroid Build Coastguard Worker if (!info.fAssignedVar) {
541*c8dee2aaSAndroid Build Coastguard Worker if (errors) {
542*c8dee2aaSAndroid Build Coastguard Worker errors->error(expr->fPosition, "can't assign to expression '" + expr->description() +
543*c8dee2aaSAndroid Build Coastguard Worker "'");
544*c8dee2aaSAndroid Build Coastguard Worker }
545*c8dee2aaSAndroid Build Coastguard Worker return false;
546*c8dee2aaSAndroid Build Coastguard Worker }
547*c8dee2aaSAndroid Build Coastguard Worker info.fAssignedVar->setRefKind(kind);
548*c8dee2aaSAndroid Build Coastguard Worker return true;
549*c8dee2aaSAndroid Build Coastguard Worker }
550*c8dee2aaSAndroid Build Coastguard Worker
551*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
552*c8dee2aaSAndroid Build Coastguard Worker // ProgramVisitor
553*c8dee2aaSAndroid Build Coastguard Worker
visit(const Program & program)554*c8dee2aaSAndroid Build Coastguard Worker bool ProgramVisitor::visit(const Program& program) {
555*c8dee2aaSAndroid Build Coastguard Worker for (const ProgramElement* pe : program.elements()) {
556*c8dee2aaSAndroid Build Coastguard Worker if (this->visitProgramElement(*pe)) {
557*c8dee2aaSAndroid Build Coastguard Worker return true;
558*c8dee2aaSAndroid Build Coastguard Worker }
559*c8dee2aaSAndroid Build Coastguard Worker }
560*c8dee2aaSAndroid Build Coastguard Worker return false;
561*c8dee2aaSAndroid Build Coastguard Worker }
562*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(typename T::Expression & e)563*c8dee2aaSAndroid Build Coastguard Worker template <typename T> bool TProgramVisitor<T>::visitExpression(typename T::Expression& e) {
564*c8dee2aaSAndroid Build Coastguard Worker switch (e.kind()) {
565*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kEmpty:
566*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFunctionReference:
567*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kLiteral:
568*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kMethodReference:
569*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kPoison:
570*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kSetting:
571*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kTypeReference:
572*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kVariableReference:
573*c8dee2aaSAndroid Build Coastguard Worker // Leaf expressions return false
574*c8dee2aaSAndroid Build Coastguard Worker return false;
575*c8dee2aaSAndroid Build Coastguard Worker
576*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kBinary: {
577*c8dee2aaSAndroid Build Coastguard Worker auto& b = e.template as<BinaryExpression>();
578*c8dee2aaSAndroid Build Coastguard Worker return (b.left() && this->visitExpressionPtr(b.left())) ||
579*c8dee2aaSAndroid Build Coastguard Worker (b.right() && this->visitExpressionPtr(b.right()));
580*c8dee2aaSAndroid Build Coastguard Worker }
581*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kChildCall: {
582*c8dee2aaSAndroid Build Coastguard Worker // We don't visit the child variable itself, just the arguments
583*c8dee2aaSAndroid Build Coastguard Worker auto& c = e.template as<ChildCall>();
584*c8dee2aaSAndroid Build Coastguard Worker for (auto& arg : c.arguments()) {
585*c8dee2aaSAndroid Build Coastguard Worker if (arg && this->visitExpressionPtr(arg)) { return true; }
586*c8dee2aaSAndroid Build Coastguard Worker }
587*c8dee2aaSAndroid Build Coastguard Worker return false;
588*c8dee2aaSAndroid Build Coastguard Worker }
589*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorArray:
590*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorArrayCast:
591*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorCompound:
592*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorCompoundCast:
593*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorDiagonalMatrix:
594*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorMatrixResize:
595*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorScalarCast:
596*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorSplat:
597*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kConstructorStruct: {
598*c8dee2aaSAndroid Build Coastguard Worker auto& c = e.asAnyConstructor();
599*c8dee2aaSAndroid Build Coastguard Worker for (auto& arg : c.argumentSpan()) {
600*c8dee2aaSAndroid Build Coastguard Worker if (this->visitExpressionPtr(arg)) { return true; }
601*c8dee2aaSAndroid Build Coastguard Worker }
602*c8dee2aaSAndroid Build Coastguard Worker return false;
603*c8dee2aaSAndroid Build Coastguard Worker }
604*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFieldAccess:
605*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(e.template as<FieldAccess>().base());
606*c8dee2aaSAndroid Build Coastguard Worker
607*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFunctionCall: {
608*c8dee2aaSAndroid Build Coastguard Worker auto& c = e.template as<FunctionCall>();
609*c8dee2aaSAndroid Build Coastguard Worker for (auto& arg : c.arguments()) {
610*c8dee2aaSAndroid Build Coastguard Worker if (arg && this->visitExpressionPtr(arg)) { return true; }
611*c8dee2aaSAndroid Build Coastguard Worker }
612*c8dee2aaSAndroid Build Coastguard Worker return false;
613*c8dee2aaSAndroid Build Coastguard Worker }
614*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kIndex: {
615*c8dee2aaSAndroid Build Coastguard Worker auto& i = e.template as<IndexExpression>();
616*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(i.base()) || this->visitExpressionPtr(i.index());
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kPostfix:
619*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(e.template as<PostfixExpression>().operand());
620*c8dee2aaSAndroid Build Coastguard Worker
621*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kPrefix:
622*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(e.template as<PrefixExpression>().operand());
623*c8dee2aaSAndroid Build Coastguard Worker
624*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kSwizzle: {
625*c8dee2aaSAndroid Build Coastguard Worker auto& s = e.template as<Swizzle>();
626*c8dee2aaSAndroid Build Coastguard Worker return s.base() && this->visitExpressionPtr(s.base());
627*c8dee2aaSAndroid Build Coastguard Worker }
628*c8dee2aaSAndroid Build Coastguard Worker
629*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kTernary: {
630*c8dee2aaSAndroid Build Coastguard Worker auto& t = e.template as<TernaryExpression>();
631*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(t.test()) ||
632*c8dee2aaSAndroid Build Coastguard Worker (t.ifTrue() && this->visitExpressionPtr(t.ifTrue())) ||
633*c8dee2aaSAndroid Build Coastguard Worker (t.ifFalse() && this->visitExpressionPtr(t.ifFalse()));
634*c8dee2aaSAndroid Build Coastguard Worker }
635*c8dee2aaSAndroid Build Coastguard Worker default:
636*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
637*c8dee2aaSAndroid Build Coastguard Worker }
638*c8dee2aaSAndroid Build Coastguard Worker }
639*c8dee2aaSAndroid Build Coastguard Worker
visitStatement(typename T::Statement & s)640*c8dee2aaSAndroid Build Coastguard Worker template <typename T> bool TProgramVisitor<T>::visitStatement(typename T::Statement& s) {
641*c8dee2aaSAndroid Build Coastguard Worker switch (s.kind()) {
642*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kBreak:
643*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kContinue:
644*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kDiscard:
645*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kNop:
646*c8dee2aaSAndroid Build Coastguard Worker // Leaf statements just return false
647*c8dee2aaSAndroid Build Coastguard Worker return false;
648*c8dee2aaSAndroid Build Coastguard Worker
649*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kBlock:
650*c8dee2aaSAndroid Build Coastguard Worker for (auto& stmt : s.template as<Block>().children()) {
651*c8dee2aaSAndroid Build Coastguard Worker if (stmt && this->visitStatementPtr(stmt)) {
652*c8dee2aaSAndroid Build Coastguard Worker return true;
653*c8dee2aaSAndroid Build Coastguard Worker }
654*c8dee2aaSAndroid Build Coastguard Worker }
655*c8dee2aaSAndroid Build Coastguard Worker return false;
656*c8dee2aaSAndroid Build Coastguard Worker
657*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kSwitchCase: {
658*c8dee2aaSAndroid Build Coastguard Worker auto& sc = s.template as<SwitchCase>();
659*c8dee2aaSAndroid Build Coastguard Worker return this->visitStatementPtr(sc.statement());
660*c8dee2aaSAndroid Build Coastguard Worker }
661*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kDo: {
662*c8dee2aaSAndroid Build Coastguard Worker auto& d = s.template as<DoStatement>();
663*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(d.test()) || this->visitStatementPtr(d.statement());
664*c8dee2aaSAndroid Build Coastguard Worker }
665*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kExpression:
666*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(s.template as<ExpressionStatement>().expression());
667*c8dee2aaSAndroid Build Coastguard Worker
668*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kFor: {
669*c8dee2aaSAndroid Build Coastguard Worker auto& f = s.template as<ForStatement>();
670*c8dee2aaSAndroid Build Coastguard Worker return (f.initializer() && this->visitStatementPtr(f.initializer())) ||
671*c8dee2aaSAndroid Build Coastguard Worker (f.test() && this->visitExpressionPtr(f.test())) ||
672*c8dee2aaSAndroid Build Coastguard Worker (f.next() && this->visitExpressionPtr(f.next())) ||
673*c8dee2aaSAndroid Build Coastguard Worker this->visitStatementPtr(f.statement());
674*c8dee2aaSAndroid Build Coastguard Worker }
675*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kIf: {
676*c8dee2aaSAndroid Build Coastguard Worker auto& i = s.template as<IfStatement>();
677*c8dee2aaSAndroid Build Coastguard Worker return (i.test() && this->visitExpressionPtr(i.test())) ||
678*c8dee2aaSAndroid Build Coastguard Worker (i.ifTrue() && this->visitStatementPtr(i.ifTrue())) ||
679*c8dee2aaSAndroid Build Coastguard Worker (i.ifFalse() && this->visitStatementPtr(i.ifFalse()));
680*c8dee2aaSAndroid Build Coastguard Worker }
681*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kReturn: {
682*c8dee2aaSAndroid Build Coastguard Worker auto& r = s.template as<ReturnStatement>();
683*c8dee2aaSAndroid Build Coastguard Worker return r.expression() && this->visitExpressionPtr(r.expression());
684*c8dee2aaSAndroid Build Coastguard Worker }
685*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kSwitch: {
686*c8dee2aaSAndroid Build Coastguard Worker auto& sw = s.template as<SwitchStatement>();
687*c8dee2aaSAndroid Build Coastguard Worker return this->visitExpressionPtr(sw.value()) || this->visitStatementPtr(sw.caseBlock());
688*c8dee2aaSAndroid Build Coastguard Worker }
689*c8dee2aaSAndroid Build Coastguard Worker case Statement::Kind::kVarDeclaration: {
690*c8dee2aaSAndroid Build Coastguard Worker auto& v = s.template as<VarDeclaration>();
691*c8dee2aaSAndroid Build Coastguard Worker return v.value() && this->visitExpressionPtr(v.value());
692*c8dee2aaSAndroid Build Coastguard Worker }
693*c8dee2aaSAndroid Build Coastguard Worker default:
694*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
695*c8dee2aaSAndroid Build Coastguard Worker }
696*c8dee2aaSAndroid Build Coastguard Worker }
697*c8dee2aaSAndroid Build Coastguard Worker
visitProgramElement(typename T::ProgramElement & pe)698*c8dee2aaSAndroid Build Coastguard Worker template <typename T> bool TProgramVisitor<T>::visitProgramElement(typename T::ProgramElement& pe) {
699*c8dee2aaSAndroid Build Coastguard Worker switch (pe.kind()) {
700*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kExtension:
701*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kFunctionPrototype:
702*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kInterfaceBlock:
703*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kModifiers:
704*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kStructDefinition:
705*c8dee2aaSAndroid Build Coastguard Worker // Leaf program elements just return false by default
706*c8dee2aaSAndroid Build Coastguard Worker return false;
707*c8dee2aaSAndroid Build Coastguard Worker
708*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kFunction:
709*c8dee2aaSAndroid Build Coastguard Worker return this->visitStatementPtr(pe.template as<FunctionDefinition>().body());
710*c8dee2aaSAndroid Build Coastguard Worker
711*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kGlobalVar:
712*c8dee2aaSAndroid Build Coastguard Worker return this->visitStatementPtr(pe.template as<GlobalVarDeclaration>().declaration());
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker default:
715*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
716*c8dee2aaSAndroid Build Coastguard Worker }
717*c8dee2aaSAndroid Build Coastguard Worker }
718*c8dee2aaSAndroid Build Coastguard Worker
719*c8dee2aaSAndroid Build Coastguard Worker template class TProgramVisitor<ProgramVisitorTypes>;
720*c8dee2aaSAndroid Build Coastguard Worker template class TProgramVisitor<ProgramWriterTypes>;
721*c8dee2aaSAndroid Build Coastguard Worker
722*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
723