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
436 return success();
437}
438
439uint32_t Serializer::encodeDebugStringInst(StringRef str) {
440 uint32_t stringID = debugStringIDMap.lookup(str);
441 if (stringID > 0) {
442 return stringID;
443 }
444
446 stringID = getNextID();
447 debugStringIDMap[str] = stringID;
448 operands.push_back(stringID);
449 spirv::encodeStringLiteralInto(operands, str);
450 encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
451
452 return stringID;
453}
454
455LogicalResult Serializer::encodeDebugInfoGraphInst(spirv::GraphARMOp op,
456 uint32_t &debugGraphID) {
457 if (!options.emitDebugInfo)
458 return success();
459
460 uint32_t voidTypeID = 0;
461 if (failed(processType(op.getLoc(), getVoidType(), voidTypeID)))
462 return failure();
463
464 uint32_t stringID =
465 encodeDebugStringInst(getDebugInfoStringFromLoc(op.getLoc()));
466
467 SmallVector<uint32_t, 6> operands;
468 operands.push_back(voidTypeID);
469 debugGraphID = getNextID();
470 operands.push_back(debugGraphID);
471 uint32_t graphID = getOrCreateFunctionID(op.getName());
472 operands.push_back(graphID);
473 operands.push_back(stringID);
474
475 if (failed(encodeExtensionInstruction(
476 nullptr, extDebugInfo,
477 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugGraph), operands,
478 graphsDebugInfo)))
479 return failure();
480
481 return success();
482}
483
484LogicalResult
485Serializer::encodeDebugInfoOperationInst(uint32_t debugGraphID,
486 const SetVector<Operation *> &ops) {
487 if (!options.emitDebugInfo)
488 return success();
490 if (ops.empty())
491 return success();
492
493 SmallVector<uint32_t, 4> instructionIDs;
494 for (Operation *op : ops)
495 for (OpResult result : op->getOpResults())
496 instructionIDs.push_back(getValueID(result));
497
498 if (instructionIDs.empty())
499 return success();
500
501 uint32_t voidTypeID = 0;
502 if (failed(processType(ops[0]->getLoc(), getVoidType(), voidTypeID)))
503 return failure();
504
505 uint32_t stringID =
506 encodeDebugStringInst(getDebugInfoStringFromLoc(ops[0]->getLoc()));
507
509 operands.push_back(voidTypeID);
510 operands.push_back(getNextID());
511 operands.push_back(debugGraphID);
512 operands.push_back(stringID);
513 operands.append(instructionIDs);
514
515 if (failed(encodeExtensionInstruction(
516 nullptr, extDebugInfo,
517 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugOperation),
518 operands, graphsDebugInfo)))
519 return failure();
520
521 return success();
522}
523
524LogicalResult Serializer::encodeDebugInfoTensorInst(Value tensor) {
525 if (!options.emitDebugInfo)
526 return success();
528 uint32_t voidTypeID = 0;
529 if (failed(processType(tensor.getLoc(), getVoidType(), voidTypeID)))
530 return failure();
531
532 uint32_t tensorID = valueIDMap.lookup(tensor);
533 if (tensorID == 0)
534 return success();
535
536 uint32_t stringID =
537 encodeDebugStringInst(getDebugInfoStringFromLoc(tensor.getLoc()));
538
540 operands.push_back(voidTypeID);
541 operands.push_back(getNextID());
542 operands.push_back(tensorID);
543 operands.push_back(stringID);
544
545 if (failed(encodeExtensionInstruction(
546 nullptr, extDebugInfo,
547 static_cast<uint32_t>(GraphDebugInfoExtInst::DebugTensor), operands,
548 graphsDebugInfo)))
549 return failure();
550
551 return success();
552}
553
554LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
555 if (op.getNumResults() < 1) {
556 return op.emitError("cannot serialize graph with no return types");
557 }
558
559 LLVM_DEBUG(llvm::dbgs() << "-- start graph '" << op.getName() << "' --\n");
560 assert(functionHeader.empty() && functionBody.empty());
561
562 uint32_t funcID = getOrCreateFunctionID(op.getName());
563 uint32_t fnTypeID = 0;
564 // Generate type of the function.
565 if (failed(processType(op.getLoc(), op.getFunctionType(), fnTypeID)))
566 return failure();
567 encodeInstructionInto(functionHeader, spirv::Opcode::OpGraphARM,
568 {fnTypeID, funcID});
569
570 // Declare the parameters.
571 for (auto [idx, arg] : llvm::enumerate(op.getArguments())) {
572 uint32_t argTypeID = 0;
573 SmallVector<uint32_t, 3> inputOperands;
574
575 if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
576 return failure();
577 }
578
579 uint32_t argValueID = getNextID();
580 valueIDMap[arg] = argValueID;
581
582 auto attr = IntegerAttr::get(IntegerType::get(op.getContext(), 32), idx);
583 uint32_t indexID = prepareConstantInt(op.getLoc(), attr, false);
584
585 inputOperands.push_back(argTypeID);
586 inputOperands.push_back(argValueID);
587 inputOperands.push_back(indexID);
588
589 encodeInstructionInto(functionHeader, spirv::Opcode::OpGraphInputARM,
590 inputOperands);
591
592 if (failed(encodeDebugInfoTensorInst(arg)))
593 return failure();
594 }
595
596 if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
597 return failure();
598 if (failed(visitInPrettyBlockOrder(
599 &op.front(), [&](Block *block) { return processBlock(block); },
600 /*skipHeader=*/true))) {
601 return failure();
602 }
603
604 LLVM_DEBUG(llvm::dbgs() << "-- completed graph '" << op.getName()
605 << "' --\n");
606 // Insert OpGraphEndARM.
607 encodeInstructionInto(functionBody, spirv::Opcode::OpGraphEndARM, {});
608
609 llvm::append_range(graphs, functionHeader);
610 llvm::append_range(graphs, functionBody);
611 functionHeader.clear();
612 functionBody.clear();
613
614 uint32_t debugGraphID = 0;
615 if (failed(encodeDebugInfoGraphInst(op, debugGraphID)))
616 return failure();
617
618 for (const auto &debugEntry : tosaOpsMap[funcID]) {
619 if (failed(encodeDebugInfoOperationInst(debugGraphID, debugEntry.second)))
620 return failure();
621 }
622
623 return success();
624}
625
626LogicalResult
627Serializer::processGraphEntryPointARMOp(spirv::GraphEntryPointARMOp op) {
628 SmallVector<uint32_t, 4> operands;
629 StringRef graph = op.getFn();
630 // Add the graph <id>.
631 uint32_t graphID = getOrCreateFunctionID(graph);
632 operands.push_back(graphID);
633 // Add the name of the graph.
634 spirv::encodeStringLiteralInto(operands, graph);
635
636 // Add the interface values.
637 if (ArrayAttr interface = op.getInterface()) {
638 for (Attribute var : interface.getValue()) {
639 StringRef value = cast<FlatSymbolRefAttr>(var).getValue();
640 if (uint32_t id = getVariableID(value)) {
641 operands.push_back(id);
642 } else {
643 return op.emitError(
644 "referencing undefined global variable."
645 "spirv.GraphEntryPointARM is at the end of spirv.module. All "
646 "referenced variables should already be defined");
647 }
648 }
649 }
650 encodeInstructionInto(graphs, spirv::Opcode::OpGraphEntryPointARM, operands);
651 return success();
652}
653
654LogicalResult
655Serializer::processGraphOutputsARMOp(spirv::GraphOutputsARMOp op) {
656 for (auto [idx, value] : llvm::enumerate(op->getOperands())) {
657 SmallVector<uint32_t, 2> outputOperands;
658
659 Type resType = value.getType();
660 uint32_t resTypeID = 0;
661 if (failed(processType(op.getLoc(), resType, resTypeID))) {
662 return failure();
663 }
664
665 uint32_t outputID = getValueID(value);
666 auto attr = IntegerAttr::get(IntegerType::get(op.getContext(), 32), idx);
667 uint32_t indexID = prepareConstantInt(op.getLoc(), attr, false);
668
669 outputOperands.push_back(outputID);
670 outputOperands.push_back(indexID);
671
672 if (failed(encodeDebugInfoTensorInst(value)))
673 return failure();
674
675 encodeInstructionInto(functionBody, spirv::Opcode::OpGraphSetOutputARM,
676 outputOperands);
677 }
678 return success();
679}
680
681LogicalResult Serializer::processVariableOp(spirv::VariableOp op) {
682 SmallVector<uint32_t, 4> operands;
683 SmallVector<StringRef, 2> elidedAttrs;
684 uint32_t resultID = 0;
685 uint32_t resultTypeID = 0;
686 if (failed(processType(op.getLoc(), op.getType(), resultTypeID))) {
687 return failure();
688 }
689 operands.push_back(resultTypeID);
690 resultID = getNextID();
691 valueIDMap[op.getResult()] = resultID;
692 operands.push_back(resultID);
693 operands.push_back(static_cast<uint32_t>(op.getStorageClass()));
694 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
695 for (auto arg : op.getODSOperands(0)) {
696 auto argID = getValueID(arg);
697 if (!argID) {
698 return emitError(op.getLoc(), "operand 0 has a use before def");
699 }
700 operands.push_back(argID);
701 }
702 if (failed(emitDebugLine(functionHeader, op.getLoc())))
703 return failure();
704 encodeInstructionInto(functionHeader, spirv::Opcode::OpVariable, operands);
705 for (auto attr : op->getDiscardableAttrDictionary().getValue()) {
706 if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
707 return attr.getName() == elided;
708 })) {
709 continue;
710 }
711 if (failed(processDecoration(op.getLoc(), resultID, attr))) {
712 return failure();
713 }
714 }
715 return success();
716}
717
718LogicalResult
719Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
720 // Get TypeID.
721 uint32_t resultTypeID = 0;
722 SmallVector<StringRef, 4> elidedAttrs;
723 if (failed(processType(varOp.getLoc(), varOp.getType(), resultTypeID))) {
724 return failure();
725 }
726
727 elidedAttrs.push_back("type");
728 SmallVector<uint32_t, 4> operands;
729 operands.push_back(resultTypeID);
730 auto resultID = getNextID();
732 // Encode the name.
733 auto varName = varOp.getSymName();
734 elidedAttrs.push_back(varOp.getSymNameAttrName());
735 if (failed(processName(resultID, varName))) {
736 return failure();
737 }
738 globalVarIDMap[varName] = resultID;
739 operands.push_back(resultID);
740
741 // Encode StorageClass.
742 operands.push_back(static_cast<uint32_t>(varOp.storageClass()));
743
744 // Encode initialization.
745 StringRef initAttrName = varOp.getInitializerAttrName().getValue();
746 if (std::optional<StringRef> initSymbolName = varOp.getInitializer()) {
747 uint32_t initializerID = 0;
748 FlatSymbolRefAttr initRef = varOp.getInitializerAttr();
750 varOp->getParentOp(), initRef.getAttr());
751
752 // Check if initializer is GlobalVariable or SpecConstant* cases.
753 if (isa<spirv::GlobalVariableOp>(initOp))
754 initializerID = getVariableID(*initSymbolName);
755 else
756 initializerID = getSpecConstID(*initSymbolName);
757
758 if (!initializerID)
759 return emitError(varOp.getLoc(),
760 "invalid usage of undefined variable as initializer");
761
762 operands.push_back(initializerID);
763 elidedAttrs.push_back(initAttrName);
764 }
765
766 if (failed(emitDebugLine(typesGlobalValues, varOp.getLoc())))
767 return failure();
768 encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, operands);
769 elidedAttrs.push_back(initAttrName);
770
771 // Encode decorations.
772 NamedAttrList attrs(varOp->getDiscardableAttrDictionary().getValue());
773 varOp->getName().populateInherentAttrs(varOp, attrs);
774 for (auto attr : attrs) {
775 if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
776 return attr.getName() == elided;
777 })) {
778 continue;
779 }
780 if (failed(processDecoration(varOp.getLoc(), resultID, attr))) {
781 return failure();
782 }
783 }
784 return success();
785}
787LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) {
788 // Assign <id>s to all blocks so that branches inside the SelectionOp can
789 // resolve properly.
790 auto &body = selectionOp.getBody();
791 for (Block &block : body)
792 getOrCreateBlockID(&block);
793
794 auto *headerBlock = selectionOp.getHeaderBlock();
795 auto *mergeBlock = selectionOp.getMergeBlock();
796 auto headerID = getBlockID(headerBlock);
797 auto mergeID = getBlockID(mergeBlock);
798 auto loc = selectionOp.getLoc();
799
800 // Before we do anything replace results of the selection operation with
801 // values yielded (with `mlir.merge`) from inside the region. The selection op
802 // is being flattened so we do not have to worry about values being defined
803 // inside a region and used outside it anymore.
804 auto mergeOp = cast<spirv::MergeOp>(mergeBlock->back());
805 assert(selectionOp.getNumResults() == mergeOp.getNumOperands());
806 for (unsigned i = 0, e = selectionOp.getNumResults(); i != e; ++i)
807 selectionOp.getResult(i).replaceAllUsesWith(mergeOp.getOperand(i));
808
809 // This SelectionOp is in some MLIR block with preceding and following ops. In
810 // the binary format, it should reside in separate SPIR-V blocks from its
811 // preceding and following ops. So we need to emit unconditional branches to
812 // jump to this SelectionOp's SPIR-V blocks and jumping back to the normal
813 // flow afterwards.
814 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
815
816 // Emit the selection header block, which dominates all other blocks, first.
817 // We need to emit an OpSelectionMerge instruction before the selection header
818 // block's terminator.
819 auto emitSelectionMerge = [&]() {
820 if (failed(emitDebugLine(functionBody, loc)))
821 return failure();
822 lastProcessedWasMergeInst = true;
824 functionBody, spirv::Opcode::OpSelectionMerge,
825 {mergeID, static_cast<uint32_t>(selectionOp.getSelectionControl())});
826 return success();
827 };
828 if (failed(
829 processBlock(headerBlock, /*omitLabel=*/false, emitSelectionMerge)))
830 return failure();
831
832 // Process all blocks with a depth-first visitor starting from the header
833 // block. The selection header block and merge block are skipped by this
834 // visitor.
835 if (failed(visitInPrettyBlockOrder(
836 headerBlock, [&](Block *block) { return processBlock(block); },
837 /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock})))
838 return failure();
839
840 // There is nothing to do for the merge block in the selection, which just
841 // contains a spirv.mlir.merge op, itself. But we need to have an OpLabel
842 // instruction to start a new SPIR-V block for ops following this SelectionOp.
843 // The block should use the <id> for the merge block.
844 encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
845
846 // We do not process the mergeBlock but we still need to generate phi
847 // functions from its block arguments.
848 if (failed(emitPhiForBlockArguments(mergeBlock)))
849 return failure();
850
851 LLVM_DEBUG(llvm::dbgs() << "done merge ");
852 LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
853 LLVM_DEBUG(llvm::dbgs() << "\n");
854 return success();
855}
856
857LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) {
858 // Assign <id>s to all blocks so that branches inside the LoopOp can resolve
859 // properly. We don't need to assign for the entry block, which is just for
860 // satisfying MLIR region's structural requirement.
861 auto &body = loopOp.getBody();
862 for (Block &block : llvm::drop_begin(body))
863 getOrCreateBlockID(&block);
864
865 auto *headerBlock = loopOp.getHeaderBlock();
866 auto *continueBlock = loopOp.getContinueBlock();
867 auto *mergeBlock = loopOp.getMergeBlock();
868 auto headerID = getBlockID(headerBlock);
869 auto continueID = getBlockID(continueBlock);
870 auto mergeID = getBlockID(mergeBlock);
871 auto loc = loopOp.getLoc();
872
873 // Before we do anything replace results of the selection operation with
874 // values yielded (with `mlir.merge`) from inside the region.
875 auto mergeOp = cast<spirv::MergeOp>(mergeBlock->back());
876 assert(loopOp.getNumResults() == mergeOp.getNumOperands());
877 for (unsigned i = 0, e = loopOp.getNumResults(); i != e; ++i)
878 loopOp.getResult(i).replaceAllUsesWith(mergeOp.getOperand(i));
879
880 // This LoopOp is in some MLIR block with preceding and following ops. In the
881 // binary format, it should reside in separate SPIR-V blocks from its
882 // preceding and following ops. So we need to emit unconditional branches to
883 // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow
884 // afterwards.
885 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
886
887 // LoopOp's entry block is just there for satisfying MLIR's structural
888 // requirements so we omit it and start serialization from the loop header
889 // block.
890
891 // Emit the loop header block, which dominates all other blocks, first. We
892 // need to emit an OpLoopMerge instruction before the loop header block's
893 // terminator.
894 auto emitLoopMerge = [&]() {
895 if (failed(emitDebugLine(functionBody, loc)))
896 return failure();
897 lastProcessedWasMergeInst = true;
899 functionBody, spirv::Opcode::OpLoopMerge,
900 {mergeID, continueID, static_cast<uint32_t>(loopOp.getLoopControl())});
901 return success();
902 };
903 if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge)))
904 return failure();
905
906 // Process all blocks with a depth-first visitor starting from the header
907 // block. The loop header block, loop continue block, and loop merge block are
908 // skipped by this visitor and handled later in this function.
909 if (failed(visitInPrettyBlockOrder(
910 headerBlock, [&](Block *block) { return processBlock(block); },
911 /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock})))
912 return failure();
913
914 // We have handled all other blocks. Now get to the loop continue block.
915 if (failed(processBlock(continueBlock)))
916 return failure();
917
918 // There is nothing to do for the merge block in the loop, which just contains
919 // a spirv.mlir.merge op, itself. But we need to have an OpLabel instruction
920 // to start a new SPIR-V block for ops following this LoopOp. The block should
921 // use the <id> for the merge block.
922 encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
923 LLVM_DEBUG(llvm::dbgs() << "done merge ");
924 LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
925 LLVM_DEBUG(llvm::dbgs() << "\n");
926 return success();
927}
928
929LogicalResult Serializer::processBranchConditionalOp(
930 spirv::BranchConditionalOp condBranchOp) {
931 auto conditionID = getValueID(condBranchOp.getCondition());
932 auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock());
933 auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock());
934 SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID};
935
936 if (auto weights = condBranchOp.getBranchWeights()) {
937 for (auto val : weights->getValue())
938 arguments.push_back(cast<IntegerAttr>(val).getInt());
939 }
940
941 if (failed(emitDebugLine(functionBody, condBranchOp.getLoc())))
942 return failure();
943 encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional,
944 arguments);
945 return success();
947
948LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) {
949 if (failed(emitDebugLine(functionBody, branchOp.getLoc())))
950 return failure();
951 encodeInstructionInto(functionBody, spirv::Opcode::OpBranch,
952 {getOrCreateBlockID(branchOp.getTarget())});
953 return success();
954}
955
956LogicalResult Serializer::processSwitchOp(spirv::SwitchOp switchOp) {
957 uint32_t selectorID = getValueID(switchOp.getSelector());
958 uint32_t defaultLabelID = getOrCreateBlockID(switchOp.getDefaultTarget());
959 SmallVector<uint32_t> arguments{selectorID, defaultLabelID};
960
961 std::optional<mlir::DenseIntElementsAttr> literals = switchOp.getLiterals();
962 BlockRange targets = switchOp.getTargets();
963 if (literals) {
964 for (auto [literal, target] : llvm::zip_equal(*literals, targets)) {
965 arguments.push_back(literal.getLimitedValue());
966 uint32_t targetLabelID = getOrCreateBlockID(target);
967 arguments.push_back(targetLabelID);
968 }
969 }
970
971 if (failed(emitDebugLine(functionBody, switchOp.getLoc())))
972 return failure();
973 encodeInstructionInto(functionBody, spirv::Opcode::OpSwitch, arguments);
974 return success();
975}
977LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) {
978 auto varName = addressOfOp.getVariable();
979 auto variableID = getVariableID(varName);
980 if (!variableID) {
981 return addressOfOp.emitError("unknown result <id> for variable ")
982 << varName;
983 }
984 valueIDMap[addressOfOp.getPointer()] = variableID;
985 return success();
987
988LogicalResult
989Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) {
990 auto constName = referenceOfOp.getSpecConst();
991 auto constID = getSpecConstID(constName);
992 if (!constID) {
993 return referenceOfOp.emitError(
994 "unknown result <id> for specialization constant ")
995 << constName;
997 valueIDMap[referenceOfOp.getReference()] = constID;
998 return success();
999}
1000
1001template <>
1002LogicalResult
1003Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) {
1004 SmallVector<uint32_t, 4> operands;
1005 // Add the ExecutionModel.
1006 operands.push_back(static_cast<uint32_t>(op.getExecutionModel()));
1007 // Add the function <id>.
1008 auto funcID = getFunctionID(op.getFn());
1009 if (!funcID) {
1010 return op.emitError("missing <id> for function ")
1011 << op.getFn()
1012 << "; function needs to be defined before spirv.EntryPoint is "
1013 "serialized";
1014 }
1015 operands.push_back(funcID);
1016 // Add the name of the function.
1017 spirv::encodeStringLiteralInto(operands, op.getFn());
1018
1019 // Add the interface values.
1020 if (auto interface = op.getInterface()) {
1021 for (auto var : interface.getValue()) {
1022 auto id = getVariableID(cast<FlatSymbolRefAttr>(var).getValue());
1023 if (!id) {
1024 return op.emitError(
1025 "referencing undefined global variable."
1026 "spirv.EntryPoint is at the end of spirv.module. All "
1027 "referenced variables should already be defined");
1028 }
1029 operands.push_back(id);
1030 }
1032 encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, operands);
1033 return success();
1034}
1035
1036template <>
1037LogicalResult
1038Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) {
1039 SmallVector<uint32_t, 4> operands;
1040 // Add the function <id>.
1041 auto funcID = getFunctionID(op.getFn());
1042 if (!funcID) {
1043 return op.emitError("missing <id> for function ")
1044 << op.getFn()
1045 << "; function needs to be serialized before ExecutionModeOp is "
1046 "serialized";
1047 }
1048 operands.push_back(funcID);
1049 // Add the ExecutionMode.
1050 operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
1052 // Serialize values if any.
1053 auto values = op.getValues();
1054 if (values) {
1055 for (auto &intVal : values.getValue()) {
1056 operands.push_back(static_cast<uint32_t>(
1057 cast<IntegerAttr>(intVal).getValue().getZExtValue()));
1058 }
1059 }
1060 encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode,
1061 operands);
1062 return success();
1063}
1064
1065template <>
1066LogicalResult
1067Serializer::processOp<spirv::ExecutionModeIdOp>(spirv::ExecutionModeIdOp op) {
1068 SmallVector<uint32_t, 4> operands;
1069 // Add the function <id>.
1070 uint32_t funcID = getFunctionID(op.getFn());
1071 if (!funcID)
1072 return op.emitError("missing <id> for function ")
1073 << op.getFn()
1074 << "; function needs to be serialized before ExecutionModeIdOp is "
1075 "serialized";
1077 operands.push_back(funcID);
1078 operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
1079
1080 for (Attribute refVal : op.getValues().getValue()) {
1081 uint32_t id = getSpecConstID(cast<FlatSymbolRefAttr>(refVal).getValue());
1082 if (!id)
1083 return op.emitError("unknown <id> for specialization constant ")
1084 << cast<FlatSymbolRefAttr>(refVal).getValue();
1085
1086 operands.push_back(id);
1087 }
1088 encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionModeId,
1089 operands);
1090 return success();
1092
1093template <>
1094LogicalResult
1095Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) {
1096 auto funcName = op.getCallee();
1097 uint32_t resTypeID = 0;
1098
1099 Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType();
1100 if (failed(processType(op.getLoc(), resultTy, resTypeID)))
1101 return failure();
1102
1103 auto funcID = getOrCreateFunctionID(funcName);
1104 auto funcCallID = getNextID();
1105 SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID};
1107 for (auto value : op.getArguments()) {
1108 auto valueID = getValueID(value);
1109 assert(valueID && "cannot find a value for spirv.FunctionCall");
1110 operands.push_back(valueID);
1112
1113 if (!isa<NoneType>(resultTy))
1114 valueIDMap[op.getResult(0)] = funcCallID;
1115
1116 encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, operands);
1117 for (auto attr : op->getDiscardableAttrs())
1118 if (failed(processDecoration(op.getLoc(), funcCallID, attr)))
1119 return failure();
1120 return success();
1122
1123template <>
1124LogicalResult
1125Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
1127 SmallVector<StringRef, 2> elidedAttrs;
1128
1129 for (Value operand : op->getOperands()) {
1130 auto id = getValueID(operand);
1131 assert(id && "use before def!");
1132 operands.push_back(id);
1133 }
1134
1135 StringAttr memoryAccess = op.getMemoryAccessAttrName();
1136 if (std::optional<spirv::MemoryAccess> value = op.getMemoryAccess())
1137 operands.push_back(static_cast<uint32_t>(*value));
1138
1139 elidedAttrs.push_back(memoryAccess.strref());
1140
1141 StringAttr alignment = op.getAlignmentAttrName();
1142 if (std::optional<uint32_t> value = op.getAlignment())
1143 operands.push_back(*value);
1144
1145 elidedAttrs.push_back(alignment.strref());
1146
1147 StringAttr sourceMemoryAccess = op.getSourceMemoryAccessAttrName();
1148 if (std::optional<spirv::MemoryAccess> value = op.getSourceMemoryAccess())
1149 operands.push_back(static_cast<uint32_t>(*value));
1150
1151 elidedAttrs.push_back(sourceMemoryAccess.strref());
1152
1153 StringAttr sourceAlignment = op.getSourceAlignmentAttrName();
1154 if (std::optional<uint32_t> value = op.getSourceAlignment())
1155 operands.push_back(*value);
1156
1157 elidedAttrs.push_back(sourceAlignment.strref());
1158 if (failed(emitDebugLine(functionBody, op.getLoc())))
1159 return failure();
1160 encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, operands);
1161
1162 return success();
1163}
1164template <>
1165LogicalResult Serializer::processOp<spirv::GenericCastToPtrExplicitOp>(
1166 spirv::GenericCastToPtrExplicitOp op) {
1167 SmallVector<uint32_t, 4> operands;
1168 Type resultTy;
1169 Location loc = op->getLoc();
1170 uint32_t resultTypeID = 0;
1171 uint32_t resultID = 0;
1172 resultTy = op->getResult(0).getType();
1173 if (failed(processType(loc, resultTy, resultTypeID)))
1174 return failure();
1175 operands.push_back(resultTypeID);
1176
1177 resultID = getNextID();
1178 operands.push_back(resultID);
1179 valueIDMap[op->getResult(0)] = resultID;
1181 for (Value operand : op->getOperands())
1182 operands.push_back(getValueID(operand));
1183 spirv::StorageClass resultStorage =
1184 cast<spirv::PointerType>(resultTy).getStorageClass();
1185 operands.push_back(static_cast<uint32_t>(resultStorage));
1186 encodeInstructionInto(functionBody, spirv::Opcode::OpGenericCastToPtrExplicit,
1187 operands);
1188 return success();
1189}
1191// Pull in auto-generated Serializer::dispatchToAutogenSerialization() and
1192// various Serializer::processOp<...>() specializations.
1193#define GET_SERIALIZATION_FNS
1194#include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"
1196} // namespace spirv
1197} // 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:454
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:403
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
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
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