13 #include <spirv-tools/libspirv.hpp>
15 #include <spirv/unified1/spirv.h>
33 void init(uint32_t IdBound)
35 assert(!
Mod &&
"Module already initialized");
36 Mod = std::unique_ptr<Module>(
new Module(IdBound));
47 if (Inst->opcode == SpvOpFunction)
50 const Type *FuncType =
51 Mod->getType(Inst->words[Inst->operands[3].offset]);
52 CurrentFunction = std::make_unique<Function>(Inst->result_id, FuncType);
63 auto ModVars =
Mod->getVariables();
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);
77 else if (Inst->opcode == SpvOpFunctionEnd)
86 else if (Inst->opcode == SpvOpFunctionParameter)
90 else if (Inst->opcode == SpvOpLabel)
100 CurrentBlock = std::make_unique<Block>(Inst->result_id);
106 if (Inst->opcode == SpvOpLine || Inst->opcode == SpvOpNoLine)
110 uint32_t *Operands =
new uint32_t[Inst->num_operands];
111 for (
int i = 0; i < Inst->num_operands; i++)
114 assert(Inst->operands[i].num_words == 1);
115 Operands[i] = Inst->words[Inst->operands[i].offset];
119 const Type *ResultType =
120 Inst->type_id ?
Mod->getType(Inst->type_id) :
nullptr;
122 Operands, ResultType);
132 switch (Inst->opcode)
134 case SpvOpCapability:
136 uint32_t Capability = Inst->words[Inst->operands[0].offset];
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:
166 std::cerr <<
"Unimplemented capability: " << Capability << std::endl;
172 case SpvOpSpecConstant:
174 const Type *Ty =
Mod->getType(Inst->type_id);
177 uint16_t Offset = Inst->operands[2].offset;
181 Constant.
set<uint16_t>(*(uint16_t *)(Inst->words + Offset));
184 Constant.
set<uint32_t>(Inst->words[Offset]);
187 Constant.
set<uint64_t>(*(uint64_t *)(Inst->words + Offset));
190 std::cerr <<
"Unhandled OpConstant type size: " << Ty->
getSize()
194 Mod->addObject(Inst->result_id, Constant);
197 case SpvOpConstantComposite:
199 const Type *Ty =
Mod->getType(Inst->type_id);
205 for (uint32_t i = 2; i < Inst->num_operands; i++)
207 uint32_t Id = Inst->words[Inst->operands[i].offset];
208 Composite.
insert({i - 2},
Mod->getObject(Id));
212 Mod->addObject(Inst->result_id, Composite);
215 case SpvOpConstantFalse:
216 case SpvOpSpecConstantFalse:
218 const Type *Ty =
Mod->getType(Inst->type_id);
219 Mod->addObject(Inst->result_id,
Object(Ty,
false));
222 case SpvOpConstantNull:
225 const Type *Ty =
Mod->getType(Inst->type_id);
228 Mod->addObject(Inst->result_id, Value);
231 case SpvOpConstantTrue:
232 case SpvOpSpecConstantTrue:
234 const Type *Ty =
Mod->getType(Inst->type_id);
235 Mod->addObject(Inst->result_id,
Object(Ty,
true));
240 uint32_t Target = Inst->words[Inst->operands[0].offset];
241 uint32_t Decoration = Inst->words[Inst->operands[1].offset];
244 case SpvDecorationArrayStride:
245 ArrayStrides[Target] = Inst->words[Inst->operands[2].offset];
247 case SpvDecorationBinding:
248 case SpvDecorationComponent:
249 case SpvDecorationDescriptorSet:
250 case SpvDecorationLocation:
252 {Decoration, Inst->words[Inst->operands[2].offset]});
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:
266 case SpvDecorationBlock:
267 case SpvDecorationBufferBlock:
270 case SpvDecorationBuiltIn:
272 switch (Inst->words[Inst->operands[2].offset])
274 case SpvBuiltInWorkgroupSize:
275 Mod->setWorkgroupSizeId(Target);
279 {Decoration, Inst->words[Inst->operands[2].offset]});
284 case SpvDecorationRelaxedPrecision:
286 case SpvDecorationSpecId:
287 Mod->addSpecConstant(Inst->words[Inst->operands[2].offset], Target);
290 std::cerr <<
"Unhandled decoration " << Decoration << std::endl;
295 case SpvOpEntryPoint:
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)
304 std::cerr <<
"Unimplemented execution model: " << ExecutionModel
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};
316 case SpvOpExecutionMode:
318 uint32_t Entry = Inst->words[Inst->operands[0].offset];
319 uint32_t Mode = Inst->words[Inst->operands[1].offset];
322 case SpvExecutionModeLocalSize:
323 Mod->addLocalSize(Entry,
324 Dim3(Inst->words + Inst->operands[2].offset));
326 case SpvExecutionModeOriginUpperLeft:
330 std::cerr <<
"Unimplemented execution mode: " << Mode << std::endl;
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"))
343 std::cerr <<
"Unimplemented extension " << Extension << std::endl;
348 case SpvOpExtInstImport:
351 char *ExtInstSet = (
char *)(Inst->words + Inst->operands[1].offset);
352 if (strcmp(ExtInstSet,
"GLSL.std.450"))
354 std::cerr <<
"Unrecognized extended instruction set " << ExtInstSet
363 case SpvOpMemberDecorate:
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;
371 case SpvDecorationBuiltIn:
372 case SpvDecorationMatrixStride:
373 case SpvDecorationOffset:
376 case SpvDecorationCoherent:
377 case SpvDecorationColMajor:
378 case SpvDecorationRowMajor:
379 case SpvDecorationFlat:
380 case SpvDecorationNonReadable:
381 case SpvDecorationNonWritable:
382 case SpvDecorationNoPerspective:
385 case SpvDecorationRelaxedPrecision:
388 std::cerr <<
"Unhandled member decoration " << Decoration
394 case SpvOpMemberName:
397 case SpvOpMemoryModel:
399 uint32_t AddressingMode = Inst->words[Inst->operands[0].offset];
400 uint32_t MemoryModel = Inst->words[Inst->operands[1].offset];
401 if (AddressingMode != SpvAddressingModelLogical)
403 std::cerr <<
"Unrecognized addressing mode " << AddressingMode
407 if (MemoryModel != SpvMemoryModelGLSL450)
409 std::cerr <<
"Unrecognized memory model " << MemoryModel << std::endl;
414 case SpvOpModuleProcessed:
422 case SpvOpSpecConstantComposite:
424 const Type *ResultType =
Mod->getType(Inst->type_id);
427 std::vector<uint32_t> Operands(Inst->num_operands);
428 for (
int i = 0; i < Inst->num_operands; i++)
430 assert(Inst->operands[i].num_words == 1);
431 Operands[i] = Inst->words[Inst->operands[i].offset];
437 Operands.data(), ResultType));
440 case SpvOpSpecConstantOp:
442 const Type *ResultType =
Mod->getType(Inst->type_id);
443 uint16_t Opcode = Inst->words[Inst->operands[2].offset];
446 std::vector<uint32_t> Operands;
447 for (
int i = 0; i < Inst->num_operands; i++)
451 assert(Inst->operands[i].num_words == 1);
452 Operands.push_back(Inst->words[Inst->operands[i].offset]);
456 Mod->addSpecConstantOp(
new Instruction(Opcode, Inst->num_operands - 1,
457 Operands.data(), ResultType));
461 case SpvOpSourceContinued:
464 case SpvOpSourceExtension:
472 uint32_t LengthId = Inst->words[Inst->operands[2].offset];
473 const Object &LengthObj =
Mod->getObject(LengthId);
478 Length = (uint32_t)LengthObj.
get<uint16_t>();
481 Length = (uint32_t)LengthObj.
get<uint32_t>();
484 assert(LengthObj.
get<uint64_t>() <= UINT32_MAX);
485 Length = (uint32_t)LengthObj.
get<uint64_t>();
488 std::cerr <<
"Invalid array length bitwidth" << std::endl;
492 const Type *ElemType =
493 Mod->getType(Inst->words[Inst->operands[1].offset]);
494 uint32_t ArrayStride = (uint32_t)ElemType->
getSize();
497 Mod->addType(Inst->result_id,
508 uint32_t Width = Inst->words[Inst->operands[1].offset];
514 uint32_t Width = Inst->words[Inst->operands[1].offset];
518 case SpvOpTypeFunction:
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++)
524 uint32_t ArgType = Inst->words[Inst->operands[i].offset];
525 ArgTypes.push_back(
Mod->getType(ArgType));
527 Mod->addType(Inst->result_id,
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,
546 case SpvOpTypeMatrix:
548 const Type *ColumnType =
549 Mod->getType(Inst->words[Inst->operands[1].offset]);
550 uint32_t NumColumns = Inst->words[Inst->operands[2].offset];
554 case SpvOpTypePointer:
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();
562 Mod->addType(Inst->result_id,
566 case SpvOpTypeRuntimeArray:
568 const Type *ElemType =
569 Mod->getType(Inst->words[Inst->operands[1].offset]);
570 uint32_t ArrayStride = (uint32_t)ElemType->
getSize();
573 Mod->addType(Inst->result_id,
577 case SpvOpTypeSampledImage:
579 const Type *ImageType =
580 Mod->getType(Inst->words[Inst->operands[1].offset]);
584 case SpvOpTypeSampler:
589 case SpvOpTypeStruct:
592 for (
int i = 1; i < Inst->num_operands; i++)
595 uint32_t ElemTypeId = Inst->words[Inst->operands[i].offset];
596 const Type *ElemType =
Mod->getType(ElemTypeId);
601 ElemTypes.push_back({ElemType, {}});
606 case SpvOpTypeVector:
608 const Type *ElemType =
609 Mod->getType(Inst->words[Inst->operands[1].offset]);
610 uint32_t ElemCount = Inst->words[Inst->operands[2].offset];
620 Mod->addObject(Inst->result_id,
Object(
Mod->getType(Inst->type_id)));
625 uint32_t Initializer =
626 ((Inst->num_operands > 3) ? Inst->words[Inst->operands[3].offset]
629 Mod->getType(Inst->type_id), Initializer);
638 Mod->addVariable(Var);
642 std::cerr <<
"Unhandled instruction: "
644 << Inst->opcode <<
")" << std::endl;
656 std::shared_ptr<Module>
Mod;
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>>>
678 uint32_t , uint32_t version,
679 uint32_t generator, uint32_t id_bound,
689 const spv_parsed_instruction_t *parsed_instruction)
691 ((
ModuleBuilder *)user_data)->processInstruction(parsed_instruction);
695 Module::Module(uint32_t IdBound)
697 this->IdBound = IdBound;
698 this->Objects.resize(IdBound);
704 for (
auto Op : SpecConstantOps)
707 for (
auto EP : EntryPoints)
710 for (
auto Var : Variables)
717 EntryPoints.push_back(EP);
720 void Module::addFunction(std::unique_ptr<Function> Func)
722 assert(Functions.count(Func->getId()) == 0);
723 Functions[Func->getId()] = std::move(Func);
726 void Module::addLocalSize(uint32_t Entry,
Dim3 LocalSize)
728 assert(LocalSizes.count(Entry) == 0);
729 LocalSizes[Entry] = LocalSize;
732 void Module::addObject(uint32_t Id,
const Object &Obj)
734 assert(Id < Objects.size());
738 void Module::addSpecConstant(uint32_t SpecId, uint32_t ResultId)
741 assert(SpecConstants.count(SpecId) == 0);
742 SpecConstants[SpecId] = ResultId;
747 SpecConstantOps.push_back(Op);
750 void Module::addType(uint32_t Id, std::unique_ptr<Type> Ty)
752 assert(!Types.count(Id));
753 Types[Id] = std::move(Ty);
756 const EntryPoint *Module::getEntryPoint(
const std::string &Name,
757 uint32_t ExecutionModel)
const
759 auto Itr = std::find_if(EntryPoints.begin(), EntryPoints.end(), [&](
auto EP) {
760 return EP->getName() == Name && EP->getExecutionModel() == ExecutionModel;
762 if (Itr == EntryPoints.end())
767 const Function *Module::getFunction(uint32_t Id)
const
769 if (!Functions.count(Id))
771 return Functions.at(Id).get();
774 Dim3 Module::getLocalSize(uint32_t Entry)
const
776 if (LocalSizes.count(Entry))
777 return LocalSizes.at(Entry);
779 return Dim3(1, 1, 1);
782 const Object &Module::getObject(uint32_t Id)
const {
return Objects.at(Id); }
784 const std::vector<Object> &Module::getObjects()
const {
return Objects; }
786 uint32_t Module::getSpecConstant(uint32_t SpecId)
const
788 if (SpecConstants.count(SpecId) == 0)
790 return SpecConstants.at(SpecId);
793 const std::vector<Instruction *> &Module::getSpecConstantOps()
const
795 return SpecConstantOps;
798 const Type *Module::getType(uint32_t Id)
const
800 if (Types.count(Id) == 0)
802 return Types.at(Id).get();
805 std::shared_ptr<Module> Module::load(
const uint32_t *Words,
size_t NumWords)
807 spvtools::Context SPVContext(SPV_ENV_VULKAN_1_1);
808 spv_diagnostic Diagnostic =
nullptr;
811 if (spvValidateBinary(SPVContext.CContext(), Words, NumWords, &Diagnostic))
813 spvDiagnosticPrint(Diagnostic);
819 spvBinaryParse(SPVContext.CContext(), &MB, Words, NumWords,
HandleHeader,
823 spvDiagnosticPrint(Diagnostic);
830 std::shared_ptr<Module> Module::load(
const std::string &FileName)
833 FILE *SPVFile = fopen(FileName.c_str(),
"rb");
836 std::cerr <<
"Failed to open '" << FileName <<
"'" << std::endl;
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);
850 if (((uint32_t *)Bytes.data())[0] == 0x07230203)
851 return load((uint32_t *)Bytes.data(), NumBytes / 4);
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);
862 spvDiagnosticPrint(Diagnostic);
863 spvBinaryDestroy(Binary);
868 std::shared_ptr<Module> M = load(Binary->code, Binary->wordCount);
869 spvBinaryDestroy(Binary);
This class represents a module-scope variable declaration.
This file declares the Block class.
static std::unique_ptr< Type > getVector(const Type *ElemType, uint32_t ElemCount)
Create a vector type.
static std::unique_ptr< Type > getVoid()
Create a void type.
Instruction * PreviousInstruction
void init(uint32_t IdBound)
Initialize the module builder.
static std::unique_ptr< Type > getPointer(uint32_t StorageClass, const Type *ElemType, uint32_t ArrayStride)
Create a pointer type.
size_t getSize() const
Returns the size of this type in bytes.
std::vector< const Variable * > VariableList
A list of module scope variables.
static std::unique_ptr< Type > getSampledImage(const Type *ImageType)
Create a sampled image type.
std::vector< std::pair< const Type *, std::map< uint32_t, uint32_t > > > StructElementTypeList
A list of types used for structure members.
static std::unique_ptr< Type > getBool()
Create a boolean type.
This class represents a SPIR-V module.
This file declares the Module class.
static std::unique_ptr< Type > getSampler()
Create a sampler type.
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.
This file declares the EntryPoint class.
This class represents a function in a SPIR-V Module.
void set(T Value, uint32_t Element=0)
Set the value of this object to a scalar of type T.
void insertAfter(Instruction *I)
Insert this instruction into a sequence, immediately following I.
Internal class used to construct a Module during SPIRV-Tools parsing.
void processInstruction(const spv_parsed_instruction_t *Inst)
Process a parsed SPIR-V instruction.
std::unique_ptr< Function > CurrentFunction
std::map< uint32_t, uint32_t > ArrayStrides
This file declares the Instruction class.
This file declares the Type class.
Class representing a 3-dimensional size or ID.
const std::string & getName() const
Returns the name of this entry point.
static std::unique_ptr< Type > getInt(uint32_t Width)
Create an integer type.
std::shared_ptr< Module > getModule()
Returns the Module that has been built.
std::map< uint32_t, EntryPointSpec > EntryPoints
uint32_t getBitWidth() const
Returns the bit-width of this type.
void zero()
Set all of the value bits in this object to zero.
std::vector< uint32_t > Variables
static std::unique_ptr< Type > getRuntimeArray(const Type *ElemType, uint32_t ArrayStride)
Create a runtime array type.
const Type * getType() const
Returns the type of this object.
static std::unique_ptr< Type > getStruct(const StructElementTypeList &ElemTypes)
Create a structure type.
void addDecoration(uint32_t Decoration, uint32_t Data)
Add a decoration to this variable.
This class represents a SPIR-V type.
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.
static std::unique_ptr< Type > getFunction(const Type *ReturnType, const std::vector< const Type * > &ArgTypes)
Create a function type.
This class represents an instruction result.
std::unique_ptr< Block > CurrentBlock
spv_result_t HandleInstruction(void *user_data, const spv_parsed_instruction_t *parsed_instruction)
Callback for SPIRV-Tools parsing a SPIR-V instruction.
This class represents a SPIR-V instruction.
std::shared_ptr< Module > Mod
Internal ModuleBuilder variables.
std::map< std::pair< uint32_t, uint32_t >, std::map< uint32_t, uint32_t > > MemberDecorations
static const char * opcodeToString(uint16_t Opcode)
Return the string representation of an instruction opcode.
static std::unique_ptr< Type > getFloat(uint32_t Width)
Create a floating point type.
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.
static std::unique_ptr< Type > getMatrix(const Type *ColumnType, uint32_t NumColumns)
Create a matrix type.
T get(uint32_t Element=0) const
Get the value of this object as a scalar of type T.
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.
uint32_t getExecutionModel() const
Returns the shader execution model of this entry point.
This class represents a shader entry point.
std::map< uint32_t, std::vector< std::pair< uint32_t, uint32_t > > > ObjectDecorations