MLIR  17.0.0git
SerializeOps.cpp
Go to the documentation of this file.
1 //===- SerializeOps.cpp - MLIR SPIR-V Serialization (Ops) -----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the serialization methods for MLIR SPIR-V module ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "Serializer.h"
14 
20 #include "llvm/ADT/DepthFirstIterator.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/Support/Debug.h"
23 
24 #define DEBUG_TYPE "spirv-serialization"
25 
26 using namespace mlir;
27 
28 /// A pre-order depth-first visitor function for processing basic blocks.
29 ///
30 /// Visits the basic blocks starting from the given `headerBlock` in pre-order
31 /// depth-first manner and calls `blockHandler` on each block. Skips handling
32 /// blocks in the `skipBlocks` list. If `skipHeader` is true, `blockHandler`
33 /// will not be invoked in `headerBlock` but still handles all `headerBlock`'s
34 /// successors.
35 ///
36 /// SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order
37 /// of blocks in a function must satisfy the rule that blocks appear before
38 /// all blocks they dominate." This can be achieved by a pre-order CFG
39 /// traversal algorithm. To make the serialization output more logical and
40 /// readable to human, we perform depth-first CFG traversal and delay the
41 /// serialization of the merge block and the continue block, if exists, until
42 /// after all other blocks have been processed.
43 static LogicalResult
45  function_ref<LogicalResult(Block *)> blockHandler,
46  bool skipHeader = false, BlockRange skipBlocks = {}) {
47  llvm::df_iterator_default_set<Block *, 4> doneBlocks;
48  doneBlocks.insert(skipBlocks.begin(), skipBlocks.end());
49 
50  for (Block *block : llvm::depth_first_ext(headerBlock, doneBlocks)) {
51  if (skipHeader && block == headerBlock)
52  continue;
53  if (failed(blockHandler(block)))
54  return failure();
55  }
56  return success();
57 }
58 
59 namespace mlir {
60 namespace spirv {
61 LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) {
62  if (auto resultID =
63  prepareConstant(op.getLoc(), op.getType(), op.getValue())) {
64  valueIDMap[op.getResult()] = resultID;
65  return success();
66  }
67  return failure();
68 }
69 
70 LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) {
71  if (auto resultID = prepareConstantScalar(op.getLoc(), op.getDefaultValue(),
72  /*isSpec=*/true)) {
73  // Emit the OpDecorate instruction for SpecId.
74  if (auto specID = op->getAttrOfType<IntegerAttr>("spec_id")) {
75  auto val = static_cast<uint32_t>(specID.getInt());
76  if (failed(emitDecoration(resultID, spirv::Decoration::SpecId, {val})))
77  return failure();
78  }
79 
80  specConstIDMap[op.getSymName()] = resultID;
81  return processName(resultID, op.getSymName());
82  }
83  return failure();
84 }
85 
87 Serializer::processSpecConstantCompositeOp(spirv::SpecConstantCompositeOp op) {
88  uint32_t typeID = 0;
89  if (failed(processType(op.getLoc(), op.getType(), typeID))) {
90  return failure();
91  }
92 
93  auto resultID = getNextID();
94 
95  SmallVector<uint32_t, 8> operands;
96  operands.push_back(typeID);
97  operands.push_back(resultID);
98 
99  auto constituents = op.getConstituents();
100 
101  for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
102  auto constituent = dyn_cast<FlatSymbolRefAttr>(constituents[index]);
103 
104  auto constituentName = constituent.getValue();
105  auto constituentID = getSpecConstID(constituentName);
106 
107  if (!constituentID) {
108  return op.emitError("unknown result <id> for specialization constant ")
109  << constituentName;
110  }
111 
112  operands.push_back(constituentID);
113  }
114 
115  encodeInstructionInto(typesGlobalValues,
116  spirv::Opcode::OpSpecConstantComposite, operands);
117  specConstIDMap[op.getSymName()] = resultID;
118 
119  return processName(resultID, op.getSymName());
120 }
121 
123 Serializer::processSpecConstantOperationOp(spirv::SpecConstantOperationOp op) {
124  uint32_t typeID = 0;
125  if (failed(processType(op.getLoc(), op.getType(), typeID))) {
126  return failure();
127  }
128 
129  auto resultID = getNextID();
130 
131  SmallVector<uint32_t, 8> operands;
132  operands.push_back(typeID);
133  operands.push_back(resultID);
134 
135  Block &block = op.getRegion().getBlocks().front();
136  Operation &enclosedOp = block.getOperations().front();
137 
138  std::string enclosedOpName;
139  llvm::raw_string_ostream rss(enclosedOpName);
140  rss << "Op" << enclosedOp.getName().stripDialect();
141  auto enclosedOpcode = spirv::symbolizeOpcode(rss.str());
142 
143  if (!enclosedOpcode) {
144  op.emitError("Couldn't find op code for op ")
145  << enclosedOp.getName().getStringRef();
146  return failure();
147  }
148 
149  operands.push_back(static_cast<uint32_t>(*enclosedOpcode));
150 
151  // Append operands to the enclosed op to the list of operands.
152  for (Value operand : enclosedOp.getOperands()) {
153  uint32_t id = getValueID(operand);
154  assert(id && "use before def!");
155  operands.push_back(id);
156  }
157 
158  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpSpecConstantOp,
159  operands);
160  valueIDMap[op.getResult()] = resultID;
161 
162  return success();
163 }
164 
165 LogicalResult Serializer::processUndefOp(spirv::UndefOp op) {
166  auto undefType = op.getType();
167  auto &id = undefValIDMap[undefType];
168  if (!id) {
169  id = getNextID();
170  uint32_t typeID = 0;
171  if (failed(processType(op.getLoc(), undefType, typeID)))
172  return failure();
173  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpUndef,
174  {typeID, id});
175  }
176  valueIDMap[op.getResult()] = id;
177  return success();
178 }
179 
180 LogicalResult Serializer::processFuncOp(spirv::FuncOp op) {
181  LLVM_DEBUG(llvm::dbgs() << "-- start function '" << op.getName() << "' --\n");
182  assert(functionHeader.empty() && functionBody.empty());
183 
184  uint32_t fnTypeID = 0;
185  // Generate type of the function.
186  if (failed(processType(op.getLoc(), op.getFunctionType(), fnTypeID)))
187  return failure();
188 
189  // Add the function definition.
190  SmallVector<uint32_t, 4> operands;
191  uint32_t resTypeID = 0;
192  auto resultTypes = op.getFunctionType().getResults();
193  if (resultTypes.size() > 1) {
194  return op.emitError("cannot serialize function with multiple return types");
195  }
196  if (failed(processType(op.getLoc(),
197  (resultTypes.empty() ? getVoidType() : resultTypes[0]),
198  resTypeID))) {
199  return failure();
200  }
201  operands.push_back(resTypeID);
202  auto funcID = getOrCreateFunctionID(op.getName());
203  operands.push_back(funcID);
204  operands.push_back(static_cast<uint32_t>(op.getFunctionControl()));
205  operands.push_back(fnTypeID);
206  encodeInstructionInto(functionHeader, spirv::Opcode::OpFunction, operands);
207 
208  // Add function name.
209  if (failed(processName(funcID, op.getName()))) {
210  return failure();
211  }
212  // Handle external functions with linkage_attributes(LinkageAttributes)
213  // differently.
214  auto linkageAttr = op.getLinkageAttributes();
215  auto hasImportLinkage =
216  linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
217  spirv::LinkageType::Import);
218  if (op.isExternal() && !hasImportLinkage) {
219  return op.emitError(
220  "'spirv.module' cannot contain external functions "
221  "without 'Import' linkage_attributes (LinkageAttributes)");
222  } else if (op.isExternal() && hasImportLinkage) {
223  // Add an entry block to set up the block arguments
224  // to match the signature of the function.
225  // This is to generate OpFunctionParameter for functions with
226  // LinkageAttributes.
227  // WARNING: This operation has side-effect, it essentially adds a body
228  // to the func. Hence, making it not external anymore (isExternal()
229  // is going to return false for this function from now on)
230  // Hence, we'll remove the body once we are done with the serialization.
231  op.addEntryBlock();
232  for (auto arg : op.getArguments()) {
233  uint32_t argTypeID = 0;
234  if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
235  return failure();
236  }
237  auto argValueID = getNextID();
238  valueIDMap[arg] = argValueID;
239  encodeInstructionInto(functionHeader, spirv::Opcode::OpFunctionParameter,
240  {argTypeID, argValueID});
241  }
242  // Don't need to process the added block, there is nothing to process,
243  // the fake body was added just to get the arguments, remove the body,
244  // since it's use is done.
245  op.eraseBody();
246  } else {
247  // Declare the parameters.
248  for (auto arg : op.getArguments()) {
249  uint32_t argTypeID = 0;
250  if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
251  return failure();
252  }
253  auto argValueID = getNextID();
254  valueIDMap[arg] = argValueID;
255  encodeInstructionInto(functionHeader, spirv::Opcode::OpFunctionParameter,
256  {argTypeID, argValueID});
257  }
258 
259  // Some instructions (e.g., OpVariable) in a function must be in the first
260  // block in the function. These instructions will be put in
261  // functionHeader. Thus, we put the label in functionHeader first, and
262  // omit it from the first block. OpLabel only needs to be added for
263  // functions with body (including empty body). Since, we added a fake body
264  // for functions with 'Import' Linkage attributes, these functions are
265  // essentially function delcaration, so they should not have OpLabel and a
266  // terminating instruction. That's why we skipped it for those functions.
267  encodeInstructionInto(functionHeader, spirv::Opcode::OpLabel,
268  {getOrCreateBlockID(&op.front())});
269  if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
270  return failure();
272  &op.front(), [&](Block *block) { return processBlock(block); },
273  /*skipHeader=*/true))) {
274  return failure();
275  }
276 
277  // There might be OpPhi instructions who have value references needing to
278  // fix.
279  for (const auto &deferredValue : deferredPhiValues) {
280  Value value = deferredValue.first;
281  uint32_t id = getValueID(value);
282  LLVM_DEBUG(llvm::dbgs() << "[phi] fix reference of value " << value
283  << " to id = " << id << '\n');
284  assert(id && "OpPhi references undefined value!");
285  for (size_t offset : deferredValue.second)
286  functionBody[offset] = id;
287  }
288  deferredPhiValues.clear();
289  }
290  LLVM_DEBUG(llvm::dbgs() << "-- completed function '" << op.getName()
291  << "' --\n");
292  // Insert Decorations based on Function Attributes.
293  // Only attributes we should be considering for decoration are the
294  // ::mlir::spirv::Decoration attributes.
295 
296  for (auto attr : op->getAttrs()) {
297  // Only generate OpDecorate op for spirv::Decoration attributes.
298  auto isValidDecoration = mlir::spirv::symbolizeEnum<spirv::Decoration>(
299  llvm::convertToCamelFromSnakeCase(attr.getName().strref(),
300  /*capitalizeFirst=*/true));
301  if (isValidDecoration != std::nullopt) {
302  if (failed(processDecoration(op.getLoc(), funcID, attr))) {
303  return failure();
304  }
305  }
306  }
307  // Insert OpFunctionEnd.
308  encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionEnd, {});
309 
310  functions.append(functionHeader.begin(), functionHeader.end());
311  functions.append(functionBody.begin(), functionBody.end());
312  functionHeader.clear();
313  functionBody.clear();
314 
315  return success();
316 }
317 
318 LogicalResult Serializer::processVariableOp(spirv::VariableOp op) {
319  SmallVector<uint32_t, 4> operands;
320  SmallVector<StringRef, 2> elidedAttrs;
321  uint32_t resultID = 0;
322  uint32_t resultTypeID = 0;
323  if (failed(processType(op.getLoc(), op.getType(), resultTypeID))) {
324  return failure();
325  }
326  operands.push_back(resultTypeID);
327  resultID = getNextID();
328  valueIDMap[op.getResult()] = resultID;
329  operands.push_back(resultID);
330  auto attr = op->getAttr(spirv::attributeName<spirv::StorageClass>());
331  if (attr) {
332  operands.push_back(
333  static_cast<uint32_t>(cast<spirv::StorageClassAttr>(attr).getValue()));
334  }
335  elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
336  for (auto arg : op.getODSOperands(0)) {
337  auto argID = getValueID(arg);
338  if (!argID) {
339  return emitError(op.getLoc(), "operand 0 has a use before def");
340  }
341  operands.push_back(argID);
342  }
343  if (failed(emitDebugLine(functionHeader, op.getLoc())))
344  return failure();
345  encodeInstructionInto(functionHeader, spirv::Opcode::OpVariable, operands);
346  for (auto attr : op->getAttrs()) {
347  if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
348  return attr.getName() == elided;
349  })) {
350  continue;
351  }
352  if (failed(processDecoration(op.getLoc(), resultID, attr))) {
353  return failure();
354  }
355  }
356  return success();
357 }
358 
360 Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
361  // Get TypeID.
362  uint32_t resultTypeID = 0;
363  SmallVector<StringRef, 4> elidedAttrs;
364  if (failed(processType(varOp.getLoc(), varOp.getType(), resultTypeID))) {
365  return failure();
366  }
367 
368  elidedAttrs.push_back("type");
369  SmallVector<uint32_t, 4> operands;
370  operands.push_back(resultTypeID);
371  auto resultID = getNextID();
372 
373  // Encode the name.
374  auto varName = varOp.getSymName();
375  elidedAttrs.push_back(SymbolTable::getSymbolAttrName());
376  if (failed(processName(resultID, varName))) {
377  return failure();
378  }
379  globalVarIDMap[varName] = resultID;
380  operands.push_back(resultID);
381 
382  // Encode StorageClass.
383  operands.push_back(static_cast<uint32_t>(varOp.storageClass()));
384 
385  // Encode initialization.
386  if (auto initializer = varOp.getInitializer()) {
387  auto initializerID = getVariableID(*initializer);
388  if (!initializerID) {
389  return emitError(varOp.getLoc(),
390  "invalid usage of undefined variable as initializer");
391  }
392  operands.push_back(initializerID);
393  elidedAttrs.push_back("initializer");
394  }
395 
396  if (failed(emitDebugLine(typesGlobalValues, varOp.getLoc())))
397  return failure();
398  encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, operands);
399  elidedAttrs.push_back("initializer");
400 
401  // Encode decorations.
402  for (auto attr : varOp->getAttrs()) {
403  if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
404  return attr.getName() == elided;
405  })) {
406  continue;
407  }
408  if (failed(processDecoration(varOp.getLoc(), resultID, attr))) {
409  return failure();
410  }
411  }
412  return success();
413 }
414 
415 LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) {
416  // Assign <id>s to all blocks so that branches inside the SelectionOp can
417  // resolve properly.
418  auto &body = selectionOp.getBody();
419  for (Block &block : body)
420  getOrCreateBlockID(&block);
421 
422  auto *headerBlock = selectionOp.getHeaderBlock();
423  auto *mergeBlock = selectionOp.getMergeBlock();
424  auto headerID = getBlockID(headerBlock);
425  auto mergeID = getBlockID(mergeBlock);
426  auto loc = selectionOp.getLoc();
427 
428  // This SelectionOp is in some MLIR block with preceding and following ops. In
429  // the binary format, it should reside in separate SPIR-V blocks from its
430  // preceding and following ops. So we need to emit unconditional branches to
431  // jump to this SelectionOp's SPIR-V blocks and jumping back to the normal
432  // flow afterwards.
433  encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
434 
435  // Emit the selection header block, which dominates all other blocks, first.
436  // We need to emit an OpSelectionMerge instruction before the selection header
437  // block's terminator.
438  auto emitSelectionMerge = [&]() {
439  if (failed(emitDebugLine(functionBody, loc)))
440  return failure();
441  lastProcessedWasMergeInst = true;
443  functionBody, spirv::Opcode::OpSelectionMerge,
444  {mergeID, static_cast<uint32_t>(selectionOp.getSelectionControl())});
445  return success();
446  };
447  if (failed(
448  processBlock(headerBlock, /*omitLabel=*/false, emitSelectionMerge)))
449  return failure();
450 
451  // Process all blocks with a depth-first visitor starting from the header
452  // block. The selection header block and merge block are skipped by this
453  // visitor.
455  headerBlock, [&](Block *block) { return processBlock(block); },
456  /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock})))
457  return failure();
458 
459  // There is nothing to do for the merge block in the selection, which just
460  // contains a spirv.mlir.merge op, itself. But we need to have an OpLabel
461  // instruction to start a new SPIR-V block for ops following this SelectionOp.
462  // The block should use the <id> for the merge block.
463  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
464  LLVM_DEBUG(llvm::dbgs() << "done merge ");
465  LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
466  LLVM_DEBUG(llvm::dbgs() << "\n");
467  return success();
468 }
469 
470 LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) {
471  // Assign <id>s to all blocks so that branches inside the LoopOp can resolve
472  // properly. We don't need to assign for the entry block, which is just for
473  // satisfying MLIR region's structural requirement.
474  auto &body = loopOp.getBody();
475  for (Block &block : llvm::drop_begin(body))
476  getOrCreateBlockID(&block);
477 
478  auto *headerBlock = loopOp.getHeaderBlock();
479  auto *continueBlock = loopOp.getContinueBlock();
480  auto *mergeBlock = loopOp.getMergeBlock();
481  auto headerID = getBlockID(headerBlock);
482  auto continueID = getBlockID(continueBlock);
483  auto mergeID = getBlockID(mergeBlock);
484  auto loc = loopOp.getLoc();
485 
486  // This LoopOp is in some MLIR block with preceding and following ops. In the
487  // binary format, it should reside in separate SPIR-V blocks from its
488  // preceding and following ops. So we need to emit unconditional branches to
489  // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow
490  // afterwards.
491  encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
492 
493  // LoopOp's entry block is just there for satisfying MLIR's structural
494  // requirements so we omit it and start serialization from the loop header
495  // block.
496 
497  // Emit the loop header block, which dominates all other blocks, first. We
498  // need to emit an OpLoopMerge instruction before the loop header block's
499  // terminator.
500  auto emitLoopMerge = [&]() {
501  if (failed(emitDebugLine(functionBody, loc)))
502  return failure();
503  lastProcessedWasMergeInst = true;
505  functionBody, spirv::Opcode::OpLoopMerge,
506  {mergeID, continueID, static_cast<uint32_t>(loopOp.getLoopControl())});
507  return success();
508  };
509  if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge)))
510  return failure();
511 
512  // Process all blocks with a depth-first visitor starting from the header
513  // block. The loop header block, loop continue block, and loop merge block are
514  // skipped by this visitor and handled later in this function.
516  headerBlock, [&](Block *block) { return processBlock(block); },
517  /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock})))
518  return failure();
519 
520  // We have handled all other blocks. Now get to the loop continue block.
521  if (failed(processBlock(continueBlock)))
522  return failure();
523 
524  // There is nothing to do for the merge block in the loop, which just contains
525  // a spirv.mlir.merge op, itself. But we need to have an OpLabel instruction
526  // to start a new SPIR-V block for ops following this LoopOp. The block should
527  // use the <id> for the merge block.
528  encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
529  LLVM_DEBUG(llvm::dbgs() << "done merge ");
530  LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
531  LLVM_DEBUG(llvm::dbgs() << "\n");
532  return success();
533 }
534 
535 LogicalResult Serializer::processBranchConditionalOp(
536  spirv::BranchConditionalOp condBranchOp) {
537  auto conditionID = getValueID(condBranchOp.getCondition());
538  auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock());
539  auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock());
540  SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID};
541 
542  if (auto weights = condBranchOp.getBranchWeights()) {
543  for (auto val : weights->getValue())
544  arguments.push_back(cast<IntegerAttr>(val).getInt());
545  }
546 
547  if (failed(emitDebugLine(functionBody, condBranchOp.getLoc())))
548  return failure();
549  encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional,
550  arguments);
551  return success();
552 }
553 
554 LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) {
555  if (failed(emitDebugLine(functionBody, branchOp.getLoc())))
556  return failure();
557  encodeInstructionInto(functionBody, spirv::Opcode::OpBranch,
558  {getOrCreateBlockID(branchOp.getTarget())});
559  return success();
560 }
561 
562 LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) {
563  auto varName = addressOfOp.getVariable();
564  auto variableID = getVariableID(varName);
565  if (!variableID) {
566  return addressOfOp.emitError("unknown result <id> for variable ")
567  << varName;
568  }
569  valueIDMap[addressOfOp.getPointer()] = variableID;
570  return success();
571 }
572 
574 Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) {
575  auto constName = referenceOfOp.getSpecConst();
576  auto constID = getSpecConstID(constName);
577  if (!constID) {
578  return referenceOfOp.emitError(
579  "unknown result <id> for specialization constant ")
580  << constName;
581  }
582  valueIDMap[referenceOfOp.getReference()] = constID;
583  return success();
584 }
585 
586 template <>
588 Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) {
589  SmallVector<uint32_t, 4> operands;
590  // Add the ExecutionModel.
591  operands.push_back(static_cast<uint32_t>(op.getExecutionModel()));
592  // Add the function <id>.
593  auto funcID = getFunctionID(op.getFn());
594  if (!funcID) {
595  return op.emitError("missing <id> for function ")
596  << op.getFn()
597  << "; function needs to be defined before spirv.EntryPoint is "
598  "serialized";
599  }
600  operands.push_back(funcID);
601  // Add the name of the function.
602  spirv::encodeStringLiteralInto(operands, op.getFn());
603 
604  // Add the interface values.
605  if (auto interface = op.getInterface()) {
606  for (auto var : interface.getValue()) {
607  auto id = getVariableID(cast<FlatSymbolRefAttr>(var).getValue());
608  if (!id) {
609  return op.emitError(
610  "referencing undefined global variable."
611  "spirv.EntryPoint is at the end of spirv.module. All "
612  "referenced variables should already be defined");
613  }
614  operands.push_back(id);
615  }
616  }
617  encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, operands);
618  return success();
619 }
620 
621 template <>
623 Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) {
624  SmallVector<uint32_t, 4> operands;
625  // Add the function <id>.
626  auto funcID = getFunctionID(op.getFn());
627  if (!funcID) {
628  return op.emitError("missing <id> for function ")
629  << op.getFn()
630  << "; function needs to be serialized before ExecutionModeOp is "
631  "serialized";
632  }
633  operands.push_back(funcID);
634  // Add the ExecutionMode.
635  operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
636 
637  // Serialize values if any.
638  auto values = op.getValues();
639  if (values) {
640  for (auto &intVal : values.getValue()) {
641  operands.push_back(static_cast<uint32_t>(
642  llvm::cast<IntegerAttr>(intVal).getValue().getZExtValue()));
643  }
644  }
645  encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode,
646  operands);
647  return success();
648 }
649 
650 template <>
652 Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) {
653  auto funcName = op.getCallee();
654  uint32_t resTypeID = 0;
655 
656  Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType();
657  if (failed(processType(op.getLoc(), resultTy, resTypeID)))
658  return failure();
659 
660  auto funcID = getOrCreateFunctionID(funcName);
661  auto funcCallID = getNextID();
662  SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID};
663 
664  for (auto value : op.getArguments()) {
665  auto valueID = getValueID(value);
666  assert(valueID && "cannot find a value for spirv.FunctionCall");
667  operands.push_back(valueID);
668  }
669 
670  if (!isa<NoneType>(resultTy))
671  valueIDMap[op.getResult(0)] = funcCallID;
672 
673  encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, operands);
674  return success();
675 }
676 
677 template <>
679 Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
680  SmallVector<uint32_t, 4> operands;
681  SmallVector<StringRef, 2> elidedAttrs;
682 
683  for (Value operand : op->getOperands()) {
684  auto id = getValueID(operand);
685  assert(id && "use before def!");
686  operands.push_back(id);
687  }
688 
689  if (auto attr = op->getAttr("memory_access")) {
690  operands.push_back(
691  static_cast<uint32_t>(cast<spirv::MemoryAccessAttr>(attr).getValue()));
692  }
693 
694  elidedAttrs.push_back("memory_access");
695 
696  if (auto attr = op->getAttr("alignment")) {
697  operands.push_back(static_cast<uint32_t>(
698  cast<IntegerAttr>(attr).getValue().getZExtValue()));
699  }
700 
701  elidedAttrs.push_back("alignment");
702 
703  if (auto attr = op->getAttr("source_memory_access")) {
704  operands.push_back(
705  static_cast<uint32_t>(cast<spirv::MemoryAccessAttr>(attr).getValue()));
706  }
707 
708  elidedAttrs.push_back("source_memory_access");
709 
710  if (auto attr = op->getAttr("source_alignment")) {
711  operands.push_back(static_cast<uint32_t>(
712  cast<IntegerAttr>(attr).getValue().getZExtValue()));
713  }
714 
715  elidedAttrs.push_back("source_alignment");
716  if (failed(emitDebugLine(functionBody, op.getLoc())))
717  return failure();
718  encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, operands);
719 
720  return success();
721 }
722 template <>
723 LogicalResult Serializer::processOp<spirv::GenericCastToPtrExplicitOp>(
724  spirv::GenericCastToPtrExplicitOp op) {
725  SmallVector<uint32_t, 4> operands;
726  Type resultTy;
727  Location loc = op->getLoc();
728  uint32_t resultTypeID = 0;
729  uint32_t resultID = 0;
730  resultTy = op->getResult(0).getType();
731  if (failed(processType(loc, resultTy, resultTypeID)))
732  return failure();
733  operands.push_back(resultTypeID);
734 
735  resultID = getNextID();
736  operands.push_back(resultID);
737  valueIDMap[op->getResult(0)] = resultID;
738 
739  for (Value operand : op->getOperands())
740  operands.push_back(getValueID(operand));
741  spirv::StorageClass resultStorage =
742  cast<spirv::PointerType>(resultTy).getStorageClass();
743  operands.push_back(static_cast<uint32_t>(resultStorage));
744  encodeInstructionInto(functionBody, spirv::Opcode::OpGenericCastToPtrExplicit,
745  operands);
746  return success();
747 }
748 
749 // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and
750 // various Serializer::processOp<...>() specializations.
751 #define GET_SERIALIZATION_FNS
752 #include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"
753 
754 } // namespace spirv
755 } // namespace mlir
static LogicalResult visitInPrettyBlockOrder(Block *headerBlock, function_ref< LogicalResult(Block *)> blockHandler, bool skipHeader=false, BlockRange skipBlocks={})
A pre-order depth-first visitor function for processing basic blocks.
This class provides an abstraction over the different types of ranges over Blocks.
Definition: BlockSupport.h:106
Block represents an ordered list of Operations.
Definition: Block.h:30
OpListType & getOperations()
Definition: Block.h:126
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
StringRef stripDialect() const
Return the operation name with dialect name stripped, if it has one.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
AttrClass getAttrOfType(StringAttr name)
Definition: Operation.h:511
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition: Operation.h:495
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:402
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition: Operation.h:469
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:266
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition: Operation.h:648
result_type_iterator result_type_begin()
Definition: Operation.h:421
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:119
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
result_range getResults()
Definition: Operation.h:410
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:399
BlockListType & getBlocks()
Definition: Region.h:45
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition: SymbolTable.h:59
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:93
Type getType() const
Return the type of this value.
Definition: Value.h:122
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
void encodeInstructionInto(SmallVectorImpl< uint32_t > &binary, spirv::Opcode op, ArrayRef< uint32_t > operands)
Encodes an SPIR-V instruction with the given opcode and operands into the given binary vector.
Definition: Serializer.cpp:78
This header declares functions that assit transformations in the MemRef dialect.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26