• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/private/SkSLLayout.h"
11 #include "include/private/SkSLModifiers.h"
12 #include "include/private/SkSLProgramKind.h"
13 #include "include/private/SkSLString.h"
14 #include "include/sksl/SkSLErrorReporter.h"
15 #include "include/sksl/SkSLPosition.h"
16 #include "src/sksl/SkSLAnalysis.h"
17 #include "src/sksl/SkSLBuiltinTypes.h"
18 #include "src/sksl/SkSLCompiler.h"
19 #include "src/sksl/SkSLContext.h"
20 #include "src/sksl/SkSLProgramSettings.h"
21 #include "src/sksl/SkSLThreadContext.h"
22 #include "src/sksl/ir/SkSLSymbolTable.h"
23 #include "src/sksl/ir/SkSLType.h"
24 
25 #include <cstddef>
26 #include <string_view>
27 #include <vector>
28 
29 namespace SkSL {
30 namespace {
31 
check_valid_uniform_type(Position pos,const Type * t,const Context & context,bool topLevel=true)32 static bool check_valid_uniform_type(Position pos,
33                                      const Type* t,
34                                      const Context& context,
35                                      bool topLevel = true) {
36     const Type& ct = t->componentType();
37 
38     // In RuntimeEffects we only allow a restricted set of types, namely shader/blender/colorFilter,
39     // 32-bit signed integers, 16-bit and 32-bit floats, and their composites.
40     {
41         bool error = false;
42         if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
43             // `shader`, `blender`, `colorFilter`
44             if (t->isEffectChild()) {
45                 return true;
46             }
47 
48             // `int`, `int2`, `int3`, `int4`
49             if (ct.isSigned() && ct.bitWidth() == 32 && (t->isScalar() || t->isVector())) {
50                 return true;
51             }
52 
53             // `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`
54             // `half`, `half2`, `half3`, `half4`, `half2x2`, `half3x3`, `half4x4`
55             if (ct.isFloat() &&
56                 (t->isScalar() || t->isVector() || (t->isMatrix() && t->rows() == t->columns()))) {
57                 return true;
58             }
59 
60             // Everything else is an error.
61             error = true;
62         }
63 
64         // We disallow boolean uniforms in SkSL since they are not well supported by backend
65         // platforms and drivers. We disallow atomic variables in uniforms as that doesn't map
66         // cleanly to all backends.
67         if (error || (ct.isBoolean() && (t->isScalar() || t->isVector())) || ct.isAtomic()) {
68             context.fErrors->error(
69                     pos, "variables of type '" + t->displayName() + "' may not be uniform");
70             return false;
71         }
72     }
73 
74     // In non-RTE SkSL we allow structs and interface blocks to be uniforms but we must make sure
75     // their fields are allowed.
76     if (t->isStruct()) {
77         for (const Type::Field& field : t->fields()) {
78             if (!check_valid_uniform_type(
79                         field.fPosition, field.fType, context, /*topLevel=*/false)) {
80                 // Emit a "caused by" line only for the top-level uniform type and not for any
81                 // nested structs.
82                 if (topLevel) {
83                     context.fErrors->error(pos, "caused by:");
84                 }
85                 return false;
86             }
87         }
88     }
89     return true;
90 }
91 
92 }  // namespace
93 
clone() const94 std::unique_ptr<Statement> VarDeclaration::clone() const {
95     // Cloning a VarDeclaration is inherently problematic, as we normally expect a one-to-one
96     // mapping between Variables and VarDeclarations and a straightforward clone would violate this
97     // assumption. We could of course theoretically clone the Variable as well, but that would
98     // require additional context and tracking, since for the whole process to work we would also
99     // have to fixup any subsequent VariableReference clones to point to the newly cloned Variables
100     // instead of the originals.
101     //
102     // Since the only reason we ever clone VarDeclarations is to support tests of clone() and we do
103     // not expect to ever need to do so otherwise, a full solution to this issue is unnecessary at
104     // the moment. We instead just keep track of whether a VarDeclaration is a clone so we can
105     // handle its cleanup properly. This allows clone() to work in the simple case that a
106     // VarDeclaration's clone does not outlive the original, which is adequate for testing. Since
107     // this leaves a sharp  edge in place - destroying the original could cause a use-after-free in
108     // some circumstances - we also disable cloning altogether unless the
109     // fAllowVarDeclarationCloneForTesting ProgramSetting is enabled.
110     if (ThreadContext::Settings().fAllowVarDeclarationCloneForTesting) {
111         return std::make_unique<VarDeclaration>(this->var(),
112                                                 &this->baseType(),
113                                                 fArraySize,
114                                                 this->value() ? this->value()->clone() : nullptr,
115                                                 /*isClone=*/true);
116     } else {
117         SkDEBUGFAIL("VarDeclaration::clone() is unsupported");
118         return nullptr;
119     }
120 }
121 
description() const122 std::string VarDeclaration::description() const {
123     std::string result = this->var()->modifiers().description() + this->baseType().description() +
124                          " " + std::string(this->var()->name());
125     if (this->arraySize() > 0) {
126         String::appendf(&result, "[%d]", this->arraySize());
127     }
128     if (this->value()) {
129         result += " = " + this->value()->description();
130     }
131     result += ";";
132     return result;
133 }
134 
ErrorCheck(const Context & context,Position pos,Position modifiersPosition,const Modifiers & modifiers,const Type * type,Variable::Storage storage)135 void VarDeclaration::ErrorCheck(const Context& context,
136                                 Position pos,
137                                 Position modifiersPosition,
138                                 const Modifiers& modifiers,
139                                 const Type* type,
140                                 Variable::Storage storage) {
141     const Type* baseType = type;
142     if (baseType->isArray()) {
143         baseType = &baseType->componentType();
144     }
145     SkASSERT(!baseType->isArray());
146 
147     if (baseType->matches(*context.fTypes.fInvalid)) {
148         context.fErrors->error(pos, "invalid type");
149         return;
150     }
151     if (baseType->isVoid()) {
152         context.fErrors->error(pos, "variables of type 'void' are not allowed");
153         return;
154     }
155 
156     if (baseType->componentType().isOpaque() && !baseType->componentType().isAtomic() &&
157         storage != Variable::Storage::kGlobal) {
158         context.fErrors->error(pos,
159                 "variables of type '" + baseType->displayName() + "' must be global");
160     }
161     if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
162         context.fErrors->error(pos, "'in' variables may not have matrix type");
163     }
164     if ((modifiers.fFlags & Modifiers::kIn_Flag) && type->isUnsizedArray()) {
165         context.fErrors->error(pos, "'in' variables may not have unsized array type");
166     }
167     if ((modifiers.fFlags & Modifiers::kOut_Flag) && type->isUnsizedArray()) {
168         context.fErrors->error(pos, "'out' variables may not have unsized array type");
169     }
170     if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
171         context.fErrors->error(pos, "'in uniform' variables not permitted");
172     }
173     if ((modifiers.fFlags & Modifiers::kReadOnly_Flag) &&
174         (modifiers.fFlags & Modifiers::kWriteOnly_Flag)) {
175         context.fErrors->error(pos, "'readonly' and 'writeonly' qualifiers cannot be combined");
176     }
177     if ((modifiers.fFlags & Modifiers::kUniform_Flag) &&
178         (modifiers.fFlags & Modifiers::kBuffer_Flag)) {
179         context.fErrors->error(pos, "'uniform buffer' variables not permitted");
180     }
181     if ((modifiers.fFlags & Modifiers::kWorkgroup_Flag) &&
182         (modifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kOut_Flag))) {
183         context.fErrors->error(pos, "in / out variables may not be declared workgroup");
184     }
185     if ((modifiers.fFlags & Modifiers::kUniform_Flag)) {
186         check_valid_uniform_type(pos, baseType, context);
187     }
188     if (baseType->isEffectChild() && !(modifiers.fFlags & Modifiers::kUniform_Flag)) {
189         context.fErrors->error(pos,
190                 "variables of type '" + baseType->displayName() + "' must be uniform");
191     }
192     if (baseType->isEffectChild() && (context.fConfig->fKind == ProgramKind::kMeshVertex ||
193                                       context.fConfig->fKind == ProgramKind::kMeshFragment)) {
194         context.fErrors->error(pos, "effects are not permitted in custom mesh shaders");
195     }
196     if (baseType->isOrContainsAtomic()) {
197         // An atomic variable (or a struct or an array that contains an atomic member) must be
198         // either:
199         //   a. Declared as a workgroup-shared variable, OR
200         //   b. Declared as the member of writable storage buffer block (i.e. has no readonly
201         //   restriction).
202         //
203         // The checks below will enforce these two rules on all declarations. If the variable is not
204         // declared with the workgroup modifier, then it must be declared in the interface block
205         // storage. If this is the declaration for an interface block that contains an atomic
206         // member, then it must have the `buffer` modifier and no `readonly` modifier.
207         bool isWorkgroup = modifiers.fFlags & Modifiers::kWorkgroup_Flag;
208         bool isBlockMember = (storage == Variable::Storage::kInterfaceBlock);
209         bool isWritableStorageBuffer = modifiers.fFlags & Modifiers::kBuffer_Flag &&
210                                        !(modifiers.fFlags & Modifiers::kReadOnly_Flag);
211 
212         if (!isWorkgroup &&
213             !(baseType->isInterfaceBlock() ? isWritableStorageBuffer : isBlockMember)) {
214             context.fErrors->error(pos,
215                                    "atomics are only permitted in workgroup variables and writable "
216                                    "storage blocks");
217         }
218     }
219     if (modifiers.fLayout.fFlags & Layout::kColor_Flag) {
220         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
221             context.fErrors->error(pos, "'layout(color)' is only permitted in runtime effects");
222         }
223         if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
224             context.fErrors->error(pos,
225                                    "'layout(color)' is only permitted on 'uniform' variables");
226         }
227         auto validColorXformType = [](const Type& t) {
228             return t.isVector() && t.componentType().isFloat() &&
229                    (t.columns() == 3 || t.columns() == 4);
230         };
231         if (!validColorXformType(*baseType)) {
232             context.fErrors->error(pos,
233                                    "'layout(color)' is not permitted on variables of type '" +
234                                            baseType->displayName() + "'");
235         }
236     }
237 
238     int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
239                     Modifiers::kLowp_Flag;
240     if (storage == Variable::Storage::kGlobal) {
241         // Uniforms are allowed in all programs
242         permitted |= Modifiers::kUniform_Flag;
243 
244         // No other modifiers are allowed in runtime effects.
245         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
246             if (baseType->isInterfaceBlock()) {
247                 // Interface blocks allow `buffer`.
248                 permitted |= Modifiers::kBuffer_Flag;
249 
250                 if (modifiers.fFlags & Modifiers::kBuffer_Flag) {
251                     // Only storage blocks allow `readonly` and `writeonly`.
252                     // (`readonly` and `writeonly` textures are converted to separate types via
253                     // applyAccessQualifiers.)
254                     permitted |= Modifiers::kReadOnly_Flag | Modifiers::kWriteOnly_Flag;
255                 }
256 
257                 // It is an error for an unsized array to appear anywhere but the last member of a
258                 // "buffer" block.
259                 const auto& fields = baseType->fields();
260                 const size_t illegalRangeEnd =
261                         fields.size() - ((modifiers.fFlags & Modifiers::kBuffer_Flag) ? 1 : 0);
262                 for (size_t i = 0; i < illegalRangeEnd; ++i) {
263                     if (fields[i].fType->isUnsizedArray()) {
264                         context.fErrors->error(
265                                 fields[i].fPosition,
266                                 "unsized array must be the last member of a storage block");
267                     }
268                 }
269             }
270 
271             if (!baseType->isOpaque()) {
272                 // Only non-opaque types allow `in` and `out`.
273                 permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag;
274             }
275             if (ProgramConfig::IsCompute(context.fConfig->fKind)) {
276                 // Only compute shaders allow `workgroup`.
277                 if (!baseType->isOpaque() || baseType->isAtomic()) {
278                     permitted |= Modifiers::kWorkgroup_Flag;
279                 }
280             } else {
281                 // Only vertex/fragment shaders allow `flat` and `noperspective`.
282                 permitted |= Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
283             }
284         }
285     }
286 
287     int permittedLayoutFlags = ~0;
288 
289     // The `texture` and `sampler` modifiers can be present respectively on a texture and sampler or
290     // simultaneously on a combined image-sampler but they are not permitted on any other type.
291     switch (baseType->typeKind()) {
292         case Type::TypeKind::kSampler:
293             // Both texture and sampler flags are permitted
294             break;
295         case Type::TypeKind::kTexture:
296             permittedLayoutFlags &= ~Layout::kSampler_Flag;
297             break;
298         case Type::TypeKind::kSeparateSampler:
299             permittedLayoutFlags &= ~Layout::kTexture_Flag;
300             break;
301         default:
302             permittedLayoutFlags &= ~(Layout::kTexture_Flag | Layout::kSampler_Flag);
303             break;
304     }
305 
306     // We don't allow 'binding' or 'set' on normal uniform variables, only on textures, samplers,
307     // and interface blocks (holding uniform variables). They're also only allowed at global scope,
308     // not on interface block fields (or locals/parameters).
309     bool permitBindingAndSet = baseType->typeKind() == Type::TypeKind::kSampler ||
310                                baseType->typeKind() == Type::TypeKind::kSeparateSampler ||
311                                baseType->typeKind() == Type::TypeKind::kTexture ||
312                                baseType->isInterfaceBlock();
313     if (storage != Variable::Storage::kGlobal ||
314         ((modifiers.fFlags & Modifiers::kUniform_Flag) && !permitBindingAndSet)) {
315         permittedLayoutFlags &= ~Layout::kBinding_Flag;
316         permittedLayoutFlags &= ~Layout::kSet_Flag;
317         permittedLayoutFlags &= ~Layout::kSPIRV_Flag;
318         permittedLayoutFlags &= ~Layout::kMetal_Flag;
319         permittedLayoutFlags &= ~Layout::kWGSL_Flag;
320         permittedLayoutFlags &= ~Layout::kGL_Flag;
321     }
322     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
323         // Disallow all layout flags except 'color' in runtime effects
324         permittedLayoutFlags &= Layout::kColor_Flag;
325     }
326     modifiers.checkPermitted(context, modifiersPosition, permitted, permittedLayoutFlags);
327 }
328 
ErrorCheckAndCoerce(const Context & context,const Variable & var,std::unique_ptr<Expression> & value)329 bool VarDeclaration::ErrorCheckAndCoerce(const Context& context, const Variable& var,
330         std::unique_ptr<Expression>& value) {
331     ErrorCheck(context, var.fPosition, var.modifiersPosition(), var.modifiers(), &var.type(),
332             var.storage());
333     if (value) {
334         if (var.type().isOpaque()) {
335             context.fErrors->error(value->fPosition, "opaque type '" + var.type().displayName() +
336                                                      "' cannot use initializer expressions");
337             return false;
338         }
339         if (var.modifiers().fFlags & Modifiers::kIn_Flag) {
340             context.fErrors->error(value->fPosition,
341                                    "'in' variables cannot use initializer expressions");
342             return false;
343         }
344         if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
345             context.fErrors->error(value->fPosition,
346                                    "'uniform' variables cannot use initializer expressions");
347             return false;
348         }
349         if (var.storage() == Variable::Storage::kInterfaceBlock) {
350             context.fErrors->error(value->fPosition,
351                                    "initializers are not permitted on interface block fields");
352             return false;
353         }
354         value = var.type().coerceExpression(std::move(value), context);
355         if (!value) {
356             return false;
357         }
358     }
359     if (var.modifiers().fFlags & Modifiers::kConst_Flag) {
360         if (!value) {
361             context.fErrors->error(var.fPosition, "'const' variables must be initialized");
362             return false;
363         }
364         if (!Analysis::IsConstantExpression(*value)) {
365             context.fErrors->error(value->fPosition,
366                                    "'const' variable initializer must be a constant expression");
367             return false;
368         }
369     }
370     if (var.storage() == Variable::Storage::kInterfaceBlock) {
371         if (var.type().isOpaque()) {
372             context.fErrors->error(var.fPosition, "opaque type '" + var.type().displayName() +
373                     "' is not permitted in an interface block");
374             return false;
375         }
376     }
377     if (var.storage() == Variable::Storage::kGlobal) {
378         if (value && !Analysis::IsConstantExpression(*value)) {
379             context.fErrors->error(value->fPosition,
380                                    "global variable initializer must be a constant expression");
381             return false;
382         }
383     }
384     return true;
385 }
386 
Convert(const Context & context,std::unique_ptr<Variable> var,std::unique_ptr<Expression> value,bool addToSymbolTable)387 std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
388                                                    std::unique_ptr<Variable> var,
389                                                    std::unique_ptr<Expression> value,
390                                                    bool addToSymbolTable) {
391     if (!ErrorCheckAndCoerce(context, *var, value)) {
392         return nullptr;
393     }
394     const Type* baseType = &var->type();
395     int arraySize = 0;
396     if (baseType->isArray()) {
397         arraySize = baseType->columns();
398         baseType = &baseType->componentType();
399     }
400     std::unique_ptr<Statement> varDecl = VarDeclaration::Make(context, var.get(), baseType,
401                                                               arraySize, std::move(value));
402     if (!varDecl) {
403         return nullptr;
404     }
405 
406     SymbolTable* symbols = ThreadContext::SymbolTable().get();
407     if (var->storage() == Variable::Storage::kGlobal ||
408         var->storage() == Variable::Storage::kInterfaceBlock) {
409         // Check if this globally-scoped variable name overlaps an existing symbol name.
410         if (symbols->find(var->name())) {
411             context.fErrors->error(var->fPosition,
412                                    "symbol '" + std::string(var->name()) + "' was already defined");
413             return nullptr;
414         }
415 
416         // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
417         if (var->name() == Compiler::RTADJUST_NAME) {
418             if (ThreadContext::RTAdjustState().fVar ||
419                 ThreadContext::RTAdjustState().fInterfaceBlock) {
420                 context.fErrors->error(var->fPosition, "duplicate definition of 'sk_RTAdjust'");
421                 return nullptr;
422             }
423             if (!var->type().matches(*context.fTypes.fFloat4)) {
424                 context.fErrors->error(var->fPosition, "sk_RTAdjust must have type 'float4'");
425                 return nullptr;
426             }
427             ThreadContext::RTAdjustState().fVar = var.get();
428         }
429     }
430 
431     if (addToSymbolTable) {
432         symbols->add(std::move(var));
433     } else {
434         symbols->takeOwnershipOfSymbol(std::move(var));
435     }
436     return varDecl;
437 }
438 
Make(const Context & context,Variable * var,const Type * baseType,int arraySize,std::unique_ptr<Expression> value)439 std::unique_ptr<Statement> VarDeclaration::Make(const Context& context, Variable* var,
440         const Type* baseType, int arraySize, std::unique_ptr<Expression> value) {
441     SkASSERT(!baseType->isArray());
442     // function parameters cannot have variable declarations
443     SkASSERT(var->storage() != Variable::Storage::kParameter);
444     // 'const' variables must be initialized
445     SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || value);
446     // 'const' variable initializer must be a constant expression
447     SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) ||
448              Analysis::IsConstantExpression(*value));
449     // global variable initializer must be a constant expression
450     SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
451                !Analysis::IsConstantExpression(*value)));
452     // opaque type not permitted on an interface block
453     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
454     // initializers are not permitted on interface block fields
455     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
456     // opaque type cannot use initializer expressions
457     SkASSERT(!(value && var->type().isOpaque()));
458     // 'in' variables cannot use initializer expressions
459     SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kIn_Flag)));
460     // 'uniform' variables cannot use initializer expressions
461     SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kUniform_Flag)));
462 
463     auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
464     var->setVarDeclaration(result.get());
465     return std::move(result);
466 }
467 
468 }  // namespace SkSL
469