Skip to content

Commit

Permalink
Add preserver-interface option to spirv-opt
Browse files Browse the repository at this point in the history
The optimizer is able to preserve the interface variables of the
shaders, but that feature has not been exposed to the command line
tool.

This commit adds an option `--preserve-interface` to spirv-opt that will
cause all calls to ADCE to leave the input and output variables, even if
the variable is unused. It will apply regardless of where the option
appears on the command line.

Fixes KhronosGroup#5522
  • Loading branch information
s-perron committed Jan 10, 2024
1 parent c7affa1 commit ac11724
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 18 deletions.
7 changes: 7 additions & 0 deletions include/spirv-tools/libspirv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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|.
//
Expand Down
15 changes: 9 additions & 6 deletions include/spirv-tools/optimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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<std::string>& flags);
bool RegisterPassesFromFlags(const std::vector<std::string>& 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
Expand All @@ -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:
//
Expand Down
47 changes: 36 additions & 11 deletions source/opt/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@

namespace spvtools {

std::vector<std::string> GetVectorOfStrings(const char** strings,
const size_t string_count) {
std::vector<std::string> 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<opt::Pass> p) : pass(std::move(p)) {}

Expand Down Expand Up @@ -256,8 +265,13 @@ Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }

bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
return RegisterPassesFromFlags(flags, false);
}

bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags,
bool preserve_interface) {
for (const auto& flag : flags) {
if (!RegisterPassFromFlag(flag)) {
if (!RegisterPassFromFlag(flag, preserve_interface)) {
return false;
}
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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<std::string> opt_flags;
for (uint32_t i = 0; i < flag_count; i++) {
opt_flags.emplace_back(flags[i]);
}
std::vector<std::string> opt_flags =
spvtools::GetVectorOfStrings(flags, flag_count);
return reinterpret_cast<spvtools::Optimizer*>(optimizer)
->RegisterPassesFromFlags(opt_flags, false);
}

return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
RegisterPassesFromFlags(opt_flags);
SPIRV_TOOLS_EXPORT bool
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
std::vector<std::string> opt_flags =
spvtools::GetVectorOfStrings(flags, flag_count);
return reinterpret_cast<spvtools::Optimizer*>(optimizer)
->RegisterPassesFromFlags(opt_flags, true);
}

SPIRV_TOOLS_EXPORT
Expand Down
10 changes: 9 additions & 1 deletion tools/opt/opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.)");
Expand Down Expand Up @@ -701,6 +706,7 @@ OptStatus ParseFlags(int argc, const char** argv,
spvtools::ValidatorOptions* validator_options,
spvtools::OptimizerOptions* optimizer_options) {
std::vector<std::string> pass_flags;
bool preserve_interface = true;
for (int argi = 1; argi < argc; ++argi) {
const char* cur_arg = argv[argi];
if ('-' == cur_arg[0]) {
Expand Down Expand Up @@ -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'.
Expand All @@ -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};
}

Expand Down

0 comments on commit ac11724

Please sign in to comment.