23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/Sequence.h"
25 #include "llvm/ADT/SmallVector.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/ADT/bit.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/SaveAndRestore.h"
30 #include "llvm/Support/raw_ostream.h"
35 #define DEBUG_TYPE "spirv-deserialization"
44 isa_and_nonnull<spirv::FuncOp>(block->
getParentOp());
53 : binary(binary), context(context), unknownLoc(UnknownLoc::
get(context)),
54 module(createModuleOp()), opBuilder(module->getRegion())
66 <<
"//+++---------- start deserialization ----------+++//\n";
69 if (failed(processHeader()))
72 spirv::Opcode opcode = spirv::Opcode::OpNop;
74 auto binarySize = binary.size();
75 while (curOffset < binarySize) {
78 if (failed(sliceInstruction(opcode, operands)))
81 if (failed(processInstruction(opcode, operands)))
85 assert(curOffset == binarySize &&
86 "deserializer should never index beyond the binary end");
88 for (
auto &deferred : deferredInstructions) {
89 if (failed(processInstruction(deferred.first, deferred.second,
false))) {
96 LLVM_DEBUG(logger.startLine()
97 <<
"//+++-------- completed deserialization --------+++//\n");
102 return std::move(module);
111 OperationState state(unknownLoc, spirv::ModuleOp::getOperationName());
112 spirv::ModuleOp::build(builder, state);
116 LogicalResult spirv::Deserializer::processHeader() {
119 "SPIR-V binary module must have a 5-word header");
122 return emitError(unknownLoc,
"incorrect magic number");
125 uint32_t majorVersion = (binary[1] << 8) >> 24;
126 uint32_t minorVersion = (binary[1] << 16) >> 24;
127 if (majorVersion == 1) {
128 switch (minorVersion) {
129 #define MIN_VERSION_CASE(v) \
131 version = spirv::Version::V_1_##v; \
140 #undef MIN_VERSION_CASE
142 return emitError(unknownLoc,
"unsupported SPIR-V minor version: ")
146 return emitError(unknownLoc,
"unsupported SPIR-V major version: ")
157 if (operands.size() != 1)
158 return emitError(unknownLoc,
"OpMemoryModel must have one parameter");
160 auto cap = spirv::symbolizeCapability(operands[0]);
162 return emitError(unknownLoc,
"unknown capability: ") << operands[0];
164 capabilities.insert(*cap);
172 "OpExtension must have a literal string for the extension name");
175 unsigned wordIndex = 0;
177 if (wordIndex != words.size())
179 "unexpected trailing words in OpExtension instruction");
180 auto ext = spirv::symbolizeExtension(extName);
182 return emitError(unknownLoc,
"unknown extension: ") << extName;
184 extensions.insert(*ext);
190 if (words.size() < 2) {
192 "OpExtInstImport must have a result <id> and a literal "
193 "string for the extended instruction set name");
196 unsigned wordIndex = 1;
198 if (wordIndex != words.size()) {
200 "unexpected trailing words in OpExtInstImport");
205 void spirv::Deserializer::attachVCETriple() {
207 spirv::ModuleOp::getVCETripleAttrName(),
209 extensions.getArrayRef(), context));
214 if (operands.size() != 2)
215 return emitError(unknownLoc,
"OpMemoryModel must have two operands");
218 module->getAddressingModelAttrName(),
219 opBuilder.getAttr<spirv::AddressingModelAttr>(
220 static_cast<spirv::AddressingModel
>(operands.front())));
222 (*module)->setAttr(module->getMemoryModelAttrName(),
223 opBuilder.getAttr<spirv::MemoryModelAttr>(
224 static_cast<spirv::MemoryModel
>(operands.back())));
233 if (words.size() < 2) {
235 unknownLoc,
"OpDecorate must have at least result <id> and Decoration");
237 auto decorationName =
238 stringifyDecoration(
static_cast<spirv::Decoration
>(words[1]));
239 if (decorationName.empty()) {
240 return emitError(unknownLoc,
"invalid Decoration code : ") << words[1];
242 auto symbol = getSymbolDecoration(decorationName);
243 switch (
static_cast<spirv::Decoration
>(words[1])) {
244 case spirv::Decoration::FPFastMathMode:
245 if (words.size() != 3) {
246 return emitError(unknownLoc,
"OpDecorate with ")
247 << decorationName <<
" needs a single integer literal";
249 decorations[words[0]].set(
251 static_cast<FPFastMathMode
>(words[2])));
253 case spirv::Decoration::DescriptorSet:
254 case spirv::Decoration::Binding:
255 if (words.size() != 3) {
256 return emitError(unknownLoc,
"OpDecorate with ")
257 << decorationName <<
" needs a single integer literal";
259 decorations[words[0]].set(
260 symbol, opBuilder.getI32IntegerAttr(
static_cast<int32_t
>(words[2])));
262 case spirv::Decoration::BuiltIn:
263 if (words.size() != 3) {
264 return emitError(unknownLoc,
"OpDecorate with ")
265 << decorationName <<
" needs a single integer literal";
267 decorations[words[0]].set(
268 symbol, opBuilder.getStringAttr(
269 stringifyBuiltIn(
static_cast<spirv::BuiltIn
>(words[2]))));
271 case spirv::Decoration::ArrayStride:
272 if (words.size() != 3) {
273 return emitError(unknownLoc,
"OpDecorate with ")
274 << decorationName <<
" needs a single integer literal";
276 typeDecorations[words[0]] = words[2];
278 case spirv::Decoration::LinkageAttributes: {
279 if (words.size() < 4) {
280 return emitError(unknownLoc,
"OpDecorate with ")
282 <<
" needs at least 1 string and 1 integer literal";
290 unsigned wordIndex = 2;
292 auto linkageTypeAttr = opBuilder.getAttr<::mlir::spirv::LinkageTypeAttr>(
293 static_cast<::mlir::spirv::LinkageType
>(words[wordIndex++]));
294 auto linkageAttr = opBuilder.getAttr<::mlir::spirv::LinkageAttributesAttr>(
296 decorations[words[0]].set(symbol, llvm::dyn_cast<Attribute>(linkageAttr));
299 case spirv::Decoration::Aliased:
300 case spirv::Decoration::AliasedPointer:
301 case spirv::Decoration::Block:
302 case spirv::Decoration::BufferBlock:
303 case spirv::Decoration::Flat:
304 case spirv::Decoration::NonReadable:
305 case spirv::Decoration::NonWritable:
306 case spirv::Decoration::NoPerspective:
307 case spirv::Decoration::NoSignedWrap:
308 case spirv::Decoration::NoUnsignedWrap:
309 case spirv::Decoration::RelaxedPrecision:
310 case spirv::Decoration::Restrict:
311 case spirv::Decoration::RestrictPointer:
312 case spirv::Decoration::NoContraction:
313 if (words.size() != 2) {
314 return emitError(unknownLoc,
"OpDecoration with ")
315 << decorationName <<
"needs a single target <id>";
321 decorations[words[0]].set(symbol, opBuilder.getUnitAttr());
323 case spirv::Decoration::Location:
324 case spirv::Decoration::SpecId:
325 if (words.size() != 3) {
326 return emitError(unknownLoc,
"OpDecoration with ")
327 << decorationName <<
"needs a single integer literal";
329 decorations[words[0]].set(
330 symbol, opBuilder.getI32IntegerAttr(
static_cast<int32_t
>(words[2])));
333 return emitError(unknownLoc,
"unhandled Decoration : '") << decorationName;
341 if (words.size() < 3) {
343 "OpMemberDecorate must have at least 3 operands");
346 auto decoration =
static_cast<spirv::Decoration
>(words[2]);
347 if (decoration == spirv::Decoration::Offset && words.size() != 4) {
349 " missing offset specification in OpMemberDecorate with "
350 "Offset decoration");
353 if (words.size() > 3) {
354 decorationOperands = words.slice(3);
356 memberDecorationMap[words[0]][words[1]][decoration] = decorationOperands;
361 if (words.size() < 3) {
362 return emitError(unknownLoc,
"OpMemberName must have at least 3 operands");
364 unsigned wordIndex = 2;
366 if (wordIndex != words.size()) {
368 "unexpected trailing words in OpMemberName instruction");
370 memberNameMap[words[0]][words[1]] = name;
374 LogicalResult spirv::Deserializer::setFunctionArgAttrs(
376 if (!decorations.contains(argID)) {
381 spirv::DecorationAttr foundDecorationAttr;
383 for (
auto decoration :
384 {spirv::Decoration::Aliased, spirv::Decoration::Restrict,
385 spirv::Decoration::AliasedPointer,
386 spirv::Decoration::RestrictPointer}) {
388 if (decAttr.getName() !=
389 getSymbolDecoration(stringifyDecoration(decoration)))
392 if (foundDecorationAttr)
394 "more than one Aliased/Restrict decorations for "
395 "function argument with result <id> ")
403 if (!foundDecorationAttr)
404 return emitError(unknownLoc,
"unimplemented decoration support for "
405 "function argument with result <id> ")
409 foundDecorationAttr);
417 return emitError(unknownLoc,
"found function inside function");
421 if (operands.size() != 4) {
422 return emitError(unknownLoc,
"OpFunction must have 4 parameters");
426 return emitError(unknownLoc,
"undefined result type from <id> ")
430 uint32_t fnID = operands[1];
431 if (funcMap.count(fnID)) {
432 return emitError(unknownLoc,
"duplicate function definition/declaration");
435 auto fnControl = spirv::symbolizeFunctionControl(operands[2]);
437 return emitError(unknownLoc,
"unknown Function Control: ") << operands[2];
441 if (!fnType || !isa<FunctionType>(fnType)) {
442 return emitError(unknownLoc,
"unknown function type from <id> ")
445 auto functionType = cast<FunctionType>(fnType);
447 if ((isVoidType(resultType) && functionType.getNumResults() != 0) ||
448 (functionType.getNumResults() == 1 &&
449 functionType.getResult(0) != resultType)) {
450 return emitError(unknownLoc,
"mismatch in function type ")
451 << functionType <<
" and return type " << resultType <<
" specified";
454 std::string fnName = getFunctionSymbol(fnID);
455 auto funcOp = opBuilder.create<spirv::FuncOp>(
456 unknownLoc, fnName, functionType, fnControl.value());
458 if (decorations.count(fnID)) {
459 for (
auto attr : decorations[fnID].getAttrs()) {
460 funcOp->setAttr(attr.getName(), attr.getValue());
463 curFunction = funcMap[fnID] = funcOp;
464 auto *entryBlock = funcOp.addEntryBlock();
467 <<
"//===-------------------------------------------===//\n";
468 logger.startLine() <<
"[fn] name: " << fnName <<
"\n";
469 logger.startLine() <<
"[fn] type: " << fnType <<
"\n";
470 logger.startLine() <<
"[fn] ID: " << fnID <<
"\n";
471 logger.startLine() <<
"[fn] entry block: " << entryBlock <<
"\n";
476 argAttrs.resize(functionType.getNumInputs());
479 if (functionType.getNumInputs()) {
480 for (
size_t i = 0, e = functionType.getNumInputs(); i != e; ++i) {
481 auto argType = functionType.getInput(i);
482 spirv::Opcode opcode = spirv::Opcode::OpNop;
484 if (failed(sliceInstruction(opcode, operands,
485 spirv::Opcode::OpFunctionParameter))) {
488 if (opcode != spirv::Opcode::OpFunctionParameter) {
491 "missing OpFunctionParameter instruction for argument ")
494 if (operands.size() != 2) {
497 "expected result type and result <id> for OpFunctionParameter");
499 auto argDefinedType =
getType(operands[0]);
500 if (!argDefinedType || argDefinedType != argType) {
502 "mismatch in argument type between function type "
504 << functionType <<
" and argument type definition "
505 << argDefinedType <<
" at argument " << i;
507 if (getValue(operands[1])) {
508 return emitError(unknownLoc,
"duplicate definition of result <id> ")
511 if (failed(setFunctionArgAttrs(operands[1], argAttrs, i))) {
515 auto argValue = funcOp.getArgument(i);
516 valueMap[operands[1]] = argValue;
520 if (llvm::any_of(argAttrs, [](
Attribute attr) {
521 auto argAttr = cast<DictionaryAttr>(attr);
522 return !argAttr.empty();
529 auto linkageAttr = funcOp.getLinkageAttributes();
530 auto hasImportLinkage =
531 linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
532 spirv::LinkageType::Import);
533 if (hasImportLinkage)
540 spirv::Opcode opcode = spirv::Opcode::OpNop;
548 if (failed(sliceInstruction(opcode, instOperands,
549 spirv::Opcode::OpFunctionEnd))) {
552 if (opcode == spirv::Opcode::OpFunctionEnd) {
553 return processFunctionEnd(instOperands);
555 if (opcode != spirv::Opcode::OpLabel) {
556 return emitError(unknownLoc,
"a basic block must start with OpLabel");
558 if (instOperands.size() != 1) {
559 return emitError(unknownLoc,
"OpLabel should only have result <id>");
561 blockMap[instOperands[0]] = entryBlock;
562 if (failed(processLabel(instOperands))) {
568 while (succeeded(sliceInstruction(opcode, instOperands,
569 spirv::Opcode::OpFunctionEnd)) &&
570 opcode != spirv::Opcode::OpFunctionEnd) {
571 if (failed(processInstruction(opcode, instOperands))) {
575 if (opcode != spirv::Opcode::OpFunctionEnd) {
579 return processFunctionEnd(instOperands);
585 if (!operands.empty()) {
586 return emitError(unknownLoc,
"unexpected operands for OpFunctionEnd");
592 if (failed(wireUpBlockArgument()) || failed(structurizeControlFlow())) {
597 curFunction = std::nullopt;
602 <<
"//===-------------------------------------------===//\n";
607 std::optional<std::pair<Attribute, Type>>
608 spirv::Deserializer::getConstant(uint32_t
id) {
609 auto constIt = constantMap.find(
id);
610 if (constIt == constantMap.end())
612 return constIt->getSecond();
615 std::optional<spirv::SpecConstOperationMaterializationInfo>
616 spirv::Deserializer::getSpecConstantOperation(uint32_t
id) {
617 auto constIt = specConstOperationMap.find(
id);
618 if (constIt == specConstOperationMap.end())
620 return constIt->getSecond();
623 std::string spirv::Deserializer::getFunctionSymbol(uint32_t
id) {
624 auto funcName = nameMap.lookup(
id).str();
625 if (funcName.empty()) {
626 funcName =
"spirv_fn_" + std::to_string(
id);
631 std::string spirv::Deserializer::getSpecConstantSymbol(uint32_t
id) {
632 auto constName = nameMap.lookup(
id).str();
633 if (constName.empty()) {
634 constName =
"spirv_spec_const_" + std::to_string(
id);
639 spirv::SpecConstantOp
640 spirv::Deserializer::createSpecConstant(
Location loc, uint32_t resultID,
641 TypedAttr defaultValue) {
642 auto symName = opBuilder.getStringAttr(getSpecConstantSymbol(resultID));
643 auto op = opBuilder.
create<spirv::SpecConstantOp>(unknownLoc, symName,
645 if (decorations.count(resultID)) {
646 for (
auto attr : decorations[resultID].getAttrs())
647 op->
setAttr(attr.getName(), attr.getValue());
649 specConstMap[resultID] = op;
655 unsigned wordIndex = 0;
656 if (operands.size() < 3) {
659 "OpVariable needs at least 3 operands, type, <id> and storage class");
663 auto type =
getType(operands[wordIndex]);
665 return emitError(unknownLoc,
"unknown result type <id> : ")
666 << operands[wordIndex];
668 auto ptrType = dyn_cast<spirv::PointerType>(type);
671 "expected a result type <id> to be a spirv.ptr, found : ")
677 auto variableID = operands[wordIndex];
678 auto variableName = nameMap.lookup(variableID).str();
679 if (variableName.empty()) {
680 variableName =
"spirv_var_" + std::to_string(variableID);
685 auto storageClass =
static_cast<spirv::StorageClass
>(operands[wordIndex]);
686 if (ptrType.getStorageClass() != storageClass) {
687 return emitError(unknownLoc,
"mismatch in storage class of pointer type ")
688 << type <<
" and that specified in OpVariable instruction : "
689 << stringifyStorageClass(storageClass);
696 if (wordIndex < operands.size()) {
699 if (
auto initOp = getGlobalVariable(operands[wordIndex]))
701 else if (
auto initOp = getSpecConstant(operands[wordIndex]))
703 else if (
auto initOp = getSpecConstantComposite(operands[wordIndex]))
706 return emitError(unknownLoc,
"unknown <id> ")
707 << operands[wordIndex] <<
"used as initializer";
712 if (wordIndex != operands.size()) {
714 "found more operands than expected when deserializing "
715 "OpVariable instruction, only ")
716 << wordIndex <<
" of " << operands.size() <<
" processed";
718 auto loc = createFileLineColLoc(opBuilder);
719 auto varOp = opBuilder.create<spirv::GlobalVariableOp>(
720 loc,
TypeAttr::get(type), opBuilder.getStringAttr(variableName),
724 if (decorations.count(variableID)) {
725 for (
auto attr : decorations[variableID].getAttrs())
726 varOp->setAttr(attr.getName(), attr.getValue());
728 globalVariableMap[variableID] = varOp;
732 IntegerAttr spirv::Deserializer::getConstantInt(uint32_t
id) {
733 auto constInfo = getConstant(
id);
737 return dyn_cast<IntegerAttr>(constInfo->first);
741 if (operands.size() < 2) {
742 return emitError(unknownLoc,
"OpName needs at least 2 operands");
744 if (!nameMap.lookup(operands[0]).empty()) {
745 return emitError(unknownLoc,
"duplicate name found for result <id> ")
748 unsigned wordIndex = 1;
750 if (wordIndex != operands.size()) {
752 "unexpected trailing words in OpName instruction");
754 nameMap[operands[0]] = name;
762 LogicalResult spirv::Deserializer::processType(spirv::Opcode opcode,
764 if (operands.empty()) {
765 return emitError(unknownLoc,
"type instruction with opcode ")
766 << spirv::stringifyOpcode(opcode) <<
" needs at least one <id>";
771 if (typeMap.count(operands[0])) {
772 return emitError(unknownLoc,
"duplicate definition for result <id> ")
777 case spirv::Opcode::OpTypeVoid:
778 if (operands.size() != 1)
779 return emitError(unknownLoc,
"OpTypeVoid must have no parameters");
780 typeMap[operands[0]] = opBuilder.getNoneType();
782 case spirv::Opcode::OpTypeBool:
783 if (operands.size() != 1)
784 return emitError(unknownLoc,
"OpTypeBool must have no parameters");
785 typeMap[operands[0]] = opBuilder.getI1Type();
787 case spirv::Opcode::OpTypeInt: {
788 if (operands.size() != 3)
790 unknownLoc,
"OpTypeInt must have bitwidth and signedness parameters");
800 : IntegerType::SignednessSemantics::Signless;
803 case spirv::Opcode::OpTypeFloat: {
804 if (operands.size() != 2)
805 return emitError(unknownLoc,
"OpTypeFloat must have bitwidth parameter");
808 switch (operands[1]) {
810 floatTy = opBuilder.getF16Type();
813 floatTy = opBuilder.getF32Type();
816 floatTy = opBuilder.getF64Type();
819 return emitError(unknownLoc,
"unsupported OpTypeFloat bitwidth: ")
822 typeMap[operands[0]] = floatTy;
824 case spirv::Opcode::OpTypeVector: {
825 if (operands.size() != 3) {
828 "OpTypeVector must have element type and count parameters");
832 return emitError(unknownLoc,
"OpTypeVector references undefined <id> ")
837 case spirv::Opcode::OpTypePointer: {
838 return processOpTypePointer(operands);
840 case spirv::Opcode::OpTypeArray:
841 return processArrayType(operands);
842 case spirv::Opcode::OpTypeCooperativeMatrixKHR:
843 return processCooperativeMatrixTypeKHR(operands);
844 case spirv::Opcode::OpTypeFunction:
845 return processFunctionType(operands);
846 case spirv::Opcode::OpTypeJointMatrixINTEL:
847 return processJointMatrixType(operands);
848 case spirv::Opcode::OpTypeImage:
849 return processImageType(operands);
850 case spirv::Opcode::OpTypeSampledImage:
851 return processSampledImageType(operands);
852 case spirv::Opcode::OpTypeRuntimeArray:
853 return processRuntimeArrayType(operands);
854 case spirv::Opcode::OpTypeStruct:
855 return processStructType(operands);
856 case spirv::Opcode::OpTypeMatrix:
857 return processMatrixType(operands);
859 return emitError(unknownLoc,
"unhandled type instruction");
866 if (operands.size() != 3)
867 return emitError(unknownLoc,
"OpTypePointer must have two parameters");
869 auto pointeeType =
getType(operands[2]);
871 return emitError(unknownLoc,
"unknown OpTypePointer pointee type <id> ")
874 uint32_t typePointerID = operands[0];
875 auto storageClass =
static_cast<spirv::StorageClass
>(operands[1]);
878 for (
auto *deferredStructIt = std::begin(deferredStructTypesInfos);
879 deferredStructIt != std::end(deferredStructTypesInfos);) {
880 for (
auto *unresolvedMemberIt =
881 std::begin(deferredStructIt->unresolvedMemberTypes);
882 unresolvedMemberIt !=
883 std::end(deferredStructIt->unresolvedMemberTypes);) {
884 if (unresolvedMemberIt->first == typePointerID) {
888 deferredStructIt->memberTypes[unresolvedMemberIt->second] =
889 typeMap[typePointerID];
891 deferredStructIt->unresolvedMemberTypes.erase(unresolvedMemberIt);
893 ++unresolvedMemberIt;
897 if (deferredStructIt->unresolvedMemberTypes.empty()) {
899 auto structType = deferredStructIt->deferredStructType;
901 assert(structType &&
"expected a spirv::StructType");
902 assert(structType.isIdentified() &&
"expected an indentified struct");
904 if (failed(structType.trySetBody(
905 deferredStructIt->memberTypes, deferredStructIt->offsetInfo,
906 deferredStructIt->memberDecorationsInfo)))
909 deferredStructIt = deferredStructTypesInfos.erase(deferredStructIt);
920 if (operands.size() != 3) {
922 "OpTypeArray must have element type and count parameters");
927 return emitError(unknownLoc,
"OpTypeArray references undefined <id> ")
933 auto countInfo = getConstant(operands[2]);
935 return emitError(unknownLoc,
"OpTypeArray count <id> ")
936 << operands[2] <<
"can only come from normal constant right now";
939 if (
auto intVal = dyn_cast<IntegerAttr>(countInfo->first)) {
940 count = intVal.getValue().getZExtValue();
942 return emitError(unknownLoc,
"OpTypeArray count must come from a "
943 "scalar integer constant instruction");
947 elementTy, count, typeDecorations.lookup(operands[0]));
953 assert(!operands.empty() &&
"No operands for processing function type");
954 if (operands.size() == 1) {
955 return emitError(unknownLoc,
"missing return type for OpTypeFunction");
957 auto returnType =
getType(operands[1]);
959 return emitError(unknownLoc,
"unknown return type in OpTypeFunction");
962 for (
size_t i = 2, e = operands.size(); i < e; ++i) {
963 auto ty =
getType(operands[i]);
965 return emitError(unknownLoc,
"unknown argument type in OpTypeFunction");
967 argTypes.push_back(ty);
970 if (!isVoidType(returnType)) {
977 LogicalResult spirv::Deserializer::processCooperativeMatrixTypeKHR(
979 if (operands.size() != 6) {
981 "OpTypeCooperativeMatrixKHR must have element type, "
982 "scope, row and column parameters, and use");
988 "OpTypeCooperativeMatrixKHR references undefined <id> ")
992 std::optional<spirv::Scope> scope =
993 spirv::symbolizeScope(getConstantInt(operands[2]).getInt());
997 "OpTypeCooperativeMatrixKHR references undefined scope <id> ")
1001 unsigned rows = getConstantInt(operands[3]).getInt();
1002 unsigned columns = getConstantInt(operands[4]).getInt();
1004 std::optional<spirv::CooperativeMatrixUseKHR> use =
1005 spirv::symbolizeCooperativeMatrixUseKHR(
1006 getConstantInt(operands[5]).getInt());
1010 "OpTypeCooperativeMatrixKHR references undefined use <id> ")
1014 typeMap[operands[0]] =
1021 if (operands.size() != 6) {
1022 return emitError(unknownLoc,
"OpTypeJointMatrix must have element "
1023 "type and row x column parameters");
1028 return emitError(unknownLoc,
"OpTypeJointMatrix references undefined <id> ")
1032 auto scope = spirv::symbolizeScope(getConstantInt(operands[5]).getInt());
1035 "OpTypeJointMatrix references undefined scope <id> ")
1039 spirv::symbolizeMatrixLayout(getConstantInt(operands[4]).getInt());
1040 if (!matrixLayout) {
1042 "OpTypeJointMatrix references undefined scope <id> ")
1045 unsigned rows = getConstantInt(operands[2]).getInt();
1046 unsigned columns = getConstantInt(operands[3]).getInt();
1049 elementTy, scope.value(),
rows, columns, matrixLayout.value());
1055 if (operands.size() != 2) {
1056 return emitError(unknownLoc,
"OpTypeRuntimeArray must have two operands");
1061 "OpTypeRuntimeArray references undefined <id> ")
1065 memberType, typeDecorations.lookup(operands[0]));
1073 if (operands.empty()) {
1074 return emitError(unknownLoc,
"OpTypeStruct must have at least result <id>");
1077 if (operands.size() == 1) {
1079 typeMap[operands[0]] =
1088 for (
auto op : llvm::drop_begin(operands, 1)) {
1090 bool typeForwardPtr = (typeForwardPointerIDs.count(op) != 0);
1092 if (!memberType && !typeForwardPtr)
1093 return emitError(unknownLoc,
"OpTypeStruct references undefined <id> ")
1097 unresolvedMemberTypes.emplace_back(op, memberTypes.size());
1099 memberTypes.push_back(memberType);
1104 if (memberDecorationMap.count(operands[0])) {
1105 auto &allMemberDecorations = memberDecorationMap[operands[0]];
1106 for (
auto memberIndex : llvm::seq<uint32_t>(0, memberTypes.size())) {
1107 if (allMemberDecorations.count(memberIndex)) {
1108 for (
auto &memberDecoration : allMemberDecorations[memberIndex]) {
1110 if (memberDecoration.first == spirv::Decoration::Offset) {
1112 if (offsetInfo.empty()) {
1113 offsetInfo.resize(memberTypes.size());
1115 offsetInfo[memberIndex] = memberDecoration.second[0];
1117 if (!memberDecoration.second.empty()) {
1118 memberDecorationsInfo.emplace_back(memberIndex, 1,
1119 memberDecoration.first,
1120 memberDecoration.second[0]);
1122 memberDecorationsInfo.emplace_back(memberIndex, 0,
1123 memberDecoration.first, 0);
1131 uint32_t structID = operands[0];
1132 std::string structIdentifier = nameMap.lookup(structID).str();
1134 if (structIdentifier.empty()) {
1135 assert(unresolvedMemberTypes.empty() &&
1136 "didn't expect unresolved member types");
1141 typeMap[structID] = structTy;
1143 if (!unresolvedMemberTypes.empty())
1144 deferredStructTypesInfos.push_back({structTy, unresolvedMemberTypes,
1145 memberTypes, offsetInfo,
1146 memberDecorationsInfo});
1147 else if (failed(structTy.trySetBody(memberTypes, offsetInfo,
1148 memberDecorationsInfo)))
1159 if (operands.size() != 3) {
1161 return emitError(unknownLoc,
"OpTypeMatrix must have 3 operands"
1162 " (result_id, column_type, and column_count)");
1168 "OpTypeMatrix references undefined column type.")
1172 uint32_t colsCount = operands[2];
1179 if (operands.size() != 2)
1181 "OpTypeForwardPointer instruction must have two operands");
1183 typeForwardPointerIDs.insert(operands[0]);
1193 if (operands.size() != 8)
1196 "OpTypeImage with non-eight operands are not supported yet");
1200 return emitError(unknownLoc,
"OpTypeImage references undefined <id>: ")
1203 auto dim = spirv::symbolizeDim(operands[2]);
1205 return emitError(unknownLoc,
"unknown Dim for OpTypeImage: ")
1208 auto depthInfo = spirv::symbolizeImageDepthInfo(operands[3]);
1210 return emitError(unknownLoc,
"unknown Depth for OpTypeImage: ")
1213 auto arrayedInfo = spirv::symbolizeImageArrayedInfo(operands[4]);
1215 return emitError(unknownLoc,
"unknown Arrayed for OpTypeImage: ")
1218 auto samplingInfo = spirv::symbolizeImageSamplingInfo(operands[5]);
1220 return emitError(unknownLoc,
"unknown MS for OpTypeImage: ") << operands[5];
1222 auto samplerUseInfo = spirv::symbolizeImageSamplerUseInfo(operands[6]);
1223 if (!samplerUseInfo)
1224 return emitError(unknownLoc,
"unknown Sampled for OpTypeImage: ")
1227 auto format = spirv::symbolizeImageFormat(operands[7]);
1229 return emitError(unknownLoc,
"unknown Format for OpTypeImage: ")
1233 elementTy, dim.value(), depthInfo.value(), arrayedInfo.value(),
1234 samplingInfo.value(), samplerUseInfo.value(), format.value());
1240 if (operands.size() != 2)
1241 return emitError(unknownLoc,
"OpTypeSampledImage must have two operands");
1246 "OpTypeSampledImage references undefined <id>: ")
1259 StringRef opname = isSpec ?
"OpSpecConstant" :
"OpConstant";
1261 if (operands.size() < 2) {
1263 << opname <<
" must have type <id> and result <id>";
1265 if (operands.size() < 3) {
1267 << opname <<
" must have at least 1 more parameter";
1272 return emitError(unknownLoc,
"undefined result type from <id> ")
1276 auto checkOperandSizeForBitwidth = [&](
unsigned bitwidth) -> LogicalResult {
1277 if (bitwidth == 64) {
1278 if (operands.size() == 4) {
1282 << opname <<
" should have 2 parameters for 64-bit values";
1284 if (bitwidth <= 32) {
1285 if (operands.size() == 3) {
1291 <<
" should have 1 parameter for values with no more than 32 bits";
1293 return emitError(unknownLoc,
"unsupported OpConstant bitwidth: ")
1297 auto resultID = operands[1];
1299 if (
auto intType = dyn_cast<IntegerType>(resultType)) {
1300 auto bitwidth = intType.getWidth();
1301 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1306 if (bitwidth == 64) {
1313 } words = {operands[2], operands[3]};
1314 value = APInt(64, llvm::bit_cast<uint64_t>(words),
true);
1315 }
else if (bitwidth <= 32) {
1316 value = APInt(bitwidth, operands[2],
true);
1319 auto attr = opBuilder.getIntegerAttr(intType, value);
1322 createSpecConstant(unknownLoc, resultID, attr);
1326 constantMap.try_emplace(resultID, attr, intType);
1332 if (
auto floatType = dyn_cast<FloatType>(resultType)) {
1333 auto bitwidth = floatType.getWidth();
1334 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1339 if (floatType.isF64()) {
1346 } words = {operands[2], operands[3]};
1347 value = APFloat(llvm::bit_cast<double>(words));
1348 }
else if (floatType.isF32()) {
1349 value = APFloat(llvm::bit_cast<float>(operands[2]));
1350 }
else if (floatType.isF16()) {
1351 APInt data(16, operands[2]);
1352 value = APFloat(APFloat::IEEEhalf(), data);
1355 auto attr = opBuilder.getFloatAttr(floatType, value);
1357 createSpecConstant(unknownLoc, resultID, attr);
1361 constantMap.try_emplace(resultID, attr, floatType);
1367 return emitError(unknownLoc,
"OpConstant can only generate values of "
1368 "scalar integer or floating-point type");
1371 LogicalResult spirv::Deserializer::processConstantBool(
1373 if (operands.size() != 2) {
1375 << (isSpec ?
"Spec" :
"") <<
"Constant"
1376 << (isTrue ?
"True" :
"False")
1377 <<
" must have type <id> and result <id>";
1380 auto attr = opBuilder.getBoolAttr(isTrue);
1381 auto resultID = operands[1];
1383 createSpecConstant(unknownLoc, resultID, attr);
1387 constantMap.try_emplace(resultID, attr, opBuilder.getI1Type());
1395 if (operands.size() < 2) {
1397 "OpConstantComposite must have type <id> and result <id>");
1399 if (operands.size() < 3) {
1401 "OpConstantComposite must have at least 1 parameter");
1406 return emitError(unknownLoc,
"undefined result type from <id> ")
1411 elements.reserve(operands.size() - 2);
1412 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
1413 auto elementInfo = getConstant(operands[i]);
1415 return emitError(unknownLoc,
"OpConstantComposite component <id> ")
1416 << operands[i] <<
" must come from a normal constant";
1418 elements.push_back(elementInfo->first);
1421 auto resultID = operands[1];
1422 if (
auto vectorType = dyn_cast<VectorType>(resultType)) {
1426 constantMap.try_emplace(resultID, attr, resultType);
1427 }
else if (
auto arrayType = dyn_cast<spirv::ArrayType>(resultType)) {
1428 auto attr = opBuilder.getArrayAttr(elements);
1429 constantMap.try_emplace(resultID, attr, resultType);
1431 return emitError(unknownLoc,
"unsupported OpConstantComposite type: ")
1440 if (operands.size() < 2) {
1442 "OpConstantComposite must have type <id> and result <id>");
1444 if (operands.size() < 3) {
1446 "OpConstantComposite must have at least 1 parameter");
1451 return emitError(unknownLoc,
"undefined result type from <id> ")
1455 auto resultID = operands[1];
1456 auto symName = opBuilder.getStringAttr(getSpecConstantSymbol(resultID));
1459 elements.reserve(operands.size() - 2);
1460 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
1461 auto elementInfo = getSpecConstant(operands[i]);
1465 auto op = opBuilder.
create<spirv::SpecConstantCompositeOp>(
1467 opBuilder.getArrayAttr(elements));
1468 specConstCompositeMap[resultID] = op;
1475 if (operands.size() < 3)
1476 return emitError(unknownLoc,
"OpConstantOperation must have type <id>, "
1477 "result <id>, and operand opcode");
1479 uint32_t resultTypeID = operands[0];
1482 return emitError(unknownLoc,
"undefined result type from <id> ")
1485 uint32_t resultID = operands[1];
1486 spirv::Opcode enclosedOpcode =
static_cast<spirv::Opcode
>(operands[2]);
1487 auto emplaceResult = specConstOperationMap.try_emplace(
1489 SpecConstOperationMaterializationInfo{
1490 enclosedOpcode, resultTypeID,
1493 if (!emplaceResult.second)
1494 return emitError(unknownLoc,
"value with <id>: ")
1495 << resultID <<
" is probably defined before.";
1500 Value spirv::Deserializer::materializeSpecConstantOperation(
1501 uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID,
1517 llvm::SaveAndRestore valueMapGuard(valueMap, newValueMap);
1518 constexpr uint32_t fakeID =
static_cast<uint32_t
>(-3);
1521 enclosedOpResultTypeAndOperands.push_back(resultTypeID);
1522 enclosedOpResultTypeAndOperands.push_back(fakeID);
1523 enclosedOpResultTypeAndOperands.append(enclosedOpOperands.begin(),
1524 enclosedOpOperands.end());
1531 processInstruction(enclosedOpcode, enclosedOpResultTypeAndOperands)))
1538 auto loc = createFileLineColLoc(opBuilder);
1539 auto specConstOperationOp =
1540 opBuilder.create<spirv::SpecConstantOperationOp>(loc, resultType);
1542 Region &body = specConstOperationOp.getBody();
1544 body.
getBlocks().splice(body.end(), curBlock->getParent()->getBlocks(),
1551 opBuilder.setInsertionPointToEnd(&block);
1553 opBuilder.create<spirv::YieldOp>(loc, block.
front().
getResult(0));
1554 return specConstOperationOp.getResult();
1559 if (operands.size() != 2) {
1561 "OpConstantNull must have type <id> and result <id>");
1566 return emitError(unknownLoc,
"undefined result type from <id> ")
1570 auto resultID = operands[1];
1571 if (resultType.
isIntOrFloat() || isa<VectorType>(resultType)) {
1572 auto attr = opBuilder.getZeroAttr(resultType);
1575 constantMap.try_emplace(resultID, attr, resultType);
1579 return emitError(unknownLoc,
"unsupported OpConstantNull type: ")
1587 Block *spirv::Deserializer::getOrCreateBlock(uint32_t
id) {
1588 if (
auto *block = getBlock(
id)) {
1589 LLVM_DEBUG(logger.startLine() <<
"[block] got exiting block for id = " <<
id
1590 <<
" @ " << block <<
"\n");
1597 auto *block = curFunction->addBlock();
1598 LLVM_DEBUG(logger.startLine() <<
"[block] created block for id = " <<
id
1599 <<
" @ " << block <<
"\n");
1600 return blockMap[id] = block;
1605 return emitError(unknownLoc,
"OpBranch must appear inside a block");
1608 if (operands.size() != 1) {
1609 return emitError(unknownLoc,
"OpBranch must take exactly one target label");
1612 auto *target = getOrCreateBlock(operands[0]);
1613 auto loc = createFileLineColLoc(opBuilder);
1617 opBuilder.create<spirv::BranchOp>(loc, target);
1627 "OpBranchConditional must appear inside a block");
1630 if (operands.size() != 3 && operands.size() != 5) {
1632 "OpBranchConditional must have condition, true label, "
1633 "false label, and optionally two branch weights");
1636 auto condition = getValue(operands[0]);
1637 auto *trueBlock = getOrCreateBlock(operands[1]);
1638 auto *falseBlock = getOrCreateBlock(operands[2]);
1640 std::optional<std::pair<uint32_t, uint32_t>> weights;
1641 if (operands.size() == 5) {
1642 weights = std::make_pair(operands[3], operands[4]);
1647 auto loc = createFileLineColLoc(opBuilder);
1648 opBuilder.create<spirv::BranchConditionalOp>(
1649 loc, condition, trueBlock,
1659 return emitError(unknownLoc,
"OpLabel must appear inside a function");
1662 if (operands.size() != 1) {
1663 return emitError(unknownLoc,
"OpLabel should only have result <id>");
1666 auto labelID = operands[0];
1668 auto *block = getOrCreateBlock(labelID);
1669 LLVM_DEBUG(logger.startLine()
1670 <<
"[block] populating block " << block <<
"\n");
1672 assert(block->
empty() &&
"re-deserialize the same block!");
1674 opBuilder.setInsertionPointToStart(block);
1675 blockMap[labelID] = curBlock = block;
1683 return emitError(unknownLoc,
"OpSelectionMerge must appear in a block");
1686 if (operands.size() < 2) {
1689 "OpSelectionMerge must specify merge target and selection control");
1692 auto *mergeBlock = getOrCreateBlock(operands[0]);
1693 auto loc = createFileLineColLoc(opBuilder);
1694 auto selectionControl = operands[1];
1696 if (!blockMergeInfo.try_emplace(curBlock, loc, selectionControl, mergeBlock)
1700 "a block cannot have more than one OpSelectionMerge instruction");
1709 return emitError(unknownLoc,
"OpLoopMerge must appear in a block");
1712 if (operands.size() < 3) {
1713 return emitError(unknownLoc,
"OpLoopMerge must specify merge target, "
1714 "continue target and loop control");
1717 auto *mergeBlock = getOrCreateBlock(operands[0]);
1718 auto *continueBlock = getOrCreateBlock(operands[1]);
1719 auto loc = createFileLineColLoc(opBuilder);
1720 uint32_t loopControl = operands[2];
1723 .try_emplace(curBlock, loc, loopControl, mergeBlock, continueBlock)
1727 "a block cannot have more than one OpLoopMerge instruction");
1735 return emitError(unknownLoc,
"OpPhi must appear in a block");
1738 if (operands.size() < 4) {
1739 return emitError(unknownLoc,
"OpPhi must specify result type, result <id>, "
1740 "and variable-parent pairs");
1745 BlockArgument blockArg = curBlock->addArgument(blockArgType, unknownLoc);
1746 valueMap[operands[1]] = blockArg;
1747 LLVM_DEBUG(logger.startLine()
1748 <<
"[phi] created block argument " << blockArg
1749 <<
" id = " << operands[1] <<
" of type " << blockArgType <<
"\n");
1753 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
1754 uint32_t value = operands[i];
1755 Block *predecessor = getOrCreateBlock(operands[i + 1]);
1756 std::pair<Block *, Block *> predecessorTargetPair{predecessor, curBlock};
1757 blockPhiInfo[predecessorTargetPair].
push_back(value);
1758 LLVM_DEBUG(logger.startLine() <<
"[phi] predecessor @ " << predecessor
1759 <<
" with arg id = " << value <<
"\n");
1768 class ControlFlowStructurizer {
1771 ControlFlowStructurizer(
Location loc, uint32_t control,
1774 llvm::ScopedPrinter &logger)
1775 : location(loc), control(control), blockMergeInfo(mergeInfo),
1776 headerBlock(header), mergeBlock(merge), continueBlock(cont),
1779 ControlFlowStructurizer(
Location loc, uint32_t control,
1782 : location(loc), control(control), blockMergeInfo(mergeInfo),
1783 headerBlock(header), mergeBlock(merge), continueBlock(cont) {}
1793 LogicalResult structurize();
1798 spirv::SelectionOp createSelectionOp(uint32_t selectionControl);
1801 spirv::LoopOp createLoopOp(uint32_t loopControl);
1804 void collectBlocksInConstruct();
1813 Block *continueBlock;
1819 llvm::ScopedPrinter &logger;
1825 ControlFlowStructurizer::createSelectionOp(uint32_t selectionControl) {
1828 OpBuilder builder(&mergeBlock->front());
1830 auto control =
static_cast<spirv::SelectionControl
>(selectionControl);
1831 auto selectionOp = builder.create<spirv::SelectionOp>(location, control);
1832 selectionOp.addMergeBlock(builder);
1837 spirv::LoopOp ControlFlowStructurizer::createLoopOp(uint32_t loopControl) {
1840 OpBuilder builder(&mergeBlock->front());
1842 auto control =
static_cast<spirv::LoopControl
>(loopControl);
1843 auto loopOp = builder.create<spirv::LoopOp>(location, control);
1844 loopOp.addEntryAndMergeBlock(builder);
1849 void ControlFlowStructurizer::collectBlocksInConstruct() {
1850 assert(constructBlocks.empty() &&
"expected empty constructBlocks");
1853 constructBlocks.insert(headerBlock);
1857 for (
unsigned i = 0; i < constructBlocks.size(); ++i) {
1858 for (
auto *successor : constructBlocks[i]->getSuccessors())
1859 if (successor != mergeBlock)
1860 constructBlocks.insert(successor);
1864 LogicalResult ControlFlowStructurizer::structurize() {
1866 bool isLoop = continueBlock !=
nullptr;
1868 if (
auto loopOp = createLoopOp(control))
1869 op = loopOp.getOperation();
1871 if (
auto selectionOp = createSelectionOp(control))
1872 op = selectionOp.getOperation();
1881 mapper.
map(mergeBlock, &body.
back());
1883 collectBlocksInConstruct();
1905 for (
auto *block : constructBlocks) {
1908 auto *newBlock = builder.createBlock(&body.
back());
1909 mapper.
map(block, newBlock);
1910 LLVM_DEBUG(logger.startLine() <<
"[cf] cloned block " << newBlock
1911 <<
" from block " << block <<
"\n");
1915 newBlock->addArgument(blockArg.
getType(), blockArg.
getLoc());
1916 mapper.
map(blockArg, newArg);
1917 LLVM_DEBUG(logger.startLine() <<
"[cf] remapped block argument "
1918 << blockArg <<
" to " << newArg <<
"\n");
1921 LLVM_DEBUG(logger.startLine()
1922 <<
"[cf] block " << block <<
" is a function entry block\n");
1925 for (
auto &op : *block)
1926 newBlock->push_back(op.
clone(mapper));
1930 auto remapOperands = [&](
Operation *op) {
1933 operand.set(mappedOp);
1936 succOp.set(mappedOp);
1938 for (
auto &block : body)
1939 block.walk(remapOperands);
1947 headerBlock->replaceAllUsesWith(mergeBlock);
1950 logger.startLine() <<
"[cf] after cloning and fixing references:\n";
1951 headerBlock->getParentOp()->
print(logger.getOStream());
1952 logger.startLine() <<
"\n";
1956 if (!mergeBlock->args_empty()) {
1957 return mergeBlock->getParentOp()->emitError(
1958 "OpPhi in loop merge block unsupported");
1965 mergeBlock->addArgument(blockArg.
getType(), blockArg.
getLoc());
1970 if (!headerBlock->args_empty())
1971 blockArgs = {mergeBlock->args_begin(), mergeBlock->args_end()};
1975 builder.setInsertionPointToEnd(&body.front());
1976 builder.create<spirv::BranchOp>(location, mapper.
lookupOrNull(headerBlock),
1982 LLVM_DEBUG(logger.startLine() <<
"[cf] cleaning up blocks after clone\n");
1985 for (
auto *block : constructBlocks)
1986 block->dropAllReferences();
1993 for (
auto *block : constructBlocks) {
1997 "failed control flow structurization: it has uses outside of the "
1998 "enclosing selection/loop construct");
2002 for (
auto *block : constructBlocks) {
2011 auto it = blockMergeInfo.find(block);
2012 if (it != blockMergeInfo.end()) {
2018 return emitError(loc,
"failed control flow structurization: nested "
2019 "loop header block should be remapped!");
2021 Block *newContinue = it->second.continueBlock;
2025 return emitError(loc,
"failed control flow structurization: nested "
2026 "loop continue block should be remapped!");
2029 Block *newMerge = it->second.mergeBlock;
2031 newMerge = mappedTo;
2035 blockMergeInfo.
erase(it);
2036 blockMergeInfo.try_emplace(newHeader, loc, it->second.control, newMerge,
2045 LLVM_DEBUG(logger.startLine() <<
"[cf] changing entry block " << block
2046 <<
" to only contain a spirv.Branch op\n");
2050 builder.setInsertionPointToEnd(block);
2051 builder.create<spirv::BranchOp>(location, mergeBlock);
2053 LLVM_DEBUG(logger.startLine() <<
"[cf] erasing block " << block <<
"\n");
2058 LLVM_DEBUG(logger.startLine()
2059 <<
"[cf] after structurizing construct with header block "
2060 << headerBlock <<
":\n"
2066 LogicalResult spirv::Deserializer::wireUpBlockArgument() {
2069 <<
"//----- [phi] start wiring up block arguments -----//\n";
2075 for (
const auto &info : blockPhiInfo) {
2076 Block *block = info.first.first;
2077 Block *target = info.first.second;
2078 const BlockPhiInfo &phiInfo = info.second;
2080 logger.startLine() <<
"[phi] block " << block <<
"\n";
2081 logger.startLine() <<
"[phi] before creating block argument:\n";
2083 logger.startLine() <<
"\n";
2089 opBuilder.setInsertionPoint(op);
2092 blockArgs.reserve(phiInfo.size());
2093 for (uint32_t valueId : phiInfo) {
2094 if (
Value value = getValue(valueId)) {
2095 blockArgs.push_back(value);
2096 LLVM_DEBUG(logger.startLine() <<
"[phi] block argument " << value
2097 <<
" id = " << valueId <<
"\n");
2099 return emitError(unknownLoc,
"OpPhi references undefined value!");
2103 if (
auto branchOp = dyn_cast<spirv::BranchOp>(op)) {
2105 opBuilder.create<spirv::BranchOp>(branchOp.getLoc(), branchOp.getTarget(),
2108 }
else if (
auto branchCondOp = dyn_cast<spirv::BranchConditionalOp>(op)) {
2109 assert((branchCondOp.getTrueBlock() == target ||
2110 branchCondOp.getFalseBlock() == target) &&
2111 "expected target to be either the true or false target");
2112 if (target == branchCondOp.getTrueTarget())
2113 opBuilder.create<spirv::BranchConditionalOp>(
2114 branchCondOp.getLoc(), branchCondOp.getCondition(), blockArgs,
2115 branchCondOp.getFalseBlockArguments(),
2116 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueTarget(),
2117 branchCondOp.getFalseTarget());
2119 opBuilder.create<spirv::BranchConditionalOp>(
2120 branchCondOp.getLoc(), branchCondOp.getCondition(),
2121 branchCondOp.getTrueBlockArguments(), blockArgs,
2122 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueBlock(),
2123 branchCondOp.getFalseBlock());
2125 branchCondOp.erase();
2127 return emitError(unknownLoc,
"unimplemented terminator for Phi creation");
2131 logger.startLine() <<
"[phi] after creating block argument:\n";
2133 logger.startLine() <<
"\n";
2136 blockPhiInfo.clear();
2141 <<
"//--- [phi] completed wiring up block arguments ---//\n";
2146 LogicalResult spirv::Deserializer::structurizeControlFlow() {
2149 <<
"//----- [cf] start structurizing control flow -----//\n";
2153 while (!blockMergeInfo.empty()) {
2154 Block *headerBlock = blockMergeInfo.
begin()->first;
2155 BlockMergeInfo mergeInfo = blockMergeInfo.begin()->second;
2158 logger.startLine() <<
"[cf] header block " << headerBlock <<
":\n";
2159 headerBlock->
print(logger.getOStream());
2160 logger.startLine() <<
"\n";
2163 auto *mergeBlock = mergeInfo.mergeBlock;
2164 assert(mergeBlock &&
"merge block cannot be nullptr");
2165 if (!mergeBlock->args_empty())
2166 return emitError(unknownLoc,
"OpPhi in loop merge block unimplemented");
2168 logger.startLine() <<
"[cf] merge block " << mergeBlock <<
":\n";
2169 mergeBlock->print(logger.getOStream());
2170 logger.startLine() <<
"\n";
2173 auto *continueBlock = mergeInfo.continueBlock;
2174 LLVM_DEBUG(
if (continueBlock) {
2175 logger.startLine() <<
"[cf] continue block " << continueBlock <<
":\n";
2176 continueBlock->print(logger.getOStream());
2177 logger.startLine() <<
"\n";
2181 blockMergeInfo.erase(blockMergeInfo.begin());
2182 ControlFlowStructurizer structurizer(mergeInfo.loc, mergeInfo.control,
2183 blockMergeInfo, headerBlock,
2184 mergeBlock, continueBlock
2190 if (failed(structurizer.structurize()))
2197 <<
"//--- [cf] completed structurizing control flow ---//\n";
2210 auto fileName = debugInfoMap.lookup(debugLine->fileID).str();
2211 if (fileName.empty())
2212 fileName =
"<unknown>";
2224 if (operands.size() != 3)
2225 return emitError(unknownLoc,
"OpLine must have 3 operands");
2226 debugLine = DebugLine{operands[0], operands[1], operands[2]};
2230 void spirv::Deserializer::clearDebugLine() { debugLine = std::nullopt; }
2234 if (operands.size() < 2)
2235 return emitError(unknownLoc,
"OpString needs at least 2 operands");
2237 if (!debugInfoMap.lookup(operands[0]).empty())
2239 "duplicate debug string found for result <id> ")
2242 unsigned wordIndex = 1;
2244 if (wordIndex != operands.size())
2246 "unexpected trailing words in OpString instruction");
static bool isLoop(Operation *op)
Returns true if the given operation represents a loop by testing whether it implements the LoopLikeOp...
static bool isFnEntryBlock(Block *block)
Returns true if the given block is a function entry block.
#define MIN_VERSION_CASE(v)
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Location getLoc() const
Return the location for this argument.
Block represents an ordered list of Operations.
void erase()
Unlink this Block from its parent region and delete it.
Block * splitBlock(iterator splitBefore)
Split the block into two blocks before the specified operation or iterator.
Operation * getTerminator()
Get the terminator operation of this block.
void print(raw_ostream &os)
BlockArgListType getArguments()
bool isEntryBlock()
Return if this block is the entry block in the parent region.
void push_back(Operation *op)
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
StringAttr getStringAttr(const Twine &bytes)
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
A symbol reference with a reference path containing a single element.
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
auto lookupOrNull(T from) const
Lookup a mapped value within the map.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttribute represents a combination of a name and an Attribute value.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
bool use_empty()
Returns true if this operation has no uses.
Operation * clone(IRMapping &mapper, CloneOptions options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, OpaqueProperties properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
MutableArrayRef< BlockOperand > getBlockOperands()
MutableArrayRef< OpOperand > getOpOperands()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
BlockListType & getBlocks()
BlockListType::iterator iterator
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
static ArrayType get(Type elementType, unsigned elementCount)
static CooperativeMatrixType get(Type elementType, uint32_t rows, uint32_t columns, Scope scope, CooperativeMatrixUseKHR use)
LogicalResult deserialize()
Deserializes the remembered SPIR-V binary module.
Deserializer(ArrayRef< uint32_t > binary, MLIRContext *context)
Creates a deserializer for the given SPIR-V binary module.
OwningOpRef< spirv::ModuleOp > collect()
Collects the final SPIR-V ModuleOp.
static ImageType get(Type elementType, Dim dim, ImageDepthInfo depth=ImageDepthInfo::DepthUnknown, ImageArrayedInfo arrayed=ImageArrayedInfo::NonArrayed, ImageSamplingInfo samplingInfo=ImageSamplingInfo::SingleSampled, ImageSamplerUseInfo samplerUse=ImageSamplerUseInfo::SamplerUnknown, ImageFormat format=ImageFormat::Unknown)
static JointMatrixINTELType get(Type elementType, Scope scope, unsigned rows, unsigned columns, MatrixLayout matrixLayout)
static MatrixType get(Type columnType, uint32_t columnCount)
static PointerType get(Type pointeeType, StorageClass storageClass)
static RuntimeArrayType get(Type elementType)
static SampledImageType get(Type imageType)
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
static StructType getEmpty(MLIRContext *context, StringRef identifier="")
Construct a (possibly identified) StructType with no members.
static StructType get(ArrayRef< Type > memberTypes, ArrayRef< OffsetInfo > offsetInfo={}, ArrayRef< MemberDecorationInfo > memberDecorations={})
Construct a literal StructType with at least one member.
static VerCapExtAttr get(Version version, ArrayRef< Capability > capabilities, ArrayRef< Extension > extensions, MLIRContext *context)
Gets a VerCapExtAttr instance.
Include the generated interface declarations.
constexpr uint32_t kMagicNumber
SPIR-V magic number.
StringRef decodeStringLiteral(ArrayRef< uint32_t > words, unsigned &wordIndex)
Decodes a string literal in words starting at wordIndex.
constexpr unsigned kHeaderWordCount
SPIR-V binary header word count.
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
static std::string debugString(T &&op)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
This represents an operation in an abstracted form, suitable for use with the builder APIs.