2021-02-08 12:45:11 +01:00
|
|
|
|
//===- Serializer.cpp - MLIR SPIR-V Serializer ----------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// This file defines the MLIR SPIR-V module to SPIR-V binary serializer.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
#include "Serializer.h"
|
|
|
|
|
|
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h"
|
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
|
2022-08-09 14:03:54 -04:00
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"
|
2021-02-08 12:45:11 +01:00
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h"
|
|
|
|
|
#include "mlir/Support/LogicalResult.h"
|
|
|
|
|
#include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
|
|
|
|
|
#include "llvm/ADT/Sequence.h"
|
|
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
|
|
|
#include "llvm/ADT/bit.h"
|
|
|
|
|
#include "llvm/Support/Debug.h"
|
2022-08-09 14:03:54 -04:00
|
|
|
|
#include <cstdint>
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "spirv-serialization"
|
|
|
|
|
|
|
|
|
|
using namespace mlir;
|
|
|
|
|
|
|
|
|
|
/// Returns the merge block if the given `op` is a structured control flow op.
|
|
|
|
|
/// Otherwise returns nullptr.
|
|
|
|
|
static Block *getStructuredControlFlowOpMergeBlock(Operation *op) {
|
|
|
|
|
if (auto selectionOp = dyn_cast<spirv::SelectionOp>(op))
|
|
|
|
|
return selectionOp.getMergeBlock();
|
|
|
|
|
if (auto loopOp = dyn_cast<spirv::LoopOp>(op))
|
|
|
|
|
return loopOp.getMergeBlock();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Given a predecessor `block` for a block with arguments, returns the block
|
|
|
|
|
/// that should be used as the parent block for SPIR-V OpPhi instructions
|
|
|
|
|
/// corresponding to the block arguments.
|
|
|
|
|
static Block *getPhiIncomingBlock(Block *block) {
|
2021-03-05 15:35:35 -05:00
|
|
|
|
// If the predecessor block in question is the entry block for a
|
|
|
|
|
// spv.mlir.loop, we jump to this spv.mlir.loop from its enclosing block.
|
2021-02-08 12:45:11 +01:00
|
|
|
|
if (block->isEntryBlock()) {
|
|
|
|
|
if (auto loopOp = dyn_cast<spirv::LoopOp>(block->getParentOp())) {
|
|
|
|
|
// Then the incoming parent block for OpPhi should be the merge block of
|
|
|
|
|
// the structured control flow op before this loop.
|
|
|
|
|
Operation *op = loopOp.getOperation();
|
|
|
|
|
while ((op = op->getPrevNode()) != nullptr)
|
|
|
|
|
if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(op))
|
|
|
|
|
return incomingBlock;
|
|
|
|
|
// Or the enclosing block itself if no structured control flow ops
|
|
|
|
|
// exists before this loop.
|
|
|
|
|
return loopOp->getBlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, we jump from the given predecessor block. Try to see if there is
|
|
|
|
|
// a structured control flow op inside it.
|
|
|
|
|
for (Operation &op : llvm::reverse(block->getOperations())) {
|
|
|
|
|
if (Block *incomingBlock = getStructuredControlFlowOpMergeBlock(&op))
|
|
|
|
|
return incomingBlock;
|
|
|
|
|
}
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace mlir {
|
|
|
|
|
namespace spirv {
|
|
|
|
|
|
|
|
|
|
/// Encodes an SPIR-V instruction with the given `opcode` and `operands` into
|
|
|
|
|
/// the given `binary` vector.
|
2021-12-10 18:55:44 -05:00
|
|
|
|
void encodeInstructionInto(SmallVectorImpl<uint32_t> &binary, spirv::Opcode op,
|
|
|
|
|
ArrayRef<uint32_t> operands) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
uint32_t wordCount = 1 + operands.size();
|
|
|
|
|
binary.push_back(spirv::getPrefixedOpcode(wordCount, op));
|
|
|
|
|
binary.append(operands.begin(), operands.end());
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 13:57:29 -05:00
|
|
|
|
Serializer::Serializer(spirv::ModuleOp module,
|
|
|
|
|
const SerializationOptions &options)
|
|
|
|
|
: module(module), mlirBuilder(module.getContext()), options(options) {}
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::serialize() {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "+++ starting serialization +++\n");
|
|
|
|
|
|
2022-02-01 15:01:30 -08:00
|
|
|
|
if (failed(module.verifyInvariants()))
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
// TODO: handle the other sections
|
|
|
|
|
processCapability();
|
|
|
|
|
processExtension();
|
|
|
|
|
processMemoryModel();
|
|
|
|
|
processDebugInfo();
|
|
|
|
|
|
|
|
|
|
// Iterate over the module body to serialize it. Assumptions are that there is
|
|
|
|
|
// only one basic block in the moduleOp
|
2021-06-09 13:58:13 -04:00
|
|
|
|
for (auto &op : *module.getBody()) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
if (failed(processOperation(&op))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "+++ completed serialization +++\n");
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Serializer::collect(SmallVectorImpl<uint32_t> &binary) {
|
|
|
|
|
auto moduleSize = spirv::kHeaderWordCount + capabilities.size() +
|
|
|
|
|
extensions.size() + extendedSets.size() +
|
|
|
|
|
memoryModel.size() + entryPoints.size() +
|
|
|
|
|
executionModes.size() + decorations.size() +
|
|
|
|
|
typesGlobalValues.size() + functions.size();
|
|
|
|
|
|
|
|
|
|
binary.clear();
|
|
|
|
|
binary.reserve(moduleSize);
|
|
|
|
|
|
|
|
|
|
spirv::appendModuleHeader(binary, module.vce_triple()->getVersion(), nextID);
|
|
|
|
|
binary.append(capabilities.begin(), capabilities.end());
|
|
|
|
|
binary.append(extensions.begin(), extensions.end());
|
|
|
|
|
binary.append(extendedSets.begin(), extendedSets.end());
|
|
|
|
|
binary.append(memoryModel.begin(), memoryModel.end());
|
|
|
|
|
binary.append(entryPoints.begin(), entryPoints.end());
|
|
|
|
|
binary.append(executionModes.begin(), executionModes.end());
|
|
|
|
|
binary.append(debug.begin(), debug.end());
|
|
|
|
|
binary.append(names.begin(), names.end());
|
|
|
|
|
binary.append(decorations.begin(), decorations.end());
|
|
|
|
|
binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
|
|
|
|
|
binary.append(functions.begin(), functions.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
void Serializer::printValueIDMap(raw_ostream &os) {
|
|
|
|
|
os << "\n= Value <id> Map =\n\n";
|
|
|
|
|
for (auto valueIDPair : valueIDMap) {
|
|
|
|
|
Value val = valueIDPair.first;
|
|
|
|
|
os << " " << val << " "
|
|
|
|
|
<< "id = " << valueIDPair.second << ' ';
|
|
|
|
|
if (auto *op = val.getDefiningOp()) {
|
|
|
|
|
os << "from op '" << op->getName() << "'";
|
|
|
|
|
} else if (auto arg = val.dyn_cast<BlockArgument>()) {
|
|
|
|
|
Block *block = arg.getOwner();
|
|
|
|
|
os << "from argument of block " << block << ' ';
|
|
|
|
|
os << " in op '" << block->getParentOp()->getName() << "'";
|
|
|
|
|
}
|
|
|
|
|
os << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Module structure
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::getOrCreateFunctionID(StringRef fnName) {
|
|
|
|
|
auto funcID = funcIDMap.lookup(fnName);
|
|
|
|
|
if (!funcID) {
|
|
|
|
|
funcID = getNextID();
|
|
|
|
|
funcIDMap[fnName] = funcID;
|
|
|
|
|
}
|
|
|
|
|
return funcID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Serializer::processCapability() {
|
|
|
|
|
for (auto cap : module.vce_triple()->getCapabilities())
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(capabilities, spirv::Opcode::OpCapability,
|
|
|
|
|
{static_cast<uint32_t>(cap)});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Serializer::processDebugInfo() {
|
2021-12-10 13:57:29 -05:00
|
|
|
|
if (!options.emitDebugInfo)
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return;
|
|
|
|
|
auto fileLoc = module.getLoc().dyn_cast<FileLineColLoc>();
|
2021-03-08 14:25:38 -08:00
|
|
|
|
auto fileName = fileLoc ? fileLoc.getFilename().strref() : "<unknown>";
|
2021-02-08 12:45:11 +01:00
|
|
|
|
fileID = getNextID();
|
|
|
|
|
SmallVector<uint32_t, 16> operands;
|
|
|
|
|
operands.push_back(fileID);
|
2021-12-10 18:55:44 -05:00
|
|
|
|
spirv::encodeStringLiteralInto(operands, fileName);
|
|
|
|
|
encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
// TODO: Encode more debug instructions.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Serializer::processExtension() {
|
|
|
|
|
llvm::SmallVector<uint32_t, 16> extName;
|
|
|
|
|
for (spirv::Extension ext : module.vce_triple()->getExtensions()) {
|
|
|
|
|
extName.clear();
|
2021-12-10 18:55:44 -05:00
|
|
|
|
spirv::encodeStringLiteralInto(extName, spirv::stringifyExtension(ext));
|
|
|
|
|
encodeInstructionInto(extensions, spirv::Opcode::OpExtension, extName);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Serializer::processMemoryModel() {
|
2022-08-09 14:03:54 -04:00
|
|
|
|
auto mm = static_cast<uint32_t>(
|
|
|
|
|
module->getAttrOfType<spirv::MemoryModelAttr>("memory_model").getValue());
|
|
|
|
|
auto am = static_cast<uint32_t>(
|
|
|
|
|
module->getAttrOfType<spirv::AddressingModelAttr>("addressing_model")
|
|
|
|
|
.getValue());
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(memoryModel, spirv::Opcode::OpMemoryModel, {am, mm});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
|
|
|
|
|
NamedAttribute attr) {
|
2021-11-18 05:23:32 +00:00
|
|
|
|
auto attrName = attr.getName().strref();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
auto decorationName = llvm::convertToCamelFromSnakeCase(attrName, true);
|
|
|
|
|
auto decoration = spirv::symbolizeDecoration(decorationName);
|
|
|
|
|
if (!decoration) {
|
|
|
|
|
return emitError(
|
|
|
|
|
loc, "non-argument attributes expected to have snake-case-ified "
|
|
|
|
|
"decoration name, unhandled attribute with name : ")
|
|
|
|
|
<< attrName;
|
|
|
|
|
}
|
|
|
|
|
SmallVector<uint32_t, 1> args;
|
2022-06-20 23:20:25 -07:00
|
|
|
|
switch (*decoration) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
case spirv::Decoration::Binding:
|
|
|
|
|
case spirv::Decoration::DescriptorSet:
|
|
|
|
|
case spirv::Decoration::Location:
|
2021-11-18 05:23:32 +00:00
|
|
|
|
if (auto intAttr = attr.getValue().dyn_cast<IntegerAttr>()) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
args.push_back(intAttr.getValue().getZExtValue());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return emitError(loc, "expected integer attribute for ") << attrName;
|
|
|
|
|
case spirv::Decoration::BuiltIn:
|
2021-11-18 05:23:32 +00:00
|
|
|
|
if (auto strAttr = attr.getValue().dyn_cast<StringAttr>()) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
|
|
|
|
|
if (enumVal) {
|
2022-06-20 23:20:25 -07:00
|
|
|
|
args.push_back(static_cast<uint32_t>(*enumVal));
|
2021-02-08 12:45:11 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return emitError(loc, "invalid ")
|
|
|
|
|
<< attrName << " attribute " << strAttr.getValue();
|
|
|
|
|
}
|
|
|
|
|
return emitError(loc, "expected string attribute for ") << attrName;
|
|
|
|
|
case spirv::Decoration::Aliased:
|
|
|
|
|
case spirv::Decoration::Flat:
|
|
|
|
|
case spirv::Decoration::NonReadable:
|
|
|
|
|
case spirv::Decoration::NonWritable:
|
|
|
|
|
case spirv::Decoration::NoPerspective:
|
|
|
|
|
case spirv::Decoration::Restrict:
|
2021-10-05 00:04:33 +08:00
|
|
|
|
case spirv::Decoration::RelaxedPrecision:
|
2021-02-08 12:45:11 +01:00
|
|
|
|
// For unit attributes, the args list has no values so we do nothing
|
2021-11-18 05:23:32 +00:00
|
|
|
|
if (auto unitAttr = attr.getValue().dyn_cast<UnitAttr>())
|
2021-02-08 12:45:11 +01:00
|
|
|
|
break;
|
|
|
|
|
return emitError(loc, "expected unit attribute for ") << attrName;
|
|
|
|
|
default:
|
|
|
|
|
return emitError(loc, "unhandled decoration ") << decorationName;
|
|
|
|
|
}
|
2022-06-20 23:20:25 -07:00
|
|
|
|
return emitDecoration(resultID, *decoration, args);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
|
|
|
|
|
assert(!name.empty() && "unexpected empty string for OpName");
|
2021-12-10 13:57:29 -05:00
|
|
|
|
if (!options.emitSymbolName)
|
|
|
|
|
return success();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
SmallVector<uint32_t, 4> nameOperands;
|
|
|
|
|
nameOperands.push_back(resultID);
|
2021-12-10 18:55:44 -05:00
|
|
|
|
spirv::encodeStringLiteralInto(nameOperands, name);
|
|
|
|
|
encodeInstructionInto(names, spirv::Opcode::OpName, nameOperands);
|
|
|
|
|
return success();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <>
|
|
|
|
|
LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
|
|
|
|
|
Location loc, spirv::ArrayType type, uint32_t resultID) {
|
|
|
|
|
if (unsigned stride = type.getArrayStride()) {
|
|
|
|
|
// OpDecorate %arrayTypeSSA ArrayStride strideLiteral
|
|
|
|
|
return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
|
|
|
|
|
}
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <>
|
|
|
|
|
LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
|
|
|
|
|
Location loc, spirv::RuntimeArrayType type, uint32_t resultID) {
|
|
|
|
|
if (unsigned stride = type.getArrayStride()) {
|
|
|
|
|
// OpDecorate %arrayTypeSSA ArrayStride strideLiteral
|
|
|
|
|
return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
|
|
|
|
|
}
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processMemberDecoration(
|
|
|
|
|
uint32_t structID,
|
|
|
|
|
const spirv::StructType::MemberDecorationInfo &memberDecoration) {
|
|
|
|
|
SmallVector<uint32_t, 4> args(
|
|
|
|
|
{structID, memberDecoration.memberIndex,
|
|
|
|
|
static_cast<uint32_t>(memberDecoration.decoration)});
|
|
|
|
|
if (memberDecoration.hasValue) {
|
|
|
|
|
args.push_back(memberDecoration.decorationValue);
|
|
|
|
|
}
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate, args);
|
|
|
|
|
return success();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Type
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
// According to the SPIR-V spec "Validation Rules for Shader Capabilities":
|
|
|
|
|
// "Composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform, and
|
|
|
|
|
// PushConstant Storage Classes must be explicitly laid out."
|
|
|
|
|
bool Serializer::isInterfaceStructPtrType(Type type) const {
|
|
|
|
|
if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
|
|
|
|
|
switch (ptrType.getStorageClass()) {
|
|
|
|
|
case spirv::StorageClass::PhysicalStorageBuffer:
|
|
|
|
|
case spirv::StorageClass::PushConstant:
|
|
|
|
|
case spirv::StorageClass::StorageBuffer:
|
|
|
|
|
case spirv::StorageClass::Uniform:
|
|
|
|
|
return ptrType.getPointeeType().isa<spirv::StructType>();
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processType(Location loc, Type type,
|
|
|
|
|
uint32_t &typeID) {
|
|
|
|
|
// Maintains a set of names for nested identified struct types. This is used
|
|
|
|
|
// to properly serialize recursive references.
|
2021-04-15 16:08:54 -07:00
|
|
|
|
SetVector<StringRef> serializationCtx;
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return processTypeImpl(loc, type, typeID, serializationCtx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
|
2021-04-15 16:08:54 -07:00
|
|
|
|
SetVector<StringRef> &serializationCtx) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
typeID = getTypeID(type);
|
2021-12-10 13:57:46 -05:00
|
|
|
|
if (typeID)
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return success();
|
2021-12-10 13:57:46 -05:00
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
typeID = getNextID();
|
|
|
|
|
SmallVector<uint32_t, 4> operands;
|
|
|
|
|
|
|
|
|
|
operands.push_back(typeID);
|
|
|
|
|
auto typeEnum = spirv::Opcode::OpTypeVoid;
|
|
|
|
|
bool deferSerialization = false;
|
|
|
|
|
|
|
|
|
|
if ((type.isa<FunctionType>() &&
|
|
|
|
|
succeeded(prepareFunctionType(loc, type.cast<FunctionType>(), typeEnum,
|
|
|
|
|
operands))) ||
|
|
|
|
|
succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
|
|
|
|
|
deferSerialization, serializationCtx))) {
|
|
|
|
|
if (deferSerialization)
|
|
|
|
|
return success();
|
|
|
|
|
|
|
|
|
|
typeIDMap[type] = typeID;
|
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, typeEnum, operands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
if (recursiveStructInfos.count(type) != 0) {
|
|
|
|
|
// This recursive struct type is emitted already, now the OpTypePointer
|
|
|
|
|
// instructions referring to recursive references are emitted as well.
|
|
|
|
|
for (auto &ptrInfo : recursiveStructInfos[type]) {
|
|
|
|
|
// TODO: This might not work if more than 1 recursive reference is
|
|
|
|
|
// present in the struct.
|
|
|
|
|
SmallVector<uint32_t, 4> ptrOperands;
|
|
|
|
|
ptrOperands.push_back(ptrInfo.pointerTypeID);
|
|
|
|
|
ptrOperands.push_back(static_cast<uint32_t>(ptrInfo.storageClass));
|
|
|
|
|
ptrOperands.push_back(typeIDMap[type]);
|
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpTypePointer,
|
|
|
|
|
ptrOperands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recursiveStructInfos[type].clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::prepareBasicType(
|
|
|
|
|
Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
|
|
|
|
|
SmallVectorImpl<uint32_t> &operands, bool &deferSerialization,
|
2021-04-15 16:08:54 -07:00
|
|
|
|
SetVector<StringRef> &serializationCtx) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
deferSerialization = false;
|
|
|
|
|
|
|
|
|
|
if (isVoidType(type)) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeVoid;
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto intType = type.dyn_cast<IntegerType>()) {
|
|
|
|
|
if (intType.getWidth() == 1) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeBool;
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeInt;
|
|
|
|
|
operands.push_back(intType.getWidth());
|
|
|
|
|
// SPIR-V OpTypeInt "Signedness specifies whether there are signed semantics
|
|
|
|
|
// to preserve or validate.
|
|
|
|
|
// 0 indicates unsigned, or no signedness semantics
|
|
|
|
|
// 1 indicates signed semantics."
|
|
|
|
|
operands.push_back(intType.isSigned() ? 1 : 0);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto floatType = type.dyn_cast<FloatType>()) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeFloat;
|
|
|
|
|
operands.push_back(floatType.getWidth());
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto vectorType = type.dyn_cast<VectorType>()) {
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
|
|
|
|
|
serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeVector;
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
operands.push_back(vectorType.getNumElements());
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto imageType = type.dyn_cast<spirv::ImageType>()) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeImage;
|
|
|
|
|
uint32_t sampledTypeID = 0;
|
|
|
|
|
if (failed(processType(loc, imageType.getElementType(), sampledTypeID)))
|
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
operands.push_back(sampledTypeID);
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getDim()));
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getDepthInfo()));
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getArrayedInfo()));
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getSamplingInfo()));
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getSamplerUseInfo()));
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(imageType.getImageFormat()));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto arrayType = type.dyn_cast<spirv::ArrayType>()) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeArray;
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
|
|
|
|
|
serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
if (auto elementCountID = prepareConstantInt(
|
|
|
|
|
loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
|
|
|
|
|
operands.push_back(elementCountID);
|
|
|
|
|
}
|
|
|
|
|
return processTypeDecoration(loc, arrayType, resultID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto ptrType = type.dyn_cast<spirv::PointerType>()) {
|
|
|
|
|
uint32_t pointeeTypeID = 0;
|
|
|
|
|
spirv::StructType pointeeStruct =
|
|
|
|
|
ptrType.getPointeeType().dyn_cast<spirv::StructType>();
|
|
|
|
|
|
|
|
|
|
if (pointeeStruct && pointeeStruct.isIdentified() &&
|
|
|
|
|
serializationCtx.count(pointeeStruct.getIdentifier()) != 0) {
|
|
|
|
|
// A recursive reference to an enclosing struct is found.
|
|
|
|
|
//
|
|
|
|
|
// 1. Prepare an OpTypeForwardPointer with resultID and the ptr storage
|
|
|
|
|
// class as operands.
|
|
|
|
|
SmallVector<uint32_t, 2> forwardPtrOperands;
|
|
|
|
|
forwardPtrOperands.push_back(resultID);
|
|
|
|
|
forwardPtrOperands.push_back(
|
|
|
|
|
static_cast<uint32_t>(ptrType.getStorageClass()));
|
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues,
|
|
|
|
|
spirv::Opcode::OpTypeForwardPointer,
|
|
|
|
|
forwardPtrOperands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
// 2. Find the pointee (enclosing) struct.
|
|
|
|
|
auto structType = spirv::StructType::getIdentified(
|
|
|
|
|
module.getContext(), pointeeStruct.getIdentifier());
|
|
|
|
|
|
|
|
|
|
if (!structType)
|
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
// 3. Mark the OpTypePointer that is supposed to be emitted by this call
|
|
|
|
|
// as deferred.
|
|
|
|
|
deferSerialization = true;
|
|
|
|
|
|
|
|
|
|
// 4. Record the info needed to emit the deferred OpTypePointer
|
|
|
|
|
// instruction when the enclosing struct is completely serialized.
|
|
|
|
|
recursiveStructInfos[structType].push_back(
|
|
|
|
|
{resultID, ptrType.getStorageClass()});
|
|
|
|
|
} else {
|
|
|
|
|
if (failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
|
|
|
|
|
serializationCtx)))
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypePointer;
|
|
|
|
|
operands.push_back(static_cast<uint32_t>(ptrType.getStorageClass()));
|
|
|
|
|
operands.push_back(pointeeTypeID);
|
2021-12-10 13:57:46 -05:00
|
|
|
|
|
|
|
|
|
if (isInterfaceStructPtrType(ptrType)) {
|
|
|
|
|
if (failed(emitDecoration(getTypeID(pointeeStruct),
|
|
|
|
|
spirv::Decoration::Block)))
|
|
|
|
|
return emitError(loc, "cannot decorate ")
|
|
|
|
|
<< pointeeStruct << " with Block decoration";
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto runtimeArrayType = type.dyn_cast<spirv::RuntimeArrayType>()) {
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
|
|
|
|
|
elementTypeID, serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeRuntimeArray;
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
return processTypeDecoration(loc, runtimeArrayType, resultID);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 13:47:12 -05:00
|
|
|
|
if (auto sampledImageType = type.dyn_cast<spirv::SampledImageType>()) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeSampledImage;
|
|
|
|
|
uint32_t imageTypeID = 0;
|
|
|
|
|
if (failed(
|
|
|
|
|
processType(loc, sampledImageType.getImageType(), imageTypeID))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
operands.push_back(imageTypeID);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
if (auto structType = type.dyn_cast<spirv::StructType>()) {
|
|
|
|
|
if (structType.isIdentified()) {
|
2021-12-10 18:55:44 -05:00
|
|
|
|
if (failed(processName(resultID, structType.getIdentifier())))
|
|
|
|
|
return failure();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
serializationCtx.insert(structType.getIdentifier());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasOffset = structType.hasOffset();
|
|
|
|
|
for (auto elementIndex :
|
|
|
|
|
llvm::seq<uint32_t>(0, structType.getNumElements())) {
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, structType.getElementType(elementIndex),
|
|
|
|
|
elementTypeID, serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
if (hasOffset) {
|
|
|
|
|
// Decorate each struct member with an offset
|
|
|
|
|
spirv::StructType::MemberDecorationInfo offsetDecoration{
|
|
|
|
|
elementIndex, /*hasValue=*/1, spirv::Decoration::Offset,
|
|
|
|
|
static_cast<uint32_t>(structType.getMemberOffset(elementIndex))};
|
|
|
|
|
if (failed(processMemberDecoration(resultID, offsetDecoration))) {
|
|
|
|
|
return emitError(loc, "cannot decorate ")
|
|
|
|
|
<< elementIndex << "-th member of " << structType
|
|
|
|
|
<< " with its offset";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
|
|
|
|
|
structType.getMemberDecorations(memberDecorations);
|
|
|
|
|
|
|
|
|
|
for (auto &memberDecoration : memberDecorations) {
|
|
|
|
|
if (failed(processMemberDecoration(resultID, memberDecoration))) {
|
|
|
|
|
return emitError(loc, "cannot decorate ")
|
|
|
|
|
<< static_cast<uint32_t>(memberDecoration.memberIndex)
|
|
|
|
|
<< "-th member of " << structType << " with "
|
|
|
|
|
<< stringifyDecoration(memberDecoration.decoration);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeStruct;
|
|
|
|
|
|
|
|
|
|
if (structType.isIdentified())
|
|
|
|
|
serializationCtx.remove(structType.getIdentifier());
|
|
|
|
|
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto cooperativeMatrixType =
|
|
|
|
|
type.dyn_cast<spirv::CooperativeMatrixNVType>()) {
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
|
|
|
|
|
elementTypeID, serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeCooperativeMatrixNV;
|
|
|
|
|
auto getConstantOp = [&](uint32_t id) {
|
|
|
|
|
auto attr = IntegerAttr::get(IntegerType::get(type.getContext(), 32), id);
|
|
|
|
|
return prepareConstantInt(loc, attr);
|
|
|
|
|
};
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
operands.push_back(
|
|
|
|
|
getConstantOp(static_cast<uint32_t>(cooperativeMatrixType.getScope())));
|
|
|
|
|
operands.push_back(getConstantOp(cooperativeMatrixType.getRows()));
|
|
|
|
|
operands.push_back(getConstantOp(cooperativeMatrixType.getColumns()));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto matrixType = type.dyn_cast<spirv::MatrixType>()) {
|
|
|
|
|
uint32_t elementTypeID = 0;
|
|
|
|
|
if (failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
|
|
|
|
|
serializationCtx))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeMatrix;
|
|
|
|
|
operands.push_back(elementTypeID);
|
|
|
|
|
operands.push_back(matrixType.getNumColumns());
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Handle other types.
|
|
|
|
|
return emitError(loc, "unhandled type in serialization: ") << type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
Serializer::prepareFunctionType(Location loc, FunctionType type,
|
|
|
|
|
spirv::Opcode &typeEnum,
|
|
|
|
|
SmallVectorImpl<uint32_t> &operands) {
|
|
|
|
|
typeEnum = spirv::Opcode::OpTypeFunction;
|
|
|
|
|
assert(type.getNumResults() <= 1 &&
|
|
|
|
|
"serialization supports only a single return value");
|
|
|
|
|
uint32_t resultID = 0;
|
|
|
|
|
if (failed(processType(
|
|
|
|
|
loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
|
|
|
|
|
resultID))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
operands.push_back(resultID);
|
|
|
|
|
for (auto &res : type.getInputs()) {
|
|
|
|
|
uint32_t argTypeID = 0;
|
|
|
|
|
if (failed(processType(loc, res, argTypeID))) {
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
operands.push_back(argTypeID);
|
|
|
|
|
}
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Constant
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareConstant(Location loc, Type constType,
|
|
|
|
|
Attribute valueAttr) {
|
|
|
|
|
if (auto id = prepareConstantScalar(loc, valueAttr)) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is a composite literal. We need to handle each component separately
|
|
|
|
|
// and then emit an OpConstantComposite for the whole.
|
|
|
|
|
|
|
|
|
|
if (auto id = getConstantID(valueAttr)) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t typeID = 0;
|
|
|
|
|
if (failed(processType(loc, constType, typeID))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t resultID = 0;
|
|
|
|
|
if (auto attr = valueAttr.dyn_cast<DenseElementsAttr>()) {
|
|
|
|
|
int rank = attr.getType().dyn_cast<ShapedType>().getRank();
|
|
|
|
|
SmallVector<uint64_t, 4> index(rank);
|
|
|
|
|
resultID = prepareDenseElementsConstant(loc, constType, attr,
|
|
|
|
|
/*dim=*/0, index);
|
|
|
|
|
} else if (auto arrayAttr = valueAttr.dyn_cast<ArrayAttr>()) {
|
|
|
|
|
resultID = prepareArrayConstant(loc, constType, arrayAttr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (resultID == 0) {
|
|
|
|
|
emitError(loc, "cannot serialize attribute: ") << valueAttr;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constIDMap[valueAttr] = resultID;
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
|
|
|
|
|
ArrayAttr attr) {
|
|
|
|
|
uint32_t typeID = 0;
|
|
|
|
|
if (failed(processType(loc, constType, typeID))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t resultID = getNextID();
|
|
|
|
|
SmallVector<uint32_t, 4> operands = {typeID, resultID};
|
|
|
|
|
operands.reserve(attr.size() + 2);
|
|
|
|
|
auto elementType = constType.cast<spirv::ArrayType>().getElementType();
|
|
|
|
|
for (Attribute elementAttr : attr) {
|
|
|
|
|
if (auto elementID = prepareConstant(loc, elementType, elementAttr)) {
|
|
|
|
|
operands.push_back(elementID);
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, operands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Turn the below function into iterative function, instead of
|
|
|
|
|
// recursive function.
|
|
|
|
|
uint32_t
|
|
|
|
|
Serializer::prepareDenseElementsConstant(Location loc, Type constType,
|
|
|
|
|
DenseElementsAttr valueAttr, int dim,
|
|
|
|
|
MutableArrayRef<uint64_t> index) {
|
|
|
|
|
auto shapedType = valueAttr.getType().dyn_cast<ShapedType>();
|
|
|
|
|
assert(dim <= shapedType.getRank());
|
|
|
|
|
if (shapedType.getRank() == dim) {
|
|
|
|
|
if (auto attr = valueAttr.dyn_cast<DenseIntElementsAttr>()) {
|
|
|
|
|
return attr.getType().getElementType().isInteger(1)
|
2021-11-09 00:05:55 +00:00
|
|
|
|
? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
|
|
|
|
|
: prepareConstantInt(loc,
|
|
|
|
|
attr.getValues<IntegerAttr>()[index]);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
if (auto attr = valueAttr.dyn_cast<DenseFPElementsAttr>()) {
|
2021-11-09 00:05:55 +00:00
|
|
|
|
return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t typeID = 0;
|
|
|
|
|
if (failed(processType(loc, constType, typeID))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t resultID = getNextID();
|
|
|
|
|
SmallVector<uint32_t, 4> operands = {typeID, resultID};
|
|
|
|
|
operands.reserve(shapedType.getDimSize(dim) + 2);
|
|
|
|
|
auto elementType = constType.cast<spirv::CompositeType>().getElementType(0);
|
|
|
|
|
for (int i = 0; i < shapedType.getDimSize(dim); ++i) {
|
|
|
|
|
index[dim] = i;
|
|
|
|
|
if (auto elementID = prepareDenseElementsConstant(
|
|
|
|
|
loc, elementType, valueAttr, dim + 1, index)) {
|
|
|
|
|
operands.push_back(elementID);
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, operands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
|
|
|
|
|
bool isSpec) {
|
|
|
|
|
if (auto floatAttr = valueAttr.dyn_cast<FloatAttr>()) {
|
|
|
|
|
return prepareConstantFp(loc, floatAttr, isSpec);
|
|
|
|
|
}
|
|
|
|
|
if (auto boolAttr = valueAttr.dyn_cast<BoolAttr>()) {
|
|
|
|
|
return prepareConstantBool(loc, boolAttr, isSpec);
|
|
|
|
|
}
|
|
|
|
|
if (auto intAttr = valueAttr.dyn_cast<IntegerAttr>()) {
|
|
|
|
|
return prepareConstantInt(loc, intAttr, isSpec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
|
|
|
|
|
bool isSpec) {
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
// We can de-duplicate normal constants, but not specialization constants.
|
|
|
|
|
if (auto id = getConstantID(boolAttr)) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the type for this bool literal
|
|
|
|
|
uint32_t typeID = 0;
|
[mlir] Remove types from attributes
This patch removes the `type` field from `Attribute` along with the
`Attribute::getType` accessor.
Going forward, this means that attributes in MLIR will no longer have
types as a first-class concept. This patch lays the groundwork to
incrementally remove or refactor code that relies on generic attributes
being typed. The immediate impact will be on attributes that rely on
`Attribute` containing a type, such as `IntegerAttr`,
`DenseElementsAttr`, and `ml_program::ExternAttr`, which will now need
to define a type parameter on their storage classes. This will save
memory as all other attribute kinds will no longer contain a type.
Moreover, it will not be possible to generically query the type of an
attribute directly. This patch provides an attribute interface
`TypedAttr` that implements only one method, `getType`, which can be
used to generically query the types of attributes that implement the
interface. This interface can be used to retain the concept of a "typed
attribute". The ODS-generated accessor for a `type` parameter
automatically implements this method.
Next steps will be to refactor the assembly formats of certain operations
that rely on `parseAttribute(type)` and `printAttributeWithoutType` to
remove special handling of type elision until `type` can be removed from
the dialect parsing hook entirely; and incrementally remove uses of
`TypedAttr`.
Reviewed By: lattner, rriddle, jpienaar
Differential Revision: https://reviews.llvm.org/D130092
2022-07-18 21:32:38 -07:00
|
|
|
|
if (failed(
|
|
|
|
|
processType(loc, boolAttr.cast<IntegerAttr>().getType(), typeID))) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto resultID = getNextID();
|
|
|
|
|
auto opcode = boolAttr.getValue()
|
|
|
|
|
? (isSpec ? spirv::Opcode::OpSpecConstantTrue
|
|
|
|
|
: spirv::Opcode::OpConstantTrue)
|
|
|
|
|
: (isSpec ? spirv::Opcode::OpSpecConstantFalse
|
|
|
|
|
: spirv::Opcode::OpConstantFalse);
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
constIDMap[boolAttr] = resultID;
|
|
|
|
|
}
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
|
|
|
|
|
bool isSpec) {
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
// We can de-duplicate normal constants, but not specialization constants.
|
|
|
|
|
if (auto id = getConstantID(intAttr)) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the type for this integer literal
|
|
|
|
|
uint32_t typeID = 0;
|
|
|
|
|
if (failed(processType(loc, intAttr.getType(), typeID))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto resultID = getNextID();
|
|
|
|
|
APInt value = intAttr.getValue();
|
|
|
|
|
unsigned bitwidth = value.getBitWidth();
|
|
|
|
|
bool isSigned = value.isSignedIntN(bitwidth);
|
|
|
|
|
|
|
|
|
|
auto opcode =
|
|
|
|
|
isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
|
|
|
|
|
|
2021-07-30 17:39:32 -04:00
|
|
|
|
switch (bitwidth) {
|
|
|
|
|
// According to SPIR-V spec, "When the type's bit width is less than
|
|
|
|
|
// 32-bits, the literal's value appears in the low-order bits of the word,
|
|
|
|
|
// and the high-order bits must be 0 for a floating-point type, or 0 for an
|
|
|
|
|
// integer type with Signedness of 0, or sign extended when Signedness
|
|
|
|
|
// is 1."
|
|
|
|
|
case 32:
|
|
|
|
|
case 16:
|
|
|
|
|
case 8: {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
uint32_t word = 0;
|
|
|
|
|
if (isSigned) {
|
|
|
|
|
word = static_cast<int32_t>(value.getSExtValue());
|
|
|
|
|
} else {
|
|
|
|
|
word = static_cast<uint32_t>(value.getZExtValue());
|
|
|
|
|
}
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
|
2021-07-30 17:39:32 -04:00
|
|
|
|
} break;
|
|
|
|
|
// According to SPIR-V spec: "When the type's bit width is larger than one
|
|
|
|
|
// word, the literal’s low-order words appear first."
|
|
|
|
|
case 64: {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
struct DoubleWord {
|
|
|
|
|
uint32_t word1;
|
|
|
|
|
uint32_t word2;
|
|
|
|
|
} words;
|
|
|
|
|
if (isSigned) {
|
|
|
|
|
words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
|
|
|
|
|
} else {
|
|
|
|
|
words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
|
|
|
|
|
}
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode,
|
|
|
|
|
{typeID, resultID, words.word1, words.word2});
|
2021-07-30 17:39:32 -04:00
|
|
|
|
} break;
|
|
|
|
|
default: {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
std::string valueStr;
|
|
|
|
|
llvm::raw_string_ostream rss(valueStr);
|
|
|
|
|
value.print(rss, /*isSigned=*/false);
|
|
|
|
|
|
|
|
|
|
emitError(loc, "cannot serialize ")
|
|
|
|
|
<< bitwidth << "-bit integer literal: " << rss.str();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-07-30 17:39:32 -04:00
|
|
|
|
}
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
constIDMap[intAttr] = resultID;
|
|
|
|
|
}
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
|
|
|
|
|
bool isSpec) {
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
// We can de-duplicate normal constants, but not specialization constants.
|
|
|
|
|
if (auto id = getConstantID(floatAttr)) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the type for this float literal
|
|
|
|
|
uint32_t typeID = 0;
|
|
|
|
|
if (failed(processType(loc, floatAttr.getType(), typeID))) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto resultID = getNextID();
|
|
|
|
|
APFloat value = floatAttr.getValue();
|
|
|
|
|
APInt intValue = value.bitcastToAPInt();
|
|
|
|
|
|
|
|
|
|
auto opcode =
|
|
|
|
|
isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
|
|
|
|
|
|
|
|
|
|
if (&value.getSemantics() == &APFloat::IEEEsingle()) {
|
|
|
|
|
uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
} else if (&value.getSemantics() == &APFloat::IEEEdouble()) {
|
|
|
|
|
struct DoubleWord {
|
|
|
|
|
uint32_t word1;
|
|
|
|
|
uint32_t word2;
|
|
|
|
|
} words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode,
|
|
|
|
|
{typeID, resultID, words.word1, words.word2});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
} else if (&value.getSemantics() == &APFloat::IEEEhalf()) {
|
|
|
|
|
uint32_t word =
|
|
|
|
|
static_cast<uint32_t>(value.bitcastToAPInt().getZExtValue());
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
} else {
|
|
|
|
|
std::string valueStr;
|
|
|
|
|
llvm::raw_string_ostream rss(valueStr);
|
|
|
|
|
value.print(rss);
|
|
|
|
|
|
|
|
|
|
emitError(loc, "cannot serialize ")
|
|
|
|
|
<< floatAttr.getType() << "-typed float literal: " << rss.str();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isSpec) {
|
|
|
|
|
constIDMap[floatAttr] = resultID;
|
|
|
|
|
}
|
|
|
|
|
return resultID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Control flow
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
uint32_t Serializer::getOrCreateBlockID(Block *block) {
|
|
|
|
|
if (uint32_t id = getBlockID(block))
|
|
|
|
|
return id;
|
|
|
|
|
return blockIDMap[block] = getNextID();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-11 14:40:24 -05:00
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
void Serializer::printBlock(Block *block, raw_ostream &os) {
|
|
|
|
|
os << "block " << block << " (id = ";
|
|
|
|
|
if (uint32_t id = getBlockID(block))
|
|
|
|
|
os << id;
|
|
|
|
|
else
|
|
|
|
|
os << "unknown";
|
|
|
|
|
os << ")\n";
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
LogicalResult
|
|
|
|
|
Serializer::processBlock(Block *block, bool omitLabel,
|
2021-12-11 14:40:24 -05:00
|
|
|
|
function_ref<LogicalResult()> emitMerge) {
|
2021-02-08 12:45:11 +01:00
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "processing block " << block << ":\n");
|
|
|
|
|
LLVM_DEBUG(block->print(llvm::dbgs()));
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << '\n');
|
|
|
|
|
if (!omitLabel) {
|
|
|
|
|
uint32_t blockID = getOrCreateBlockID(block);
|
2021-12-11 14:40:24 -05:00
|
|
|
|
LLVM_DEBUG(printBlock(block, llvm::dbgs()));
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
// Emit OpLabel for this block.
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit OpPhi instructions for block arguments, if any.
|
|
|
|
|
if (failed(emitPhiForBlockArguments(block)))
|
|
|
|
|
return failure();
|
|
|
|
|
|
2021-12-11 14:40:24 -05:00
|
|
|
|
// If we need to emit merge instructions, it must happen in this block. Check
|
|
|
|
|
// whether we have other structured control flow ops, which will be expanded
|
|
|
|
|
// into multiple basic blocks. If that's the case, we need to emit the merge
|
|
|
|
|
// right now and then create new blocks for further serialization of the ops
|
|
|
|
|
// in this block.
|
|
|
|
|
if (emitMerge && llvm::any_of(block->getOperations(), [](Operation &op) {
|
|
|
|
|
return isa<spirv::LoopOp, spirv::SelectionOp>(op);
|
|
|
|
|
})) {
|
|
|
|
|
if (failed(emitMerge()))
|
|
|
|
|
return failure();
|
|
|
|
|
emitMerge = nullptr;
|
|
|
|
|
|
|
|
|
|
// Start a new block for further serialization.
|
|
|
|
|
uint32_t blockID = getNextID();
|
|
|
|
|
encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, {blockID});
|
|
|
|
|
encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {blockID});
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
// Process each op in this block except the terminator.
|
|
|
|
|
for (auto &op : llvm::make_range(block->begin(), std::prev(block->end()))) {
|
|
|
|
|
if (failed(processOperation(&op)))
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the terminator.
|
2021-12-11 14:40:24 -05:00
|
|
|
|
if (emitMerge)
|
|
|
|
|
if (failed(emitMerge()))
|
2021-12-10 18:55:44 -05:00
|
|
|
|
return failure();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
if (failed(processOperation(&block->back())))
|
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::emitPhiForBlockArguments(Block *block) {
|
|
|
|
|
// Nothing to do if this block has no arguments or it's the entry block, which
|
|
|
|
|
// always has the same arguments as the function signature.
|
|
|
|
|
if (block->args_empty() || block->isEntryBlock())
|
|
|
|
|
return success();
|
|
|
|
|
|
2021-12-11 14:40:24 -05:00
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "emitting phi instructions..\n");
|
|
|
|
|
|
2021-02-08 12:45:11 +01:00
|
|
|
|
// If the block has arguments, we need to create SPIR-V OpPhi instructions.
|
|
|
|
|
// A SPIR-V OpPhi instruction is of the syntax:
|
|
|
|
|
// OpPhi | result type | result <id> | (value <id>, parent block <id>) pair
|
|
|
|
|
// So we need to collect all predecessor blocks and the arguments they send
|
|
|
|
|
// to this block.
|
2021-05-07 08:59:35 +02:00
|
|
|
|
SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
|
2021-12-11 14:40:24 -05:00
|
|
|
|
for (Block *mlirPredecessor : block->getPredecessors()) {
|
|
|
|
|
auto *terminator = mlirPredecessor->getTerminator();
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " mlir predecessor ");
|
|
|
|
|
LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " terminator: " << *terminator << "\n");
|
2021-02-08 12:45:11 +01:00
|
|
|
|
// The predecessor here is the immediate one according to MLIR's IR
|
|
|
|
|
// structure. It does not directly map to the incoming parent block for the
|
|
|
|
|
// OpPhi instructions at SPIR-V binary level. This is because structured
|
|
|
|
|
// control flow ops are serialized to multiple SPIR-V blocks. If there is a
|
2021-03-06 15:40:14 +01:00
|
|
|
|
// spv.mlir.selection/spv.mlir.loop op in the MLIR predecessor block, the
|
|
|
|
|
// branch op jumping to the OpPhi's block then resides in the previous
|
|
|
|
|
// structured control flow op's merge block.
|
2021-12-11 14:40:24 -05:00
|
|
|
|
Block *spirvPredecessor = getPhiIncomingBlock(mlirPredecessor);
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " spirv predecessor ");
|
|
|
|
|
LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
|
2021-02-08 12:45:11 +01:00
|
|
|
|
if (auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
|
2021-12-11 14:40:24 -05:00
|
|
|
|
predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
|
2021-05-07 08:59:35 +02:00
|
|
|
|
} else if (auto branchCondOp =
|
|
|
|
|
dyn_cast<spirv::BranchConditionalOp>(terminator)) {
|
|
|
|
|
Optional<OperandRange> blockOperands;
|
2021-12-11 14:40:24 -05:00
|
|
|
|
if (branchCondOp.trueTarget() == block) {
|
|
|
|
|
blockOperands = branchCondOp.trueTargetOperands();
|
|
|
|
|
} else {
|
|
|
|
|
assert(branchCondOp.falseTarget() == block);
|
|
|
|
|
blockOperands = branchCondOp.falseTargetOperands();
|
|
|
|
|
}
|
2021-05-07 08:59:35 +02:00
|
|
|
|
|
2021-12-11 14:40:24 -05:00
|
|
|
|
assert(!blockOperands->empty() &&
|
2021-05-07 08:59:35 +02:00
|
|
|
|
"expected non-empty block operand range");
|
2021-12-11 14:40:24 -05:00
|
|
|
|
predecessors.emplace_back(spirvPredecessor, *blockOperands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
} else {
|
|
|
|
|
return terminator->emitError("unimplemented terminator for Phi creation");
|
|
|
|
|
}
|
2021-12-11 14:40:24 -05:00
|
|
|
|
LLVM_DEBUG({
|
|
|
|
|
llvm::dbgs() << " block arguments:\n";
|
|
|
|
|
for (Value v : predecessors.back().second)
|
|
|
|
|
llvm::dbgs() << " " << v << "\n";
|
|
|
|
|
});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Then create OpPhi instruction for each of the block argument.
|
|
|
|
|
for (auto argIndex : llvm::seq<unsigned>(0, block->getNumArguments())) {
|
|
|
|
|
BlockArgument arg = block->getArgument(argIndex);
|
|
|
|
|
|
|
|
|
|
// Get the type <id> and result <id> for this OpPhi instruction.
|
|
|
|
|
uint32_t phiTypeID = 0;
|
|
|
|
|
if (failed(processType(arg.getLoc(), arg.getType(), phiTypeID)))
|
|
|
|
|
return failure();
|
|
|
|
|
uint32_t phiID = getNextID();
|
|
|
|
|
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "[phi] for block argument #" << argIndex << ' '
|
|
|
|
|
<< arg << " (id = " << phiID << ")\n");
|
|
|
|
|
|
|
|
|
|
// Prepare the (value <id>, parent block <id>) pairs.
|
|
|
|
|
SmallVector<uint32_t, 8> phiArgs;
|
|
|
|
|
phiArgs.push_back(phiTypeID);
|
|
|
|
|
phiArgs.push_back(phiID);
|
|
|
|
|
|
|
|
|
|
for (auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
|
2021-05-07 08:59:35 +02:00
|
|
|
|
Value value = predecessors[predIndex].second[argIndex];
|
2021-02-08 12:45:11 +01:00
|
|
|
|
uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "[phi] use predecessor (id = " << predBlockId
|
|
|
|
|
<< ") value " << value << ' ');
|
|
|
|
|
// Each pair is a value <id> ...
|
|
|
|
|
uint32_t valueId = getValueID(value);
|
|
|
|
|
if (valueId == 0) {
|
|
|
|
|
// The op generating this value hasn't been visited yet so we don't have
|
|
|
|
|
// an <id> assigned yet. Record this to fix up later.
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "(need to fix)\n");
|
|
|
|
|
deferredPhiValues[value].push_back(functionBody.size() + 1 +
|
|
|
|
|
phiArgs.size());
|
|
|
|
|
} else {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "(id = " << valueId << ")\n");
|
|
|
|
|
}
|
|
|
|
|
phiArgs.push_back(valueId);
|
|
|
|
|
// ... and a parent block <id>.
|
|
|
|
|
phiArgs.push_back(predBlockId);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(functionBody, spirv::Opcode::OpPhi, phiArgs);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
valueIDMap[arg] = phiID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Operation
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::encodeExtensionInstruction(
|
|
|
|
|
Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
|
|
|
|
|
ArrayRef<uint32_t> operands) {
|
|
|
|
|
// Check if the extension has been imported.
|
|
|
|
|
auto &setID = extendedInstSetIDMap[extensionSetName];
|
|
|
|
|
if (!setID) {
|
|
|
|
|
setID = getNextID();
|
|
|
|
|
SmallVector<uint32_t, 16> importOperands;
|
|
|
|
|
importOperands.push_back(setID);
|
2021-12-10 18:55:44 -05:00
|
|
|
|
spirv::encodeStringLiteralInto(importOperands, extensionSetName);
|
|
|
|
|
encodeInstructionInto(extendedSets, spirv::Opcode::OpExtInstImport,
|
|
|
|
|
importOperands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The first two operands are the result type <id> and result <id>. The set
|
|
|
|
|
// <id> and the opcode need to be insert after this.
|
|
|
|
|
if (operands.size() < 2) {
|
|
|
|
|
return op->emitError("extended instructions must have a result encoding");
|
|
|
|
|
}
|
|
|
|
|
SmallVector<uint32_t, 8> extInstOperands;
|
|
|
|
|
extInstOperands.reserve(operands.size() + 2);
|
|
|
|
|
extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
|
|
|
|
|
extInstOperands.push_back(setID);
|
|
|
|
|
extInstOperands.push_back(extensionOpcode);
|
|
|
|
|
extInstOperands.append(std::next(operands.begin(), 2), operands.end());
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(functionBody, spirv::Opcode::OpExtInst,
|
|
|
|
|
extInstOperands);
|
|
|
|
|
return success();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processOperation(Operation *opInst) {
|
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "[op] '" << opInst->getName() << "'\n");
|
|
|
|
|
|
|
|
|
|
// First dispatch the ops that do not directly mirror an instruction from
|
|
|
|
|
// the SPIR-V spec.
|
|
|
|
|
return TypeSwitch<Operation *, LogicalResult>(opInst)
|
|
|
|
|
.Case([&](spirv::AddressOfOp op) { return processAddressOfOp(op); })
|
|
|
|
|
.Case([&](spirv::BranchOp op) { return processBranchOp(op); })
|
|
|
|
|
.Case([&](spirv::BranchConditionalOp op) {
|
|
|
|
|
return processBranchConditionalOp(op);
|
|
|
|
|
})
|
|
|
|
|
.Case([&](spirv::ConstantOp op) { return processConstantOp(op); })
|
|
|
|
|
.Case([&](spirv::FuncOp op) { return processFuncOp(op); })
|
|
|
|
|
.Case([&](spirv::GlobalVariableOp op) {
|
|
|
|
|
return processGlobalVariableOp(op);
|
|
|
|
|
})
|
|
|
|
|
.Case([&](spirv::LoopOp op) { return processLoopOp(op); })
|
|
|
|
|
.Case([&](spirv::ReferenceOfOp op) { return processReferenceOfOp(op); })
|
|
|
|
|
.Case([&](spirv::SelectionOp op) { return processSelectionOp(op); })
|
|
|
|
|
.Case([&](spirv::SpecConstantOp op) { return processSpecConstantOp(op); })
|
|
|
|
|
.Case([&](spirv::SpecConstantCompositeOp op) {
|
|
|
|
|
return processSpecConstantCompositeOp(op);
|
|
|
|
|
})
|
|
|
|
|
.Case([&](spirv::SpecConstantOperationOp op) {
|
|
|
|
|
return processSpecConstantOperationOp(op);
|
|
|
|
|
})
|
|
|
|
|
.Case([&](spirv::UndefOp op) { return processUndefOp(op); })
|
|
|
|
|
.Case([&](spirv::VariableOp op) { return processVariableOp(op); })
|
|
|
|
|
|
|
|
|
|
// Then handle all the ops that directly mirror SPIR-V instructions with
|
|
|
|
|
// auto-generated methods.
|
|
|
|
|
.Default(
|
|
|
|
|
[&](Operation *op) { return dispatchToAutogenSerialization(op); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
|
|
|
|
|
StringRef extInstSet,
|
|
|
|
|
uint32_t opcode) {
|
|
|
|
|
SmallVector<uint32_t, 4> operands;
|
|
|
|
|
Location loc = op->getLoc();
|
|
|
|
|
|
|
|
|
|
uint32_t resultID = 0;
|
|
|
|
|
if (op->getNumResults() != 0) {
|
|
|
|
|
uint32_t resultTypeID = 0;
|
|
|
|
|
if (failed(processType(loc, op->getResult(0).getType(), resultTypeID)))
|
|
|
|
|
return failure();
|
|
|
|
|
operands.push_back(resultTypeID);
|
|
|
|
|
|
|
|
|
|
resultID = getNextID();
|
|
|
|
|
operands.push_back(resultID);
|
|
|
|
|
valueIDMap[op->getResult(0)] = resultID;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (Value operand : op->getOperands())
|
|
|
|
|
operands.push_back(getValueID(operand));
|
|
|
|
|
|
2021-12-10 18:55:44 -05:00
|
|
|
|
if (failed(emitDebugLine(functionBody, loc)))
|
|
|
|
|
return failure();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
|
|
|
|
|
if (extInstSet.empty()) {
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(functionBody, static_cast<spirv::Opcode>(opcode),
|
|
|
|
|
operands);
|
2021-02-08 12:45:11 +01:00
|
|
|
|
} else {
|
2021-12-10 18:55:44 -05:00
|
|
|
|
if (failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
|
|
|
|
|
return failure();
|
2021-02-08 12:45:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (op->getNumResults() != 0) {
|
|
|
|
|
for (auto attr : op->getAttrs()) {
|
|
|
|
|
if (failed(processDecoration(loc, resultID, attr)))
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::emitDecoration(uint32_t target,
|
|
|
|
|
spirv::Decoration decoration,
|
|
|
|
|
ArrayRef<uint32_t> params) {
|
|
|
|
|
uint32_t wordCount = 3 + params.size();
|
|
|
|
|
decorations.push_back(
|
|
|
|
|
spirv::getPrefixedOpcode(wordCount, spirv::Opcode::OpDecorate));
|
|
|
|
|
decorations.push_back(target);
|
|
|
|
|
decorations.push_back(static_cast<uint32_t>(decoration));
|
|
|
|
|
decorations.append(params.begin(), params.end());
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
|
|
|
|
|
Location loc) {
|
2021-12-10 13:57:29 -05:00
|
|
|
|
if (!options.emitDebugInfo)
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return success();
|
|
|
|
|
|
|
|
|
|
if (lastProcessedWasMergeInst) {
|
|
|
|
|
lastProcessedWasMergeInst = false;
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto fileLoc = loc.dyn_cast<FileLineColLoc>();
|
|
|
|
|
if (fileLoc)
|
2021-12-10 18:55:44 -05:00
|
|
|
|
encodeInstructionInto(binary, spirv::Opcode::OpLine,
|
|
|
|
|
{fileID, fileLoc.getLine(), fileLoc.getColumn()});
|
2021-02-08 12:45:11 +01:00
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
} // namespace spirv
|
|
|
|
|
} // namespace mlir
|