blob: e44be3e899e581b63d4dd15f66619183e8b30365 [file] [log] [blame]
Chris Forbesaf4ed532018-12-06 18:33:27 -08001// 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"
Chris Forbesebe5f7f2019-01-16 10:38:34 -080018#include "Vulkan/VkDebug.hpp"
Chris Forbesaf4ed532018-12-06 18:33:27 -080019#include "Device/Config.hpp"
20
21namespace sw
22{
23 volatile int SpirvShader::serialCounter = 1; // Start at 1, 0 is invalid shader.
24
Chris Forbes5839dcf2018-12-10 19:02:58 -080025 SpirvShader::SpirvShader(InsnStore const &insns)
26 : insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
27 outputs{MAX_INTERFACE_COMPONENTS},
28 serialID{serialCounter++}, modes{}
Chris Forbesaf4ed532018-12-06 18:33:27 -080029 {
30 // Simplifying assumptions (to be satisfied by earlier transformations)
Chris Forbesbde34082018-12-28 12:03:10 -080031 // - There is exactly one entrypoint in the module, and it's the one we want
Chris Forbesaf4ed532018-12-06 18:33:27 -080032 // - The only input/output OpVariables present are those used by the entrypoint
33
Chris Forbes4a979dc2019-01-17 09:36:46 -080034 for (auto insn : *this)
35 {
36 switch (insn.opcode())
37 {
38 case spv::OpExecutionMode:
39 ProcessExecutionMode(insn);
40 break;
Chris Forbesaf4ed532018-12-06 18:33:27 -080041
Chris Forbesc25b8072018-12-10 15:10:39 -080042 case spv::OpDecorate:
43 {
Ben Claytonab51bbf2019-02-20 14:36:27 +000044 TypeOrObjectID targetId = insn.word(1);
Chris Forbes93f70b32019-02-10 21:26:27 +000045 auto decoration = static_cast<spv::Decoration>(insn.word(2));
Chris Forbesc25b8072018-12-10 15:10:39 -080046 decorations[targetId].Apply(
Chris Forbes93f70b32019-02-10 21:26:27 +000047 decoration,
Chris Forbesc25b8072018-12-10 15:10:39 -080048 insn.wordCount() > 3 ? insn.word(3) : 0);
Chris Forbes93f70b32019-02-10 21:26:27 +000049
50 if (decoration == spv::DecorationCentroid)
51 modes.NeedsCentroid = true;
Chris Forbesc25b8072018-12-10 15:10:39 -080052 break;
53 }
54
55 case spv::OpMemberDecorate:
56 {
Ben Claytonab51bbf2019-02-20 14:36:27 +000057 TypeID targetId = insn.word(1);
Chris Forbesc25b8072018-12-10 15:10:39 -080058 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...
Chris Forbes93f70b32019-02-10 21:26:27 +000062 auto decoration = static_cast<spv::Decoration>(insn.word(3));
Chris Forbesc25b8072018-12-10 15:10:39 -080063 d[memberIndex].Apply(
Chris Forbes93f70b32019-02-10 21:26:27 +000064 decoration,
Chris Forbesc25b8072018-12-10 15:10:39 -080065 insn.wordCount() > 4 ? insn.word(4) : 0);
Chris Forbes93f70b32019-02-10 21:26:27 +000066
67 if (decoration == spv::DecorationCentroid)
68 modes.NeedsCentroid = true;
Chris Forbesc25b8072018-12-10 15:10:39 -080069 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
Chris Forbes4a979dc2019-01-17 09:36:46 -0800104 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 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000119 TypeID resultId = insn.word(1);
Ben Claytone205d342019-02-20 10:22:09 +0000120 auto &type = types[resultId];
121 type.definition = insn;
122 type.sizeInComponents = ComputeTypeSize(insn);
Chris Forbesbde34082018-12-28 12:03:10 -0800123
124 // A structure is a builtin block if it has a builtin
125 // member. All members of such a structure are builtins.
Ben Clayton9a162482019-02-25 11:54:43 +0000126 switch (insn.opcode())
127 {
128 case spv::OpTypeStruct:
Chris Forbesbde34082018-12-28 12:03:10 -0800129 {
130 auto d = memberDecorations.find(resultId);
131 if (d != memberDecorations.end())
132 {
133 for (auto &m : d->second)
134 {
135 if (m.HasBuiltIn)
136 {
Ben Claytone205d342019-02-20 10:22:09 +0000137 type.isBuiltInBlock = true;
Chris Forbesbde34082018-12-28 12:03:10 -0800138 break;
139 }
140 }
141 }
Ben Clayton9a162482019-02-25 11:54:43 +0000142 break;
Chris Forbesbde34082018-12-28 12:03:10 -0800143 }
Ben Clayton9a162482019-02-25 11:54:43 +0000144 case spv::OpTypePointer:
Chris Forbesbde34082018-12-28 12:03:10 -0800145 {
Ben Clayton9a162482019-02-25 11:54:43 +0000146 TypeID elementTypeId = insn.word(3);
147 type.element = elementTypeId;
148 type.isBuiltInBlock = getType(elementTypeId).isBuiltInBlock;
149 type.storageClass = static_cast<spv::StorageClass>(insn.word(2));
150 break;
151 }
152 case spv::OpTypeVector:
153 case spv::OpTypeMatrix:
154 case spv::OpTypeArray:
155 case spv::OpTypeRuntimeArray:
156 {
157 TypeID elementTypeId = insn.word(2);
158 type.element = elementTypeId;
159 break;
160 }
161 default:
162 break;
Chris Forbesbde34082018-12-28 12:03:10 -0800163 }
Chris Forbes4a979dc2019-01-17 09:36:46 -0800164 break;
165 }
Chris Forbes296aa252018-12-27 11:48:21 -0800166
Chris Forbes4a979dc2019-01-17 09:36:46 -0800167 case spv::OpVariable:
168 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000169 TypeID typeId = insn.word(1);
170 ObjectID resultId = insn.word(2);
Chris Forbes4a979dc2019-01-17 09:36:46 -0800171 auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
172 if (insn.wordCount() > 4)
173 UNIMPLEMENTED("Variable initializers not yet supported");
Chris Forbes296aa252018-12-27 11:48:21 -0800174
Chris Forbes4a979dc2019-01-17 09:36:46 -0800175 auto &object = defs[resultId];
176 object.kind = Object::Kind::Variable;
177 object.definition = insn;
Ben Clayton9a162482019-02-25 11:54:43 +0000178 object.type = typeId;
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000179 object.pointerBase = insn.word(2); // base is itself
Chris Forbesc25b8072018-12-10 15:10:39 -0800180
181 // Register builtins
Ben Clayton9a162482019-02-25 11:54:43 +0000182 switch (storageClass)
Chris Forbesc25b8072018-12-10 15:10:39 -0800183 {
Ben Clayton9a162482019-02-25 11:54:43 +0000184 case spv::StorageClassInput:
185 case spv::StorageClassOutput:
186 ProcessInterfaceVariable(object);
187 break;
188 default:
189 UNIMPLEMENTED("Unhandled storage class %d for OpVariable", (int)storageClass);
Chris Forbesc25b8072018-12-10 15:10:39 -0800190 }
Chris Forbes4a979dc2019-01-17 09:36:46 -0800191 break;
192 }
Chris Forbes296aa252018-12-27 11:48:21 -0800193
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800194 case spv::OpConstant:
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800195 CreateConstant(insn).constantValue[0] = insn.word(3);
196 break;
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800197 case spv::OpConstantFalse:
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800198 CreateConstant(insn).constantValue[0] = 0; // represent boolean false as zero
199 break;
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800200 case spv::OpConstantTrue:
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800201 CreateConstant(insn).constantValue[0] = ~0u; // represent boolean true as all bits set
202 break;
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800203 case spv::OpConstantNull:
204 {
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800205 // OpConstantNull forms a constant of arbitrary type, all zeros.
Ben Clayton9a162482019-02-25 11:54:43 +0000206 auto &object = CreateConstant(insn);
207 auto &objectTy = getType(object.type);
208 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800209 {
210 object.constantValue[i] = 0;
211 }
212 break;
213 }
214 case spv::OpConstantComposite:
215 {
216 auto &object = CreateConstant(insn);
217 auto offset = 0u;
218 for (auto i = 0u; i < insn.wordCount() - 3; i++)
219 {
Ben Clayton9a162482019-02-25 11:54:43 +0000220 auto &constituent = getObject(insn.word(i + 3));
221 auto &constituentTy = getType(constituent.type);
222 for (auto j = 0u; j < constituentTy.sizeInComponents; j++)
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800223 object.constantValue[offset++] = constituent.constantValue[j];
224 }
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800225 break;
226 }
227
Chris Forbesbde34082018-12-28 12:03:10 -0800228 case spv::OpCapability:
229 // Various capabilities will be declared, but none affect our code generation at this point.
230 case spv::OpMemoryModel:
231 // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
232 case spv::OpEntryPoint:
Chris Forbes7edf5342019-02-10 22:41:21 +0000233 case spv::OpFunction:
234 case spv::OpFunctionEnd:
235 // Due to preprocessing, the entrypoint and its function provide no value.
236 break;
237 case spv::OpExtInstImport:
238 // We will only support the GLSL 450 extended instruction set, so no point in tracking the ID we assign it.
239 // Valid shaders will not attempt to import any other instruction sets.
Chris Forbes1776af72019-02-22 17:39:57 -0800240 case spv::OpName:
241 case spv::OpMemberName:
242 case spv::OpSource:
243 case spv::OpSourceContinued:
244 case spv::OpSourceExtension:
245 // No semantic impact
Chris Forbes7edf5342019-02-10 22:41:21 +0000246 break;
247
248 case spv::OpFunctionParameter:
249 case spv::OpFunctionCall:
250 case spv::OpSpecConstant:
251 case spv::OpSpecConstantComposite:
252 case spv::OpSpecConstantFalse:
253 case spv::OpSpecConstantOp:
254 case spv::OpSpecConstantTrue:
255 // These should have all been removed by preprocessing passes. If we see them here,
256 // our assumptions are wrong and we will probably generate wrong code.
257 UNIMPLEMENTED("These instructions should have already been lowered.");
258 break;
259
Chris Forbesa71b8e92019-02-10 22:42:42 +0000260 case spv::OpLoad:
261 case spv::OpAccessChain:
Chris Forbesb97a9572019-02-21 16:51:42 -0800262 case spv::OpCompositeConstruct:
Chris Forbes1bc1acf2019-02-21 18:40:33 -0800263 case spv::OpCompositeInsert:
Chris Forbesb12846d2019-02-21 18:53:58 -0800264 case spv::OpCompositeExtract:
Chris Forbesa71b8e92019-02-10 22:42:42 +0000265 // Instructions that yield an ssavalue.
266 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000267 TypeID typeId = insn.word(1);
268 ObjectID resultId = insn.word(2);
Chris Forbesa71b8e92019-02-10 22:42:42 +0000269 auto &object = defs[resultId];
Ben Clayton9a162482019-02-25 11:54:43 +0000270 object.type = typeId;
Chris Forbesa71b8e92019-02-10 22:42:42 +0000271 object.kind = Object::Kind::Value;
272 object.definition = insn;
Chris Forbesa71b8e92019-02-10 22:42:42 +0000273
274 if (insn.opcode() == spv::OpAccessChain)
275 {
276 // interior ptr has two parts:
277 // - logical base ptr, common across all lanes and known at compile time
278 // - per-lane offset
Ben Clayton9a162482019-02-25 11:54:43 +0000279 ObjectID baseId = insn.word(3);
280 object.pointerBase = getObject(baseId).pointerBase;
Chris Forbesa71b8e92019-02-10 22:42:42 +0000281 }
282 break;
283 }
284
Chris Forbes7edf5342019-02-10 22:41:21 +0000285 case spv::OpStore:
286 case spv::OpReturn:
287 // Don't need to do anything during analysis pass
288 break;
289
290 case spv::OpKill:
291 modes.ContainsKill = true;
Chris Forbesbde34082018-12-28 12:03:10 -0800292 break;
293
Chris Forbes4a979dc2019-01-17 09:36:46 -0800294 default:
Chris Forbes64c44b02018-12-28 16:04:57 -0800295 printf("Warning: ignored opcode %u\n", insn.opcode());
Chris Forbes4a979dc2019-01-17 09:36:46 -0800296 break; // This is OK, these passes are intentionally partial
Chris Forbesaf4ed532018-12-06 18:33:27 -0800297 }
298 }
299 }
300
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800301 SpirvShader::Object& SpirvShader::CreateConstant(InsnIterator insn)
302 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000303 TypeID typeId = insn.word(1);
304 ObjectID resultId = insn.word(2);
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800305 auto &object = defs[resultId];
Ben Clayton9a162482019-02-25 11:54:43 +0000306 auto &objectTy = getType(typeId);
307 object.type = typeId;
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800308 object.kind = Object::Kind::Constant;
309 object.definition = insn;
Ben Clayton9a162482019-02-25 11:54:43 +0000310 object.constantValue = std::unique_ptr<uint32_t[]>(new uint32_t[objectTy.sizeInComponents]);
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800311 return object;
312 }
313
Chris Forbes049ff382019-02-02 15:16:43 -0800314 void SpirvShader::ProcessInterfaceVariable(Object &object)
Chris Forbesbde34082018-12-28 12:03:10 -0800315 {
Ben Clayton9a162482019-02-25 11:54:43 +0000316 auto &objectTy = getType(object.type);
317 assert(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
Chris Forbesbde34082018-12-28 12:03:10 -0800318
Ben Clayton9a162482019-02-25 11:54:43 +0000319 assert(objectTy.definition.opcode() == spv::OpTypePointer);
320 auto pointeeTy = getType(objectTy.element);
Chris Forbesbde34082018-12-28 12:03:10 -0800321
Ben Clayton9a162482019-02-25 11:54:43 +0000322 auto &builtinInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
323 auto &userDefinedInterface = (objectTy.storageClass == spv::StorageClassInput) ? inputs : outputs;
324
325 assert(object.definition.opcode() == spv::OpVariable);
Ben Claytonab51bbf2019-02-20 14:36:27 +0000326 ObjectID resultId = object.definition.word(2);
Ben Clayton9a162482019-02-25 11:54:43 +0000327
328 if (objectTy.isBuiltInBlock)
Chris Forbesbde34082018-12-28 12:03:10 -0800329 {
330 // walk the builtin block, registering each of its members separately.
Ben Clayton9a162482019-02-25 11:54:43 +0000331 auto m = memberDecorations.find(objectTy.element);
Chris Forbesbde34082018-12-28 12:03:10 -0800332 assert(m != memberDecorations.end()); // otherwise we wouldn't have marked the type chain
Ben Clayton9a162482019-02-25 11:54:43 +0000333 auto &structType = pointeeTy.definition;
Chris Forbesbde34082018-12-28 12:03:10 -0800334 auto offset = 0u;
335 auto word = 2u;
336 for (auto &member : m->second)
337 {
Chris Forbes840809a2019-01-14 14:30:20 -0800338 auto &memberType = getType(structType.word(word));
Chris Forbesbde34082018-12-28 12:03:10 -0800339
340 if (member.HasBuiltIn)
341 {
342 builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
343 }
344
345 offset += memberType.sizeInComponents;
346 ++word;
347 }
348 return;
349 }
350
351 auto d = decorations.find(resultId);
352 if (d != decorations.end() && d->second.HasBuiltIn)
353 {
Ben Clayton9a162482019-02-25 11:54:43 +0000354 builtinInterface[d->second.BuiltIn] = {resultId, 0, pointeeTy.sizeInComponents};
Chris Forbesbde34082018-12-28 12:03:10 -0800355 }
356 else
357 {
Chris Forbes049ff382019-02-02 15:16:43 -0800358 object.kind = Object::Kind::InterfaceVariable;
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800359 VisitInterface(resultId,
360 [&userDefinedInterface](Decorations const &d, AttribType type) {
361 // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
362 auto scalarSlot = (d.Location << 2) | d.Component;
363 assert(scalarSlot >= 0 &&
364 scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
365
366 auto &slot = userDefinedInterface[scalarSlot];
367 slot.Type = type;
368 slot.Flat = d.Flat;
369 slot.NoPerspective = d.NoPerspective;
370 slot.Centroid = d.Centroid;
371 });
Chris Forbesbde34082018-12-28 12:03:10 -0800372 }
373 }
374
Chris Forbesaf4ed532018-12-06 18:33:27 -0800375 void SpirvShader::ProcessExecutionMode(InsnIterator insn)
376 {
377 auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
Chris Forbes4a979dc2019-01-17 09:36:46 -0800378 switch (mode)
379 {
380 case spv::ExecutionModeEarlyFragmentTests:
381 modes.EarlyFragmentTests = true;
382 break;
383 case spv::ExecutionModeDepthReplacing:
384 modes.DepthReplacing = true;
385 break;
386 case spv::ExecutionModeDepthGreater:
387 modes.DepthGreater = true;
388 break;
389 case spv::ExecutionModeDepthLess:
390 modes.DepthLess = true;
391 break;
392 case spv::ExecutionModeDepthUnchanged:
393 modes.DepthUnchanged = true;
394 break;
395 case spv::ExecutionModeLocalSize:
396 modes.LocalSizeX = insn.word(3);
397 modes.LocalSizeZ = insn.word(5);
398 modes.LocalSizeY = insn.word(4);
399 break;
400 case spv::ExecutionModeOriginUpperLeft:
401 // This is always the case for a Vulkan shader. Do nothing.
402 break;
403 default:
404 UNIMPLEMENTED("No other execution modes are permitted");
Chris Forbesaf4ed532018-12-06 18:33:27 -0800405 }
406 }
Chris Forbes739a7fb2018-12-08 13:09:40 -0800407
408 uint32_t SpirvShader::ComputeTypeSize(sw::SpirvShader::InsnIterator insn)
409 {
410 // Types are always built from the bottom up (with the exception of forward ptrs, which
411 // don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
412 // already been described (and so their sizes determined)
413 switch (insn.opcode())
414 {
415 case spv::OpTypeVoid:
416 case spv::OpTypeSampler:
417 case spv::OpTypeImage:
418 case spv::OpTypeSampledImage:
419 case spv::OpTypeFunction:
420 case spv::OpTypeRuntimeArray:
421 // Objects that don't consume any space.
422 // Descriptor-backed objects currently only need exist at compile-time.
423 // Runtime arrays don't appear in places where their size would be interesting
424 return 0;
425
426 case spv::OpTypeBool:
427 case spv::OpTypeFloat:
428 case spv::OpTypeInt:
429 // All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
430 // we might need to change this, but only 32 bit components are required for Vulkan 1.1.
431 return 1;
432
433 case spv::OpTypeVector:
434 case spv::OpTypeMatrix:
435 // Vectors and matrices both consume element count * element size.
Chris Forbes840809a2019-01-14 14:30:20 -0800436 return getType(insn.word(2)).sizeInComponents * insn.word(3);
Chris Forbes739a7fb2018-12-08 13:09:40 -0800437
438 case spv::OpTypeArray:
Chris Forbes5be4d702018-12-27 16:12:31 -0800439 {
440 // Element count * element size. Array sizes come from constant ids.
441 auto arraySize = GetConstantInt(insn.word(3));
Chris Forbes840809a2019-01-14 14:30:20 -0800442 return getType(insn.word(2)).sizeInComponents * arraySize;
Chris Forbes5be4d702018-12-27 16:12:31 -0800443 }
Chris Forbes739a7fb2018-12-08 13:09:40 -0800444
445 case spv::OpTypeStruct:
446 {
447 uint32_t size = 0;
448 for (uint32_t i = 2u; i < insn.wordCount(); i++)
449 {
Chris Forbes840809a2019-01-14 14:30:20 -0800450 size += getType(insn.word(i)).sizeInComponents;
Chris Forbes739a7fb2018-12-08 13:09:40 -0800451 }
452 return size;
453 }
454
455 case spv::OpTypePointer:
Chris Forbes0f59a2c2019-02-10 23:03:12 +0000456 // Runtime representation of a pointer is a per-lane index.
457 // Note: clients are expected to look through the pointer if they want the pointee size instead.
458 return 1;
Chris Forbes739a7fb2018-12-08 13:09:40 -0800459
460 default:
461 // Some other random insn.
462 UNIMPLEMENTED("Only types are supported");
Ben Clayton60a3d6f2019-02-26 17:24:46 +0000463 return 0;
Chris Forbes739a7fb2018-12-08 13:09:40 -0800464 }
465 }
Chris Forbesc25b8072018-12-10 15:10:39 -0800466
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800467 template<typename F>
Ben Claytonab51bbf2019-02-20 14:36:27 +0000468 int SpirvShader::VisitInterfaceInner(TypeID id, Decorations d, F f) const
Chris Forbes5839dcf2018-12-10 19:02:58 -0800469 {
470 // Recursively walks variable definition and its type tree, taking into account
471 // any explicit Location or Component decorations encountered; where explicit
472 // Locations or Components are not specified, assigns them sequentially.
473 // Collected decorations are carried down toward the leaves and across
474 // siblings; Effect of decorations intentionally does not flow back up the tree.
475 //
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800476 // F is a functor to be called with the effective decoration set for every component.
477 //
478 // Returns the next available location, and calls f().
Chris Forbes5839dcf2018-12-10 19:02:58 -0800479
480 // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
481
Chris Forbes49d664d2019-02-12 19:24:50 +0000482 ApplyDecorationsForId(&d, id);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800483
Chris Forbes840809a2019-01-14 14:30:20 -0800484 auto const &obj = getType(id);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800485 switch (obj.definition.opcode())
486 {
Chris Forbes5839dcf2018-12-10 19:02:58 -0800487 case spv::OpTypePointer:
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800488 return VisitInterfaceInner<F>(obj.definition.word(3), d, f);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800489 case spv::OpTypeMatrix:
490 for (auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
491 {
492 // consumes same components of N consecutive locations
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800493 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800494 }
495 return d.Location;
496 case spv::OpTypeVector:
497 for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
498 {
499 // consumes N consecutive components in the same location
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800500 VisitInterfaceInner<F>(obj.definition.word(2), d, f);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800501 }
502 return d.Location + 1;
503 case spv::OpTypeFloat:
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800504 f(d, ATTRIBTYPE_FLOAT);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800505 return d.Location + 1;
506 case spv::OpTypeInt:
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800507 f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800508 return d.Location + 1;
509 case spv::OpTypeBool:
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800510 f(d, ATTRIBTYPE_UINT);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800511 return d.Location + 1;
512 case spv::OpTypeStruct:
513 {
Chris Forbes5839dcf2018-12-10 19:02:58 -0800514 // iterate over members, which may themselves have Location/Component decorations
515 for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
516 {
Chris Forbes49d664d2019-02-12 19:24:50 +0000517 ApplyDecorationsForIdMember(&d, id, i);
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800518 d.Location = VisitInterfaceInner<F>(obj.definition.word(i + 2), d, f);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800519 d.Component = 0; // Implicit locations always have component=0
520 }
521 return d.Location;
522 }
Chris Forbes5be4d702018-12-27 16:12:31 -0800523 case spv::OpTypeArray:
524 {
525 auto arraySize = GetConstantInt(obj.definition.word(3));
526 for (auto i = 0u; i < arraySize; i++)
527 {
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800528 d.Location = VisitInterfaceInner<F>(obj.definition.word(2), d, f);
Chris Forbes5be4d702018-12-27 16:12:31 -0800529 }
530 return d.Location;
531 }
Chris Forbes5839dcf2018-12-10 19:02:58 -0800532 default:
533 // Intentionally partial; most opcodes do not participate in type hierarchies
534 return 0;
535 }
536 }
537
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800538 template<typename F>
Ben Claytonab51bbf2019-02-20 14:36:27 +0000539 void SpirvShader::VisitInterface(ObjectID id, F f) const
Chris Forbes5839dcf2018-12-10 19:02:58 -0800540 {
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800541 // Walk a variable definition and call f for each component in it.
Chris Forbes5839dcf2018-12-10 19:02:58 -0800542 Decorations d{};
Chris Forbes49d664d2019-02-12 19:24:50 +0000543 ApplyDecorationsForId(&d, id);
Chris Forbes1c658232019-02-01 17:12:25 -0800544
545 auto def = getObject(id).definition;
546 assert(def.opcode() == spv::OpVariable);
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800547 VisitInterfaceInner<F>(def.word(1), d, f);
Chris Forbes5839dcf2018-12-10 19:02:58 -0800548 }
549
Ben Claytonab51bbf2019-02-20 14:36:27 +0000550 Int4 SpirvShader::WalkAccessChain(ObjectID id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const
Chris Forbes38f85b32019-02-12 20:10:05 +0000551 {
Chris Forbes38f85b32019-02-12 20:10:05 +0000552 // TODO: think about explicit layout (UBO/SSBO) storage classes
553 // TODO: avoid doing per-lane work in some cases if we can?
554
Chris Forbes6397ed02019-02-15 16:39:17 -0800555 int constantOffset = 0;
556 Int4 dynamicOffset = Int4(0);
Ben Clayton9a162482019-02-25 11:54:43 +0000557 auto &baseObject = getObject(id);
558 TypeID typeId = getType(baseObject.type).element;
Chris Forbes38f85b32019-02-12 20:10:05 +0000559
Chris Forbese4ef5f72019-02-15 16:00:08 -0800560 // The <base> operand is an intermediate value itself, ie produced by a previous OpAccessChain.
561 // Start with its offset and build from there.
Chris Forbes38f85b32019-02-12 20:10:05 +0000562 if (baseObject.kind == Object::Kind::Value)
Chris Forbese4ef5f72019-02-15 16:00:08 -0800563 dynamicOffset += As<Int4>(routine->getIntermediate(id)[0]);
Chris Forbes38f85b32019-02-12 20:10:05 +0000564
565 for (auto i = 0u; i < numIndexes; i++)
566 {
567 auto & type = getType(typeId);
568 switch (type.definition.opcode())
569 {
570 case spv::OpTypeStruct:
571 {
572 int memberIndex = GetConstantInt(indexIds[i]);
573 int offsetIntoStruct = 0;
574 for (auto j = 0; j < memberIndex; j++) {
Chris Forbes58bee562019-02-19 17:41:41 -0800575 auto memberType = type.definition.word(2u + j);
576 offsetIntoStruct += getType(memberType).sizeInComponents;
Chris Forbes38f85b32019-02-12 20:10:05 +0000577 }
Chris Forbes6397ed02019-02-15 16:39:17 -0800578 constantOffset += offsetIntoStruct;
Chris Forbes58bee562019-02-19 17:41:41 -0800579 typeId = type.definition.word(2u + memberIndex);
Chris Forbes38f85b32019-02-12 20:10:05 +0000580 break;
581 }
582
583 case spv::OpTypeVector:
584 case spv::OpTypeMatrix:
585 case spv::OpTypeArray:
586 {
Ben Clayton9a162482019-02-25 11:54:43 +0000587 auto stride = getType(type.element).sizeInComponents;
Chris Forbes38f85b32019-02-12 20:10:05 +0000588 auto & obj = getObject(indexIds[i]);
589 if (obj.kind == Object::Kind::Constant)
Chris Forbes6397ed02019-02-15 16:39:17 -0800590 constantOffset += stride * GetConstantInt(indexIds[i]);
Chris Forbes38f85b32019-02-12 20:10:05 +0000591 else
Chris Forbese4ef5f72019-02-15 16:00:08 -0800592 dynamicOffset += Int4(stride) * As<Int4>(routine->getIntermediate(indexIds[i])[0]);
Ben Clayton9a162482019-02-25 11:54:43 +0000593 typeId = type.element;
Chris Forbes38f85b32019-02-12 20:10:05 +0000594 break;
595 }
596
597 default:
598 UNIMPLEMENTED("Unexpected type in WalkAccessChain");
599 }
600 }
601
Chris Forbes6397ed02019-02-15 16:39:17 -0800602 return dynamicOffset + Int4(constantOffset);
Chris Forbes38f85b32019-02-12 20:10:05 +0000603 }
604
Chris Forbes9638b942019-02-21 18:39:31 -0800605 uint32_t SpirvShader::WalkLiteralAccessChain(TypeID typeId, uint32_t numIndexes, uint32_t const *indexes) const
606 {
607 uint32_t constantOffset = 0;
608
609 for (auto i = 0u; i < numIndexes; i++)
610 {
611 auto & type = getType(typeId);
612 switch (type.definition.opcode())
613 {
614 case spv::OpTypeStruct:
615 {
616 int memberIndex = indexes[i];
617 int offsetIntoStruct = 0;
618 for (auto j = 0; j < memberIndex; j++) {
619 auto memberType = type.definition.word(2u + j);
620 offsetIntoStruct += getType(memberType).sizeInComponents;
621 }
622 constantOffset += offsetIntoStruct;
623 typeId = type.definition.word(2u + memberIndex);
624 break;
625 }
626
627 case spv::OpTypeVector:
628 case spv::OpTypeMatrix:
629 case spv::OpTypeArray:
630 {
631 auto elementType = type.definition.word(2);
632 auto stride = getType(elementType).sizeInComponents;
633 constantOffset += stride * indexes[i];
634 typeId = elementType;
635 break;
636 }
637
638 default:
639 UNIMPLEMENTED("Unexpected type in WalkLiteralAccessChain");
640 }
641 }
642
643 return constantOffset;
644 }
645
Chris Forbesc25b8072018-12-10 15:10:39 -0800646 void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
647 {
648 switch (decoration)
649 {
650 case spv::DecorationLocation:
651 HasLocation = true;
652 Location = static_cast<int32_t>(arg);
653 break;
654 case spv::DecorationComponent:
655 HasComponent = true;
656 Component = arg;
657 break;
658 case spv::DecorationBuiltIn:
659 HasBuiltIn = true;
660 BuiltIn = static_cast<spv::BuiltIn>(arg);
661 break;
662 case spv::DecorationFlat:
663 Flat = true;
664 break;
665 case spv::DecorationNoPerspective:
Chris Forbes5839dcf2018-12-10 19:02:58 -0800666 NoPerspective = true;
Chris Forbesc25b8072018-12-10 15:10:39 -0800667 break;
668 case spv::DecorationCentroid:
669 Centroid = true;
670 break;
671 case spv::DecorationBlock:
672 Block = true;
673 break;
674 case spv::DecorationBufferBlock:
675 BufferBlock = true;
676 break;
677 default:
678 // Intentionally partial, there are many decorations we just don't care about.
679 break;
680 }
681 }
682
683 void SpirvShader::Decorations::Apply(const sw::SpirvShader::Decorations &src)
684 {
685 // Apply a decoration group to this set of decorations
686 if (src.HasBuiltIn)
687 {
688 HasBuiltIn = true;
689 BuiltIn = src.BuiltIn;
690 }
691
692 if (src.HasLocation)
693 {
694 HasLocation = true;
695 Location = src.Location;
696 }
697
698 if (src.HasComponent)
699 {
700 HasComponent = true;
701 Component = src.Component;
702 }
703
704 Flat |= src.Flat;
Chris Forbes5839dcf2018-12-10 19:02:58 -0800705 NoPerspective |= src.NoPerspective;
Chris Forbesc25b8072018-12-10 15:10:39 -0800706 Centroid |= src.Centroid;
707 Block |= src.Block;
708 BufferBlock |= src.BufferBlock;
709 }
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800710
Ben Claytonab51bbf2019-02-20 14:36:27 +0000711 void SpirvShader::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
Chris Forbes49d664d2019-02-12 19:24:50 +0000712 {
713 auto it = decorations.find(id);
714 if (it != decorations.end())
715 d->Apply(it->second);
716 }
717
Ben Claytonab51bbf2019-02-20 14:36:27 +0000718 void SpirvShader::ApplyDecorationsForIdMember(Decorations *d, TypeID id, uint32_t member) const
Chris Forbes49d664d2019-02-12 19:24:50 +0000719 {
720 auto it = memberDecorations.find(id);
721 if (it != memberDecorations.end() && member < it->second.size())
722 {
723 d->Apply(it->second[member]);
724 }
725 }
726
Ben Claytonab51bbf2019-02-20 14:36:27 +0000727 uint32_t SpirvShader::GetConstantInt(ObjectID id) const
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800728 {
729 // Slightly hackish access to constants very early in translation.
730 // General consumption of constants by other instructions should
731 // probably be just lowered to Reactor.
732
733 // TODO: not encountered yet since we only use this for array sizes etc,
734 // but is possible to construct integer constant 0 via OpConstantNull.
Chris Forbesb8fb08a2019-02-13 11:45:27 -0800735 auto insn = getObject(id).definition;
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800736 assert(insn.opcode() == spv::OpConstant);
Chris Forbes840809a2019-01-14 14:30:20 -0800737 assert(getType(insn.word(1)).definition.opcode() == spv::OpTypeInt);
Chris Forbesbc3a0ee2018-12-27 16:02:58 -0800738 return insn.word(3);
739 }
Chris Forbesd5aed492019-02-02 15:18:52 -0800740
741 // emit-time
742
Chris Forbesc61271e2019-02-19 17:01:28 -0800743 void SpirvShader::emitProlog(SpirvRoutine *routine) const
Chris Forbesd5aed492019-02-02 15:18:52 -0800744 {
745 for (auto insn : *this)
746 {
747 switch (insn.opcode())
748 {
749 case spv::OpVariable:
750 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000751 ObjectID resultId = insn.word(2);
Chris Forbes0eba65b2019-02-13 12:24:35 -0800752 auto &object = getObject(resultId);
Ben Clayton9a162482019-02-25 11:54:43 +0000753 auto &objectTy = getType(object.type);
754 auto &pointeeTy = getType(objectTy.element);
Chris Forbesd5aed492019-02-02 15:18:52 -0800755 // TODO: what to do about zero-slot objects?
Ben Clayton9a162482019-02-25 11:54:43 +0000756 if (pointeeTy.sizeInComponents > 0)
Chris Forbesd5aed492019-02-02 15:18:52 -0800757 {
Ben Clayton9a162482019-02-25 11:54:43 +0000758 routine->createLvalue(insn.word(2), pointeeTy.sizeInComponents);
Chris Forbesd5aed492019-02-02 15:18:52 -0800759 }
760 break;
761 }
762 default:
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000763 // Nothing else produces interface variables, so can all be safely ignored.
Chris Forbesd5aed492019-02-02 15:18:52 -0800764 break;
765 }
766 }
767 }
768
769 void SpirvShader::emit(SpirvRoutine *routine) const
770 {
Chris Forbesd5aed492019-02-02 15:18:52 -0800771 for (auto insn : *this)
772 {
773 switch (insn.opcode())
774 {
Chris Forbese53533d2019-02-21 16:49:51 -0800775 case spv::OpTypeVoid:
776 case spv::OpTypeInt:
777 case spv::OpTypeFloat:
778 case spv::OpTypeBool:
779 case spv::OpTypeVector:
780 case spv::OpTypeArray:
781 case spv::OpTypeRuntimeArray:
782 case spv::OpTypeMatrix:
783 case spv::OpTypeStruct:
784 case spv::OpTypePointer:
785 case spv::OpTypeFunction:
786 case spv::OpExecutionMode:
787 case spv::OpMemoryModel:
788 case spv::OpFunction:
789 case spv::OpFunctionEnd:
790 case spv::OpConstant:
791 case spv::OpConstantNull:
792 case spv::OpConstantTrue:
793 case spv::OpConstantFalse:
794 case spv::OpConstantComposite:
795 case spv::OpExtension:
796 case spv::OpCapability:
797 case spv::OpEntryPoint:
798 case spv::OpExtInstImport:
799 case spv::OpDecorate:
800 case spv::OpMemberDecorate:
801 case spv::OpGroupDecorate:
802 case spv::OpGroupMemberDecorate:
803 case spv::OpDecorationGroup:
Chris Forbes1776af72019-02-22 17:39:57 -0800804 case spv::OpName:
805 case spv::OpMemberName:
806 case spv::OpSource:
807 case spv::OpSourceContinued:
808 case spv::OpSourceExtension:
Chris Forbese53533d2019-02-21 16:49:51 -0800809 // Nothing to do at emit time. These are either fully handled at analysis time,
810 // or don't require any work at all.
811 break;
812
Chris Forbes0eba65b2019-02-13 12:24:35 -0800813 case spv::OpVariable:
814 {
Ben Claytonab51bbf2019-02-20 14:36:27 +0000815 ObjectID resultId = insn.word(2);
Chris Forbes0eba65b2019-02-13 12:24:35 -0800816 auto &object = getObject(resultId);
Ben Clayton9a162482019-02-25 11:54:43 +0000817 auto &objectTy = getType(object.type);
818 if (object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassInput)
Chris Forbes0eba65b2019-02-13 12:24:35 -0800819 {
820 auto &dst = routine->getValue(resultId);
821 int offset = 0;
822 VisitInterface(resultId,
823 [&](Decorations const &d, AttribType type) {
824 auto scalarSlot = d.Location << 2 | d.Component;
Chris Forbes64be7c72019-02-19 16:40:57 -0800825 dst[offset++] = routine->inputs[scalarSlot];
Chris Forbes0eba65b2019-02-13 12:24:35 -0800826 });
827 }
828 break;
829 }
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000830 case spv::OpLoad:
831 {
Ben Clayton9a162482019-02-25 11:54:43 +0000832 ObjectID objectId = insn.word(2);
833 ObjectID pointerId = insn.word(3);
834 auto &object = getObject(objectId);
835 auto &objectTy = getType(object.type);
836 auto &pointer = getObject(pointerId);
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000837 auto &pointerBase = getObject(pointer.pointerBase);
Ben Clayton9a162482019-02-25 11:54:43 +0000838 auto &pointerBaseTy = getType(pointerBase.type);
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000839
Ben Clayton60a3d6f2019-02-26 17:24:46 +0000840 ASSERT(getType(pointer.type).element == object.type);
841 ASSERT(TypeID(insn.word(1)) == object.type);
Ben Clayton9a162482019-02-25 11:54:43 +0000842
843 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
844 pointerBaseTy.storageClass == spv::StorageClassUniform ||
845 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000846 {
847 UNIMPLEMENTED("Descriptor-backed load not yet implemented");
848 }
849
Ben Clayton9a162482019-02-25 11:54:43 +0000850 auto &ptrBase = routine->getValue(pointer.pointerBase);
Chris Forbes928dfee2019-02-26 21:22:32 -0800851 auto &dst = routine->createIntermediate(objectId, objectTy.sizeInComponents);
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000852
853 if (pointer.kind == Object::Kind::Value)
854 {
Chris Forbese4ef5f72019-02-15 16:00:08 -0800855 auto offsets = As<Int4>(routine->getIntermediate(insn.word(3))[0]);
Ben Clayton9a162482019-02-25 11:54:43 +0000856 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000857 {
858 // i wish i had a Float,Float,Float,Float constructor here..
859 Float4 v;
860 for (int j = 0; j < 4; j++)
Ben Clayton9a162482019-02-25 11:54:43 +0000861 {
862 Int offset = Int(i) + Extract(offsets, j);
863 v = Insert(v, Extract(ptrBase[offset], j), j);
864 }
Chris Forbese4ef5f72019-02-15 16:00:08 -0800865 dst.emplace(i, v);
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000866 }
867 }
868 else
869 {
870 // no divergent offsets to worry about
Ben Clayton9a162482019-02-25 11:54:43 +0000871 for (auto i = 0u; i < objectTy.sizeInComponents; i++)
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000872 {
Chris Forbese4ef5f72019-02-15 16:00:08 -0800873 dst.emplace(i, ptrBase[i]);
Chris Forbese9f8f5b2019-02-11 00:20:16 +0000874 }
875 }
876 break;
877 }
Chris Forbes38f85b32019-02-12 20:10:05 +0000878 case spv::OpAccessChain:
879 {
Ben Clayton9a162482019-02-25 11:54:43 +0000880 TypeID typeId = insn.word(1);
881 ObjectID objectId = insn.word(2);
882 ObjectID baseId = insn.word(3);
883 auto &object = getObject(objectId);
884 auto &type = getType(typeId);
Chris Forbes38f85b32019-02-12 20:10:05 +0000885 auto &pointerBase = getObject(object.pointerBase);
Ben Clayton9a162482019-02-25 11:54:43 +0000886 auto &pointerBaseTy = getType(pointerBase.type);
Ben Clayton60a3d6f2019-02-26 17:24:46 +0000887 ASSERT(type.sizeInComponents == 1);
888 ASSERT(getObject(baseId).pointerBase == object.pointerBase);
Chris Forbes38f85b32019-02-12 20:10:05 +0000889
Ben Clayton9a162482019-02-25 11:54:43 +0000890 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
891 pointerBaseTy.storageClass == spv::StorageClassUniform ||
892 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
Chris Forbes38f85b32019-02-12 20:10:05 +0000893 {
894 UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
895 }
Chris Forbes928dfee2019-02-26 21:22:32 -0800896 auto &dst = routine->createIntermediate(objectId, type.sizeInComponents);
Ben Clayton9a162482019-02-25 11:54:43 +0000897 dst.emplace(0, As<Float4>(WalkAccessChain(baseId, insn.wordCount() - 4, insn.wordPointer(4), routine)));
Chris Forbes38f85b32019-02-12 20:10:05 +0000898 break;
899 }
Chris Forbes1c588002019-02-12 18:56:38 +0000900 case spv::OpStore:
901 {
Ben Clayton9a162482019-02-25 11:54:43 +0000902 ObjectID pointerId = insn.word(1);
903 ObjectID objectId = insn.word(2);
904 auto &object = getObject(objectId);
905 auto &pointer = getObject(pointerId);
906 auto &pointerTy = getType(pointer.type);
907 auto &elementTy = getType(pointerTy.element);
Chris Forbes1c588002019-02-12 18:56:38 +0000908 auto &pointerBase = getObject(pointer.pointerBase);
Ben Clayton9a162482019-02-25 11:54:43 +0000909 auto &pointerBaseTy = getType(pointerBase.type);
Chris Forbes1c588002019-02-12 18:56:38 +0000910
Ben Clayton9a162482019-02-25 11:54:43 +0000911 if (pointerBaseTy.storageClass == spv::StorageClassImage ||
912 pointerBaseTy.storageClass == spv::StorageClassUniform ||
913 pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
Chris Forbes1c588002019-02-12 18:56:38 +0000914 {
915 UNIMPLEMENTED("Descriptor-backed store not yet implemented");
916 }
917
Ben Clayton9a162482019-02-25 11:54:43 +0000918 auto &ptrBase = routine->getValue(pointer.pointerBase);
Chris Forbes1c588002019-02-12 18:56:38 +0000919
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800920 if (object.kind == Object::Kind::Constant)
Chris Forbes1c588002019-02-12 18:56:38 +0000921 {
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800922 auto src = reinterpret_cast<float *>(object.constantValue.get());
923
924 if (pointer.kind == Object::Kind::Value)
Chris Forbes1c588002019-02-12 18:56:38 +0000925 {
Ben Clayton9a162482019-02-25 11:54:43 +0000926 auto offsets = As<Int4>(routine->getIntermediate(pointerId)[0]);
927 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
Chris Forbes1c588002019-02-12 18:56:38 +0000928 {
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800929 // Scattered store
930 for (int j = 0; j < 4; j++)
931 {
932 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
933 dst = Insert(dst, Float(src[i]), j);
934 }
935 }
936 }
937 else
938 {
939 // no divergent offsets
Ben Clayton9a162482019-02-25 11:54:43 +0000940 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800941 {
942 ptrBase[i] = RValue<Float4>(src[i]);
Chris Forbes1c588002019-02-12 18:56:38 +0000943 }
944 }
945 }
946 else
947 {
Ben Clayton9a162482019-02-25 11:54:43 +0000948 auto &src = routine->getIntermediate(objectId);
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800949
950 if (pointer.kind == Object::Kind::Value)
Chris Forbes1c588002019-02-12 18:56:38 +0000951 {
Ben Clayton9a162482019-02-25 11:54:43 +0000952 auto offsets = As<Int4>(routine->getIntermediate(pointerId)[0]);
953 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800954 {
955 // Scattered store
956 for (int j = 0; j < 4; j++)
957 {
958 auto dst = ptrBase[Int(i) + Extract(offsets, j)];
959 dst = Insert(dst, Extract(src[i], j), j);
960 }
961 }
962 }
963 else
964 {
965 // no divergent offsets
Ben Clayton9a162482019-02-25 11:54:43 +0000966 for (auto i = 0u; i < elementTy.sizeInComponents; i++)
Chris Forbes1ca8acd2019-02-20 13:00:54 -0800967 {
968 ptrBase[i] = src[i];
969 }
Chris Forbes1c588002019-02-12 18:56:38 +0000970 }
971 }
972 break;
973 }
Chris Forbesb97a9572019-02-21 16:51:42 -0800974 case spv::OpCompositeConstruct:
975 {
976 auto &type = getType(insn.word(1));
977 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
978 auto offset = 0u;
979
980 for (auto i = 0u; i < insn.wordCount() - 3; i++)
981 {
982 ObjectID srcObjectId = insn.word(3u + i);
983 auto & srcObject = getObject(srcObjectId);
984 auto & srcObjectTy = getType(srcObject.type);
985 GenericValue srcObjectAccess(this, routine, srcObjectId);
986
987 for (auto j = 0u; j < srcObjectTy.sizeInComponents; j++)
988 dst.emplace(offset++, srcObjectAccess[j]);
989 }
990 break;
991 }
Chris Forbes1bc1acf2019-02-21 18:40:33 -0800992 case spv::OpCompositeInsert:
993 {
994 TypeID resultTypeId = insn.word(1);
995 auto &type = getType(resultTypeId);
996 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
997 auto &newPartObject = getObject(insn.word(3));
998 auto &newPartObjectTy = getType(newPartObject.type);
999 auto firstNewComponent = WalkLiteralAccessChain(resultTypeId, insn.wordCount() - 5, insn.wordPointer(5));
1000
1001 GenericValue srcObjectAccess(this, routine, insn.word(4));
1002 GenericValue newPartObjectAccess(this, routine, insn.word(3));
1003
1004 // old components before
1005 for (auto i = 0u; i < firstNewComponent; i++)
1006 dst.emplace(i, srcObjectAccess[i]);
1007 // new part
1008 for (auto i = 0u; i < newPartObjectTy.sizeInComponents; i++)
1009 dst.emplace(firstNewComponent + i, newPartObjectAccess[i]);
1010 // old components after
1011 for (auto i = firstNewComponent + newPartObjectTy.sizeInComponents; i < type.sizeInComponents; i++)
1012 dst.emplace(i, srcObjectAccess[i]);
1013 break;
1014 }
Chris Forbesb12846d2019-02-21 18:53:58 -08001015 case spv::OpCompositeExtract:
1016 {
1017 auto &type = getType(insn.word(1));
1018 auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
1019 auto &compositeObject = getObject(insn.word(3));
1020 TypeID compositeTypeId = compositeObject.definition.word(1);
1021 auto firstComponent = WalkLiteralAccessChain(compositeTypeId, insn.wordCount() - 4, insn.wordPointer(4));
1022
1023 GenericValue compositeObjectAccess(this, routine, insn.word(3));
1024 for (auto i = 0u; i < type.sizeInComponents; i++)
1025 dst.emplace(i, compositeObjectAccess[firstComponent + i]);
1026 break;
1027 }
Chris Forbesd5aed492019-02-02 15:18:52 -08001028 default:
1029 printf("emit: ignoring opcode %d\n", insn.opcode());
1030 break;
1031 }
1032 }
1033 }
Chris Forbesc61271e2019-02-19 17:01:28 -08001034
1035 void SpirvShader::emitEpilog(SpirvRoutine *routine) const
1036 {
1037 for (auto insn : *this)
1038 {
1039 switch (insn.opcode())
1040 {
1041 case spv::OpVariable:
1042 {
Ben Claytonab51bbf2019-02-20 14:36:27 +00001043 ObjectID resultId = insn.word(2);
Chris Forbesc61271e2019-02-19 17:01:28 -08001044 auto &object = getObject(resultId);
Ben Clayton9a162482019-02-25 11:54:43 +00001045 auto &objectTy = getType(object.type);
1046 if (object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
Chris Forbesc61271e2019-02-19 17:01:28 -08001047 {
1048 auto &dst = routine->getValue(resultId);
1049 int offset = 0;
1050 VisitInterface(resultId,
1051 [&](Decorations const &d, AttribType type) {
1052 auto scalarSlot = d.Location << 2 | d.Component;
1053 routine->outputs[scalarSlot] = dst[offset++];
1054 });
1055 }
1056 break;
1057 }
1058 default:
1059 break;
1060 }
1061 }
1062 }
Chris Forbesc25b8072018-12-10 15:10:39 -08001063}