1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "SpirvShader.hpp"
16
17 #include "SpirvProfiler.hpp"
18 #include "SpirvShaderDebug.hpp"
19
20 #include "Device/Context.hpp"
21 #include "System/Debug.hpp"
22 #include "Vulkan/VkPipelineLayout.hpp"
23 #include "Vulkan/VkRenderPass.hpp"
24
25 #include "marl/defer.h"
26
27 #include <spirv/unified1/spirv.hpp>
28
29 namespace sw {
30
Spirv(VkShaderStageFlagBits pipelineStage,const char * entryPointName,const SpirvBinary & insns)31 Spirv::Spirv(
32 VkShaderStageFlagBits pipelineStage,
33 const char *entryPointName,
34 const SpirvBinary &insns)
35 : insns{ insns }
36 , inputs{ MAX_INTERFACE_COMPONENTS }
37 , outputs{ MAX_INTERFACE_COMPONENTS }
38 {
39 ASSERT(insns.size() > 0);
40
41 // The identifiers of all OpVariables that define the entry point's IO variables.
42 std::unordered_set<Object::ID> interfaceIds;
43
44 Function::ID currentFunction;
45 Block::ID currentBlock;
46 InsnIterator blockStart;
47
48 for(auto insn : *this)
49 {
50 spv::Op opcode = insn.opcode();
51
52 switch(opcode)
53 {
54 case spv::OpEntryPoint:
55 {
56 spv::ExecutionModel executionModel = spv::ExecutionModel(insn.word(1));
57 Function::ID entryPoint = Function::ID(insn.word(2));
58 const char *name = insn.string(3);
59 VkShaderStageFlagBits stage = executionModelToStage(executionModel);
60
61 if(stage == pipelineStage && strcmp(name, entryPointName) == 0)
62 {
63 ASSERT_MSG(this->entryPoint == 0, "Duplicate entry point with name '%s' and stage %d", name, int(stage));
64 this->entryPoint = entryPoint;
65 this->executionModel = executionModel;
66
67 auto interfaceIdsOffset = 3 + insn.stringSizeInWords(3);
68 for(uint32_t i = interfaceIdsOffset; i < insn.wordCount(); i++)
69 {
70 interfaceIds.emplace(insn.word(i));
71 }
72 }
73 }
74 break;
75
76 case spv::OpExecutionMode:
77 case spv::OpExecutionModeId:
78 ProcessExecutionMode(insn);
79 break;
80
81 case spv::OpDecorate:
82 {
83 TypeOrObjectID targetId = insn.word(1);
84 auto decoration = static_cast<spv::Decoration>(insn.word(2));
85 uint32_t value = insn.wordCount() > 3 ? insn.word(3) : 0;
86
87 decorations[targetId].Apply(decoration, value);
88
89 switch(decoration)
90 {
91 case spv::DecorationDescriptorSet:
92 descriptorDecorations[targetId].DescriptorSet = value;
93 break;
94 case spv::DecorationBinding:
95 descriptorDecorations[targetId].Binding = value;
96 break;
97 case spv::DecorationInputAttachmentIndex:
98 descriptorDecorations[targetId].InputAttachmentIndex = value;
99 break;
100 case spv::DecorationSample:
101 analysis.ContainsSampleQualifier = true;
102 break;
103 default:
104 // Only handling descriptor decorations here.
105 break;
106 }
107
108 if(decoration == spv::DecorationCentroid)
109 {
110 analysis.NeedsCentroid = true;
111 }
112 }
113 break;
114
115 case spv::OpMemberDecorate:
116 {
117 Type::ID targetId = insn.word(1);
118 auto memberIndex = insn.word(2);
119 auto decoration = static_cast<spv::Decoration>(insn.word(3));
120 uint32_t value = insn.wordCount() > 4 ? insn.word(4) : 0;
121
122 auto &d = memberDecorations[targetId];
123 if(memberIndex >= d.size())
124 d.resize(memberIndex + 1); // on demand; exact size would require another pass...
125
126 d[memberIndex].Apply(decoration, value);
127
128 if(decoration == spv::DecorationCentroid)
129 {
130 analysis.NeedsCentroid = true;
131 }
132 }
133 break;
134
135 case spv::OpDecorateId:
136 {
137 auto decoration = static_cast<spv::Decoration>(insn.word(2));
138
139 // Currently OpDecorateId only supports UniformId, which provides information for
140 // potential optimizations that we don't perform, and CounterBuffer, which is used
141 // by HLSL to build the graphics pipeline with shader reflection. At the driver level,
142 // the CounterBuffer decoration does nothing, so we can safely ignore both decorations.
143 ASSERT(decoration == spv::DecorationUniformId || decoration == spv::DecorationCounterBuffer);
144 }
145 break;
146
147 case spv::OpDecorateString:
148 {
149 auto decoration = static_cast<spv::Decoration>(insn.word(2));
150
151 // We assume these are for HLSL semantics, ignore them (b/214576937).
152 ASSERT(decoration == spv::DecorationUserSemantic || decoration == spv::DecorationUserTypeGOOGLE);
153 }
154 break;
155
156 case spv::OpMemberDecorateString:
157 {
158 auto decoration = static_cast<spv::Decoration>(insn.word(3));
159
160 // We assume these are for HLSL semantics, ignore them (b/214576937).
161 ASSERT(decoration == spv::DecorationUserSemantic || decoration == spv::DecorationUserTypeGOOGLE);
162 }
163 break;
164
165 case spv::OpDecorationGroup:
166 // Nothing to do here. We don't need to record the definition of the group; we'll just have
167 // the bundle of decorations float around. If we were to ever walk the decorations directly,
168 // we might think about introducing this as a real Object.
169 break;
170
171 case spv::OpGroupDecorate:
172 {
173 uint32_t group = insn.word(1);
174 const auto &groupDecorations = decorations[group];
175 const auto &descriptorGroupDecorations = descriptorDecorations[group];
176 for(auto i = 2u; i < insn.wordCount(); i++)
177 {
178 // Remaining operands are targets to apply the group to.
179 uint32_t target = insn.word(i);
180 decorations[target].Apply(groupDecorations);
181 descriptorDecorations[target].Apply(descriptorGroupDecorations);
182 }
183 }
184 break;
185
186 case spv::OpGroupMemberDecorate:
187 {
188 const auto &srcDecorations = decorations[insn.word(1)];
189 for(auto i = 2u; i < insn.wordCount(); i += 2)
190 {
191 // remaining operands are pairs of <id>, literal for members to apply to.
192 auto &d = memberDecorations[insn.word(i)];
193 auto memberIndex = insn.word(i + 1);
194 if(memberIndex >= d.size())
195 d.resize(memberIndex + 1); // on demand resize, see above...
196 d[memberIndex].Apply(srcDecorations);
197 }
198 }
199 break;
200
201 case spv::OpLabel:
202 {
203 ASSERT(currentBlock == 0);
204 currentBlock = Block::ID(insn.word(1));
205 blockStart = insn;
206 }
207 break;
208
209 // Termination instructions:
210 case spv::OpKill:
211 case spv::OpTerminateInvocation:
212 analysis.ContainsDiscard = true;
213 // [[fallthrough]]
214
215 case spv::OpUnreachable:
216
217 // Branch Instructions (subset of Termination Instructions):
218 case spv::OpBranch:
219 case spv::OpBranchConditional:
220 case spv::OpSwitch:
221 case spv::OpReturn:
222 {
223 ASSERT(currentBlock != 0);
224 ASSERT(currentFunction != 0);
225
226 auto blockEnd = insn;
227 blockEnd++;
228 functions[currentFunction].blocks[currentBlock] = Block(blockStart, blockEnd);
229 currentBlock = Block::ID(0);
230 }
231 break;
232
233 case spv::OpDemoteToHelperInvocation:
234 analysis.ContainsDiscard = true;
235 break;
236
237 case spv::OpLoopMerge:
238 case spv::OpSelectionMerge:
239 break; // Nothing to do in analysis pass.
240
241 case spv::OpTypeVoid:
242 case spv::OpTypeBool:
243 case spv::OpTypeInt:
244 case spv::OpTypeFloat:
245 case spv::OpTypeVector:
246 case spv::OpTypeMatrix:
247 case spv::OpTypeImage:
248 case spv::OpTypeSampler:
249 case spv::OpTypeSampledImage:
250 case spv::OpTypeArray:
251 case spv::OpTypeRuntimeArray:
252 case spv::OpTypeStruct:
253 case spv::OpTypePointer:
254 case spv::OpTypeForwardPointer:
255 case spv::OpTypeFunction:
256 DeclareType(insn);
257 break;
258
259 case spv::OpVariable:
260 {
261 Type::ID typeId = insn.word(1);
262 Object::ID resultId = insn.word(2);
263 auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
264
265 auto &object = defs[resultId];
266 object.kind = Object::Kind::Pointer;
267 object.definition = insn;
268
269 ASSERT(getType(typeId).definition.opcode() == spv::OpTypePointer);
270 ASSERT(getType(typeId).storageClass == storageClass);
271
272 switch(storageClass)
273 {
274 case spv::StorageClassInput:
275 case spv::StorageClassOutput:
276 if(interfaceIds.count(resultId))
277 {
278 ProcessInterfaceVariable(object);
279 }
280 break;
281
282 case spv::StorageClassUniform:
283 case spv::StorageClassStorageBuffer:
284 case spv::StorageClassPhysicalStorageBuffer:
285 object.kind = Object::Kind::DescriptorSet;
286 break;
287
288 case spv::StorageClassPushConstant:
289 case spv::StorageClassPrivate:
290 case spv::StorageClassFunction:
291 case spv::StorageClassUniformConstant:
292 break; // Correctly handled.
293
294 case spv::StorageClassWorkgroup:
295 {
296 auto &elTy = getType(getType(typeId).element);
297 auto sizeInBytes = elTy.componentCount * static_cast<uint32_t>(sizeof(float));
298 workgroupMemory.allocate(resultId, sizeInBytes);
299 object.kind = Object::Kind::Pointer;
300 }
301 break;
302 case spv::StorageClassAtomicCounter:
303 case spv::StorageClassImage:
304 UNSUPPORTED("StorageClass %d not yet supported", (int)storageClass);
305 break;
306
307 case spv::StorageClassCrossWorkgroup:
308 UNSUPPORTED("SPIR-V OpenCL Execution Model (StorageClassCrossWorkgroup)");
309 break;
310
311 case spv::StorageClassGeneric:
312 UNSUPPORTED("SPIR-V GenericPointer Capability (StorageClassGeneric)");
313 break;
314
315 default:
316 UNREACHABLE("Unexpected StorageClass %d", storageClass); // See Appendix A of the Vulkan spec.
317 break;
318 }
319 }
320 break;
321
322 case spv::OpConstant:
323 case spv::OpSpecConstant:
324 CreateConstant(insn).constantValue[0] = insn.word(3);
325 break;
326 case spv::OpConstantFalse:
327 case spv::OpSpecConstantFalse:
328 CreateConstant(insn).constantValue[0] = 0; // Represent Boolean false as zero.
329 break;
330 case spv::OpConstantTrue:
331 case spv::OpSpecConstantTrue:
332 CreateConstant(insn).constantValue[0] = ~0u; // Represent Boolean true as all bits set.
333 break;
334 case spv::OpConstantNull:
335 case spv::OpUndef:
336 {
337 // TODO: consider a real LLVM-level undef. For now, zero is a perfectly good value.
338 // OpConstantNull forms a constant of arbitrary type, all zeros.
339 auto &object = CreateConstant(insn);
340 auto &objectTy = getType(object);
341 for(auto i = 0u; i < objectTy.componentCount; i++)
342 {
343 object.constantValue[i] = 0;
344 }
345 }
346 break;
347 case spv::OpConstantComposite:
348 case spv::OpSpecConstantComposite:
349 {
350 auto &object = CreateConstant(insn);
351 auto offset = 0u;
352 for(auto i = 0u; i < insn.wordCount() - 3; i++)
353 {
354 auto &constituent = getObject(insn.word(i + 3));
355 auto &constituentTy = getType(constituent);
356 for(auto j = 0u; j < constituentTy.componentCount; j++)
357 {
358 object.constantValue[offset++] = constituent.constantValue[j];
359 }
360 }
361
362 auto objectId = Object::ID(insn.word(2));
363 auto decorationsIt = decorations.find(objectId);
364 if(decorationsIt != decorations.end() &&
365 decorationsIt->second.BuiltIn == spv::BuiltInWorkgroupSize)
366 {
367 // https://www.khronos.org/registry/vulkan/specs/1.1/html/vkspec.html#interfaces-builtin-variables :
368 // Decorating an object with the WorkgroupSize built-in
369 // decoration will make that object contain the dimensions
370 // of a local workgroup. If an object is decorated with the
371 // WorkgroupSize decoration, this must take precedence over
372 // any execution mode set for LocalSize.
373 // The object decorated with WorkgroupSize must be declared
374 // as a three-component vector of 32-bit integers.
375 ASSERT(getType(object).componentCount == 3);
376 executionModes.WorkgroupSizeX = object.constantValue[0];
377 executionModes.WorkgroupSizeY = object.constantValue[1];
378 executionModes.WorkgroupSizeZ = object.constantValue[2];
379 executionModes.useWorkgroupSizeId = false;
380 }
381 }
382 break;
383 case spv::OpSpecConstantOp:
384 EvalSpecConstantOp(insn);
385 break;
386
387 case spv::OpCapability:
388 {
389 auto capability = static_cast<spv::Capability>(insn.word(1));
390 switch(capability)
391 {
392 case spv::CapabilityMatrix: capabilities.Matrix = true; break;
393 case spv::CapabilityShader: capabilities.Shader = true; break;
394 case spv::CapabilityStorageImageMultisample: capabilities.StorageImageMultisample = true; break;
395 case spv::CapabilityClipDistance: capabilities.ClipDistance = true; break;
396 case spv::CapabilityCullDistance: capabilities.CullDistance = true; break;
397 case spv::CapabilityImageCubeArray: capabilities.ImageCubeArray = true; break;
398 case spv::CapabilitySampleRateShading: capabilities.SampleRateShading = true; break;
399 case spv::CapabilityInputAttachment: capabilities.InputAttachment = true; break;
400 case spv::CapabilitySampled1D: capabilities.Sampled1D = true; break;
401 case spv::CapabilityImage1D: capabilities.Image1D = true; break;
402 case spv::CapabilitySampledBuffer: capabilities.SampledBuffer = true; break;
403 case spv::CapabilitySampledCubeArray: capabilities.SampledCubeArray = true; break;
404 case spv::CapabilityImageBuffer: capabilities.ImageBuffer = true; break;
405 case spv::CapabilityImageMSArray: capabilities.ImageMSArray = true; break;
406 case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break;
407 case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break;
408 case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break;
409 case spv::CapabilityDotProductInputAll: capabilities.DotProductInputAll = true; break;
410 case spv::CapabilityDotProductInput4x8Bit: capabilities.DotProductInput4x8Bit = true; break;
411 case spv::CapabilityDotProductInput4x8BitPacked: capabilities.DotProductInput4x8BitPacked = true; break;
412 case spv::CapabilityDotProduct: capabilities.DotProduct = true; break;
413 case spv::CapabilityInterpolationFunction: capabilities.InterpolationFunction = true; break;
414 case spv::CapabilityStorageImageWriteWithoutFormat: capabilities.StorageImageWriteWithoutFormat = true; break;
415 case spv::CapabilityGroupNonUniform: capabilities.GroupNonUniform = true; break;
416 case spv::CapabilityGroupNonUniformVote: capabilities.GroupNonUniformVote = true; break;
417 case spv::CapabilityGroupNonUniformArithmetic: capabilities.GroupNonUniformArithmetic = true; break;
418 case spv::CapabilityGroupNonUniformBallot: capabilities.GroupNonUniformBallot = true; break;
419 case spv::CapabilityGroupNonUniformShuffle: capabilities.GroupNonUniformShuffle = true; break;
420 case spv::CapabilityGroupNonUniformShuffleRelative: capabilities.GroupNonUniformShuffleRelative = true; break;
421 case spv::CapabilityGroupNonUniformQuad: capabilities.GroupNonUniformQuad = true; break;
422 case spv::CapabilityDeviceGroup: capabilities.DeviceGroup = true; break;
423 case spv::CapabilityMultiView: capabilities.MultiView = true; break;
424 case spv::CapabilitySignedZeroInfNanPreserve: capabilities.SignedZeroInfNanPreserve = true; break;
425 case spv::CapabilityDemoteToHelperInvocation: capabilities.DemoteToHelperInvocation = true; break;
426 case spv::CapabilityStencilExportEXT: capabilities.StencilExportEXT = true; break;
427 case spv::CapabilityVulkanMemoryModel: capabilities.VulkanMemoryModel = true; break;
428 case spv::CapabilityVulkanMemoryModelDeviceScope: capabilities.VulkanMemoryModelDeviceScope = true; break;
429 case spv::CapabilityShaderNonUniform: capabilities.ShaderNonUniform = true; break;
430 case spv::CapabilityRuntimeDescriptorArray: capabilities.RuntimeDescriptorArray = true; break;
431 case spv::CapabilityStorageBufferArrayNonUniformIndexing: capabilities.StorageBufferArrayNonUniformIndexing = true; break;
432 case spv::CapabilityStorageTexelBufferArrayNonUniformIndexing: capabilities.StorageTexelBufferArrayNonUniformIndexing = true; break;
433 case spv::CapabilityUniformTexelBufferArrayNonUniformIndexing: capabilities.UniformTexelBufferArrayNonUniformIndexing = true; break;
434 case spv::CapabilityUniformTexelBufferArrayDynamicIndexing: capabilities.UniformTexelBufferArrayDynamicIndexing = true; break;
435 case spv::CapabilityStorageTexelBufferArrayDynamicIndexing: capabilities.StorageTexelBufferArrayDynamicIndexing = true; break;
436 case spv::CapabilityUniformBufferArrayNonUniformIndexing: capabilities.UniformBufferArrayNonUniformIndex = true; break;
437 case spv::CapabilitySampledImageArrayNonUniformIndexing: capabilities.SampledImageArrayNonUniformIndexing = true; break;
438 case spv::CapabilityStorageImageArrayNonUniformIndexing: capabilities.StorageImageArrayNonUniformIndexing = true; break;
439 case spv::CapabilityPhysicalStorageBufferAddresses: capabilities.PhysicalStorageBufferAddresses = true; break;
440 default:
441 UNSUPPORTED("Unsupported capability %u", insn.word(1));
442 }
443
444 // Various capabilities will be declared, but none affect our code generation at this point.
445 }
446 break;
447
448 case spv::OpMemoryModel:
449 {
450 addressingModel = static_cast<spv::AddressingModel>(insn.word(1));
451 memoryModel = static_cast<spv::MemoryModel>(insn.word(2));
452 }
453 break;
454
455 case spv::OpFunction:
456 {
457 auto functionId = Function::ID(insn.word(2));
458 ASSERT_MSG(currentFunction == 0, "Functions %d and %d overlap", currentFunction.value(), functionId.value());
459 currentFunction = functionId;
460 auto &function = functions[functionId];
461 function.result = Type::ID(insn.word(1));
462 function.type = Type::ID(insn.word(4));
463 // Scan forward to find the function's label.
464 for(auto it = insn; it != end(); it++)
465 {
466 if(it.opcode() == spv::OpLabel)
467 {
468 function.entry = Block::ID(it.word(1));
469 break;
470 }
471 }
472 ASSERT_MSG(function.entry != 0, "Function<%d> has no label", currentFunction.value());
473 }
474 break;
475
476 case spv::OpFunctionEnd:
477 currentFunction = 0;
478 break;
479
480 case spv::OpExtInstImport:
481 {
482 static constexpr std::pair<const char *, Extension::Name> extensionsByName[] = {
483 { "GLSL.std.450", Extension::GLSLstd450 },
484 { "NonSemantic.", Extension::NonSemanticInfo },
485 };
486 static constexpr auto extensionCount = sizeof(extensionsByName) / sizeof(extensionsByName[0]);
487
488 auto id = Extension::ID(insn.word(1));
489 auto name = insn.string(2);
490 auto ext = Extension{ Extension::Unknown };
491 for(size_t i = 0; i < extensionCount; i++)
492 {
493 if(0 == strncmp(name, extensionsByName[i].first, strlen(extensionsByName[i].first)))
494 {
495 ext = Extension{ extensionsByName[i].second };
496 break;
497 }
498 }
499 if(ext.name == Extension::Unknown)
500 {
501 UNSUPPORTED("SPIR-V Extension: %s", name);
502 break;
503 }
504 extensionsByID.emplace(id, ext);
505 extensionsImported.emplace(ext.name);
506 }
507 break;
508 case spv::OpName:
509 case spv::OpMemberName:
510 case spv::OpSource:
511 case spv::OpSourceContinued:
512 case spv::OpSourceExtension:
513 case spv::OpLine:
514 case spv::OpNoLine:
515 case spv::OpModuleProcessed:
516 // No semantic impact
517 break;
518
519 case spv::OpString:
520 strings.emplace(insn.word(1), insn.string(2));
521 break;
522
523 case spv::OpFunctionParameter:
524 // These should have all been removed by preprocessing passes. If we see them here,
525 // our assumptions are wrong and we will probably generate wrong code.
526 UNREACHABLE("%s should have already been lowered.", OpcodeName(opcode));
527 break;
528
529 case spv::OpFunctionCall:
530 // TODO(b/141246700): Add full support for spv::OpFunctionCall
531 break;
532
533 case spv::OpFConvert:
534 UNSUPPORTED("SPIR-V Float16 or Float64 Capability (OpFConvert)");
535 break;
536
537 case spv::OpSConvert:
538 UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpSConvert)");
539 break;
540
541 case spv::OpUConvert:
542 UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpUConvert)");
543 break;
544
545 case spv::OpLoad:
546 case spv::OpAccessChain:
547 case spv::OpInBoundsAccessChain:
548 case spv::OpPtrAccessChain:
549 case spv::OpSampledImage:
550 case spv::OpImage:
551 case spv::OpCopyObject:
552 case spv::OpCopyLogical:
553 {
554 // Propagate the descriptor decorations to the result.
555 Object::ID resultId = insn.word(2);
556 Object::ID pointerId = insn.word(3);
557 const auto &d = descriptorDecorations.find(pointerId);
558
559 if(d != descriptorDecorations.end())
560 {
561 descriptorDecorations[resultId] = d->second;
562 }
563
564 DefineResult(insn);
565
566 if(opcode == spv::OpAccessChain || opcode == spv::OpInBoundsAccessChain || opcode == spv::OpPtrAccessChain)
567 {
568 int indexId = (insn.opcode() == spv::OpPtrAccessChain) ? 5 : 4;
569 Decorations dd{};
570 ApplyDecorationsForAccessChain(&dd, &descriptorDecorations[resultId], pointerId, Span(insn, indexId, insn.wordCount() - indexId));
571 // Note: offset is the one thing that does *not* propagate, as the access chain accounts for it.
572 dd.HasOffset = false;
573 decorations[resultId].Apply(dd);
574 }
575 }
576 break;
577
578 case spv::OpCompositeConstruct:
579 case spv::OpCompositeInsert:
580 case spv::OpCompositeExtract:
581 case spv::OpVectorShuffle:
582 case spv::OpVectorTimesScalar:
583 case spv::OpMatrixTimesScalar:
584 case spv::OpMatrixTimesVector:
585 case spv::OpVectorTimesMatrix:
586 case spv::OpMatrixTimesMatrix:
587 case spv::OpOuterProduct:
588 case spv::OpTranspose:
589 case spv::OpVectorExtractDynamic:
590 case spv::OpVectorInsertDynamic:
591 // Unary ops
592 case spv::OpNot:
593 case spv::OpBitFieldInsert:
594 case spv::OpBitFieldSExtract:
595 case spv::OpBitFieldUExtract:
596 case spv::OpBitReverse:
597 case spv::OpBitCount:
598 case spv::OpSNegate:
599 case spv::OpFNegate:
600 case spv::OpLogicalNot:
601 case spv::OpQuantizeToF16:
602 // Binary ops
603 case spv::OpIAdd:
604 case spv::OpISub:
605 case spv::OpIMul:
606 case spv::OpSDiv:
607 case spv::OpUDiv:
608 case spv::OpFAdd:
609 case spv::OpFSub:
610 case spv::OpFMul:
611 case spv::OpFDiv:
612 case spv::OpFMod:
613 case spv::OpFRem:
614 case spv::OpFOrdEqual:
615 case spv::OpFUnordEqual:
616 case spv::OpFOrdNotEqual:
617 case spv::OpFUnordNotEqual:
618 case spv::OpFOrdLessThan:
619 case spv::OpFUnordLessThan:
620 case spv::OpFOrdGreaterThan:
621 case spv::OpFUnordGreaterThan:
622 case spv::OpFOrdLessThanEqual:
623 case spv::OpFUnordLessThanEqual:
624 case spv::OpFOrdGreaterThanEqual:
625 case spv::OpFUnordGreaterThanEqual:
626 case spv::OpSMod:
627 case spv::OpSRem:
628 case spv::OpUMod:
629 case spv::OpIEqual:
630 case spv::OpINotEqual:
631 case spv::OpUGreaterThan:
632 case spv::OpSGreaterThan:
633 case spv::OpUGreaterThanEqual:
634 case spv::OpSGreaterThanEqual:
635 case spv::OpULessThan:
636 case spv::OpSLessThan:
637 case spv::OpULessThanEqual:
638 case spv::OpSLessThanEqual:
639 case spv::OpShiftRightLogical:
640 case spv::OpShiftRightArithmetic:
641 case spv::OpShiftLeftLogical:
642 case spv::OpBitwiseOr:
643 case spv::OpBitwiseXor:
644 case spv::OpBitwiseAnd:
645 case spv::OpLogicalOr:
646 case spv::OpLogicalAnd:
647 case spv::OpLogicalEqual:
648 case spv::OpLogicalNotEqual:
649 case spv::OpUMulExtended:
650 case spv::OpSMulExtended:
651 case spv::OpIAddCarry:
652 case spv::OpISubBorrow:
653 case spv::OpDot:
654 case spv::OpSDot:
655 case spv::OpUDot:
656 case spv::OpSUDot:
657 case spv::OpSDotAccSat:
658 case spv::OpUDotAccSat:
659 case spv::OpSUDotAccSat:
660 case spv::OpConvertFToU:
661 case spv::OpConvertFToS:
662 case spv::OpConvertSToF:
663 case spv::OpConvertUToF:
664 case spv::OpBitcast:
665 case spv::OpSelect:
666 case spv::OpIsInf:
667 case spv::OpIsNan:
668 case spv::OpAny:
669 case spv::OpAll:
670 case spv::OpDPdx:
671 case spv::OpDPdxCoarse:
672 case spv::OpDPdy:
673 case spv::OpDPdyCoarse:
674 case spv::OpFwidth:
675 case spv::OpFwidthCoarse:
676 case spv::OpDPdxFine:
677 case spv::OpDPdyFine:
678 case spv::OpFwidthFine:
679 case spv::OpAtomicLoad:
680 case spv::OpAtomicIAdd:
681 case spv::OpAtomicISub:
682 case spv::OpAtomicSMin:
683 case spv::OpAtomicSMax:
684 case spv::OpAtomicUMin:
685 case spv::OpAtomicUMax:
686 case spv::OpAtomicAnd:
687 case spv::OpAtomicOr:
688 case spv::OpAtomicXor:
689 case spv::OpAtomicIIncrement:
690 case spv::OpAtomicIDecrement:
691 case spv::OpAtomicExchange:
692 case spv::OpAtomicCompareExchange:
693 case spv::OpPhi:
694 case spv::OpImageSampleImplicitLod:
695 case spv::OpImageSampleExplicitLod:
696 case spv::OpImageSampleDrefImplicitLod:
697 case spv::OpImageSampleDrefExplicitLod:
698 case spv::OpImageSampleProjImplicitLod:
699 case spv::OpImageSampleProjExplicitLod:
700 case spv::OpImageSampleProjDrefImplicitLod:
701 case spv::OpImageSampleProjDrefExplicitLod:
702 case spv::OpImageGather:
703 case spv::OpImageDrefGather:
704 case spv::OpImageFetch:
705 case spv::OpImageQuerySizeLod:
706 case spv::OpImageQuerySize:
707 case spv::OpImageQueryLod:
708 case spv::OpImageQueryLevels:
709 case spv::OpImageQuerySamples:
710 case spv::OpImageRead:
711 case spv::OpImageTexelPointer:
712 case spv::OpGroupNonUniformElect:
713 case spv::OpGroupNonUniformAll:
714 case spv::OpGroupNonUniformAny:
715 case spv::OpGroupNonUniformAllEqual:
716 case spv::OpGroupNonUniformBroadcast:
717 case spv::OpGroupNonUniformBroadcastFirst:
718 case spv::OpGroupNonUniformQuadBroadcast:
719 case spv::OpGroupNonUniformQuadSwap:
720 case spv::OpGroupNonUniformBallot:
721 case spv::OpGroupNonUniformInverseBallot:
722 case spv::OpGroupNonUniformBallotBitExtract:
723 case spv::OpGroupNonUniformBallotBitCount:
724 case spv::OpGroupNonUniformBallotFindLSB:
725 case spv::OpGroupNonUniformBallotFindMSB:
726 case spv::OpGroupNonUniformShuffle:
727 case spv::OpGroupNonUniformShuffleXor:
728 case spv::OpGroupNonUniformShuffleUp:
729 case spv::OpGroupNonUniformShuffleDown:
730 case spv::OpGroupNonUniformIAdd:
731 case spv::OpGroupNonUniformFAdd:
732 case spv::OpGroupNonUniformIMul:
733 case spv::OpGroupNonUniformFMul:
734 case spv::OpGroupNonUniformSMin:
735 case spv::OpGroupNonUniformUMin:
736 case spv::OpGroupNonUniformFMin:
737 case spv::OpGroupNonUniformSMax:
738 case spv::OpGroupNonUniformUMax:
739 case spv::OpGroupNonUniformFMax:
740 case spv::OpGroupNonUniformBitwiseAnd:
741 case spv::OpGroupNonUniformBitwiseOr:
742 case spv::OpGroupNonUniformBitwiseXor:
743 case spv::OpGroupNonUniformLogicalAnd:
744 case spv::OpGroupNonUniformLogicalOr:
745 case spv::OpGroupNonUniformLogicalXor:
746 case spv::OpArrayLength:
747 case spv::OpIsHelperInvocationEXT:
748 // Instructions that yield an intermediate value or divergent pointer
749 DefineResult(insn);
750 break;
751
752 case spv::OpExtInst:
753 switch(getExtension(insn.word(3)).name)
754 {
755 case Extension::GLSLstd450:
756 DefineResult(insn);
757 break;
758 case Extension::NonSemanticInfo:
759 // An extended set name which is prefixed with "NonSemantic." is
760 // guaranteed to contain only non-semantic instructions and all
761 // OpExtInst instructions referencing this set can be ignored.
762 break;
763 default:
764 UNREACHABLE("Unexpected Extension name %d", int(getExtension(insn.word(3)).name));
765 break;
766 }
767 break;
768
769 case spv::OpStore:
770 case spv::OpAtomicStore:
771 case spv::OpCopyMemory:
772 case spv::OpMemoryBarrier:
773 // Don't need to do anything during analysis pass
774 break;
775
776 case spv::OpImageWrite:
777 analysis.ContainsImageWrite = true;
778 break;
779
780 case spv::OpControlBarrier:
781 analysis.ContainsControlBarriers = true;
782 break;
783
784 case spv::OpExtension:
785 {
786 const char *ext = insn.string(1);
787 // Part of core SPIR-V 1.3. Vulkan 1.1 implementations must also accept the pre-1.3
788 // extension per Appendix A, `Vulkan Environment for SPIR-V`.
789 if(!strcmp(ext, "SPV_KHR_storage_buffer_storage_class")) break;
790 if(!strcmp(ext, "SPV_KHR_shader_draw_parameters")) break;
791 if(!strcmp(ext, "SPV_KHR_16bit_storage")) break;
792 if(!strcmp(ext, "SPV_KHR_variable_pointers")) break;
793 if(!strcmp(ext, "SPV_KHR_device_group")) break;
794 if(!strcmp(ext, "SPV_KHR_multiview")) break;
795 if(!strcmp(ext, "SPV_EXT_demote_to_helper_invocation")) break;
796 if(!strcmp(ext, "SPV_KHR_terminate_invocation")) break;
797 if(!strcmp(ext, "SPV_EXT_shader_stencil_export")) break;
798 if(!strcmp(ext, "SPV_KHR_float_controls")) break;
799 if(!strcmp(ext, "SPV_KHR_integer_dot_product")) break;
800 if(!strcmp(ext, "SPV_KHR_non_semantic_info")) break;
801 if(!strcmp(ext, "SPV_KHR_physical_storage_buffer")) break;
802 if(!strcmp(ext, "SPV_KHR_vulkan_memory_model")) break;
803 if(!strcmp(ext, "SPV_GOOGLE_decorate_string")) break;
804 if(!strcmp(ext, "SPV_GOOGLE_hlsl_functionality1")) break;
805 if(!strcmp(ext, "SPV_GOOGLE_user_type")) break;
806 if(!strcmp(ext, "SPV_EXT_descriptor_indexing")) break;
807 UNSUPPORTED("SPIR-V Extension: %s", ext);
808 }
809 break;
810
811 default:
812 UNSUPPORTED("%s", OpcodeName(opcode));
813 }
814 }
815
816 ASSERT_MSG(entryPoint != 0, "Entry point '%s' not found", entryPointName);
817 for(auto &it : functions)
818 {
819 it.second.AssignBlockFields();
820 }
821
822 #ifdef SPIRV_SHADER_CFG_GRAPHVIZ_DOT_FILEPATH
823 {
824 char path[1024];
825 snprintf(path, sizeof(path), SPIRV_SHADER_CFG_GRAPHVIZ_DOT_FILEPATH, codeSerialID);
826 WriteCFGGraphVizDotFile(path);
827 }
828 #endif
829 }
830
~Spirv()831 Spirv::~Spirv()
832 {
833 }
834
DeclareType(InsnIterator insn)835 void Spirv::DeclareType(InsnIterator insn)
836 {
837 Type::ID resultId = insn.word(1);
838
839 auto &type = types[resultId];
840 type.definition = insn;
841 type.componentCount = ComputeTypeSize(insn);
842
843 // A structure is a builtin block if it has a builtin
844 // member. All members of such a structure are builtins.
845 spv::Op opcode = insn.opcode();
846 switch(opcode)
847 {
848 case spv::OpTypeStruct:
849 {
850 auto d = memberDecorations.find(resultId);
851 if(d != memberDecorations.end())
852 {
853 for(auto &m : d->second)
854 {
855 if(m.HasBuiltIn)
856 {
857 type.isBuiltInBlock = true;
858 break;
859 }
860 }
861 }
862 }
863 break;
864 case spv::OpTypePointer:
865 case spv::OpTypeForwardPointer:
866 {
867 Type::ID elementTypeId = insn.word((opcode == spv::OpTypeForwardPointer) ? 1 : 3);
868 type.element = elementTypeId;
869 type.isBuiltInBlock = getType(elementTypeId).isBuiltInBlock;
870 type.storageClass = static_cast<spv::StorageClass>(insn.word(2));
871 }
872 break;
873 case spv::OpTypeVector:
874 case spv::OpTypeMatrix:
875 case spv::OpTypeArray:
876 case spv::OpTypeRuntimeArray:
877 {
878 Type::ID elementTypeId = insn.word(2);
879 type.element = elementTypeId;
880 }
881 break;
882 default:
883 break;
884 }
885 }
886
CreateConstant(InsnIterator insn)887 Spirv::Object &Spirv::CreateConstant(InsnIterator insn)
888 {
889 Type::ID typeId = insn.word(1);
890 Object::ID resultId = insn.word(2);
891 auto &object = defs[resultId];
892 auto &objectTy = getType(typeId);
893 object.kind = Object::Kind::Constant;
894 object.definition = insn;
895 object.constantValue.resize(objectTy.componentCount);
896
897 return object;
898 }
899
ProcessInterfaceVariable(Object & object)900 void Spirv::ProcessInterfaceVariable(Object &object)
901 {
902 auto &objectTy = getType(object);
903 ASSERT(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
904
905 ASSERT(objectTy.opcode() == spv::OpTypePointer);
906 auto pointeeTy = getType(objectTy.element);
907
908 auto &builtinInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
909 auto &userDefinedInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputs : outputs;
910
911 ASSERT(object.opcode() == spv::OpVariable);
912 Object::ID resultId = object.definition.word(2);
913
914 if(objectTy.isBuiltInBlock)
915 {
916 // Walk the builtin block, registering each of its members separately.
917 auto m = memberDecorations.find(objectTy.element);
918 ASSERT(m != memberDecorations.end()); // Otherwise we wouldn't have marked the type chain
919 auto &structType = pointeeTy.definition;
920 auto memberIndex = 0u;
921 auto offset = 0u;
922
923 for(auto &member : m->second)
924 {
925 auto &memberType = getType(structType.word(2 + memberIndex));
926
927 if(member.HasBuiltIn)
928 {
929 builtinInterface[member.BuiltIn] = { resultId, offset, memberType.componentCount };
930 }
931
932 offset += memberType.componentCount;
933 ++memberIndex;
934 }
935
936 return;
937 }
938
939 auto d = decorations.find(resultId);
940 if(d != decorations.end() && d->second.HasBuiltIn)
941 {
942 builtinInterface[d->second.BuiltIn] = { resultId, 0, pointeeTy.componentCount };
943 }
944 else
945 {
946 object.kind = Object::Kind::InterfaceVariable;
947 VisitInterface(resultId,
948 [&userDefinedInterface](const Decorations &d, AttribType type) {
949 // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
950 int32_t scalarSlot = (d.Location << 2) | d.Component;
951 ASSERT(scalarSlot >= 0 &&
952 scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
953
954 auto &slot = userDefinedInterface[scalarSlot];
955 slot.Type = type;
956 slot.Flat = d.Flat;
957 slot.NoPerspective = d.NoPerspective;
958 slot.Centroid = d.Centroid;
959 });
960 }
961 }
962
GetNumInputComponents(int32_t location) const963 uint32_t Spirv::GetNumInputComponents(int32_t location) const
964 {
965 ASSERT(location >= 0);
966
967 // Verify how many component(s) per input
968 // 1 to 4, for float, vec2, vec3, vec4.
969 // Note that matrices are divided over multiple inputs
970 uint32_t num_components_per_input = 0;
971 for(; num_components_per_input < 4; ++num_components_per_input)
972 {
973 if(inputs[(location << 2) | num_components_per_input].Type == ATTRIBTYPE_UNUSED)
974 {
975 break;
976 }
977 }
978
979 return num_components_per_input;
980 }
981
GetPackedInterpolant(int32_t location) const982 uint32_t Spirv::GetPackedInterpolant(int32_t location) const
983 {
984 ASSERT(location >= 0);
985 const uint32_t maxInterpolant = (location << 2);
986
987 // Return the number of used components only at location
988 uint32_t packedInterpolant = 0;
989 for(uint32_t i = 0; i < maxInterpolant; ++i)
990 {
991 if(inputs[i].Type != ATTRIBTYPE_UNUSED)
992 {
993 ++packedInterpolant;
994 }
995 }
996
997 return packedInterpolant;
998 }
999
ProcessExecutionMode(InsnIterator insn)1000 void Spirv::ProcessExecutionMode(InsnIterator insn)
1001 {
1002 Function::ID function = insn.word(1);
1003 if(function != entryPoint)
1004 {
1005 return;
1006 }
1007
1008 auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
1009 switch(mode)
1010 {
1011 case spv::ExecutionModeEarlyFragmentTests:
1012 executionModes.EarlyFragmentTests = true;
1013 break;
1014 case spv::ExecutionModeDepthReplacing:
1015 executionModes.DepthReplacing = true;
1016 break;
1017 case spv::ExecutionModeDepthGreater:
1018 // TODO(b/177915067): Can be used to optimize depth test, currently unused.
1019 executionModes.DepthGreater = true;
1020 break;
1021 case spv::ExecutionModeDepthLess:
1022 // TODO(b/177915067): Can be used to optimize depth test, currently unused.
1023 executionModes.DepthLess = true;
1024 break;
1025 case spv::ExecutionModeDepthUnchanged:
1026 // TODO(b/177915067): Can be used to optimize depth test, currently unused.
1027 executionModes.DepthUnchanged = true;
1028 break;
1029 case spv::ExecutionModeStencilRefReplacingEXT:
1030 executionModes.StencilRefReplacing = true;
1031 break;
1032 case spv::ExecutionModeLocalSize:
1033 case spv::ExecutionModeLocalSizeId:
1034 executionModes.WorkgroupSizeX = insn.word(3);
1035 executionModes.WorkgroupSizeY = insn.word(4);
1036 executionModes.WorkgroupSizeZ = insn.word(5);
1037 executionModes.useWorkgroupSizeId = (mode == spv::ExecutionModeLocalSizeId);
1038 break;
1039 case spv::ExecutionModeOriginUpperLeft:
1040 // This is always the case for a Vulkan shader. Do nothing.
1041 break;
1042 case spv::ExecutionModeSignedZeroInfNanPreserve:
1043 // We currently don't perform any aggressive fast-math optimizations.
1044 break;
1045 default:
1046 UNREACHABLE("Execution mode: %d", int(mode));
1047 }
1048 }
1049
getWorkgroupSizeX() const1050 uint32_t Spirv::getWorkgroupSizeX() const
1051 {
1052 return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeX).constantValue[0] : executionModes.WorkgroupSizeX.value();
1053 }
1054
getWorkgroupSizeY() const1055 uint32_t Spirv::getWorkgroupSizeY() const
1056 {
1057 return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeY).constantValue[0] : executionModes.WorkgroupSizeY.value();
1058 }
1059
getWorkgroupSizeZ() const1060 uint32_t Spirv::getWorkgroupSizeZ() const
1061 {
1062 return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeZ).constantValue[0] : executionModes.WorkgroupSizeZ.value();
1063 }
1064
ComputeTypeSize(InsnIterator insn)1065 uint32_t Spirv::ComputeTypeSize(InsnIterator insn)
1066 {
1067 // Types are always built from the bottom up (with the exception of forward ptrs, which
1068 // don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
1069 // already been described (and so their sizes determined)
1070 switch(insn.opcode())
1071 {
1072 case spv::OpTypeVoid:
1073 case spv::OpTypeSampler:
1074 case spv::OpTypeImage:
1075 case spv::OpTypeSampledImage:
1076 case spv::OpTypeForwardPointer:
1077 case spv::OpTypeFunction:
1078 case spv::OpTypeRuntimeArray:
1079 // Objects that don't consume any space.
1080 // Descriptor-backed objects currently only need exist at compile-time.
1081 // Runtime arrays don't appear in places where their size would be interesting
1082 return 0;
1083
1084 case spv::OpTypeBool:
1085 case spv::OpTypeFloat:
1086 case spv::OpTypeInt:
1087 // All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
1088 // we might need to change this, but only 32 bit components are required for Vulkan 1.1.
1089 return 1;
1090
1091 case spv::OpTypeVector:
1092 case spv::OpTypeMatrix:
1093 // Vectors and matrices both consume element count * element size.
1094 return getType(insn.word(2)).componentCount * insn.word(3);
1095
1096 case spv::OpTypeArray:
1097 {
1098 // Element count * element size. Array sizes come from constant ids.
1099 auto arraySize = GetConstScalarInt(insn.word(3));
1100 return getType(insn.word(2)).componentCount * arraySize;
1101 }
1102
1103 case spv::OpTypeStruct:
1104 {
1105 uint32_t size = 0;
1106 for(uint32_t i = 2u; i < insn.wordCount(); i++)
1107 {
1108 size += getType(insn.word(i)).componentCount;
1109 }
1110 return size;
1111 }
1112
1113 case spv::OpTypePointer:
1114 // Runtime representation of a pointer is a per-lane index.
1115 // Note: clients are expected to look through the pointer if they want the pointee size instead.
1116 return 1;
1117
1118 default:
1119 UNREACHABLE("%s", OpcodeName(insn.opcode()));
1120 return 0;
1121 }
1122 }
1123
VisitInterfaceInner(Type::ID id,Decorations d,const InterfaceVisitor & f) const1124 int Spirv::VisitInterfaceInner(Type::ID id, Decorations d, const InterfaceVisitor &f) const
1125 {
1126 // Recursively walks variable definition and its type tree, taking into account
1127 // any explicit Location or Component decorations encountered; where explicit
1128 // Locations or Components are not specified, assigns them sequentially.
1129 // Collected decorations are carried down toward the leaves and across
1130 // siblings; Effect of decorations intentionally does not flow back up the tree.
1131 //
1132 // F is a functor to be called with the effective decoration set for every component.
1133 //
1134 // Returns the next available location, and calls f().
1135
1136 // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
1137
1138 ApplyDecorationsForId(&d, id);
1139
1140 const auto &obj = getType(id);
1141 switch(obj.opcode())
1142 {
1143 case spv::OpTypePointer:
1144 return VisitInterfaceInner(obj.definition.word(3), d, f);
1145 case spv::OpTypeMatrix:
1146 for(auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
1147 {
1148 // consumes same components of N consecutive locations
1149 VisitInterfaceInner(obj.definition.word(2), d, f);
1150 }
1151 return d.Location;
1152 case spv::OpTypeVector:
1153 for(auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
1154 {
1155 // consumes N consecutive components in the same location
1156 VisitInterfaceInner(obj.definition.word(2), d, f);
1157 }
1158 return d.Location + 1;
1159 case spv::OpTypeFloat:
1160 f(d, ATTRIBTYPE_FLOAT);
1161 return d.Location + 1;
1162 case spv::OpTypeInt:
1163 f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
1164 return d.Location + 1;
1165 case spv::OpTypeBool:
1166 f(d, ATTRIBTYPE_UINT);
1167 return d.Location + 1;
1168 case spv::OpTypeStruct:
1169 {
1170 // iterate over members, which may themselves have Location/Component decorations
1171 for(auto i = 0u; i < obj.definition.wordCount() - 2; i++)
1172 {
1173 Decorations dMember = d;
1174 ApplyDecorationsForIdMember(&dMember, id, i);
1175 d.Location = VisitInterfaceInner(obj.definition.word(i + 2), dMember, f);
1176 d.Component = 0; // Implicit locations always have component=0
1177 }
1178 return d.Location;
1179 }
1180 case spv::OpTypeArray:
1181 {
1182 auto arraySize = GetConstScalarInt(obj.definition.word(3));
1183 for(auto i = 0u; i < arraySize; i++)
1184 {
1185 d.Location = VisitInterfaceInner(obj.definition.word(2), d, f);
1186 }
1187 return d.Location;
1188 }
1189 default:
1190 // Intentionally partial; most opcodes do not participate in type hierarchies
1191 return 0;
1192 }
1193 }
1194
VisitInterface(Object::ID id,const InterfaceVisitor & f) const1195 void Spirv::VisitInterface(Object::ID id, const InterfaceVisitor &f) const
1196 {
1197 // Walk a variable definition and call f for each component in it.
1198 Decorations d = GetDecorationsForId(id);
1199
1200 auto def = getObject(id).definition;
1201 ASSERT(def.opcode() == spv::OpVariable);
1202 VisitInterfaceInner(def.word(1), d, f);
1203 }
1204
ApplyDecorationsForAccessChain(Decorations * d,DescriptorDecorations * dd,Object::ID baseId,const Span & indexIds) const1205 void Spirv::ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, const Span &indexIds) const
1206 {
1207 ApplyDecorationsForId(d, baseId);
1208 auto &baseObject = getObject(baseId);
1209 ApplyDecorationsForId(d, baseObject.typeId());
1210 auto typeId = getType(baseObject).element;
1211
1212 for(uint32_t i = 0; i < indexIds.size(); i++)
1213 {
1214 ApplyDecorationsForId(d, typeId);
1215 auto &type = getType(typeId);
1216 switch(type.opcode())
1217 {
1218 case spv::OpTypeStruct:
1219 {
1220 int memberIndex = GetConstScalarInt(indexIds[i]);
1221 ApplyDecorationsForIdMember(d, typeId, memberIndex);
1222 typeId = type.definition.word(2u + memberIndex);
1223 }
1224 break;
1225 case spv::OpTypeArray:
1226 case spv::OpTypeRuntimeArray:
1227 if(dd->InputAttachmentIndex >= 0)
1228 {
1229 dd->InputAttachmentIndex += GetConstScalarInt(indexIds[i]);
1230 }
1231 typeId = type.element;
1232 break;
1233 case spv::OpTypeVector:
1234 typeId = type.element;
1235 break;
1236 case spv::OpTypeMatrix:
1237 typeId = type.element;
1238 d->InsideMatrix = true;
1239 break;
1240 default:
1241 UNREACHABLE("%s", OpcodeName(type.definition.opcode()));
1242 }
1243 }
1244 }
1245
WalkExplicitLayoutAccessChain(Object::ID baseId,Object::ID elementId,const Span & indexIds,bool nonUniform) const1246 SIMD::Pointer SpirvEmitter::WalkExplicitLayoutAccessChain(Object::ID baseId, Object::ID elementId, const Span &indexIds, bool nonUniform) const
1247 {
1248 // Produce a offset into external memory in sizeof(float) units
1249
1250 auto &baseObject = shader.getObject(baseId);
1251 Type::ID typeId = shader.getType(baseObject).element;
1252 Decorations d = shader.GetDecorationsForId(baseObject.typeId());
1253 SIMD::Int arrayIndex = 0;
1254
1255 uint32_t start = 0;
1256 if(baseObject.kind == Object::Kind::DescriptorSet)
1257 {
1258 auto type = shader.getType(typeId).definition.opcode();
1259 if(type == spv::OpTypeArray || type == spv::OpTypeRuntimeArray)
1260 {
1261 auto &obj = shader.getObject(indexIds[0]);
1262 ASSERT(obj.kind == Object::Kind::Constant || obj.kind == Object::Kind::Intermediate);
1263 if(obj.kind == Object::Kind::Constant)
1264 {
1265 arrayIndex = shader.GetConstScalarInt(indexIds[0]);
1266 }
1267 else
1268 {
1269 nonUniform |= shader.GetDecorationsForId(indexIds[0]).NonUniform;
1270 arrayIndex = getIntermediate(indexIds[0]).Int(0);
1271 }
1272
1273 start = 1;
1274 typeId = shader.getType(typeId).element;
1275 }
1276 }
1277
1278 auto ptr = GetPointerToData(baseId, arrayIndex, nonUniform);
1279 OffsetToElement(ptr, elementId, d.ArrayStride);
1280
1281 int constantOffset = 0;
1282
1283 for(uint32_t i = start; i < indexIds.size(); i++)
1284 {
1285 auto &type = shader.getType(typeId);
1286 shader.ApplyDecorationsForId(&d, typeId);
1287
1288 switch(type.definition.opcode())
1289 {
1290 case spv::OpTypeStruct:
1291 {
1292 int memberIndex = shader.GetConstScalarInt(indexIds[i]);
1293 shader.ApplyDecorationsForIdMember(&d, typeId, memberIndex);
1294 ASSERT(d.HasOffset);
1295 constantOffset += d.Offset;
1296 typeId = type.definition.word(2u + memberIndex);
1297 }
1298 break;
1299 case spv::OpTypeArray:
1300 case spv::OpTypeRuntimeArray:
1301 {
1302 // TODO: b/127950082: Check bounds.
1303 ASSERT(d.HasArrayStride);
1304 auto &obj = shader.getObject(indexIds[i]);
1305 if(obj.kind == Object::Kind::Constant)
1306 {
1307 constantOffset += d.ArrayStride * shader.GetConstScalarInt(indexIds[i]);
1308 }
1309 else
1310 {
1311 ptr += SIMD::Int(d.ArrayStride) * getIntermediate(indexIds[i]).Int(0);
1312 }
1313 typeId = type.element;
1314 }
1315 break;
1316 case spv::OpTypeMatrix:
1317 {
1318 // TODO: b/127950082: Check bounds.
1319 ASSERT(d.HasMatrixStride);
1320 d.InsideMatrix = true;
1321 auto columnStride = (d.HasRowMajor && d.RowMajor) ? static_cast<int32_t>(sizeof(float)) : d.MatrixStride;
1322 auto &obj = shader.getObject(indexIds[i]);
1323 if(obj.kind == Object::Kind::Constant)
1324 {
1325 constantOffset += columnStride * shader.GetConstScalarInt(indexIds[i]);
1326 }
1327 else
1328 {
1329 ptr += SIMD::Int(columnStride) * getIntermediate(indexIds[i]).Int(0);
1330 }
1331 typeId = type.element;
1332 }
1333 break;
1334 case spv::OpTypeVector:
1335 {
1336 auto elemStride = (d.InsideMatrix && d.HasRowMajor && d.RowMajor) ? d.MatrixStride : static_cast<int32_t>(sizeof(float));
1337 auto &obj = shader.getObject(indexIds[i]);
1338 if(obj.kind == Object::Kind::Constant)
1339 {
1340 constantOffset += elemStride * shader.GetConstScalarInt(indexIds[i]);
1341 }
1342 else
1343 {
1344 ptr += SIMD::Int(elemStride) * getIntermediate(indexIds[i]).Int(0);
1345 }
1346 typeId = type.element;
1347 }
1348 break;
1349 default:
1350 UNREACHABLE("%s", shader.OpcodeName(type.definition.opcode()));
1351 }
1352 }
1353
1354 ptr += constantOffset;
1355 return ptr;
1356 }
1357
WalkAccessChain(Object::ID baseId,Object::ID elementId,const Span & indexIds,bool nonUniform) const1358 SIMD::Pointer SpirvEmitter::WalkAccessChain(Object::ID baseId, Object::ID elementId, const Span &indexIds, bool nonUniform) const
1359 {
1360 // TODO: avoid doing per-lane work in some cases if we can?
1361 auto &baseObject = shader.getObject(baseId);
1362 Type::ID typeId = shader.getType(baseObject).element;
1363 Decorations d = shader.GetDecorationsForId(baseObject.typeId());
1364 auto storageClass = shader.getType(baseObject).storageClass;
1365 bool interleavedByLane = IsStorageInterleavedByLane(storageClass);
1366
1367 auto ptr = getPointer(baseId);
1368 OffsetToElement(ptr, elementId, d.ArrayStride);
1369
1370 int constantOffset = 0;
1371
1372 for(uint32_t i = 0; i < indexIds.size(); i++)
1373 {
1374 auto &type = shader.getType(typeId);
1375 switch(type.opcode())
1376 {
1377 case spv::OpTypeStruct:
1378 {
1379 int memberIndex = shader.GetConstScalarInt(indexIds[i]);
1380 int offsetIntoStruct = 0;
1381 for(auto j = 0; j < memberIndex; j++)
1382 {
1383 auto memberType = type.definition.word(2u + j);
1384 offsetIntoStruct += shader.getType(memberType).componentCount * sizeof(float);
1385 }
1386 constantOffset += offsetIntoStruct;
1387 typeId = type.definition.word(2u + memberIndex);
1388 }
1389 break;
1390
1391 case spv::OpTypeVector:
1392 case spv::OpTypeMatrix:
1393 case spv::OpTypeArray:
1394 case spv::OpTypeRuntimeArray:
1395 {
1396 // TODO(b/127950082): Check bounds.
1397 if(storageClass == spv::StorageClassUniformConstant)
1398 {
1399 // indexing into an array of descriptors.
1400 auto d = shader.descriptorDecorations.at(baseId);
1401 ASSERT(d.DescriptorSet >= 0);
1402 ASSERT(d.Binding >= 0);
1403 uint32_t descriptorSize = routine->pipelineLayout->getDescriptorSize(d.DescriptorSet, d.Binding);
1404
1405 auto &obj = shader.getObject(indexIds[i]);
1406 if(obj.kind == Object::Kind::Constant)
1407 {
1408 ptr += descriptorSize * shader.GetConstScalarInt(indexIds[i]);
1409 }
1410 else
1411 {
1412 nonUniform |= shader.GetDecorationsForId(indexIds[i]).NonUniform;
1413 SIMD::Int intermediate = getIntermediate(indexIds[i]).Int(0);
1414 if(nonUniform)
1415 {
1416 // NonUniform array data can deal with pointers not bound by a 32-bit address
1417 // space, so we need to ensure we're using an array pointer, and not a base+offset
1418 // pointer.
1419 std::vector<Pointer<Byte>> pointers(SIMD::Width);
1420 for(int i = 0; i < SIMD::Width; i++)
1421 {
1422 pointers[i] = ptr.getPointerForLane(i);
1423 }
1424 ptr = SIMD::Pointer(pointers);
1425 ptr += descriptorSize * intermediate;
1426 }
1427 else
1428 {
1429 ptr += descriptorSize * Extract(intermediate, 0);
1430 }
1431 }
1432 }
1433 else
1434 {
1435 auto stride = shader.getType(type.element).componentCount * static_cast<uint32_t>(sizeof(float));
1436
1437 if(interleavedByLane)
1438 {
1439 stride *= SIMD::Width;
1440 }
1441
1442 if(shader.getObject(indexIds[i]).kind == Object::Kind::Constant)
1443 {
1444 ptr += stride * shader.GetConstScalarInt(indexIds[i]);
1445 }
1446 else
1447 {
1448 ptr += SIMD::Int(stride) * getIntermediate(indexIds[i]).Int(0);
1449 }
1450 }
1451 typeId = type.element;
1452 }
1453 break;
1454
1455 default:
1456 UNREACHABLE("%s", shader.OpcodeName(type.opcode()));
1457 }
1458 }
1459
1460 if(constantOffset != 0)
1461 {
1462 if(interleavedByLane)
1463 {
1464 constantOffset *= SIMD::Width;
1465 }
1466
1467 ptr += constantOffset;
1468 }
1469
1470 return ptr;
1471 }
1472
WalkLiteralAccessChain(Type::ID typeId,const Span & indexes) const1473 uint32_t Spirv::WalkLiteralAccessChain(Type::ID typeId, const Span &indexes) const
1474 {
1475 uint32_t componentOffset = 0;
1476
1477 for(uint32_t i = 0; i < indexes.size(); i++)
1478 {
1479 auto &type = getType(typeId);
1480 switch(type.opcode())
1481 {
1482 case spv::OpTypeStruct:
1483 {
1484 int memberIndex = indexes[i];
1485 int offsetIntoStruct = 0;
1486 for(auto j = 0; j < memberIndex; j++)
1487 {
1488 auto memberType = type.definition.word(2u + j);
1489 offsetIntoStruct += getType(memberType).componentCount;
1490 }
1491 componentOffset += offsetIntoStruct;
1492 typeId = type.definition.word(2u + memberIndex);
1493 }
1494 break;
1495
1496 case spv::OpTypeVector:
1497 case spv::OpTypeMatrix:
1498 case spv::OpTypeArray:
1499 {
1500 auto elementType = type.definition.word(2);
1501 auto stride = getType(elementType).componentCount;
1502 componentOffset += stride * indexes[i];
1503 typeId = elementType;
1504 }
1505 break;
1506
1507 default:
1508 UNREACHABLE("%s", OpcodeName(type.opcode()));
1509 }
1510 }
1511
1512 return componentOffset;
1513 }
1514
Apply(spv::Decoration decoration,uint32_t arg)1515 void Spirv::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
1516 {
1517 switch(decoration)
1518 {
1519 case spv::DecorationLocation:
1520 HasLocation = true;
1521 Location = static_cast<int32_t>(arg);
1522 break;
1523 case spv::DecorationComponent:
1524 HasComponent = true;
1525 Component = arg;
1526 break;
1527 case spv::DecorationBuiltIn:
1528 HasBuiltIn = true;
1529 BuiltIn = static_cast<spv::BuiltIn>(arg);
1530 break;
1531 case spv::DecorationFlat:
1532 Flat = true;
1533 break;
1534 case spv::DecorationNoPerspective:
1535 NoPerspective = true;
1536 break;
1537 case spv::DecorationCentroid:
1538 Centroid = true;
1539 break;
1540 case spv::DecorationBlock:
1541 Block = true;
1542 break;
1543 case spv::DecorationBufferBlock:
1544 BufferBlock = true;
1545 break;
1546 case spv::DecorationOffset:
1547 HasOffset = true;
1548 Offset = static_cast<int32_t>(arg);
1549 break;
1550 case spv::DecorationArrayStride:
1551 HasArrayStride = true;
1552 ArrayStride = static_cast<int32_t>(arg);
1553 break;
1554 case spv::DecorationMatrixStride:
1555 HasMatrixStride = true;
1556 MatrixStride = static_cast<int32_t>(arg);
1557 break;
1558 case spv::DecorationRelaxedPrecision:
1559 RelaxedPrecision = true;
1560 break;
1561 case spv::DecorationRowMajor:
1562 HasRowMajor = true;
1563 RowMajor = true;
1564 break;
1565 case spv::DecorationColMajor:
1566 HasRowMajor = true;
1567 RowMajor = false;
1568 break;
1569 case spv::DecorationNonUniform:
1570 NonUniform = true;
1571 break;
1572 default:
1573 // Intentionally partial, there are many decorations we just don't care about.
1574 break;
1575 }
1576 }
1577
Apply(const Decorations & src)1578 void Spirv::Decorations::Apply(const Decorations &src)
1579 {
1580 // Apply a decoration group to this set of decorations
1581 if(src.HasBuiltIn)
1582 {
1583 HasBuiltIn = true;
1584 BuiltIn = src.BuiltIn;
1585 }
1586
1587 if(src.HasLocation)
1588 {
1589 HasLocation = true;
1590 Location = src.Location;
1591 }
1592
1593 if(src.HasComponent)
1594 {
1595 HasComponent = true;
1596 Component = src.Component;
1597 }
1598
1599 if(src.HasOffset)
1600 {
1601 HasOffset = true;
1602 Offset = src.Offset;
1603 }
1604
1605 if(src.HasArrayStride)
1606 {
1607 HasArrayStride = true;
1608 ArrayStride = src.ArrayStride;
1609 }
1610
1611 if(src.HasMatrixStride)
1612 {
1613 HasMatrixStride = true;
1614 MatrixStride = src.MatrixStride;
1615 }
1616
1617 if(src.HasRowMajor)
1618 {
1619 HasRowMajor = true;
1620 RowMajor = src.RowMajor;
1621 }
1622
1623 Flat |= src.Flat;
1624 NoPerspective |= src.NoPerspective;
1625 Centroid |= src.Centroid;
1626 Block |= src.Block;
1627 BufferBlock |= src.BufferBlock;
1628 RelaxedPrecision |= src.RelaxedPrecision;
1629 InsideMatrix |= src.InsideMatrix;
1630 NonUniform |= src.NonUniform;
1631 }
1632
Apply(const sw::Spirv::DescriptorDecorations & src)1633 void Spirv::DescriptorDecorations::Apply(const sw::Spirv::DescriptorDecorations &src)
1634 {
1635 if(src.DescriptorSet >= 0)
1636 {
1637 DescriptorSet = src.DescriptorSet;
1638 }
1639
1640 if(src.Binding >= 0)
1641 {
1642 Binding = src.Binding;
1643 }
1644
1645 if(src.InputAttachmentIndex >= 0)
1646 {
1647 InputAttachmentIndex = src.InputAttachmentIndex;
1648 }
1649 }
1650
GetDecorationsForId(TypeOrObjectID id) const1651 Spirv::Decorations Spirv::GetDecorationsForId(TypeOrObjectID id) const
1652 {
1653 Decorations d;
1654 ApplyDecorationsForId(&d, id);
1655
1656 return d;
1657 }
1658
ApplyDecorationsForId(Decorations * d,TypeOrObjectID id) const1659 void Spirv::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
1660 {
1661 auto it = decorations.find(id);
1662 if(it != decorations.end())
1663 {
1664 d->Apply(it->second);
1665 }
1666 }
1667
ApplyDecorationsForIdMember(Decorations * d,Type::ID id,uint32_t member) const1668 void Spirv::ApplyDecorationsForIdMember(Decorations *d, Type::ID id, uint32_t member) const
1669 {
1670 auto it = memberDecorations.find(id);
1671 if(it != memberDecorations.end() && member < it->second.size())
1672 {
1673 d->Apply(it->second[member]);
1674 }
1675 }
1676
DefineResult(const InsnIterator & insn)1677 void Spirv::DefineResult(const InsnIterator &insn)
1678 {
1679 Type::ID typeId = insn.word(1);
1680 Object::ID resultId = insn.word(2);
1681 auto &object = defs[resultId];
1682
1683 switch(getType(typeId).opcode())
1684 {
1685 case spv::OpTypeSampledImage:
1686 object.kind = Object::Kind::SampledImage;
1687 break;
1688
1689 case spv::OpTypePointer:
1690 case spv::OpTypeImage:
1691 case spv::OpTypeSampler:
1692 object.kind = Object::Kind::Pointer;
1693 break;
1694
1695 default:
1696 object.kind = Object::Kind::Intermediate;
1697 }
1698
1699 object.definition = insn;
1700 }
1701
getOutOfBoundsBehavior(Object::ID pointerId,const vk::PipelineLayout * pipelineLayout) const1702 OutOfBoundsBehavior SpirvShader::getOutOfBoundsBehavior(Object::ID pointerId, const vk::PipelineLayout *pipelineLayout) const
1703 {
1704 auto it = descriptorDecorations.find(pointerId);
1705 if(it != descriptorDecorations.end())
1706 {
1707 const auto &d = it->second;
1708 if((d.DescriptorSet >= 0) && (d.Binding >= 0))
1709 {
1710 auto descriptorType = pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
1711 if(descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
1712 {
1713 return OutOfBoundsBehavior::UndefinedBehavior;
1714 }
1715 }
1716 }
1717
1718 auto &pointer = getObject(pointerId);
1719 auto &pointerTy = getType(pointer);
1720 switch(pointerTy.storageClass)
1721 {
1722 case spv::StorageClassUniform:
1723 case spv::StorageClassStorageBuffer:
1724 // Buffer resource access. robustBufferAccess feature applies.
1725 return robustBufferAccess ? OutOfBoundsBehavior::RobustBufferAccess
1726 : OutOfBoundsBehavior::UndefinedBehavior;
1727
1728 case spv::StorageClassPhysicalStorageBuffer:
1729 return OutOfBoundsBehavior::UndefinedBehavior;
1730
1731 case spv::StorageClassImage:
1732 // VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
1733 // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
1734 return OutOfBoundsBehavior::Nullify;
1735
1736 case spv::StorageClassInput:
1737 if(executionModel == spv::ExecutionModelVertex)
1738 {
1739 // Vertex attributes follow robustBufferAccess rules.
1740 return robustBufferAccess ? OutOfBoundsBehavior::RobustBufferAccess
1741 : OutOfBoundsBehavior::UndefinedBehavior;
1742 }
1743 // Fall through to default case.
1744 default:
1745 // TODO(b/192310780): StorageClassFunction out-of-bounds accesses are undefined behavior.
1746 // TODO(b/137183137): Optimize if the pointer resulted from OpInBoundsAccessChain.
1747 // TODO(b/131224163): Optimize cases statically known to be within bounds.
1748 return OutOfBoundsBehavior::UndefinedValue;
1749 }
1750
1751 return OutOfBoundsBehavior::Nullify;
1752 }
1753
getInputAttachmentFormat(const vk::Attachments & attachments,int32_t index) const1754 vk::Format SpirvShader::getInputAttachmentFormat(const vk::Attachments &attachments, int32_t index) const
1755 {
1756 if(isUsedWithDynamicRendering)
1757 {
1758 // If no index is given in the shader, it refers to the depth/stencil
1759 // attachment.
1760 if(index < 0 || index == depthInputIndex || index == stencilInputIndex)
1761 {
1762 return attachments.depthStencilFormat();
1763 }
1764
1765 // See if the input index is mapped to an attachment. If it isn't, the
1766 // mapping is identity.
1767 int32_t attachmentIndex = index;
1768 if(inputIndexToColorIndex.count(index) > 0)
1769 {
1770 attachmentIndex = inputIndexToColorIndex.at(index);
1771 }
1772
1773 // Map the index to its location. This is where read-only input attachments
1774 // that aren't mapped to any color attachment cannot be supported the way
1775 // SwiftShader currently works (see comment above `inputIndexToColorIndex`).
1776 ASSERT(attachmentIndex >= 0 && attachmentIndex < sw::MAX_COLOR_BUFFERS);
1777 const int32_t location = attachments.indexToLocation[attachmentIndex];
1778
1779 return attachments.colorFormat(location);
1780 }
1781
1782 return inputAttachmentFormats[index];
1783 }
1784
1785 // emit-time
1786
emitProlog(SpirvRoutine * routine) const1787 void SpirvShader::emitProlog(SpirvRoutine *routine) const
1788 {
1789 for(auto insn : *this)
1790 {
1791 switch(insn.opcode())
1792 {
1793 case spv::OpVariable:
1794 {
1795 auto resultPointerType = getType(insn.resultTypeId());
1796 auto pointeeType = getType(resultPointerType.element);
1797
1798 if(pointeeType.componentCount > 0)
1799 {
1800 routine->createVariable(insn.resultId(), pointeeType.componentCount);
1801 }
1802 }
1803 break;
1804
1805 case spv::OpImageSampleImplicitLod:
1806 case spv::OpImageSampleExplicitLod:
1807 case spv::OpImageSampleDrefImplicitLod:
1808 case spv::OpImageSampleDrefExplicitLod:
1809 case spv::OpImageSampleProjImplicitLod:
1810 case spv::OpImageSampleProjExplicitLod:
1811 case spv::OpImageSampleProjDrefImplicitLod:
1812 case spv::OpImageSampleProjDrefExplicitLod:
1813 case spv::OpImageFetch:
1814 case spv::OpImageGather:
1815 case spv::OpImageDrefGather:
1816 case spv::OpImageWrite:
1817 case spv::OpImageQueryLod:
1818 {
1819 // The 'inline' sampler caches must be created in the prolog to initialize the tags.
1820 uint32_t instructionPosition = insn.distanceFrom(this->begin());
1821 routine->samplerCache.emplace(instructionPosition, SpirvRoutine::SamplerCache{});
1822 }
1823 break;
1824
1825 default:
1826 // Nothing else produces interface variables, so can all be safely ignored.
1827 break;
1828 }
1829 }
1830 }
1831
emit(SpirvRoutine * routine,const RValue<SIMD::Int> & activeLaneMask,const RValue<SIMD::Int> & storesAndAtomicsMask,const vk::DescriptorSet::Bindings & descriptorSets,const vk::Attachments * attachments,unsigned int multiSampleCount) const1832 void SpirvShader::emit(SpirvRoutine *routine, const RValue<SIMD::Int> &activeLaneMask, const RValue<SIMD::Int> &storesAndAtomicsMask, const vk::DescriptorSet::Bindings &descriptorSets, const vk::Attachments *attachments, unsigned int multiSampleCount) const
1833 {
1834 SpirvEmitter::emit(*this, routine, entryPoint, activeLaneMask, storesAndAtomicsMask, attachments, descriptorSets, multiSampleCount);
1835 }
1836
SpirvShader(VkShaderStageFlagBits stage,const char * entryPointName,const SpirvBinary & insns,const vk::RenderPass * renderPass,uint32_t subpassIndex,const VkRenderingInputAttachmentIndexInfoKHR * inputAttachmentMapping,bool robustBufferAccess)1837 SpirvShader::SpirvShader(VkShaderStageFlagBits stage,
1838 const char *entryPointName,
1839 const SpirvBinary &insns,
1840 const vk::RenderPass *renderPass,
1841 uint32_t subpassIndex,
1842 const VkRenderingInputAttachmentIndexInfoKHR *inputAttachmentMapping,
1843 bool robustBufferAccess)
1844 : Spirv(stage, entryPointName, insns)
1845 , robustBufferAccess(robustBufferAccess)
1846 , isUsedWithDynamicRendering(renderPass == nullptr)
1847 {
1848 if(renderPass)
1849 {
1850 // capture formats of any input attachments present
1851 auto subpass = renderPass->getSubpass(subpassIndex);
1852 inputAttachmentFormats.reserve(subpass.inputAttachmentCount);
1853 for(auto i = 0u; i < subpass.inputAttachmentCount; i++)
1854 {
1855 auto attachmentIndex = subpass.pInputAttachments[i].attachment;
1856 inputAttachmentFormats.push_back(attachmentIndex != VK_ATTACHMENT_UNUSED
1857 ? renderPass->getAttachment(attachmentIndex).format
1858 : VK_FORMAT_UNDEFINED);
1859 }
1860 }
1861 else if(inputAttachmentMapping)
1862 {
1863 for(auto i = 0u; i < inputAttachmentMapping->colorAttachmentCount; i++)
1864 {
1865 auto inputIndex = inputAttachmentMapping->pColorAttachmentInputIndices != nullptr ? inputAttachmentMapping->pColorAttachmentInputIndices[i] : i;
1866 if(inputIndex != VK_ATTACHMENT_UNUSED)
1867 {
1868 inputIndexToColorIndex[inputIndex] = i;
1869 }
1870 }
1871
1872 if(inputAttachmentMapping->pDepthInputAttachmentIndex)
1873 {
1874 auto attachmentIndex = *inputAttachmentMapping->pDepthInputAttachmentIndex;
1875 if(attachmentIndex != VK_ATTACHMENT_UNUSED)
1876 {
1877 depthInputIndex = attachmentIndex;
1878 }
1879 }
1880
1881 if(inputAttachmentMapping->pStencilInputAttachmentIndex)
1882 {
1883 auto attachmentIndex = *inputAttachmentMapping->pStencilInputAttachmentIndex;
1884 if(attachmentIndex != VK_ATTACHMENT_UNUSED)
1885 {
1886 stencilInputIndex = attachmentIndex;
1887 }
1888 }
1889 }
1890 }
1891
~SpirvShader()1892 SpirvShader::~SpirvShader()
1893 {
1894 }
1895
SpirvEmitter(const SpirvShader & shader,SpirvRoutine * routine,Spirv::Function::ID entryPoint,RValue<SIMD::Int> activeLaneMask,RValue<SIMD::Int> storesAndAtomicsMask,const vk::Attachments * attachments,const vk::DescriptorSet::Bindings & descriptorSets,unsigned int multiSampleCount)1896 SpirvEmitter::SpirvEmitter(const SpirvShader &shader,
1897 SpirvRoutine *routine,
1898 Spirv::Function::ID entryPoint,
1899 RValue<SIMD::Int> activeLaneMask,
1900 RValue<SIMD::Int> storesAndAtomicsMask,
1901 const vk::Attachments *attachments,
1902 const vk::DescriptorSet::Bindings &descriptorSets,
1903 unsigned int multiSampleCount)
1904 : shader(shader)
1905 , routine(routine)
1906 , function(entryPoint)
1907 , activeLaneMaskValue(activeLaneMask.value())
1908 , storesAndAtomicsMaskValue(storesAndAtomicsMask.value())
1909 , attachments(attachments)
1910 , descriptorSets(descriptorSets)
1911 , multiSampleCount(multiSampleCount)
1912 {
1913 }
1914
emit(const SpirvShader & shader,SpirvRoutine * routine,Spirv::Function::ID entryPoint,RValue<SIMD::Int> activeLaneMask,RValue<SIMD::Int> storesAndAtomicsMask,const vk::Attachments * attachments,const vk::DescriptorSet::Bindings & descriptorSets,unsigned int multiSampleCount)1915 void SpirvEmitter::emit(const SpirvShader &shader,
1916 SpirvRoutine *routine,
1917 Spirv::Function::ID entryPoint,
1918 RValue<SIMD::Int> activeLaneMask,
1919 RValue<SIMD::Int> storesAndAtomicsMask,
1920 const vk::Attachments *attachments,
1921 const vk::DescriptorSet::Bindings &descriptorSets,
1922 unsigned int multiSampleCount)
1923 {
1924 SpirvEmitter state(shader, routine, entryPoint, activeLaneMask, storesAndAtomicsMask, attachments, descriptorSets, multiSampleCount);
1925
1926 // Create phi variables
1927 for(auto insn : shader)
1928 {
1929 if(insn.opcode() == spv::OpPhi)
1930 {
1931 auto type = shader.getType(insn.resultTypeId());
1932 state.phis.emplace(insn.resultId(), std::vector<SIMD::Float>(type.componentCount));
1933 }
1934 }
1935
1936 // Emit everything up to the first label
1937 // TODO: Separate out dispatch of block from non-block instructions?
1938 for(auto insn : shader)
1939 {
1940 if(insn.opcode() == spv::OpLabel)
1941 {
1942 break;
1943 }
1944
1945 state.EmitInstruction(insn);
1946 }
1947
1948 // Emit all the blocks starting from entryPoint.
1949 state.EmitBlocks(shader.getFunction(entryPoint).entry);
1950 }
1951
EmitInstructions(InsnIterator begin,InsnIterator end)1952 void SpirvEmitter::EmitInstructions(InsnIterator begin, InsnIterator end)
1953 {
1954 for(auto insn = begin; insn != end; insn++)
1955 {
1956 EmitInstruction(insn);
1957
1958 if(shader.IsTerminator(insn.opcode()))
1959 {
1960 break;
1961 }
1962 }
1963 }
1964
EmitInstruction(InsnIterator insn)1965 void SpirvEmitter::EmitInstruction(InsnIterator insn)
1966 {
1967 auto opcode = insn.opcode();
1968
1969 #if SPIRV_SHADER_ENABLE_DBG
1970 {
1971 auto text = spvtools::spvInstructionBinaryToText(
1972 vk::SPIRV_VERSION,
1973 insn.data(),
1974 insn.wordCount(),
1975 shader.insns.data(),
1976 shader.insns.size(),
1977 SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1978 SPIRV_SHADER_DBG("{0}", text);
1979 }
1980 #endif // ENABLE_DBG_MSGS
1981
1982 if(shader.IsTerminator(opcode))
1983 {
1984 switch(opcode)
1985 {
1986 case spv::OpBranch:
1987 return EmitBranch(insn);
1988
1989 case spv::OpBranchConditional:
1990 return EmitBranchConditional(insn);
1991
1992 case spv::OpSwitch:
1993 return EmitSwitch(insn);
1994
1995 case spv::OpUnreachable:
1996 return EmitUnreachable(insn);
1997
1998 case spv::OpReturn:
1999 return EmitReturn(insn);
2000
2001 case spv::OpKill:
2002 case spv::OpTerminateInvocation:
2003 return EmitTerminateInvocation(insn);
2004
2005 default:
2006 UNREACHABLE("Unknown terminal instruction %s", shader.OpcodeName(opcode));
2007 break;
2008 }
2009 }
2010 else // Non-terminal instructions
2011 {
2012 switch(opcode)
2013 {
2014 case spv::OpTypeVoid:
2015 case spv::OpTypeInt:
2016 case spv::OpTypeFloat:
2017 case spv::OpTypeBool:
2018 case spv::OpTypeVector:
2019 case spv::OpTypeArray:
2020 case spv::OpTypeRuntimeArray:
2021 case spv::OpTypeMatrix:
2022 case spv::OpTypeStruct:
2023 case spv::OpTypePointer:
2024 case spv::OpTypeForwardPointer:
2025 case spv::OpTypeFunction:
2026 case spv::OpTypeImage:
2027 case spv::OpTypeSampledImage:
2028 case spv::OpTypeSampler:
2029 case spv::OpExecutionMode:
2030 case spv::OpExecutionModeId:
2031 case spv::OpMemoryModel:
2032 case spv::OpFunction:
2033 case spv::OpFunctionEnd:
2034 case spv::OpConstant:
2035 case spv::OpConstantNull:
2036 case spv::OpConstantTrue:
2037 case spv::OpConstantFalse:
2038 case spv::OpConstantComposite:
2039 case spv::OpSpecConstant:
2040 case spv::OpSpecConstantTrue:
2041 case spv::OpSpecConstantFalse:
2042 case spv::OpSpecConstantComposite:
2043 case spv::OpSpecConstantOp:
2044 case spv::OpUndef:
2045 case spv::OpExtension:
2046 case spv::OpCapability:
2047 case spv::OpEntryPoint:
2048 case spv::OpExtInstImport:
2049 case spv::OpDecorate:
2050 case spv::OpMemberDecorate:
2051 case spv::OpGroupDecorate:
2052 case spv::OpGroupMemberDecorate:
2053 case spv::OpDecorationGroup:
2054 case spv::OpDecorateId:
2055 case spv::OpDecorateString:
2056 case spv::OpMemberDecorateString:
2057 case spv::OpName:
2058 case spv::OpMemberName:
2059 case spv::OpSource:
2060 case spv::OpSourceContinued:
2061 case spv::OpSourceExtension:
2062 case spv::OpNoLine:
2063 case spv::OpModuleProcessed:
2064 case spv::OpString:
2065 // Nothing to do at emit time. These are either fully handled at analysis time,
2066 // or don't require any work at all.
2067 return;
2068
2069 case spv::OpLine:
2070 return; // TODO(b/251802301)
2071
2072 case spv::OpLabel:
2073 return;
2074
2075 case spv::OpVariable:
2076 return EmitVariable(insn);
2077
2078 case spv::OpLoad:
2079 case spv::OpAtomicLoad:
2080 return EmitLoad(insn);
2081
2082 case spv::OpStore:
2083 case spv::OpAtomicStore:
2084 return EmitStore(insn);
2085
2086 case spv::OpAtomicIAdd:
2087 case spv::OpAtomicISub:
2088 case spv::OpAtomicSMin:
2089 case spv::OpAtomicSMax:
2090 case spv::OpAtomicUMin:
2091 case spv::OpAtomicUMax:
2092 case spv::OpAtomicAnd:
2093 case spv::OpAtomicOr:
2094 case spv::OpAtomicXor:
2095 case spv::OpAtomicIIncrement:
2096 case spv::OpAtomicIDecrement:
2097 case spv::OpAtomicExchange:
2098 return EmitAtomicOp(insn);
2099
2100 case spv::OpAtomicCompareExchange:
2101 return EmitAtomicCompareExchange(insn);
2102
2103 case spv::OpAccessChain:
2104 case spv::OpInBoundsAccessChain:
2105 case spv::OpPtrAccessChain:
2106 return EmitAccessChain(insn);
2107
2108 case spv::OpCompositeConstruct:
2109 return EmitCompositeConstruct(insn);
2110
2111 case spv::OpCompositeInsert:
2112 return EmitCompositeInsert(insn);
2113
2114 case spv::OpCompositeExtract:
2115 return EmitCompositeExtract(insn);
2116
2117 case spv::OpVectorShuffle:
2118 return EmitVectorShuffle(insn);
2119
2120 case spv::OpVectorExtractDynamic:
2121 return EmitVectorExtractDynamic(insn);
2122
2123 case spv::OpVectorInsertDynamic:
2124 return EmitVectorInsertDynamic(insn);
2125
2126 case spv::OpVectorTimesScalar:
2127 case spv::OpMatrixTimesScalar:
2128 return EmitVectorTimesScalar(insn);
2129
2130 case spv::OpMatrixTimesVector:
2131 return EmitMatrixTimesVector(insn);
2132
2133 case spv::OpVectorTimesMatrix:
2134 return EmitVectorTimesMatrix(insn);
2135
2136 case spv::OpMatrixTimesMatrix:
2137 return EmitMatrixTimesMatrix(insn);
2138
2139 case spv::OpOuterProduct:
2140 return EmitOuterProduct(insn);
2141
2142 case spv::OpTranspose:
2143 return EmitTranspose(insn);
2144
2145 case spv::OpNot:
2146 case spv::OpBitFieldInsert:
2147 case spv::OpBitFieldSExtract:
2148 case spv::OpBitFieldUExtract:
2149 case spv::OpBitReverse:
2150 case spv::OpBitCount:
2151 case spv::OpSNegate:
2152 case spv::OpFNegate:
2153 case spv::OpLogicalNot:
2154 case spv::OpConvertFToU:
2155 case spv::OpConvertFToS:
2156 case spv::OpConvertSToF:
2157 case spv::OpConvertUToF:
2158 case spv::OpBitcast:
2159 case spv::OpIsInf:
2160 case spv::OpIsNan:
2161 case spv::OpDPdx:
2162 case spv::OpDPdxCoarse:
2163 case spv::OpDPdy:
2164 case spv::OpDPdyCoarse:
2165 case spv::OpFwidth:
2166 case spv::OpFwidthCoarse:
2167 case spv::OpDPdxFine:
2168 case spv::OpDPdyFine:
2169 case spv::OpFwidthFine:
2170 case spv::OpQuantizeToF16:
2171 return EmitUnaryOp(insn);
2172
2173 case spv::OpIAdd:
2174 case spv::OpISub:
2175 case spv::OpIMul:
2176 case spv::OpSDiv:
2177 case spv::OpUDiv:
2178 case spv::OpFAdd:
2179 case spv::OpFSub:
2180 case spv::OpFMul:
2181 case spv::OpFDiv:
2182 case spv::OpFMod:
2183 case spv::OpFRem:
2184 case spv::OpFOrdEqual:
2185 case spv::OpFUnordEqual:
2186 case spv::OpFOrdNotEqual:
2187 case spv::OpFUnordNotEqual:
2188 case spv::OpFOrdLessThan:
2189 case spv::OpFUnordLessThan:
2190 case spv::OpFOrdGreaterThan:
2191 case spv::OpFUnordGreaterThan:
2192 case spv::OpFOrdLessThanEqual:
2193 case spv::OpFUnordLessThanEqual:
2194 case spv::OpFOrdGreaterThanEqual:
2195 case spv::OpFUnordGreaterThanEqual:
2196 case spv::OpSMod:
2197 case spv::OpSRem:
2198 case spv::OpUMod:
2199 case spv::OpIEqual:
2200 case spv::OpINotEqual:
2201 case spv::OpUGreaterThan:
2202 case spv::OpSGreaterThan:
2203 case spv::OpUGreaterThanEqual:
2204 case spv::OpSGreaterThanEqual:
2205 case spv::OpULessThan:
2206 case spv::OpSLessThan:
2207 case spv::OpULessThanEqual:
2208 case spv::OpSLessThanEqual:
2209 case spv::OpShiftRightLogical:
2210 case spv::OpShiftRightArithmetic:
2211 case spv::OpShiftLeftLogical:
2212 case spv::OpBitwiseOr:
2213 case spv::OpBitwiseXor:
2214 case spv::OpBitwiseAnd:
2215 case spv::OpLogicalOr:
2216 case spv::OpLogicalAnd:
2217 case spv::OpLogicalEqual:
2218 case spv::OpLogicalNotEqual:
2219 case spv::OpUMulExtended:
2220 case spv::OpSMulExtended:
2221 case spv::OpIAddCarry:
2222 case spv::OpISubBorrow:
2223 return EmitBinaryOp(insn);
2224
2225 case spv::OpDot:
2226 case spv::OpSDot:
2227 case spv::OpUDot:
2228 case spv::OpSUDot:
2229 case spv::OpSDotAccSat:
2230 case spv::OpUDotAccSat:
2231 case spv::OpSUDotAccSat:
2232 return EmitDot(insn);
2233
2234 case spv::OpSelect:
2235 return EmitSelect(insn);
2236
2237 case spv::OpExtInst:
2238 return EmitExtendedInstruction(insn);
2239
2240 case spv::OpAny:
2241 return EmitAny(insn);
2242
2243 case spv::OpAll:
2244 return EmitAll(insn);
2245
2246 case spv::OpPhi:
2247 return EmitPhi(insn);
2248
2249 case spv::OpSelectionMerge:
2250 case spv::OpLoopMerge:
2251 return;
2252
2253 case spv::OpFunctionCall:
2254 return EmitFunctionCall(insn);
2255
2256 case spv::OpDemoteToHelperInvocation:
2257 return EmitDemoteToHelperInvocation(insn);
2258
2259 case spv::OpIsHelperInvocationEXT:
2260 return EmitIsHelperInvocation(insn);
2261
2262 case spv::OpImageSampleImplicitLod:
2263 case spv::OpImageSampleExplicitLod:
2264 case spv::OpImageSampleDrefImplicitLod:
2265 case spv::OpImageSampleDrefExplicitLod:
2266 case spv::OpImageSampleProjImplicitLod:
2267 case spv::OpImageSampleProjExplicitLod:
2268 case spv::OpImageSampleProjDrefImplicitLod:
2269 case spv::OpImageSampleProjDrefExplicitLod:
2270 case spv::OpImageGather:
2271 case spv::OpImageDrefGather:
2272 case spv::OpImageFetch:
2273 case spv::OpImageQueryLod:
2274 return EmitImageSample(ImageInstruction(insn, shader, *this));
2275
2276 case spv::OpImageQuerySizeLod:
2277 return EmitImageQuerySizeLod(insn);
2278
2279 case spv::OpImageQuerySize:
2280 return EmitImageQuerySize(insn);
2281
2282 case spv::OpImageQueryLevels:
2283 return EmitImageQueryLevels(insn);
2284
2285 case spv::OpImageQuerySamples:
2286 return EmitImageQuerySamples(insn);
2287
2288 case spv::OpImageRead:
2289 return EmitImageRead(ImageInstruction(insn, shader, *this));
2290
2291 case spv::OpImageWrite:
2292 return EmitImageWrite(ImageInstruction(insn, shader, *this));
2293
2294 case spv::OpImageTexelPointer:
2295 return EmitImageTexelPointer(ImageInstruction(insn, shader, *this));
2296
2297 case spv::OpSampledImage:
2298 return EmitSampledImage(insn);
2299
2300 case spv::OpImage:
2301 return EmitImage(insn);
2302
2303 case spv::OpCopyObject:
2304 case spv::OpCopyLogical:
2305 return EmitCopyObject(insn);
2306
2307 case spv::OpCopyMemory:
2308 return EmitCopyMemory(insn);
2309
2310 case spv::OpControlBarrier:
2311 return EmitControlBarrier(insn);
2312
2313 case spv::OpMemoryBarrier:
2314 return EmitMemoryBarrier(insn);
2315
2316 case spv::OpGroupNonUniformElect:
2317 case spv::OpGroupNonUniformAll:
2318 case spv::OpGroupNonUniformAny:
2319 case spv::OpGroupNonUniformAllEqual:
2320 case spv::OpGroupNonUniformBroadcast:
2321 case spv::OpGroupNonUniformBroadcastFirst:
2322 case spv::OpGroupNonUniformQuadBroadcast:
2323 case spv::OpGroupNonUniformQuadSwap:
2324 case spv::OpGroupNonUniformBallot:
2325 case spv::OpGroupNonUniformInverseBallot:
2326 case spv::OpGroupNonUniformBallotBitExtract:
2327 case spv::OpGroupNonUniformBallotBitCount:
2328 case spv::OpGroupNonUniformBallotFindLSB:
2329 case spv::OpGroupNonUniformBallotFindMSB:
2330 case spv::OpGroupNonUniformShuffle:
2331 case spv::OpGroupNonUniformShuffleXor:
2332 case spv::OpGroupNonUniformShuffleUp:
2333 case spv::OpGroupNonUniformShuffleDown:
2334 case spv::OpGroupNonUniformIAdd:
2335 case spv::OpGroupNonUniformFAdd:
2336 case spv::OpGroupNonUniformIMul:
2337 case spv::OpGroupNonUniformFMul:
2338 case spv::OpGroupNonUniformSMin:
2339 case spv::OpGroupNonUniformUMin:
2340 case spv::OpGroupNonUniformFMin:
2341 case spv::OpGroupNonUniformSMax:
2342 case spv::OpGroupNonUniformUMax:
2343 case spv::OpGroupNonUniformFMax:
2344 case spv::OpGroupNonUniformBitwiseAnd:
2345 case spv::OpGroupNonUniformBitwiseOr:
2346 case spv::OpGroupNonUniformBitwiseXor:
2347 case spv::OpGroupNonUniformLogicalAnd:
2348 case spv::OpGroupNonUniformLogicalOr:
2349 case spv::OpGroupNonUniformLogicalXor:
2350 return EmitGroupNonUniform(insn);
2351
2352 case spv::OpArrayLength:
2353 return EmitArrayLength(insn);
2354
2355 default:
2356 UNREACHABLE("Unknown non-terminal instruction %s", shader.OpcodeName(opcode));
2357 break;
2358 }
2359 }
2360 }
2361
EmitAccessChain(InsnIterator insn)2362 void SpirvEmitter::EmitAccessChain(InsnIterator insn)
2363 {
2364 Type::ID typeId = insn.word(1);
2365 Object::ID resultId = insn.word(2);
2366 bool nonUniform = shader.GetDecorationsForId(resultId).NonUniform;
2367 Object::ID baseId = insn.word(3);
2368 auto &type = shader.getType(typeId);
2369 ASSERT(type.componentCount == 1);
2370 ASSERT(shader.getObject(resultId).kind == Object::Kind::Pointer);
2371
2372 Object::ID elementId = (insn.opcode() == spv::OpPtrAccessChain) ? insn.word(4) : 0;
2373 int indexId = (insn.opcode() == spv::OpPtrAccessChain) ? 5 : 4;
2374 // TODO(b/236280746): Eliminate lookahead by optimizing inside SIMD::Pointer.
2375 for(auto it = insn; it != shader.end(); it++)
2376 {
2377 if(it.opcode() == spv::OpLoad)
2378 {
2379 Object::ID pointerId = it.word(3);
2380 if(pointerId.value() == resultId.value())
2381 {
2382 nonUniform |= shader.GetDecorationsForId(it.word(2)).NonUniform;
2383 break;
2384 }
2385 }
2386 }
2387
2388 if(Spirv::IsExplicitLayout(type.storageClass))
2389 {
2390 auto ptr = WalkExplicitLayoutAccessChain(baseId, elementId, Span(insn, indexId, insn.wordCount() - indexId), nonUniform);
2391 createPointer(resultId, ptr);
2392 }
2393 else
2394 {
2395 auto ptr = WalkAccessChain(baseId, elementId, Span(insn, indexId, insn.wordCount() - indexId), nonUniform);
2396 createPointer(resultId, ptr);
2397 }
2398 }
2399
EmitCompositeConstruct(InsnIterator insn)2400 void SpirvEmitter::EmitCompositeConstruct(InsnIterator insn)
2401 {
2402 auto &type = shader.getType(insn.resultTypeId());
2403 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2404 auto offset = 0u;
2405
2406 for(auto i = 0u; i < insn.wordCount() - 3; i++)
2407 {
2408 Object::ID srcObjectId = insn.word(3u + i);
2409 auto &srcObject = shader.getObject(srcObjectId);
2410 auto &srcObjectTy = shader.getType(srcObject);
2411 Operand srcObjectAccess(shader, *this, srcObjectId);
2412
2413 for(auto j = 0u; j < srcObjectTy.componentCount; j++)
2414 {
2415 dst.move(offset++, srcObjectAccess.Float(j));
2416 }
2417 }
2418 }
2419
EmitCompositeInsert(InsnIterator insn)2420 void SpirvEmitter::EmitCompositeInsert(InsnIterator insn)
2421 {
2422 Type::ID resultTypeId = insn.word(1);
2423 auto &type = shader.getType(resultTypeId);
2424 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2425 auto &newPartObject = shader.getObject(insn.word(3));
2426 auto &newPartObjectTy = shader.getType(newPartObject);
2427 auto firstNewComponent = shader.WalkLiteralAccessChain(resultTypeId, Span(insn, 5, insn.wordCount() - 5));
2428
2429 Operand srcObjectAccess(shader, *this, insn.word(4));
2430 Operand newPartObjectAccess(shader, *this, insn.word(3));
2431
2432 // old components before
2433 for(auto i = 0u; i < firstNewComponent; i++)
2434 {
2435 dst.move(i, srcObjectAccess.Float(i));
2436 }
2437 // new part
2438 for(auto i = 0u; i < newPartObjectTy.componentCount; i++)
2439 {
2440 dst.move(firstNewComponent + i, newPartObjectAccess.Float(i));
2441 }
2442 // old components after
2443 for(auto i = firstNewComponent + newPartObjectTy.componentCount; i < type.componentCount; i++)
2444 {
2445 dst.move(i, srcObjectAccess.Float(i));
2446 }
2447 }
2448
EmitCompositeExtract(InsnIterator insn)2449 void SpirvEmitter::EmitCompositeExtract(InsnIterator insn)
2450 {
2451 auto &type = shader.getType(insn.resultTypeId());
2452 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2453 auto &compositeObject = shader.getObject(insn.word(3));
2454 Type::ID compositeTypeId = compositeObject.definition.word(1);
2455 auto firstComponent = shader.WalkLiteralAccessChain(compositeTypeId, Span(insn, 4, insn.wordCount() - 4));
2456
2457 Operand compositeObjectAccess(shader, *this, insn.word(3));
2458 for(auto i = 0u; i < type.componentCount; i++)
2459 {
2460 dst.move(i, compositeObjectAccess.Float(firstComponent + i));
2461 }
2462 }
2463
EmitVectorShuffle(InsnIterator insn)2464 void SpirvEmitter::EmitVectorShuffle(InsnIterator insn)
2465 {
2466 // Note: number of components in result, first vector, and second vector are all independent.
2467 uint32_t resultSize = shader.getType(insn.resultTypeId()).componentCount;
2468 uint32_t firstVectorSize = shader.getObjectType(insn.word(3)).componentCount;
2469
2470 auto &result = createIntermediate(insn.resultId(), resultSize);
2471 Operand firstVector(shader, *this, insn.word(3));
2472 Operand secondVector(shader, *this, insn.word(4));
2473
2474 for(uint32_t i = 0u; i < resultSize; i++)
2475 {
2476 uint32_t selector = insn.word(5 + i);
2477 if(selector == 0xFFFFFFFF) // Undefined value.
2478 {
2479 result.move(i, SIMD::Float());
2480 }
2481 else if(selector < firstVectorSize)
2482 {
2483 result.move(i, firstVector.Float(selector));
2484 }
2485 else
2486 {
2487 result.move(i, secondVector.Float(selector - firstVectorSize));
2488 }
2489 }
2490 }
2491
EmitVectorExtractDynamic(InsnIterator insn)2492 void SpirvEmitter::EmitVectorExtractDynamic(InsnIterator insn)
2493 {
2494 auto &type = shader.getType(insn.resultTypeId());
2495 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2496 auto &srcType = shader.getObjectType(insn.word(3));
2497
2498 Operand src(shader, *this, insn.word(3));
2499 Operand index(shader, *this, insn.word(4));
2500
2501 SIMD::UInt v = SIMD::UInt(0);
2502
2503 for(auto i = 0u; i < srcType.componentCount; i++)
2504 {
2505 v |= CmpEQ(index.UInt(0), SIMD::UInt(i)) & src.UInt(i);
2506 }
2507
2508 dst.move(0, v);
2509 }
2510
EmitVectorInsertDynamic(InsnIterator insn)2511 void SpirvEmitter::EmitVectorInsertDynamic(InsnIterator insn)
2512 {
2513 auto &type = shader.getType(insn.resultTypeId());
2514 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2515
2516 Operand src(shader, *this, insn.word(3));
2517 Operand component(shader, *this, insn.word(4));
2518 Operand index(shader, *this, insn.word(5));
2519
2520 for(auto i = 0u; i < type.componentCount; i++)
2521 {
2522 SIMD::UInt mask = CmpEQ(SIMD::UInt(i), index.UInt(0));
2523 dst.move(i, (src.UInt(i) & ~mask) | (component.UInt(0) & mask));
2524 }
2525 }
2526
EmitSelect(InsnIterator insn)2527 void SpirvEmitter::EmitSelect(InsnIterator insn)
2528 {
2529 auto &type = shader.getType(insn.resultTypeId());
2530 auto result = shader.getObject(insn.resultId());
2531 auto cond = Operand(shader, *this, insn.word(3));
2532 auto condIsScalar = (cond.componentCount == 1);
2533
2534 if(result.kind == Object::Kind::Pointer)
2535 {
2536 ASSERT(condIsScalar);
2537 ASSERT(type.storageClass == spv::StorageClassPhysicalStorageBuffer);
2538
2539 auto &lhs = getPointer(insn.word(4));
2540 auto &rhs = getPointer(insn.word(5));
2541 createPointer(insn.resultId(), SIMD::Pointer::IfThenElse(cond.Int(0), lhs, rhs));
2542
2543 SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), cond);
2544 SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), lhs);
2545 SPIRV_SHADER_DBG("{0}: {1}", insn.word(5), rhs);
2546 }
2547 else
2548 {
2549 auto lhs = Operand(shader, *this, insn.word(4));
2550 auto rhs = Operand(shader, *this, insn.word(5));
2551 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2552
2553 for(auto i = 0u; i < type.componentCount; i++)
2554 {
2555 auto sel = cond.Int(condIsScalar ? 0 : i);
2556 dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i))); // TODO: IfThenElse()
2557 }
2558
2559 SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst);
2560 SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), cond);
2561 SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), lhs);
2562 SPIRV_SHADER_DBG("{0}: {1}", insn.word(5), rhs);
2563 }
2564 }
2565
EmitAny(InsnIterator insn)2566 void SpirvEmitter::EmitAny(InsnIterator insn)
2567 {
2568 auto &type = shader.getType(insn.resultTypeId());
2569 ASSERT(type.componentCount == 1);
2570 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2571 auto &srcType = shader.getObjectType(insn.word(3));
2572 auto src = Operand(shader, *this, insn.word(3));
2573
2574 SIMD::UInt result = src.UInt(0);
2575
2576 for(auto i = 1u; i < srcType.componentCount; i++)
2577 {
2578 result |= src.UInt(i);
2579 }
2580
2581 dst.move(0, result);
2582 }
2583
EmitAll(InsnIterator insn)2584 void SpirvEmitter::EmitAll(InsnIterator insn)
2585 {
2586 auto &type = shader.getType(insn.resultTypeId());
2587 ASSERT(type.componentCount == 1);
2588 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2589 auto &srcType = shader.getObjectType(insn.word(3));
2590 auto src = Operand(shader, *this, insn.word(3));
2591
2592 SIMD::UInt result = src.UInt(0);
2593
2594 for(uint32_t i = 1; i < srcType.componentCount; i++)
2595 {
2596 result &= src.UInt(i);
2597 }
2598
2599 dst.move(0, result);
2600 }
2601
EmitAtomicOp(InsnIterator insn)2602 void SpirvEmitter::EmitAtomicOp(InsnIterator insn)
2603 {
2604 auto &resultType = shader.getType(Type::ID(insn.word(1)));
2605 Object::ID resultId = insn.word(2);
2606 Object::ID pointerId = insn.word(3);
2607 Object::ID semanticsId = insn.word(5);
2608 auto memorySemantics = static_cast<spv::MemorySemanticsMask>(shader.getObject(semanticsId).constantValue[0]);
2609 auto memoryOrder = shader.MemoryOrder(memorySemantics);
2610 // Where no value is provided (increment/decrement) use an implicit value of 1.
2611 auto value = (insn.wordCount() == 7) ? Operand(shader, *this, insn.word(6)).UInt(0) : RValue<SIMD::UInt>(1);
2612 auto &dst = createIntermediate(resultId, resultType.componentCount);
2613 auto ptr = getPointer(pointerId);
2614
2615 SIMD::Int mask = activeLaneMask() & storesAndAtomicsMask();
2616
2617 if((shader.getObject(pointerId).opcode() == spv::OpImageTexelPointer) && ptr.isBasePlusOffset)
2618 {
2619 mask &= ptr.isInBounds(sizeof(int32_t), OutOfBoundsBehavior::Nullify);
2620 }
2621
2622 SIMD::UInt result(0);
2623 for(int j = 0; j < SIMD::Width; j++)
2624 {
2625 If(Extract(mask, j) != 0)
2626 {
2627 auto laneValue = Extract(value, j);
2628 UInt v;
2629 switch(insn.opcode())
2630 {
2631 case spv::OpAtomicIAdd:
2632 case spv::OpAtomicIIncrement:
2633 v = AddAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2634 break;
2635 case spv::OpAtomicISub:
2636 case spv::OpAtomicIDecrement:
2637 v = SubAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2638 break;
2639 case spv::OpAtomicAnd:
2640 v = AndAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2641 break;
2642 case spv::OpAtomicOr:
2643 v = OrAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2644 break;
2645 case spv::OpAtomicXor:
2646 v = XorAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2647 break;
2648 case spv::OpAtomicSMin:
2649 v = As<UInt>(MinAtomic(Pointer<Int>(ptr.getPointerForLane(j)), As<Int>(laneValue), memoryOrder));
2650 break;
2651 case spv::OpAtomicSMax:
2652 v = As<UInt>(MaxAtomic(Pointer<Int>(ptr.getPointerForLane(j)), As<Int>(laneValue), memoryOrder));
2653 break;
2654 case spv::OpAtomicUMin:
2655 v = MinAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2656 break;
2657 case spv::OpAtomicUMax:
2658 v = MaxAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2659 break;
2660 case spv::OpAtomicExchange:
2661 v = ExchangeAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, memoryOrder);
2662 break;
2663 default:
2664 UNREACHABLE("%s", shader.OpcodeName(insn.opcode()));
2665 break;
2666 }
2667 result = Insert(result, v, j);
2668 }
2669 }
2670
2671 dst.move(0, result);
2672 }
2673
EmitAtomicCompareExchange(InsnIterator insn)2674 void SpirvEmitter::EmitAtomicCompareExchange(InsnIterator insn)
2675 {
2676 // Separate from EmitAtomicOp due to different instruction encoding
2677 auto &resultType = shader.getType(Type::ID(insn.word(1)));
2678 Object::ID resultId = insn.word(2);
2679
2680 auto memorySemanticsEqual = static_cast<spv::MemorySemanticsMask>(shader.getObject(insn.word(5)).constantValue[0]);
2681 auto memoryOrderEqual = shader.MemoryOrder(memorySemanticsEqual);
2682 auto memorySemanticsUnequal = static_cast<spv::MemorySemanticsMask>(shader.getObject(insn.word(6)).constantValue[0]);
2683 auto memoryOrderUnequal = shader.MemoryOrder(memorySemanticsUnequal);
2684
2685 auto value = Operand(shader, *this, insn.word(7));
2686 auto comparator = Operand(shader, *this, insn.word(8));
2687 auto &dst = createIntermediate(resultId, resultType.componentCount);
2688 auto ptr = getPointer(insn.word(3));
2689
2690 SIMD::UInt x(0);
2691 auto mask = activeLaneMask() & storesAndAtomicsMask();
2692 for(int j = 0; j < SIMD::Width; j++)
2693 {
2694 If(Extract(mask, j) != 0)
2695 {
2696 auto laneValue = Extract(value.UInt(0), j);
2697 auto laneComparator = Extract(comparator.UInt(0), j);
2698 UInt v = CompareExchangeAtomic(Pointer<UInt>(ptr.getPointerForLane(j)), laneValue, laneComparator, memoryOrderEqual, memoryOrderUnequal);
2699 x = Insert(x, v, j);
2700 }
2701 }
2702
2703 dst.move(0, x);
2704 }
2705
EmitCopyObject(InsnIterator insn)2706 void SpirvEmitter::EmitCopyObject(InsnIterator insn)
2707 {
2708 auto src = Operand(shader, *this, insn.word(3));
2709 if(src.isPointer())
2710 {
2711 createPointer(insn.resultId(), src.Pointer());
2712 }
2713 else if(src.isSampledImage())
2714 {
2715 createSampledImage(insn.resultId(), src.SampledImage());
2716 }
2717 else
2718 {
2719 auto type = shader.getType(insn.resultTypeId());
2720 auto &dst = createIntermediate(insn.resultId(), type.componentCount);
2721 for(uint32_t i = 0; i < type.componentCount; i++)
2722 {
2723 dst.move(i, src.Int(i));
2724 }
2725 }
2726 }
2727
EmitArrayLength(InsnIterator insn)2728 void SpirvEmitter::EmitArrayLength(InsnIterator insn)
2729 {
2730 auto structPtrId = Object::ID(insn.word(3));
2731 auto arrayFieldIdx = insn.word(4);
2732
2733 auto &resultType = shader.getType(insn.resultTypeId());
2734 ASSERT(resultType.componentCount == 1);
2735 ASSERT(resultType.definition.opcode() == spv::OpTypeInt);
2736
2737 auto &structPtrTy = shader.getObjectType(structPtrId);
2738 auto &structTy = shader.getType(structPtrTy.element);
2739 auto arrayId = Type::ID(structTy.definition.word(2 + arrayFieldIdx));
2740
2741 auto &result = createIntermediate(insn.resultId(), 1);
2742 auto structBase = GetPointerToData(structPtrId, 0, false);
2743
2744 Decorations structDecorations = {};
2745 shader.ApplyDecorationsForIdMember(&structDecorations, structPtrTy.element, arrayFieldIdx);
2746 ASSERT(structDecorations.HasOffset);
2747
2748 auto arrayBase = structBase + structDecorations.Offset;
2749 auto arraySizeInBytes = SIMD::Int(arrayBase.limit()) - arrayBase.offsets();
2750
2751 Decorations arrayDecorations = shader.GetDecorationsForId(arrayId);
2752 ASSERT(arrayDecorations.HasArrayStride);
2753 auto arrayLength = arraySizeInBytes / SIMD::Int(arrayDecorations.ArrayStride);
2754
2755 result.move(0, SIMD::Int(arrayLength));
2756 }
2757
EmitExtendedInstruction(InsnIterator insn)2758 void SpirvEmitter::EmitExtendedInstruction(InsnIterator insn)
2759 {
2760 auto ext = shader.getExtension(insn.word(3));
2761 switch(ext.name)
2762 {
2763 case Spirv::Extension::GLSLstd450:
2764 return EmitExtGLSLstd450(insn);
2765 case Spirv::Extension::NonSemanticInfo:
2766 // An extended set name which is prefixed with "NonSemantic." is
2767 // guaranteed to contain only non-semantic instructions and all
2768 // OpExtInst instructions referencing this set can be ignored.
2769 break;
2770 default:
2771 UNREACHABLE("Unknown Extension::Name<%d>", int(ext.name));
2772 }
2773 }
2774
GetConstScalarInt(Object::ID id) const2775 uint32_t Spirv::GetConstScalarInt(Object::ID id) const
2776 {
2777 auto &scopeObj = getObject(id);
2778 ASSERT(scopeObj.kind == Object::Kind::Constant);
2779 ASSERT(getType(scopeObj).componentCount == 1);
2780
2781 return scopeObj.constantValue[0];
2782 }
2783
emitEpilog(SpirvRoutine * routine) const2784 void SpirvShader::emitEpilog(SpirvRoutine *routine) const
2785 {
2786 for(auto insn : *this)
2787 {
2788 if(insn.opcode() == spv::OpVariable)
2789 {
2790 auto &object = getObject(insn.resultId());
2791 auto &objectTy = getType(object);
2792
2793 if(object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
2794 {
2795 auto &dst = routine->getVariable(insn.resultId());
2796 int offset = 0;
2797
2798 VisitInterface(insn.resultId(),
2799 [&](const Decorations &d, AttribType type) {
2800 auto scalarSlot = d.Location << 2 | d.Component;
2801 routine->outputs[scalarSlot] = dst[offset++];
2802 });
2803 }
2804 }
2805 }
2806 }
2807
executionModelToStage(spv::ExecutionModel model)2808 VkShaderStageFlagBits Spirv::executionModelToStage(spv::ExecutionModel model)
2809 {
2810 switch(model)
2811 {
2812 case spv::ExecutionModelVertex: return VK_SHADER_STAGE_VERTEX_BIT;
2813 // case spv::ExecutionModelTessellationControl: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
2814 // case spv::ExecutionModelTessellationEvaluation: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
2815 // case spv::ExecutionModelGeometry: return VK_SHADER_STAGE_GEOMETRY_BIT;
2816 case spv::ExecutionModelFragment: return VK_SHADER_STAGE_FRAGMENT_BIT;
2817 case spv::ExecutionModelGLCompute: return VK_SHADER_STAGE_COMPUTE_BIT;
2818 // case spv::ExecutionModelKernel: return VkShaderStageFlagBits(0); // Not supported by vulkan.
2819 // case spv::ExecutionModelTaskNV: return VK_SHADER_STAGE_TASK_BIT_NV;
2820 // case spv::ExecutionModelMeshNV: return VK_SHADER_STAGE_MESH_BIT_NV;
2821 // case spv::ExecutionModelRayGenerationNV: return VK_SHADER_STAGE_RAYGEN_BIT_NV;
2822 // case spv::ExecutionModelIntersectionNV: return VK_SHADER_STAGE_INTERSECTION_BIT_NV;
2823 // case spv::ExecutionModelAnyHitNV: return VK_SHADER_STAGE_ANY_HIT_BIT_NV;
2824 // case spv::ExecutionModelClosestHitNV: return VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV;
2825 // case spv::ExecutionModelMissNV: return VK_SHADER_STAGE_MISS_BIT_NV;
2826 // case spv::ExecutionModelCallableNV: return VK_SHADER_STAGE_CALLABLE_BIT_NV;
2827 default:
2828 UNSUPPORTED("ExecutionModel: %d", int(model));
2829 return VkShaderStageFlagBits(0);
2830 }
2831 }
2832
Operand(const Spirv & shader,const SpirvEmitter & state,Object::ID objectId)2833 SpirvEmitter::Operand::Operand(const Spirv &shader, const SpirvEmitter &state, Object::ID objectId)
2834 : Operand(state, shader.getObject(objectId))
2835 {}
2836
Operand(const SpirvEmitter & state,const Object & object)2837 SpirvEmitter::Operand::Operand(const SpirvEmitter &state, const Object &object)
2838 : constant(object.kind == Object::Kind::Constant ? object.constantValue.data() : nullptr)
2839 , intermediate(object.kind == Object::Kind::Intermediate ? &state.getIntermediate(object.id()) : nullptr)
2840 , pointer(object.kind == Object::Kind::Pointer ? &state.getPointer(object.id()) : nullptr)
2841 , sampledImage(object.kind == Object::Kind::SampledImage ? &state.getSampledImage(object.id()) : nullptr)
2842 , componentCount(intermediate ? intermediate->componentCount : object.constantValue.size())
2843 {
2844 ASSERT(intermediate || constant || pointer || sampledImage);
2845 }
2846
Operand(const Intermediate & value)2847 SpirvEmitter::Operand::Operand(const Intermediate &value)
2848 : intermediate(&value)
2849 , componentCount(value.componentCount)
2850 {
2851 }
2852
isConstantZero() const2853 bool Spirv::Object::isConstantZero() const
2854 {
2855 if(kind != Kind::Constant)
2856 {
2857 return false;
2858 }
2859
2860 for(uint32_t i = 0; i < constantValue.size(); i++)
2861 {
2862 if(constantValue[i] != 0)
2863 {
2864 return false;
2865 }
2866 }
2867
2868 return true;
2869 }
2870
SpirvRoutine(const vk::PipelineLayout * pipelineLayout)2871 SpirvRoutine::SpirvRoutine(const vk::PipelineLayout *pipelineLayout)
2872 : pipelineLayout(pipelineLayout)
2873 {
2874 }
2875
setImmutableInputBuiltins(const SpirvShader * shader)2876 void SpirvRoutine::setImmutableInputBuiltins(const SpirvShader *shader)
2877 {
2878 setInputBuiltin(shader, spv::BuiltInSubgroupLocalInvocationId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2879 ASSERT(builtin.SizeInComponents == 1);
2880 value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(0, 1, 2, 3));
2881 });
2882
2883 setInputBuiltin(shader, spv::BuiltInSubgroupEqMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2884 ASSERT(builtin.SizeInComponents == 4);
2885 value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(1, 2, 4, 8));
2886 value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2887 value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2888 value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2889 });
2890
2891 setInputBuiltin(shader, spv::BuiltInSubgroupGeMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2892 ASSERT(builtin.SizeInComponents == 4);
2893 value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(15, 14, 12, 8));
2894 value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2895 value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2896 value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2897 });
2898
2899 setInputBuiltin(shader, spv::BuiltInSubgroupGtMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2900 ASSERT(builtin.SizeInComponents == 4);
2901 value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(14, 12, 8, 0));
2902 value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2903 value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2904 value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2905 });
2906
2907 setInputBuiltin(shader, spv::BuiltInSubgroupLeMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2908 ASSERT(builtin.SizeInComponents == 4);
2909 value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(1, 3, 7, 15));
2910 value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2911 value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2912 value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2913 });
2914
2915 setInputBuiltin(shader, spv::BuiltInSubgroupLtMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2916 ASSERT(builtin.SizeInComponents == 4);
2917 value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(0, 1, 3, 7));
2918 value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2919 value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2920 value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2921 });
2922
2923 setInputBuiltin(shader, spv::BuiltInDeviceIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
2924 ASSERT(builtin.SizeInComponents == 1);
2925 // Only a single physical device is supported.
2926 value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
2927 });
2928 }
2929
2930 } // namespace sw
2931