diff --git a/Android.mk b/Android.mk index cc336a892..d3c4c72da 100644 --- a/Android.mk +++ b/Android.mk @@ -19,6 +19,7 @@ SPVTOOLS_SRC_FILES := \ source/print.cpp \ source/software_version.cpp \ source/spirv_endian.cpp \ + source/spirv_optimizer_options.cpp \ source/spirv_target_env.cpp \ source/spirv_validator_options.cpp \ source/table.cpp \ diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index a7e1b3007..b3855ba18 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -288,6 +288,12 @@ typedef enum spv_binary_to_text_options_t { SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t) } spv_binary_to_text_options_t; +// Constants + +// The default id bound is to the minimum value for the id limit +// in the spir-v specification under the section "Universal Limits". +const uint32_t kDefaultMaxIdBound = 0x3FFFFF; + // Structures // Information about an operand parsed from a binary SPIR-V module. @@ -360,6 +366,8 @@ typedef struct spv_context_t spv_context_t; typedef struct spv_validator_options_t spv_validator_options_t; +typedef struct spv_optimizer_options_t spv_optimizer_options_t; + // Type Definitions typedef spv_const_binary_t* spv_const_binary; @@ -371,6 +379,8 @@ typedef const spv_context_t* spv_const_context; typedef spv_context_t* spv_context; typedef spv_validator_options_t* spv_validator_options; typedef const spv_validator_options_t* spv_const_validator_options; +typedef spv_optimizer_options_t* spv_optimizer_options; +typedef const spv_optimizer_options_t* spv_const_optimizer_options; // Platform API @@ -485,6 +495,29 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout( SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( spv_validator_options options, bool val); +// Creates an optimizer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvOptimizerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate(); + +// Destroys the given optimizer options object. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options); + +// Records whether or not the optimizer should run the validator before +// optimizing. If |val| is true, the validator will be run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val); + +// Records the validator options that should be passed to the validator if it is +// run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val); + +// Records the maximum possible value for the id bound. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val); + // Encodes the given SPIR-V assembly text to its binary representation. The // length parameter specifies the number of bytes for text. Encoded binary will // be stored into *binary. Any error will be written into *diagnostic if diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index b6ae38c10..cfe5fc873 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -106,6 +106,36 @@ class ValidatorOptions { spv_validator_options options_; }; +// A C++ wrapper around an optimization options object. +class OptimizerOptions { + public: + OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} + ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_optimizer_options() const { return options_; } + + // Records whether or not the optimizer should run the validator before + // optimizing. If |run| is true, the validator will be run. + void set_run_validator(bool run) { + spvOptimizerOptionsSetRunValidator(options_, run); + } + + // Records the validator options that should be passed to the validator if it + // is run. + void set_validator_options(const ValidatorOptions& val_options) { + spvOptimizerOptionsSetValidatorOptions(options_, val_options); + } + + // Records the maximum possible value for the id bound. + void set_max_id_bound(uint32_t new_bound) { + spvOptimizerOptionsSetMaxIdBound(options_, new_bound); + } + + private: + spv_optimizer_options options_; +}; + // C++ interface for SPIRV-Tools functionalities. It wraps the context // (including target environment and the corresponding SPIR-V grammar) and // provides methods for assembling, disassembling, and validating. @@ -173,7 +203,7 @@ class SpirvTools { bool Validate(const uint32_t* binary, size_t binary_size) const; // Like the previous overload, but takes an options object. bool Validate(const uint32_t* binary, size_t binary_size, - const ValidatorOptions& options) const; + spv_validator_options options) const; private: struct Impl; // Opaque struct for holding the data fields used by this class. diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 4364d9ff5..5a3c52f36 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -160,13 +160,20 @@ class Optimizer { bool Run(const uint32_t* original_binary, size_t original_binary_size, std::vector* optimized_binary) const; - // Same as above, except passes |options| to the validator when trying to - // validate the binary. If |skip_validation| is true, then the caller is - // guaranteeing that |original_binary| is valid, and the validator will not - // be run. + // DEPRECATED: Same as above, except passes |options| to the validator when + // trying to validate the binary. If |skip_validation| is true, then the + // caller is guaranteeing that |original_binary| is valid, and the validator + // will not be run. The |max_id_bound| is the limit on the max id in the + // module. bool Run(const uint32_t* original_binary, const size_t original_binary_size, std::vector* optimized_binary, - const ValidatorOptions& options, bool skip_validation = false) const; + const ValidatorOptions& options, bool skip_validation) const; + + // Same as above, except it takes an options object. See the documentation + // for |OptimizerOptions| to see which options can be set. + bool Run(const uint32_t* original_binary, const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const; // Returns a vector of strings with all the pass names added to this // optimizer's pass manager. These strings are valid until the associated diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4df5de3ad..99bf79e32 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -247,6 +247,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/spirv_constant.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h ${CMAKE_CURRENT_SOURCE_DIR}/table.h @@ -273,6 +274,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp diff --git a/source/libspirv.cpp b/source/libspirv.cpp index cbbc4c908..b5fe89766 100644 --- a/source/libspirv.cpp +++ b/source/libspirv.cpp @@ -115,7 +115,7 @@ bool SpirvTools::Validate(const uint32_t* binary, } bool SpirvTools::Validate(const uint32_t* binary, const size_t binary_size, - const ValidatorOptions& options) const { + spv_validator_options options) const { spv_const_binary_t the_binary{binary, binary_size}; spv_diagnostic diagnostic = nullptr; bool valid = spvValidateWithOptions(impl_->context, options, &the_binary, diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index a9d892fa2..71b6788aa 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -90,7 +90,8 @@ class IRContext { valid_analyses_(kAnalysisNone), constant_mgr_(nullptr), type_mgr_(nullptr), - id_to_name_(nullptr) { + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); } @@ -104,7 +105,8 @@ class IRContext { def_use_mgr_(nullptr), valid_analyses_(kAnalysisNone), type_mgr_(nullptr), - id_to_name_(nullptr) { + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); InitializeCombinators(); @@ -456,6 +458,9 @@ class IRContext { return *inst_folder_; } + uint32_t max_id_bound() const { return max_id_bound_; } + void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; } + private: // Builds the def-use manager from scratch, even if it was already valid. void BuildDefUseManager() { @@ -613,6 +618,9 @@ class IRContext { std::unique_ptr vn_table_; std::unique_ptr inst_folder_; + + // The maximum legal value for the id bound. + uint32_t max_id_bound_; }; inline IRContext::Analysis operator|(IRContext::Analysis lhs, diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 6d024b5bc..04e4e9733 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -19,11 +19,24 @@ #include #include "source/operand.h" +#include "source/opt/ir_context.h" #include "source/opt/reflect.h" namespace spvtools { namespace opt { +uint32_t Module::TakeNextIdBound() { + if (context()) { + if (id_bound() >= context()->max_id_bound()) { + return 0; + } + } else if (id_bound() >= kDefaultMaxIdBound) { + return 0; + } + + return header_.bound++; +} + std::vector Module::GetTypes() { std::vector type_insts; for (auto& inst : types_values_) { diff --git a/source/opt/module.h b/source/opt/module.h index eca8cc779..ede0bbbf3 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -53,14 +53,22 @@ class Module { // Sets the header to the given |header|. void SetHeader(const ModuleHeader& header) { header_ = header; } - // Sets the Id bound. - void SetIdBound(uint32_t bound) { header_.bound = bound; } + // Sets the Id bound. The Id bound cannot be set to 0. + void SetIdBound(uint32_t bound) { + assert(bound != 0); + header_.bound = bound; + } // Returns the Id bound. uint32_t IdBound() { return header_.bound; } // Returns the current Id bound and increases it to the next available value. - uint32_t TakeNextIdBound() { return header_.bound++; } + // If the id bound has already reached its maximum value, then 0 is returned. + // The maximum value for the id bound is obtained from the context. If there + // is none, then the minimum that limit can be according to the spir-v + // specification. + // TODO(1841): Update the uses to check for a 0 return value. + uint32_t TakeNextIdBound(); // Appends a capability instruction to this module. inline void AddCapability(std::unique_ptr c); diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 30654869c..3a8b4d967 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -20,13 +20,13 @@ #include #include +#include #include "source/opt/build_module.h" #include "source/opt/log.h" #include "source/opt/pass_manager.h" #include "source/opt/passes.h" -#include "source/opt/reduce_load_size.h" -#include "source/opt/simplification_pass.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { @@ -223,29 +223,6 @@ bool Optimizer::RegisterPassesFromFlags(const std::vector& flags) { return true; } -namespace { - -// Splits the string |flag|, of the form '--pass_name[=pass_args]' into two -// strings "pass_name" and "pass_args". If |flag| has no arguments, the second -// string will be empty. -std::pair SplitFlagArgs(const std::string& flag) { - if (flag.size() < 2) return make_pair(flag, std::string()); - - // Detect the last dash before the pass name. Since we have to - // handle single dash options (-O and -Os), count up to two dashes. - size_t dash_ix = 0; - if (flag[0] == '-' && flag[1] == '-') - dash_ix = 2; - else if (flag[0] == '-') - dash_ix = 1; - - size_t ix = flag.find('='); - return (ix != std::string::npos) - ? make_pair(flag.substr(dash_ix, ix - 2), flag.substr(ix + 1)) - : make_pair(flag.substr(dash_ix), std::string()); -} -} // namespace - bool Optimizer::FlagHasValidForm(const std::string& flag) const { if (flag == "-O" || flag == "-Os") { return true; @@ -267,7 +244,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } // Split flags of the form --pass_name=pass_args. - auto p = SplitFlagArgs(flag); + auto p = utils::SplitFlagArgs(flag); std::string pass_name = p.first; std::string pass_args = p.second; @@ -458,18 +435,30 @@ bool Optimizer::Run(const uint32_t* original_binary, const size_t original_binary_size, std::vector* optimized_binary) const { return Run(original_binary, original_binary_size, optimized_binary, - ValidatorOptions()); + OptimizerOptions()); } bool Optimizer::Run(const uint32_t* original_binary, const size_t original_binary_size, std::vector* optimized_binary, - const ValidatorOptions& options, + const ValidatorOptions& validator_options, bool skip_validation) const { + OptimizerOptions opt_options; + opt_options.set_run_validator(!skip_validation); + opt_options.set_validator_options(validator_options); + return Run(original_binary, original_binary_size, optimized_binary, + opt_options); +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const { spvtools::SpirvTools tools(impl_->target_env); tools.SetMessageConsumer(impl_->pass_manager.consumer()); - if (!skip_validation && - !tools.Validate(original_binary, original_binary_size, options)) { + if (opt_options->run_validator_ && + !tools.Validate(original_binary, original_binary_size, + &opt_options->val_options_)) { return false; } @@ -477,6 +466,8 @@ bool Optimizer::Run(const uint32_t* original_binary, impl_->target_env, consumer(), original_binary, original_binary_size); if (context == nullptr) return false; + context->set_max_id_bound(opt_options->max_id_bound_); + auto status = impl_->pass_manager.Run(context.get()); if (status == opt::Pass::Status::SuccessWithChange || (status == opt::Pass::Status::SuccessWithoutChange && diff --git a/source/opt/passes.h b/source/opt/passes.h index 42106c8f7..987af1ee5 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -56,6 +56,7 @@ #include "source/opt/replace_invalid_opc.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" +#include "source/opt/simplification_pass.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" diff --git a/source/spirv_optimizer_options.cpp b/source/spirv_optimizer_options.cpp new file mode 100644 index 000000000..1fab8619d --- /dev/null +++ b/source/spirv_optimizer_options.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_optimizer_options.h" + +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate() { + return new spv_optimizer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val) { + options->run_validator_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val) { + options->val_options_ = *val; +} +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val) { + options->max_id_bound_ = val; +} diff --git a/source/spirv_optimizer_options.h b/source/spirv_optimizer_options.h new file mode 100644 index 000000000..1eb4d3f1b --- /dev/null +++ b/source/spirv_optimizer_options.h @@ -0,0 +1,40 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ +#define SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ + +#include "source/spirv_validator_options.h" +#include "spirv-tools/libspirv.h" + +// Manages command line options passed to the SPIR-V Validator. New struct +// members may be added for any new option. +struct spv_optimizer_options_t { + spv_optimizer_options_t() + : run_validator_(true), + val_options_(), + max_id_bound_(kDefaultMaxIdBound) {} + + // When true the validator will be run before optimizations are run. + bool run_validator_; + + // Options to pass to the validator if it is run. + spv_validator_options_t val_options_; + + // The maximum value the id bound for a module can have. The Spir-V spec says + // this value must be at least 0x3FFFFF, but implementations can allow for a + // higher value. + uint32_t max_id_bound_; +}; +#endif // SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ diff --git a/source/util/string_utils.cpp b/source/util/string_utils.cpp index 29ce2aa4a..b56c353af 100644 --- a/source/util/string_utils.cpp +++ b/source/util/string_utils.cpp @@ -37,5 +37,22 @@ std::string CardinalToOrdinal(size_t cardinal) { return ToString(cardinal) + suffix; } +std::pair SplitFlagArgs(const std::string& flag) { + if (flag.size() < 2) return make_pair(flag, std::string()); + + // Detect the last dash before the pass name. Since we have to + // handle single dash options (-O and -Os), count up to two dashes. + size_t dash_ix = 0; + if (flag[0] == '-' && flag[1] == '-') + dash_ix = 2; + else if (flag[0] == '-') + dash_ix = 1; + + size_t ix = flag.find('='); + return (ix != std::string::npos) + ? make_pair(flag.substr(dash_ix, ix - 2), flag.substr(ix + 1)) + : make_pair(flag.substr(dash_ix), std::string()); +} + } // namespace utils } // namespace spvtools diff --git a/source/util/string_utils.h b/source/util/string_utils.h index 322c574fb..f1cd179c9 100644 --- a/source/util/string_utils.h +++ b/source/util/string_utils.h @@ -37,6 +37,11 @@ std::string ToString(T val) { // Converts cardinal number to ordinal number string. std::string CardinalToOrdinal(size_t cardinal); +// Splits the string |flag|, of the form '--pass_name[=pass_args]' into two +// strings "pass_name" and "pass_args". If |flag| has no arguments, the second +// string will be empty. +std::pair SplitFlagArgs(const std::string& flag); + } // namespace utils } // namespace spvtools diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp index c4f450ea9..569cf9bcd 100644 --- a/test/opt/module_test.cpp +++ b/test/opt/module_test.cpp @@ -139,6 +139,95 @@ OpFunctionEnd)"; EXPECT_EQ(text, str.str()); } +TEST(ModuleTest, IdBoundTestAtLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST(ModuleTest, IdBoundTestBelowLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 100); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound + 1); +} + +TEST(ModuleTest, IdBoundTestNearLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 1); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST(ModuleTest, IdBoundTestUIntMax) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4294967294 = OpLabel ; ID is UINT_MAX-1 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + + // Expecting |BuildModule| to preserve the numeric ids. + EXPECT_EQ(current_bound, std::numeric_limits::max()); + + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index fcd260e45..803a00cd9 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -23,9 +23,8 @@ #include #include "source/opt/log.h" -#include "source/opt/loop_peeling.h" -#include "source/opt/set_spec_constant_default_value_pass.h" -#include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" +#include "spirv-tools/libspirv.hpp" #include "spirv-tools/optimizer.hpp" #include "tools/io.h" #include "tools/util/cli_consumer.h" @@ -214,6 +213,10 @@ Options (in lexicographical order): growth threshold. The threshold prevents the loop peeling from happening if the code size increase created by the optimization is above the threshold. + --max-id-bound= + Sets the maximum value for the id bound for the moudle. The + default is the minimum value for this limit, 0x3FFFFF. See + section 2.17 of the Spir-V specification. --merge-blocks Join two blocks into a single block if the second has the first as its only predecessor. Performed only on entry point @@ -406,8 +409,9 @@ bool ReadFlagsFromFile(const char* oconfig_flag, OptStatus ParseFlags(int argc, const char** argv, spvtools::Optimizer* optimizer, const char** in_file, - const char** out_file, spvtools::ValidatorOptions* options, - bool* skip_validator); + const char** out_file, + spvtools::ValidatorOptions* validator_options, + spvtools::OptimizerOptions* optimizer_options); // Parses and handles the -Oconfig flag. |prog_name| contains the name of // the spirv-opt binary (used to build a new argv vector for the recursive @@ -440,9 +444,8 @@ OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag, new_argv[i] = flags[i].c_str(); } - bool skip_validator = false; return ParseFlags(static_cast(flags.size()), new_argv, optimizer, - in_file, out_file, nullptr, &skip_validator); + in_file, out_file, nullptr, nullptr); } // Canonicalize the flag in |argv[argi]| of the form '--pass arg' into @@ -495,8 +498,9 @@ std::string CanonicalizeFlag(const char** argv, int argc, int* argi) { // success. OptStatus ParseFlags(int argc, const char** argv, spvtools::Optimizer* optimizer, const char** in_file, - const char** out_file, spvtools::ValidatorOptions* options, - bool* skip_validator) { + const char** out_file, + spvtools::ValidatorOptions* validator_options, + spvtools::OptimizerOptions* optimizer_options) { std::vector pass_flags; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; @@ -531,13 +535,28 @@ OptStatus ParseFlags(int argc, const char** argv, return status; } } else if (0 == strcmp(cur_arg, "--skip-validation")) { - *skip_validator = true; + optimizer_options->set_run_validator(false); } else if (0 == strcmp(cur_arg, "--print-all")) { optimizer->SetPrintAll(&std::cerr); } else if (0 == strcmp(cur_arg, "--time-report")) { optimizer->SetTimeReport(&std::cerr); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { - options->SetRelaxStructStore(true); + validator_options->SetRelaxStructStore(true); + } else if (0 == strncmp(cur_arg, "--max-id-bound=", + sizeof("--max-id-bound=") - 1)) { + auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + // Will not allow values in the range [2^31,2^32). + uint32_t max_id_bound = + static_cast(atoi(split_flag.second.c_str())); + + // That SPIR-V mandates the minimum value for max id bound but + // implementations may allow higher minimum bounds. + if (max_id_bound < kDefaultMaxIdBound) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "The max id bound must be at least 0x3FFFFF"); + return {OPT_STOP, 1}; + } + optimizer_options->set_max_id_bound(max_id_bound); } else { // Some passes used to accept the form '--pass arg', canonicalize them // to '--pass=arg'. @@ -546,7 +565,7 @@ OptStatus ParseFlags(int argc, const char** argv, // If we were requested to legalize SPIR-V generated from the HLSL // front-end, skip validation. if (0 == strcmp(cur_arg, "--legalize-hlsl")) { - options->SetRelaxLogicalPointer(true); + validator_options->SetRelaxLogicalPointer(true); } } } else { @@ -575,13 +594,14 @@ int main(int argc, const char** argv) { bool skip_validator = false; spv_target_env target_env = kDefaultEnvironment; - spvtools::ValidatorOptions options; + spvtools::ValidatorOptions validator_options; + spvtools::OptimizerOptions optimizer_options; spvtools::Optimizer optimizer(target_env); optimizer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); OptStatus status = ParseFlags(argc, argv, &optimizer, &in_file, &out_file, - &options, &skip_validator); + &validator_options, &optimizer_options); if (status.action == OPT_STOP) { return status.code; @@ -599,8 +619,8 @@ int main(int argc, const char** argv) { // By using the same vector as input and output, we save time in the case // that there was no change. - bool ok = optimizer.Run(binary.data(), binary.size(), &binary, options, - skip_validator); + bool ok = optimizer.Run(binary.data(), binary.size(), &binary, + validator_options, skip_validator); if (!WriteFile(out_file, "wb", binary.data(), binary.size())) { return 1;