blob: e46411268683497e9629a69bceb5edbdc8f1aa06 [file] [log] [blame]
John Stiles2dfbf992021-10-06 20:06:41 -04001/*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/private/SkSLStatement.h"
9#include "src/sksl/SkSLAnalysis.h"
10#include "src/sksl/analysis/SkSLProgramVisitor.h"
11#include "src/sksl/ir/SkSLDoStatement.h"
12#include "src/sksl/ir/SkSLForStatement.h"
13#include "src/sksl/ir/SkSLFunctionDeclaration.h"
14#include "src/sksl/ir/SkSLIfStatement.h"
15#include "src/sksl/ir/SkSLSwitchStatement.h"
16
17namespace SkSL {
18
19// Checks for ES2 constant-expression rules, and (optionally) constant-index-expression rules
20// (if loopIndices is non-nullptr)
21class ConstantExpressionVisitor : public ProgramVisitor {
22public:
23 ConstantExpressionVisitor(const std::set<const Variable*>* loopIndices)
24 : fLoopIndices(loopIndices) {}
25
26 bool visitExpression(const Expression& e) override {
27 // A constant-(index)-expression is one of...
28 switch (e.kind()) {
29 // ... a literal value
30 case Expression::Kind::kLiteral:
31 return false;
32
33 // ... settings can appear in fragment processors; they will resolve when compiled
34 case Expression::Kind::kSetting:
35 return false;
36
37 // ... a global or local variable qualified as 'const', excluding function parameters.
38 // ... loop indices as defined in section 4. [constant-index-expression]
39 case Expression::Kind::kVariableReference: {
40 const Variable* v = e.as<VariableReference>().variable();
41 if ((v->storage() == Variable::Storage::kGlobal ||
42 v->storage() == Variable::Storage::kLocal) &&
43 (v->modifiers().fFlags & Modifiers::kConst_Flag)) {
44 return false;
45 }
46 return !fLoopIndices || fLoopIndices->find(v) == fLoopIndices->end();
47 }
48
49 // ... expressions composed of both of the above
50 case Expression::Kind::kBinary:
51 case Expression::Kind::kConstructorArray:
52 case Expression::Kind::kConstructorArrayCast:
53 case Expression::Kind::kConstructorCompound:
54 case Expression::Kind::kConstructorCompoundCast:
55 case Expression::Kind::kConstructorDiagonalMatrix:
56 case Expression::Kind::kConstructorMatrixResize:
57 case Expression::Kind::kConstructorScalarCast:
58 case Expression::Kind::kConstructorSplat:
59 case Expression::Kind::kConstructorStruct:
60 case Expression::Kind::kFieldAccess:
61 case Expression::Kind::kIndex:
62 case Expression::Kind::kPrefix:
63 case Expression::Kind::kPostfix:
64 case Expression::Kind::kSwizzle:
65 case Expression::Kind::kTernary:
66 return INHERITED::visitExpression(e);
67
68 // Function calls are completely disallowed in SkSL constant-(index)-expressions.
69 // GLSL does mandate that calling a built-in function where the arguments are all
70 // constant-expressions should result in a constant-expression. SkSL handles this by
71 // optimizing fully-constant function calls into literals in FunctionCall::Make.
72 case Expression::Kind::kFunctionCall:
73 case Expression::Kind::kExternalFunctionCall:
74 case Expression::Kind::kChildCall:
75
76 // These shouldn't appear in a valid program at all, and definitely aren't
77 // constant-(index)-expressions.
78 case Expression::Kind::kPoison:
79 case Expression::Kind::kFunctionReference:
80 case Expression::Kind::kExternalFunctionReference:
81 case Expression::Kind::kMethodReference:
82 case Expression::Kind::kTypeReference:
83 case Expression::Kind::kCodeString:
84 return true;
85
86 default:
87 SkDEBUGFAIL("Unexpected expression type");
88 return true;
89 }
90 }
91
92private:
93 const std::set<const Variable*>* fLoopIndices;
94 using INHERITED = ProgramVisitor;
95};
96
97bool Analysis::IsConstantExpression(const Expression& expr) {
98 return !ConstantExpressionVisitor{/*loopIndices=*/nullptr}.visitExpression(expr);
99}
100
101bool Analysis::IsConstantIndexExpression(const Expression& expr,
102 const std::set<const Variable*>* loopIndices) {
103 return !ConstantExpressionVisitor{loopIndices}.visitExpression(expr);
104}
105
106} // namespace SkSL