Add option for the max id bound. (#1870)

* Create a new entry point for the optimizer

Creates a new struct to hold the options for the optimizer, and creates
an entry point that take the optimizer options as a parameter.

The old entry point that takes validator options are now deprecated.
The validator options will be one of the optimizer options.

Part of the optimizer options will also be the upper bound on the id bound.

* Add a command line option to set the max value for the id bound.  The default is 0x3FFFFF.

* Modify `TakeNextIdBound` to return 0 when the limit is reached.
This commit is contained in:
Steven Perron 2018-09-10 11:49:41 -04:00 committed by GitHub
parent f62d7978fc
commit 75c1bf2843
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 364 additions and 58 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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.

View File

@ -160,13 +160,20 @@ class Optimizer {
bool Run(const uint32_t* original_binary, size_t original_binary_size,
std::vector<uint32_t>* 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<uint32_t>* 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<uint32_t>* 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

View File

@ -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

View File

@ -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,

View File

@ -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<ValueNumberTable> vn_table_;
std::unique_ptr<InstructionFolder> inst_folder_;
// The maximum legal value for the id bound.
uint32_t max_id_bound_;
};
inline IRContext::Analysis operator|(IRContext::Analysis lhs,

View File

@ -19,11 +19,24 @@
#include <ostream>
#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<Instruction*> Module::GetTypes() {
std::vector<Instruction*> type_insts;
for (auto& inst : types_values_) {

View File

@ -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<Instruction> c);

View File

@ -20,13 +20,13 @@
#include <utility>
#include <vector>
#include <source/spirv_optimizer_options.h>
#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<std::string>& 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<std::string, std::string> 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<uint32_t>* 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<uint32_t>* 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<uint32_t>* 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 &&

View File

@ -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"

View File

@ -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 <cassert>
#include <cstring>
#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;
}

View File

@ -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_

View File

@ -37,5 +37,22 @@ std::string CardinalToOrdinal(size_t cardinal) {
return ToString(cardinal) + suffix;
}
std::pair<std::string, std::string> 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

View File

@ -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<std::string, std::string> SplitFlagArgs(const std::string& flag);
} // namespace utils
} // namespace spvtools

View File

@ -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<IRContext> 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<IRContext> 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<IRContext> 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<IRContext> 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<uint32_t>::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

View File

@ -23,9 +23,8 @@
#include <vector>
#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=<n>
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<int>(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<std::string> 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<uint32_t>(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<uint32_t>(out_file, "wb", binary.data(), binary.size())) {
return 1;