1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <spirv/unified1/spirv.hpp>
16 #include "SpirvShader.hpp"
17 #include "System/Math.hpp"
18 #include "Vulkan/VkDebug.hpp"
19 #include "Device/Config.hpp"
23 volatile int SpirvShader::serialCounter = 1; // Start at 1, 0 is invalid shader.
25 SpirvShader::SpirvShader(InsnStore const &insns)
26 : insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
27 outputs{MAX_INTERFACE_COMPONENTS},
28 serialID{serialCounter++}, modes{}
30 // Simplifying assumptions (to be satisfied by earlier transformations)
31 // - There is exactly one entrypoint in the module, and it's the one we want
32 // - The only input/output OpVariables present are those used by the entrypoint
34 for (auto insn : *this)
36 switch (insn.opcode())
38 case spv::OpExecutionMode:
39 ProcessExecutionMode(insn);
44 TypeOrObjectID targetId = insn.word(1);
45 auto decoration = static_cast<spv::Decoration>(insn.word(2));
46 decorations[targetId].Apply(
48 insn.wordCount() > 3 ? insn.word(3) : 0);
50 if (decoration == spv::DecorationCentroid)
51 modes.NeedsCentroid = true;
55 case spv::OpMemberDecorate:
57 TypeID targetId = insn.word(1);
58 auto memberIndex = insn.word(2);
59 auto &d = memberDecorations[targetId];
60 if (memberIndex >= d.size())
61 d.resize(memberIndex + 1); // on demand; exact size would require another pass...
62 auto decoration = static_cast<spv::Decoration>(insn.word(3));
65 insn.wordCount() > 4 ? insn.word(4) : 0);
67 if (decoration == spv::DecorationCentroid)
68 modes.NeedsCentroid = true;
72 case spv::OpDecorationGroup:
73 // Nothing to do here. We don't need to record the definition of the group; we'll just have
74 // the bundle of decorations float around. If we were to ever walk the decorations directly,
75 // we might think about introducing this as a real Object.
78 case spv::OpGroupDecorate:
80 auto const &srcDecorations = decorations[insn.word(1)];
81 for (auto i = 2u; i < insn.wordCount(); i++)
83 // remaining operands are targets to apply the group to.
84 decorations[insn.word(i)].Apply(srcDecorations);
89 case spv::OpGroupMemberDecorate:
91 auto const &srcDecorations = decorations[insn.word(1)];
92 for (auto i = 2u; i < insn.wordCount(); i += 2)
94 // remaining operands are pairs of <id>, literal for members to apply to.
95 auto &d = memberDecorations[insn.word(i)];
96 auto memberIndex = insn.word(i + 1);
97 if (memberIndex >= d.size())
98 d.resize(memberIndex + 1); // on demand resize, see above...
99 d[memberIndex].Apply(srcDecorations);
104 case spv::OpTypeVoid:
105 case spv::OpTypeBool:
107 case spv::OpTypeFloat:
108 case spv::OpTypeVector:
109 case spv::OpTypeMatrix:
110 case spv::OpTypeImage:
111 case spv::OpTypeSampler:
112 case spv::OpTypeSampledImage:
113 case spv::OpTypeArray:
114 case spv::OpTypeRuntimeArray:
115 case spv::OpTypeStruct:
116 case spv::OpTypePointer:
117 case spv::OpTypeFunction:
121 case spv::OpVariable:
123 TypeID typeId = insn.word(1);
124 ObjectID resultId = insn.word(2);
125 auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
126 if (insn.wordCount() > 4)
127 UNIMPLEMENTED("Variable initializers not yet supported");
129 auto &object = defs[resultId];
130 object.kind = Object::Kind::Variable;
131 object.definition = insn;
132 object.type = typeId;
133 object.pointerBase = insn.word(2); // base is itself
136 if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
138 ProcessInterfaceVariable(object);
143 case spv::OpConstant:
144 CreateConstant(insn).constantValue[0] = insn.word(3);
146 case spv::OpConstantFalse:
147 CreateConstant(insn).constantValue[0] = 0; // represent boolean false as zero
149 case spv::OpConstantTrue:
150 CreateConstant(insn).constantValue[0] = ~0u; // represent boolean true as all bits set
152 case spv::OpConstantNull:
154 // OpConstantNull forms a constant of arbitrary type, all zeros.
155 auto &object = CreateConstant(insn);
156 auto &objectTy = getType(object.type);
157 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
159 object.constantValue[i] = 0;
163 case spv::OpConstantComposite:
165 auto &object = CreateConstant(insn);
167 for (auto i = 0u; i < insn.wordCount() - 3; i++)
169 auto &constituent = getObject(insn.word(i + 3));
170 auto &constituentTy = getType(constituent.type);
171 for (auto j = 0u; j < constituentTy.sizeInComponents; j++)
172 object.constantValue[offset++] = constituent.constantValue[j];
177 case spv::OpCapability:
178 // Various capabilities will be declared, but none affect our code generation at this point.
179 case spv::OpMemoryModel:
180 // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
181 case spv::OpEntryPoint:
182 case spv::OpFunction:
183 case spv::OpFunctionEnd:
184 // Due to preprocessing, the entrypoint and its function provide no value.
186 case spv::OpExtInstImport:
187 // We will only support the GLSL 450 extended instruction set, so no point in tracking the ID we assign it.
188 // Valid shaders will not attempt to import any other instruction sets.
190 case spv::OpMemberName:
192 case spv::OpSourceContinued:
193 case spv::OpSourceExtension:
194 // No semantic impact
197 case spv::OpFunctionParameter:
198 case spv::OpFunctionCall:
199 case spv::OpSpecConstant:
200 case spv::OpSpecConstantComposite:
201 case spv::OpSpecConstantFalse:
202 case spv::OpSpecConstantOp:
203 case spv::OpSpecConstantTrue:
204 // These should have all been removed by preprocessing passes. If we see them here,
205 // our assumptions are wrong and we will probably generate wrong code.
206 UNIMPLEMENTED("These instructions should have already been lowered.");
209 case spv::OpFConvert:
210 case spv::OpSConvert:
211 case spv::OpUConvert:
212 UNIMPLEMENTED("No valid uses for Op*Convert until we support multiple bit widths");
216 case spv::OpAccessChain:
217 case spv::OpCompositeConstruct:
218 case spv::OpCompositeInsert:
219 case spv::OpCompositeExtract:
220 case spv::OpVectorShuffle:
221 case spv::OpNot: // Unary ops
224 case spv::OpLogicalNot:
225 case spv::OpIAdd: // Binary ops
235 case spv::OpINotEqual:
236 case spv::OpUGreaterThan:
237 case spv::OpSGreaterThan:
238 case spv::OpUGreaterThanEqual:
239 case spv::OpSGreaterThanEqual:
240 case spv::OpULessThan:
241 case spv::OpSLessThan:
242 case spv::OpULessThanEqual:
243 case spv::OpSLessThanEqual:
244 case spv::OpShiftRightLogical:
245 case spv::OpShiftRightArithmetic:
246 case spv::OpShiftLeftLogical:
247 case spv::OpBitwiseOr:
248 case spv::OpBitwiseXor:
249 case spv::OpBitwiseAnd:
250 case spv::OpLogicalOr:
251 case spv::OpLogicalAnd:
252 case spv::OpUMulExtended:
253 case spv::OpSMulExtended:
255 case spv::OpConvertFToU:
256 case spv::OpConvertFToS:
257 case spv::OpConvertSToF:
258 case spv::OpConvertUToF:
260 // Instructions that yield an intermediate value
262 TypeID typeId = insn.word(1);
263 ObjectID resultId = insn.word(2);
264 auto &object = defs[resultId];
265 object.type = typeId;
266 object.kind = Object::Kind::Value;
267 object.definition = insn;
269 if (insn.opcode() == spv::OpAccessChain)
271 // interior ptr has two parts:
272 // - logical base ptr, common across all lanes and known at compile time
274 ObjectID baseId = insn.word(3);
275 object.pointerBase = getObject(baseId).pointerBase;
282 // Don't need to do anything during analysis pass
286 modes.ContainsKill = true;
290 printf("Warning: ignored opcode %s\n", OpcodeName(insn.opcode()).c_str());
291 break; // This is OK, these passes are intentionally partial
296 void SpirvShader::DeclareType(InsnIterator insn)
298 TypeID resultId = insn.word(1);
300 auto &type = types[resultId];
301 type.definition = insn;
302 type.sizeInComponents = ComputeTypeSize(insn);
304 // A structure is a builtin block if it has a builtin
305 // member. All members of such a structure are builtins.
306 switch (insn.opcode())
308 case spv::OpTypeStruct:
310 auto d = memberDecorations.find(resultId);
311 if (d != memberDecorations.end())
313 for (auto &m : d->second)
317 type.isBuiltInBlock = true;
324 case spv::OpTypePointer:
326 TypeID elementTypeId = insn.word(3);
327 type.element = elementTypeId;
328 type.isBuiltInBlock = getType(elementTypeId).isBuiltInBlock;
329 type.storageClass = static_cast<spv::StorageClass>(insn.word(2));
332 case spv::OpTypeVector:
333 case spv::OpTypeMatrix:
334 case spv::OpTypeArray:
335 case spv::OpTypeRuntimeArray:
337 TypeID elementTypeId = insn.word(2);
338 type.element = elementTypeId;
346 SpirvShader::Object& SpirvShader::CreateConstant(InsnIterator insn)
348 TypeID typeId = insn.word(1);
349 ObjectID resultId = insn.word(2);
350 auto &object = defs[resultId];
351 auto &objectTy = getType(typeId);
352 object.type = typeId;
353 object.kind = Object::Kind::Constant;
354 object.definition = insn;
355 object.constantValue = std::unique_ptr<uint32_t[]>(new uint32_t[objectTy.sizeInComponents]);
359 void SpirvShader::ProcessInterfaceVariable(Object &object)
361 auto &objectTy = getType(object.type);
362 ASSERT(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
364 ASSERT(objectTy.definition.opcode() == spv::OpTypePointer);
365 auto pointeeTy = getType(objectTy.element);
367 auto &builtinInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
368 auto &userDefinedInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputs : outputs;
370 ASSERT(object.definition.opcode() == spv::OpVariable);
371 ObjectID resultId = object.definition.word(2);
373 if (objectTy.isBuiltInBlock)
375 // walk the builtin block, registering each of its members separately.
376 auto m = memberDecorations.find(objectTy.element);
377 ASSERT(m != memberDecorations.end()); // otherwise we wouldn't have marked the type chain
378 auto &structType = pointeeTy.definition;
381 for (auto &member : m->second)
383 auto &memberType = getType(structType.word(word));
385 if (member.HasBuiltIn)
387 builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
390 offset += memberType.sizeInComponents;
396 auto d = decorations.find(resultId);
397 if (d != decorations.end() && d->second.HasBuiltIn)
399 builtinInterface[d->second.BuiltIn] = {resultId, 0, pointeeTy.sizeInComponents};
403 object.kind = Object::Kind::InterfaceVariable;
404 VisitInterface(resultId,
405 [&userDefinedInterface](Decorations const &d, AttribType type) {
406 // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
407 auto scalarSlot = (d.Location << 2) | d.Component;
408 ASSERT(scalarSlot >= 0 &&
409 scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
411 auto &slot = userDefinedInterface[scalarSlot];
414 slot.NoPerspective = d.NoPerspective;
415 slot.Centroid = d.Centroid;
420 void SpirvShader::ProcessExecutionMode(InsnIterator insn)
422 auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
425 case spv::ExecutionModeEarlyFragmentTests:
426 modes.EarlyFragmentTests = true;
428 case spv::ExecutionModeDepthReplacing:
429 modes.DepthReplacing = true;
431 case spv::ExecutionModeDepthGreater:
432 modes.DepthGreater = true;
434 case spv::ExecutionModeDepthLess:
435 modes.DepthLess = true;
437 case spv::ExecutionModeDepthUnchanged:
438 modes.DepthUnchanged = true;
440 case spv::ExecutionModeLocalSize:
441 modes.LocalSizeX = insn.word(3);
442 modes.LocalSizeZ = insn.word(5);
443 modes.LocalSizeY = insn.word(4);
445 case spv::ExecutionModeOriginUpperLeft:
446 // This is always the case for a Vulkan shader. Do nothing.
449 UNIMPLEMENTED("No other execution modes are permitted");
453 uint32_t SpirvShader::ComputeTypeSize(sw::SpirvShader::InsnIterator insn)
455 // Types are always built from the bottom up (with the exception of forward ptrs, which
456 // don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
457 // already been described (and so their sizes determined)
458 switch (insn.opcode())
460 case spv::OpTypeVoid:
461 case spv::OpTypeSampler:
462 case spv::OpTypeImage:
463 case spv::OpTypeSampledImage:
464 case spv::OpTypeFunction:
465 case spv::OpTypeRuntimeArray:
466 // Objects that don't consume any space.
467 // Descriptor-backed objects currently only need exist at compile-time.
468 // Runtime arrays don't appear in places where their size would be interesting
471 case spv::OpTypeBool:
472 case spv::OpTypeFloat:
474 // All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
475 // we might need to change this, but only 32 bit components are required for Vulkan 1.1.
478 case spv::OpTypeVector:
479 case spv::OpTypeMatrix:
480 // Vectors and matrices both consume element count * element size.
481 return getType(insn.word(2)).sizeInComponents * insn.word(3);
483 case spv::OpTypeArray:
485 // Element count * element size. Array sizes come from constant ids.
486 auto arraySize = GetConstantInt(insn.word(3));
487 return getType(insn.word(2)).sizeInComponents * arraySize;
490 case spv::OpTypeStruct:
493 for (uint32_t i = 2u; i < insn.wordCount(); i++)
495 size += getType(insn.word(i)).sizeInComponents;
500 case spv::OpTypePointer:
501 // Runtime representation of a pointer is a per-lane index.
502 // Note: clients are expected to look through the pointer if they want the pointee size instead.
506 // Some other random insn.
507 UNIMPLEMENTED("Only types are supported");
513 int SpirvShader::VisitInterfaceInner(TypeID id, Decorations d, F f) const
515 // Recursively walks variable definition and its type tree, taking into account
516 // any explicit Location or Component decorations encountered; where explicit
517 // Locations or Components are not specified, assigns them sequentially.
518 // Collected decorations are carried down toward the leaves and across
519 // siblings; Effect of decorations intentionally does not flow back up the tree.
521 // F is a functor to be called with the effective decoration set for every component.
523 // Returns the next available location, and calls f().
525 // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
527 ApplyDecorationsForId(&d, id);
529 auto const &obj = getType(id);
530 switch (obj.definition.opcode())
532 case spv::OpTypePointer:
533 return VisitInterfaceInner<F>(obj.definition.word(3), d, f);
534 case spv::OpTypeMatrix:
535 for (auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
537 // consumes same components of N consecutive locations
538 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
541 case spv::OpTypeVector:
542 for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
544 // consumes N consecutive components in the same location
545 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
547 return d.Location + 1;
548 case spv::OpTypeFloat:
549 f(d, ATTRIBTYPE_FLOAT);
550 return d.Location + 1;
552 f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
553 return d.Location + 1;
554 case spv::OpTypeBool:
555 f(d, ATTRIBTYPE_UINT);
556 return d.Location + 1;
557 case spv::OpTypeStruct:
559 // iterate over members, which may themselves have Location/Component decorations
560 for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
562 ApplyDecorationsForIdMember(&d, id, i);
563 d.Location = VisitInterfaceInner<F>(obj.definition.word(i + 2), d, f);
564 d.Component = 0; // Implicit locations always have component=0
568 case spv::OpTypeArray:
570 auto arraySize = GetConstantInt(obj.definition.word(3));
571 for (auto i = 0u; i < arraySize; i++)
573 d.Location = VisitInterfaceInner<F>(obj.definition.word(2), d, f);
578 // Intentionally partial; most opcodes do not participate in type hierarchies
584 void SpirvShader::VisitInterface(ObjectID id, F f) const
586 // Walk a variable definition and call f for each component in it.
588 ApplyDecorationsForId(&d, id);
590 auto def = getObject(id).definition;
591 ASSERT(def.opcode() == spv::OpVariable);
592 VisitInterfaceInner<F>(def.word(1), d, f);
595 SIMD::Int SpirvShader::WalkAccessChain(ObjectID id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const
597 // TODO: think about explicit layout (UBO/SSBO) storage classes
598 // TODO: avoid doing per-lane work in some cases if we can?
600 int constantOffset = 0;
601 SIMD::Int dynamicOffset = SIMD::Int(0);
602 auto &baseObject = getObject(id);
603 TypeID typeId = getType(baseObject.type).element;
605 // The <base> operand is an intermediate value itself, ie produced by a previous OpAccessChain.
606 // Start with its offset and build from there.
607 if (baseObject.kind == Object::Kind::Value)
608 dynamicOffset += As<SIMD::Int>(routine->getIntermediate(id)[0]);
610 for (auto i = 0u; i < numIndexes; i++)
612 auto & type = getType(typeId);
613 switch (type.definition.opcode())
615 case spv::OpTypeStruct:
617 int memberIndex = GetConstantInt(indexIds[i]);
618 int offsetIntoStruct = 0;
619 for (auto j = 0; j < memberIndex; j++) {
620 auto memberType = type.definition.word(2u + j);
621 offsetIntoStruct += getType(memberType).sizeInComponents;
623 constantOffset += offsetIntoStruct;
624 typeId = type.definition.word(2u + memberIndex);
628 case spv::OpTypeVector:
629 case spv::OpTypeMatrix:
630 case spv::OpTypeArray:
632 auto stride = getType(type.element).sizeInComponents;
633 auto & obj = getObject(indexIds[i]);
634 if (obj.kind == Object::Kind::Constant)
635 constantOffset += stride * GetConstantInt(indexIds[i]);
637 dynamicOffset += SIMD::Int(stride) * As<SIMD::Int>(routine->getIntermediate(indexIds[i])[0]);
638 typeId = type.element;
643 UNIMPLEMENTED("Unexpected type '%s' in WalkAccessChain", OpcodeName(type.definition.opcode()).c_str());
647 return dynamicOffset + SIMD::Int(constantOffset);
650 uint32_t SpirvShader::WalkLiteralAccessChain(TypeID typeId, uint32_t numIndexes, uint32_t const *indexes) const
652 uint32_t constantOffset = 0;
654 for (auto i = 0u; i < numIndexes; i++)
656 auto & type = getType(typeId);
657 switch (type.definition.opcode())
659 case spv::OpTypeStruct:
661 int memberIndex = indexes[i];
662 int offsetIntoStruct = 0;
663 for (auto j = 0; j < memberIndex; j++) {
664 auto memberType = type.definition.word(2u + j);
665 offsetIntoStruct += getType(memberType).sizeInComponents;
667 constantOffset += offsetIntoStruct;
668 typeId = type.definition.word(2u + memberIndex);
672 case spv::OpTypeVector:
673 case spv::OpTypeMatrix:
674 case spv::OpTypeArray:
676 auto elementType = type.definition.word(2);
677 auto stride = getType(elementType).sizeInComponents;
678 constantOffset += stride * indexes[i];
679 typeId = elementType;
684 UNIMPLEMENTED("Unexpected type in WalkLiteralAccessChain");
688 return constantOffset;
691 void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
695 case spv::DecorationLocation:
697 Location = static_cast<int32_t>(arg);
699 case spv::DecorationComponent:
703 case spv::DecorationDescriptorSet:
704 HasDescriptorSet = true;
707 case spv::DecorationBinding:
711 case spv::DecorationBuiltIn:
713 BuiltIn = static_cast<spv::BuiltIn>(arg);
715 case spv::DecorationFlat:
718 case spv::DecorationNoPerspective:
719 NoPerspective = true;
721 case spv::DecorationCentroid:
724 case spv::DecorationBlock:
727 case spv::DecorationBufferBlock:
731 // Intentionally partial, there are many decorations we just don't care about.
736 void SpirvShader::Decorations::Apply(const sw::SpirvShader::Decorations &src)
738 // Apply a decoration group to this set of decorations
742 BuiltIn = src.BuiltIn;
748 Location = src.Location;
751 if (src.HasComponent)
754 Component = src.Component;
757 if (src.HasDescriptorSet)
759 HasDescriptorSet = true;
760 DescriptorSet = src.DescriptorSet;
766 Binding = src.Binding;
770 NoPerspective |= src.NoPerspective;
771 Centroid |= src.Centroid;
773 BufferBlock |= src.BufferBlock;
776 void SpirvShader::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
778 auto it = decorations.find(id);
779 if (it != decorations.end())
780 d->Apply(it->second);
783 void SpirvShader::ApplyDecorationsForIdMember(Decorations *d, TypeID id, uint32_t member) const
785 auto it = memberDecorations.find(id);
786 if (it != memberDecorations.end() && member < it->second.size())
788 d->Apply(it->second[member]);
792 uint32_t SpirvShader::GetConstantInt(ObjectID id) const
794 // Slightly hackish access to constants very early in translation.
795 // General consumption of constants by other instructions should
796 // probably be just lowered to Reactor.
798 // TODO: not encountered yet since we only use this for array sizes etc,
799 // but is possible to construct integer constant 0 via OpConstantNull.
800 auto insn = getObject(id).definition;
801 ASSERT(insn.opcode() == spv::OpConstant);
802 ASSERT(getType(insn.word(1)).definition.opcode() == spv::OpTypeInt);
808 void SpirvShader::emitProlog(SpirvRoutine *routine) const
810 for (auto insn : *this)
812 switch (insn.opcode())
814 case spv::OpVariable:
816 ObjectID resultId = insn.word(2);
817 auto &object = getObject(resultId);
818 auto &objectTy = getType(object.type);
819 auto &pointeeTy = getType(objectTy.element);
820 // TODO: what to do about zero-slot objects?
821 if (pointeeTy.sizeInComponents > 0)
823 routine->createLvalue(insn.word(2), pointeeTy.sizeInComponents);
828 // Nothing else produces interface variables, so can all be safely ignored.
834 void SpirvShader::emit(SpirvRoutine *routine) const
836 for (auto insn : *this)
838 switch (insn.opcode())
840 case spv::OpTypeVoid:
842 case spv::OpTypeFloat:
843 case spv::OpTypeBool:
844 case spv::OpTypeVector:
845 case spv::OpTypeArray:
846 case spv::OpTypeRuntimeArray:
847 case spv::OpTypeMatrix:
848 case spv::OpTypeStruct:
849 case spv::OpTypePointer:
850 case spv::OpTypeFunction:
851 case spv::OpExecutionMode:
852 case spv::OpMemoryModel:
853 case spv::OpFunction:
854 case spv::OpFunctionEnd:
855 case spv::OpConstant:
856 case spv::OpConstantNull:
857 case spv::OpConstantTrue:
858 case spv::OpConstantFalse:
859 case spv::OpConstantComposite:
860 case spv::OpExtension:
861 case spv::OpCapability:
862 case spv::OpEntryPoint:
863 case spv::OpExtInstImport:
864 case spv::OpDecorate:
865 case spv::OpMemberDecorate:
866 case spv::OpGroupDecorate:
867 case spv::OpGroupMemberDecorate:
868 case spv::OpDecorationGroup:
870 case spv::OpMemberName:
872 case spv::OpSourceContinued:
873 case spv::OpSourceExtension:
874 // Nothing to do at emit time. These are either fully handled at analysis time,
875 // or don't require any work at all.
878 case spv::OpVariable:
879 EmitVariable(insn, routine);
883 EmitLoad(insn, routine);
887 EmitStore(insn, routine);
890 case spv::OpAccessChain:
891 EmitAccessChain(insn, routine);
894 case spv::OpCompositeConstruct:
895 EmitCompositeConstruct(insn, routine);
898 case spv::OpCompositeInsert:
899 EmitCompositeInsert(insn, routine);
902 case spv::OpCompositeExtract:
903 EmitCompositeExtract(insn, routine);
906 case spv::OpVectorShuffle:
907 EmitVectorShuffle(insn, routine);
913 case spv::OpLogicalNot:
914 case spv::OpConvertFToU:
915 case spv::OpConvertFToS:
916 case spv::OpConvertSToF:
917 case spv::OpConvertUToF:
919 EmitUnaryOp(insn, routine);
932 case spv::OpINotEqual:
933 case spv::OpUGreaterThan:
934 case spv::OpSGreaterThan:
935 case spv::OpUGreaterThanEqual:
936 case spv::OpSGreaterThanEqual:
937 case spv::OpULessThan:
938 case spv::OpSLessThan:
939 case spv::OpULessThanEqual:
940 case spv::OpSLessThanEqual:
941 case spv::OpShiftRightLogical:
942 case spv::OpShiftRightArithmetic:
943 case spv::OpShiftLeftLogical:
944 case spv::OpBitwiseOr:
945 case spv::OpBitwiseXor:
946 case spv::OpBitwiseAnd:
947 case spv::OpLogicalOr:
948 case spv::OpLogicalAnd:
949 case spv::OpUMulExtended:
950 case spv::OpSMulExtended:
951 EmitBinaryOp(insn, routine);
955 EmitDot(insn, routine);
959 printf("emit: ignoring opcode %s\n", OpcodeName(insn.opcode()).c_str());
965 void SpirvShader::EmitVariable(InsnIterator insn, SpirvRoutine *routine) const
967 ObjectID resultId = insn.word(2);
968 auto &object = getObject(resultId);
969 auto &objectTy = getType(object.type);
970 if (object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassInput)
972 auto &dst = routine->getValue(resultId);
974 VisitInterface(resultId,
975 [&](Decorations const &d, AttribType type) {
976 auto scalarSlot = d.Location << 2 | d.Component;
977 dst[offset++] = routine->inputs[scalarSlot];
982 void SpirvShader::EmitLoad(InsnIterator insn, SpirvRoutine *routine) const
984 ObjectID objectId = insn.word(2);
985 ObjectID pointerId = insn.word(3);
986 auto &object = getObject(objectId);
987 auto &objectTy = getType(object.type);
988 auto &pointer = getObject(pointerId);
989 auto &pointerBase = getObject(pointer.pointerBase);
990 auto &pointerBaseTy = getType(pointerBase.type);
992 ASSERT(getType(pointer.type).element == object.type);
993 ASSERT(TypeID(insn.word(1)) == object.type);
995 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
996 pointerBaseTy.storageClass == spv::StorageClassUniform ||
997 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
999 UNIMPLEMENTED("Descriptor-backed load not yet implemented");
1002 auto &ptrBase = routine->getValue(pointer.pointerBase);
1003 auto &dst = routine->createIntermediate(objectId, objectTy.sizeInComponents);
1005 if (pointer.kind == Object::Kind::Value)
1007 auto offsets = As<SIMD::Int>(routine->getIntermediate(insn.word(3))[0]);
1008 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
1010 // i wish i had a Float,Float,Float,Float constructor here..
1012 for (int j = 0; j < SIMD::Width; j++)
1014 Int offset = Int(i) + Extract(offsets, j);
1015 v = Insert(v, Extract(ptrBase[offset], j), j);
1022 // no divergent offsets to worry about
1023 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
1025 dst.emplace(i, ptrBase[i]);
1030 void SpirvShader::EmitAccessChain(InsnIterator insn, SpirvRoutine *routine) const
1032 TypeID typeId = insn.word(1);
1033 ObjectID objectId = insn.word(2);
1034 ObjectID baseId = insn.word(3);
1035 auto &object = getObject(objectId);
1036 auto &type = getType(typeId);
1037 auto &pointerBase = getObject(object.pointerBase);
1038 auto &pointerBaseTy = getType(pointerBase.type);
1039 ASSERT(type.sizeInComponents == 1);
1040 ASSERT(getObject(baseId).pointerBase == object.pointerBase);
1042 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
1043 pointerBaseTy.storageClass == spv::StorageClassUniform ||
1044 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
1046 UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
1048 auto &dst = routine->createIntermediate(objectId, type.sizeInComponents);
1049 dst.emplace(0, As<SIMD::Float>(WalkAccessChain(baseId, insn.wordCount() - 4, insn.wordPointer(4), routine)));
1052 void SpirvShader::EmitStore(InsnIterator insn, SpirvRoutine *routine) const
1054 ObjectID pointerId = insn.word(1);
1055 ObjectID objectId = insn.word(2);
1056 auto &object = getObject(objectId);
1057 auto &pointer = getObject(pointerId);
1058 auto &pointerTy = getType(pointer.type);
1059 auto &elementTy = getType(pointerTy.element);
1060 auto &pointerBase = getObject(pointer.pointerBase);
1061 auto &pointerBaseTy = getType(pointerBase.type);
1063 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
1064 pointerBaseTy.storageClass == spv::StorageClassUniform ||
1065 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
1067 UNIMPLEMENTED("Descriptor-backed store not yet implemented");
1070 auto &ptrBase = routine->getValue(pointer.pointerBase);
1072 if (object.kind == Object::Kind::Constant)
1074 auto src = reinterpret_cast<float *>(object.constantValue.get());
1076 if (pointer.kind == Object::Kind::Value)
1078 auto offsets = As<SIMD::Int>(routine->getIntermediate(pointerId)[0]);
1079 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1082 for (int j = 0; j < SIMD::Width; j++)
1084 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
1085 dst = Insert(dst, Float(src[i]), j);
1091 // no divergent offsets
1092 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1094 ptrBase[i] = RValue<SIMD::Float>(src[i]);
1100 auto &src = routine->getIntermediate(objectId);
1102 if (pointer.kind == Object::Kind::Value)
1104 auto offsets = As<SIMD::Int>(routine->getIntermediate(pointerId)[0]);
1105 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1108 for (int j = 0; j < SIMD::Width; j++)
1110 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
1111 dst = Insert(dst, Extract(src[i], j), j);
1117 // no divergent offsets
1118 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1120 ptrBase[i] = src[i];
1126 void SpirvShader::EmitCompositeConstruct(InsnIterator insn, SpirvRoutine *routine) const
1128 auto &type = getType(insn.word(1));
1129 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1132 for (auto i = 0u; i < insn.wordCount() - 3; i++)
1134 ObjectID srcObjectId = insn.word(3u + i);
1135 auto & srcObject = getObject(srcObjectId);
1136 auto & srcObjectTy = getType(srcObject.type);
1137 GenericValue srcObjectAccess(this, routine, srcObjectId);
1139 for (auto j = 0u; j < srcObjectTy.sizeInComponents; j++)
1140 dst.emplace(offset++, srcObjectAccess[j]);
1144 void SpirvShader::EmitCompositeInsert(InsnIterator insn, SpirvRoutine *routine) const
1146 TypeID resultTypeId = insn.word(1);
1147 auto &type = getType(resultTypeId);
1148 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1149 auto &newPartObject = getObject(insn.word(3));
1150 auto &newPartObjectTy = getType(newPartObject.type);
1151 auto firstNewComponent = WalkLiteralAccessChain(resultTypeId, insn.wordCount() - 5, insn.wordPointer(5));
1153 GenericValue srcObjectAccess(this, routine, insn.word(4));
1154 GenericValue newPartObjectAccess(this, routine, insn.word(3));
1156 // old components before
1157 for (auto i = 0u; i < firstNewComponent; i++)
1159 dst.emplace(i, srcObjectAccess[i]);
1162 for (auto i = 0u; i < newPartObjectTy.sizeInComponents; i++)
1164 dst.emplace(firstNewComponent + i, newPartObjectAccess[i]);
1166 // old components after
1167 for (auto i = firstNewComponent + newPartObjectTy.sizeInComponents; i < type.sizeInComponents; i++)
1169 dst.emplace(i, srcObjectAccess[i]);
1173 void SpirvShader::EmitCompositeExtract(InsnIterator insn, SpirvRoutine *routine) const
1175 auto &type = getType(insn.word(1));
1176 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1177 auto &compositeObject = getObject(insn.word(3));
1178 TypeID compositeTypeId = compositeObject.definition.word(1);
1179 auto firstComponent = WalkLiteralAccessChain(compositeTypeId, insn.wordCount() - 4, insn.wordPointer(4));
1181 GenericValue compositeObjectAccess(this, routine, insn.word(3));
1182 for (auto i = 0u; i < type.sizeInComponents; i++)
1184 dst.emplace(i, compositeObjectAccess[firstComponent + i]);
1188 void SpirvShader::EmitVectorShuffle(InsnIterator insn, SpirvRoutine *routine) const
1190 auto &type = getType(insn.word(1));
1191 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1193 GenericValue firstHalfAccess(this, routine, insn.word(3));
1194 GenericValue secondHalfAccess(this, routine, insn.word(4));
1196 for (auto i = 0u; i < type.sizeInComponents; i++)
1198 auto selector = insn.word(5 + i);
1199 if (selector == static_cast<uint32_t>(-1))
1201 // Undefined value. Until we decide to do real undef values, zero is as good
1203 dst.emplace(i, RValue<SIMD::Float>(0.0f));
1205 else if (selector < type.sizeInComponents)
1207 dst.emplace(i, firstHalfAccess[selector]);
1211 dst.emplace(i, secondHalfAccess[selector - type.sizeInComponents]);
1216 void SpirvShader::EmitUnaryOp(InsnIterator insn, SpirvRoutine *routine) const
1218 auto &type = getType(insn.word(1));
1219 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1220 auto src = GenericValue(this, routine, insn.word(3));
1222 for (auto i = 0u; i < type.sizeInComponents; i++)
1226 switch (insn.opcode())
1229 case spv::OpLogicalNot: // logical not == bitwise not due to all-bits boolean representation
1230 dst.emplace(i, As<SIMD::Float>(~As<SIMD::UInt>(val)));
1232 case spv::OpSNegate:
1233 dst.emplace(i, As<SIMD::Float>(-As<SIMD::Int>(val)));
1235 case spv::OpFNegate:
1236 dst.emplace(i, -val);
1238 case spv::OpConvertFToU:
1239 dst.emplace(i, As<SIMD::Float>(SIMD::UInt(val)));
1241 case spv::OpConvertFToS:
1242 dst.emplace(i, As<SIMD::Float>(SIMD::Int(val)));
1244 case spv::OpConvertSToF:
1245 dst.emplace(i, SIMD::Float(As<SIMD::Int>(val)));
1247 case spv::OpConvertUToF:
1248 dst.emplace(i, SIMD::Float(As<SIMD::UInt>(val)));
1250 case spv::OpBitcast:
1251 dst.emplace(i, val);
1254 UNIMPLEMENTED("Unhandled unary operator %s", OpcodeName(insn.opcode()).c_str());
1259 void SpirvShader::EmitBinaryOp(InsnIterator insn, SpirvRoutine *routine) const
1261 auto &type = getType(insn.word(1));
1262 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1263 auto &lhsType = getType(getObject(insn.word(3)).type);
1264 auto srcLHS = GenericValue(this, routine, insn.word(3));
1265 auto srcRHS = GenericValue(this, routine, insn.word(4));
1267 for (auto i = 0u; i < lhsType.sizeInComponents; i++)
1269 auto lhs = srcLHS[i];
1270 auto rhs = srcRHS[i];
1272 switch (insn.opcode())
1275 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) + As<SIMD::Int>(rhs)));
1278 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) - As<SIMD::Int>(rhs)));
1281 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) * As<SIMD::Int>(rhs)));
1284 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) / As<SIMD::Int>(rhs)));
1287 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) / As<SIMD::UInt>(rhs)));
1290 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) % As<SIMD::UInt>(rhs)));
1293 dst.emplace(i, As<SIMD::Float>(CmpEQ(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1295 case spv::OpINotEqual:
1296 dst.emplace(i, As<SIMD::Float>(CmpNEQ(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1298 case spv::OpUGreaterThan:
1299 dst.emplace(i, As<SIMD::Float>(CmpGT(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1301 case spv::OpSGreaterThan:
1302 dst.emplace(i, As<SIMD::Float>(CmpGT(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1304 case spv::OpUGreaterThanEqual:
1305 dst.emplace(i, As<SIMD::Float>(CmpGE(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1307 case spv::OpSGreaterThanEqual:
1308 dst.emplace(i, As<SIMD::Float>(CmpGE(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1310 case spv::OpULessThan:
1311 dst.emplace(i, As<SIMD::Float>(CmpLT(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1313 case spv::OpSLessThan:
1314 dst.emplace(i, As<SIMD::Float>(CmpLT(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1316 case spv::OpULessThanEqual:
1317 dst.emplace(i, As<SIMD::Float>(CmpLE(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1319 case spv::OpSLessThanEqual:
1320 dst.emplace(i, As<SIMD::Float>(CmpLE(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1323 dst.emplace(i, lhs + rhs);
1326 dst.emplace(i, lhs - rhs);
1329 dst.emplace(i, lhs / rhs);
1331 case spv::OpShiftRightLogical:
1332 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) >> As<SIMD::UInt>(rhs)));
1334 case spv::OpShiftRightArithmetic:
1335 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) >> As<SIMD::Int>(rhs)));
1337 case spv::OpShiftLeftLogical:
1338 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) << As<SIMD::UInt>(rhs)));
1340 case spv::OpBitwiseOr:
1341 case spv::OpLogicalOr:
1342 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) | As<SIMD::UInt>(rhs)));
1344 case spv::OpBitwiseXor:
1345 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) ^ As<SIMD::UInt>(rhs)));
1347 case spv::OpBitwiseAnd:
1348 case spv::OpLogicalAnd:
1349 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) & As<SIMD::UInt>(rhs)));
1351 case spv::OpSMulExtended:
1352 // Extended ops: result is a structure containing two members of the same type as lhs & rhs.
1353 // In our flat view then, component i is the i'th component of the first member;
1354 // component i + N is the i'th component of the second member.
1355 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) * As<SIMD::Int>(rhs)));
1356 dst.emplace(i + lhsType.sizeInComponents, As<SIMD::Float>(MulHigh(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1358 case spv::OpUMulExtended:
1359 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) * As<SIMD::UInt>(rhs)));
1360 dst.emplace(i + lhsType.sizeInComponents, As<SIMD::Float>(MulHigh(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1363 UNIMPLEMENTED("Unhandled binary operator %s", OpcodeName(insn.opcode()).c_str());
1368 void SpirvShader::EmitDot(InsnIterator insn, SpirvRoutine *routine) const
1370 auto &type = getType(insn.word(1));
1371 assert(type.sizeInComponents == 1);
1372 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1373 auto &lhsType = getType(getObject(insn.word(3)).type);
1374 auto srcLHS = GenericValue(this, routine, insn.word(3));
1375 auto srcRHS = GenericValue(this, routine, insn.word(4));
1377 SIMD::Float result = srcLHS[0] * srcRHS[0];
1379 for (auto i = 1u; i < lhsType.sizeInComponents; i++)
1381 result += srcLHS[i] * srcRHS[i];
1384 dst.emplace(0, result);
1387 void SpirvShader::emitEpilog(SpirvRoutine *routine) const
1389 for (auto insn : *this)
1391 switch (insn.opcode())
1393 case spv::OpVariable:
1395 ObjectID resultId = insn.word(2);
1396 auto &object = getObject(resultId);
1397 auto &objectTy = getType(object.type);
1398 if (object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
1400 auto &dst = routine->getValue(resultId);
1402 VisitInterface(resultId,
1403 [&](Decorations const &d, AttribType type) {
1404 auto scalarSlot = d.Location << 2 | d.Component;
1405 routine->outputs[scalarSlot] = dst[offset++];