MLIR  22.0.0git
Serializer.cpp
Go to the documentation of this file.
1 //===- Serializer.cpp - MLIR SPIR-V Serializer ----------------------------===//
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 MLIR SPIR-V module to SPIR-V binary serializer.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "Serializer.h"
14 
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/Sequence.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/TypeSwitch.h"
25 #include "llvm/ADT/bit.h"
26 #include "llvm/Support/Debug.h"
27 #include <cstdint>
28 #include <optional>
29 
30 #define DEBUG_TYPE "spirv-serialization"
31 
32 using namespace mlir;
33 
34 /// Returns the merge block if the given `op` is a structured control flow op.
35 /// Otherwise returns nullptr.
37  if (auto selectionOp = dyn_cast<spirv::SelectionOp>(op))
38  return selectionOp.getMergeBlock();
39  if (auto loopOp = dyn_cast<spirv::LoopOp>(op))
40  return loopOp.getMergeBlock();
41  return nullptr;
42 }
43 
44 /// Given a predecessor `block` for a block with arguments, returns the block
45 /// that should be used as the parent block for SPIR-V OpPhi instructions
46 /// corresponding to the block arguments.
47 static Block *getPhiIncomingBlock(Block *block) {
48  // If the predecessor block in question is the entry block for a
49  // spirv.mlir.loop, we jump to this spirv.mlir.loop from its enclosing block.
50  if (block->isEntryBlock()) {
51  if (auto loopOp = dyn_cast<spirv::LoopOp>(block->getParentOp())) {
52  // Then the incoming parent block for OpPhi should be the merge block of
53  // the structured control flow op before this loop.
54  Operation *op = loopOp.getOperation();
55  while ((op = op->getPrevNode()) != nullptr)
56  if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(op))
57  return incomingBlock;
58  // Or the enclosing block itself if no structured control flow ops
59  // exists before this loop.
60  return loopOp->getBlock();
61  }
62  }
63 
64  // Otherwise, we jump from the given predecessor block. Try to see if there is
65  // a structured control flow op inside it.
66  for (Operation &op : llvm::reverse(block->getOperations())) {
67  if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(&op))
68  return incomingBlock;
69  }
70  return block;
71 }
72 
73 static bool isZeroValue(Attribute attr) {
74  if (auto floatAttr = dyn_cast<FloatAttr>(attr)) {
75  return floatAttr.getValue().isZero();
76  }
77  if (auto boolAttr = dyn_cast<BoolAttr>(attr)) {
78  return !boolAttr.getValue();
79  }
80  if (auto intAttr = dyn_cast<IntegerAttr>(attr)) {
81  return intAttr.getValue().isZero();
82  }
83  if (auto splatElemAttr = dyn_cast<SplatElementsAttr>(attr)) {
84  return isZeroValue(splatElemAttr.getSplatValue<Attribute>());
85  }
86  if (auto denseElemAttr = dyn_cast<DenseElementsAttr>(attr)) {
87  return all_of(denseElemAttr.getValues<Attribute>(), isZeroValue);
88  }
89  return false;
90 }
91 
92 namespace mlir {
93 namespace spirv {
94 
95 /// Encodes an SPIR-V instruction with the given `opcode` and `operands` into
96 /// the given `binary` vector.
97 void encodeInstructionInto(SmallVectorImpl<uint32_t> &binary, spirv::Opcode op,
98  ArrayRef<uint32_t> operands) {
99  uint32_t wordCount = 1 + operands.size();
100  binary.push_back(spirv::getPrefixedOpcode(wordCount, op));
101  binary.append(operands.begin(), operands.end());
102 }
103 
104 Serializer::Serializer(spirv::ModuleOp module,
106  : module(module), mlirBuilder(module.getContext()), options(options) {}
107 
108 LogicalResult Serializer::serialize() {
109  LLVM_DEBUG(llvm::dbgs() << "+++ starting serialization +++\n");
110 
111  if (failed(module.verifyInvariants()))
112  return failure();
113 
114  // TODO: handle the other sections
115  processCapability();
116  if (failed(processExtension())) {
117  return failure();
118  }
119  processMemoryModel();
120  processDebugInfo();
121 
122  // Iterate over the module body to serialize it. Assumptions are that there is
123  // only one basic block in the moduleOp
124  for (auto &op : *module.getBody()) {
125  if (failed(processOperation(&op))) {
126  return failure();
127  }
128  }
129 
130  LLVM_DEBUG(llvm::dbgs() << "+++ completed serialization +++\n");
131  return success();
132 }
133 
135  auto moduleSize = spirv::kHeaderWordCount + capabilities.size() +
136  extensions.size() + extendedSets.size() +
137  memoryModel.size() + entryPoints.size() +
138  executionModes.size() + decorations.size() +
139  typesGlobalValues.size() + functions.size() + graphs.size();
140 
141  binary.clear();
142  binary.reserve(moduleSize);
143 
144  spirv::appendModuleHeader(binary, module.getVceTriple()->getVersion(),
145  nextID);
146  binary.append(capabilities.begin(), capabilities.end());
147  binary.append(extensions.begin(), extensions.end());
148  binary.append(extendedSets.begin(), extendedSets.end());
149  binary.append(memoryModel.begin(), memoryModel.end());
150  binary.append(entryPoints.begin(), entryPoints.end());
151  binary.append(executionModes.begin(), executionModes.end());
152  binary.append(debug.begin(), debug.end());
153  binary.append(names.begin(), names.end());
154  binary.append(decorations.begin(), decorations.end());
155  binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
156  binary.append(functions.begin(), functions.end());
157  binary.append(graphs.begin(), graphs.end());
158 }
159 
160 #ifndef NDEBUG
161 void Serializer::printValueIDMap(raw_ostream &os) {
162  os << "\n= Value <id> Map =\n\n";
163  for (auto valueIDPair : valueIDMap) {
164  Value val = valueIDPair.first;
165  os << " " << val << " "
166  << "id = " << valueIDPair.second << ' ';
167  if (auto *op = val.getDefiningOp()) {
168  os << "from op '" << op->getName() << "'";
169  } else if (auto arg = dyn_cast<BlockArgument>(val)) {
170  Block *block = arg.getOwner();
171  os << "from argument of block " << block << ' ';
172  os << " in op '" << block->getParentOp()->getName() << "'";
173  }
174  os << '\n';
175  }
176 }
177 #endif
178 
179 //===----------------------------------------------------------------------===//
180 // Module structure
181 //===----------------------------------------------------------------------===//
182 
183 uint32_t Serializer::getOrCreateFunctionID(StringRef fnName) {
184  auto funcID = funcIDMap.lookup(fnName);
185  if (!funcID) {
186  funcID = getNextID();
187  funcIDMap[fnName] = funcID;
188  }
189  return funcID;
190 }
191 
192 void Serializer::processCapability() {
193  for (auto cap : module.getVceTriple()->getCapabilities())
194  encodeInstructionInto(capabilities, spirv::Opcode::OpCapability,
195  {static_cast<uint32_t>(cap)});
196 }
197 
198 void Serializer::processDebugInfo() {
199  if (!options.emitDebugInfo)
200  return;
201  auto fileLoc = dyn_cast<FileLineColLoc>(module.getLoc());
202  auto fileName = fileLoc ? fileLoc.getFilename().strref() : "<unknown>";
203  fileID = getNextID();
204  SmallVector<uint32_t, 16> operands;
205  operands.push_back(fileID);
206  spirv::encodeStringLiteralInto(operands, fileName);
207  encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
208  // TODO: Encode more debug instructions.
209 }
210 
211 LogicalResult Serializer::processExtension() {
213  llvm::SmallSet<Extension, 4> deducedExts(
214  llvm::from_range, module.getVceTriple()->getExtensions());
215  auto nonSemanticInfoExt = spirv::Extension::SPV_KHR_non_semantic_info;
216  if (options.emitDebugInfo && !deducedExts.contains(nonSemanticInfoExt)) {
217  TargetEnvAttr targetEnvAttr = lookupTargetEnvOrDefault(module);
218  if (!is_contained(targetEnvAttr.getExtensions(), nonSemanticInfoExt))
219  return module.emitError(
220  "SPV_KHR_non_semantic_info extension not available");
221  deducedExts.insert(nonSemanticInfoExt);
222  }
223  for (spirv::Extension ext : deducedExts) {
224  extName.clear();
225  spirv::encodeStringLiteralInto(extName, spirv::stringifyExtension(ext));
226  encodeInstructionInto(extensions, spirv::Opcode::OpExtension, extName);
227  }
228  return success();
229 }
230 
231 void Serializer::processMemoryModel() {
232  StringAttr memoryModelName = module.getMemoryModelAttrName();
233  auto mm = static_cast<uint32_t>(
234  module->getAttrOfType<spirv::MemoryModelAttr>(memoryModelName)
235  .getValue());
236 
237  StringAttr addressingModelName = module.getAddressingModelAttrName();
238  auto am = static_cast<uint32_t>(
239  module->getAttrOfType<spirv::AddressingModelAttr>(addressingModelName)
240  .getValue());
241 
242  encodeInstructionInto(memoryModel, spirv::Opcode::OpMemoryModel, {am, mm});
243 }
244 
245 static std::string getDecorationName(StringRef attrName) {
246  // convertToCamelFromSnakeCase will convert this to FpFastMathMode instead of
247  // expected FPFastMathMode.
248  if (attrName == "fp_fast_math_mode")
249  return "FPFastMathMode";
250  // similar here
251  if (attrName == "fp_rounding_mode")
252  return "FPRoundingMode";
253  // convertToCamelFromSnakeCase will not capitalize "INTEL".
254  if (attrName == "cache_control_load_intel")
255  return "CacheControlLoadINTEL";
256  if (attrName == "cache_control_store_intel")
257  return "CacheControlStoreINTEL";
258 
259  return llvm::convertToCamelFromSnakeCase(attrName, /*capitalizeFirst=*/true);
260 }
261 
262 template <typename AttrTy, typename EmitF>
263 LogicalResult processDecorationList(Location loc, Decoration decoration,
264  Attribute attrList, StringRef attrName,
265  EmitF emitter) {
266  auto arrayAttr = dyn_cast<ArrayAttr>(attrList);
267  if (!arrayAttr) {
268  return emitError(loc, "expecting array attribute of ")
269  << attrName << " for " << stringifyDecoration(decoration);
270  }
271  if (arrayAttr.empty()) {
272  return emitError(loc, "expecting non-empty array attribute of ")
273  << attrName << " for " << stringifyDecoration(decoration);
274  }
275  for (Attribute attr : arrayAttr.getValue()) {
276  auto cacheControlAttr = dyn_cast<AttrTy>(attr);
277  if (!cacheControlAttr) {
278  return emitError(loc, "expecting array attribute of ")
279  << attrName << " for " << stringifyDecoration(decoration);
280  }
281  // This named attribute encodes several decorations. Emit one per
282  // element in the array.
283  if (failed(emitter(cacheControlAttr)))
284  return failure();
285  }
286  return success();
287 }
288 
289 LogicalResult Serializer::processDecorationAttr(Location loc, uint32_t resultID,
290  Decoration decoration,
291  Attribute attr) {
293  switch (decoration) {
294  case spirv::Decoration::LinkageAttributes: {
295  // Get the value of the Linkage Attributes
296  // e.g., LinkageAttributes=["linkageName", linkageType].
297  auto linkageAttr = llvm::dyn_cast<spirv::LinkageAttributesAttr>(attr);
298  auto linkageName = linkageAttr.getLinkageName();
299  auto linkageType = linkageAttr.getLinkageType().getValue();
300  // Encode the Linkage Name (string literal to uint32_t).
301  spirv::encodeStringLiteralInto(args, linkageName);
302  // Encode LinkageType & Add the Linkagetype to the args.
303  args.push_back(static_cast<uint32_t>(linkageType));
304  break;
305  }
306  case spirv::Decoration::FPFastMathMode:
307  if (auto intAttr = dyn_cast<FPFastMathModeAttr>(attr)) {
308  args.push_back(static_cast<uint32_t>(intAttr.getValue()));
309  break;
310  }
311  return emitError(loc, "expected FPFastMathModeAttr attribute for ")
312  << stringifyDecoration(decoration);
313  case spirv::Decoration::FPRoundingMode:
314  if (auto intAttr = dyn_cast<FPRoundingModeAttr>(attr)) {
315  args.push_back(static_cast<uint32_t>(intAttr.getValue()));
316  break;
317  }
318  return emitError(loc, "expected FPRoundingModeAttr attribute for ")
319  << stringifyDecoration(decoration);
320  case spirv::Decoration::Binding:
321  case spirv::Decoration::DescriptorSet:
322  case spirv::Decoration::Location:
323  if (auto intAttr = dyn_cast<IntegerAttr>(attr)) {
324  args.push_back(intAttr.getValue().getZExtValue());
325  break;
326  }
327  return emitError(loc, "expected integer attribute for ")
328  << stringifyDecoration(decoration);
329  case spirv::Decoration::BuiltIn:
330  if (auto strAttr = dyn_cast<StringAttr>(attr)) {
331  auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
332  if (enumVal) {
333  args.push_back(static_cast<uint32_t>(*enumVal));
334  break;
335  }
336  return emitError(loc, "invalid ")
337  << stringifyDecoration(decoration) << " decoration attribute "
338  << strAttr.getValue();
339  }
340  return emitError(loc, "expected string attribute for ")
341  << stringifyDecoration(decoration);
342  case spirv::Decoration::Aliased:
343  case spirv::Decoration::AliasedPointer:
344  case spirv::Decoration::Flat:
345  case spirv::Decoration::NonReadable:
346  case spirv::Decoration::NonWritable:
347  case spirv::Decoration::NoPerspective:
348  case spirv::Decoration::NoSignedWrap:
349  case spirv::Decoration::NoUnsignedWrap:
350  case spirv::Decoration::RelaxedPrecision:
351  case spirv::Decoration::Restrict:
352  case spirv::Decoration::RestrictPointer:
353  case spirv::Decoration::NoContraction:
354  case spirv::Decoration::Constant:
355  case spirv::Decoration::Block:
356  case spirv::Decoration::Invariant:
357  case spirv::Decoration::Patch:
358  // For unit attributes and decoration attributes, the args list
359  // has no values so we do nothing.
360  if (isa<UnitAttr, DecorationAttr>(attr))
361  break;
362  return emitError(loc,
363  "expected unit attribute or decoration attribute for ")
364  << stringifyDecoration(decoration);
365  case spirv::Decoration::CacheControlLoadINTEL:
366  return processDecorationList<CacheControlLoadINTELAttr>(
367  loc, decoration, attr, "CacheControlLoadINTEL",
368  [&](CacheControlLoadINTELAttr attr) {
369  unsigned cacheLevel = attr.getCacheLevel();
370  LoadCacheControl loadCacheControl = attr.getLoadCacheControl();
371  return emitDecoration(
372  resultID, decoration,
373  {cacheLevel, static_cast<uint32_t>(loadCacheControl)});
374  });
375  case spirv::Decoration::CacheControlStoreINTEL:
376  return processDecorationList<CacheControlStoreINTELAttr>(
377  loc, decoration, attr, "CacheControlStoreINTEL",
378  [&](CacheControlStoreINTELAttr attr) {
379  unsigned cacheLevel = attr.getCacheLevel();
380  StoreCacheControl storeCacheControl = attr.getStoreCacheControl();
381  return emitDecoration(
382  resultID, decoration,
383  {cacheLevel, static_cast<uint32_t>(storeCacheControl)});
384  });
385  default:
386  return emitError(loc, "unhandled decoration ")
387  << stringifyDecoration(decoration);
388  }
389  return emitDecoration(resultID, decoration, args);
390 }
391 
392 LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
393  NamedAttribute attr) {
394  StringRef attrName = attr.getName().strref();
395  std::string decorationName = getDecorationName(attrName);
396  std::optional<Decoration> decoration =
397  spirv::symbolizeDecoration(decorationName);
398  if (!decoration) {
399  return emitError(
400  loc, "non-argument attributes expected to have snake-case-ified "
401  "decoration name, unhandled attribute with name : ")
402  << attrName;
403  }
404  return processDecorationAttr(loc, resultID, *decoration, attr.getValue());
405 }
406 
407 LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
408  assert(!name.empty() && "unexpected empty string for OpName");
409  if (!options.emitSymbolName)
410  return success();
411 
412  SmallVector<uint32_t, 4> nameOperands;
413  nameOperands.push_back(resultID);
414  spirv::encodeStringLiteralInto(nameOperands, name);
415  encodeInstructionInto(names, spirv::Opcode::OpName, nameOperands);
416  return success();
417 }
418 
419 template <>
420 LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
421  Location loc, spirv::ArrayType type, uint32_t resultID) {
422  if (unsigned stride = type.getArrayStride()) {
423  // OpDecorate %arrayTypeSSA ArrayStride strideLiteral
424  return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
425  }
426  return success();
427 }
428 
429 template <>
430 LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
431  Location loc, spirv::RuntimeArrayType type, uint32_t resultID) {
432  if (unsigned stride = type.getArrayStride()) {
433  // OpDecorate %arrayTypeSSA ArrayStride strideLiteral
434  return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
435  }
436  return success();
437 }
438 
439 LogicalResult Serializer::processMemberDecoration(
440  uint32_t structID,
441  const spirv::StructType::MemberDecorationInfo &memberDecoration) {
443  {structID, memberDecoration.memberIndex,
444  static_cast<uint32_t>(memberDecoration.decoration)});
445  if (memberDecoration.hasValue()) {
446  args.push_back(
447  cast<IntegerAttr>(memberDecoration.decorationValue).getInt());
448  }
449  encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate, args);
450  return success();
451 }
452 
453 //===----------------------------------------------------------------------===//
454 // Type
455 //===----------------------------------------------------------------------===//
456 
457 // According to the SPIR-V spec "Validation Rules for Shader Capabilities":
458 // "Composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform, and
459 // PushConstant Storage Classes must be explicitly laid out."
460 bool Serializer::isInterfaceStructPtrType(Type type) const {
461  if (auto ptrType = dyn_cast<spirv::PointerType>(type)) {
462  switch (ptrType.getStorageClass()) {
463  case spirv::StorageClass::PhysicalStorageBuffer:
464  case spirv::StorageClass::PushConstant:
465  case spirv::StorageClass::StorageBuffer:
466  case spirv::StorageClass::Uniform:
467  return isa<spirv::StructType>(ptrType.getPointeeType());
468  default:
469  break;
470  }
471  }
472  return false;
473 }
474 
475 LogicalResult Serializer::processType(Location loc, Type type,
476  uint32_t &typeID) {
477  // Maintains a set of names for nested identified struct types. This is used
478  // to properly serialize recursive references.
479  SetVector<StringRef> serializationCtx;
480  return processTypeImpl(loc, type, typeID, serializationCtx);
481 }
482 
483 LogicalResult
484 Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
485  SetVector<StringRef> &serializationCtx) {
486 
487  // Map unsigned integer types to singless integer types.
488  // This is needed otherwise the generated spirv assembly will contain
489  // twice a type declaration (like OpTypeInt 32 0) which is no permitted and
490  // such module fails validation. Indeed at MLIR level the two types are
491  // different and lookup in the cache below misses.
492  // Note: This conversion needs to happen here before the type is looked up in
493  // the cache.
494  if (type.isUnsignedInteger()) {
495  type = IntegerType::get(loc->getContext(), type.getIntOrFloatBitWidth(),
496  IntegerType::SignednessSemantics::Signless);
497  }
498 
499  typeID = getTypeID(type);
500  if (typeID)
501  return success();
502 
503  typeID = getNextID();
504  SmallVector<uint32_t, 4> operands;
505 
506  operands.push_back(typeID);
507  auto typeEnum = spirv::Opcode::OpTypeVoid;
508  bool deferSerialization = false;
509 
510  if ((isa<FunctionType>(type) &&
511  succeeded(prepareFunctionType(loc, cast<FunctionType>(type), typeEnum,
512  operands))) ||
513  (isa<GraphType>(type) &&
514  succeeded(
515  prepareGraphType(loc, cast<GraphType>(type), typeEnum, operands))) ||
516  succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
517  deferSerialization, serializationCtx))) {
518  if (deferSerialization)
519  return success();
520 
521  typeIDMap[type] = typeID;
522 
523  encodeInstructionInto(typesGlobalValues, typeEnum, operands);
524 
525  if (recursiveStructInfos.count(type) != 0) {
526  // This recursive struct type is emitted already, now the OpTypePointer
527  // instructions referring to recursive references are emitted as well.
528  for (auto &ptrInfo : recursiveStructInfos[type]) {
529  // TODO: This might not work if more than 1 recursive reference is
530  // present in the struct.
531  SmallVector<uint32_t, 4> ptrOperands;
532  ptrOperands.push_back(ptrInfo.pointerTypeID);
533  ptrOperands.push_back(static_cast<uint32_t>(ptrInfo.storageClass));
534  ptrOperands.push_back(typeIDMap[type]);
535 
536  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpTypePointer,
537  ptrOperands);
538  }
539 
540  recursiveStructInfos[type].clear();
541  }
542 
543  return success();
544  }
545 
546  return emitError(loc, "failed to process type: ") << type;
547 }
548 
549 LogicalResult Serializer::prepareBasicType(
550  Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
551  SmallVectorImpl<uint32_t> &operands, bool &deferSerialization,
552  SetVector<StringRef> &serializationCtx) {
553  deferSerialization = false;
554 
555  if (isVoidType(type)) {
556  typeEnum = spirv::Opcode::OpTypeVoid;
557  return success();
558  }
559 
560  if (auto intType = dyn_cast<IntegerType>(type)) {
561  if (intType.getWidth() == 1) {
562  typeEnum = spirv::Opcode::OpTypeBool;
563  return success();
564  }
565 
566  typeEnum = spirv::Opcode::OpTypeInt;
567  operands.push_back(intType.getWidth());
568  // SPIR-V OpTypeInt "Signedness specifies whether there are signed semantics
569  // to preserve or validate.
570  // 0 indicates unsigned, or no signedness semantics
571  // 1 indicates signed semantics."
572  operands.push_back(intType.isSigned() ? 1 : 0);
573  return success();
574  }
575 
576  if (auto floatType = dyn_cast<FloatType>(type)) {
577  typeEnum = spirv::Opcode::OpTypeFloat;
578  operands.push_back(floatType.getWidth());
579  if (floatType.isBF16()) {
580  operands.push_back(static_cast<uint32_t>(spirv::FPEncoding::BFloat16KHR));
581  }
582  return success();
583  }
584 
585  if (auto vectorType = dyn_cast<VectorType>(type)) {
586  uint32_t elementTypeID = 0;
587  if (failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
588  serializationCtx))) {
589  return failure();
590  }
591  typeEnum = spirv::Opcode::OpTypeVector;
592  operands.push_back(elementTypeID);
593  operands.push_back(vectorType.getNumElements());
594  return success();
595  }
596 
597  if (auto imageType = dyn_cast<spirv::ImageType>(type)) {
598  typeEnum = spirv::Opcode::OpTypeImage;
599  uint32_t sampledTypeID = 0;
600  if (failed(processType(loc, imageType.getElementType(), sampledTypeID)))
601  return failure();
602 
603  llvm::append_values(operands, sampledTypeID,
604  static_cast<uint32_t>(imageType.getDim()),
605  static_cast<uint32_t>(imageType.getDepthInfo()),
606  static_cast<uint32_t>(imageType.getArrayedInfo()),
607  static_cast<uint32_t>(imageType.getSamplingInfo()),
608  static_cast<uint32_t>(imageType.getSamplerUseInfo()),
609  static_cast<uint32_t>(imageType.getImageFormat()));
610  return success();
611  }
612 
613  if (auto arrayType = dyn_cast<spirv::ArrayType>(type)) {
614  typeEnum = spirv::Opcode::OpTypeArray;
615  uint32_t elementTypeID = 0;
616  if (failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
617  serializationCtx))) {
618  return failure();
619  }
620  operands.push_back(elementTypeID);
621  if (auto elementCountID = prepareConstantInt(
622  loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
623  operands.push_back(elementCountID);
624  }
625  return processTypeDecoration(loc, arrayType, resultID);
626  }
627 
628  if (auto ptrType = dyn_cast<spirv::PointerType>(type)) {
629  uint32_t pointeeTypeID = 0;
630  spirv::StructType pointeeStruct =
631  dyn_cast<spirv::StructType>(ptrType.getPointeeType());
632 
633  if (pointeeStruct && pointeeStruct.isIdentified() &&
634  serializationCtx.count(pointeeStruct.getIdentifier()) != 0) {
635  // A recursive reference to an enclosing struct is found.
636  //
637  // 1. Prepare an OpTypeForwardPointer with resultID and the ptr storage
638  // class as operands.
639  SmallVector<uint32_t, 2> forwardPtrOperands;
640  forwardPtrOperands.push_back(resultID);
641  forwardPtrOperands.push_back(
642  static_cast<uint32_t>(ptrType.getStorageClass()));
643 
644  encodeInstructionInto(typesGlobalValues,
645  spirv::Opcode::OpTypeForwardPointer,
646  forwardPtrOperands);
647 
648  // 2. Find the pointee (enclosing) struct.
649  auto structType = spirv::StructType::getIdentified(
650  module.getContext(), pointeeStruct.getIdentifier());
651 
652  if (!structType)
653  return failure();
654 
655  // 3. Mark the OpTypePointer that is supposed to be emitted by this call
656  // as deferred.
657  deferSerialization = true;
658 
659  // 4. Record the info needed to emit the deferred OpTypePointer
660  // instruction when the enclosing struct is completely serialized.
661  recursiveStructInfos[structType].push_back(
662  {resultID, ptrType.getStorageClass()});
663  } else {
664  if (failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
665  serializationCtx)))
666  return failure();
667  }
668 
669  typeEnum = spirv::Opcode::OpTypePointer;
670  operands.push_back(static_cast<uint32_t>(ptrType.getStorageClass()));
671  operands.push_back(pointeeTypeID);
672 
673  // TODO: Now struct decorations are supported this code may not be
674  // necessary. However, it is left to support backwards compatibility.
675  // Ideally, Block decorations should be inserted when converting to SPIR-V.
676  if (isInterfaceStructPtrType(ptrType)) {
677  auto structType = cast<spirv::StructType>(ptrType.getPointeeType());
678  if (!structType.hasDecoration(spirv::Decoration::Block))
679  if (failed(emitDecoration(getTypeID(pointeeStruct),
680  spirv::Decoration::Block)))
681  return emitError(loc, "cannot decorate ")
682  << pointeeStruct << " with Block decoration";
683  }
684 
685  return success();
686  }
687 
688  if (auto runtimeArrayType = dyn_cast<spirv::RuntimeArrayType>(type)) {
689  uint32_t elementTypeID = 0;
690  if (failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
691  elementTypeID, serializationCtx))) {
692  return failure();
693  }
694  typeEnum = spirv::Opcode::OpTypeRuntimeArray;
695  operands.push_back(elementTypeID);
696  return processTypeDecoration(loc, runtimeArrayType, resultID);
697  }
698 
699  if (auto sampledImageType = dyn_cast<spirv::SampledImageType>(type)) {
700  typeEnum = spirv::Opcode::OpTypeSampledImage;
701  uint32_t imageTypeID = 0;
702  if (failed(
703  processType(loc, sampledImageType.getImageType(), imageTypeID))) {
704  return failure();
705  }
706  operands.push_back(imageTypeID);
707  return success();
708  }
709 
710  if (auto structType = dyn_cast<spirv::StructType>(type)) {
711  if (structType.isIdentified()) {
712  if (failed(processName(resultID, structType.getIdentifier())))
713  return failure();
714  serializationCtx.insert(structType.getIdentifier());
715  }
716 
717  bool hasOffset = structType.hasOffset();
718  for (auto elementIndex :
719  llvm::seq<uint32_t>(0, structType.getNumElements())) {
720  uint32_t elementTypeID = 0;
721  if (failed(processTypeImpl(loc, structType.getElementType(elementIndex),
722  elementTypeID, serializationCtx))) {
723  return failure();
724  }
725  operands.push_back(elementTypeID);
726  if (hasOffset) {
727  auto intType = IntegerType::get(structType.getContext(), 32);
728  // Decorate each struct member with an offset
730  elementIndex, spirv::Decoration::Offset,
731  IntegerAttr::get(intType,
732  structType.getMemberOffset(elementIndex))};
733  if (failed(processMemberDecoration(resultID, offsetDecoration))) {
734  return emitError(loc, "cannot decorate ")
735  << elementIndex << "-th member of " << structType
736  << " with its offset";
737  }
738  }
739  }
741  structType.getMemberDecorations(memberDecorations);
742 
743  for (auto &memberDecoration : memberDecorations) {
744  if (failed(processMemberDecoration(resultID, memberDecoration))) {
745  return emitError(loc, "cannot decorate ")
746  << static_cast<uint32_t>(memberDecoration.memberIndex)
747  << "-th member of " << structType << " with "
748  << stringifyDecoration(memberDecoration.decoration);
749  }
750  }
751 
753  structType.getStructDecorations(structDecorations);
754 
755  for (spirv::StructType::StructDecorationInfo &structDecoration :
756  structDecorations) {
757  if (failed(processDecorationAttr(loc, resultID,
758  structDecoration.decoration,
759  structDecoration.decorationValue))) {
760  return emitError(loc, "cannot decorate struct ")
761  << structType << " with "
762  << stringifyDecoration(structDecoration.decoration);
763  }
764  }
765 
766  typeEnum = spirv::Opcode::OpTypeStruct;
767 
768  if (structType.isIdentified())
769  serializationCtx.remove(structType.getIdentifier());
770 
771  return success();
772  }
773 
774  if (auto cooperativeMatrixType =
775  dyn_cast<spirv::CooperativeMatrixType>(type)) {
776  uint32_t elementTypeID = 0;
777  if (failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
778  elementTypeID, serializationCtx))) {
779  return failure();
780  }
781  typeEnum = spirv::Opcode::OpTypeCooperativeMatrixKHR;
782  auto getConstantOp = [&](uint32_t id) {
783  auto attr = IntegerAttr::get(IntegerType::get(type.getContext(), 32), id);
784  return prepareConstantInt(loc, attr);
785  };
786  llvm::append_values(
787  operands, elementTypeID,
788  getConstantOp(static_cast<uint32_t>(cooperativeMatrixType.getScope())),
789  getConstantOp(cooperativeMatrixType.getRows()),
790  getConstantOp(cooperativeMatrixType.getColumns()),
791  getConstantOp(static_cast<uint32_t>(cooperativeMatrixType.getUse())));
792  return success();
793  }
794 
795  if (auto matrixType = dyn_cast<spirv::MatrixType>(type)) {
796  uint32_t elementTypeID = 0;
797  if (failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
798  serializationCtx))) {
799  return failure();
800  }
801  typeEnum = spirv::Opcode::OpTypeMatrix;
802  llvm::append_values(operands, elementTypeID, matrixType.getNumColumns());
803  return success();
804  }
805 
806  if (auto tensorArmType = llvm::dyn_cast<TensorArmType>(type)) {
807  uint32_t elementTypeID = 0;
808  uint32_t rank = 0;
809  uint32_t shapeID = 0;
810  uint32_t rankID = 0;
811  if (failed(processTypeImpl(loc, tensorArmType.getElementType(),
812  elementTypeID, serializationCtx))) {
813  return failure();
814  }
815  if (tensorArmType.hasRank()) {
816  ArrayRef<int64_t> dims = tensorArmType.getShape();
817  rank = dims.size();
818  rankID = prepareConstantInt(loc, mlirBuilder.getI32IntegerAttr(rank));
819  if (rankID == 0) {
820  return failure();
821  }
822 
823  bool shaped = llvm::all_of(dims, [](const auto &dim) { return dim > 0; });
824  if (rank > 0 && shaped) {
825  auto I32Type = IntegerType::get(type.getContext(), 32);
826  auto shapeType = ArrayType::get(I32Type, rank);
827  if (rank == 1) {
828  SmallVector<uint64_t, 1> index(rank);
829  shapeID = prepareDenseElementsConstant(
830  loc, shapeType,
831  mlirBuilder.getI32TensorAttr(SmallVector<int32_t>(dims)), 0,
832  index);
833  } else {
834  shapeID = prepareArrayConstant(
835  loc, shapeType,
836  mlirBuilder.getI32ArrayAttr(SmallVector<int32_t>(dims)));
837  }
838  if (shapeID == 0) {
839  return failure();
840  }
841  }
842  }
843  typeEnum = spirv::Opcode::OpTypeTensorARM;
844  operands.push_back(elementTypeID);
845  if (rankID == 0)
846  return success();
847  operands.push_back(rankID);
848  if (shapeID == 0)
849  return success();
850  operands.push_back(shapeID);
851  return success();
852  }
853 
854  // TODO: Handle other types.
855  return emitError(loc, "unhandled type in serialization: ") << type;
856 }
857 
858 LogicalResult
859 Serializer::prepareFunctionType(Location loc, FunctionType type,
860  spirv::Opcode &typeEnum,
861  SmallVectorImpl<uint32_t> &operands) {
862  typeEnum = spirv::Opcode::OpTypeFunction;
863  assert(type.getNumResults() <= 1 &&
864  "serialization supports only a single return value");
865  uint32_t resultID = 0;
866  if (failed(processType(
867  loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
868  resultID))) {
869  return failure();
870  }
871  operands.push_back(resultID);
872  for (auto &res : type.getInputs()) {
873  uint32_t argTypeID = 0;
874  if (failed(processType(loc, res, argTypeID))) {
875  return failure();
876  }
877  operands.push_back(argTypeID);
878  }
879  return success();
880 }
881 
882 LogicalResult
883 Serializer::prepareGraphType(Location loc, GraphType type,
884  spirv::Opcode &typeEnum,
885  SmallVectorImpl<uint32_t> &operands) {
886  typeEnum = spirv::Opcode::OpTypeGraphARM;
887  assert(type.getNumResults() >= 1 &&
888  "serialization requires at least a return value");
889 
890  operands.push_back(type.getNumInputs());
891 
892  for (Type argType : type.getInputs()) {
893  uint32_t argTypeID = 0;
894  if (failed(processType(loc, argType, argTypeID)))
895  return failure();
896  operands.push_back(argTypeID);
897  }
898 
899  for (Type resType : type.getResults()) {
900  uint32_t resTypeID = 0;
901  if (failed(processType(loc, resType, resTypeID)))
902  return failure();
903  operands.push_back(resTypeID);
904  }
905 
906  return success();
907 }
908 
909 //===----------------------------------------------------------------------===//
910 // Constant
911 //===----------------------------------------------------------------------===//
912 
913 uint32_t Serializer::prepareConstant(Location loc, Type constType,
914  Attribute valueAttr) {
915  if (auto id = prepareConstantScalar(loc, valueAttr)) {
916  return id;
917  }
918 
919  // This is a composite literal. We need to handle each component separately
920  // and then emit an OpConstantComposite for the whole.
921 
922  if (auto id = getConstantID(valueAttr)) {
923  return id;
924  }
925 
926  uint32_t typeID = 0;
927  if (failed(processType(loc, constType, typeID))) {
928  return 0;
929  }
930 
931  uint32_t resultID = 0;
932  if (auto attr = dyn_cast<DenseElementsAttr>(valueAttr)) {
933  int rank = dyn_cast<ShapedType>(attr.getType()).getRank();
934  SmallVector<uint64_t, 4> index(rank);
935  resultID = prepareDenseElementsConstant(loc, constType, attr,
936  /*dim=*/0, index);
937  } else if (auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
938  resultID = prepareArrayConstant(loc, constType, arrayAttr);
939  }
940 
941  if (resultID == 0) {
942  emitError(loc, "cannot serialize attribute: ") << valueAttr;
943  return 0;
944  }
945 
946  constIDMap[valueAttr] = resultID;
947  return resultID;
948 }
949 
950 uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
951  ArrayAttr attr) {
952  uint32_t typeID = 0;
953  if (failed(processType(loc, constType, typeID))) {
954  return 0;
955  }
956 
957  uint32_t resultID = getNextID();
958  SmallVector<uint32_t, 4> operands = {typeID, resultID};
959  operands.reserve(attr.size() + 2);
960  auto elementType = cast<spirv::ArrayType>(constType).getElementType();
961  for (Attribute elementAttr : attr) {
962  if (auto elementID = prepareConstant(loc, elementType, elementAttr)) {
963  operands.push_back(elementID);
964  } else {
965  return 0;
966  }
967  }
968  spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
969  encodeInstructionInto(typesGlobalValues, opcode, operands);
970 
971  return resultID;
972 }
973 
974 // TODO: Turn the below function into iterative function, instead of
975 // recursive function.
976 uint32_t
977 Serializer::prepareDenseElementsConstant(Location loc, Type constType,
978  DenseElementsAttr valueAttr, int dim,
980  auto shapedType = dyn_cast<ShapedType>(valueAttr.getType());
981  assert(dim <= shapedType.getRank());
982  if (shapedType.getRank() == dim) {
983  if (auto attr = dyn_cast<DenseIntElementsAttr>(valueAttr)) {
984  return attr.getType().getElementType().isInteger(1)
985  ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
986  : prepareConstantInt(loc,
987  attr.getValues<IntegerAttr>()[index]);
988  }
989  if (auto attr = dyn_cast<DenseFPElementsAttr>(valueAttr)) {
990  return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
991  }
992  return 0;
993  }
994 
995  uint32_t typeID = 0;
996  if (failed(processType(loc, constType, typeID))) {
997  return 0;
998  }
999 
1000  int64_t numberOfConstituents = shapedType.getDimSize(dim);
1001  uint32_t resultID = getNextID();
1002  SmallVector<uint32_t, 4> operands = {typeID, resultID};
1003  auto elementType = cast<spirv::CompositeType>(constType).getElementType(0);
1004  if (auto tensorArmType = dyn_cast<spirv::TensorArmType>(constType)) {
1005  ArrayRef<int64_t> innerShape = tensorArmType.getShape().drop_front();
1006  if (!innerShape.empty())
1007  elementType = spirv::TensorArmType::get(innerShape, elementType);
1008  }
1009 
1010  // "If the Result Type is a cooperative matrix type, then there must be only
1011  // one Constituent, with scalar type matching the cooperative matrix Component
1012  // Type, and all components of the matrix are initialized to that value."
1013  // (https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_cooperative_matrix.html)
1014  if (isa<spirv::CooperativeMatrixType>(constType)) {
1015  if (!valueAttr.isSplat()) {
1016  emitError(
1017  loc,
1018  "cannot serialize a non-splat value for a cooperative matrix type");
1019  return 0;
1020  }
1021  // numberOfConstituents is 1, so we only need one more elements in the
1022  // SmallVector, so the total is 3 (1 + 2).
1023  operands.reserve(3);
1024  // We set dim directly to `shapedType.getRank()` so the recursive call
1025  // directly returns the scalar type.
1026  if (auto elementID = prepareDenseElementsConstant(
1027  loc, elementType, valueAttr, /*dim=*/shapedType.getRank(), index)) {
1028  operands.push_back(elementID);
1029  } else {
1030  return 0;
1031  }
1032  } else if (isa<spirv::TensorArmType>(constType) && isZeroValue(valueAttr)) {
1033  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpConstantNull,
1034  {typeID, resultID});
1035  return resultID;
1036  } else {
1037  operands.reserve(numberOfConstituents + 2);
1038  for (int i = 0; i < numberOfConstituents; ++i) {
1039  index[dim] = i;
1040  if (auto elementID = prepareDenseElementsConstant(
1041  loc, elementType, valueAttr, dim + 1, index)) {
1042  operands.push_back(elementID);
1043  } else {
1044  return 0;
1045  }
1046  }
1047  }
1048  spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1049  encodeInstructionInto(typesGlobalValues, opcode, operands);
1050 
1051  return resultID;
1052 }
1053 
1054 uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
1055  bool isSpec) {
1056  if (auto floatAttr = dyn_cast<FloatAttr>(valueAttr)) {
1057  return prepareConstantFp(loc, floatAttr, isSpec);
1058  }
1059  if (auto boolAttr = dyn_cast<BoolAttr>(valueAttr)) {
1060  return prepareConstantBool(loc, boolAttr, isSpec);
1061  }
1062  if (auto intAttr = dyn_cast<IntegerAttr>(valueAttr)) {
1063  return prepareConstantInt(loc, intAttr, isSpec);
1064  }
1065 
1066  return 0;
1067 }
1068 
1069 uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
1070  bool isSpec) {
1071  if (!isSpec) {
1072  // We can de-duplicate normal constants, but not specialization constants.
1073  if (auto id = getConstantID(boolAttr)) {
1074  return id;
1075  }
1076  }
1077 
1078  // Process the type for this bool literal
1079  uint32_t typeID = 0;
1080  if (failed(processType(loc, cast<IntegerAttr>(boolAttr).getType(), typeID))) {
1081  return 0;
1082  }
1083 
1084  auto resultID = getNextID();
1085  auto opcode = boolAttr.getValue()
1086  ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
1087  : spirv::Opcode::OpConstantTrue)
1088  : (isSpec ? spirv::Opcode::OpSpecConstantFalse
1089  : spirv::Opcode::OpConstantFalse);
1090  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID});
1091 
1092  if (!isSpec) {
1093  constIDMap[boolAttr] = resultID;
1094  }
1095  return resultID;
1096 }
1097 
1098 uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
1099  bool isSpec) {
1100  if (!isSpec) {
1101  // We can de-duplicate normal constants, but not specialization constants.
1102  if (auto id = getConstantID(intAttr)) {
1103  return id;
1104  }
1105  }
1106 
1107  // Process the type for this integer literal
1108  uint32_t typeID = 0;
1109  if (failed(processType(loc, intAttr.getType(), typeID))) {
1110  return 0;
1111  }
1112 
1113  auto resultID = getNextID();
1114  APInt value = intAttr.getValue();
1115  unsigned bitwidth = value.getBitWidth();
1116  bool isSigned = intAttr.getType().isSignedInteger();
1117  auto opcode =
1118  isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1119 
1120  switch (bitwidth) {
1121  // According to SPIR-V spec, "When the type's bit width is less than
1122  // 32-bits, the literal's value appears in the low-order bits of the word,
1123  // and the high-order bits must be 0 for a floating-point type, or 0 for an
1124  // integer type with Signedness of 0, or sign extended when Signedness
1125  // is 1."
1126  case 32:
1127  case 16:
1128  case 8: {
1129  uint32_t word = 0;
1130  if (isSigned) {
1131  word = static_cast<int32_t>(value.getSExtValue());
1132  } else {
1133  word = static_cast<uint32_t>(value.getZExtValue());
1134  }
1135  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
1136  } break;
1137  // According to SPIR-V spec: "When the type's bit width is larger than one
1138  // word, the literal’s low-order words appear first."
1139  case 64: {
1140  struct DoubleWord {
1141  uint32_t word1;
1142  uint32_t word2;
1143  } words;
1144  if (isSigned) {
1145  words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
1146  } else {
1147  words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
1148  }
1149  encodeInstructionInto(typesGlobalValues, opcode,
1150  {typeID, resultID, words.word1, words.word2});
1151  } break;
1152  default: {
1153  std::string valueStr;
1154  llvm::raw_string_ostream rss(valueStr);
1155  value.print(rss, /*isSigned=*/false);
1156 
1157  emitError(loc, "cannot serialize ")
1158  << bitwidth << "-bit integer literal: " << valueStr;
1159  return 0;
1160  }
1161  }
1162 
1163  if (!isSpec) {
1164  constIDMap[intAttr] = resultID;
1165  }
1166  return resultID;
1167 }
1168 
1169 uint32_t Serializer::prepareGraphConstantId(Location loc, Type graphConstType,
1170  IntegerAttr intAttr) {
1171  // De-duplicate graph constants.
1172  if (uint32_t id = getGraphConstantARMId(intAttr)) {
1173  return id;
1174  }
1175 
1176  // Process the type for this graph constant.
1177  uint32_t typeID = 0;
1178  if (failed(processType(loc, graphConstType, typeID))) {
1179  return 0;
1180  }
1181 
1182  uint32_t resultID = getNextID();
1183  APInt value = intAttr.getValue();
1184  unsigned bitwidth = value.getBitWidth();
1185  if (bitwidth > 32) {
1186  emitError(loc, "Too wide attribute for OpGraphConstantARM: ")
1187  << bitwidth << " bits";
1188  return 0;
1189  }
1190  bool isSigned = value.isSignedIntN(bitwidth);
1191 
1192  uint32_t word = 0;
1193  if (isSigned) {
1194  word = static_cast<int32_t>(value.getSExtValue());
1195  } else {
1196  word = static_cast<uint32_t>(value.getZExtValue());
1197  }
1198  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpGraphConstantARM,
1199  {typeID, resultID, word});
1200  graphConstIDMap[intAttr] = resultID;
1201  return resultID;
1202 }
1203 
1204 uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
1205  bool isSpec) {
1206  if (!isSpec) {
1207  // We can de-duplicate normal constants, but not specialization constants.
1208  if (auto id = getConstantID(floatAttr)) {
1209  return id;
1210  }
1211  }
1212 
1213  // Process the type for this float literal
1214  uint32_t typeID = 0;
1215  if (failed(processType(loc, floatAttr.getType(), typeID))) {
1216  return 0;
1217  }
1218 
1219  auto resultID = getNextID();
1220  APFloat value = floatAttr.getValue();
1221  const llvm::fltSemantics *semantics = &value.getSemantics();
1222 
1223  auto opcode =
1224  isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1225 
1226  if (semantics == &APFloat::IEEEsingle()) {
1227  uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
1228  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
1229  } else if (semantics == &APFloat::IEEEdouble()) {
1230  struct DoubleWord {
1231  uint32_t word1;
1232  uint32_t word2;
1233  } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
1234  encodeInstructionInto(typesGlobalValues, opcode,
1235  {typeID, resultID, words.word1, words.word2});
1236  } else if (semantics == &APFloat::IEEEhalf() ||
1237  semantics == &APFloat::BFloat()) {
1238  uint32_t word =
1239  static_cast<uint32_t>(value.bitcastToAPInt().getZExtValue());
1240  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
1241  } else {
1242  std::string valueStr;
1243  llvm::raw_string_ostream rss(valueStr);
1244  value.print(rss);
1245 
1246  emitError(loc, "cannot serialize ")
1247  << floatAttr.getType() << "-typed float literal: " << valueStr;
1248  return 0;
1249  }
1250 
1251  if (!isSpec) {
1252  constIDMap[floatAttr] = resultID;
1253  }
1254  return resultID;
1255 }
1256 
1257 // Returns type of attribute. In case of a TypedAttr this will simply return
1258 // the type. But for an ArrayAttr which is untyped and can be multidimensional
1259 // it creates the ArrayType recursively.
1261  if (auto typedAttr = dyn_cast<TypedAttr>(attr)) {
1262  return typedAttr.getType();
1263  }
1264 
1265  if (auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
1266  return spirv::ArrayType::get(getValueType(arrayAttr[0]), arrayAttr.size());
1267  }
1268 
1269  return nullptr;
1270 }
1271 
1272 uint32_t Serializer::prepareConstantCompositeReplicate(Location loc,
1273  Type resultType,
1274  Attribute valueAttr) {
1275  std::pair<Attribute, Type> valueTypePair{valueAttr, resultType};
1276  if (uint32_t id = getConstantCompositeReplicateID(valueTypePair)) {
1277  return id;
1278  }
1279 
1280  uint32_t typeID = 0;
1281  if (failed(processType(loc, resultType, typeID))) {
1282  return 0;
1283  }
1284 
1285  Type valueType = getValueType(valueAttr);
1286  if (!valueAttr)
1287  return 0;
1288 
1289  auto compositeType = dyn_cast<CompositeType>(resultType);
1290  if (!compositeType)
1291  return 0;
1292  Type elementType = compositeType.getElementType(0);
1293 
1294  uint32_t constandID;
1295  if (elementType == valueType) {
1296  constandID = prepareConstant(loc, elementType, valueAttr);
1297  } else {
1298  constandID = prepareConstantCompositeReplicate(loc, elementType, valueAttr);
1299  }
1300 
1301  uint32_t resultID = getNextID();
1302  if (dyn_cast<spirv::TensorArmType>(resultType) && isZeroValue(valueAttr)) {
1303  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpConstantNull,
1304  {typeID, resultID});
1305  } else {
1306  encodeInstructionInto(typesGlobalValues,
1307  spirv::Opcode::OpConstantCompositeReplicateEXT,
1308  {typeID, resultID, constandID});
1309  }
1310 
1311  constCompositeReplicateIDMap[valueTypePair] = resultID;
1312  return resultID;
1313 }
1314 
1315 //===----------------------------------------------------------------------===//
1316 // Control flow
1317 //===----------------------------------------------------------------------===//
1318 
1319 uint32_t Serializer::getOrCreateBlockID(Block *block) {
1320  if (uint32_t id = getBlockID(block))
1321  return id;
1322  return blockIDMap[block] = getNextID();
1323 }
1324 
1325 #ifndef NDEBUG
1326 void Serializer::printBlock(Block *block, raw_ostream &os) {
1327  os << "block " << block << " (id = ";
1328  if (uint32_t id = getBlockID(block))
1329  os << id;
1330  else
1331  os << "unknown";
1332  os << ")\n";
1333 }
1334 #endif
1335 
1336 LogicalResult
1337 Serializer::processBlock(Block *block, bool omitLabel,
1338  function_ref<LogicalResult()> emitMerge) {
1339  LLVM_DEBUG(llvm::dbgs() << "processing block " << block << ":\n");
1340  LLVM_DEBUG(block->print(llvm::dbgs()));
1341  LLVM_DEBUG(llvm::dbgs() << '\n');
1342  if (!omitLabel) {
1343  uint32_t blockID = getOrCreateBlockID(block);
1344  LLVM_DEBUG(printBlock(block, llvm::dbgs()));
1345 
1346  // Emit OpLabel for this block.
1347  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
1348  }
1349 
1350  // Emit OpPhi instructions for block arguments, if any.
1351  if (failed(emitPhiForBlockArguments(block)))
1352  return failure();
1353 
1354  // If we need to emit merge instructions, it must happen in this block. Check
1355  // whether we have other structured control flow ops, which will be expanded
1356  // into multiple basic blocks. If that's the case, we need to emit the merge
1357  // right now and then create new blocks for further serialization of the ops
1358  // in this block.
1359  if (emitMerge &&
1360  llvm::any_of(block->getOperations(),
1361  llvm::IsaPred<spirv::LoopOp, spirv::SelectionOp>)) {
1362  if (failed(emitMerge()))
1363  return failure();
1364  emitMerge = nullptr;
1365 
1366  // Start a new block for further serialization.
1367  uint32_t blockID = getNextID();
1368  encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {blockID});
1369  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
1370  }
1371 
1372  // Process each op in this block except the terminator.
1373  for (Operation &op : llvm::drop_end(*block)) {
1374  if (failed(processOperation(&op)))
1375  return failure();
1376  }
1377 
1378  // Process the terminator.
1379  if (emitMerge)
1380  if (failed(emitMerge()))
1381  return failure();
1382  if (failed(processOperation(&block->back())))
1383  return failure();
1384 
1385  return success();
1386 }
1387 
1388 LogicalResult Serializer::emitPhiForBlockArguments(Block *block) {
1389  // Nothing to do if this block has no arguments or it's the entry block, which
1390  // always has the same arguments as the function signature.
1391  if (block->args_empty() || block->isEntryBlock())
1392  return success();
1393 
1394  LLVM_DEBUG(llvm::dbgs() << "emitting phi instructions..\n");
1395 
1396  // If the block has arguments, we need to create SPIR-V OpPhi instructions.
1397  // A SPIR-V OpPhi instruction is of the syntax:
1398  // OpPhi | result type | result <id> | (value <id>, parent block <id>) pair
1399  // So we need to collect all predecessor blocks and the arguments they send
1400  // to this block.
1402  for (Block *mlirPredecessor : block->getPredecessors()) {
1403  auto *terminator = mlirPredecessor->getTerminator();
1404  LLVM_DEBUG(llvm::dbgs() << " mlir predecessor ");
1405  LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1406  LLVM_DEBUG(llvm::dbgs() << " terminator: " << *terminator << "\n");
1407  // The predecessor here is the immediate one according to MLIR's IR
1408  // structure. It does not directly map to the incoming parent block for the
1409  // OpPhi instructions at SPIR-V binary level. This is because structured
1410  // control flow ops are serialized to multiple SPIR-V blocks. If there is a
1411  // spirv.mlir.selection/spirv.mlir.loop op in the MLIR predecessor block,
1412  // the branch op jumping to the OpPhi's block then resides in the previous
1413  // structured control flow op's merge block.
1414  Block *spirvPredecessor = getPhiIncomingBlock(mlirPredecessor);
1415  LLVM_DEBUG(llvm::dbgs() << " spirv predecessor ");
1416  LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1417  if (auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1418  predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1419  } else if (auto branchCondOp =
1420  dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1421  std::optional<OperandRange> blockOperands;
1422  if (branchCondOp.getTrueTarget() == block) {
1423  blockOperands = branchCondOp.getTrueTargetOperands();
1424  } else {
1425  assert(branchCondOp.getFalseTarget() == block);
1426  blockOperands = branchCondOp.getFalseTargetOperands();
1427  }
1428 
1429  assert(!blockOperands->empty() &&
1430  "expected non-empty block operand range");
1431  predecessors.emplace_back(spirvPredecessor, *blockOperands);
1432  } else {
1433  return terminator->emitError("unimplemented terminator for Phi creation");
1434  }
1435  LLVM_DEBUG({
1436  llvm::dbgs() << " block arguments:\n";
1437  for (Value v : predecessors.back().second)
1438  llvm::dbgs() << " " << v << "\n";
1439  });
1440  }
1441 
1442  // Then create OpPhi instruction for each of the block argument.
1443  for (auto argIndex : llvm::seq<unsigned>(0, block->getNumArguments())) {
1444  BlockArgument arg = block->getArgument(argIndex);
1445 
1446  // Get the type <id> and result <id> for this OpPhi instruction.
1447  uint32_t phiTypeID = 0;
1448  if (failed(processType(arg.getLoc(), arg.getType(), phiTypeID)))
1449  return failure();
1450  uint32_t phiID = getNextID();
1451 
1452  LLVM_DEBUG(llvm::dbgs() << "[phi] for block argument #" << argIndex << ' '
1453  << arg << " (id = " << phiID << ")\n");
1454 
1455  // Prepare the (value <id>, parent block <id>) pairs.
1456  SmallVector<uint32_t, 8> phiArgs;
1457  phiArgs.push_back(phiTypeID);
1458  phiArgs.push_back(phiID);
1459 
1460  for (auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1461  Value value = predecessors[predIndex].second[argIndex];
1462  uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1463  LLVM_DEBUG(llvm::dbgs() << "[phi] use predecessor (id = " << predBlockId
1464  << ") value " << value << ' ');
1465  // Each pair is a value <id> ...
1466  uint32_t valueId = getValueID(value);
1467  if (valueId == 0) {
1468  // The op generating this value hasn't been visited yet so we don't have
1469  // an <id> assigned yet. Record this to fix up later.
1470  LLVM_DEBUG(llvm::dbgs() << "(need to fix)\n");
1471  deferredPhiValues[value].push_back(functionBody.size() + 1 +
1472  phiArgs.size());
1473  } else {
1474  LLVM_DEBUG(llvm::dbgs() << "(id = " << valueId << ")\n");
1475  }
1476  phiArgs.push_back(valueId);
1477  // ... and a parent block <id>.
1478  phiArgs.push_back(predBlockId);
1479  }
1480 
1481  encodeInstructionInto(functionBody, spirv::Opcode::OpPhi, phiArgs);
1482  valueIDMap[arg] = phiID;
1483  }
1484 
1485  return success();
1486 }
1487 
1488 //===----------------------------------------------------------------------===//
1489 // Operation
1490 //===----------------------------------------------------------------------===//
1491 
1492 LogicalResult Serializer::encodeExtensionInstruction(
1493  Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1494  ArrayRef<uint32_t> operands) {
1495  // Check if the extension has been imported.
1496  auto &setID = extendedInstSetIDMap[extensionSetName];
1497  if (!setID) {
1498  setID = getNextID();
1499  SmallVector<uint32_t, 16> importOperands;
1500  importOperands.push_back(setID);
1501  spirv::encodeStringLiteralInto(importOperands, extensionSetName);
1502  encodeInstructionInto(extendedSets, spirv::Opcode::OpExtInstImport,
1503  importOperands);
1504  }
1505 
1506  // The first two operands are the result type <id> and result <id>. The set
1507  // <id> and the opcode need to be insert after this.
1508  if (operands.size() < 2) {
1509  return op->emitError("extended instructions must have a result encoding");
1510  }
1511  SmallVector<uint32_t, 8> extInstOperands;
1512  extInstOperands.reserve(operands.size() + 2);
1513  extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1514  extInstOperands.push_back(setID);
1515  extInstOperands.push_back(extensionOpcode);
1516  extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1517  encodeInstructionInto(functionBody, spirv::Opcode::OpExtInst,
1518  extInstOperands);
1519  return success();
1520 }
1521 
1522 LogicalResult Serializer::processOperation(Operation *opInst) {
1523  LLVM_DEBUG(llvm::dbgs() << "[op] '" << opInst->getName() << "'\n");
1524 
1525  // First dispatch the ops that do not directly mirror an instruction from
1526  // the SPIR-V spec.
1528  .Case([&](spirv::AddressOfOp op) { return processAddressOfOp(op); })
1529  .Case([&](spirv::BranchOp op) { return processBranchOp(op); })
1530  .Case([&](spirv::BranchConditionalOp op) {
1531  return processBranchConditionalOp(op);
1532  })
1533  .Case([&](spirv::ConstantOp op) { return processConstantOp(op); })
1534  .Case([&](spirv::EXTConstantCompositeReplicateOp op) {
1535  return processConstantCompositeReplicateOp(op);
1536  })
1537  .Case([&](spirv::FuncOp op) { return processFuncOp(op); })
1538  .Case([&](spirv::GraphARMOp op) { return processGraphARMOp(op); })
1539  .Case([&](spirv::GraphEntryPointARMOp op) {
1540  return processGraphEntryPointARMOp(op);
1541  })
1542  .Case([&](spirv::GraphOutputsARMOp op) {
1543  return processGraphOutputsARMOp(op);
1544  })
1545  .Case([&](spirv::GlobalVariableOp op) {
1546  return processGlobalVariableOp(op);
1547  })
1548  .Case([&](spirv::GraphConstantARMOp op) {
1549  return processGraphConstantARMOp(op);
1550  })
1551  .Case([&](spirv::LoopOp op) { return processLoopOp(op); })
1552  .Case([&](spirv::ReferenceOfOp op) { return processReferenceOfOp(op); })
1553  .Case([&](spirv::SelectionOp op) { return processSelectionOp(op); })
1554  .Case([&](spirv::SpecConstantOp op) { return processSpecConstantOp(op); })
1555  .Case([&](spirv::SpecConstantCompositeOp op) {
1556  return processSpecConstantCompositeOp(op);
1557  })
1558  .Case([&](spirv::EXTSpecConstantCompositeReplicateOp op) {
1559  return processSpecConstantCompositeReplicateOp(op);
1560  })
1561  .Case([&](spirv::SpecConstantOperationOp op) {
1562  return processSpecConstantOperationOp(op);
1563  })
1564  .Case([&](spirv::UndefOp op) { return processUndefOp(op); })
1565  .Case([&](spirv::VariableOp op) { return processVariableOp(op); })
1566 
1567  // Then handle all the ops that directly mirror SPIR-V instructions with
1568  // auto-generated methods.
1569  .Default(
1570  [&](Operation *op) { return dispatchToAutogenSerialization(op); });
1571 }
1572 
1573 LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1574  StringRef extInstSet,
1575  uint32_t opcode) {
1576  SmallVector<uint32_t, 4> operands;
1577  Location loc = op->getLoc();
1578 
1579  uint32_t resultID = 0;
1580  if (op->getNumResults() != 0) {
1581  uint32_t resultTypeID = 0;
1582  if (failed(processType(loc, op->getResult(0).getType(), resultTypeID)))
1583  return failure();
1584  operands.push_back(resultTypeID);
1585 
1586  resultID = getNextID();
1587  operands.push_back(resultID);
1588  valueIDMap[op->getResult(0)] = resultID;
1589  };
1590 
1591  for (Value operand : op->getOperands())
1592  operands.push_back(getValueID(operand));
1593 
1594  if (failed(emitDebugLine(functionBody, loc)))
1595  return failure();
1596 
1597  if (extInstSet.empty()) {
1598  encodeInstructionInto(functionBody, static_cast<spirv::Opcode>(opcode),
1599  operands);
1600  } else {
1601  if (failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1602  return failure();
1603  }
1604 
1605  if (op->getNumResults() != 0) {
1606  for (auto attr : op->getAttrs()) {
1607  if (failed(processDecoration(loc, resultID, attr)))
1608  return failure();
1609  }
1610  }
1611 
1612  return success();
1613 }
1614 
1615 LogicalResult Serializer::emitDecoration(uint32_t target,
1616  spirv::Decoration decoration,
1617  ArrayRef<uint32_t> params) {
1618  uint32_t wordCount = 3 + params.size();
1619  llvm::append_values(
1620  decorations,
1621  spirv::getPrefixedOpcode(wordCount, spirv::Opcode::OpDecorate), target,
1622  static_cast<uint32_t>(decoration));
1623  llvm::append_range(decorations, params);
1624  return success();
1625 }
1626 
1627 LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1628  Location loc) {
1629  if (!options.emitDebugInfo)
1630  return success();
1631 
1632  if (lastProcessedWasMergeInst) {
1633  lastProcessedWasMergeInst = false;
1634  return success();
1635  }
1636 
1637  auto fileLoc = dyn_cast<FileLineColLoc>(loc);
1638  if (fileLoc)
1639  encodeInstructionInto(binary, spirv::Opcode::OpLine,
1640  {fileID, fileLoc.getLine(), fileLoc.getColumn()});
1641  return success();
1642 }
1643 } // namespace spirv
1644 } // namespace mlir
static MLIRContext * getContext(OpFoldResult val)
static llvm::ManagedStatic< PassManagerOptions > options
static bool isZeroValue(Attribute attr)
Definition: Serializer.cpp:73
static Block * getStructuredControlFlowOpMergeBlock(Operation *op)
Returns the merge block if the given op is a structured control flow op.
Definition: Serializer.cpp:36
static Block * getPhiIncomingBlock(Block *block)
Given a predecessor block for a block with arguments, returns the block that should be used as the pa...
Definition: Serializer.cpp:47
Attributes are known-constant values of operations.
Definition: Attributes.h:25
MLIRContext * getContext() const
Return the context this attribute belongs to.
Definition: Attributes.cpp:37
This class represents an argument of a Block.
Definition: Value.h:309
Location getLoc() const
Return the location for this argument.
Definition: Value.h:324
Block represents an ordered list of Operations.
Definition: Block.h:33
BlockArgument getArgument(unsigned i)
Definition: Block.h:129
unsigned getNumArguments()
Definition: Block.h:128
Operation & back()
Definition: Block.h:152
iterator_range< pred_iterator > getPredecessors()
Definition: Block.h:240
OpListType & getOperations()
Definition: Block.h:137
void print(raw_ostream &os)
bool args_empty()
Definition: Block.h:99
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition: Block.cpp:36
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:31
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
bool getValue() const
Return the boolean value of this attribute.
IntegerAttr getI32IntegerAttr(int32_t value)
Definition: Builders.cpp:199
ArrayAttr getI32ArrayAttr(ArrayRef< int32_t > values)
Definition: Builders.cpp:275
DenseIntElementsAttr getI32TensorAttr(ArrayRef< int32_t > values)
Tensor-typed DenseIntElementsAttr getters.
Definition: Builders.cpp:178
An attribute that represents a reference to a dense vector or tensor object.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
NamedAttribute represents a combination of a name and an Attribute value.
Definition: Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
Definition: Attributes.cpp:55
Attribute getValue() const
Return the value of the attribute.
Definition: Attributes.h:179
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:407
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition: Operation.h:512
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:267
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:119
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:378
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:404
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:35
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
Definition: Types.cpp:88
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition: Types.cpp:122
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
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:18
unsigned getArrayStride() const
Returns the array stride in bytes.
Definition: SPIRVTypes.cpp:174
static ArrayType get(Type elementType, unsigned elementCount)
Definition: SPIRVTypes.cpp:158
unsigned getArrayStride() const
Returns the array stride in bytes.
Definition: SPIRVTypes.cpp:522
void printValueIDMap(raw_ostream &os)
(For debugging) prints each value and its corresponding result <id>.
Definition: Serializer.cpp:161
Serializer(spirv::ModuleOp module, const SerializationOptions &options)
Creates a serializer for the given SPIR-V module.
Definition: Serializer.cpp:104
LogicalResult serialize()
Serializes the remembered SPIR-V module.
Definition: Serializer.cpp:108
void collect(SmallVectorImpl< uint32_t > &binary)
Collects the final SPIR-V binary.
Definition: Serializer.cpp:134
SPIR-V struct type.
Definition: SPIRVTypes.h:251
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
bool isIdentified() const
Returns true if the StructType is identified.
StringRef getIdentifier() const
For literal structs, return an empty string.
static TensorArmType get(ArrayRef< int64_t > shape, Type elementType)
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
static Type getValueType(Attribute attr)
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
TargetEnvAttr lookupTargetEnvOrDefault(Operation *op)
Queries the target environment recursively from enclosing symbol table ops containing the given op or...
LogicalResult processDecorationList(Location loc, Decoration decoration, Attribute attrList, StringRef attrName, EmitF emitter)
Definition: Serializer.cpp:263
uint32_t getPrefixedOpcode(uint32_t wordCount, spirv::Opcode opcode)
Returns the word-count-prefixed opcode for an SPIR-V instruction.
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.
Definition: Serializer.cpp:97
void appendModuleHeader(SmallVectorImpl< uint32_t > &header, spirv::Version version, uint32_t idBound)
Appends a SPRI-V module header to header with the given version and idBound.
constexpr unsigned kHeaderWordCount
SPIR-V binary header word count.
static std::string getDecorationName(StringRef attrName)
Definition: Serializer.cpp:245
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition: Utils.cpp:304
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool emitSymbolName
Whether to emit OpName instructions for SPIR-V symbol ops.
Definition: Serialization.h:28
bool emitDebugInfo
Whether to emit OpLine location information for SPIR-V ops.
Definition: Serialization.h:30