Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 &&
222 (ref.refKind() == VariableReference::RefKind::kWrite ||
223 ref.refKind() == VariableReference::RefKind::kReadWrite ||
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 }
286 case Expression::Kind::kIndex:
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
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>() &&
395 expr.as<VariableReference>().variable()->name() == Compiler::RTADJUST_NAME) {
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()) {
434 case Expression::Kind::kLiteral:
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
514 ErrorReporter* errors) {
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()) {
544 case Expression::Kind::kEmpty:
545 case Expression::Kind::kFunctionReference:
546 case Expression::Kind::kLiteral:
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 }
593 case Expression::Kind::kIndex: {
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:
622 case Statement::Kind::kContinue:
623 case Statement::Kind::kDiscard:
624 case Statement::Kind::kNop:
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()) {
679 case ProgramElement::Kind::kExtension:
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
687 case ProgramElement::Kind::kFunction:
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
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT(cond)
Definition SkAssert.h:116
#define INHERITED(method,...)
constexpr int SK_FRAGCOORD_BUILTIN
const Context & fContext
const StatementArray & children() const
Definition SkSLBlock.h:71
bool isScope() const
Definition SkSLBlock.h:79
const Variable & child() const
static const Expression * GetConstantValueForVariable(const Expression &value)
Kind kind() const
virtual 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
bool empty() const
Definition SkTArray.h:194
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)
std::vector< std::unique_ptr< ProgramElement > > fOwnedElements
ElementsCollection elements() const
std::shared_ptr< Context > fContext
const ProgramUsage * usage() const
std::unique_ptr< ProgramUsage > fUsage