1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkSafeMath.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramUsage.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramVisitor.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionCall.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLInterfaceBlock.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifiersDeclaration.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
36*c8dee2aaSAndroid Build Coastguard Worker
37*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
38*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
39*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
40*c8dee2aaSAndroid Build Coastguard Worker #include <string>
41*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
46*c8dee2aaSAndroid Build Coastguard Worker namespace {
47*c8dee2aaSAndroid Build Coastguard Worker
48*c8dee2aaSAndroid Build Coastguard Worker class FinalizationVisitor : public ProgramVisitor {
49*c8dee2aaSAndroid Build Coastguard Worker public:
FinalizationVisitor(const Context & c,const ProgramUsage & u)50*c8dee2aaSAndroid Build Coastguard Worker FinalizationVisitor(const Context& c, const ProgramUsage& u) : fContext(c), fUsage(u) {}
51*c8dee2aaSAndroid Build Coastguard Worker
visitProgramElement(const ProgramElement & pe)52*c8dee2aaSAndroid Build Coastguard Worker bool visitProgramElement(const ProgramElement& pe) override {
53*c8dee2aaSAndroid Build Coastguard Worker switch (pe.kind()) {
54*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kGlobalVar:
55*c8dee2aaSAndroid Build Coastguard Worker this->checkGlobalVariableSizeLimit(pe.as<GlobalVarDeclaration>());
56*c8dee2aaSAndroid Build Coastguard Worker break;
57*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kInterfaceBlock:
58*c8dee2aaSAndroid Build Coastguard Worker // TODO(skia:13664): Enforce duplicate checks universally. This is currently not
59*c8dee2aaSAndroid Build Coastguard Worker // possible without changes to the binding index assignment logic in graphite.
60*c8dee2aaSAndroid Build Coastguard Worker this->checkBindUniqueness(pe.as<InterfaceBlock>());
61*c8dee2aaSAndroid Build Coastguard Worker break;
62*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kFunction:
63*c8dee2aaSAndroid Build Coastguard Worker this->checkOutParamsAreAssigned(pe.as<FunctionDefinition>());
64*c8dee2aaSAndroid Build Coastguard Worker break;
65*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kModifiers:
66*c8dee2aaSAndroid Build Coastguard Worker this->checkWorkgroupLocalSize(pe.as<ModifiersDeclaration>());
67*c8dee2aaSAndroid Build Coastguard Worker break;
68*c8dee2aaSAndroid Build Coastguard Worker default:
69*c8dee2aaSAndroid Build Coastguard Worker break;
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitProgramElement(pe);
72*c8dee2aaSAndroid Build Coastguard Worker }
73*c8dee2aaSAndroid Build Coastguard Worker
checkGlobalVariableSizeLimit(const GlobalVarDeclaration & globalDecl)74*c8dee2aaSAndroid Build Coastguard Worker void checkGlobalVariableSizeLimit(const GlobalVarDeclaration& globalDecl) {
75*c8dee2aaSAndroid Build Coastguard Worker if (!ProgramConfig::IsRuntimeEffect(fContext.fConfig->fKind)) {
76*c8dee2aaSAndroid Build Coastguard Worker return;
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker const VarDeclaration& decl = globalDecl.varDeclaration();
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker size_t prevSlotsUsed = fGlobalSlotsUsed;
81*c8dee2aaSAndroid Build Coastguard Worker fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var()->type().slotCount());
82*c8dee2aaSAndroid Build Coastguard Worker // To avoid overzealous error reporting, only trigger the error at the first place where the
83*c8dee2aaSAndroid Build Coastguard Worker // global limit is exceeded.
84*c8dee2aaSAndroid Build Coastguard Worker if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
85*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(decl.fPosition,
86*c8dee2aaSAndroid Build Coastguard Worker "global variable '" + std::string(decl.var()->name()) +
87*c8dee2aaSAndroid Build Coastguard Worker "' exceeds the size limit");
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker
checkBindUniqueness(const InterfaceBlock & block)91*c8dee2aaSAndroid Build Coastguard Worker void checkBindUniqueness(const InterfaceBlock& block) {
92*c8dee2aaSAndroid Build Coastguard Worker const Variable* var = block.var();
93*c8dee2aaSAndroid Build Coastguard Worker int32_t set = var->layout().fSet;
94*c8dee2aaSAndroid Build Coastguard Worker int32_t binding = var->layout().fBinding;
95*c8dee2aaSAndroid Build Coastguard Worker if (binding != -1) {
96*c8dee2aaSAndroid Build Coastguard Worker // TODO(skia:13664): This should map a `set` value of -1 to the default settings value
97*c8dee2aaSAndroid Build Coastguard Worker // used by codegen backends to prevent duplicates that may arise from the effective
98*c8dee2aaSAndroid Build Coastguard Worker // default set value.
99*c8dee2aaSAndroid Build Coastguard Worker uint64_t key = ((uint64_t)set << 32) + binding;
100*c8dee2aaSAndroid Build Coastguard Worker if (!fBindings.contains(key)) {
101*c8dee2aaSAndroid Build Coastguard Worker fBindings.add(key);
102*c8dee2aaSAndroid Build Coastguard Worker } else {
103*c8dee2aaSAndroid Build Coastguard Worker if (set != -1) {
104*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(block.fPosition,
105*c8dee2aaSAndroid Build Coastguard Worker "layout(set=" + std::to_string(set) +
106*c8dee2aaSAndroid Build Coastguard Worker ", binding=" + std::to_string(binding) +
107*c8dee2aaSAndroid Build Coastguard Worker ") has already been defined");
108*c8dee2aaSAndroid Build Coastguard Worker } else {
109*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(block.fPosition,
110*c8dee2aaSAndroid Build Coastguard Worker "layout(binding=" + std::to_string(binding) +
111*c8dee2aaSAndroid Build Coastguard Worker ") has already been defined");
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker }
116*c8dee2aaSAndroid Build Coastguard Worker
checkOutParamsAreAssigned(const FunctionDefinition & funcDef)117*c8dee2aaSAndroid Build Coastguard Worker void checkOutParamsAreAssigned(const FunctionDefinition& funcDef) {
118*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration& funcDecl = funcDef.declaration();
119*c8dee2aaSAndroid Build Coastguard Worker
120*c8dee2aaSAndroid Build Coastguard Worker // Searches for `out` parameters that are not written to. According to the GLSL spec,
121*c8dee2aaSAndroid Build Coastguard Worker // the value of an out-param that's never assigned to is unspecified, so report it.
122*c8dee2aaSAndroid Build Coastguard Worker for (const Variable* param : funcDecl.parameters()) {
123*c8dee2aaSAndroid Build Coastguard Worker const ModifierFlags paramInout = param->modifierFlags() & (ModifierFlag::kIn |
124*c8dee2aaSAndroid Build Coastguard Worker ModifierFlag::kOut);
125*c8dee2aaSAndroid Build Coastguard Worker if (paramInout == ModifierFlag::kOut) {
126*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage::VariableCounts counts = fUsage.get(*param);
127*c8dee2aaSAndroid Build Coastguard Worker if (counts.fWrite <= 0) {
128*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(param->fPosition,
129*c8dee2aaSAndroid Build Coastguard Worker "function '" + std::string(funcDecl.name()) +
130*c8dee2aaSAndroid Build Coastguard Worker "' never assigns a value to out parameter '" +
131*c8dee2aaSAndroid Build Coastguard Worker std::string(param->name()) + "'");
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker
checkWorkgroupLocalSize(const ModifiersDeclaration & d)137*c8dee2aaSAndroid Build Coastguard Worker void checkWorkgroupLocalSize(const ModifiersDeclaration& d) {
138*c8dee2aaSAndroid Build Coastguard Worker if (d.layout().fLocalSizeX >= 0) {
139*c8dee2aaSAndroid Build Coastguard Worker if (fLocalSizeX >= 0) {
140*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(d.fPosition, "'local_size_x' was specified more than once");
141*c8dee2aaSAndroid Build Coastguard Worker } else {
142*c8dee2aaSAndroid Build Coastguard Worker fLocalSizeX = d.layout().fLocalSizeX;
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker if (d.layout().fLocalSizeY >= 0) {
146*c8dee2aaSAndroid Build Coastguard Worker if (fLocalSizeY >= 0) {
147*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(d.fPosition, "'local_size_y' was specified more than once");
148*c8dee2aaSAndroid Build Coastguard Worker } else {
149*c8dee2aaSAndroid Build Coastguard Worker fLocalSizeY = d.layout().fLocalSizeY;
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker if (d.layout().fLocalSizeZ >= 0) {
153*c8dee2aaSAndroid Build Coastguard Worker if (fLocalSizeZ >= 0) {
154*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(d.fPosition, "'local_size_z' was specified more than once");
155*c8dee2aaSAndroid Build Coastguard Worker } else {
156*c8dee2aaSAndroid Build Coastguard Worker fLocalSizeZ = d.layout().fLocalSizeZ;
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker
visitExpression(const Expression & expr)161*c8dee2aaSAndroid Build Coastguard Worker bool visitExpression(const Expression& expr) override {
162*c8dee2aaSAndroid Build Coastguard Worker switch (expr.kind()) {
163*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFunctionCall: {
164*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
165*c8dee2aaSAndroid Build Coastguard Worker if (!decl.isBuiltin() && !decl.definition()) {
166*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(expr.fPosition, "function '" + decl.description() +
167*c8dee2aaSAndroid Build Coastguard Worker "' is not defined");
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker break;
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kFunctionReference:
172*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kMethodReference:
173*c8dee2aaSAndroid Build Coastguard Worker case Expression::Kind::kTypeReference:
174*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
175*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(expr.fPosition, "invalid expression");
176*c8dee2aaSAndroid Build Coastguard Worker break;
177*c8dee2aaSAndroid Build Coastguard Worker default:
178*c8dee2aaSAndroid Build Coastguard Worker if (expr.type().matches(*fContext.fTypes.fInvalid)) {
179*c8dee2aaSAndroid Build Coastguard Worker fContext.fErrors->error(expr.fPosition, "invalid expression");
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker break;
182*c8dee2aaSAndroid Build Coastguard Worker }
183*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitExpression(expr);
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker
definesLocalSize() const186*c8dee2aaSAndroid Build Coastguard Worker bool definesLocalSize() const {
187*c8dee2aaSAndroid Build Coastguard Worker return fLocalSizeX >= 0 || fLocalSizeY >= 0 || fLocalSizeZ >= 0;
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker private:
191*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramVisitor;
192*c8dee2aaSAndroid Build Coastguard Worker size_t fGlobalSlotsUsed = 0;
193*c8dee2aaSAndroid Build Coastguard Worker const Context& fContext;
194*c8dee2aaSAndroid Build Coastguard Worker const ProgramUsage& fUsage;
195*c8dee2aaSAndroid Build Coastguard Worker // we pack the set/binding pair into a single 64 bit int
196*c8dee2aaSAndroid Build Coastguard Worker THashSet<uint64_t> fBindings;
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker // Compute programs must at least specify the X dimension of the local size. The other
199*c8dee2aaSAndroid Build Coastguard Worker // dimensions have a default value of "1".
200*c8dee2aaSAndroid Build Coastguard Worker int fLocalSizeX = -1;
201*c8dee2aaSAndroid Build Coastguard Worker int fLocalSizeY = -1;
202*c8dee2aaSAndroid Build Coastguard Worker int fLocalSizeZ = -1;
203*c8dee2aaSAndroid Build Coastguard Worker };
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker } // namespace
206*c8dee2aaSAndroid Build Coastguard Worker
DoFinalizationChecks(const Program & program)207*c8dee2aaSAndroid Build Coastguard Worker void Analysis::DoFinalizationChecks(const Program& program) {
208*c8dee2aaSAndroid Build Coastguard Worker // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
209*c8dee2aaSAndroid Build Coastguard Worker FinalizationVisitor visitor{*program.fContext, *program.usage()};
210*c8dee2aaSAndroid Build Coastguard Worker for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
211*c8dee2aaSAndroid Build Coastguard Worker visitor.visitProgramElement(*element);
212*c8dee2aaSAndroid Build Coastguard Worker }
213*c8dee2aaSAndroid Build Coastguard Worker if (ProgramConfig::IsCompute(program.fConfig->fKind) && !visitor.definesLocalSize()) {
214*c8dee2aaSAndroid Build Coastguard Worker program.fContext->fErrors->error(Position(),
215*c8dee2aaSAndroid Build Coastguard Worker "compute programs must specify a workgroup size");
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker
219*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
220