1/*
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
107