24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/Sequence.h"
26#include "llvm/ADT/SmallVector.h"
27#include "llvm/ADT/StringExtras.h"
28#include "llvm/ADT/bit.h"
29#include "llvm/Support/Debug.h"
30#include "llvm/Support/SaveAndRestore.h"
31#include "llvm/Support/raw_ostream.h"
37#define DEBUG_TYPE "spirv-deserialization"
46 isa_and_nonnull<spirv::FuncOp>(block->
getParentOp());
65 : binary(binary), context(context), unknownLoc(UnknownLoc::
get(context)),
66 module(createModuleOp()), opBuilder(module->getRegion()),
options(
options)
74LogicalResult spirv::Deserializer::deserialize() {
78 <<
"//+++---------- start deserialization ----------+++//\n";
81 if (
failed(processHeader()))
84 spirv::Opcode opcode = spirv::Opcode::OpNop;
85 ArrayRef<uint32_t> operands;
86 auto binarySize = binary.size();
87 while (curOffset < binarySize) {
97 assert(curOffset == binarySize &&
98 "deserializer should never index beyond the binary end");
100 for (
auto &deferred : deferredInstructions) {
106 if (
failed(resolveDeferredIdDecorations()))
109 if (!functionParameterAttrs.empty())
110 return emitError(unknownLoc,
"FuncParamAttr decoration on result <id> ")
111 << functionParameterAttrs.begin()->first
112 <<
" is not supported: expected OpFunctionParameter or OpFunction";
116 LLVM_DEBUG(logger.startLine()
117 <<
"//+++-------- completed deserialization --------+++//\n");
121OwningOpRef<spirv::ModuleOp> spirv::Deserializer::collect() {
122 return std::move(module);
129OwningOpRef<spirv::ModuleOp> spirv::Deserializer::createModuleOp() {
130 OpBuilder builder(context);
131 OperationState state(unknownLoc, spirv::ModuleOp::getOperationName());
132 spirv::ModuleOp::build(builder, state);
136LogicalResult spirv::Deserializer::processHeader() {
139 "SPIR-V binary module must have a 5-word header");
142 return emitError(unknownLoc,
"incorrect magic number");
145 uint32_t majorVersion = (binary[1] << 8) >> 24;
146 uint32_t minorVersion = (binary[1] << 16) >> 24;
147 if (majorVersion == 1) {
148 switch (minorVersion) {
149#define MIN_VERSION_CASE(v) \
151 version = spirv::Version::V_1_##v; \
161#undef MIN_VERSION_CASE
163 return emitError(unknownLoc,
"unsupported SPIR-V minor version: ")
167 return emitError(unknownLoc,
"unsupported SPIR-V major version: ")
177spirv::Deserializer::processCapability(ArrayRef<uint32_t> operands) {
178 if (operands.size() != 1)
179 return emitError(unknownLoc,
"OpCapability must have one parameter");
181 auto cap = spirv::symbolizeCapability(operands[0]);
183 return emitError(unknownLoc,
"unknown capability: ") << operands[0];
185 capabilities.insert(*cap);
189LogicalResult spirv::Deserializer::processExtension(ArrayRef<uint32_t> words) {
193 "OpExtension must have a literal string for the extension name");
196 unsigned wordIndex = 0;
198 if (wordIndex != words.size())
200 "unexpected trailing words in OpExtension instruction");
201 auto ext = spirv::symbolizeExtension(extName);
203 return emitError(unknownLoc,
"unknown extension: ") << extName;
205 extensions.insert(*ext);
210spirv::Deserializer::processExtInstImport(ArrayRef<uint32_t> words) {
211 if (words.size() < 2) {
213 "OpExtInstImport must have a result <id> and a literal "
214 "string for the extended instruction set name");
217 unsigned wordIndex = 1;
219 if (wordIndex != words.size()) {
221 "unexpected trailing words in OpExtInstImport");
226void spirv::Deserializer::attachVCETriple() {
227 module->setVceTripleAttr(spirv::VerCapExtAttr::get(
228 version, capabilities.getArrayRef(), extensions.getArrayRef(), context));
232spirv::Deserializer::processMemoryModel(ArrayRef<uint32_t> operands) {
233 if (operands.size() != 2)
234 return emitError(unknownLoc,
"OpMemoryModel must have two operands");
236 module->setAddressingModel(
237 static_cast<spirv::AddressingModel>(operands.front()));
239 module->setMemoryModel(static_cast<spirv::MemoryModel>(operands.back()));
244template <
typename AttrTy,
typename EnumAttrTy,
typename EnumTy>
248 StringAttr symbol, StringRef decorationName, StringRef cacheControlKind) {
249 if (words.size() != 4) {
250 return emitError(loc,
"OpDecorate with ")
251 << decorationName <<
" needs a cache control integer literal and a "
252 << cacheControlKind <<
" cache control literal";
254 unsigned cacheLevel = words[2];
255 auto cacheControlAttr =
static_cast<EnumTy
>(words[3]);
256 auto value = opBuilder.
getAttr<AttrTy>(cacheLevel, cacheControlAttr);
259 dyn_cast_or_null<ArrayAttr>(decorations[words[0]].
get(symbol)))
260 llvm::append_range(attrs, attrList);
261 attrs.push_back(value);
262 decorations[words[0]].set(symbol, opBuilder.
getArrayAttr(attrs));
266LogicalResult spirv::Deserializer::processDecoration(ArrayRef<uint32_t> words) {
270 if (words.size() < 2) {
272 unknownLoc,
"OpDecorate must have at least result <id> and Decoration");
274 auto decorationName =
275 stringifyDecoration(
static_cast<spirv::Decoration
>(words[1]));
276 if (decorationName.empty()) {
277 return emitError(unknownLoc,
"invalid Decoration code : ") << words[1];
279 auto symbol = getSymbolDecoration(decorationName);
280 switch (
static_cast<spirv::Decoration
>(words[1])) {
281 case spirv::Decoration::FPFastMathMode:
282 if (words.size() != 3) {
283 return emitError(unknownLoc,
"OpDecorate with ")
284 << decorationName <<
" needs a single integer literal";
286 decorations[words[0]].set(
287 symbol, FPFastMathModeAttr::get(opBuilder.getContext(),
288 static_cast<FPFastMathMode
>(words[2])));
290 case spirv::Decoration::FPRoundingMode:
291 if (words.size() != 3) {
292 return emitError(unknownLoc,
"OpDecorate with ")
293 << decorationName <<
" needs a single integer literal";
295 decorations[words[0]].set(
296 symbol, FPRoundingModeAttr::get(opBuilder.getContext(),
297 static_cast<FPRoundingMode
>(words[2])));
299 case spirv::Decoration::FuncParamAttr: {
300 if (words.size() != 3)
302 "OpDecorate with FuncParamAttr needs a single "
303 "function parameter attribute");
304 auto value = spirv::symbolizeFunctionParameterAttribute(words[2]);
306 return emitError(unknownLoc,
"invalid FunctionParameterAttribute code: ")
308 SmallVector<Attribute> attrs;
309 if (
auto previous = functionParameterAttrs.lookup(words[0]))
310 llvm::append_range(attrs, previous);
312 spirv::FunctionParameterAttributeAttr::get(context, *value));
313 functionParameterAttrs[words[0]] = opBuilder.getArrayAttr(attrs);
316 case spirv::Decoration::Alignment:
317 case spirv::Decoration::DescriptorSet:
318 case spirv::Decoration::Binding:
319 case spirv::Decoration::Location:
320 case spirv::Decoration::SpecId:
321 case spirv::Decoration::Index:
322 case spirv::Decoration::Offset:
323 case spirv::Decoration::XfbBuffer:
324 case spirv::Decoration::XfbStride:
325 if (words.size() != 3) {
326 return emitError(unknownLoc,
"OpDecorate with ")
327 << decorationName <<
" needs a single integer literal";
329 decorations[words[0]].set(
330 symbol, opBuilder.getI32IntegerAttr(
static_cast<int32_t
>(words[2])));
332 case spirv::Decoration::BuiltIn:
333 if (words.size() != 3) {
334 return emitError(unknownLoc,
"OpDecorate with ")
335 << decorationName <<
" needs a single integer literal";
337 decorations[words[0]].set(
338 symbol, opBuilder.getStringAttr(
339 stringifyBuiltIn(
static_cast<spirv::BuiltIn
>(words[2]))));
341 case spirv::Decoration::ArrayStride:
342 if (words.size() != 3) {
343 return emitError(unknownLoc,
"OpDecorate with ")
344 << decorationName <<
" needs a single integer literal";
346 typeDecorations[words[0]] = words[2];
348 case spirv::Decoration::LinkageAttributes: {
349 if (words.size() < 4) {
350 return emitError(unknownLoc,
"OpDecorate with ")
352 <<
" needs at least 1 string and 1 integer literal";
360 unsigned wordIndex = 2;
362 auto linkageTypeAttr = opBuilder.getAttr<::mlir::spirv::LinkageTypeAttr>(
363 static_cast<::mlir::spirv::LinkageType
>(words[wordIndex++]));
364 auto linkageAttr = opBuilder.getAttr<::mlir::spirv::LinkageAttributesAttr>(
365 StringAttr::get(context, linkageName), linkageTypeAttr);
366 decorations[words[0]].set(symbol, dyn_cast<Attribute>(linkageAttr));
369 case spirv::Decoration::Aliased:
370 case spirv::Decoration::AliasedPointer:
371 case spirv::Decoration::Block:
372 case spirv::Decoration::BufferBlock:
373 case spirv::Decoration::CPacked:
374 case spirv::Decoration::Flat:
375 case spirv::Decoration::NonReadable:
376 case spirv::Decoration::NonWritable:
377 case spirv::Decoration::NoPerspective:
378 case spirv::Decoration::NoSignedWrap:
379 case spirv::Decoration::NoUnsignedWrap:
380 case spirv::Decoration::RelaxedPrecision:
381 case spirv::Decoration::Restrict:
382 case spirv::Decoration::RestrictPointer:
383 case spirv::Decoration::NoContraction:
384 case spirv::Decoration::Constant:
385 case spirv::Decoration::Invariant:
386 case spirv::Decoration::Patch:
387 case spirv::Decoration::Coherent:
388 case spirv::Decoration::Volatile:
389 case spirv::Decoration::SaturatedConversion:
390 if (words.size() != 2) {
391 return emitError(unknownLoc,
"OpDecorate with ")
392 << decorationName <<
" needs a single target <id>";
394 decorations[words[0]].set(symbol, opBuilder.getUnitAttr());
396 case spirv::Decoration::CacheControlLoadINTEL: {
398 CacheControlLoadINTELAttr, LoadCacheControlAttr, LoadCacheControl>(
399 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
405 case spirv::Decoration::CacheControlStoreINTEL: {
407 CacheControlStoreINTELAttr, StoreCacheControlAttr, StoreCacheControl>(
408 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
414 case spirv::Decoration::AlignmentId:
415 case spirv::Decoration::MaxByteOffsetId:
416 case spirv::Decoration::CounterBuffer:
417 if (words.size() != 3) {
418 return emitError(unknownLoc,
"OpDecorateId with ")
419 << decorationName <<
" needs a single <id> operand";
421 pendingIdDecorations.push_back({words[0],
422 static_cast<spirv::Decoration
>(words[1]),
423 words[2], unknownLoc});
426 return emitError(unknownLoc,
"unhandled Decoration : '") << decorationName;
431LogicalResult spirv::Deserializer::resolveDeferredIdDecorations() {
432 for (
const DeferredIdDecoration &entry : pendingIdDecorations) {
433 StringRef decorationName = stringifyDecoration(entry.decoration);
434 StringAttr symbol = getSymbolDecoration(decorationName);
438 StringRef operandSymName;
439 if (spirv::GlobalVariableOp varOp =
440 globalVariableMap.lookup(entry.operandID))
441 operandSymName = varOp.getSymName();
442 else if (spirv::SpecConstantOp specOp =
443 specConstMap.lookup(entry.operandID))
444 operandSymName = specOp.getSymName();
446 return emitError(entry.loc,
"OpDecorateId with ")
447 << decorationName <<
" references <id> " << entry.operandID
448 <<
" which is not a global variable or specialization constant";
455 Operation *targetOp =
nullptr;
456 if (spirv::GlobalVariableOp varOp =
457 globalVariableMap.lookup(entry.targetID))
459 else if (spirv::SpecConstantOp specOp = specConstMap.lookup(entry.targetID))
461 else if (spirv::FuncOp fnOp = funcMap.lookup(entry.targetID))
463 else if (Value v = valueMap.lookup(entry.targetID))
464 targetOp = v.getDefiningOp();
467 return emitError(entry.loc,
"OpDecorateId with ")
468 << decorationName <<
" references unknown target <id> "
477spirv::Deserializer::processMemberDecoration(ArrayRef<uint32_t> words) {
479 if (words.size() < 3) {
481 "OpMemberDecorate must have at least 3 operands");
484 auto decoration =
static_cast<spirv::Decoration
>(words[2]);
485 if (decoration == spirv::Decoration::Offset && words.size() != 4) {
487 " missing offset specification in OpMemberDecorate with "
488 "Offset decoration");
490 ArrayRef<uint32_t> decorationOperands;
491 if (words.size() > 3) {
492 decorationOperands = words.slice(3);
494 memberDecorationMap[words[0]][words[1]][decoration] = decorationOperands;
498LogicalResult spirv::Deserializer::processMemberName(ArrayRef<uint32_t> words) {
499 if (words.size() < 3) {
500 return emitError(unknownLoc,
"OpMemberName must have at least 3 operands");
502 unsigned wordIndex = 2;
504 if (wordIndex != words.size()) {
506 "unexpected trailing words in OpMemberName instruction");
508 memberNameMap[words[0]][words[1]] = name;
514 spirv::DecorationAttr foundDecorationAttr;
516 if (
auto it = functionParameterAttrs.find(argID);
517 it != functionParameterAttrs.end()) {
519 functionParameterAttrs.erase(it);
523 spirv::Decoration::Alignment))) {
528 if (!isa<UnitAttr>(decAttr.getValue()))
529 return emitError(unknownLoc,
"unhandled decoration '")
530 << decAttr.getName().getValue()
531 <<
"' on function argument with result <id> " << argID;
534 if (foundDecorationAttr)
535 return emitError(unknownLoc,
"already found a decoration for function "
536 "argument with result <id> ")
539 std::optional<spirv::Decoration> decoration = spirv::symbolizeDecoration(
540 llvm::convertToCamelFromSnakeCase(decAttr.getName(),
true));
542 return emitError(unknownLoc,
"unknown decoration '")
543 << decAttr.getName().getValue()
544 <<
"' on function argument with result <id> " << argID;
545 foundDecorationAttr = spirv::DecorationAttr::get(context, *decoration);
548 if (foundDecorationAttr)
549 attributes.
set(spirv::DecorationAttr::name, foundDecorationAttr);
557 return emitError(unknownLoc,
"found function inside function");
561 if (operands.size() != 4) {
562 return emitError(unknownLoc,
"OpFunction must have 4 parameters");
566 return emitError(unknownLoc,
"undefined result type from <id> ")
570 uint32_t fnID = operands[1];
571 if (funcMap.count(fnID)) {
572 return emitError(unknownLoc,
"duplicate function definition/declaration");
575 auto fnControl = spirv::symbolizeFunctionControl(operands[2]);
577 return emitError(unknownLoc,
"unknown Function Control: ") << operands[2];
581 if (!fnType || !isa<FunctionType>(fnType)) {
582 return emitError(unknownLoc,
"unknown function type from <id> ")
585 auto functionType = cast<FunctionType>(fnType);
587 if ((
isVoidType(resultType) && functionType.getNumResults() != 0) ||
588 (functionType.getNumResults() == 1 &&
589 functionType.getResult(0) != resultType)) {
590 return emitError(unknownLoc,
"mismatch in function type ")
591 << functionType <<
" and return type " << resultType <<
" specified";
595 auto funcOp = spirv::FuncOp::create(opBuilder, unknownLoc, fnName,
596 functionType, fnControl.value());
597 if (
auto it = functionParameterAttrs.find(fnID);
598 it != functionParameterAttrs.end()) {
599 if (functionType.getNumResults() == 0)
601 "FuncParamAttr decoration requires a non-void function "
604 functionParameterAttrs.erase(it);
607 if (decorations.count(fnID)) {
608 for (
auto attr : decorations[fnID].getAttrs()) {
612 curFunction = funcMap[fnID] = funcOp;
613 auto *entryBlock = funcOp.addEntryBlock();
616 <<
"//===-------------------------------------------===//\n";
617 logger.startLine() <<
"[fn] name: " << fnName <<
"\n";
618 logger.startLine() <<
"[fn] type: " << fnType <<
"\n";
619 logger.startLine() <<
"[fn] ID: " << fnID <<
"\n";
620 logger.startLine() <<
"[fn] entry block: " << entryBlock <<
"\n";
625 argAttrs.resize(functionType.getNumInputs());
628 if (functionType.getNumInputs()) {
629 for (
size_t i = 0, e = functionType.getNumInputs(); i != e; ++i) {
630 auto argType = functionType.getInput(i);
631 spirv::Opcode opcode = spirv::Opcode::OpNop;
634 spirv::Opcode::OpFunctionParameter))) {
637 if (opcode != spirv::Opcode::OpFunctionParameter) {
640 "missing OpFunctionParameter instruction for argument ")
643 if (operands.size() != 2) {
646 "expected result type and result <id> for OpFunctionParameter");
648 auto argDefinedType =
getType(operands[0]);
649 if (!argDefinedType || argDefinedType != argType) {
651 "mismatch in argument type between function type "
653 << functionType <<
" and argument type definition "
654 << argDefinedType <<
" at argument " << i;
657 return emitError(unknownLoc,
"duplicate definition of result <id> ")
664 auto argValue = funcOp.getArgument(i);
665 valueMap[operands[1]] = argValue;
669 if (llvm::any_of(argAttrs, [](
Attribute attr) {
670 auto argAttr = cast<DictionaryAttr>(attr);
671 return !argAttr.empty();
673 funcOp.setArgAttrsAttr(ArrayAttr::get(context, argAttrs));
678 auto linkageAttr = funcOp.getLinkageAttributes();
679 auto hasImportLinkage =
680 linkageAttr && (linkageAttr.value().getLinkageType().
getValue() ==
681 spirv::LinkageType::Import);
682 if (hasImportLinkage)
689 spirv::Opcode opcode = spirv::Opcode::OpNop;
698 spirv::Opcode::OpFunctionEnd))) {
701 if (opcode == spirv::Opcode::OpFunctionEnd) {
704 if (opcode != spirv::Opcode::OpLabel) {
705 return emitError(unknownLoc,
"a basic block must start with OpLabel");
707 if (instOperands.size() != 1) {
708 return emitError(unknownLoc,
"OpLabel should only have result <id>");
710 blockMap[instOperands[0]] = entryBlock;
718 spirv::Opcode::OpFunctionEnd)) &&
719 opcode != spirv::Opcode::OpFunctionEnd) {
724 if (opcode != spirv::Opcode::OpFunctionEnd) {
734 if (!operands.empty()) {
735 return emitError(unknownLoc,
"unexpected operands for OpFunctionEnd");
746 curFunction = std::nullopt;
751 <<
"//===-------------------------------------------===//\n";
758 if (operands.size() < 2) {
760 "missing graph defintion in OpGraphEntryPointARM");
763 unsigned wordIndex = 0;
764 uint32_t graphID = operands[wordIndex++];
765 if (!graphMap.contains(graphID)) {
767 "missing graph definition/declaration with id ")
771 spirv::GraphARMOp graphARM = graphMap[graphID];
773 graphARM.setSymName(name);
774 graphARM.setEntryPoint(
true);
777 for (
int64_t size = operands.size(); wordIndex < size; ++wordIndex) {
779 interface.push_back(SymbolRefAttr::get(arg.getOperation()));
781 return emitError(unknownLoc,
"undefined result <id> ")
782 << operands[wordIndex] <<
" while decoding OpGraphEntryPoint";
788 opBuilder.setInsertionPoint(graphARM);
789 spirv::GraphEntryPointARMOp::create(
790 opBuilder, unknownLoc, SymbolRefAttr::get(opBuilder.getContext(), name),
791 opBuilder.getArrayAttr(interface));
799 return emitError(unknownLoc,
"found graph inside graph");
802 if (operands.size() < 2) {
803 return emitError(unknownLoc,
"OpGraphARM must have at least 2 parameters");
807 if (!type || !isa<GraphType>(type)) {
808 return emitError(unknownLoc,
"unknown graph type from <id> ")
811 auto graphType = cast<GraphType>(type);
812 if (graphType.getNumResults() <= 0) {
813 return emitError(unknownLoc,
"expected at least one result");
816 uint32_t graphID = operands[1];
817 if (graphMap.count(graphID)) {
818 return emitError(unknownLoc,
"duplicate graph definition/declaration");
823 spirv::GraphARMOp::create(opBuilder, unknownLoc, graphName, graphType);
824 curGraph = graphMap[graphID] = graphOp;
825 Block *entryBlock = graphOp.addEntryBlock();
828 <<
"//===-------------------------------------------===//\n";
829 logger.startLine() <<
"[graph] name: " << graphName <<
"\n";
830 logger.startLine() <<
"[graph] type: " << graphType <<
"\n";
831 logger.startLine() <<
"[graph] ID: " << graphID <<
"\n";
832 logger.startLine() <<
"[graph] entry block: " << entryBlock <<
"\n";
837 for (
auto [
index, argType] : llvm::enumerate(graphType.getInputs())) {
838 spirv::Opcode opcode;
841 spirv::Opcode::OpGraphInputARM))) {
844 if (operands.size() != 3) {
845 return emitError(unknownLoc,
"expected result type, result <id> and "
846 "input index for OpGraphInputARM");
850 if (!argDefinedType) {
851 return emitError(unknownLoc,
"unknown operand type <id> ") << operands[0];
854 if (argDefinedType != argType) {
856 "mismatch in argument type between graph type "
858 << graphType <<
" and argument type definition " << argDefinedType
859 <<
" at argument " <<
index;
862 return emitError(unknownLoc,
"duplicate definition of result <id> ")
867 if (!inputIndexAttr) {
869 "unable to read inputIndex value from constant op ")
872 BlockArgument argValue = graphOp.getArgument(inputIndexAttr.getInt());
873 valueMap[operands[1]] = argValue;
876 graphOutputs.resize(graphType.getNumResults());
882 blockMap[graphID] = entryBlock;
889 spirv::Opcode opcode;
899 }
while (opcode != spirv::Opcode::OpGraphEndARM);
906 if (operands.size() != 2) {
909 "expected value id and output index for OpGraphSetOutputARM");
912 uint32_t
id = operands[0];
915 return emitError(unknownLoc,
"could not find result <id> ") << id;
919 if (!outputIndexAttr) {
921 "unable to read outputIndex value from constant op ")
924 graphOutputs[outputIndexAttr.getInt()] = value;
931 spirv::GraphOutputsARMOp::create(opBuilder, unknownLoc, graphOutputs);
934 if (!operands.empty()) {
935 return emitError(unknownLoc,
"unexpected operands for OpGraphEndARM");
939 curGraph = std::nullopt;
940 graphOutputs.clear();
945 <<
"//===-------------------------------------------===//\n";
950std::optional<std::pair<Attribute, Type>>
952 auto constIt = constantMap.find(
id);
953 if (constIt != constantMap.end())
954 return constIt->getSecond();
956 auto replicatedConstIt = constantCompositeReplicateMap.find(
id);
957 if (replicatedConstIt == constantCompositeReplicateMap.end())
960 auto [value, type] = replicatedConstIt->getSecond();
961 auto shapedType = dyn_cast<ShapedType>(type);
967std::optional<std::pair<Attribute, Type>>
969 if (
auto it = constantCompositeReplicateMap.find(
id);
970 it != constantCompositeReplicateMap.end())
975std::optional<spirv::SpecConstOperationMaterializationInfo>
977 auto constIt = specConstOperationMap.find(
id);
978 if (constIt == specConstOperationMap.end())
980 return constIt->getSecond();
984 auto funcName = nameMap.lookup(
id).str();
985 if (funcName.empty()) {
986 funcName =
"spirv_fn_" + std::to_string(
id);
992 std::string graphName = nameMap.lookup(
id).str();
993 if (graphName.empty()) {
994 graphName =
"spirv_graph_" + std::to_string(
id);
1000 auto constName = nameMap.lookup(
id).str();
1001 if (constName.empty()) {
1002 constName =
"spirv_spec_const_" + std::to_string(
id);
1007spirv::SpecConstantOp
1009 TypedAttr defaultValue) {
1011 auto op = spirv::SpecConstantOp::create(opBuilder, unknownLoc, symName,
1014 if (decorations.count(resultID)) {
1015 for (
auto attr : decorations[resultID].getAttrs())
1018 specConstMap[resultID] = op;
1022std::optional<spirv::GraphConstantARMOpMaterializationInfo>
1024 auto graphConstIt = graphConstantMap.find(
id);
1025 if (graphConstIt == graphConstantMap.end())
1026 return std::nullopt;
1027 return graphConstIt->getSecond();
1032 unsigned wordIndex = 0;
1033 if (operands.size() < 3) {
1036 "OpVariable needs at least 3 operands, type, <id> and storage class");
1040 auto type =
getType(operands[wordIndex]);
1042 return emitError(unknownLoc,
"unknown result type <id> : ")
1043 << operands[wordIndex];
1045 auto ptrType = dyn_cast<spirv::PointerType>(type);
1048 "expected a result type <id> to be a spirv.ptr, found : ")
1054 auto variableID = operands[wordIndex];
1055 auto variableName = nameMap.lookup(variableID).str();
1056 if (variableName.empty()) {
1057 variableName =
"spirv_var_" + std::to_string(variableID);
1062 auto storageClass =
static_cast<spirv::StorageClass
>(operands[wordIndex]);
1063 if (ptrType.getStorageClass() != storageClass) {
1064 return emitError(unknownLoc,
"mismatch in storage class of pointer type ")
1065 << type <<
" and that specified in OpVariable instruction : "
1066 << stringifyStorageClass(storageClass);
1073 if (wordIndex < operands.size()) {
1083 return emitError(unknownLoc,
"unknown <id> ")
1084 << operands[wordIndex] <<
"used as initializer";
1086 initializer = SymbolRefAttr::get(op);
1089 if (wordIndex != operands.size()) {
1091 "found more operands than expected when deserializing "
1092 "OpVariable instruction, only ")
1093 << wordIndex <<
" of " << operands.size() <<
" processed";
1096 auto varOp = spirv::GlobalVariableOp::create(
1097 opBuilder, loc, TypeAttr::get(type),
1098 opBuilder.getStringAttr(variableName), initializer);
1101 if (decorations.count(variableID)) {
1102 for (
auto attr : decorations[variableID].getAttrs())
1105 globalVariableMap[variableID] = varOp;
1114 return dyn_cast<IntegerAttr>(constInfo->first);
1118 if (operands.size() < 2) {
1119 return emitError(unknownLoc,
"OpName needs at least 2 operands");
1122 unsigned wordIndex = 1;
1124 if (wordIndex != operands.size()) {
1126 "unexpected trailing words in OpName instruction");
1131 nameMap.emplace_or_assign(operands[0], name);
1142 if (operands.empty()) {
1143 return emitError(unknownLoc,
"type instruction with opcode ")
1144 << spirv::stringifyOpcode(opcode) <<
" needs at least one <id>";
1149 if (typeMap.count(operands[0])) {
1150 return emitError(unknownLoc,
"duplicate definition for result <id> ")
1155 case spirv::Opcode::OpTypeVoid:
1156 if (operands.size() != 1)
1157 return emitError(unknownLoc,
"OpTypeVoid must have no parameters");
1158 typeMap[operands[0]] = opBuilder.getNoneType();
1160 case spirv::Opcode::OpTypeBool:
1161 if (operands.size() != 1)
1162 return emitError(unknownLoc,
"OpTypeBool must have no parameters");
1163 typeMap[operands[0]] = opBuilder.getI1Type();
1165 case spirv::Opcode::OpTypeInt: {
1166 if (operands.size() != 3)
1168 unknownLoc,
"OpTypeInt must have bitwidth and signedness parameters");
1177 auto sign = operands[2] == 1 ? IntegerType::SignednessSemantics::Signed
1178 : IntegerType::SignednessSemantics::Signless;
1179 typeMap[operands[0]] = IntegerType::get(context, operands[1], sign);
1181 case spirv::Opcode::OpTypeFloat: {
1182 if (operands.size() != 2 && operands.size() != 3)
1184 "OpTypeFloat expects either 2 operands (type, bitwidth) "
1185 "or 3 operands (type, bitwidth, encoding), but got ")
1187 uint32_t bitWidth = operands[1];
1190 if (operands.size() == 2) {
1193 floatTy = opBuilder.getF16Type();
1196 floatTy = opBuilder.getF32Type();
1199 floatTy = opBuilder.getF64Type();
1202 return emitError(unknownLoc,
"unsupported OpTypeFloat bitwidth: ")
1207 if (operands.size() == 3) {
1208 if (spirv::FPEncoding(operands[2]) == spirv::FPEncoding::BFloat16KHR &&
1210 floatTy = opBuilder.getBF16Type();
1211 else if (spirv::FPEncoding(operands[2]) ==
1212 spirv::FPEncoding::Float8E4M3EXT &&
1214 floatTy = opBuilder.getF8E4M3FNType();
1215 else if (spirv::FPEncoding(operands[2]) ==
1216 spirv::FPEncoding::Float8E5M2EXT &&
1218 floatTy = opBuilder.getF8E5M2Type();
1220 return emitError(unknownLoc,
"unsupported OpTypeFloat FP encoding: ")
1221 << operands[2] <<
" and bitWidth " << bitWidth;
1224 typeMap[operands[0]] = floatTy;
1226 case spirv::Opcode::OpTypeVector: {
1227 if (operands.size() != 3) {
1230 "OpTypeVector must have element type and count parameters");
1234 return emitError(unknownLoc,
"OpTypeVector references undefined <id> ")
1237 typeMap[operands[0]] = VectorType::get({operands[2]}, elementTy);
1239 case spirv::Opcode::OpTypePointer: {
1242 case spirv::Opcode::OpTypeArray:
1244 case spirv::Opcode::OpTypeCooperativeMatrixKHR:
1246 case spirv::Opcode::OpTypeFunction:
1248 case spirv::Opcode::OpTypeImage:
1250 case spirv::Opcode::OpTypeSampler:
1252 case spirv::Opcode::OpTypeNamedBarrier:
1254 case spirv::Opcode::OpTypeSampledImage:
1256 case spirv::Opcode::OpTypeRuntimeArray:
1258 case spirv::Opcode::OpTypeStruct:
1260 case spirv::Opcode::OpTypeMatrix:
1262 case spirv::Opcode::OpTypeTensorARM:
1264 case spirv::Opcode::OpTypeGraphARM:
1267 return emitError(unknownLoc,
"unhandled type instruction");
1274 if (operands.size() != 3)
1275 return emitError(unknownLoc,
"OpTypePointer must have two parameters");
1277 auto pointeeType =
getType(operands[2]);
1279 return emitError(unknownLoc,
"unknown OpTypePointer pointee type <id> ")
1282 uint32_t typePointerID = operands[0];
1283 auto storageClass =
static_cast<spirv::StorageClass
>(operands[1]);
1286 for (
auto *deferredStructIt = std::begin(deferredStructTypesInfos);
1287 deferredStructIt != std::end(deferredStructTypesInfos);) {
1288 for (
auto *unresolvedMemberIt =
1289 std::begin(deferredStructIt->unresolvedMemberTypes);
1290 unresolvedMemberIt !=
1291 std::end(deferredStructIt->unresolvedMemberTypes);) {
1292 if (unresolvedMemberIt->first == typePointerID) {
1296 deferredStructIt->memberTypes[unresolvedMemberIt->second] =
1297 typeMap[typePointerID];
1298 unresolvedMemberIt =
1299 deferredStructIt->unresolvedMemberTypes.erase(unresolvedMemberIt);
1301 ++unresolvedMemberIt;
1305 if (deferredStructIt->unresolvedMemberTypes.empty()) {
1307 auto structType = deferredStructIt->deferredStructType;
1309 assert(structType &&
"expected a spirv::StructType");
1310 assert(structType.isIdentified() &&
"expected an indentified struct");
1312 if (failed(structType.trySetBody(
1313 deferredStructIt->memberTypes, deferredStructIt->offsetInfo,
1314 deferredStructIt->memberDecorationsInfo,
1315 deferredStructIt->structDecorationsInfo)))
1318 deferredStructIt = deferredStructTypesInfos.erase(deferredStructIt);
1329 if (operands.size() != 3) {
1331 "OpTypeArray must have element type and count parameters");
1336 return emitError(unknownLoc,
"OpTypeArray references undefined <id> ")
1344 return emitError(unknownLoc,
"OpTypeArray count <id> ")
1345 << operands[2] <<
"can only come from normal constant right now";
1348 if (
auto intVal = dyn_cast<IntegerAttr>(countInfo->first)) {
1349 count = intVal.getValue().getZExtValue();
1351 return emitError(unknownLoc,
"OpTypeArray count must come from a "
1352 "scalar integer constant instruction");
1356 elementTy, count, typeDecorations.lookup(operands[0]));
1362 assert(!operands.empty() &&
"No operands for processing function type");
1363 if (operands.size() == 1) {
1364 return emitError(unknownLoc,
"missing return type for OpTypeFunction");
1366 auto returnType =
getType(operands[1]);
1368 return emitError(unknownLoc,
"unknown return type in OpTypeFunction");
1371 for (
size_t i = 2, e = operands.size(); i < e; ++i) {
1372 auto ty =
getType(operands[i]);
1374 return emitError(unknownLoc,
"unknown argument type in OpTypeFunction");
1376 argTypes.push_back(ty);
1382 typeMap[operands[0]] = FunctionType::get(context, argTypes, returnTypes);
1388 if (operands.size() != 6) {
1390 "OpTypeCooperativeMatrixKHR must have element type, "
1391 "scope, row and column parameters, and use");
1397 "OpTypeCooperativeMatrixKHR references undefined <id> ")
1401 std::optional<spirv::Scope> scope =
1406 "OpTypeCooperativeMatrixKHR references undefined scope <id> ")
1415 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Rows` references "
1416 "undefined constant <id> ")
1420 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Columns` "
1421 "references undefined constant <id> ")
1425 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Use` references "
1426 "undefined constant <id> ")
1429 unsigned rows = rowsAttr.getInt();
1430 unsigned columns = columnsAttr.getInt();
1432 std::optional<spirv::CooperativeMatrixUseKHR> use =
1433 spirv::symbolizeCooperativeMatrixUseKHR(useAttr.getInt());
1437 "OpTypeCooperativeMatrixKHR references undefined use <id> ")
1441 typeMap[operands[0]] =
1448 if (operands.size() != 2) {
1449 return emitError(unknownLoc,
"OpTypeRuntimeArray must have two operands");
1454 "OpTypeRuntimeArray references undefined <id> ")
1458 memberType, typeDecorations.lookup(operands[0]));
1466 if (operands.empty()) {
1467 return emitError(unknownLoc,
"OpTypeStruct must have at least result <id>");
1470 if (operands.size() == 1) {
1472 typeMap[operands[0]] =
1481 for (
auto op : llvm::drop_begin(operands, 1)) {
1483 bool typeForwardPtr = (typeForwardPointerIDs.count(op) != 0);
1485 if (!memberType && !typeForwardPtr)
1486 return emitError(unknownLoc,
"OpTypeStruct references undefined <id> ")
1490 unresolvedMemberTypes.emplace_back(op, memberTypes.size());
1492 memberTypes.push_back(memberType);
1497 if (memberDecorationMap.count(operands[0])) {
1498 auto &allMemberDecorations = memberDecorationMap[operands[0]];
1499 for (
auto memberIndex : llvm::seq<uint32_t>(0, memberTypes.size())) {
1500 if (allMemberDecorations.count(memberIndex)) {
1501 for (
auto &memberDecoration : allMemberDecorations[memberIndex]) {
1503 if (memberDecoration.first == spirv::Decoration::Offset) {
1505 if (offsetInfo.empty()) {
1506 offsetInfo.resize(memberTypes.size());
1508 offsetInfo[memberIndex] = memberDecoration.second[0];
1510 auto intType = mlir::IntegerType::get(context, 32);
1511 if (!memberDecoration.second.empty()) {
1512 memberDecorationsInfo.emplace_back(
1513 memberIndex, memberDecoration.first,
1514 IntegerAttr::get(intType, memberDecoration.second[0]));
1516 memberDecorationsInfo.emplace_back(
1517 memberIndex, memberDecoration.first, UnitAttr::get(context));
1526 if (decorations.count(operands[0])) {
1529 std::optional<spirv::Decoration> decoration = spirv::symbolizeDecoration(
1530 llvm::convertToCamelFromSnakeCase(decorationAttr.getName(),
true));
1531 assert(decoration.has_value());
1532 structDecorationsInfo.emplace_back(decoration.value(),
1533 decorationAttr.getValue());
1537 uint32_t structID = operands[0];
1538 std::string structIdentifier = nameMap.lookup(structID).str();
1540 if (structIdentifier.empty()) {
1541 assert(unresolvedMemberTypes.empty() &&
1542 "didn't expect unresolved member types");
1544 memberTypes, offsetInfo, memberDecorationsInfo, structDecorationsInfo);
1547 typeMap[structID] = structTy;
1549 if (!unresolvedMemberTypes.empty())
1550 deferredStructTypesInfos.push_back(
1551 {structTy, std::move(unresolvedMemberTypes), std::move(memberTypes),
1552 std::move(offsetInfo), std::move(memberDecorationsInfo),
1553 std::move(structDecorationsInfo)});
1554 else if (failed(structTy.trySetBody(memberTypes, offsetInfo,
1555 memberDecorationsInfo,
1556 structDecorationsInfo)))
1567 if (operands.size() != 3) {
1569 return emitError(unknownLoc,
"OpTypeMatrix must have 3 operands"
1570 " (result_id, column_type, and column_count)");
1576 "OpTypeMatrix references undefined column type.")
1580 uint32_t colsCount = operands[2];
1587 unsigned size = operands.size();
1588 if (size < 2 || size > 4)
1589 return emitError(unknownLoc,
"OpTypeTensorARM must have 2-4 operands "
1590 "(result_id, element_type, (rank), (shape)) ")
1596 "OpTypeTensorARM references undefined element type ")
1606 return emitError(unknownLoc,
"OpTypeTensorARM rank must come from a "
1607 "scalar integer constant instruction");
1608 unsigned rank = rankAttr.getValue().getZExtValue();
1615 std::optional<std::pair<Attribute, Type>> shapeInfo =
1618 return emitError(unknownLoc,
"OpTypeTensorARM shape must come from a "
1619 "constant instruction of type OpTypeArray");
1621 ArrayAttr shapeArrayAttr = dyn_cast<ArrayAttr>(shapeInfo->first);
1623 for (
auto dimAttr : shapeArrayAttr.getValue()) {
1624 auto dimIntAttr = dyn_cast<IntegerAttr>(dimAttr);
1626 return emitError(unknownLoc,
"OpTypeTensorARM shape has an invalid "
1628 shape.push_back(dimIntAttr.getValue().getSExtValue());
1636 unsigned size = operands.size();
1638 return emitError(unknownLoc,
"OpTypeGraphARM must have at least 2 operands "
1639 "(result_id, num_inputs, (inout0_type, "
1640 "inout1_type, ...))")
1643 uint32_t numInputs = operands[1];
1646 for (
unsigned i = 2; i < size; ++i) {
1650 "OpTypeGraphARM references undefined element type.")
1653 if (i - 2 >= numInputs) {
1654 returnTypes.push_back(inOutTy);
1656 argTypes.push_back(inOutTy);
1659 typeMap[operands[0]] = GraphType::get(context, argTypes, returnTypes);
1665 if (operands.size() != 2)
1667 "OpTypeForwardPointer instruction must have two operands");
1669 typeForwardPointerIDs.insert(operands[0]);
1679 if (operands.size() != 8)
1682 "OpTypeImage with non-eight operands are not supported yet");
1686 return emitError(unknownLoc,
"OpTypeImage references undefined <id>: ")
1689 auto dim = spirv::symbolizeDim(operands[2]);
1691 return emitError(unknownLoc,
"unknown Dim for OpTypeImage: ")
1694 auto depthInfo = spirv::symbolizeImageDepthInfo(operands[3]);
1696 return emitError(unknownLoc,
"unknown Depth for OpTypeImage: ")
1699 auto arrayedInfo = spirv::symbolizeImageArrayedInfo(operands[4]);
1701 return emitError(unknownLoc,
"unknown Arrayed for OpTypeImage: ")
1704 auto samplingInfo = spirv::symbolizeImageSamplingInfo(operands[5]);
1706 return emitError(unknownLoc,
"unknown MS for OpTypeImage: ") << operands[5];
1708 auto samplerUseInfo = spirv::symbolizeImageSamplerUseInfo(operands[6]);
1709 if (!samplerUseInfo)
1710 return emitError(unknownLoc,
"unknown Sampled for OpTypeImage: ")
1713 auto format = spirv::symbolizeImageFormat(operands[7]);
1715 return emitError(unknownLoc,
"unknown Format for OpTypeImage: ")
1719 elementTy, dim.value(), depthInfo.value(), arrayedInfo.value(),
1720 samplingInfo.value(), samplerUseInfo.value(), format.value());
1726 if (operands.size() != 2)
1727 return emitError(unknownLoc,
"OpTypeSampledImage must have two operands");
1732 "OpTypeSampledImage references undefined <id>: ")
1741 if (operands.size() != 1)
1742 return emitError(unknownLoc,
"OpTypeSampler must have no parameters");
1750 if (operands.size() != 1)
1751 return emitError(unknownLoc,
"OpTypeNamedBarrier must have no parameters");
1763 StringRef opname = isSpec ?
"OpSpecConstant" :
"OpConstant";
1765 if (operands.size() < 2) {
1767 << opname <<
" must have type <id> and result <id>";
1769 if (operands.size() < 3) {
1771 << opname <<
" must have at least 1 more parameter";
1776 return emitError(unknownLoc,
"undefined result type from <id> ")
1780 auto checkOperandSizeForBitwidth = [&](
unsigned bitwidth) -> LogicalResult {
1781 if (bitwidth == 64) {
1782 if (operands.size() == 4) {
1786 << opname <<
" should have 2 parameters for 64-bit values";
1788 if (bitwidth <= 32) {
1789 if (operands.size() == 3) {
1795 <<
" should have 1 parameter for values with no more than 32 bits";
1797 return emitError(unknownLoc,
"unsupported OpConstant bitwidth: ")
1801 auto resultID = operands[1];
1803 if (
auto intType = dyn_cast<IntegerType>(resultType)) {
1804 auto bitwidth = intType.getWidth();
1805 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1810 if (bitwidth == 64) {
1817 } words = {operands[2], operands[3]};
1818 value = APInt(64, llvm::bit_cast<uint64_t>(words),
true);
1819 }
else if (bitwidth <= 32) {
1820 value = APInt(bitwidth, operands[2],
true,
1824 auto attr = opBuilder.getIntegerAttr(intType, value);
1831 constantMap.try_emplace(resultID, attr, intType);
1837 if (
auto floatType = dyn_cast<FloatType>(resultType)) {
1838 auto bitwidth = floatType.getWidth();
1839 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1844 if (floatType.isF64()) {
1851 } words = {operands[2], operands[3]};
1852 value = APFloat(llvm::bit_cast<double>(words));
1853 }
else if (floatType.isF32()) {
1854 value = APFloat(llvm::bit_cast<float>(operands[2]));
1855 }
else if (floatType.isF16()) {
1856 APInt data(16, operands[2]);
1857 value = APFloat(APFloat::IEEEhalf(), data);
1858 }
else if (floatType.isBF16()) {
1859 APInt data(16, operands[2]);
1860 value = APFloat(APFloat::BFloat(), data);
1861 }
else if (floatType.isF8E4M3FN()) {
1862 APInt data(8, operands[2]);
1863 value = APFloat(APFloat::Float8E4M3FN(), data);
1864 }
else if (floatType.isF8E5M2()) {
1865 APInt data(8, operands[2]);
1866 value = APFloat(APFloat::Float8E5M2(), data);
1869 auto attr = opBuilder.getFloatAttr(floatType, value);
1875 constantMap.try_emplace(resultID, attr, floatType);
1881 return emitError(unknownLoc,
"OpConstant can only generate values of "
1882 "scalar integer or floating-point type");
1887 if (operands.size() != 2) {
1889 << (isSpec ?
"Spec" :
"") <<
"Constant"
1890 << (isTrue ?
"True" :
"False")
1891 <<
" must have type <id> and result <id>";
1894 auto attr = opBuilder.getBoolAttr(isTrue);
1895 auto resultID = operands[1];
1901 constantMap.try_emplace(resultID, attr, opBuilder.getI1Type());
1909 if (operands.size() < 2) {
1911 "OpConstantComposite must have type <id> and result <id>");
1913 if (operands.size() < 3) {
1915 "OpConstantComposite must have at least 1 parameter");
1920 return emitError(unknownLoc,
"undefined result type from <id> ")
1925 elements.reserve(operands.size() - 2);
1926 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
1929 return emitError(unknownLoc,
"OpConstantComposite component <id> ")
1930 << operands[i] <<
" must come from a normal constant";
1932 elements.push_back(elementInfo->first);
1935 auto resultID = operands[1];
1936 if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
1939 if (
auto denseElemAttr = dyn_cast<DenseElementsAttr>(element)) {
1940 for (
auto value : denseElemAttr.getValues<
Attribute>())
1941 flattenedElems.push_back(value);
1943 flattenedElems.push_back(element);
1947 constantMap.try_emplace(resultID, attr, tensorType);
1948 }
else if (
auto shapedType = dyn_cast<ShapedType>(resultType)) {
1952 constantMap.try_emplace(resultID, attr, shapedType);
1953 }
else if (isa<spirv::ArrayType, spirv::StructType>(resultType)) {
1954 auto attr = opBuilder.getArrayAttr(elements);
1955 constantMap.try_emplace(resultID, attr, resultType);
1957 return emitError(unknownLoc,
"unsupported OpConstantComposite type: ")
1966 if (operands.size() != 3) {
1969 "OpConstantCompositeReplicateEXT expects 3 operands but found ")
1975 return emitError(unknownLoc,
"undefined result type from <id> ")
1979 auto compositeType = dyn_cast<CompositeType>(resultType);
1980 if (!compositeType) {
1982 "result type from <id> is not a composite type")
1986 uint32_t resultID = operands[1];
1987 uint32_t constantID = operands[2];
1989 std::optional<std::pair<Attribute, Type>> replicatedConstantCompositeInfo =
1991 if (replicatedConstantCompositeInfo.has_value()) {
1992 constantCompositeReplicateMap.try_emplace(
1993 resultID, replicatedConstantCompositeInfo.value().first, resultType);
1997 std::optional<std::pair<Attribute, Type>> constantInfo =
1999 if (constantInfo.has_value()) {
2000 constantCompositeReplicateMap.try_emplace(
2001 resultID, constantInfo.value().first, resultType);
2005 return emitError(unknownLoc,
"OpConstantCompositeReplicateEXT operand <id> ")
2007 <<
" must come from a normal constant or a "
2008 "OpConstantCompositeReplicateEXT";
2013 if (operands.size() < 2) {
2016 "OpSpecConstantComposite must have type <id> and result <id>");
2018 if (operands.size() < 3) {
2020 "OpSpecConstantComposite must have at least 1 parameter");
2025 return emitError(unknownLoc,
"undefined result type from <id> ")
2029 auto resultID = operands[1];
2033 elements.reserve(operands.size() - 2);
2034 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
2036 elements.push_back(SymbolRefAttr::get(elementInfo));
2039 auto op = spirv::SpecConstantCompositeOp::create(
2040 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2041 opBuilder.getArrayAttr(elements),
nullptr);
2042 specConstCompositeMap[resultID] = op;
2049 if (operands.size() != 3) {
2050 return emitError(unknownLoc,
"OpSpecConstantCompositeReplicateEXT expects "
2051 "3 operands but found ")
2057 return emitError(unknownLoc,
"undefined result type from <id> ")
2061 auto compositeType = dyn_cast<CompositeType>(resultType);
2062 if (!compositeType) {
2064 "result type from <id> is not a composite type")
2068 uint32_t resultID = operands[1];
2071 spirv::SpecConstantOp constituentSpecConstantOp =
2073 auto op = spirv::EXTSpecConstantCompositeReplicateOp::create(
2074 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2075 SymbolRefAttr::get(constituentSpecConstantOp),
2078 specConstCompositeReplicateMap[resultID] = op;
2085 if (operands.size() < 3)
2086 return emitError(unknownLoc,
"OpConstantOperation must have type <id>, "
2087 "result <id>, and operand opcode");
2089 uint32_t resultTypeID = operands[0];
2092 return emitError(unknownLoc,
"undefined result type from <id> ")
2095 uint32_t resultID = operands[1];
2096 spirv::Opcode enclosedOpcode =
static_cast<spirv::Opcode
>(operands[2]);
2097 auto emplaceResult = specConstOperationMap.try_emplace(
2100 enclosedOpcode, resultTypeID,
2103 if (!emplaceResult.second)
2104 return emitError(unknownLoc,
"value with <id>: ")
2105 << resultID <<
" is probably defined before.";
2111 uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID,
2127 llvm::SaveAndRestore valueMapGuard(valueMap, newValueMap);
2128 constexpr uint32_t fakeID =
static_cast<uint32_t
>(-3);
2131 enclosedOpResultTypeAndOperands.push_back(resultTypeID);
2132 enclosedOpResultTypeAndOperands.push_back(fakeID);
2133 enclosedOpResultTypeAndOperands.append(enclosedOpOperands.begin(),
2134 enclosedOpOperands.end());
2149 auto specConstOperationOp =
2150 spirv::SpecConstantOperationOp::create(opBuilder, loc, resultType);
2152 Region &body = specConstOperationOp.getBody();
2154 body.
getBlocks().splice(body.
end(), curBlock->getParent()->getBlocks(),
2161 opBuilder.setInsertionPointToEnd(&block);
2163 spirv::YieldOp::create(opBuilder, loc, block.
front().
getResult(0));
2164 return specConstOperationOp.getResult();
2169 if (operands.size() != 2) {
2171 "OpConstantNull must only have type <id> and result <id>");
2176 return emitError(unknownLoc,
"undefined result type from <id> ")
2180 auto resultID = operands[1];
2182 if (resultType.
isIntOrFloat() || isa<VectorType>(resultType)) {
2183 attr = opBuilder.getZeroAttr(resultType);
2184 }
else if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
2185 if (
auto element = opBuilder.getZeroAttr(tensorType.getElementType()))
2192 constantMap.try_emplace(resultID, attr, resultType);
2196 return emitError(unknownLoc,
"unsupported OpConstantNull type: ")
2202 if (operands.size() < 3) {
2204 <<
"OpGraphConstantARM must have at least 2 operands";
2209 return emitError(unknownLoc,
"undefined result type from <id> ")
2213 uint32_t resultID = operands[1];
2215 if (!dyn_cast<spirv::TensorArmType>(resultType)) {
2216 return emitError(unknownLoc,
"result must be of type OpTypeTensorARM");
2219 APInt graph_constant_id = APInt(32, operands[2],
true);
2220 Type i32Ty = opBuilder.getIntegerType(32);
2221 IntegerAttr attr = opBuilder.getIntegerAttr(i32Ty, graph_constant_id);
2222 graphConstantMap.try_emplace(
2234 LLVM_DEBUG(logger.startLine() <<
"[block] got exiting block for id = " <<
id
2235 <<
" @ " << block <<
"\n");
2242 auto *block = curFunction->addBlock();
2243 LLVM_DEBUG(logger.startLine() <<
"[block] created block for id = " <<
id
2244 <<
" @ " << block <<
"\n");
2245 return blockMap[id] = block;
2250 return emitError(unknownLoc,
"OpBranch must appear inside a block");
2253 if (operands.size() != 1) {
2254 return emitError(unknownLoc,
"OpBranch must take exactly one target label");
2262 spirv::BranchOp::create(opBuilder, loc,
target);
2272 "OpBranchConditional must appear inside a block");
2275 if (operands.size() != 3 && operands.size() != 5) {
2277 "OpBranchConditional must have condition, true label, "
2278 "false label, and optionally two branch weights");
2281 auto condition =
getValue(operands[0]);
2285 std::optional<std::pair<uint32_t, uint32_t>> weights;
2286 if (operands.size() == 5) {
2287 weights = std::make_pair(operands[3], operands[4]);
2293 spirv::BranchConditionalOp::create(
2294 opBuilder, loc, condition, trueBlock,
2304 return emitError(unknownLoc,
"OpLabel must appear inside a function");
2307 if (operands.size() != 1) {
2308 return emitError(unknownLoc,
"OpLabel should only have result <id>");
2311 auto labelID = operands[0];
2314 LLVM_DEBUG(logger.startLine()
2315 <<
"[block] populating block " << block <<
"\n");
2317 assert(block->empty() &&
"re-deserialize the same block!");
2319 opBuilder.setInsertionPointToStart(block);
2320 blockMap[labelID] = curBlock = block;
2327 return emitError(unknownLoc,
"a graph block must appear inside a graph");
2332 LLVM_DEBUG(logger.startLine()
2333 <<
"[block] populating block " << block <<
"\n");
2335 assert(block->
empty() &&
"re-deserialize the same block!");
2337 opBuilder.setInsertionPointToStart(block);
2338 blockMap[graphID] = curBlock = block;
2346 return emitError(unknownLoc,
"OpSelectionMerge must appear in a block");
2349 if (operands.size() < 2) {
2352 "OpSelectionMerge must specify merge target and selection control");
2357 auto selectionControl = operands[1];
2359 if (!blockMergeInfo.try_emplace(curBlock, loc, selectionControl, mergeBlock)
2363 "a block cannot have more than one OpSelectionMerge instruction");
2372 return emitError(unknownLoc,
"OpLoopMerge must appear in a block");
2375 if (operands.size() < 3) {
2376 return emitError(unknownLoc,
"OpLoopMerge must specify merge target, "
2377 "continue target and loop control");
2383 uint32_t loopControl = operands[2];
2386 .try_emplace(curBlock, loc, loopControl, mergeBlock, continueBlock)
2390 "a block cannot have more than one OpLoopMerge instruction");
2398 return emitError(unknownLoc,
"OpPhi must appear in a block");
2401 if (operands.size() < 4) {
2402 return emitError(unknownLoc,
"OpPhi must specify result type, result <id>, "
2403 "and variable-parent pairs");
2408 BlockArgument blockArg = curBlock->addArgument(blockArgType, unknownLoc);
2409 valueMap[operands[1]] = blockArg;
2410 LLVM_DEBUG(logger.startLine()
2411 <<
"[phi] created block argument " << blockArg
2412 <<
" id = " << operands[1] <<
" of type " << blockArgType <<
"\n");
2416 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
2417 uint32_t value = operands[i];
2419 std::pair<Block *, Block *> predecessorTargetPair{predecessor, curBlock};
2420 blockPhiInfo[predecessorTargetPair].push_back(value);
2421 LLVM_DEBUG(logger.startLine() <<
"[phi] predecessor @ " << predecessor
2422 <<
" with arg id = " << value <<
"\n");
2430 return emitError(unknownLoc,
"OpSwitch must appear in a block");
2432 if (operands.size() < 2)
2433 return emitError(unknownLoc,
"OpSwitch must at least specify selector and "
2434 "a default target");
2436 if (operands.size() % 2)
2438 "OpSwitch must at have an even number of operands: "
2439 "selector, default target and any number of literal and "
2440 "label <id> pairs");
2448 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
2449 literals.push_back(operands[i]);
2454 spirv::SwitchOp::create(opBuilder, loc, selector, defaultBlock,
2463class ControlFlowStructurizer {
2466 ControlFlowStructurizer(
Location loc, uint32_t control,
2469 llvm::ScopedPrinter &logger)
2470 : location(loc), control(control), blockMergeInfo(mergeInfo),
2471 headerBlock(header), mergeBlock(merge), continueBlock(cont),
2474 ControlFlowStructurizer(
Location loc, uint32_t control,
2477 : location(loc), control(control), blockMergeInfo(mergeInfo),
2478 headerBlock(header), mergeBlock(merge), continueBlock(cont) {}
2488 LogicalResult structurize();
2493 spirv::SelectionOp createSelectionOp(uint32_t selectionControl);
2496 spirv::LoopOp createLoopOp(uint32_t loopControl);
2499 void collectBlocksInConstruct();
2508 Block *continueBlock;
2514 llvm::ScopedPrinter &logger;
2520ControlFlowStructurizer::createSelectionOp(uint32_t selectionControl) {
2523 OpBuilder builder(&mergeBlock->front());
2525 auto control =
static_cast<spirv::SelectionControl
>(selectionControl);
2526 auto selectionOp = spirv::SelectionOp::create(builder, location, control);
2527 selectionOp.addMergeBlock(builder);
2532spirv::LoopOp ControlFlowStructurizer::createLoopOp(uint32_t loopControl) {
2535 OpBuilder builder(&mergeBlock->front());
2537 auto control =
static_cast<spirv::LoopControl
>(loopControl);
2538 auto loopOp = spirv::LoopOp::create(builder, location, control);
2539 loopOp.addEntryAndMergeBlock(builder);
2544void ControlFlowStructurizer::collectBlocksInConstruct() {
2545 assert(constructBlocks.empty() &&
"expected empty constructBlocks");
2548 constructBlocks.insert(headerBlock);
2552 for (
unsigned i = 0; i < constructBlocks.size(); ++i) {
2553 for (
auto *successor : constructBlocks[i]->getSuccessors())
2554 if (successor != mergeBlock)
2555 constructBlocks.insert(successor);
2559LogicalResult ControlFlowStructurizer::structurize() {
2560 Operation *op =
nullptr;
2561 bool isLoop = continueBlock !=
nullptr;
2563 if (
auto loopOp = createLoopOp(control))
2564 op = loopOp.getOperation();
2566 if (
auto selectionOp = createSelectionOp(control))
2567 op = selectionOp.getOperation();
2576 mapper.
map(mergeBlock, &body.
back());
2578 collectBlocksInConstruct();
2599 OpBuilder builder(body);
2600 for (
auto *block : constructBlocks) {
2603 auto *newBlock = builder.createBlock(&body.
back());
2604 mapper.
map(block, newBlock);
2605 LLVM_DEBUG(logger.startLine() <<
"[cf] cloned block " << newBlock
2606 <<
" from block " << block <<
"\n");
2608 for (BlockArgument blockArg : block->getArguments()) {
2610 newBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2611 mapper.
map(blockArg, newArg);
2612 LLVM_DEBUG(logger.startLine() <<
"[cf] remapped block argument "
2613 << blockArg <<
" to " << newArg <<
"\n");
2616 LLVM_DEBUG(logger.startLine()
2617 <<
"[cf] block " << block <<
" is a function entry block\n");
2620 for (
auto &op : *block)
2621 newBlock->push_back(op.
clone(mapper));
2625 auto remapOperands = [&](Operation *op) {
2627 if (Value mappedOp = mapper.
lookupOrNull(operand.get()))
2628 operand.set(mappedOp);
2631 succOp.set(mappedOp);
2633 for (
auto &block : body)
2634 block.walk(remapOperands);
2642 headerBlock->replaceAllUsesWith(mergeBlock);
2645 logger.startLine() <<
"[cf] after cloning and fixing references:\n";
2646 headerBlock->getParentOp()->print(logger.getOStream());
2647 logger.startLine() <<
"\n";
2651 if (!mergeBlock->args_empty()) {
2652 return mergeBlock->getParentOp()->emitError(
2653 "OpPhi in loop merge block unsupported");
2659 for (BlockArgument blockArg : headerBlock->getArguments())
2660 mergeBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2664 SmallVector<Value, 4> blockArgs;
2665 if (!headerBlock->args_empty())
2666 blockArgs = {mergeBlock->args_begin(), mergeBlock->args_end()};
2670 builder.setInsertionPointToEnd(&body.front());
2671 spirv::BranchOp::create(builder, location, mapper.
lookupOrNull(headerBlock),
2672 ArrayRef<Value>(blockArgs));
2677 SmallVector<Value> valuesToYield;
2680 SmallVector<Value> outsideUses;
2694 for (BlockArgument blockArg : mergeBlock->getArguments()) {
2699 body.back().addArgument(blockArg.getType(), blockArg.getLoc());
2700 valuesToYield.push_back(body.back().getArguments().back());
2701 outsideUses.push_back(blockArg);
2706 LLVM_DEBUG(logger.startLine() <<
"[cf] cleaning up blocks after clone\n");
2709 for (
auto *block : constructBlocks)
2710 block->dropAllReferences();
2715 for (
Block *block : constructBlocks) {
2716 for (Operation &op : *block) {
2720 outsideUses.push_back(
result);
2723 for (BlockArgument &arg : block->getArguments()) {
2724 if (!arg.use_empty()) {
2726 outsideUses.push_back(arg);
2731 assert(valuesToYield.size() == outsideUses.size());
2735 if (!valuesToYield.empty()) {
2736 LLVM_DEBUG(logger.startLine()
2737 <<
"[cf] yielding values from the selection / loop region\n");
2740 auto mergeOps = body.back().getOps<spirv::MergeOp>();
2741 Operation *merge = llvm::getSingleElement(mergeOps);
2743 merge->setOperands(valuesToYield);
2751 builder.setInsertionPoint(&mergeBlock->front());
2753 Operation *newOp =
nullptr;
2756 newOp = spirv::LoopOp::create(builder, location,
2758 static_cast<spirv::LoopControl
>(control));
2760 newOp = spirv::SelectionOp::create(
2762 static_cast<spirv::SelectionControl
>(control));
2772 for (
unsigned i = 0, e = outsideUses.size(); i != e; ++i)
2773 outsideUses[i].replaceAllUsesWith(op->
getResult(i));
2779 mergeBlock->eraseArguments(0, mergeBlock->getNumArguments());
2786 for (
auto *block : constructBlocks) {
2787 if (!block->use_empty())
2788 return emitError(block->getParent()->getLoc(),
2789 "failed control flow structurization: "
2790 "block has uses outside of the "
2791 "enclosing selection/loop construct");
2792 for (Operation &op : *block)
2794 return op.
emitOpError(
"failed control flow structurization: value has "
2795 "uses outside of the "
2796 "enclosing selection/loop construct");
2797 for (BlockArgument &arg : block->getArguments())
2798 if (!arg.use_empty())
2799 return emitError(arg.getLoc(),
"failed control flow structurization: "
2800 "block argument has uses outside of the "
2801 "enclosing selection/loop construct");
2805 for (
auto *block : constructBlocks) {
2845 auto updateMergeInfo = [&](
Block *block) -> WalkResult {
2846 auto it = blockMergeInfo.find(block);
2847 if (it != blockMergeInfo.end()) {
2849 Location loc = it->second.loc;
2851 uint32_t nestedControl = it->second.control;
2855 return emitError(loc,
"failed control flow structurization: nested "
2856 "loop header block should be remapped!");
2858 Block *newContinue = it->second.continueBlock;
2862 return emitError(loc,
"failed control flow structurization: nested "
2863 "loop continue block should be remapped!");
2866 Block *newMerge = it->second.mergeBlock;
2868 newMerge = mappedTo;
2871 blockMergeInfo.
erase(it);
2872 blockMergeInfo.try_emplace(newHeader, loc, nestedControl, newMerge,
2879 if (block->walk(updateMergeInfo).wasInterrupted())
2887 LLVM_DEBUG(logger.startLine() <<
"[cf] changing entry block " << block
2888 <<
" to only contain a spirv.Branch op\n");
2892 builder.setInsertionPointToEnd(block);
2893 spirv::BranchOp::create(builder, location, mergeBlock);
2895 LLVM_DEBUG(logger.startLine() <<
"[cf] erasing block " << block <<
"\n");
2900 LLVM_DEBUG(logger.startLine()
2901 <<
"[cf] after structurizing construct with header block "
2902 << headerBlock <<
":\n"
2911 <<
"//----- [phi] start wiring up block arguments -----//\n";
2917 for (
const auto &info : blockPhiInfo) {
2918 Block *block = info.first.first;
2922 logger.startLine() <<
"[phi] block " << block <<
"\n";
2923 logger.startLine() <<
"[phi] before creating block argument:\n";
2925 logger.startLine() <<
"\n";
2931 opBuilder.setInsertionPoint(op);
2934 blockArgs.reserve(phiInfo.size());
2935 for (uint32_t valueId : phiInfo) {
2937 blockArgs.push_back(value);
2938 LLVM_DEBUG(logger.startLine() <<
"[phi] block argument " << value
2939 <<
" id = " << valueId <<
"\n");
2941 return emitError(unknownLoc,
"OpPhi references undefined value!");
2945 if (
auto branchOp = dyn_cast<spirv::BranchOp>(op)) {
2947 spirv::BranchOp::create(opBuilder, branchOp.getLoc(),
2948 branchOp.getTarget(), blockArgs);
2950 }
else if (
auto branchCondOp = dyn_cast<spirv::BranchConditionalOp>(op)) {
2951 assert((branchCondOp.getTrueBlock() ==
target ||
2952 branchCondOp.getFalseBlock() ==
target) &&
2953 "expected target to be either the true or false target");
2954 if (
target == branchCondOp.getTrueTarget())
2955 spirv::BranchConditionalOp::create(
2956 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2957 blockArgs, branchCondOp.getFalseBlockArguments(),
2958 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueTarget(),
2959 branchCondOp.getFalseTarget());
2961 spirv::BranchConditionalOp::create(
2962 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2963 branchCondOp.getTrueBlockArguments(), blockArgs,
2964 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueBlock(),
2965 branchCondOp.getFalseBlock());
2967 branchCondOp.erase();
2968 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(op)) {
2969 if (
target == switchOp.getDefaultTarget()) {
2973 spirv::SwitchOp::create(
2974 opBuilder, switchOp.getLoc(), switchOp.getSelector(),
2975 switchOp.getDefaultTarget(), blockArgs, literals,
2976 switchOp.getTargets(), targetOperands);
2980 auto it = llvm::find(targets,
target);
2981 assert(it != targets.end());
2982 size_t index = std::distance(targets.begin(), it);
2983 switchOp.getTargetOperandsMutable(
index).assign(blockArgs);
2986 return emitError(unknownLoc,
"unimplemented terminator for Phi creation");
2990 logger.startLine() <<
"[phi] after creating block argument:\n";
2992 logger.startLine() <<
"\n";
2995 blockPhiInfo.clear();
3000 <<
"//--- [phi] completed wiring up block arguments ---//\n";
3008 for (
auto [block, mergeInfo] : blockMergeInfoCopy) {
3010 if (mergeInfo.continueBlock)
3013 if (!block->mightHaveTerminator())
3016 Operation *terminator = block->getTerminator();
3019 if (!isa<spirv::BranchConditionalOp, spirv::SwitchOp>(terminator))
3023 bool splitHeaderMergeBlock =
false;
3024 for (
const auto &[_, mergeInfo] : blockMergeInfo) {
3025 if (mergeInfo.mergeBlock == block)
3026 splitHeaderMergeBlock =
true;
3033 if (!llvm::hasSingleElement(*block) || splitHeaderMergeBlock) {
3036 spirv::BranchOp::create(builder, block->getParent()->getLoc(), newBlock);
3040 blockMergeInfo.erase(block);
3041 blockMergeInfo.try_emplace(newBlock, mergeInfo);
3049 if (!options.enableControlFlowStructurization) {
3053 <<
"//----- [cf] skip structurizing control flow -----//\n";
3061 <<
"//----- [cf] start structurizing control flow -----//\n";
3066 logger.startLine() <<
"[cf] split conditional blocks\n";
3067 logger.startLine() <<
"\n";
3074 while (!blockMergeInfo.empty()) {
3075 Block *headerBlock = blockMergeInfo.
begin()->first;
3079 logger.startLine() <<
"[cf] header block " << headerBlock <<
":\n";
3080 headerBlock->
print(logger.getOStream());
3081 logger.startLine() <<
"\n";
3085 assert(mergeBlock &&
"merge block cannot be nullptr");
3087 return emitError(unknownLoc,
"OpPhi in loop merge block unimplemented");
3089 logger.startLine() <<
"[cf] merge block " << mergeBlock <<
":\n";
3090 mergeBlock->print(logger.getOStream());
3091 logger.startLine() <<
"\n";
3095 LLVM_DEBUG(
if (continueBlock) {
3096 logger.startLine() <<
"[cf] continue block " << continueBlock <<
":\n";
3097 continueBlock->print(logger.getOStream());
3098 logger.startLine() <<
"\n";
3102 blockMergeInfo.
erase(blockMergeInfo.begin());
3103 ControlFlowStructurizer structurizer(mergeInfo.
loc, mergeInfo.
control,
3104 blockMergeInfo, headerBlock,
3105 mergeBlock, continueBlock
3111 if (failed(structurizer.structurize()))
3118 <<
"//--- [cf] completed structurizing control flow ---//\n";
3131 auto fileName = debugInfoMap.lookup(debugLine->fileID).str();
3132 if (fileName.empty())
3133 fileName =
"<unknown>";
3145 if (operands.size() != 3)
3146 return emitError(unknownLoc,
"OpLine must have 3 operands");
3147 debugLine =
DebugLine{operands[0], operands[1], operands[2]};
3155 if (operands.size() < 2)
3156 return emitError(unknownLoc,
"OpString needs at least 2 operands");
3158 if (!debugInfoMap.lookup(operands[0]).empty())
3160 "duplicate debug string found for result <id> ")
3163 unsigned wordIndex = 1;
3165 if (wordIndex != operands.size())
3167 "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...
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
Attribute set(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
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 getFuncParamAttrName()
Returns the attribute name for specifying function parameter and result attributes.
StringRef getFuncParamAlignmentAttrName()
Returns the attribute name for specifying function parameter alignment.
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.