21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/Sequence.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/ADT/bit.h"
26#include "llvm/Support/Debug.h"
30#define DEBUG_TYPE "spirv-serialization"
37 if (
auto selectionOp = dyn_cast<spirv::SelectionOp>(op))
38 return selectionOp.getMergeBlock();
39 if (
auto loopOp = dyn_cast<spirv::LoopOp>(op))
40 return loopOp.getMergeBlock();
51 if (
auto loopOp = dyn_cast<spirv::LoopOp>(block->
getParentOp())) {
55 while ((op = op->getPrevNode()) !=
nullptr)
74 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
75 return floatAttr.getValue().isZero();
77 if (
auto boolAttr = dyn_cast<BoolAttr>(attr)) {
78 return !boolAttr.getValue();
80 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
81 return intAttr.getValue().isZero();
83 if (
auto splatElemAttr = dyn_cast<SplatElementsAttr>(attr)) {
86 if (
auto denseElemAttr = dyn_cast<DenseElementsAttr>(attr)) {
102 for (
Operation &op : llvm::drop_begin(ops))
103 if (
auto funcOp = dyn_cast<spirv::FuncOp>(op))
104 if (funcOp.getBody().empty())
115 uint32_t wordCount = 1 + operands.size();
117 binary.append(operands.begin(), operands.end());
122 : module(module), mlirBuilder(module.
getContext()), options(options) {}
125 LLVM_DEBUG(llvm::dbgs() <<
"+++ starting serialization +++\n");
127 if (failed(module.verifyInvariants()))
132 if (failed(processExtension())) {
135 processMemoryModel();
142 for (
auto &op : *module.getBody()) {
143 if (failed(processOperation(&op))) {
148 LLVM_DEBUG(llvm::dbgs() <<
"+++ completed serialization +++\n");
154 extensions.size() + extendedSets.size() +
155 memoryModel.size() + entryPoints.size() +
156 executionModes.size() + decorations.size() +
157 typesGlobalValues.size() + functions.size() + graphs.size();
160 binary.reserve(moduleSize);
164 binary.append(capabilities.begin(), capabilities.end());
165 binary.append(extensions.begin(), extensions.end());
166 binary.append(extendedSets.begin(), extendedSets.end());
167 binary.append(memoryModel.begin(), memoryModel.end());
168 binary.append(entryPoints.begin(), entryPoints.end());
169 binary.append(executionModes.begin(), executionModes.end());
170 binary.append(debug.begin(), debug.end());
171 binary.append(names.begin(), names.end());
172 binary.append(decorations.begin(), decorations.end());
173 binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
174 binary.append(functions.begin(), functions.end());
175 binary.append(graphs.begin(), graphs.end());
180 os <<
"\n= Value <id> Map =\n\n";
181 for (
auto valueIDPair : valueIDMap) {
182 Value val = valueIDPair.first;
183 os <<
" " << val <<
" "
184 <<
"id = " << valueIDPair.second <<
' ';
186 os <<
"from op '" << op->getName() <<
"'";
187 }
else if (
auto arg = dyn_cast<BlockArgument>(val)) {
188 Block *block = arg.getOwner();
189 os <<
"from argument of block " << block <<
' ';
201uint32_t Serializer::getOrCreateFunctionID(StringRef fnName) {
202 auto funcID = funcIDMap.lookup(fnName);
204 funcID = getNextID();
205 funcIDMap[fnName] = funcID;
210void Serializer::processCapability() {
211 for (
auto cap : module.getVceTriple()->getCapabilities())
213 {
static_cast<uint32_t
>(cap)});
216void Serializer::processDebugInfo() {
217 if (!options.emitDebugInfo)
219 auto fileLoc = dyn_cast<FileLineColLoc>(module.getLoc());
220 auto fileName = fileLoc ? fileLoc.getFilename().strref() :
"<unknown>";
221 fileID = getNextID();
222 SmallVector<uint32_t, 16> operands;
223 operands.push_back(fileID);
229LogicalResult Serializer::processExtension() {
230 llvm::SmallVector<uint32_t, 16> extName;
231 llvm::SmallSet<Extension, 4> deducedExts(
232 llvm::from_range, module.getVceTriple()->getExtensions());
233 auto nonSemanticInfoExt = spirv::Extension::SPV_KHR_non_semantic_info;
234 if (options.emitDebugInfo && !deducedExts.contains(nonSemanticInfoExt)) {
236 if (!is_contained(targetEnvAttr.getExtensions(), nonSemanticInfoExt))
237 return module.emitError(
238 "SPV_KHR_non_semantic_info extension not available");
239 deducedExts.insert(nonSemanticInfoExt);
241 for (spirv::Extension ext : deducedExts) {
249void Serializer::processMemoryModel() {
250 StringAttr memoryModelName =
module.getMemoryModelAttrName();
251 auto mm =
static_cast<uint32_t
>(
252 module->getAttrOfType<spirv::MemoryModelAttr>(memoryModelName)
255 StringAttr addressingModelName =
module.getAddressingModelAttrName();
256 auto am =
static_cast<uint32_t
>(
257 module->getAttrOfType<spirv::AddressingModelAttr>(addressingModelName)
266 if (attrName ==
"fp_fast_math_mode")
267 return "FPFastMathMode";
269 if (attrName ==
"fp_rounding_mode")
270 return "FPRoundingMode";
272 if (attrName ==
"cache_control_load_intel")
273 return "CacheControlLoadINTEL";
274 if (attrName ==
"cache_control_store_intel")
275 return "CacheControlStoreINTEL";
277 return llvm::convertToCamelFromSnakeCase(attrName,
true);
280template <
typename AttrTy,
typename EmitF>
283 StringRef attrName, EmitF emitter) {
284 auto arrayAttr = dyn_cast<ArrayAttr>(attrList);
286 return emitError(loc,
"expecting array attribute of ")
287 << attrName <<
" for " << stringifyDecoration(decoration);
289 if (arrayAttr.empty()) {
290 return emitError(loc,
"expecting non-empty array attribute of ")
291 << attrName <<
" for " << stringifyDecoration(decoration);
293 for (
Attribute attr : arrayAttr.getValue()) {
294 auto cacheControlAttr = dyn_cast<AttrTy>(attr);
295 if (!cacheControlAttr) {
296 return emitError(loc,
"expecting array attribute of ")
297 << attrName <<
" for " << stringifyDecoration(decoration);
301 if (failed(emitter(cacheControlAttr)))
307LogicalResult Serializer::processDecorationAttr(
Location loc, uint32_t resultID,
308 Decoration decoration,
311 switch (decoration) {
312 case spirv::Decoration::LinkageAttributes: {
315 auto linkageAttr = dyn_cast<spirv::LinkageAttributesAttr>(attr);
316 auto linkageName = linkageAttr.getLinkageName();
317 auto linkageType = linkageAttr.getLinkageType().getValue();
321 args.push_back(
static_cast<uint32_t
>(linkageType));
324 case spirv::Decoration::FPFastMathMode:
325 if (
auto intAttr = dyn_cast<FPFastMathModeAttr>(attr)) {
326 args.push_back(
static_cast<uint32_t
>(intAttr.getValue()));
329 return emitError(loc,
"expected FPFastMathModeAttr attribute for ")
330 << stringifyDecoration(decoration);
331 case spirv::Decoration::FPRoundingMode:
332 if (
auto intAttr = dyn_cast<FPRoundingModeAttr>(attr)) {
333 args.push_back(
static_cast<uint32_t
>(intAttr.getValue()));
336 return emitError(loc,
"expected FPRoundingModeAttr attribute for ")
337 << stringifyDecoration(decoration);
338 case spirv::Decoration::Binding:
339 case spirv::Decoration::DescriptorSet:
340 case spirv::Decoration::Location:
341 case spirv::Decoration::Index:
342 case spirv::Decoration::Offset:
343 case spirv::Decoration::XfbBuffer:
344 case spirv::Decoration::XfbStride:
345 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
346 args.push_back(intAttr.getValue().getZExtValue());
349 return emitError(loc,
"expected integer attribute for ")
350 << stringifyDecoration(decoration);
351 case spirv::Decoration::BuiltIn:
352 if (
auto strAttr = dyn_cast<StringAttr>(attr)) {
353 auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
355 args.push_back(
static_cast<uint32_t
>(*enumVal));
359 << stringifyDecoration(decoration) <<
" decoration attribute "
360 << strAttr.getValue();
362 return emitError(loc,
"expected string attribute for ")
363 << stringifyDecoration(decoration);
364 case spirv::Decoration::Aliased:
365 case spirv::Decoration::AliasedPointer:
366 case spirv::Decoration::Flat:
367 case spirv::Decoration::NonReadable:
368 case spirv::Decoration::NonWritable:
369 case spirv::Decoration::NoPerspective:
370 case spirv::Decoration::NoSignedWrap:
371 case spirv::Decoration::NoUnsignedWrap:
372 case spirv::Decoration::RelaxedPrecision:
373 case spirv::Decoration::Restrict:
374 case spirv::Decoration::RestrictPointer:
375 case spirv::Decoration::NoContraction:
376 case spirv::Decoration::Constant:
377 case spirv::Decoration::Block:
378 case spirv::Decoration::Invariant:
379 case spirv::Decoration::Patch:
380 case spirv::Decoration::Coherent:
383 if (isa<UnitAttr, DecorationAttr>(attr))
386 "expected unit attribute or decoration attribute for ")
387 << stringifyDecoration(decoration);
388 case spirv::Decoration::CacheControlLoadINTEL:
390 loc, decoration, attr,
"CacheControlLoadINTEL",
391 [&](CacheControlLoadINTELAttr attr) {
392 unsigned cacheLevel = attr.getCacheLevel();
393 LoadCacheControl loadCacheControl = attr.getLoadCacheControl();
394 return emitDecoration(
395 resultID, decoration,
396 {cacheLevel,
static_cast<uint32_t
>(loadCacheControl)});
398 case spirv::Decoration::CacheControlStoreINTEL:
400 loc, decoration, attr,
"CacheControlStoreINTEL",
401 [&](CacheControlStoreINTELAttr attr) {
402 unsigned cacheLevel = attr.getCacheLevel();
403 StoreCacheControl storeCacheControl = attr.getStoreCacheControl();
404 return emitDecoration(
405 resultID, decoration,
406 {cacheLevel,
static_cast<uint32_t
>(storeCacheControl)});
409 return emitError(loc,
"unhandled decoration ")
410 << stringifyDecoration(decoration);
412 return emitDecoration(resultID, decoration, args);
415LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
416 NamedAttribute attr) {
417 StringRef attrName = attr.
getName().strref();
419 std::optional<Decoration> decoration =
420 spirv::symbolizeDecoration(decorationName);
423 loc,
"non-argument attributes expected to have snake-case-ified "
424 "decoration name, unhandled attribute with name : ")
427 return processDecorationAttr(loc, resultID, *decoration, attr.
getValue());
430LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
431 assert(!name.empty() &&
"unexpected empty string for OpName");
432 if (!options.emitSymbolName)
435 SmallVector<uint32_t, 4> nameOperands;
436 nameOperands.push_back(resultID);
443LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
447 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
453LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
457 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
462LogicalResult Serializer::processMemberDecoration(
467 static_cast<uint32_t
>(memberDecoration.
decoration)});
483bool Serializer::isInterfaceStructPtrType(Type type)
const {
484 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
485 switch (ptrType.getStorageClass()) {
486 case spirv::StorageClass::PhysicalStorageBuffer:
487 case spirv::StorageClass::PushConstant:
488 case spirv::StorageClass::StorageBuffer:
489 case spirv::StorageClass::Uniform:
490 return isa<spirv::StructType>(ptrType.getPointeeType());
498LogicalResult Serializer::processType(Location loc, Type type,
503 return processTypeImpl(loc, type, typeID, serializationCtx);
507Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
519 IntegerType::SignednessSemantics::Signless);
522 typeID = getTypeID(type);
526 typeID = getNextID();
527 SmallVector<uint32_t, 4> operands;
529 operands.push_back(typeID);
530 auto typeEnum = spirv::Opcode::OpTypeVoid;
531 bool deferSerialization =
false;
533 if ((isa<FunctionType>(type) &&
534 succeeded(prepareFunctionType(loc, cast<FunctionType>(type), typeEnum,
536 (isa<GraphType>(type) &&
538 prepareGraphType(loc, cast<GraphType>(type), typeEnum, operands))) ||
539 succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
540 deferSerialization, serializationCtx))) {
541 if (deferSerialization)
544 typeIDMap[type] = typeID;
548 if (recursiveStructInfos.count(type) != 0) {
551 for (
auto &ptrInfo : recursiveStructInfos[type]) {
554 SmallVector<uint32_t, 4> ptrOperands;
555 ptrOperands.push_back(ptrInfo.pointerTypeID);
556 ptrOperands.push_back(
static_cast<uint32_t
>(ptrInfo.storageClass));
557 ptrOperands.push_back(typeIDMap[type]);
563 recursiveStructInfos[type].clear();
569 return emitError(loc,
"failed to process type: ") << type;
572LogicalResult Serializer::prepareBasicType(
573 Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
574 SmallVectorImpl<uint32_t> &operands,
bool &deferSerialization,
576 deferSerialization =
false;
578 if (isVoidType(type)) {
579 typeEnum = spirv::Opcode::OpTypeVoid;
583 if (
auto intType = dyn_cast<IntegerType>(type)) {
584 if (intType.getWidth() == 1) {
585 typeEnum = spirv::Opcode::OpTypeBool;
589 typeEnum = spirv::Opcode::OpTypeInt;
590 operands.push_back(intType.getWidth());
595 operands.push_back(intType.isSigned() ? 1 : 0);
599 if (
auto floatType = dyn_cast<FloatType>(type)) {
600 typeEnum = spirv::Opcode::OpTypeFloat;
601 operands.push_back(floatType.getWidth());
602 if (floatType.isBF16()) {
603 operands.push_back(
static_cast<uint32_t
>(spirv::FPEncoding::BFloat16KHR));
605 if (floatType.isF8E4M3FN()) {
607 static_cast<uint32_t
>(spirv::FPEncoding::Float8E4M3EXT));
609 if (floatType.isF8E5M2()) {
611 static_cast<uint32_t
>(spirv::FPEncoding::Float8E5M2EXT));
617 if (
auto vectorType = dyn_cast<VectorType>(type)) {
618 uint32_t elementTypeID = 0;
619 if (
failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
620 serializationCtx))) {
623 typeEnum = spirv::Opcode::OpTypeVector;
624 operands.push_back(elementTypeID);
625 operands.push_back(vectorType.getNumElements());
629 if (
auto imageType = dyn_cast<spirv::ImageType>(type)) {
630 typeEnum = spirv::Opcode::OpTypeImage;
631 uint32_t sampledTypeID = 0;
632 if (
failed(processType(loc, imageType.getElementType(), sampledTypeID)))
635 llvm::append_values(operands, sampledTypeID,
636 static_cast<uint32_t
>(imageType.getDim()),
637 static_cast<uint32_t
>(imageType.getDepthInfo()),
638 static_cast<uint32_t
>(imageType.getArrayedInfo()),
639 static_cast<uint32_t
>(imageType.getSamplingInfo()),
640 static_cast<uint32_t
>(imageType.getSamplerUseInfo()),
641 static_cast<uint32_t
>(imageType.getImageFormat()));
645 if (
auto arrayType = dyn_cast<spirv::ArrayType>(type)) {
646 typeEnum = spirv::Opcode::OpTypeArray;
647 uint32_t elementTypeID = 0;
648 if (
failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
649 serializationCtx))) {
652 operands.push_back(elementTypeID);
653 if (
auto elementCountID = prepareConstantInt(
654 loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
655 operands.push_back(elementCountID);
657 return processTypeDecoration(loc, arrayType, resultID);
660 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
661 uint32_t pointeeTypeID = 0;
662 spirv::StructType pointeeStruct =
663 dyn_cast<spirv::StructType>(ptrType.getPointeeType());
666 serializationCtx.count(pointeeStruct.
getIdentifier()) != 0) {
671 SmallVector<uint32_t, 2> forwardPtrOperands;
672 forwardPtrOperands.push_back(resultID);
673 forwardPtrOperands.push_back(
674 static_cast<uint32_t
>(ptrType.getStorageClass()));
677 spirv::Opcode::OpTypeForwardPointer,
689 deferSerialization =
true;
693 recursiveStructInfos[structType].push_back(
694 {resultID, ptrType.getStorageClass()});
696 if (
failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
701 typeEnum = spirv::Opcode::OpTypePointer;
702 operands.push_back(
static_cast<uint32_t
>(ptrType.getStorageClass()));
703 operands.push_back(pointeeTypeID);
708 if (isInterfaceStructPtrType(ptrType)) {
709 auto structType = cast<spirv::StructType>(ptrType.getPointeeType());
710 if (!structType.hasDecoration(spirv::Decoration::Block))
711 if (
failed(emitDecoration(getTypeID(pointeeStruct),
712 spirv::Decoration::Block)))
713 return emitError(loc,
"cannot decorate ")
714 << pointeeStruct <<
" with Block decoration";
720 if (
auto runtimeArrayType = dyn_cast<spirv::RuntimeArrayType>(type)) {
721 uint32_t elementTypeID = 0;
722 if (
failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
723 elementTypeID, serializationCtx))) {
726 typeEnum = spirv::Opcode::OpTypeRuntimeArray;
727 operands.push_back(elementTypeID);
728 return processTypeDecoration(loc, runtimeArrayType, resultID);
731 if (
auto sampledImageType = dyn_cast<spirv::SampledImageType>(type)) {
732 typeEnum = spirv::Opcode::OpTypeSampledImage;
733 uint32_t imageTypeID = 0;
735 processType(loc, sampledImageType.getImageType(), imageTypeID))) {
738 operands.push_back(imageTypeID);
742 if (
auto structType = dyn_cast<spirv::StructType>(type)) {
743 if (structType.isIdentified()) {
744 if (
failed(processName(resultID, structType.getIdentifier())))
746 serializationCtx.insert(structType.getIdentifier());
749 bool hasOffset = structType.hasOffset();
750 for (
auto elementIndex :
751 llvm::seq<uint32_t>(0, structType.getNumElements())) {
752 uint32_t elementTypeID = 0;
753 if (
failed(processTypeImpl(loc, structType.getElementType(elementIndex),
754 elementTypeID, serializationCtx))) {
757 operands.push_back(elementTypeID);
759 auto intType = IntegerType::get(structType.getContext(), 32);
761 spirv::StructType::MemberDecorationInfo offsetDecoration{
762 elementIndex, spirv::Decoration::Offset,
763 IntegerAttr::get(intType,
764 structType.getMemberOffset(elementIndex))};
765 if (
failed(processMemberDecoration(resultID, offsetDecoration))) {
766 return emitError(loc,
"cannot decorate ")
767 << elementIndex <<
"-th member of " << structType
768 <<
" with its offset";
772 SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
773 structType.getMemberDecorations(memberDecorations);
775 for (
auto &memberDecoration : memberDecorations) {
776 if (
failed(processMemberDecoration(resultID, memberDecoration))) {
777 return emitError(loc,
"cannot decorate ")
778 <<
static_cast<uint32_t
>(memberDecoration.
memberIndex)
779 <<
"-th member of " << structType <<
" with "
780 << stringifyDecoration(memberDecoration.
decoration);
784 SmallVector<spirv::StructType::StructDecorationInfo, 1> structDecorations;
785 structType.getStructDecorations(structDecorations);
787 for (spirv::StructType::StructDecorationInfo &structDecoration :
789 if (
failed(processDecorationAttr(loc, resultID,
790 structDecoration.decoration,
791 structDecoration.decorationValue))) {
792 return emitError(loc,
"cannot decorate struct ")
793 << structType <<
" with "
794 << stringifyDecoration(structDecoration.decoration);
798 typeEnum = spirv::Opcode::OpTypeStruct;
800 if (structType.isIdentified())
801 serializationCtx.remove(structType.getIdentifier());
806 if (
auto cooperativeMatrixType =
807 dyn_cast<spirv::CooperativeMatrixType>(type)) {
808 uint32_t elementTypeID = 0;
809 if (
failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
810 elementTypeID, serializationCtx))) {
813 typeEnum = spirv::Opcode::OpTypeCooperativeMatrixKHR;
814 auto getConstantOp = [&](uint32_t id) {
815 auto attr = IntegerAttr::get(IntegerType::get(type.
getContext(), 32),
id);
816 return prepareConstantInt(loc, attr);
819 operands, elementTypeID,
820 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getScope())),
821 getConstantOp(cooperativeMatrixType.getRows()),
822 getConstantOp(cooperativeMatrixType.getColumns()),
823 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getUse())));
827 if (
auto matrixType = dyn_cast<spirv::MatrixType>(type)) {
828 uint32_t elementTypeID = 0;
829 if (
failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
830 serializationCtx))) {
833 typeEnum = spirv::Opcode::OpTypeMatrix;
834 llvm::append_values(operands, elementTypeID, matrixType.getNumColumns());
838 if (
auto tensorArmType = dyn_cast<TensorArmType>(type)) {
839 uint32_t elementTypeID = 0;
841 uint32_t shapeID = 0;
843 if (
failed(processTypeImpl(loc, tensorArmType.getElementType(),
844 elementTypeID, serializationCtx))) {
847 if (tensorArmType.hasRank()) {
848 ArrayRef<int64_t> dims = tensorArmType.getShape();
850 rankID = prepareConstantInt(loc, mlirBuilder.getI32IntegerAttr(rank));
855 bool shaped = llvm::all_of(dims, [](
const auto &dim) {
return dim > 0; });
856 if (rank > 0 && shaped) {
857 auto I32Type = IntegerType::get(type.
getContext(), 32);
860 SmallVector<uint64_t, 1> index(rank);
861 shapeID = prepareDenseElementsConstant(
863 mlirBuilder.getI32TensorAttr(SmallVector<int32_t>(dims)), 0,
866 shapeID = prepareArrayConstant(
868 mlirBuilder.getI32ArrayAttr(SmallVector<int32_t>(dims)));
875 typeEnum = spirv::Opcode::OpTypeTensorARM;
876 operands.push_back(elementTypeID);
879 operands.push_back(rankID);
882 operands.push_back(shapeID);
887 return emitError(loc,
"unhandled type in serialization: ") << type;
891Serializer::prepareFunctionType(Location loc, FunctionType type,
892 spirv::Opcode &typeEnum,
893 SmallVectorImpl<uint32_t> &operands) {
894 typeEnum = spirv::Opcode::OpTypeFunction;
895 assert(type.getNumResults() <= 1 &&
896 "serialization supports only a single return value");
897 uint32_t resultID = 0;
899 loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
903 operands.push_back(resultID);
904 for (
auto &res : type.getInputs()) {
905 uint32_t argTypeID = 0;
906 if (
failed(processType(loc, res, argTypeID))) {
909 operands.push_back(argTypeID);
915Serializer::prepareGraphType(Location loc, GraphType type,
916 spirv::Opcode &typeEnum,
917 SmallVectorImpl<uint32_t> &operands) {
918 typeEnum = spirv::Opcode::OpTypeGraphARM;
919 assert(type.getNumResults() >= 1 &&
920 "serialization requires at least a return value");
922 operands.push_back(type.getNumInputs());
924 for (Type argType : type.getInputs()) {
925 uint32_t argTypeID = 0;
926 if (
failed(processType(loc, argType, argTypeID)))
928 operands.push_back(argTypeID);
931 for (Type resType : type.getResults()) {
932 uint32_t resTypeID = 0;
933 if (
failed(processType(loc, resType, resTypeID)))
935 operands.push_back(resTypeID);
945uint32_t Serializer::prepareConstant(Location loc, Type constType,
946 Attribute valueAttr) {
947 if (
auto id = prepareConstantScalar(loc, valueAttr)) {
954 if (
auto id = getConstantID(valueAttr)) {
959 if (
failed(processType(loc, constType, typeID))) {
963 uint32_t resultID = 0;
964 if (
auto attr = dyn_cast<DenseElementsAttr>(valueAttr)) {
965 int rank = dyn_cast<ShapedType>(attr.getType()).getRank();
966 SmallVector<uint64_t, 4> index(rank);
967 resultID = prepareDenseElementsConstant(loc, constType, attr,
969 }
else if (
auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
970 resultID = prepareArrayConstant(loc, constType, arrayAttr);
974 emitError(loc,
"cannot serialize attribute: ") << valueAttr;
978 constIDMap[valueAttr] = resultID;
982uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
985 if (
failed(processType(loc, constType, typeID))) {
989 uint32_t resultID = getNextID();
990 SmallVector<uint32_t, 4> operands = {typeID, resultID};
991 operands.reserve(attr.size() + 2);
992 auto elementType = cast<spirv::ArrayType>(constType).getElementType();
993 for (Attribute elementAttr : attr) {
994 if (
auto elementID = prepareConstant(loc, elementType, elementAttr)) {
995 operands.push_back(elementID);
1000 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1009Serializer::prepareDenseElementsConstant(Location loc, Type constType,
1010 DenseElementsAttr valueAttr,
int dim,
1011 MutableArrayRef<uint64_t> index) {
1012 auto shapedType = dyn_cast<ShapedType>(valueAttr.
getType());
1013 assert(dim <= shapedType.getRank());
1014 if (shapedType.getRank() == dim) {
1015 if (
auto attr = dyn_cast<DenseIntElementsAttr>(valueAttr)) {
1016 return attr.getType().getElementType().isInteger(1)
1017 ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
1018 : prepareConstantInt(loc,
1019 attr.getValues<IntegerAttr>()[index]);
1021 if (
auto attr = dyn_cast<DenseFPElementsAttr>(valueAttr)) {
1022 return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
1027 uint32_t typeID = 0;
1028 if (
failed(processType(loc, constType, typeID))) {
1032 int64_t numberOfConstituents = shapedType.getDimSize(dim);
1033 uint32_t resultID = getNextID();
1034 SmallVector<uint32_t, 4> operands = {typeID, resultID};
1035 auto elementType = cast<spirv::CompositeType>(constType).getElementType(0);
1036 if (
auto tensorArmType = dyn_cast<spirv::TensorArmType>(constType)) {
1037 ArrayRef<int64_t> innerShape = tensorArmType.getShape().drop_front();
1038 if (!innerShape.empty())
1046 if (isa<spirv::CooperativeMatrixType>(constType)) {
1050 "cannot serialize a non-splat value for a cooperative matrix type");
1055 operands.reserve(3);
1058 if (
auto elementID = prepareDenseElementsConstant(
1059 loc, elementType, valueAttr, shapedType.getRank(), index)) {
1060 operands.push_back(elementID);
1064 }
else if (isa<spirv::TensorArmType>(constType) &&
isZeroValue(valueAttr)) {
1066 {typeID, resultID});
1069 operands.reserve(numberOfConstituents + 2);
1070 for (
int i = 0; i < numberOfConstituents; ++i) {
1072 if (
auto elementID = prepareDenseElementsConstant(
1073 loc, elementType, valueAttr, dim + 1, index)) {
1074 operands.push_back(elementID);
1080 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1086uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
1088 if (
auto floatAttr = dyn_cast<FloatAttr>(valueAttr)) {
1089 return prepareConstantFp(loc, floatAttr, isSpec);
1091 if (
auto boolAttr = dyn_cast<BoolAttr>(valueAttr)) {
1092 return prepareConstantBool(loc, boolAttr, isSpec);
1094 if (
auto intAttr = dyn_cast<IntegerAttr>(valueAttr)) {
1095 return prepareConstantInt(loc, intAttr, isSpec);
1101uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
1105 if (
auto id = getConstantID(boolAttr)) {
1111 uint32_t typeID = 0;
1112 if (
failed(processType(loc, cast<IntegerAttr>(boolAttr).
getType(), typeID))) {
1116 auto resultID = getNextID();
1118 ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
1119 : spirv::Opcode::OpConstantTrue)
1120 : (isSpec ? spirv::Opcode::OpSpecConstantFalse
1121 : spirv::Opcode::OpConstantFalse);
1125 constIDMap[boolAttr] = resultID;
1130uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
1134 if (
auto id = getConstantID(intAttr)) {
1140 uint32_t typeID = 0;
1141 if (
failed(processType(loc, intAttr.getType(), typeID))) {
1145 auto resultID = getNextID();
1146 APInt value = intAttr.getValue();
1147 unsigned bitwidth = value.getBitWidth();
1148 bool isSigned = intAttr.getType().isSignedInteger();
1150 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1163 word =
static_cast<int32_t
>(value.getSExtValue());
1165 word =
static_cast<uint32_t
>(value.getZExtValue());
1177 words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
1179 words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
1182 {typeID, resultID, words.word1, words.word2});
1185 std::string valueStr;
1186 llvm::raw_string_ostream rss(valueStr);
1187 value.print(rss,
false);
1190 << bitwidth <<
"-bit integer literal: " << valueStr;
1196 constIDMap[intAttr] = resultID;
1201uint32_t Serializer::prepareGraphConstantId(Location loc, Type graphConstType,
1202 IntegerAttr intAttr) {
1204 if (uint32_t
id = getGraphConstantARMId(intAttr)) {
1209 uint32_t typeID = 0;
1210 if (
failed(processType(loc, graphConstType, typeID))) {
1214 uint32_t resultID = getNextID();
1215 APInt value = intAttr.getValue();
1216 unsigned bitwidth = value.getBitWidth();
1217 if (bitwidth > 32) {
1218 emitError(loc,
"Too wide attribute for OpGraphConstantARM: ")
1219 << bitwidth <<
" bits";
1222 bool isSigned = value.isSignedIntN(bitwidth);
1226 word =
static_cast<int32_t
>(value.getSExtValue());
1228 word =
static_cast<uint32_t
>(value.getZExtValue());
1231 {typeID, resultID, word});
1232 graphConstIDMap[intAttr] = resultID;
1236uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
1240 if (
auto id = getConstantID(floatAttr)) {
1246 uint32_t typeID = 0;
1247 if (
failed(processType(loc, floatAttr.getType(), typeID))) {
1251 auto resultID = getNextID();
1252 APFloat value = floatAttr.getValue();
1253 const llvm::fltSemantics *semantics = &value.getSemantics();
1256 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1258 if (semantics == &APFloat::IEEEsingle()) {
1259 uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
1261 }
else if (semantics == &APFloat::IEEEdouble()) {
1265 } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
1267 {typeID, resultID, words.word1, words.word2});
1268 }
else if (llvm::is_contained({&APFloat::IEEEhalf(), &APFloat::BFloat(),
1269 &APFloat::Float8E4M3FN(),
1270 &APFloat::Float8E5M2()},
1273 static_cast<uint32_t
>(value.bitcastToAPInt().getZExtValue());
1276 std::string valueStr;
1277 llvm::raw_string_ostream rss(valueStr);
1281 << floatAttr.getType() <<
"-typed float literal: " << valueStr;
1286 constIDMap[floatAttr] = resultID;
1295 if (
auto typedAttr = dyn_cast<TypedAttr>(attr)) {
1296 return typedAttr.getType();
1299 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
1306uint32_t Serializer::prepareConstantCompositeReplicate(
Location loc,
1309 std::pair<Attribute, Type> valueTypePair{valueAttr, resultType};
1310 if (uint32_t
id = getConstantCompositeReplicateID(valueTypePair)) {
1314 uint32_t typeID = 0;
1315 if (
failed(processType(loc, resultType, typeID))) {
1323 auto compositeType = dyn_cast<CompositeType>(resultType);
1326 Type elementType = compositeType.getElementType(0);
1328 uint32_t constandID;
1329 if (elementType == valueType) {
1330 constandID = prepareConstant(loc, elementType, valueAttr);
1332 constandID = prepareConstantCompositeReplicate(loc, elementType, valueAttr);
1335 uint32_t resultID = getNextID();
1336 if (dyn_cast<spirv::TensorArmType>(resultType) &&
isZeroValue(valueAttr)) {
1338 {typeID, resultID});
1341 spirv::Opcode::OpConstantCompositeReplicateEXT,
1342 {typeID, resultID, constandID});
1345 constCompositeReplicateIDMap[valueTypePair] = resultID;
1353uint32_t Serializer::getOrCreateBlockID(
Block *block) {
1354 if (uint32_t
id = getBlockID(block))
1356 return blockIDMap[block] = getNextID();
1360void Serializer::printBlock(
Block *block, raw_ostream &os) {
1361 os <<
"block " << block <<
" (id = ";
1362 if (uint32_t
id = getBlockID(block))
1371Serializer::processBlock(
Block *block,
bool omitLabel,
1373 LLVM_DEBUG(llvm::dbgs() <<
"processing block " << block <<
":\n");
1374 LLVM_DEBUG(block->
print(llvm::dbgs()));
1375 LLVM_DEBUG(llvm::dbgs() <<
'\n');
1377 uint32_t blockID = getOrCreateBlockID(block);
1378 LLVM_DEBUG(printBlock(block, llvm::dbgs()));
1385 if (
failed(emitPhiForBlockArguments(block)))
1395 llvm::IsaPred<spirv::LoopOp, spirv::SelectionOp>)) {
1398 emitMerge =
nullptr;
1401 uint32_t blockID = getNextID();
1407 for (Operation &op : llvm::drop_end(*block)) {
1408 if (
failed(processOperation(&op)))
1416 if (
failed(processOperation(&block->
back())))
1422LogicalResult Serializer::emitPhiForBlockArguments(
Block *block) {
1428 LLVM_DEBUG(llvm::dbgs() <<
"emitting phi instructions..\n");
1435 SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
1437 auto *terminator = mlirPredecessor->getTerminator();
1438 LLVM_DEBUG(llvm::dbgs() <<
" mlir predecessor ");
1439 LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1440 LLVM_DEBUG(llvm::dbgs() <<
" terminator: " << *terminator <<
"\n");
1449 LLVM_DEBUG(llvm::dbgs() <<
" spirv predecessor ");
1450 LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1451 if (
auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1452 predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1453 }
else if (
auto branchCondOp =
1454 dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1455 std::optional<OperandRange> blockOperands;
1456 if (branchCondOp.getTrueTarget() == block) {
1457 blockOperands = branchCondOp.getTrueTargetOperands();
1459 assert(branchCondOp.getFalseTarget() == block);
1460 blockOperands = branchCondOp.getFalseTargetOperands();
1462 assert(!blockOperands->empty() &&
1463 "expected non-empty block operand range");
1464 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1465 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(terminator)) {
1466 std::optional<OperandRange> blockOperands;
1467 if (block == switchOp.getDefaultTarget()) {
1468 blockOperands = switchOp.getDefaultOperands();
1470 SuccessorRange targets = switchOp.getTargets();
1471 auto it = llvm::find(targets, block);
1472 assert(it != targets.end());
1473 size_t index = std::distance(targets.begin(), it);
1474 blockOperands = switchOp.getTargetOperands(index);
1476 assert(!blockOperands->empty() &&
1477 "expected non-empty block operand range");
1478 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1480 return terminator->emitError(
"unimplemented terminator for Phi creation");
1483 llvm::dbgs() <<
" block arguments:\n";
1484 for (Value v : predecessors.back().second)
1485 llvm::dbgs() <<
" " << v <<
"\n";
1490 for (
auto argIndex : llvm::seq<unsigned>(0, block->
getNumArguments())) {
1494 uint32_t phiTypeID = 0;
1497 uint32_t phiID = getNextID();
1499 LLVM_DEBUG(llvm::dbgs() <<
"[phi] for block argument #" << argIndex <<
' '
1500 << arg <<
" (id = " << phiID <<
")\n");
1503 SmallVector<uint32_t, 8> phiArgs;
1504 phiArgs.push_back(phiTypeID);
1505 phiArgs.push_back(phiID);
1507 for (
auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1508 Value value = predecessors[predIndex].second[argIndex];
1509 uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1510 LLVM_DEBUG(llvm::dbgs() <<
"[phi] use predecessor (id = " << predBlockId
1511 <<
") value " << value <<
' ');
1513 uint32_t valueId = getValueID(value);
1517 LLVM_DEBUG(llvm::dbgs() <<
"(need to fix)\n");
1518 deferredPhiValues[value].push_back(functionBody.size() + 1 +
1521 LLVM_DEBUG(llvm::dbgs() <<
"(id = " << valueId <<
")\n");
1523 phiArgs.push_back(valueId);
1525 phiArgs.push_back(predBlockId);
1529 valueIDMap[arg] = phiID;
1539LogicalResult Serializer::encodeExtensionInstruction(
1540 Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1541 ArrayRef<uint32_t> operands) {
1543 auto &setID = extendedInstSetIDMap[extensionSetName];
1545 setID = getNextID();
1546 SmallVector<uint32_t, 16> importOperands;
1547 importOperands.push_back(setID);
1555 if (operands.size() < 2) {
1556 return op->
emitError(
"extended instructions must have a result encoding");
1558 SmallVector<uint32_t, 8> extInstOperands;
1559 extInstOperands.reserve(operands.size() + 2);
1560 extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1561 extInstOperands.push_back(setID);
1562 extInstOperands.push_back(extensionOpcode);
1563 extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1569LogicalResult Serializer::processOperation(Operation *opInst) {
1570 LLVM_DEBUG(llvm::dbgs() <<
"[op] '" << opInst->
getName() <<
"'\n");
1575 .Case([&](spirv::AddressOfOp op) {
return processAddressOfOp(op); })
1576 .Case([&](spirv::BranchOp op) {
return processBranchOp(op); })
1577 .Case([&](spirv::BranchConditionalOp op) {
1578 return processBranchConditionalOp(op);
1580 .Case([&](spirv::ConstantOp op) {
return processConstantOp(op); })
1581 .Case([&](spirv::EXTConstantCompositeReplicateOp op) {
1582 return processConstantCompositeReplicateOp(op);
1584 .Case([&](spirv::FuncOp op) {
return processFuncOp(op); })
1585 .Case([&](spirv::GraphARMOp op) {
return processGraphARMOp(op); })
1586 .Case([&](spirv::GraphEntryPointARMOp op) {
1587 return processGraphEntryPointARMOp(op);
1589 .Case([&](spirv::GraphOutputsARMOp op) {
1590 return processGraphOutputsARMOp(op);
1592 .Case([&](spirv::GlobalVariableOp op) {
1593 return processGlobalVariableOp(op);
1595 .Case([&](spirv::GraphConstantARMOp op) {
1596 return processGraphConstantARMOp(op);
1598 .Case([&](spirv::LoopOp op) {
return processLoopOp(op); })
1599 .Case([&](spirv::ReferenceOfOp op) {
return processReferenceOfOp(op); })
1600 .Case([&](spirv::SelectionOp op) {
return processSelectionOp(op); })
1601 .Case([&](spirv::SpecConstantOp op) {
return processSpecConstantOp(op); })
1602 .Case([&](spirv::SpecConstantCompositeOp op) {
1603 return processSpecConstantCompositeOp(op);
1605 .Case([&](spirv::EXTSpecConstantCompositeReplicateOp op) {
1606 return processSpecConstantCompositeReplicateOp(op);
1608 .Case([&](spirv::SpecConstantOperationOp op) {
1609 return processSpecConstantOperationOp(op);
1611 .Case([&](spirv::SwitchOp op) {
return processSwitchOp(op); })
1612 .Case([&](spirv::UndefOp op) {
return processUndefOp(op); })
1613 .Case([&](spirv::VariableOp op) {
return processVariableOp(op); })
1618 [&](Operation *op) {
return dispatchToAutogenSerialization(op); });
1621LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1622 StringRef extInstSet,
1624 SmallVector<uint32_t, 4> operands;
1625 Location loc = op->
getLoc();
1627 uint32_t resultID = 0;
1629 uint32_t resultTypeID = 0;
1632 operands.push_back(resultTypeID);
1634 resultID = getNextID();
1635 operands.push_back(resultID);
1636 valueIDMap[op->
getResult(0)] = resultID;
1640 operands.push_back(getValueID(operand));
1642 if (
failed(emitDebugLine(functionBody, loc)))
1645 if (extInstSet.empty()) {
1649 if (
failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1655 if (
failed(processDecoration(loc, resultID, attr)))
1663LogicalResult Serializer::emitDecoration(uint32_t
target,
1664 spirv::Decoration decoration,
1665 ArrayRef<uint32_t> params) {
1666 uint32_t wordCount = 3 + params.size();
1667 llvm::append_values(
1670 static_cast<uint32_t
>(decoration));
1671 llvm::append_range(decorations, params);
1675LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1677 if (!options.emitDebugInfo)
1680 if (lastProcessedWasMergeInst) {
1681 lastProcessedWasMergeInst =
false;
1685 auto fileLoc = dyn_cast<FileLineColLoc>(loc);
1688 {fileID, fileLoc.getLine(), fileLoc.getColumn()});
static Block * getStructuredControlFlowOpMergeBlock(Operation *op)
Returns the merge block if the given op is a structured control flow op.
static Block * getPhiIncomingBlock(Block *block)
Given a predecessor block for a block with arguments, returns the block that should be used as the pa...
static bool isZeroValue(Attribute attr)
static void moveFuncDeclarationsToTop(spirv::ModuleOp moduleOp)
Move all functions declaration before functions definitions.
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
Location getLoc() const
Return the location for this argument.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< pred_iterator > getPredecessors()
OpListType & getOperations()
void print(raw_ostream &os)
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
llvm::iplist< Operation > OpListType
This is the list of operations in the block.
bool getValue() const
Return the boolean value of this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Operation is the basic unit of execution within MLIR.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_range getOperands()
Returns an iterator on the underlying Value's.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
unsigned getNumResults()
Return the number of results held by this operation.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
unsigned getArrayStride() const
Returns the array stride in bytes.
static ArrayType get(Type elementType, unsigned elementCount)
unsigned getArrayStride() const
Returns the array stride in bytes.
void printValueIDMap(raw_ostream &os)
(For debugging) prints each value and its corresponding result <id>.
Serializer(spirv::ModuleOp module, const SerializationOptions &options)
Creates a serializer for the given SPIR-V module.
LogicalResult serialize()
Serializes the remembered SPIR-V module.
void collect(SmallVectorImpl< uint32_t > &binary)
Collects the final SPIR-V binary.
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
bool isIdentified() const
Returns true if the StructType is identified.
StringRef getIdentifier() const
For literal structs, return an empty string.
static TensorArmType get(ArrayRef< int64_t > shape, Type elementType)
static Type getValueType(Attribute attr)
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
TargetEnvAttr lookupTargetEnvOrDefault(Operation *op)
Queries the target environment recursively from enclosing symbol table ops containing the given op or...
uint32_t getPrefixedOpcode(uint32_t wordCount, spirv::Opcode opcode)
Returns the word-count-prefixed opcode for an SPIR-V instruction.
void encodeInstructionInto(SmallVectorImpl< uint32_t > &binary, spirv::Opcode op, ArrayRef< uint32_t > operands)
Encodes an SPIR-V instruction with the given opcode and operands into the given binary vector.
void appendModuleHeader(SmallVectorImpl< uint32_t > &header, spirv::Version version, uint32_t idBound)
Appends a SPRI-V module header to header with the given version and idBound.
constexpr unsigned kHeaderWordCount
SPIR-V binary header word count.
static LogicalResult processDecorationList(Location loc, Decoration decoration, Attribute attrList, StringRef attrName, EmitF emitter)
static std::string getDecorationName(StringRef attrName)
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::SetVector< T, Vector, Set, N > SetVector
llvm::TypeSwitch< T, ResultT > TypeSwitch
llvm::function_ref< Fn > function_ref
Attribute decorationValue