Flutter Engine
The Flutter Engine
SkSLAnalysis.cpp
Go to the documentation of this file.
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
9
10#include "include/core/SkSpan.h"
15#include "src/core/SkTHash.h"
60
61#include <optional>
62#include <string>
63#include <string_view>
64
65namespace SkSL {
66
67namespace {
68
69// Visitor that determines the merged SampleUsage for a given child in the program.
70class MergeSampleUsageVisitor : public ProgramVisitor {
71public:
72 MergeSampleUsageVisitor(const Context& context,
73 const Variable& child,
74 bool writesToSampleCoords)
75 : fContext(context), fChild(child), fWritesToSampleCoords(writesToSampleCoords) {}
76
77 SampleUsage visit(const Program& program) {
78 fUsage = SampleUsage(); // reset to none
79 INHERITED::visit(program);
80 return fUsage;
81 }
82
83 int elidedSampleCoordCount() const { return fElidedSampleCoordCount; }
84
85protected:
86 const Context& fContext;
87 const Variable& fChild;
88 const Variable* fMainCoordsParam = nullptr;
89 const bool fWritesToSampleCoords;
90 SampleUsage fUsage;
91 int fElidedSampleCoordCount = 0;
92
93 bool visitProgramElement(const ProgramElement& pe) override {
94 fMainCoordsParam = pe.is<FunctionDefinition>()
96 : nullptr;
98 }
99
100 bool visitExpression(const Expression& e) override {
101 // Looking for child(...)
102 if (e.is<ChildCall>() && &e.as<ChildCall>().child() == &fChild) {
103 // Determine the type of call at this site, and merge it with the accumulated state
104 const ExpressionArray& arguments = e.as<ChildCall>().arguments();
105 SkASSERT(!arguments.empty());
106
107 const Expression* maybeCoords = arguments[0].get();
108 if (maybeCoords->type().matches(*fContext.fTypes.fFloat2)) {
109 // If the coords are a direct reference to the program's sample-coords, and those
110 // coords are never modified, we can conservatively turn this into PassThrough
111 // sampling. In all other cases, we consider it Explicit.
112 if (!fWritesToSampleCoords && maybeCoords->is<VariableReference>() &&
113 maybeCoords->as<VariableReference>().variable() == fMainCoordsParam) {
115 ++fElidedSampleCoordCount;
116 } else {
118 }
119 } else {
120 // child(inputColor) or child(srcColor, dstColor) -> PassThrough
122 }
123 }
124
126 }
127
129};
130
131// Visitor that searches for child calls from a function other than main()
132class SampleOutsideMainVisitor : public ProgramVisitor {
133public:
134 SampleOutsideMainVisitor() {}
135
136 bool visitExpression(const Expression& e) override {
137 if (e.is<ChildCall>()) {
138 return true;
139 }
141 }
142
143 bool visitProgramElement(const ProgramElement& p) override {
144 return p.is<FunctionDefinition>() &&
147 }
148
150};
151
152class ReturnsNonOpaqueColorVisitor : public ProgramVisitor {
153public:
154 ReturnsNonOpaqueColorVisitor() {}
155
156 bool visitStatement(const Statement& s) override {
157 if (s.is<ReturnStatement>()) {
158 const Expression* e = s.as<ReturnStatement>().expression().get();
159 bool knownOpaque = e && e->type().slotCount() == 4 &&
161 ->getConstantValue(/*n=*/3)
162 .value_or(0) == 1;
163 return !knownOpaque;
164 }
166 }
167
168 bool visitExpression(const Expression& e) override {
169 // No need to recurse into expressions, these can never contain return statements
170 return false;
171 }
172
175};
176
177// Visitor that counts the number of nodes visited
178class NodeCountVisitor : public ProgramVisitor {
179public:
180 NodeCountVisitor(int limit) : fLimit(limit) {}
181
182 int visit(const Statement& s) {
183 this->visitStatement(s);
184 return fCount;
185 }
186
187 bool visitExpression(const Expression& e) override {
188 ++fCount;
189 return (fCount >= fLimit) || INHERITED::visitExpression(e);
190 }
191
192 bool visitProgramElement(const ProgramElement& p) override {
193 ++fCount;
194 return (fCount >= fLimit) || INHERITED::visitProgramElement(p);
195 }
196
197 bool visitStatement(const Statement& s) override {
198 ++fCount;
199 return (fCount >= fLimit) || INHERITED::visitStatement(s);
200 }
201
202private:
203 int fCount = 0;
204 int fLimit;
205
207};
208
209class VariableWriteVisitor : public ProgramVisitor {
210public:
211 VariableWriteVisitor(const Variable* var)
212 : fVar(var) {}
213
214 bool visit(const Statement& s) {
215 return this->visitStatement(s);
216 }
217
218 bool visitExpression(const Expression& e) override {
219 if (e.is<VariableReference>()) {
220 const VariableReference& ref = e.as<VariableReference>();
221 if (ref.variable() == fVar &&
224 ref.refKind() == VariableReference::RefKind::kPointer)) {
225 return true;
226 }
227 }
229 }
230
231private:
232 const Variable* fVar;
233
235};
236
237// This isn't actually using ProgramVisitor, because it only considers a subset of the fields for
238// any given expression kind. For instance, when indexing an array (e.g. `x[1]`), we only want to
239// know if the base (`x`) is assignable; the index expression (`1`) doesn't need to be.
240class IsAssignableVisitor {
241public:
242 IsAssignableVisitor(ErrorReporter* errors) : fErrors(errors) {}
243
244 bool visit(Expression& expr, Analysis::AssignmentInfo* info) {
245 int oldErrorCount = fErrors->errorCount();
246 this->visitExpression(expr);
247 if (info) {
248 info->fAssignedVar = fAssignedVar;
249 }
250 return fErrors->errorCount() == oldErrorCount;
251 }
252
253 void visitExpression(Expression& expr, const FieldAccess* fieldAccess = nullptr) {
254 switch (expr.kind()) {
255 case Expression::Kind::kVariableReference: {
256 VariableReference& varRef = expr.as<VariableReference>();
257 const Variable* var = varRef.variable();
258 auto fieldName = [&] {
259 return fieldAccess ? fieldAccess->description(OperatorPrecedence::kExpression)
260 : std::string(var->name());
261 };
262 if (var->modifierFlags().isConst() || var->modifierFlags().isUniform()) {
263 fErrors->error(expr.fPosition,
264 "cannot modify immutable variable '" + fieldName() + "'");
265 } else if (var->storage() == Variable::Storage::kGlobal &&
266 (var->modifierFlags() & ModifierFlag::kIn)) {
267 fErrors->error(expr.fPosition,
268 "cannot modify pipeline input variable '" + fieldName() + "'");
269 } else {
270 SkASSERT(fAssignedVar == nullptr);
271 fAssignedVar = &varRef;
272 }
273 break;
274 }
275 case Expression::Kind::kFieldAccess: {
276 const FieldAccess& f = expr.as<FieldAccess>();
277 this->visitExpression(*f.base(), &f);
278 break;
279 }
280 case Expression::Kind::kSwizzle: {
281 const Swizzle& swizzle = expr.as<Swizzle>();
282 this->checkSwizzleWrite(swizzle);
283 this->visitExpression(*swizzle.base(), fieldAccess);
284 break;
285 }
287 this->visitExpression(*expr.as<IndexExpression>().base(), fieldAccess);
288 break;
289
290 case Expression::Kind::kPoison:
291 break;
292
293 default:
294 fErrors->error(expr.fPosition, "cannot assign to this expression");
295 break;
296 }
297 }
298
299private:
300 void checkSwizzleWrite(const Swizzle& swizzle) {
301 int bits = 0;
302 for (int8_t idx : swizzle.components()) {
304 int bit = 1 << idx;
305 if (bits & bit) {
306 fErrors->error(swizzle.fPosition,
307 "cannot write to the same swizzle field more than once");
308 break;
309 }
310 bits |= bit;
311 }
312 }
313
314 ErrorReporter* fErrors;
315 VariableReference* fAssignedVar = nullptr;
316
318};
319
320} // namespace
321
322////////////////////////////////////////////////////////////////////////////////
323// Analysis
324
326 const Variable& child,
327 bool writesToSampleCoords,
328 int* elidedSampleCoordCount) {
329 MergeSampleUsageVisitor visitor(*program.fContext, child, writesToSampleCoords);
330 SampleUsage result = visitor.visit(program);
331 if (elidedSampleCoordCount) {
332 *elidedSampleCoordCount += visitor.elidedSampleCoordCount();
333 }
334 return result;
335}
336
337bool Analysis::ReferencesBuiltin(const Program& program, int builtin) {
338 SkASSERT(program.fUsage);
339 for (const auto& [variable, counts] : program.fUsage->fVariableCounts) {
340 if (counts.fRead > 0 && variable->layout().fBuiltin == builtin) {
341 return true;
342 }
343 }
344 return false;
345}
346
348 // Look for main().
349 for (const std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
350 if (pe->is<FunctionDefinition>()) {
351 const FunctionDeclaration& func = pe->as<FunctionDefinition>().declaration();
352 if (func.isMain()) {
353 // See if main() has a coords parameter that is read from anywhere.
354 if (const Variable* coords = func.getMainCoordsParameter()) {
355 ProgramUsage::VariableCounts counts = program.fUsage->get(*coords);
356 return counts.fRead > 0;
357 }
358 }
359 }
360 }
361 // The program is missing a main().
362 return false;
363}
364
367}
368
370 SampleOutsideMainVisitor visitor;
371 return visitor.visit(program);
372}
373
375 for (auto [symbol, count] : program.usage()->fCallCounts) {
376 const FunctionDeclaration& fn = symbol->as<FunctionDeclaration>();
377 if (count != 0 && (fn.intrinsicKind() == k_toLinearSrgb_IntrinsicKind ||
378 fn.intrinsicKind() == k_fromLinearSrgb_IntrinsicKind)) {
379 return true;
380 }
381 }
382 return false;
383}
384
386 ReturnsNonOpaqueColorVisitor visitor;
387 return !visitor.visitProgramElement(function);
388}
389
391 class ContainsRTAdjustVisitor : public ProgramVisitor {
392 public:
393 bool visitExpression(const Expression& expr) override {
394 if (expr.is<VariableReference>() &&
396 return true;
397 }
398 return INHERITED::visitExpression(expr);
399 }
400
402 };
403
404 ContainsRTAdjustVisitor visitor;
405 return visitor.visitExpression(expr);
406}
407
408bool Analysis::ContainsVariable(const Expression& expr, const Variable& var) {
409 class ContainsVariableVisitor : public ProgramVisitor {
410 public:
411 ContainsVariableVisitor(const Variable* v) : fVariable(v) {}
412
413 bool visitExpression(const Expression& expr) override {
414 if (expr.is<VariableReference>() &&
415 expr.as<VariableReference>().variable() == fVariable) {
416 return true;
417 }
418 return INHERITED::visitExpression(expr);
419 }
420
422 const Variable* fVariable;
423 };
424
425 ContainsVariableVisitor visitor{&var};
426 return visitor.visitExpression(expr);
427}
428
430 class IsCompileTimeConstantVisitor : public ProgramVisitor {
431 public:
432 bool visitExpression(const Expression& expr) override {
433 switch (expr.kind()) {
435 // Literals are compile-time constants.
436 return false;
437
438 case Expression::Kind::kConstructorArray:
439 case Expression::Kind::kConstructorCompound:
440 case Expression::Kind::kConstructorDiagonalMatrix:
441 case Expression::Kind::kConstructorMatrixResize:
442 case Expression::Kind::kConstructorSplat:
443 case Expression::Kind::kConstructorStruct:
444 // Constructors might be compile-time constants, if they are composed entirely
445 // of literals and constructors. (Casting constructors are intentionally omitted
446 // here. If the value inside was a compile-time constant, we would have not have
447 // generated a cast at all.)
448 return INHERITED::visitExpression(expr);
449
450 default:
451 // This expression isn't a compile-time constant.
452 fIsConstant = false;
453 return true;
454 }
455 }
456
457 bool fIsConstant = true;
459 };
460
461 IsCompileTimeConstantVisitor visitor;
462 visitor.visitExpression(expr);
463 return visitor.fIsConstant;
464}
465
467 // A variable declaration can create either a lone VarDeclaration or an unscoped Block
468 // containing multiple VarDeclaration statements. We need to detect either case.
469 const Variable* var;
470 if (stmt.is<VarDeclaration>()) {
471 // The single-variable case. No blocks at all.
472 var = stmt.as<VarDeclaration>().var();
473 } else if (stmt.is<Block>()) {
474 // The multiple-variable case: an unscoped, non-empty block...
475 const Block& block = stmt.as<Block>();
476 if (block.isScope() || block.children().empty()) {
477 return false;
478 }
479 // ... holding a variable declaration.
480 const Statement& innerStmt = *block.children().front();
481 if (!innerStmt.is<VarDeclaration>()) {
482 return false;
483 }
484 var = innerStmt.as<VarDeclaration>().var();
485 } else {
486 // This statement wasn't a variable declaration. No problem.
487 return false;
488 }
489
490 // Report an error.
491 SkASSERT(var);
492 if (errors) {
493 errors->error(var->fPosition,
494 "variable '" + std::string(var->name()) + "' must be created in a scope");
495 }
496 return true;
497}
498
500 return NodeCountVisitor{limit}.visit(*function.body());
501}
502
504 return VariableWriteVisitor(&var).visit(stmt);
505}
506
508 NoOpErrorReporter unusedErrors;
509 return IsAssignableVisitor{errors ? errors : &unusedErrors}.visit(expr, info);
510}
511
516 if (!Analysis::IsAssignable(*expr, &info, errors)) {
517 return false;
518 }
519 if (!info.fAssignedVar) {
520 if (errors) {
521 errors->error(expr->fPosition, "can't assign to expression '" + expr->description() +
522 "'");
523 }
524 return false;
525 }
526 info.fAssignedVar->setRefKind(kind);
527 return true;
528}
529
530////////////////////////////////////////////////////////////////////////////////
531// ProgramVisitor
532
533bool ProgramVisitor::visit(const Program& program) {
534 for (const ProgramElement* pe : program.elements()) {
535 if (this->visitProgramElement(*pe)) {
536 return true;
537 }
538 }
539 return false;
540}
541
542template <typename T> bool TProgramVisitor<T>::visitExpression(typename T::Expression& e) {
543 switch (e.kind()) {
545 case Expression::Kind::kFunctionReference:
547 case Expression::Kind::kMethodReference:
548 case Expression::Kind::kPoison:
549 case Expression::Kind::kSetting:
550 case Expression::Kind::kTypeReference:
551 case Expression::Kind::kVariableReference:
552 // Leaf expressions return false
553 return false;
554
555 case Expression::Kind::kBinary: {
556 auto& b = e.template as<BinaryExpression>();
557 return (b.left() && this->visitExpressionPtr(b.left())) ||
558 (b.right() && this->visitExpressionPtr(b.right()));
559 }
560 case Expression::Kind::kChildCall: {
561 // We don't visit the child variable itself, just the arguments
562 auto& c = e.template as<ChildCall>();
563 for (auto& arg : c.arguments()) {
564 if (arg && this->visitExpressionPtr(arg)) { return true; }
565 }
566 return false;
567 }
568 case Expression::Kind::kConstructorArray:
569 case Expression::Kind::kConstructorArrayCast:
570 case Expression::Kind::kConstructorCompound:
571 case Expression::Kind::kConstructorCompoundCast:
572 case Expression::Kind::kConstructorDiagonalMatrix:
573 case Expression::Kind::kConstructorMatrixResize:
574 case Expression::Kind::kConstructorScalarCast:
575 case Expression::Kind::kConstructorSplat:
576 case Expression::Kind::kConstructorStruct: {
577 auto& c = e.asAnyConstructor();
578 for (auto& arg : c.argumentSpan()) {
579 if (this->visitExpressionPtr(arg)) { return true; }
580 }
581 return false;
582 }
583 case Expression::Kind::kFieldAccess:
584 return this->visitExpressionPtr(e.template as<FieldAccess>().base());
585
586 case Expression::Kind::kFunctionCall: {
587 auto& c = e.template as<FunctionCall>();
588 for (auto& arg : c.arguments()) {
589 if (arg && this->visitExpressionPtr(arg)) { return true; }
590 }
591 return false;
592 }
594 auto& i = e.template as<IndexExpression>();
595 return this->visitExpressionPtr(i.base()) || this->visitExpressionPtr(i.index());
596 }
597 case Expression::Kind::kPostfix:
598 return this->visitExpressionPtr(e.template as<PostfixExpression>().operand());
599
600 case Expression::Kind::kPrefix:
601 return this->visitExpressionPtr(e.template as<PrefixExpression>().operand());
602
603 case Expression::Kind::kSwizzle: {
604 auto& s = e.template as<Swizzle>();
605 return s.base() && this->visitExpressionPtr(s.base());
606 }
607
608 case Expression::Kind::kTernary: {
609 auto& t = e.template as<TernaryExpression>();
610 return this->visitExpressionPtr(t.test()) ||
611 (t.ifTrue() && this->visitExpressionPtr(t.ifTrue())) ||
612 (t.ifFalse() && this->visitExpressionPtr(t.ifFalse()));
613 }
614 default:
616 }
617}
618
619template <typename T> bool TProgramVisitor<T>::visitStatement(typename T::Statement& s) {
620 switch (s.kind()) {
621 case Statement::Kind::kBreak:
625 // Leaf statements just return false
626 return false;
627
628 case Statement::Kind::kBlock:
629 for (auto& stmt : s.template as<Block>().children()) {
630 if (stmt && this->visitStatementPtr(stmt)) {
631 return true;
632 }
633 }
634 return false;
635
636 case Statement::Kind::kSwitchCase: {
637 auto& sc = s.template as<SwitchCase>();
638 return this->visitStatementPtr(sc.statement());
639 }
640 case Statement::Kind::kDo: {
641 auto& d = s.template as<DoStatement>();
642 return this->visitExpressionPtr(d.test()) || this->visitStatementPtr(d.statement());
643 }
644 case Statement::Kind::kExpression:
645 return this->visitExpressionPtr(s.template as<ExpressionStatement>().expression());
646
647 case Statement::Kind::kFor: {
648 auto& f = s.template as<ForStatement>();
649 return (f.initializer() && this->visitStatementPtr(f.initializer())) ||
650 (f.test() && this->visitExpressionPtr(f.test())) ||
651 (f.next() && this->visitExpressionPtr(f.next())) ||
652 this->visitStatementPtr(f.statement());
653 }
654 case Statement::Kind::kIf: {
655 auto& i = s.template as<IfStatement>();
656 return (i.test() && this->visitExpressionPtr(i.test())) ||
657 (i.ifTrue() && this->visitStatementPtr(i.ifTrue())) ||
658 (i.ifFalse() && this->visitStatementPtr(i.ifFalse()));
659 }
660 case Statement::Kind::kReturn: {
661 auto& r = s.template as<ReturnStatement>();
662 return r.expression() && this->visitExpressionPtr(r.expression());
663 }
664 case Statement::Kind::kSwitch: {
665 auto& sw = s.template as<SwitchStatement>();
666 return this->visitExpressionPtr(sw.value()) || this->visitStatementPtr(sw.caseBlock());
667 }
668 case Statement::Kind::kVarDeclaration: {
669 auto& v = s.template as<VarDeclaration>();
670 return v.value() && this->visitExpressionPtr(v.value());
671 }
672 default:
674 }
675}
676
677template <typename T> bool TProgramVisitor<T>::visitProgramElement(typename T::ProgramElement& pe) {
678 switch (pe.kind()) {
680 case ProgramElement::Kind::kFunctionPrototype:
681 case ProgramElement::Kind::kInterfaceBlock:
682 case ProgramElement::Kind::kModifiers:
683 case ProgramElement::Kind::kStructDefinition:
684 // Leaf program elements just return false by default
685 return false;
686
688 return this->visitStatementPtr(pe.template as<FunctionDefinition>().body());
689
690 case ProgramElement::Kind::kGlobalVar:
691 return this->visitStatementPtr(pe.template as<GlobalVarDeclaration>().declaration());
692
693 default:
695 }
696}
697
700
701} // namespace SkSL
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
constexpr int SK_FRAGCOORD_BUILTIN
Definition: SkSLCompiler.h:30
const Context & fContext
const StatementArray & children() const
Definition: SkSLBlock.h:71
bool isScope() const
Definition: SkSLBlock.h:79
const Variable & child() const
Definition: SkSLChildCall.h:47
static constexpr const char RTADJUST_NAME[]
Definition: SkSLCompiler.h:72
static const Expression * GetConstantValueForVariable(const Expression &value)
Kind kind() const
const Type & type() const
virtual std::optional< double > getConstantValue(int n) const
std::string description() const final
const Variable * getMainCoordsParameter() const
IntrinsicKind intrinsicKind() const
const FunctionDeclaration & declaration() const
bool is() const
Definition: SkSLIRNode.h:124
const T & as() const
Definition: SkSLIRNode.h:133
Position fPosition
Definition: SkSLIRNode.h:109
std::unique_ptr< Expression > & base()
skia_private::THashMap< const Symbol *, int > fCallCounts
bool visit(const Program &program)
SampleUsage merge(const SampleUsage &other)
static SampleUsage Explicit()
static SampleUsage PassThrough()
std::unique_ptr< Expression > & base()
Definition: SkSLSwizzle.h:82
const ComponentArray & components() const
Definition: SkSLSwizzle.h:90
std::string_view name() const
Definition: SkSLSymbol.h:51
virtual bool visitStatement(typename T::Statement &statement)
virtual bool visitExpression(typename T::Expression &expression)
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
bool matches(const Type &other) const
Definition: SkSLType.h:269
const Variable * variable() const
std::string description() const override
Definition: SkSLVariable.h:134
bool empty() const
Definition: SkTArray.h:199
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct s
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
bool IsCompileTimeConstant(const Expression &expr)
bool ContainsVariable(const Expression &expr, const Variable &var)
bool ContainsRTAdjust(const Expression &expr)
bool ReturnsOpaqueColor(const FunctionDefinition &function)
bool ReferencesBuiltin(const Program &program, int builtin)
bool ReferencesFragCoords(const Program &program)
SampleUsage GetSampleUsage(const Program &program, const Variable &child, bool writesToSampleCoords=true, int *elidedSampleCoordCount=nullptr)
int NodeCountUpToLimit(const FunctionDefinition &function, int limit)
bool ReferencesSampleCoords(const Program &program)
bool DetectVarDeclarationWithoutScope(const Statement &stmt, ErrorReporter *errors=nullptr)
bool CallsColorTransformIntrinsics(const Program &program)
bool UpdateVariableRefKind(Expression *expr, VariableRefKind kind, ErrorReporter *errors=nullptr)
bool CallsSampleOutsideMain(const Program &program)
bool StatementWritesToVariable(const Statement &stmt, const Variable &var)
bool IsAssignable(Expression &expr, AssignmentInfo *info=nullptr, ErrorReporter *errors=nullptr)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core counts
Definition: switches.h:239
std::vector< std::unique_ptr< ProgramElement > > fOwnedElements
Definition: SkSLProgram.h:161
ElementsCollection elements() const
Definition: SkSLProgram.h:140
std::shared_ptr< Context > fContext
Definition: SkSLProgram.h:154
const ProgramUsage * usage() const
Definition: SkSLProgram.h:150
std::unique_ptr< ProgramUsage > fUsage
Definition: SkSLProgram.h:155