Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Enumerations | Functions
SkSL::Analysis Namespace Reference

Classes

struct  AssignmentInfo
 
struct  LoopControlFlowInfo
 
class  SymbolTableStackBuilder
 

Enumerations

enum class  ReturnComplexity { kSingleSafeReturn , kScopedReturns , kEarlyReturns }
 

Functions

LoopControlFlowInfo GetLoopControlFlowInfo (const Statement &stmt)
 
SampleUsage GetSampleUsage (const Program &program, const Variable &child, bool writesToSampleCoords=true, int *elidedSampleCoordCount=nullptr)
 
bool ReferencesBuiltin (const Program &program, int builtin)
 
bool ReferencesSampleCoords (const Program &program)
 
bool ReferencesFragCoords (const Program &program)
 
bool CallsSampleOutsideMain (const Program &program)
 
bool CallsColorTransformIntrinsics (const Program &program)
 
bool ReturnsOpaqueColor (const FunctionDefinition &function)
 
bool ReturnsInputAlpha (const FunctionDefinition &function, const ProgramUsage &usage)
 
bool CheckProgramStructure (const Program &program, bool enforceSizeLimit)
 
bool ContainsRTAdjust (const Expression &expr)
 
bool ContainsVariable (const Expression &expr, const Variable &var)
 
bool HasSideEffects (const Expression &expr)
 
bool IsCompileTimeConstant (const Expression &expr)
 
bool IsDynamicallyUniformExpression (const Expression &expr)
 
bool DetectVarDeclarationWithoutScope (const Statement &stmt, ErrorReporter *errors=nullptr)
 
int NodeCountUpToLimit (const FunctionDefinition &function, int limit)
 
bool SwitchCaseContainsUnconditionalExit (const Statement &stmt)
 
bool SwitchCaseContainsConditionalExit (const Statement &stmt)
 
std::unique_ptr< ProgramUsageGetUsage (const Program &program)
 
std::unique_ptr< ProgramUsageGetUsage (const Module &module)
 
bool StatementWritesToVariable (const Statement &stmt, const Variable &var)
 
bool IsAssignable (Expression &expr, AssignmentInfo *info=nullptr, ErrorReporter *errors=nullptr)
 
bool UpdateVariableRefKind (Expression *expr, VariableRefKind kind, ErrorReporter *errors=nullptr)
 
bool IsTrivialExpression (const Expression &expr)
 
bool IsSameExpressionTree (const Expression &left, const Expression &right)
 
bool IsConstantExpression (const Expression &expr)
 
void ValidateIndexingForES2 (const ProgramElement &pe, ErrorReporter &errors)
 
void CheckSymbolTableCorrectness (const Program &program)
 
std::unique_ptr< LoopUnrollInfoGetLoopUnrollInfo (const Context &context, Position pos, const ForLoopPositions &positions, const Statement *loopInitializer, std::unique_ptr< Expression > *loopTestPtr, const Expression *loopNext, const Statement *loopStatement, ErrorReporter *errors)
 
bool CanExitWithoutReturningValue (const FunctionDeclaration &funcDecl, const Statement &body)
 
ReturnComplexity GetReturnComplexity (const FunctionDefinition &funcDef)
 
void DoFinalizationChecks (const Program &program)
 
skia_private::TArray< const SkSL::Variable * > GetComputeShaderMainParams (const Context &context, const Program &program)
 

Detailed Description

Provides utilities for analyzing SkSL statically before it's composed into a full program.

Enumeration Type Documentation

◆ ReturnComplexity

Determines if a given function has multiple and/or early returns.

Enumerator
kSingleSafeReturn 
kScopedReturns 
kEarlyReturns 

Definition at line 238 of file SkSLAnalysis.h.

Function Documentation

◆ CallsColorTransformIntrinsics()

bool SkSL::Analysis::CallsColorTransformIntrinsics ( const Program program)

Definition at line 374 of file SkSLAnalysis.cpp.

374 {
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}
int count
IntrinsicKind intrinsicKind() const
const T & as() const
Definition SkSLIRNode.h:133
static void usage(char *argv0)

◆ CallsSampleOutsideMain()

bool SkSL::Analysis::CallsSampleOutsideMain ( const Program program)

Definition at line 369 of file SkSLAnalysis.cpp.

369 {
370 SampleOutsideMainVisitor visitor;
371 return visitor.visit(program);
372}

◆ CanExitWithoutReturningValue()

bool SkSL::Analysis::CanExitWithoutReturningValue ( const FunctionDeclaration funcDecl,
const Statement body 
)

Detects functions that fail to return a value on at least one path.

Definition at line 166 of file SkSLCanExitWithoutReturningValue.cpp.

167 {
168 if (funcDecl.returnType().isVoid()) {
169 return false;
170 }
171 ReturnsOnAllPathsVisitor visitor;
172 visitor.visitStatement(body);
173 return !visitor.fFoundReturn;
174}
bool isVoid() const
Definition SkSLType.h:496

◆ CheckProgramStructure()

bool SkSL::Analysis::CheckProgramStructure ( const Program program,
bool  enforceSizeLimit 
)

Checks for recursion or overly-deep function-call chains, and rejects programs which have them. Also, computes the size of the program in a completely flattened state–loops fully unrolled, function calls inlined–and rejects programs that exceed an arbitrary upper bound.

Definition at line 36 of file SkSLCheckProgramStructure.cpp.

36 {
37 // We check the size of strict-ES2 programs; this behavior is a holdover from SkVM, which would
38 // completely unroll all loops. (SkRP supports loops properly, but does inline function calls.)
39 // For non-ES2 code, we compute an approximate lower bound by assuming all non-unrollable loops
40 // will execute one time only.
41 const Context& context = *program.fContext;
42
43 // If we decide that expressions are cheaper than statements, or that certain statements are
44 // more expensive than others, etc., we can always tweak these ratios as needed. A very rough
45 // ballpark estimate is currently good enough for our purposes.
46 static constexpr size_t kExpressionCost = 1;
47 static constexpr size_t kStatementCost = 1;
48 static constexpr size_t kUnknownCost = -1;
49 static constexpr size_t kProgramSizeLimit = 100000;
50 static constexpr size_t kProgramStackDepthLimit = 50;
51
52 class ProgramSizeVisitor : public ProgramVisitor {
53 public:
54 ProgramSizeVisitor(const Context& c) : fContext(c) {}
55
56 using ProgramVisitor::visitProgramElement;
57
58 size_t functionSize() const {
59 return fFunctionSize;
60 }
61
62 bool visitProgramElement(const ProgramElement& pe) override {
63 if (pe.is<FunctionDefinition>()) {
64 // Check the function-size cache map first. We don't need to visit this function if
65 // we already processed it before.
66 const FunctionDeclaration* decl = &pe.as<FunctionDefinition>().declaration();
67 if (size_t *cachedCost = fFunctionCostMap.find(decl)) {
68 // We already have this function in our map. We don't need to check it again.
69 if (*cachedCost == kUnknownCost) {
70 // If the function is present in the map with an unknown cost, we're
71 // recursively processing it--in other words, we found a cycle in the code.
72 // Unwind our stack into a string.
73 std::string msg = "\n\t" + decl->description();
74 for (auto unwind = fStack.rbegin(); unwind != fStack.rend(); ++unwind) {
75 msg = "\n\t" + (*unwind)->description() + msg;
76 if (*unwind == decl) {
77 break;
78 }
79 }
80 msg = "potential recursion (function call cycle) not allowed:" + msg;
81 fContext.fErrors->error(pe.fPosition, std::move(msg));
82 fFunctionSize = 0;
83 *cachedCost = 0;
84 return true;
85 }
86 // Set the size to its known value.
87 fFunctionSize = *cachedCost;
88 return false;
89 }
90
91 // If the function-call stack has gotten too deep, stop the analysis.
92 if (fStack.size() >= kProgramStackDepthLimit) {
93 std::string msg = "exceeded max function call depth:";
94 for (auto unwind = fStack.begin(); unwind != fStack.end(); ++unwind) {
95 msg += "\n\t" + (*unwind)->description();
96 }
97 msg += "\n\t" + decl->description();
98 fContext.fErrors->error(pe.fPosition, std::move(msg));
99 fFunctionSize = 0;
100 fFunctionCostMap.set(decl, 0);
101 return true;
102 }
103
104 // Calculate the function cost and store it in our cache.
105 fFunctionCostMap.set(decl, kUnknownCost);
106 fStack.push_back(decl);
107 fFunctionSize = 0;
108 bool result = INHERITED::visitProgramElement(pe);
109 fFunctionCostMap.set(decl, fFunctionSize);
110 fStack.pop_back();
111
112 return result;
113 }
114
115 return INHERITED::visitProgramElement(pe);
116 }
117
118 bool visitStatement(const Statement& stmt) override {
119 switch (stmt.kind()) {
120 case Statement::Kind::kFor: {
121 // We count a for-loop's unrolled size here. We expect that the init statement
122 // will be emitted once, and the test-expr, next-expr and statement will be
123 // repeated in the output for every iteration of the loop.
124 bool earlyExit = false;
125 const ForStatement& forStmt = stmt.as<ForStatement>();
126 if (forStmt.initializer() && this->visitStatement(*forStmt.initializer())) {
127 earlyExit = true;
128 }
129
130 size_t originalFunctionSize = fFunctionSize;
131 fFunctionSize = 0;
132
133 if (forStmt.next() && this->visitExpression(*forStmt.next())) {
134 earlyExit = true;
135 }
136 if (forStmt.test() && this->visitExpression(*forStmt.test())) {
137 earlyExit = true;
138 }
139 if (this->visitStatement(*forStmt.statement())) {
140 earlyExit = true;
141 }
142
143 // ES2 programs always have a known unroll count. Non-ES2 programs don't enforce
144 // a maximum program size, so it's fine to treat the loop as executing once.
145 if (const LoopUnrollInfo* unrollInfo = forStmt.unrollInfo()) {
146 fFunctionSize = SkSafeMath::Mul(fFunctionSize, unrollInfo->fCount);
147 }
148 fFunctionSize = SkSafeMath::Add(fFunctionSize, originalFunctionSize);
149 return earlyExit;
150 }
151
152 case Statement::Kind::kExpression:
153 // The cost of an expression-statement is counted in visitExpression. It would
154 // be double-dipping to count it here too.
155 break;
156
157 case Statement::Kind::kNop:
158 case Statement::Kind::kVarDeclaration:
159 // These statements don't directly consume any space in a compiled program.
160 break;
161
162 default:
163 // Note that we don't make any attempt to estimate the number of iterations of
164 // do-while loops here. Those aren't an ES2 construct so we aren't enforcing
165 // program size on them.
166 fFunctionSize = SkSafeMath::Add(fFunctionSize, kStatementCost);
167 break;
168 }
169
170 return INHERITED::visitStatement(stmt);
171 }
172
173 bool visitExpression(const Expression& expr) override {
174 // Other than function calls, all expressions are assumed to have a fixed unit cost.
175 bool earlyExit = false;
176 size_t expressionCost = kExpressionCost;
177
178 if (expr.is<FunctionCall>()) {
179 // Visit this function call to calculate its size. If we've already sized it, this
180 // will retrieve the size from our cache.
181 const FunctionCall& call = expr.as<FunctionCall>();
182 const FunctionDeclaration* decl = &call.function();
183 if (decl->definition() && !decl->isIntrinsic()) {
184 size_t originalFunctionSize = fFunctionSize;
185 fFunctionSize = 0;
186
187 earlyExit = this->visitProgramElement(*decl->definition());
188 expressionCost = fFunctionSize;
189
190 fFunctionSize = originalFunctionSize;
191 }
192 }
193
194 fFunctionSize = SkSafeMath::Add(fFunctionSize, expressionCost);
195 return earlyExit || INHERITED::visitExpression(expr);
196 }
197
198 private:
199 using INHERITED = ProgramVisitor;
200
201 const Context& fContext;
202 size_t fFunctionSize = 0;
204 std::vector<const FunctionDeclaration*> fStack;
205 };
206
207 // Process every function in our program.
208 ProgramSizeVisitor visitor{context};
209 for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
210 if (element->is<FunctionDefinition>()) {
211 // Visit every function--we want to detect static recursion and report it as an error,
212 // even in unreferenced functions.
213 visitor.visitProgramElement(*element);
214 // Report an error when main()'s flattened size is larger than our program limit.
215 if (enforceSizeLimit &&
216 visitor.functionSize() > kProgramSizeLimit &&
217 element->as<FunctionDefinition>().declaration().isMain()) {
218 context.fErrors->error(Position(), "program is too large");
219 }
220 }
221 }
222
223 return true;
224}
#define INHERITED(method,...)
const Context & fContext
ErrorReporter * fErrors
Definition SkSLContext.h:36
void error(Position position, std::string_view msg)
virtual bool visitStatement(typename T::Statement &statement)
virtual bool visitExpression(typename T::Expression &expression)
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
static size_t Add(size_t x, size_t y)
static size_t Mul(size_t x, size_t y)
GAsyncResult * result
call(args)
Definition dom.py:159
std::shared_ptr< Context > fContext

◆ CheckSymbolTableCorrectness()

void SkSL::Analysis::CheckSymbolTableCorrectness ( const Program program)

Emits an internal error if a VarDeclaration exists without a matching entry in the nearest SymbolTable.

Definition at line 33 of file SkSLCheckSymbolTableCorrectness.cpp.

33 {
34 const Context& context = *program.fContext;
35
36 class SymbolTableCorrectnessVisitor : public ProgramVisitor {
37 public:
38 SymbolTableCorrectnessVisitor(const Context& c, SymbolTable* sym)
39 : fContext(c)
40 , fSymbolTableStack({sym}) {}
41
42 using ProgramVisitor::visitProgramElement;
43
44 bool visitStatement(const Statement& stmt) override {
45 Analysis::SymbolTableStackBuilder symbolTableStackBuilder(&stmt, &fSymbolTableStack);
46 if (stmt.is<VarDeclaration>()) {
47 // Check the top of the symbol table stack to see if it contains this exact symbol.
48 const VarDeclaration& vardecl = stmt.as<VarDeclaration>();
49 bool containsSymbol = false;
50
51 // We want to do an exact Symbol* comparison in just one symbol table; we don't want
52 // to look up by name, and we don't want to walk the symbol table tree. This makes
53 // SymbolTable::find() an inappropriate tool for the job. Instead, we can iterate
54 // the symbol table's contents directly and check for a pointer match.
55 fSymbolTableStack.back()->foreach([&](std::string_view, const Symbol* symbol) {
56 if (symbol == vardecl.var()) {
57 containsSymbol = true;
58 }
59 });
60 if (!containsSymbol) {
61 fContext.fErrors->error(vardecl.position(), "internal error (variable '" +
62 std::string(vardecl.var()->name()) +
63 "' is incorrectly scoped)");
64 }
65 }
66 return INHERITED::visitStatement(stmt);
67 }
68
69 bool visitExpression(const Expression&) override {
70 return false;
71 }
72
73 private:
74 using INHERITED = ProgramVisitor;
75
76 const Context& fContext;
77 std::vector<SymbolTable*> fSymbolTableStack;
78 };
79
80 SymbolTableCorrectnessVisitor visitor{context, program.fSymbols.get()};
81 for (const std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
82 visitor.visitProgramElement(*pe);
83 }
84}
std::unique_ptr< SymbolTable > fSymbols

◆ ContainsRTAdjust()

bool SkSL::Analysis::ContainsRTAdjust ( const Expression expr)

Determines if expr contains a reference to the variable sk_RTAdjust.

Definition at line 390 of file SkSLAnalysis.cpp.

390 {
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
401 using INHERITED = ProgramVisitor;
402 };
403
404 ContainsRTAdjustVisitor visitor;
405 return visitor.visitExpression(expr);
406}
bool is() const
Definition SkSLIRNode.h:124
std::string_view name() const
Definition SkSLSymbol.h:51
const Variable * variable() const

◆ ContainsVariable()

bool SkSL::Analysis::ContainsVariable ( const Expression expr,
const Variable var 
)

Determines if expr contains a reference to variable var.

Definition at line 408 of file SkSLAnalysis.cpp.

408 {
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
421 using INHERITED = ProgramVisitor;
422 const Variable* fVariable;
423 };
424
425 ContainsVariableVisitor visitor{&var};
426 return visitor.visitExpression(expr);
427}

◆ DetectVarDeclarationWithoutScope()

bool SkSL::Analysis::DetectVarDeclarationWithoutScope ( const Statement stmt,
ErrorReporter errors = nullptr 
)

Detect an orphaned variable declaration outside of a scope, e.g. if (true) int a;. Returns true if an error was reported.

Definition at line 466 of file SkSLAnalysis.cpp.

466 {
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}
#define SkASSERT(cond)
Definition SkAssert.h:116
const StatementArray & children() const
Definition SkSLBlock.h:71
bool isScope() const
Definition SkSLBlock.h:79
Position fPosition
Definition SkSLIRNode.h:109
bool empty() const
Definition SkTArray.h:194

◆ DoFinalizationChecks()

void SkSL::Analysis::DoFinalizationChecks ( const Program program)

Runs at finalization time to perform any last-minute correctness checks:

  • Reports dangling FunctionReference or TypeReference expressions
  • Reports function out params which are never written to (structs are currently exempt)

Definition at line 207 of file SkSLFinalizationChecks.cpp.

207 {
208 // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
209 FinalizationVisitor visitor{*program.fContext, *program.usage()};
210 for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
211 visitor.visitProgramElement(*element);
212 }
213 if (ProgramConfig::IsCompute(program.fConfig->fKind) && !visitor.definesLocalSize()) {
214 program.fContext->fErrors->error(Position(),
215 "compute programs must specify a workgroup size");
216 }
217}
const ProgramUsage * usage() const
std::unique_ptr< ProgramConfig > fConfig

◆ GetComputeShaderMainParams()

skia_private::TArray< const SkSL::Variable * > SkSL::Analysis::GetComputeShaderMainParams ( const Context context,
const Program program 
)

Error checks compute shader in/outs and returns a vector containing them ordered by location.

◆ GetLoopControlFlowInfo()

LoopControlFlowInfo SkSL::Analysis::GetLoopControlFlowInfo ( const Statement stmt)

Definition at line 72 of file SkSLGetLoopControlFlowInfo.cpp.

72 {
73 LoopControlFlowVisitor visitor;
74 visitor.visitStatement(stmt);
75 return visitor.fResult;
76}

◆ GetLoopUnrollInfo()

std::unique_ptr< LoopUnrollInfo > SkSL::Analysis::GetLoopUnrollInfo ( const Context context,
Position  pos,
const ForLoopPositions positions,
const Statement loopInitializer,
std::unique_ptr< Expression > *  loopTestPtr,
const Expression loopNext,
const Statement loopStatement,
ErrorReporter errors 
)

Ensures that a for-loop meets the strict requirements of The OpenGL ES Shading Language 1.00, Appendix A, Section 4. If the requirements are met, information about the loop's structure is returned. If the requirements are not met, the problem is reported via errors (if not nullptr), and null is returned. The loop test-expression may be altered by this check. For example, a loop like this: for (float x = 1.0; x != 0.0; x -= 0.01) {...} appears to be ES2-safe, but due to floating-point rounding error, it may not actually terminate. We rewrite the test condition to x > 0.0 in order to ensure loop termination.

Definition at line 59 of file SkSLGetLoopUnrollInfo.cpp.

66 {
68 ErrorReporter& errors = errorPtr ? *errorPtr : unused;
69
70 auto loopInfo = std::make_unique<LoopUnrollInfo>();
71
72 //
73 // init_declaration has the form: type_specifier identifier = constant_expression
74 //
75 if (!loopInitializer) {
76 Position pos = positions.initPosition.valid() ? positions.initPosition : loopPos;
77 errors.error(pos, "missing init declaration");
78 return nullptr;
79 }
80 if (!loopInitializer->is<VarDeclaration>()) {
81 errors.error(loopInitializer->fPosition, "invalid init declaration");
82 return nullptr;
83 }
84 const VarDeclaration& initDecl = loopInitializer->as<VarDeclaration>();
85 if (!initDecl.baseType().isNumber()) {
86 errors.error(loopInitializer->fPosition, "invalid type for loop index");
87 return nullptr;
88 }
89 if (initDecl.arraySize() != 0) {
90 errors.error(loopInitializer->fPosition, "invalid type for loop index");
91 return nullptr;
92 }
93 if (!initDecl.value()) {
94 errors.error(loopInitializer->fPosition, "missing loop index initializer");
95 return nullptr;
96 }
97 if (!ConstantFolder::GetConstantValue(*initDecl.value(), &loopInfo->fStart)) {
98 errors.error(loopInitializer->fPosition,
99 "loop index initializer must be a constant expression");
100 return nullptr;
101 }
102
103 loopInfo->fIndex = initDecl.var();
104
105 auto is_loop_index = [&](const std::unique_ptr<Expression>& expr) {
106 return expr->is<VariableReference>() &&
107 expr->as<VariableReference>().variable() == loopInfo->fIndex;
108 };
109
110 //
111 // condition has the form: loop_index relational_operator constant_expression
112 //
113 if (!loopTest || !*loopTest) {
114 Position pos = positions.conditionPosition.valid() ? positions.conditionPosition : loopPos;
115 errors.error(pos, "missing condition");
116 return nullptr;
117 }
118 if (!loopTest->get()->is<BinaryExpression>()) {
119 errors.error(loopTest->get()->fPosition, "invalid condition");
120 return nullptr;
121 }
122 const BinaryExpression* cond = &loopTest->get()->as<BinaryExpression>();
123 if (!is_loop_index(cond->left())) {
124 errors.error(cond->fPosition, "expected loop index on left hand side of condition");
125 return nullptr;
126 }
127 // relational_operator is one of: > >= < <= == or !=
128 switch (cond->getOperator().kind()) {
129 case Operator::Kind::GT:
130 case Operator::Kind::GTEQ:
131 case Operator::Kind::LT:
132 case Operator::Kind::LTEQ:
133 case Operator::Kind::EQEQ:
134 case Operator::Kind::NEQ:
135 break;
136 default:
137 errors.error(cond->fPosition, "invalid relational operator");
138 return nullptr;
139 }
140 double loopEnd = 0;
141 if (!ConstantFolder::GetConstantValue(*cond->right(), &loopEnd)) {
142 errors.error(cond->fPosition, "loop index must be compared with a constant expression");
143 return nullptr;
144 }
145
146 //
147 // expression has one of the following forms:
148 // loop_index++
149 // loop_index--
150 // loop_index += constant_expression
151 // loop_index -= constant_expression
152 // The spec doesn't mention prefix increment and decrement, but there is some consensus that
153 // it's an oversight, so we allow those as well.
154 //
155 if (!loopNext) {
156 Position pos = positions.nextPosition.valid() ? positions.nextPosition : loopPos;
157 errors.error(pos, "missing loop expression");
158 return nullptr;
159 }
160 switch (loopNext->kind()) {
161 case Expression::Kind::kBinary: {
162 const BinaryExpression& next = loopNext->as<BinaryExpression>();
163 if (!is_loop_index(next.left())) {
164 errors.error(loopNext->fPosition, "expected loop index in loop expression");
165 return nullptr;
166 }
167 if (!ConstantFolder::GetConstantValue(*next.right(), &loopInfo->fDelta)) {
168 errors.error(loopNext->fPosition,
169 "loop index must be modified by a constant expression");
170 return nullptr;
171 }
172 switch (next.getOperator().kind()) {
173 case Operator::Kind::PLUSEQ: break;
174 case Operator::Kind::MINUSEQ: loopInfo->fDelta = -loopInfo->fDelta; break;
175 default:
176 errors.error(loopNext->fPosition, "invalid operator in loop expression");
177 return nullptr;
178 }
179 break;
180 }
181 case Expression::Kind::kPrefix: {
182 const PrefixExpression& next = loopNext->as<PrefixExpression>();
183 if (!is_loop_index(next.operand())) {
184 errors.error(loopNext->fPosition, "expected loop index in loop expression");
185 return nullptr;
186 }
187 switch (next.getOperator().kind()) {
188 case Operator::Kind::PLUSPLUS: loopInfo->fDelta = 1; break;
189 case Operator::Kind::MINUSMINUS: loopInfo->fDelta = -1; break;
190 default:
191 errors.error(loopNext->fPosition, "invalid operator in loop expression");
192 return nullptr;
193 }
194 break;
195 }
196 case Expression::Kind::kPostfix: {
197 const PostfixExpression& next = loopNext->as<PostfixExpression>();
198 if (!is_loop_index(next.operand())) {
199 errors.error(loopNext->fPosition, "expected loop index in loop expression");
200 return nullptr;
201 }
202 switch (next.getOperator().kind()) {
203 case Operator::Kind::PLUSPLUS: loopInfo->fDelta = 1; break;
204 case Operator::Kind::MINUSMINUS: loopInfo->fDelta = -1; break;
205 default:
206 errors.error(loopNext->fPosition, "invalid operator in loop expression");
207 return nullptr;
208 }
209 break;
210 }
211 default:
212 errors.error(loopNext->fPosition, "invalid loop expression");
213 return nullptr;
214 }
215
216 //
217 // Within the body of the loop, the loop index is not statically assigned to, nor is it used as
218 // argument to a function 'out' or 'inout' parameter.
219 //
220 if (Analysis::StatementWritesToVariable(*loopStatement, *initDecl.var())) {
221 errors.error(loopStatement->fPosition,
222 "loop index must not be modified within body of the loop");
223 return nullptr;
224 }
225
226 // Finally, compute the iteration count, based on the bounds, and the termination operator.
227 loopInfo->fCount = 0;
228
229 switch (cond->getOperator().kind()) {
230 case Operator::Kind::LT:
231 loopInfo->fCount = calculate_count(loopInfo->fStart, loopEnd, loopInfo->fDelta,
232 /*forwards=*/true, /*inclusive=*/false);
233 break;
234
235 case Operator::Kind::GT:
236 loopInfo->fCount = calculate_count(loopInfo->fStart, loopEnd, loopInfo->fDelta,
237 /*forwards=*/false, /*inclusive=*/false);
238 break;
239
240 case Operator::Kind::LTEQ:
241 loopInfo->fCount = calculate_count(loopInfo->fStart, loopEnd, loopInfo->fDelta,
242 /*forwards=*/true, /*inclusive=*/true);
243 break;
244
245 case Operator::Kind::GTEQ:
246 loopInfo->fCount = calculate_count(loopInfo->fStart, loopEnd, loopInfo->fDelta,
247 /*forwards=*/false, /*inclusive=*/true);
248 break;
249
250 case Operator::Kind::NEQ: {
251 float iterations = sk_ieee_double_divide(loopEnd - loopInfo->fStart, loopInfo->fDelta);
252 loopInfo->fCount = std::ceil(iterations);
253 if (loopInfo->fCount < 0 || loopInfo->fCount != iterations ||
254 !std::isfinite(iterations)) {
255 // The loop doesn't reach the exact endpoint and so will never terminate.
256 loopInfo->fCount = kLoopTerminationLimit;
257 }
258 if (loopInfo->fIndex->type().componentType().isFloat()) {
259 // Rewrite `x != n` tests as `x < n` or `x > n` depending on the loop direction.
260 // Less-than and greater-than tests avoid infinite loops caused by rounding error.
261 Operator::Kind op = (loopInfo->fDelta > 0) ? Operator::Kind::LT
262 : Operator::Kind::GT;
263 *loopTest = BinaryExpression::Make(context,
264 cond->fPosition,
265 cond->left()->clone(),
266 op,
267 cond->right()->clone());
268 cond = &loopTest->get()->as<BinaryExpression>();
269 }
270 break;
271 }
272 case Operator::Kind::EQEQ: {
273 if (loopInfo->fStart == loopEnd) {
274 // Start and end begin in the same place, so we can run one iteration...
275 if (loopInfo->fDelta) {
276 // ... and then they diverge, so the loop terminates.
277 loopInfo->fCount = 1;
278 } else {
279 // ... but they never diverge, so the loop runs forever.
280 loopInfo->fCount = kLoopTerminationLimit;
281 }
282 } else {
283 // Start never equals end, so the loop will not run a single iteration.
284 loopInfo->fCount = 0;
285 }
286 break;
287 }
288 default: SkUNREACHABLE;
289 }
290
291 SkASSERT(loopInfo->fCount >= 0);
292 if (loopInfo->fCount >= kLoopTerminationLimit) {
293 errors.error(loopPos, "loop must guarantee termination in fewer iterations");
294 return nullptr;
295 }
296
297 return loopInfo;
298}
static bool unused
SkPoint pos
static float next(float f)
#define SkUNREACHABLE
Definition SkAssert.h:135
static constexpr double sk_ieee_double_divide(double numer, double denom)
Kind kind() const
bool valid() const
static constexpr int kLoopTerminationLimit
static int calculate_count(double start, double end, double delta, bool forwards, bool inclusive)

◆ GetReturnComplexity()

Analysis::ReturnComplexity SkSL::Analysis::GetReturnComplexity ( const FunctionDefinition funcDef)

Definition at line 116 of file SkSLGetReturnComplexity.cpp.

116 {
117 int returnsAtEndOfControlFlow = count_returns_at_end_of_control_flow(funcDef);
118 CountReturnsWithLimit counter{funcDef, returnsAtEndOfControlFlow + 1};
119 if (counter.fNumReturns > returnsAtEndOfControlFlow) {
120 return ReturnComplexity::kEarlyReturns;
121 }
122 if (counter.fNumReturns > 1) {
123 return ReturnComplexity::kScopedReturns;
124 }
125 if (counter.fVariablesInBlocks && counter.fDeepestReturn > 1) {
126 return ReturnComplexity::kScopedReturns;
127 }
128 return ReturnComplexity::kSingleSafeReturn;
129}
static int count_returns_at_end_of_control_flow(const FunctionDefinition &funcDef)

◆ GetSampleUsage()

SampleUsage SkSL::Analysis::GetSampleUsage ( const Program program,
const Variable child,
bool  writesToSampleCoords = true,
int elidedSampleCoordCount = nullptr 
)

Determines how program samples child. By default, assumes that the sample coords might be modified, so child.eval(sampleCoords) is treated as Explicit. If writesToSampleCoords is false, treats that as PassThrough, instead. If elidedSampleCoordCount is provided, the pointed to value will be incremented by the number of sample calls where the above rewrite was performed.

Definition at line 325 of file SkSLAnalysis.cpp.

328 {
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}

◆ GetUsage() [1/2]

std::unique_ptr< ProgramUsage > SkSL::Analysis::GetUsage ( const Module module)

Definition at line 152 of file SkSLProgramUsage.cpp.

152 {
153 auto usage = std::make_unique<ProgramUsage>();
154 ProgramUsageVisitor addRefs(usage.get(), /*delta=*/+1);
155
156 for (const Module* m = &module; m != nullptr; m = m->fParent) {
157 for (const std::unique_ptr<ProgramElement>& element : m->fElements) {
158 addRefs.visitProgramElement(*element);
159 }
160 }
161 return usage;
162}

◆ GetUsage() [2/2]

std::unique_ptr< ProgramUsage > SkSL::Analysis::GetUsage ( const Program program)

Definition at line 145 of file SkSLProgramUsage.cpp.

145 {
146 auto usage = std::make_unique<ProgramUsage>();
147 ProgramUsageVisitor addRefs(usage.get(), /*delta=*/+1);
148 addRefs.visit(program);
149 return usage;
150}

◆ HasSideEffects()

bool SkSL::Analysis::HasSideEffects ( const Expression expr)

Determines if expr has any side effects. (Is the expression state-altering or pure?)

Definition at line 22 of file SkSLHasSideEffects.cpp.

22 {
23 class HasSideEffectsVisitor : public ProgramVisitor {
24 public:
25 bool visitExpression(const Expression& expr) override {
26 switch (expr.kind()) {
27 case Expression::Kind::kFunctionCall: {
28 const FunctionCall& call = expr.as<FunctionCall>();
29 if (!call.function().modifierFlags().isPure()) {
30 return true;
31 }
32 break;
33 }
34 case Expression::Kind::kPrefix: {
35 const PrefixExpression& prefix = expr.as<PrefixExpression>();
36 if (prefix.getOperator().kind() == Operator::Kind::PLUSPLUS ||
37 prefix.getOperator().kind() == Operator::Kind::MINUSMINUS) {
38 return true;
39 }
40 break;
41 }
42 case Expression::Kind::kBinary: {
43 const BinaryExpression& binary = expr.as<BinaryExpression>();
44 if (binary.getOperator().isAssignment()) {
45 return true;
46 }
47 break;
48 }
49 case Expression::Kind::kPostfix:
50 return true;
51
52 default:
53 break;
54 }
55 return INHERITED::visitExpression(expr);
56 }
57
58 using INHERITED = ProgramVisitor;
59 };
60
61 HasSideEffectsVisitor visitor;
62 return visitor.visitExpression(expr);
63}

◆ IsAssignable()

bool SkSL::Analysis::IsAssignable ( Expression expr,
AssignmentInfo info = nullptr,
ErrorReporter errors = nullptr 
)

Definition at line 507 of file SkSLAnalysis.cpp.

507 {
508 NoOpErrorReporter unusedErrors;
509 return IsAssignableVisitor{errors ? errors : &unusedErrors}.visit(expr, info);
510}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213

◆ IsCompileTimeConstant()

bool SkSL::Analysis::IsCompileTimeConstant ( const Expression expr)

Determines if expr is a compile-time constant (composed of just constructors and literals).

Definition at line 429 of file SkSLAnalysis.cpp.

429 {
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;
458 using INHERITED = ProgramVisitor;
459 };
460
461 IsCompileTimeConstantVisitor visitor;
462 visitor.visitExpression(expr);
463 return visitor.fIsConstant;
464}

◆ IsConstantExpression()

bool SkSL::Analysis::IsConstantExpression ( const Expression expr)

Returns true if expr is a constant-expression, as defined by GLSL 1.0, section 5.10. A constant expression is one of:

  • A literal value
  • A global or local variable qualified as 'const', excluding function parameters
  • An expression formed by an operator on operands that are constant expressions, including getting an element of a constant vector or a constant matrix, or a field of a constant structure
  • A constructor whose arguments are all constant expressions
  • A built-in function call whose arguments are all constant expressions, with the exception of the texture lookup functions

Definition at line 157 of file SkSLIsConstantExpression.cpp.

157 {
158 return !ConstantExpressionVisitor{/*loopIndices=*/nullptr}.visitExpression(expr);
159}

◆ IsDynamicallyUniformExpression()

bool SkSL::Analysis::IsDynamicallyUniformExpression ( const Expression expr)

Determines if expr is a dynamically-uniform expression; this returns true if the expression could be evaluated at compile time if uniform values were known.

Definition at line 21 of file SkSLIsDynamicallyUniformExpression.cpp.

21 {
22 class IsDynamicallyUniformExpressionVisitor : public ProgramVisitor {
23 public:
24 bool visitExpression(const Expression& expr) override {
25 switch (expr.kind()) {
26 case Expression::Kind::kBinary:
27 case Expression::Kind::kConstructorArray:
28 case Expression::Kind::kConstructorArrayCast:
29 case Expression::Kind::kConstructorCompound:
30 case Expression::Kind::kConstructorCompoundCast:
31 case Expression::Kind::kConstructorDiagonalMatrix:
32 case Expression::Kind::kConstructorMatrixResize:
33 case Expression::Kind::kConstructorScalarCast:
34 case Expression::Kind::kConstructorSplat:
35 case Expression::Kind::kConstructorStruct:
36 case Expression::Kind::kFieldAccess:
37 case Expression::Kind::kIndex:
38 case Expression::Kind::kPostfix:
39 case Expression::Kind::kPrefix:
40 case Expression::Kind::kSwizzle:
41 case Expression::Kind::kTernary:
42 // These expressions might be dynamically uniform, if they are composed entirely
43 // of constants and uniforms.
44 break;
45
46 case Expression::Kind::kVariableReference: {
47 // Verify that variable references are const or uniform.
48 const Variable* var = expr.as<VariableReference>().variable();
49 if (var && (var->modifierFlags().isConst() ||
50 var->modifierFlags().isUniform())) {
51 break;
52 }
53 fIsDynamicallyUniform = false;
54 return true;
55 }
56 case Expression::Kind::kFunctionCall: {
57 // Verify that function calls are pure.
58 const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
59 if (decl.modifierFlags().isPure()) {
60 break;
61 }
62 fIsDynamicallyUniform = false;
63 return true;
64 }
65 case Expression::Kind::kLiteral:
66 // Literals are compile-time constants.
67 return false;
68
69 default:
70 // This expression isn't dynamically uniform.
71 fIsDynamicallyUniform = false;
72 return true;
73 }
74 return INHERITED::visitExpression(expr);
75 }
76
77 bool fIsDynamicallyUniform = true;
78 using INHERITED = ProgramVisitor;
79 };
80
81 IsDynamicallyUniformExpressionVisitor visitor;
82 visitor.visitExpression(expr);
83 return visitor.fIsDynamicallyUniform;
84}
ModifierFlags modifierFlags() const
Dart_NativeFunction function
Definition fuchsia.cc:51

◆ IsSameExpressionTree()

bool SkSL::Analysis::IsSameExpressionTree ( const Expression left,
const Expression right 
)

Returns true if both expression trees are the same. Used by the optimizer to look for self- assignment or self-comparison; won't necessarily catch complex cases. Rejects expressions that may cause side effects.

Definition at line 30 of file SkSLIsSameExpressionTree.cpp.

30 {
31 if (left.kind() != right.kind() || !left.type().matches(right.type())) {
32 return false;
33 }
34
35 // This isn't a fully exhaustive list of expressions by any stretch of the imagination; for
36 // instance, `x[y+1] = x[y+1]` isn't detected because we don't look at BinaryExpressions.
37 // Since this is intended to be used for optimization purposes, handling the common cases is
38 // sufficient.
39 switch (left.kind()) {
40 case Expression::Kind::kLiteral:
41 return left.as<Literal>().value() == right.as<Literal>().value();
42
43 case Expression::Kind::kConstructorArray:
44 case Expression::Kind::kConstructorArrayCast:
45 case Expression::Kind::kConstructorCompound:
46 case Expression::Kind::kConstructorCompoundCast:
47 case Expression::Kind::kConstructorDiagonalMatrix:
48 case Expression::Kind::kConstructorMatrixResize:
49 case Expression::Kind::kConstructorScalarCast:
50 case Expression::Kind::kConstructorStruct:
51 case Expression::Kind::kConstructorSplat: {
52 if (left.kind() != right.kind()) {
53 return false;
54 }
55 const AnyConstructor& leftCtor = left.asAnyConstructor();
56 const AnyConstructor& rightCtor = right.asAnyConstructor();
57 const auto leftSpan = leftCtor.argumentSpan();
58 const auto rightSpan = rightCtor.argumentSpan();
59 if (leftSpan.size() != rightSpan.size()) {
60 return false;
61 }
62 for (size_t index = 0; index < leftSpan.size(); ++index) {
63 if (!IsSameExpressionTree(*leftSpan[index], *rightSpan[index])) {
64 return false;
65 }
66 }
67 return true;
68 }
69 case Expression::Kind::kFieldAccess:
70 return left.as<FieldAccess>().fieldIndex() == right.as<FieldAccess>().fieldIndex() &&
71 IsSameExpressionTree(*left.as<FieldAccess>().base(),
72 *right.as<FieldAccess>().base());
73
74 case Expression::Kind::kIndex:
75 return IsSameExpressionTree(*left.as<IndexExpression>().index(),
76 *right.as<IndexExpression>().index()) &&
77 IsSameExpressionTree(*left.as<IndexExpression>().base(),
78 *right.as<IndexExpression>().base());
79
80 case Expression::Kind::kPrefix:
81 return (left.as<PrefixExpression>().getOperator().kind() ==
82 right.as<PrefixExpression>().getOperator().kind()) &&
83 IsSameExpressionTree(*left.as<PrefixExpression>().operand(),
84 *right.as<PrefixExpression>().operand());
85
86 case Expression::Kind::kSwizzle:
87 return left.as<Swizzle>().components() == right.as<Swizzle>().components() &&
88 IsSameExpressionTree(*left.as<Swizzle>().base(), *right.as<Swizzle>().base());
89
90 case Expression::Kind::kVariableReference:
91 return left.as<VariableReference>().variable() ==
92 right.as<VariableReference>().variable();
93
94 default:
95 return false;
96 }
97}
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
uint8_t value
bool IsSameExpressionTree(const Expression &left, const Expression &right)

◆ IsTrivialExpression()

bool SkSL::Analysis::IsTrivialExpression ( const Expression expr)

A "trivial" expression is one where we'd feel comfortable cloning it multiple times in the code, without worrying about incurring a performance penalty. Examples:

  • true
  • 3.14159265
  • myIntVariable
  • myColor.rgb
  • myArray[123]
  • myStruct.myField
  • half4(0)
  • !myBoolean
  • +myValue
  • -myValue
  • ~myInteger

Trivial-ness is stackable. Somewhat large expressions can occasionally make the cut:

  • half4(myColor.a)
  • myStruct.myArrayField[7].xzy

Definition at line 25 of file SkSLIsTrivialExpression.cpp.

25 {
26 switch (expr.kind()) {
27 case Expression::Kind::kLiteral:
28 case Expression::Kind::kVariableReference:
29 return true;
30
31 case Expression::Kind::kSwizzle:
32 // All swizzles are considered to be trivial.
33 return IsTrivialExpression(*expr.as<Swizzle>().base());
34
35 case Expression::Kind::kPrefix: {
36 const PrefixExpression& prefix = expr.as<PrefixExpression>();
37 switch (prefix.getOperator().kind()) {
38 case OperatorKind::PLUS:
39 case OperatorKind::MINUS:
40 case OperatorKind::LOGICALNOT:
41 case OperatorKind::BITWISENOT:
42 return IsTrivialExpression(*prefix.operand());
43
44 default:
45 return false;
46 }
47 }
48 case Expression::Kind::kFieldAccess:
49 // Accessing a field is trivial.
50 return IsTrivialExpression(*expr.as<FieldAccess>().base());
51
52 case Expression::Kind::kIndex: {
53 // Accessing a constant array index is trivial.
54 const IndexExpression& inner = expr.as<IndexExpression>();
55 return inner.index()->isIntLiteral() && IsTrivialExpression(*inner.base());
56 }
57 case Expression::Kind::kConstructorArray:
58 case Expression::Kind::kConstructorStruct:
59 // Only consider small arrays/structs of compile-time-constants to be trivial.
60 return expr.type().slotCount() <= 4 && IsCompileTimeConstant(expr);
61
62 case Expression::Kind::kConstructorArrayCast:
63 case Expression::Kind::kConstructorMatrixResize:
64 // These operations require function calls in Metal, so they're never trivial.
65 return false;
66
67 case Expression::Kind::kConstructorCompound:
68 // Only compile-time-constant compound constructors are considered to be trivial.
69 return IsCompileTimeConstant(expr);
70
71 case Expression::Kind::kConstructorCompoundCast:
72 case Expression::Kind::kConstructorScalarCast:
73 case Expression::Kind::kConstructorSplat:
74 case Expression::Kind::kConstructorDiagonalMatrix: {
75 // Single-argument constructors are trivial when their inner expression is trivial.
76 SkASSERT(expr.asAnyConstructor().argumentSpan().size() == 1);
77 const Expression& inner = *expr.asAnyConstructor().argumentSpan().front();
78 return IsTrivialExpression(inner);
79 }
80 default:
81 return false;
82 }
83}
virtual SkSpan< std::unique_ptr< Expression > > argumentSpan()=0
virtual const Type & type() const
AnyConstructor & asAnyConstructor()
std::unique_ptr< Expression > & base()
Definition SkSLSwizzle.h:82
virtual size_t slotCount() const
Definition SkSLType.h:457
bool IsTrivialExpression(const Expression &expr)
constexpr bool IsCompileTimeConstant(const T)

◆ NodeCountUpToLimit()

int SkSL::Analysis::NodeCountUpToLimit ( const FunctionDefinition function,
int  limit 
)

Definition at line 499 of file SkSLAnalysis.cpp.

499 {
500 return NodeCountVisitor{limit}.visit(*function.body());
501}

◆ ReferencesBuiltin()

bool SkSL::Analysis::ReferencesBuiltin ( const Program program,
int  builtin 
)

Definition at line 337 of file SkSLAnalysis.cpp.

337 {
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}
std::unique_ptr< ProgramUsage > fUsage

◆ ReferencesFragCoords()

bool SkSL::Analysis::ReferencesFragCoords ( const Program program)

Definition at line 365 of file SkSLAnalysis.cpp.

365 {
366 return Analysis::ReferencesBuiltin(program, SK_FRAGCOORD_BUILTIN);
367}
constexpr int SK_FRAGCOORD_BUILTIN

◆ ReferencesSampleCoords()

bool SkSL::Analysis::ReferencesSampleCoords ( const Program program)

Definition at line 347 of file SkSLAnalysis.cpp.

347 {
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}
const Variable * getMainCoordsParameter() const

◆ ReturnsInputAlpha()

bool SkSL::Analysis::ReturnsInputAlpha ( const FunctionDefinition function,
const ProgramUsage usage 
)

Determines if function is a color filter which returns the alpha component of the input color unchanged. This is a very conservative analysis, and only supports returning a swizzle of the input color, or returning a constructor that ends with input.a.

Definition at line 127 of file SkSLReturnsInputAlpha.cpp.

127 {
128 ReturnsInputAlphaVisitor visitor{usage};
129 return !visitor.visitProgramElement(function);
130}

◆ ReturnsOpaqueColor()

bool SkSL::Analysis::ReturnsOpaqueColor ( const FunctionDefinition function)

Determines if function always returns an opaque color (a vec4 where the last component is known to be 1). This is conservative, and based on constant expression analysis.

Definition at line 385 of file SkSLAnalysis.cpp.

385 {
386 ReturnsNonOpaqueColorVisitor visitor;
387 return !visitor.visitProgramElement(function);
388}

◆ StatementWritesToVariable()

bool SkSL::Analysis::StatementWritesToVariable ( const Statement stmt,
const Variable var 
)

Returns true if the passed-in statement might alter var.

Definition at line 503 of file SkSLAnalysis.cpp.

503 {
504 return VariableWriteVisitor(&var).visit(stmt);
505}

◆ SwitchCaseContainsConditionalExit()

bool SkSL::Analysis::SwitchCaseContainsConditionalExit ( const Statement stmt)

Finds conditional exits from a switch-case. Returns true if this statement contains a conditional that wraps a potential exit from the switch (via continue, break or return).

Definition at line 94 of file SkSLSwitchCaseContainsExit.cpp.

94 {
95 return SwitchCaseContainsExit{/*conditionalExits=*/true}.visitStatement(stmt);
96}

◆ SwitchCaseContainsUnconditionalExit()

bool SkSL::Analysis::SwitchCaseContainsUnconditionalExit ( const Statement stmt)

Finds unconditional exits from a switch-case. Returns true if this statement unconditionally causes an exit from this switch (via continue, break or return).

Definition at line 90 of file SkSLSwitchCaseContainsExit.cpp.

90 {
91 return SwitchCaseContainsExit{/*conditionalExits=*/false}.visitStatement(stmt);
92}

◆ UpdateVariableRefKind()

bool SkSL::Analysis::UpdateVariableRefKind ( Expression expr,
VariableRefKind  kind,
ErrorReporter errors = nullptr 
)

Updates the refKind field of the VariableReference at the top level of expr. If expr can be assigned to (IsAssignable), true is returned and no errors are reported. If not, false is returned. and an error is reported if errors is non-null.

Definition at line 512 of file SkSLAnalysis.cpp.

514 {
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}
std::string description() const final

◆ ValidateIndexingForES2()

void SkSL::Analysis::ValidateIndexingForES2 ( const ProgramElement pe,
ErrorReporter errors 
)

Ensures that any index-expressions inside of for-loops qualify as 'constant-index-expressions' as defined by GLSL 1.0, Appendix A, Section 5. A constant-index-expression is:

  • A constant-expression
  • Loop indices (as defined in Appendix A, Section 4)
  • Expressions composed of both of the above

Definition at line 161 of file SkSLIsConstantExpression.cpp.

161 {
162 ES2IndexingVisitor visitor(errors);
163 visitor.visitProgramElement(pe);
164}