MLIR  14.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 
20 #include "llvm/ADT/Sequence.h"
21 #include "llvm/ADT/SmallPtrSet.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/ADT/TypeSwitch.h"
24 #include "llvm/ADT/bit.h"
25 #include "llvm/Support/Debug.h"
26 
27 #define DEBUG_TYPE "spirv-serialization"
28 
29 using namespace mlir;
30 
31 /// Returns the merge block if the given `op` is a structured control flow op.
32 /// Otherwise returns nullptr.
34  if (auto selectionOp = dyn_cast<spirv::SelectionOp>(op))
35  return selectionOp.getMergeBlock();
36  if (auto loopOp = dyn_cast<spirv::LoopOp>(op))
37  return loopOp.getMergeBlock();
38  return nullptr;
39 }
40 
41 /// Given a predecessor `block` for a block with arguments, returns the block
42 /// that should be used as the parent block for SPIR-V OpPhi instructions
43 /// corresponding to the block arguments.
44 static Block *getPhiIncomingBlock(Block *block) {
45  // If the predecessor block in question is the entry block for a
46  // spv.mlir.loop, we jump to this spv.mlir.loop from its enclosing block.
47  if (block->isEntryBlock()) {
48  if (auto loopOp = dyn_cast<spirv::LoopOp>(block->getParentOp())) {
49  // Then the incoming parent block for OpPhi should be the merge block of
50  // the structured control flow op before this loop.
51  Operation *op = loopOp.getOperation();
52  while ((op = op->getPrevNode()) != nullptr)
53  if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(op))
54  return incomingBlock;
55  // Or the enclosing block itself if no structured control flow ops
56  // exists before this loop.
57  return loopOp->getBlock();
58  }
59  }
60 
61  // Otherwise, we jump from the given predecessor block. Try to see if there is
62  // a structured control flow op inside it.
63  for (Operation &op : llvm::reverse(block->getOperations())) {
64  if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(&op))
65  return incomingBlock;
66  }
67  return block;
68 }
69 
70 namespace mlir {
71 namespace spirv {
72 
73 /// Encodes an SPIR-V instruction with the given `opcode` and `operands` into
74 /// the given `binary` vector.
75 void encodeInstructionInto(SmallVectorImpl<uint32_t> &binary, spirv::Opcode op,
76  ArrayRef<uint32_t> operands) {
77  uint32_t wordCount = 1 + operands.size();
78  binary.push_back(spirv::getPrefixedOpcode(wordCount, op));
79  binary.append(operands.begin(), operands.end());
80 }
81 
82 Serializer::Serializer(spirv::ModuleOp module,
84  : module(module), mlirBuilder(module.getContext()), options(options) {}
85 
87  LLVM_DEBUG(llvm::dbgs() << "+++ starting serialization +++\n");
88 
89  if (failed(module.verify()))
90  return failure();
91 
92  // TODO: handle the other sections
93  processCapability();
94  processExtension();
95  processMemoryModel();
96  processDebugInfo();
97 
98  // Iterate over the module body to serialize it. Assumptions are that there is
99  // only one basic block in the moduleOp
100  for (auto &op : *module.getBody()) {
101  if (failed(processOperation(&op))) {
102  return failure();
103  }
104  }
105 
106  LLVM_DEBUG(llvm::dbgs() << "+++ completed serialization +++\n");
107  return success();
108 }
109 
111  auto moduleSize = spirv::kHeaderWordCount + capabilities.size() +
112  extensions.size() + extendedSets.size() +
113  memoryModel.size() + entryPoints.size() +
114  executionModes.size() + decorations.size() +
115  typesGlobalValues.size() + functions.size();
116 
117  binary.clear();
118  binary.reserve(moduleSize);
119 
120  spirv::appendModuleHeader(binary, module.vce_triple()->getVersion(), nextID);
121  binary.append(capabilities.begin(), capabilities.end());
122  binary.append(extensions.begin(), extensions.end());
123  binary.append(extendedSets.begin(), extendedSets.end());
124  binary.append(memoryModel.begin(), memoryModel.end());
125  binary.append(entryPoints.begin(), entryPoints.end());
126  binary.append(executionModes.begin(), executionModes.end());
127  binary.append(debug.begin(), debug.end());
128  binary.append(names.begin(), names.end());
129  binary.append(decorations.begin(), decorations.end());
130  binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
131  binary.append(functions.begin(), functions.end());
132 }
133 
134 #ifndef NDEBUG
135 void Serializer::printValueIDMap(raw_ostream &os) {
136  os << "\n= Value <id> Map =\n\n";
137  for (auto valueIDPair : valueIDMap) {
138  Value val = valueIDPair.first;
139  os << " " << val << " "
140  << "id = " << valueIDPair.second << ' ';
141  if (auto *op = val.getDefiningOp()) {
142  os << "from op '" << op->getName() << "'";
143  } else if (auto arg = val.dyn_cast<BlockArgument>()) {
144  Block *block = arg.getOwner();
145  os << "from argument of block " << block << ' ';
146  os << " in op '" << block->getParentOp()->getName() << "'";
147  }
148  os << '\n';
149  }
150 }
151 #endif
152 
153 //===----------------------------------------------------------------------===//
154 // Module structure
155 //===----------------------------------------------------------------------===//
156 
157 uint32_t Serializer::getOrCreateFunctionID(StringRef fnName) {
158  auto funcID = funcIDMap.lookup(fnName);
159  if (!funcID) {
160  funcID = getNextID();
161  funcIDMap[fnName] = funcID;
162  }
163  return funcID;
164 }
165 
166 void Serializer::processCapability() {
167  for (auto cap : module.vce_triple()->getCapabilities())
168  encodeInstructionInto(capabilities, spirv::Opcode::OpCapability,
169  {static_cast<uint32_t>(cap)});
170 }
171 
172 void Serializer::processDebugInfo() {
173  if (!options.emitDebugInfo)
174  return;
175  auto fileLoc = module.getLoc().dyn_cast<FileLineColLoc>();
176  auto fileName = fileLoc ? fileLoc.getFilename().strref() : "<unknown>";
177  fileID = getNextID();
178  SmallVector<uint32_t, 16> operands;
179  operands.push_back(fileID);
180  spirv::encodeStringLiteralInto(operands, fileName);
181  encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
182  // TODO: Encode more debug instructions.
183 }
184 
185 void Serializer::processExtension() {
187  for (spirv::Extension ext : module.vce_triple()->getExtensions()) {
188  extName.clear();
189  spirv::encodeStringLiteralInto(extName, spirv::stringifyExtension(ext));
190  encodeInstructionInto(extensions, spirv::Opcode::OpExtension, extName);
191  }
192 }
193 
194 void Serializer::processMemoryModel() {
195  uint32_t mm = module->getAttrOfType<IntegerAttr>("memory_model").getInt();
196  uint32_t am = module->getAttrOfType<IntegerAttr>("addressing_model").getInt();
197 
198  encodeInstructionInto(memoryModel, spirv::Opcode::OpMemoryModel, {am, mm});
199 }
200 
201 LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
202  NamedAttribute attr) {
203  auto attrName = attr.getName().strref();
204  auto decorationName = llvm::convertToCamelFromSnakeCase(attrName, true);
205  auto decoration = spirv::symbolizeDecoration(decorationName);
206  if (!decoration) {
207  return emitError(
208  loc, "non-argument attributes expected to have snake-case-ified "
209  "decoration name, unhandled attribute with name : ")
210  << attrName;
211  }
213  switch (decoration.getValue()) {
214  case spirv::Decoration::Binding:
215  case spirv::Decoration::DescriptorSet:
216  case spirv::Decoration::Location:
217  if (auto intAttr = attr.getValue().dyn_cast<IntegerAttr>()) {
218  args.push_back(intAttr.getValue().getZExtValue());
219  break;
220  }
221  return emitError(loc, "expected integer attribute for ") << attrName;
222  case spirv::Decoration::BuiltIn:
223  if (auto strAttr = attr.getValue().dyn_cast<StringAttr>()) {
224  auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
225  if (enumVal) {
226  args.push_back(static_cast<uint32_t>(enumVal.getValue()));
227  break;
228  }
229  return emitError(loc, "invalid ")
230  << attrName << " attribute " << strAttr.getValue();
231  }
232  return emitError(loc, "expected string attribute for ") << attrName;
233  case spirv::Decoration::Aliased:
234  case spirv::Decoration::Flat:
235  case spirv::Decoration::NonReadable:
236  case spirv::Decoration::NonWritable:
237  case spirv::Decoration::NoPerspective:
238  case spirv::Decoration::Restrict:
239  case spirv::Decoration::RelaxedPrecision:
240  // For unit attributes, the args list has no values so we do nothing
241  if (auto unitAttr = attr.getValue().dyn_cast<UnitAttr>())
242  break;
243  return emitError(loc, "expected unit attribute for ") << attrName;
244  default:
245  return emitError(loc, "unhandled decoration ") << decorationName;
246  }
247  return emitDecoration(resultID, decoration.getValue(), args);
248 }
249 
250 LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
251  assert(!name.empty() && "unexpected empty string for OpName");
252  if (!options.emitSymbolName)
253  return success();
254 
255  SmallVector<uint32_t, 4> nameOperands;
256  nameOperands.push_back(resultID);
257  spirv::encodeStringLiteralInto(nameOperands, name);
258  encodeInstructionInto(names, spirv::Opcode::OpName, nameOperands);
259  return success();
260 }
261 
262 template <>
263 LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
264  Location loc, spirv::ArrayType type, uint32_t resultID) {
265  if (unsigned stride = type.getArrayStride()) {
266  // OpDecorate %arrayTypeSSA ArrayStride strideLiteral
267  return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
268  }
269  return success();
270 }
271 
272 template <>
273 LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
274  Location loc, spirv::RuntimeArrayType type, uint32_t resultID) {
275  if (unsigned stride = type.getArrayStride()) {
276  // OpDecorate %arrayTypeSSA ArrayStride strideLiteral
277  return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
278  }
279  return success();
280 }
281 
282 LogicalResult Serializer::processMemberDecoration(
283  uint32_t structID,
284  const spirv::StructType::MemberDecorationInfo &memberDecoration) {
286  {structID, memberDecoration.memberIndex,
287  static_cast<uint32_t>(memberDecoration.decoration)});
288  if (memberDecoration.hasValue) {
289  args.push_back(memberDecoration.decorationValue);
290  }
291  encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate, args);
292  return success();
293 }
294 
295 //===----------------------------------------------------------------------===//
296 // Type
297 //===----------------------------------------------------------------------===//
298 
299 // According to the SPIR-V spec "Validation Rules for Shader Capabilities":
300 // "Composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform, and
301 // PushConstant Storage Classes must be explicitly laid out."
302 bool Serializer::isInterfaceStructPtrType(Type type) const {
303  if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
304  switch (ptrType.getStorageClass()) {
305  case spirv::StorageClass::PhysicalStorageBuffer:
306  case spirv::StorageClass::PushConstant:
307  case spirv::StorageClass::StorageBuffer:
308  case spirv::StorageClass::Uniform:
309  return ptrType.getPointeeType().isa<spirv::StructType>();
310  default:
311  break;
312  }
313  }
314  return false;
315 }
316 
317 LogicalResult Serializer::processType(Location loc, Type type,
318  uint32_t &typeID) {
319  // Maintains a set of names for nested identified struct types. This is used
320  // to properly serialize recursive references.
321  SetVector<StringRef> serializationCtx;
322  return processTypeImpl(loc, type, typeID, serializationCtx);
323 }
324 
326 Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
327  SetVector<StringRef> &serializationCtx) {
328  typeID = getTypeID(type);
329  if (typeID)
330  return success();
331 
332  typeID = getNextID();
333  SmallVector<uint32_t, 4> operands;
334 
335  operands.push_back(typeID);
336  auto typeEnum = spirv::Opcode::OpTypeVoid;
337  bool deferSerialization = false;
338 
339  if ((type.isa<FunctionType>() &&
340  succeeded(prepareFunctionType(loc, type.cast<FunctionType>(), typeEnum,
341  operands))) ||
342  succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
343  deferSerialization, serializationCtx))) {
344  if (deferSerialization)
345  return success();
346 
347  typeIDMap[type] = typeID;
348 
349  encodeInstructionInto(typesGlobalValues, typeEnum, operands);
350 
351  if (recursiveStructInfos.count(type) != 0) {
352  // This recursive struct type is emitted already, now the OpTypePointer
353  // instructions referring to recursive references are emitted as well.
354  for (auto &ptrInfo : recursiveStructInfos[type]) {
355  // TODO: This might not work if more than 1 recursive reference is
356  // present in the struct.
357  SmallVector<uint32_t, 4> ptrOperands;
358  ptrOperands.push_back(ptrInfo.pointerTypeID);
359  ptrOperands.push_back(static_cast<uint32_t>(ptrInfo.storageClass));
360  ptrOperands.push_back(typeIDMap[type]);
361 
362  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpTypePointer,
363  ptrOperands);
364  }
365 
366  recursiveStructInfos[type].clear();
367  }
368 
369  return success();
370  }
371 
372  return failure();
373 }
374 
375 LogicalResult Serializer::prepareBasicType(
376  Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
377  SmallVectorImpl<uint32_t> &operands, bool &deferSerialization,
378  SetVector<StringRef> &serializationCtx) {
379  deferSerialization = false;
380 
381  if (isVoidType(type)) {
382  typeEnum = spirv::Opcode::OpTypeVoid;
383  return success();
384  }
385 
386  if (auto intType = type.dyn_cast<IntegerType>()) {
387  if (intType.getWidth() == 1) {
388  typeEnum = spirv::Opcode::OpTypeBool;
389  return success();
390  }
391 
392  typeEnum = spirv::Opcode::OpTypeInt;
393  operands.push_back(intType.getWidth());
394  // SPIR-V OpTypeInt "Signedness specifies whether there are signed semantics
395  // to preserve or validate.
396  // 0 indicates unsigned, or no signedness semantics
397  // 1 indicates signed semantics."
398  operands.push_back(intType.isSigned() ? 1 : 0);
399  return success();
400  }
401 
402  if (auto floatType = type.dyn_cast<FloatType>()) {
403  typeEnum = spirv::Opcode::OpTypeFloat;
404  operands.push_back(floatType.getWidth());
405  return success();
406  }
407 
408  if (auto vectorType = type.dyn_cast<VectorType>()) {
409  uint32_t elementTypeID = 0;
410  if (failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
411  serializationCtx))) {
412  return failure();
413  }
414  typeEnum = spirv::Opcode::OpTypeVector;
415  operands.push_back(elementTypeID);
416  operands.push_back(vectorType.getNumElements());
417  return success();
418  }
419 
420  if (auto imageType = type.dyn_cast<spirv::ImageType>()) {
421  typeEnum = spirv::Opcode::OpTypeImage;
422  uint32_t sampledTypeID = 0;
423  if (failed(processType(loc, imageType.getElementType(), sampledTypeID)))
424  return failure();
425 
426  operands.push_back(sampledTypeID);
427  operands.push_back(static_cast<uint32_t>(imageType.getDim()));
428  operands.push_back(static_cast<uint32_t>(imageType.getDepthInfo()));
429  operands.push_back(static_cast<uint32_t>(imageType.getArrayedInfo()));
430  operands.push_back(static_cast<uint32_t>(imageType.getSamplingInfo()));
431  operands.push_back(static_cast<uint32_t>(imageType.getSamplerUseInfo()));
432  operands.push_back(static_cast<uint32_t>(imageType.getImageFormat()));
433  return success();
434  }
435 
436  if (auto arrayType = type.dyn_cast<spirv::ArrayType>()) {
437  typeEnum = spirv::Opcode::OpTypeArray;
438  uint32_t elementTypeID = 0;
439  if (failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
440  serializationCtx))) {
441  return failure();
442  }
443  operands.push_back(elementTypeID);
444  if (auto elementCountID = prepareConstantInt(
445  loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
446  operands.push_back(elementCountID);
447  }
448  return processTypeDecoration(loc, arrayType, resultID);
449  }
450 
451  if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
452  uint32_t pointeeTypeID = 0;
453  spirv::StructType pointeeStruct =
454  ptrType.getPointeeType().dyn_cast<spirv::StructType>();
455 
456  if (pointeeStruct && pointeeStruct.isIdentified() &&
457  serializationCtx.count(pointeeStruct.getIdentifier()) != 0) {
458  // A recursive reference to an enclosing struct is found.
459  //
460  // 1. Prepare an OpTypeForwardPointer with resultID and the ptr storage
461  // class as operands.
462  SmallVector<uint32_t, 2> forwardPtrOperands;
463  forwardPtrOperands.push_back(resultID);
464  forwardPtrOperands.push_back(
465  static_cast<uint32_t>(ptrType.getStorageClass()));
466 
467  encodeInstructionInto(typesGlobalValues,
468  spirv::Opcode::OpTypeForwardPointer,
469  forwardPtrOperands);
470 
471  // 2. Find the pointee (enclosing) struct.
472  auto structType = spirv::StructType::getIdentified(
473  module.getContext(), pointeeStruct.getIdentifier());
474 
475  if (!structType)
476  return failure();
477 
478  // 3. Mark the OpTypePointer that is supposed to be emitted by this call
479  // as deferred.
480  deferSerialization = true;
481 
482  // 4. Record the info needed to emit the deferred OpTypePointer
483  // instruction when the enclosing struct is completely serialized.
484  recursiveStructInfos[structType].push_back(
485  {resultID, ptrType.getStorageClass()});
486  } else {
487  if (failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
488  serializationCtx)))
489  return failure();
490  }
491 
492  typeEnum = spirv::Opcode::OpTypePointer;
493  operands.push_back(static_cast<uint32_t>(ptrType.getStorageClass()));
494  operands.push_back(pointeeTypeID);
495 
496  if (isInterfaceStructPtrType(ptrType)) {
497  if (failed(emitDecoration(getTypeID(pointeeStruct),
498  spirv::Decoration::Block)))
499  return emitError(loc, "cannot decorate ")
500  << pointeeStruct << " with Block decoration";
501  }
502 
503  return success();
504  }
505 
506  if (auto runtimeArrayType = type.dyn_cast<spirv::RuntimeArrayType>()) {
507  uint32_t elementTypeID = 0;
508  if (failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
509  elementTypeID, serializationCtx))) {
510  return failure();
511  }
512  typeEnum = spirv::Opcode::OpTypeRuntimeArray;
513  operands.push_back(elementTypeID);
514  return processTypeDecoration(loc, runtimeArrayType, resultID);
515  }
516 
517  if (auto sampledImageType = type.dyn_cast<spirv::SampledImageType>()) {
518  typeEnum = spirv::Opcode::OpTypeSampledImage;
519  uint32_t imageTypeID = 0;
520  if (failed(
521  processType(loc, sampledImageType.getImageType(), imageTypeID))) {
522  return failure();
523  }
524  operands.push_back(imageTypeID);
525  return success();
526  }
527 
528  if (auto structType = type.dyn_cast<spirv::StructType>()) {
529  if (structType.isIdentified()) {
530  if (failed(processName(resultID, structType.getIdentifier())))
531  return failure();
532  serializationCtx.insert(structType.getIdentifier());
533  }
534 
535  bool hasOffset = structType.hasOffset();
536  for (auto elementIndex :
537  llvm::seq<uint32_t>(0, structType.getNumElements())) {
538  uint32_t elementTypeID = 0;
539  if (failed(processTypeImpl(loc, structType.getElementType(elementIndex),
540  elementTypeID, serializationCtx))) {
541  return failure();
542  }
543  operands.push_back(elementTypeID);
544  if (hasOffset) {
545  // Decorate each struct member with an offset
547  elementIndex, /*hasValue=*/1, spirv::Decoration::Offset,
548  static_cast<uint32_t>(structType.getMemberOffset(elementIndex))};
549  if (failed(processMemberDecoration(resultID, offsetDecoration))) {
550  return emitError(loc, "cannot decorate ")
551  << elementIndex << "-th member of " << structType
552  << " with its offset";
553  }
554  }
555  }
557  structType.getMemberDecorations(memberDecorations);
558 
559  for (auto &memberDecoration : memberDecorations) {
560  if (failed(processMemberDecoration(resultID, memberDecoration))) {
561  return emitError(loc, "cannot decorate ")
562  << static_cast<uint32_t>(memberDecoration.memberIndex)
563  << "-th member of " << structType << " with "
564  << stringifyDecoration(memberDecoration.decoration);
565  }
566  }
567 
568  typeEnum = spirv::Opcode::OpTypeStruct;
569 
570  if (structType.isIdentified())
571  serializationCtx.remove(structType.getIdentifier());
572 
573  return success();
574  }
575 
576  if (auto cooperativeMatrixType =
578  uint32_t elementTypeID = 0;
579  if (failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
580  elementTypeID, serializationCtx))) {
581  return failure();
582  }
583  typeEnum = spirv::Opcode::OpTypeCooperativeMatrixNV;
584  auto getConstantOp = [&](uint32_t id) {
585  auto attr = IntegerAttr::get(IntegerType::get(type.getContext(), 32), id);
586  return prepareConstantInt(loc, attr);
587  };
588  operands.push_back(elementTypeID);
589  operands.push_back(
590  getConstantOp(static_cast<uint32_t>(cooperativeMatrixType.getScope())));
591  operands.push_back(getConstantOp(cooperativeMatrixType.getRows()));
592  operands.push_back(getConstantOp(cooperativeMatrixType.getColumns()));
593  return success();
594  }
595 
596  if (auto matrixType = type.dyn_cast<spirv::MatrixType>()) {
597  uint32_t elementTypeID = 0;
598  if (failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
599  serializationCtx))) {
600  return failure();
601  }
602  typeEnum = spirv::Opcode::OpTypeMatrix;
603  operands.push_back(elementTypeID);
604  operands.push_back(matrixType.getNumColumns());
605  return success();
606  }
607 
608  // TODO: Handle other types.
609  return emitError(loc, "unhandled type in serialization: ") << type;
610 }
611 
613 Serializer::prepareFunctionType(Location loc, FunctionType type,
614  spirv::Opcode &typeEnum,
615  SmallVectorImpl<uint32_t> &operands) {
616  typeEnum = spirv::Opcode::OpTypeFunction;
617  assert(type.getNumResults() <= 1 &&
618  "serialization supports only a single return value");
619  uint32_t resultID = 0;
620  if (failed(processType(
621  loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
622  resultID))) {
623  return failure();
624  }
625  operands.push_back(resultID);
626  for (auto &res : type.getInputs()) {
627  uint32_t argTypeID = 0;
628  if (failed(processType(loc, res, argTypeID))) {
629  return failure();
630  }
631  operands.push_back(argTypeID);
632  }
633  return success();
634 }
635 
636 //===----------------------------------------------------------------------===//
637 // Constant
638 //===----------------------------------------------------------------------===//
639 
640 uint32_t Serializer::prepareConstant(Location loc, Type constType,
641  Attribute valueAttr) {
642  if (auto id = prepareConstantScalar(loc, valueAttr)) {
643  return id;
644  }
645 
646  // This is a composite literal. We need to handle each component separately
647  // and then emit an OpConstantComposite for the whole.
648 
649  if (auto id = getConstantID(valueAttr)) {
650  return id;
651  }
652 
653  uint32_t typeID = 0;
654  if (failed(processType(loc, constType, typeID))) {
655  return 0;
656  }
657 
658  uint32_t resultID = 0;
659  if (auto attr = valueAttr.dyn_cast<DenseElementsAttr>()) {
660  int rank = attr.getType().dyn_cast<ShapedType>().getRank();
661  SmallVector<uint64_t, 4> index(rank);
662  resultID = prepareDenseElementsConstant(loc, constType, attr,
663  /*dim=*/0, index);
664  } else if (auto arrayAttr = valueAttr.dyn_cast<ArrayAttr>()) {
665  resultID = prepareArrayConstant(loc, constType, arrayAttr);
666  }
667 
668  if (resultID == 0) {
669  emitError(loc, "cannot serialize attribute: ") << valueAttr;
670  return 0;
671  }
672 
673  constIDMap[valueAttr] = resultID;
674  return resultID;
675 }
676 
677 uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
678  ArrayAttr attr) {
679  uint32_t typeID = 0;
680  if (failed(processType(loc, constType, typeID))) {
681  return 0;
682  }
683 
684  uint32_t resultID = getNextID();
685  SmallVector<uint32_t, 4> operands = {typeID, resultID};
686  operands.reserve(attr.size() + 2);
687  auto elementType = constType.cast<spirv::ArrayType>().getElementType();
688  for (Attribute elementAttr : attr) {
689  if (auto elementID = prepareConstant(loc, elementType, elementAttr)) {
690  operands.push_back(elementID);
691  } else {
692  return 0;
693  }
694  }
695  spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
696  encodeInstructionInto(typesGlobalValues, opcode, operands);
697 
698  return resultID;
699 }
700 
701 // TODO: Turn the below function into iterative function, instead of
702 // recursive function.
703 uint32_t
704 Serializer::prepareDenseElementsConstant(Location loc, Type constType,
705  DenseElementsAttr valueAttr, int dim,
707  auto shapedType = valueAttr.getType().dyn_cast<ShapedType>();
708  assert(dim <= shapedType.getRank());
709  if (shapedType.getRank() == dim) {
710  if (auto attr = valueAttr.dyn_cast<DenseIntElementsAttr>()) {
711  return attr.getType().getElementType().isInteger(1)
712  ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
713  : prepareConstantInt(loc,
714  attr.getValues<IntegerAttr>()[index]);
715  }
716  if (auto attr = valueAttr.dyn_cast<DenseFPElementsAttr>()) {
717  return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
718  }
719  return 0;
720  }
721 
722  uint32_t typeID = 0;
723  if (failed(processType(loc, constType, typeID))) {
724  return 0;
725  }
726 
727  uint32_t resultID = getNextID();
728  SmallVector<uint32_t, 4> operands = {typeID, resultID};
729  operands.reserve(shapedType.getDimSize(dim) + 2);
730  auto elementType = constType.cast<spirv::CompositeType>().getElementType(0);
731  for (int i = 0; i < shapedType.getDimSize(dim); ++i) {
732  index[dim] = i;
733  if (auto elementID = prepareDenseElementsConstant(
734  loc, elementType, valueAttr, dim + 1, index)) {
735  operands.push_back(elementID);
736  } else {
737  return 0;
738  }
739  }
740  spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
741  encodeInstructionInto(typesGlobalValues, opcode, operands);
742 
743  return resultID;
744 }
745 
746 uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
747  bool isSpec) {
748  if (auto floatAttr = valueAttr.dyn_cast<FloatAttr>()) {
749  return prepareConstantFp(loc, floatAttr, isSpec);
750  }
751  if (auto boolAttr = valueAttr.dyn_cast<BoolAttr>()) {
752  return prepareConstantBool(loc, boolAttr, isSpec);
753  }
754  if (auto intAttr = valueAttr.dyn_cast<IntegerAttr>()) {
755  return prepareConstantInt(loc, intAttr, isSpec);
756  }
757 
758  return 0;
759 }
760 
761 uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
762  bool isSpec) {
763  if (!isSpec) {
764  // We can de-duplicate normal constants, but not specialization constants.
765  if (auto id = getConstantID(boolAttr)) {
766  return id;
767  }
768  }
769 
770  // Process the type for this bool literal
771  uint32_t typeID = 0;
772  if (failed(processType(loc, boolAttr.getType(), typeID))) {
773  return 0;
774  }
775 
776  auto resultID = getNextID();
777  auto opcode = boolAttr.getValue()
778  ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
779  : spirv::Opcode::OpConstantTrue)
780  : (isSpec ? spirv::Opcode::OpSpecConstantFalse
781  : spirv::Opcode::OpConstantFalse);
782  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID});
783 
784  if (!isSpec) {
785  constIDMap[boolAttr] = resultID;
786  }
787  return resultID;
788 }
789 
790 uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
791  bool isSpec) {
792  if (!isSpec) {
793  // We can de-duplicate normal constants, but not specialization constants.
794  if (auto id = getConstantID(intAttr)) {
795  return id;
796  }
797  }
798 
799  // Process the type for this integer literal
800  uint32_t typeID = 0;
801  if (failed(processType(loc, intAttr.getType(), typeID))) {
802  return 0;
803  }
804 
805  auto resultID = getNextID();
806  APInt value = intAttr.getValue();
807  unsigned bitwidth = value.getBitWidth();
808  bool isSigned = value.isSignedIntN(bitwidth);
809 
810  auto opcode =
811  isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
812 
813  switch (bitwidth) {
814  // According to SPIR-V spec, "When the type's bit width is less than
815  // 32-bits, the literal's value appears in the low-order bits of the word,
816  // and the high-order bits must be 0 for a floating-point type, or 0 for an
817  // integer type with Signedness of 0, or sign extended when Signedness
818  // is 1."
819  case 32:
820  case 16:
821  case 8: {
822  uint32_t word = 0;
823  if (isSigned) {
824  word = static_cast<int32_t>(value.getSExtValue());
825  } else {
826  word = static_cast<uint32_t>(value.getZExtValue());
827  }
828  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
829  } break;
830  // According to SPIR-V spec: "When the type's bit width is larger than one
831  // word, the literal’s low-order words appear first."
832  case 64: {
833  struct DoubleWord {
834  uint32_t word1;
835  uint32_t word2;
836  } words;
837  if (isSigned) {
838  words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
839  } else {
840  words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
841  }
842  encodeInstructionInto(typesGlobalValues, opcode,
843  {typeID, resultID, words.word1, words.word2});
844  } break;
845  default: {
846  std::string valueStr;
847  llvm::raw_string_ostream rss(valueStr);
848  value.print(rss, /*isSigned=*/false);
849 
850  emitError(loc, "cannot serialize ")
851  << bitwidth << "-bit integer literal: " << rss.str();
852  return 0;
853  }
854  }
855 
856  if (!isSpec) {
857  constIDMap[intAttr] = resultID;
858  }
859  return resultID;
860 }
861 
862 uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
863  bool isSpec) {
864  if (!isSpec) {
865  // We can de-duplicate normal constants, but not specialization constants.
866  if (auto id = getConstantID(floatAttr)) {
867  return id;
868  }
869  }
870 
871  // Process the type for this float literal
872  uint32_t typeID = 0;
873  if (failed(processType(loc, floatAttr.getType(), typeID))) {
874  return 0;
875  }
876 
877  auto resultID = getNextID();
878  APFloat value = floatAttr.getValue();
879  APInt intValue = value.bitcastToAPInt();
880 
881  auto opcode =
882  isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
883 
884  if (&value.getSemantics() == &APFloat::IEEEsingle()) {
885  uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
886  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
887  } else if (&value.getSemantics() == &APFloat::IEEEdouble()) {
888  struct DoubleWord {
889  uint32_t word1;
890  uint32_t word2;
891  } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
892  encodeInstructionInto(typesGlobalValues, opcode,
893  {typeID, resultID, words.word1, words.word2});
894  } else if (&value.getSemantics() == &APFloat::IEEEhalf()) {
895  uint32_t word =
896  static_cast<uint32_t>(value.bitcastToAPInt().getZExtValue());
897  encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
898  } else {
899  std::string valueStr;
900  llvm::raw_string_ostream rss(valueStr);
901  value.print(rss);
902 
903  emitError(loc, "cannot serialize ")
904  << floatAttr.getType() << "-typed float literal: " << rss.str();
905  return 0;
906  }
907 
908  if (!isSpec) {
909  constIDMap[floatAttr] = resultID;
910  }
911  return resultID;
912 }
913 
914 //===----------------------------------------------------------------------===//
915 // Control flow
916 //===----------------------------------------------------------------------===//
917 
918 uint32_t Serializer::getOrCreateBlockID(Block *block) {
919  if (uint32_t id = getBlockID(block))
920  return id;
921  return blockIDMap[block] = getNextID();
922 }
923 
924 #ifndef NDEBUG
925 void Serializer::printBlock(Block *block, raw_ostream &os) {
926  os << "block " << block << " (id = ";
927  if (uint32_t id = getBlockID(block))
928  os << id;
929  else
930  os << "unknown";
931  os << ")\n";
932 }
933 #endif
934 
936 Serializer::processBlock(Block *block, bool omitLabel,
937  function_ref<LogicalResult()> emitMerge) {
938  LLVM_DEBUG(llvm::dbgs() << "processing block " << block << ":\n");
939  LLVM_DEBUG(block->print(llvm::dbgs()));
940  LLVM_DEBUG(llvm::dbgs() << '\n');
941  if (!omitLabel) {
942  uint32_t blockID = getOrCreateBlockID(block);
943  LLVM_DEBUG(printBlock(block, llvm::dbgs()));
944 
945  // Emit OpLabel for this block.
946  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
947  }
948 
949  // Emit OpPhi instructions for block arguments, if any.
950  if (failed(emitPhiForBlockArguments(block)))
951  return failure();
952 
953  // If we need to emit merge instructions, it must happen in this block. Check
954  // whether we have other structured control flow ops, which will be expanded
955  // into multiple basic blocks. If that's the case, we need to emit the merge
956  // right now and then create new blocks for further serialization of the ops
957  // in this block.
958  if (emitMerge && llvm::any_of(block->getOperations(), [](Operation &op) {
959  return isa<spirv::LoopOp, spirv::SelectionOp>(op);
960  })) {
961  if (failed(emitMerge()))
962  return failure();
963  emitMerge = nullptr;
964 
965  // Start a new block for further serialization.
966  uint32_t blockID = getNextID();
967  encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {blockID});
968  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
969  }
970 
971  // Process each op in this block except the terminator.
972  for (auto &op : llvm::make_range(block->begin(), std::prev(block->end()))) {
973  if (failed(processOperation(&op)))
974  return failure();
975  }
976 
977  // Process the terminator.
978  if (emitMerge)
979  if (failed(emitMerge()))
980  return failure();
981  if (failed(processOperation(&block->back())))
982  return failure();
983 
984  return success();
985 }
986 
987 LogicalResult Serializer::emitPhiForBlockArguments(Block *block) {
988  // Nothing to do if this block has no arguments or it's the entry block, which
989  // always has the same arguments as the function signature.
990  if (block->args_empty() || block->isEntryBlock())
991  return success();
992 
993  LLVM_DEBUG(llvm::dbgs() << "emitting phi instructions..\n");
994 
995  // If the block has arguments, we need to create SPIR-V OpPhi instructions.
996  // A SPIR-V OpPhi instruction is of the syntax:
997  // OpPhi | result type | result <id> | (value <id>, parent block <id>) pair
998  // So we need to collect all predecessor blocks and the arguments they send
999  // to this block.
1001  for (Block *mlirPredecessor : block->getPredecessors()) {
1002  auto *terminator = mlirPredecessor->getTerminator();
1003  LLVM_DEBUG(llvm::dbgs() << " mlir predecessor ");
1004  LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1005  LLVM_DEBUG(llvm::dbgs() << " terminator: " << *terminator << "\n");
1006  // The predecessor here is the immediate one according to MLIR's IR
1007  // structure. It does not directly map to the incoming parent block for the
1008  // OpPhi instructions at SPIR-V binary level. This is because structured
1009  // control flow ops are serialized to multiple SPIR-V blocks. If there is a
1010  // spv.mlir.selection/spv.mlir.loop op in the MLIR predecessor block, the
1011  // branch op jumping to the OpPhi's block then resides in the previous
1012  // structured control flow op's merge block.
1013  Block *spirvPredecessor = getPhiIncomingBlock(mlirPredecessor);
1014  LLVM_DEBUG(llvm::dbgs() << " spirv predecessor ");
1015  LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1016  if (auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1017  predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1018  } else if (auto branchCondOp =
1019  dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1020  Optional<OperandRange> blockOperands;
1021  if (branchCondOp.trueTarget() == block) {
1022  blockOperands = branchCondOp.trueTargetOperands();
1023  } else {
1024  assert(branchCondOp.falseTarget() == block);
1025  blockOperands = branchCondOp.falseTargetOperands();
1026  }
1027 
1028  assert(!blockOperands->empty() &&
1029  "expected non-empty block operand range");
1030  predecessors.emplace_back(spirvPredecessor, *blockOperands);
1031  } else {
1032  return terminator->emitError("unimplemented terminator for Phi creation");
1033  }
1034  LLVM_DEBUG({
1035  llvm::dbgs() << " block arguments:\n";
1036  for (Value v : predecessors.back().second)
1037  llvm::dbgs() << " " << v << "\n";
1038  });
1039  }
1040 
1041  // Then create OpPhi instruction for each of the block argument.
1042  for (auto argIndex : llvm::seq<unsigned>(0, block->getNumArguments())) {
1043  BlockArgument arg = block->getArgument(argIndex);
1044 
1045  // Get the type <id> and result <id> for this OpPhi instruction.
1046  uint32_t phiTypeID = 0;
1047  if (failed(processType(arg.getLoc(), arg.getType(), phiTypeID)))
1048  return failure();
1049  uint32_t phiID = getNextID();
1050 
1051  LLVM_DEBUG(llvm::dbgs() << "[phi] for block argument #" << argIndex << ' '
1052  << arg << " (id = " << phiID << ")\n");
1053 
1054  // Prepare the (value <id>, parent block <id>) pairs.
1055  SmallVector<uint32_t, 8> phiArgs;
1056  phiArgs.push_back(phiTypeID);
1057  phiArgs.push_back(phiID);
1058 
1059  for (auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1060  Value value = predecessors[predIndex].second[argIndex];
1061  uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1062  LLVM_DEBUG(llvm::dbgs() << "[phi] use predecessor (id = " << predBlockId
1063  << ") value " << value << ' ');
1064  // Each pair is a value <id> ...
1065  uint32_t valueId = getValueID(value);
1066  if (valueId == 0) {
1067  // The op generating this value hasn't been visited yet so we don't have
1068  // an <id> assigned yet. Record this to fix up later.
1069  LLVM_DEBUG(llvm::dbgs() << "(need to fix)\n");
1070  deferredPhiValues[value].push_back(functionBody.size() + 1 +
1071  phiArgs.size());
1072  } else {
1073  LLVM_DEBUG(llvm::dbgs() << "(id = " << valueId << ")\n");
1074  }
1075  phiArgs.push_back(valueId);
1076  // ... and a parent block <id>.
1077  phiArgs.push_back(predBlockId);
1078  }
1079 
1080  encodeInstructionInto(functionBody, spirv::Opcode::OpPhi, phiArgs);
1081  valueIDMap[arg] = phiID;
1082  }
1083 
1084  return success();
1085 }
1086 
1087 //===----------------------------------------------------------------------===//
1088 // Operation
1089 //===----------------------------------------------------------------------===//
1090 
1091 LogicalResult Serializer::encodeExtensionInstruction(
1092  Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1093  ArrayRef<uint32_t> operands) {
1094  // Check if the extension has been imported.
1095  auto &setID = extendedInstSetIDMap[extensionSetName];
1096  if (!setID) {
1097  setID = getNextID();
1098  SmallVector<uint32_t, 16> importOperands;
1099  importOperands.push_back(setID);
1100  spirv::encodeStringLiteralInto(importOperands, extensionSetName);
1101  encodeInstructionInto(extendedSets, spirv::Opcode::OpExtInstImport,
1102  importOperands);
1103  }
1104 
1105  // The first two operands are the result type <id> and result <id>. The set
1106  // <id> and the opcode need to be insert after this.
1107  if (operands.size() < 2) {
1108  return op->emitError("extended instructions must have a result encoding");
1109  }
1110  SmallVector<uint32_t, 8> extInstOperands;
1111  extInstOperands.reserve(operands.size() + 2);
1112  extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1113  extInstOperands.push_back(setID);
1114  extInstOperands.push_back(extensionOpcode);
1115  extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1116  encodeInstructionInto(functionBody, spirv::Opcode::OpExtInst,
1117  extInstOperands);
1118  return success();
1119 }
1120 
1121 LogicalResult Serializer::processOperation(Operation *opInst) {
1122  LLVM_DEBUG(llvm::dbgs() << "[op] '" << opInst->getName() << "'\n");
1123 
1124  // First dispatch the ops that do not directly mirror an instruction from
1125  // the SPIR-V spec.
1127  .Case([&](spirv::AddressOfOp op) { return processAddressOfOp(op); })
1128  .Case([&](spirv::BranchOp op) { return processBranchOp(op); })
1129  .Case([&](spirv::BranchConditionalOp op) {
1130  return processBranchConditionalOp(op);
1131  })
1132  .Case([&](spirv::ConstantOp op) { return processConstantOp(op); })
1133  .Case([&](spirv::FuncOp op) { return processFuncOp(op); })
1134  .Case([&](spirv::GlobalVariableOp op) {
1135  return processGlobalVariableOp(op);
1136  })
1137  .Case([&](spirv::LoopOp op) { return processLoopOp(op); })
1138  .Case([&](spirv::ReferenceOfOp op) { return processReferenceOfOp(op); })
1139  .Case([&](spirv::SelectionOp op) { return processSelectionOp(op); })
1140  .Case([&](spirv::SpecConstantOp op) { return processSpecConstantOp(op); })
1141  .Case([&](spirv::SpecConstantCompositeOp op) {
1142  return processSpecConstantCompositeOp(op);
1143  })
1144  .Case([&](spirv::SpecConstantOperationOp op) {
1145  return processSpecConstantOperationOp(op);
1146  })
1147  .Case([&](spirv::UndefOp op) { return processUndefOp(op); })
1148  .Case([&](spirv::VariableOp op) { return processVariableOp(op); })
1149 
1150  // Then handle all the ops that directly mirror SPIR-V instructions with
1151  // auto-generated methods.
1152  .Default(
1153  [&](Operation *op) { return dispatchToAutogenSerialization(op); });
1154 }
1155 
1156 LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1157  StringRef extInstSet,
1158  uint32_t opcode) {
1159  SmallVector<uint32_t, 4> operands;
1160  Location loc = op->getLoc();
1161 
1162  uint32_t resultID = 0;
1163  if (op->getNumResults() != 0) {
1164  uint32_t resultTypeID = 0;
1165  if (failed(processType(loc, op->getResult(0).getType(), resultTypeID)))
1166  return failure();
1167  operands.push_back(resultTypeID);
1168 
1169  resultID = getNextID();
1170  operands.push_back(resultID);
1171  valueIDMap[op->getResult(0)] = resultID;
1172  };
1173 
1174  for (Value operand : op->getOperands())
1175  operands.push_back(getValueID(operand));
1176 
1177  if (failed(emitDebugLine(functionBody, loc)))
1178  return failure();
1179 
1180  if (extInstSet.empty()) {
1181  encodeInstructionInto(functionBody, static_cast<spirv::Opcode>(opcode),
1182  operands);
1183  } else {
1184  if (failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1185  return failure();
1186  }
1187 
1188  if (op->getNumResults() != 0) {
1189  for (auto attr : op->getAttrs()) {
1190  if (failed(processDecoration(loc, resultID, attr)))
1191  return failure();
1192  }
1193  }
1194 
1195  return success();
1196 }
1197 
1198 LogicalResult Serializer::emitDecoration(uint32_t target,
1199  spirv::Decoration decoration,
1200  ArrayRef<uint32_t> params) {
1201  uint32_t wordCount = 3 + params.size();
1202  decorations.push_back(
1203  spirv::getPrefixedOpcode(wordCount, spirv::Opcode::OpDecorate));
1204  decorations.push_back(target);
1205  decorations.push_back(static_cast<uint32_t>(decoration));
1206  decorations.append(params.begin(), params.end());
1207  return success();
1208 }
1209 
1210 LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1211  Location loc) {
1212  if (!options.emitDebugInfo)
1213  return success();
1214 
1215  if (lastProcessedWasMergeInst) {
1216  lastProcessedWasMergeInst = false;
1217  return success();
1218  }
1219 
1220  auto fileLoc = loc.dyn_cast<FileLineColLoc>();
1221  if (fileLoc)
1222  encodeInstructionInto(binary, spirv::Opcode::OpLine,
1223  {fileID, fileLoc.getLine(), fileLoc.getColumn()});
1224  return success();
1225 }
1226 } // namespace spirv
1227 } // namespace mlir
Include the generated interface declarations.
iterator begin()
Definition: Block.h:134
An attribute that represents a reference to a dense float vector or tensor object.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
Attribute getValue() const
Return the value of the attribute.
Definition: Attributes.h:151
Operation & back()
Definition: Block.h:143
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:30
operand_range getOperands()
Returns an iterator on the underlying Value&#39;s.
Definition: Operation.h:247
Block represents an ordered list of Operations.
Definition: Block.h:29
constexpr unsigned kHeaderWordCount
SPIR-V binary header word count.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition: Operation.h:308
OpListType & getOperations()
Definition: Block.h:128
Serializer(spirv::ModuleOp module, const SerializationOptions &options)
Creates a serializer for the given SPIR-V module.
Definition: Serializer.cpp:82
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity...
Definition: SPIRVOps.cpp:639
iterator_range< pred_iterator > getPredecessors()
Definition: Block.h:225
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape...
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:75
bool isIdentified() const
Returns true if the StructType is identified.
Definition: SPIRVTypes.cpp:987
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value...
Definition: LogicalResult.h:68
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:96
bool args_empty()
Definition: Block.h:88
BlockArgument getArgument(unsigned i)
Definition: Block.h:120
static constexpr const bool value
void collect(SmallVectorImpl< uint32_t > &binary)
Collects the final SPIR-V binary.
Definition: Serializer.cpp:110
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:48
NamedAttribute represents a combination of a name and an Attribute value.
Definition: Attributes.h:137
bool getValue() const
Return the boolean value of this attribute.
IntegerAttr getI32IntegerAttr(int32_t value)
Definition: Builders.cpp:148
void printValueIDMap(raw_ostream &os)
(For debugging) prints each value and its corresponding result <id>.
Definition: Serializer.cpp:135
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.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
An attribute that represents a reference to a dense vector or tensor object.
U dyn_cast() const
Definition: Types.h:244
iterator end()
Definition: Block.h:135
unsigned getNumArguments()
Definition: Block.h:119
Attributes are known-constant values of operations.
Definition: Attributes.h:24
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
U dyn_cast() const
Definition: Value.h:99
StringAttr getName() const
Return the name of the attribute.
Definition: Attributes.cpp:32
OpResult getResult(unsigned idx)
Get the &#39;idx&#39;th result of this operation.
Definition: Operation.h:276
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition: Block.cpp:35
StringRef getIdentifier() const
For literal structs, return an empty string.
Definition: SPIRVTypes.cpp:985
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:106
uint32_t getPrefixedOpcode(uint32_t wordCount, spirv::Opcode opcode)
Returns the word-count-prefixed opcode for an SPIR-V instruction.
This class represents an argument of a Block.
Definition: Value.h:298
bool emitSymbolName
Whether to emit OpName instructions for SPIR-V symbol ops.
Definition: Serialization.h:27
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:72
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:19
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:84
static llvm::ManagedStatic< PassManagerOptions > options
U dyn_cast() const
Definition: Location.h:66
Type getType() const
Return the type of this attribute.
Definition: Attributes.h:64
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Type getType() const
Return the type of this value.
Definition: Value.h:117
U dyn_cast() const
Definition: Attributes.h:117
static Block * getStructuredControlFlowOpMergeBlock(Operation *op)
Returns the merge block if the given op is a structured control flow op.
Definition: Serializer.cpp:33
static VectorType vectorType(CodeGen &codegen, Type etp)
Constructs vector type.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
SPIR-V struct type.
Definition: SPIRVTypes.h:278
bool emitDebugInfo
Whether to emit OpLine location information for SPIR-V ops.
Definition: Serialization.h:29
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:273
LogicalResult serialize()
Serializes the remembered SPIR-V module.
Definition: Serializer.cpp:86
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
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:44
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
Definition: SPIRVTypes.cpp:961
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:57
bool isa() const
Definition: Types.h:234
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:231
void print(raw_ostream &os)
Location getLoc() const
Return the location for this argument.
Definition: Value.h:313
An attribute that represents a reference to a dense integer vector or tensor object.
U cast() const
Definition: Types.h:250