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"
36#define DEBUG_TYPE "spirv-deserialization"
45 isa_and_nonnull<spirv::FuncOp>(block->
getParentOp());
64 : binary(binary), context(context), unknownLoc(UnknownLoc::
get(context)),
65 module(createModuleOp()), opBuilder(module->getRegion()),
options(
options)
73LogicalResult spirv::Deserializer::deserialize() {
77 <<
"//+++---------- start deserialization ----------+++//\n";
80 if (
failed(processHeader()))
83 spirv::Opcode opcode = spirv::Opcode::OpNop;
84 ArrayRef<uint32_t> operands;
85 auto binarySize = binary.size();
86 while (curOffset < binarySize) {
96 assert(curOffset == binarySize &&
97 "deserializer should never index beyond the binary end");
99 for (
auto &deferred : deferredInstructions) {
105 if (
failed(resolveDeferredIdDecorations()))
110 LLVM_DEBUG(logger.startLine()
111 <<
"//+++-------- completed deserialization --------+++//\n");
115OwningOpRef<spirv::ModuleOp> spirv::Deserializer::collect() {
116 return std::move(module);
123OwningOpRef<spirv::ModuleOp> spirv::Deserializer::createModuleOp() {
124 OpBuilder builder(context);
125 OperationState state(unknownLoc, spirv::ModuleOp::getOperationName());
126 spirv::ModuleOp::build(builder, state);
130LogicalResult spirv::Deserializer::processHeader() {
133 "SPIR-V binary module must have a 5-word header");
136 return emitError(unknownLoc,
"incorrect magic number");
139 uint32_t majorVersion = (binary[1] << 8) >> 24;
140 uint32_t minorVersion = (binary[1] << 16) >> 24;
141 if (majorVersion == 1) {
142 switch (minorVersion) {
143#define MIN_VERSION_CASE(v) \
145 version = spirv::Version::V_1_##v; \
155#undef MIN_VERSION_CASE
157 return emitError(unknownLoc,
"unsupported SPIR-V minor version: ")
161 return emitError(unknownLoc,
"unsupported SPIR-V major version: ")
171spirv::Deserializer::processCapability(ArrayRef<uint32_t> operands) {
172 if (operands.size() != 1)
173 return emitError(unknownLoc,
"OpCapability must have one parameter");
175 auto cap = spirv::symbolizeCapability(operands[0]);
177 return emitError(unknownLoc,
"unknown capability: ") << operands[0];
179 capabilities.insert(*cap);
183LogicalResult spirv::Deserializer::processExtension(ArrayRef<uint32_t> words) {
187 "OpExtension must have a literal string for the extension name");
190 unsigned wordIndex = 0;
192 if (wordIndex != words.size())
194 "unexpected trailing words in OpExtension instruction");
195 auto ext = spirv::symbolizeExtension(extName);
197 return emitError(unknownLoc,
"unknown extension: ") << extName;
199 extensions.insert(*ext);
204spirv::Deserializer::processExtInstImport(ArrayRef<uint32_t> words) {
205 if (words.size() < 2) {
207 "OpExtInstImport must have a result <id> and a literal "
208 "string for the extended instruction set name");
211 unsigned wordIndex = 1;
213 if (wordIndex != words.size()) {
215 "unexpected trailing words in OpExtInstImport");
220void spirv::Deserializer::attachVCETriple() {
221 module->setVceTripleAttr(spirv::VerCapExtAttr::get(
222 version, capabilities.getArrayRef(), extensions.getArrayRef(), context));
226spirv::Deserializer::processMemoryModel(ArrayRef<uint32_t> operands) {
227 if (operands.size() != 2)
228 return emitError(unknownLoc,
"OpMemoryModel must have two operands");
230 module->setAddressingModel(
231 static_cast<spirv::AddressingModel>(operands.front()));
233 module->setMemoryModel(static_cast<spirv::MemoryModel>(operands.back()));
238template <
typename AttrTy,
typename EnumAttrTy,
typename EnumTy>
242 StringAttr symbol, StringRef decorationName, StringRef cacheControlKind) {
243 if (words.size() != 4) {
244 return emitError(loc,
"OpDecorate with ")
245 << decorationName <<
" needs a cache control integer literal and a "
246 << cacheControlKind <<
" cache control literal";
248 unsigned cacheLevel = words[2];
249 auto cacheControlAttr =
static_cast<EnumTy
>(words[3]);
250 auto value = opBuilder.
getAttr<AttrTy>(cacheLevel, cacheControlAttr);
253 dyn_cast_or_null<ArrayAttr>(decorations[words[0]].
get(symbol)))
254 llvm::append_range(attrs, attrList);
255 attrs.push_back(value);
256 decorations[words[0]].set(symbol, opBuilder.
getArrayAttr(attrs));
260LogicalResult spirv::Deserializer::processDecoration(ArrayRef<uint32_t> words) {
264 if (words.size() < 2) {
266 unknownLoc,
"OpDecorate must have at least result <id> and Decoration");
268 auto decorationName =
269 stringifyDecoration(
static_cast<spirv::Decoration
>(words[1]));
270 if (decorationName.empty()) {
271 return emitError(unknownLoc,
"invalid Decoration code : ") << words[1];
273 auto symbol = getSymbolDecoration(decorationName);
274 switch (
static_cast<spirv::Decoration
>(words[1])) {
275 case spirv::Decoration::FPFastMathMode:
276 if (words.size() != 3) {
277 return emitError(unknownLoc,
"OpDecorate with ")
278 << decorationName <<
" needs a single integer literal";
280 decorations[words[0]].set(
281 symbol, FPFastMathModeAttr::get(opBuilder.getContext(),
282 static_cast<FPFastMathMode
>(words[2])));
284 case spirv::Decoration::FPRoundingMode:
285 if (words.size() != 3) {
286 return emitError(unknownLoc,
"OpDecorate with ")
287 << decorationName <<
" needs a single integer literal";
289 decorations[words[0]].set(
290 symbol, FPRoundingModeAttr::get(opBuilder.getContext(),
291 static_cast<FPRoundingMode
>(words[2])));
293 case spirv::Decoration::DescriptorSet:
294 case spirv::Decoration::Binding:
295 case spirv::Decoration::Location:
296 case spirv::Decoration::SpecId:
297 case spirv::Decoration::Index:
298 case spirv::Decoration::Offset:
299 case spirv::Decoration::XfbBuffer:
300 case spirv::Decoration::XfbStride:
301 if (words.size() != 3) {
302 return emitError(unknownLoc,
"OpDecorate with ")
303 << decorationName <<
" needs a single integer literal";
305 decorations[words[0]].set(
306 symbol, opBuilder.getI32IntegerAttr(
static_cast<int32_t
>(words[2])));
308 case spirv::Decoration::BuiltIn:
309 if (words.size() != 3) {
310 return emitError(unknownLoc,
"OpDecorate with ")
311 << decorationName <<
" needs a single integer literal";
313 decorations[words[0]].set(
314 symbol, opBuilder.getStringAttr(
315 stringifyBuiltIn(
static_cast<spirv::BuiltIn
>(words[2]))));
317 case spirv::Decoration::ArrayStride:
318 if (words.size() != 3) {
319 return emitError(unknownLoc,
"OpDecorate with ")
320 << decorationName <<
" needs a single integer literal";
322 typeDecorations[words[0]] = words[2];
324 case spirv::Decoration::LinkageAttributes: {
325 if (words.size() < 4) {
326 return emitError(unknownLoc,
"OpDecorate with ")
328 <<
" needs at least 1 string and 1 integer literal";
336 unsigned wordIndex = 2;
338 auto linkageTypeAttr = opBuilder.getAttr<::mlir::spirv::LinkageTypeAttr>(
339 static_cast<::mlir::spirv::LinkageType
>(words[wordIndex++]));
340 auto linkageAttr = opBuilder.getAttr<::mlir::spirv::LinkageAttributesAttr>(
341 StringAttr::get(context, linkageName), linkageTypeAttr);
342 decorations[words[0]].set(symbol, dyn_cast<Attribute>(linkageAttr));
345 case spirv::Decoration::Aliased:
346 case spirv::Decoration::AliasedPointer:
347 case spirv::Decoration::Block:
348 case spirv::Decoration::BufferBlock:
349 case spirv::Decoration::Flat:
350 case spirv::Decoration::NonReadable:
351 case spirv::Decoration::NonWritable:
352 case spirv::Decoration::NoPerspective:
353 case spirv::Decoration::NoSignedWrap:
354 case spirv::Decoration::NoUnsignedWrap:
355 case spirv::Decoration::RelaxedPrecision:
356 case spirv::Decoration::Restrict:
357 case spirv::Decoration::RestrictPointer:
358 case spirv::Decoration::NoContraction:
359 case spirv::Decoration::Constant:
360 case spirv::Decoration::Invariant:
361 case spirv::Decoration::Patch:
362 case spirv::Decoration::Coherent:
363 case spirv::Decoration::Volatile:
364 if (words.size() != 2) {
365 return emitError(unknownLoc,
"OpDecorate with ")
366 << decorationName <<
" needs a single target <id>";
368 decorations[words[0]].set(symbol, opBuilder.getUnitAttr());
370 case spirv::Decoration::CacheControlLoadINTEL: {
372 CacheControlLoadINTELAttr, LoadCacheControlAttr, LoadCacheControl>(
373 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
379 case spirv::Decoration::CacheControlStoreINTEL: {
381 CacheControlStoreINTELAttr, StoreCacheControlAttr, StoreCacheControl>(
382 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
388 case spirv::Decoration::AlignmentId:
389 case spirv::Decoration::MaxByteOffsetId:
390 case spirv::Decoration::CounterBuffer:
391 if (words.size() != 3) {
392 return emitError(unknownLoc,
"OpDecorateId with ")
393 << decorationName <<
" needs a single <id> operand";
395 pendingIdDecorations.push_back({words[0],
396 static_cast<spirv::Decoration
>(words[1]),
397 words[2], unknownLoc});
400 return emitError(unknownLoc,
"unhandled Decoration : '") << decorationName;
405LogicalResult spirv::Deserializer::resolveDeferredIdDecorations() {
406 for (
const DeferredIdDecoration &entry : pendingIdDecorations) {
407 StringRef decorationName = stringifyDecoration(entry.decoration);
408 StringAttr symbol = getSymbolDecoration(decorationName);
412 StringRef operandSymName;
413 if (spirv::GlobalVariableOp varOp =
414 globalVariableMap.lookup(entry.operandID))
415 operandSymName = varOp.getSymName();
416 else if (spirv::SpecConstantOp specOp =
417 specConstMap.lookup(entry.operandID))
418 operandSymName = specOp.getSymName();
420 return emitError(entry.loc,
"OpDecorateId with ")
421 << decorationName <<
" references <id> " << entry.operandID
422 <<
" which is not a global variable or specialization constant";
429 Operation *targetOp =
nullptr;
430 if (spirv::GlobalVariableOp varOp =
431 globalVariableMap.lookup(entry.targetID))
433 else if (spirv::SpecConstantOp specOp = specConstMap.lookup(entry.targetID))
435 else if (spirv::FuncOp fnOp = funcMap.lookup(entry.targetID))
437 else if (Value v = valueMap.lookup(entry.targetID))
438 targetOp = v.getDefiningOp();
441 return emitError(entry.loc,
"OpDecorateId with ")
442 << decorationName <<
" references unknown target <id> "
451spirv::Deserializer::processMemberDecoration(ArrayRef<uint32_t> words) {
453 if (words.size() < 3) {
455 "OpMemberDecorate must have at least 3 operands");
458 auto decoration =
static_cast<spirv::Decoration
>(words[2]);
459 if (decoration == spirv::Decoration::Offset && words.size() != 4) {
461 " missing offset specification in OpMemberDecorate with "
462 "Offset decoration");
464 ArrayRef<uint32_t> decorationOperands;
465 if (words.size() > 3) {
466 decorationOperands = words.slice(3);
468 memberDecorationMap[words[0]][words[1]][decoration] = decorationOperands;
472LogicalResult spirv::Deserializer::processMemberName(ArrayRef<uint32_t> words) {
473 if (words.size() < 3) {
474 return emitError(unknownLoc,
"OpMemberName must have at least 3 operands");
476 unsigned wordIndex = 2;
478 if (wordIndex != words.size()) {
480 "unexpected trailing words in OpMemberName instruction");
482 memberNameMap[words[0]][words[1]] = name;
488 if (!decorations.contains(argID)) {
489 argAttrs[argIndex] = DictionaryAttr::get(context, {});
493 spirv::DecorationAttr foundDecorationAttr;
495 for (
auto decoration :
496 {spirv::Decoration::Aliased, spirv::Decoration::Restrict,
497 spirv::Decoration::AliasedPointer,
498 spirv::Decoration::RestrictPointer}) {
500 if (decAttr.getName() !=
504 if (foundDecorationAttr)
506 "more than one Aliased/Restrict decorations for "
507 "function argument with result <id> ")
510 foundDecorationAttr = spirv::DecorationAttr::get(context, decoration);
515 spirv::Decoration::RelaxedPrecision))) {
520 if (foundDecorationAttr)
521 return emitError(unknownLoc,
"already found a decoration for function "
522 "argument with result <id> ")
525 foundDecorationAttr = spirv::DecorationAttr::get(
526 context, spirv::Decoration::RelaxedPrecision);
530 if (!foundDecorationAttr)
531 return emitError(unknownLoc,
"unimplemented decoration support for "
532 "function argument with result <id> ")
535 NamedAttribute attr(StringAttr::get(context, spirv::DecorationAttr::name),
536 foundDecorationAttr);
537 argAttrs[argIndex] = DictionaryAttr::get(context, attr);
544 return emitError(unknownLoc,
"found function inside function");
548 if (operands.size() != 4) {
549 return emitError(unknownLoc,
"OpFunction must have 4 parameters");
553 return emitError(unknownLoc,
"undefined result type from <id> ")
557 uint32_t fnID = operands[1];
558 if (funcMap.count(fnID)) {
559 return emitError(unknownLoc,
"duplicate function definition/declaration");
562 auto fnControl = spirv::symbolizeFunctionControl(operands[2]);
564 return emitError(unknownLoc,
"unknown Function Control: ") << operands[2];
568 if (!fnType || !isa<FunctionType>(fnType)) {
569 return emitError(unknownLoc,
"unknown function type from <id> ")
572 auto functionType = cast<FunctionType>(fnType);
574 if ((
isVoidType(resultType) && functionType.getNumResults() != 0) ||
575 (functionType.getNumResults() == 1 &&
576 functionType.getResult(0) != resultType)) {
577 return emitError(unknownLoc,
"mismatch in function type ")
578 << functionType <<
" and return type " << resultType <<
" specified";
582 auto funcOp = spirv::FuncOp::create(opBuilder, unknownLoc, fnName,
583 functionType, fnControl.value());
585 if (decorations.count(fnID)) {
586 for (
auto attr : decorations[fnID].getAttrs()) {
590 curFunction = funcMap[fnID] = funcOp;
591 auto *entryBlock = funcOp.addEntryBlock();
594 <<
"//===-------------------------------------------===//\n";
595 logger.startLine() <<
"[fn] name: " << fnName <<
"\n";
596 logger.startLine() <<
"[fn] type: " << fnType <<
"\n";
597 logger.startLine() <<
"[fn] ID: " << fnID <<
"\n";
598 logger.startLine() <<
"[fn] entry block: " << entryBlock <<
"\n";
603 argAttrs.resize(functionType.getNumInputs());
606 if (functionType.getNumInputs()) {
607 for (
size_t i = 0, e = functionType.getNumInputs(); i != e; ++i) {
608 auto argType = functionType.getInput(i);
609 spirv::Opcode opcode = spirv::Opcode::OpNop;
612 spirv::Opcode::OpFunctionParameter))) {
615 if (opcode != spirv::Opcode::OpFunctionParameter) {
618 "missing OpFunctionParameter instruction for argument ")
621 if (operands.size() != 2) {
624 "expected result type and result <id> for OpFunctionParameter");
626 auto argDefinedType =
getType(operands[0]);
627 if (!argDefinedType || argDefinedType != argType) {
629 "mismatch in argument type between function type "
631 << functionType <<
" and argument type definition "
632 << argDefinedType <<
" at argument " << i;
635 return emitError(unknownLoc,
"duplicate definition of result <id> ")
642 auto argValue = funcOp.getArgument(i);
643 valueMap[operands[1]] = argValue;
647 if (llvm::any_of(argAttrs, [](
Attribute attr) {
648 auto argAttr = cast<DictionaryAttr>(attr);
649 return !argAttr.empty();
651 funcOp.setArgAttrsAttr(ArrayAttr::get(context, argAttrs));
656 auto linkageAttr = funcOp.getLinkageAttributes();
657 auto hasImportLinkage =
658 linkageAttr && (linkageAttr.value().getLinkageType().
getValue() ==
659 spirv::LinkageType::Import);
660 if (hasImportLinkage)
667 spirv::Opcode opcode = spirv::Opcode::OpNop;
676 spirv::Opcode::OpFunctionEnd))) {
679 if (opcode == spirv::Opcode::OpFunctionEnd) {
682 if (opcode != spirv::Opcode::OpLabel) {
683 return emitError(unknownLoc,
"a basic block must start with OpLabel");
685 if (instOperands.size() != 1) {
686 return emitError(unknownLoc,
"OpLabel should only have result <id>");
688 blockMap[instOperands[0]] = entryBlock;
696 spirv::Opcode::OpFunctionEnd)) &&
697 opcode != spirv::Opcode::OpFunctionEnd) {
702 if (opcode != spirv::Opcode::OpFunctionEnd) {
712 if (!operands.empty()) {
713 return emitError(unknownLoc,
"unexpected operands for OpFunctionEnd");
724 curFunction = std::nullopt;
729 <<
"//===-------------------------------------------===//\n";
736 if (operands.size() < 2) {
738 "missing graph defintion in OpGraphEntryPointARM");
741 unsigned wordIndex = 0;
742 uint32_t graphID = operands[wordIndex++];
743 if (!graphMap.contains(graphID)) {
745 "missing graph definition/declaration with id ")
749 spirv::GraphARMOp graphARM = graphMap[graphID];
751 graphARM.setSymName(name);
752 graphARM.setEntryPoint(
true);
755 for (
int64_t size = operands.size(); wordIndex < size; ++wordIndex) {
757 interface.push_back(SymbolRefAttr::get(arg.getOperation()));
759 return emitError(unknownLoc,
"undefined result <id> ")
760 << operands[wordIndex] <<
" while decoding OpGraphEntryPoint";
766 opBuilder.setInsertionPoint(graphARM);
767 spirv::GraphEntryPointARMOp::create(
768 opBuilder, unknownLoc, SymbolRefAttr::get(opBuilder.getContext(), name),
769 opBuilder.getArrayAttr(interface));
777 return emitError(unknownLoc,
"found graph inside graph");
780 if (operands.size() < 2) {
781 return emitError(unknownLoc,
"OpGraphARM must have at least 2 parameters");
785 if (!type || !isa<GraphType>(type)) {
786 return emitError(unknownLoc,
"unknown graph type from <id> ")
789 auto graphType = cast<GraphType>(type);
790 if (graphType.getNumResults() <= 0) {
791 return emitError(unknownLoc,
"expected at least one result");
794 uint32_t graphID = operands[1];
795 if (graphMap.count(graphID)) {
796 return emitError(unknownLoc,
"duplicate graph definition/declaration");
801 spirv::GraphARMOp::create(opBuilder, unknownLoc, graphName, graphType);
802 curGraph = graphMap[graphID] = graphOp;
803 Block *entryBlock = graphOp.addEntryBlock();
806 <<
"//===-------------------------------------------===//\n";
807 logger.startLine() <<
"[graph] name: " << graphName <<
"\n";
808 logger.startLine() <<
"[graph] type: " << graphType <<
"\n";
809 logger.startLine() <<
"[graph] ID: " << graphID <<
"\n";
810 logger.startLine() <<
"[graph] entry block: " << entryBlock <<
"\n";
815 for (
auto [
index, argType] : llvm::enumerate(graphType.getInputs())) {
816 spirv::Opcode opcode;
819 spirv::Opcode::OpGraphInputARM))) {
822 if (operands.size() != 3) {
823 return emitError(unknownLoc,
"expected result type, result <id> and "
824 "input index for OpGraphInputARM");
828 if (!argDefinedType) {
829 return emitError(unknownLoc,
"unknown operand type <id> ") << operands[0];
832 if (argDefinedType != argType) {
834 "mismatch in argument type between graph type "
836 << graphType <<
" and argument type definition " << argDefinedType
837 <<
" at argument " <<
index;
840 return emitError(unknownLoc,
"duplicate definition of result <id> ")
845 if (!inputIndexAttr) {
847 "unable to read inputIndex value from constant op ")
850 BlockArgument argValue = graphOp.getArgument(inputIndexAttr.getInt());
851 valueMap[operands[1]] = argValue;
854 graphOutputs.resize(graphType.getNumResults());
860 blockMap[graphID] = entryBlock;
867 spirv::Opcode opcode;
877 }
while (opcode != spirv::Opcode::OpGraphEndARM);
884 if (operands.size() != 2) {
887 "expected value id and output index for OpGraphSetOutputARM");
890 uint32_t
id = operands[0];
893 return emitError(unknownLoc,
"could not find result <id> ") << id;
897 if (!outputIndexAttr) {
899 "unable to read outputIndex value from constant op ")
902 graphOutputs[outputIndexAttr.getInt()] = value;
909 spirv::GraphOutputsARMOp::create(opBuilder, unknownLoc, graphOutputs);
912 if (!operands.empty()) {
913 return emitError(unknownLoc,
"unexpected operands for OpGraphEndARM");
917 curGraph = std::nullopt;
918 graphOutputs.clear();
923 <<
"//===-------------------------------------------===//\n";
928std::optional<std::pair<Attribute, Type>>
930 auto constIt = constantMap.find(
id);
931 if (constIt != constantMap.end())
932 return constIt->getSecond();
934 auto replicatedConstIt = constantCompositeReplicateMap.find(
id);
935 if (replicatedConstIt == constantCompositeReplicateMap.end())
938 auto [value, type] = replicatedConstIt->getSecond();
939 auto shapedType = dyn_cast<ShapedType>(type);
945std::optional<std::pair<Attribute, Type>>
947 if (
auto it = constantCompositeReplicateMap.find(
id);
948 it != constantCompositeReplicateMap.end())
953std::optional<spirv::SpecConstOperationMaterializationInfo>
955 auto constIt = specConstOperationMap.find(
id);
956 if (constIt == specConstOperationMap.end())
958 return constIt->getSecond();
962 auto funcName = nameMap.lookup(
id).str();
963 if (funcName.empty()) {
964 funcName =
"spirv_fn_" + std::to_string(
id);
970 std::string graphName = nameMap.lookup(
id).str();
971 if (graphName.empty()) {
972 graphName =
"spirv_graph_" + std::to_string(
id);
978 auto constName = nameMap.lookup(
id).str();
979 if (constName.empty()) {
980 constName =
"spirv_spec_const_" + std::to_string(
id);
987 TypedAttr defaultValue) {
989 auto op = spirv::SpecConstantOp::create(opBuilder, unknownLoc, symName,
992 if (decorations.count(resultID)) {
993 for (
auto attr : decorations[resultID].getAttrs())
996 specConstMap[resultID] = op;
1000std::optional<spirv::GraphConstantARMOpMaterializationInfo>
1002 auto graphConstIt = graphConstantMap.find(
id);
1003 if (graphConstIt == graphConstantMap.end())
1004 return std::nullopt;
1005 return graphConstIt->getSecond();
1010 unsigned wordIndex = 0;
1011 if (operands.size() < 3) {
1014 "OpVariable needs at least 3 operands, type, <id> and storage class");
1018 auto type =
getType(operands[wordIndex]);
1020 return emitError(unknownLoc,
"unknown result type <id> : ")
1021 << operands[wordIndex];
1023 auto ptrType = dyn_cast<spirv::PointerType>(type);
1026 "expected a result type <id> to be a spirv.ptr, found : ")
1032 auto variableID = operands[wordIndex];
1033 auto variableName = nameMap.lookup(variableID).str();
1034 if (variableName.empty()) {
1035 variableName =
"spirv_var_" + std::to_string(variableID);
1040 auto storageClass =
static_cast<spirv::StorageClass
>(operands[wordIndex]);
1041 if (ptrType.getStorageClass() != storageClass) {
1042 return emitError(unknownLoc,
"mismatch in storage class of pointer type ")
1043 << type <<
" and that specified in OpVariable instruction : "
1044 << stringifyStorageClass(storageClass);
1051 if (wordIndex < operands.size()) {
1061 return emitError(unknownLoc,
"unknown <id> ")
1062 << operands[wordIndex] <<
"used as initializer";
1064 initializer = SymbolRefAttr::get(op);
1067 if (wordIndex != operands.size()) {
1069 "found more operands than expected when deserializing "
1070 "OpVariable instruction, only ")
1071 << wordIndex <<
" of " << operands.size() <<
" processed";
1074 auto varOp = spirv::GlobalVariableOp::create(
1075 opBuilder, loc, TypeAttr::get(type),
1076 opBuilder.getStringAttr(variableName), initializer);
1079 if (decorations.count(variableID)) {
1080 for (
auto attr : decorations[variableID].getAttrs())
1083 globalVariableMap[variableID] = varOp;
1092 return dyn_cast<IntegerAttr>(constInfo->first);
1096 if (operands.size() < 2) {
1097 return emitError(unknownLoc,
"OpName needs at least 2 operands");
1100 unsigned wordIndex = 1;
1102 if (wordIndex != operands.size()) {
1104 "unexpected trailing words in OpName instruction");
1109 nameMap.emplace_or_assign(operands[0], name);
1120 if (operands.empty()) {
1121 return emitError(unknownLoc,
"type instruction with opcode ")
1122 << spirv::stringifyOpcode(opcode) <<
" needs at least one <id>";
1127 if (typeMap.count(operands[0])) {
1128 return emitError(unknownLoc,
"duplicate definition for result <id> ")
1133 case spirv::Opcode::OpTypeVoid:
1134 if (operands.size() != 1)
1135 return emitError(unknownLoc,
"OpTypeVoid must have no parameters");
1136 typeMap[operands[0]] = opBuilder.getNoneType();
1138 case spirv::Opcode::OpTypeBool:
1139 if (operands.size() != 1)
1140 return emitError(unknownLoc,
"OpTypeBool must have no parameters");
1141 typeMap[operands[0]] = opBuilder.getI1Type();
1143 case spirv::Opcode::OpTypeInt: {
1144 if (operands.size() != 3)
1146 unknownLoc,
"OpTypeInt must have bitwidth and signedness parameters");
1155 auto sign = operands[2] == 1 ? IntegerType::SignednessSemantics::Signed
1156 : IntegerType::SignednessSemantics::Signless;
1157 typeMap[operands[0]] = IntegerType::get(context, operands[1], sign);
1159 case spirv::Opcode::OpTypeFloat: {
1160 if (operands.size() != 2 && operands.size() != 3)
1162 "OpTypeFloat expects either 2 operands (type, bitwidth) "
1163 "or 3 operands (type, bitwidth, encoding), but got ")
1165 uint32_t bitWidth = operands[1];
1168 if (operands.size() == 2) {
1171 floatTy = opBuilder.getF16Type();
1174 floatTy = opBuilder.getF32Type();
1177 floatTy = opBuilder.getF64Type();
1180 return emitError(unknownLoc,
"unsupported OpTypeFloat bitwidth: ")
1185 if (operands.size() == 3) {
1186 if (spirv::FPEncoding(operands[2]) == spirv::FPEncoding::BFloat16KHR &&
1188 floatTy = opBuilder.getBF16Type();
1189 else if (spirv::FPEncoding(operands[2]) ==
1190 spirv::FPEncoding::Float8E4M3EXT &&
1192 floatTy = opBuilder.getF8E4M3FNType();
1193 else if (spirv::FPEncoding(operands[2]) ==
1194 spirv::FPEncoding::Float8E5M2EXT &&
1196 floatTy = opBuilder.getF8E5M2Type();
1198 return emitError(unknownLoc,
"unsupported OpTypeFloat FP encoding: ")
1199 << operands[2] <<
" and bitWidth " << bitWidth;
1202 typeMap[operands[0]] = floatTy;
1204 case spirv::Opcode::OpTypeVector: {
1205 if (operands.size() != 3) {
1208 "OpTypeVector must have element type and count parameters");
1212 return emitError(unknownLoc,
"OpTypeVector references undefined <id> ")
1215 typeMap[operands[0]] = VectorType::get({operands[2]}, elementTy);
1217 case spirv::Opcode::OpTypePointer: {
1220 case spirv::Opcode::OpTypeArray:
1222 case spirv::Opcode::OpTypeCooperativeMatrixKHR:
1224 case spirv::Opcode::OpTypeFunction:
1226 case spirv::Opcode::OpTypeImage:
1228 case spirv::Opcode::OpTypeSampler:
1230 case spirv::Opcode::OpTypeNamedBarrier:
1232 case spirv::Opcode::OpTypeSampledImage:
1234 case spirv::Opcode::OpTypeRuntimeArray:
1236 case spirv::Opcode::OpTypeStruct:
1238 case spirv::Opcode::OpTypeMatrix:
1240 case spirv::Opcode::OpTypeTensorARM:
1242 case spirv::Opcode::OpTypeGraphARM:
1245 return emitError(unknownLoc,
"unhandled type instruction");
1252 if (operands.size() != 3)
1253 return emitError(unknownLoc,
"OpTypePointer must have two parameters");
1255 auto pointeeType =
getType(operands[2]);
1257 return emitError(unknownLoc,
"unknown OpTypePointer pointee type <id> ")
1260 uint32_t typePointerID = operands[0];
1261 auto storageClass =
static_cast<spirv::StorageClass
>(operands[1]);
1264 for (
auto *deferredStructIt = std::begin(deferredStructTypesInfos);
1265 deferredStructIt != std::end(deferredStructTypesInfos);) {
1266 for (
auto *unresolvedMemberIt =
1267 std::begin(deferredStructIt->unresolvedMemberTypes);
1268 unresolvedMemberIt !=
1269 std::end(deferredStructIt->unresolvedMemberTypes);) {
1270 if (unresolvedMemberIt->first == typePointerID) {
1274 deferredStructIt->memberTypes[unresolvedMemberIt->second] =
1275 typeMap[typePointerID];
1276 unresolvedMemberIt =
1277 deferredStructIt->unresolvedMemberTypes.erase(unresolvedMemberIt);
1279 ++unresolvedMemberIt;
1283 if (deferredStructIt->unresolvedMemberTypes.empty()) {
1285 auto structType = deferredStructIt->deferredStructType;
1287 assert(structType &&
"expected a spirv::StructType");
1288 assert(structType.isIdentified() &&
"expected an indentified struct");
1290 if (failed(structType.trySetBody(
1291 deferredStructIt->memberTypes, deferredStructIt->offsetInfo,
1292 deferredStructIt->memberDecorationsInfo,
1293 deferredStructIt->structDecorationsInfo)))
1296 deferredStructIt = deferredStructTypesInfos.erase(deferredStructIt);
1307 if (operands.size() != 3) {
1309 "OpTypeArray must have element type and count parameters");
1314 return emitError(unknownLoc,
"OpTypeArray references undefined <id> ")
1322 return emitError(unknownLoc,
"OpTypeArray count <id> ")
1323 << operands[2] <<
"can only come from normal constant right now";
1326 if (
auto intVal = dyn_cast<IntegerAttr>(countInfo->first)) {
1327 count = intVal.getValue().getZExtValue();
1329 return emitError(unknownLoc,
"OpTypeArray count must come from a "
1330 "scalar integer constant instruction");
1334 elementTy, count, typeDecorations.lookup(operands[0]));
1340 assert(!operands.empty() &&
"No operands for processing function type");
1341 if (operands.size() == 1) {
1342 return emitError(unknownLoc,
"missing return type for OpTypeFunction");
1344 auto returnType =
getType(operands[1]);
1346 return emitError(unknownLoc,
"unknown return type in OpTypeFunction");
1349 for (
size_t i = 2, e = operands.size(); i < e; ++i) {
1350 auto ty =
getType(operands[i]);
1352 return emitError(unknownLoc,
"unknown argument type in OpTypeFunction");
1354 argTypes.push_back(ty);
1360 typeMap[operands[0]] = FunctionType::get(context, argTypes, returnTypes);
1366 if (operands.size() != 6) {
1368 "OpTypeCooperativeMatrixKHR must have element type, "
1369 "scope, row and column parameters, and use");
1375 "OpTypeCooperativeMatrixKHR references undefined <id> ")
1379 std::optional<spirv::Scope> scope =
1384 "OpTypeCooperativeMatrixKHR references undefined scope <id> ")
1393 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Rows` references "
1394 "undefined constant <id> ")
1398 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Columns` "
1399 "references undefined constant <id> ")
1403 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Use` references "
1404 "undefined constant <id> ")
1407 unsigned rows = rowsAttr.getInt();
1408 unsigned columns = columnsAttr.getInt();
1410 std::optional<spirv::CooperativeMatrixUseKHR> use =
1411 spirv::symbolizeCooperativeMatrixUseKHR(useAttr.getInt());
1415 "OpTypeCooperativeMatrixKHR references undefined use <id> ")
1419 typeMap[operands[0]] =
1426 if (operands.size() != 2) {
1427 return emitError(unknownLoc,
"OpTypeRuntimeArray must have two operands");
1432 "OpTypeRuntimeArray references undefined <id> ")
1436 memberType, typeDecorations.lookup(operands[0]));
1444 if (operands.empty()) {
1445 return emitError(unknownLoc,
"OpTypeStruct must have at least result <id>");
1448 if (operands.size() == 1) {
1450 typeMap[operands[0]] =
1459 for (
auto op : llvm::drop_begin(operands, 1)) {
1461 bool typeForwardPtr = (typeForwardPointerIDs.count(op) != 0);
1463 if (!memberType && !typeForwardPtr)
1464 return emitError(unknownLoc,
"OpTypeStruct references undefined <id> ")
1468 unresolvedMemberTypes.emplace_back(op, memberTypes.size());
1470 memberTypes.push_back(memberType);
1475 if (memberDecorationMap.count(operands[0])) {
1476 auto &allMemberDecorations = memberDecorationMap[operands[0]];
1477 for (
auto memberIndex : llvm::seq<uint32_t>(0, memberTypes.size())) {
1478 if (allMemberDecorations.count(memberIndex)) {
1479 for (
auto &memberDecoration : allMemberDecorations[memberIndex]) {
1481 if (memberDecoration.first == spirv::Decoration::Offset) {
1483 if (offsetInfo.empty()) {
1484 offsetInfo.resize(memberTypes.size());
1486 offsetInfo[memberIndex] = memberDecoration.second[0];
1488 auto intType = mlir::IntegerType::get(context, 32);
1489 if (!memberDecoration.second.empty()) {
1490 memberDecorationsInfo.emplace_back(
1491 memberIndex, memberDecoration.first,
1492 IntegerAttr::get(intType, memberDecoration.second[0]));
1494 memberDecorationsInfo.emplace_back(
1495 memberIndex, memberDecoration.first, UnitAttr::get(context));
1504 if (decorations.count(operands[0])) {
1507 std::optional<spirv::Decoration> decoration = spirv::symbolizeDecoration(
1508 llvm::convertToCamelFromSnakeCase(decorationAttr.getName(),
true));
1509 assert(decoration.has_value());
1510 structDecorationsInfo.emplace_back(decoration.value(),
1511 decorationAttr.getValue());
1515 uint32_t structID = operands[0];
1516 std::string structIdentifier = nameMap.lookup(structID).str();
1518 if (structIdentifier.empty()) {
1519 assert(unresolvedMemberTypes.empty() &&
1520 "didn't expect unresolved member types");
1522 memberTypes, offsetInfo, memberDecorationsInfo, structDecorationsInfo);
1525 typeMap[structID] = structTy;
1527 if (!unresolvedMemberTypes.empty())
1528 deferredStructTypesInfos.push_back(
1529 {structTy, std::move(unresolvedMemberTypes), std::move(memberTypes),
1530 std::move(offsetInfo), std::move(memberDecorationsInfo),
1531 std::move(structDecorationsInfo)});
1532 else if (failed(structTy.trySetBody(memberTypes, offsetInfo,
1533 memberDecorationsInfo,
1534 structDecorationsInfo)))
1545 if (operands.size() != 3) {
1547 return emitError(unknownLoc,
"OpTypeMatrix must have 3 operands"
1548 " (result_id, column_type, and column_count)");
1554 "OpTypeMatrix references undefined column type.")
1558 uint32_t colsCount = operands[2];
1565 unsigned size = operands.size();
1566 if (size < 2 || size > 4)
1567 return emitError(unknownLoc,
"OpTypeTensorARM must have 2-4 operands "
1568 "(result_id, element_type, (rank), (shape)) ")
1574 "OpTypeTensorARM references undefined element type ")
1584 return emitError(unknownLoc,
"OpTypeTensorARM rank must come from a "
1585 "scalar integer constant instruction");
1586 unsigned rank = rankAttr.getValue().getZExtValue();
1593 std::optional<std::pair<Attribute, Type>> shapeInfo =
1596 return emitError(unknownLoc,
"OpTypeTensorARM shape must come from a "
1597 "constant instruction of type OpTypeArray");
1599 ArrayAttr shapeArrayAttr = dyn_cast<ArrayAttr>(shapeInfo->first);
1601 for (
auto dimAttr : shapeArrayAttr.getValue()) {
1602 auto dimIntAttr = dyn_cast<IntegerAttr>(dimAttr);
1604 return emitError(unknownLoc,
"OpTypeTensorARM shape has an invalid "
1606 shape.push_back(dimIntAttr.getValue().getSExtValue());
1614 unsigned size = operands.size();
1616 return emitError(unknownLoc,
"OpTypeGraphARM must have at least 2 operands "
1617 "(result_id, num_inputs, (inout0_type, "
1618 "inout1_type, ...))")
1621 uint32_t numInputs = operands[1];
1624 for (
unsigned i = 2; i < size; ++i) {
1628 "OpTypeGraphARM references undefined element type.")
1631 if (i - 2 >= numInputs) {
1632 returnTypes.push_back(inOutTy);
1634 argTypes.push_back(inOutTy);
1637 typeMap[operands[0]] = GraphType::get(context, argTypes, returnTypes);
1643 if (operands.size() != 2)
1645 "OpTypeForwardPointer instruction must have two operands");
1647 typeForwardPointerIDs.insert(operands[0]);
1657 if (operands.size() != 8)
1660 "OpTypeImage with non-eight operands are not supported yet");
1664 return emitError(unknownLoc,
"OpTypeImage references undefined <id>: ")
1667 auto dim = spirv::symbolizeDim(operands[2]);
1669 return emitError(unknownLoc,
"unknown Dim for OpTypeImage: ")
1672 auto depthInfo = spirv::symbolizeImageDepthInfo(operands[3]);
1674 return emitError(unknownLoc,
"unknown Depth for OpTypeImage: ")
1677 auto arrayedInfo = spirv::symbolizeImageArrayedInfo(operands[4]);
1679 return emitError(unknownLoc,
"unknown Arrayed for OpTypeImage: ")
1682 auto samplingInfo = spirv::symbolizeImageSamplingInfo(operands[5]);
1684 return emitError(unknownLoc,
"unknown MS for OpTypeImage: ") << operands[5];
1686 auto samplerUseInfo = spirv::symbolizeImageSamplerUseInfo(operands[6]);
1687 if (!samplerUseInfo)
1688 return emitError(unknownLoc,
"unknown Sampled for OpTypeImage: ")
1691 auto format = spirv::symbolizeImageFormat(operands[7]);
1693 return emitError(unknownLoc,
"unknown Format for OpTypeImage: ")
1697 elementTy, dim.value(), depthInfo.value(), arrayedInfo.value(),
1698 samplingInfo.value(), samplerUseInfo.value(), format.value());
1704 if (operands.size() != 2)
1705 return emitError(unknownLoc,
"OpTypeSampledImage must have two operands");
1710 "OpTypeSampledImage references undefined <id>: ")
1719 if (operands.size() != 1)
1720 return emitError(unknownLoc,
"OpTypeSampler must have no parameters");
1728 if (operands.size() != 1)
1729 return emitError(unknownLoc,
"OpTypeNamedBarrier must have no parameters");
1741 StringRef opname = isSpec ?
"OpSpecConstant" :
"OpConstant";
1743 if (operands.size() < 2) {
1745 << opname <<
" must have type <id> and result <id>";
1747 if (operands.size() < 3) {
1749 << opname <<
" must have at least 1 more parameter";
1754 return emitError(unknownLoc,
"undefined result type from <id> ")
1758 auto checkOperandSizeForBitwidth = [&](
unsigned bitwidth) -> LogicalResult {
1759 if (bitwidth == 64) {
1760 if (operands.size() == 4) {
1764 << opname <<
" should have 2 parameters for 64-bit values";
1766 if (bitwidth <= 32) {
1767 if (operands.size() == 3) {
1773 <<
" should have 1 parameter for values with no more than 32 bits";
1775 return emitError(unknownLoc,
"unsupported OpConstant bitwidth: ")
1779 auto resultID = operands[1];
1781 if (
auto intType = dyn_cast<IntegerType>(resultType)) {
1782 auto bitwidth = intType.getWidth();
1783 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1788 if (bitwidth == 64) {
1795 } words = {operands[2], operands[3]};
1796 value = APInt(64, llvm::bit_cast<uint64_t>(words),
true);
1797 }
else if (bitwidth <= 32) {
1798 value = APInt(bitwidth, operands[2],
true,
1802 auto attr = opBuilder.getIntegerAttr(intType, value);
1809 constantMap.try_emplace(resultID, attr, intType);
1815 if (
auto floatType = dyn_cast<FloatType>(resultType)) {
1816 auto bitwidth = floatType.getWidth();
1817 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1822 if (floatType.isF64()) {
1829 } words = {operands[2], operands[3]};
1830 value = APFloat(llvm::bit_cast<double>(words));
1831 }
else if (floatType.isF32()) {
1832 value = APFloat(llvm::bit_cast<float>(operands[2]));
1833 }
else if (floatType.isF16()) {
1834 APInt data(16, operands[2]);
1835 value = APFloat(APFloat::IEEEhalf(), data);
1836 }
else if (floatType.isBF16()) {
1837 APInt data(16, operands[2]);
1838 value = APFloat(APFloat::BFloat(), data);
1839 }
else if (floatType.isF8E4M3FN()) {
1840 APInt data(8, operands[2]);
1841 value = APFloat(APFloat::Float8E4M3FN(), data);
1842 }
else if (floatType.isF8E5M2()) {
1843 APInt data(8, operands[2]);
1844 value = APFloat(APFloat::Float8E5M2(), data);
1847 auto attr = opBuilder.getFloatAttr(floatType, value);
1853 constantMap.try_emplace(resultID, attr, floatType);
1859 return emitError(unknownLoc,
"OpConstant can only generate values of "
1860 "scalar integer or floating-point type");
1865 if (operands.size() != 2) {
1867 << (isSpec ?
"Spec" :
"") <<
"Constant"
1868 << (isTrue ?
"True" :
"False")
1869 <<
" must have type <id> and result <id>";
1872 auto attr = opBuilder.getBoolAttr(isTrue);
1873 auto resultID = operands[1];
1879 constantMap.try_emplace(resultID, attr, opBuilder.getI1Type());
1887 if (operands.size() < 2) {
1889 "OpConstantComposite must have type <id> and result <id>");
1891 if (operands.size() < 3) {
1893 "OpConstantComposite must have at least 1 parameter");
1898 return emitError(unknownLoc,
"undefined result type from <id> ")
1903 elements.reserve(operands.size() - 2);
1904 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
1907 return emitError(unknownLoc,
"OpConstantComposite component <id> ")
1908 << operands[i] <<
" must come from a normal constant";
1910 elements.push_back(elementInfo->first);
1913 auto resultID = operands[1];
1914 if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
1917 if (
auto denseElemAttr = dyn_cast<DenseElementsAttr>(element)) {
1918 for (
auto value : denseElemAttr.getValues<
Attribute>())
1919 flattenedElems.push_back(value);
1921 flattenedElems.push_back(element);
1925 constantMap.try_emplace(resultID, attr, tensorType);
1926 }
else if (
auto shapedType = dyn_cast<ShapedType>(resultType)) {
1930 constantMap.try_emplace(resultID, attr, shapedType);
1931 }
else if (isa<spirv::ArrayType, spirv::StructType>(resultType)) {
1932 auto attr = opBuilder.getArrayAttr(elements);
1933 constantMap.try_emplace(resultID, attr, resultType);
1935 return emitError(unknownLoc,
"unsupported OpConstantComposite type: ")
1944 if (operands.size() != 3) {
1947 "OpConstantCompositeReplicateEXT expects 3 operands but found ")
1953 return emitError(unknownLoc,
"undefined result type from <id> ")
1957 auto compositeType = dyn_cast<CompositeType>(resultType);
1958 if (!compositeType) {
1960 "result type from <id> is not a composite type")
1964 uint32_t resultID = operands[1];
1965 uint32_t constantID = operands[2];
1967 std::optional<std::pair<Attribute, Type>> replicatedConstantCompositeInfo =
1969 if (replicatedConstantCompositeInfo.has_value()) {
1970 constantCompositeReplicateMap.try_emplace(
1971 resultID, replicatedConstantCompositeInfo.value().first, resultType);
1975 std::optional<std::pair<Attribute, Type>> constantInfo =
1977 if (constantInfo.has_value()) {
1978 constantCompositeReplicateMap.try_emplace(
1979 resultID, constantInfo.value().first, resultType);
1983 return emitError(unknownLoc,
"OpConstantCompositeReplicateEXT operand <id> ")
1985 <<
" must come from a normal constant or a "
1986 "OpConstantCompositeReplicateEXT";
1991 if (operands.size() < 2) {
1994 "OpSpecConstantComposite must have type <id> and result <id>");
1996 if (operands.size() < 3) {
1998 "OpSpecConstantComposite must have at least 1 parameter");
2003 return emitError(unknownLoc,
"undefined result type from <id> ")
2007 auto resultID = operands[1];
2011 elements.reserve(operands.size() - 2);
2012 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
2014 elements.push_back(SymbolRefAttr::get(elementInfo));
2017 auto op = spirv::SpecConstantCompositeOp::create(
2018 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2019 opBuilder.getArrayAttr(elements),
nullptr);
2020 specConstCompositeMap[resultID] = op;
2027 if (operands.size() != 3) {
2028 return emitError(unknownLoc,
"OpSpecConstantCompositeReplicateEXT expects "
2029 "3 operands but found ")
2035 return emitError(unknownLoc,
"undefined result type from <id> ")
2039 auto compositeType = dyn_cast<CompositeType>(resultType);
2040 if (!compositeType) {
2042 "result type from <id> is not a composite type")
2046 uint32_t resultID = operands[1];
2049 spirv::SpecConstantOp constituentSpecConstantOp =
2051 auto op = spirv::EXTSpecConstantCompositeReplicateOp::create(
2052 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2053 SymbolRefAttr::get(constituentSpecConstantOp),
2056 specConstCompositeReplicateMap[resultID] = op;
2063 if (operands.size() < 3)
2064 return emitError(unknownLoc,
"OpConstantOperation must have type <id>, "
2065 "result <id>, and operand opcode");
2067 uint32_t resultTypeID = operands[0];
2070 return emitError(unknownLoc,
"undefined result type from <id> ")
2073 uint32_t resultID = operands[1];
2074 spirv::Opcode enclosedOpcode =
static_cast<spirv::Opcode
>(operands[2]);
2075 auto emplaceResult = specConstOperationMap.try_emplace(
2078 enclosedOpcode, resultTypeID,
2081 if (!emplaceResult.second)
2082 return emitError(unknownLoc,
"value with <id>: ")
2083 << resultID <<
" is probably defined before.";
2089 uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID,
2105 llvm::SaveAndRestore valueMapGuard(valueMap, newValueMap);
2106 constexpr uint32_t fakeID =
static_cast<uint32_t
>(-3);
2109 enclosedOpResultTypeAndOperands.push_back(resultTypeID);
2110 enclosedOpResultTypeAndOperands.push_back(fakeID);
2111 enclosedOpResultTypeAndOperands.append(enclosedOpOperands.begin(),
2112 enclosedOpOperands.end());
2127 auto specConstOperationOp =
2128 spirv::SpecConstantOperationOp::create(opBuilder, loc, resultType);
2130 Region &body = specConstOperationOp.getBody();
2132 body.
getBlocks().splice(body.
end(), curBlock->getParent()->getBlocks(),
2139 opBuilder.setInsertionPointToEnd(&block);
2141 spirv::YieldOp::create(opBuilder, loc, block.
front().
getResult(0));
2142 return specConstOperationOp.getResult();
2147 if (operands.size() != 2) {
2149 "OpConstantNull must only have type <id> and result <id>");
2154 return emitError(unknownLoc,
"undefined result type from <id> ")
2158 auto resultID = operands[1];
2160 if (resultType.
isIntOrFloat() || isa<VectorType>(resultType)) {
2161 attr = opBuilder.getZeroAttr(resultType);
2162 }
else if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
2163 if (
auto element = opBuilder.getZeroAttr(tensorType.getElementType()))
2170 constantMap.try_emplace(resultID, attr, resultType);
2174 return emitError(unknownLoc,
"unsupported OpConstantNull type: ")
2180 if (operands.size() < 3) {
2182 <<
"OpGraphConstantARM must have at least 2 operands";
2187 return emitError(unknownLoc,
"undefined result type from <id> ")
2191 uint32_t resultID = operands[1];
2193 if (!dyn_cast<spirv::TensorArmType>(resultType)) {
2194 return emitError(unknownLoc,
"result must be of type OpTypeTensorARM");
2197 APInt graph_constant_id = APInt(32, operands[2],
true);
2198 Type i32Ty = opBuilder.getIntegerType(32);
2199 IntegerAttr attr = opBuilder.getIntegerAttr(i32Ty, graph_constant_id);
2200 graphConstantMap.try_emplace(
2212 LLVM_DEBUG(logger.startLine() <<
"[block] got exiting block for id = " <<
id
2213 <<
" @ " << block <<
"\n");
2220 auto *block = curFunction->addBlock();
2221 LLVM_DEBUG(logger.startLine() <<
"[block] created block for id = " <<
id
2222 <<
" @ " << block <<
"\n");
2223 return blockMap[id] = block;
2228 return emitError(unknownLoc,
"OpBranch must appear inside a block");
2231 if (operands.size() != 1) {
2232 return emitError(unknownLoc,
"OpBranch must take exactly one target label");
2240 spirv::BranchOp::create(opBuilder, loc,
target);
2250 "OpBranchConditional must appear inside a block");
2253 if (operands.size() != 3 && operands.size() != 5) {
2255 "OpBranchConditional must have condition, true label, "
2256 "false label, and optionally two branch weights");
2259 auto condition =
getValue(operands[0]);
2263 std::optional<std::pair<uint32_t, uint32_t>> weights;
2264 if (operands.size() == 5) {
2265 weights = std::make_pair(operands[3], operands[4]);
2271 spirv::BranchConditionalOp::create(
2272 opBuilder, loc, condition, trueBlock,
2282 return emitError(unknownLoc,
"OpLabel must appear inside a function");
2285 if (operands.size() != 1) {
2286 return emitError(unknownLoc,
"OpLabel should only have result <id>");
2289 auto labelID = operands[0];
2292 LLVM_DEBUG(logger.startLine()
2293 <<
"[block] populating block " << block <<
"\n");
2295 assert(block->empty() &&
"re-deserialize the same block!");
2297 opBuilder.setInsertionPointToStart(block);
2298 blockMap[labelID] = curBlock = block;
2305 return emitError(unknownLoc,
"a graph block must appear inside a graph");
2310 LLVM_DEBUG(logger.startLine()
2311 <<
"[block] populating block " << block <<
"\n");
2313 assert(block->
empty() &&
"re-deserialize the same block!");
2315 opBuilder.setInsertionPointToStart(block);
2316 blockMap[graphID] = curBlock = block;
2324 return emitError(unknownLoc,
"OpSelectionMerge must appear in a block");
2327 if (operands.size() < 2) {
2330 "OpSelectionMerge must specify merge target and selection control");
2335 auto selectionControl = operands[1];
2337 if (!blockMergeInfo.try_emplace(curBlock, loc, selectionControl, mergeBlock)
2341 "a block cannot have more than one OpSelectionMerge instruction");
2350 return emitError(unknownLoc,
"OpLoopMerge must appear in a block");
2353 if (operands.size() < 3) {
2354 return emitError(unknownLoc,
"OpLoopMerge must specify merge target, "
2355 "continue target and loop control");
2361 uint32_t loopControl = operands[2];
2364 .try_emplace(curBlock, loc, loopControl, mergeBlock, continueBlock)
2368 "a block cannot have more than one OpLoopMerge instruction");
2376 return emitError(unknownLoc,
"OpPhi must appear in a block");
2379 if (operands.size() < 4) {
2380 return emitError(unknownLoc,
"OpPhi must specify result type, result <id>, "
2381 "and variable-parent pairs");
2386 BlockArgument blockArg = curBlock->addArgument(blockArgType, unknownLoc);
2387 valueMap[operands[1]] = blockArg;
2388 LLVM_DEBUG(logger.startLine()
2389 <<
"[phi] created block argument " << blockArg
2390 <<
" id = " << operands[1] <<
" of type " << blockArgType <<
"\n");
2394 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
2395 uint32_t value = operands[i];
2397 std::pair<Block *, Block *> predecessorTargetPair{predecessor, curBlock};
2398 blockPhiInfo[predecessorTargetPair].push_back(value);
2399 LLVM_DEBUG(logger.startLine() <<
"[phi] predecessor @ " << predecessor
2400 <<
" with arg id = " << value <<
"\n");
2408 return emitError(unknownLoc,
"OpSwitch must appear in a block");
2410 if (operands.size() < 2)
2411 return emitError(unknownLoc,
"OpSwitch must at least specify selector and "
2412 "a default target");
2414 if (operands.size() % 2)
2416 "OpSwitch must at have an even number of operands: "
2417 "selector, default target and any number of literal and "
2418 "label <id> pairs");
2426 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
2427 literals.push_back(operands[i]);
2432 spirv::SwitchOp::create(opBuilder, loc, selector, defaultBlock,
2441class ControlFlowStructurizer {
2444 ControlFlowStructurizer(
Location loc, uint32_t control,
2447 llvm::ScopedPrinter &logger)
2448 : location(loc), control(control), blockMergeInfo(mergeInfo),
2449 headerBlock(header), mergeBlock(merge), continueBlock(cont),
2452 ControlFlowStructurizer(
Location loc, uint32_t control,
2455 : location(loc), control(control), blockMergeInfo(mergeInfo),
2456 headerBlock(header), mergeBlock(merge), continueBlock(cont) {}
2466 LogicalResult structurize();
2471 spirv::SelectionOp createSelectionOp(uint32_t selectionControl);
2474 spirv::LoopOp createLoopOp(uint32_t loopControl);
2477 void collectBlocksInConstruct();
2486 Block *continueBlock;
2492 llvm::ScopedPrinter &logger;
2498ControlFlowStructurizer::createSelectionOp(uint32_t selectionControl) {
2501 OpBuilder builder(&mergeBlock->front());
2503 auto control =
static_cast<spirv::SelectionControl
>(selectionControl);
2504 auto selectionOp = spirv::SelectionOp::create(builder, location, control);
2505 selectionOp.addMergeBlock(builder);
2510spirv::LoopOp ControlFlowStructurizer::createLoopOp(uint32_t loopControl) {
2513 OpBuilder builder(&mergeBlock->front());
2515 auto control =
static_cast<spirv::LoopControl
>(loopControl);
2516 auto loopOp = spirv::LoopOp::create(builder, location, control);
2517 loopOp.addEntryAndMergeBlock(builder);
2522void ControlFlowStructurizer::collectBlocksInConstruct() {
2523 assert(constructBlocks.empty() &&
"expected empty constructBlocks");
2526 constructBlocks.insert(headerBlock);
2530 for (
unsigned i = 0; i < constructBlocks.size(); ++i) {
2531 for (
auto *successor : constructBlocks[i]->getSuccessors())
2532 if (successor != mergeBlock)
2533 constructBlocks.insert(successor);
2537LogicalResult ControlFlowStructurizer::structurize() {
2538 Operation *op =
nullptr;
2539 bool isLoop = continueBlock !=
nullptr;
2541 if (
auto loopOp = createLoopOp(control))
2542 op = loopOp.getOperation();
2544 if (
auto selectionOp = createSelectionOp(control))
2545 op = selectionOp.getOperation();
2554 mapper.
map(mergeBlock, &body.
back());
2556 collectBlocksInConstruct();
2577 OpBuilder builder(body);
2578 for (
auto *block : constructBlocks) {
2581 auto *newBlock = builder.createBlock(&body.
back());
2582 mapper.
map(block, newBlock);
2583 LLVM_DEBUG(logger.startLine() <<
"[cf] cloned block " << newBlock
2584 <<
" from block " << block <<
"\n");
2586 for (BlockArgument blockArg : block->getArguments()) {
2588 newBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2589 mapper.
map(blockArg, newArg);
2590 LLVM_DEBUG(logger.startLine() <<
"[cf] remapped block argument "
2591 << blockArg <<
" to " << newArg <<
"\n");
2594 LLVM_DEBUG(logger.startLine()
2595 <<
"[cf] block " << block <<
" is a function entry block\n");
2598 for (
auto &op : *block)
2599 newBlock->push_back(op.
clone(mapper));
2603 auto remapOperands = [&](Operation *op) {
2605 if (Value mappedOp = mapper.
lookupOrNull(operand.get()))
2606 operand.set(mappedOp);
2609 succOp.set(mappedOp);
2611 for (
auto &block : body)
2612 block.walk(remapOperands);
2620 headerBlock->replaceAllUsesWith(mergeBlock);
2623 logger.startLine() <<
"[cf] after cloning and fixing references:\n";
2624 headerBlock->getParentOp()->print(logger.getOStream());
2625 logger.startLine() <<
"\n";
2629 if (!mergeBlock->args_empty()) {
2630 return mergeBlock->getParentOp()->emitError(
2631 "OpPhi in loop merge block unsupported");
2637 for (BlockArgument blockArg : headerBlock->getArguments())
2638 mergeBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2642 SmallVector<Value, 4> blockArgs;
2643 if (!headerBlock->args_empty())
2644 blockArgs = {mergeBlock->args_begin(), mergeBlock->args_end()};
2648 builder.setInsertionPointToEnd(&body.front());
2649 spirv::BranchOp::create(builder, location, mapper.
lookupOrNull(headerBlock),
2650 ArrayRef<Value>(blockArgs));
2655 SmallVector<Value> valuesToYield;
2658 SmallVector<Value> outsideUses;
2672 for (BlockArgument blockArg : mergeBlock->getArguments()) {
2677 body.back().addArgument(blockArg.getType(), blockArg.getLoc());
2678 valuesToYield.push_back(body.back().getArguments().back());
2679 outsideUses.push_back(blockArg);
2684 LLVM_DEBUG(logger.startLine() <<
"[cf] cleaning up blocks after clone\n");
2687 for (
auto *block : constructBlocks)
2688 block->dropAllReferences();
2693 for (
Block *block : constructBlocks) {
2694 for (Operation &op : *block) {
2698 outsideUses.push_back(
result);
2701 for (BlockArgument &arg : block->getArguments()) {
2702 if (!arg.use_empty()) {
2704 outsideUses.push_back(arg);
2709 assert(valuesToYield.size() == outsideUses.size());
2713 if (!valuesToYield.empty()) {
2714 LLVM_DEBUG(logger.startLine()
2715 <<
"[cf] yielding values from the selection / loop region\n");
2718 auto mergeOps = body.back().getOps<spirv::MergeOp>();
2719 Operation *merge = llvm::getSingleElement(mergeOps);
2721 merge->setOperands(valuesToYield);
2729 builder.setInsertionPoint(&mergeBlock->front());
2731 Operation *newOp =
nullptr;
2734 newOp = spirv::LoopOp::create(builder, location,
2736 static_cast<spirv::LoopControl
>(control));
2738 newOp = spirv::SelectionOp::create(
2740 static_cast<spirv::SelectionControl
>(control));
2750 for (
unsigned i = 0, e = outsideUses.size(); i != e; ++i)
2751 outsideUses[i].replaceAllUsesWith(op->
getResult(i));
2757 mergeBlock->eraseArguments(0, mergeBlock->getNumArguments());
2764 for (
auto *block : constructBlocks) {
2765 if (!block->use_empty())
2766 return emitError(block->getParent()->getLoc(),
2767 "failed control flow structurization: "
2768 "block has uses outside of the "
2769 "enclosing selection/loop construct");
2770 for (Operation &op : *block)
2772 return op.
emitOpError(
"failed control flow structurization: value has "
2773 "uses outside of the "
2774 "enclosing selection/loop construct");
2775 for (BlockArgument &arg : block->getArguments())
2776 if (!arg.use_empty())
2777 return emitError(arg.getLoc(),
"failed control flow structurization: "
2778 "block argument has uses outside of the "
2779 "enclosing selection/loop construct");
2783 for (
auto *block : constructBlocks) {
2823 auto updateMergeInfo = [&](
Block *block) -> WalkResult {
2824 auto it = blockMergeInfo.find(block);
2825 if (it != blockMergeInfo.end()) {
2827 Location loc = it->second.loc;
2831 return emitError(loc,
"failed control flow structurization: nested "
2832 "loop header block should be remapped!");
2834 Block *newContinue = it->second.continueBlock;
2838 return emitError(loc,
"failed control flow structurization: nested "
2839 "loop continue block should be remapped!");
2842 Block *newMerge = it->second.mergeBlock;
2844 newMerge = mappedTo;
2848 blockMergeInfo.
erase(it);
2849 blockMergeInfo.try_emplace(newHeader, loc, it->second.control, newMerge,
2856 if (block->walk(updateMergeInfo).wasInterrupted())
2864 LLVM_DEBUG(logger.startLine() <<
"[cf] changing entry block " << block
2865 <<
" to only contain a spirv.Branch op\n");
2869 builder.setInsertionPointToEnd(block);
2870 spirv::BranchOp::create(builder, location, mergeBlock);
2872 LLVM_DEBUG(logger.startLine() <<
"[cf] erasing block " << block <<
"\n");
2877 LLVM_DEBUG(logger.startLine()
2878 <<
"[cf] after structurizing construct with header block "
2879 << headerBlock <<
":\n"
2888 <<
"//----- [phi] start wiring up block arguments -----//\n";
2894 for (
const auto &info : blockPhiInfo) {
2895 Block *block = info.first.first;
2899 logger.startLine() <<
"[phi] block " << block <<
"\n";
2900 logger.startLine() <<
"[phi] before creating block argument:\n";
2902 logger.startLine() <<
"\n";
2908 opBuilder.setInsertionPoint(op);
2911 blockArgs.reserve(phiInfo.size());
2912 for (uint32_t valueId : phiInfo) {
2914 blockArgs.push_back(value);
2915 LLVM_DEBUG(logger.startLine() <<
"[phi] block argument " << value
2916 <<
" id = " << valueId <<
"\n");
2918 return emitError(unknownLoc,
"OpPhi references undefined value!");
2922 if (
auto branchOp = dyn_cast<spirv::BranchOp>(op)) {
2924 spirv::BranchOp::create(opBuilder, branchOp.getLoc(),
2925 branchOp.getTarget(), blockArgs);
2927 }
else if (
auto branchCondOp = dyn_cast<spirv::BranchConditionalOp>(op)) {
2928 assert((branchCondOp.getTrueBlock() ==
target ||
2929 branchCondOp.getFalseBlock() ==
target) &&
2930 "expected target to be either the true or false target");
2931 if (
target == branchCondOp.getTrueTarget())
2932 spirv::BranchConditionalOp::create(
2933 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2934 blockArgs, branchCondOp.getFalseBlockArguments(),
2935 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueTarget(),
2936 branchCondOp.getFalseTarget());
2938 spirv::BranchConditionalOp::create(
2939 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2940 branchCondOp.getTrueBlockArguments(), blockArgs,
2941 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueBlock(),
2942 branchCondOp.getFalseBlock());
2944 branchCondOp.erase();
2945 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(op)) {
2946 if (
target == switchOp.getDefaultTarget()) {
2950 spirv::SwitchOp::create(
2951 opBuilder, switchOp.getLoc(), switchOp.getSelector(),
2952 switchOp.getDefaultTarget(), blockArgs, literals,
2953 switchOp.getTargets(), targetOperands);
2957 auto it = llvm::find(targets,
target);
2958 assert(it != targets.end());
2959 size_t index = std::distance(targets.begin(), it);
2960 switchOp.getTargetOperandsMutable(
index).assign(blockArgs);
2963 return emitError(unknownLoc,
"unimplemented terminator for Phi creation");
2967 logger.startLine() <<
"[phi] after creating block argument:\n";
2969 logger.startLine() <<
"\n";
2972 blockPhiInfo.clear();
2977 <<
"//--- [phi] completed wiring up block arguments ---//\n";
2985 for (
auto [block, mergeInfo] : blockMergeInfoCopy) {
2987 if (mergeInfo.continueBlock)
2990 if (!block->mightHaveTerminator())
2993 Operation *terminator = block->getTerminator();
2996 if (!isa<spirv::BranchConditionalOp, spirv::SwitchOp>(terminator))
3000 bool splitHeaderMergeBlock =
false;
3001 for (
const auto &[_, mergeInfo] : blockMergeInfo) {
3002 if (mergeInfo.mergeBlock == block)
3003 splitHeaderMergeBlock =
true;
3010 if (!llvm::hasSingleElement(*block) || splitHeaderMergeBlock) {
3013 spirv::BranchOp::create(builder, block->getParent()->getLoc(), newBlock);
3017 blockMergeInfo.erase(block);
3018 blockMergeInfo.try_emplace(newBlock, mergeInfo);
3026 if (!options.enableControlFlowStructurization) {
3030 <<
"//----- [cf] skip structurizing control flow -----//\n";
3038 <<
"//----- [cf] start structurizing control flow -----//\n";
3043 logger.startLine() <<
"[cf] split conditional blocks\n";
3044 logger.startLine() <<
"\n";
3051 while (!blockMergeInfo.empty()) {
3052 Block *headerBlock = blockMergeInfo.
begin()->first;
3056 logger.startLine() <<
"[cf] header block " << headerBlock <<
":\n";
3057 headerBlock->
print(logger.getOStream());
3058 logger.startLine() <<
"\n";
3062 assert(mergeBlock &&
"merge block cannot be nullptr");
3064 return emitError(unknownLoc,
"OpPhi in loop merge block unimplemented");
3066 logger.startLine() <<
"[cf] merge block " << mergeBlock <<
":\n";
3067 mergeBlock->print(logger.getOStream());
3068 logger.startLine() <<
"\n";
3072 LLVM_DEBUG(
if (continueBlock) {
3073 logger.startLine() <<
"[cf] continue block " << continueBlock <<
":\n";
3074 continueBlock->print(logger.getOStream());
3075 logger.startLine() <<
"\n";
3079 blockMergeInfo.
erase(blockMergeInfo.begin());
3080 ControlFlowStructurizer structurizer(mergeInfo.
loc, mergeInfo.
control,
3081 blockMergeInfo, headerBlock,
3082 mergeBlock, continueBlock
3088 if (failed(structurizer.structurize()))
3095 <<
"//--- [cf] completed structurizing control flow ---//\n";
3108 auto fileName = debugInfoMap.lookup(debugLine->fileID).str();
3109 if (fileName.empty())
3110 fileName =
"<unknown>";
3122 if (operands.size() != 3)
3123 return emitError(unknownLoc,
"OpLine must have 3 operands");
3124 debugLine =
DebugLine{operands[0], operands[1], operands[2]};
3132 if (operands.size() < 2)
3133 return emitError(unknownLoc,
"OpString needs at least 2 operands");
3135 if (!debugInfoMap.lookup(operands[0]).empty())
3137 "duplicate debug string found for result <id> ")
3140 unsigned wordIndex = 1;
3142 if (wordIndex != operands.size())
3144 "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 setInherentOrDiscardableAttr(Operation *op, StringAttr name, Attribute value)
static LogicalResult deserializeCacheControlDecoration(Location loc, OpBuilder &opBuilder, DenseMap< uint32_t, NamedAttrList > &decorations, ArrayRef< uint32_t > words, StringAttr symbol, StringRef decorationName, StringRef cacheControlKind)
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
This class represents an argument of a Block.
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)
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
An attribute that represents a reference to a dense integer vector or tensor object.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
A symbol reference with a reference path containing a single element.
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
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.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
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.
void setInherentAttr(Operation *op, StringAttr name, Attribute value) const
std::optional< Attribute > getInherentAttr(Operation *op, StringRef name) const
Lookup an inherent attribute by name, this method isn't recommended and may be removed in the future.
Operation is the basic unit of execution within MLIR.
MutableArrayRef< BlockOperand > getBlockOperands()
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
bool use_empty()
Returns true if this operation has no uses.
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
MutableArrayRef< OpOperand > getOpOperands()
OperationName getName()
The name of an operation is the key identifier for it.
void print(raw_ostream &os, const OpPrintingFlags &flags={})
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
result_range getResults()
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
BlockListType & getBlocks()
BlockListType::iterator iterator
void takeBody(Region &other)
Takes body of another region (that region will have no body after this operation completes).
This class implements the successor iterators for Block.
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...
static WalkResult advance()
static ArrayType get(Type elementType, unsigned elementCount)
static CooperativeMatrixType get(Type elementType, uint32_t rows, uint32_t columns, Scope scope, CooperativeMatrixUseKHR use)
LogicalResult wireUpBlockArgument()
Creates block arguments on predecessors previously recorded when handling OpPhi instructions.
Value materializeSpecConstantOperation(uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID, ArrayRef< uint32_t > enclosedOpOperands)
Materializes/emits an OpSpecConstantOp instruction.
LogicalResult processOpTypePointer(ArrayRef< uint32_t > operands)
Value getValue(uint32_t id)
Get the Value associated with a result <id>.
LogicalResult processMatrixType(ArrayRef< uint32_t > operands)
LogicalResult processGlobalVariable(ArrayRef< uint32_t > operands)
Processes the OpVariable instructions at current offset into binary.
std::optional< SpecConstOperationMaterializationInfo > getSpecConstantOperation(uint32_t id)
Gets the info needed to materialize the spec constant operation op associated with the given <id>.
LogicalResult processConstantNull(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpConstantNull instruction with the given operands.
LogicalResult processSpecConstantComposite(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSpecConstantComposite instruction with the given operands.
LogicalResult processInstruction(spirv::Opcode opcode, ArrayRef< uint32_t > operands, bool deferInstructions=true)
Processes a SPIR-V instruction with the given opcode and operands.
LogicalResult processBranchConditional(ArrayRef< uint32_t > operands)
spirv::GlobalVariableOp getGlobalVariable(uint32_t id)
Gets the global variable associated with a result <id> of OpVariable.
LogicalResult createGraphBlock(uint32_t graphID)
Creates a block for graph with the given graphID.
LogicalResult processStructType(ArrayRef< uint32_t > operands)
LogicalResult processGraphARM(ArrayRef< uint32_t > operands)
LogicalResult processSamplerType(ArrayRef< uint32_t > operands)
LogicalResult setFunctionArgAttrs(uint32_t argID, SmallVectorImpl< Attribute > &argAttrs, size_t argIndex)
Sets the function argument's attributes.
LogicalResult structurizeControlFlow()
Extracts blocks belonging to a structured selection/loop into a spirv.mlir.selection/spirv....
LogicalResult processLabel(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpLabel instruction with the given operands.
LogicalResult processSampledImageType(ArrayRef< uint32_t > operands)
LogicalResult processTensorARMType(ArrayRef< uint32_t > operands)
std::optional< spirv::GraphConstantARMOpMaterializationInfo > getGraphConstantARM(uint32_t id)
Gets the GraphConstantARM ID attribute and result type with the given result <id>.
std::optional< std::pair< Attribute, Type > > getConstant(uint32_t id)
Gets the constant's attribute and type associated with the given <id>.
LogicalResult processType(spirv::Opcode opcode, ArrayRef< uint32_t > operands)
Processes a SPIR-V type instruction with given opcode and operands and registers the type into module...
LogicalResult processLoopMerge(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpLoopMerge instruction with the given operands.
LogicalResult processArrayType(ArrayRef< uint32_t > operands)
LogicalResult sliceInstruction(spirv::Opcode &opcode, ArrayRef< uint32_t > &operands, std::optional< spirv::Opcode > expectedOpcode=std::nullopt)
Slices the first instruction out of binary and returns its opcode and operands via opcode and operand...
spirv::SpecConstantCompositeOp getSpecConstantComposite(uint32_t id)
Gets the composite specialization constant with the given result <id>.
LogicalResult processNamedBarrierType(ArrayRef< uint32_t > operands)
SmallVector< uint32_t, 2 > BlockPhiInfo
For OpPhi instructions, we use block arguments to represent them.
LogicalResult processSpecConstantCompositeReplicateEXT(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSpecConstantCompositeReplicateEXT instruction with the given operands.
LogicalResult processCooperativeMatrixTypeKHR(ArrayRef< uint32_t > operands)
LogicalResult processGraphEntryPointARM(ArrayRef< uint32_t > operands)
LogicalResult processFunction(ArrayRef< uint32_t > operands)
Creates a deserializer for the given SPIR-V binary module.
StringAttr getSymbolDecoration(StringRef decorationName)
Gets the symbol name from the name of decoration.
Block * getOrCreateBlock(uint32_t id)
Gets or creates the block corresponding to the given label <id>.
bool isVoidType(Type type) const
Returns true if the given type is for SPIR-V void type.
std::string getSpecConstantSymbol(uint32_t id)
Returns a symbol to be used for the specialization constant with the given result <id>.
LogicalResult processDebugString(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpString instruction with the given operands.
LogicalResult processPhi(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpPhi instruction with the given operands.
std::string getFunctionSymbol(uint32_t id)
Returns a symbol to be used for the function name with the given result <id>.
void clearDebugLine()
Discontinues any source-level location information that might be active from a previous OpLine instru...
LogicalResult processFunctionType(ArrayRef< uint32_t > operands)
IntegerAttr getConstantInt(uint32_t id)
Gets the constant's integer attribute with the given <id>.
LogicalResult processTypeForwardPointer(ArrayRef< uint32_t > operands)
LogicalResult processSwitch(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSwitch instruction with the given operands.
LogicalResult processGraphEndARM(ArrayRef< uint32_t > operands)
LogicalResult processImageType(ArrayRef< uint32_t > operands)
LogicalResult processConstantComposite(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpConstantComposite instruction with the given operands.
spirv::SpecConstantOp createSpecConstant(Location loc, uint32_t resultID, TypedAttr defaultValue)
Creates a spirv::SpecConstantOp.
Block * getBlock(uint32_t id) const
Returns the block for the given label <id>.
LogicalResult processGraphTypeARM(ArrayRef< uint32_t > operands)
LogicalResult processBranch(ArrayRef< uint32_t > operands)
std::optional< std::pair< Attribute, Type > > getConstantCompositeReplicate(uint32_t id)
Gets the replicated composite constant's attribute and type associated with the given <id>.
LogicalResult processFunctionEnd(ArrayRef< uint32_t > operands)
Processes OpFunctionEnd and finalizes function.
LogicalResult processRuntimeArrayType(ArrayRef< uint32_t > operands)
LogicalResult processSpecConstantOperation(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSpecConstantOp instruction with the given operands.
LogicalResult processConstant(ArrayRef< uint32_t > operands, bool isSpec)
Processes a SPIR-V Op{|Spec}Constant instruction with the given operands.
Location createFileLineColLoc(OpBuilder opBuilder)
Creates a FileLineColLoc with the OpLine location information.
LogicalResult processGraphConstantARM(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpGraphConstantARM instruction with the given operands.
LogicalResult processConstantBool(bool isTrue, ArrayRef< uint32_t > operands, bool isSpec)
Processes a SPIR-V Op{|Spec}Constant{True|False} instruction with the given operands.
spirv::SpecConstantOp getSpecConstant(uint32_t id)
Gets the specialization constant with the given result <id>.
LogicalResult processConstantCompositeReplicateEXT(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpConstantCompositeReplicateEXT instruction with the given operands.
LogicalResult processSelectionMerge(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSelectionMerge instruction with the given operands.
LogicalResult processOpGraphSetOutputARM(ArrayRef< uint32_t > operands)
LogicalResult processDebugLine(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpLine instruction with the given operands.
LogicalResult splitSelectionHeader()
Move a conditional branch or a switch into a separate basic block to avoid unnecessary sinking of def...
std::string getGraphSymbol(uint32_t id)
Returns a symbol to be used for the graph name with the given result <id>.
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 MatrixType get(Type columnType, uint32_t columnCount)
static NamedBarrierType get(MLIRContext *context)
static PointerType get(Type pointeeType, StorageClass storageClass)
static RuntimeArrayType get(Type elementType)
static SampledImageType get(Type imageType)
static SamplerType get(MLIRContext *context)
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={}, ArrayRef< StructDecorationInfo > structDecorations={})
Construct a literal StructType with at least one member.
static TensorArmType get(ArrayRef< int64_t > shape, Type elementType)
The OpAsmOpInterface, see OpAsmInterface.td for more details.
SmallVector< Operation * > mergeOps
Computation function returning, for the op currently being tiled or fused, the per-iteration-domain-d...
constexpr uint32_t kMagicNumber
SPIR-V magic number.
llvm::MapVector< Block *, BlockMergeInfo > BlockMergeInfoMap
Map from a selection/loop's header block to its merge (and continue) target.
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.
llvm::SetVector< T, Vector, Set, N > SetVector
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
std::string debugString(T &&op)
A struct for containing a header block's merge and continue targets.
A struct for containing OpLine instruction information.
A struct that collects the info needed to materialize/emit a GraphConstantARMOp.
A struct that collects the info needed to materialize/emit a SpecConstantOperation op.