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