xref: /llvm-project/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp (revision 095b41c6eedb3acc908dc63ee91ff77944c07d75)
188d5c4c2SKareemErgawy-TomTom //===- SerializeOps.cpp - MLIR SPIR-V Serialization (Ops) -----------------===//
288d5c4c2SKareemErgawy-TomTom //
388d5c4c2SKareemErgawy-TomTom // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
488d5c4c2SKareemErgawy-TomTom // See https://llvm.org/LICENSE.txt for license information.
588d5c4c2SKareemErgawy-TomTom // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
688d5c4c2SKareemErgawy-TomTom //
788d5c4c2SKareemErgawy-TomTom //===----------------------------------------------------------------------===//
888d5c4c2SKareemErgawy-TomTom //
988d5c4c2SKareemErgawy-TomTom // This file defines the serialization methods for MLIR SPIR-V module ops.
1088d5c4c2SKareemErgawy-TomTom //
1188d5c4c2SKareemErgawy-TomTom //===----------------------------------------------------------------------===//
1288d5c4c2SKareemErgawy-TomTom 
1388d5c4c2SKareemErgawy-TomTom #include "Serializer.h"
1488d5c4c2SKareemErgawy-TomTom 
1588d5c4c2SKareemErgawy-TomTom #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
16a29fffc4SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"
1788d5c4c2SKareemErgawy-TomTom #include "mlir/IR/RegionGraphTraits.h"
1888d5c4c2SKareemErgawy-TomTom #include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
1988d5c4c2SKareemErgawy-TomTom #include "llvm/ADT/DepthFirstIterator.h"
206d578669SMd Abdullah Shahneous Bari #include "llvm/ADT/StringExtras.h"
2188d5c4c2SKareemErgawy-TomTom #include "llvm/Support/Debug.h"
2288d5c4c2SKareemErgawy-TomTom 
2388d5c4c2SKareemErgawy-TomTom #define DEBUG_TYPE "spirv-serialization"
2488d5c4c2SKareemErgawy-TomTom 
2588d5c4c2SKareemErgawy-TomTom using namespace mlir;
2688d5c4c2SKareemErgawy-TomTom 
2788d5c4c2SKareemErgawy-TomTom /// A pre-order depth-first visitor function for processing basic blocks.
2888d5c4c2SKareemErgawy-TomTom ///
2988d5c4c2SKareemErgawy-TomTom /// Visits the basic blocks starting from the given `headerBlock` in pre-order
3088d5c4c2SKareemErgawy-TomTom /// depth-first manner and calls `blockHandler` on each block. Skips handling
3188d5c4c2SKareemErgawy-TomTom /// blocks in the `skipBlocks` list. If `skipHeader` is true, `blockHandler`
3288d5c4c2SKareemErgawy-TomTom /// will not be invoked in `headerBlock` but still handles all `headerBlock`'s
3388d5c4c2SKareemErgawy-TomTom /// successors.
3488d5c4c2SKareemErgawy-TomTom ///
3588d5c4c2SKareemErgawy-TomTom /// SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order
3688d5c4c2SKareemErgawy-TomTom /// of blocks in a function must satisfy the rule that blocks appear before
3788d5c4c2SKareemErgawy-TomTom /// all blocks they dominate." This can be achieved by a pre-order CFG
3888d5c4c2SKareemErgawy-TomTom /// traversal algorithm. To make the serialization output more logical and
3988d5c4c2SKareemErgawy-TomTom /// readable to human, we perform depth-first CFG traversal and delay the
4088d5c4c2SKareemErgawy-TomTom /// serialization of the merge block and the continue block, if exists, until
4188d5c4c2SKareemErgawy-TomTom /// after all other blocks have been processed.
4288d5c4c2SKareemErgawy-TomTom static LogicalResult
4388d5c4c2SKareemErgawy-TomTom visitInPrettyBlockOrder(Block *headerBlock,
4488d5c4c2SKareemErgawy-TomTom                         function_ref<LogicalResult(Block *)> blockHandler,
4588d5c4c2SKareemErgawy-TomTom                         bool skipHeader = false, BlockRange skipBlocks = {}) {
4688d5c4c2SKareemErgawy-TomTom   llvm::df_iterator_default_set<Block *, 4> doneBlocks;
4788d5c4c2SKareemErgawy-TomTom   doneBlocks.insert(skipBlocks.begin(), skipBlocks.end());
4888d5c4c2SKareemErgawy-TomTom 
4988d5c4c2SKareemErgawy-TomTom   for (Block *block : llvm::depth_first_ext(headerBlock, doneBlocks)) {
5088d5c4c2SKareemErgawy-TomTom     if (skipHeader && block == headerBlock)
5188d5c4c2SKareemErgawy-TomTom       continue;
5288d5c4c2SKareemErgawy-TomTom     if (failed(blockHandler(block)))
5388d5c4c2SKareemErgawy-TomTom       return failure();
5488d5c4c2SKareemErgawy-TomTom   }
5588d5c4c2SKareemErgawy-TomTom   return success();
5688d5c4c2SKareemErgawy-TomTom }
5788d5c4c2SKareemErgawy-TomTom 
5888d5c4c2SKareemErgawy-TomTom namespace mlir {
5988d5c4c2SKareemErgawy-TomTom namespace spirv {
6088d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) {
6190a1632dSJakub Kuderski   if (auto resultID =
6290a1632dSJakub Kuderski           prepareConstant(op.getLoc(), op.getType(), op.getValue())) {
6388d5c4c2SKareemErgawy-TomTom     valueIDMap[op.getResult()] = resultID;
6488d5c4c2SKareemErgawy-TomTom     return success();
6588d5c4c2SKareemErgawy-TomTom   }
6688d5c4c2SKareemErgawy-TomTom   return failure();
6788d5c4c2SKareemErgawy-TomTom }
6888d5c4c2SKareemErgawy-TomTom 
6988d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) {
7090a1632dSJakub Kuderski   if (auto resultID = prepareConstantScalar(op.getLoc(), op.getDefaultValue(),
7188d5c4c2SKareemErgawy-TomTom                                             /*isSpec=*/true)) {
7288d5c4c2SKareemErgawy-TomTom     // Emit the OpDecorate instruction for SpecId.
7388d5c4c2SKareemErgawy-TomTom     if (auto specID = op->getAttrOfType<IntegerAttr>("spec_id")) {
7488d5c4c2SKareemErgawy-TomTom       auto val = static_cast<uint32_t>(specID.getInt());
753ed47bccSLei Zhang       if (failed(emitDecoration(resultID, spirv::Decoration::SpecId, {val})))
763ed47bccSLei Zhang         return failure();
7788d5c4c2SKareemErgawy-TomTom     }
7888d5c4c2SKareemErgawy-TomTom 
7990a1632dSJakub Kuderski     specConstIDMap[op.getSymName()] = resultID;
8090a1632dSJakub Kuderski     return processName(resultID, op.getSymName());
8188d5c4c2SKareemErgawy-TomTom   }
8288d5c4c2SKareemErgawy-TomTom   return failure();
8388d5c4c2SKareemErgawy-TomTom }
8488d5c4c2SKareemErgawy-TomTom 
8588d5c4c2SKareemErgawy-TomTom LogicalResult
8688d5c4c2SKareemErgawy-TomTom Serializer::processSpecConstantCompositeOp(spirv::SpecConstantCompositeOp op) {
8788d5c4c2SKareemErgawy-TomTom   uint32_t typeID = 0;
8890a1632dSJakub Kuderski   if (failed(processType(op.getLoc(), op.getType(), typeID))) {
8988d5c4c2SKareemErgawy-TomTom     return failure();
9088d5c4c2SKareemErgawy-TomTom   }
9188d5c4c2SKareemErgawy-TomTom 
9288d5c4c2SKareemErgawy-TomTom   auto resultID = getNextID();
9388d5c4c2SKareemErgawy-TomTom 
9488d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 8> operands;
9588d5c4c2SKareemErgawy-TomTom   operands.push_back(typeID);
9688d5c4c2SKareemErgawy-TomTom   operands.push_back(resultID);
9788d5c4c2SKareemErgawy-TomTom 
9890a1632dSJakub Kuderski   auto constituents = op.getConstituents();
9988d5c4c2SKareemErgawy-TomTom 
10088d5c4c2SKareemErgawy-TomTom   for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
1015550c821STres Popp     auto constituent = dyn_cast<FlatSymbolRefAttr>(constituents[index]);
10288d5c4c2SKareemErgawy-TomTom 
10388d5c4c2SKareemErgawy-TomTom     auto constituentName = constituent.getValue();
10488d5c4c2SKareemErgawy-TomTom     auto constituentID = getSpecConstID(constituentName);
10588d5c4c2SKareemErgawy-TomTom 
10688d5c4c2SKareemErgawy-TomTom     if (!constituentID) {
10788d5c4c2SKareemErgawy-TomTom       return op.emitError("unknown result <id> for specialization constant ")
10888d5c4c2SKareemErgawy-TomTom              << constituentName;
10988d5c4c2SKareemErgawy-TomTom     }
11088d5c4c2SKareemErgawy-TomTom 
11188d5c4c2SKareemErgawy-TomTom     operands.push_back(constituentID);
11288d5c4c2SKareemErgawy-TomTom   }
11388d5c4c2SKareemErgawy-TomTom 
1143ed47bccSLei Zhang   encodeInstructionInto(typesGlobalValues,
11588d5c4c2SKareemErgawy-TomTom                         spirv::Opcode::OpSpecConstantComposite, operands);
11690a1632dSJakub Kuderski   specConstIDMap[op.getSymName()] = resultID;
11788d5c4c2SKareemErgawy-TomTom 
11890a1632dSJakub Kuderski   return processName(resultID, op.getSymName());
11988d5c4c2SKareemErgawy-TomTom }
12088d5c4c2SKareemErgawy-TomTom 
12188d5c4c2SKareemErgawy-TomTom LogicalResult
12288d5c4c2SKareemErgawy-TomTom Serializer::processSpecConstantOperationOp(spirv::SpecConstantOperationOp op) {
12388d5c4c2SKareemErgawy-TomTom   uint32_t typeID = 0;
12488d5c4c2SKareemErgawy-TomTom   if (failed(processType(op.getLoc(), op.getType(), typeID))) {
12588d5c4c2SKareemErgawy-TomTom     return failure();
12688d5c4c2SKareemErgawy-TomTom   }
12788d5c4c2SKareemErgawy-TomTom 
12888d5c4c2SKareemErgawy-TomTom   auto resultID = getNextID();
12988d5c4c2SKareemErgawy-TomTom 
13088d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 8> operands;
13188d5c4c2SKareemErgawy-TomTom   operands.push_back(typeID);
13288d5c4c2SKareemErgawy-TomTom   operands.push_back(resultID);
13388d5c4c2SKareemErgawy-TomTom 
13488d5c4c2SKareemErgawy-TomTom   Block &block = op.getRegion().getBlocks().front();
13588d5c4c2SKareemErgawy-TomTom   Operation &enclosedOp = block.getOperations().front();
13688d5c4c2SKareemErgawy-TomTom 
13788d5c4c2SKareemErgawy-TomTom   std::string enclosedOpName;
13888d5c4c2SKareemErgawy-TomTom   llvm::raw_string_ostream rss(enclosedOpName);
13988d5c4c2SKareemErgawy-TomTom   rss << "Op" << enclosedOp.getName().stripDialect();
140*095b41c6SJOE1994   auto enclosedOpcode = spirv::symbolizeOpcode(enclosedOpName);
14188d5c4c2SKareemErgawy-TomTom 
14288d5c4c2SKareemErgawy-TomTom   if (!enclosedOpcode) {
14388d5c4c2SKareemErgawy-TomTom     op.emitError("Couldn't find op code for op ")
14488d5c4c2SKareemErgawy-TomTom         << enclosedOp.getName().getStringRef();
14588d5c4c2SKareemErgawy-TomTom     return failure();
14688d5c4c2SKareemErgawy-TomTom   }
14788d5c4c2SKareemErgawy-TomTom 
1486d5fc1e3SKazu Hirata   operands.push_back(static_cast<uint32_t>(*enclosedOpcode));
14988d5c4c2SKareemErgawy-TomTom 
15088d5c4c2SKareemErgawy-TomTom   // Append operands to the enclosed op to the list of operands.
15188d5c4c2SKareemErgawy-TomTom   for (Value operand : enclosedOp.getOperands()) {
15288d5c4c2SKareemErgawy-TomTom     uint32_t id = getValueID(operand);
15388d5c4c2SKareemErgawy-TomTom     assert(id && "use before def!");
15488d5c4c2SKareemErgawy-TomTom     operands.push_back(id);
15588d5c4c2SKareemErgawy-TomTom   }
15688d5c4c2SKareemErgawy-TomTom 
1573ed47bccSLei Zhang   encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpSpecConstantOp,
1583ed47bccSLei Zhang                         operands);
15988d5c4c2SKareemErgawy-TomTom   valueIDMap[op.getResult()] = resultID;
16088d5c4c2SKareemErgawy-TomTom 
16188d5c4c2SKareemErgawy-TomTom   return success();
16288d5c4c2SKareemErgawy-TomTom }
16388d5c4c2SKareemErgawy-TomTom 
16488d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processUndefOp(spirv::UndefOp op) {
16588d5c4c2SKareemErgawy-TomTom   auto undefType = op.getType();
16688d5c4c2SKareemErgawy-TomTom   auto &id = undefValIDMap[undefType];
16788d5c4c2SKareemErgawy-TomTom   if (!id) {
16888d5c4c2SKareemErgawy-TomTom     id = getNextID();
16988d5c4c2SKareemErgawy-TomTom     uint32_t typeID = 0;
1703ed47bccSLei Zhang     if (failed(processType(op.getLoc(), undefType, typeID)))
17188d5c4c2SKareemErgawy-TomTom       return failure();
1723ed47bccSLei Zhang     encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpUndef,
1733ed47bccSLei Zhang                           {typeID, id});
17488d5c4c2SKareemErgawy-TomTom   }
17588d5c4c2SKareemErgawy-TomTom   valueIDMap[op.getResult()] = id;
17688d5c4c2SKareemErgawy-TomTom   return success();
17788d5c4c2SKareemErgawy-TomTom }
17888d5c4c2SKareemErgawy-TomTom 
179747d8fb0SKohei Yamaguchi LogicalResult Serializer::processFuncParameter(spirv::FuncOp op) {
180747d8fb0SKohei Yamaguchi   for (auto [idx, arg] : llvm::enumerate(op.getArguments())) {
181747d8fb0SKohei Yamaguchi     uint32_t argTypeID = 0;
182747d8fb0SKohei Yamaguchi     if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) {
183747d8fb0SKohei Yamaguchi       return failure();
184747d8fb0SKohei Yamaguchi     }
185747d8fb0SKohei Yamaguchi     auto argValueID = getNextID();
186747d8fb0SKohei Yamaguchi 
187747d8fb0SKohei Yamaguchi     // Process decoration attributes of arguments.
188747d8fb0SKohei Yamaguchi     auto funcOp = cast<FunctionOpInterface>(*op);
189747d8fb0SKohei Yamaguchi     for (auto argAttr : funcOp.getArgAttrs(idx)) {
190747d8fb0SKohei Yamaguchi       if (argAttr.getName() != DecorationAttr::name)
191747d8fb0SKohei Yamaguchi         continue;
192747d8fb0SKohei Yamaguchi 
193747d8fb0SKohei Yamaguchi       if (auto decAttr = dyn_cast<DecorationAttr>(argAttr.getValue())) {
194747d8fb0SKohei Yamaguchi         if (failed(processDecorationAttr(op->getLoc(), argValueID,
195747d8fb0SKohei Yamaguchi                                          decAttr.getValue(), decAttr)))
196747d8fb0SKohei Yamaguchi           return failure();
197747d8fb0SKohei Yamaguchi       }
198747d8fb0SKohei Yamaguchi     }
199747d8fb0SKohei Yamaguchi 
200747d8fb0SKohei Yamaguchi     valueIDMap[arg] = argValueID;
201747d8fb0SKohei Yamaguchi     encodeInstructionInto(functionHeader, spirv::Opcode::OpFunctionParameter,
202747d8fb0SKohei Yamaguchi                           {argTypeID, argValueID});
203747d8fb0SKohei Yamaguchi   }
204747d8fb0SKohei Yamaguchi   return success();
205747d8fb0SKohei Yamaguchi }
206747d8fb0SKohei Yamaguchi 
20788d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processFuncOp(spirv::FuncOp op) {
20888d5c4c2SKareemErgawy-TomTom   LLVM_DEBUG(llvm::dbgs() << "-- start function '" << op.getName() << "' --\n");
20988d5c4c2SKareemErgawy-TomTom   assert(functionHeader.empty() && functionBody.empty());
21088d5c4c2SKareemErgawy-TomTom 
21188d5c4c2SKareemErgawy-TomTom   uint32_t fnTypeID = 0;
21288d5c4c2SKareemErgawy-TomTom   // Generate type of the function.
2134a3460a7SRiver Riddle   if (failed(processType(op.getLoc(), op.getFunctionType(), fnTypeID)))
2143ed47bccSLei Zhang     return failure();
21588d5c4c2SKareemErgawy-TomTom 
21688d5c4c2SKareemErgawy-TomTom   // Add the function definition.
21788d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
21888d5c4c2SKareemErgawy-TomTom   uint32_t resTypeID = 0;
2194a3460a7SRiver Riddle   auto resultTypes = op.getFunctionType().getResults();
22088d5c4c2SKareemErgawy-TomTom   if (resultTypes.size() > 1) {
22188d5c4c2SKareemErgawy-TomTom     return op.emitError("cannot serialize function with multiple return types");
22288d5c4c2SKareemErgawy-TomTom   }
22388d5c4c2SKareemErgawy-TomTom   if (failed(processType(op.getLoc(),
22488d5c4c2SKareemErgawy-TomTom                          (resultTypes.empty() ? getVoidType() : resultTypes[0]),
22588d5c4c2SKareemErgawy-TomTom                          resTypeID))) {
22688d5c4c2SKareemErgawy-TomTom     return failure();
22788d5c4c2SKareemErgawy-TomTom   }
22888d5c4c2SKareemErgawy-TomTom   operands.push_back(resTypeID);
22988d5c4c2SKareemErgawy-TomTom   auto funcID = getOrCreateFunctionID(op.getName());
23088d5c4c2SKareemErgawy-TomTom   operands.push_back(funcID);
23190a1632dSJakub Kuderski   operands.push_back(static_cast<uint32_t>(op.getFunctionControl()));
23288d5c4c2SKareemErgawy-TomTom   operands.push_back(fnTypeID);
2333ed47bccSLei Zhang   encodeInstructionInto(functionHeader, spirv::Opcode::OpFunction, operands);
23488d5c4c2SKareemErgawy-TomTom 
23588d5c4c2SKareemErgawy-TomTom   // Add function name.
23688d5c4c2SKareemErgawy-TomTom   if (failed(processName(funcID, op.getName()))) {
23788d5c4c2SKareemErgawy-TomTom     return failure();
23888d5c4c2SKareemErgawy-TomTom   }
2396d578669SMd Abdullah Shahneous Bari   // Handle external functions with linkage_attributes(LinkageAttributes)
2406d578669SMd Abdullah Shahneous Bari   // differently.
2416d578669SMd Abdullah Shahneous Bari   auto linkageAttr = op.getLinkageAttributes();
2426d578669SMd Abdullah Shahneous Bari   auto hasImportLinkage =
2436d578669SMd Abdullah Shahneous Bari       linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
2446d578669SMd Abdullah Shahneous Bari                       spirv::LinkageType::Import);
2456d578669SMd Abdullah Shahneous Bari   if (op.isExternal() && !hasImportLinkage) {
2466d578669SMd Abdullah Shahneous Bari     return op.emitError(
2476d578669SMd Abdullah Shahneous Bari         "'spirv.module' cannot contain external functions "
2486d578669SMd Abdullah Shahneous Bari         "without 'Import' linkage_attributes (LinkageAttributes)");
249eb0c8de1SMehdi Amini   }
250eb0c8de1SMehdi Amini   if (op.isExternal() && hasImportLinkage) {
2516d578669SMd Abdullah Shahneous Bari     // Add an entry block to set up the block arguments
2526d578669SMd Abdullah Shahneous Bari     // to match the signature of the function.
2536d578669SMd Abdullah Shahneous Bari     // This is to generate OpFunctionParameter for functions with
2546d578669SMd Abdullah Shahneous Bari     // LinkageAttributes.
2556d578669SMd Abdullah Shahneous Bari     // WARNING: This operation has side-effect, it essentially adds a body
2566d578669SMd Abdullah Shahneous Bari     // to the func. Hence, making it not external anymore (isExternal()
2576d578669SMd Abdullah Shahneous Bari     // is going to return false for this function from now on)
2586d578669SMd Abdullah Shahneous Bari     // Hence, we'll remove the body once we are done with the serialization.
2596d578669SMd Abdullah Shahneous Bari     op.addEntryBlock();
260747d8fb0SKohei Yamaguchi     if (failed(processFuncParameter(op)))
2616d578669SMd Abdullah Shahneous Bari       return failure();
2626d578669SMd Abdullah Shahneous Bari     // Don't need to process the added block, there is nothing to process,
2636d578669SMd Abdullah Shahneous Bari     // the fake body was added just to get the arguments, remove the body,
2646d578669SMd Abdullah Shahneous Bari     // since it's use is done.
2656d578669SMd Abdullah Shahneous Bari     op.eraseBody();
2666d578669SMd Abdullah Shahneous Bari   } else {
267747d8fb0SKohei Yamaguchi     if (failed(processFuncParameter(op)))
26888d5c4c2SKareemErgawy-TomTom       return failure();
26988d5c4c2SKareemErgawy-TomTom 
27088d5c4c2SKareemErgawy-TomTom     // Some instructions (e.g., OpVariable) in a function must be in the first
2716d578669SMd Abdullah Shahneous Bari     // block in the function. These instructions will be put in
2726d578669SMd Abdullah Shahneous Bari     // functionHeader. Thus, we put the label in functionHeader first, and
2736d578669SMd Abdullah Shahneous Bari     // omit it from the first block. OpLabel only needs to be added for
2746d578669SMd Abdullah Shahneous Bari     // functions with body (including empty body). Since, we added a fake body
2756d578669SMd Abdullah Shahneous Bari     // for functions with 'Import' Linkage attributes, these functions are
2766d578669SMd Abdullah Shahneous Bari     // essentially function delcaration, so they should not have OpLabel and a
2776d578669SMd Abdullah Shahneous Bari     // terminating instruction. That's why we skipped it for those functions.
2783ed47bccSLei Zhang     encodeInstructionInto(functionHeader, spirv::Opcode::OpLabel,
27988d5c4c2SKareemErgawy-TomTom                           {getOrCreateBlockID(&op.front())});
2803ed47bccSLei Zhang     if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
2813ed47bccSLei Zhang       return failure();
28288d5c4c2SKareemErgawy-TomTom     if (failed(visitInPrettyBlockOrder(
28388d5c4c2SKareemErgawy-TomTom             &op.front(), [&](Block *block) { return processBlock(block); },
28488d5c4c2SKareemErgawy-TomTom             /*skipHeader=*/true))) {
28588d5c4c2SKareemErgawy-TomTom       return failure();
28688d5c4c2SKareemErgawy-TomTom     }
28788d5c4c2SKareemErgawy-TomTom 
2886d578669SMd Abdullah Shahneous Bari     // There might be OpPhi instructions who have value references needing to
2896d578669SMd Abdullah Shahneous Bari     // fix.
2903ed47bccSLei Zhang     for (const auto &deferredValue : deferredPhiValues) {
29188d5c4c2SKareemErgawy-TomTom       Value value = deferredValue.first;
29288d5c4c2SKareemErgawy-TomTom       uint32_t id = getValueID(value);
29388d5c4c2SKareemErgawy-TomTom       LLVM_DEBUG(llvm::dbgs() << "[phi] fix reference of value " << value
29488d5c4c2SKareemErgawy-TomTom                               << " to id = " << id << '\n');
29588d5c4c2SKareemErgawy-TomTom       assert(id && "OpPhi references undefined value!");
29688d5c4c2SKareemErgawy-TomTom       for (size_t offset : deferredValue.second)
29788d5c4c2SKareemErgawy-TomTom         functionBody[offset] = id;
29888d5c4c2SKareemErgawy-TomTom     }
29988d5c4c2SKareemErgawy-TomTom     deferredPhiValues.clear();
3006d578669SMd Abdullah Shahneous Bari   }
30188d5c4c2SKareemErgawy-TomTom   LLVM_DEBUG(llvm::dbgs() << "-- completed function '" << op.getName()
30288d5c4c2SKareemErgawy-TomTom                           << "' --\n");
3036d578669SMd Abdullah Shahneous Bari   // Insert Decorations based on Function Attributes.
3046d578669SMd Abdullah Shahneous Bari   // Only attributes we should be considering for decoration are the
3056d578669SMd Abdullah Shahneous Bari   // ::mlir::spirv::Decoration attributes.
3066d578669SMd Abdullah Shahneous Bari 
3076d578669SMd Abdullah Shahneous Bari   for (auto attr : op->getAttrs()) {
3086d578669SMd Abdullah Shahneous Bari     // Only generate OpDecorate op for spirv::Decoration attributes.
3096d578669SMd Abdullah Shahneous Bari     auto isValidDecoration = mlir::spirv::symbolizeEnum<spirv::Decoration>(
3106d578669SMd Abdullah Shahneous Bari         llvm::convertToCamelFromSnakeCase(attr.getName().strref(),
3116d578669SMd Abdullah Shahneous Bari                                           /*capitalizeFirst=*/true));
3126d578669SMd Abdullah Shahneous Bari     if (isValidDecoration != std::nullopt) {
3136d578669SMd Abdullah Shahneous Bari       if (failed(processDecoration(op.getLoc(), funcID, attr))) {
3146d578669SMd Abdullah Shahneous Bari         return failure();
3156d578669SMd Abdullah Shahneous Bari       }
3166d578669SMd Abdullah Shahneous Bari     }
3176d578669SMd Abdullah Shahneous Bari   }
31888d5c4c2SKareemErgawy-TomTom   // Insert OpFunctionEnd.
3193ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionEnd, {});
32088d5c4c2SKareemErgawy-TomTom 
32188d5c4c2SKareemErgawy-TomTom   functions.append(functionHeader.begin(), functionHeader.end());
32288d5c4c2SKareemErgawy-TomTom   functions.append(functionBody.begin(), functionBody.end());
32388d5c4c2SKareemErgawy-TomTom   functionHeader.clear();
32488d5c4c2SKareemErgawy-TomTom   functionBody.clear();
32588d5c4c2SKareemErgawy-TomTom 
32688d5c4c2SKareemErgawy-TomTom   return success();
32788d5c4c2SKareemErgawy-TomTom }
32888d5c4c2SKareemErgawy-TomTom 
32988d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processVariableOp(spirv::VariableOp op) {
33088d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
33188d5c4c2SKareemErgawy-TomTom   SmallVector<StringRef, 2> elidedAttrs;
33288d5c4c2SKareemErgawy-TomTom   uint32_t resultID = 0;
33388d5c4c2SKareemErgawy-TomTom   uint32_t resultTypeID = 0;
33488d5c4c2SKareemErgawy-TomTom   if (failed(processType(op.getLoc(), op.getType(), resultTypeID))) {
33588d5c4c2SKareemErgawy-TomTom     return failure();
33688d5c4c2SKareemErgawy-TomTom   }
33788d5c4c2SKareemErgawy-TomTom   operands.push_back(resultTypeID);
33888d5c4c2SKareemErgawy-TomTom   resultID = getNextID();
33988d5c4c2SKareemErgawy-TomTom   valueIDMap[op.getResult()] = resultID;
34088d5c4c2SKareemErgawy-TomTom   operands.push_back(resultID);
34188d5c4c2SKareemErgawy-TomTom   auto attr = op->getAttr(spirv::attributeName<spirv::StorageClass>());
34288d5c4c2SKareemErgawy-TomTom   if (attr) {
343a29fffc4SLei Zhang     operands.push_back(
3445550c821STres Popp         static_cast<uint32_t>(cast<spirv::StorageClassAttr>(attr).getValue()));
34588d5c4c2SKareemErgawy-TomTom   }
34688d5c4c2SKareemErgawy-TomTom   elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>());
34788d5c4c2SKareemErgawy-TomTom   for (auto arg : op.getODSOperands(0)) {
34888d5c4c2SKareemErgawy-TomTom     auto argID = getValueID(arg);
34988d5c4c2SKareemErgawy-TomTom     if (!argID) {
35088d5c4c2SKareemErgawy-TomTom       return emitError(op.getLoc(), "operand 0 has a use before def");
35188d5c4c2SKareemErgawy-TomTom     }
35288d5c4c2SKareemErgawy-TomTom     operands.push_back(argID);
35388d5c4c2SKareemErgawy-TomTom   }
3543ed47bccSLei Zhang   if (failed(emitDebugLine(functionHeader, op.getLoc())))
3553ed47bccSLei Zhang     return failure();
3563ed47bccSLei Zhang   encodeInstructionInto(functionHeader, spirv::Opcode::OpVariable, operands);
35788d5c4c2SKareemErgawy-TomTom   for (auto attr : op->getAttrs()) {
3580c7890c8SRiver Riddle     if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
3590c7890c8SRiver Riddle           return attr.getName() == elided;
3600c7890c8SRiver Riddle         })) {
36188d5c4c2SKareemErgawy-TomTom       continue;
36288d5c4c2SKareemErgawy-TomTom     }
36388d5c4c2SKareemErgawy-TomTom     if (failed(processDecoration(op.getLoc(), resultID, attr))) {
36488d5c4c2SKareemErgawy-TomTom       return failure();
36588d5c4c2SKareemErgawy-TomTom     }
36688d5c4c2SKareemErgawy-TomTom   }
36788d5c4c2SKareemErgawy-TomTom   return success();
36888d5c4c2SKareemErgawy-TomTom }
36988d5c4c2SKareemErgawy-TomTom 
37088d5c4c2SKareemErgawy-TomTom LogicalResult
37188d5c4c2SKareemErgawy-TomTom Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
37288d5c4c2SKareemErgawy-TomTom   // Get TypeID.
37388d5c4c2SKareemErgawy-TomTom   uint32_t resultTypeID = 0;
37488d5c4c2SKareemErgawy-TomTom   SmallVector<StringRef, 4> elidedAttrs;
37590a1632dSJakub Kuderski   if (failed(processType(varOp.getLoc(), varOp.getType(), resultTypeID))) {
37688d5c4c2SKareemErgawy-TomTom     return failure();
37788d5c4c2SKareemErgawy-TomTom   }
37888d5c4c2SKareemErgawy-TomTom 
37988d5c4c2SKareemErgawy-TomTom   elidedAttrs.push_back("type");
38088d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
38188d5c4c2SKareemErgawy-TomTom   operands.push_back(resultTypeID);
38288d5c4c2SKareemErgawy-TomTom   auto resultID = getNextID();
38388d5c4c2SKareemErgawy-TomTom 
38488d5c4c2SKareemErgawy-TomTom   // Encode the name.
38590a1632dSJakub Kuderski   auto varName = varOp.getSymName();
38688d5c4c2SKareemErgawy-TomTom   elidedAttrs.push_back(SymbolTable::getSymbolAttrName());
38788d5c4c2SKareemErgawy-TomTom   if (failed(processName(resultID, varName))) {
38888d5c4c2SKareemErgawy-TomTom     return failure();
38988d5c4c2SKareemErgawy-TomTom   }
39088d5c4c2SKareemErgawy-TomTom   globalVarIDMap[varName] = resultID;
39188d5c4c2SKareemErgawy-TomTom   operands.push_back(resultID);
39288d5c4c2SKareemErgawy-TomTom 
39388d5c4c2SKareemErgawy-TomTom   // Encode StorageClass.
39488d5c4c2SKareemErgawy-TomTom   operands.push_back(static_cast<uint32_t>(varOp.storageClass()));
39588d5c4c2SKareemErgawy-TomTom 
39688d5c4c2SKareemErgawy-TomTom   // Encode initialization.
3975e54319bSDimple Prajapati   StringRef initAttrName = varOp.getInitializerAttrName().getValue();
3985e54319bSDimple Prajapati   if (std::optional<StringRef> initSymbolName = varOp.getInitializer()) {
3995e54319bSDimple Prajapati     uint32_t initializerID = 0;
4005e54319bSDimple Prajapati     auto initRef = varOp->getAttrOfType<FlatSymbolRefAttr>(initAttrName);
4015e54319bSDimple Prajapati     Operation *initOp = SymbolTable::lookupNearestSymbolFrom(
4025e54319bSDimple Prajapati         varOp->getParentOp(), initRef.getAttr());
4035e54319bSDimple Prajapati 
4045e54319bSDimple Prajapati     // Check if initializer is GlobalVariable or SpecConstant* cases.
4055e54319bSDimple Prajapati     if (isa<spirv::GlobalVariableOp>(initOp))
4065e54319bSDimple Prajapati       initializerID = getVariableID(*initSymbolName);
4075e54319bSDimple Prajapati     else
4085e54319bSDimple Prajapati       initializerID = getSpecConstID(*initSymbolName);
4095e54319bSDimple Prajapati 
4105e54319bSDimple Prajapati     if (!initializerID)
41188d5c4c2SKareemErgawy-TomTom       return emitError(varOp.getLoc(),
41288d5c4c2SKareemErgawy-TomTom                        "invalid usage of undefined variable as initializer");
4135e54319bSDimple Prajapati 
41488d5c4c2SKareemErgawy-TomTom     operands.push_back(initializerID);
4155e54319bSDimple Prajapati     elidedAttrs.push_back(initAttrName);
41688d5c4c2SKareemErgawy-TomTom   }
41788d5c4c2SKareemErgawy-TomTom 
4183ed47bccSLei Zhang   if (failed(emitDebugLine(typesGlobalValues, varOp.getLoc())))
41988d5c4c2SKareemErgawy-TomTom     return failure();
4203ed47bccSLei Zhang   encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, operands);
4215e54319bSDimple Prajapati   elidedAttrs.push_back(initAttrName);
42288d5c4c2SKareemErgawy-TomTom 
42388d5c4c2SKareemErgawy-TomTom   // Encode decorations.
42488d5c4c2SKareemErgawy-TomTom   for (auto attr : varOp->getAttrs()) {
4250c7890c8SRiver Riddle     if (llvm::any_of(elidedAttrs, [&](StringRef elided) {
4260c7890c8SRiver Riddle           return attr.getName() == elided;
4270c7890c8SRiver Riddle         })) {
42888d5c4c2SKareemErgawy-TomTom       continue;
42988d5c4c2SKareemErgawy-TomTom     }
43088d5c4c2SKareemErgawy-TomTom     if (failed(processDecoration(varOp.getLoc(), resultID, attr))) {
43188d5c4c2SKareemErgawy-TomTom       return failure();
43288d5c4c2SKareemErgawy-TomTom     }
43388d5c4c2SKareemErgawy-TomTom   }
43488d5c4c2SKareemErgawy-TomTom   return success();
43588d5c4c2SKareemErgawy-TomTom }
43688d5c4c2SKareemErgawy-TomTom 
43788d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) {
43888d5c4c2SKareemErgawy-TomTom   // Assign <id>s to all blocks so that branches inside the SelectionOp can
43988d5c4c2SKareemErgawy-TomTom   // resolve properly.
44090a1632dSJakub Kuderski   auto &body = selectionOp.getBody();
44188d5c4c2SKareemErgawy-TomTom   for (Block &block : body)
44288d5c4c2SKareemErgawy-TomTom     getOrCreateBlockID(&block);
44388d5c4c2SKareemErgawy-TomTom 
44488d5c4c2SKareemErgawy-TomTom   auto *headerBlock = selectionOp.getHeaderBlock();
44588d5c4c2SKareemErgawy-TomTom   auto *mergeBlock = selectionOp.getMergeBlock();
4465e55a201SLei Zhang   auto headerID = getBlockID(headerBlock);
44788d5c4c2SKareemErgawy-TomTom   auto mergeID = getBlockID(mergeBlock);
44888d5c4c2SKareemErgawy-TomTom   auto loc = selectionOp.getLoc();
44988d5c4c2SKareemErgawy-TomTom 
4505e55a201SLei Zhang   // This SelectionOp is in some MLIR block with preceding and following ops. In
4515e55a201SLei Zhang   // the binary format, it should reside in separate SPIR-V blocks from its
4525e55a201SLei Zhang   // preceding and following ops. So we need to emit unconditional branches to
4535e55a201SLei Zhang   // jump to this SelectionOp's SPIR-V blocks and jumping back to the normal
4545e55a201SLei Zhang   // flow afterwards.
4555e55a201SLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
4565e55a201SLei Zhang 
45788d5c4c2SKareemErgawy-TomTom   // Emit the selection header block, which dominates all other blocks, first.
45888d5c4c2SKareemErgawy-TomTom   // We need to emit an OpSelectionMerge instruction before the selection header
45988d5c4c2SKareemErgawy-TomTom   // block's terminator.
46088d5c4c2SKareemErgawy-TomTom   auto emitSelectionMerge = [&]() {
4613ed47bccSLei Zhang     if (failed(emitDebugLine(functionBody, loc)))
4623ed47bccSLei Zhang       return failure();
46388d5c4c2SKareemErgawy-TomTom     lastProcessedWasMergeInst = true;
4643ed47bccSLei Zhang     encodeInstructionInto(
46588d5c4c2SKareemErgawy-TomTom         functionBody, spirv::Opcode::OpSelectionMerge,
46690a1632dSJakub Kuderski         {mergeID, static_cast<uint32_t>(selectionOp.getSelectionControl())});
4673ed47bccSLei Zhang     return success();
46888d5c4c2SKareemErgawy-TomTom   };
4695e55a201SLei Zhang   if (failed(
4705e55a201SLei Zhang           processBlock(headerBlock, /*omitLabel=*/false, emitSelectionMerge)))
47188d5c4c2SKareemErgawy-TomTom     return failure();
47288d5c4c2SKareemErgawy-TomTom 
47388d5c4c2SKareemErgawy-TomTom   // Process all blocks with a depth-first visitor starting from the header
47488d5c4c2SKareemErgawy-TomTom   // block. The selection header block and merge block are skipped by this
47588d5c4c2SKareemErgawy-TomTom   // visitor.
47688d5c4c2SKareemErgawy-TomTom   if (failed(visitInPrettyBlockOrder(
47788d5c4c2SKareemErgawy-TomTom           headerBlock, [&](Block *block) { return processBlock(block); },
47888d5c4c2SKareemErgawy-TomTom           /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock})))
47988d5c4c2SKareemErgawy-TomTom     return failure();
48088d5c4c2SKareemErgawy-TomTom 
48188d5c4c2SKareemErgawy-TomTom   // There is nothing to do for the merge block in the selection, which just
4825ab6ef75SJakub Kuderski   // contains a spirv.mlir.merge op, itself. But we need to have an OpLabel
48388d5c4c2SKareemErgawy-TomTom   // instruction to start a new SPIR-V block for ops following this SelectionOp.
48488d5c4c2SKareemErgawy-TomTom   // The block should use the <id> for the merge block.
4853ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
486731676b1SLei Zhang   LLVM_DEBUG(llvm::dbgs() << "done merge ");
487731676b1SLei Zhang   LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
488731676b1SLei Zhang   LLVM_DEBUG(llvm::dbgs() << "\n");
4893ed47bccSLei Zhang   return success();
49088d5c4c2SKareemErgawy-TomTom }
49188d5c4c2SKareemErgawy-TomTom 
49288d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) {
49388d5c4c2SKareemErgawy-TomTom   // Assign <id>s to all blocks so that branches inside the LoopOp can resolve
49488d5c4c2SKareemErgawy-TomTom   // properly. We don't need to assign for the entry block, which is just for
49588d5c4c2SKareemErgawy-TomTom   // satisfying MLIR region's structural requirement.
49690a1632dSJakub Kuderski   auto &body = loopOp.getBody();
4975605a1eeSKazu Hirata   for (Block &block : llvm::drop_begin(body))
49888d5c4c2SKareemErgawy-TomTom     getOrCreateBlockID(&block);
499731676b1SLei Zhang 
50088d5c4c2SKareemErgawy-TomTom   auto *headerBlock = loopOp.getHeaderBlock();
50188d5c4c2SKareemErgawy-TomTom   auto *continueBlock = loopOp.getContinueBlock();
50288d5c4c2SKareemErgawy-TomTom   auto *mergeBlock = loopOp.getMergeBlock();
50388d5c4c2SKareemErgawy-TomTom   auto headerID = getBlockID(headerBlock);
50488d5c4c2SKareemErgawy-TomTom   auto continueID = getBlockID(continueBlock);
50588d5c4c2SKareemErgawy-TomTom   auto mergeID = getBlockID(mergeBlock);
50688d5c4c2SKareemErgawy-TomTom   auto loc = loopOp.getLoc();
50788d5c4c2SKareemErgawy-TomTom 
50888d5c4c2SKareemErgawy-TomTom   // This LoopOp is in some MLIR block with preceding and following ops. In the
50988d5c4c2SKareemErgawy-TomTom   // binary format, it should reside in separate SPIR-V blocks from its
51088d5c4c2SKareemErgawy-TomTom   // preceding and following ops. So we need to emit unconditional branches to
51188d5c4c2SKareemErgawy-TomTom   // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow
51288d5c4c2SKareemErgawy-TomTom   // afterwards.
5133ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {headerID});
51488d5c4c2SKareemErgawy-TomTom 
51588d5c4c2SKareemErgawy-TomTom   // LoopOp's entry block is just there for satisfying MLIR's structural
51688d5c4c2SKareemErgawy-TomTom   // requirements so we omit it and start serialization from the loop header
51788d5c4c2SKareemErgawy-TomTom   // block.
51888d5c4c2SKareemErgawy-TomTom 
51988d5c4c2SKareemErgawy-TomTom   // Emit the loop header block, which dominates all other blocks, first. We
52088d5c4c2SKareemErgawy-TomTom   // need to emit an OpLoopMerge instruction before the loop header block's
52188d5c4c2SKareemErgawy-TomTom   // terminator.
52288d5c4c2SKareemErgawy-TomTom   auto emitLoopMerge = [&]() {
5233ed47bccSLei Zhang     if (failed(emitDebugLine(functionBody, loc)))
5243ed47bccSLei Zhang       return failure();
52588d5c4c2SKareemErgawy-TomTom     lastProcessedWasMergeInst = true;
5263ed47bccSLei Zhang     encodeInstructionInto(
52788d5c4c2SKareemErgawy-TomTom         functionBody, spirv::Opcode::OpLoopMerge,
52890a1632dSJakub Kuderski         {mergeID, continueID, static_cast<uint32_t>(loopOp.getLoopControl())});
5293ed47bccSLei Zhang     return success();
53088d5c4c2SKareemErgawy-TomTom   };
53188d5c4c2SKareemErgawy-TomTom   if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge)))
53288d5c4c2SKareemErgawy-TomTom     return failure();
53388d5c4c2SKareemErgawy-TomTom 
53488d5c4c2SKareemErgawy-TomTom   // Process all blocks with a depth-first visitor starting from the header
53588d5c4c2SKareemErgawy-TomTom   // block. The loop header block, loop continue block, and loop merge block are
53688d5c4c2SKareemErgawy-TomTom   // skipped by this visitor and handled later in this function.
53788d5c4c2SKareemErgawy-TomTom   if (failed(visitInPrettyBlockOrder(
53888d5c4c2SKareemErgawy-TomTom           headerBlock, [&](Block *block) { return processBlock(block); },
53988d5c4c2SKareemErgawy-TomTom           /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock})))
54088d5c4c2SKareemErgawy-TomTom     return failure();
54188d5c4c2SKareemErgawy-TomTom 
54288d5c4c2SKareemErgawy-TomTom   // We have handled all other blocks. Now get to the loop continue block.
54388d5c4c2SKareemErgawy-TomTom   if (failed(processBlock(continueBlock)))
54488d5c4c2SKareemErgawy-TomTom     return failure();
54588d5c4c2SKareemErgawy-TomTom 
54688d5c4c2SKareemErgawy-TomTom   // There is nothing to do for the merge block in the loop, which just contains
5475ab6ef75SJakub Kuderski   // a spirv.mlir.merge op, itself. But we need to have an OpLabel instruction
5485ab6ef75SJakub Kuderski   // to start a new SPIR-V block for ops following this LoopOp. The block should
54988d5c4c2SKareemErgawy-TomTom   // use the <id> for the merge block.
5503ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID});
551731676b1SLei Zhang   LLVM_DEBUG(llvm::dbgs() << "done merge ");
552731676b1SLei Zhang   LLVM_DEBUG(printBlock(mergeBlock, llvm::dbgs()));
553731676b1SLei Zhang   LLVM_DEBUG(llvm::dbgs() << "\n");
5543ed47bccSLei Zhang   return success();
55588d5c4c2SKareemErgawy-TomTom }
55688d5c4c2SKareemErgawy-TomTom 
55788d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processBranchConditionalOp(
55888d5c4c2SKareemErgawy-TomTom     spirv::BranchConditionalOp condBranchOp) {
55990a1632dSJakub Kuderski   auto conditionID = getValueID(condBranchOp.getCondition());
56088d5c4c2SKareemErgawy-TomTom   auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock());
56188d5c4c2SKareemErgawy-TomTom   auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock());
56288d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID};
56388d5c4c2SKareemErgawy-TomTom 
56490a1632dSJakub Kuderski   if (auto weights = condBranchOp.getBranchWeights()) {
56588d5c4c2SKareemErgawy-TomTom     for (auto val : weights->getValue())
5665550c821STres Popp       arguments.push_back(cast<IntegerAttr>(val).getInt());
56788d5c4c2SKareemErgawy-TomTom   }
56888d5c4c2SKareemErgawy-TomTom 
5693ed47bccSLei Zhang   if (failed(emitDebugLine(functionBody, condBranchOp.getLoc())))
5703ed47bccSLei Zhang     return failure();
5713ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional,
57288d5c4c2SKareemErgawy-TomTom                         arguments);
5733ed47bccSLei Zhang   return success();
57488d5c4c2SKareemErgawy-TomTom }
57588d5c4c2SKareemErgawy-TomTom 
57688d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) {
5773ed47bccSLei Zhang   if (failed(emitDebugLine(functionBody, branchOp.getLoc())))
5783ed47bccSLei Zhang     return failure();
5793ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpBranch,
58088d5c4c2SKareemErgawy-TomTom                         {getOrCreateBlockID(branchOp.getTarget())});
5813ed47bccSLei Zhang   return success();
58288d5c4c2SKareemErgawy-TomTom }
58388d5c4c2SKareemErgawy-TomTom 
58488d5c4c2SKareemErgawy-TomTom LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) {
58590a1632dSJakub Kuderski   auto varName = addressOfOp.getVariable();
58688d5c4c2SKareemErgawy-TomTom   auto variableID = getVariableID(varName);
58788d5c4c2SKareemErgawy-TomTom   if (!variableID) {
58888d5c4c2SKareemErgawy-TomTom     return addressOfOp.emitError("unknown result <id> for variable ")
58988d5c4c2SKareemErgawy-TomTom            << varName;
59088d5c4c2SKareemErgawy-TomTom   }
59190a1632dSJakub Kuderski   valueIDMap[addressOfOp.getPointer()] = variableID;
59288d5c4c2SKareemErgawy-TomTom   return success();
59388d5c4c2SKareemErgawy-TomTom }
59488d5c4c2SKareemErgawy-TomTom 
59588d5c4c2SKareemErgawy-TomTom LogicalResult
59688d5c4c2SKareemErgawy-TomTom Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) {
59790a1632dSJakub Kuderski   auto constName = referenceOfOp.getSpecConst();
59888d5c4c2SKareemErgawy-TomTom   auto constID = getSpecConstID(constName);
59988d5c4c2SKareemErgawy-TomTom   if (!constID) {
60088d5c4c2SKareemErgawy-TomTom     return referenceOfOp.emitError(
60188d5c4c2SKareemErgawy-TomTom                "unknown result <id> for specialization constant ")
60288d5c4c2SKareemErgawy-TomTom            << constName;
60388d5c4c2SKareemErgawy-TomTom   }
60490a1632dSJakub Kuderski   valueIDMap[referenceOfOp.getReference()] = constID;
60588d5c4c2SKareemErgawy-TomTom   return success();
60688d5c4c2SKareemErgawy-TomTom }
60788d5c4c2SKareemErgawy-TomTom 
60888d5c4c2SKareemErgawy-TomTom template <>
60988d5c4c2SKareemErgawy-TomTom LogicalResult
61088d5c4c2SKareemErgawy-TomTom Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) {
61188d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
61288d5c4c2SKareemErgawy-TomTom   // Add the ExecutionModel.
61390a1632dSJakub Kuderski   operands.push_back(static_cast<uint32_t>(op.getExecutionModel()));
61488d5c4c2SKareemErgawy-TomTom   // Add the function <id>.
61590a1632dSJakub Kuderski   auto funcID = getFunctionID(op.getFn());
61688d5c4c2SKareemErgawy-TomTom   if (!funcID) {
61788d5c4c2SKareemErgawy-TomTom     return op.emitError("missing <id> for function ")
61890a1632dSJakub Kuderski            << op.getFn()
6195ab6ef75SJakub Kuderski            << "; function needs to be defined before spirv.EntryPoint is "
62088d5c4c2SKareemErgawy-TomTom               "serialized";
62188d5c4c2SKareemErgawy-TomTom   }
62288d5c4c2SKareemErgawy-TomTom   operands.push_back(funcID);
62388d5c4c2SKareemErgawy-TomTom   // Add the name of the function.
62490a1632dSJakub Kuderski   spirv::encodeStringLiteralInto(operands, op.getFn());
62588d5c4c2SKareemErgawy-TomTom 
62688d5c4c2SKareemErgawy-TomTom   // Add the interface values.
62790a1632dSJakub Kuderski   if (auto interface = op.getInterface()) {
62888d5c4c2SKareemErgawy-TomTom     for (auto var : interface.getValue()) {
6295550c821STres Popp       auto id = getVariableID(cast<FlatSymbolRefAttr>(var).getValue());
63088d5c4c2SKareemErgawy-TomTom       if (!id) {
6315ab6ef75SJakub Kuderski         return op.emitError(
6325ab6ef75SJakub Kuderski             "referencing undefined global variable."
6335ab6ef75SJakub Kuderski             "spirv.EntryPoint is at the end of spirv.module. All "
63488d5c4c2SKareemErgawy-TomTom             "referenced variables should already be defined");
63588d5c4c2SKareemErgawy-TomTom       }
63688d5c4c2SKareemErgawy-TomTom       operands.push_back(id);
63788d5c4c2SKareemErgawy-TomTom     }
63888d5c4c2SKareemErgawy-TomTom   }
6393ed47bccSLei Zhang   encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, operands);
6403ed47bccSLei Zhang   return success();
64188d5c4c2SKareemErgawy-TomTom }
64288d5c4c2SKareemErgawy-TomTom 
64388d5c4c2SKareemErgawy-TomTom template <>
64488d5c4c2SKareemErgawy-TomTom LogicalResult
64588d5c4c2SKareemErgawy-TomTom Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) {
64688d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
64788d5c4c2SKareemErgawy-TomTom   // Add the function <id>.
64890a1632dSJakub Kuderski   auto funcID = getFunctionID(op.getFn());
64988d5c4c2SKareemErgawy-TomTom   if (!funcID) {
65088d5c4c2SKareemErgawy-TomTom     return op.emitError("missing <id> for function ")
65190a1632dSJakub Kuderski            << op.getFn()
65288d5c4c2SKareemErgawy-TomTom            << "; function needs to be serialized before ExecutionModeOp is "
65388d5c4c2SKareemErgawy-TomTom               "serialized";
65488d5c4c2SKareemErgawy-TomTom   }
65588d5c4c2SKareemErgawy-TomTom   operands.push_back(funcID);
65688d5c4c2SKareemErgawy-TomTom   // Add the ExecutionMode.
65790a1632dSJakub Kuderski   operands.push_back(static_cast<uint32_t>(op.getExecutionMode()));
65888d5c4c2SKareemErgawy-TomTom 
65988d5c4c2SKareemErgawy-TomTom   // Serialize values if any.
66090a1632dSJakub Kuderski   auto values = op.getValues();
66188d5c4c2SKareemErgawy-TomTom   if (values) {
66288d5c4c2SKareemErgawy-TomTom     for (auto &intVal : values.getValue()) {
66388d5c4c2SKareemErgawy-TomTom       operands.push_back(static_cast<uint32_t>(
66468f58812STres Popp           llvm::cast<IntegerAttr>(intVal).getValue().getZExtValue()));
66588d5c4c2SKareemErgawy-TomTom     }
66688d5c4c2SKareemErgawy-TomTom   }
6673ed47bccSLei Zhang   encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode,
66888d5c4c2SKareemErgawy-TomTom                         operands);
6693ed47bccSLei Zhang   return success();
67088d5c4c2SKareemErgawy-TomTom }
67188d5c4c2SKareemErgawy-TomTom 
67288d5c4c2SKareemErgawy-TomTom template <>
67388d5c4c2SKareemErgawy-TomTom LogicalResult
67488d5c4c2SKareemErgawy-TomTom Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) {
67590a1632dSJakub Kuderski   auto funcName = op.getCallee();
67688d5c4c2SKareemErgawy-TomTom   uint32_t resTypeID = 0;
67788d5c4c2SKareemErgawy-TomTom 
67888d5c4c2SKareemErgawy-TomTom   Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType();
67988d5c4c2SKareemErgawy-TomTom   if (failed(processType(op.getLoc(), resultTy, resTypeID)))
68088d5c4c2SKareemErgawy-TomTom     return failure();
68188d5c4c2SKareemErgawy-TomTom 
68288d5c4c2SKareemErgawy-TomTom   auto funcID = getOrCreateFunctionID(funcName);
68388d5c4c2SKareemErgawy-TomTom   auto funcCallID = getNextID();
68488d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID};
68588d5c4c2SKareemErgawy-TomTom 
68690a1632dSJakub Kuderski   for (auto value : op.getArguments()) {
68788d5c4c2SKareemErgawy-TomTom     auto valueID = getValueID(value);
6885ab6ef75SJakub Kuderski     assert(valueID && "cannot find a value for spirv.FunctionCall");
68988d5c4c2SKareemErgawy-TomTom     operands.push_back(valueID);
69088d5c4c2SKareemErgawy-TomTom   }
69188d5c4c2SKareemErgawy-TomTom 
6925550c821STres Popp   if (!isa<NoneType>(resultTy))
69388d5c4c2SKareemErgawy-TomTom     valueIDMap[op.getResult(0)] = funcCallID;
69488d5c4c2SKareemErgawy-TomTom 
6953ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, operands);
6963ed47bccSLei Zhang   return success();
69788d5c4c2SKareemErgawy-TomTom }
69888d5c4c2SKareemErgawy-TomTom 
69988d5c4c2SKareemErgawy-TomTom template <>
70088d5c4c2SKareemErgawy-TomTom LogicalResult
70188d5c4c2SKareemErgawy-TomTom Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
70288d5c4c2SKareemErgawy-TomTom   SmallVector<uint32_t, 4> operands;
70388d5c4c2SKareemErgawy-TomTom   SmallVector<StringRef, 2> elidedAttrs;
70488d5c4c2SKareemErgawy-TomTom 
70588d5c4c2SKareemErgawy-TomTom   for (Value operand : op->getOperands()) {
70688d5c4c2SKareemErgawy-TomTom     auto id = getValueID(operand);
70788d5c4c2SKareemErgawy-TomTom     assert(id && "use before def!");
70888d5c4c2SKareemErgawy-TomTom     operands.push_back(id);
70988d5c4c2SKareemErgawy-TomTom   }
71088d5c4c2SKareemErgawy-TomTom 
711ead0a977SSahilPatidar   StringAttr memoryAccess = op.getMemoryAccessAttrName();
712ead0a977SSahilPatidar   if (auto attr = op->getAttr(memoryAccess)) {
713a29fffc4SLei Zhang     operands.push_back(
7145550c821STres Popp         static_cast<uint32_t>(cast<spirv::MemoryAccessAttr>(attr).getValue()));
71588d5c4c2SKareemErgawy-TomTom   }
71688d5c4c2SKareemErgawy-TomTom 
717ead0a977SSahilPatidar   elidedAttrs.push_back(memoryAccess.strref());
71888d5c4c2SKareemErgawy-TomTom 
719ead0a977SSahilPatidar   StringAttr alignment = op.getAlignmentAttrName();
720ead0a977SSahilPatidar   if (auto attr = op->getAttr(alignment)) {
72188d5c4c2SKareemErgawy-TomTom     operands.push_back(static_cast<uint32_t>(
7225550c821STres Popp         cast<IntegerAttr>(attr).getValue().getZExtValue()));
72388d5c4c2SKareemErgawy-TomTom   }
72488d5c4c2SKareemErgawy-TomTom 
725ead0a977SSahilPatidar   elidedAttrs.push_back(alignment.strref());
72688d5c4c2SKareemErgawy-TomTom 
727ead0a977SSahilPatidar   StringAttr sourceMemoryAccess = op.getSourceMemoryAccessAttrName();
728ead0a977SSahilPatidar   if (auto attr = op->getAttr(sourceMemoryAccess)) {
729a29fffc4SLei Zhang     operands.push_back(
7305550c821STres Popp         static_cast<uint32_t>(cast<spirv::MemoryAccessAttr>(attr).getValue()));
73188d5c4c2SKareemErgawy-TomTom   }
73288d5c4c2SKareemErgawy-TomTom 
733ead0a977SSahilPatidar   elidedAttrs.push_back(sourceMemoryAccess.strref());
73488d5c4c2SKareemErgawy-TomTom 
735ead0a977SSahilPatidar   StringAttr sourceAlignment = op.getSourceAlignmentAttrName();
736ead0a977SSahilPatidar   if (auto attr = op->getAttr(sourceAlignment)) {
73788d5c4c2SKareemErgawy-TomTom     operands.push_back(static_cast<uint32_t>(
7385550c821STres Popp         cast<IntegerAttr>(attr).getValue().getZExtValue()));
73988d5c4c2SKareemErgawy-TomTom   }
74088d5c4c2SKareemErgawy-TomTom 
741ead0a977SSahilPatidar   elidedAttrs.push_back(sourceAlignment.strref());
7423ed47bccSLei Zhang   if (failed(emitDebugLine(functionBody, op.getLoc())))
7433ed47bccSLei Zhang     return failure();
7443ed47bccSLei Zhang   encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, operands);
74588d5c4c2SKareemErgawy-TomTom 
74688d5c4c2SKareemErgawy-TomTom   return success();
74788d5c4c2SKareemErgawy-TomTom }
7487f19e59aSNirvedh Meshram template <>
7497f19e59aSNirvedh Meshram LogicalResult Serializer::processOp<spirv::GenericCastToPtrExplicitOp>(
7507f19e59aSNirvedh Meshram     spirv::GenericCastToPtrExplicitOp op) {
7517f19e59aSNirvedh Meshram   SmallVector<uint32_t, 4> operands;
7527f19e59aSNirvedh Meshram   Type resultTy;
7537f19e59aSNirvedh Meshram   Location loc = op->getLoc();
7547f19e59aSNirvedh Meshram   uint32_t resultTypeID = 0;
7557f19e59aSNirvedh Meshram   uint32_t resultID = 0;
7567f19e59aSNirvedh Meshram   resultTy = op->getResult(0).getType();
7577f19e59aSNirvedh Meshram   if (failed(processType(loc, resultTy, resultTypeID)))
7587f19e59aSNirvedh Meshram     return failure();
7597f19e59aSNirvedh Meshram   operands.push_back(resultTypeID);
7607f19e59aSNirvedh Meshram 
7617f19e59aSNirvedh Meshram   resultID = getNextID();
7627f19e59aSNirvedh Meshram   operands.push_back(resultID);
7637f19e59aSNirvedh Meshram   valueIDMap[op->getResult(0)] = resultID;
7647f19e59aSNirvedh Meshram 
7657f19e59aSNirvedh Meshram   for (Value operand : op->getOperands())
7667f19e59aSNirvedh Meshram     operands.push_back(getValueID(operand));
7677f19e59aSNirvedh Meshram   spirv::StorageClass resultStorage =
7685550c821STres Popp       cast<spirv::PointerType>(resultTy).getStorageClass();
7697f19e59aSNirvedh Meshram   operands.push_back(static_cast<uint32_t>(resultStorage));
7707f19e59aSNirvedh Meshram   encodeInstructionInto(functionBody, spirv::Opcode::OpGenericCastToPtrExplicit,
7717f19e59aSNirvedh Meshram                         operands);
7727f19e59aSNirvedh Meshram   return success();
7737f19e59aSNirvedh Meshram }
77488d5c4c2SKareemErgawy-TomTom 
77588d5c4c2SKareemErgawy-TomTom // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and
77688d5c4c2SKareemErgawy-TomTom // various Serializer::processOp<...>() specializations.
77788d5c4c2SKareemErgawy-TomTom #define GET_SERIALIZATION_FNS
77888d5c4c2SKareemErgawy-TomTom #include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"
77988d5c4c2SKareemErgawy-TomTom 
78088d5c4c2SKareemErgawy-TomTom } // namespace spirv
78188d5c4c2SKareemErgawy-TomTom } // namespace mlir
782