From 310bc84cf7b01f61b1926f50ece3dae3f46ceb54 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Wed, 29 Jun 2016 16:16:03 -0400 Subject: [PATCH] Add the spirv-opt command line tool. --- tools/CMakeLists.txt | 8 +- tools/opt/opt.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 tools/opt/opt.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 4c8c9d37a..90040bb8f 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -25,7 +25,7 @@ # MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. if (NOT ${SPIRV_SKIP_EXECUTABLES}) - set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val) + set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt) add_executable(spirv-as ${CMAKE_CURRENT_SOURCE_DIR}/as/as.cpp) spvtools_default_compile_options(spirv-as) @@ -44,6 +44,12 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) target_include_directories(spirv-val PRIVATE ${spirv-tools_BINARY_DIR} ${spirv-tools_SOURCE_DIR}/source) + add_executable(spirv-opt ${CMAKE_CURRENT_SOURCE_DIR}/opt/opt.cpp) + spvtools_default_compile_options(spirv-opt) + target_link_libraries(spirv-opt PRIVATE SPIRV-Tools-opt ${SPIRV_TOOLS}) + target_include_directories(spirv-opt PRIVATE + ${spirv-tools_SOURCE_DIR}/source ${spirv-tools_BINARY_DIR}) + install(TARGETS ${SPIRV_INSTALL_TARGETS} RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp new file mode 100644 index 000000000..d4d4b8732 --- /dev/null +++ b/tools/opt/opt.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2016 Google Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +#include +#include +#include + +#include "opt/ir_loader.h" +#include "opt/libspirv.hpp" +#include "opt/pass_manager.h" + +using namespace spvtools; + +void PrintUsage(const char* program) { + printf( + R"(%s - Optimize a SPIR-V binary file. + +USAGE: %s [options] [] -o + +The SPIR-V binary is read from . If no file is specified, +or if is "-", then the binary is read from standard input. +if is "-", then the optimized output is written to +standard output. + +NOTE: The optimizer is a work in progress. + +Options: + --strip-debug + Remove all debug instructions. + -h, --help Print this help. + --version Display optimizer version information. +)", + program, program); +} + +int main(int argc, char** argv) { + const char* in_file = nullptr; + const char* out_file = nullptr; + + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1; + + opt::PassManager pass_manager; + + for (int argi = 1; argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0]) { + if (0 == strcmp(cur_arg, "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + return 0; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + PrintUsage(argv[0]); + return 0; + } else if (0 == strcmp(cur_arg, "-o")) { + if (!out_file && argi + 1 < argc) { + out_file = argv[++argi]; + } else { + PrintUsage(argv[0]); + return 1; + } + } else if (0 == strcmp(cur_arg, "--strip-debug")) { + pass_manager.AddPass(); + } else if ('\0' == cur_arg[1]) { + // Setting a filename of "-" to indicate stdin. + if (!in_file) { + in_file = cur_arg; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } else { + PrintUsage(argv[0]); + return 1; + } + } else { + if (!in_file) { + in_file = cur_arg; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } + } + + if (out_file == nullptr) { + fprintf(stderr, "error: -o required\n"); + return 1; + } + + std::vector source; + const bool use_file = in_file && strcmp("-", in_file); + if (FILE* fp = (use_file ? fopen(in_file, "rb") : stdin)) { + uint32_t buf[1024]; + while (size_t len = fread(buf, sizeof(uint32_t), + sizeof(buf) / sizeof(uint32_t), fp)) { + source.insert(source.end(), buf, buf + len); + } + if (ftell(fp) == -1L) { + if (ferror(fp)) { + fprintf(stderr, "error: error reading file '%s'\n", in_file); + return 1; + } + } else { + if (ftell(fp) % sizeof(uint32_t)) { + fprintf(stderr, "error: corrupted word found in file '%s'\n", in_file); + return 1; + } + } + if (use_file) fclose(fp); + } else { + fprintf(stderr, "error: file does not exist '%s'\n", in_file); + return 1; + } + + // Let's do validation first. + spv_context context = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {source.data(), source.size()}; + spv_result_t error = spvValidate(context, &binary, &diagnostic); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); + return error; + } + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); + + std::unique_ptr module = SpvTools(target_env).BuildModule(source); + pass_manager.Run(module.get()); + + std::vector target; + module->ToBinary(&target, /* skip_nop = */ true); + + const bool use_stdout = out_file[0] == '-' && out_file[1] == 0; + if (FILE* fp = (use_stdout ? stdout : fopen(out_file, "wb"))) { + size_t written = fwrite(target.data(), sizeof(uint32_t), target.size(), fp); + if (target.size() != written) { + fprintf(stderr, "error: could not write to file '%s'\n", out_file); + return 1; + } + if (!use_stdout) fclose(fp); + } else { + fprintf(stderr, "error: could not open file '%s'\n", out_file); + return 1; + } + + return 0; +}