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