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 
15 #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
16 #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
17 #include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h"
18 #include "mlir/Support/LogicalResult.h"
19 #include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
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.
33 static Block *getStructuredControlFlowOpMergeBlock(Operation *op) {
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,
83                        const SerializationOptions &options)
84     : module(module), mlirBuilder(module.getContext()), options(options) {}
85 
86 LogicalResult Serializer::serialize() {
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 
110 void Serializer::collect(SmallVectorImpl<uint32_t> &binary) {
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() {
186   llvm::SmallVector<uint32_t, 16> extName;
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   }
212   SmallVector<uint32_t, 1> args;
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) {
285   SmallVector<uint32_t, 4> args(
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 
325 LogicalResult
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
546         spirv::StructType::MemberDecorationInfo offsetDecoration{
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     }
556     SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
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 =
577           type.dyn_cast<spirv::CooperativeMatrixNVType>()) {
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 
612 LogicalResult
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,
706                                          MutableArrayRef<uint64_t> index) {
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 
935 LogicalResult
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.
1000   SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
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.
1126   return TypeSwitch<Operation *, LogicalResult>(opInst)
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
1228