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