MLIR 24.0.0git
DeserializeOps.cpp
Go to the documentation of this file.
1//===- DeserializeOps.cpp - MLIR SPIR-V Deserialization (Ops) -------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the Deserializer methods for SPIR-V binary instructions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "Deserializer.h"
14
17#include "mlir/IR/Builders.h"
18#include "mlir/IR/Location.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/Debug.h"
24#include <optional>
25
26using namespace mlir;
27
28#define DEBUG_TYPE "spirv-deserialization"
29
30//===----------------------------------------------------------------------===//
31// Utility Functions
32//===----------------------------------------------------------------------===//
33
34/// Extracts the opcode from the given first word of a SPIR-V instruction.
35static inline spirv::Opcode extractOpcode(uint32_t word) {
36 return static_cast<spirv::Opcode>(word & 0xffff);
37}
38
39/// Returns a NameLoc location from the given debug info string.
40static NameLoc getLocFromDebugInfoString(OpBuilder &builder, StringRef source) {
41 return NameLoc::get(builder.getStringAttr(source));
42}
43
44//===----------------------------------------------------------------------===//
45// Instruction
46//===----------------------------------------------------------------------===//
47
49 if (std::optional<std::pair<Attribute, Type>> constCompositeReplicateInfo =
51 Value &value = blockConstantMap[{opBuilder.getInsertionBlock(), id}];
52 if (!value)
53 value = spirv::EXTConstantCompositeReplicateOp::create(
54 opBuilder, unknownLoc, constCompositeReplicateInfo->second,
55 constCompositeReplicateInfo->first);
56 return value;
57 }
58 if (auto constInfo = getConstant(id)) {
59 // Materialize a `spirv.Constant` op once per block.
60 Value &value = blockConstantMap[{opBuilder.getInsertionBlock(), id}];
61 if (!value) {
62 Location loc = unknownLoc;
63 if (LocationAttr locAttr = constantLocMap.lookup(id))
64 loc = Location(locAttr);
65 value = spirv::ConstantOp::create(opBuilder, loc, constInfo->second,
66 constInfo->first);
67 }
68 return value;
69 }
70 if (auto varOp = getGlobalVariable(id)) {
71 auto addressOfOp =
72 spirv::AddressOfOp::create(opBuilder, unknownLoc, varOp.getType(),
73 SymbolRefAttr::get(varOp.getOperation()));
74 return addressOfOp.getPointer();
75 }
76 if (auto constOp = getSpecConstant(id)) {
77 auto referenceOfOp = spirv::ReferenceOfOp::create(
78 opBuilder, unknownLoc, constOp.getDefaultValue().getType(),
79 SymbolRefAttr::get(constOp.getOperation()));
80 return referenceOfOp.getReference();
81 }
82 if (SpecConstantCompositeOp specConstCompositeOp =
84 auto referenceOfOp = spirv::ReferenceOfOp::create(
85 opBuilder, unknownLoc, specConstCompositeOp.getType(),
86 SymbolRefAttr::get(specConstCompositeOp.getOperation()));
87 return referenceOfOp.getReference();
88 }
89 if (auto specConstCompositeReplicateOp =
91 auto referenceOfOp = spirv::ReferenceOfOp::create(
92 opBuilder, unknownLoc, specConstCompositeReplicateOp.getType(),
93 SymbolRefAttr::get(specConstCompositeReplicateOp.getOperation()));
94 return referenceOfOp.getReference();
95 }
96 if (auto specConstOperationInfo = getSpecConstantOperation(id)) {
98 id, specConstOperationInfo->enclodesOpcode,
99 specConstOperationInfo->resultTypeID,
100 specConstOperationInfo->enclosedOpOperands);
101 }
102 if (auto undef = getUndefType(id)) {
103 Value &value = blockConstantMap[{opBuilder.getInsertionBlock(), id}];
104 if (!value)
105 value = spirv::UndefOp::create(opBuilder, unknownLoc, undef);
106 return value;
107 }
108 if (std::optional<spirv::GraphConstantARMOpMaterializationInfo>
109 graphConstantARMInfo = getGraphConstantARM(id)) {
110 IntegerAttr graphConstantID = graphConstantARMInfo->graphConstantID;
111 Type resultType = graphConstantARMInfo->resultType;
112 return spirv::GraphConstantARMOp::create(opBuilder, unknownLoc, resultType,
113 graphConstantID);
114 }
115 return valueMap.lookup(id);
116}
117
119 spirv::Opcode &opcode, ArrayRef<uint32_t> &operands,
120 std::optional<spirv::Opcode> expectedOpcode) {
121 auto binarySize = binary.size();
122 if (curOffset >= binarySize) {
123 return emitError(unknownLoc, "expected ")
124 << (expectedOpcode ? spirv::stringifyOpcode(*expectedOpcode)
125 : "more")
126 << " instruction";
127 }
128
129 // For each instruction, get its word count from the first word to slice it
130 // from the stream properly, and then dispatch to the instruction handler.
131
132 uint32_t wordCount = binary[curOffset] >> 16;
133
134 if (wordCount == 0)
135 return emitError(unknownLoc, "word count cannot be zero");
136
137 uint32_t nextOffset = curOffset + wordCount;
138 if (nextOffset > binarySize)
139 return emitError(unknownLoc, "insufficient words for the last instruction");
140
141 opcode = extractOpcode(binary[curOffset]);
142 operands = binary.slice(curOffset + 1, wordCount - 1);
143 curOffset = nextOffset;
144 return success();
145}
146
148 spirv::Opcode opcode, ArrayRef<uint32_t> &operands,
149 SmallVectorImpl<uint32_t> &mergedStorage) {
150 std::optional<spirv::Opcode> continuationOp = getContinuationOpcode(opcode);
151 if (!continuationOp)
152 return;
153
154 size_t binarySize = binary.size();
155 auto isNextContinuation = [&]() {
156 if (curOffset >= binarySize)
157 return false;
158 uint32_t wordCount = binary[curOffset] >> 16;
159 if (wordCount == 0 || curOffset + wordCount > binarySize)
160 return false;
161 return extractOpcode(binary[curOffset]) == *continuationOp;
162 };
163
164 if (!isNextContinuation())
165 return;
166
167 mergedStorage.assign(operands);
168 do {
169 spirv::Opcode contOpcode;
170 ArrayRef<uint32_t> contOperands;
171 if (failed(sliceInstruction(contOpcode, contOperands, *continuationOp)))
172 return;
173 llvm::append_range(mergedStorage, contOperands);
174 } while (isNextContinuation());
175 operands = mergedStorage;
176}
177
179 spirv::Opcode opcode, ArrayRef<uint32_t> operands, bool deferInstructions) {
180 LLVM_DEBUG(logger.startLine() << "[inst] processing instruction "
181 << spirv::stringifyOpcode(opcode) << "\n");
182
183 SmallVector<uint32_t, 0> mergedStorage;
184 mergeLongCompositeContinuations(opcode, operands, mergedStorage);
185
186 // First dispatch all the instructions whose opcode does not correspond to
187 // those that have a direct mirror in the SPIR-V dialect
188 switch (opcode) {
189 case spirv::Opcode::OpCapability:
190 return processCapability(operands);
191 case spirv::Opcode::OpExtension:
192 return processExtension(operands);
193 case spirv::Opcode::OpExtInst: {
195 operands.size() >= 4 ? extendedInstSets.find(operands[2])
196 : extendedInstSets.end();
197 if (setIt != extendedInstSets.end() && setIt->second == extDebugInfo)
198 return processDebugInfoExtInst(operands, deferInstructions);
199 return processExtInst(operands);
200 }
201 case spirv::Opcode::OpExtInstImport:
202 return processExtInstImport(operands);
203 case spirv::Opcode::OpMemberName:
204 return processMemberName(operands);
205 case spirv::Opcode::OpMemoryModel:
206 return processMemoryModel(operands);
207 case spirv::Opcode::OpEntryPoint:
208 case spirv::Opcode::OpExecutionMode:
209 case spirv::Opcode::OpExecutionModeId:
210 if (deferInstructions) {
211 deferredInstructions.emplace_back(opcode, operands);
212 return success();
213 }
214 break;
215 case spirv::Opcode::OpVariable:
216 if (isa<spirv::ModuleOp>(opBuilder.getBlock()->getParentOp())) {
217 return processGlobalVariable(operands);
218 }
219 break;
220 case spirv::Opcode::OpLine:
221 return processDebugLine(operands);
222 case spirv::Opcode::OpNoLine:
224 return success();
225 case spirv::Opcode::OpName:
226 return processName(operands);
227 case spirv::Opcode::OpString:
228 return processDebugString(operands);
229 case spirv::Opcode::OpModuleProcessed:
230 case spirv::Opcode::OpSource:
231 case spirv::Opcode::OpSourceContinued:
232 case spirv::Opcode::OpSourceExtension:
233 // TODO: This is debug information embedded in the binary which should be
234 // translated into the spirv.module.
235 return success();
236 case spirv::Opcode::OpTypeVoid:
237 case spirv::Opcode::OpTypeBool:
238 case spirv::Opcode::OpTypeInt:
239 case spirv::Opcode::OpTypeFloat:
240 case spirv::Opcode::OpTypeVector:
241 case spirv::Opcode::OpTypeMatrix:
242 case spirv::Opcode::OpTypeArray:
243 case spirv::Opcode::OpTypeFunction:
244 case spirv::Opcode::OpTypeImage:
245 case spirv::Opcode::OpTypeSampler:
246 case spirv::Opcode::OpTypeNamedBarrier:
247 case spirv::Opcode::OpTypeSampledImage:
248 case spirv::Opcode::OpTypeRuntimeArray:
249 case spirv::Opcode::OpTypeStruct:
250 case spirv::Opcode::OpTypePointer:
251 case spirv::Opcode::OpTypeTensorARM:
252 case spirv::Opcode::OpTypeGraphARM:
253 case spirv::Opcode::OpTypeCooperativeMatrixKHR:
254 return processType(opcode, operands);
255 case spirv::Opcode::OpTypeForwardPointer:
256 return processTypeForwardPointer(operands);
257 case spirv::Opcode::OpConstant:
258 return processConstant(operands, /*isSpec=*/false);
259 case spirv::Opcode::OpSpecConstant:
260 return processConstant(operands, /*isSpec=*/true);
261 case spirv::Opcode::OpConstantComposite:
262 return processConstantComposite(operands);
263 case spirv::Opcode::OpConstantCompositeReplicateEXT:
265 case spirv::Opcode::OpSpecConstantComposite:
266 return processSpecConstantComposite(operands);
267 case spirv::Opcode::OpSpecConstantCompositeReplicateEXT:
269 case spirv::Opcode::OpSpecConstantOp:
270 return processSpecConstantOperation(operands);
271 case spirv::Opcode::OpConstantTrue:
272 return processConstantBool(/*isTrue=*/true, operands, /*isSpec=*/false);
273 case spirv::Opcode::OpSpecConstantTrue:
274 return processConstantBool(/*isTrue=*/true, operands, /*isSpec=*/true);
275 case spirv::Opcode::OpConstantFalse:
276 return processConstantBool(/*isTrue=*/false, operands, /*isSpec=*/false);
277 case spirv::Opcode::OpSpecConstantFalse:
278 return processConstantBool(/*isTrue=*/false, operands, /*isSpec=*/true);
279 case spirv::Opcode::OpConstantNull:
280 return processConstantNull(operands);
281 case spirv::Opcode::OpGraphConstantARM:
282 return processGraphConstantARM(operands);
283 case spirv::Opcode::OpDecorate:
284 case spirv::Opcode::OpDecorateId:
285 return processDecoration(operands);
286 case spirv::Opcode::OpMemberDecorate:
287 return processMemberDecoration(operands);
288 case spirv::Opcode::OpFunction:
289 return processFunction(operands);
290 case spirv::Opcode::OpGraphEntryPointARM:
291 if (deferInstructions) {
292 deferredInstructions.emplace_back(opcode, operands);
293 return success();
294 }
295 return processGraphEntryPointARM(operands);
296 case spirv::Opcode::OpGraphARM:
297 return processGraphARM(operands);
298 case spirv::Opcode::OpGraphSetOutputARM:
299 return processOpGraphSetOutputARM(operands);
300 case spirv::Opcode::OpGraphEndARM:
301 return processGraphEndARM(operands);
302 case spirv::Opcode::OpLabel:
303 return processLabel(operands);
304 case spirv::Opcode::OpBranch:
305 return processBranch(operands);
306 case spirv::Opcode::OpBranchConditional:
307 return processBranchConditional(operands);
308 case spirv::Opcode::OpSelectionMerge:
309 return processSelectionMerge(operands);
310 case spirv::Opcode::OpLoopMerge:
311 return processLoopMerge(operands);
312 case spirv::Opcode::OpPhi:
313 return processPhi(operands);
314 case spirv::Opcode::OpSwitch:
315 return processSwitch(operands);
316 case spirv::Opcode::OpUndef:
317 return processUndef(operands);
318 default:
319 break;
320 }
321 return dispatchToAutogenDeserialization(opcode, operands);
322}
323
325 ArrayRef<uint32_t> words, StringRef opName, bool hasResult,
326 unsigned numOperands) {
327 SmallVector<Type, 1> resultTypes;
328 uint32_t valueID = 0;
329
330 size_t wordIndex = 0;
331 if (hasResult) {
332 if (wordIndex >= words.size())
333 return emitError(unknownLoc,
334 "expected result type <id> while deserializing for ")
335 << opName;
336
337 // Decode the type <id>
338 auto type = getType(words[wordIndex]);
339 if (!type)
340 return emitError(unknownLoc, "unknown type result <id>: ")
341 << words[wordIndex];
342 resultTypes.push_back(type);
343 ++wordIndex;
344
345 // Decode the result <id>
346 if (wordIndex >= words.size())
347 return emitError(unknownLoc,
348 "expected result <id> while deserializing for ")
349 << opName;
350 valueID = words[wordIndex];
351 ++wordIndex;
352 }
353
354 SmallVector<Value, 4> operands;
356
357 // Decode operands
358 size_t operandIndex = 0;
359 for (; operandIndex < numOperands && wordIndex < words.size();
360 ++operandIndex, ++wordIndex) {
361 auto arg = getValue(words[wordIndex]);
362 if (!arg)
363 return emitError(unknownLoc, "unknown result <id>: ") << words[wordIndex];
364 operands.push_back(arg);
365 }
366 if (operandIndex != numOperands) {
367 return emitError(
368 unknownLoc,
369 "found less operands than expected when deserializing for ")
370 << opName << "; only " << operandIndex << " of " << numOperands
371 << " processed";
372 }
373 if (wordIndex != words.size()) {
374 return emitError(
375 unknownLoc,
376 "found more operands than expected when deserializing for ")
377 << opName << "; only " << wordIndex << " of " << words.size()
378 << " processed";
379 }
380
381 // Attach attributes from decorations
382 if (decorations.count(valueID)) {
383 auto attrs = decorations[valueID].getAttrs();
384 attributes.append(attrs.begin(), attrs.end());
385 }
386
387 // Create the op and update bookkeeping maps
388 Location loc = createFileLineColLoc(opBuilder);
389 OperationState opState(loc, opName);
390 opState.addOperands(operands);
391 if (hasResult)
392 opState.addTypes(resultTypes);
393 opState.addAttributes(attributes);
394 Operation *op = opBuilder.create(opState);
395 if (hasResult)
396 valueMap[valueID] = op->getResult(0);
397
400
401 return success();
402}
403
405 if (operands.size() != 2) {
406 return emitError(unknownLoc, "OpUndef instruction must have two operands");
407 }
408 auto type = getType(operands[0]);
409 if (!type) {
410 return emitError(unknownLoc, "unknown type <id> with OpUndef instruction");
411 }
412 undefMap[operands[1]] = type;
413 return success();
414}
415
416LogicalResult
418 bool deferInstructions) {
419 if (deferInstructions) {
420 deferredInstructions.emplace_back(spirv::Opcode::OpExtInst, operands);
421 return success();
422 }
423
424 if (operands.size() < 4) {
425 return emitError(unknownLoc,
426 "OpExtInst must have at least 4 operands, result type "
427 "<id>, result <id>, set <id> and instruction opcode");
428 }
429
430 Type resultType = getType(operands[0]);
431 if (!resultType || !isVoidType(resultType))
432 return emitError(unknownLoc,
433 "DebugInfo instructions must have OpTypeVoid result type");
434
435 auto getDebugLoc = [&](uint32_t stringID) -> FailureOr<Location> {
437 debugInfoMap.find(stringID);
438 if (stringIt == debugInfoMap.end()) {
439 return emitError(unknownLoc, "undefined string <id> ")
440 << stringID << " in DebugInfo";
441 }
442 return Location(getLocFromDebugInfoString(opBuilder, stringIt->second));
443 };
444
445 if (!spirv::isValidGraphDebugInfoExtInst(operands[3]))
446 return emitError(unknownLoc, "unknown DebugInfo instruction opcode: ")
447 << operands[3];
448
449 auto instructionID = static_cast<spirv::GraphDebugInfoExtInst>(operands[3]);
450 switch (instructionID) {
452 if (operands.size() < 6)
453 return emitError(unknownLoc, "DebugGraph must have graph and string IDs");
454 uint32_t graphID = operands[4];
455 uint32_t stringID = operands[5];
457 graphMap.find(graphID);
458 if (graphIt == graphMap.end())
459 return emitError(unknownLoc, "undefined graph <id> ")
460 << graphID << " in DebugGraph";
461 FailureOr<Location> loc = getDebugLoc(stringID);
462 if (failed(loc))
463 return failure();
464 graphIt->second->setLoc(*loc);
465 break;
466 }
468 if (operands.size() < 7)
469 return emitError(unknownLoc, "DebugOperation must have graph, string and "
470 "instruction IDs");
471 uint32_t stringID = operands[5];
472 FailureOr<Location> loc = getDebugLoc(stringID);
473 if (failed(loc))
474 return failure();
475 SmallVector<uint32_t> operationIDs;
476 operationIDs.append(std::next(operands.begin(), 6), operands.end());
477 for (uint32_t operationID : operationIDs) {
478 DenseMap<uint32_t, Value>::iterator valueIt = valueMap.find(operationID);
479 if (valueIt == valueMap.end())
480 return emitError(unknownLoc, "undefined operation <id> ")
481 << operationID << " in DebugOperation";
482 valueIt->second.setLoc(*loc);
483 }
484 break;
485 }
487 if (operands.size() < 6)
488 return emitError(unknownLoc,
489 "DebugTensor must have tensor and string IDs");
490 uint32_t stringID = operands[5];
491 uint32_t tensorID = operands[4];
492 FailureOr<Location> loc = getDebugLoc(stringID);
493 if (failed(loc))
494 return failure();
495 if (constantMap.contains(tensorID)) {
496 constantLocMap[tensorID] = *loc;
497 break;
498 }
499 DenseMap<uint32_t, Value>::iterator valueIt = valueMap.find(tensorID);
500 if (valueIt == valueMap.end())
501 return emitError(unknownLoc, "undefined tensor <id> ")
502 << tensorID << " in DebugTensor";
503 valueIt->second.setLoc(*loc);
504 break;
505 }
506 }
507
508 return success();
509}
510
512 if (operands.size() < 4) {
513 return emitError(unknownLoc,
514 "OpExtInst must have at least 4 operands, result type "
515 "<id>, result <id>, set <id> and instruction opcode");
516 }
517 if (!extendedInstSets.count(operands[2])) {
518 return emitError(unknownLoc, "undefined set <id> in OpExtInst");
519 }
520 SmallVector<uint32_t, 4> slicedOperands;
521 slicedOperands.append(operands.begin(), std::next(operands.begin(), 2));
522 slicedOperands.append(std::next(operands.begin(), 4), operands.end());
524 extendedInstSets[operands[2]], operands[3], slicedOperands);
525}
526
527namespace mlir {
528namespace spirv {
529
530template <>
531LogicalResult
533 unsigned wordIndex = 0;
534 if (wordIndex >= words.size()) {
535 return emitError(unknownLoc,
536 "missing Execution Model specification in OpEntryPoint");
537 }
538 auto execModel = spirv::ExecutionModelAttr::get(
539 context, static_cast<spirv::ExecutionModel>(words[wordIndex++]));
540 if (wordIndex >= words.size()) {
541 return emitError(unknownLoc, "missing <id> in OpEntryPoint");
542 }
543 // Get the function <id>
544 auto fnID = words[wordIndex++];
545 // Get the function name
546 auto fnName = decodeStringLiteral(words, wordIndex);
547 // Verify that the function <id> matches the fnName
548 auto parsedFunc = getFunction(fnID);
549 if (!parsedFunc) {
550 return emitError(unknownLoc, "no function matching <id> ") << fnID;
551 }
552 if (parsedFunc.getName() != fnName) {
553 // The deserializer uses "spirv_fn_<id>" as the function name if the input
554 // SPIR-V blob does not contain a name for it. We should use a more clear
555 // indication for such case rather than relying on naming details.
556 if (!parsedFunc.getName().starts_with("spirv_fn_"))
557 return emitError(unknownLoc,
558 "function name mismatch between OpEntryPoint "
559 "and OpFunction with <id> ")
560 << fnID << ": " << fnName << " vs. " << parsedFunc.getName();
561 parsedFunc.setName(fnName);
562 }
564 while (wordIndex < words.size()) {
565 auto arg = getGlobalVariable(words[wordIndex]);
566 if (!arg) {
567 return emitError(unknownLoc, "undefined result <id> ")
568 << words[wordIndex] << " while decoding OpEntryPoint";
569 }
570 interface.push_back(SymbolRefAttr::get(arg.getOperation()));
571 wordIndex++;
572 }
573 spirv::EntryPointOp::create(
574 opBuilder, unknownLoc, execModel,
575 SymbolRefAttr::get(opBuilder.getContext(), fnName),
576 opBuilder.getArrayAttr(interface));
577 return success();
578}
579
580template <>
581LogicalResult
583 unsigned wordIndex = 0;
584 if (wordIndex >= words.size()) {
585 return emitError(unknownLoc,
586 "missing function result <id> in OpExecutionMode");
587 }
588 // Get the function <id> to get the name of the function
589 auto fnID = words[wordIndex++];
590 auto fn = getFunction(fnID);
591 if (!fn) {
592 return emitError(unknownLoc, "no function matching <id> ") << fnID;
593 }
594 // Get the Execution mode
595 if (wordIndex >= words.size()) {
596 return emitError(unknownLoc, "missing Execution Mode in OpExecutionMode");
597 }
598 auto execMode = spirv::ExecutionModeAttr::get(
599 context, static_cast<spirv::ExecutionMode>(words[wordIndex++]));
600
601 // Get the values
602 SmallVector<Attribute, 4> attrListElems;
603 while (wordIndex < words.size()) {
604 attrListElems.push_back(opBuilder.getI32IntegerAttr(words[wordIndex++]));
605 }
606 auto values = opBuilder.getArrayAttr(attrListElems);
607 spirv::ExecutionModeOp::create(
608 opBuilder, unknownLoc,
609 SymbolRefAttr::get(opBuilder.getContext(), fn.getName()), execMode,
610 values);
611 return success();
612}
613
614template <>
615LogicalResult
617 unsigned wordIndex = 0;
618 unsigned const wordsSize = words.size();
619 if (wordIndex >= wordsSize)
620 return emitError(unknownLoc,
621 "missing function result <id> in OpExecutionModeId");
622
623 // Get the function <id> to get the name of the function.
624 uint32_t fnID = words[wordIndex++];
625 FuncOp fn = getFunction(fnID);
626 if (!fn)
627 return emitError(unknownLoc, "no function matching <id> ") << fnID;
628
629 // Get the Execution mode.
630 if (wordIndex >= wordsSize)
631 return emitError(unknownLoc, "missing Execution Mode in OpExecutionModeId");
632
633 ExecutionModeAttr execMode = spirv::ExecutionModeAttr::get(
634 context, static_cast<spirv::ExecutionMode>(words[wordIndex++]));
635
636 // Get the values.
637 SmallVector<Attribute, 4> attrListElems;
638 while (wordIndex < words.size()) {
639 std::string id = getSpecConstantSymbol(words[wordIndex++]);
640 attrListElems.push_back(FlatSymbolRefAttr::get(context, id));
641 }
642 ArrayAttr values = opBuilder.getArrayAttr(attrListElems);
643 spirv::ExecutionModeIdOp::create(
644 opBuilder, unknownLoc,
645 SymbolRefAttr::get(opBuilder.getContext(), fn.getName()), execMode,
646 values);
647 return success();
648}
649
650template <>
651LogicalResult
653 if (operands.size() < 3) {
654 return emitError(unknownLoc,
655 "OpFunctionCall must have at least 3 operands");
656 }
657
658 Type resultType = getType(operands[0]);
659 if (!resultType) {
660 return emitError(unknownLoc, "undefined result type from <id> ")
661 << operands[0];
662 }
663
664 // Use null type to mean no result type.
665 if (isVoidType(resultType))
666 resultType = nullptr;
667
668 auto resultID = operands[1];
669 auto functionID = operands[2];
670
671 auto functionName = getFunctionSymbol(functionID);
672
673 SmallVector<Value, 4> arguments;
674 for (auto operand : llvm::drop_begin(operands, 3)) {
675 auto value = getValue(operand);
676 if (!value) {
677 return emitError(unknownLoc, "unknown <id> ")
678 << operand << " used by OpFunctionCall";
679 }
680 arguments.push_back(value);
681 }
682
683 auto opFunctionCall = spirv::FunctionCallOp::create(
684 opBuilder, unknownLoc, resultType,
685 SymbolRefAttr::get(opBuilder.getContext(), functionName), arguments);
686
687 if (auto it = decorations.find(resultID); it != decorations.end())
688 for (NamedAttribute attr : it->second)
689 opFunctionCall->setAttr(attr.getName(), attr.getValue());
690
691 if (resultType)
692 valueMap[resultID] = opFunctionCall.getResult(0);
693 return success();
694}
695
696template <>
697LogicalResult
699 SmallVector<Type, 1> resultTypes;
700 size_t wordIndex = 0;
701 SmallVector<Value, 4> operands;
703
704 if (wordIndex < words.size()) {
705 auto arg = getValue(words[wordIndex]);
706
707 if (!arg) {
708 return emitError(unknownLoc, "unknown result <id> : ")
709 << words[wordIndex];
710 }
711
712 operands.push_back(arg);
713 wordIndex++;
714 }
715
716 if (wordIndex < words.size()) {
717 auto arg = getValue(words[wordIndex]);
718
719 if (!arg) {
720 return emitError(unknownLoc, "unknown result <id> : ")
721 << words[wordIndex];
722 }
723
724 operands.push_back(arg);
725 wordIndex++;
726 }
727
728 bool isAlignedAttr = false;
729
730 if (wordIndex < words.size()) {
731 auto attrValue = words[wordIndex++];
732 auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(
733 static_cast<spirv::MemoryAccess>(attrValue));
734 attributes.push_back(
735 opBuilder.getNamedAttr(attributeName<MemoryAccess>(), attr));
736 isAlignedAttr = (attrValue == 2);
737 }
738
739 if (isAlignedAttr && wordIndex < words.size()) {
740 attributes.push_back(opBuilder.getNamedAttr(
741 "alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));
742 }
743
744 if (wordIndex < words.size()) {
745 auto attrValue = words[wordIndex++];
746 auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(
747 static_cast<spirv::MemoryAccess>(attrValue));
748 attributes.push_back(opBuilder.getNamedAttr("source_memory_access", attr));
749 }
750
751 if (wordIndex < words.size()) {
752 attributes.push_back(opBuilder.getNamedAttr(
753 "source_alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));
754 }
755
756 if (wordIndex != words.size()) {
757 return emitError(unknownLoc,
758 "found more operands than expected when deserializing "
759 "spirv::CopyMemoryOp, only ")
760 << wordIndex << " of " << words.size() << " processed";
761 }
762
763 Location loc = createFileLineColLoc(opBuilder);
764 spirv::CopyMemoryOp::Properties properties{};
765 spirv::CopyMemoryOp::populateDefaultProperties(
766 OperationName(spirv::CopyMemoryOp::getOperationName(),
767 opBuilder.getContext()),
768 properties);
769 if (failed(spirv::CopyMemoryOp::setPropertiesFromAttr(
770 properties, opBuilder.getDictionaryAttr(attributes),
771 [&]() { return emitError(loc); })))
772 return failure();
773 spirv::CopyMemoryOp::create(opBuilder, loc, resultTypes, operands, properties,
774 /*discardableAttributes=*/{});
775
776 return success();
777}
778
779template <>
781 ArrayRef<uint32_t> words) {
782 if (words.size() != 4) {
783 return emitError(unknownLoc,
784 "expected 4 words in GenericCastToPtrExplicitOp"
785 " but got : ")
786 << words.size();
787 }
788 SmallVector<Type, 1> resultTypes;
789 SmallVector<Value, 4> operands;
790 uint32_t valueID = 0;
791 auto type = getType(words[0]);
792
793 if (!type)
794 return emitError(unknownLoc, "unknown type result <id> : ") << words[0];
795 resultTypes.push_back(type);
796
797 valueID = words[1];
798
799 auto arg = getValue(words[2]);
800 if (!arg)
801 return emitError(unknownLoc, "unknown result <id> : ") << words[2];
802 operands.push_back(arg);
803
804 Location loc = createFileLineColLoc(opBuilder);
805 Operation *op = spirv::GenericCastToPtrExplicitOp::create(
806 opBuilder, loc, resultTypes, operands);
807 valueMap[valueID] = op->getResult(0);
808 return success();
809}
810
811// Pull in auto-generated Deserializer::dispatchToAutogenDeserialization() and
812// various Deserializer::processOp<...>() specializations.
813#define GET_DESERIALIZATION_FNS
814#include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"
815
816} // namespace spirv
817} // namespace mlir
return success()
static spirv::Opcode extractOpcode(uint32_t word)
Extracts the opcode from the given first word of a SPIR-V instruction.
static NameLoc getLocFromDebugInfoString(OpBuilder &builder, StringRef source)
Returns a NameLoc location from the given debug info string.
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
Location objects represent source locations information in MLIR.
Definition Location.h:32
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:166
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Definition Attributes.h:181
This class helps build Operations.
Definition Builders.h:210
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:434
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.
Definition Operation.cpp:65
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Value materializeSpecConstantOperation(uint32_t resultID, spirv::Opcode enclosedOpcode, uint32_t resultTypeID, ArrayRef< uint32_t > enclosedOpOperands)
Materializes/emits an OpSpecConstantOp instruction.
Value getValue(uint32_t id)
Get the Value associated with a result <id>.
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 processDebugInfoExtInst(ArrayRef< uint32_t > operands, bool deferInstructions)
Processes a SPIR-V OpExtInst with given operands for a DebugInfo extension instruction.
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 processGraphARM(ArrayRef< uint32_t > operands)
LogicalResult processLabel(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpLabel instruction with the given operands.
LogicalResult processExtInst(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpExtInst with given 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 dispatchToExtensionSetAutogenDeserialization(StringRef extensionSetName, uint32_t instructionID, ArrayRef< uint32_t > words)
Dispatches the deserialization of extended instruction set operation based on the extended instructio...
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>.
spirv::EXTSpecConstantCompositeReplicateOp getSpecConstantCompositeReplicate(uint32_t id)
Gets the replicated composite specialization constant with the given result <id>.
LogicalResult processOp(ArrayRef< uint32_t > words)
Method to deserialize an operation in the SPIR-V dialect that is a mirror of an instruction in the SP...
Type getUndefType(uint32_t id)
Get the type associated with the result <id> of an OpUndef.
LogicalResult processSpecConstantCompositeReplicateEXT(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSpecConstantCompositeReplicateEXT instruction with the given operands.
LogicalResult processGraphEntryPointARM(ArrayRef< uint32_t > operands)
LogicalResult processFunction(ArrayRef< uint32_t > operands)
Creates a deserializer for the given SPIR-V binary module.
bool isVoidType(Type type) const
Returns true if the given type is for SPIR-V void type.
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.
void clearDebugLine()
Discontinues any source-level location information that might be active from a previous OpLine instru...
void mergeLongCompositeContinuations(spirv::Opcode opcode, ArrayRef< uint32_t > &operands, SmallVectorImpl< uint32_t > &mergedStorage)
If opcode is a SPV_INTEL_long_composites splittable opcode and the next binary instruction(s) are mat...
LogicalResult processTypeForwardPointer(ArrayRef< uint32_t > operands)
LogicalResult processSwitch(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpSwitch instruction with the given operands.
LogicalResult dispatchToAutogenDeserialization(spirv::Opcode opcode, ArrayRef< uint32_t > words)
Method to dispatch to the specialized deserialization function for an operation in SPIR-V dialect tha...
LogicalResult processOpWithoutGrammarAttr(ArrayRef< uint32_t > words, StringRef opName, bool hasResult, unsigned numOperands)
Processes a SPIR-V instruction from the given operands.
LogicalResult processGraphEndARM(ArrayRef< uint32_t > operands)
LogicalResult processConstantComposite(ArrayRef< uint32_t > operands)
Processes a SPIR-V OpConstantComposite instruction with the given 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 processUndef(ArrayRef< uint32_t > operands)
Processes a OpUndef instruction.
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.
constexpr llvm::StringLiteral extDebugInfo
Extension set name for non-semantic graph debug info.
std::optional< spirv::Opcode > getContinuationOpcode(spirv::Opcode parent)
Returns the SPV_INTEL_long_composites continuation opcode that may follow parent, or std::nullopt if ...
constexpr bool isValidGraphDebugInfoExtInst(uint32_t opcode)
constexpr StringRef attributeName()
StringRef decodeStringLiteral(ArrayRef< uint32_t > words, unsigned &wordIndex)
Decodes a string literal in words starting at wordIndex.
GraphDebugInfoExtInst
Instruction opcodes in the NonSemantic.Graph.DebugInfo.1 extended instruction set.
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addTypes(ArrayRef< Type > newTypes)