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#include "src/sksl/SkSLAnalysis.h"
9
10#include "include/private/SkSLSampleUsage.h"
11#include "src/sksl/ir/SkSLExpression.h"
12#include "src/sksl/ir/SkSLProgram.h"
13#include "src/sksl/ir/SkSLProgramElement.h"
14#include "src/sksl/ir/SkSLStatement.h"
15
16// ProgramElements
17#include "src/sksl/ir/SkSLEnum.h"
18#include "src/sksl/ir/SkSLExtension.h"
19#include "src/sksl/ir/SkSLFunctionDefinition.h"
20#include "src/sksl/ir/SkSLInterfaceBlock.h"
21#include "src/sksl/ir/SkSLModifiers.h"
22#include "src/sksl/ir/SkSLSection.h"
23#include "src/sksl/ir/SkSLVarDeclarations.h"
24
25// Statements
26#include "src/sksl/ir/SkSLBlock.h"
27#include "src/sksl/ir/SkSLBreakStatement.h"
28#include "src/sksl/ir/SkSLContinueStatement.h"
29#include "src/sksl/ir/SkSLDiscardStatement.h"
30#include "src/sksl/ir/SkSLDoStatement.h"
31#include "src/sksl/ir/SkSLExpressionStatement.h"
32#include "src/sksl/ir/SkSLForStatement.h"
33#include "src/sksl/ir/SkSLIfStatement.h"
34#include "src/sksl/ir/SkSLNop.h"
35#include "src/sksl/ir/SkSLReturnStatement.h"
36#include "src/sksl/ir/SkSLSwitchStatement.h"
37#include "src/sksl/ir/SkSLVarDeclarationsStatement.h"
38#include "src/sksl/ir/SkSLWhileStatement.h"
39
40// Expressions
41#include "src/sksl/ir/SkSLBinaryExpression.h"
42#include "src/sksl/ir/SkSLBoolLiteral.h"
43#include "src/sksl/ir/SkSLConstructor.h"
44#include "src/sksl/ir/SkSLExternalFunctionCall.h"
45#include "src/sksl/ir/SkSLExternalValueReference.h"
46#include "src/sksl/ir/SkSLFieldAccess.h"
47#include "src/sksl/ir/SkSLFloatLiteral.h"
48#include "src/sksl/ir/SkSLFunctionCall.h"
49#include "src/sksl/ir/SkSLFunctionReference.h"
50#include "src/sksl/ir/SkSLIndexExpression.h"
51#include "src/sksl/ir/SkSLIntLiteral.h"
52#include "src/sksl/ir/SkSLNullLiteral.h"
53#include "src/sksl/ir/SkSLPostfixExpression.h"
54#include "src/sksl/ir/SkSLPrefixExpression.h"
55#include "src/sksl/ir/SkSLSetting.h"
56#include "src/sksl/ir/SkSLSwizzle.h"
57#include "src/sksl/ir/SkSLTernaryExpression.h"
58#include "src/sksl/ir/SkSLTypeReference.h"
59#include "src/sksl/ir/SkSLVariableReference.h"
60
61namespace SkSL {
62
63namespace {
64
65static bool is_sample_call_to_fp(const FunctionCall& fc, const Variable& fp) {
66 const FunctionDeclaration& f = fc.fFunction;
67 return f.fBuiltin && f.fName == "sample" && fc.fArguments.size() >= 1 &&
68 fc.fArguments[0]->fKind == Expression::kVariableReference_Kind &&
69 &((VariableReference&) *fc.fArguments[0]).fVariable == &fp;
70}
71
72// Visitor that determines the merged SampleUsage for a given child 'fp' in the program.
73class MergeSampleUsageVisitor : public ProgramVisitor {
74public:
75 MergeSampleUsageVisitor(const Variable& fp) : fFP(fp) {}
76
77 SampleUsage visit(const Program& program) {
78 fUsage = SampleUsage(); // reset to none
79 this->INHERITED::visit(program);
80 return fUsage;
81 }
82
83protected:
84 const Variable& fFP;
85 SampleUsage fUsage;
86
87 bool visitExpression(const Expression& e) override {
88 // Looking for sample(fp, inColor?, ...)
89 if (e.fKind == Expression::kFunctionCall_Kind) {
90 const FunctionCall& fc = (const FunctionCall&) e;
91 if (is_sample_call_to_fp(fc, fFP)) {
92 // Determine the type of call at this site, and merge it with the accumulated state
93 const Expression* lastArg = fc.fArguments.back().get();
94 const Context& context = *this->program().fContext;
95
96 if (lastArg->fType == *context.fFloat2_Type) {
97 fUsage.merge(SampleUsage::Explicit());
98 } else if (lastArg->fType == *context.fFloat3x3_Type) {
99 // Determine the type of matrix for this call site
100 if (lastArg->isConstantOrUniform()) {
101 if (lastArg->fKind == Expression::Kind::kVariableReference_Kind ||
102 lastArg->fKind == Expression::Kind::kConstructor_Kind) {
103 // FIXME if this is a constant, we should parse the float3x3 constructor
104 // and determine if the resulting matrix introduces perspective.
105 fUsage.merge(SampleUsage::UniformMatrix(lastArg->description()));
106 } else {
107 // FIXME this is really to workaround a restriction of the downstream
108 // code that relies on the SampleUsage's fExpression to identify uniform
109 // names. Once they are tracked separately, any uniform expression can
110 // work, but right now this avoids issues from '0.5 * matrix' that is
111 // both a constant AND a uniform.
112 fUsage.merge(SampleUsage::VariableMatrix());
113 }
114 } else {
115 fUsage.merge(SampleUsage::VariableMatrix());
116 }
117 } else {
118 // The only other signatures do pass-through sampling
119 fUsage.merge(SampleUsage::PassThrough());
120 }
121 // NOTE: we don't return true here just because we found a sample call. We need to
122 // process the entire program and merge across all encountered calls.
123 }
124 }
125
126 return this->INHERITED::visitExpression(e);
127 }
128
129 typedef ProgramVisitor INHERITED;
130};
131
132// Visitor that searches through the program for references to a particular builtin variable
133class BuiltinVariableVisitor : public ProgramVisitor {
134public:
135 BuiltinVariableVisitor(int builtin) : fBuiltin(builtin) {}
136
137 bool visitExpression(const Expression& e) override {
138 if (e.fKind == Expression::kVariableReference_Kind) {
139 const VariableReference& var = (const VariableReference&) e;
140 return var.fVariable.fModifiers.fLayout.fBuiltin == fBuiltin;
141 }
142 return this->INHERITED::visitExpression(e);
143 }
144
145 int fBuiltin;
146
147 typedef ProgramVisitor INHERITED;
148};
149
150} // namespace
151
152////////////////////////////////////////////////////////////////////////////////
153// Analysis
154
155SampleUsage Analysis::GetSampleUsage(const Program& program, const Variable& fp) {
156 MergeSampleUsageVisitor visitor(fp);
157 return visitor.visit(program);
158}
159
160bool Analysis::ReferencesBuiltin(const Program& program, int builtin) {
161 BuiltinVariableVisitor visitor(builtin);
162 return visitor.visit(program);
163}
164
165bool Analysis::ReferencesSampleCoords(const Program& program) {
166 return Analysis::ReferencesBuiltin(program, SK_MAIN_COORDS_BUILTIN);
167}
168
169bool Analysis::ReferencesFragCoords(const Program& program) {
170 return Analysis::ReferencesBuiltin(program, SK_FRAGCOORD_BUILTIN);
171}
172
173////////////////////////////////////////////////////////////////////////////////
174// ProgramVisitor
175
176bool ProgramVisitor::visit(const Program& program) {
177 fProgram = &program;
178 bool result = false;
179 for (const auto& pe : program) {
180 if (this->visitProgramElement(pe)) {
181 result = true;
182 break;
183 }
184 }
185 fProgram = nullptr;
186 return result;
187}
188
189bool ProgramVisitor::visitExpression(const Expression& e) {
190 switch(e.fKind) {
191 case Expression::kBoolLiteral_Kind:
192 case Expression::kDefined_Kind:
193 case Expression::kExternalValue_Kind:
194 case Expression::kFieldAccess_Kind:
195 case Expression::kFloatLiteral_Kind:
196 case Expression::kFunctionReference_Kind:
197 case Expression::kIntLiteral_Kind:
198 case Expression::kNullLiteral_Kind:
199 case Expression::kSetting_Kind:
200 case Expression::kTypeReference_Kind:
201 case Expression::kVariableReference_Kind:
202 // Leaf expressions return false
203 return false;
204 case Expression::kBinary_Kind: {
205 const BinaryExpression& b = (const BinaryExpression&) e;
206 return this->visitExpression(*b.fLeft) || this->visitExpression(*b.fRight); }
207 case Expression::kConstructor_Kind: {
208 const Constructor& c = (const Constructor&) e;
209 for (const auto& arg : c.fArguments) {
210 if (this->visitExpression(*arg)) { return true; }
211 }
212 return false; }
213 case Expression::kExternalFunctionCall_Kind: {
214 const ExternalFunctionCall& c = (const ExternalFunctionCall&) e;
215 for (const auto& arg : c.fArguments) {
216 if (this->visitExpression(*arg)) { return true; }
217 }
218 return false; }
219 case Expression::kFunctionCall_Kind: {
220 const FunctionCall& c = (const FunctionCall&) e;
221 for (const auto& arg : c.fArguments) {
222 if (this->visitExpression(*arg)) { return true; }
223 }
224 return false; }
225 case Expression::kIndex_Kind:{
226 const IndexExpression& i = (const IndexExpression&) e;
227 return this->visitExpression(*i.fBase) || this->visitExpression(*i.fIndex); }
228 case Expression::kPostfix_Kind:
229 return this->visitExpression(*((const PostfixExpression&) e).fOperand);
230 case Expression::kPrefix_Kind:
231 return this->visitExpression(*((const PrefixExpression&) e).fOperand);
232 case Expression::kSwizzle_Kind:
233 return this->visitExpression(*((const Swizzle&) e).fBase);
234 case Expression::kTernary_Kind: {
235 const TernaryExpression& t = (const TernaryExpression&) e;
236 return this->visitExpression(*t.fTest) ||
237 this->visitExpression(*t.fIfTrue) ||
238 this->visitExpression(*t.fIfFalse); }
239 default:
240 SkUNREACHABLE;
241 }
242}
243
244bool ProgramVisitor::visitStatement(const Statement& s) {
245 switch(s.fKind) {
246 case Statement::kBreak_Kind:
247 case Statement::kContinue_Kind:
248 case Statement::kDiscard_Kind:
249 case Statement::kNop_Kind:
250 // Leaf statements just return false
251 return false;
252 case Statement::kBlock_Kind:
253 for (const auto& s : ((const Block&) s).fStatements) {
254 if (this->visitStatement(*s)) { return true; }
255 }
256 return false;
257 case Statement::kDo_Kind: {
258 const DoStatement& d = (const DoStatement&) s;
259 return this->visitExpression(*d.fTest) || this->visitStatement(*d.fStatement); }
260 case Statement::kExpression_Kind:
261 return this->visitExpression(*((const ExpressionStatement&) s).fExpression);
262 case Statement::kFor_Kind: {
263 const ForStatement& f = (const ForStatement&) s;
264 return (f.fInitializer && this->visitStatement(*f.fInitializer)) ||
265 (f.fInitializer && this->visitExpression(*f.fTest)) ||
266 (f.fNext && this->visitExpression(*f.fNext)) ||
267 this->visitStatement(*f.fStatement); }
268 case Statement::kIf_Kind: {
269 const IfStatement& i = (const IfStatement&) s;
270 return this->visitExpression(*i.fTest) ||
271 this->visitStatement(*i.fIfTrue) ||
272 (i.fIfFalse && this->visitStatement(*i.fIfFalse)); }
273 case Statement::kReturn_Kind: {
274 const ReturnStatement& r = (const ReturnStatement&) s;
275 return r.fExpression && this->visitExpression(*r.fExpression); }
276 case Statement::kSwitch_Kind: {
277 const SwitchStatement& sw = (const SwitchStatement&) s;
278 if (this->visitExpression(*sw.fValue)) { return true; }
279 for (const auto& c : sw.fCases) {
280 if (c->fValue && this->visitExpression(*c->fValue)) { return true; }
281 for (const auto& st : c->fStatements) {
282 if (this->visitStatement(*st)) { return true; }
283 }
284 }
285 return false; }
286 case Statement::kVarDeclaration_Kind: {
287 const VarDeclaration& v = (const VarDeclaration&) s;
288 for (const auto& s : v.fSizes) {
289 if (this->visitExpression(*s)) { return true; }
290 }
291 return v.fValue && this->visitExpression(*v.fValue); }
292 case Statement::kVarDeclarations_Kind: {
293 // Technically this statement points to a program element, but it's convenient
294 // to have program element > statement > expression, so visit the declaration elements
295 // directly without going up to visitProgramElement.
296 const VarDeclarations& vars = *((const VarDeclarationsStatement&) s).fDeclaration;
297 for (const auto& v : vars.fVars) {
298 if (this->visitStatement(*v)) { return true; }
299 }
300 return false;
301 }
302 case Statement::kWhile_Kind: {
303 const WhileStatement& w = (const WhileStatement&) s;
304 return this->visitExpression(*w.fTest) || this->visitStatement(*w.fStatement); }
305 default:
306 SkUNREACHABLE;
307 }
308}
309
310bool ProgramVisitor::visitProgramElement(const ProgramElement& pe) {
311 switch(pe.fKind) {
312 case ProgramElement::kEnum_Kind:
313 case ProgramElement::kExtension_Kind:
314 case ProgramElement::kModifiers_Kind:
315 case ProgramElement::kSection_Kind:
316 // Leaf program elements just return false by default
317 return false;
318 case ProgramElement::kFunction_Kind:
319 return this->visitStatement(*((const FunctionDefinition&) pe).fBody);
320 case ProgramElement::kInterfaceBlock_Kind:
321 for (const auto& e : ((const InterfaceBlock&) pe).fSizes) {
322 if (this->visitExpression(*e)) { return true; }
323 }
324 return false;
325 case ProgramElement::kVar_Kind:
326 for (const auto& v : ((const VarDeclarations&) pe).fVars) {
327 if (this->visitStatement(*v)) { return true; }
328 }
329 return false;
330 default:
331 SkUNREACHABLE;
332 }
333}
334
335} // namespace SkSL
336