Skip to content

Commit d82720f

Browse files
committed
[mlir][spirv] Enable serializer to write SPIR-V modules into separate files
By default, `mlir-translate` writes all output into a single file even when `--split-input-file` is used. This is not an issue for text files as they can be easily split with an output separator. However, this causes issues with binary SPIR-V modules. Firstly, a binary file with multiple modules is not a valid SPIR-V, but will be created if multiple modules are specified in the same file and separated by "// -----". This does not cause issues with MLIR internal tools but does not work with SPIRV-Tools. Secondly, splitting binary files after serialization is non-trivial, when compared to text files, so using an external tool is not desirable. This patch adds a SPIR-V serialization option that write SPIR-V modules to separate files in addition to writing them to the `mlir-translate` output file. This is not the ideal solution and ideally `mlir-translate` would allow generating multiple output files when `--split-input-file` is used, however adding such functionality is again non-trival due to how processing and splitting is done: function handles writing to a single `os` that are passed around, and number of split buffers is not known ahead of time. As such a I propose to have a SPIR-V internal option that will dump modules to files in the form they can be processed by `spirv-val`. The behaviour of the new added argument may be confusing, but benefits from being internal to SPIR-V target. Alternatively, we could expose the spirv option in `mlir/lib/Tools/mlir-translate/MlirTranslateMain.cpp`, and slice the output file on the SPIR-V magic number, and not keep the file generated by default by `mlir-translate`. This would be a bit cleaner in API sense, as it would not generate the additional file containing all modules together. However, it pushes SPIR-V specific code into the generic part of the `mlir-translate` and slicing is potentially more error prone that just writing a single module after it was serialized.
1 parent e80e7e7 commit d82720f

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

mlir/include/mlir/Target/SPIRV/Serialization.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ struct SerializationOptions {
2727
bool emitSymbolName = true;
2828
/// Whether to emit `OpLine` location information for SPIR-V ops.
2929
bool emitDebugInfo = false;
30+
/// Whether to store a module to an additional file during
31+
/// serialization. This is used to store the SPIR-V module to the
32+
/// file in addition to writing it to `os` passed from the calling
33+
/// tool. This saved file is later used for validation.
34+
bool saveModuleForValidation = false;
35+
/// A prefix prepended to the file used when `saveModuleForValidation`
36+
/// is set to `true`.
37+
std::string validationFilePrefix = "";
3038
};
3139

3240
/// Serializes the given SPIR-V `module` and writes to `binary`. On failure,

mlir/lib/Target/SPIRV/TranslateRegistration.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/ADT/StringRef.h"
2424
#include "llvm/Support/MemoryBuffer.h"
2525
#include "llvm/Support/SourceMgr.h"
26+
#include "llvm/Support/ToolOutputFile.h"
2627

2728
using namespace mlir;
2829

@@ -76,24 +77,57 @@ void registerFromSPIRVTranslation() {
7677
// Serialization registration
7778
//===----------------------------------------------------------------------===//
7879

79-
static LogicalResult serializeModule(spirv::ModuleOp module,
80-
raw_ostream &output) {
80+
// Static variable is probably not ideal, but it lets us have unique files names
81+
// without taking additional parameters from `mlir-translate`.
82+
static size_t validationFileCounter = 0;
83+
84+
static LogicalResult
85+
serializeModule(spirv::ModuleOp module, raw_ostream &output,
86+
const spirv::SerializationOptions &options) {
87+
8188
SmallVector<uint32_t, 0> binary;
8289
if (failed(spirv::serialize(module, binary)))
8390
return failure();
8491

85-
output.write(reinterpret_cast<char *>(binary.data()),
86-
binary.size() * sizeof(uint32_t));
92+
size_t sizeInBytes = binary.size() * sizeof(uint32_t);
93+
94+
output.write(reinterpret_cast<char *>(binary.data()), sizeInBytes);
95+
96+
if (options.saveModuleForValidation) {
97+
std::string errorMessage;
98+
std::string filename =
99+
options.validationFilePrefix + std::to_string(validationFileCounter++);
100+
auto validationOutput = openOutputFile(filename, &errorMessage);
101+
if (!validationOutput) {
102+
llvm::errs() << errorMessage << "\n";
103+
return failure();
104+
}
105+
validationOutput->os().write(reinterpret_cast<char *>(binary.data()),
106+
sizeInBytes);
107+
validationOutput->keep();
108+
}
87109

88110
return mlir::success();
89111
}
90112

91113
namespace mlir {
92114
void registerToSPIRVTranslation() {
115+
static llvm::cl::opt<std::string> validationFilesPrefix(
116+
"spirv-save-validation-files-with-prefix",
117+
llvm::cl::desc(
118+
"When non-empty string is passed each serialized SPIR-V module is "
119+
"saved to an additional file that starts with the given prefix. This "
120+
"is used to generate separate binaries for validation, where "
121+
"`--split-input-file` normally combines all outputs into one. The "
122+
"one combined output (`-o`) is still written."),
123+
llvm::cl::init(""));
124+
93125
TranslateFromMLIRRegistration toBinary(
94126
"serialize-spirv", "serialize SPIR-V dialect",
95127
[](spirv::ModuleOp module, raw_ostream &output) {
96-
return serializeModule(module, output);
128+
return serializeModule(module, output,
129+
{true, false, (validationFilesPrefix != ""),
130+
validationFilesPrefix});
97131
},
98132
[](DialectRegistry &registry) {
99133
registry.insert<spirv::SPIRVDialect>();

0 commit comments

Comments
 (0)