MLIR 24.0.0git
SerializeOps.cpp
Go to the documentation of this file.
1//===- SerializeOps.cpp - MLIR SPIR-V Serialization (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 serialization methods for MLIR SPIR-V module ops.
10//
11//===----------------------------------------------------------------------===//
12
13#include "Serializer.h"
14
21#include "llvm/ADT/DepthFirstIterator.h"
22#include "llvm/ADT/StringExtras.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/FormatVariadic.h"
25
26#define DEBUG_TYPE "spirv-serialization"
27
28using namespace mlir;
29
30namespace {
31// Location::print() emits MLIR syntax such as `loc("name")` or
32// `loc(fused["op", "file":1:2])`. NonSemantic.Graph.DebugInfo stores the
33// source/debug name itself in an OpString, so keep this conversion to the
34// payload string explicit.
35std::string getDebugInfoStringFromLoc(Location loc) {
36 if (auto fileLineCol = dyn_cast<FileLineColLoc>(loc)) {
37 return llvm::formatv("{0}:{1}:{2}", fileLineCol.getFilename(),
38 fileLineCol.getLine(), fileLineCol.getColumn());
39 }
40 if (auto nameLoc = dyn_cast<NameLoc>(loc)) {
41 return nameLoc.getName().str();
42 }
43 if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
44 std::string result;
45 llvm::raw_string_ostream os(result);
46 llvm::interleave(
47 map_range(fusedLoc.getLocations(), getDebugInfoStringFromLoc), os, ";");
48 return result;
49 }
50 return "";
51}
52} // namespace
54/// A pre-order depth-first visitor function for processing basic blocks.
55///
56/// Visits the basic blocks starting from the given `headerBlock` in pre-order
57/// depth-first manner and calls `blockHandler` on each block. Skips handling
58/// blocks in the `skipBlocks` list. If `skipHeader` is true, `blockHandler`
59/// will not be invoked in `headerBlock` but still handles all `headerBlock`'s
60/// successors.
61///
62/// SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order
63/// of blocks in a function must satisfy the rule that blocks appear before
64/// all blocks they dominate." This can be achieved by a pre-order CFG
65/// traversal algorithm. To make the serialization output more logical and
66/// readable to human, we perform depth-first CFG traversal and delay the
67/// serialization of the merge block and the continue block, if exists, until
68/// after all other blocks have been processed.
69static LogicalResult
71 function_ref<LogicalResult(Block *)> blockHandler,
72 bool skipHeader = false, BlockRange skipBlocks = {}) {
73 llvm::df_iterator_default_set<Block *, 4> doneBlocks;
74 doneBlocks.insert(skipBlocks.begin(), skipBlocks.end());
75
76 for (Block *block : llvm::depth_first_ext(headerBlock, doneBlocks)) {
77 if (skipHeader && block == headerBlock)
78 continue;
79 if (failed(blockHandler(block)))
80 return failure();
81 }
82 return success();
83}
84
85namespace mlir {
86namespace spirv {
87LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) {
88 if (auto resultID =
89 prepareConstant(op.getLoc(), op.getType(), op.getValue())) {
90 valueIDMap[op.getResult()] = resultID;
91 if (isa<spirv::TensorArmType>(op.getType()) &&
92 failed(encodeDebugInfoTensorInst(op.getResult())))
93 return failure();
94 return success();
95 }
96 return failure();
97}
98
99LogicalResult Serializer::processConstantCompositeReplicateOp(
100 spirv::EXTConstantCompositeReplicateOp op) {
101 if (uint32_t resultID = prepareConstantCompositeReplicate(
102 op.getLoc(), op.getType(), op.getValue())) {
103 valueIDMap[op.getResult()] = resultID;
104 return success();
105 }
106 return failure();
107}
109LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) {
110 if (auto resultID = prepareConstantScalar(op.getLoc(), op.getDefaultValue(),
111 /*isSpec=*/true)) {
112 // Emit the OpDecorate instruction for SpecId.
113 if (auto specID = op->getDiscardableAttrOfType<IntegerAttr>("spec_id")) {
114 auto val = static_cast<uint32_t>(specID.getInt());
115 if (failed(emitDecoration(resultID, spirv::Decoration::SpecId, {val})))
116 return failure();
117 }
118
119 specConstIDMap[op.getSymName()] = resultID;
120 return processName(resultID, op.getSymName());
121 }
122 return failure();
123}
124
125LogicalResult
126Serializer::processSpecConstantCompositeOp(spirv::SpecConstantCompositeOp op) {
127 uint32_t typeID = 0;
128 if (failed(processType(op.getLoc(), op.getType(), typeID))) {
129 return failure();
130 }
131
132 auto resultID = getNextID();
133
135 operands.push_back(typeID);
136 operands.push_back(resultID);
137
138 auto constituents = op.getConstituents();
139
140 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
141 auto constituent = dyn_cast<FlatSymbolRefAttr>(constituents[index]);
142
143 auto constituentName = constituent.getValue();
144 auto constituentID = getSpecConstID(constituentName);
145
146 if (!constituentID) {
147 return op.emitError("unknown result <id> for specialization constant ")
148 << constituentName;
149 }
150
151 operands.push_back(constituentID);
152 }
153
154 encodeInstructionWithContinuationInto(
155 typesGlobalValues, spirv::Opcode::OpSpecConstantComposite, operands);
156 specConstIDMap[op.getSymName()] = resultID;
157
158 return processName(resultID, op.getSymName());
159}
160
161LogicalResult Serializer::processSpecConstantCompositeReplicateOp(
162 spirv::EXTSpecConstantCompositeReplicateOp op) {
163 uint32_t typeID = 0;
164 if (failed(processType(op.getLoc(), op.getType(), typeID))) {
165 return failure();
166 }
167
168 auto constituent = dyn_cast<FlatSymbolRefAttr>(op.getConstituent());
169 if (!constituent)
170 return op.emitError(
171 "expected flat symbol reference for constituent instead of ")
172 << op.getConstituent();
173
174 StringRef constituentName = constituent.getValue();
175 uint32_t constituentID = getSpecConstID(constituentName);
176 if (!constituentID) {
177 return op.emitError("unknown result <id> for replicated spec constant ")
178 << constituentName;
179 }
180
181 uint32_t resultID = getNextID();
182 uint32_t operands[] = {typeID, resultID, constituentID};
183
184 encodeInstructionInto(typesGlobalValues,
185 spirv::Opcode::OpSpecConstantCompositeReplicateEXT,
186 operands);
187
188 specConstIDMap[op.getSymName()] = resultID;
189
190 return processName(resultID, op.getSymName());
191}
192
193LogicalResult
194Serializer::processSpecConstantOperationOp(spirv::SpecConstantOperationOp op) {
195 uint32_t typeID = 0;
196 if (failed(processType(op.getLoc(), op.getType(), typeID))) {
197 return failure();
198 }
199
200 auto resultID = getNextID();
201
202 SmallVector<uint32_t, 8> operands;
203 operands.push_back(typeID);
204 operands.push_back(resultID);
205
206 Block &block = op.getRegion().getBlocks().front();
207 Operation &enclosedOp = block.getOperations().front();
208
209 std::string enclosedOpName;
210 llvm::raw_string_ostream rss(enclosedOpName);
211 rss << "Op" << enclosedOp.getName().stripDialect();
212 auto enclosedOpcode = spirv::symbolizeOpcode(enclosedOpName);
213
214 if (!enclosedOpcode) {
215 op.emitError("Couldn't find op code for op ")
216 << enclosedOp.getName().getStringRef();
217 return failure();
218 }
219
220 operands.push_back(static_cast<uint32_t>(*enclosedOpcode));
221
222 // Append operands to the enclosed op to the list of operands.
223 for (Value operand : enclosedOp.getOperands()) {
224 uint32_t id = getValueID(operand);
225 assert(id && "use before def!");
226 operands.push_back(id);
227 }
228
229 encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpSpecConstantOp,
230 operands);
231 valueIDMap[op.getResult()] = resultID;
232
233 return success();
234}
235
236LogicalResult
237Serializer::processGraphConstantARMOp(spirv::GraphConstantARMOp op) {
238 if (uint32_t resultID = prepareGraphConstantId(op.getLoc(), op.getType(),
239 op.getGraphConstantIdAttr())) {
240 valueIDMap[op.getResult()] = resultID;
241 return success();
242 }
243 return failure();
244}
245
246LogicalResult Serializer::processUndefOp(spirv::UndefOp op) {
247 auto undefType = op.getType();
248 auto &id = undefValIDMap[undefType];
249 if (!id) {
250 id = getNextID();
251 uint32_t typeID = 0;
252 if (failed(processType(op.getLoc(), undefType, typeID)))
253 return failure();
254 encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpUndef,
255 {typeID, id});
256 }
257 valueIDMap[op.getResult()] = id;
258 return success();
259}
260
261LogicalResult Serializer::processFuncParameter(spirv::FuncOp op) {
262 for (auto [idx, arg] : llvm::enumerate(op.getArguments())) {
263 uint32_t argTypeID = 0;
264 if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
265 return failure();
266 }
267 auto argValueID = getNextID();
268
269 // Process decoration attributes of arguments.
270 auto funcOp = cast<FunctionOpInterface>(*op);
271 for (auto argAttr : funcOp.getArgAttrs(idx)) {
272 if (argAttr.getName() == spirv::getFuncParamAlignmentAttrName() ||
273 argAttr.getName() == spirv::getFuncParamAttrName()) {
274 auto decoration =
275 argAttr.getName() == spirv::getFuncParamAlignmentAttrName()
276 ? Decoration::Alignment
277 : Decoration::FuncParamAttr;
278 if (failed(processDecorationAttr(op.getLoc(), argValueID, decoration,
279 argAttr.getValue())))
280 return failure();
281 continue;
282 }
283 if (argAttr.getName() != DecorationAttr::name)
284 continue;
285
286 if (auto decAttr = dyn_cast<DecorationAttr>(argAttr.getValue())) {
287 if (failed(processDecorationAttr(op->getLoc(), argValueID,
288 decAttr.getValue(), decAttr)))
289 return failure();
290 }
291 }
292
293 valueIDMap[arg] = argValueID;
294 encodeInstructionInto(functionHeader, spirv::Opcode::OpFunctionParameter,
295 {argTypeID, argValueID});
296 }
297 return success();
298}
299
300LogicalResult Serializer::processFuncOp(spirv::FuncOp op) {
301 LLVM_DEBUG(llvm::dbgs() << "-- start function '" << op.getName() << "' --\n");
302 assert(functionHeader.empty() && functionBody.empty());
304 uint32_t fnTypeID = 0;
305 // Generate type of the function.
306 if (failed(processType(op.getLoc(), op.getFunctionType(), fnTypeID)))
307 return failure();
308
309 // Add the function definition.
311 uint32_t resTypeID = 0;
312 auto resultTypes = op.getFunctionType().getResults();
313 if (resultTypes.size() > 1) {
314 return op.emitError("cannot serialize function with multiple return types");
315 }
316 if (failed(processType(op.getLoc(),
317 (resultTypes.empty() ? getVoidType() : resultTypes[0]),
318 resTypeID))) {
319 return failure();
320 }
321 operands.push_back(resTypeID);
322 auto funcID = getOrCreateFunctionID(op.getName());
323 operands.push_back(funcID);
324 operands.push_back(static_cast<uint32_t>(op.getFunctionControl()));
325 operands.push_back(fnTypeID);
326 encodeInstructionInto(functionHeader, spirv::Opcode::OpFunction, operands);
327
328 if (!resultTypes.empty()) {
329 if (Attribute attr = op.getResultAttr(0, spirv::getFuncParamAttrName()))
330 if (failed(processDecorationAttr(op.getLoc(), funcID,
331 Decoration::FuncParamAttr, attr)))
332 return failure();
333 }
334
335 // Add function name.
336 if (failed(processName(funcID, op.getName()))) {
337 return failure();
338 }
339 // Handle external functions with linkage_attributes(LinkageAttributes)
340 // differently.
341 auto linkageAttr = op.getLinkageAttributes();
342 auto hasImportLinkage =
343 linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
344 spirv::LinkageType::Import);
345 if (op.isExternal() && !hasImportLinkage) {
346 return op.emitError(
347 "'spirv.module' cannot contain external functions "
348 "without 'Import' linkage_attributes (LinkageAttributes)");
349 }
350 if (op.isExternal() && hasImportLinkage) {
351 // Add an entry block to set up the block arguments
352 // to match the signature of the function.
353 // This is to generate OpFunctionParameter for functions with
354 // LinkageAttributes.
355 // WARNING: This operation has side-effect, it essentially adds a body
356 // to the func. Hence, making it not external anymore (isExternal()
357 // is going to return false for this function from now on)
358 // Hence, we'll remove the body once we are done with the serialization.
359 op.addEntryBlock();
360 if (failed(processFuncParameter(op)))
361 return failure();
362
363 // Erasing the body of the function destroys arguments, so we need to remove
364 // them from the map to avoid problems when processing invalid values used
365 // as keys. We have already serialized function arguments so we probably can
366 // remove them from the map as external function will not have any uses.
367 for (Value arg : op.getArguments())
368 valueIDMap.erase(arg);
369
370 // Don't need to process the added block, there is nothing to process,
371 // the fake body was added just to get the arguments, remove the body,
372 // since it's use is done.
373 op.eraseBody();
374 } else {
375 if (failed(processFuncParameter(op)))
376 return failure();
377
378 // Some instructions (e.g., OpVariable) in a function must be in the first
379 // block in the function. These instructions will be put in
380 // functionHeader. Thus, we put the label in functionHeader first, and
381 // omit it from the first block. OpLabel only needs to be added for
382 // functions with body (including empty body). Since, we added a fake body
383 // for functions with 'Import' Linkage attributes, these functions are
384 // essentially function delcaration, so they should not have OpLabel and a
385 // terminating instruction. That's why we skipped it for those functions.
386 encodeInstructionInto(functionHeader, spirv::Opcode::OpLabel,
387 {getOrCreateBlockID(&op.front())});
388 if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
389 return failure();
390 if (failed(visitInPrettyBlockOrder(
391 &op.front(), [&](Block *block) { return processBlock(block); },
392 /*skipHeader=*/true))) {
393 return failure();
394 }
395
396 // There might be OpPhi instructions who have value references needing to
397 // fix.
398 for (const auto &deferredValue : deferredPhiValues) {
399 Value value = deferredValue.first;
400 uint32_t id = getValueID(value);
401 LLVM_DEBUG(llvm::dbgs() << "[phi] fix reference of value " << value
402 << " to id = " << id << '\n');
403 assert(id && "OpPhi references undefined value!");
404 for (size_t offset : deferredValue.second)
405 functionBody[offset] = id;
406 }
407 deferredPhiValues.clear();
408 }
409 LLVM_DEBUG(llvm::dbgs() << "-- completed function '" << op.getName()
410 << "' --\n");
411 // Insert Decorations based on Function Attributes.
412 // Only attributes we should be considering for decoration are the
413 // ::mlir::spirv::Decoration attributes.
414
415 NamedAttrList attrs(op->getDiscardableAttrDictionary().getValue());
416 op->getName().populateInherentAttrs(op, attrs);
417 for (auto attr : attrs) {
418 // Only generate OpDecorate op for spirv::Decoration attributes.
419 auto isValidDecoration = mlir::spirv::symbolizeEnum<spirv::Decoration>(
420 llvm::convertToCamelFromSnakeCase(attr.getName().strref(),
421 /*capitalizeFirst=*/true));
422 if (isValidDecoration != std::nullopt) {
423 if (failed(processDecoration(op.getLoc(), funcID, attr))) {
424 return failure();
425 }
426 }
427 }
428 // Insert OpFunctionEnd.
429 encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionEnd, {});
430
431 functions.append(functionHeader.begin(), functionHeader.end());
432 functions.append(functionBody.begin(), functionBody.end());
433 functionHeader.clear();
434 functionBody.clear();
435 valueIDMap.clear();
436 blockIDMap.clear();
437
438 return success();
439}
440
441uint32_t Serializer::encodeDebugStringInst(StringRef str) {
442 uint32_t stringID = debugStringIDMap.lookup(str);
443 if (stringID > 0) {
444 return stringID;
445 }
446
448 stringID = getNextID();
449 debugStringIDMap[str] = stringID;
450 operands.push_back(stringID);
451 spirv::encodeStringLiteralInto(operands, str);
452 encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
453
454 return stringID;
455}
456
457LogicalResult Serializer::encodeDebugInfoGraphInst(spirv::GraphARMOp op,
458 uint32_t &debugGraphID) {
459 if (!options.emitDebugInfo)
460 return success();
461
462 uint32_t voidTypeID = 0;
463 if (failed(processType(op.getLoc(), getVoidType(), voidTypeID)))
464 return failure();
465
466 uint32_t stringID =
467 encodeDebugStringInst(getDebugInfoStringFromLoc(op.getLoc()));
468
469 SmallVector<uint32_t, 6> operands;
470 operands.push_back(voidTypeID);
471 debugGraphID = getNextID();
472 operands.push_back(debugGraphID);
473 uint32_t graphID = getOrCreateFunctionID(op.getName());
474 operands.push_back(graphID);
475 operands.push_back(stringID);
476
477 if (failed(encodeExtensionInstruction(
478 nullptr, extDebugInfo,
479 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugGraph), operands,
480 graphsDebugInfo)))
481 return failure();
482
483 return success();
484}
485
486LogicalResult
487Serializer::encodeDebugInfoOperationInst(uint32_t debugGraphID,
488 const SetVector<Operation *> &ops) {
489 if (!options.emitDebugInfo)
490 return success();
491
492 if (ops.empty())
493 return success();
494
495 SmallVector<uint32_t, 4> instructionIDs;
496 for (Operation *op : ops)
497 for (OpResult result : op->getOpResults())
498 instructionIDs.push_back(getValueID(result));
499
500 if (instructionIDs.empty())
501 return success();
502
503 uint32_t voidTypeID = 0;
504 if (failed(processType(ops[0]->getLoc(), getVoidType(), voidTypeID)))
505 return failure();
506
507 uint32_t stringID =
508 encodeDebugStringInst(getDebugInfoStringFromLoc(ops[0]->getLoc()));
509
511 operands.push_back(voidTypeID);
512 operands.push_back(getNextID());
513 operands.push_back(debugGraphID);
514 operands.push_back(stringID);
515 operands.append(instructionIDs);
516
517 if (failed(encodeExtensionInstruction(
518 nullptr, extDebugInfo,
519 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugOperation),
520 operands, graphsDebugInfo)))
521 return failure();
522
523 return success();
524}
525
526LogicalResult Serializer::encodeDebugInfoTensorInst(Value tensor) {
527 if (!options.emitDebugInfo)
528 return success();
529
530 uint32_t voidTypeID = 0;
531 if (failed(processType(tensor.getLoc(), getVoidType(), voidTypeID)))
532 return failure();
533
534 uint32_t tensorID = valueIDMap.lookup(tensor);
535 if (tensorID == 0)
536 return success();
537
538 uint32_t stringID =
539 encodeDebugStringInst(getDebugInfoStringFromLoc(tensor.getLoc()));
540
542 operands.push_back(voidTypeID);
543 operands.push_back(getNextID());
544 operands.push_back(tensorID);
545 operands.push_back(stringID);
546
547 if (failed(encodeExtensionInstruction(
548 nullptr, extDebugInfo,
549 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugTensor), operands,
550 graphsDebugInfo)))
551 return failure();
552
553 return success();
554}
555
556LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
557 if (op.getNumResults() < 1) {
558 return op.emitError("cannot serialize graph with no return types");
559 }
560
561 LLVM_DEBUG(llvm::dbgs() << "-- start graph '" << op.getName() << "' --\n");
562 assert(functionHeader.empty() && functionBody.empty());
563
564 uint32_t funcID = getOrCreateFunctionID(op.getName());
565 uint32_t fnTypeID = 0;
566 // Generate type of the function.
567 if (failed(processType(op.getLoc(), op.getFunctionType(), fnTypeID)))
568 return failure();
569 encodeInstructionInto(functionHeader, spirv::Opcode::OpGraphARM,
570 {fnTypeID, funcID});
571
572 // Declare the parameters.
573 for (auto [idx, arg] : llvm::enumerate(op.getArguments())) {
574 uint32_t argTypeID = 0;
575 SmallVector<uint32_t, 3> inputOperands;
576
577 if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
578 return failure();
579 }
580
581 uint32_t argValueID = getNextID();
582 valueIDMap[arg] = argValueID;
583
584 auto attr = IntegerAttr::get(IntegerType::get(op.getContext(), 32), idx);
585 uint32_t indexID = prepareConstantInt(op.getLoc(), attr, false);
586
587 inputOperands.push_back(argTypeID);
588 inputOperands.push_back(argValueID);
589 inputOperands.push_back(indexID);
590
591 encodeInstructionInto(functionHeader, spirv::Opcode::OpGraphInputARM,
592 inputOperands);
593
594 if (failed(encodeDebugInfoTensorInst(arg)))
595 return failure();
596 }
597
598 if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
599 return failure();
600 if (failed(visitInPrettyBlockOrder(
601 &op.front(), [&](Block *block) { return processBlock(block); },
602 /*skipHeader=*/true))) {
603 return failure();
604 }
605
606 LLVM_DEBUG(llvm::dbgs() << "-- completed graph '" << op.getName()
607 << "' --\n");
608 // Insert OpGraphEndARM.
609 encodeInstructionInto(functionBody, spirv::Opcode::OpGraphEndARM, {});
610
611 llvm::append_range(graphs, functionHeader);
612 llvm::append_range(graphs, functionBody);
613 functionHeader.clear();
614 functionBody.clear();
615
616 uint32_t debugGraphID = 0;
617 if (failed(encodeDebugInfoGraphInst(op, debugGraphID)))
618 return failure();
619
620 for (const auto &debugEntry : tosaOpsMap[funcID]) {
621 if (failed(encodeDebugInfoOperationInst(debugGraphID, debugEntry.second)))
622 return failure();
623 }
624
625 return success();
626}
627
628LogicalResult
629Serializer::processGraphEntryPointARMOp(spirv::GraphEntryPointARMOp op) {
630 SmallVector<uint32_t, 4> operands;
631 StringRef graph = op.getFn();
632 // Add the graph <id>.
633 uint32_t graphID = getOrCreateFunctionID(graph);
634 operands.push_back(graphID);
635 // Add the name of the graph.
636 spirv::encodeStringLiteralInto(operands, graph);
638 // Add the interface values.
639 if (ArrayAttr interface = op.getInterface()) {
640 for (Attribute var : interface.getValue()) {
641 StringRef value = cast<FlatSymbolRefAttr>(var).getValue();
642 if (uint32_t id = getVariableID(value)) {
643 operands.push_back(id);
644 } else {
645 return op.emitError(
646 "referencing undefined global variable."
647 "spirv.GraphEntryPointARM is at the end of spirv.module. All "
648 "referenced variables should already be defined");
649 }
650 }
651 }
652 encodeInstructionInto(graphs, spirv::Opcode::OpGraphEntryPointARM, operands);
653 return success();
654}
655
656LogicalResult
657Serializer::processGraphOutputsARMOp(spirv::GraphOutputsARMOp op) {
658 for (auto [idx, value] : llvm::enumerate(op->getOperands())) {
659 SmallVector<uint32_t, 2> outputOperands;
660
661 Type resType = value.getType();
662 uint32_t resTypeID = 0;
663 if (failed(processType(op.getLoc(), resType, resTypeID))) {
664 return failure();
665 }
666
667 uint32_t outputID = getValueID(value);
668 auto attr = IntegerAttr::get(IntegerType::get(op.getContext(), 32), idx);
669 uint32_t indexID = prepareConstantInt(op.getLoc(), attr, false);
670
671 outputOperands.push_back(outputID);
672 outputOperands.push_back(indexID);
673
674 if (failed(encodeDebugInfoTensorInst(value)))
675 return failure();
676
677 encodeInstructionInto(functionBody, spirv::Opcode::OpGraphSetOutputARM,
678 outputOperands);
679 }
680 return success();
681}
682
683LogicalResult Serializer::processVariableOp(spirv::VariableOp op) {
684 SmallVector<uint32_t, 4> operands;
685 SmallVector<StringRef, 2> elidedAttrs;
686 uint32_t resultID = 0;
687 uint32_t resultTypeID = 0;
688 if (failed(processType(op.getLoc(), op.getType(), resultTypeID))) {
689 return failure();
690 }
691 operands.push_back(resultTypeID);
692 resultID = getNextID();
693 valueIDMap[op.getResult()] = resultID;
694 operands.push_back(resultID);
695 operands.push_back(static_cast<uint32_t>(op.getStorageClass()));
696 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
697 for (auto arg : op.getODSOperands(0)) {
698 auto argID = getValueID(arg);
699 if (!argID) {
700 return emitError(op.getLoc(), "operand 0 has a use before def");
701 }
702 operands.push_back(argID);
703 }
704 if (failed(emitDebugLine(functionHeader, op.getLoc())))
705 return failure();
706 encodeInstructionInto(functionHeader, spirv::Opcode::OpVariable, operands);
707 for (auto attr : op->getDiscardableAttrDictionary().getValue()) {
708 if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
709 return attr.getName() == elided;
710 })) {
711 continue;
712 }
713 if (failed(processDecoration(op.getLoc(), resultID, attr))) {
714 return failure();
715 }
716 }
717 return success();
718}
719
720LogicalResult
721Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
722 // Get TypeID.
723 uint32_t resultTypeID = 0;
724 SmallVector<StringRef, 4> elidedAttrs;
725 if (failed(processType(varOp.getLoc(), varOp.getType(), resultTypeID))) {
726 return failure();
727 }
728
729 elidedAttrs.push_back("type");
730 SmallVector<uint32_t, 4> operands;
731 operands.push_back(resultTypeID);
732 auto resultID = getNextID();
733
734 // Encode the name.
735 auto varName = varOp.getSymName();
736 elidedAttrs.push_back(varOp.getSymNameAttrName());
737 if (failed(processName(resultID, varName))) {
738 return failure();
739 }
740 globalVarIDMap[varName] = resultID;
741 operands.push_back(resultID);
742
743 // Encode StorageClass.
744 operands.push_back(static_cast<uint32_t>(varOp.storageClass()));
745
746 // Encode initialization.
747 StringRef initAttrName = varOp.getInitializerAttrName().getValue();
748 if (std::optional<StringRef> initSymbolName = varOp.getInitializer()) {
749 uint32_t initializerID = 0;
750 FlatSymbolRefAttr initRef = varOp.getInitializerAttr();
752 varOp->getParentOp(), initRef.getAttr());
753
754 // Check if initializer is GlobalVariable or SpecConstant* cases.
755 if (isa<spirv::GlobalVariableOp>(initOp))
756 initializerID = getVariableID(*initSymbolName);
757 else
758 initializerID = getSpecConstID(*initSymbolName);
759
760 if (!initializerID)
761 return emitError(varOp.getLoc(),
762 "invalid usage of undefined variable as initializer");
763
764 operands.push_back(initializerID);
765 elidedAttrs.push_back(initAttrName);
766 }
767
768 if (failed(emitDebugLine(typesGlobalValues, varOp.getLoc())))
769 return failure();
770 encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, operands);
771 elidedAttrs.push_back(initAttrName);
772
773 // Encode decorations.
774 NamedAttrList attrs(varOp->getDiscardableAttrDictionary().getValue());
775 varOp->getName().populateInherentAttrs(varOp, attrs);
776 for (auto attr : attrs) {
777 if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
778 return attr.getName() == elided;
779 })) {
780 continue;
781 }
782 if (failed(processDecoration(varOp.getLoc(), resultID, attr))) {
783 return failure();
784 }
785 }
786 return success();
787}
788
789LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) {
790 // Assign <id>s to all blocks so that branches inside the SelectionOp can
791 // resolve properly.
792 auto &body = selectionOp.getBody();
793 for (Block &block : body)
794 getOrCreateBlockID(&block);
795
796 auto *headerBlock = selectionOp.getHeaderBlock();
797 auto *mergeBlock = selectionOp.getMergeBlock();
798 auto headerID = getBlockID(headerBlock);
799 auto mergeID = getBlockID(mergeBlock);
800 auto loc = selectionOp.getLoc();
801
802 // Before we do anything replace results of the selection operation with
803 // values yielded (with `mlir.merge`) from inside the region. The selection op
804 // is being flattened so we do not have to worry about values being defined
805 // inside a region and used outside it anymore.
806 auto mergeOp = cast<spirv::MergeOp>(mergeBlock->back());
807 assert(selectionOp.getNumResults() == mergeOp.getNumOperands());
808 for (unsigned i = 0, e = selectionOp.getNumResults(); i != e; ++i)
809 selectionOp.getResult(i).replaceAllUsesWith(mergeOp.getOperand(i));
810
811 // This SelectionOp is in some MLIR block with preceding and following ops. In
812 // the binary format, it should reside in separate SPIR-V blocks from its
813 // preceding and following ops. So we need to emit unconditional branches to
814 // jump to this SelectionOp's SPIR-V blocks and jumping back to the normal
815 // flow afterwards.
816 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
817
818 // Emit the selection header block, which dominates all other blocks, first.
819 // We need to emit an OpSelectionMerge instruction before the selection header
820 // block's terminator.
821 auto emitSelectionMerge = [&]() {
822 if (failed(emitDebugLine(functionBody, loc)))
823 return failure();
824 lastProcessedWasMergeInst = true;
826 functionBody, spirv::Opcode::OpSelectionMerge,
827 {mergeID, static_cast<uint32_t>(selectionOp.getSelectionControl())});
828 return success();
829 };
830 if (failed(
831 processBlock(headerBlock, /*omitLabel=*/false, emitSelectionMerge)))
832 return failure();
833
834 // Process all blocks with a depth-first visitor starting from the header
835 // block. The selection header block and merge block are skipped by this
836 // visitor.
838 headerBlock, [&](Block *block) { return processBlock(block); },
839 /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock})))
840 return failure();
842 // There is nothing to do for the merge block in the selection, which just
843 // contains a spirv.mlir.merge op, itself. But we need to have an OpLabel
844 // instruction to start a new SPIR-V block for ops following this SelectionOp.
845 // The block should use the <id> for the merge block.
846 encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
847
848 // We do not process the mergeBlock but we still need to generate phi
849 // functions from its block arguments.
850 if (failed(emitPhiForBlockArguments(mergeBlock)))
851 return failure();
852
853 LLVM_DEBUG(llvm::dbgs() << "done merge ");
854 LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
855 LLVM_DEBUG(llvm::dbgs() << "\n");
856 return success();
857}
858
859LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) {
860 // Assign <id>s to all blocks so that branches inside the LoopOp can resolve
861 // properly. We don't need to assign for the entry block, which is just for
862 // satisfying MLIR region's structural requirement.
863 auto &body = loopOp.getBody();
864 for (Block &block : llvm::drop_begin(body))
865 getOrCreateBlockID(&block);
866
867 auto *headerBlock = loopOp.getHeaderBlock();
868 auto *continueBlock = loopOp.getContinueBlock();
869 auto *mergeBlock = loopOp.getMergeBlock();
870 auto headerID = getBlockID(headerBlock);
871 auto continueID = getBlockID(continueBlock);
872 auto mergeID = getBlockID(mergeBlock);
873 auto loc = loopOp.getLoc();
874
875 // Before we do anything replace results of the selection operation with
876 // values yielded (with `mlir.merge`) from inside the region.
877 auto mergeOp = cast<spirv::MergeOp>(mergeBlock->back());
878 assert(loopOp.getNumResults() == mergeOp.getNumOperands());
879 for (unsigned i = 0, e = loopOp.getNumResults(); i != e; ++i)
880 loopOp.getResult(i).replaceAllUsesWith(mergeOp.getOperand(i));
881
882 // This LoopOp is in some MLIR block with preceding and following ops. In the
883 // binary format, it should reside in separate SPIR-V blocks from its
884 // preceding and following ops. So we need to emit unconditional branches to
885 // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow
886 // afterwards.
887 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
888
889 // LoopOp's entry block is just there for satisfying MLIR's structural
890 // requirements so we omit it and start serialization from the loop header
891 // block.
892
893 // Emit the loop header block, which dominates all other blocks, first. We
894 // need to emit an OpLoopMerge instruction before the loop header block's
895 // terminator.
896 auto emitLoopMerge = [&]() {
897 if (failed(emitDebugLine(functionBody, loc)))
898 return failure();
899 lastProcessedWasMergeInst = true;
901 functionBody, spirv::Opcode::OpLoopMerge,
902 {mergeID, continueID, static_cast<uint32_t>(loopOp.getLoopControl())});
903 return success();
904 };
905 if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge)))
906 return failure();
907
908 // Process all blocks with a depth-first visitor starting from the header
909 // block. The loop header block, loop continue block, and loop merge block are
910 // skipped by this visitor and handled later in this function.
912 headerBlock, [&](Block *block) { return processBlock(block); },
913 /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock})))
914 return failure();
915
916 // We have handled all other blocks. Now get to the loop continue block.
917 if (failed(processBlock(continueBlock)))
918 return failure();
919
920 // There is nothing to do for the merge block in the loop, which just contains
921 // a spirv.mlir.merge op, itself. But we need to have an OpLabel instruction
922 // to start a new SPIR-V block for ops following this LoopOp. The block should
923 // use the <id> for the merge block.
924 encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
925 LLVM_DEBUG(llvm::dbgs() << "done merge ");
926 LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
927 LLVM_DEBUG(llvm::dbgs() << "\n");
928 return success();
929}
930
931LogicalResult Serializer::processBranchConditionalOp(
932 spirv::BranchConditionalOp condBranchOp) {
933 auto conditionID = getValueID(condBranchOp.getCondition());
934 auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock());
935 auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock());
936 SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID};
937
938 if (auto weights = condBranchOp.getBranchWeights()) {
939 for (auto val : weights->getValue())
940 arguments.push_back(cast<IntegerAttr>(val).getInt());
942
943 if (failed(emitDebugLine(functionBody, condBranchOp.getLoc())))
944 return failure();
945 encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional,
946 arguments);
947 return success();
948}
949
950LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) {
951 if (failed(emitDebugLine(functionBody, branchOp.getLoc())))
952 return failure();
953 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch,
954 {getOrCreateBlockID(branchOp.getTarget())});
955 return success();
957
958LogicalResult Serializer::processSwitchOp(spirv::SwitchOp switchOp) {
959 Value selector = switchOp.getSelector();
960 uint32_t selectorID = getValueID(selector);
961 uint32_t defaultLabelID = getOrCreateBlockID(switchOp.getDefaultTarget());
962 SmallVector<uint32_t> arguments{selectorID, defaultLabelID};
963
964 std::optional<mlir::DenseIntElementsAttr> literals = switchOp.getLiterals();
965 BlockRange targets = switchOp.getTargets();
966 if (literals) {
967 Type selectorType = selector.getType();
968 unsigned bitWidth = selectorType.getIntOrFloatBitWidth();
969 bool isSigned = selectorType.isSignedInteger();
970 for (auto [literal, target] : llvm::zip_equal(*literals, targets)) {
971 if (bitWidth <= 32) {
972 // Sub-word literals occupy the low-order bits of the word and are
973 // sign-extended for signed selector types, zero-extended otherwise.
974 arguments.push_back(
975 isSigned ? static_cast<uint32_t>(literal.getSExtValue())
976 : static_cast<uint32_t>(literal.getZExtValue()));
977 } else {
978 // Wider literals span multiple words, low-order word first.
979 for (unsigned offset = 0; offset < bitWidth; offset += 32)
980 arguments.push_back(literal.extractBitsAsZExtValue(32, offset));
982 uint32_t targetLabelID = getOrCreateBlockID(target);
983 arguments.push_back(targetLabelID);
984 }
985 }
987 if (failed(emitDebugLine(functionBody, switchOp.getLoc())))
988 return failure();
989 encodeInstructionInto(functionBody, spirv::Opcode::OpSwitch, arguments);
990 return success();
992
993LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) {
994 auto varName = addressOfOp.getVariable();
995 auto variableID = getVariableID(varName);
996 if (!variableID) {
997 return addressOfOp.emitError("unknown result <id> for variable ")
998 << varName;
999 }
1000 valueIDMap[addressOfOp.getPointer()] = variableID;
1001 return success();
1002}
1003
1004LogicalResult
1005Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) {
1006 auto constName = referenceOfOp.getSpecConst();
1007 auto constID = getSpecConstID(constName);
1008 if (!constID) {
1009 return referenceOfOp.emitError(
1010 "unknown result <id> for specialization constant ")
1011 << constName;
1012 }
1013 valueIDMap[referenceOfOp.getReference()] = constID;
1014 return success();
1015}
1017template <>
1018LogicalResult
1019Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) {
1020 SmallVector<uint32_t, 4> operands;
1021 // Add the ExecutionModel.
1022 operands.push_back(static_cast<uint32_t>(op.getExecutionModel()));
1023 // Add the function <id>.
1024 auto funcID = getFunctionID(op.getFn());
1025 if (!funcID) {
1026 return op.emitError("missing <id> for function ")
1027 << op.getFn()
1028 << "; function needs to be defined before spirv.EntryPoint is "
1029 "serialized";
1030 }
1031 operands.push_back(funcID);
1032 // Add the name of the function.
1033 spirv::encodeStringLiteralInto(operands, op.getFn());
1034
1035 // Add the interface values.
1036 if (auto interface = op.getInterface()) {
1037 for (auto var : interface.getValue()) {
1038 auto id = getVariableID(cast<FlatSymbolRefAttr>(var).getValue());
1039 if (!id) {
1040 return op.emitError(
1041 "referencing undefined global variable."
1042 "spirv.EntryPoint is at the end of spirv.module. All "
1043 "referenced variables should already be defined");
1044 }
1045 operands.push_back(id);
1047 }
1048 encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, operands);
1049 return success();
1050}
1052template <>
1053LogicalResult
1054Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) {
1055 SmallVector<uint32_t, 4> operands;
1056 // Add the function <id>.
1057 auto funcID = getFunctionID(op.getFn());
1058 if (!funcID) {
1059 return op.emitError("missing <id> for function ")
1060 << op.getFn()
1061 << "; function needs to be serialized before ExecutionModeOp is "
1062 "serialized";
1063 }
1064 operands.push_back(funcID);
1065 // Add the ExecutionMode.
1066 operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
1067
1068 // Serialize values if any.
1069 auto values = op.getValues();
1070 if (values) {
1071 for (auto &intVal : values.getValue()) {
1072 operands.push_back(static_cast<uint32_t>(
1073 cast<IntegerAttr>(intVal).getValue().getZExtValue()));
1074 }
1075 }
1076 encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode,
1077 operands);
1078 return success();
1079}
1080
1081template <>
1082LogicalResult
1083Serializer::processOp<spirv::ExecutionModeIdOp>(spirv::ExecutionModeIdOp op) {
1084 SmallVector<uint32_t, 4> operands;
1085 // Add the function <id>.
1086 uint32_t funcID = getFunctionID(op.getFn());
1087 if (!funcID)
1088 return op.emitError("missing <id> for function ")
1089 << op.getFn()
1090 << "; function needs to be serialized before ExecutionModeIdOp is "
1091 "serialized";
1092
1093 operands.push_back(funcID);
1094 operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
1095
1096 for (Attribute refVal : op.getValues().getValue()) {
1097 uint32_t id = getSpecConstID(cast<FlatSymbolRefAttr>(refVal).getValue());
1098 if (!id)
1099 return op.emitError("unknown <id> for specialization constant ")
1100 << cast<FlatSymbolRefAttr>(refVal).getValue();
1102 operands.push_back(id);
1103 }
1104 encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionModeId,
1105 operands);
1106 return success();
1107}
1108
1109template <>
1110LogicalResult
1111Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) {
1112 auto funcName = op.getCallee();
1113 uint32_t resTypeID = 0;
1114
1115 Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType();
1116 if (failed(processType(op.getLoc(), resultTy, resTypeID)))
1117 return failure();
1118
1119 auto funcID = getOrCreateFunctionID(funcName);
1120 auto funcCallID = getNextID();
1121 SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID};
1122
1123 for (auto value : op.getArguments()) {
1124 auto valueID = getValueID(value);
1125 assert(valueID && "cannot find a value for spirv.FunctionCall");
1126 operands.push_back(valueID);
1127 }
1128
1129 if (!isa<NoneType>(resultTy))
1130 valueIDMap[op.getResult(0)] = funcCallID;
1132 encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, operands);
1133 for (auto attr : op->getDiscardableAttrs())
1134 if (failed(processDecoration(op.getLoc(), funcCallID, attr)))
1135 return failure();
1136 return success();
1137}
1138
1139template <>
1140LogicalResult
1141Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
1142 SmallVector<uint32_t, 4> operands;
1143 SmallVector<StringRef, 2> elidedAttrs;
1144
1145 for (Value operand : op->getOperands()) {
1146 auto id = getValueID(operand);
1147 assert(id && "use before def!");
1148 operands.push_back(id);
1149 }
1150
1151 StringAttr memoryAccess = op.getMemoryAccessAttrName();
1152 if (std::optional<spirv::MemoryAccess> value = op.getMemoryAccess())
1153 operands.push_back(static_cast<uint32_t>(*value));
1154
1155 elidedAttrs.push_back(memoryAccess.strref());
1156
1157 StringAttr alignment = op.getAlignmentAttrName();
1158 if (std::optional<uint32_t> value = op.getAlignment())
1159 operands.push_back(*value);
1161 elidedAttrs.push_back(alignment.strref());
1162
1163 StringAttr sourceMemoryAccess = op.getSourceMemoryAccessAttrName();
1164 if (std::optional<spirv::MemoryAccess> value = op.getSourceMemoryAccess())
1165 operands.push_back(static_cast<uint32_t>(*value));
1166
1167 elidedAttrs.push_back(sourceMemoryAccess.strref());
1168
1169 StringAttr sourceAlignment = op.getSourceAlignmentAttrName();
1170 if (std::optional<uint32_t> value = op.getSourceAlignment())
1171 operands.push_back(*value);
1172
1173 elidedAttrs.push_back(sourceAlignment.strref());
1174 if (failed(emitDebugLine(functionBody, op.getLoc())))
1175 return failure();
1176 encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, operands);
1177
1178 return success();
1179}
1180template <>
1181LogicalResult Serializer::processOp<spirv::GenericCastToPtrExplicitOp>(
1182 spirv::GenericCastToPtrExplicitOp op) {
1183 SmallVector<uint32_t, 4> operands;
1184 Type resultTy;
1185 Location loc = op->getLoc();
1186 uint32_t resultTypeID = 0;
1187 uint32_t resultID = 0;
1188 resultTy = op->getResult(0).getType();
1189 if (failed(processType(loc, resultTy, resultTypeID)))
1190 return failure();
1191 operands.push_back(resultTypeID);
1192
1193 resultID = getNextID();
1194 operands.push_back(resultID);
1195 valueIDMap[op->getResult(0)] = resultID;
1196
1197 for (Value operand : op->getOperands())
1198 operands.push_back(getValueID(operand));
1199 spirv::StorageClass resultStorage =
1200 cast<spirv::PointerType>(resultTy).getStorageClass();
1201 operands.push_back(static_cast<uint32_t>(resultStorage));
1202 encodeInstructionInto(functionBody, spirv::Opcode::OpGenericCastToPtrExplicit,
1203 operands);
1204 return success();
1206
1207// Pull in auto-generated Serializer::dispatchToAutogenSerialization() and
1208// various Serializer::processOp<...>() specializations.
1209#define GET_SERIALIZATION_FNS
1210#include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"
1211
1212} // namespace spirv
1213} // namespace mlir
return success()
static llvm::ManagedStatic< PassManagerOptions > options
static LogicalResult visitInPrettyBlockOrder(Block *headerBlock, function_ref< LogicalResult(Block *)> blockHandler, bool skipHeader=false, BlockRange skipBlocks={})
A pre-order depth-first visitor function for processing basic blocks.
static void printBlock(llvm::raw_ostream &os, Block *block, OpPrintingFlags &flags)
Definition Unit.cpp:37
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class provides an abstraction over the different types of ranges over Blocks.
Block represents an ordered list of Operations.
Definition Block.h:34
OpListType & getOperations()
Definition Block.h:162
A symbol reference with a reference path containing a single element.
StringAttr getAttr() const
Returns the name of the held symbol reference as a StringAttr.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
This is a value defined by a result of an operation.
Definition Value.h:456
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
StringRef stripDialect() const
Return the operation name with dialect name stripped, if it has one.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
Definition Types.cpp:78
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:853
constexpr llvm::StringLiteral extDebugInfo
Extension set name for non-semantic graph debug info.
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
void encodeInstructionInto(SmallVectorImpl< uint32_t > &binary, spirv::Opcode op, ArrayRef< uint32_t > operands)
Encodes an SPIR-V instruction with the given opcode and operands into the given binary vector.
StringRef getFuncParamAttrName()
Returns the attribute name for specifying function parameter and result attributes.
constexpr StringRef attributeName()
StringRef getFuncParamAlignmentAttrName()
Returns the attribute name for specifying function parameter alignment.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155