Talvos  0.1
SPIR-V interpreter and dynamic analysis framework
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Invocation.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018 the Talvos developers. All rights reserved.
2 //
3 // This file is distributed under a three-clause BSD license. For full license
4 // terms please see the LICENSE file distributed with this source code.
5 
8 
9 #include <array>
10 #include <cassert>
11 #include <cmath>
12 #include <iostream>
13 #include <sstream>
14 
15 #include <spirv/unified1/GLSL.std.450.h>
16 #include <spirv/unified1/spirv.h>
17 
18 #include "talvos/Block.h"
19 #include "talvos/Device.h"
20 #include "talvos/EntryPoint.h"
21 #include "talvos/Function.h"
22 #include "talvos/Image.h"
23 #include "talvos/Instruction.h"
24 #include "talvos/Invocation.h"
25 #include "talvos/Memory.h"
26 #include "talvos/Module.h"
27 #include "talvos/PipelineStage.h"
28 #include "talvos/Type.h"
29 #include "talvos/Variable.h"
30 #include "talvos/Workgroup.h"
31 
33 #define OP(Index, Type) Objects[Inst->getOperand(Index)].get<Type>()
34 
35 namespace talvos
36 {
37 
38 Invocation::Invocation(Device &Dev, const std::vector<Object> &InitialObjects)
39  : Dev(Dev)
40 {
41  CurrentInstruction = nullptr;
42  PrivateMemory = nullptr;
43  PipelineMemory = nullptr;
44  Objects = InitialObjects;
45 }
46 
48  const std::vector<Object> &InitialObjects,
49  std::shared_ptr<Memory> PipelineMemory, Workgroup *Group,
50  Dim3 GlobalId)
51  : Dev(Dev), Group(Group), GlobalId(GlobalId), PipelineMemory(PipelineMemory)
52 {
54 
55  AtBarrier = false;
56  Discarded = false;
57  CurrentModule = Stage.getModule();
60 
61  // Clone initial object values.
62  Objects = InitialObjects;
63 
64  // Copy workgroup variable pointer values.
65  if (Group)
66  {
67  for (auto V : Group->getVariables())
68  Objects[V.first] = V.second;
69  }
70 
71  // Set up private variables.
72  for (auto V : CurrentModule->getVariables())
73  {
74  const Type *Ty = V->getType();
75  if (Ty->getStorageClass() != SpvStorageClassPrivate)
76  continue;
77 
78  // Allocate and initialize variable in private memory.
79  uint64_t NumBytes = Ty->getElementType()->getSize();
80  uint64_t Address = PrivateMemory->allocate(NumBytes);
81  Objects[V->getId()] = Object(Ty, Address);
82  if (V->getInitializer())
83  Objects[V->getInitializer()].store(*PrivateMemory, Address);
84  }
85 
86  Dev.reportInvocationBegin(this);
87 }
88 
90 
92 {
93  // Dispatch instruction to handler method.
94  uint16_t Opcode = Inst->getOpcode();
95  switch (Opcode)
96  {
97 #define DISPATCH(Op, Func) \
98  case Op: \
99  execute##Func(Inst); \
100  break
101 #define NOP(Op) \
102  case Op: \
103  break
104 
105  DISPATCH(SpvOpAccessChain, AccessChain);
106  DISPATCH(SpvOpAll, All);
107  DISPATCH(SpvOpAny, Any);
108  DISPATCH(SpvOpAtomicAnd, AtomicOp<uint32_t>);
109  DISPATCH(SpvOpAtomicCompareExchange, AtomicCompareExchange);
110  DISPATCH(SpvOpAtomicExchange, AtomicOp<uint32_t>);
111  DISPATCH(SpvOpAtomicIAdd, AtomicOp<uint32_t>);
112  DISPATCH(SpvOpAtomicIDecrement, AtomicOp<uint32_t>);
113  DISPATCH(SpvOpAtomicIIncrement, AtomicOp<uint32_t>);
114  DISPATCH(SpvOpAtomicISub, AtomicOp<uint32_t>);
115  DISPATCH(SpvOpAtomicLoad, AtomicOp<uint32_t>);
116  DISPATCH(SpvOpAtomicOr, AtomicOp<uint32_t>);
117  DISPATCH(SpvOpAtomicSMax, AtomicOp<int32_t>);
118  DISPATCH(SpvOpAtomicSMin, AtomicOp<int32_t>);
119  DISPATCH(SpvOpAtomicStore, AtomicOp<uint32_t>);
120  DISPATCH(SpvOpAtomicUMax, AtomicOp<uint32_t>);
121  DISPATCH(SpvOpAtomicUMin, AtomicOp<uint32_t>);
122  DISPATCH(SpvOpAtomicXor, AtomicOp<uint32_t>);
123  DISPATCH(SpvOpBitcast, Bitcast);
124  DISPATCH(SpvOpBitwiseAnd, BitwiseAnd);
125  DISPATCH(SpvOpBitwiseOr, BitwiseOr);
126  DISPATCH(SpvOpBitwiseXor, BitwiseXor);
127  DISPATCH(SpvOpBranch, Branch);
128  DISPATCH(SpvOpBranchConditional, BranchConditional);
129  DISPATCH(SpvOpCompositeConstruct, CompositeConstruct);
130  DISPATCH(SpvOpCompositeExtract, CompositeExtract);
131  DISPATCH(SpvOpCompositeInsert, CompositeInsert);
132  DISPATCH(SpvOpControlBarrier, ControlBarrier);
133  DISPATCH(SpvOpConvertFToS, ConvertFToS);
134  DISPATCH(SpvOpConvertFToU, ConvertFToU);
135  DISPATCH(SpvOpConvertSToF, ConvertSToF);
136  DISPATCH(SpvOpConvertUToF, ConvertUToF);
137  DISPATCH(SpvOpCopyMemory, CopyMemory);
138  DISPATCH(SpvOpCopyObject, CopyObject);
139  DISPATCH(SpvOpDot, Dot);
140  DISPATCH(SpvOpExtInst, ExtInst);
141  DISPATCH(SpvOpFAdd, FAdd);
142  DISPATCH(SpvOpFConvert, FConvert);
143  DISPATCH(SpvOpFDiv, FDiv);
144  DISPATCH(SpvOpFMod, FMod);
145  DISPATCH(SpvOpFMul, FMul);
146  DISPATCH(SpvOpFNegate, FNegate);
147  DISPATCH(SpvOpFOrdEqual, FOrdEqual);
148  DISPATCH(SpvOpFOrdGreaterThan, FOrdGreaterThan);
149  DISPATCH(SpvOpFOrdGreaterThanEqual, FOrdGreaterThanEqual);
150  DISPATCH(SpvOpFOrdLessThan, FOrdLessThan);
151  DISPATCH(SpvOpFOrdLessThanEqual, FOrdLessThanEqual);
152  DISPATCH(SpvOpFOrdNotEqual, FOrdNotEqual);
153  DISPATCH(SpvOpFRem, FRem);
154  DISPATCH(SpvOpFSub, FSub);
155  DISPATCH(SpvOpFunctionCall, FunctionCall);
156  DISPATCH(SpvOpFUnordEqual, FUnordEqual);
157  DISPATCH(SpvOpFUnordGreaterThan, FUnordGreaterThan);
158  DISPATCH(SpvOpFUnordGreaterThanEqual, FUnordGreaterThanEqual);
159  DISPATCH(SpvOpFUnordLessThan, FUnordLessThan);
160  DISPATCH(SpvOpFUnordLessThanEqual, FUnordLessThanEqual);
161  DISPATCH(SpvOpFUnordNotEqual, FUnordNotEqual);
162  DISPATCH(SpvOpIAdd, IAdd);
163  DISPATCH(SpvOpIEqual, IEqual);
164  DISPATCH(SpvOpImage, Image);
165  DISPATCH(SpvOpImageFetch, ImageRead);
166  DISPATCH(SpvOpImageQuerySize, ImageQuerySize);
167  DISPATCH(SpvOpImageQuerySizeLod, ImageQuerySize);
168  DISPATCH(SpvOpImageRead, ImageRead);
169  DISPATCH(SpvOpImageSampleExplicitLod, ImageSampleExplicitLod);
170  DISPATCH(SpvOpImageWrite, ImageWrite);
171  DISPATCH(SpvOpIMul, IMul);
172  DISPATCH(SpvOpInBoundsAccessChain, AccessChain);
173  DISPATCH(SpvOpINotEqual, INotEqual);
174  DISPATCH(SpvOpIsInf, IsInf);
175  DISPATCH(SpvOpIsNan, IsNan);
176  DISPATCH(SpvOpISub, ISub);
177  DISPATCH(SpvOpKill, Kill);
178  DISPATCH(SpvOpLoad, Load);
179  DISPATCH(SpvOpLogicalEqual, LogicalEqual);
180  DISPATCH(SpvOpLogicalNotEqual, LogicalNotEqual);
181  DISPATCH(SpvOpLogicalOr, LogicalOr);
182  DISPATCH(SpvOpLogicalAnd, LogicalAnd);
183  DISPATCH(SpvOpLogicalNot, LogicalNot);
184  DISPATCH(SpvOpMatrixTimesScalar, MatrixTimesScalar);
185  DISPATCH(SpvOpMatrixTimesVector, MatrixTimesVector);
186  DISPATCH(SpvOpNot, Not);
187  DISPATCH(SpvOpPhi, Phi);
188  DISPATCH(SpvOpPtrAccessChain, AccessChain);
189  DISPATCH(SpvOpReturn, Return);
190  DISPATCH(SpvOpReturnValue, ReturnValue);
191  DISPATCH(SpvOpSampledImage, SampledImage);
192  DISPATCH(SpvOpSConvert, SConvert);
193  DISPATCH(SpvOpSDiv, SDiv);
194  DISPATCH(SpvOpSelect, Select);
195  DISPATCH(SpvOpSGreaterThan, SGreaterThan);
196  DISPATCH(SpvOpSGreaterThanEqual, SGreaterThanEqual);
197  DISPATCH(SpvOpShiftLeftLogical, ShiftLeftLogical);
198  DISPATCH(SpvOpShiftRightArithmetic, ShiftRightArithmetic);
199  DISPATCH(SpvOpShiftRightLogical, ShiftRightLogical);
200  DISPATCH(SpvOpSLessThan, SLessThan);
201  DISPATCH(SpvOpSLessThanEqual, SLessThanEqual);
202  DISPATCH(SpvOpSMod, SMod);
203  DISPATCH(SpvOpSNegate, SNegate);
204  DISPATCH(SpvOpSRem, SRem);
205  DISPATCH(SpvOpStore, Store);
206  DISPATCH(SpvOpSwitch, Switch);
207  DISPATCH(SpvOpUConvert, UConvert);
208  DISPATCH(SpvOpUDiv, UDiv);
209  DISPATCH(SpvOpUGreaterThan, UGreaterThan);
210  DISPATCH(SpvOpUGreaterThanEqual, UGreaterThanEqual);
211  DISPATCH(SpvOpULessThan, ULessThan);
212  DISPATCH(SpvOpULessThanEqual, ULessThanEqual);
213  DISPATCH(SpvOpUMod, UMod);
214  DISPATCH(SpvOpUndef, Undef);
215  DISPATCH(SpvOpUnreachable, Unreachable);
216  DISPATCH(SpvOpVariable, Variable);
217  DISPATCH(SpvOpVectorExtractDynamic, VectorExtractDynamic);
218  DISPATCH(SpvOpVectorInsertDynamic, VectorInsertDynamic);
219  DISPATCH(SpvOpVectorShuffle, VectorShuffle);
220  DISPATCH(SpvOpVectorTimesMatrix, VectorTimesMatrix);
221  DISPATCH(SpvOpVectorTimesScalar, VectorTimesScalar);
222 
223  NOP(SpvOpNop);
224  NOP(SpvOpLine);
225  NOP(SpvOpLoopMerge);
226  NOP(SpvOpMemoryBarrier);
227  NOP(SpvOpNoLine);
228  NOP(SpvOpSelectionMerge);
229 
230 #undef DISPATCH
231 #undef NOP
232 
233  default:
234  Dev.reportError("Unimplemented instruction", true);
235  }
236 }
237 
239 {
240  // Base pointer.
241  uint32_t Id = Inst->getOperand(1);
242  Object &Base = Objects[Inst->getOperand(2)];
243 
244  // Ensure base pointer is valid.
245  if (!Base)
246  {
247  // Check for buffer variable matching base pointer ID.
248  for (auto V : CurrentModule->getVariables())
249  {
250  if (V->getId() == Inst->getOperand(2))
251  {
252  // Report error for missing descriptor set entry.
253  if (V->isBufferVariable())
254  {
255  std::stringstream Err;
256  Err << "Invalid base pointer for descriptor set entry ("
257  << V->getDecoration(SpvDecorationDescriptorSet) << ","
258  << V->getDecoration(SpvDecorationBinding) << ")";
259  Dev.reportError(Err.str());
260  }
261  else
262  {
263  Dev.reportError("Unresolved OpVariable pointer", true);
264  }
265 
266  // Set result pointer to null.
267  Objects[Id] = Object(Inst->getResultType(), (uint64_t)0);
268  return;
269  }
270  }
271  assert(false && "Invalid base pointer for AccessChain");
272  }
273 
274  uint64_t Result = Base.get<uint64_t>();
275  const Type *Ty = Base.getType()->getElementType();
276 
277  // Initialize matrix layout.
278  PtrMatrixLayout MatrixLayout;
279  if (Ty->isMatrix() || Ty->isVector())
280  MatrixLayout = Base.getMatrixLayout();
281 
282  // Offset of the first index operand.
283  uint32_t FirstIndexOperand = 3;
284 
285  // Perform initial dereference for element index for OpPtrAccessChain.
286  if (Inst->getOpcode() == SpvOpPtrAccessChain)
287  {
288  // TODO: Need to handle this?
289  assert(!Base.getDescriptorElements());
290 
291  FirstIndexOperand = 4;
292  switch (Objects[Inst->getOperand(3)].getType()->getSize())
293  {
294  case 2:
295  Result += Base.getType()->getElementOffset(OP(3, uint16_t));
296  break;
297  case 4:
298  Result += Base.getType()->getElementOffset(OP(3, uint32_t));
299  break;
300  case 8:
301  Result += Base.getType()->getElementOffset(OP(3, uint64_t));
302  break;
303  default:
304  Dev.reportError("Unhandled index size", true);
305  return;
306  }
307  }
308 
309  // Loop over indices.
310  for (uint32_t i = FirstIndexOperand; i < Inst->getNumOperands(); i++)
311  {
312  uint64_t Index;
313  const Object &IndexObj = Objects[Inst->getOperand(i)];
314  switch (IndexObj.getType()->getSize())
315  {
316  case 2:
317  Index = IndexObj.get<uint16_t>();
318  break;
319  case 4:
320  Index = IndexObj.get<uint32_t>();
321  break;
322  case 8:
323  Index = IndexObj.get<uint64_t>();
324  break;
325  default:
326  Dev.reportError("Unhandled index size", true);
327  return;
328  }
329 
330  const Type *ElemTy = Ty->getElementType(Index);
331 
332  if (Base.getDescriptorElements() && i == FirstIndexOperand)
333  {
334  // Special case for arrays of descriptors.
335  if (Index < Ty->getElementCount())
336  {
337  Result = Base.getDescriptorElements()[Index].Address;
338  }
339  else
340  {
341  Dev.reportError("Descriptor array element exceeds array size", false);
342  Result = 0;
343  }
344  }
345  else if (Ty->isMatrix() && MatrixLayout)
346  {
347  // Special case for matrix pointers with non-default layouts.
348  if (MatrixLayout.Order == PtrMatrixLayout::COL_MAJOR)
349  Result += Index * MatrixLayout.Stride;
350  else
351  Result += Index * ElemTy->getElementType()->getSize();
352  }
353  else if (Ty->isVector() && MatrixLayout)
354  {
355  // Special case for vector pointers with non-default layouts.
356  if (MatrixLayout.Order == PtrMatrixLayout::COL_MAJOR)
357  Result += Index * ElemTy->getSize();
358  else
359  Result += Index * MatrixLayout.Stride;
360  }
361  else
362  {
363  Result += Ty->getElementOffset(Index);
364  }
365 
366  // Check for structure member decorations that affect memory layout.
367  if (Ty->getTypeId() == Type::STRUCT)
368  {
369  auto &Decorations = Ty->getStructMemberDecorations((uint32_t)Index);
370  if (Decorations.count(SpvDecorationMatrixStride))
371  {
372  // Track matrix layout.
373  MatrixLayout.Stride = Decorations.at(SpvDecorationMatrixStride);
374  if (Decorations.count(SpvDecorationColMajor))
375  {
376  MatrixLayout.Order = PtrMatrixLayout::COL_MAJOR;
377  }
378  else
379  {
380  assert(Decorations.count(SpvDecorationRowMajor));
381  MatrixLayout.Order = PtrMatrixLayout::ROW_MAJOR;
382  }
383  }
384  }
385 
386  Ty = ElemTy;
387  }
388 
389  Objects[Id] = Object(Inst->getResultType(), Result);
390 
391  // Set matrix layout for result pointer if necessary.
392  if (MatrixLayout && (Ty->isVector() || Ty->isMatrix()))
393  Objects[Id].setMatrixLayout(MatrixLayout);
394 }
395 
397 {
398  uint32_t Id = Inst->getOperand(1);
399  Object Result(Inst->getResultType(), true);
400  const Object &Vector = Objects[Inst->getOperand(2)];
401  for (uint32_t i = 0; i < Vector.getType()->getElementCount(); i++)
402  {
403  if (!Vector.get<bool>(i))
404  {
405  Result.set(false);
406  break;
407  }
408  }
409  Objects[Id] = Result;
410 }
411 
413 {
414  uint32_t Id = Inst->getOperand(1);
415  Object Result(Inst->getResultType(), false);
416  const Object &Vector = Objects[Inst->getOperand(2)];
417  for (uint32_t i = 0; i < Vector.getType()->getElementCount(); i++)
418  {
419  if (Vector.get<bool>(i))
420  {
421  Result.set(true);
422  break;
423  }
424  }
425  Objects[Id] = Result;
426 }
427 
428 template <typename T> void Invocation::executeAtomicOp(const Instruction *Inst)
429 {
430  assert(Inst->getOpcode() != SpvOpAtomicCompareExchange);
431 
432  // Get index of pointer operand.
433  uint32_t PtrOp = (Inst->getOpcode() == SpvOpAtomicStore) ? 0 : 2;
434 
435  Object Pointer = Objects[Inst->getOperand(PtrOp)];
436  uint32_t Scope = Objects[Inst->getOperand(PtrOp + 1)].get<uint32_t>();
437  uint32_t Semantics = Objects[Inst->getOperand(PtrOp + 2)].get<uint32_t>();
438 
439  // Get value operand if present.
440  T Value = 0;
441  if (Inst->getNumOperands() > (PtrOp + 3))
442  Value = Objects[Inst->getOperand(PtrOp + 3)].get<T>();
443 
444  // Perform atomic operation.
445  Memory &Mem = getMemory(Pointer.getType()->getStorageClass());
446  T Result = Mem.atomic<T>(Pointer.get<uint64_t>(), Inst->getOpcode(), Scope,
447  Semantics, Value);
448 
449  // Create result if necessary.
450  if (PtrOp == 2)
451  Objects[Inst->getOperand(1)] = Object(Inst->getResultType(), Result);
452 }
453 
455 {
456  Object Pointer = Objects[Inst->getOperand(2)];
457  uint32_t Scope = Objects[Inst->getOperand(3)].get<uint32_t>();
458  uint32_t EqualSemantics = Objects[Inst->getOperand(4)].get<uint32_t>();
459  uint32_t UnequalSemantics = Objects[Inst->getOperand(5)].get<uint32_t>();
460  uint32_t Value = Objects[Inst->getOperand(6)].get<uint32_t>();
461  uint32_t Comparator = Objects[Inst->getOperand(7)].get<uint32_t>();
462 
463  Memory &Mem = getMemory(Pointer.getType()->getStorageClass());
464  uint32_t Result =
465  Mem.atomicCmpXchg(Pointer.get<uint64_t>(), Scope, EqualSemantics,
466  UnequalSemantics, Value, Comparator);
467  Objects[Inst->getOperand(1)] = Object(Inst->getResultType(), Result);
468 }
469 
471 {
472  const Object &Source = Objects[Inst->getOperand(2)];
473  Object Result = Object(Inst->getResultType(), Source.getData());
474  Objects[Inst->getOperand(1)] = Result;
475 }
476 
478 {
479  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A & B; });
480 }
481 
483 {
484  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A | B; });
485 }
486 
488 {
489  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A ^ B; });
490 }
491 
493 {
494  moveToBlock(Inst->getOperand(0));
495 }
496 
498 {
499  bool Condition = OP(0, bool);
500  moveToBlock(Inst->getOperand(Condition ? 1 : 2));
501 }
502 
504 {
505  uint32_t Id = Inst->getOperand(1);
506 
507  Object Result = Object(Inst->getResultType());
508 
509  // Set constituent values.
510  for (uint32_t i = 2; i < Inst->getNumOperands(); i++)
511  {
512  uint32_t Id = Inst->getOperand(i);
513  Result.insert({i - 2}, Objects[Id]);
514  }
515 
516  Objects[Id] = Result;
517 }
518 
520 {
521  uint32_t Id = Inst->getOperand(1);
522  // TODO: Handle indices of different sizes.
523  std::vector<uint32_t> Indices(Inst->getOperands() + 3,
524  Inst->getOperands() + Inst->getNumOperands());
525  Objects[Id] = Objects[Inst->getOperand(2)].extract(Indices);
526 }
527 
529 {
530  uint32_t Id = Inst->getOperand(1);
531  Object &Element = Objects[Inst->getOperand(2)];
532  // TODO: Handle indices of different sizes.
533  std::vector<uint32_t> Indices(Inst->getOperands() + 4,
534  Inst->getOperands() + Inst->getNumOperands());
535  assert(Objects[Inst->getOperand(3)].getType()->isComposite());
536  Objects[Id] = Objects[Inst->getOperand(3)];
537  Objects[Id].insert(Indices, Element);
538 }
539 
541 {
542  // TODO: Handle other execution scopes
543  assert(Objects[Inst->getOperand(0)].get<uint32_t>() == SpvScopeWorkgroup);
544  AtBarrier = true;
545 }
546 
548 {
549  switch (Inst->getResultType()->getScalarType()->getBitWidth())
550  {
551  case 16:
552  executeOpFP<1>(Inst, [](auto A) -> int16_t { return (int16_t)A; });
553  break;
554  case 32:
555  executeOpFP<1>(Inst, [](auto A) -> int32_t { return (int32_t)A; });
556  break;
557  case 64:
558  executeOpFP<1>(Inst, [](auto A) -> int64_t { return (int64_t)A; });
559  break;
560  default:
561  assert(false && "Unhandled integer size for OpConvertFToS");
562  }
563 }
564 
566 {
567  switch (Inst->getResultType()->getScalarType()->getBitWidth())
568  {
569  case 16:
570  executeOpFP<1>(Inst, [](auto A) -> uint16_t { return (uint16_t)A; });
571  break;
572  case 32:
573  executeOpFP<1>(Inst, [](auto A) -> uint32_t { return (uint32_t)A; });
574  break;
575  case 64:
576  executeOpFP<1>(Inst, [](auto A) -> uint64_t { return (uint64_t)A; });
577  break;
578  default:
579  assert(false && "Unhandled integer size for OpConvertFToU");
580  }
581 }
582 
584 {
585  switch (Inst->getResultType()->getScalarType()->getBitWidth())
586  {
587  case 32:
588  executeOpSInt<1>(Inst, [](auto A) -> float { return (float)A; });
589  break;
590  case 64:
591  executeOpSInt<1>(Inst, [](auto A) -> double { return (double)A; });
592  break;
593  default:
594  assert(false && "Unhandled floating point size for OpConvertUToF");
595  }
596 }
597 
599 {
600  switch (Inst->getResultType()->getScalarType()->getBitWidth())
601  {
602  case 32:
603  executeOpUInt<1>(Inst, [](auto A) -> float { return (float)A; });
604  break;
605  case 64:
606  executeOpUInt<1>(Inst, [](auto A) -> double { return (double)A; });
607  break;
608  default:
609  assert(false && "Unhandled floating point size for OpConvertUToF");
610  }
611 }
612 
614 {
615  const Object &Dst = Objects[Inst->getOperand(0)];
616  const Object &Src = Objects[Inst->getOperand(1)];
617 
618  const Type *DstType = Dst.getType();
619  const Type *SrcType = Src.getType();
620  assert(DstType->getElementType() == SrcType->getElementType());
621 
622  Memory &DstMem = getMemory(DstType->getStorageClass());
623  Memory &SrcMem = getMemory(SrcType->getStorageClass());
624 
625  uint64_t DstAddress = Dst.get<uint64_t>();
626  uint64_t SrcAddress = Src.get<uint64_t>();
627  uint64_t NumBytes = DstType->getElementType()->getSize();
628  Memory::copy(DstAddress, DstMem, SrcAddress, SrcMem, NumBytes);
629 }
630 
632 {
633  Objects[Inst->getOperand(1)] = Objects[Inst->getOperand(2)];
634 }
635 
637 {
638  Object &A = Objects[Inst->getOperand(2)];
639  Object &B = Objects[Inst->getOperand(3)];
640  switch (Inst->getResultType()->getBitWidth())
641  {
642  case 32:
643  {
644  float Result = 0.f;
645  for (uint32_t i = 0; i < A.getType()->getElementCount(); i++)
646  Result += A.get<float>(i) * B.get<float>(i);
647  Objects[Inst->getOperand(1)] = Object(Inst->getResultType(), Result);
648  break;
649  }
650  case 64:
651  {
652  double Result = 0.0;
653  for (uint32_t i = 0; i < A.getType()->getElementCount(); i++)
654  Result += A.get<double>(i) * B.get<double>(i);
655  Objects[Inst->getOperand(1)] = Object(Inst->getResultType(), Result);
656  break;
657  }
658  default:
659  assert(false && "Unhandled floating point size for OpDot");
660  }
661 }
662 
664 {
665  // TODO: Currently assumes extended instruction set is GLSL.std.450
666  uint32_t ExtInst = Inst->getOperand(3);
667  switch (ExtInst)
668  {
669  case GLSLstd450Acos:
670  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return acos(X); });
671  break;
672  case GLSLstd450Acosh:
673  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return acosh(X); });
674  break;
675  case GLSLstd450Asin:
676  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return asin(X); });
677  break;
678  case GLSLstd450Asinh:
679  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return asinh(X); });
680  break;
681  case GLSLstd450Atan:
682  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return atan(X); });
683  break;
684  case GLSLstd450Atanh:
685  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return atanh(X); });
686  break;
687  case GLSLstd450Atan2:
688  executeOpFP<2, 4>(
689  Inst, [](auto Y, auto X) -> decltype(X) { return atan2(Y, X); });
690  break;
691  case GLSLstd450Cos:
692  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return cos(X); });
693  break;
694  case GLSLstd450Cosh:
695  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return cosh(X); });
696  break;
697  case GLSLstd450FAbs:
698  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return fabs(X); });
699  break;
700  case GLSLstd450Fma:
701  {
702  executeOpFP<3, 4>(
703  Inst, [](auto A, auto B, auto C) -> decltype(A) { return A * B + C; });
704  break;
705  }
706  case GLSLstd450Floor:
707  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return floor(X); });
708  break;
709  case GLSLstd450InverseSqrt:
710  executeOpFP<1, 4>(Inst,
711  [](auto X) -> decltype(X) { return 1.f / sqrt(X); });
712  break;
713  case GLSLstd450NClamp:
714  executeOpFP<3, 4>(Inst, [](auto X, auto Min, auto Max) -> decltype(X) {
715  return fmin(fmax(X, Min), Max);
716  });
717  break;
718  case GLSLstd450FMax:
719  case GLSLstd450NMax:
720  executeOpFP<2, 4>(Inst,
721  [](auto X, auto Y) -> decltype(X) { return fmax(X, Y); });
722  break;
723  case GLSLstd450FMin:
724  case GLSLstd450NMin:
725  executeOpFP<2, 4>(Inst,
726  [](auto X, auto Y) -> decltype(X) { return fmin(X, Y); });
727  break;
728  case GLSLstd450Sin:
729  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return sin(X); });
730  break;
731  case GLSLstd450Sinh:
732  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return sinh(X); });
733  break;
734  case GLSLstd450Sqrt:
735  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return sqrt(X); });
736  break;
737  case GLSLstd450Tan:
738  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return tan(X); });
739  break;
740  case GLSLstd450Tanh:
741  executeOpFP<1, 4>(Inst, [](auto X) -> decltype(X) { return tanh(X); });
742  break;
743  default:
744  Dev.reportError("Unimplemented GLSL.std.450 extended instruction", true);
745  }
746 }
747 
749 {
750  executeOpFP<2>(Inst, [](auto A, auto B) -> decltype(A) { return A + B; });
751 }
752 
754 {
755  switch (Inst->getResultType()->getBitWidth())
756  {
757  case 32:
758  executeOpFP<1>(Inst, [](auto A) -> float { return (float)A; });
759  break;
760  case 64:
761  executeOpFP<1>(Inst, [](auto A) -> double { return (double)A; });
762  break;
763  default:
764  assert(false && "Unhandled floating point size for OpFConvert");
765  }
766 }
767 
769 {
770  executeOpFP<2>(Inst, [](auto A, auto B) -> decltype(A) { return A / B; });
771 }
772 
774 {
775  executeOpFP<2>(Inst, [](auto A, auto B) -> decltype(A) {
776  return A - (B * floor(A / B));
777  });
778 }
779 
781 {
782  executeOpFP<2>(Inst, [](auto A, auto B) -> decltype(A) { return A * B; });
783 }
784 
786 {
787  executeOpFP<1>(Inst, [](auto A) -> decltype(A) { return -A; });
788 }
789 
791 {
792  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
793  return A == B && !std::isunordered(A, B);
794  });
795 }
796 
798 {
799  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
800  return A > B && !std::isunordered(A, B);
801  });
802 }
803 
805 {
806  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
807  return A >= B && !std::isunordered(A, B);
808  });
809 }
810 
812 {
813  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
814  return A < B && !std::isunordered(A, B);
815  });
816 }
817 
819 {
820  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
821  return A <= B && !std::isunordered(A, B);
822  });
823 }
824 
826 {
827  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
828  return A != B && !std::isunordered(A, B);
829  });
830 }
831 
833 {
834  executeOpFP<2>(Inst,
835  [](auto A, auto B) -> decltype(A) { return fmod(A, B); });
836 }
837 
839 {
840  executeOpFP<2>(Inst, [](auto A, auto B) -> decltype(A) { return A - B; });
841 }
842 
844 {
845  const Function *Func = CurrentModule->getFunction(Inst->getOperand(2));
846 
847  // Copy function parameters.
848  assert(Inst->getNumOperands() == Func->getNumParams() + 3);
849  for (int i = 3; i < Inst->getNumOperands(); i++)
850  Objects[Func->getParamId(i - 3)] = Objects[Inst->getOperand(i)];
851 
852  // Create call stack entry.
853  StackEntry SE;
854  SE.CallInst = Inst;
856  SE.CallBlock = CurrentBlock;
857  CallStack.push_back(SE);
858 
859  // Move to first block of callee function.
860  CurrentFunction = Func;
862 }
863 
865 {
866  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
867  return A == B || std::isunordered(A, B);
868  });
869 }
870 
872 {
873  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
874  return A > B || std::isunordered(A, B);
875  });
876 }
877 
879 {
880  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
881  return A >= B || std::isunordered(A, B);
882  });
883 }
884 
886 {
887  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
888  return A < B || std::isunordered(A, B);
889  });
890 }
891 
893 {
894  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
895  return A <= B || std::isunordered(A, B);
896  });
897 }
898 
900 {
901  executeOpFP<2>(Inst, [](auto A, auto B) -> bool {
902  return A != B || std::isunordered(A, B);
903  });
904 }
905 
907 {
908  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A + B; });
909 }
910 
912 {
913  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A == B; });
914 }
915 
917 {
918  // Extract image object from a sampled image.
919  const Object &SampledImageObj = Objects[Inst->getOperand(2)];
920  const SampledImage *SI = (const SampledImage *)(SampledImageObj.getData());
921  Objects[Inst->getOperand(1)] =
922  Object(SampledImageObj.getType()->getElementType(),
923  (const uint8_t *)&(SI->Image));
924 }
925 
927 {
928  // Get image view object.
929  const Object &ImageObj = Objects[Inst->getOperand(2)];
930  const ImageView *Image = *(const ImageView **)(ImageObj.getData());
931 
932  Object Result(Inst->getResultType());
933  assert(Result.getType()->getScalarType()->getBitWidth() == 32);
934 
935  // Get mip level (if explicit).
936  uint32_t Level = 0;
937  if (Inst->getOpcode() == SpvOpImageQuerySizeLod)
938  Level = Objects[Inst->getOperand(3)].get<uint32_t>();
939 
940  // Get size in each dimension.
941  uint32_t ArraySizeIndex;
942  switch (ImageObj.getType()->getDimensionality())
943  {
944  case SpvDim1D:
945  case SpvDimBuffer:
946  Result.set<uint32_t>(Image->getWidth(Level), 0);
947  ArraySizeIndex = 1;
948  break;
949  case SpvDim2D:
950  case SpvDimCube:
951  case SpvDimRect:
952  Result.set<uint32_t>(Image->getWidth(Level), 0);
953  Result.set<uint32_t>(Image->getHeight(Level), 1);
954  ArraySizeIndex = 2;
955  break;
956  case SpvDim3D:
957  Result.set<uint32_t>(Image->getWidth(Level), 0);
958  Result.set<uint32_t>(Image->getHeight(Level), 1);
959  Result.set<uint32_t>(Image->getDepth(Level), 2);
960  ArraySizeIndex = 3;
961  break;
962  default:
963  Dev.reportError("Unhandled image dimensionality", true);
964  break;
965  }
966 
967  // Get number of array layers.
968  if (ImageObj.getType()->isArrayedImage())
969  {
970  if (ImageObj.getType()->getDimensionality() == SpvDimCube)
971  Result.set<uint32_t>(Image->getNumArrayLayers() / 6, ArraySizeIndex);
972  else
973  Result.set<uint32_t>(Image->getNumArrayLayers(), ArraySizeIndex);
974  }
975 
976  Objects[Inst->getOperand(1)] = Result;
977 }
978 
980 {
981  // Get image view object.
982  const Object &ImageObj = Objects[Inst->getOperand(2)];
983  const ImageView *Image = *(const ImageView **)(ImageObj.getData());
984 
985  // TODO: Handle subpass data dimensionality
986  assert(ImageObj.getType()->getDimensionality() != SpvDimSubpassData);
987 
988  // Get coordinate operand.
989  const Object &Coord = Objects[Inst->getOperand(3)];
990  const Type *CoordType = Coord.getType();
991  uint32_t NumCoords = CoordType->getElementCount();
992  assert(NumCoords <= 3);
993 
994  // Last coordinate is array layer if required.
995  uint32_t Layer = 0;
996  if (ImageObj.getType()->isArrayedImage() ||
997  ImageObj.getType()->getDimensionality() == SpvDimCube)
998  Layer = Coord.get<uint32_t>(--NumCoords);
999 
1000  // Extract coordinates.
1001  uint32_t X = Coord.get<uint32_t>(0);
1002  uint32_t Y = (NumCoords > 1) ? Coord.get<uint32_t>(1) : 0;
1003  uint32_t Z = (NumCoords > 2) ? Coord.get<uint32_t>(2) : 0;
1004  uint32_t Level = 0;
1005 
1006  // Handle optional image operands.
1007  if (Inst->getNumOperands() > 4)
1008  {
1009  uint32_t OpIdx = 5;
1010  uint32_t OperandMask = Inst->getOperand(4);
1011 
1012  if (OperandMask & SpvImageOperandsLodMask)
1013  {
1014  Level = Objects[Inst->getOperand(OpIdx++)].get<uint32_t>();
1015  OperandMask ^= SpvImageOperandsLodMask;
1016  }
1017 
1018  // Check for any remaining values after all supported operands handled.
1019  if (OperandMask)
1020  Dev.reportError("Unhandled image operand mask", true);
1021  assert(OpIdx == Inst->getNumOperands());
1022  }
1023 
1024  // Read texel from image.
1025  Image::Texel T;
1026  Image->read(T, X, Y, Z, Layer, Level);
1027  Objects[Inst->getOperand(1)] = T.toObject(Inst->getResultType());
1028 }
1029 
1031 {
1032  // Get sampler and image view objects.
1033  const Object &SampledImageObj = Objects[Inst->getOperand(2)];
1034  const SampledImage *SI = (const SampledImage *)(SampledImageObj.getData());
1035  const ImageView *Image = SI->Image;
1036  const Sampler *Sampler = SI->Sampler;
1037  const Type *ImageType = SampledImageObj.getType()->getElementType();
1038 
1039  // Get coordinate operand.
1040  const Object &Coord = Objects[Inst->getOperand(3)];
1041  const Type *CoordType = Coord.getType();
1042  uint32_t NumCoords = CoordType->getElementCount();
1043  assert(CoordType->getScalarType()->isFloat());
1044  assert(NumCoords <= 3);
1045 
1046  // Last coordinate is array layer if required.
1047  float Layer = 0;
1048  if (ImageType->isArrayedImage() ||
1049  ImageType->getDimensionality() == SpvDimCube)
1050  Layer = Coord.get<float>(--NumCoords);
1051 
1052  // Extract coordinates.
1053  float X = Coord.get<float>(0);
1054  float Y = (NumCoords > 1) ? Coord.get<float>(1) : 0;
1055  float Z = (NumCoords > 2) ? Coord.get<float>(2) : 0;
1056 
1057  // TODO: Handle additional operands
1058  // TODO: Handle Lod properly
1059  assert(Inst->getNumOperands() == 6);
1060  assert(Inst->getOperand(4) == SpvImageOperandsLodMask);
1061  assert(Objects[Inst->getOperand(5)].get<float>() == 0);
1062 
1063  // Sample texel from image.
1064  Image::Texel Texel;
1065  Sampler->sample(Image, Texel, X, Y, Z, Layer);
1066  Objects[Inst->getOperand(1)] = Texel.toObject(Inst->getResultType());
1067 }
1068 
1070 {
1071  // Get image view object.
1072  const Object &ImageObj = Objects[Inst->getOperand(0)];
1073  const ImageView *Image = *(const ImageView **)(ImageObj.getData());
1074 
1075  // TODO: Handle additional operands
1076  assert(Inst->getNumOperands() == 3);
1077 
1078  // Get coordinate operand.
1079  const Object &Coord = Objects[Inst->getOperand(1)];
1080  const Type *CoordType = Coord.getType();
1081  uint32_t NumCoords = CoordType->getElementCount();
1082  assert(NumCoords <= 3);
1083 
1084  // Last coordinate is array layer if required.
1085  uint32_t Layer = 0;
1086  if (ImageObj.getType()->isArrayedImage() ||
1087  ImageObj.getType()->getDimensionality() == SpvDimCube)
1088  Layer = Coord.get<uint32_t>(--NumCoords);
1089 
1090  // Extract coordinates.
1091  uint32_t X = Coord.get<uint32_t>(0);
1092  uint32_t Y = (NumCoords > 1) ? Coord.get<uint32_t>(1) : 0;
1093  uint32_t Z = (NumCoords > 2) ? Coord.get<uint32_t>(2) : 0;
1094 
1095  // Write texel to image.
1096  const Object &Texel = Objects[Inst->getOperand(2)];
1097  Image->write(Texel, X, Y, Z, Layer);
1098 }
1099 
1101 {
1102  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A * B; });
1103 }
1104 
1106 {
1107  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A != B; });
1108 }
1109 
1111 {
1112  executeOpFP<1>(Inst, [](auto A) -> bool { return std::isinf(A); });
1113 }
1114 
1116 {
1117  executeOpFP<1>(Inst, [](auto A) -> bool { return std::isnan(A); });
1118 }
1119 
1121 {
1122  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A - B; });
1123 }
1124 
1126 {
1127  Discarded = true;
1128  CurrentInstruction = nullptr;
1129 }
1130 
1132 {
1133  uint32_t Id = Inst->getOperand(1);
1134  const Object &Src = Objects[Inst->getOperand(2)];
1135  Memory &Mem = getMemory(Src.getType()->getStorageClass());
1136  Objects[Id] = Object::load(Inst->getResultType(), Mem, Src);
1137 }
1138 
1140 {
1141  executeOp<bool, 2>(Inst, [](bool A, bool B) { return A && B; });
1142 }
1143 
1145 {
1146  executeOp<bool, 2>(Inst, [](bool A, bool B) { return A == B; });
1147 }
1148 
1150 {
1151  executeOp<bool, 1>(Inst, [](bool A) { return !A; });
1152 }
1153 
1155 {
1156  executeOp<bool, 2>(Inst, [](bool A, bool B) { return A != B; });
1157 }
1158 
1160 {
1161  executeOp<bool, 2>(Inst, [](bool A, bool B) { return A || B; });
1162 }
1163 
1165 {
1166  Object Matrix = Objects[Inst->getOperand(2)];
1167  Object Scalar = Objects[Inst->getOperand(3)];
1168  const Type *MatrixType = Matrix.getType();
1169  const Type *VectorType = MatrixType->getElementType();
1170  const Type *ScalarType = VectorType->getElementType();
1171  assert(ScalarType->isFloat());
1172 
1173  for (uint32_t col = 0; col < MatrixType->getElementCount(); col++)
1174  {
1175  for (uint32_t row = 0; row < VectorType->getElementCount(); row++)
1176  {
1177  Object Element = Matrix.extract({col, row});
1178  switch (ScalarType->getBitWidth())
1179  {
1180  case 32:
1181  Element.set(Element.get<float>() * Scalar.get<float>());
1182  break;
1183  case 64:
1184  Element.set(Element.get<double>() * Scalar.get<double>());
1185  break;
1186  default:
1187  Dev.reportError("Unhandled floating point size", true);
1188  break;
1189  }
1190  Matrix.insert({col, row}, Element);
1191  }
1192  }
1193 
1194  Objects[Inst->getOperand(1)] = Matrix;
1195 }
1196 
1198 {
1199  Object Matrix = Objects[Inst->getOperand(2)];
1200  Object Vector = Objects[Inst->getOperand(3)];
1201  const Type *MatrixType = Matrix.getType();
1202  const Type *ColumnType = MatrixType->getElementType();
1203  const Type *ScalarType = Inst->getResultType()->getElementType();
1204  assert(ScalarType->isFloat());
1205 
1206  Object Result(Inst->getResultType());
1207  for (uint32_t row = 0; row < ColumnType->getElementCount(); row++)
1208  {
1209  switch (ScalarType->getBitWidth())
1210  {
1211  case 32:
1212  {
1213  float R = 0.f;
1214  for (uint32_t col = 0; col < MatrixType->getElementCount(); col++)
1215  R += Vector.get<float>(col) * Matrix.extract({col, row}).get<float>();
1216  Result.set(R, row);
1217  break;
1218  }
1219  case 64:
1220  {
1221  double R = 0.f;
1222  for (uint32_t col = 0; col < MatrixType->getElementCount(); col++)
1223  R += Vector.get<double>(col) * Matrix.extract({col, row}).get<double>();
1224  Result.set(R, row);
1225  break;
1226  }
1227  default:
1228  Dev.reportError("Unhandled floating point size", true);
1229  break;
1230  }
1231  }
1232  Objects[Inst->getOperand(1)] = Result;
1233 }
1234 
1236 {
1237  executeOpUInt<1>(Inst, [](auto A) -> decltype(A) { return ~A; });
1238 }
1239 
1241 {
1242  uint32_t Id = Inst->getOperand(1);
1243 
1244  assert(PreviousBlock);
1245  for (int i = 2; i < Inst->getNumOperands(); i += 2)
1246  {
1247  assert(i + 1 < Inst->getNumOperands());
1248  if (Inst->getOperand(i + 1) == PreviousBlock)
1249  {
1250  PhiTemps.push_back({Id, Objects[Inst->getOperand(i)]});
1251  return;
1252  }
1253  }
1254  assert(false && "no matching predecessor block for OpPhi");
1255 }
1256 
1258 {
1259  // If this is the entry function, do nothing.
1260  if (CallStack.empty())
1261  return;
1262 
1263  StackEntry SE = CallStack.back();
1264  CallStack.pop_back();
1265 
1266  // Release function scope allocations.
1267  for (uint64_t Address : SE.Allocations)
1268  PrivateMemory->release(Address);
1269 
1270  // Return to calling function.
1272  CurrentBlock = SE.CallBlock;
1274 }
1275 
1277 {
1278  assert(!CallStack.empty());
1279 
1280  StackEntry SE = CallStack.back();
1281  CallStack.pop_back();
1282 
1283  // Set return value.
1284  Objects[SE.CallInst->getOperand(1)] = Objects[Inst->getOperand(0)];
1285 
1286  // Release function scope allocations.
1287  for (uint64_t Address : SE.Allocations)
1288  PrivateMemory->release(Address);
1289 
1290  // Return to calling function.
1291  CurrentFunction = SE.CallFunc;
1292  CurrentBlock = SE.CallBlock;
1293  CurrentInstruction = SE.CallInst->next();
1294 }
1295 
1297 {
1298  // Get image view object.
1299  const Object &ImageObj = Objects[Inst->getOperand(2)];
1300  const ImageView *Image = *(const ImageView **)(ImageObj.getData());
1301 
1302  // Get sampler object.
1303  const Object &SamplerObj = Objects[Inst->getOperand(3)];
1304  const Sampler *Sampler = *(const talvos::Sampler **)(SamplerObj.getData());
1305 
1306  // Create and populate SampledImage structure.
1307  Object Result(Inst->getResultType());
1308  ((SampledImage *)(Result.getData()))->Image = Image;
1309  ((SampledImage *)(Result.getData()))->Sampler = Sampler;
1310  Objects[Inst->getOperand(1)] = Result;
1311 }
1312 
1314 {
1315  switch (Inst->getResultType()->getBitWidth())
1316  {
1317  case 16:
1318  executeOpSInt<1>(Inst, [](auto A) -> int16_t { return (int16_t)A; });
1319  break;
1320  case 32:
1321  executeOpSInt<1>(Inst, [](auto A) -> int32_t { return (int32_t)A; });
1322  break;
1323  case 64:
1324  executeOpSInt<1>(Inst, [](auto A) -> int64_t { return (int64_t)A; });
1325  break;
1326  default:
1327  assert(false && "Unhandled integer size for OpSConvert");
1328  }
1329 }
1330 
1332 {
1333  executeOpSInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A / B; });
1334 }
1335 
1337 {
1338  uint32_t Id = Inst->getOperand(1);
1339  const Object &Condition = Objects[Inst->getOperand(2)];
1340  const Object &Object1 = Objects[Inst->getOperand(3)];
1341  const Object &Object2 = Objects[Inst->getOperand(4)];
1342 
1343  if (Condition.getType()->isScalar())
1344  {
1345  Objects[Id] = Condition.get<bool>() ? Object1 : Object2;
1346  }
1347  else
1348  {
1349  Object Result(Inst->getResultType());
1350  for (uint32_t i = 0; i < Result.getType()->getElementCount(); i++)
1351  {
1352  Result.insert({i}, Condition.get<bool>(i) ? Object1.extract({i})
1353  : Object2.extract({i}));
1354  }
1355  Objects[Id] = Result;
1356  }
1357 }
1358 
1359 void Invocation::executeSGreaterThan(const Instruction *Inst)
1360 {
1361  executeOpSInt<2>(Inst, [](auto A, auto B) -> bool { return A > B; });
1362 }
1363 
1364 void Invocation::executeSGreaterThanEqual(const Instruction *Inst)
1365 {
1366  executeOpSInt<2>(Inst, [](auto A, auto B) -> bool { return A >= B; });
1367 }
1368 
1369 void Invocation::executeShiftLeftLogical(const Instruction *Inst)
1370 {
1371  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A << B; });
1372 }
1373 
1374 void Invocation::executeShiftRightArithmetic(const Instruction *Inst)
1375 {
1376  executeOpSInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A >> B; });
1377 }
1378 
1379 void Invocation::executeShiftRightLogical(const Instruction *Inst)
1380 {
1381  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A >> B; });
1382 }
1383 
1384 void Invocation::executeSLessThan(const Instruction *Inst)
1385 {
1386  executeOpSInt<2>(Inst, [](auto A, auto B) -> bool { return A < B; });
1387 }
1388 
1389 void Invocation::executeSLessThanEqual(const Instruction *Inst)
1390 {
1391  executeOpSInt<2>(Inst, [](auto A, auto B) -> bool { return A <= B; });
1392 }
1393 
1394 void Invocation::executeSMod(const Instruction *Inst)
1395 {
1396  executeOpSInt<2>(Inst, [](auto A, auto B) -> decltype(A) {
1397  return (std::abs(A) % B) * (B < 0 ? -1 : 1);
1398  });
1399 }
1400 
1401 void Invocation::executeSNegate(const Instruction *Inst)
1402 {
1403  executeOpSInt<1>(Inst, [](auto A) -> decltype(A) { return -A; });
1404 }
1405 
1406 void Invocation::executeSRem(const Instruction *Inst)
1407 {
1408  executeOpSInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A % B; });
1409 }
1410 
1411 void Invocation::executeStore(const Instruction *Inst)
1412 {
1413  uint32_t Id = Inst->getOperand(1);
1414  const Object &Dest = Objects[Inst->getOperand(0)];
1415  Memory &Mem = getMemory(Dest.getType()->getStorageClass());
1416  Objects[Id].store(Mem, Dest);
1417 }
1418 
1419 void Invocation::executeSwitch(const Instruction *Inst)
1420 {
1421  const Object &Selector = Objects[Inst->getOperand(0)];
1422 
1423  // TODO: Handle other selector sizes
1424  if (Selector.getType()->getBitWidth() != 32)
1425  Dev.reportError("OpSwitch is only implemented for 32-bit selectors", true);
1426 
1427  for (uint32_t i = 2; i < Inst->getNumOperands(); i += 2)
1428  {
1429  if (Selector.get<uint32_t>() == Inst->getOperand(i))
1430  {
1431  moveToBlock(Inst->getOperand(i + 1));
1432  return;
1433  }
1434  }
1435  moveToBlock(Inst->getOperand(1));
1436 }
1437 
1438 void Invocation::executeUConvert(const Instruction *Inst)
1439 {
1440  switch (Inst->getResultType()->getBitWidth())
1441  {
1442  case 16:
1443  executeOpUInt<1>(Inst, [](auto A) -> uint16_t { return (uint16_t)A; });
1444  break;
1445  case 32:
1446  executeOpUInt<1>(Inst, [](auto A) -> uint32_t { return (uint32_t)A; });
1447  break;
1448  case 64:
1449  executeOpUInt<1>(Inst, [](auto A) -> uint64_t { return (uint64_t)A; });
1450  break;
1451  default:
1452  assert(false && "Unhandled integer size for OpUConvert");
1453  }
1454 }
1455 
1456 void Invocation::executeUDiv(const Instruction *Inst)
1457 {
1458  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A / B; });
1459 }
1460 
1461 void Invocation::executeUGreaterThan(const Instruction *Inst)
1462 {
1463  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A > B; });
1464 }
1465 
1466 void Invocation::executeUGreaterThanEqual(const Instruction *Inst)
1467 {
1468  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A >= B; });
1469 }
1470 
1471 void Invocation::executeULessThan(const Instruction *Inst)
1472 {
1473  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A < B; });
1474 }
1475 
1476 void Invocation::executeULessThanEqual(const Instruction *Inst)
1477 {
1478  executeOpUInt<2>(Inst, [](auto A, auto B) -> bool { return A <= B; });
1479 }
1480 
1481 void Invocation::executeUndef(const Instruction *Inst)
1482 {
1483  Objects[Inst->getOperand(1)] = Object(Inst->getResultType());
1484 }
1485 
1486 void Invocation::executeUnreachable(const Instruction *Inst)
1487 {
1488  Dev.reportError("OpUnreachable instruction executed", true);
1489 }
1490 
1491 void Invocation::executeUMod(const Instruction *Inst)
1492 {
1493  executeOpUInt<2>(Inst, [](auto A, auto B) -> decltype(A) { return A % B; });
1494 }
1495 
1496 void Invocation::executeVariable(const Instruction *Inst)
1497 {
1498  assert(Inst->getOperand(2) == SpvStorageClassFunction);
1499 
1500  uint32_t Id = Inst->getOperand(1);
1501  size_t AllocSize = Inst->getResultType()->getElementType()->getSize();
1502  uint64_t Address = PrivateMemory->allocate(AllocSize);
1503  Objects[Id] = Object(Inst->getResultType(), Address);
1504 
1505  // Initialize if necessary.
1506  if (Inst->getNumOperands() > 3)
1507  Objects[Inst->getOperand(3)].store(*PrivateMemory, Address);
1508 
1509  // Track function scope allocations.
1510  if (!CallStack.empty())
1511  CallStack.back().Allocations.push_back(Address);
1512 }
1513 
1514 void Invocation::executeVectorExtractDynamic(const Instruction *Inst)
1515 {
1516  uint32_t Id = Inst->getOperand(1);
1517  uint16_t Index = 0;
1518  switch (Objects[Inst->getOperand(3)].getType()->getSize())
1519  {
1520  case 2:
1521  Index = OP(3, uint16_t);
1522  break;
1523  case 4:
1524  Index = (uint16_t)OP(3, uint32_t);
1525  break;
1526  case 8:
1527  Index = (uint16_t)OP(3, uint64_t);
1528  break;
1529  default:
1530  assert(false && "Unhandled index size in OpVectorExtractDynamic");
1531  }
1532 
1533  const Object &Vector = Objects[Inst->getOperand(2)];
1534  if (Index >= Vector.getType()->getElementCount())
1535  Dev.reportError("Vector index out of range");
1536  Objects[Id] = Vector.extract({Index});
1537 }
1538 
1539 void Invocation::executeVectorInsertDynamic(const Instruction *Inst)
1540 {
1541  uint32_t Id = Inst->getOperand(1);
1542  uint16_t Index = 0;
1543  switch (Objects[Inst->getOperand(4)].getType()->getSize())
1544  {
1545  case 2:
1546  Index = OP(4, uint16_t);
1547  break;
1548  case 4:
1549  Index = (uint16_t)OP(4, uint32_t);
1550  break;
1551  case 8:
1552  Index = (uint16_t)OP(4, uint64_t);
1553  break;
1554  default:
1555  assert(false && "Unhandled index size in OpVectorInsertDynamic");
1556  }
1557 
1558  const Object &Vector = Objects[Inst->getOperand(2)];
1559  const Object &Component = Objects[Inst->getOperand(3)];
1560  if (Index >= Vector.getType()->getElementCount())
1561  Dev.reportError("Vector index out of range");
1562  Objects[Id] = Vector;
1563  Objects[Id].insert({Index}, Component);
1564 }
1565 
1566 void Invocation::executeVectorShuffle(const Instruction *Inst)
1567 {
1568  uint32_t Id = Inst->getOperand(1);
1569  Object Result(Inst->getResultType());
1570 
1571  const Object &Vec1 = Objects[Inst->getOperand(2)];
1572  const Object &Vec2 = Objects[Inst->getOperand(3)];
1573  uint32_t Vec1Length = Vec1.getType()->getElementCount();
1574 
1575  for (uint32_t i = 0; i < Inst->getResultType()->getElementCount(); i++)
1576  {
1577  uint32_t Idx = Inst->getOperand(4 + i);
1578  if (Idx == 0xFFFFFFFF)
1579  ;
1580  else if (Idx < Vec1Length)
1581  Result.insert({i}, Vec1.extract({Idx}));
1582  else
1583  Result.insert({i}, Vec2.extract({Idx - Vec1Length}));
1584  }
1585 
1586  Objects[Id] = Result;
1587 }
1588 
1589 void Invocation::executeVectorTimesMatrix(const Instruction *Inst)
1590 {
1591  Object Vector = Objects[Inst->getOperand(2)];
1592  Object Matrix = Objects[Inst->getOperand(3)];
1593  const Type *MatrixType = Matrix.getType();
1594  const Type *VectorType = Vector.getType();
1595  const Type *ScalarType = Inst->getResultType()->getElementType();
1596  assert(ScalarType->isFloat());
1597 
1598  Object Result(Inst->getResultType());
1599  for (uint32_t col = 0; col < MatrixType->getElementCount(); col++)
1600  {
1601  switch (ScalarType->getBitWidth())
1602  {
1603  case 32:
1604  {
1605  float R = 0.f;
1606  for (uint32_t row = 0; row < VectorType->getElementCount(); row++)
1607  R += Vector.get<float>(row) * Matrix.extract({col, row}).get<float>();
1608  Result.set(R, col);
1609  break;
1610  }
1611  case 64:
1612  {
1613  double R = 0.0;
1614  for (uint32_t row = 0; row < VectorType->getElementCount(); row++)
1615  R += Vector.get<double>(row) * Matrix.extract({col, row}).get<double>();
1616  Result.set(R, col);
1617  break;
1618  }
1619  default:
1620  Dev.reportError("Unhandled floating point size", true);
1621  break;
1622  }
1623  }
1624  Objects[Inst->getOperand(1)] = Result;
1625 }
1626 
1627 void Invocation::executeVectorTimesScalar(const Instruction *Inst)
1628 {
1629  switch (Inst->getResultType()->getScalarType()->getBitWidth())
1630  {
1631  case 32:
1632  {
1633  float Scalar = Objects[Inst->getOperand(3)].get<float>();
1634  executeOp<float, 1>(Inst, [&](float A) { return A * Scalar; });
1635  break;
1636  }
1637  case 64:
1638  {
1639  double Scalar = Objects[Inst->getOperand(3)].get<double>();
1640  executeOp<double, 1>(Inst, [&](double A) { return A * Scalar; });
1641  break;
1642  }
1643  default:
1644  assert(false && "Unhandled floating point size for OpDot");
1645  }
1646 }
1647 
1648 Memory &Invocation::getMemory(uint32_t StorageClass)
1649 {
1650  switch (StorageClass)
1651  {
1652  case SpvStorageClassPushConstant:
1653  case SpvStorageClassStorageBuffer:
1654  case SpvStorageClassUniform:
1655  case SpvStorageClassUniformConstant:
1656  return Dev.getGlobalMemory();
1657  case SpvStorageClassWorkgroup:
1658  assert(Group && "Not executing within a workgroup.");
1659  return Group->getLocalMemory();
1660  case SpvStorageClassInput:
1661  case SpvStorageClassOutput:
1662  return *PipelineMemory;
1663  case SpvStorageClassFunction:
1664  case SpvStorageClassPrivate:
1665  return *PrivateMemory;
1666  default:
1667  assert(false && "Unhandled storage class");
1668  abort();
1669  }
1670 }
1671 
1672 Object Invocation::getObject(uint32_t Id) const
1673 {
1674  if (Id < Objects.size())
1675  return Objects[Id];
1676  else
1677  return Object();
1678 }
1679 
1680 Invocation::State Invocation::getState() const
1681 {
1682  if (AtBarrier)
1683  return BARRIER;
1684  return CurrentInstruction ? READY : FINISHED;
1685 }
1686 
1687 void Invocation::moveToBlock(uint32_t Id)
1688 {
1689  const Block *B = CurrentFunction->getBlock(Id);
1690  CurrentInstruction = B->getLabel().next();
1691  PreviousBlock = CurrentBlock;
1692  CurrentBlock = Id;
1693 }
1694 
1695 void Invocation::step()
1696 {
1697  assert(getState() == READY);
1698  assert(CurrentInstruction);
1699 
1700  const Instruction *I = CurrentInstruction;
1701 
1702  if (!PhiTemps.empty() && I->getOpcode() != SpvOpPhi &&
1703  I->getOpcode() != SpvOpLine)
1704  {
1705  for (auto &P : PhiTemps)
1706  Objects[P.first] = std::move(P.second);
1707  PhiTemps.clear();
1708  }
1709 
1710  execute(I);
1711 
1712  // Move program counter to next instruction, unless a terminator instruction
1713  // was executed.
1714  if (I == CurrentInstruction)
1715  CurrentInstruction = CurrentInstruction->next();
1716 
1717  Dev.reportInstructionExecuted(this, I);
1718 
1719  if (getState() == FINISHED)
1720  Dev.reportInvocationComplete(this);
1721 }
1722 
1723 // Private helper functions for executing simple instructions.
1724 
1725 template <typename OpTy, typename F>
1726 static auto apply(const std::array<OpTy, 1> Operands, const F &Op)
1727 {
1728  return Op(Operands[0]);
1729 }
1730 
1731 template <typename OpTy, typename F>
1732 static auto apply(const std::array<OpTy, 2> Operands, const F &Op)
1733 {
1734  return Op(Operands[0], Operands[1]);
1735 }
1736 
1737 template <typename OpTy, typename F>
1738 static auto apply(const std::array<OpTy, 3> Operands, const F &Op)
1739 {
1740  return Op(Operands[0], Operands[1], Operands[2]);
1741 }
1742 
1743 template <typename OpTy, unsigned N, unsigned Offset, typename F>
1744 void Invocation::executeOp(const Instruction *Inst, const F &Op)
1745 {
1746  uint32_t Id = Inst->getOperand(1);
1747  Object Result(Inst->getResultType());
1748  std::array<OpTy, N> Operands;
1749 
1750  // Loop over each vector component.
1751  for (uint32_t i = 0; i < Inst->getResultType()->getElementCount(); i++)
1752  {
1753  // Gather operands.
1754  for (unsigned j = 0; j < N; j++)
1755  Operands[j] = Objects[Inst->getOperand(Offset + j)].get<OpTy>(i);
1756 
1757  // Apply lambda and set result.
1758  Result.set(apply(Operands, Op), i);
1759  }
1760 
1761  Objects[Id] = Result;
1762 }
1763 
1764 template <unsigned N, unsigned Offset, typename F>
1765 void Invocation::executeOpSInt(const Instruction *Inst, const F &&Op)
1766 {
1767  const Type *OpType = Objects[Inst->getOperand(Offset)].getType();
1768  OpType = OpType->getScalarType();
1769  assert(OpType->isInt());
1770  switch (OpType->getBitWidth())
1771  {
1772  case 8:
1773  executeOp<int8_t, N, Offset>(Inst, Op);
1774  break;
1775  case 16:
1776  executeOp<int16_t, N, Offset>(Inst, Op);
1777  break;
1778  case 32:
1779  executeOp<int32_t, N, Offset>(Inst, Op);
1780  break;
1781  case 64:
1782  executeOp<int64_t, N, Offset>(Inst, Op);
1783  break;
1784  default:
1785  assert(false && "Unhandled binary operation integer width");
1786  }
1787 }
1788 
1789 template <unsigned N, unsigned Offset, typename F>
1790 void Invocation::executeOpFP(const Instruction *Inst, const F &&Op)
1791 {
1792  const Type *OpType = Objects[Inst->getOperand(Offset)].getType();
1793  OpType = OpType->getScalarType();
1794  assert(OpType->isFloat());
1795  switch (OpType->getBitWidth())
1796  {
1797  case 32:
1798  executeOp<float, N, Offset>(Inst, Op);
1799  break;
1800  case 64:
1801  executeOp<double, N, Offset>(Inst, Op);
1802  break;
1803  default:
1804  assert(false && "Unhandled binary operation floating point size");
1805  }
1806 }
1807 
1808 template <unsigned N, unsigned Offset, typename F>
1809 void Invocation::executeOpUInt(const Instruction *Inst, const F &&Op)
1810 {
1811  const Type *OpType = Objects[Inst->getOperand(Offset)].getType();
1812  OpType = OpType->getScalarType();
1813  assert(OpType->isInt());
1814  switch (OpType->getBitWidth())
1815  {
1816  case 8:
1817  executeOp<uint8_t, N>(Inst, Op);
1818  break;
1819  case 16:
1820  executeOp<uint16_t, N>(Inst, Op);
1821  break;
1822  case 32:
1823  executeOp<uint32_t, N>(Inst, Op);
1824  break;
1825  case 64:
1826  executeOp<uint64_t, N>(Inst, Op);
1827  break;
1828  default:
1829  assert(false && "Unhandled binary operation integer width");
1830  }
1831 }
1832 
1833 } // namespace talvos
This class represents a module-scope variable declaration.
Definition: Variable.h:21
Instruction & getLabel() const
Returns the label instruction for this block.
Definition: Block.h:39
This file declares the Block class.
void executeImageWrite(const Instruction *Inst)
const DescriptorElement * getDescriptorElements() const
Returns the descriptor array element information.
Definition: Object.cpp:106
This file declares the Workgroup class.
void reportError(const std::string &Error, bool Fatal=false)
Report an error that has occurred during emulation.
Definition: Device.cpp:146
std::vector< std::pair< uint32_t, Object > > PhiTemps
Temporary OpPhi results to be applied when we reach first non-OpPhi.
Definition: Invocation.h:235
bool isScalar() const
Returns true if this is a scalar type.
Definition: Type.cpp:77
void executeCompositeConstruct(const Instruction *Inst)
Definition: Invocation.cpp:503
void executeFRem(const Instruction *Inst)
Definition: Invocation.cpp:832
void executePhi(const Instruction *Inst)
void executeIsInf(const Instruction *Inst)
This file declares the Device class.
void write(const Image::Texel &T, uint32_t X, uint32_t Y=0, uint32_t Z=0, uint32_t Layer=0, uint32_t MipLevel=0) const
Write a texel to the image view at the specified coordinate.
Definition: Image.cpp:511
This class represents an image object.
Definition: Image.h:24
uint32_t PreviousBlock
The previous block (for OpPhi).
Definition: Invocation.h:206
bool isFloat() const
Returns true if this is a floating point type.
Definition: Type.h:107
std::vector< StackEntry > CallStack
The function call stack.
Definition: Invocation.h:222
#define DISPATCH(Op, Func)
size_t getSize() const
Returns the size of this type in bytes.
Definition: Type.h:81
std::vector< uint64_t > Allocations
Function scope allocations within this stack frame.
Definition: Invocation.h:219
void executeExtInst(const Instruction *Inst)
Definition: Invocation.cpp:663
void executeFAdd(const Instruction *Inst)
Definition: Invocation.cpp:748
static void copy(uint64_t DstAddress, Memory &DstMem, uint64_t SrcAddress, const Memory &SrcMem, uint64_t NumBytes)
Copy data between memory instances.
Definition: Memory.cpp:328
A data structure holding information for a function call.
Definition: Invocation.h:211
uint32_t atomicCmpXchg(uint64_t Address, uint32_t Scope, uint32_t EqualSemantics, uint32_t UnequalSemantics, uint32_t Value, uint32_t Comparator)
Perform an atomic compare-exchange operation at Address.
Definition: Memory.cpp:156
bool Discarded
True when fragment was discarded.
Definition: Invocation.h:208
void release(uint64_t Address)
Release the allocation with base address Address.
Definition: Memory.cpp:292
A combination of an image and a sampler used to access it.
Definition: Image.h:268
void executeCopyObject(const Instruction *Inst)
Definition: Invocation.cpp:631
Structure to describe the memory layout of a matrix.
Definition: Object.h:24
const Function * getFunction() const
Returns the function specified by this entry point.
Definition: EntryPoint.h:38
void executeCompositeInsert(const Instruction *Inst)
Definition: Invocation.cpp:528
void executeImageQuerySize(const Instruction *Inst)
Definition: Invocation.cpp:926
void executeConvertUToF(const Instruction *Inst)
Definition: Invocation.cpp:598
This file declares the Module class.
size_t getElementOffset(uint64_t Index) const
Returns the byte offset of the element at Index.
Definition: Type.cpp:26
const Type * getElementType(uint64_t Index=0) const
Returns the type of the element at Index.
Definition: Type.cpp:38
const Type * getScalarType() const
Returns the element type for vector types, or this for scalar types.
Definition: Type.cpp:49
void executeINotEqual(const Instruction *Inst)
Object toObject(const Type *Ty) const
Create an object with type Ty from the texel data.
Definition: Image.cpp:135
bool isInt() const
Returns true if this is an integer type.
Definition: Type.h:110
Memory * PrivateMemory
The private memory instance.
Definition: Invocation.h:229
Device & Dev
The device this invocation is executing on.
Definition: Invocation.h:226
void executeAtomicOp(const Instruction *Inst)
Definition: Invocation.cpp:428
void executeIsNan(const Instruction *Inst)
void sample(const ImageView *Image, Image::Texel &Texel, float S, float T=0, float R=0, float A=0, float Lod=0) const
Sample a texel from an image at the specified coordinates.
Definition: Image.cpp:517
std::shared_ptr< const Module > getModule() const
Return the module this pipeline stage is using.
Definition: PipelineStage.h:57
void executeFUnordGreaterThan(const Instruction *Inst)
Definition: Invocation.cpp:871
void executeAll(const Instruction *Inst)
Definition: Invocation.cpp:396
void executeFOrdLessThanEqual(const Instruction *Inst)
Definition: Invocation.cpp:818
This class represents a view into a range of image subresources.
Definition: Image.h:178
This file declares the EntryPoint class.
This class represents a function in a SPIR-V Module.
Definition: Function.h:23
void executeKill(const Instruction *Inst)
void executeAccessChain(const Instruction *Inst)
Definition: Invocation.cpp:238
void executeMatrixTimesScalar(const Instruction *Inst)
void executeFDiv(const Instruction *Inst)
Definition: Invocation.cpp:768
~Invocation()
Destroy this invocation.
Definition: Invocation.cpp:89
void executeReturn(const Instruction *Inst)
void executeFSub(const Instruction *Inst)
Definition: Invocation.cpp:838
const Instruction * CallInst
The calling instruction.
Definition: Invocation.h:214
void store(Memory &Mem, uint64_t Address) const
Store the value of this object to memory at Address.
Definition: Object.cpp:317
void executeFOrdNotEqual(const Instruction *Inst)
Definition: Invocation.cpp:825
const EntryPoint * getEntryPoint() const
Return the entry point this pipeline stage will invoke.
Definition: PipelineStage.h:51
uint32_t getOperand(unsigned i) const
Returns the operand at index i;.
Definition: Instruction.h:52
void executeSelect(const Instruction *Inst)
Invocation(Device &Dev, const std::vector< Object > &InitialObjects)
Create a standalone invocation for a device, with an initial set of result objects.
Definition: Invocation.cpp:38
void executeBitwiseXor(const Instruction *Inst)
Definition: Invocation.cpp:487
void set(T Value, uint32_t Element=0)
Set the value of this object to a scalar of type T.
Definition: Object.cpp:295
uint32_t getDimensionality() const
Returns the dimensionality of an image type.
Definition: Type.h:60
std::shared_ptr< const Module > CurrentModule
The current module.
Definition: Invocation.h:201
uint16_t getOpcode() const
Returns the opcode.
Definition: Instruction.h:49
void executeIAdd(const Instruction *Inst)
Definition: Invocation.cpp:906
#define NOP(Op)
This class represents a single texel with four 32-bit component values.
Definition: Image.h:28
uint32_t getElementCount() const
Returns the number of elements in this array, struct, or vector type.
Definition: Type.h:64
void executeAtomicCompareExchange(const Instruction *Inst)
Definition: Invocation.cpp:454
void executeImageSampleExplicitLod(const Instruction *Inst)
void executeNot(const Instruction *Inst)
void executeBitcast(const Instruction *Inst)
Definition: Invocation.cpp:470
This file declares the Instruction class.
const class ImageView * Image
Definition: Image.h:270
uint32_t getNumArrayLayers() const
Returns the number of array layers in the image view.
Definition: Image.h:204
static Object load(const Type *Ty, const Memory &Mem, uint64_t Address)
Create an object of type Ty from the data at Address.
Definition: Object.cpp:137
void executeFOrdLessThan(const Instruction *Inst)
Definition: Invocation.cpp:811
uint32_t getWidth(uint32_t Level=0) const
Get the width of the image view at the specified mip level.
Definition: Image.cpp:482
void executeFUnordNotEqual(const Instruction *Inst)
Definition: Invocation.cpp:899
This file declares the Type class.
void executeSampledImage(const Instruction *Inst)
const Function * CallFunc
The function containing CallInst.
Definition: Invocation.h:215
Class representing a 3-dimensional size or ID.
Definition: Dim3.h:22
uint32_t getFirstBlockId() const
Returns the ID of the first block in this function.
Definition: Function.h:48
State
Used to indicate whether an invocation is ready to execute, waiting at a barrier, or complete...
Definition: Invocation.h:38
void executeFMul(const Instruction *Inst)
Definition: Invocation.cpp:780
void execute(const Instruction *Inst)
Execute Inst in this invocation.
Definition: Invocation.cpp:91
enum talvos::PtrMatrixLayout::@4 Order
Specifies the order of the elements in memory.
void executeConvertSToF(const Instruction *Inst)
Definition: Invocation.cpp:583
void executeIEqual(const Instruction *Inst)
Definition: Invocation.cpp:911
void executeFUnordLessThan(const Instruction *Inst)
Definition: Invocation.cpp:885
void executeCompositeExtract(const Instruction *Inst)
Definition: Invocation.cpp:519
uint32_t Stride
The stride in bytes between columns (COL_MAJOR) or rows (ROW_MAJOR).
Definition: Object.h:35
T atomic(uint64_t Address, uint32_t Opcode, uint32_t Scope, uint32_t Semantics, T Value=0)
Atomically apply operation defined by Opcode to Address.
Definition: Memory.cpp:81
This class represents an address space in the virtual device.
Definition: Memory.h:37
uint32_t getDepth(uint32_t Level=0) const
Get the depth of the image view at the specified mip level.
Definition: Image.cpp:465
void executeSDiv(const Instruction *Inst)
void executeISub(const Instruction *Inst)
This file declares the PipelineStage class.
const VariableList & getVariables() const
Return the workgroup scope variable pointer values.
Definition: Workgroup.h:61
void executeLogicalEqual(const Instruction *Inst)
This file declares data structures and functions for handling images.
uint32_t getBitWidth() const
Returns the bit-width of this type.
Definition: Type.cpp:20
const Instruction * CurrentInstruction
The current instruction.
Definition: Invocation.h:204
uint32_t getStorageClass() const
Returns the storage class of this type.
Definition: Type.cpp:60
size_t getNumParams() const
Returns the number of parameters in this function.
Definition: Function.h:57
A Device instance encapsulates properties and state for the virtual device.
Definition: Device.h:29
void executeLogicalNotEqual(const Instruction *Inst)
void executeLogicalAnd(const Instruction *Inst)
void executeAny(const Instruction *Inst)
Definition: Invocation.cpp:412
uint32_t CallBlock
The block containing CallInst.
Definition: Invocation.h:216
This class represents a workgroup executing a compute command.
Definition: Workgroup.h:27
void executeMatrixTimesVector(const Instruction *Inst)
void executeCopyMemory(const Instruction *Inst)
Definition: Invocation.cpp:613
void executeConvertFToU(const Instruction *Inst)
Definition: Invocation.cpp:565
uint8_t * getData()
Returns a mutable pointer to the raw data backing this object.
Definition: Object.h:88
void executeLogicalNot(const Instruction *Inst)
void executeFOrdGreaterThan(const Instruction *Inst)
Definition: Invocation.cpp:797
This class represents a sampler object.
Definition: Image.h:252
uint16_t getNumOperands() const
Returns the number of operands this instruction has.
Definition: Instruction.h:46
uint32_t getParamId(uint32_t I) const
Returns the ID of the parameter at index I.
Definition: Function.h:54
const Type * getType() const
Returns the type of this object.
Definition: Object.h:101
This class encapsulates information about a pipeline stage.
Definition: PipelineStage.h:30
const Type * getResultType() const
Returns the result type of this instruction, or nullptr if it does not produce a result.
Definition: Instruction.h:59
void reportInvocationBegin(const Invocation *Invoc)
Definition: Device.cpp:259
uint32_t getHeight(uint32_t Level=0) const
Get the height of the image view at the specified mip level.
Definition: Image.cpp:470
void executeBitwiseAnd(const Instruction *Inst)
Definition: Invocation.cpp:477
This file declares the Memory class.
bool AtBarrier
True when at a barrier.
Definition: Invocation.h:207
const uint32_t * getOperands() const
Returns the operands.
Definition: Instruction.h:55
void executeFUnordLessThanEqual(const Instruction *Inst)
Definition: Invocation.cpp:892
void executeBitwiseOr(const Instruction *Inst)
Definition: Invocation.cpp:482
void executeDot(const Instruction *Inst)
Definition: Invocation.cpp:636
const PtrMatrixLayout & getMatrixLayout() const
Get the matrix layout for this object.
Definition: Object.cpp:111
void executeFNegate(const Instruction *Inst)
Definition: Invocation.cpp:785
void executeFUnordGreaterThanEqual(const Instruction *Inst)
Definition: Invocation.cpp:878
void executeImageRead(const Instruction *Inst)
Definition: Invocation.cpp:979
void executeLoad(const Instruction *Inst)
This class represents a SPIR-V type.
Definition: Type.h:33
This file declares the Function class.
void insert(const std::vector< uint32_t > &Indices, const Object &Element)
Insert the value of Element into a composite object.
Definition: Object.cpp:118
void executeFConvert(const Instruction *Inst)
Definition: Invocation.cpp:753
This file declares the Invocation class.
uint64_t allocate(uint64_t NumBytes)
Allocate a new buffer of size NumBytes.
Definition: Memory.cpp:52
void executeConvertFToS(const Instruction *Inst)
Definition: Invocation.cpp:547
std::vector< Object > Objects
Set of result objects.
Definition: Invocation.h:224
bool isArrayedImage() const
Returns the Arrayed flag of an image type.
Definition: Type.h:98
void executeControlBarrier(const Instruction *Inst)
Definition: Invocation.cpp:540
#define OP(Index, Type)
Get scalar operand at index Index with type Type.
Definition: Invocation.cpp:33
void executeImage(const Instruction *Inst)
Definition: Invocation.cpp:916
This class represents an instruction result.
Definition: Object.h:51
void store(uint64_t Address, uint64_t NumBytes, const uint8_t *Data)
Store NumBytes of data from Data to Address.
Definition: Memory.cpp:306
void moveToBlock(uint32_t Id)
Move this invocation to the block with ID Id.
void executeReturnValue(const Instruction *Inst)
void executeBranch(const Instruction *Inst)
Definition: Invocation.cpp:492
void executeIMul(const Instruction *Inst)
const Instruction * next() const
Get the next instruction in the containing block.
Definition: Instruction.h:68
uint32_t CurrentBlock
The current block.
Definition: Invocation.h:205
void executeBranchConditional(const Instruction *Inst)
Definition: Invocation.cpp:497
const class Sampler * Sampler
Definition: Image.h:271
void executeFUnordEqual(const Instruction *Inst)
Definition: Invocation.cpp:864
This class represents a SPIR-V instruction.
Definition: Instruction.h:27
void executeSConvert(const Instruction *Inst)
void executeLogicalOr(const Instruction *Inst)
This file declares the Variable class.
void executeFOrdEqual(const Instruction *Inst)
Definition: Invocation.cpp:790
void executeFunctionCall(const Instruction *Inst)
Definition: Invocation.cpp:843
T get(uint32_t Element=0) const
Get the value of this object as a scalar of type T.
Definition: Object.cpp:97
Object extract(const std::vector< uint32_t > &Indices) const
Extract an element from a composite object.
Definition: Object.cpp:75
Memory & getMemory(uint32_t StorageClass)
Returns the memory instance associated with StorageClass.
const Function * CurrentFunction
The current function.
Definition: Invocation.h:203
void executeFOrdGreaterThanEqual(const Instruction *Inst)
Definition: Invocation.cpp:804
std::shared_ptr< Memory > PipelineMemory
Memory used for input and output storage classes.
Definition: Invocation.h:232
void read(Image::Texel &T, uint32_t X, uint32_t Y=0, uint32_t Z=0, uint32_t Layer=0, uint32_t MipLevel=0) const
Read a texel from the image view at the specified coordinate.
Definition: Image.cpp:505
uint64_t Address
Address of descriptor element.
Definition: Object.h:44
void executeFMod(const Instruction *Inst)
Definition: Invocation.cpp:773
A block of instructions ending with a termination instruction.
Definition: Block.h:21