OSDN Git Service

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