github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp (about)

     1  //===- Serializer.cpp - MLIR SPIR-V Serialization -------------------------===//
     2  //
     3  // Copyright 2019 The MLIR Authors.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //   http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  // =============================================================================
    17  //
    18  // This file defines the MLIR SPIR-V module to SPIR-V binary seralization.
    19  //
    20  //===----------------------------------------------------------------------===//
    21  
    22  #include "mlir/Dialect/SPIRV/Serialization.h"
    23  
    24  #include "mlir/Dialect/SPIRV/SPIRVBinaryUtils.h"
    25  #include "mlir/Dialect/SPIRV/SPIRVDialect.h"
    26  #include "mlir/Dialect/SPIRV/SPIRVOps.h"
    27  #include "mlir/Dialect/SPIRV/SPIRVTypes.h"
    28  #include "mlir/IR/Builders.h"
    29  #include "mlir/Support/LogicalResult.h"
    30  #include "mlir/Support/StringExtras.h"
    31  #include "llvm/ADT/Sequence.h"
    32  #include "llvm/ADT/SmallVector.h"
    33  #include "llvm/ADT/bit.h"
    34  #include "llvm/Support/raw_ostream.h"
    35  
    36  using namespace mlir;
    37  
    38  /// Returns the word-count-prefixed opcode for an SPIR-V instruction.
    39  static inline uint32_t getPrefixedOpcode(uint32_t wordCount,
    40                                           spirv::Opcode opcode) {
    41    assert(((wordCount >> 16) == 0) && "word count out of range!");
    42    return (wordCount << 16) | static_cast<uint32_t>(opcode);
    43  }
    44  
    45  /// Encodes an SPIR-V instruction with the given `opcode` and `operands` into
    46  /// the given `binary` vector.
    47  static LogicalResult encodeInstructionInto(SmallVectorImpl<uint32_t> &binary,
    48                                             spirv::Opcode op,
    49                                             ArrayRef<uint32_t> operands) {
    50    uint32_t wordCount = 1 + operands.size();
    51    binary.push_back(getPrefixedOpcode(wordCount, op));
    52    if (!operands.empty()) {
    53      binary.append(operands.begin(), operands.end());
    54    }
    55    return success();
    56  }
    57  
    58  /// Encodes an SPIR-V `literal` string into the given `binary` vector.
    59  static LogicalResult encodeStringLiteralInto(SmallVectorImpl<uint32_t> &binary,
    60                                               StringRef literal) {
    61    // We need to encode the literal and the null termination.
    62    auto encodingSize = literal.size() / 4 + 1;
    63    auto bufferStartSize = binary.size();
    64    binary.resize(bufferStartSize + encodingSize, 0);
    65    std::memcpy(binary.data() + bufferStartSize, literal.data(), literal.size());
    66    return success();
    67  }
    68  
    69  namespace {
    70  
    71  /// A SPIR-V module serializer.
    72  ///
    73  /// A SPIR-V binary module is a single linear stream of instructions; each
    74  /// instruction is composed of 32-bit words with the layout:
    75  ///
    76  ///   | <word-count>|<opcode> |  <operand>   |  <operand>   | ... |
    77  ///   | <------ word -------> | <-- word --> | <-- word --> | ... |
    78  ///
    79  /// For the first word, the 16 high-order bits are the word count of the
    80  /// instruction, the 16 low-order bits are the opcode enumerant. The
    81  /// instructions then belong to different sections, which must be laid out in
    82  /// the particular order as specified in "2.4 Logical Layout of a Module" of
    83  /// the SPIR-V spec.
    84  class Serializer {
    85  public:
    86    /// Creates a serializer for the given SPIR-V `module`.
    87    explicit Serializer(spirv::ModuleOp module);
    88  
    89    /// Serializes the remembered SPIR-V module.
    90    LogicalResult serialize();
    91  
    92    /// Collects the final SPIR-V `binary`.
    93    void collect(SmallVectorImpl<uint32_t> &binary);
    94  
    95  private:
    96    // Note that there are two main categories of methods in this class:
    97    // * process*() methods are meant to fully serialize a SPIR-V module entity
    98    //   (header, type, op, etc.). They update internal vectors containing
    99    //   different binary sections. They are not meant to be called except the
   100    //   top-level serialization loop.
   101    // * prepare*() methods are meant to be helpers that prepare for serializing
   102    //   certain entity. They may or may not update internal vectors containing
   103    //   different binary sections. They are meant to be called among themselves
   104    //   or by other process*() methods for subtasks.
   105  
   106    //===--------------------------------------------------------------------===//
   107    // <id>
   108    //===--------------------------------------------------------------------===//
   109  
   110    // Note that it is illegal to use id <0> in SPIR-V binary module. Various
   111    // methods in this class, if using SPIR-V word (uint32_t) as interface,
   112    // check or return id <0> to indicate error in processing.
   113  
   114    /// Consumes the next unused <id>. This method will never return 0.
   115    uint32_t getNextID() { return nextID++; }
   116  
   117    //===--------------------------------------------------------------------===//
   118    // Module structure
   119    //===--------------------------------------------------------------------===//
   120  
   121    uint32_t findSpecConstID(StringRef constName) const {
   122      return specConstIDMap.lookup(constName);
   123    }
   124  
   125    uint32_t findVariableID(StringRef varName) const {
   126      return globalVarIDMap.lookup(varName);
   127    }
   128  
   129    uint32_t findFunctionID(StringRef fnName) const {
   130      return funcIDMap.lookup(fnName);
   131    }
   132  
   133    void processCapability();
   134  
   135    void processExtension();
   136  
   137    void processMemoryModel();
   138  
   139    LogicalResult processConstantOp(spirv::ConstantOp op);
   140  
   141    LogicalResult processSpecConstantOp(spirv::SpecConstantOp op);
   142  
   143    /// Emit OpName for the given `resultID`.
   144    LogicalResult processName(uint32_t resultID, StringRef name);
   145  
   146    /// Processes a SPIR-V function op.
   147    LogicalResult processFuncOp(FuncOp op);
   148  
   149    /// Process a SPIR-V GlobalVariableOp
   150    LogicalResult processGlobalVariableOp(spirv::GlobalVariableOp varOp);
   151  
   152    /// Process attributes that translate to decorations on the result <id>
   153    LogicalResult processDecoration(Location loc, uint32_t resultID,
   154                                    NamedAttribute attr);
   155  
   156    template <typename DType>
   157    LogicalResult processTypeDecoration(Location loc, DType type,
   158                                        uint32_t resultId) {
   159      return emitError(loc, "unhandled decoraion for type:") << type;
   160    }
   161  
   162    /// Process member decoration
   163    LogicalResult processMemberDecoration(uint32_t structID, uint32_t memberNum,
   164                                          spirv::Decoration decorationType,
   165                                          uint32_t value);
   166  
   167    //===--------------------------------------------------------------------===//
   168    // Types
   169    //===--------------------------------------------------------------------===//
   170  
   171    uint32_t findTypeID(Type type) const { return typeIDMap.lookup(type); }
   172  
   173    Type getVoidType() { return mlirBuilder.getNoneType(); }
   174  
   175    bool isVoidType(Type type) const { return type.isa<NoneType>(); }
   176  
   177    /// Returns true if the given type is a pointer type to a struct in Uniform or
   178    /// StorageBuffer storage class.
   179    bool isInterfaceStructPtrType(Type type) const;
   180  
   181    /// Main dispatch method for serializing a type. The result <id> of the
   182    /// serialized type will be returned as `typeID`.
   183    LogicalResult processType(Location loc, Type type, uint32_t &typeID);
   184  
   185    /// Method for preparing basic SPIR-V type serialization. Returns the type's
   186    /// opcode and operands for the instruction via `typeEnum` and `operands`.
   187    LogicalResult prepareBasicType(Location loc, Type type, uint32_t resultID,
   188                                   spirv::Opcode &typeEnum,
   189                                   SmallVectorImpl<uint32_t> &operands);
   190  
   191    LogicalResult prepareFunctionType(Location loc, FunctionType type,
   192                                      spirv::Opcode &typeEnum,
   193                                      SmallVectorImpl<uint32_t> &operands);
   194  
   195    //===--------------------------------------------------------------------===//
   196    // Constant
   197    //===--------------------------------------------------------------------===//
   198  
   199    uint32_t findConstantID(Attribute value) const {
   200      return constIDMap.lookup(value);
   201    }
   202  
   203    /// Main dispatch method for processing a constant with the given `constType`
   204    /// and `valueAttr`. `constType` is needed here because we can interpret the
   205    /// `valueAttr` as a different type than the type of `valueAttr` itself; for
   206    /// example, ArrayAttr, whose type is NoneType, is used for spirv::ArrayType
   207    /// constants.
   208    uint32_t prepareConstant(Location loc, Type constType, Attribute valueAttr);
   209  
   210    /// Prepares bool ElementsAttr serialization. This method updates `opcode`
   211    /// with a proper OpConstant* instruction and pushes literal values for the
   212    /// constant to `operands`.
   213    LogicalResult prepareBoolVectorConstant(Location loc,
   214                                            DenseIntElementsAttr elementsAttr,
   215                                            spirv::Opcode &opcode,
   216                                            SmallVectorImpl<uint32_t> &operands);
   217  
   218    /// Prepares int ElementsAttr serialization. This method updates `opcode` with
   219    /// a proper OpConstant* instruction and pushes literal values for the
   220    /// constant to `operands`.
   221    LogicalResult prepareIntVectorConstant(Location loc,
   222                                           DenseIntElementsAttr elementsAttr,
   223                                           spirv::Opcode &opcode,
   224                                           SmallVectorImpl<uint32_t> &operands);
   225  
   226    /// Prepares float ElementsAttr serialization. This method updates `opcode`
   227    /// with a proper OpConstant* instruction and pushes literal values for the
   228    /// constant to `operands`.
   229    LogicalResult prepareFloatVectorConstant(Location loc,
   230                                             DenseFPElementsAttr elementsAttr,
   231                                             spirv::Opcode &opcode,
   232                                             SmallVectorImpl<uint32_t> &operands);
   233  
   234    /// Prepares scalar attribute serialization. This method emits corresponding
   235    /// OpConstant* and returns the result <id> associated with it. Returns 0 if
   236    /// the attribute is not for a scalar bool/integer/float value. If `isSpec` is
   237    /// true, then the constant will be serialized as a specialization constant.
   238    uint32_t prepareConstantScalar(Location loc, Attribute valueAttr,
   239                                   bool isSpec = false);
   240  
   241    uint32_t prepareConstantBool(Location loc, BoolAttr boolAttr,
   242                                 bool isSpec = false);
   243  
   244    uint32_t prepareConstantInt(Location loc, IntegerAttr intAttr,
   245                                bool isSpec = false);
   246  
   247    uint32_t prepareConstantFp(Location loc, FloatAttr floatAttr,
   248                               bool isSpec = false);
   249  
   250    //===--------------------------------------------------------------------===//
   251    // Operations
   252    //===--------------------------------------------------------------------===//
   253  
   254    uint32_t findValueID(Value *val) const { return valueIDMap.lookup(val); }
   255  
   256    LogicalResult processAddressOfOp(spirv::AddressOfOp addressOfOp);
   257  
   258    LogicalResult processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp);
   259  
   260    /// Main dispatch method for serializing an operation.
   261    LogicalResult processOperation(Operation *op);
   262  
   263    /// Method to dispatch to the serialization function for an operation in
   264    /// SPIR-V dialect that is a mirror of an instruction in the SPIR-V spec.
   265    /// This is auto-generated from ODS. Dispatch is handled for all operations
   266    /// in SPIR-V dialect that have hasOpcode == 1.
   267    LogicalResult dispatchToAutogenSerialization(Operation *op);
   268  
   269    /// Method to serialize an operation in the SPIR-V dialect that is a mirror of
   270    /// an instruction in the SPIR-V spec. This is auto generated if hasOpcode ==
   271    /// 1 and autogenSerialization == 1 in ODS.
   272    template <typename OpTy> LogicalResult processOp(OpTy op) {
   273      return op.emitError("unsupported op serialization");
   274    }
   275  
   276  private:
   277    /// The SPIR-V module to be serialized.
   278    spirv::ModuleOp module;
   279  
   280    /// An MLIR builder for getting MLIR constructs.
   281    mlir::Builder mlirBuilder;
   282  
   283    /// The next available result <id>.
   284    uint32_t nextID = 1;
   285  
   286    // The following are for different SPIR-V instruction sections. They follow
   287    // the logical layout of a SPIR-V module.
   288  
   289    SmallVector<uint32_t, 4> capabilities;
   290    SmallVector<uint32_t, 0> extensions;
   291    SmallVector<uint32_t, 0> extendedSets;
   292    SmallVector<uint32_t, 3> memoryModel;
   293    SmallVector<uint32_t, 0> entryPoints;
   294    SmallVector<uint32_t, 4> executionModes;
   295    // TODO(antiagainst): debug instructions
   296    SmallVector<uint32_t, 0> names;
   297    SmallVector<uint32_t, 0> decorations;
   298    SmallVector<uint32_t, 0> typesGlobalValues;
   299    SmallVector<uint32_t, 0> functions;
   300  
   301    /// Map from type used in SPIR-V module to their <id>s.
   302    DenseMap<Type, uint32_t> typeIDMap;
   303  
   304    /// Map from constant values to their <id>s.
   305    DenseMap<Attribute, uint32_t> constIDMap;
   306  
   307    /// Map from specialization constant names to their <id>s.
   308    llvm::StringMap<uint32_t> specConstIDMap;
   309  
   310    /// Map from GlobalVariableOps name to <id>s.
   311    llvm::StringMap<uint32_t> globalVarIDMap;
   312  
   313    /// Map from FuncOps name to <id>s.
   314    llvm::StringMap<uint32_t> funcIDMap;
   315  
   316    /// Map from results of normal operations to their <id>s.
   317    DenseMap<Value *, uint32_t> valueIDMap;
   318  };
   319  } // namespace
   320  
   321  Serializer::Serializer(spirv::ModuleOp module)
   322      : module(module), mlirBuilder(module.getContext()) {}
   323  
   324  LogicalResult Serializer::serialize() {
   325    if (failed(module.verify()))
   326      return failure();
   327  
   328    // TODO(antiagainst): handle the other sections
   329    processCapability();
   330    processExtension();
   331    processMemoryModel();
   332  
   333    // Iterate over the module body to serialze it. Assumptions are that there is
   334    // only one basic block in the moduleOp
   335    for (auto &op : module.getBlock()) {
   336      if (failed(processOperation(&op))) {
   337        return failure();
   338      }
   339    }
   340    return success();
   341  }
   342  
   343  void Serializer::collect(SmallVectorImpl<uint32_t> &binary) {
   344    auto moduleSize = spirv::kHeaderWordCount + capabilities.size() +
   345                      extensions.size() + extendedSets.size() +
   346                      memoryModel.size() + entryPoints.size() +
   347                      executionModes.size() + decorations.size() +
   348                      typesGlobalValues.size() + functions.size();
   349  
   350    binary.clear();
   351    binary.reserve(moduleSize);
   352  
   353    spirv::appendModuleHeader(binary, nextID);
   354    binary.append(capabilities.begin(), capabilities.end());
   355    binary.append(extensions.begin(), extensions.end());
   356    binary.append(extendedSets.begin(), extendedSets.end());
   357    binary.append(memoryModel.begin(), memoryModel.end());
   358    binary.append(entryPoints.begin(), entryPoints.end());
   359    binary.append(executionModes.begin(), executionModes.end());
   360    binary.append(names.begin(), names.end());
   361    binary.append(decorations.begin(), decorations.end());
   362    binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
   363    binary.append(functions.begin(), functions.end());
   364  }
   365  //===----------------------------------------------------------------------===//
   366  // Module structure
   367  //===----------------------------------------------------------------------===//
   368  
   369  void Serializer::processCapability() {
   370    auto caps = module.getAttrOfType<ArrayAttr>("capabilities");
   371    if (!caps)
   372      return;
   373  
   374    for (auto cap : caps.getValue()) {
   375      auto capStr = cap.cast<StringAttr>().getValue();
   376      auto capVal = spirv::symbolizeCapability(capStr);
   377      encodeInstructionInto(capabilities, spirv::Opcode::OpCapability,
   378                            {static_cast<uint32_t>(*capVal)});
   379    }
   380  }
   381  
   382  void Serializer::processExtension() {
   383    auto exts = module.getAttrOfType<ArrayAttr>("extensions");
   384    if (!exts)
   385      return;
   386  
   387    SmallVector<uint32_t, 16> extName;
   388    for (auto ext : exts.getValue()) {
   389      auto extStr = ext.cast<StringAttr>().getValue();
   390      extName.clear();
   391      encodeStringLiteralInto(extName, extStr);
   392      encodeInstructionInto(extensions, spirv::Opcode::OpExtension, extName);
   393    }
   394  }
   395  
   396  void Serializer::processMemoryModel() {
   397    uint32_t mm = module.getAttrOfType<IntegerAttr>("memory_model").getInt();
   398    uint32_t am = module.getAttrOfType<IntegerAttr>("addressing_model").getInt();
   399  
   400    encodeInstructionInto(memoryModel, spirv::Opcode::OpMemoryModel, {am, mm});
   401  }
   402  
   403  LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) {
   404    if (auto resultID = prepareConstant(op.getLoc(), op.getType(), op.value())) {
   405      valueIDMap[op.getResult()] = resultID;
   406      return success();
   407    }
   408    return failure();
   409  }
   410  
   411  LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) {
   412    if (auto resultID = prepareConstantScalar(op.getLoc(), op.default_value(),
   413                                              /*isSpec=*/true)) {
   414      specConstIDMap[op.sym_name()] = resultID;
   415      return processName(resultID, op.sym_name());
   416    }
   417    return failure();
   418  }
   419  
   420  LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
   421                                              NamedAttribute attr) {
   422    auto attrName = attr.first.strref();
   423    auto decorationName = mlir::convertToCamelCase(attrName, true);
   424    auto decoration = spirv::symbolizeDecoration(decorationName);
   425    if (!decoration) {
   426      return emitError(
   427                 loc, "non-argument attributes expected to have snake-case-ified "
   428                      "decoration name, unhandled attribute with name : ")
   429             << attrName;
   430    }
   431    SmallVector<uint32_t, 1> args;
   432    args.push_back(resultID);
   433    args.push_back(static_cast<uint32_t>(decoration.getValue()));
   434    switch (decoration.getValue()) {
   435    case spirv::Decoration::DescriptorSet:
   436    case spirv::Decoration::Binding:
   437      if (auto intAttr = attr.second.dyn_cast<IntegerAttr>()) {
   438        args.push_back(intAttr.getValue().getZExtValue());
   439        break;
   440      }
   441      return emitError(loc, "expected integer attribute for ") << attrName;
   442    case spirv::Decoration::BuiltIn:
   443      if (auto strAttr = attr.second.dyn_cast<StringAttr>()) {
   444        auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
   445        if (enumVal) {
   446          args.push_back(static_cast<uint32_t>(enumVal.getValue()));
   447          break;
   448        }
   449        return emitError(loc, "invalid ")
   450               << attrName << " attribute " << strAttr.getValue();
   451      }
   452      return emitError(loc, "expected string attribute for ") << attrName;
   453    default:
   454      return emitError(loc, "unhandled decoration ") << decorationName;
   455    }
   456    return encodeInstructionInto(decorations, spirv::Opcode::OpDecorate, args);
   457  }
   458  
   459  LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
   460    assert(!name.empty() && "unexpected empty string for OpName");
   461  
   462    SmallVector<uint32_t, 4> nameOperands;
   463    nameOperands.push_back(resultID);
   464    if (failed(encodeStringLiteralInto(nameOperands, name))) {
   465      return failure();
   466    }
   467    return encodeInstructionInto(names, spirv::Opcode::OpName, nameOperands);
   468  }
   469  
   470  namespace {
   471  template <>
   472  LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
   473      Location loc, spirv::ArrayType type, uint32_t resultID) {
   474    if (type.hasLayout()) {
   475      // OpDecorate %arrayTypeSSA ArrayStride strideLiteral
   476      SmallVector<uint32_t, 3> args;
   477      args.push_back(resultID);
   478      args.push_back(static_cast<uint32_t>(spirv::Decoration::ArrayStride));
   479      args.push_back(type.getArrayStride());
   480      return encodeInstructionInto(decorations, spirv::Opcode::OpDecorate, args);
   481    }
   482    return success();
   483  }
   484  
   485  LogicalResult
   486  Serializer::processMemberDecoration(uint32_t structID, uint32_t memberIndex,
   487                                      spirv::Decoration decorationType,
   488                                      uint32_t value) {
   489    SmallVector<uint32_t, 4> args(
   490        {structID, memberIndex, static_cast<uint32_t>(decorationType), value});
   491    return encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate,
   492                                 args);
   493  }
   494  } // namespace
   495  
   496  LogicalResult Serializer::processFuncOp(FuncOp op) {
   497    uint32_t fnTypeID = 0;
   498    // Generate type of the function.
   499    processType(op.getLoc(), op.getType(), fnTypeID);
   500  
   501    // Add the function definition.
   502    SmallVector<uint32_t, 4> operands;
   503    uint32_t resTypeID = 0;
   504    auto resultTypes = op.getType().getResults();
   505    if (resultTypes.size() > 1) {
   506      return emitError(op.getLoc(),
   507                       "cannot serialize function with multiple return types");
   508    }
   509    if (failed(processType(op.getLoc(),
   510                           (resultTypes.empty() ? getVoidType() : resultTypes[0]),
   511                           resTypeID))) {
   512      return failure();
   513    }
   514    operands.push_back(resTypeID);
   515    auto funcID = getNextID();
   516    funcIDMap[op.getName()] = funcID;
   517    operands.push_back(funcID);
   518    // TODO : Support other function control options.
   519    operands.push_back(static_cast<uint32_t>(spirv::FunctionControl::None));
   520    operands.push_back(fnTypeID);
   521    encodeInstructionInto(functions, spirv::Opcode::OpFunction, operands);
   522  
   523    // Add function name.
   524    if (failed(processName(funcID, op.getName()))) {
   525      return failure();
   526    }
   527  
   528    // Declare the parameters.
   529    for (auto arg : op.getArguments()) {
   530      uint32_t argTypeID = 0;
   531      if (failed(processType(op.getLoc(), arg->getType(), argTypeID))) {
   532        return failure();
   533      }
   534      auto argValueID = getNextID();
   535      valueIDMap[arg] = argValueID;
   536      encodeInstructionInto(functions, spirv::Opcode::OpFunctionParameter,
   537                            {argTypeID, argValueID});
   538    }
   539  
   540    // Process the body.
   541    if (op.isExternal()) {
   542      return emitError(op.getLoc(), "external function is unhandled");
   543    }
   544  
   545    for (auto &b : op) {
   546      // TODO(antiagainst): support basic blocks and control flow properly.
   547      encodeInstructionInto(functions, spirv::Opcode::OpLabel, {getNextID()});
   548      for (auto &op : b) {
   549        if (failed(processOperation(&op))) {
   550          return failure();
   551        }
   552      }
   553    }
   554  
   555    // Insert Function End.
   556    return encodeInstructionInto(functions, spirv::Opcode::OpFunctionEnd, {});
   557  }
   558  
   559  LogicalResult
   560  Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
   561    // Get TypeID.
   562    uint32_t resultTypeID = 0;
   563    SmallVector<StringRef, 4> elidedAttrs;
   564    if (failed(processType(varOp.getLoc(), varOp.type(), resultTypeID))) {
   565      return failure();
   566    }
   567  
   568    if (isInterfaceStructPtrType(varOp.type())) {
   569      auto structType = varOp.type()
   570                            .cast<spirv::PointerType>()
   571                            .getPointeeType()
   572                            .cast<spirv::StructType>();
   573      SmallVector<uint32_t, 2> args{
   574          findTypeID(structType),
   575          static_cast<uint32_t>(spirv::Decoration::Block)};
   576      if (failed(encodeInstructionInto(decorations, spirv::Opcode::OpDecorate,
   577                                       args))) {
   578        return varOp.emitError("cannot decorate ")
   579               << structType << " with Block decoration";
   580      }
   581    }
   582  
   583    elidedAttrs.push_back("type");
   584    SmallVector<uint32_t, 4> operands;
   585    operands.push_back(resultTypeID);
   586    auto resultID = getNextID();
   587  
   588    // Encode the name.
   589    auto varName = varOp.sym_name();
   590    elidedAttrs.push_back(SymbolTable::getSymbolAttrName());
   591    if (failed(processName(resultID, varName))) {
   592      return failure();
   593    }
   594    globalVarIDMap[varName] = resultID;
   595    operands.push_back(resultID);
   596  
   597    // Encode StorageClass.
   598    operands.push_back(static_cast<uint32_t>(varOp.storageClass()));
   599  
   600    // Encode initialization.
   601    if (auto initializer = varOp.initializer()) {
   602      auto initializerID = findVariableID(initializer.getValue());
   603      if (!initializerID) {
   604        return emitError(varOp.getLoc(),
   605                         "invalid usage of undefined variable as initializer");
   606      }
   607      operands.push_back(initializerID);
   608      elidedAttrs.push_back("initializer");
   609    }
   610  
   611    if (failed(encodeInstructionInto(functions, spirv::Opcode::OpVariable,
   612                                     operands))) {
   613      elidedAttrs.push_back("initializer");
   614      return failure();
   615    }
   616  
   617    // Encode decorations.
   618    for (auto attr : varOp.getAttrs()) {
   619      if (llvm::any_of(elidedAttrs,
   620                       [&](StringRef elided) { return attr.first.is(elided); })) {
   621        continue;
   622      }
   623      if (failed(processDecoration(varOp.getLoc(), resultID, attr))) {
   624        return failure();
   625      }
   626    }
   627    return success();
   628  }
   629  
   630  //===----------------------------------------------------------------------===//
   631  // Type
   632  //===----------------------------------------------------------------------===//
   633  
   634  bool Serializer::isInterfaceStructPtrType(Type type) const {
   635    if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
   636      auto storageClass = ptrType.getStorageClass();
   637      if (storageClass == spirv::StorageClass::Uniform ||
   638          storageClass == spirv::StorageClass::StorageBuffer) {
   639        return ptrType.getPointeeType().isa<spirv::StructType>();
   640      }
   641    }
   642    return false;
   643  }
   644  
   645  LogicalResult Serializer::processType(Location loc, Type type,
   646                                        uint32_t &typeID) {
   647    typeID = findTypeID(type);
   648    if (typeID) {
   649      return success();
   650    }
   651    typeID = getNextID();
   652    SmallVector<uint32_t, 4> operands;
   653    operands.push_back(typeID);
   654    auto typeEnum = spirv::Opcode::OpTypeVoid;
   655    if ((type.isa<FunctionType>() &&
   656         succeeded(prepareFunctionType(loc, type.cast<FunctionType>(), typeEnum,
   657                                       operands))) ||
   658        succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands))) {
   659      typeIDMap[type] = typeID;
   660      return encodeInstructionInto(typesGlobalValues, typeEnum, operands);
   661    }
   662    return failure();
   663  }
   664  
   665  LogicalResult
   666  Serializer::prepareBasicType(Location loc, Type type, uint32_t resultID,
   667                               spirv::Opcode &typeEnum,
   668                               SmallVectorImpl<uint32_t> &operands) {
   669    if (isVoidType(type)) {
   670      typeEnum = spirv::Opcode::OpTypeVoid;
   671      return success();
   672    }
   673  
   674    if (auto intType = type.dyn_cast<IntegerType>()) {
   675      if (intType.getWidth() == 1) {
   676        typeEnum = spirv::Opcode::OpTypeBool;
   677        return success();
   678      }
   679  
   680      typeEnum = spirv::Opcode::OpTypeInt;
   681      operands.push_back(intType.getWidth());
   682      // TODO(antiagainst): support unsigned integers
   683      operands.push_back(1);
   684      return success();
   685    }
   686  
   687    if (auto floatType = type.dyn_cast<FloatType>()) {
   688      typeEnum = spirv::Opcode::OpTypeFloat;
   689      operands.push_back(floatType.getWidth());
   690      return success();
   691    }
   692  
   693    if (auto vectorType = type.dyn_cast<VectorType>()) {
   694      uint32_t elementTypeID = 0;
   695      if (failed(processType(loc, vectorType.getElementType(), elementTypeID))) {
   696        return failure();
   697      }
   698      typeEnum = spirv::Opcode::OpTypeVector;
   699      operands.push_back(elementTypeID);
   700      operands.push_back(vectorType.getNumElements());
   701      return success();
   702    }
   703  
   704    if (auto arrayType = type.dyn_cast<spirv::ArrayType>()) {
   705      typeEnum = spirv::Opcode::OpTypeArray;
   706      uint32_t elementTypeID = 0;
   707      if (failed(processType(loc, arrayType.getElementType(), elementTypeID))) {
   708        return failure();
   709      }
   710      operands.push_back(elementTypeID);
   711      if (auto elementCountID = prepareConstantInt(
   712              loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
   713        operands.push_back(elementCountID);
   714      }
   715      return processTypeDecoration(loc, arrayType, resultID);
   716    }
   717  
   718    if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
   719      uint32_t pointeeTypeID = 0;
   720      if (failed(processType(loc, ptrType.getPointeeType(), pointeeTypeID))) {
   721        return failure();
   722      }
   723      typeEnum = spirv::Opcode::OpTypePointer;
   724      operands.push_back(static_cast<uint32_t>(ptrType.getStorageClass()));
   725      operands.push_back(pointeeTypeID);
   726      return success();
   727    }
   728  
   729    if (auto structType = type.dyn_cast<spirv::StructType>()) {
   730      bool hasLayout = structType.hasLayout();
   731      for (auto elementIndex :
   732           llvm::seq<uint32_t>(0, structType.getNumElements())) {
   733        uint32_t elementTypeID = 0;
   734        if (failed(processType(loc, structType.getElementType(elementIndex),
   735                               elementTypeID))) {
   736          return failure();
   737        }
   738        operands.push_back(elementTypeID);
   739        if (hasLayout) {
   740          // Decorate each struct member with an offset
   741          if (failed(processMemberDecoration(
   742                  resultID, elementIndex, spirv::Decoration::Offset,
   743                  static_cast<uint32_t>(structType.getOffset(elementIndex))))) {
   744            return emitError(loc, "cannot decorate ")
   745                   << elementIndex << "-th member of : " << structType
   746                   << "with its offset";
   747          }
   748        }
   749      }
   750      typeEnum = spirv::Opcode::OpTypeStruct;
   751      return success();
   752    }
   753  
   754    // TODO(ravishankarm) : Handle other types.
   755    return emitError(loc, "unhandled type in serialization: ") << type;
   756  }
   757  
   758  LogicalResult
   759  Serializer::prepareFunctionType(Location loc, FunctionType type,
   760                                  spirv::Opcode &typeEnum,
   761                                  SmallVectorImpl<uint32_t> &operands) {
   762    typeEnum = spirv::Opcode::OpTypeFunction;
   763    assert(type.getNumResults() <= 1 &&
   764           "Serialization supports only a single return value");
   765    uint32_t resultID = 0;
   766    if (failed(processType(
   767            loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
   768            resultID))) {
   769      return failure();
   770    }
   771    operands.push_back(resultID);
   772    for (auto &res : type.getInputs()) {
   773      uint32_t argTypeID = 0;
   774      if (failed(processType(loc, res, argTypeID))) {
   775        return failure();
   776      }
   777      operands.push_back(argTypeID);
   778    }
   779    return success();
   780  }
   781  
   782  //===----------------------------------------------------------------------===//
   783  // Constant
   784  //===----------------------------------------------------------------------===//
   785  
   786  uint32_t Serializer::prepareConstant(Location loc, Type constType,
   787                                       Attribute valueAttr) {
   788    if (auto id = prepareConstantScalar(loc, valueAttr)) {
   789      return id;
   790    }
   791    // This is a composite literal. We need to handle each component separately
   792    // and then emit an OpConstantComposite for the whole.
   793  
   794    if (auto id = findConstantID(valueAttr)) {
   795      return id;
   796    }
   797  
   798    uint32_t typeID = 0;
   799    if (failed(processType(loc, constType, typeID))) {
   800      return 0;
   801    }
   802    auto resultID = getNextID();
   803  
   804    spirv::Opcode opcode = spirv::Opcode::OpNop;
   805    SmallVector<uint32_t, 4> operands;
   806    operands.push_back(typeID);
   807    operands.push_back(resultID);
   808  
   809    if (auto vectorAttr = valueAttr.dyn_cast<DenseIntElementsAttr>()) {
   810      if (vectorAttr.getType().getElementType().isInteger(1)) {
   811        if (failed(prepareBoolVectorConstant(loc, vectorAttr, opcode, operands)))
   812          return 0;
   813      } else if (failed(
   814                     prepareIntVectorConstant(loc, vectorAttr, opcode, operands)))
   815        return 0;
   816    } else if (auto vectorAttr = valueAttr.dyn_cast<DenseFPElementsAttr>()) {
   817      if (failed(prepareFloatVectorConstant(loc, vectorAttr, opcode, operands)))
   818        return 0;
   819    } else if (auto arrayAttr = valueAttr.dyn_cast<ArrayAttr>()) {
   820      opcode = spirv::Opcode::OpConstantComposite;
   821      operands.reserve(arrayAttr.size() + 2);
   822  
   823      auto elementType = constType.cast<spirv::ArrayType>().getElementType();
   824      for (Attribute elementAttr : arrayAttr)
   825        if (auto elementID = prepareConstant(loc, elementType, elementAttr)) {
   826          operands.push_back(elementID);
   827        } else {
   828          return 0;
   829        }
   830    } else {
   831      emitError(loc, "cannot serialize attribute: ") << valueAttr;
   832      return 0;
   833    }
   834  
   835    encodeInstructionInto(typesGlobalValues, opcode, operands);
   836    constIDMap[valueAttr] = resultID;
   837    return resultID;
   838  }
   839  
   840  LogicalResult Serializer::prepareBoolVectorConstant(
   841      Location loc, DenseIntElementsAttr elementsAttr, spirv::Opcode &opcode,
   842      SmallVectorImpl<uint32_t> &operands) {
   843    auto type = elementsAttr.getType();
   844    assert(type.hasRank() && type.getRank() == 1 &&
   845           "spv.constant should have verified only vector literal uses "
   846           "ElementsAttr");
   847    assert(type.getElementType().isInteger(1) && "must be bool ElementsAttr");
   848    auto count = type.getNumElements();
   849  
   850    // Operands for constructing the SPIR-V OpConstant* instruction
   851    operands.reserve(count + 2);
   852  
   853    // For splat cases, we don't need to loop over all elements, especially when
   854    // the splat value is zero.
   855    if (elementsAttr.isSplat()) {
   856      // We can use OpConstantNull if this bool ElementsAttr is splatting false.
   857      if (!elementsAttr.getSplatValue<bool>()) {
   858        opcode = spirv::Opcode::OpConstantNull;
   859        return success();
   860      }
   861  
   862      if (auto id =
   863              prepareConstantBool(loc, elementsAttr.getSplatValue<BoolAttr>())) {
   864        opcode = spirv::Opcode::OpConstantComposite;
   865        operands.append(count, id);
   866        return success();
   867      }
   868  
   869      return failure();
   870    }
   871  
   872    // Otherwise, we need to process each element and compose them with
   873    // OpConstantComposite.
   874    opcode = spirv::Opcode::OpConstantComposite;
   875    for (auto boolAttr : elementsAttr.getValues<BoolAttr>()) {
   876      // We are constructing an BoolAttr for each value here. But given that
   877      // we only use ElementsAttr for vectors with no more than 4 elements, it
   878      // should be fine here.
   879      if (auto elementID = prepareConstantBool(loc, boolAttr)) {
   880        operands.push_back(elementID);
   881      } else {
   882        return failure();
   883      }
   884    }
   885    return success();
   886  }
   887  
   888  LogicalResult Serializer::prepareIntVectorConstant(
   889      Location loc, DenseIntElementsAttr elementsAttr, spirv::Opcode &opcode,
   890      SmallVectorImpl<uint32_t> &operands) {
   891    auto type = elementsAttr.getType();
   892    assert(type.hasRank() && type.getRank() == 1 &&
   893           "spv.constant should have verified only vector literal uses "
   894           "ElementsAttr");
   895    assert(!type.getElementType().isInteger(1) &&
   896           "must be non-bool ElementsAttr");
   897    auto count = type.getNumElements();
   898  
   899    // Operands for constructing the SPIR-V OpConstant* instruction
   900    operands.reserve(count + 2);
   901  
   902    // For splat cases, we don't need to loop over all elements, especially when
   903    // the splat value is zero.
   904    if (elementsAttr.isSplat()) {
   905      auto splatAttr = elementsAttr.getSplatValue<IntegerAttr>();
   906  
   907      // We can use OpConstantNull if this int ElementsAttr is splatting 0.
   908      if (splatAttr.getValue().isNullValue()) {
   909        opcode = spirv::Opcode::OpConstantNull;
   910        return success();
   911      }
   912  
   913      if (auto id = prepareConstantInt(loc, splatAttr)) {
   914        opcode = spirv::Opcode::OpConstantComposite;
   915        operands.append(count, id);
   916        return success();
   917      }
   918      return failure();
   919    }
   920  
   921    // Otherwise, we need to process each element and compose them with
   922    // OpConstantComposite.
   923    opcode = spirv::Opcode::OpConstantComposite;
   924    for (auto intAttr : elementsAttr.getValues<IntegerAttr>()) {
   925      // We are constructing an IntegerAttr for each value here. But given that
   926      // we only use ElementsAttr for vectors with no more than 4 elements, it
   927      // should be fine here.
   928      // TODO(antiagainst): revisit this if special extensions enabling large
   929      // vectors are supported.
   930      if (auto elementID = prepareConstantInt(loc, intAttr)) {
   931        operands.push_back(elementID);
   932      } else {
   933        return failure();
   934      }
   935    }
   936    return success();
   937  }
   938  
   939  LogicalResult Serializer::prepareFloatVectorConstant(
   940      Location loc, DenseFPElementsAttr elementsAttr, spirv::Opcode &opcode,
   941      SmallVectorImpl<uint32_t> &operands) {
   942    auto type = elementsAttr.getType();
   943    assert(type.hasRank() && type.getRank() == 1 &&
   944           "spv.constant should have verified only vector literal uses "
   945           "ElementsAttr");
   946    auto count = type.getNumElements();
   947  
   948    operands.reserve(count + 2);
   949  
   950    if (elementsAttr.isSplat()) {
   951      FloatAttr splatAttr = elementsAttr.getSplatValue<FloatAttr>();
   952      if (splatAttr.getValue().isZero()) {
   953        opcode = spirv::Opcode::OpConstantNull;
   954        return success();
   955      }
   956  
   957      if (auto id = prepareConstantFp(loc, splatAttr)) {
   958        opcode = spirv::Opcode::OpConstantComposite;
   959        operands.append(count, id);
   960        return success();
   961      }
   962  
   963      return failure();
   964    }
   965  
   966    opcode = spirv::Opcode::OpConstantComposite;
   967    for (auto fpAttr : elementsAttr.getValues<FloatAttr>()) {
   968      if (auto elementID = prepareConstantFp(loc, fpAttr)) {
   969        operands.push_back(elementID);
   970      } else {
   971        return failure();
   972      }
   973    }
   974    return success();
   975  }
   976  
   977  uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
   978                                             bool isSpec) {
   979    if (auto floatAttr = valueAttr.dyn_cast<FloatAttr>()) {
   980      return prepareConstantFp(loc, floatAttr, isSpec);
   981    }
   982    if (auto intAttr = valueAttr.dyn_cast<IntegerAttr>()) {
   983      return prepareConstantInt(loc, intAttr, isSpec);
   984    }
   985    if (auto boolAttr = valueAttr.dyn_cast<BoolAttr>()) {
   986      return prepareConstantBool(loc, boolAttr, isSpec);
   987    }
   988  
   989    return 0;
   990  }
   991  
   992  uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
   993                                           bool isSpec) {
   994    if (!isSpec) {
   995      // We can de-duplicate nomral contants, but not specialization constants.
   996      if (auto id = findConstantID(boolAttr)) {
   997        return id;
   998      }
   999    }
  1000  
  1001    // Process the type for this bool literal
  1002    uint32_t typeID = 0;
  1003    if (failed(processType(loc, boolAttr.getType(), typeID))) {
  1004      return 0;
  1005    }
  1006  
  1007    auto resultID = getNextID();
  1008    auto opcode = boolAttr.getValue()
  1009                      ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
  1010                                : spirv::Opcode::OpConstantTrue)
  1011                      : (isSpec ? spirv::Opcode::OpSpecConstantFalse
  1012                                : spirv::Opcode::OpConstantFalse);
  1013    encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID});
  1014  
  1015    if (!isSpec) {
  1016      constIDMap[boolAttr] = resultID;
  1017    }
  1018    return resultID;
  1019  }
  1020  
  1021  uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
  1022                                          bool isSpec) {
  1023    if (!isSpec) {
  1024      // We can de-duplicate nomral contants, but not specialization constants.
  1025      if (auto id = findConstantID(intAttr)) {
  1026        return id;
  1027      }
  1028    }
  1029  
  1030    // Process the type for this integer literal
  1031    uint32_t typeID = 0;
  1032    if (failed(processType(loc, intAttr.getType(), typeID))) {
  1033      return 0;
  1034    }
  1035  
  1036    auto resultID = getNextID();
  1037    APInt value = intAttr.getValue();
  1038    unsigned bitwidth = value.getBitWidth();
  1039    bool isSigned = value.isSignedIntN(bitwidth);
  1040  
  1041    auto opcode =
  1042        isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
  1043  
  1044    // According to SPIR-V spec, "When the type's bit width is less than 32-bits,
  1045    // the literal's value appears in the low-order bits of the word, and the
  1046    // high-order bits must be 0 for a floating-point type, or 0 for an integer
  1047    // type with Signedness of 0, or sign extended when Signedness is 1."
  1048    if (bitwidth == 32 || bitwidth == 16) {
  1049      uint32_t word = 0;
  1050      if (isSigned) {
  1051        word = static_cast<int32_t>(value.getSExtValue());
  1052      } else {
  1053        word = static_cast<uint32_t>(value.getZExtValue());
  1054      }
  1055      encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
  1056    }
  1057    // According to SPIR-V spec: "When the type's bit width is larger than one
  1058    // word, the literal’s low-order words appear first."
  1059    else if (bitwidth == 64) {
  1060      struct DoubleWord {
  1061        uint32_t word1;
  1062        uint32_t word2;
  1063      } words;
  1064      if (isSigned) {
  1065        words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
  1066      } else {
  1067        words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
  1068      }
  1069      encodeInstructionInto(typesGlobalValues, opcode,
  1070                            {typeID, resultID, words.word1, words.word2});
  1071    } else {
  1072      std::string valueStr;
  1073      llvm::raw_string_ostream rss(valueStr);
  1074      value.print(rss, /*isSigned=*/false);
  1075  
  1076      emitError(loc, "cannot serialize ")
  1077          << bitwidth << "-bit integer literal: " << rss.str();
  1078      return 0;
  1079    }
  1080  
  1081    if (!isSpec) {
  1082      constIDMap[intAttr] = resultID;
  1083    }
  1084    return resultID;
  1085  }
  1086  
  1087  uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
  1088                                         bool isSpec) {
  1089    if (!isSpec) {
  1090      // We can de-duplicate nomral contants, but not specialization constants.
  1091      if (auto id = findConstantID(floatAttr)) {
  1092        return id;
  1093      }
  1094    }
  1095  
  1096    // Process the type for this float literal
  1097    uint32_t typeID = 0;
  1098    if (failed(processType(loc, floatAttr.getType(), typeID))) {
  1099      return 0;
  1100    }
  1101  
  1102    auto resultID = getNextID();
  1103    APFloat value = floatAttr.getValue();
  1104    APInt intValue = value.bitcastToAPInt();
  1105  
  1106    auto opcode =
  1107        isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
  1108  
  1109    if (&value.getSemantics() == &APFloat::IEEEsingle()) {
  1110      uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
  1111      encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
  1112    } else if (&value.getSemantics() == &APFloat::IEEEdouble()) {
  1113      struct DoubleWord {
  1114        uint32_t word1;
  1115        uint32_t word2;
  1116      } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
  1117      encodeInstructionInto(typesGlobalValues, opcode,
  1118                            {typeID, resultID, words.word1, words.word2});
  1119    } else if (&value.getSemantics() == &APFloat::IEEEhalf()) {
  1120      uint32_t word =
  1121          static_cast<uint32_t>(value.bitcastToAPInt().getZExtValue());
  1122      encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
  1123    } else {
  1124      std::string valueStr;
  1125      llvm::raw_string_ostream rss(valueStr);
  1126      value.print(rss);
  1127  
  1128      emitError(loc, "cannot serialize ")
  1129          << floatAttr.getType() << "-typed float literal: " << rss.str();
  1130      return 0;
  1131    }
  1132  
  1133    if (!isSpec) {
  1134      constIDMap[floatAttr] = resultID;
  1135    }
  1136    return resultID;
  1137  }
  1138  
  1139  //===----------------------------------------------------------------------===//
  1140  // Operation
  1141  //===----------------------------------------------------------------------===//
  1142  
  1143  LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) {
  1144    auto varName = addressOfOp.variable();
  1145    auto variableID = findVariableID(varName);
  1146    if (!variableID) {
  1147      return addressOfOp.emitError("unknown result <id> for variable ")
  1148             << varName;
  1149    }
  1150    valueIDMap[addressOfOp.pointer()] = variableID;
  1151    return success();
  1152  }
  1153  
  1154  LogicalResult
  1155  Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) {
  1156    auto constName = referenceOfOp.spec_const();
  1157    auto constID = findSpecConstID(constName);
  1158    if (!constID) {
  1159      return referenceOfOp.emitError(
  1160                 "unknown result <id> for specialization constant ")
  1161             << constName;
  1162    }
  1163    valueIDMap[referenceOfOp.reference()] = constID;
  1164    return success();
  1165  }
  1166  
  1167  LogicalResult Serializer::processOperation(Operation *op) {
  1168    // First dispatch the methods that do not directly mirror an operation from
  1169    // the SPIR-V spec
  1170    if (auto constOp = dyn_cast<spirv::ConstantOp>(op)) {
  1171      return processConstantOp(constOp);
  1172    }
  1173    if (auto specConstOp = dyn_cast<spirv::SpecConstantOp>(op)) {
  1174      return processSpecConstantOp(specConstOp);
  1175    }
  1176    if (auto refOpOp = dyn_cast<spirv::ReferenceOfOp>(op)) {
  1177      return processReferenceOfOp(refOpOp);
  1178    }
  1179    if (auto fnOp = dyn_cast<FuncOp>(op)) {
  1180      return processFuncOp(fnOp);
  1181    }
  1182    if (isa<spirv::ModuleEndOp>(op)) {
  1183      return success();
  1184    }
  1185    if (auto varOp = dyn_cast<spirv::GlobalVariableOp>(op)) {
  1186      return processGlobalVariableOp(varOp);
  1187    }
  1188    if (auto addressOfOp = dyn_cast<spirv::AddressOfOp>(op)) {
  1189      return processAddressOfOp(addressOfOp);
  1190    }
  1191    return dispatchToAutogenSerialization(op);
  1192  }
  1193  
  1194  namespace {
  1195  template <>
  1196  LogicalResult
  1197  Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) {
  1198    SmallVector<uint32_t, 4> operands;
  1199    // Add the ExectionModel.
  1200    operands.push_back(static_cast<uint32_t>(op.execution_model()));
  1201    // Add the function <id>.
  1202    auto funcID = findFunctionID(op.fn());
  1203    if (!funcID) {
  1204      return op.emitError("missing <id> for function ")
  1205             << op.fn()
  1206             << "; function needs to be defined before spv.EntryPoint is "
  1207                "serialized";
  1208    }
  1209    operands.push_back(funcID);
  1210    // Add the name of the function.
  1211    encodeStringLiteralInto(operands, op.fn());
  1212  
  1213    // Add the interface values.
  1214    if (auto interface = op.interface()) {
  1215      for (auto var : interface.getValue()) {
  1216        auto id = findVariableID(var.cast<SymbolRefAttr>().getValue());
  1217        if (!id) {
  1218          return op.emitError("referencing undefined global variable."
  1219                              "spv.EntryPoint is at the end of spv.module. All "
  1220                              "referenced variables should already be defined");
  1221        }
  1222        operands.push_back(id);
  1223      }
  1224    }
  1225    return encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint,
  1226                                 operands);
  1227  }
  1228  
  1229  template <>
  1230  LogicalResult
  1231  Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) {
  1232    SmallVector<uint32_t, 4> operands;
  1233    // Add the function <id>.
  1234    auto funcID = findFunctionID(op.fn());
  1235    if (!funcID) {
  1236      return op.emitError("missing <id> for function ")
  1237             << op.fn()
  1238             << "; function needs to be serialized before ExecutionModeOp is "
  1239                "serialized";
  1240    }
  1241    operands.push_back(funcID);
  1242    // Add the ExecutionMode.
  1243    operands.push_back(static_cast<uint32_t>(op.execution_mode()));
  1244  
  1245    // Serialize values if any.
  1246    auto values = op.values();
  1247    if (values) {
  1248      for (auto &intVal : values.getValue()) {
  1249        operands.push_back(static_cast<uint32_t>(
  1250            intVal.cast<IntegerAttr>().getValue().getZExtValue()));
  1251      }
  1252    }
  1253    return encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode,
  1254                                 operands);
  1255  }
  1256  
  1257  // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and
  1258  // various Serializer::processOp<...>() specializations.
  1259  #define GET_SERIALIZATION_FNS
  1260  #include "mlir/Dialect/SPIRV/SPIRVSerialization.inc"
  1261  } // namespace
  1262  
  1263  LogicalResult spirv::serialize(spirv::ModuleOp module,
  1264                                 SmallVectorImpl<uint32_t> &binary) {
  1265    Serializer serializer(module);
  1266  
  1267    if (failed(serializer.serialize()))
  1268      return failure();
  1269  
  1270    serializer.collect(binary);
  1271    return success();
  1272  }