• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 "include/private/SkSLDefines.h"
10 #include "include/private/SkSLIRNode.h"
11 #include "include/private/SkSLLayout.h"
12 #include "include/private/SkSLModifiers.h"
13 #include "include/private/SkSLProgramElement.h"
14 #include "include/sksl/SkSLErrorReporter.h"
15 #include "src/base/SkSafeMath.h"
16 #include "src/core/SkTHash.h"
17 #include "src/sksl/SkSLAnalysis.h"
18 #include "src/sksl/SkSLBuiltinTypes.h"
19 #include "src/sksl/SkSLContext.h"
20 #include "src/sksl/SkSLProgramSettings.h"
21 #include "src/sksl/analysis/SkSLProgramUsage.h"
22 #include "src/sksl/analysis/SkSLProgramVisitor.h"
23 #include "src/sksl/ir/SkSLExpression.h"
24 #include "src/sksl/ir/SkSLFunctionCall.h"
25 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
26 #include "src/sksl/ir/SkSLFunctionDefinition.h"
27 #include "src/sksl/ir/SkSLInterfaceBlock.h"
28 #include "src/sksl/ir/SkSLProgram.h"
29 #include "src/sksl/ir/SkSLType.h"
30 #include "src/sksl/ir/SkSLVarDeclarations.h"
31 #include "src/sksl/ir/SkSLVariable.h"
32 
33 #include <cstddef>
34 #include <cstdint>
35 #include <memory>
36 #include <string>
37 #include <vector>
38 
39 namespace SkSL {
40 namespace {
41 
42 class FinalizationVisitor : public ProgramVisitor {
43 public:
FinalizationVisitor(const Context & c,const ProgramUsage & u)44     FinalizationVisitor(const Context& c, const ProgramUsage& u) : fContext(c), fUsage(u) {}
45 
visitProgramElement(const ProgramElement & pe)46     bool visitProgramElement(const ProgramElement& pe) override {
47         switch (pe.kind()) {
48             case ProgramElement::Kind::kGlobalVar:
49                 this->checkGlobalVariableSizeLimit(pe.as<GlobalVarDeclaration>());
50                 break;
51             case ProgramElement::Kind::kInterfaceBlock:
52                 // TODO(skia:13664): Enforce duplicate checks universally. This is currently not
53                 // possible without changes to the binding index assignment logic in graphite.
54                 this->checkBindUniqueness(pe.as<InterfaceBlock>());
55                 break;
56             case ProgramElement::Kind::kFunction:
57                 this->checkOutParamsAreAssigned(pe.as<FunctionDefinition>());
58                 break;
59             default:
60                 break;
61         }
62         return INHERITED::visitProgramElement(pe);
63     }
64 
checkGlobalVariableSizeLimit(const GlobalVarDeclaration & globalDecl)65     void checkGlobalVariableSizeLimit(const GlobalVarDeclaration& globalDecl) {
66         if (!ProgramConfig::IsRuntimeEffect(fContext.fConfig->fKind)) {
67             return;
68         }
69         const VarDeclaration& decl = globalDecl.varDeclaration();
70 
71         size_t prevSlotsUsed = fGlobalSlotsUsed;
72         fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var()->type().slotCount());
73         // To avoid overzealous error reporting, only trigger the error at the first place where the
74         // global limit is exceeded.
75         if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
76             fContext.fErrors->error(decl.fPosition,
77                                     "global variable '" + std::string(decl.var()->name()) +
78                                     "' exceeds the size limit");
79         }
80     }
81 
checkBindUniqueness(const InterfaceBlock & block)82     void checkBindUniqueness(const InterfaceBlock& block) {
83         const Variable* var = block.var();
84         int32_t set = var->modifiers().fLayout.fSet;
85         int32_t binding = var->modifiers().fLayout.fBinding;
86         if (binding != -1) {
87             // TODO(skia:13664): This should map a `set` value of -1 to the default settings value
88             // used by codegen backends to prevent duplicates that may arise from the effective
89             // default set value.
90             uint64_t key = ((uint64_t)set << 32) + binding;
91             if (!fBindings.contains(key)) {
92                 fBindings.add(key);
93             } else {
94                 if (set != -1) {
95                     fContext.fErrors->error(block.fPosition,
96                                             "layout(set=" + std::to_string(set) +
97                                                     ", binding=" + std::to_string(binding) +
98                                                     ") has already been defined");
99                 } else {
100                     fContext.fErrors->error(block.fPosition,
101                                             "layout(binding=" + std::to_string(binding) +
102                                                     ") has already been defined");
103                 }
104             }
105         }
106     }
107 
checkOutParamsAreAssigned(const FunctionDefinition & funcDef)108     void checkOutParamsAreAssigned(const FunctionDefinition& funcDef) {
109         const FunctionDeclaration& funcDecl = funcDef.declaration();
110 
111         // Searches for `out` parameters that are not written to. According to the GLSL spec,
112         // the value of an out-param that's never assigned to is unspecified, so report it.
113         for (const Variable* param : funcDecl.parameters()) {
114             const int paramInout = param->modifiers().fFlags & (Modifiers::Flag::kIn_Flag |
115                                                                 Modifiers::Flag::kOut_Flag);
116             if (paramInout == Modifiers::Flag::kOut_Flag) {
117                 ProgramUsage::VariableCounts counts = fUsage.get(*param);
118                 if (counts.fWrite <= 0) {
119                     fContext.fErrors->error(param->fPosition,
120                                             "function '" + std::string(funcDecl.name()) +
121                                             "' never assigns a value to out parameter '" +
122                                             std::string(param->name()) + "'");
123                 }
124             }
125         }
126     }
127 
visitExpression(const Expression & expr)128     bool visitExpression(const Expression& expr) override {
129         switch (expr.kind()) {
130             case Expression::Kind::kFunctionCall: {
131                 const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
132                 if (!decl.isBuiltin() && !decl.definition()) {
133                     fContext.fErrors->error(expr.fPosition, "function '" + decl.description() +
134                             "' is not defined");
135                 }
136                 break;
137             }
138             case Expression::Kind::kFunctionReference:
139             case Expression::Kind::kMethodReference:
140             case Expression::Kind::kTypeReference:
141                 SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
142                 fContext.fErrors->error(expr.fPosition, "invalid expression");
143                 break;
144             default:
145                 if (expr.type().matches(*fContext.fTypes.fInvalid)) {
146                     fContext.fErrors->error(expr.fPosition, "invalid expression");
147                 }
148                 break;
149         }
150         return INHERITED::visitExpression(expr);
151     }
152 
153 private:
154     using INHERITED = ProgramVisitor;
155     size_t fGlobalSlotsUsed = 0;
156     const Context& fContext;
157     const ProgramUsage& fUsage;
158     // we pack the set/binding pair into a single 64 bit int
159     SkTHashSet<uint64_t> fBindings;
160 };
161 
162 }  // namespace
163 
DoFinalizationChecks(const Program & program)164 void Analysis::DoFinalizationChecks(const Program& program) {
165     // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
166     FinalizationVisitor visitor{*program.fContext, *program.usage()};
167     for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
168         visitor.visitProgramElement(*element);
169     }
170 }
171 
172 }  // namespace SkSL
173