diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index b70f084a8e..abdfc15d4f 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -966,9 +966,16 @@ SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag( spv_optimizer_t* optimizer, const char* flag); // Registers passes specified by length number of flags in an optimizer object. +// Passes may remove interface variables that are unused. SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); +// Registers passes specified by length number of flags in an optimizer object. +// Passes will not remove interface variables. +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); + // Optimizes the SPIR-V code of size |word_count| pointed to by |binary| and // returns an optimized spv_binary in |optimized_binary|. // diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 53ebc59f00..bc4f5bee89 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -100,8 +100,6 @@ class Optimizer { // // If |preserve_interface| is true, all non-io variables in the entry point // interface are considered live and are not eliminated. - // |preserve_interface| should be true if HLSL is generated - // from the SPIR-V bytecode. Optimizer& RegisterPerformancePasses(); Optimizer& RegisterPerformancePasses(bool preserve_interface); @@ -111,8 +109,6 @@ class Optimizer { // // If |preserve_interface| is true, all non-io variables in the entry point // interface are considered live and are not eliminated. - // |preserve_interface| should be true if HLSL is generated - // from the SPIR-V bytecode. Optimizer& RegisterSizePasses(); Optimizer& RegisterSizePasses(bool preserve_interface); @@ -127,8 +123,6 @@ class Optimizer { // // If |preserve_interface| is true, all non-io variables in the entry point // interface are considered live and are not eliminated. - // |preserve_interface| should be true if HLSL is generated - // from the SPIR-V bytecode. Optimizer& RegisterLegalizationPasses(); Optimizer& RegisterLegalizationPasses(bool preserve_interface); @@ -139,8 +133,13 @@ class Optimizer { // error message is emitted to the MessageConsumer object (use // Optimizer::SetMessageConsumer to define a message consumer, if needed). // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + // // If all the passes are registered successfully, it returns true. bool RegisterPassesFromFlags(const std::vector& flags); + bool RegisterPassesFromFlags(const std::vector& flags, + bool preserve_interface); // Registers the optimization pass associated with |flag|. This only accepts // |flag| values of the form "--pass_name[=pass_args]". If no such pass @@ -157,7 +156,11 @@ class Optimizer { // // --legalize-hlsl: Registers all passes that legalize SPIR-V generated by an // HLSL front-end. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. bool RegisterPassFromFlag(const std::string& flag); + bool RegisterPassFromFlag(const std::string& flag, bool preserve_interface); // Validates that |flag| has a valid format. Strings accepted: // diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index d865cf1d47..7cfc89f754 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -33,6 +33,15 @@ namespace spvtools { +std::vector GetVectorOfStrings(const char** strings, + const size_t string_count) { + std::vector result; + for (uint32_t i = 0; i < string_count; i++) { + result.emplace_back(strings[i]); + } + return result; +} + struct Optimizer::PassToken::Impl { Impl(std::unique_ptr p) : pass(std::move(p)) {} @@ -256,8 +265,13 @@ Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) { Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); } bool Optimizer::RegisterPassesFromFlags(const std::vector& flags) { + return RegisterPassesFromFlags(flags, false); +} + +bool Optimizer::RegisterPassesFromFlags(const std::vector& flags, + bool preserve_interface) { for (const auto& flag : flags) { - if (!RegisterPassFromFlag(flag)) { + if (!RegisterPassFromFlag(flag, preserve_interface)) { return false; } } @@ -281,6 +295,11 @@ bool Optimizer::FlagHasValidForm(const std::string& flag) const { } bool Optimizer::RegisterPassFromFlag(const std::string& flag) { + return RegisterPassFromFlag(flag, false); +} + +bool Optimizer::RegisterPassFromFlag(const std::string& flag, + bool preserve_interface) { if (!FlagHasValidForm(flag)) { return false; } @@ -342,7 +361,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(preserve_interface)); } else if (pass_name == "eliminate-insert-extract") { RegisterPass(CreateInsertExtractElimPass()); } else if (pass_name == "eliminate-local-single-block") { @@ -513,11 +532,11 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "fix-storage-class") { RegisterPass(CreateFixStorageClassPass()); } else if (pass_name == "O") { - RegisterPerformancePasses(); + RegisterPerformancePasses(preserve_interface); } else if (pass_name == "Os") { - RegisterSizePasses(); + RegisterSizePasses(preserve_interface); } else if (pass_name == "legalize-hlsl") { - RegisterLegalizationPasses(); + RegisterLegalizationPasses(preserve_interface); } else if (pass_name == "remove-unused-interface-variables") { RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()); } else if (pass_name == "graphics-robust-access") { @@ -1170,13 +1189,19 @@ SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag( SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { - std::vector opt_flags; - for (uint32_t i = 0; i < flag_count; i++) { - opt_flags.emplace_back(flags[i]); - } + std::vector opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast(optimizer) + ->RegisterPassesFromFlags(opt_flags, false); +} - return reinterpret_cast(optimizer)-> - RegisterPassesFromFlags(opt_flags); +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { + std::vector opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast(optimizer) + ->RegisterPassesFromFlags(opt_flags, true); } SPIRV_TOOLS_EXPORT diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 3dfa021fdd..24724e59fa 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -392,6 +392,11 @@ Options (in lexicographical order):)", Ensure that the optimizer preserves all bindings declared within the module, even when those bindings are unused.)"); printf(R"( + --preserve-interface + Ensure that input and output variables are not removed from the + shader, even if they are unused. Note that this option applies to + all passes that will be run regardless of the order of the flags.)"); + printf(R"( --preserve-spec-constants Ensure that the optimizer preserves all specialization constants declared within the module, even when those constants are unused.)"); @@ -701,6 +706,7 @@ OptStatus ParseFlags(int argc, const char** argv, spvtools::ValidatorOptions* validator_options, spvtools::OptimizerOptions* optimizer_options) { std::vector pass_flags; + bool preserve_interface = true; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; if ('-' == cur_arg[0]) { @@ -790,6 +796,8 @@ OptStatus ParseFlags(int argc, const char** argv, validator_options->SetSkipBlockLayout(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { validator_options->SetRelaxStructStore(true); + } else if (0 == strcmp(cur_arg, "--preserve-interface")) { + preserve_interface = true; } else { // Some passes used to accept the form '--pass arg', canonicalize them // to '--pass=arg'. @@ -812,7 +820,7 @@ OptStatus ParseFlags(int argc, const char** argv, } } - if (!optimizer->RegisterPassesFromFlags(pass_flags)) { + if (!optimizer->RegisterPassesFromFlags(pass_flags, preserve_interface)) { return {OPT_STOP, 1}; }