xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLVarDeclarations.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/sksl/ir/SkSLVarDeclarations.h"
9 
10 #include "include/core/SkSpan.h"
11 #include "include/private/base/SkTo.h"
12 #include "src/base/SkEnumBitMask.h"
13 #include "src/sksl/SkSLAnalysis.h"
14 #include "src/sksl/SkSLBuiltinTypes.h"
15 #include "src/sksl/SkSLCompiler.h"
16 #include "src/sksl/SkSLContext.h"
17 #include "src/sksl/SkSLErrorReporter.h"
18 #include "src/sksl/SkSLPosition.h"
19 #include "src/sksl/SkSLProgramKind.h"
20 #include "src/sksl/SkSLProgramSettings.h"
21 #include "src/sksl/SkSLString.h"
22 #include "src/sksl/ir/SkSLLayout.h"
23 #include "src/sksl/ir/SkSLModifierFlags.h"
24 #include "src/sksl/ir/SkSLModifiers.h"
25 #include "src/sksl/ir/SkSLSymbolTable.h"
26 #include "src/sksl/ir/SkSLType.h"
27 
28 #include <string_view>
29 
30 namespace SkSL {
31 namespace {
32 
check_valid_uniform_type(Position pos,const Type * t,const Context & context,bool topLevel=true)33 static bool check_valid_uniform_type(Position pos,
34                                      const Type* t,
35                                      const Context& context,
36                                      bool topLevel = true) {
37     auto reportError = [&]() {
38         context.fErrors->error(pos, "variables of type '" + t->displayName() +
39                                     "' may not be uniform");
40     };
41 
42     // In Runtime Effects we only allow a restricted set of types: shader, blender, colorFilter,
43     // 32-bit signed integers, 16-bit and 32-bit floats, and their vector/square-matrix composites.
44     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
45         // `shader`, `blender`, `colorFilter`
46         if (t->isEffectChild()) {
47             return true;
48         }
49 
50         // `int`, `int2`, `int3`, `int4`
51         const Type& ct = t->componentType();
52         if (ct.isSigned() && ct.bitWidth() == 32 && (t->isScalar() || t->isVector())) {
53             return true;
54         }
55 
56         // `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`
57         // `half`, `half2`, `half3`, `half4`, `half2x2`, `half3x3`, `half4x4`
58         if (ct.isFloat() &&
59             (t->isScalar() || t->isVector() || (t->isMatrix() && t->rows() == t->columns()))) {
60             return true;
61         }
62 
63         // Everything else is an error.
64         reportError();
65         return false;
66     }
67 
68     Position errorPosition = {};
69     if (!t->isAllowedInUniform(&errorPosition)) {
70         reportError();
71         if (errorPosition.valid()) {
72             context.fErrors->error(errorPosition, "caused by:");
73         }
74         return false;
75     }
76 
77     return true;
78 }
79 
80 }  // namespace
81 
description() const82 std::string VarDeclaration::description() const {
83     std::string result = this->var()->layout().paddedDescription() +
84                          this->var()->modifierFlags().paddedDescription() +
85                          this->baseType().description() + ' ' + std::string(this->var()->name());
86     if (this->arraySize() > 0) {
87         String::appendf(&result, "[%d]", this->arraySize());
88     }
89     if (this->value()) {
90         result += " = " + this->value()->description();
91     }
92     result += ";";
93     return result;
94 }
95 
ErrorCheck(const Context & context,Position pos,Position modifiersPosition,const Layout & layout,ModifierFlags modifierFlags,const Type * type,const Type * baseType,Variable::Storage storage)96 void VarDeclaration::ErrorCheck(const Context& context,
97                                 Position pos,
98                                 Position modifiersPosition,
99                                 const Layout& layout,
100                                 ModifierFlags modifierFlags,
101                                 const Type* type,
102                                 const Type* baseType,
103                                 Variable::Storage storage) {
104     SkASSERT(type->isArray() ? baseType->matches(type->componentType())
105                              : baseType->matches(*type));
106 
107     if (baseType->componentType().isOpaque() && !baseType->componentType().isAtomic() &&
108         storage != Variable::Storage::kGlobal) {
109         context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
110                                     "' must be global");
111     }
112     if ((modifierFlags & ModifierFlag::kIn) && baseType->isMatrix()) {
113         context.fErrors->error(pos, "'in' variables may not have matrix type");
114     }
115     if ((modifierFlags & ModifierFlag::kIn) && type->isUnsizedArray()) {
116         context.fErrors->error(pos, "'in' variables may not have unsized array type");
117     }
118     if ((modifierFlags & ModifierFlag::kOut) && type->isUnsizedArray()) {
119         context.fErrors->error(pos, "'out' variables may not have unsized array type");
120     }
121     if ((modifierFlags & ModifierFlag::kIn) && modifierFlags.isUniform()) {
122         context.fErrors->error(pos, "'in uniform' variables not permitted");
123     }
124     if (modifierFlags.isReadOnly() && modifierFlags.isWriteOnly()) {
125         context.fErrors->error(pos, "'readonly' and 'writeonly' qualifiers cannot be combined");
126     }
127     if (modifierFlags.isUniform() && modifierFlags.isBuffer()) {
128         context.fErrors->error(pos, "'uniform buffer' variables not permitted");
129     }
130     if (modifierFlags.isWorkgroup() && (modifierFlags & (ModifierFlag::kIn |
131                                                          ModifierFlag::kOut))) {
132         context.fErrors->error(pos, "in / out variables may not be declared workgroup");
133     }
134     if (modifierFlags.isUniform()) {
135         check_valid_uniform_type(pos, baseType, context);
136     }
137     if (baseType->isEffectChild() && !modifierFlags.isUniform()) {
138         context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
139                                     "' must be uniform");
140     }
141     if (baseType->isEffectChild() && context.fConfig->fKind == ProgramKind::kMeshVertex) {
142         context.fErrors->error(pos, "effects are not permitted in mesh vertex shaders");
143     }
144     if (baseType->isOrContainsAtomic()) {
145         // An atomic variable (or a struct or an array that contains an atomic member) must be
146         // either:
147         //   a. Declared as a workgroup-shared variable, OR
148         //   b. Declared as the member of writable storage buffer block (i.e. has no readonly
149         //   restriction).
150         //
151         // The checks below will enforce these two rules on all declarations. If the variable is not
152         // declared with the workgroup modifier, then it must be declared in the interface block
153         // storage. If this is the declaration for an interface block that contains an atomic
154         // member, then it must have the `buffer` modifier and no `readonly` modifier.
155         bool isBlockMember = (storage == Variable::Storage::kInterfaceBlock);
156         bool isWritableStorageBuffer = modifierFlags.isBuffer() && !modifierFlags.isReadOnly();
157 
158         if (!modifierFlags.isWorkgroup() &&
159             !(baseType->isInterfaceBlock() ? isWritableStorageBuffer : isBlockMember)) {
160             context.fErrors->error(pos, "atomics are only permitted in workgroup variables and "
161                                         "writable storage blocks");
162         }
163     }
164     if (layout.fFlags & LayoutFlag::kColor) {
165         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
166             context.fErrors->error(pos, "'layout(color)' is only permitted in runtime effects");
167         }
168         if (!modifierFlags.isUniform()) {
169             context.fErrors->error(pos, "'layout(color)' is only permitted on 'uniform' variables");
170         }
171         auto validColorXformType = [](const Type& t) {
172             return t.isVector() && t.componentType().isFloat() &&
173                    (t.columns() == 3 || t.columns() == 4);
174         };
175         if (!validColorXformType(*baseType)) {
176             context.fErrors->error(pos, "'layout(color)' is not permitted on variables of type '" +
177                                         baseType->displayName() + "'");
178         }
179     }
180 
181     ModifierFlags permitted = ModifierFlag::kConst | ModifierFlag::kHighp | ModifierFlag::kMediump |
182                               ModifierFlag::kLowp;
183     if (storage == Variable::Storage::kGlobal) {
184         // Uniforms are allowed in all programs
185         permitted |= ModifierFlag::kUniform;
186 
187         // No other modifiers are allowed in runtime effects.
188         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
189             if (baseType->isInterfaceBlock()) {
190                 // Interface blocks allow `buffer`.
191                 permitted |= ModifierFlag::kBuffer;
192 
193                 if (modifierFlags.isBuffer()) {
194                     // Only storage blocks allow `readonly` and `writeonly`.
195                     // (`readonly` and `writeonly` textures are converted to separate types via
196                     // applyAccessQualifiers.)
197                     permitted |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
198                 }
199 
200                 // It is an error for an unsized array to appear anywhere but the last member of a
201                 // "buffer" block.
202                 const auto& fields = baseType->fields();
203                 const int illegalRangeEnd = SkToInt(fields.size()) -
204                                             (modifierFlags.isBuffer() ? 1 : 0);
205                 for (int i = 0; i < illegalRangeEnd; ++i) {
206                     if (fields[i].fType->isUnsizedArray()) {
207                         context.fErrors->error(
208                                 fields[i].fPosition,
209                                 "unsized array must be the last member of a storage block");
210                     }
211                 }
212             }
213 
214             if (!baseType->isOpaque()) {
215                 // Only non-opaque types allow `in` and `out`.
216                 permitted |= ModifierFlag::kIn | ModifierFlag::kOut;
217             }
218             if (ProgramConfig::IsFragment(context.fConfig->fKind) && baseType->isStruct() &&
219                 !baseType->isInterfaceBlock()) {
220                 // Only structs in fragment shaders allow `pixel_local`.
221                 permitted |= ModifierFlag::kPixelLocal;
222             }
223             if (ProgramConfig::IsCompute(context.fConfig->fKind)) {
224                 // Only compute shaders allow `workgroup`.
225                 if (!baseType->isOpaque() || baseType->isAtomic()) {
226                     permitted |= ModifierFlag::kWorkgroup;
227                 }
228             } else {
229                 // Only vertex/fragment shaders allow `flat` and `noperspective`.
230                 permitted |= ModifierFlag::kFlat | ModifierFlag::kNoPerspective;
231             }
232         }
233     }
234 
235     LayoutFlags permittedLayoutFlags = LayoutFlag::kAll;
236 
237     // Pixel format modifiers are required on storage textures, and forbidden on other types.
238     if (baseType->isStorageTexture()) {
239         if (!(layout.fFlags & LayoutFlag::kAllPixelFormats)) {
240             context.fErrors->error(pos, "storage textures must declare a pixel format");
241         }
242     } else {
243         permittedLayoutFlags &= ~LayoutFlag::kAllPixelFormats;
244     }
245 
246     // The `texture` and `sampler` modifiers can be present respectively on a texture and sampler or
247     // simultaneously on a combined image-sampler but they are not permitted on any other type.
248     switch (baseType->typeKind()) {
249         case Type::TypeKind::kSampler:
250             // Both texture and sampler flags are permitted
251             break;
252         case Type::TypeKind::kTexture:
253             permittedLayoutFlags &= ~LayoutFlag::kSampler;
254             break;
255         case Type::TypeKind::kSeparateSampler:
256             permittedLayoutFlags &= ~LayoutFlag::kTexture;
257             break;
258         default:
259             permittedLayoutFlags &= ~(LayoutFlag::kTexture | LayoutFlag::kSampler);
260             break;
261     }
262 
263     // We don't allow 'binding' or 'set' on normal uniform variables, only on textures, samplers,
264     // and interface blocks (holding uniform variables). They're also only allowed at global scope,
265     // not on interface block fields (or locals/parameters).
266     bool permitBindingAndSet = baseType->typeKind() == Type::TypeKind::kSampler ||
267                                baseType->typeKind() == Type::TypeKind::kSeparateSampler ||
268                                baseType->typeKind() == Type::TypeKind::kTexture ||
269                                baseType->isInterfaceBlock();
270     if (storage != Variable::Storage::kGlobal || (modifierFlags.isUniform() &&
271                                                   !permitBindingAndSet)) {
272         permittedLayoutFlags &= ~LayoutFlag::kBinding;
273         permittedLayoutFlags &= ~LayoutFlag::kSet;
274         permittedLayoutFlags &= ~LayoutFlag::kAllBackends;
275     }
276     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
277         // Disallow all layout flags except 'color' in runtime effects
278         permittedLayoutFlags &= LayoutFlag::kColor;
279     }
280 
281     // The `push_constant` flag isn't allowed on in-variables, out-variables, bindings or sets.
282     if ((layout.fFlags & (LayoutFlag::kSet | LayoutFlag::kBinding)) ||
283         (modifierFlags & (ModifierFlag::kIn | ModifierFlag::kOut))) {
284         permittedLayoutFlags &= ~LayoutFlag::kPushConstant;
285     }
286     // The `builtin` layout flag is only allowed in modules.
287     if (!context.fConfig->isBuiltinCode()) {
288         permittedLayoutFlags &= ~LayoutFlag::kBuiltin;
289     }
290 
291     modifierFlags.checkPermittedFlags(context, modifiersPosition, permitted);
292     layout.checkPermittedLayout(context, modifiersPosition, permittedLayoutFlags);
293 }
294 
ErrorCheckAndCoerce(const Context & context,const Variable & var,const Type * baseType,std::unique_ptr<Expression> & value)295 bool VarDeclaration::ErrorCheckAndCoerce(const Context& context,
296                                          const Variable& var,
297                                          const Type* baseType,
298                                          std::unique_ptr<Expression>& value) {
299     if (baseType->matches(*context.fTypes.fInvalid)) {
300         context.fErrors->error(var.fPosition, "invalid type");
301         return false;
302     }
303     if (baseType->isVoid()) {
304         context.fErrors->error(var.fPosition, "variables of type 'void' are not allowed");
305         return false;
306     }
307 
308     ErrorCheck(context, var.fPosition, var.modifiersPosition(), var.layout(), var.modifierFlags(),
309                &var.type(), baseType, var.storage());
310     if (value) {
311         if (var.type().isOpaque() || var.type().isOrContainsAtomic()) {
312             context.fErrors->error(value->fPosition, "opaque type '" + var.type().displayName() +
313                                                      "' cannot use initializer expressions");
314             return false;
315         }
316         if (var.modifierFlags() & ModifierFlag::kIn) {
317             context.fErrors->error(value->fPosition,
318                                    "'in' variables cannot use initializer expressions");
319             return false;
320         }
321         if (var.modifierFlags().isUniform()) {
322             context.fErrors->error(value->fPosition,
323                                    "'uniform' variables cannot use initializer expressions");
324             return false;
325         }
326         if (var.storage() == Variable::Storage::kInterfaceBlock) {
327             context.fErrors->error(value->fPosition,
328                                    "initializers are not permitted on interface block fields");
329             return false;
330         }
331         if (context.fConfig->strictES2Mode() && var.type().isOrContainsArray()) {
332             context.fErrors->error(value->fPosition, "initializers are not permitted on arrays "
333                                                      "(or structs containing arrays)");
334             return false;
335         }
336         value = var.type().coerceExpression(std::move(value), context);
337         if (!value) {
338             return false;
339         }
340     }
341     if (var.modifierFlags().isConst()) {
342         if (!value) {
343             context.fErrors->error(var.fPosition, "'const' variables must be initialized");
344             return false;
345         }
346         if (!Analysis::IsConstantExpression(*value)) {
347             context.fErrors->error(value->fPosition,
348                                    "'const' variable initializer must be a constant expression");
349             return false;
350         }
351     }
352     if (var.storage() == Variable::Storage::kInterfaceBlock) {
353         if (var.type().isOpaque()) {
354             context.fErrors->error(var.fPosition, "opaque type '" + var.type().displayName() +
355                                                   "' is not permitted in an interface block");
356             return false;
357         }
358     }
359     if (var.storage() == Variable::Storage::kGlobal) {
360         if (value && !Analysis::IsConstantExpression(*value)) {
361             context.fErrors->error(value->fPosition,
362                                    "global variable initializer must be a constant expression");
363             return false;
364         }
365     }
366     return true;
367 }
368 
Convert(const Context & context,Position overallPos,const Modifiers & modifiers,const Type & type,Position namePos,std::string_view name,VariableStorage storage,std::unique_ptr<Expression> value)369 std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
370                                                         Position overallPos,
371                                                         const Modifiers& modifiers,
372                                                         const Type& type,
373                                                         Position namePos,
374                                                         std::string_view name,
375                                                         VariableStorage storage,
376                                                         std::unique_ptr<Expression> value) {
377     // Parameter declaration-statements do not exist in the grammar (unlike, say, K&R C).
378     SkASSERT(storage != VariableStorage::kParameter);
379 
380     std::unique_ptr<Variable> var = Variable::Convert(context,
381                                                       overallPos,
382                                                       modifiers.fPosition,
383                                                       modifiers.fLayout,
384                                                       modifiers.fFlags,
385                                                       &type,
386                                                       namePos,
387                                                       name,
388                                                       storage);
389     if (!var) {
390         return nullptr;
391     }
392     return VarDeclaration::Convert(context, std::move(var), std::move(value));
393 }
394 
Convert(const Context & context,std::unique_ptr<Variable> var,std::unique_ptr<Expression> value)395 std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
396                                                         std::unique_ptr<Variable> var,
397                                                         std::unique_ptr<Expression> value) {
398     const Type* baseType = &var->type();
399     int arraySize = 0;
400     if (baseType->isArray()) {
401         arraySize = baseType->columns();
402         baseType = &baseType->componentType();
403     }
404     if (!ErrorCheckAndCoerce(context, *var, baseType, value)) {
405         return nullptr;
406     }
407     std::unique_ptr<VarDeclaration> varDecl = VarDeclaration::Make(context, var.get(), baseType,
408                                                                    arraySize, std::move(value));
409     if (!varDecl) {
410         return nullptr;
411     }
412 
413     if (var->storage() == Variable::Storage::kGlobal ||
414         var->storage() == Variable::Storage::kInterfaceBlock) {
415         // Check if this globally-scoped variable name overlaps an existing symbol name.
416         if (context.fSymbolTable->find(var->name())) {
417             context.fErrors->error(var->fPosition,
418                                    "symbol '" + std::string(var->name()) + "' was already defined");
419             return nullptr;
420         }
421 
422         // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
423         if (var->name() == Compiler::RTADJUST_NAME) {
424             if (!var->type().matches(*context.fTypes.fFloat4)) {
425                 context.fErrors->error(var->fPosition, "sk_RTAdjust must have type 'float4'");
426                 return nullptr;
427             }
428         }
429     }
430 
431     context.fSymbolTable->add(context, std::move(var));
432     return varDecl;
433 }
434 
Make(const Context & context,Variable * var,const Type * baseType,int arraySize,std::unique_ptr<Expression> value)435 std::unique_ptr<VarDeclaration> VarDeclaration::Make(const Context& context,
436                                                      Variable* var,
437                                                      const Type* baseType,
438                                                      int arraySize,
439                                                      std::unique_ptr<Expression> value) {
440     SkASSERT(!baseType->isArray());
441     // function parameters cannot have variable declarations
442     SkASSERT(var->storage() != Variable::Storage::kParameter);
443     // 'const' variables must be initialized
444     SkASSERT(!var->modifierFlags().isConst() || value);
445     // 'const' variable initializer must be a constant expression
446     SkASSERT(!var->modifierFlags().isConst() || Analysis::IsConstantExpression(*value));
447     // global variable initializer must be a constant expression
448     SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
449                !Analysis::IsConstantExpression(*value)));
450     // opaque type not permitted on an interface block
451     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
452     // initializers are not permitted on interface block fields
453     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
454     // opaque type cannot use initializer expressions
455     SkASSERT(!(value && var->type().isOpaque()));
456     // 'in' variables cannot use initializer expressions
457     SkASSERT(!(value && (var->modifierFlags() & ModifierFlag::kIn)));
458     // 'uniform' variables cannot use initializer expressions
459     SkASSERT(!(value && var->modifierFlags().isUniform()));
460     // in strict-ES2 mode, is-or-contains-array types cannot use initializer expressions
461     SkASSERT(!(value && var->type().isOrContainsArray() && context.fConfig->strictES2Mode()));
462 
463     auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
464     var->setVarDeclaration(result.get());
465     return result;
466 }
467 
468 }  // namespace SkSL
469