[tint][ir][fuzz] Add SPIR-V output to ir_fuzz_dis
Fixes #352276241
Change-Id: Ic4354f7503f04b0fdd85147894bcbf63183148b6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/197734
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f167311..fd26dbe 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -345,7 +345,7 @@
if (tint_build_wgsl_reader) {
deps += [ "${tint_src_dir}/cmd/fuzz/ir/as" ]
}
- if (tint_build_wgsl_writer) {
+ if (tint_build_wgsl_writer && tint_build_spv_writer) {
deps += [ "${tint_src_dir}/cmd/fuzz/ir/dis" ]
}
}
diff --git a/src/tint/cmd/fuzz/ir/dis/BUILD.bazel b/src/tint/cmd/fuzz/ir/dis/BUILD.bazel
index b427ad4..f82eb66 100644
--- a/src/tint/cmd/fuzz/ir/dis/BUILD.bazel
+++ b/src/tint/cmd/fuzz/ir/dis/BUILD.bazel
@@ -88,6 +88,12 @@
],
"//conditions:default": [],
}) + select({
+ ":tint_build_spv_writer": [
+ "//src/tint/lang/spirv/writer",
+ "//src/tint/lang/spirv/writer/common",
+ ],
+ "//conditions:default": [],
+ }) + select({
":tint_build_wgsl_writer": [
"//src/tint/lang/wgsl/writer",
],
@@ -113,6 +119,11 @@
)
alias(
+ name = "tint_build_spv_writer",
+ actual = "//src/tint:tint_build_spv_writer_true",
+)
+
+alias(
name = "tint_build_wgsl_writer",
actual = "//src/tint:tint_build_wgsl_writer_true",
)
@@ -125,11 +136,12 @@
],
)
selects.config_setting_group(
- name = "tint_build_ir_binary_and_tint_build_wgsl_writer_and_tint_build_ir_fuzzer",
+ name = "tint_build_ir_binary_and_tint_build_ir_fuzzer_and_tint_build_wgsl_writer_and_tint_build_spv_writer",
match_all = [
":tint_build_ir_binary",
- ":tint_build_wgsl_writer",
":tint_build_ir_fuzzer",
+ ":tint_build_wgsl_writer",
+ ":tint_build_spv_writer",
],
)
diff --git a/src/tint/cmd/fuzz/ir/dis/BUILD.cfg b/src/tint/cmd/fuzz/ir/dis/BUILD.cfg
index 8038547..93c3706 100644
--- a/src/tint/cmd/fuzz/ir/dis/BUILD.cfg
+++ b/src/tint/cmd/fuzz/ir/dis/BUILD.cfg
@@ -1,6 +1,6 @@
{
"cmd": {
- "Condition": "tint_build_ir_binary && tint_build_wgsl_writer && tint_build_ir_fuzzer",
+ "Condition": "tint_build_ir_binary && tint_build_ir_fuzzer && tint_build_wgsl_writer && tint_build_spv_writer",
"OutputName": "ir_fuzz_dis",
"Internal": [ "utils/protos/ir_fuzz:proto" ],
},
diff --git a/src/tint/cmd/fuzz/ir/dis/BUILD.cmake b/src/tint/cmd/fuzz/ir/dis/BUILD.cmake
index e1ab456..169c6c8 100644
--- a/src/tint/cmd/fuzz/ir/dis/BUILD.cmake
+++ b/src/tint/cmd/fuzz/ir/dis/BUILD.cmake
@@ -34,11 +34,11 @@
# Do not modify this file directly
################################################################################
-if(TINT_BUILD_IR_BINARY AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_IR_FUZZER)
+if(TINT_BUILD_IR_BINARY AND TINT_BUILD_IR_FUZZER AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_SPV_WRITER)
################################################################################
# Target: tint_cmd_fuzz_ir_dis_cmd
# Kind: cmd
-# Condition: TINT_BUILD_IR_BINARY AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_IR_FUZZER
+# Condition: TINT_BUILD_IR_BINARY AND TINT_BUILD_IR_FUZZER AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_SPV_WRITER
################################################################################
tint_add_target(tint_cmd_fuzz_ir_dis_cmd cmd
cmd/fuzz/ir/dis/main.cc
@@ -95,6 +95,13 @@
)
endif(TINT_BUILD_SPV_READER)
+if(TINT_BUILD_SPV_WRITER)
+ tint_target_add_dependencies(tint_cmd_fuzz_ir_dis_cmd cmd
+ tint_lang_spirv_writer
+ tint_lang_spirv_writer_common
+ )
+endif(TINT_BUILD_SPV_WRITER)
+
if(TINT_BUILD_WGSL_WRITER)
tint_target_add_dependencies(tint_cmd_fuzz_ir_dis_cmd cmd
tint_lang_wgsl_writer
@@ -103,4 +110,4 @@
tint_target_set_output_name(tint_cmd_fuzz_ir_dis_cmd cmd "ir_fuzz_dis")
-endif(TINT_BUILD_IR_BINARY AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_IR_FUZZER)
\ No newline at end of file
+endif(TINT_BUILD_IR_BINARY AND TINT_BUILD_IR_FUZZER AND TINT_BUILD_WGSL_WRITER AND TINT_BUILD_SPV_WRITER)
\ No newline at end of file
diff --git a/src/tint/cmd/fuzz/ir/dis/BUILD.gn b/src/tint/cmd/fuzz/ir/dis/BUILD.gn
index 6b47007..c1c471a 100644
--- a/src/tint/cmd/fuzz/ir/dis/BUILD.gn
+++ b/src/tint/cmd/fuzz/ir/dis/BUILD.gn
@@ -37,7 +37,8 @@
import("../../../../../../scripts/tint_overrides_with_defaults.gni")
import("${tint_src_dir}/tint.gni")
-if (tint_build_ir_binary && tint_build_wgsl_writer && tint_build_ir_fuzzer) {
+if (tint_build_ir_binary && tint_build_ir_fuzzer && tint_build_wgsl_writer &&
+ tint_build_spv_writer) {
tint_executable("dis") {
output_name = "ir_fuzz_dis"
sources = [ "main.cc" ]
@@ -86,6 +87,13 @@
deps += [ "${tint_src_dir}/lang/spirv/reader/common" ]
}
+ if (tint_build_spv_writer) {
+ deps += [
+ "${tint_src_dir}/lang/spirv/writer",
+ "${tint_src_dir}/lang/spirv/writer/common",
+ ]
+ }
+
if (tint_build_wgsl_writer) {
deps += [ "${tint_src_dir}/lang/wgsl/writer" ]
}
diff --git a/src/tint/cmd/fuzz/ir/dis/main.cc b/src/tint/cmd/fuzz/ir/dis/main.cc
index f6685cd..40ff405 100644
--- a/src/tint/cmd/fuzz/ir/dis/main.cc
+++ b/src/tint/cmd/fuzz/ir/dis/main.cc
@@ -34,6 +34,7 @@
#include "src/tint/cmd/common/helper.h"
#include "src/tint/lang/core/ir/binary/decode.h"
#include "src/tint/lang/core/ir/disassembler.h"
+#include "src/tint/lang/spirv/writer/writer.h"
#include "src/tint/lang/wgsl/writer/writer.h"
#include "src/tint/utils/cli/cli.h"
#include "src/tint/utils/containers/transform.h"
@@ -43,25 +44,112 @@
#include "src/tint/utils/text/styled_text.h"
#include "src/tint/utils/text/styled_text_printer.h"
+#include "third_party/spirv-tools/src/include/spirv-tools/libspirv.h"
+#include "third_party/spirv-tools/src/include/spirv-tools/libspirv.hpp"
+
TINT_BEGIN_DISABLE_PROTOBUF_WARNINGS();
#include "src/tint/utils/protos/ir_fuzz/ir_fuzz.pb.h"
TINT_END_DISABLE_PROTOBUF_WARNINGS();
namespace {
+/// @param data spriv shader to be converted
+/// @returns human readable text representation of a SPIR-V shader
+tint::Result<std::string> DisassembleSpv(const std::vector<uint32_t>& data) {
+ std::string spv_errors;
+ spv_target_env target_env = SPV_ENV_VULKAN_1_1;
+
+ auto msg_consumer = [&spv_errors](spv_message_level_t level, const char*,
+ const spv_position_t& position, const char* message) {
+ switch (level) {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ spv_errors +=
+ "error: line " + std::to_string(position.index) + ": " + message + "\n";
+ break;
+ case SPV_MSG_WARNING:
+ spv_errors +=
+ "warning: line " + std::to_string(position.index) + ": " + message + "\n";
+ break;
+ case SPV_MSG_INFO:
+ spv_errors +=
+ "info: line " + std::to_string(position.index) + ": " + message + "\n";
+ break;
+ case SPV_MSG_DEBUG:
+ break;
+ }
+ };
+
+ spvtools::SpirvTools tools(target_env);
+ tools.SetMessageConsumer(msg_consumer);
+
+ std::string result;
+ if (!tools.Disassemble(
+ data, &result,
+ SPV_BINARY_TO_TEXT_OPTION_INDENT | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)) {
+ return tint::Failure(spv_errors);
+ }
+
+ return result;
+}
+
+enum class Format : uint8_t {
+ kUnknown,
+ kSpirv,
+ kSpvAsm,
+ kWgsl,
+};
+
+/// @param filename the filename to inspect
+/// @returns the inferred format for the filename suffix
+Format InferFormat(const std::string& filename) {
+ if (tint::HasSuffix(filename, ".spv")) {
+ return Format::kSpirv;
+ }
+ if (tint::HasSuffix(filename, ".spvasm")) {
+ return Format::kSpvAsm;
+ }
+
+ if (tint::HasSuffix(filename, ".wgsl")) {
+ return Format::kWgsl;
+ }
+
+ return Format::kUnknown;
+}
+
struct Options {
std::unique_ptr<tint::StyledTextPrinter> printer;
std::string input_filename;
std::string output_filename;
+ Format format = Format::kUnknown;
+
bool dump_wgsl = false;
+ bool dump_spirv = false;
};
bool ParseArgs(tint::VectorRef<std::string_view> arguments, Options* opts) {
using namespace tint::cli; // NOLINT(build/namespaces)
OptionSet options;
+
+ tint::Vector<EnumName<Format>, 3> format_enum_names{
+ EnumName(Format::kSpirv, "spirv"),
+ EnumName(Format::kSpvAsm, "spvasm"),
+ EnumName(Format::kWgsl, "wgsl"),
+ };
+
+ auto& fmt = options.Add<EnumOption<Format>>("format",
+ R"(Output format to be written to file.
+If not provided, will be inferred from output filename extension:
+ .spvasm -> spvasm
+ .spv -> spirv
+ .wgsl -> wgsl)",
+ format_enum_names, ShortName{"f"});
+ TINT_DEFER(opts->format = fmt.value.value_or(Format::kUnknown));
+
auto& col = options.Add<EnumOption<tint::ColorMode>>(
"color", "Use colored output",
tint::Vector{
@@ -73,7 +161,8 @@
TINT_DEFER(opts->printer = CreatePrinter(*col.value));
auto& output = options.Add<StringOption>(
- "output-filename", "Output file name, if not specified, IR text output will go to STDOUT",
+ "output-filename",
+ "Output file name for shader output, if not specified nothing will be written to file",
ShortName{"o"}, Parameter{"name"});
TINT_DEFER(opts->output_filename = output.value.value_or(""));
@@ -82,6 +171,12 @@
Alias{"emit-wgsl"}, Default{false});
TINT_DEFER(opts->dump_wgsl = *dump_wgsl.value);
+ auto& dump_spirv = options.Add<BoolOption>(
+ "dump-spirv",
+ "Writes the SPIR-V form of input to stdout, may fail due to validation errors",
+ Alias{"emit-spirv"}, Default{false});
+ TINT_DEFER(opts->dump_spirv = *dump_spirv.value);
+
auto& help = options.Add<BoolOption>("help", "Show usage", ShortName{"h"});
auto show_usage = [&] {
@@ -98,6 +193,7 @@
show_usage();
return false;
}
+
if (help.value.value_or(false)) {
show_usage();
return false;
@@ -109,6 +205,7 @@
<< tint::Join(Transform(args, tint::Quote), ", ") << "\n";
return false;
}
+
if (args.Length() == 1) {
opts->input_filename = args[0];
}
@@ -116,6 +213,28 @@
return true;
}
+/// Infer any missing option values, then validate that the provide values are
+/// usable
+/// @param options set of values provided by the user to the binary
+/// @returns true if the options are usable, otherwise false. Prints error messages to STDERR
+bool InferAndValidateOptions(Options& options) {
+ if (options.format == Format::kUnknown && !options.output_filename.empty()) {
+ options.format = InferFormat(options.output_filename);
+ if (options.format == Format::kUnknown) {
+ std::cerr << "Unable to determine output format from filename, "
+ << options.output_filename << "\n";
+ return false;
+ }
+ }
+
+ if (options.format != Format::kUnknown && options.output_filename.empty()) {
+ std::cerr << "Format provided, but no output filename provided\n";
+ return false;
+ }
+
+ return true;
+}
+
/// @returns a fuzzer test case protobuf for the given file
/// @param options program options that contains the filename to be read, etc.
tint::Result<tint::cmd::fuzz::ir::pb::Root> GenerateFuzzCaseProto(const Options& options) {
@@ -129,47 +248,134 @@
return std::move(fuzz_pb);
}
-bool ProcessFile(const Options& options) {
+/// Prints IR text representation to STDOUT
+/// @param options options passed into the binary
+/// @param module IR module parsed from input protobuf
+void EmitIR(const Options& options, tint::core::ir::Module& module) {
+ const auto ir_text = tint::core::ir::Disassembler(module).Text();
+ if (options.output_filename.empty()) {
+ options.printer->Print(ir_text);
+ options.printer->Print(tint::StyledText{} << "\n");
+ }
+}
+
+/// Prints WGSL shader to STDOUT or to a file as determined by options
+/// @param options options passed into the binary
+/// @param module IR module parsed from input protobuf
+/// @returns true if all operations succeeded, otherwise false. Prints error messages to STDERR
+bool EmitWGSL(const Options& options, tint::core::ir::Module& module) {
+ if (!options.dump_wgsl && options.format != Format::kWgsl) {
+ return true;
+ }
+
+ tint::wgsl::writer::ProgramOptions writer_options;
+ auto output = tint::wgsl::writer::WgslFromIR(module, writer_options);
+ if (output != tint::Success) {
+ std::cerr << "Failed to convert IR to WGSL Program: " << output.Failure() << "\n";
+ return false;
+ }
+
+ if (options.dump_wgsl) {
+ options.printer->Print(tint::StyledText{} << output->wgsl);
+ options.printer->Print(tint::StyledText{} << "\n");
+ }
+
+ if (options.format == Format::kWgsl) {
+ if (!tint::cmd::WriteFile(options.output_filename, "w", output->wgsl)) {
+ std::cerr << "Unable to print WGSL to file, " << options.output_filename << "\n";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/// Prints SPIR-V shader to STDOUT or to a file as determined by options
+/// @param options options passed into the binary
+/// @param module IR module parsed from input protobuf
+/// @returns true if all operations succeeded, otherwise false. Prints error messages to STDERR
+bool EmitSpv(const Options& options, tint::core::ir::Module& module) {
+ if (!options.dump_spirv && options.format != Format::kSpirv &&
+ options.format != Format::kSpvAsm) {
+ return true;
+ }
+ tint::spirv::writer::Options gen_options;
+ auto spv = tint::spirv::writer::Generate(module, gen_options);
+
+ if (spv != tint::Success) {
+ std::cerr << "Failed to convert IR to SPIR-V: " << spv.Failure() << "\n";
+ return false;
+ }
+
+ if (options.dump_spirv || options.format == Format::kSpvAsm) {
+ auto spv_asm = DisassembleSpv(spv.Get().spirv);
+ if (spv_asm != tint::Success) {
+ std::cerr << "Failed to disassemble SPIR-V: " << spv_asm.Failure() << "\n";
+ return false;
+ }
+
+ if (options.dump_spirv) {
+ options.printer->Print(tint::StyledText{} << spv_asm.Get());
+ options.printer->Print(tint::StyledText{} << "\n");
+ }
+
+ if (options.format == Format::kSpvAsm) {
+ if (!tint::cmd::WriteFile(options.output_filename, "w", spv_asm.Get())) {
+ std::cerr << "Unable to print SPIR-V text to file, " << options.output_filename
+ << "\n";
+ return false;
+ }
+ }
+ }
+
+ if (options.format == Format::kSpirv) {
+ if (!tint::cmd::WriteFile(options.output_filename, "wb", spv.Get().spirv)) {
+ std::cerr << "Unable to print SPIR-V to file, " << options.output_filename << "\n";
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Converts and displays the given test case file as determined by the options
+///
+/// NB: There is multiple ::Decode calls, because each emission step may modify the passed in Module
+/// and modules are intentionally non-copyable.
+///
+/// @param options options passed into the binary
+/// @returns true if all operations succeeded, otherwise false. Prints error messages to STDERR
+bool Run(const Options& options) {
auto fuzz_pb = GenerateFuzzCaseProto(options);
if (fuzz_pb != tint::Success) {
std::cerr << "Failed to read test case protobuf: " << fuzz_pb.Failure() << "\n";
return false;
}
- auto module = tint::core::ir::binary::Decode(fuzz_pb.Get().module());
- if (module != tint::Success) {
- std::cerr << "Unable to decode ir protobuf from test case protobuf: " << module.Failure()
- << "\n";
- return false;
- }
-
- const auto ir_text = tint::core::ir::Disassembler(module.Get()).Text();
- if (options.output_filename.empty()) {
- options.printer->Print(ir_text);
- options.printer->Print(tint::StyledText{} << "\n");
- } else {
- if (!tint::cmd::WriteFile(options.output_filename, "w", ir_text.Plain())) {
- std::cerr << "Unable to print IR text to file, " << options.output_filename << "\n";
+ {
+ auto module = tint::core::ir::binary::Decode(fuzz_pb.Get().module());
+ if (module != tint::Success) {
+ std::cerr << "Unable to decode ir protobuf from test case protobuf: "
+ << module.Failure() << "\n";
return false;
}
}
- if (options.dump_wgsl) {
- tint::wgsl::writer::ProgramOptions writer_options;
- auto output = tint::wgsl::writer::WgslFromIR(module.Get(), writer_options);
- if (output != tint::Success) {
- std::cerr << "Failed to convert IR to Program: " << output.Failure() << "\n";
+ {
+ auto module = tint::core::ir::binary::Decode(fuzz_pb.Get().module());
+ EmitIR(options, module.Get());
+ }
+
+ {
+ auto module = tint::core::ir::binary::Decode(fuzz_pb.Get().module());
+ if (!EmitWGSL(options, module.Get())) {
return false;
}
+ }
- if (options.output_filename.empty()) {
- options.printer->Print(tint::StyledText{} << output->wgsl);
- options.printer->Print(tint::StyledText{} << "\n");
- } else {
- if (!tint::cmd::WriteFile(options.output_filename, "w", output->wgsl)) {
- std::cerr << "Unable to print WGSL to file, " << options.output_filename << "\n";
- return false;
- }
+ {
+ auto module = tint::core::ir::binary::Decode(fuzz_pb.Get().module());
+ if (!EmitSpv(options, module.Get())) {
+ return false;
}
}
@@ -196,7 +402,11 @@
return EXIT_FAILURE;
}
- if (!ProcessFile(options)) {
+ if (!InferAndValidateOptions(options)) {
+ return EXIT_FAILURE;
+ }
+
+ if (!Run(options)) {
return EXIT_FAILURE;
}