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/core/SkTypes.h" 9 #include "src/core/SkTHash.h" 10 #include "src/sksl/SkSLAnalysis.h" 11 #include "src/sksl/SkSLErrorReporter.h" 12 #include "src/sksl/SkSLOperator.h" 13 #include "src/sksl/analysis/SkSLProgramVisitor.h" 14 #include "src/sksl/ir/SkSLBinaryExpression.h" 15 #include "src/sksl/ir/SkSLExpression.h" 16 #include "src/sksl/ir/SkSLForStatement.h" 17 #include "src/sksl/ir/SkSLIRNode.h" 18 #include "src/sksl/ir/SkSLIndexExpression.h" 19 #include "src/sksl/ir/SkSLModifierFlags.h" 20 #include "src/sksl/ir/SkSLStatement.h" 21 #include "src/sksl/ir/SkSLVarDeclarations.h" 22 #include "src/sksl/ir/SkSLVariable.h" 23 #include "src/sksl/ir/SkSLVariableReference.h" 24 25 #include <memory> 26 27 using namespace skia_private; 28 29 namespace SkSL { 30 31 class ProgramElement; 32 33 namespace { 34 35 // Checks for ES2 constant-expression rules, and (optionally) constant-index-expression rules 36 // (if loopIndices is non-nullptr) 37 class ConstantExpressionVisitor : public ProgramVisitor { 38 public: ConstantExpressionVisitor(const THashSet<const Variable * > * loopIndices)39 ConstantExpressionVisitor(const THashSet<const Variable*>* loopIndices) 40 : fLoopIndices(loopIndices) {} 41 visitExpression(const Expression & e)42 bool visitExpression(const Expression& e) override { 43 // A constant-(index)-expression is one of... 44 switch (e.kind()) { 45 // ... a literal value 46 case Expression::Kind::kLiteral: 47 return false; 48 49 // ... settings can appear in fragment processors; they will resolve when compiled 50 case Expression::Kind::kSetting: 51 return false; 52 53 // ... a global or local variable qualified as 'const', excluding function parameters. 54 // ... loop indices as defined in section 4. [constant-index-expression] 55 case Expression::Kind::kVariableReference: { 56 const Variable* v = e.as<VariableReference>().variable(); 57 if (v->modifierFlags().isConst() && (v->storage() == Variable::Storage::kGlobal || 58 v->storage() == Variable::Storage::kLocal)) { 59 return false; 60 } 61 return !fLoopIndices || !fLoopIndices->contains(v); 62 } 63 64 // ... not a sequence expression (skia:13311)... 65 case Expression::Kind::kBinary: 66 if (e.as<BinaryExpression>().getOperator().kind() == Operator::Kind::COMMA) { 67 return true; 68 } 69 [[fallthrough]]; 70 71 // ... expressions composed of both of the above 72 case Expression::Kind::kConstructorArray: 73 case Expression::Kind::kConstructorArrayCast: 74 case Expression::Kind::kConstructorCompound: 75 case Expression::Kind::kConstructorCompoundCast: 76 case Expression::Kind::kConstructorDiagonalMatrix: 77 case Expression::Kind::kConstructorMatrixResize: 78 case Expression::Kind::kConstructorScalarCast: 79 case Expression::Kind::kConstructorSplat: 80 case Expression::Kind::kConstructorStruct: 81 case Expression::Kind::kFieldAccess: 82 case Expression::Kind::kIndex: 83 case Expression::Kind::kPrefix: 84 case Expression::Kind::kPostfix: 85 case Expression::Kind::kSwizzle: 86 case Expression::Kind::kTernary: 87 return INHERITED::visitExpression(e); 88 89 // Function calls are completely disallowed in SkSL constant-(index)-expressions. 90 // GLSL does mandate that calling a built-in function where the arguments are all 91 // constant-expressions should result in a constant-expression. SkSL handles this by 92 // optimizing fully-constant function calls into literals in FunctionCall::Make. 93 case Expression::Kind::kFunctionCall: 94 case Expression::Kind::kChildCall: 95 96 // These shouldn't appear in a valid program at all, and definitely aren't 97 // constant-(index)-expressions. 98 case Expression::Kind::kPoison: 99 case Expression::Kind::kFunctionReference: 100 case Expression::Kind::kMethodReference: 101 case Expression::Kind::kTypeReference: 102 case Expression::Kind::kEmpty: 103 return true; 104 105 default: 106 SkDEBUGFAIL("Unexpected expression type"); 107 return true; 108 } 109 } 110 111 private: 112 const THashSet<const Variable*>* fLoopIndices; 113 using INHERITED = ProgramVisitor; 114 }; 115 116 // Visits a function, tracks its loop indices, and verifies that every index-expression in the 117 // function qualifies as a constant-index-expression. 118 class ES2IndexingVisitor : public ProgramVisitor { 119 public: ES2IndexingVisitor(ErrorReporter & errors)120 ES2IndexingVisitor(ErrorReporter& errors) : fErrors(errors) {} 121 visitStatement(const Statement & s)122 bool visitStatement(const Statement& s) override { 123 if (s.is<ForStatement>()) { 124 const ForStatement& f = s.as<ForStatement>(); 125 SkASSERT(f.initializer() && f.initializer()->is<VarDeclaration>()); 126 const Variable* var = f.initializer()->as<VarDeclaration>().var(); 127 SkASSERT(!fLoopIndices.contains(var)); 128 fLoopIndices.add(var); 129 bool result = this->visitStatement(*f.statement()); 130 fLoopIndices.remove(var); 131 return result; 132 } 133 return INHERITED::visitStatement(s); 134 } 135 visitExpression(const Expression & e)136 bool visitExpression(const Expression& e) override { 137 if (e.is<IndexExpression>()) { 138 const IndexExpression& i = e.as<IndexExpression>(); 139 if (ConstantExpressionVisitor{&fLoopIndices}.visitExpression(*i.index())) { 140 fErrors.error(i.fPosition, "index expression must be constant"); 141 return true; 142 } 143 } 144 return INHERITED::visitExpression(e); 145 } 146 147 using ProgramVisitor::visitProgramElement; 148 149 private: 150 ErrorReporter& fErrors; 151 THashSet<const Variable*> fLoopIndices; 152 using INHERITED = ProgramVisitor; 153 }; 154 155 } // namespace 156 IsConstantExpression(const Expression & expr)157bool Analysis::IsConstantExpression(const Expression& expr) { 158 return !ConstantExpressionVisitor{/*loopIndices=*/nullptr}.visitExpression(expr); 159 } 160 ValidateIndexingForES2(const ProgramElement & pe,ErrorReporter & errors)161void Analysis::ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors) { 162 ES2IndexingVisitor visitor(errors); 163 visitor.visitProgramElement(pe); 164 } 165 166 } // namespace SkSL 167