Talvos  0.1
SPIR-V interpreter and dynamic analysis framework
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Module.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 <algorithm>
10 #include <cassert>
11 #include <cstdio>
12 #include <iostream>
13 #include <spirv-tools/libspirv.hpp>
14 
15 #include <spirv/unified1/spirv.h>
16 
17 #include "talvos/Block.h"
18 #include "talvos/EntryPoint.h"
19 #include "talvos/Function.h"
20 #include "talvos/Instruction.h"
21 #include "talvos/Module.h"
22 #include "talvos/Type.h"
23 #include "talvos/Variable.h"
24 
25 namespace talvos
26 {
27 
30 {
31 public:
33  void init(uint32_t IdBound)
34  {
35  assert(!Mod && "Module already initialized");
36  Mod = std::unique_ptr<Module>(new Module(IdBound));
37  CurrentFunction = nullptr;
38  CurrentBlock = nullptr;
39  PreviousInstruction = nullptr;
40  }
41 
43  void processInstruction(const spv_parsed_instruction_t *Inst)
44  {
45  assert(Mod);
46 
47  if (Inst->opcode == SpvOpFunction)
48  {
49  assert(CurrentFunction == nullptr);
50  const Type *FuncType =
51  Mod->getType(Inst->words[Inst->operands[3].offset]);
52  CurrentFunction = std::make_unique<Function>(Inst->result_id, FuncType);
53 
54  // Check if this is an entry point.
55  if (EntryPoints.count(Inst->result_id))
56  {
57  EntryPointSpec EPS = EntryPoints.at(Inst->result_id);
58 
59  // Gather the list of input/output variables used by the entry point.
60  VariableList Variables;
61  for (auto V : EPS.Variables)
62  {
63  auto ModVars = Mod->getVariables();
64  auto VarItr =
65  std::find_if(ModVars.begin(), ModVars.end(),
66  [V](auto Var) { return Var->getId() == V; });
67  assert(VarItr != ModVars.end());
68  Variables.push_back(*VarItr);
69  }
70 
71  // Create the entry point and add it to the module.
72  Mod->addEntryPoint(new EntryPoint(Inst->result_id, EPS.Name,
73  EPS.ExecutionModel,
74  CurrentFunction.get(), Variables));
75  }
76  }
77  else if (Inst->opcode == SpvOpFunctionEnd)
78  {
79  assert(CurrentFunction);
80  assert(CurrentBlock);
81  CurrentFunction->addBlock(std::move(CurrentBlock));
82  Mod->addFunction(std::move(CurrentFunction));
83  CurrentFunction = nullptr;
84  CurrentBlock = nullptr;
85  }
86  else if (Inst->opcode == SpvOpFunctionParameter)
87  {
88  CurrentFunction->addParam(Inst->result_id);
89  }
90  else if (Inst->opcode == SpvOpLabel)
91  {
92  if (CurrentBlock)
93  // Add previous block to function.
94  CurrentFunction->addBlock(std::move(CurrentBlock));
95  else
96  // First block - set as entry block.
97  CurrentFunction->setFirstBlock(Inst->result_id);
98 
99  // Create new block.
100  CurrentBlock = std::make_unique<Block>(Inst->result_id);
101  PreviousInstruction = &CurrentBlock->getLabel();
102  }
103  else if (CurrentFunction)
104  {
105  // Skip OpLine/OpNoLine instructions.
106  if (Inst->opcode == SpvOpLine || Inst->opcode == SpvOpNoLine)
107  return;
108 
109  // Create an array of operand values.
110  uint32_t *Operands = new uint32_t[Inst->num_operands];
111  for (int i = 0; i < Inst->num_operands; i++)
112  {
113  // TODO: Handle larger operands
114  assert(Inst->operands[i].num_words == 1);
115  Operands[i] = Inst->words[Inst->operands[i].offset];
116  }
117 
118  // Create the instruction.
119  const Type *ResultType =
120  Inst->type_id ? Mod->getType(Inst->type_id) : nullptr;
121  Instruction *I = new Instruction(Inst->opcode, Inst->num_operands,
122  Operands, ResultType);
123  delete[] Operands;
124 
125  // Insert this instruction into the current block.
126  assert(PreviousInstruction);
129  }
130  else
131  {
132  switch (Inst->opcode)
133  {
134  case SpvOpCapability:
135  {
136  uint32_t Capability = Inst->words[Inst->operands[0].offset];
137  switch (Capability)
138  {
139  case SpvCapabilityClipDistance:
140  case SpvCapabilityCullDistance:
141  case SpvCapabilityImage1D:
142  case SpvCapabilityImageCubeArray:
143  case SpvCapabilityImageQuery:
144  case SpvCapabilityInputAttachment:
145  case SpvCapabilityInt16:
146  case SpvCapabilityInt64:
147  case SpvCapabilityFloat64:
148  case SpvCapabilityImageBuffer:
149  case SpvCapabilityMatrix:
150  case SpvCapabilitySampled1D:
151  case SpvCapabilitySampledBuffer:
152  case SpvCapabilityShader:
153  case SpvCapabilityStorageBuffer8BitAccess:
154  case SpvCapabilityStorageBuffer16BitAccess:
155  case SpvCapabilityStorageImageReadWithoutFormat:
156  case SpvCapabilityStorageImageWriteWithoutFormat:
157  case SpvCapabilityStorageInputOutput16:
158  case SpvCapabilityStoragePushConstant8:
159  case SpvCapabilityStoragePushConstant16:
160  case SpvCapabilityStorageUniform16:
161  case SpvCapabilityUniformAndStorageBuffer8BitAccess:
162  case SpvCapabilityVariablePointers:
163  case SpvCapabilityVariablePointersStorageBuffer:
164  break;
165  default:
166  std::cerr << "Unimplemented capability: " << Capability << std::endl;
167  abort();
168  }
169  break;
170  }
171  case SpvOpConstant:
172  case SpvOpSpecConstant:
173  {
174  const Type *Ty = Mod->getType(Inst->type_id);
175 
176  Object Constant = Object(Ty);
177  uint16_t Offset = Inst->operands[2].offset;
178  switch (Ty->getSize())
179  {
180  case 2:
181  Constant.set<uint16_t>(*(uint16_t *)(Inst->words + Offset));
182  break;
183  case 4:
184  Constant.set<uint32_t>(Inst->words[Offset]);
185  break;
186  case 8:
187  Constant.set<uint64_t>(*(uint64_t *)(Inst->words + Offset));
188  break;
189  default:
190  std::cerr << "Unhandled OpConstant type size: " << Ty->getSize()
191  << std::endl;
192  abort();
193  }
194  Mod->addObject(Inst->result_id, Constant);
195  break;
196  }
197  case SpvOpConstantComposite:
198  {
199  const Type *Ty = Mod->getType(Inst->type_id);
200 
201  // Create composite object.
202  Object Composite(Ty);
203 
204  // Set constituent values.
205  for (uint32_t i = 2; i < Inst->num_operands; i++)
206  {
207  uint32_t Id = Inst->words[Inst->operands[i].offset];
208  Composite.insert({i - 2}, Mod->getObject(Id));
209  }
210 
211  // Add object to module.
212  Mod->addObject(Inst->result_id, Composite);
213  break;
214  }
215  case SpvOpConstantFalse:
216  case SpvOpSpecConstantFalse:
217  {
218  const Type *Ty = Mod->getType(Inst->type_id);
219  Mod->addObject(Inst->result_id, Object(Ty, false));
220  break;
221  }
222  case SpvOpConstantNull:
223  {
224  // Create and add object.
225  const Type *Ty = Mod->getType(Inst->type_id);
226  Object Value(Ty);
227  Value.zero();
228  Mod->addObject(Inst->result_id, Value);
229  break;
230  }
231  case SpvOpConstantTrue:
232  case SpvOpSpecConstantTrue:
233  {
234  const Type *Ty = Mod->getType(Inst->type_id);
235  Mod->addObject(Inst->result_id, Object(Ty, true));
236  break;
237  }
238  case SpvOpDecorate:
239  {
240  uint32_t Target = Inst->words[Inst->operands[0].offset];
241  uint32_t Decoration = Inst->words[Inst->operands[1].offset];
242  switch (Decoration)
243  {
244  case SpvDecorationArrayStride:
245  ArrayStrides[Target] = Inst->words[Inst->operands[2].offset];
246  break;
247  case SpvDecorationBinding:
248  case SpvDecorationComponent:
249  case SpvDecorationDescriptorSet:
250  case SpvDecorationLocation:
251  ObjectDecorations[Target].push_back(
252  {Decoration, Inst->words[Inst->operands[2].offset]});
253  break;
254  case SpvDecorationAliased:
255  case SpvDecorationCentroid:
256  case SpvDecorationCoherent:
257  case SpvDecorationFlat:
258  case SpvDecorationNoContraction:
259  case SpvDecorationNonReadable:
260  case SpvDecorationNonWritable:
261  case SpvDecorationNoPerspective:
262  case SpvDecorationRestrict:
263  case SpvDecorationVolatile:
264  ObjectDecorations[Target].push_back({Decoration, 1});
265  break;
266  case SpvDecorationBlock:
267  case SpvDecorationBufferBlock:
268  // TODO: Need to handle these?
269  break;
270  case SpvDecorationBuiltIn:
271  {
272  switch (Inst->words[Inst->operands[2].offset])
273  {
274  case SpvBuiltInWorkgroupSize:
275  Mod->setWorkgroupSizeId(Target);
276  break;
277  default:
278  ObjectDecorations[Target].push_back(
279  {Decoration, Inst->words[Inst->operands[2].offset]});
280  break;
281  }
282  break;
283  }
284  case SpvDecorationRelaxedPrecision:
285  break;
286  case SpvDecorationSpecId:
287  Mod->addSpecConstant(Inst->words[Inst->operands[2].offset], Target);
288  break;
289  default:
290  std::cerr << "Unhandled decoration " << Decoration << std::endl;
291  abort();
292  }
293  break;
294  }
295  case SpvOpEntryPoint:
296  {
297  uint32_t ExecutionModel = Inst->words[Inst->operands[0].offset];
298  uint32_t Id = Inst->words[Inst->operands[1].offset];
299  char *Name = (char *)(Inst->words + Inst->operands[2].offset);
300  if (ExecutionModel != SpvExecutionModelGLCompute &&
301  ExecutionModel != SpvExecutionModelVertex &&
302  ExecutionModel != SpvExecutionModelFragment)
303  {
304  std::cerr << "Unimplemented execution model: " << ExecutionModel
305  << std::endl;
306  abort();
307  }
308 
309  // Save entry point specification for creation later.
310  std::vector<uint32_t> Variables(Inst->words + Inst->operands[3].offset,
311  Inst->words + Inst->operands[3].offset +
312  Inst->num_operands - 3);
313  EntryPoints[Id] = {Name, ExecutionModel, Variables};
314  break;
315  }
316  case SpvOpExecutionMode:
317  {
318  uint32_t Entry = Inst->words[Inst->operands[0].offset];
319  uint32_t Mode = Inst->words[Inst->operands[1].offset];
320  switch (Mode)
321  {
322  case SpvExecutionModeLocalSize:
323  Mod->addLocalSize(Entry,
324  Dim3(Inst->words + Inst->operands[2].offset));
325  break;
326  case SpvExecutionModeOriginUpperLeft:
327  // TODO: Store this for later use?
328  break;
329  default:
330  std::cerr << "Unimplemented execution mode: " << Mode << std::endl;
331  abort();
332  }
333  break;
334  }
335  case SpvOpExtension:
336  {
337  char *Extension = (char *)(Inst->words + Inst->operands[0].offset);
338  if (strcmp(Extension, "SPV_KHR_8bit_storage") &&
339  strcmp(Extension, "SPV_KHR_16bit_storage") &&
340  strcmp(Extension, "SPV_KHR_storage_buffer_storage_class") &&
341  strcmp(Extension, "SPV_KHR_variable_pointers"))
342  {
343  std::cerr << "Unimplemented extension " << Extension << std::endl;
344  abort();
345  }
346  break;
347  }
348  case SpvOpExtInstImport:
349  {
350  // TODO: Store the mapping from result ID to set for later use
351  char *ExtInstSet = (char *)(Inst->words + Inst->operands[1].offset);
352  if (strcmp(ExtInstSet, "GLSL.std.450"))
353  {
354  std::cerr << "Unrecognized extended instruction set " << ExtInstSet
355  << std::endl;
356  abort();
357  }
358  break;
359  }
360  case SpvOpLine:
361  // TODO: Do something with this
362  break;
363  case SpvOpMemberDecorate:
364  {
365  uint32_t Target = Inst->words[Inst->operands[0].offset];
366  uint32_t Member = Inst->words[Inst->operands[1].offset];
367  uint32_t Decoration = Inst->words[Inst->operands[2].offset];
368  uint32_t Offset = Inst->operands[3].offset;
369  switch (Decoration)
370  {
371  case SpvDecorationBuiltIn:
372  case SpvDecorationMatrixStride:
373  case SpvDecorationOffset:
374  MemberDecorations[{Target, Member}][Decoration] = Inst->words[Offset];
375  break;
376  case SpvDecorationCoherent:
377  case SpvDecorationColMajor:
378  case SpvDecorationRowMajor:
379  case SpvDecorationFlat:
380  case SpvDecorationNonReadable:
381  case SpvDecorationNonWritable:
382  case SpvDecorationNoPerspective:
383  MemberDecorations[{Target, Member}][Decoration] = 1;
384  break;
385  case SpvDecorationRelaxedPrecision:
386  break;
387  default:
388  std::cerr << "Unhandled member decoration " << Decoration
389  << std::endl;
390  abort();
391  }
392  break;
393  }
394  case SpvOpMemberName:
395  // TODO: Do something with this
396  break;
397  case SpvOpMemoryModel:
398  {
399  uint32_t AddressingMode = Inst->words[Inst->operands[0].offset];
400  uint32_t MemoryModel = Inst->words[Inst->operands[1].offset];
401  if (AddressingMode != SpvAddressingModelLogical)
402  {
403  std::cerr << "Unrecognized addressing mode " << AddressingMode
404  << std::endl;
405  abort();
406  }
407  if (MemoryModel != SpvMemoryModelGLSL450)
408  {
409  std::cerr << "Unrecognized memory model " << MemoryModel << std::endl;
410  abort();
411  }
412  break;
413  }
414  case SpvOpModuleProcessed:
415  break;
416  case SpvOpName:
417  // TODO: Do something with this
418  break;
419  case SpvOpNoLine:
420  // TODO: Do something with this
421  break;
422  case SpvOpSpecConstantComposite:
423  {
424  const Type *ResultType = Mod->getType(Inst->type_id);
425 
426  // Build list of operands (the IDs of the constituents).
427  std::vector<uint32_t> Operands(Inst->num_operands);
428  for (int i = 0; i < Inst->num_operands; i++)
429  {
430  assert(Inst->operands[i].num_words == 1);
431  Operands[i] = Inst->words[Inst->operands[i].offset];
432  }
433 
434  // Create an OpCompositeConstruct instruction.
435  Mod->addSpecConstantOp(new Instruction(SpvOpCompositeConstruct,
436  Inst->num_operands,
437  Operands.data(), ResultType));
438  break;
439  }
440  case SpvOpSpecConstantOp:
441  {
442  const Type *ResultType = Mod->getType(Inst->type_id);
443  uint16_t Opcode = Inst->words[Inst->operands[2].offset];
444 
445  // Build list of operands (skip opcode).
446  std::vector<uint32_t> Operands;
447  for (int i = 0; i < Inst->num_operands; i++)
448  {
449  if (i == 2)
450  continue;
451  assert(Inst->operands[i].num_words == 1);
452  Operands.push_back(Inst->words[Inst->operands[i].offset]);
453  }
454 
455  // Create the instruction.
456  Mod->addSpecConstantOp(new Instruction(Opcode, Inst->num_operands - 1,
457  Operands.data(), ResultType));
458  break;
459  }
460  case SpvOpSource:
461  case SpvOpSourceContinued:
462  // TODO: Do something with these
463  break;
464  case SpvOpSourceExtension:
465  break;
466  case SpvOpString:
467  // TODO: Do something with this
468  break;
469  case SpvOpTypeArray:
470  {
471  // Get array length.
472  uint32_t LengthId = Inst->words[Inst->operands[2].offset];
473  const Object &LengthObj = Mod->getObject(LengthId);
474  uint32_t Length;
475  switch (LengthObj.getType()->getBitWidth())
476  {
477  case 16:
478  Length = (uint32_t)LengthObj.get<uint16_t>();
479  break;
480  case 32:
481  Length = (uint32_t)LengthObj.get<uint32_t>();
482  break;
483  case 64:
484  assert(LengthObj.get<uint64_t>() <= UINT32_MAX);
485  Length = (uint32_t)LengthObj.get<uint64_t>();
486  break;
487  default:
488  std::cerr << "Invalid array length bitwidth" << std::endl;
489  abort();
490  }
491 
492  const Type *ElemType =
493  Mod->getType(Inst->words[Inst->operands[1].offset]);
494  uint32_t ArrayStride = (uint32_t)ElemType->getSize();
495  if (ArrayStrides.count(Inst->result_id))
496  ArrayStride = ArrayStrides[Inst->result_id];
497  Mod->addType(Inst->result_id,
498  Type::getArray(ElemType, Length, ArrayStride));
499  break;
500  }
501  case SpvOpTypeBool:
502  {
503  Mod->addType(Inst->result_id, Type::getBool());
504  break;
505  }
506  case SpvOpTypeFloat:
507  {
508  uint32_t Width = Inst->words[Inst->operands[1].offset];
509  Mod->addType(Inst->result_id, Type::getFloat(Width));
510  break;
511  }
512  case SpvOpTypeInt:
513  {
514  uint32_t Width = Inst->words[Inst->operands[1].offset];
515  Mod->addType(Inst->result_id, Type::getInt(Width));
516  break;
517  }
518  case SpvOpTypeFunction:
519  {
520  uint32_t ReturnType = Inst->words[Inst->operands[1].offset];
521  std::vector<const Type *> ArgTypes;
522  for (int i = 2; i < Inst->num_operands; i++)
523  {
524  uint32_t ArgType = Inst->words[Inst->operands[i].offset];
525  ArgTypes.push_back(Mod->getType(ArgType));
526  }
527  Mod->addType(Inst->result_id,
528  Type::getFunction(Mod->getType(ReturnType), ArgTypes));
529  break;
530  }
531  case SpvOpTypeImage:
532  {
533  const Type *SampledType =
534  Mod->getType(Inst->words[Inst->operands[1].offset]);
535  uint32_t Dim = Inst->words[Inst->operands[2].offset];
536  uint32_t Depth = Inst->words[Inst->operands[3].offset];
537  uint32_t Arrayed = Inst->words[Inst->operands[4].offset];
538  uint32_t MS = Inst->words[Inst->operands[5].offset];
539  uint32_t Sampled = Inst->words[Inst->operands[6].offset];
540  uint32_t Format = Inst->words[Inst->operands[7].offset];
541  Mod->addType(Inst->result_id,
542  Type::getImage(SampledType, Dim, Depth, Arrayed, MS,
543  Sampled, Format));
544  break;
545  }
546  case SpvOpTypeMatrix:
547  {
548  const Type *ColumnType =
549  Mod->getType(Inst->words[Inst->operands[1].offset]);
550  uint32_t NumColumns = Inst->words[Inst->operands[2].offset];
551  Mod->addType(Inst->result_id, Type::getMatrix(ColumnType, NumColumns));
552  break;
553  }
554  case SpvOpTypePointer:
555  {
556  uint32_t StorageClass = Inst->words[Inst->operands[1].offset];
557  const Type *ElemType =
558  Mod->getType(Inst->words[Inst->operands[2].offset]);
559  uint32_t ArrayStride = (uint32_t)ElemType->getSize();
560  if (ArrayStrides.count(Inst->result_id))
561  ArrayStride = ArrayStrides[Inst->result_id];
562  Mod->addType(Inst->result_id,
563  Type::getPointer(StorageClass, ElemType, ArrayStride));
564  break;
565  }
566  case SpvOpTypeRuntimeArray:
567  {
568  const Type *ElemType =
569  Mod->getType(Inst->words[Inst->operands[1].offset]);
570  uint32_t ArrayStride = (uint32_t)ElemType->getSize();
571  if (ArrayStrides.count(Inst->result_id))
572  ArrayStride = ArrayStrides[Inst->result_id];
573  Mod->addType(Inst->result_id,
574  Type::getRuntimeArray(ElemType, ArrayStride));
575  break;
576  }
577  case SpvOpTypeSampledImage:
578  {
579  const Type *ImageType =
580  Mod->getType(Inst->words[Inst->operands[1].offset]);
581  Mod->addType(Inst->result_id, Type::getSampledImage(ImageType));
582  break;
583  }
584  case SpvOpTypeSampler:
585  {
586  Mod->addType(Inst->result_id, Type::getSampler());
587  break;
588  }
589  case SpvOpTypeStruct:
590  {
591  StructElementTypeList ElemTypes;
592  for (int i = 1; i < Inst->num_operands; i++)
593  {
594  // Get member type and decorations if present.
595  uint32_t ElemTypeId = Inst->words[Inst->operands[i].offset];
596  const Type *ElemType = Mod->getType(ElemTypeId);
597  if (MemberDecorations.count({Inst->result_id, i - 1}))
598  ElemTypes.push_back(
599  {ElemType, MemberDecorations[{Inst->result_id, i - 1}]});
600  else
601  ElemTypes.push_back({ElemType, {}});
602  }
603  Mod->addType(Inst->result_id, Type::getStruct(ElemTypes));
604  break;
605  }
606  case SpvOpTypeVector:
607  {
608  const Type *ElemType =
609  Mod->getType(Inst->words[Inst->operands[1].offset]);
610  uint32_t ElemCount = Inst->words[Inst->operands[2].offset];
611  Mod->addType(Inst->result_id, Type::getVector(ElemType, ElemCount));
612  break;
613  }
614  case SpvOpTypeVoid:
615  {
616  Mod->addType(Inst->result_id, Type::getVoid());
617  break;
618  }
619  case SpvOpUndef:
620  Mod->addObject(Inst->result_id, Object(Mod->getType(Inst->type_id)));
621  break;
622  case SpvOpVariable:
623  {
624  // Create variable.
625  uint32_t Initializer =
626  ((Inst->num_operands > 3) ? Inst->words[Inst->operands[3].offset]
627  : 0);
628  Variable *Var = new Variable(Inst->result_id,
629  Mod->getType(Inst->type_id), Initializer);
630 
631  // Add decorations if present.
632  if (ObjectDecorations.count(Inst->result_id))
633  {
634  for (auto &VD : ObjectDecorations[Inst->result_id])
635  Var->addDecoration(VD.first, VD.second);
636  }
637 
638  Mod->addVariable(Var);
639  break;
640  }
641  default:
642  std::cerr << "Unhandled instruction: "
643  << Instruction::opcodeToString(Inst->opcode) << " ("
644  << Inst->opcode << ")" << std::endl;
645  abort();
646  }
647  }
648  };
649 
651  std::shared_ptr<Module> getModule() { return Mod; }
652 
653 private:
656  std::shared_ptr<Module> Mod;
657  std::unique_ptr<Function> CurrentFunction;
658  std::unique_ptr<Block> CurrentBlock;
660  std::map<uint32_t, uint32_t> ArrayStrides;
661  std::map<std::pair<uint32_t, uint32_t>, std::map<uint32_t, uint32_t>>
663  std::map<uint32_t, std::vector<std::pair<uint32_t, uint32_t>>>
665 
667  {
668  std::string Name;
669  uint32_t ExecutionModel;
670  std::vector<uint32_t> Variables;
671  };
672  std::map<uint32_t, EntryPointSpec> EntryPoints;
674 };
675 
677 spv_result_t HandleHeader(void *user_data, spv_endianness_t endian,
678  uint32_t /* magic */, uint32_t version,
679  uint32_t generator, uint32_t id_bound,
680  uint32_t schema)
681 {
682  ((ModuleBuilder *)user_data)->init(id_bound);
683  return SPV_SUCCESS;
684 }
685 
687 spv_result_t
688 HandleInstruction(void *user_data,
689  const spv_parsed_instruction_t *parsed_instruction)
690 {
691  ((ModuleBuilder *)user_data)->processInstruction(parsed_instruction);
692  return SPV_SUCCESS;
693 }
694 
695 Module::Module(uint32_t IdBound)
696 {
697  this->IdBound = IdBound;
698  this->Objects.resize(IdBound);
699  WorkgroupSizeId = 0;
700 }
701 
702 Module::~Module()
703 {
704  for (auto Op : SpecConstantOps)
705  delete Op;
706 
707  for (auto EP : EntryPoints)
708  delete EP;
709 
710  for (auto Var : Variables)
711  delete Var;
712 }
713 
714 void Module::addEntryPoint(EntryPoint *EP)
715 {
716  assert(getEntryPoint(EP->getName(), EP->getExecutionModel()) == nullptr);
717  EntryPoints.push_back(EP);
718 }
719 
720 void Module::addFunction(std::unique_ptr<Function> Func)
721 {
722  assert(Functions.count(Func->getId()) == 0);
723  Functions[Func->getId()] = std::move(Func);
724 }
725 
726 void Module::addLocalSize(uint32_t Entry, Dim3 LocalSize)
727 {
728  assert(LocalSizes.count(Entry) == 0);
729  LocalSizes[Entry] = LocalSize;
730 }
731 
732 void Module::addObject(uint32_t Id, const Object &Obj)
733 {
734  assert(Id < Objects.size());
735  Objects[Id] = Obj;
736 }
737 
738 void Module::addSpecConstant(uint32_t SpecId, uint32_t ResultId)
739 {
740  // TODO: Allow the same SpecId to apply to multiple results.
741  assert(SpecConstants.count(SpecId) == 0);
742  SpecConstants[SpecId] = ResultId;
743 }
744 
745 void Module::addSpecConstantOp(Instruction *Op)
746 {
747  SpecConstantOps.push_back(Op);
748 }
749 
750 void Module::addType(uint32_t Id, std::unique_ptr<Type> Ty)
751 {
752  assert(!Types.count(Id));
753  Types[Id] = std::move(Ty);
754 }
755 
756 const EntryPoint *Module::getEntryPoint(const std::string &Name,
757  uint32_t ExecutionModel) const
758 {
759  auto Itr = std::find_if(EntryPoints.begin(), EntryPoints.end(), [&](auto EP) {
760  return EP->getName() == Name && EP->getExecutionModel() == ExecutionModel;
761  });
762  if (Itr == EntryPoints.end())
763  return nullptr;
764  return *Itr;
765 }
766 
767 const Function *Module::getFunction(uint32_t Id) const
768 {
769  if (!Functions.count(Id))
770  return nullptr;
771  return Functions.at(Id).get();
772 }
773 
774 Dim3 Module::getLocalSize(uint32_t Entry) const
775 {
776  if (LocalSizes.count(Entry))
777  return LocalSizes.at(Entry);
778  else
779  return Dim3(1, 1, 1);
780 }
781 
782 const Object &Module::getObject(uint32_t Id) const { return Objects.at(Id); }
783 
784 const std::vector<Object> &Module::getObjects() const { return Objects; }
785 
786 uint32_t Module::getSpecConstant(uint32_t SpecId) const
787 {
788  if (SpecConstants.count(SpecId) == 0)
789  return 0;
790  return SpecConstants.at(SpecId);
791 }
792 
793 const std::vector<Instruction *> &Module::getSpecConstantOps() const
794 {
795  return SpecConstantOps;
796 }
797 
798 const Type *Module::getType(uint32_t Id) const
799 {
800  if (Types.count(Id) == 0)
801  return nullptr;
802  return Types.at(Id).get();
803 }
804 
805 std::shared_ptr<Module> Module::load(const uint32_t *Words, size_t NumWords)
806 {
807  spvtools::Context SPVContext(SPV_ENV_VULKAN_1_1);
808  spv_diagnostic Diagnostic = nullptr;
809 
810  // Validate binary.
811  if (spvValidateBinary(SPVContext.CContext(), Words, NumWords, &Diagnostic))
812  {
813  spvDiagnosticPrint(Diagnostic);
814  return nullptr;
815  }
816 
817  // Parse binary.
818  ModuleBuilder MB;
819  spvBinaryParse(SPVContext.CContext(), &MB, Words, NumWords, HandleHeader,
820  HandleInstruction, &Diagnostic);
821  if (Diagnostic)
822  {
823  spvDiagnosticPrint(Diagnostic);
824  return nullptr;
825  }
826 
827  return MB.getModule();
828 }
829 
830 std::shared_ptr<Module> Module::load(const std::string &FileName)
831 {
832  // Open file.
833  FILE *SPVFile = fopen(FileName.c_str(), "rb");
834  if (!SPVFile)
835  {
836  std::cerr << "Failed to open '" << FileName << "'" << std::endl;
837  return nullptr;
838  }
839 
840  // Read file data.
841  std::vector<uint8_t> Bytes;
842  fseek(SPVFile, 0, SEEK_END);
843  long NumBytes = ftell(SPVFile);
844  Bytes.resize(NumBytes);
845  fseek(SPVFile, 0, SEEK_SET);
846  fread(Bytes.data(), 1, NumBytes, SPVFile);
847  fclose(SPVFile);
848 
849  // Check for SPIR-V magic number.
850  if (((uint32_t *)Bytes.data())[0] == 0x07230203)
851  return load((uint32_t *)Bytes.data(), NumBytes / 4);
852 
853  // Assume file is in textual SPIR-V format.
854  // Assemble it to a SPIR-V binary in memory.
855  spv_binary Binary;
856  spv_diagnostic Diagnostic = nullptr;
857  spvtools::Context SPVContext(SPV_ENV_VULKAN_1_1);
858  spvTextToBinary(SPVContext.CContext(), (const char *)Bytes.data(), NumBytes,
859  &Binary, &Diagnostic);
860  if (Diagnostic)
861  {
862  spvDiagnosticPrint(Diagnostic);
863  spvBinaryDestroy(Binary);
864  return nullptr;
865  }
866 
867  // Load and return Module.
868  std::shared_ptr<Module> M = load(Binary->code, Binary->wordCount);
869  spvBinaryDestroy(Binary);
870  return M;
871 }
872 
873 } // namespace talvos
This class represents a module-scope variable declaration.
Definition: Variable.h:21
This file declares the Block class.
static std::unique_ptr< Type > getVector(const Type *ElemType, uint32_t ElemCount)
Create a vector type.
Definition: Type.cpp:368
static std::unique_ptr< Type > getVoid()
Create a void type.
Definition: Type.cpp:377
Instruction * PreviousInstruction
Definition: Module.cpp:659
void init(uint32_t IdBound)
Initialize the module builder.
Definition: Module.cpp:33
static std::unique_ptr< Type > getPointer(uint32_t StorageClass, const Type *ElemType, uint32_t ArrayStride)
Create a pointer type.
Definition: Type.cpp:295
size_t getSize() const
Returns the size of this type in bytes.
Definition: Type.h:81
std::vector< const Variable * > VariableList
A list of module scope variables.
Definition: EntryPoint.h:19
uint32_t ExecutionModel
Definition: Module.cpp:669
static std::unique_ptr< Type > getSampledImage(const Type *ImageType)
Create a sampled image type.
Definition: Type.cpp:316
std::vector< std::pair< const Type *, std::map< uint32_t, uint32_t > > > StructElementTypeList
A list of types used for structure members.
Definition: Type.h:22
static std::unique_ptr< Type > getBool()
Create a boolean type.
Definition: Type.cpp:242
This class represents a SPIR-V module.
Definition: Module.h:37
This file declares the Module class.
static std::unique_ptr< Type > getSampler()
Create a sampler type.
Definition: Type.cpp:323
static std::unique_ptr< Type > getImage(const Type *SampledType, uint32_t Dim, uint32_t Depth, bool Arrayed, bool MS, uint32_t Sampled, uint32_t Format)
Create an image type.
Definition: Type.cpp:271
This file declares the EntryPoint class.
This class represents a function in a SPIR-V Module.
Definition: Function.h:23
Definition: Module.cpp:666
void set(T Value, uint32_t Element=0)
Set the value of this object to a scalar of type T.
Definition: Object.cpp:295
void insertAfter(Instruction *I)
Insert this instruction into a sequence, immediately following I.
Definition: Instruction.cpp:32
Internal class used to construct a Module during SPIRV-Tools parsing.
Definition: Module.cpp:29
void processInstruction(const spv_parsed_instruction_t *Inst)
Process a parsed SPIR-V instruction.
Definition: Module.cpp:43
std::unique_ptr< Function > CurrentFunction
Definition: Module.cpp:657
std::map< uint32_t, uint32_t > ArrayStrides
Definition: Module.cpp:660
This file declares the Instruction class.
This file declares the Type class.
Class representing a 3-dimensional size or ID.
Definition: Dim3.h:22
const std::string & getName() const
Returns the name of this entry point.
Definition: EntryPoint.h:44
static std::unique_ptr< Type > getInt(uint32_t Width)
Create an integer type.
Definition: Type.cpp:254
std::shared_ptr< Module > getModule()
Returns the Module that has been built.
Definition: Module.cpp:651
std::map< uint32_t, EntryPointSpec > EntryPoints
Definition: Module.cpp:672
uint32_t getBitWidth() const
Returns the bit-width of this type.
Definition: Type.cpp:20
void zero()
Set all of the value bits in this object to zero.
Definition: Object.cpp:367
std::vector< uint32_t > Variables
Definition: Module.cpp:670
static std::unique_ptr< Type > getRuntimeArray(const Type *ElemType, uint32_t ArrayStride)
Create a runtime array type.
Definition: Type.cpp:306
const Type * getType() const
Returns the type of this object.
Definition: Object.h:101
static std::unique_ptr< Type > getStruct(const StructElementTypeList &ElemTypes)
Create a structure type.
Definition: Type.cpp:328
void addDecoration(uint32_t Decoration, uint32_t Data)
Add a decoration to this variable.
Definition: Variable.cpp:22
std::string Name
Definition: Module.cpp:668
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
static std::unique_ptr< Type > getFunction(const Type *ReturnType, const std::vector< const Type * > &ArgTypes)
Create a function type.
Definition: Type.cpp:262
This class represents an instruction result.
Definition: Object.h:51
std::unique_ptr< Block > CurrentBlock
Definition: Module.cpp:658
spv_result_t HandleInstruction(void *user_data, const spv_parsed_instruction_t *parsed_instruction)
Callback for SPIRV-Tools parsing a SPIR-V instruction.
Definition: Module.cpp:688
This class represents a SPIR-V instruction.
Definition: Instruction.h:27
std::shared_ptr< Module > Mod
Internal ModuleBuilder variables.
Definition: Module.cpp:656
std::map< std::pair< uint32_t, uint32_t >, std::map< uint32_t, uint32_t > > MemberDecorations
Definition: Module.cpp:662
static const char * opcodeToString(uint16_t Opcode)
Return the string representation of an instruction opcode.
Definition: Instruction.cpp:72
static std::unique_ptr< Type > getFloat(uint32_t Width)
Create a floating point type.
Definition: Type.cpp:247
This file declares the Variable class.
static std::unique_ptr< Type > getArray(const Type *ElemType, uint32_t ElementCount, uint32_t ArrayStride)
Create an array type.
Definition: Type.cpp:230
static std::unique_ptr< Type > getMatrix(const Type *ColumnType, uint32_t NumColumns)
Create a matrix type.
Definition: Type.cpp:286
T get(uint32_t Element=0) const
Get the value of this object as a scalar of type T.
Definition: Object.cpp:97
spv_result_t HandleHeader(void *user_data, spv_endianness_t endian, uint32_t, uint32_t version, uint32_t generator, uint32_t id_bound, uint32_t schema)
Callback for SPIRV-Tools parsing a SPIR-V header.
Definition: Module.cpp:677
uint32_t getExecutionModel() const
Returns the shader execution model of this entry point.
Definition: EntryPoint.h:35
This class represents a shader entry point.
Definition: EntryPoint.h:25
std::map< uint32_t, std::vector< std::pair< uint32_t, uint32_t > > > ObjectDecorations
Definition: Module.cpp:664