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/MathExtras.h"
31#include "llvm/Support/SaveAndRestore.h"
32#include "llvm/Support/raw_ostream.h"
38#define DEBUG_TYPE "spirv-deserialization"
47 isa_and_nonnull<spirv::FuncOp>(block->
getParentOp());
66 : binary(binary), context(context), unknownLoc(UnknownLoc::
get(context)),
67 module(createModuleOp()), opBuilder(module->getRegion()),
options(
options)
75LogicalResult spirv::Deserializer::deserialize() {
79 <<
"//+++---------- start deserialization ----------+++//\n";
82 if (
failed(processHeader()))
85 spirv::Opcode opcode = spirv::Opcode::OpNop;
86 ArrayRef<uint32_t> operands;
87 auto binarySize = binary.size();
88 while (curOffset < binarySize) {
98 assert(curOffset == binarySize &&
99 "deserializer should never index beyond the binary end");
101 for (
auto &deferred : deferredInstructions) {
107 if (
failed(resolveDeferredIdDecorations()))
110 if (!functionParameterAttrs.empty())
111 return emitError(unknownLoc,
"FuncParamAttr decoration on result <id> ")
112 << functionParameterAttrs.begin()->first
113 <<
" is not supported: expected OpFunctionParameter or OpFunction";
117 LLVM_DEBUG(logger.startLine()
118 <<
"//+++-------- completed deserialization --------+++//\n");
122OwningOpRef<spirv::ModuleOp> spirv::Deserializer::collect() {
123 return std::move(module);
130OwningOpRef<spirv::ModuleOp> spirv::Deserializer::createModuleOp() {
131 OpBuilder builder(context);
132 OperationState state(unknownLoc, spirv::ModuleOp::getOperationName());
133 spirv::ModuleOp::build(builder, state);
137LogicalResult spirv::Deserializer::processHeader() {
140 "SPIR-V binary module must have a 5-word header");
143 return emitError(unknownLoc,
"incorrect magic number");
146 uint32_t majorVersion = (binary[1] << 8) >> 24;
147 uint32_t minorVersion = (binary[1] << 16) >> 24;
148 if (majorVersion == 1) {
149 switch (minorVersion) {
150#define MIN_VERSION_CASE(v) \
152 version = spirv::Version::V_1_##v; \
162#undef MIN_VERSION_CASE
164 return emitError(unknownLoc,
"unsupported SPIR-V minor version: ")
168 return emitError(unknownLoc,
"unsupported SPIR-V major version: ")
178spirv::Deserializer::processCapability(ArrayRef<uint32_t> operands) {
179 if (operands.size() != 1)
180 return emitError(unknownLoc,
"OpCapability must have one parameter");
182 auto cap = spirv::symbolizeCapability(operands[0]);
184 return emitError(unknownLoc,
"unknown capability: ") << operands[0];
186 capabilities.insert(*cap);
190LogicalResult spirv::Deserializer::processExtension(ArrayRef<uint32_t> words) {
194 "OpExtension must have a literal string for the extension name");
197 unsigned wordIndex = 0;
199 if (wordIndex != words.size())
201 "unexpected trailing words in OpExtension instruction");
202 auto ext = spirv::symbolizeExtension(extName);
204 return emitError(unknownLoc,
"unknown extension: ") << extName;
206 extensions.insert(*ext);
211spirv::Deserializer::processExtInstImport(ArrayRef<uint32_t> words) {
212 if (words.size() < 2) {
214 "OpExtInstImport must have a result <id> and a literal "
215 "string for the extended instruction set name");
218 unsigned wordIndex = 1;
220 if (wordIndex != words.size()) {
222 "unexpected trailing words in OpExtInstImport");
227void spirv::Deserializer::attachVCETriple() {
228 module->setVceTripleAttr(spirv::VerCapExtAttr::get(
229 version, capabilities.getArrayRef(), extensions.getArrayRef(), context));
233spirv::Deserializer::processMemoryModel(ArrayRef<uint32_t> operands) {
234 if (operands.size() != 2)
235 return emitError(unknownLoc,
"OpMemoryModel must have two operands");
237 module->setAddressingModel(
238 static_cast<spirv::AddressingModel>(operands.front()));
240 module->setMemoryModel(static_cast<spirv::MemoryModel>(operands.back()));
245template <
typename AttrTy,
typename EnumAttrTy,
typename EnumTy>
249 StringAttr symbol, StringRef decorationName, StringRef cacheControlKind) {
250 if (words.size() != 4) {
251 return emitError(loc,
"OpDecorate with ")
252 << decorationName <<
" needs a cache control integer literal and a "
253 << cacheControlKind <<
" cache control literal";
255 unsigned cacheLevel = words[2];
256 auto cacheControlAttr =
static_cast<EnumTy
>(words[3]);
257 auto value = opBuilder.
getAttr<AttrTy>(cacheLevel, cacheControlAttr);
260 dyn_cast_or_null<ArrayAttr>(decorations[words[0]].
get(symbol)))
261 llvm::append_range(attrs, attrList);
262 attrs.push_back(value);
263 decorations[words[0]].set(symbol, opBuilder.
getArrayAttr(attrs));
267LogicalResult spirv::Deserializer::processDecoration(ArrayRef<uint32_t> words) {
271 if (words.size() < 2) {
273 unknownLoc,
"OpDecorate must have at least result <id> and Decoration");
275 auto decorationName =
276 stringifyDecoration(
static_cast<spirv::Decoration
>(words[1]));
277 if (decorationName.empty()) {
278 return emitError(unknownLoc,
"invalid Decoration code : ") << words[1];
280 auto symbol = getSymbolDecoration(decorationName);
281 switch (
static_cast<spirv::Decoration
>(words[1])) {
282 case spirv::Decoration::FPFastMathMode:
283 if (words.size() != 3) {
284 return emitError(unknownLoc,
"OpDecorate with ")
285 << decorationName <<
" needs a single integer literal";
287 decorations[words[0]].set(
288 symbol, FPFastMathModeAttr::get(opBuilder.getContext(),
289 static_cast<FPFastMathMode
>(words[2])));
291 case spirv::Decoration::FPRoundingMode:
292 if (words.size() != 3) {
293 return emitError(unknownLoc,
"OpDecorate with ")
294 << decorationName <<
" needs a single integer literal";
296 decorations[words[0]].set(
297 symbol, FPRoundingModeAttr::get(opBuilder.getContext(),
298 static_cast<FPRoundingMode
>(words[2])));
300 case spirv::Decoration::FuncParamAttr: {
301 if (words.size() != 3)
303 "OpDecorate with FuncParamAttr needs a single "
304 "function parameter attribute");
305 auto value = spirv::symbolizeFunctionParameterAttribute(words[2]);
307 return emitError(unknownLoc,
"invalid FunctionParameterAttribute code: ")
309 SmallVector<Attribute> attrs;
310 if (
auto previous = functionParameterAttrs.lookup(words[0]))
311 llvm::append_range(attrs, previous);
313 spirv::FunctionParameterAttributeAttr::get(context, *value));
314 functionParameterAttrs[words[0]] = opBuilder.getArrayAttr(attrs);
317 case spirv::Decoration::Alignment:
318 case spirv::Decoration::MaxByteOffset:
319 case spirv::Decoration::DescriptorSet:
320 case spirv::Decoration::Binding:
321 case spirv::Decoration::Location:
322 case spirv::Decoration::SpecId:
323 case spirv::Decoration::Index:
324 case spirv::Decoration::Offset:
325 case spirv::Decoration::XfbBuffer:
326 case spirv::Decoration::XfbStride:
327 if (words.size() != 3) {
328 return emitError(unknownLoc,
"OpDecorate with ")
329 << decorationName <<
" needs a single integer literal";
331 decorations[words[0]].set(
332 symbol, opBuilder.getI32IntegerAttr(
static_cast<int32_t
>(words[2])));
334 case spirv::Decoration::BuiltIn:
335 if (words.size() != 3) {
336 return emitError(unknownLoc,
"OpDecorate with ")
337 << decorationName <<
" needs a single integer literal";
339 decorations[words[0]].set(
340 symbol, opBuilder.getStringAttr(
341 stringifyBuiltIn(
static_cast<spirv::BuiltIn
>(words[2]))));
343 case spirv::Decoration::ArrayStride:
344 if (words.size() != 3) {
345 return emitError(unknownLoc,
"OpDecorate with ")
346 << decorationName <<
" needs a single integer literal";
348 typeDecorations[words[0]] = words[2];
350 case spirv::Decoration::LinkageAttributes: {
351 if (words.size() < 4) {
352 return emitError(unknownLoc,
"OpDecorate with ")
354 <<
" needs at least 1 string and 1 integer literal";
362 unsigned wordIndex = 2;
364 auto linkageTypeAttr = opBuilder.getAttr<::mlir::spirv::LinkageTypeAttr>(
365 static_cast<::mlir::spirv::LinkageType
>(words[wordIndex++]));
366 auto linkageAttr = opBuilder.getAttr<::mlir::spirv::LinkageAttributesAttr>(
367 StringAttr::get(context, linkageName), linkageTypeAttr);
368 decorations[words[0]].set(symbol, dyn_cast<Attribute>(linkageAttr));
371 case spirv::Decoration::Aliased:
372 case spirv::Decoration::AliasedPointer:
373 case spirv::Decoration::Block:
374 case spirv::Decoration::BufferBlock:
375 case spirv::Decoration::CPacked:
376 case spirv::Decoration::Flat:
377 case spirv::Decoration::NonReadable:
378 case spirv::Decoration::NonWritable:
379 case spirv::Decoration::NoPerspective:
380 case spirv::Decoration::NoSignedWrap:
381 case spirv::Decoration::NoUnsignedWrap:
382 case spirv::Decoration::RelaxedPrecision:
383 case spirv::Decoration::Restrict:
384 case spirv::Decoration::RestrictPointer:
385 case spirv::Decoration::NoContraction:
386 case spirv::Decoration::Constant:
387 case spirv::Decoration::Invariant:
388 case spirv::Decoration::Patch:
389 case spirv::Decoration::Coherent:
390 case spirv::Decoration::Volatile:
391 case spirv::Decoration::SaturatedConversion:
392 if (words.size() != 2) {
393 return emitError(unknownLoc,
"OpDecorate with ")
394 << decorationName <<
" needs a single target <id>";
396 decorations[words[0]].set(symbol, opBuilder.getUnitAttr());
398 case spirv::Decoration::CacheControlLoadINTEL: {
400 CacheControlLoadINTELAttr, LoadCacheControlAttr, LoadCacheControl>(
401 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
407 case spirv::Decoration::CacheControlStoreINTEL: {
409 CacheControlStoreINTELAttr, StoreCacheControlAttr, StoreCacheControl>(
410 unknownLoc, opBuilder, decorations, words, symbol, decorationName,
416 case spirv::Decoration::AlignmentId:
417 case spirv::Decoration::MaxByteOffsetId:
418 case spirv::Decoration::CounterBuffer:
419 if (words.size() != 3) {
420 return emitError(unknownLoc,
"OpDecorateId with ")
421 << decorationName <<
" needs a single <id> operand";
423 pendingIdDecorations.push_back({words[0],
424 static_cast<spirv::Decoration
>(words[1]),
425 words[2], unknownLoc});
428 return emitError(unknownLoc,
"unhandled Decoration : '") << decorationName;
433LogicalResult spirv::Deserializer::resolveDeferredIdDecorations() {
434 for (
const DeferredIdDecoration &entry : pendingIdDecorations) {
435 StringRef decorationName = stringifyDecoration(entry.decoration);
436 StringAttr symbol = getSymbolDecoration(decorationName);
440 StringRef operandSymName;
441 if (spirv::GlobalVariableOp varOp =
442 globalVariableMap.lookup(entry.operandID))
443 operandSymName = varOp.getSymName();
444 else if (spirv::SpecConstantOp specOp =
445 specConstMap.lookup(entry.operandID))
446 operandSymName = specOp.getSymName();
448 return emitError(entry.loc,
"OpDecorateId with ")
449 << decorationName <<
" references <id> " << entry.operandID
450 <<
" which is not a global variable or specialization constant";
457 Operation *targetOp =
nullptr;
458 if (spirv::GlobalVariableOp varOp =
459 globalVariableMap.lookup(entry.targetID))
461 else if (spirv::SpecConstantOp specOp = specConstMap.lookup(entry.targetID))
463 else if (spirv::FuncOp fnOp = funcMap.lookup(entry.targetID))
465 else if (Value v = valueMap.lookup(entry.targetID))
466 targetOp = v.getDefiningOp();
469 return emitError(entry.loc,
"OpDecorateId with ")
470 << decorationName <<
" references unknown target <id> "
479spirv::Deserializer::processMemberDecoration(ArrayRef<uint32_t> words) {
481 if (words.size() < 3) {
483 "OpMemberDecorate must have at least 3 operands");
486 auto decoration =
static_cast<spirv::Decoration
>(words[2]);
487 if (decoration == spirv::Decoration::Offset && words.size() != 4) {
489 " missing offset specification in OpMemberDecorate with "
490 "Offset decoration");
492 ArrayRef<uint32_t> decorationOperands;
493 if (words.size() > 3) {
494 decorationOperands = words.slice(3);
496 memberDecorationMap[words[0]][words[1]][decoration] = decorationOperands;
500LogicalResult spirv::Deserializer::processMemberName(ArrayRef<uint32_t> words) {
501 if (words.size() < 3) {
502 return emitError(unknownLoc,
"OpMemberName must have at least 3 operands");
504 unsigned wordIndex = 2;
506 if (wordIndex != words.size()) {
508 "unexpected trailing words in OpMemberName instruction");
510 memberNameMap[words[0]][words[1]] = name;
516 spirv::DecorationAttr foundDecorationAttr;
518 if (
auto it = functionParameterAttrs.find(argID);
519 it != functionParameterAttrs.end()) {
521 functionParameterAttrs.erase(it);
525 spirv::Decoration::Alignment))) {
530 if (!isa<UnitAttr>(decAttr.getValue()))
531 return emitError(unknownLoc,
"unhandled decoration '")
532 << decAttr.getName().getValue()
533 <<
"' on function argument with result <id> " << argID;
536 if (foundDecorationAttr)
537 return emitError(unknownLoc,
"already found a decoration for function "
538 "argument with result <id> ")
541 std::optional<spirv::Decoration> decoration = spirv::symbolizeDecoration(
542 llvm::convertToCamelFromSnakeCase(decAttr.getName(),
true));
544 return emitError(unknownLoc,
"unknown decoration '")
545 << decAttr.getName().getValue()
546 <<
"' on function argument with result <id> " << argID;
547 foundDecorationAttr = spirv::DecorationAttr::get(context, *decoration);
550 if (foundDecorationAttr)
551 attributes.
set(spirv::DecorationAttr::name, foundDecorationAttr);
559 return emitError(unknownLoc,
"found function inside function");
563 if (operands.size() != 4) {
564 return emitError(unknownLoc,
"OpFunction must have 4 parameters");
568 return emitError(unknownLoc,
"undefined result type from <id> ")
572 uint32_t fnID = operands[1];
573 if (funcMap.count(fnID)) {
574 return emitError(unknownLoc,
"duplicate function definition/declaration");
577 auto fnControl = spirv::symbolizeFunctionControl(operands[2]);
579 return emitError(unknownLoc,
"unknown Function Control: ") << operands[2];
583 if (!fnType || !isa<FunctionType>(fnType)) {
584 return emitError(unknownLoc,
"unknown function type from <id> ")
587 auto functionType = cast<FunctionType>(fnType);
589 if ((
isVoidType(resultType) && functionType.getNumResults() != 0) ||
590 (functionType.getNumResults() == 1 &&
591 functionType.getResult(0) != resultType)) {
592 return emitError(unknownLoc,
"mismatch in function type ")
593 << functionType <<
" and return type " << resultType <<
" specified";
597 auto funcOp = spirv::FuncOp::create(opBuilder, unknownLoc, fnName,
598 functionType, fnControl.value());
599 if (
auto it = functionParameterAttrs.find(fnID);
600 it != functionParameterAttrs.end()) {
601 if (functionType.getNumResults() == 0)
603 "FuncParamAttr decoration requires a non-void function "
606 functionParameterAttrs.erase(it);
609 if (decorations.count(fnID)) {
610 for (
auto attr : decorations[fnID].getAttrs()) {
614 curFunction = funcMap[fnID] = funcOp;
615 auto *entryBlock = funcOp.addEntryBlock();
618 <<
"//===-------------------------------------------===//\n";
619 logger.startLine() <<
"[fn] name: " << fnName <<
"\n";
620 logger.startLine() <<
"[fn] type: " << fnType <<
"\n";
621 logger.startLine() <<
"[fn] ID: " << fnID <<
"\n";
622 logger.startLine() <<
"[fn] entry block: " << entryBlock <<
"\n";
627 argAttrs.resize(functionType.getNumInputs());
630 if (functionType.getNumInputs()) {
631 for (
size_t i = 0, e = functionType.getNumInputs(); i != e; ++i) {
632 auto argType = functionType.getInput(i);
633 spirv::Opcode opcode = spirv::Opcode::OpNop;
636 spirv::Opcode::OpFunctionParameter))) {
639 if (opcode != spirv::Opcode::OpFunctionParameter) {
642 "missing OpFunctionParameter instruction for argument ")
645 if (operands.size() != 2) {
648 "expected result type and result <id> for OpFunctionParameter");
650 auto argDefinedType =
getType(operands[0]);
651 if (!argDefinedType || argDefinedType != argType) {
653 "mismatch in argument type between function type "
655 << functionType <<
" and argument type definition "
656 << argDefinedType <<
" at argument " << i;
659 return emitError(unknownLoc,
"duplicate definition of result <id> ")
666 auto argValue = funcOp.getArgument(i);
667 valueMap[operands[1]] = argValue;
671 if (llvm::any_of(argAttrs, [](
Attribute attr) {
672 auto argAttr = cast<DictionaryAttr>(attr);
673 return !argAttr.empty();
675 funcOp.setArgAttrsAttr(ArrayAttr::get(context, argAttrs));
680 auto linkageAttr = funcOp.getLinkageAttributes();
681 auto hasImportLinkage =
682 linkageAttr && (linkageAttr.value().getLinkageType().
getValue() ==
683 spirv::LinkageType::Import);
684 if (hasImportLinkage)
691 spirv::Opcode opcode = spirv::Opcode::OpNop;
700 spirv::Opcode::OpFunctionEnd))) {
703 if (opcode == spirv::Opcode::OpFunctionEnd) {
706 if (opcode != spirv::Opcode::OpLabel) {
707 return emitError(unknownLoc,
"a basic block must start with OpLabel");
709 if (instOperands.size() != 1) {
710 return emitError(unknownLoc,
"OpLabel should only have result <id>");
712 blockMap[instOperands[0]] = entryBlock;
720 spirv::Opcode::OpFunctionEnd)) &&
721 opcode != spirv::Opcode::OpFunctionEnd) {
726 if (opcode != spirv::Opcode::OpFunctionEnd) {
736 if (!operands.empty()) {
737 return emitError(unknownLoc,
"unexpected operands for OpFunctionEnd");
747 blockConstantMap.clear();
749 curFunction = std::nullopt;
754 <<
"//===-------------------------------------------===//\n";
761 if (operands.size() < 2) {
763 "missing graph defintion in OpGraphEntryPointARM");
766 unsigned wordIndex = 0;
767 uint32_t graphID = operands[wordIndex++];
768 if (!graphMap.contains(graphID)) {
770 "missing graph definition/declaration with id ")
774 spirv::GraphARMOp graphARM = graphMap[graphID];
776 graphARM.setSymName(name);
777 graphARM.setEntryPoint(
true);
780 for (
int64_t size = operands.size(); wordIndex < size; ++wordIndex) {
782 interface.push_back(SymbolRefAttr::get(arg.getOperation()));
784 return emitError(unknownLoc,
"undefined result <id> ")
785 << operands[wordIndex] <<
" while decoding OpGraphEntryPoint";
791 opBuilder.setInsertionPoint(graphARM);
792 spirv::GraphEntryPointARMOp::create(
793 opBuilder, unknownLoc, SymbolRefAttr::get(opBuilder.getContext(), name),
794 opBuilder.getArrayAttr(interface));
802 return emitError(unknownLoc,
"found graph inside graph");
805 if (operands.size() < 2) {
806 return emitError(unknownLoc,
"OpGraphARM must have at least 2 parameters");
810 if (!type || !isa<GraphType>(type)) {
811 return emitError(unknownLoc,
"unknown graph type from <id> ")
814 auto graphType = cast<GraphType>(type);
815 if (graphType.getNumResults() <= 0) {
816 return emitError(unknownLoc,
"expected at least one result");
819 uint32_t graphID = operands[1];
820 if (graphMap.count(graphID)) {
821 return emitError(unknownLoc,
"duplicate graph definition/declaration");
826 spirv::GraphARMOp::create(opBuilder, unknownLoc, graphName, graphType);
827 curGraph = graphMap[graphID] = graphOp;
828 Block *entryBlock = graphOp.addEntryBlock();
831 <<
"//===-------------------------------------------===//\n";
832 logger.startLine() <<
"[graph] name: " << graphName <<
"\n";
833 logger.startLine() <<
"[graph] type: " << graphType <<
"\n";
834 logger.startLine() <<
"[graph] ID: " << graphID <<
"\n";
835 logger.startLine() <<
"[graph] entry block: " << entryBlock <<
"\n";
840 for (
auto [
index, argType] : llvm::enumerate(graphType.getInputs())) {
841 spirv::Opcode opcode;
844 spirv::Opcode::OpGraphInputARM))) {
847 if (operands.size() != 3) {
848 return emitError(unknownLoc,
"expected result type, result <id> and "
849 "input index for OpGraphInputARM");
853 if (!argDefinedType) {
854 return emitError(unknownLoc,
"unknown operand type <id> ") << operands[0];
857 if (argDefinedType != argType) {
859 "mismatch in argument type between graph type "
861 << graphType <<
" and argument type definition " << argDefinedType
862 <<
" at argument " <<
index;
865 return emitError(unknownLoc,
"duplicate definition of result <id> ")
870 if (!inputIndexAttr) {
872 "unable to read inputIndex value from constant op ")
875 BlockArgument argValue = graphOp.getArgument(inputIndexAttr.getInt());
876 valueMap[operands[1]] = argValue;
879 graphOutputs.resize(graphType.getNumResults());
885 blockMap[graphID] = entryBlock;
892 spirv::Opcode opcode;
902 }
while (opcode != spirv::Opcode::OpGraphEndARM);
909 if (operands.size() != 2) {
912 "expected value id and output index for OpGraphSetOutputARM");
915 uint32_t
id = operands[0];
918 return emitError(unknownLoc,
"could not find result <id> ") << id;
922 if (!outputIndexAttr) {
924 "unable to read outputIndex value from constant op ")
927 graphOutputs[outputIndexAttr.getInt()] = value;
934 spirv::GraphOutputsARMOp::create(opBuilder, unknownLoc, graphOutputs);
937 if (!operands.empty()) {
938 return emitError(unknownLoc,
"unexpected operands for OpGraphEndARM");
941 blockConstantMap.clear();
943 curGraph = std::nullopt;
944 graphOutputs.clear();
949 <<
"//===-------------------------------------------===//\n";
954std::optional<std::pair<Attribute, Type>>
956 auto constIt = constantMap.find(
id);
957 if (constIt != constantMap.end())
958 return constIt->getSecond();
960 auto replicatedConstIt = constantCompositeReplicateMap.find(
id);
961 if (replicatedConstIt == constantCompositeReplicateMap.end())
964 auto [value, type] = replicatedConstIt->getSecond();
965 auto shapedType = dyn_cast<ShapedType>(type);
971std::optional<std::pair<Attribute, Type>>
973 if (
auto it = constantCompositeReplicateMap.find(
id);
974 it != constantCompositeReplicateMap.end())
979std::optional<spirv::SpecConstOperationMaterializationInfo>
981 auto constIt = specConstOperationMap.find(
id);
982 if (constIt == specConstOperationMap.end())
984 return constIt->getSecond();
988 auto funcName = nameMap.lookup(
id).str();
989 if (funcName.empty()) {
990 funcName =
"spirv_fn_" + std::to_string(
id);
996 std::string graphName = nameMap.lookup(
id).str();
997 if (graphName.empty()) {
998 graphName =
"spirv_graph_" + std::to_string(
id);
1004 auto constName = nameMap.lookup(
id).str();
1005 if (constName.empty()) {
1006 constName =
"spirv_spec_const_" + std::to_string(
id);
1011spirv::SpecConstantOp
1013 TypedAttr defaultValue) {
1015 auto op = spirv::SpecConstantOp::create(opBuilder, unknownLoc, symName,
1018 if (decorations.count(resultID)) {
1019 for (
auto attr : decorations[resultID].getAttrs())
1022 specConstMap[resultID] = op;
1026std::optional<spirv::GraphConstantARMOpMaterializationInfo>
1028 auto graphConstIt = graphConstantMap.find(
id);
1029 if (graphConstIt == graphConstantMap.end())
1030 return std::nullopt;
1031 return graphConstIt->getSecond();
1036 unsigned wordIndex = 0;
1037 if (operands.size() < 3) {
1040 "OpVariable needs at least 3 operands, type, <id> and storage class");
1044 auto type =
getType(operands[wordIndex]);
1046 return emitError(unknownLoc,
"unknown result type <id> : ")
1047 << operands[wordIndex];
1049 auto ptrType = dyn_cast<spirv::PointerType>(type);
1052 "expected a result type <id> to be a spirv.ptr, found : ")
1058 auto variableID = operands[wordIndex];
1059 auto variableName = nameMap.lookup(variableID).str();
1060 if (variableName.empty()) {
1061 variableName =
"spirv_var_" + std::to_string(variableID);
1066 auto storageClass =
static_cast<spirv::StorageClass
>(operands[wordIndex]);
1067 if (ptrType.getStorageClass() != storageClass) {
1068 return emitError(unknownLoc,
"mismatch in storage class of pointer type ")
1069 << type <<
" and that specified in OpVariable instruction : "
1070 << stringifyStorageClass(storageClass);
1077 if (wordIndex < operands.size()) {
1087 return emitError(unknownLoc,
"unknown <id> ")
1088 << operands[wordIndex] <<
"used as initializer";
1090 initializer = SymbolRefAttr::get(op);
1093 if (wordIndex != operands.size()) {
1095 "found more operands than expected when deserializing "
1096 "OpVariable instruction, only ")
1097 << wordIndex <<
" of " << operands.size() <<
" processed";
1100 auto varOp = spirv::GlobalVariableOp::create(
1101 opBuilder, loc, TypeAttr::get(type),
1102 opBuilder.getStringAttr(variableName), initializer);
1105 if (decorations.count(variableID)) {
1106 for (
auto attr : decorations[variableID].getAttrs())
1109 globalVariableMap[variableID] = varOp;
1118 return dyn_cast<IntegerAttr>(constInfo->first);
1122 if (operands.size() < 2) {
1123 return emitError(unknownLoc,
"OpName needs at least 2 operands");
1126 unsigned wordIndex = 1;
1128 if (wordIndex != operands.size()) {
1130 "unexpected trailing words in OpName instruction");
1135 nameMap.emplace_or_assign(operands[0], name);
1146 if (operands.empty()) {
1147 return emitError(unknownLoc,
"type instruction with opcode ")
1148 << spirv::stringifyOpcode(opcode) <<
" needs at least one <id>";
1153 if (typeMap.count(operands[0])) {
1154 return emitError(unknownLoc,
"duplicate definition for result <id> ")
1159 case spirv::Opcode::OpTypeVoid:
1160 if (operands.size() != 1)
1161 return emitError(unknownLoc,
"OpTypeVoid must have no parameters");
1162 typeMap[operands[0]] = opBuilder.getNoneType();
1164 case spirv::Opcode::OpTypeBool:
1165 if (operands.size() != 1)
1166 return emitError(unknownLoc,
"OpTypeBool must have no parameters");
1167 typeMap[operands[0]] = opBuilder.getI1Type();
1169 case spirv::Opcode::OpTypeInt: {
1170 if (operands.size() != 3)
1172 unknownLoc,
"OpTypeInt must have bitwidth and signedness parameters");
1181 auto sign = operands[2] == 1 ? IntegerType::SignednessSemantics::Signed
1182 : IntegerType::SignednessSemantics::Signless;
1183 typeMap[operands[0]] = IntegerType::get(context, operands[1], sign);
1185 case spirv::Opcode::OpTypeFloat: {
1186 if (operands.size() != 2 && operands.size() != 3)
1188 "OpTypeFloat expects either 2 operands (type, bitwidth) "
1189 "or 3 operands (type, bitwidth, encoding), but got ")
1191 uint32_t bitWidth = operands[1];
1194 if (operands.size() == 2) {
1197 floatTy = opBuilder.getF16Type();
1200 floatTy = opBuilder.getF32Type();
1203 floatTy = opBuilder.getF64Type();
1206 return emitError(unknownLoc,
"unsupported OpTypeFloat bitwidth: ")
1211 if (operands.size() == 3) {
1212 if (spirv::FPEncoding(operands[2]) == spirv::FPEncoding::BFloat16KHR &&
1214 floatTy = opBuilder.getBF16Type();
1215 else if (spirv::FPEncoding(operands[2]) ==
1216 spirv::FPEncoding::Float8E4M3EXT &&
1218 floatTy = opBuilder.getF8E4M3FNType();
1219 else if (spirv::FPEncoding(operands[2]) ==
1220 spirv::FPEncoding::Float8E5M2EXT &&
1222 floatTy = opBuilder.getF8E5M2Type();
1224 return emitError(unknownLoc,
"unsupported OpTypeFloat FP encoding: ")
1225 << operands[2] <<
" and bitWidth " << bitWidth;
1228 typeMap[operands[0]] = floatTy;
1230 case spirv::Opcode::OpTypeVector: {
1231 if (operands.size() != 3) {
1234 "OpTypeVector must have element type and count parameters");
1238 return emitError(unknownLoc,
"OpTypeVector references undefined <id> ")
1241 typeMap[operands[0]] = VectorType::get({operands[2]}, elementTy);
1243 case spirv::Opcode::OpTypePointer: {
1246 case spirv::Opcode::OpTypeArray:
1248 case spirv::Opcode::OpTypeCooperativeMatrixKHR:
1250 case spirv::Opcode::OpTypeFunction:
1252 case spirv::Opcode::OpTypeImage:
1254 case spirv::Opcode::OpTypeSampler:
1256 case spirv::Opcode::OpTypeNamedBarrier:
1258 case spirv::Opcode::OpTypeSampledImage:
1260 case spirv::Opcode::OpTypeRuntimeArray:
1262 case spirv::Opcode::OpTypeStruct:
1264 case spirv::Opcode::OpTypeMatrix:
1266 case spirv::Opcode::OpTypeTensorARM:
1268 case spirv::Opcode::OpTypeGraphARM:
1271 return emitError(unknownLoc,
"unhandled type instruction");
1278 if (operands.size() != 3)
1279 return emitError(unknownLoc,
"OpTypePointer must have two parameters");
1281 auto pointeeType =
getType(operands[2]);
1283 return emitError(unknownLoc,
"unknown OpTypePointer pointee type <id> ")
1286 uint32_t typePointerID = operands[0];
1287 auto storageClass =
static_cast<spirv::StorageClass
>(operands[1]);
1290 for (
auto *deferredStructIt = std::begin(deferredStructTypesInfos);
1291 deferredStructIt != std::end(deferredStructTypesInfos);) {
1292 for (
auto *unresolvedMemberIt =
1293 std::begin(deferredStructIt->unresolvedMemberTypes);
1294 unresolvedMemberIt !=
1295 std::end(deferredStructIt->unresolvedMemberTypes);) {
1296 if (unresolvedMemberIt->first == typePointerID) {
1300 deferredStructIt->memberTypes[unresolvedMemberIt->second] =
1301 typeMap[typePointerID];
1302 unresolvedMemberIt =
1303 deferredStructIt->unresolvedMemberTypes.erase(unresolvedMemberIt);
1305 ++unresolvedMemberIt;
1309 if (deferredStructIt->unresolvedMemberTypes.empty()) {
1311 auto structType = deferredStructIt->deferredStructType;
1313 assert(structType &&
"expected a spirv::StructType");
1314 assert(structType.isIdentified() &&
"expected an indentified struct");
1316 if (failed(structType.trySetBody(
1317 deferredStructIt->memberTypes, deferredStructIt->offsetInfo,
1318 deferredStructIt->memberDecorationsInfo,
1319 deferredStructIt->structDecorationsInfo)))
1322 deferredStructIt = deferredStructTypesInfos.erase(deferredStructIt);
1333 if (operands.size() != 3) {
1335 "OpTypeArray must have element type and count parameters");
1340 return emitError(unknownLoc,
"OpTypeArray references undefined <id> ")
1348 return emitError(unknownLoc,
"OpTypeArray count <id> ")
1349 << operands[2] <<
"can only come from normal constant right now";
1352 if (
auto intVal = dyn_cast<IntegerAttr>(countInfo->first)) {
1353 count = intVal.getValue().getZExtValue();
1355 return emitError(unknownLoc,
"OpTypeArray count must come from a "
1356 "scalar integer constant instruction");
1360 elementTy, count, typeDecorations.lookup(operands[0]));
1366 assert(!operands.empty() &&
"No operands for processing function type");
1367 if (operands.size() == 1) {
1368 return emitError(unknownLoc,
"missing return type for OpTypeFunction");
1370 auto returnType =
getType(operands[1]);
1372 return emitError(unknownLoc,
"unknown return type in OpTypeFunction");
1375 for (
size_t i = 2, e = operands.size(); i < e; ++i) {
1376 auto ty =
getType(operands[i]);
1378 return emitError(unknownLoc,
"unknown argument type in OpTypeFunction");
1380 argTypes.push_back(ty);
1386 typeMap[operands[0]] = FunctionType::get(context, argTypes, returnTypes);
1392 if (operands.size() != 6) {
1394 "OpTypeCooperativeMatrixKHR must have element type, "
1395 "scope, row and column parameters, and use");
1401 "OpTypeCooperativeMatrixKHR references undefined <id> ")
1405 std::optional<spirv::Scope> scope =
1410 "OpTypeCooperativeMatrixKHR references undefined scope <id> ")
1419 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Rows` references "
1420 "undefined constant <id> ")
1424 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Columns` "
1425 "references undefined constant <id> ")
1429 return emitError(unknownLoc,
"OpTypeCooperativeMatrixKHR `Use` references "
1430 "undefined constant <id> ")
1433 unsigned rows = rowsAttr.getInt();
1434 unsigned columns = columnsAttr.getInt();
1436 std::optional<spirv::CooperativeMatrixUseKHR> use =
1437 spirv::symbolizeCooperativeMatrixUseKHR(useAttr.getInt());
1441 "OpTypeCooperativeMatrixKHR references undefined use <id> ")
1445 typeMap[operands[0]] =
1452 if (operands.size() != 2) {
1453 return emitError(unknownLoc,
"OpTypeRuntimeArray must have two operands");
1458 "OpTypeRuntimeArray references undefined <id> ")
1462 memberType, typeDecorations.lookup(operands[0]));
1470 if (operands.empty()) {
1471 return emitError(unknownLoc,
"OpTypeStruct must have at least result <id>");
1474 if (operands.size() == 1) {
1476 typeMap[operands[0]] =
1485 for (
auto op : llvm::drop_begin(operands, 1)) {
1487 bool typeForwardPtr = (typeForwardPointerIDs.count(op) != 0);
1489 if (!memberType && !typeForwardPtr)
1490 return emitError(unknownLoc,
"OpTypeStruct references undefined <id> ")
1494 unresolvedMemberTypes.emplace_back(op, memberTypes.size());
1496 memberTypes.push_back(memberType);
1501 if (memberDecorationMap.count(operands[0])) {
1502 auto &allMemberDecorations = memberDecorationMap[operands[0]];
1503 for (
auto memberIndex : llvm::seq<uint32_t>(0, memberTypes.size())) {
1504 if (allMemberDecorations.count(memberIndex)) {
1505 for (
auto &memberDecoration : allMemberDecorations[memberIndex]) {
1507 if (memberDecoration.first == spirv::Decoration::Offset) {
1509 if (offsetInfo.empty()) {
1510 offsetInfo.resize(memberTypes.size());
1512 offsetInfo[memberIndex] = memberDecoration.second[0];
1514 auto intType = mlir::IntegerType::get(context, 32);
1515 if (!memberDecoration.second.empty()) {
1516 memberDecorationsInfo.emplace_back(
1517 memberIndex, memberDecoration.first,
1518 IntegerAttr::get(intType, memberDecoration.second[0]));
1520 memberDecorationsInfo.emplace_back(
1521 memberIndex, memberDecoration.first, UnitAttr::get(context));
1530 if (decorations.count(operands[0])) {
1533 std::optional<spirv::Decoration> decoration = spirv::symbolizeDecoration(
1534 llvm::convertToCamelFromSnakeCase(decorationAttr.getName(),
true));
1535 assert(decoration.has_value());
1536 structDecorationsInfo.emplace_back(decoration.value(),
1537 decorationAttr.getValue());
1541 uint32_t structID = operands[0];
1542 std::string structIdentifier = nameMap.lookup(structID).str();
1544 if (structIdentifier.empty()) {
1545 assert(unresolvedMemberTypes.empty() &&
1546 "didn't expect unresolved member types");
1548 memberTypes, offsetInfo, memberDecorationsInfo, structDecorationsInfo);
1551 typeMap[structID] = structTy;
1553 if (!unresolvedMemberTypes.empty())
1554 deferredStructTypesInfos.push_back(
1555 {structTy, std::move(unresolvedMemberTypes), std::move(memberTypes),
1556 std::move(offsetInfo), std::move(memberDecorationsInfo),
1557 std::move(structDecorationsInfo)});
1558 else if (failed(structTy.trySetBody(memberTypes, offsetInfo,
1559 memberDecorationsInfo,
1560 structDecorationsInfo)))
1571 if (operands.size() != 3) {
1573 return emitError(unknownLoc,
"OpTypeMatrix must have 3 operands"
1574 " (result_id, column_type, and column_count)");
1580 "OpTypeMatrix references undefined column type.")
1584 uint32_t colsCount = operands[2];
1591 unsigned size = operands.size();
1592 if (size < 2 || size > 4)
1593 return emitError(unknownLoc,
"OpTypeTensorARM must have 2-4 operands "
1594 "(result_id, element_type, (rank), (shape)) ")
1600 "OpTypeTensorARM references undefined element type ")
1610 return emitError(unknownLoc,
"OpTypeTensorARM rank must come from a "
1611 "scalar integer constant instruction");
1612 unsigned rank = rankAttr.getValue().getZExtValue();
1619 std::optional<std::pair<Attribute, Type>> shapeInfo =
1622 return emitError(unknownLoc,
"OpTypeTensorARM shape must come from a "
1623 "constant instruction of type OpTypeArray");
1625 ArrayAttr shapeArrayAttr = dyn_cast<ArrayAttr>(shapeInfo->first);
1627 for (
auto dimAttr : shapeArrayAttr.getValue()) {
1628 auto dimIntAttr = dyn_cast<IntegerAttr>(dimAttr);
1630 return emitError(unknownLoc,
"OpTypeTensorARM shape has an invalid "
1632 shape.push_back(dimIntAttr.getValue().getSExtValue());
1640 unsigned size = operands.size();
1642 return emitError(unknownLoc,
"OpTypeGraphARM must have at least 2 operands "
1643 "(result_id, num_inputs, (inout0_type, "
1644 "inout1_type, ...))")
1647 uint32_t numInputs = operands[1];
1650 for (
unsigned i = 2; i < size; ++i) {
1654 "OpTypeGraphARM references undefined element type.")
1657 if (i - 2 >= numInputs) {
1658 returnTypes.push_back(inOutTy);
1660 argTypes.push_back(inOutTy);
1663 typeMap[operands[0]] = GraphType::get(context, argTypes, returnTypes);
1669 if (operands.size() != 2)
1671 "OpTypeForwardPointer instruction must have two operands");
1673 typeForwardPointerIDs.insert(operands[0]);
1683 if (operands.size() != 8)
1686 "OpTypeImage with non-eight operands are not supported yet");
1690 return emitError(unknownLoc,
"OpTypeImage references undefined <id>: ")
1693 auto dim = spirv::symbolizeDim(operands[2]);
1695 return emitError(unknownLoc,
"unknown Dim for OpTypeImage: ")
1698 auto depthInfo = spirv::symbolizeImageDepthInfo(operands[3]);
1700 return emitError(unknownLoc,
"unknown Depth for OpTypeImage: ")
1703 auto arrayedInfo = spirv::symbolizeImageArrayedInfo(operands[4]);
1705 return emitError(unknownLoc,
"unknown Arrayed for OpTypeImage: ")
1708 auto samplingInfo = spirv::symbolizeImageSamplingInfo(operands[5]);
1710 return emitError(unknownLoc,
"unknown MS for OpTypeImage: ") << operands[5];
1712 auto samplerUseInfo = spirv::symbolizeImageSamplerUseInfo(operands[6]);
1713 if (!samplerUseInfo)
1714 return emitError(unknownLoc,
"unknown Sampled for OpTypeImage: ")
1717 auto format = spirv::symbolizeImageFormat(operands[7]);
1719 return emitError(unknownLoc,
"unknown Format for OpTypeImage: ")
1723 elementTy, dim.value(), depthInfo.value(), arrayedInfo.value(),
1724 samplingInfo.value(), samplerUseInfo.value(), format.value());
1730 if (operands.size() != 2)
1731 return emitError(unknownLoc,
"OpTypeSampledImage must have two operands");
1736 "OpTypeSampledImage references undefined <id>: ")
1745 if (operands.size() != 1)
1746 return emitError(unknownLoc,
"OpTypeSampler must have no parameters");
1754 if (operands.size() != 1)
1755 return emitError(unknownLoc,
"OpTypeNamedBarrier must have no parameters");
1767 StringRef opname = isSpec ?
"OpSpecConstant" :
"OpConstant";
1769 if (operands.size() < 2) {
1771 << opname <<
" must have type <id> and result <id>";
1773 if (operands.size() < 3) {
1775 << opname <<
" must have at least 1 more parameter";
1780 return emitError(unknownLoc,
"undefined result type from <id> ")
1784 auto checkOperandSizeForBitwidth = [&](
unsigned bitwidth) -> LogicalResult {
1785 if (bitwidth == 64) {
1786 if (operands.size() == 4) {
1790 << opname <<
" should have 2 parameters for 64-bit values";
1792 if (bitwidth <= 32) {
1793 if (operands.size() == 3) {
1799 <<
" should have 1 parameter for values with no more than 32 bits";
1801 return emitError(unknownLoc,
"unsupported OpConstant bitwidth: ")
1805 auto resultID = operands[1];
1807 if (
auto intType = dyn_cast<IntegerType>(resultType)) {
1808 auto bitwidth = intType.getWidth();
1809 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1814 if (bitwidth == 64) {
1821 } words = {operands[2], operands[3]};
1822 value = APInt(64, llvm::bit_cast<uint64_t>(words),
true);
1823 }
else if (bitwidth <= 32) {
1824 value = APInt(bitwidth, operands[2],
true,
1828 auto attr = opBuilder.getIntegerAttr(intType, value);
1835 constantMap.try_emplace(resultID, attr, intType);
1841 if (
auto floatType = dyn_cast<FloatType>(resultType)) {
1842 auto bitwidth = floatType.getWidth();
1843 if (failed(checkOperandSizeForBitwidth(bitwidth))) {
1848 if (floatType.isF64()) {
1855 } words = {operands[2], operands[3]};
1856 value = APFloat(llvm::bit_cast<double>(words));
1857 }
else if (floatType.isF32()) {
1858 value = APFloat(llvm::bit_cast<float>(operands[2]));
1859 }
else if (floatType.isF16()) {
1860 APInt data(16, operands[2]);
1861 value = APFloat(APFloat::IEEEhalf(), data);
1862 }
else if (floatType.isBF16()) {
1863 APInt data(16, operands[2]);
1864 value = APFloat(APFloat::BFloat(), data);
1865 }
else if (floatType.isF8E4M3FN()) {
1866 APInt data(8, operands[2]);
1867 value = APFloat(APFloat::Float8E4M3FN(), data);
1868 }
else if (floatType.isF8E5M2()) {
1869 APInt data(8, operands[2]);
1870 value = APFloat(APFloat::Float8E5M2(), data);
1873 auto attr = opBuilder.getFloatAttr(floatType, value);
1879 constantMap.try_emplace(resultID, attr, floatType);
1885 return emitError(unknownLoc,
"OpConstant can only generate values of "
1886 "scalar integer or floating-point type");
1891 if (operands.size() != 2) {
1893 << (isSpec ?
"Spec" :
"") <<
"Constant"
1894 << (isTrue ?
"True" :
"False")
1895 <<
" must have type <id> and result <id>";
1898 auto attr = opBuilder.getBoolAttr(isTrue);
1899 auto resultID = operands[1];
1905 constantMap.try_emplace(resultID, attr, opBuilder.getI1Type());
1913 if (operands.size() < 2) {
1915 "OpConstantComposite must have type <id> and result <id>");
1917 if (operands.size() < 3) {
1919 "OpConstantComposite must have at least 1 parameter");
1924 return emitError(unknownLoc,
"undefined result type from <id> ")
1929 elements.reserve(operands.size() - 2);
1930 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
1933 return emitError(unknownLoc,
"OpConstantComposite component <id> ")
1934 << operands[i] <<
" must come from a normal constant";
1936 elements.push_back(elementInfo->first);
1939 auto resultID = operands[1];
1940 if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
1943 if (
auto denseElemAttr = dyn_cast<DenseElementsAttr>(element)) {
1944 for (
auto value : denseElemAttr.getValues<
Attribute>())
1945 flattenedElems.push_back(value);
1947 flattenedElems.push_back(element);
1951 constantMap.try_emplace(resultID, attr, tensorType);
1952 }
else if (
auto shapedType = dyn_cast<ShapedType>(resultType)) {
1956 constantMap.try_emplace(resultID, attr, shapedType);
1957 }
else if (isa<spirv::ArrayType, spirv::StructType>(resultType)) {
1958 auto attr = opBuilder.getArrayAttr(elements);
1959 constantMap.try_emplace(resultID, attr, resultType);
1961 return emitError(unknownLoc,
"unsupported OpConstantComposite type: ")
1970 if (operands.size() != 3) {
1973 "OpConstantCompositeReplicateEXT expects 3 operands but found ")
1979 return emitError(unknownLoc,
"undefined result type from <id> ")
1983 auto compositeType = dyn_cast<CompositeType>(resultType);
1984 if (!compositeType) {
1986 "result type from <id> is not a composite type")
1990 uint32_t resultID = operands[1];
1991 uint32_t constantID = operands[2];
1993 std::optional<std::pair<Attribute, Type>> replicatedConstantCompositeInfo =
1995 if (replicatedConstantCompositeInfo.has_value()) {
1996 constantCompositeReplicateMap.try_emplace(
1997 resultID, replicatedConstantCompositeInfo.value().first, resultType);
2001 std::optional<std::pair<Attribute, Type>> constantInfo =
2003 if (constantInfo.has_value()) {
2004 constantCompositeReplicateMap.try_emplace(
2005 resultID, constantInfo.value().first, resultType);
2009 return emitError(unknownLoc,
"OpConstantCompositeReplicateEXT operand <id> ")
2011 <<
" must come from a normal constant or a "
2012 "OpConstantCompositeReplicateEXT";
2017 if (operands.size() < 2) {
2020 "OpSpecConstantComposite must have type <id> and result <id>");
2022 if (operands.size() < 3) {
2024 "OpSpecConstantComposite must have at least 1 parameter");
2029 return emitError(unknownLoc,
"undefined result type from <id> ")
2033 auto resultID = operands[1];
2037 elements.reserve(operands.size() - 2);
2038 for (
unsigned i = 2, e = operands.size(); i < e; ++i) {
2040 elements.push_back(SymbolRefAttr::get(elementInfo));
2043 auto op = spirv::SpecConstantCompositeOp::create(
2044 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2045 opBuilder.getArrayAttr(elements),
nullptr);
2046 specConstCompositeMap[resultID] = op;
2053 if (operands.size() != 3) {
2054 return emitError(unknownLoc,
"OpSpecConstantCompositeReplicateEXT expects "
2055 "3 operands but found ")
2061 return emitError(unknownLoc,
"undefined result type from <id> ")
2065 auto compositeType = dyn_cast<CompositeType>(resultType);
2066 if (!compositeType) {
2068 "result type from <id> is not a composite type")
2072 uint32_t resultID = operands[1];
2075 spirv::SpecConstantOp constituentSpecConstantOp =
2077 auto op = spirv::EXTSpecConstantCompositeReplicateOp::create(
2078 opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
2079 SymbolRefAttr::get(constituentSpecConstantOp),
2082 specConstCompositeReplicateMap[resultID] = op;
2089 if (operands.size() < 3)
2090 return emitError(unknownLoc,
"OpConstantOperation must have type <id>, "
2091 "result <id>, and operand opcode");
2093 uint32_t resultTypeID = operands[0];
2096 return emitError(unknownLoc,
"undefined result type from <id> ")
2099 uint32_t resultID = operands[1];
2100 spirv::Opcode enclosedOpcode =
static_cast<spirv::Opcode
>(operands[2]);
2101 auto emplaceResult = specConstOperationMap.try_emplace(
2104 enclosedOpcode, resultTypeID,
2107 if (!emplaceResult.second)
2108 return emitError(unknownLoc,
"value with <id>: ")
2109 << resultID <<
" is probably defined before.";
2115 uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID,
2131 llvm::SaveAndRestore valueMapGuard(valueMap, newValueMap);
2132 constexpr uint32_t fakeID =
static_cast<uint32_t
>(-3);
2135 enclosedOpResultTypeAndOperands.push_back(resultTypeID);
2136 enclosedOpResultTypeAndOperands.push_back(fakeID);
2137 enclosedOpResultTypeAndOperands.append(enclosedOpOperands.begin(),
2138 enclosedOpOperands.end());
2153 auto specConstOperationOp =
2154 spirv::SpecConstantOperationOp::create(opBuilder, loc, resultType);
2156 Region &body = specConstOperationOp.getBody();
2158 body.
getBlocks().splice(body.
end(), curBlock->getParent()->getBlocks(),
2165 opBuilder.setInsertionPointToEnd(&block);
2167 spirv::YieldOp::create(opBuilder, loc, block.
front().
getResult(0));
2168 return specConstOperationOp.getResult();
2173 if (operands.size() != 2) {
2175 "OpConstantNull must only have type <id> and result <id>");
2180 return emitError(unknownLoc,
"undefined result type from <id> ")
2184 auto resultID = operands[1];
2186 if (resultType.
isIntOrFloat() || isa<VectorType>(resultType)) {
2187 attr = opBuilder.getZeroAttr(resultType);
2188 }
else if (
auto tensorType = dyn_cast<TensorArmType>(resultType)) {
2189 if (
auto element = opBuilder.getZeroAttr(tensorType.getElementType()))
2196 constantMap.try_emplace(resultID, attr, resultType);
2200 return emitError(unknownLoc,
"unsupported OpConstantNull type: ")
2206 if (operands.size() < 3) {
2208 <<
"OpGraphConstantARM must have at least 2 operands";
2213 return emitError(unknownLoc,
"undefined result type from <id> ")
2217 uint32_t resultID = operands[1];
2219 if (!dyn_cast<spirv::TensorArmType>(resultType)) {
2220 return emitError(unknownLoc,
"result must be of type OpTypeTensorARM");
2223 APInt graph_constant_id = APInt(32, operands[2],
true);
2224 Type i32Ty = opBuilder.getIntegerType(32);
2225 IntegerAttr attr = opBuilder.getIntegerAttr(i32Ty, graph_constant_id);
2226 graphConstantMap.try_emplace(
2238 LLVM_DEBUG(logger.startLine() <<
"[block] got exiting block for id = " <<
id
2239 <<
" @ " << block <<
"\n");
2246 auto *block = curFunction->addBlock();
2247 LLVM_DEBUG(logger.startLine() <<
"[block] created block for id = " <<
id
2248 <<
" @ " << block <<
"\n");
2249 return blockMap[id] = block;
2254 return emitError(unknownLoc,
"OpBranch must appear inside a block");
2257 if (operands.size() != 1) {
2258 return emitError(unknownLoc,
"OpBranch must take exactly one target label");
2266 spirv::BranchOp::create(opBuilder, loc,
target);
2276 "OpBranchConditional must appear inside a block");
2279 if (operands.size() != 3 && operands.size() != 5) {
2281 "OpBranchConditional must have condition, true label, "
2282 "false label, and optionally two branch weights");
2285 auto condition =
getValue(operands[0]);
2289 std::optional<std::pair<uint32_t, uint32_t>> weights;
2290 if (operands.size() == 5) {
2291 weights = std::make_pair(operands[3], operands[4]);
2297 spirv::BranchConditionalOp::create(
2298 opBuilder, loc, condition, trueBlock,
2308 return emitError(unknownLoc,
"OpLabel must appear inside a function");
2311 if (operands.size() != 1) {
2312 return emitError(unknownLoc,
"OpLabel should only have result <id>");
2315 auto labelID = operands[0];
2318 LLVM_DEBUG(logger.startLine()
2319 <<
"[block] populating block " << block <<
"\n");
2321 assert(block->empty() &&
"re-deserialize the same block!");
2323 opBuilder.setInsertionPointToStart(block);
2324 blockMap[labelID] = curBlock = block;
2331 return emitError(unknownLoc,
"a graph block must appear inside a graph");
2336 LLVM_DEBUG(logger.startLine()
2337 <<
"[block] populating block " << block <<
"\n");
2339 assert(block->
empty() &&
"re-deserialize the same block!");
2341 opBuilder.setInsertionPointToStart(block);
2342 blockMap[graphID] = curBlock = block;
2350 return emitError(unknownLoc,
"OpSelectionMerge must appear in a block");
2353 if (operands.size() < 2) {
2356 "OpSelectionMerge must specify merge target and selection control");
2361 auto selectionControl = operands[1];
2363 if (!blockMergeInfo.try_emplace(curBlock, loc, selectionControl, mergeBlock)
2367 "a block cannot have more than one OpSelectionMerge instruction");
2376 return emitError(unknownLoc,
"OpLoopMerge must appear in a block");
2379 if (operands.size() < 3) {
2380 return emitError(unknownLoc,
"OpLoopMerge must specify merge target, "
2381 "continue target and loop control");
2387 uint32_t loopControl = operands[2];
2390 .try_emplace(curBlock, loc, loopControl, mergeBlock, continueBlock)
2394 "a block cannot have more than one OpLoopMerge instruction");
2402 return emitError(unknownLoc,
"OpPhi must appear in a block");
2405 if (operands.size() < 4) {
2406 return emitError(unknownLoc,
"OpPhi must specify result type, result <id>, "
2407 "and variable-parent pairs");
2412 BlockArgument blockArg = curBlock->addArgument(blockArgType, unknownLoc);
2413 valueMap[operands[1]] = blockArg;
2414 LLVM_DEBUG(logger.startLine()
2415 <<
"[phi] created block argument " << blockArg
2416 <<
" id = " << operands[1] <<
" of type " << blockArgType <<
"\n");
2420 for (
unsigned i = 2, e = operands.size(); i < e; i += 2) {
2421 uint32_t value = operands[i];
2423 std::pair<Block *, Block *> predecessorTargetPair{predecessor, curBlock};
2424 blockPhiInfo[predecessorTargetPair].push_back(value);
2425 LLVM_DEBUG(logger.startLine() <<
"[phi] predecessor @ " << predecessor
2426 <<
" with arg id = " << value <<
"\n");
2434 return emitError(unknownLoc,
"OpSwitch must appear in a block");
2436 if (operands.size() < 2)
2437 return emitError(unknownLoc,
"OpSwitch must at least specify selector and "
2438 "a default target");
2442 return emitError(unknownLoc,
"unknown OpSwitch selector <id>: ")
2444 auto selectorType = dyn_cast<IntegerType>(selector.
getType());
2446 return emitError(unknownLoc,
"OpSwitch selector must be an integer");
2450 unsigned bitWidth = selectorType.getWidth();
2451 unsigned literalWords = llvm::divideCeil(bitWidth, 32);
2452 unsigned wordsPerCase = literalWords + 1;
2453 if ((operands.size() - 2) % wordsPerCase != 0)
2454 return emitError(unknownLoc,
"OpSwitch must specify selector, default "
2455 "target and any number of literal and label "
2456 "<id> pairs, where each literal of a ")
2457 << bitWidth <<
"-bit selector spans " << literalWords <<
" word(s)";
2464 for (
unsigned i = 2, e = operands.size(); i < e; i += wordsPerCase) {
2466 if (bitWidth <= 32) {
2470 literal = APInt(bitWidth, operands[i],
true,
2473 literal = APInt(bitWidth, 0);
2474 for (
unsigned w = 0; w < literalWords; ++w)
2475 literal.insertBits(APInt(32, operands[i + w]), w * 32);
2477 literals.push_back(literal);
2482 spirv::SwitchOp::create(opBuilder, loc, selector, defaultBlock,
2492class ControlFlowStructurizer {
2495 ControlFlowStructurizer(
Location loc, uint32_t control,
2498 llvm::ScopedPrinter &logger)
2499 : location(loc), control(control), blockMergeInfo(mergeInfo),
2500 headerBlock(header), mergeBlock(merge), continueBlock(cont),
2503 ControlFlowStructurizer(
Location loc, uint32_t control,
2506 : location(loc), control(control), blockMergeInfo(mergeInfo),
2507 headerBlock(header), mergeBlock(merge), continueBlock(cont) {}
2517 LogicalResult structurize();
2522 spirv::SelectionOp createSelectionOp(uint32_t selectionControl);
2525 spirv::LoopOp createLoopOp(uint32_t loopControl);
2528 void collectBlocksInConstruct();
2537 Block *continueBlock;
2543 llvm::ScopedPrinter &logger;
2549ControlFlowStructurizer::createSelectionOp(uint32_t selectionControl) {
2552 OpBuilder builder(&mergeBlock->front());
2554 auto control =
static_cast<spirv::SelectionControl
>(selectionControl);
2555 auto selectionOp = spirv::SelectionOp::create(builder, location, control);
2556 selectionOp.addMergeBlock(builder);
2561spirv::LoopOp ControlFlowStructurizer::createLoopOp(uint32_t loopControl) {
2564 OpBuilder builder(&mergeBlock->front());
2566 auto control =
static_cast<spirv::LoopControl
>(loopControl);
2567 auto loopOp = spirv::LoopOp::create(builder, location, control);
2568 loopOp.addEntryAndMergeBlock(builder);
2573void ControlFlowStructurizer::collectBlocksInConstruct() {
2574 assert(constructBlocks.empty() &&
"expected empty constructBlocks");
2577 constructBlocks.insert(headerBlock);
2581 for (
unsigned i = 0; i < constructBlocks.size(); ++i) {
2582 for (
auto *successor : constructBlocks[i]->getSuccessors())
2583 if (successor != mergeBlock)
2584 constructBlocks.insert(successor);
2588LogicalResult ControlFlowStructurizer::structurize() {
2589 Operation *op =
nullptr;
2590 bool isLoop = continueBlock !=
nullptr;
2592 if (
auto loopOp = createLoopOp(control))
2593 op = loopOp.getOperation();
2595 if (
auto selectionOp = createSelectionOp(control))
2596 op = selectionOp.getOperation();
2605 mapper.
map(mergeBlock, &body.
back());
2607 collectBlocksInConstruct();
2628 OpBuilder builder(body);
2629 for (
auto *block : constructBlocks) {
2632 auto *newBlock = builder.createBlock(&body.
back());
2633 mapper.
map(block, newBlock);
2634 LLVM_DEBUG(logger.startLine() <<
"[cf] cloned block " << newBlock
2635 <<
" from block " << block <<
"\n");
2637 for (BlockArgument blockArg : block->getArguments()) {
2639 newBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2640 mapper.
map(blockArg, newArg);
2641 LLVM_DEBUG(logger.startLine() <<
"[cf] remapped block argument "
2642 << blockArg <<
" to " << newArg <<
"\n");
2645 LLVM_DEBUG(logger.startLine()
2646 <<
"[cf] block " << block <<
" is a function entry block\n");
2649 for (
auto &op : *block)
2650 newBlock->push_back(op.
clone(mapper));
2654 auto remapOperands = [&](Operation *op) {
2656 if (Value mappedOp = mapper.
lookupOrNull(operand.get()))
2657 operand.set(mappedOp);
2660 succOp.set(mappedOp);
2662 for (
auto &block : body)
2663 block.walk(remapOperands);
2671 headerBlock->replaceAllUsesWith(mergeBlock);
2674 logger.startLine() <<
"[cf] after cloning and fixing references:\n";
2675 headerBlock->getParentOp()->print(logger.getOStream());
2676 logger.startLine() <<
"\n";
2680 if (!mergeBlock->args_empty()) {
2681 return mergeBlock->getParentOp()->emitError(
2682 "OpPhi in loop merge block unsupported");
2688 for (BlockArgument blockArg : headerBlock->getArguments())
2689 mergeBlock->addArgument(blockArg.getType(), blockArg.getLoc());
2693 SmallVector<Value, 4> blockArgs;
2694 if (!headerBlock->args_empty())
2695 blockArgs = {mergeBlock->args_begin(), mergeBlock->args_end()};
2699 builder.setInsertionPointToEnd(&body.front());
2700 spirv::BranchOp::create(builder, location, mapper.
lookupOrNull(headerBlock),
2701 ArrayRef<Value>(blockArgs));
2706 SmallVector<Value> valuesToYield;
2709 SmallVector<Value> outsideUses;
2723 for (BlockArgument blockArg : mergeBlock->getArguments()) {
2728 body.back().addArgument(blockArg.getType(), blockArg.getLoc());
2729 valuesToYield.push_back(body.back().getArguments().back());
2730 outsideUses.push_back(blockArg);
2735 LLVM_DEBUG(logger.startLine() <<
"[cf] cleaning up blocks after clone\n");
2738 for (
auto *block : constructBlocks)
2739 block->dropAllReferences();
2744 for (
Block *block : constructBlocks) {
2745 for (Operation &op : *block) {
2749 outsideUses.push_back(
result);
2752 for (BlockArgument &arg : block->getArguments()) {
2753 if (!arg.use_empty()) {
2755 outsideUses.push_back(arg);
2760 assert(valuesToYield.size() == outsideUses.size());
2764 if (!valuesToYield.empty()) {
2765 LLVM_DEBUG(logger.startLine()
2766 <<
"[cf] yielding values from the selection / loop region\n");
2769 auto mergeOps = body.back().getOps<spirv::MergeOp>();
2770 Operation *merge = llvm::getSingleElement(mergeOps);
2772 merge->setOperands(valuesToYield);
2780 builder.setInsertionPoint(&mergeBlock->front());
2782 Operation *newOp =
nullptr;
2785 newOp = spirv::LoopOp::create(builder, location,
2787 static_cast<spirv::LoopControl
>(control));
2789 newOp = spirv::SelectionOp::create(
2791 static_cast<spirv::SelectionControl
>(control));
2801 for (
unsigned i = 0, e = outsideUses.size(); i != e; ++i)
2802 outsideUses[i].replaceAllUsesWith(op->
getResult(i));
2808 mergeBlock->eraseArguments(0, mergeBlock->getNumArguments());
2815 for (
auto *block : constructBlocks) {
2816 if (!block->use_empty())
2817 return emitError(block->getParent()->getLoc(),
2818 "failed control flow structurization: "
2819 "block has uses outside of the "
2820 "enclosing selection/loop construct");
2821 for (Operation &op : *block)
2823 return op.
emitOpError(
"failed control flow structurization: value has "
2824 "uses outside of the "
2825 "enclosing selection/loop construct");
2826 for (BlockArgument &arg : block->getArguments())
2827 if (!arg.use_empty())
2828 return emitError(arg.getLoc(),
"failed control flow structurization: "
2829 "block argument has uses outside of the "
2830 "enclosing selection/loop construct");
2834 for (
auto *block : constructBlocks) {
2874 auto updateMergeInfo = [&](
Block *block) -> WalkResult {
2875 auto it = blockMergeInfo.find(block);
2876 if (it != blockMergeInfo.end()) {
2878 Location loc = it->second.loc;
2880 uint32_t nestedControl = it->second.control;
2884 return emitError(loc,
"failed control flow structurization: nested "
2885 "loop header block should be remapped!");
2887 Block *newContinue = it->second.continueBlock;
2891 return emitError(loc,
"failed control flow structurization: nested "
2892 "loop continue block should be remapped!");
2895 Block *newMerge = it->second.mergeBlock;
2897 newMerge = mappedTo;
2900 blockMergeInfo.
erase(it);
2901 blockMergeInfo.try_emplace(newHeader, loc, nestedControl, newMerge,
2908 if (block->walk(updateMergeInfo).wasInterrupted())
2916 LLVM_DEBUG(logger.startLine() <<
"[cf] changing entry block " << block
2917 <<
" to only contain a spirv.Branch op\n");
2921 builder.setInsertionPointToEnd(block);
2922 spirv::BranchOp::create(builder, location, mergeBlock);
2924 LLVM_DEBUG(logger.startLine() <<
"[cf] erasing block " << block <<
"\n");
2929 LLVM_DEBUG(logger.startLine()
2930 <<
"[cf] after structurizing construct with header block "
2931 << headerBlock <<
":\n"
2940 <<
"//----- [phi] start wiring up block arguments -----//\n";
2946 for (
const auto &info : blockPhiInfo) {
2947 Block *block = info.first.first;
2951 logger.startLine() <<
"[phi] block " << block <<
"\n";
2952 logger.startLine() <<
"[phi] before creating block argument:\n";
2954 logger.startLine() <<
"\n";
2960 opBuilder.setInsertionPoint(op);
2963 blockArgs.reserve(phiInfo.size());
2964 for (uint32_t valueId : phiInfo) {
2966 blockArgs.push_back(value);
2967 LLVM_DEBUG(logger.startLine() <<
"[phi] block argument " << value
2968 <<
" id = " << valueId <<
"\n");
2970 return emitError(unknownLoc,
"OpPhi references undefined value!");
2974 if (
auto branchOp = dyn_cast<spirv::BranchOp>(op)) {
2976 spirv::BranchOp::create(opBuilder, branchOp.getLoc(),
2977 branchOp.getTarget(), blockArgs);
2979 }
else if (
auto branchCondOp = dyn_cast<spirv::BranchConditionalOp>(op)) {
2980 assert((branchCondOp.getTrueBlock() ==
target ||
2981 branchCondOp.getFalseBlock() ==
target) &&
2982 "expected target to be either the true or false target");
2983 if (
target == branchCondOp.getTrueTarget())
2984 spirv::BranchConditionalOp::create(
2985 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2986 blockArgs, branchCondOp.getFalseBlockArguments(),
2987 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueTarget(),
2988 branchCondOp.getFalseTarget());
2990 spirv::BranchConditionalOp::create(
2991 opBuilder, branchCondOp.getLoc(), branchCondOp.getCondition(),
2992 branchCondOp.getTrueBlockArguments(), blockArgs,
2993 branchCondOp.getBranchWeightsAttr(), branchCondOp.getTrueBlock(),
2994 branchCondOp.getFalseBlock());
2996 branchCondOp.erase();
2997 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(op)) {
2998 if (
target == switchOp.getDefaultTarget()) {
3002 spirv::SwitchOp::create(
3003 opBuilder, switchOp.getLoc(), switchOp.getSelector(),
3004 switchOp.getDefaultTarget(), blockArgs, literals,
3005 switchOp.getTargets(), targetOperands);
3009 auto it = llvm::find(targets,
target);
3010 assert(it != targets.end());
3011 size_t index = std::distance(targets.begin(), it);
3012 switchOp.getTargetOperandsMutable(
index).assign(blockArgs);
3015 return emitError(unknownLoc,
"unimplemented terminator for Phi creation");
3019 logger.startLine() <<
"[phi] after creating block argument:\n";
3021 logger.startLine() <<
"\n";
3024 blockPhiInfo.clear();
3029 <<
"//--- [phi] completed wiring up block arguments ---//\n";
3037 for (
auto [block, mergeInfo] : blockMergeInfoCopy) {
3039 if (mergeInfo.continueBlock)
3042 if (!block->mightHaveTerminator())
3045 Operation *terminator = block->getTerminator();
3048 if (!isa<spirv::BranchConditionalOp, spirv::SwitchOp>(terminator))
3052 bool splitHeaderMergeBlock =
false;
3053 for (
const auto &[_, mergeInfo] : blockMergeInfo) {
3054 if (mergeInfo.mergeBlock == block)
3055 splitHeaderMergeBlock =
true;
3062 if (!llvm::hasSingleElement(*block) || splitHeaderMergeBlock ||
3066 spirv::BranchOp::create(builder, block->getParent()->getLoc(), newBlock);
3070 blockMergeInfo.erase(block);
3071 blockMergeInfo.try_emplace(newBlock, mergeInfo);
3079 if (!options.enableControlFlowStructurization) {
3083 <<
"//----- [cf] skip structurizing control flow -----//\n";
3091 <<
"//----- [cf] start structurizing control flow -----//\n";
3096 logger.startLine() <<
"[cf] split conditional blocks\n";
3097 logger.startLine() <<
"\n";
3104 while (!blockMergeInfo.empty()) {
3105 Block *headerBlock = blockMergeInfo.
begin()->first;
3109 logger.startLine() <<
"[cf] header block " << headerBlock <<
":\n";
3110 headerBlock->
print(logger.getOStream());
3111 logger.startLine() <<
"\n";
3115 assert(mergeBlock &&
"merge block cannot be nullptr");
3117 return emitError(unknownLoc,
"OpPhi in loop merge block unimplemented");
3119 logger.startLine() <<
"[cf] merge block " << mergeBlock <<
":\n";
3120 mergeBlock->print(logger.getOStream());
3121 logger.startLine() <<
"\n";
3125 LLVM_DEBUG(
if (continueBlock) {
3126 logger.startLine() <<
"[cf] continue block " << continueBlock <<
":\n";
3127 continueBlock->print(logger.getOStream());
3128 logger.startLine() <<
"\n";
3132 blockMergeInfo.
erase(blockMergeInfo.begin());
3133 ControlFlowStructurizer structurizer(mergeInfo.
loc, mergeInfo.
control,
3134 blockMergeInfo, headerBlock,
3135 mergeBlock, continueBlock
3141 if (failed(structurizer.structurize()))
3148 <<
"//--- [cf] completed structurizing control flow ---//\n";
3161 auto fileName = debugInfoMap.lookup(debugLine->fileID).str();
3162 if (fileName.empty())
3163 fileName =
"<unknown>";
3175 if (operands.size() != 3)
3176 return emitError(unknownLoc,
"OpLine must have 3 operands");
3177 debugLine =
DebugLine{operands[0], operands[1], operands[2]};
3185 if (operands.size() < 2)
3186 return emitError(unknownLoc,
"OpString needs at least 2 operands");
3188 if (!debugInfoMap.lookup(operands[0]).empty())
3190 "duplicate debug string found for result <id> ")
3193 unsigned wordIndex = 1;
3195 if (wordIndex != operands.size())
3197 "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...
Type getType() const
Return the type of this 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.