OSDN Git Service

SpirvShader: Add relational ops for integers
[android-x86/external-swiftshader.git] / src / Pipeline / SpirvShader.cpp
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 <spirv/unified1/spirv.hpp>
16 #include "SpirvShader.hpp"
17 #include "System/Math.hpp"
18 #include "Vulkan/VkDebug.hpp"
19 #include "Device/Config.hpp"
20
21 namespace sw
22 {
23         volatile int SpirvShader::serialCounter = 1;    // Start at 1, 0 is invalid shader.
24
25         SpirvShader::SpirvShader(InsnStore const &insns)
26                         : insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
27                           outputs{MAX_INTERFACE_COMPONENTS},
28                           serialID{serialCounter++}, modes{}
29         {
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
33
34                 for (auto insn : *this)
35                 {
36                         switch (insn.opcode())
37                         {
38                         case spv::OpExecutionMode:
39                                 ProcessExecutionMode(insn);
40                                 break;
41
42                         case spv::OpDecorate:
43                         {
44                                 TypeOrObjectID targetId = insn.word(1);
45                                 auto decoration = static_cast<spv::Decoration>(insn.word(2));
46                                 decorations[targetId].Apply(
47                                                 decoration,
48                                                 insn.wordCount() > 3 ? insn.word(3) : 0);
49
50                                 if (decoration == spv::DecorationCentroid)
51                                         modes.NeedsCentroid = true;
52                                 break;
53                         }
54
55                         case spv::OpMemberDecorate:
56                         {
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));
63                                 d[memberIndex].Apply(
64                                                 decoration,
65                                                 insn.wordCount() > 4 ? insn.word(4) : 0);
66
67                                 if (decoration == spv::DecorationCentroid)
68                                         modes.NeedsCentroid = true;
69                                 break;
70                         }
71
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.
76                                 break;
77
78                         case spv::OpGroupDecorate:
79                         {
80                                 auto const &srcDecorations = decorations[insn.word(1)];
81                                 for (auto i = 2u; i < insn.wordCount(); i++)
82                                 {
83                                         // remaining operands are targets to apply the group to.
84                                         decorations[insn.word(i)].Apply(srcDecorations);
85                                 }
86                                 break;
87                         }
88
89                         case spv::OpGroupMemberDecorate:
90                         {
91                                 auto const &srcDecorations = decorations[insn.word(1)];
92                                 for (auto i = 2u; i < insn.wordCount(); i += 2)
93                                 {
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);
100                                 }
101                                 break;
102                         }
103
104                         case spv::OpTypeVoid:
105                         case spv::OpTypeBool:
106                         case spv::OpTypeInt:
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:
118                                 DeclareType(insn);
119                                 break;
120
121                         case spv::OpVariable:
122                         {
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");
128
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
134
135                                 // Register builtins
136                                 if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
137                                 {
138                                         ProcessInterfaceVariable(object);
139                                 }
140                                 break;
141                         }
142
143                         case spv::OpConstant:
144                                 CreateConstant(insn).constantValue[0] = insn.word(3);
145                                 break;
146                         case spv::OpConstantFalse:
147                                 CreateConstant(insn).constantValue[0] = 0;              // represent boolean false as zero
148                                 break;
149                         case spv::OpConstantTrue:
150                                 CreateConstant(insn).constantValue[0] = ~0u;    // represent boolean true as all bits set
151                                 break;
152                         case spv::OpConstantNull:
153                         {
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++)
158                                 {
159                                         object.constantValue[i] = 0;
160                                 }
161                                 break;
162                         }
163                         case spv::OpConstantComposite:
164                         {
165                                 auto &object = CreateConstant(insn);
166                                 auto offset = 0u;
167                                 for (auto i = 0u; i < insn.wordCount() - 3; i++)
168                                 {
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];
173                                 }
174                                 break;
175                         }
176
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.
185                                 break;
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.
189                         case spv::OpName:
190                         case spv::OpMemberName:
191                         case spv::OpSource:
192                         case spv::OpSourceContinued:
193                         case spv::OpSourceExtension:
194                                 // No semantic impact
195                                 break;
196
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.");
207                                 break;
208
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");
213                                 break;
214
215                         case spv::OpLoad:
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
222                         case spv::OpSNegate:
223                         case spv::OpFNegate:
224                         case spv::OpLogicalNot:
225                         case spv::OpIAdd: // Binary ops
226                         case spv::OpISub:
227                         case spv::OpIMul:
228                         case spv::OpSDiv:
229                         case spv::OpUDiv:
230                         case spv::OpFAdd:
231                         case spv::OpFSub:
232                         case spv::OpFDiv:
233                         case spv::OpUMod:
234                         case spv::OpIEqual:
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:
254                         case spv::OpDot:
255                         case spv::OpConvertFToU:
256                         case spv::OpConvertFToS:
257                         case spv::OpConvertSToF:
258                         case spv::OpConvertUToF:
259                         case spv::OpBitcast:
260                                 // Instructions that yield an intermediate value
261                         {
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;
268
269                                 if (insn.opcode() == spv::OpAccessChain)
270                                 {
271                                         // interior ptr has two parts:
272                                         // - logical base ptr, common across all lanes and known at compile time
273                                         // - per-lane offset
274                                         ObjectID baseId = insn.word(3);
275                                         object.pointerBase = getObject(baseId).pointerBase;
276                                 }
277                                 break;
278                         }
279
280                         case spv::OpStore:
281                         case spv::OpReturn:
282                                 // Don't need to do anything during analysis pass
283                                 break;
284
285                         case spv::OpKill:
286                                 modes.ContainsKill = true;
287                                 break;
288
289                         default:
290                                 printf("Warning: ignored opcode %s\n", OpcodeName(insn.opcode()).c_str());
291                                 break;    // This is OK, these passes are intentionally partial
292                         }
293                 }
294         }
295
296         void SpirvShader::DeclareType(InsnIterator insn)
297         {
298                 TypeID resultId = insn.word(1);
299
300                 auto &type = types[resultId];
301                 type.definition = insn;
302                 type.sizeInComponents = ComputeTypeSize(insn);
303
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())
307                 {
308                 case spv::OpTypeStruct:
309                 {
310                         auto d = memberDecorations.find(resultId);
311                         if (d != memberDecorations.end())
312                         {
313                                 for (auto &m : d->second)
314                                 {
315                                         if (m.HasBuiltIn)
316                                         {
317                                                 type.isBuiltInBlock = true;
318                                                 break;
319                                         }
320                                 }
321                         }
322                         break;
323                 }
324                 case spv::OpTypePointer:
325                 {
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));
330                         break;
331                 }
332                 case spv::OpTypeVector:
333                 case spv::OpTypeMatrix:
334                 case spv::OpTypeArray:
335                 case spv::OpTypeRuntimeArray:
336                 {
337                         TypeID elementTypeId = insn.word(2);
338                         type.element = elementTypeId;
339                         break;
340                 }
341                 default:
342                         break;
343                 }
344         }
345
346         SpirvShader::Object& SpirvShader::CreateConstant(InsnIterator insn)
347         {
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]);
356                 return object;
357         }
358
359         void SpirvShader::ProcessInterfaceVariable(Object &object)
360         {
361                 auto &objectTy = getType(object.type);
362                 ASSERT(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
363
364                 ASSERT(objectTy.definition.opcode() == spv::OpTypePointer);
365                 auto pointeeTy = getType(objectTy.element);
366
367                 auto &builtinInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
368                 auto &userDefinedInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputs : outputs;
369
370                 ASSERT(object.definition.opcode() == spv::OpVariable);
371                 ObjectID resultId = object.definition.word(2);
372
373                 if (objectTy.isBuiltInBlock)
374                 {
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;
379                         auto offset = 0u;
380                         auto word = 2u;
381                         for (auto &member : m->second)
382                         {
383                                 auto &memberType = getType(structType.word(word));
384
385                                 if (member.HasBuiltIn)
386                                 {
387                                         builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
388                                 }
389
390                                 offset += memberType.sizeInComponents;
391                                 ++word;
392                         }
393                         return;
394                 }
395
396                 auto d = decorations.find(resultId);
397                 if (d != decorations.end() && d->second.HasBuiltIn)
398                 {
399                         builtinInterface[d->second.BuiltIn] = {resultId, 0, pointeeTy.sizeInComponents};
400                 }
401                 else
402                 {
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()));
410
411                                                            auto &slot = userDefinedInterface[scalarSlot];
412                                                            slot.Type = type;
413                                                            slot.Flat = d.Flat;
414                                                            slot.NoPerspective = d.NoPerspective;
415                                                            slot.Centroid = d.Centroid;
416                                                    });
417                 }
418         }
419
420         void SpirvShader::ProcessExecutionMode(InsnIterator insn)
421         {
422                 auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
423                 switch (mode)
424                 {
425                 case spv::ExecutionModeEarlyFragmentTests:
426                         modes.EarlyFragmentTests = true;
427                         break;
428                 case spv::ExecutionModeDepthReplacing:
429                         modes.DepthReplacing = true;
430                         break;
431                 case spv::ExecutionModeDepthGreater:
432                         modes.DepthGreater = true;
433                         break;
434                 case spv::ExecutionModeDepthLess:
435                         modes.DepthLess = true;
436                         break;
437                 case spv::ExecutionModeDepthUnchanged:
438                         modes.DepthUnchanged = true;
439                         break;
440                 case spv::ExecutionModeLocalSize:
441                         modes.LocalSizeX = insn.word(3);
442                         modes.LocalSizeZ = insn.word(5);
443                         modes.LocalSizeY = insn.word(4);
444                         break;
445                 case spv::ExecutionModeOriginUpperLeft:
446                         // This is always the case for a Vulkan shader. Do nothing.
447                         break;
448                 default:
449                         UNIMPLEMENTED("No other execution modes are permitted");
450                 }
451         }
452
453         uint32_t SpirvShader::ComputeTypeSize(sw::SpirvShader::InsnIterator insn)
454         {
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())
459                 {
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
469                         return 0;
470
471                 case spv::OpTypeBool:
472                 case spv::OpTypeFloat:
473                 case spv::OpTypeInt:
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.
476                         return 1;
477
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);
482
483                 case spv::OpTypeArray:
484                 {
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;
488                 }
489
490                 case spv::OpTypeStruct:
491                 {
492                         uint32_t size = 0;
493                         for (uint32_t i = 2u; i < insn.wordCount(); i++)
494                         {
495                                 size += getType(insn.word(i)).sizeInComponents;
496                         }
497                         return size;
498                 }
499
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.
503                         return 1;
504
505                 default:
506                         // Some other random insn.
507                         UNIMPLEMENTED("Only types are supported");
508                         return 0;
509                 }
510         }
511
512         template<typename F>
513         int SpirvShader::VisitInterfaceInner(TypeID id, Decorations d, F f) const
514         {
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.
520                 //
521                 // F is a functor to be called with the effective decoration set for every component.
522                 //
523                 // Returns the next available location, and calls f().
524
525                 // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
526
527                 ApplyDecorationsForId(&d, id);
528
529                 auto const &obj = getType(id);
530                 switch (obj.definition.opcode())
531                 {
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++)
536                         {
537                                 // consumes same components of N consecutive locations
538                                 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
539                         }
540                         return d.Location;
541                 case spv::OpTypeVector:
542                         for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
543                         {
544                                 // consumes N consecutive components in the same location
545                                 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
546                         }
547                         return d.Location + 1;
548                 case spv::OpTypeFloat:
549                         f(d, ATTRIBTYPE_FLOAT);
550                         return d.Location + 1;
551                 case spv::OpTypeInt:
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:
558                 {
559                         // iterate over members, which may themselves have Location/Component decorations
560                         for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
561                         {
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
565                         }
566                         return d.Location;
567                 }
568                 case spv::OpTypeArray:
569                 {
570                         auto arraySize = GetConstantInt(obj.definition.word(3));
571                         for (auto i = 0u; i < arraySize; i++)
572                         {
573                                 d.Location = VisitInterfaceInner<F>(obj.definition.word(2), d, f);
574                         }
575                         return d.Location;
576                 }
577                 default:
578                         // Intentionally partial; most opcodes do not participate in type hierarchies
579                         return 0;
580                 }
581         }
582
583         template<typename F>
584         void SpirvShader::VisitInterface(ObjectID id, F f) const
585         {
586                 // Walk a variable definition and call f for each component in it.
587                 Decorations d{};
588                 ApplyDecorationsForId(&d, id);
589
590                 auto def = getObject(id).definition;
591                 ASSERT(def.opcode() == spv::OpVariable);
592                 VisitInterfaceInner<F>(def.word(1), d, f);
593         }
594
595         SIMD::Int SpirvShader::WalkAccessChain(ObjectID id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const
596         {
597                 // TODO: think about explicit layout (UBO/SSBO) storage classes
598                 // TODO: avoid doing per-lane work in some cases if we can?
599
600                 int constantOffset = 0;
601                 SIMD::Int dynamicOffset = SIMD::Int(0);
602                 auto &baseObject = getObject(id);
603                 TypeID typeId = getType(baseObject.type).element;
604
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]);
609
610                 for (auto i = 0u; i < numIndexes; i++)
611                 {
612                         auto & type = getType(typeId);
613                         switch (type.definition.opcode())
614                         {
615                         case spv::OpTypeStruct:
616                         {
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;
622                                 }
623                                 constantOffset += offsetIntoStruct;
624                                 typeId = type.definition.word(2u + memberIndex);
625                                 break;
626                         }
627
628                         case spv::OpTypeVector:
629                         case spv::OpTypeMatrix:
630                         case spv::OpTypeArray:
631                         {
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]);
636                                 else
637                                         dynamicOffset += SIMD::Int(stride) * As<SIMD::Int>(routine->getIntermediate(indexIds[i])[0]);
638                                 typeId = type.element;
639                                 break;
640                         }
641
642                         default:
643                                 UNIMPLEMENTED("Unexpected type '%s' in WalkAccessChain", OpcodeName(type.definition.opcode()).c_str());
644                         }
645                 }
646
647                 return dynamicOffset + SIMD::Int(constantOffset);
648         }
649
650         uint32_t SpirvShader::WalkLiteralAccessChain(TypeID typeId, uint32_t numIndexes, uint32_t const *indexes) const
651         {
652                 uint32_t constantOffset = 0;
653
654                 for (auto i = 0u; i < numIndexes; i++)
655                 {
656                         auto & type = getType(typeId);
657                         switch (type.definition.opcode())
658                         {
659                         case spv::OpTypeStruct:
660                         {
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;
666                                 }
667                                 constantOffset += offsetIntoStruct;
668                                 typeId = type.definition.word(2u + memberIndex);
669                                 break;
670                         }
671
672                         case spv::OpTypeVector:
673                         case spv::OpTypeMatrix:
674                         case spv::OpTypeArray:
675                         {
676                                 auto elementType = type.definition.word(2);
677                                 auto stride = getType(elementType).sizeInComponents;
678                                 constantOffset += stride * indexes[i];
679                                 typeId = elementType;
680                                 break;
681                         }
682
683                         default:
684                                 UNIMPLEMENTED("Unexpected type in WalkLiteralAccessChain");
685                         }
686                 }
687
688                 return constantOffset;
689         }
690
691         void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
692         {
693                 switch (decoration)
694                 {
695                 case spv::DecorationLocation:
696                         HasLocation = true;
697                         Location = static_cast<int32_t>(arg);
698                         break;
699                 case spv::DecorationComponent:
700                         HasComponent = true;
701                         Component = arg;
702                         break;
703                 case spv::DecorationDescriptorSet:
704                         HasDescriptorSet = true;
705                         DescriptorSet = arg;
706                         break;
707                 case spv::DecorationBinding:
708                         HasBinding = true;
709                         Binding = arg;
710                         break;
711                 case spv::DecorationBuiltIn:
712                         HasBuiltIn = true;
713                         BuiltIn = static_cast<spv::BuiltIn>(arg);
714                         break;
715                 case spv::DecorationFlat:
716                         Flat = true;
717                         break;
718                 case spv::DecorationNoPerspective:
719                         NoPerspective = true;
720                         break;
721                 case spv::DecorationCentroid:
722                         Centroid = true;
723                         break;
724                 case spv::DecorationBlock:
725                         Block = true;
726                         break;
727                 case spv::DecorationBufferBlock:
728                         BufferBlock = true;
729                         break;
730                 default:
731                         // Intentionally partial, there are many decorations we just don't care about.
732                         break;
733                 }
734         }
735
736         void SpirvShader::Decorations::Apply(const sw::SpirvShader::Decorations &src)
737         {
738                 // Apply a decoration group to this set of decorations
739                 if (src.HasBuiltIn)
740                 {
741                         HasBuiltIn = true;
742                         BuiltIn = src.BuiltIn;
743                 }
744
745                 if (src.HasLocation)
746                 {
747                         HasLocation = true;
748                         Location = src.Location;
749                 }
750
751                 if (src.HasComponent)
752                 {
753                         HasComponent = true;
754                         Component = src.Component;
755                 }
756
757                 if (src.HasDescriptorSet)
758                 {
759                         HasDescriptorSet = true;
760                         DescriptorSet = src.DescriptorSet;
761                 }
762
763                 if (src.HasBinding)
764                 {
765                         HasBinding = true;
766                         Binding = src.Binding;
767                 }
768
769                 Flat |= src.Flat;
770                 NoPerspective |= src.NoPerspective;
771                 Centroid |= src.Centroid;
772                 Block |= src.Block;
773                 BufferBlock |= src.BufferBlock;
774         }
775
776         void SpirvShader::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
777         {
778                 auto it = decorations.find(id);
779                 if (it != decorations.end())
780                         d->Apply(it->second);
781         }
782
783         void SpirvShader::ApplyDecorationsForIdMember(Decorations *d, TypeID id, uint32_t member) const
784         {
785                 auto it = memberDecorations.find(id);
786                 if (it != memberDecorations.end() && member < it->second.size())
787                 {
788                         d->Apply(it->second[member]);
789                 }
790         }
791
792         uint32_t SpirvShader::GetConstantInt(ObjectID id) const
793         {
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.
797
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);
803                 return insn.word(3);
804         }
805
806         // emit-time
807
808         void SpirvShader::emitProlog(SpirvRoutine *routine) const
809         {
810                 for (auto insn : *this)
811                 {
812                         switch (insn.opcode())
813                         {
814                         case spv::OpVariable:
815                         {
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)
822                                 {
823                                         routine->createLvalue(insn.word(2), pointeeTy.sizeInComponents);
824                                 }
825                                 break;
826                         }
827                         default:
828                                 // Nothing else produces interface variables, so can all be safely ignored.
829                                 break;
830                         }
831                 }
832         }
833
834         void SpirvShader::emit(SpirvRoutine *routine) const
835         {
836                 for (auto insn : *this)
837                 {
838                         switch (insn.opcode())
839                         {
840                         case spv::OpTypeVoid:
841                         case spv::OpTypeInt:
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:
869                         case spv::OpName:
870                         case spv::OpMemberName:
871                         case spv::OpSource:
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.
876                                 break;
877
878                         case spv::OpVariable:
879                                 EmitVariable(insn, routine);
880                                 break;
881
882                         case spv::OpLoad:
883                                 EmitLoad(insn, routine);
884                                 break;
885
886                         case spv::OpStore:
887                                 EmitStore(insn, routine);
888                                 break;
889
890                         case spv::OpAccessChain:
891                                 EmitAccessChain(insn, routine);
892                                 break;
893
894                         case spv::OpCompositeConstruct:
895                                 EmitCompositeConstruct(insn, routine);
896                                 break;
897
898                         case spv::OpCompositeInsert:
899                                 EmitCompositeInsert(insn, routine);
900                                 break;
901
902                         case spv::OpCompositeExtract:
903                                 EmitCompositeExtract(insn, routine);
904                                 break;
905
906                         case spv::OpVectorShuffle:
907                                 EmitVectorShuffle(insn, routine);
908                                 break;
909
910                         case spv::OpNot:
911                         case spv::OpSNegate:
912                         case spv::OpFNegate:
913                         case spv::OpLogicalNot:
914                         case spv::OpConvertFToU:
915                         case spv::OpConvertFToS:
916                         case spv::OpConvertSToF:
917                         case spv::OpConvertUToF:
918                         case spv::OpBitcast:
919                                 EmitUnaryOp(insn, routine);
920                                 break;
921
922                         case spv::OpIAdd:
923                         case spv::OpISub:
924                         case spv::OpIMul:
925                         case spv::OpSDiv:
926                         case spv::OpUDiv:
927                         case spv::OpFAdd:
928                         case spv::OpFSub:
929                         case spv::OpFDiv:
930                         case spv::OpUMod:
931                         case spv::OpIEqual:
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);
952                                 break;
953
954                         case spv::OpDot:
955                                 EmitDot(insn, routine);
956                                 break;
957
958                         default:
959                                 printf("emit: ignoring opcode %s\n", OpcodeName(insn.opcode()).c_str());
960                                 break;
961                         }
962                 }
963         }
964
965         void SpirvShader::EmitVariable(InsnIterator insn, SpirvRoutine *routine) const
966         {
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)
971                 {
972                         auto &dst = routine->getValue(resultId);
973                         int offset = 0;
974                         VisitInterface(resultId,
975                                                         [&](Decorations const &d, AttribType type) {
976                                                                 auto scalarSlot = d.Location << 2 | d.Component;
977                                                                 dst[offset++] = routine->inputs[scalarSlot];
978                                                         });
979                 }
980         }
981
982         void SpirvShader::EmitLoad(InsnIterator insn, SpirvRoutine *routine) const
983         {
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);
991
992                 ASSERT(getType(pointer.type).element == object.type);
993                 ASSERT(TypeID(insn.word(1)) == object.type);
994
995                 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
996                         pointerBaseTy.storageClass == spv::StorageClassUniform ||
997                         pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
998                 {
999                         UNIMPLEMENTED("Descriptor-backed load not yet implemented");
1000                 }
1001
1002                 auto &ptrBase = routine->getValue(pointer.pointerBase);
1003                 auto &dst = routine->createIntermediate(objectId, objectTy.sizeInComponents);
1004
1005                 if (pointer.kind == Object::Kind::Value)
1006                 {
1007                         auto offsets = As<SIMD::Int>(routine->getIntermediate(insn.word(3))[0]);
1008                         for (auto i = 0u; i < objectTy.sizeInComponents; i++)
1009                         {
1010                                 // i wish i had a Float,Float,Float,Float constructor here..
1011                                 SIMD::Float v;
1012                                 for (int j = 0; j < SIMD::Width; j++)
1013                                 {
1014                                         Int offset = Int(i) + Extract(offsets, j);
1015                                         v = Insert(v, Extract(ptrBase[offset], j), j);
1016                                 }
1017                                 dst.emplace(i, v);
1018                         }
1019                 }
1020                 else
1021                 {
1022                         // no divergent offsets to worry about
1023                         for (auto i = 0u; i < objectTy.sizeInComponents; i++)
1024                         {
1025                                 dst.emplace(i, ptrBase[i]);
1026                         }
1027                 }
1028         }
1029
1030         void SpirvShader::EmitAccessChain(InsnIterator insn, SpirvRoutine *routine) const
1031         {
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);
1041
1042                 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
1043                         pointerBaseTy.storageClass == spv::StorageClassUniform ||
1044                         pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
1045                 {
1046                         UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
1047                 }
1048                 auto &dst = routine->createIntermediate(objectId, type.sizeInComponents);
1049                 dst.emplace(0, As<SIMD::Float>(WalkAccessChain(baseId, insn.wordCount() - 4, insn.wordPointer(4), routine)));
1050         }
1051
1052         void SpirvShader::EmitStore(InsnIterator insn, SpirvRoutine *routine) const
1053         {
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);
1062
1063                 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
1064                         pointerBaseTy.storageClass == spv::StorageClassUniform ||
1065                         pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
1066                 {
1067                         UNIMPLEMENTED("Descriptor-backed store not yet implemented");
1068                 }
1069
1070                 auto &ptrBase = routine->getValue(pointer.pointerBase);
1071
1072                 if (object.kind == Object::Kind::Constant)
1073                 {
1074                         auto src = reinterpret_cast<float *>(object.constantValue.get());
1075
1076                         if (pointer.kind == Object::Kind::Value)
1077                         {
1078                                 auto offsets = As<SIMD::Int>(routine->getIntermediate(pointerId)[0]);
1079                                 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1080                                 {
1081                                         // Scattered store
1082                                         for (int j = 0; j < SIMD::Width; j++)
1083                                         {
1084                                                 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
1085                                                 dst = Insert(dst, Float(src[i]), j);
1086                                         }
1087                                 }
1088                         }
1089                         else
1090                         {
1091                                 // no divergent offsets
1092                                 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1093                                 {
1094                                         ptrBase[i] = RValue<SIMD::Float>(src[i]);
1095                                 }
1096                         }
1097                 }
1098                 else
1099                 {
1100                         auto &src = routine->getIntermediate(objectId);
1101
1102                         if (pointer.kind == Object::Kind::Value)
1103                         {
1104                                 auto offsets = As<SIMD::Int>(routine->getIntermediate(pointerId)[0]);
1105                                 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1106                                 {
1107                                         // Scattered store
1108                                         for (int j = 0; j < SIMD::Width; j++)
1109                                         {
1110                                                 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
1111                                                 dst = Insert(dst, Extract(src[i], j), j);
1112                                         }
1113                                 }
1114                         }
1115                         else
1116                         {
1117                                 // no divergent offsets
1118                                 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
1119                                 {
1120                                         ptrBase[i] = src[i];
1121                                 }
1122                         }
1123                 }
1124         }
1125
1126         void SpirvShader::EmitCompositeConstruct(InsnIterator insn, SpirvRoutine *routine) const
1127         {
1128                 auto &type = getType(insn.word(1));
1129                 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1130                 auto offset = 0u;
1131
1132                 for (auto i = 0u; i < insn.wordCount() - 3; i++)
1133                 {
1134                         ObjectID srcObjectId = insn.word(3u + i);
1135                         auto & srcObject = getObject(srcObjectId);
1136                         auto & srcObjectTy = getType(srcObject.type);
1137                         GenericValue srcObjectAccess(this, routine, srcObjectId);
1138
1139                         for (auto j = 0u; j < srcObjectTy.sizeInComponents; j++)
1140                                 dst.emplace(offset++, srcObjectAccess[j]);
1141                 }
1142         }
1143
1144         void SpirvShader::EmitCompositeInsert(InsnIterator insn, SpirvRoutine *routine) const
1145         {
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));
1152
1153                 GenericValue srcObjectAccess(this, routine, insn.word(4));
1154                 GenericValue newPartObjectAccess(this, routine, insn.word(3));
1155
1156                 // old components before
1157                 for (auto i = 0u; i < firstNewComponent; i++)
1158                 {
1159                         dst.emplace(i, srcObjectAccess[i]);
1160                 }
1161                 // new part
1162                 for (auto i = 0u; i < newPartObjectTy.sizeInComponents; i++)
1163                 {
1164                         dst.emplace(firstNewComponent + i, newPartObjectAccess[i]);
1165                 }
1166                 // old components after
1167                 for (auto i = firstNewComponent + newPartObjectTy.sizeInComponents; i < type.sizeInComponents; i++)
1168                 {
1169                         dst.emplace(i, srcObjectAccess[i]);
1170                 }
1171         }
1172
1173         void SpirvShader::EmitCompositeExtract(InsnIterator insn, SpirvRoutine *routine) const
1174         {
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));
1180
1181                 GenericValue compositeObjectAccess(this, routine, insn.word(3));
1182                 for (auto i = 0u; i < type.sizeInComponents; i++)
1183                 {
1184                         dst.emplace(i, compositeObjectAccess[firstComponent + i]);
1185                 }
1186         }
1187
1188         void SpirvShader::EmitVectorShuffle(InsnIterator insn, SpirvRoutine *routine) const
1189         {
1190                 auto &type = getType(insn.word(1));
1191                 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1192
1193                 GenericValue firstHalfAccess(this, routine, insn.word(3));
1194                 GenericValue secondHalfAccess(this, routine, insn.word(4));
1195
1196                 for (auto i = 0u; i < type.sizeInComponents; i++)
1197                 {
1198                         auto selector = insn.word(5 + i);
1199                         if (selector == static_cast<uint32_t>(-1))
1200                         {
1201                                 // Undefined value. Until we decide to do real undef values, zero is as good
1202                                 // a value as any
1203                                 dst.emplace(i, RValue<SIMD::Float>(0.0f));
1204                         }
1205                         else if (selector < type.sizeInComponents)
1206                         {
1207                                 dst.emplace(i, firstHalfAccess[selector]);
1208                         }
1209                         else
1210                         {
1211                                 dst.emplace(i, secondHalfAccess[selector - type.sizeInComponents]);
1212                         }
1213                 }
1214         }
1215
1216         void SpirvShader::EmitUnaryOp(InsnIterator insn, SpirvRoutine *routine) const
1217         {
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));
1221
1222                 for (auto i = 0u; i < type.sizeInComponents; i++)
1223                 {
1224                         auto val = src[i];
1225
1226                         switch (insn.opcode())
1227                         {
1228                         case spv::OpNot:
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)));
1231                                 break;
1232                         case spv::OpSNegate:
1233                                 dst.emplace(i, As<SIMD::Float>(-As<SIMD::Int>(val)));
1234                                 break;
1235                         case spv::OpFNegate:
1236                                 dst.emplace(i, -val);
1237                                 break;
1238                         case spv::OpConvertFToU:
1239                                 dst.emplace(i, As<SIMD::Float>(SIMD::UInt(val)));
1240                                 break;
1241                         case spv::OpConvertFToS:
1242                                 dst.emplace(i, As<SIMD::Float>(SIMD::Int(val)));
1243                                 break;
1244                         case spv::OpConvertSToF:
1245                                 dst.emplace(i, SIMD::Float(As<SIMD::Int>(val)));
1246                                 break;
1247                         case spv::OpConvertUToF:
1248                                 dst.emplace(i, SIMD::Float(As<SIMD::UInt>(val)));
1249                                 break;
1250                         case spv::OpBitcast:
1251                                 dst.emplace(i, val);
1252                                 break;
1253                         default:
1254                                 UNIMPLEMENTED("Unhandled unary operator %s", OpcodeName(insn.opcode()).c_str());
1255                         }
1256                 }
1257         }
1258
1259         void SpirvShader::EmitBinaryOp(InsnIterator insn, SpirvRoutine *routine) const
1260         {
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));
1266
1267                 for (auto i = 0u; i < lhsType.sizeInComponents; i++)
1268                 {
1269                         auto lhs = srcLHS[i];
1270                         auto rhs = srcRHS[i];
1271
1272                         switch (insn.opcode())
1273                         {
1274                         case spv::OpIAdd:
1275                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) + As<SIMD::Int>(rhs)));
1276                                 break;
1277                         case spv::OpISub:
1278                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) - As<SIMD::Int>(rhs)));
1279                                 break;
1280                         case spv::OpIMul:
1281                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) * As<SIMD::Int>(rhs)));
1282                                 break;
1283                         case spv::OpSDiv:
1284                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) / As<SIMD::Int>(rhs)));
1285                                 break;
1286                         case spv::OpUDiv:
1287                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) / As<SIMD::UInt>(rhs)));
1288                                 break;
1289                         case spv::OpUMod:
1290                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) % As<SIMD::UInt>(rhs)));
1291                                 break;
1292                         case spv::OpIEqual:
1293                                 dst.emplace(i, As<SIMD::Float>(CmpEQ(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1294                                 break;
1295                         case spv::OpINotEqual:
1296                                 dst.emplace(i, As<SIMD::Float>(CmpNEQ(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1297                                 break;
1298                         case spv::OpUGreaterThan:
1299                                 dst.emplace(i, As<SIMD::Float>(CmpGT(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1300                                 break;
1301                         case spv::OpSGreaterThan:
1302                                 dst.emplace(i, As<SIMD::Float>(CmpGT(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1303                                 break;
1304                         case spv::OpUGreaterThanEqual:
1305                                 dst.emplace(i, As<SIMD::Float>(CmpGE(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1306                                 break;
1307                         case spv::OpSGreaterThanEqual:
1308                                 dst.emplace(i, As<SIMD::Float>(CmpGE(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1309                                 break;
1310                         case spv::OpULessThan:
1311                                 dst.emplace(i, As<SIMD::Float>(CmpLT(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1312                                 break;
1313                         case spv::OpSLessThan:
1314                                 dst.emplace(i, As<SIMD::Float>(CmpLT(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1315                                 break;
1316                         case spv::OpULessThanEqual:
1317                                 dst.emplace(i, As<SIMD::Float>(CmpLE(As<SIMD::UInt>(lhs), As<SIMD::UInt>(rhs))));
1318                                 break;
1319                         case spv::OpSLessThanEqual:
1320                                 dst.emplace(i, As<SIMD::Float>(CmpLE(As<SIMD::Int>(lhs), As<SIMD::Int>(rhs))));
1321                                 break;
1322                         case spv::OpFAdd:
1323                                 dst.emplace(i, lhs + rhs);
1324                                 break;
1325                         case spv::OpFSub:
1326                                 dst.emplace(i, lhs - rhs);
1327                                 break;
1328                         case spv::OpFDiv:
1329                                 dst.emplace(i, lhs / rhs);
1330                                 break;
1331                         case spv::OpShiftRightLogical:
1332                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) >> As<SIMD::UInt>(rhs)));
1333                                 break;
1334                         case spv::OpShiftRightArithmetic:
1335                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::Int>(lhs) >> As<SIMD::Int>(rhs)));
1336                                 break;
1337                         case spv::OpShiftLeftLogical:
1338                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) << As<SIMD::UInt>(rhs)));
1339                                 break;
1340                         case spv::OpBitwiseOr:
1341                         case spv::OpLogicalOr:
1342                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) | As<SIMD::UInt>(rhs)));
1343                                 break;
1344                         case spv::OpBitwiseXor:
1345                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) ^ As<SIMD::UInt>(rhs)));
1346                                 break;
1347                         case spv::OpBitwiseAnd:
1348                         case spv::OpLogicalAnd:
1349                                 dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) & As<SIMD::UInt>(rhs)));
1350                                 break;
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))));
1357                                 break;
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))));
1361                                 break;
1362                         default:
1363                                 UNIMPLEMENTED("Unhandled binary operator %s", OpcodeName(insn.opcode()).c_str());
1364                         }
1365                 }
1366         }
1367
1368         void SpirvShader::EmitDot(InsnIterator insn, SpirvRoutine *routine) const
1369         {
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));
1376
1377                 SIMD::Float result = srcLHS[0] * srcRHS[0];
1378
1379                 for (auto i = 1u; i < lhsType.sizeInComponents; i++)
1380                 {
1381                         result += srcLHS[i] * srcRHS[i];
1382                 }
1383
1384                 dst.emplace(0, result);
1385         }
1386
1387         void SpirvShader::emitEpilog(SpirvRoutine *routine) const
1388         {
1389                 for (auto insn : *this)
1390                 {
1391                         switch (insn.opcode())
1392                         {
1393                         case spv::OpVariable:
1394                         {
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)
1399                                 {
1400                                         auto &dst = routine->getValue(resultId);
1401                                         int offset = 0;
1402                                         VisitInterface(resultId,
1403                                                                    [&](Decorations const &d, AttribType type) {
1404                                                                            auto scalarSlot = d.Location << 2 | d.Component;
1405                                                                            routine->outputs[scalarSlot] = dst[offset++];
1406                                                                    });
1407                                 }
1408                                 break;
1409                         }
1410                         default:
1411                                 break;
1412                         }
1413                 }
1414         }
1415 }