mirror of
https://github.com/intel/llvm.git
synced 2026-02-05 13:21:04 +08:00
[spirv] Basic serializer and deserializer
This CL adds the basic SPIR-V serializer and deserializer for converting SPIR-V module into the binary format and back. Right now only an empty module with addressing model and memory model is supported; (de)serialize other components will be added gradually with subsequent CLs. The purpose of this library is to enable importing SPIR-V binary modules to run transformations on them and exporting SPIR-V modules to be consumed by execution environments. The focus is transformations, which inevitably means changes to the binary module; so it is not designed to be a general tool for investigating the SPIR-V binary module and does not guarantee roundtrip equivalence (at least for now). PiperOrigin-RevId: 254473019
This commit is contained in:
@@ -37,7 +37,8 @@ The SPIR-V dialect has the following conventions:
|
||||
|
||||
A SPIR-V module is defined via the `spv.module` op, which has one region that
|
||||
contains one block. Model-level instructions, including function definitions,
|
||||
are all placed inside the block.
|
||||
are all placed inside the block. Functions are defined using the standard `func`
|
||||
op.
|
||||
|
||||
Compared to the binary format, we adjust how certain module-level SPIR-V
|
||||
instructions are represented in the SPIR-V dialect. Notably,
|
||||
@@ -89,7 +90,7 @@ For example,
|
||||
|
||||
### Image type
|
||||
|
||||
This corresponds to SPIR-V [image_type][ImageType]. Its syntax is
|
||||
This corresponds to SPIR-V [image type][ImageType]. Its syntax is
|
||||
|
||||
``` {.ebnf}
|
||||
dim ::= `1D` | `2D` | `3D` | `Cube` | <and other SPIR-V Dim specifiers...>
|
||||
@@ -151,8 +152,23 @@ For example,
|
||||
!spv.rtarray<vector<4 x f32>>
|
||||
```
|
||||
|
||||
## Serialization
|
||||
|
||||
The serialization library provides two entry points, `mlir::spirv::serialize()`
|
||||
and `mlir::spirv::deserialize()`, for converting a MLIR SPIR-V module to binary
|
||||
format and back.
|
||||
|
||||
The purpose of this library is to enable importing SPIR-V binary modules to run
|
||||
transformations on them and exporting SPIR-V modules to be consumed by execution
|
||||
environments. The focus is transformations, which inevitably means changes to
|
||||
the binary module; so it is not designed to be a general tool for investigating
|
||||
the SPIR-V binary module and does not guarantee roundtrip equivalence (at least
|
||||
for now). For the latter, please use the assembler/disassembler in the
|
||||
[SPIRV-Tools][SPIRV-Tools] project.
|
||||
|
||||
[SPIR-V]: https://www.khronos.org/registry/spir-v/
|
||||
[ArrayType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeArray
|
||||
[PointerType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypePointer
|
||||
[RuntimeArrayType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeRuntimeArray
|
||||
[ImageType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeImage
|
||||
[SPIRV-Tools]: https://github.com/KhronosGroup/SPIRV-Tools
|
||||
|
||||
@@ -673,6 +673,14 @@ class EnumAttr<string name, string description, list<EnumAttrCase> cases> :
|
||||
// TODO(b/134741431): use dialect to provide the namespace.
|
||||
string cppNamespace = "";
|
||||
|
||||
// The name of the utility function that converts a value of the underlying
|
||||
// type to the corresponding symbol. It will have the following signature:
|
||||
//
|
||||
// ```c++
|
||||
// llvm::Optional<<qualified-enum-class-name>> <fn-name>(<underlying-type>);
|
||||
// ```
|
||||
string underlyingToSymbolFnName = "symbolize" # name;
|
||||
|
||||
// The name of the utility function that converts a string to the
|
||||
// corresponding symbol. It will have the following signature:
|
||||
//
|
||||
|
||||
@@ -8,4 +8,8 @@ mlir_tablegen(SPIRVEnums.h.inc -gen-enum-decls)
|
||||
mlir_tablegen(SPIRVEnums.cpp.inc -gen-enum-defs)
|
||||
add_public_tablegen_target(MLIRSPIRVEnumsIncGen)
|
||||
|
||||
set(LLVM_TARGET_DEFINITIONS SPIRVOps.td)
|
||||
mlir_tablegen(SPIRVSerialization.inc -gen-spirv-serial)
|
||||
add_public_tablegen_target(MLIRSPIRVSerializationGen)
|
||||
|
||||
add_subdirectory(Transforms)
|
||||
|
||||
@@ -290,6 +290,10 @@ def SPV_SamplerUseAttr:
|
||||
// Base class for all SPIR-V ops.
|
||||
class SPV_Op<string mnemonic, list<OpTrait> traits = []> :
|
||||
Op<SPV_Dialect, mnemonic, traits> {
|
||||
// Opcode for the binary format. Ops cannot be directly serialized will
|
||||
// leave this field as unset.
|
||||
int opcode = ?;
|
||||
|
||||
// For each SPIR-V op, the following static functions need to be defined
|
||||
// in SPVOps.cpp:
|
||||
//
|
||||
|
||||
@@ -61,6 +61,8 @@ def SPV_FMulOp : SPV_Op<"FMul", [NoSideEffect, SameOperandsAndResultType]> {
|
||||
|
||||
// No additional verification needed in addition to the ODS-generated ones.
|
||||
let verifier = [{ return success(); }];
|
||||
|
||||
let opcode = 133;
|
||||
}
|
||||
|
||||
def SPV_ReturnOp : SPV_Op<"Return", [Terminator]> {
|
||||
@@ -78,6 +80,8 @@ def SPV_ReturnOp : SPV_Op<"Return", [Terminator]> {
|
||||
let printer = [{ printNoIOOp(getOperation(), p); }];
|
||||
|
||||
let verifier = [{ return verifyReturn(*this); }];
|
||||
|
||||
let opcode = 253;
|
||||
}
|
||||
|
||||
def SPV_VariableOp : SPV_Op<"Variable"> {
|
||||
@@ -129,6 +133,8 @@ def SPV_VariableOp : SPV_Op<"Variable"> {
|
||||
let results = (outs
|
||||
SPV_AnyPtr:$pointer
|
||||
);
|
||||
|
||||
let opcode = 59;
|
||||
}
|
||||
|
||||
#endif // SPIRV_OPS
|
||||
|
||||
@@ -61,6 +61,8 @@ def SPV_ModuleOp : SPV_Op<"module", []> {
|
||||
let results = (outs);
|
||||
|
||||
let regions = (region SizedRegion<1>:$body);
|
||||
|
||||
let builders = [OpBuilder<"Builder *, OperationState *state">];
|
||||
}
|
||||
|
||||
def SPV_ModuleEndOp : SPV_Op<"_module_end", [Terminator]> {
|
||||
|
||||
48
mlir/include/mlir/SPIRV/Serialization.h
Normal file
48
mlir/include/mlir/SPIRV/Serialization.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//===- Serialization.h - MLIR SPIR-V (De)serialization ----------*- C++ -*-===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file declares the entry points for serialize and deserialze SPIR-V
|
||||
// binary modules.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef MLIR_SPIRV_SERIALIZATION_H_
|
||||
#define MLIR_SPIRV_SERIALIZATION_H_
|
||||
|
||||
#include "mlir/Support/LLVM.h"
|
||||
|
||||
namespace mlir {
|
||||
class MLIRContext;
|
||||
|
||||
namespace spirv {
|
||||
class ModuleOp;
|
||||
|
||||
/// Serializes the given SPIR-V `module` and writes to `binary`. Returns true on
|
||||
/// success; otherwise, reports errors to the error handler registered with the
|
||||
/// MLIR context for `module` and returns false.
|
||||
bool serialize(ModuleOp module, SmallVectorImpl<uint32_t> &binary);
|
||||
|
||||
/// Deserializes the given SPIR-V `binary` module and creates a MLIR ModuleOp
|
||||
/// in the given `context`. Returns the ModuleOp on success; otherwise, reports
|
||||
/// errors to the error handler registered with `context` and returns
|
||||
/// llvm::None.
|
||||
Optional<ModuleOp> deserialize(ArrayRef<uint32_t> binary, MLIRContext *context);
|
||||
|
||||
} // end namespace spirv
|
||||
} // end namespace mlir
|
||||
|
||||
#endif // MLIR_SPIRV_SERIALIZATION_H_
|
||||
@@ -152,6 +152,10 @@ public:
|
||||
// Returns the underlying type.
|
||||
StringRef getUnderlyingType() const;
|
||||
|
||||
// Returns the name of the utility function that converts a value of the
|
||||
// underlying type to the corresponding symbol.
|
||||
StringRef getUnderlyingToSymbolFnName() const;
|
||||
|
||||
// Returns the name of the utility function that converts a string to the
|
||||
// corresponding symbol.
|
||||
StringRef getStringToSymbolFnName() const;
|
||||
|
||||
@@ -18,3 +18,5 @@ target_link_libraries(MLIRSPIRV
|
||||
MLIRIR
|
||||
MLIRParser
|
||||
MLIRSupport)
|
||||
|
||||
add_subdirectory(Serialization)
|
||||
|
||||
@@ -139,6 +139,10 @@ static void ensureModuleEnd(Region *region, Builder builder, Location loc) {
|
||||
block.push_back(Operation::create(state));
|
||||
}
|
||||
|
||||
void spirv::ModuleOp::build(Builder *builder, OperationState *state) {
|
||||
ensureModuleEnd(state->addRegion(), *builder, state->location);
|
||||
}
|
||||
|
||||
static ParseResult parseModuleOp(OpAsmParser *parser, OperationState *state) {
|
||||
Region *body = state->addRegion();
|
||||
|
||||
|
||||
17
mlir/lib/SPIRV/Serialization/CMakeLists.txt
Normal file
17
mlir/lib/SPIRV/Serialization/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
add_llvm_library(MLIRSPIRVSerialization
|
||||
ConvertFromBinary.cpp
|
||||
ConvertToBinary.cpp
|
||||
Deserializer.cpp
|
||||
Serializer.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${MLIR_MAIN_INCLUDE_DIR}/mlir/SPIRV
|
||||
)
|
||||
|
||||
add_dependencies(MLIRSPIRVSerialization
|
||||
MLIRSPIRVSerializationGen)
|
||||
|
||||
target_link_libraries(MLIRSPIRVSerialization
|
||||
MLIRIR
|
||||
MLIRSPIRV
|
||||
MLIRSupport)
|
||||
98
mlir/lib/SPIRV/Serialization/ConvertFromBinary.cpp
Normal file
98
mlir/lib/SPIRV/Serialization/ConvertFromBinary.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
//===- ConvertFromBinary.cpp - MLIR SPIR-V binary to module conversion ----===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file implements a translation from SPIR-V binary module to MLIR SPIR-V
|
||||
// ModuleOp.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/Module.h"
|
||||
#include "mlir/SPIRV/SPIRVOps.h"
|
||||
#include "mlir/SPIRV/Serialization.h"
|
||||
#include "mlir/StandardOps/Ops.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "mlir/Translation.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
// Adds a one-block function named as `spirv_module` to `module` and returns the
|
||||
// block. The created block will be terminated by `std.return`.
|
||||
Block *createOneBlockFunction(Builder builder, Module *module) {
|
||||
auto fnType = builder.getFunctionType(/*inputs=*/{}, /*results=*/{});
|
||||
auto *fn = new Function(builder.getUnknownLoc(), "spirv_module", fnType);
|
||||
module->getFunctions().push_back(fn);
|
||||
|
||||
auto *block = new Block();
|
||||
fn->push_back(block);
|
||||
|
||||
OperationState state(builder.getContext(), builder.getUnknownLoc(),
|
||||
ReturnOp::getOperationName());
|
||||
ReturnOp::build(&builder, &state);
|
||||
block->push_back(Operation::create(state));
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
// Deserializes the SPIR-V binary module stored in the file named as
|
||||
// `inputFilename` and returns a module containing the SPIR-V module.
|
||||
std::unique_ptr<Module> deserializeModule(llvm::StringRef inputFilename,
|
||||
MLIRContext *context) {
|
||||
Builder builder(context);
|
||||
|
||||
std::string errorMessage;
|
||||
auto file = openInputFile(inputFilename, &errorMessage);
|
||||
if (!file) {
|
||||
context->emitError(builder.getUnknownLoc()) << errorMessage;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Make sure the input stream can be treated as a stream of SPIR-V words
|
||||
auto start = file->getBufferStart();
|
||||
auto end = file->getBufferEnd();
|
||||
if ((start - end) % sizeof(uint32_t) != 0) {
|
||||
context->emitError(builder.getUnknownLoc())
|
||||
<< "SPIR-V binary module must contain integral number of 32-bit words";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto binary = llvm::makeArrayRef(reinterpret_cast<const uint32_t *>(start),
|
||||
(end - start) / sizeof(uint32_t));
|
||||
|
||||
auto spirvModule = spirv::deserialize(binary, context);
|
||||
if (!spirvModule)
|
||||
return {};
|
||||
|
||||
// TODO(antiagainst): due to the restriction of the current translation
|
||||
// infrastructure, we must return a MLIR module here. So we are wrapping the
|
||||
// converted SPIR-V ModuleOp inside a MLIR module. This should be changed to
|
||||
// return the SPIR-V ModuleOp directly after module and function are migrated
|
||||
// to be general ops.
|
||||
std::unique_ptr<Module> module(builder.createModule());
|
||||
Block *block = createOneBlockFunction(builder, module.get());
|
||||
block->push_front(spirvModule->getOperation());
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
static TranslateToMLIRRegistration
|
||||
registration("deserialize-spirv",
|
||||
[](StringRef inputFilename, MLIRContext *context) {
|
||||
return deserializeModule(inputFilename, context);
|
||||
});
|
||||
78
mlir/lib/SPIRV/Serialization/ConvertToBinary.cpp
Normal file
78
mlir/lib/SPIRV/Serialization/ConvertToBinary.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
//===- ConvertToBinary.cpp - MLIR SPIR-V module to binary conversion ------===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file implements a translation from MLIR SPIR-V ModuleOp to SPIR-V
|
||||
// binary module.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/IR/Module.h"
|
||||
#include "mlir/SPIRV/SPIRVOps.h"
|
||||
#include "mlir/SPIRV/Serialization.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "mlir/Translation.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
LogicalResult serializeModule(Module *module, StringRef outputFilename) {
|
||||
if (!module)
|
||||
return failure();
|
||||
|
||||
SmallVector<uint32_t, 0> binary;
|
||||
bool done = false;
|
||||
bool success = false;
|
||||
|
||||
// TODO(antiagainst): we are checking there is only one SPIR-V ModuleOp in
|
||||
// this module and serialize it. This is due to the restriction of the current
|
||||
// translation infrastructure; we must take in a MLIR module here. So we are
|
||||
// wrapping the SPIR-V ModuleOp inside a MLIR module. This should be changed
|
||||
// to take in the SPIR-V ModuleOp directly after module and function are
|
||||
// migrated to be general ops.
|
||||
for (auto &fn : *module) {
|
||||
fn.walk<spirv::ModuleOp>([&](spirv::ModuleOp spirvModule) {
|
||||
if (done) {
|
||||
spirvModule.emitError("found more than one 'spv.module' op");
|
||||
return;
|
||||
}
|
||||
|
||||
done = true;
|
||||
success = spirv::serialize(spirvModule, binary);
|
||||
});
|
||||
}
|
||||
|
||||
if (!success)
|
||||
return failure();
|
||||
|
||||
auto file = openOutputFile(outputFilename);
|
||||
if (!file)
|
||||
return failure();
|
||||
|
||||
file->os().write(reinterpret_cast<char *>(binary.data()),
|
||||
binary.size() * sizeof(uint32_t));
|
||||
file->keep();
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
static TranslateFromMLIRRegistration
|
||||
registration("serialize-spirv",
|
||||
[](Module *module, StringRef outputFilename) {
|
||||
return failed(serializeModule(module, outputFilename));
|
||||
});
|
||||
197
mlir/lib/SPIRV/Serialization/Deserializer.cpp
Normal file
197
mlir/lib/SPIRV/Serialization/Deserializer.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
//===- Deserializer.cpp - MLIR SPIR-V Deserialization ---------------------===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file defines the SPIR-V binary to MLIR SPIR-V module deseralization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/SPIRV/Serialization.h"
|
||||
|
||||
#include "SPIRVBinaryUtils.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/Location.h"
|
||||
#include "mlir/SPIRV/SPIRVOps.h"
|
||||
#include "mlir/SPIRV/SPIRVTypes.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
namespace {
|
||||
/// A SPIR-V module serializer.
|
||||
///
|
||||
/// A SPIR-V binary module is a single linear stream of instructions; each
|
||||
/// instruction is composed of 32-bit words. The first word of an instruction
|
||||
/// records the total number of words of that instruction using the 16
|
||||
/// higher-order bits. So this deserializer uses that to get instruction
|
||||
/// boundary and parse instructions and build a SPIR-V ModuleOp gradually.
|
||||
///
|
||||
// TODO(antiagainst): clean up created ops on errors
|
||||
class Deserializer {
|
||||
public:
|
||||
/// Creates a deserializer for the given SPIR-V `binary` module.
|
||||
/// The SPIR-V ModuleOp will be created into `context.
|
||||
explicit Deserializer(ArrayRef<uint32_t> binary, MLIRContext *context);
|
||||
|
||||
/// Deserializes the remembered SPIR-V binary module.
|
||||
LogicalResult deserialize();
|
||||
|
||||
/// Collects the final SPIR-V ModuleOp.
|
||||
Optional<spirv::ModuleOp> collect();
|
||||
|
||||
private:
|
||||
/// Processes SPIR-V module header.
|
||||
LogicalResult processHeader();
|
||||
|
||||
/// Processes a SPIR-V instruction with the given `opcode` and `operands`.
|
||||
LogicalResult processInstruction(uint32_t opcode,
|
||||
ArrayRef<uint32_t> operands);
|
||||
|
||||
LogicalResult processMemoryModel(ArrayRef<uint32_t> operands);
|
||||
|
||||
/// Initializes the `module` ModuleOp in this deserializer instance.
|
||||
spirv::ModuleOp createModuleOp();
|
||||
|
||||
private:
|
||||
/// The SPIR-V binary module.
|
||||
ArrayRef<uint32_t> binary;
|
||||
|
||||
/// The current word offset into the binary module.
|
||||
unsigned curOffset = 0;
|
||||
|
||||
/// MLIRContext to create SPIR-V ModuleOp into.
|
||||
MLIRContext *context;
|
||||
|
||||
// TODO(antiagainst): create Location subclass for binary blob
|
||||
UnknownLoc unknownLoc;
|
||||
|
||||
/// The SPIR-V ModuleOp.
|
||||
Optional<spirv::ModuleOp> module;
|
||||
|
||||
OpBuilder opBuilder;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Deserializer::Deserializer(ArrayRef<uint32_t> binary, MLIRContext *context)
|
||||
: binary(binary), context(context), unknownLoc(UnknownLoc::get(context)),
|
||||
module(createModuleOp()),
|
||||
opBuilder(module->getOperation()->getRegion(0)) {}
|
||||
|
||||
LogicalResult Deserializer::deserialize() {
|
||||
if (failed(processHeader()))
|
||||
return failure();
|
||||
|
||||
auto binarySize = binary.size();
|
||||
curOffset = spirv::kHeaderWordCount;
|
||||
|
||||
while (curOffset < binarySize) {
|
||||
// For each instruction, get its word count from the first word to slice it
|
||||
// from the stream properly, and then dispatch to the instruction handler.
|
||||
|
||||
uint32_t wordCount = binary[curOffset] >> 16;
|
||||
uint32_t opcode = binary[curOffset] & 0xffff;
|
||||
|
||||
if (wordCount == 0)
|
||||
return context->emitError(unknownLoc, "word count cannot be zero");
|
||||
|
||||
uint32_t nextOffset = curOffset + wordCount;
|
||||
if (nextOffset > binarySize)
|
||||
return context->emitError(unknownLoc,
|
||||
"insufficient words for the last instruction");
|
||||
|
||||
auto operands = binary.slice(curOffset + 1, wordCount - 1);
|
||||
if (failed(processInstruction(opcode, operands)))
|
||||
return failure();
|
||||
|
||||
curOffset = nextOffset;
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
Optional<spirv::ModuleOp> Deserializer::collect() { return module; }
|
||||
|
||||
LogicalResult Deserializer::processHeader() {
|
||||
if (binary.size() < spirv::kHeaderWordCount)
|
||||
return context->emitError(unknownLoc,
|
||||
"SPIR-V binary module must have a 5-word header");
|
||||
|
||||
if (binary[0] != spirv::kMagicNumber)
|
||||
return context->emitError(unknownLoc, "incorrect magic number");
|
||||
|
||||
// TODO(antiagainst): generator number, bound, schema
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult Deserializer::processInstruction(uint32_t opcode,
|
||||
ArrayRef<uint32_t> operands) {
|
||||
switch (opcode) {
|
||||
case spirv::kOpMemoryModelOpcode:
|
||||
return processMemoryModel(operands);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return context->emitError(unknownLoc, "NYI: opcode ") << opcode;
|
||||
}
|
||||
|
||||
LogicalResult Deserializer::processMemoryModel(ArrayRef<uint32_t> operands) {
|
||||
if (operands.size() != 2)
|
||||
return context->emitError(unknownLoc,
|
||||
"OpMemoryModel must have two operands");
|
||||
|
||||
// TODO(antiagainst): use IntegerAttr-backed enum attributes to avoid the
|
||||
// excessive string conversions here.
|
||||
|
||||
auto am = spirv::symbolizeAddressingModel(operands.front());
|
||||
if (!am)
|
||||
return context->emitError(unknownLoc,
|
||||
"unknown addressing model for OpMemoryModel");
|
||||
|
||||
auto mm = spirv::symbolizeMemoryModel(operands.back());
|
||||
if (!mm)
|
||||
return context->emitError(unknownLoc,
|
||||
"unknown memory model for OpMemoryModel");
|
||||
|
||||
module->setAttr(
|
||||
"addressing_model",
|
||||
opBuilder.getStringAttr(spirv::stringifyAddressingModel(*am)));
|
||||
module->setAttr("memory_model",
|
||||
opBuilder.getStringAttr(spirv::stringifyMemoryModel(*mm)));
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
spirv::ModuleOp Deserializer::createModuleOp() {
|
||||
Builder builder(context);
|
||||
OperationState state(context, unknownLoc,
|
||||
spirv::ModuleOp::getOperationName());
|
||||
// TODO(antiagainst): use target environment to select the version
|
||||
state.addAttribute("major_version", builder.getI32IntegerAttr(1));
|
||||
state.addAttribute("minor_version", builder.getI32IntegerAttr(0));
|
||||
spirv::ModuleOp::build(&builder, &state);
|
||||
return llvm::cast<spirv::ModuleOp>(Operation::create(state));
|
||||
}
|
||||
|
||||
Optional<spirv::ModuleOp> spirv::deserialize(ArrayRef<uint32_t> binary,
|
||||
MLIRContext *context) {
|
||||
Deserializer deserializer(binary, context);
|
||||
|
||||
if (failed(deserializer.deserialize()))
|
||||
return llvm::None;
|
||||
|
||||
return deserializer.collect();
|
||||
}
|
||||
42
mlir/lib/SPIRV/Serialization/SPIRVBinaryUtils.h
Normal file
42
mlir/lib/SPIRV/Serialization/SPIRVBinaryUtils.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//===- SPIRVBinaryUtils.cpp - SPIR-V Binary Module Utils --------*- C++ -*-===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file defines common utilities for SPIR-V binary module.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef MLIR_SPIRV_SERIALIZATION_SPIRV_BINARY_UTILS_H_
|
||||
#define MLIR_SPIRV_SERIALIZATION_SPIRV_BINARY_UTILS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace mlir {
|
||||
namespace spirv {
|
||||
|
||||
/// SPIR-V binary header word count
|
||||
constexpr unsigned kHeaderWordCount = 5;
|
||||
|
||||
/// SPIR-V magic number
|
||||
constexpr uint32_t kMagicNumber = 0x07230203;
|
||||
|
||||
/// Opcode for SPIR-V OpMemoryModel
|
||||
constexpr uint32_t kOpMemoryModelOpcode = 14;
|
||||
|
||||
} // end namespace spirv
|
||||
} // end namespace mlir
|
||||
|
||||
#endif // MLIR_SPIRV_SERIALIZATION_SPIRV_BINARY_UTILS_H_
|
||||
168
mlir/lib/SPIRV/Serialization/Serializer.cpp
Normal file
168
mlir/lib/SPIRV/Serialization/Serializer.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
//===- Serializer.cpp - MLIR SPIR-V Serialization -------------------------===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file defines the MLIR SPIR-V module to SPIR-V binary seralization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/SPIRV/Serialization.h"
|
||||
|
||||
#include "SPIRVBinaryUtils.h"
|
||||
#include "mlir/SPIRV/SPIRVOps.h"
|
||||
#include "mlir/SPIRV/SPIRVTypes.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
static inline uint32_t getPrefixedOpcode(uint32_t wordCount, uint32_t opcode) {
|
||||
assert(((wordCount >> 16) == 0) && "word count out of range!");
|
||||
return (wordCount << 16) | opcode;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// A SPIR-V module serializer.
|
||||
///
|
||||
/// A SPIR-V binary module is a single linear stream of instructions; each
|
||||
/// instruction is composed of 32-bit words with the layout:
|
||||
///
|
||||
/// | <word-count>|<opcode> | <operand> | <operand> | ... |
|
||||
/// | <------ word -------> | <-- word --> | <-- word --> | ... |
|
||||
///
|
||||
/// For the first word, the 16 high-order bits are the word count of the
|
||||
/// instruction, the 16 low-order bits are the opcode enumerant. The
|
||||
/// instructions then belong to different sections, which must be laid out in
|
||||
/// the particular order as specified in "2.4 Logical Layout of a Module" of
|
||||
/// the SPIR-V spec.
|
||||
class Serializer {
|
||||
public:
|
||||
/// Creates a serializer for the given SPIR-V `module`.
|
||||
explicit Serializer(spirv::ModuleOp module) : module(module) {}
|
||||
|
||||
/// Serializes the remembered SPIR-V module.
|
||||
LogicalResult serialize();
|
||||
|
||||
/// Collects the final SPIR-V `binary`.
|
||||
void collect(SmallVectorImpl<uint32_t> &binary);
|
||||
|
||||
private:
|
||||
/// Creates SPIR-V module header in the given `header`.
|
||||
void processHeader(SmallVectorImpl<uint32_t> &header);
|
||||
|
||||
void processMemoryModel();
|
||||
|
||||
private:
|
||||
/// The SPIR-V module to be serialized.
|
||||
spirv::ModuleOp module;
|
||||
|
||||
/// The next available result <id>.
|
||||
uint32_t nextID = 0;
|
||||
|
||||
// The following are for different SPIR-V instruction sections. They follow
|
||||
// the logical layout of a SPIR-V module.
|
||||
|
||||
SmallVector<uint32_t, 4> capabilities;
|
||||
SmallVector<uint32_t, 0> extensions;
|
||||
SmallVector<uint32_t, 0> extendedSets;
|
||||
SmallVector<uint32_t, 3> memoryModel;
|
||||
SmallVector<uint32_t, 0> entryPoints;
|
||||
SmallVector<uint32_t, 4> executionModes;
|
||||
// TODO(antiagainst): debug instructions
|
||||
SmallVector<uint32_t, 0> decorations;
|
||||
SmallVector<uint32_t, 0> typesGlobalValues;
|
||||
SmallVector<uint32_t, 0> functions;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
#include "mlir/SPIRV/SPIRVSerialization.inc"
|
||||
}
|
||||
|
||||
LogicalResult Serializer::serialize() {
|
||||
if (failed(module.verify()))
|
||||
return failure();
|
||||
|
||||
// TODO(antiagainst): handle the other sections
|
||||
processMemoryModel();
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
void Serializer::collect(SmallVectorImpl<uint32_t> &binary) {
|
||||
// The number of words in the SPIR-V module header
|
||||
|
||||
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);
|
||||
|
||||
processHeader(binary);
|
||||
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(decorations.begin(), decorations.end());
|
||||
binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
|
||||
binary.append(functions.begin(), functions.end());
|
||||
}
|
||||
|
||||
void Serializer::processHeader(SmallVectorImpl<uint32_t> &header) {
|
||||
// The serializer tool ID registered to the Khronos Group
|
||||
constexpr uint32_t kGeneratorNumber = 22;
|
||||
// The major and minor version number for the generated SPIR-V binary.
|
||||
// TODO(antiagainst): use target environment to select the version
|
||||
constexpr uint8_t kMajorVersion = 1;
|
||||
constexpr uint8_t kMinorVersion = 0;
|
||||
|
||||
header.push_back(spirv::kMagicNumber);
|
||||
header.push_back((kMajorVersion << 16) | (kMinorVersion << 8));
|
||||
header.push_back(kGeneratorNumber);
|
||||
header.push_back(nextID); // ID bound
|
||||
header.push_back(0); // Schema (reserved word)
|
||||
}
|
||||
|
||||
void Serializer::processMemoryModel() {
|
||||
// TODO(antiagainst): use IntegerAttr-backed enum attributes to avoid the
|
||||
// excessive string conversions here.
|
||||
auto mm = static_cast<uint32_t>(*spirv::symbolizeMemoryModel(
|
||||
module.getAttrOfType<StringAttr>("memory_model").getValue()));
|
||||
auto am = static_cast<uint32_t>(*spirv::symbolizeAddressingModel(
|
||||
module.getAttrOfType<StringAttr>("addressing_model").getValue()));
|
||||
|
||||
constexpr uint32_t kNumWords = 3;
|
||||
|
||||
memoryModel.reserve(kNumWords);
|
||||
memoryModel.assign(
|
||||
{getPrefixedOpcode(kNumWords, spirv::kOpMemoryModelOpcode), am, mm});
|
||||
}
|
||||
|
||||
bool spirv::serialize(spirv::ModuleOp module,
|
||||
SmallVectorImpl<uint32_t> &binary) {
|
||||
Serializer serializer(module);
|
||||
|
||||
if (failed(serializer.serialize()))
|
||||
return false;
|
||||
|
||||
serializer.collect(binary);
|
||||
return true;
|
||||
}
|
||||
@@ -170,6 +170,10 @@ StringRef tblgen::EnumAttr::getUnderlyingType() const {
|
||||
return def->getValueAsString("underlyingType");
|
||||
}
|
||||
|
||||
StringRef tblgen::EnumAttr::getUnderlyingToSymbolFnName() const {
|
||||
return def->getValueAsString("underlyingToSymbolFnName");
|
||||
}
|
||||
|
||||
StringRef tblgen::EnumAttr::getStringToSymbolFnName() const {
|
||||
return def->getValueAsString("stringToSymbolFnName");
|
||||
}
|
||||
|
||||
13
mlir/test/SPIRV/Serialization/minimal-module.mlir
Normal file
13
mlir/test/SPIRV/Serialization/minimal-module.mlir
Normal file
@@ -0,0 +1,13 @@
|
||||
// RUN: mlir-translate -serialize-spirv %s | mlir-translate -deserialize-spirv | FileCheck %s
|
||||
|
||||
// CHECK: spv.module {
|
||||
// CHECK-NEXT: } attributes {addressing_model: "Logical", major_version: 1 : i32, memory_model: "VulkanKHR", minor_version: 0 : i32}
|
||||
|
||||
func @spirv_module() -> () {
|
||||
spv.module {
|
||||
} attributes {
|
||||
addressing_model: "Logical",
|
||||
memory_model: "VulkanKHR"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -11,5 +11,6 @@ add_tablegen(mlir-tblgen MLIR
|
||||
OpDocGen.cpp
|
||||
ReferenceImplGen.cpp
|
||||
RewriterGen.cpp
|
||||
SPIRVSerializationGen.cpp
|
||||
)
|
||||
set_target_properties(mlir-tblgen PROPERTIES FOLDER "Tablegenning")
|
||||
|
||||
@@ -109,6 +109,7 @@ static void emitEnumDecl(const Record &enumDef, raw_ostream &os) {
|
||||
StringRef description = enumAttr.getDescription();
|
||||
StringRef strToSymFnName = enumAttr.getStringToSymbolFnName();
|
||||
StringRef symToStrFnName = enumAttr.getSymbolToStringFnName();
|
||||
StringRef underlyingToSymFnName = enumAttr.getUnderlyingToSymbolFnName();
|
||||
StringRef maxEnumValFnName = enumAttr.getMaxEnumValFnName();
|
||||
auto enumerants = enumAttr.getAllCases();
|
||||
|
||||
@@ -122,6 +123,9 @@ static void emitEnumDecl(const Record &enumDef, raw_ostream &os) {
|
||||
emitEnumClass(enumDef, enumName, underlyingType, description, enumerants, os);
|
||||
|
||||
// Emit coversion function declarations
|
||||
os << formatv(
|
||||
"llvm::Optional<{0}> {1}({2});\n", enumName, underlyingToSymFnName,
|
||||
underlyingType.empty() ? std::string("unsigned") : underlyingType);
|
||||
os << formatv("llvm::StringRef {1}({0});\n", enumName, symToStrFnName);
|
||||
os << formatv("llvm::Optional<{0}> {1}(llvm::StringRef);\n", enumName,
|
||||
strToSymFnName);
|
||||
@@ -158,8 +162,10 @@ static void emitEnumDef(const Record &enumDef, raw_ostream &os) {
|
||||
EnumAttr enumAttr(enumDef);
|
||||
StringRef enumName = enumAttr.getEnumClassName();
|
||||
StringRef cppNamespace = enumAttr.getCppNamespace();
|
||||
std::string underlyingType = enumAttr.getUnderlyingType();
|
||||
StringRef strToSymFnName = enumAttr.getStringToSymbolFnName();
|
||||
StringRef symToStrFnName = enumAttr.getSymbolToStringFnName();
|
||||
StringRef underlyingToSymFnName = enumAttr.getUnderlyingToSymbolFnName();
|
||||
auto enumerants = enumAttr.getAllCases();
|
||||
|
||||
llvm::SmallVector<StringRef, 2> namespaces;
|
||||
@@ -179,6 +185,21 @@ static void emitEnumDef(const Record &enumDef, raw_ostream &os) {
|
||||
os << " return \"\";\n";
|
||||
os << "}\n\n";
|
||||
|
||||
os << formatv("llvm::Optional<{0}> {1}({2} value) {{\n", enumName,
|
||||
underlyingToSymFnName,
|
||||
underlyingType.empty() ? std::string("unsigned")
|
||||
: underlyingType)
|
||||
<< " switch (value) {\n";
|
||||
for (const auto &enumerant : enumerants) {
|
||||
auto symbol = enumerant.getSymbol();
|
||||
auto value = enumerant.getValue();
|
||||
os << formatv(" case {0}: return {1}::{2};\n", value, enumName,
|
||||
makeIdentifier(symbol));
|
||||
}
|
||||
os << " default: return llvm::None;\n"
|
||||
<< " }\n"
|
||||
<< "}\n\n";
|
||||
|
||||
os << formatv("llvm::Optional<{0}> {1}(llvm::StringRef str) {{\n", enumName,
|
||||
strToSymFnName);
|
||||
os << formatv(" return llvm::StringSwitch<llvm::Optional<{0}>>(str)\n",
|
||||
|
||||
69
mlir/tools/mlir-tblgen/SPIRVSerializationGen.cpp
Normal file
69
mlir/tools/mlir-tblgen/SPIRVSerializationGen.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
//===- SPIRVSerializationGen.cpp - SPIR-V serialization utility generator -===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// SPIRVSerializationGen generates common utility functions for SPIR-V
|
||||
// serialization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/TableGen/GenInfo.h"
|
||||
#include "mlir/TableGen/Operator.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TableGen/Error.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
#include "llvm/TableGen/TableGenBackend.h"
|
||||
|
||||
using llvm::formatv;
|
||||
using llvm::raw_ostream;
|
||||
using llvm::Record;
|
||||
using llvm::RecordKeeper;
|
||||
using mlir::tblgen::Operator;
|
||||
|
||||
// Writes the following function to `os`:
|
||||
// inline uint32_t getOpcode(<op-class-name>) { return <opcode>; }
|
||||
static void emitGetOpcodeFunction(const llvm::Record &record,
|
||||
const Operator &op, raw_ostream &os) {
|
||||
if (llvm::isa<llvm::UnsetInit>(record.getValueInit("opcode")))
|
||||
return;
|
||||
|
||||
os << formatv("inline uint32_t getOpcode({0}) {{ return {1}u; }\n",
|
||||
op.getQualCppClassName(), record.getValueAsInt("opcode"));
|
||||
}
|
||||
|
||||
static bool emitSerializationUtils(const RecordKeeper &recordKeeper,
|
||||
raw_ostream &os) {
|
||||
llvm::emitSourceFileHeader("SPIR-V Serialization Utilities", os);
|
||||
|
||||
auto defs = recordKeeper.getAllDerivedDefinitions("SPV_Op");
|
||||
for (const auto *def : defs) {
|
||||
Operator op(def);
|
||||
emitGetOpcodeFunction(*def, op, os);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Registers the enum utility generator to mlir-tblgen.
|
||||
static mlir::GenRegistration
|
||||
genEnumDefs("gen-spirv-serial",
|
||||
"Generate SPIR-V serialization utility definitions",
|
||||
[](const RecordKeeper &records, raw_ostream &os) {
|
||||
return emitSerializationUtils(records, os);
|
||||
});
|
||||
@@ -4,6 +4,8 @@ set(LIBS
|
||||
MLIREDSC
|
||||
MLIRParser
|
||||
MLIRPass
|
||||
MLIRSPIRV
|
||||
MLIRSPIRVSerialization
|
||||
MLIRStandardOps
|
||||
MLIRTargetLLVMIR
|
||||
MLIRTargetNVVMIR
|
||||
|
||||
Reference in New Issue
Block a user