OSDN Git Service

SpirvShader: Split objects from types.
[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                                 auto 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                                 auto 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                         {
119                                 auto resultId = insn.word(1);
120                                 auto &type = types[resultId];
121                                 type.definition = insn;
122                                 type.sizeInComponents = ComputeTypeSize(insn);
123
124                                 // A structure is a builtin block if it has a builtin
125                                 // member. All members of such a structure are builtins.
126                                 if (insn.opcode() == spv::OpTypeStruct)
127                                 {
128                                         auto d = memberDecorations.find(resultId);
129                                         if (d != memberDecorations.end())
130                                         {
131                                                 for (auto &m : d->second)
132                                                 {
133                                                         if (m.HasBuiltIn)
134                                                         {
135                                                                 type.isBuiltInBlock = true;
136                                                                 break;
137                                                         }
138                                                 }
139                                         }
140                                 }
141                                 else if (insn.opcode() == spv::OpTypePointer)
142                                 {
143                                         auto pointeeType = insn.word(3);
144                                         type.isBuiltInBlock = getType(pointeeType).isBuiltInBlock;
145                                 }
146                                 break;
147                         }
148
149                         case spv::OpVariable:
150                         {
151                                 auto typeId = insn.word(1);
152                                 auto resultId = insn.word(2);
153                                 auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
154                                 if (insn.wordCount() > 4)
155                                         UNIMPLEMENTED("Variable initializers not yet supported");
156
157                                 auto &object = defs[resultId];
158                                 object.kind = Object::Kind::Variable;
159                                 object.definition = insn;
160                                 object.storageClass = storageClass;
161
162                                 auto &type = getType(typeId);
163                                 auto &pointeeType = getType(type.definition.word(3));
164
165                                 // OpVariable's "size" is the size of the allocation required (the size of the pointee)
166                                 object.sizeInComponents = pointeeType.sizeInComponents;
167                                 object.isBuiltInBlock = type.isBuiltInBlock;
168                                 object.pointerBase = insn.word(2);      // base is itself
169
170                                 // Register builtins
171
172                                 if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
173                                 {
174                                         ProcessInterfaceVariable(object);
175                                 }
176                                 break;
177                         }
178
179                         case spv::OpConstant:
180                         case spv::OpConstantComposite:
181                         case spv::OpConstantFalse:
182                         case spv::OpConstantTrue:
183                         case spv::OpConstantNull:
184                         {
185                                 auto typeId = insn.word(1);
186                                 auto resultId = insn.word(2);
187                                 auto &object = defs[resultId];
188                                 object.kind = Object::Kind::Constant;
189                                 object.definition = insn;
190                                 object.sizeInComponents = getType(typeId).sizeInComponents;
191                                 break;
192                         }
193
194                         case spv::OpCapability:
195                                 // Various capabilities will be declared, but none affect our code generation at this point.
196                         case spv::OpMemoryModel:
197                                 // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
198                         case spv::OpEntryPoint:
199                         case spv::OpFunction:
200                         case spv::OpFunctionEnd:
201                                 // Due to preprocessing, the entrypoint and its function provide no value.
202                                 break;
203                         case spv::OpExtInstImport:
204                                 // We will only support the GLSL 450 extended instruction set, so no point in tracking the ID we assign it.
205                                 // Valid shaders will not attempt to import any other instruction sets.
206                                 break;
207
208                         case spv::OpFunctionParameter:
209                         case spv::OpFunctionCall:
210                         case spv::OpSpecConstant:
211                         case spv::OpSpecConstantComposite:
212                         case spv::OpSpecConstantFalse:
213                         case spv::OpSpecConstantOp:
214                         case spv::OpSpecConstantTrue:
215                                 // These should have all been removed by preprocessing passes. If we see them here,
216                                 // our assumptions are wrong and we will probably generate wrong code.
217                                 UNIMPLEMENTED("These instructions should have already been lowered.");
218                                 break;
219
220                         case spv::OpLoad:
221                         case spv::OpAccessChain:
222                                 // Instructions that yield an ssavalue.
223                         {
224                                 auto typeId = insn.word(1);
225                                 auto resultId = insn.word(2);
226                                 auto &object = defs[resultId];
227                                 object.kind = Object::Kind::Value;
228                                 object.definition = insn;
229                                 object.sizeInComponents = getType(typeId).sizeInComponents;
230
231                                 if (insn.opcode() == spv::OpAccessChain)
232                                 {
233                                         // interior ptr has two parts:
234                                         // - logical base ptr, common across all lanes and known at compile time
235                                         // - per-lane offset
236                                         object.pointerBase = getObject(insn.word(3)).pointerBase;
237                                 }
238                                 break;
239                         }
240
241                         case spv::OpStore:
242                         case spv::OpReturn:
243                                 // Don't need to do anything during analysis pass
244                                 break;
245
246                         case spv::OpKill:
247                                 modes.ContainsKill = true;
248                                 break;
249
250                         default:
251                                 printf("Warning: ignored opcode %u\n", insn.opcode());
252                                 break;    // This is OK, these passes are intentionally partial
253                         }
254                 }
255         }
256
257         void SpirvShader::ProcessInterfaceVariable(Object &object)
258         {
259                 assert(object.storageClass == spv::StorageClassInput || object.storageClass == spv::StorageClassOutput);
260
261                 auto &builtinInterface = (object.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
262                 auto &userDefinedInterface = (object.storageClass == spv::StorageClassInput) ? inputs : outputs;
263
264                 auto resultId = object.definition.word(2);
265                 if (object.isBuiltInBlock)
266                 {
267                         // walk the builtin block, registering each of its members separately.
268                         auto ptrType = getType(object.definition.word(1)).definition;
269                         assert(ptrType.opcode() == spv::OpTypePointer);
270                         auto pointeeType = ptrType.word(3);
271                         auto m = memberDecorations.find(pointeeType);
272                         assert(m != memberDecorations.end());        // otherwise we wouldn't have marked the type chain
273                         auto &structType = getType(pointeeType).definition;
274                         auto offset = 0u;
275                         auto word = 2u;
276                         for (auto &member : m->second)
277                         {
278                                 auto &memberType = getType(structType.word(word));
279
280                                 if (member.HasBuiltIn)
281                                 {
282                                         builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
283                                 }
284
285                                 offset += memberType.sizeInComponents;
286                                 ++word;
287                         }
288                         return;
289                 }
290
291                 auto d = decorations.find(resultId);
292                 if (d != decorations.end() && d->second.HasBuiltIn)
293                 {
294                         builtinInterface[d->second.BuiltIn] = {resultId, 0, object.sizeInComponents};
295                 }
296                 else
297                 {
298                         object.kind = Object::Kind::InterfaceVariable;
299                         VisitInterface(resultId,
300                                                    [&userDefinedInterface](Decorations const &d, AttribType type) {
301                                                            // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
302                                                            auto scalarSlot = (d.Location << 2) | d.Component;
303                                                            assert(scalarSlot >= 0 &&
304                                                                           scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
305
306                                                            auto &slot = userDefinedInterface[scalarSlot];
307                                                            slot.Type = type;
308                                                            slot.Flat = d.Flat;
309                                                            slot.NoPerspective = d.NoPerspective;
310                                                            slot.Centroid = d.Centroid;
311                                                    });
312                 }
313         }
314
315         void SpirvShader::ProcessExecutionMode(InsnIterator insn)
316         {
317                 auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
318                 switch (mode)
319                 {
320                 case spv::ExecutionModeEarlyFragmentTests:
321                         modes.EarlyFragmentTests = true;
322                         break;
323                 case spv::ExecutionModeDepthReplacing:
324                         modes.DepthReplacing = true;
325                         break;
326                 case spv::ExecutionModeDepthGreater:
327                         modes.DepthGreater = true;
328                         break;
329                 case spv::ExecutionModeDepthLess:
330                         modes.DepthLess = true;
331                         break;
332                 case spv::ExecutionModeDepthUnchanged:
333                         modes.DepthUnchanged = true;
334                         break;
335                 case spv::ExecutionModeLocalSize:
336                         modes.LocalSizeX = insn.word(3);
337                         modes.LocalSizeZ = insn.word(5);
338                         modes.LocalSizeY = insn.word(4);
339                         break;
340                 case spv::ExecutionModeOriginUpperLeft:
341                         // This is always the case for a Vulkan shader. Do nothing.
342                         break;
343                 default:
344                         UNIMPLEMENTED("No other execution modes are permitted");
345                 }
346         }
347
348         uint32_t SpirvShader::ComputeTypeSize(sw::SpirvShader::InsnIterator insn)
349         {
350                 // Types are always built from the bottom up (with the exception of forward ptrs, which
351                 // don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
352                 // already been described (and so their sizes determined)
353                 switch (insn.opcode())
354                 {
355                 case spv::OpTypeVoid:
356                 case spv::OpTypeSampler:
357                 case spv::OpTypeImage:
358                 case spv::OpTypeSampledImage:
359                 case spv::OpTypeFunction:
360                 case spv::OpTypeRuntimeArray:
361                         // Objects that don't consume any space.
362                         // Descriptor-backed objects currently only need exist at compile-time.
363                         // Runtime arrays don't appear in places where their size would be interesting
364                         return 0;
365
366                 case spv::OpTypeBool:
367                 case spv::OpTypeFloat:
368                 case spv::OpTypeInt:
369                         // All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
370                         // we might need to change this, but only 32 bit components are required for Vulkan 1.1.
371                         return 1;
372
373                 case spv::OpTypeVector:
374                 case spv::OpTypeMatrix:
375                         // Vectors and matrices both consume element count * element size.
376                         return getType(insn.word(2)).sizeInComponents * insn.word(3);
377
378                 case spv::OpTypeArray:
379                 {
380                         // Element count * element size. Array sizes come from constant ids.
381                         auto arraySize = GetConstantInt(insn.word(3));
382                         return getType(insn.word(2)).sizeInComponents * arraySize;
383                 }
384
385                 case spv::OpTypeStruct:
386                 {
387                         uint32_t size = 0;
388                         for (uint32_t i = 2u; i < insn.wordCount(); i++)
389                         {
390                                 size += getType(insn.word(i)).sizeInComponents;
391                         }
392                         return size;
393                 }
394
395                 case spv::OpTypePointer:
396                         // Runtime representation of a pointer is a per-lane index.
397                         // Note: clients are expected to look through the pointer if they want the pointee size instead.
398                         return 1;
399
400                 default:
401                         // Some other random insn.
402                         UNIMPLEMENTED("Only types are supported");
403                 }
404         }
405
406         template<typename F>
407         int SpirvShader::VisitInterfaceInner(uint32_t id, Decorations d, F f) const
408         {
409                 // Recursively walks variable definition and its type tree, taking into account
410                 // any explicit Location or Component decorations encountered; where explicit
411                 // Locations or Components are not specified, assigns them sequentially.
412                 // Collected decorations are carried down toward the leaves and across
413                 // siblings; Effect of decorations intentionally does not flow back up the tree.
414                 //
415                 // F is a functor to be called with the effective decoration set for every component.
416                 //
417                 // Returns the next available location, and calls f().
418
419                 // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
420
421                 ApplyDecorationsForId(&d, id);
422
423                 auto const &obj = getType(id);
424                 switch (obj.definition.opcode())
425                 {
426                 case spv::OpTypePointer:
427                         return VisitInterfaceInner<F>(obj.definition.word(3), d, f);
428                 case spv::OpTypeMatrix:
429                         for (auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
430                         {
431                                 // consumes same components of N consecutive locations
432                                 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
433                         }
434                         return d.Location;
435                 case spv::OpTypeVector:
436                         for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
437                         {
438                                 // consumes N consecutive components in the same location
439                                 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
440                         }
441                         return d.Location + 1;
442                 case spv::OpTypeFloat:
443                         f(d, ATTRIBTYPE_FLOAT);
444                         return d.Location + 1;
445                 case spv::OpTypeInt:
446                         f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
447                         return d.Location + 1;
448                 case spv::OpTypeBool:
449                         f(d, ATTRIBTYPE_UINT);
450                         return d.Location + 1;
451                 case spv::OpTypeStruct:
452                 {
453                         // iterate over members, which may themselves have Location/Component decorations
454                         for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
455                         {
456                                 ApplyDecorationsForIdMember(&d, id, i);
457                                 d.Location = VisitInterfaceInner<F>(obj.definition.word(i + 2), d, f);
458                                 d.Component = 0;    // Implicit locations always have component=0
459                         }
460                         return d.Location;
461                 }
462                 case spv::OpTypeArray:
463                 {
464                         auto arraySize = GetConstantInt(obj.definition.word(3));
465                         for (auto i = 0u; i < arraySize; i++)
466                         {
467                                 d.Location = VisitInterfaceInner<F>(obj.definition.word(2), d, f);
468                         }
469                         return d.Location;
470                 }
471                 default:
472                         // Intentionally partial; most opcodes do not participate in type hierarchies
473                         return 0;
474                 }
475         }
476
477         template<typename F>
478         void SpirvShader::VisitInterface(uint32_t id, F f) const
479         {
480                 // Walk a variable definition and call f for each component in it.
481                 Decorations d{};
482                 ApplyDecorationsForId(&d, id);
483
484                 auto def = getObject(id).definition;
485                 assert(def.opcode() == spv::OpVariable);
486                 VisitInterfaceInner<F>(def.word(1), d, f);
487         }
488
489         Int4 SpirvShader::WalkAccessChain(uint32_t id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const
490         {
491                 // TODO: think about decorations, to make this work on location based interfaces
492                 // TODO: think about explicit layout (UBO/SSBO) storage classes
493                 // TODO: avoid doing per-lane work in some cases if we can?
494
495                 int constantOffset = 0;
496                 Int4 dynamicOffset = Int4(0);
497                 auto & baseObject = getObject(id);
498                 auto typeId = baseObject.definition.word(1);
499
500                 if (baseObject.kind == Object::Kind::Value)
501                         dynamicOffset += As<Int4>(routine->getValue(id)[0]);
502
503                 for (auto i = 0u; i < numIndexes; i++)
504                 {
505                         auto & type = getType(typeId);
506                         switch (type.definition.opcode())
507                         {
508                         case spv::OpTypeStruct:
509                         {
510                                 int memberIndex = GetConstantInt(indexIds[i]);
511                                 int offsetIntoStruct = 0;
512                                 for (auto j = 0; j < memberIndex; j++) {
513                                         offsetIntoStruct += getType(type.definition.word(2 + memberIndex)).sizeInComponents;
514                                 }
515                                 constantOffset += offsetIntoStruct;
516                                 break;
517                         }
518
519                         case spv::OpTypeVector:
520                         case spv::OpTypeMatrix:
521                         case spv::OpTypeArray:
522                         {
523                                 auto stride = getType(type.definition.word(2)).sizeInComponents;
524                                 auto & obj = getObject(indexIds[i]);
525                                 if (obj.kind == Object::Kind::Constant)
526                                         constantOffset += stride * GetConstantInt(indexIds[i]);
527                                 else
528                                         dynamicOffset += Int4(stride) * As<Int4>(routine->getValue(indexIds[i])[0]);
529                                 break;
530                         }
531
532                         default:
533                                 UNIMPLEMENTED("Unexpected type in WalkAccessChain");
534                         }
535                 }
536
537                 return dynamicOffset + Int4(constantOffset);
538         }
539
540         void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
541         {
542                 switch (decoration)
543                 {
544                 case spv::DecorationLocation:
545                         HasLocation = true;
546                         Location = static_cast<int32_t>(arg);
547                         break;
548                 case spv::DecorationComponent:
549                         HasComponent = true;
550                         Component = arg;
551                         break;
552                 case spv::DecorationBuiltIn:
553                         HasBuiltIn = true;
554                         BuiltIn = static_cast<spv::BuiltIn>(arg);
555                         break;
556                 case spv::DecorationFlat:
557                         Flat = true;
558                         break;
559                 case spv::DecorationNoPerspective:
560                         NoPerspective = true;
561                         break;
562                 case spv::DecorationCentroid:
563                         Centroid = true;
564                         break;
565                 case spv::DecorationBlock:
566                         Block = true;
567                         break;
568                 case spv::DecorationBufferBlock:
569                         BufferBlock = true;
570                         break;
571                 default:
572                         // Intentionally partial, there are many decorations we just don't care about.
573                         break;
574                 }
575         }
576
577         void SpirvShader::Decorations::Apply(const sw::SpirvShader::Decorations &src)
578         {
579                 // Apply a decoration group to this set of decorations
580                 if (src.HasBuiltIn)
581                 {
582                         HasBuiltIn = true;
583                         BuiltIn = src.BuiltIn;
584                 }
585
586                 if (src.HasLocation)
587                 {
588                         HasLocation = true;
589                         Location = src.Location;
590                 }
591
592                 if (src.HasComponent)
593                 {
594                         HasComponent = true;
595                         Component = src.Component;
596                 }
597
598                 Flat |= src.Flat;
599                 NoPerspective |= src.NoPerspective;
600                 Centroid |= src.Centroid;
601                 Block |= src.Block;
602                 BufferBlock |= src.BufferBlock;
603         }
604
605         void SpirvShader::ApplyDecorationsForId(Decorations *d, uint32_t id) const
606         {
607                 auto it = decorations.find(id);
608                 if (it != decorations.end())
609                         d->Apply(it->second);
610         }
611
612         void SpirvShader::ApplyDecorationsForIdMember(Decorations *d, uint32_t id, uint32_t member) const
613         {
614                 auto it = memberDecorations.find(id);
615                 if (it != memberDecorations.end() && member < it->second.size())
616                 {
617                         d->Apply(it->second[member]);
618                 }
619         }
620
621         uint32_t SpirvShader::GetConstantInt(uint32_t id) const
622         {
623                 // Slightly hackish access to constants very early in translation.
624                 // General consumption of constants by other instructions should
625                 // probably be just lowered to Reactor.
626
627                 // TODO: not encountered yet since we only use this for array sizes etc,
628                 // but is possible to construct integer constant 0 via OpConstantNull.
629                 auto insn = getObject(id).definition;
630                 assert(insn.opcode() == spv::OpConstant);
631                 assert(getType(insn.word(1)).definition.opcode() == spv::OpTypeInt);
632                 return insn.word(3);
633         }
634
635         // emit-time
636
637         void SpirvShader::emitEarly(SpirvRoutine *routine) const
638         {
639                 for (auto insn : *this)
640                 {
641                         switch (insn.opcode())
642                         {
643                         case spv::OpVariable:
644                         {
645                                 auto resultId = insn.word(2);
646                                 auto &object = getObject(resultId);
647                                 // TODO: what to do about zero-slot objects?
648                                 if (object.sizeInComponents > 0)
649                                 {
650                                         routine->createLvalue(insn.word(2), object.sizeInComponents);
651                                 }
652                                 break;
653                         }
654                         default:
655                                 // Nothing else produces interface variables, so can all be safely ignored.
656                                 break;
657                         }
658                 }
659         }
660
661         void SpirvShader::emit(SpirvRoutine *routine) const
662         {
663                 for (auto insn : *this)
664                 {
665                         switch (insn.opcode())
666                         {
667                         case spv::OpVariable:
668                         {
669                                 auto resultId = insn.word(2);
670                                 auto &object = getObject(resultId);
671                                 if (object.kind == Object::Kind::InterfaceVariable && object.storageClass == spv::StorageClassInput)
672                                 {
673                                         auto &dst = routine->getValue(resultId);
674                                         int offset = 0;
675                                         VisitInterface(resultId,
676                                                                    [&](Decorations const &d, AttribType type) {
677                                                                            auto scalarSlot = d.Location << 2 | d.Component;
678                                                                            dst[offset++] = (*routine->inputs)[scalarSlot];
679                                                                    });
680                                 }
681                                 break;
682                         }
683                         case spv::OpLoad:
684                         {
685                                 auto &object = getObject(insn.word(2));
686                                 auto &type = getType(insn.word(1));
687                                 auto &pointer = getObject(insn.word(3));
688                                 routine->createLvalue(insn.word(2), type.sizeInComponents);             // TODO: this should be an ssavalue!
689                                 auto &pointerBase = getObject(pointer.pointerBase);
690
691                                 if (pointerBase.storageClass == spv::StorageClassImage ||
692                                         pointerBase.storageClass == spv::StorageClassUniform ||
693                                         pointerBase.storageClass == spv::StorageClassUniformConstant)
694                                 {
695                                         UNIMPLEMENTED("Descriptor-backed load not yet implemented");
696                                 }
697
698                                 SpirvRoutine::Value& ptrBase = routine->getValue(pointer.pointerBase);
699                                 auto & dst = routine->getValue(insn.word(2));
700
701                                 if (pointer.kind == Object::Kind::Value)
702                                 {
703                                         auto offsets = As<Int4>(routine->getValue(insn.word(3)));
704                                         for (auto i = 0u; i < object.sizeInComponents; i++)
705                                         {
706                                                 // i wish i had a Float,Float,Float,Float constructor here..
707                                                 Float4 v;
708                                                 for (int j = 0; j < 4; j++)
709                                                         v = Insert(v, Extract(ptrBase[Int(i) + Extract(offsets, j)], j), j);
710                                                 dst[i] = v;
711                                         }
712                                 }
713                                 else
714                                 {
715                                         // no divergent offsets to worry about
716                                         for (auto i = 0u; i < object.sizeInComponents; i++)
717                                         {
718                                                 dst[i] = ptrBase[i];
719                                         }
720                                 }
721                                 break;
722                         }
723                         case spv::OpAccessChain:
724                         {
725                                 auto &object = getObject(insn.word(2));
726                                 auto &type = getType(insn.word(1));
727                                 auto &base = getObject(insn.word(3));
728                                 routine->createLvalue(insn.word(2), type.sizeInComponents);             // TODO: this should be an ssavalue!
729                                 auto &pointerBase = getObject(object.pointerBase);
730                                 assert(type.sizeInComponents == 1);
731                                 assert(base.pointerBase == object.pointerBase);
732
733                                 if (pointerBase.storageClass == spv::StorageClassImage ||
734                                         pointerBase.storageClass == spv::StorageClassUniform ||
735                                         pointerBase.storageClass == spv::StorageClassUniformConstant)
736                                 {
737                                         UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
738                                 }
739
740                                 auto & dst = routine->getValue(insn.word(2));
741                                 dst[0] = As<Float4>(WalkAccessChain(insn.word(3), insn.wordCount() - 4, insn.wordPointer(4), routine));
742                                 break;
743                         }
744                         case spv::OpStore:
745                         {
746                                 auto &object = getObject(insn.word(2));
747                                 auto &pointer = getObject(insn.word(1));
748                                 auto &pointerBase = getObject(pointer.pointerBase);
749
750                                 if (pointerBase.storageClass == spv::StorageClassImage ||
751                                         pointerBase.storageClass == spv::StorageClassUniform ||
752                                         pointerBase.storageClass == spv::StorageClassUniformConstant)
753                                 {
754                                         UNIMPLEMENTED("Descriptor-backed store not yet implemented");
755                                 }
756
757                                 SpirvRoutine::Value& ptrBase = routine->getValue(pointer.pointerBase);
758                                 auto & src = routine->getValue(insn.word(2));;
759
760                                 if (pointer.kind == Object::Kind::Value)
761                                 {
762                                         auto offsets = As<Int4>(routine->getValue(insn.word(1)));
763                                         for (auto i = 0u; i < object.sizeInComponents; i++)
764                                         {
765                                                 // Scattered store
766                                                 for (int j = 0; j < 4; j++)
767                                                 {
768                                                         auto dst = ptrBase[Int(i) + Extract(offsets, j)];
769                                                         dst = Insert(dst, Extract(src[i], j), j);
770                                                 }
771                                         }
772                                 }
773                                 else
774                                 {
775                                         // no divergent offsets
776                                         for (auto i = 0u; i < object.sizeInComponents; i++)
777                                         {
778                                                 ptrBase[i] = src[i];
779                                         }
780                                 }
781                                 break;
782                         }
783                         default:
784                                 printf("emit: ignoring opcode %d\n", insn.opcode());
785                                 break;
786                         }
787                 }
788         }
789 }