Add API to create passes out of a list of command-line flags.

This re-implements the -Oconfig=<file> flag to use a new API that takes
a list of command-line flags representing optimization passes.

This moves the processing of flags that create new optimization passes
out of spirv-opt and into the library API.  Useful for other tools that
want to incorporate a facility similar to -Oconfig.

The main changes are:

1- Add a new public function Optimizer::RegisterPassesFromFlags. This
   takes a vector of strings.  Each string is assumed to have the form
   '--pass_name[=pass_args]'.  It creates and registers into the pass
   manager all the passes specified in the vector.  Each pass is
   validated internally.  Failure to create a pass instance causes the
   function to return false and a diagnostic is emitted to the
   registered message consumer.

2- Re-implements -Oconfig in spirv-opt to use the new API.
This commit is contained in:
Diego Novillo 2018-07-25 15:21:44 -04:00
Родитель a114e1f30c
Коммит 99fe61e724
4 изменённых файлов: 468 добавлений и 224 удалений

Просмотреть файл

@ -83,6 +83,9 @@ class Optimizer {
// invoked once for each message communicated from the library.
void SetMessageConsumer(MessageConsumer consumer);
// Returns a reference to the registered message consumer.
const MessageConsumer& consumer() const;
// Registers the given |pass| to this optimizer. Passes will be run in the
// exact order of registration. The token passed in will be consumed by this
// method.
@ -100,14 +103,51 @@ class Optimizer {
// Registers passes that attempt to legalize the generated code.
//
// Note: this recipe is specially for legalizing SPIR-V. It should be used
// by compilers after translating HLSL source code literally. It should
// Note: this recipe is specially designed for legalizing SPIR-V. It should be
// used by compilers after translating HLSL source code literally. It should
// *not* be used by general workloads for performance or size improvement.
//
// This sequence of passes is subject to constant review and will change
// from time to time.
Optimizer& RegisterLegalizationPasses();
// Register passes specified in the list of |flags|. Each flag must be a
// string of a form accepted by Optimizer::FlagHasValidForm().
//
// If the list of flags contains an invalid entry, it returns false and an
// error message is emitted to the MessageConsumer object (use
// Optimizer::SetMessageConsumer to define a message consumer, if needed).
//
// If all the passes are registered successfully, it returns true.
bool RegisterPassesFromFlags(const std::vector<std::string>& flags);
// Registers the optimization pass associated with |flag|. This only accepts
// |flag| values of the form "--pass_name[=pass_args]". If no such pass
// exists, it returns false. Otherwise, the pass is registered and it returns
// true.
//
// The following flags have special meaning:
//
// -O: Registers all performance optimization passes
// (Optimizer::RegisterPerformancePasses)
//
// -Os: Registers all size optimization passes
// (Optimizer::RegisterSizePasses).
//
// --legalize-hlsl: Registers all passes that legalize SPIR-V generated by an
// HLSL front-end.
bool RegisterPassFromFlag(const std::string& flag);
// Validates that |flag| has a valid format. Strings accepted:
//
// --pass_name[=pass_args]
// -O
// -Os
//
// If |flag| takes one of the forms above, it returns true. Otherwise, it
// returns false.
bool FlagHasValidForm(const std::string& flag) const;
// Optimizes the given SPIR-V module |original_binary| and writes the
// optimized binary into |optimized_binary|.
// Returns true on successful optimization, whether or not the module is

Просмотреть файл

@ -15,6 +15,7 @@
#include "spirv-tools/optimizer.hpp"
#include "build_module.h"
#include "log.h"
#include "make_unique.h"
#include "pass_manager.h"
#include "passes.h"
@ -65,9 +66,13 @@ void Optimizer::SetMessageConsumer(MessageConsumer c) {
impl_->pass_manager.SetMessageConsumer(std::move(c));
}
const MessageConsumer& Optimizer::consumer() const {
return impl_->pass_manager.consumer();
}
Optimizer& Optimizer::RegisterPass(PassToken&& p) {
// Change to use the pass manager's consumer.
p.impl_->pass->SetMessageConsumer(impl_->pass_manager.consumer());
p.impl_->pass->SetMessageConsumer(consumer());
impl_->pass_manager.AddPass(std::move(p.impl_->pass));
return *this;
}
@ -201,12 +206,241 @@ Optimizer& Optimizer::RegisterSizePasses() {
.RegisterPass(CreateAggressiveDCEPass());
}
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
for (const auto& flag : flags) {
if (!RegisterPassFromFlag(flag)) {
return false;
}
}
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;
} else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
return true;
}
Errorf(consumer(), nullptr, {},
"%s is not a valid flag. Flag passes should have the form "
"'--pass_name[=pass_args]'. Special flag names also accepted: -O "
"and -Os.",
flag.c_str());
return false;
}
bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
if (!FlagHasValidForm(flag)) {
return false;
}
// Split flags of the form --pass_name=pass_args.
auto p = SplitFlagArgs(flag);
std::string pass_name = p.first;
std::string pass_args = p.second;
// FIXME(dnovillo): This should be re-factored so that pass names can be
// automatically checked against Pass::name() and PassToken instances created
// via a template function. Additionally, class Pass should have a desc()
// method that describes the pass (so it can be used in --help).
//
// Both Pass::name() and Pass::desc() should be static class members so they
// can be invoked without creating a pass instance.
if (pass_name == "strip-debug") {
RegisterPass(CreateStripDebugInfoPass());
} else if (pass_name == "strip-reflect") {
RegisterPass(CreateStripReflectInfoPass());
} else if (pass_name == "set-spec-const-default-value") {
if (pass_args.size() > 0) {
auto spec_ids_vals =
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
pass_args.c_str());
if (!spec_ids_vals) {
Errorf(consumer(), nullptr, {},
"Invalid argument for --set-spec-const-default-value: %s",
pass_args.c_str());
return false;
}
RegisterPass(
CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
} else {
Errorf(consumer(), nullptr, {},
"Invalid spec constant value string '%s'. Expected a string of "
"<spec id>:<default value> pairs.",
pass_args.c_str());
return false;
}
} else if (pass_name == "if-conversion") {
RegisterPass(CreateIfConversionPass());
} else if (pass_name == "freeze-spec-const") {
RegisterPass(CreateFreezeSpecConstantValuePass());
} else if (pass_name == "inline-entry-points-exhaustive") {
RegisterPass(CreateInlineExhaustivePass());
} else if (pass_name == "inline-entry-points-opaque") {
RegisterPass(CreateInlineOpaquePass());
} else if (pass_name == "convert-local-access-chains") {
RegisterPass(CreateLocalAccessChainConvertPass());
} else if (pass_name == "eliminate-dead-code-aggressive") {
RegisterPass(CreateAggressiveDCEPass());
} else if (pass_name == "eliminate-insert-extract") {
RegisterPass(CreateInsertExtractElimPass());
} else if (pass_name == "eliminate-local-single-block") {
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
} else if (pass_name == "eliminate-local-single-store") {
RegisterPass(CreateLocalSingleStoreElimPass());
} else if (pass_name == "merge-blocks") {
RegisterPass(CreateBlockMergePass());
} else if (pass_name == "merge-return") {
RegisterPass(CreateMergeReturnPass());
} else if (pass_name == "eliminate-dead-branches") {
RegisterPass(CreateDeadBranchElimPass());
} else if (pass_name == "eliminate-dead-functions") {
RegisterPass(CreateEliminateDeadFunctionsPass());
} else if (pass_name == "eliminate-local-multi-store") {
RegisterPass(CreateLocalMultiStoreElimPass());
} else if (pass_name == "eliminate-common-uniform") {
RegisterPass(CreateCommonUniformElimPass());
} else if (pass_name == "eliminate-dead-const") {
RegisterPass(CreateEliminateDeadConstantPass());
} else if (pass_name == "eliminate-dead-inserts") {
RegisterPass(CreateDeadInsertElimPass());
} else if (pass_name == "eliminate-dead-variables") {
RegisterPass(CreateDeadVariableEliminationPass());
} else if (pass_name == "fold-spec-const-op-composite") {
RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
} else if (pass_name == "loop-unswitch") {
RegisterPass(CreateLoopUnswitchPass());
} else if (pass_name == "scalar-replacement") {
if (pass_args.size() == 0) {
RegisterPass(CreateScalarReplacementPass());
} else {
uint32_t limit = atoi(pass_args.c_str());
if (limit > 0) {
RegisterPass(CreateScalarReplacementPass(limit));
} else {
Error(consumer(), nullptr, {},
"--scalar-replacement must have no arguments or a positive "
"integer argument");
return false;
}
}
} else if (pass_name == "strength-reduction") {
RegisterPass(CreateStrengthReductionPass());
} else if (pass_name == "unify-const") {
RegisterPass(CreateUnifyConstantPass());
} else if (pass_name == "flatten-decorations") {
RegisterPass(CreateFlattenDecorationPass());
} else if (pass_name == "compact-ids") {
RegisterPass(CreateCompactIdsPass());
} else if (pass_name == "cfg-cleanup") {
RegisterPass(CreateCFGCleanupPass());
} else if (pass_name == "local-redundancy-elimination") {
RegisterPass(CreateLocalRedundancyEliminationPass());
} else if (pass_name == "loop-invariant-code-motion") {
RegisterPass(CreateLoopInvariantCodeMotionPass());
} else if (pass_name == "reduce-load-size") {
RegisterPass(CreateReduceLoadSizePass());
} else if (pass_name == "redundancy-elimination") {
RegisterPass(CreateRedundancyEliminationPass());
} else if (pass_name == "private-to-local") {
RegisterPass(CreatePrivateToLocalPass());
} else if (pass_name == "remove-duplicates") {
RegisterPass(CreateRemoveDuplicatesPass());
} else if (pass_name == "workaround-1209") {
RegisterPass(CreateWorkaround1209Pass());
} else if (pass_name == "replace-invalid-opcode") {
RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (pass_name == "simplify-instructions") {
RegisterPass(CreateSimplificationPass());
} else if (pass_name == "ssa-rewrite") {
RegisterPass(CreateSSARewritePass());
} else if (pass_name == "copy-propagate-arrays") {
RegisterPass(CreateCopyPropagateArraysPass());
} else if (pass_name == "loop-fission") {
int register_threshold_to_split =
(pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
if (register_threshold_to_split > 0) {
RegisterPass(CreateLoopFissionPass(
static_cast<size_t>(register_threshold_to_split)));
} else {
Error(consumer(), nullptr, {},
"--loop-fission must have a positive integer argument");
return false;
}
} else if (pass_name == "loop-fusion") {
int max_registers_per_loop =
(pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
if (max_registers_per_loop > 0) {
RegisterPass(
CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
} else {
Error(consumer(), nullptr, {},
"--loop-fusion must be have a positive integer argument");
return false;
}
} else if (pass_name == "loop-unroll") {
RegisterPass(CreateLoopUnrollPass(true));
} else if (pass_name == "vector-dce") {
RegisterPass(CreateVectorDCEPass());
} else if (pass_name == "loop-unroll-partial") {
int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
if (factor != 0) {
RegisterPass(CreateLoopUnrollPass(false, factor));
} else {
Error(consumer(), nullptr, {},
"--loop-unroll-partial must have a non-0 integer argument");
return false;
}
} else if (pass_name == "loop-peeling") {
RegisterPass(CreateLoopPeelingPass());
} else if (pass_name == "ccp") {
RegisterPass(CreateCCPPass());
} else if (pass_name == "O") {
RegisterPerformancePasses();
} else if (pass_name == "Os") {
RegisterSizePasses();
} else if (pass_name == "legalize-hlsl") {
RegisterLegalizationPasses();
} else {
Errorf(consumer(), nullptr, {},
"Unknown flag '--%s'. Use --help for a list of valid flags",
pass_name.c_str());
return false;
}
return true;
}
bool Optimizer::Run(const uint32_t* original_binary,
const size_t original_binary_size,
std::vector<uint32_t>* optimized_binary) const {
std::unique_ptr<opt::IRContext> context =
BuildModule(impl_->target_env, impl_->pass_manager.consumer(),
original_binary, original_binary_size);
std::unique_ptr<opt::IRContext> context = BuildModule(
impl_->target_env, consumer(), original_binary, original_binary_size);
if (context == nullptr) return false;
auto status = impl_->pass_manager.Run(context.get());

Просмотреть файл

@ -104,6 +104,110 @@ TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) {
EXPECT_THAT(disassembly, Eq("%void = OpTypeVoid\n"));
}
TEST(Optimizer, CanValidateFlags) {
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
EXPECT_FALSE(opt.FlagHasValidForm("bad-flag"));
EXPECT_TRUE(opt.FlagHasValidForm("-O"));
EXPECT_TRUE(opt.FlagHasValidForm("-Os"));
EXPECT_FALSE(opt.FlagHasValidForm("-O2"));
EXPECT_TRUE(opt.FlagHasValidForm("--this_flag"));
}
TEST(Optimizer, CanRegisterPassesFromFlags) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
spv_message_level_t msg_level;
const char* msg_fname;
spv_position_t msg_position;
const char* msg;
auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg](
spv_message_level_t ml, const char* f,
const spv_position_t& p, const char* m) {
msg_level = ml;
msg_fname = f;
msg_position = p;
msg = m;
};
opt.SetMessageConsumer(examine_message);
std::vector<std::string> pass_flags = {
"--strip-debug",
"--strip-reflect",
"--set-spec-const-default-value=23:42 21:12",
"--if-conversion",
"--freeze-spec-const",
"--inline-entry-points-exhaustive",
"--inline-entry-points-opaque",
"--convert-local-access-chains",
"--eliminate-dead-code-aggressive",
"--eliminate-insert-extract",
"--eliminate-local-single-block",
"--eliminate-local-single-store",
"--merge-blocks",
"--merge-return",
"--eliminate-dead-branches",
"--eliminate-dead-functions",
"--eliminate-local-multi-store",
"--eliminate-common-uniform",
"--eliminate-dead-const",
"--eliminate-dead-inserts",
"--eliminate-dead-variables",
"--fold-spec-const-op-composite",
"--loop-unswitch",
"--scalar-replacement=300",
"--scalar-replacement",
"--strength-reduction",
"--unify-const",
"--flatten-decorations",
"--compact-ids",
"--cfg-cleanup",
"--local-redundancy-elimination",
"--loop-invariant-code-motion",
"--reduce-load-size",
"--redundancy-elimination",
"--private-to-local",
"--remove-duplicates",
"--workaround-1209",
"--replace-invalid-opcode",
"--simplify-instructions",
"--ssa-rewrite",
"--copy-propagate-arrays",
"--loop-fission=20",
"--loop-fusion=2",
"--loop-unroll",
"--vector-dce",
"--loop-unroll-partial=3",
"--loop-peeling",
"--ccp",
"-O",
"-Os",
"--legalize-hlsl"};
EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags));
// Test some invalid flags.
EXPECT_FALSE(opt.RegisterPassFromFlag("-O2"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
}
} // namespace
} // namespace opt
} // namespace spvtools

Просмотреть файл

@ -22,6 +22,7 @@
#include <sstream>
#include <vector>
#include "opt/log.h"
#include "opt/loop_peeling.h"
#include "opt/set_spec_constant_default_value_pass.h"
#include "spirv-tools/optimizer.hpp"
@ -41,6 +42,17 @@ struct OptStatus {
int code;
};
// Message consumer for this tool. Used to emit diagnostics during
// initialization and setup. Note that |source| and |position| are irrelevant
// here because we are still not processing a SPIR-V input file.
void opt_diagnostic(spv_message_level_t level, const char* /*source*/,
const spv_position_t& /*positon*/, const char* message) {
if (level == SPV_MSG_ERROR) {
fprintf(stderr, "error: ");
}
fprintf(stderr, "%s\n", message);
}
std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) {
std::stringstream ss;
for (const auto& name : optimizer.GetPassNames()) {
@ -356,7 +368,8 @@ bool ReadFlagsFromFile(const char* oconfig_flag,
std::vector<std::string>* file_flags) {
const char* fname = strchr(oconfig_flag, '=');
if (fname == nullptr || fname[0] != '=') {
fprintf(stderr, "error: Invalid -Oconfig flag %s\n", oconfig_flag);
Errorf(opt_diagnostic, nullptr, {}, "Invalid -Oconfig flag %s",
oconfig_flag);
return false;
}
fname++;
@ -364,7 +377,7 @@ bool ReadFlagsFromFile(const char* oconfig_flag,
std::ifstream input_file;
input_file.open(fname);
if (input_file.fail()) {
fprintf(stderr, "error: Could not open file '%s'\n", fname);
Errorf(opt_diagnostic, nullptr, {}, "Could not open file '%s'", fname);
return false;
}
@ -397,8 +410,8 @@ OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag,
std::vector<std::string> file_flags;
if (!ReadFlagsFromFile(opt_flag, &file_flags)) {
fprintf(stderr,
"error: Could not read optimizer flags from configuration file\n");
Error(opt_diagnostic, nullptr, {},
"Could not read optimizer flags from configuration file");
return {OPT_STOP, 1};
}
flags.insert(flags.end(), file_flags.begin(), file_flags.end());
@ -406,9 +419,8 @@ OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag,
const char** new_argv = new const char*[flags.size()];
for (size_t i = 0; i < flags.size(); i++) {
if (flags[i].find("-Oconfig=") != std::string::npos) {
fprintf(stderr,
"error: Flag -Oconfig= may not be used inside the configuration "
"file\n");
Error(opt_diagnostic, nullptr, {},
"Flag -Oconfig= may not be used inside the configuration file");
return {OPT_STOP, 1};
}
new_argv[i] = flags[i].c_str();
@ -419,68 +431,49 @@ OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag,
in_file, out_file, nullptr, &skip_validator);
}
OptStatus ParseLoopFissionArg(int argc, const char** argv, int argi,
Optimizer* optimizer) {
if (argi < argc) {
int register_threshold_to_split = atoi(argv[argi]);
optimizer->RegisterPass(CreateLoopFissionPass(
static_cast<size_t>(register_threshold_to_split)));
return {OPT_CONTINUE, 0};
}
fprintf(
stderr,
"error: --loop-fission must be followed by a positive integer value\n");
return {OPT_STOP, 1};
}
// Canonicalize the flag in |argv[argi]| of the form '--pass arg' into
// '--pass=arg'. The optimizer only accepts arguments to pass names that use the
// form '--pass_name=arg'. Since spirv-opt also accepts the other form, this
// function makes the necessary conversion.
//
// Pass flags that require additional arguments should be handled here. Note
// that additional arguments should be given as a single string. If the flag
// requires more than one argument, the pass creator in
// Optimizer::GetPassFromFlag() should parse it accordingly (e.g., see the
// handler for --set-spec-const-default-value).
//
// If the argument requests one of the passes that need an additional argument,
// |argi| is modified to point past the current argument, and the string
// "argv[argi]=argv[argi + 1]" is returned. Otherwise, |argi| is unmodified and
// the string "|argv[argi]|" is returned.
std::string CanonicalizeFlag(const char** argv, int argc, int* argi) {
const char* cur_arg = argv[*argi];
const char* next_arg = (*(argi + 1) < argc) ? argv[*argi + 1] : nullptr;
std::ostringstream canonical_arg;
canonical_arg << cur_arg;
OptStatus ParseLoopFusionArg(int argc, const char** argv, int argi,
Optimizer* optimizer) {
if (argi < argc) {
int max_registers_per_loop = atoi(argv[argi]);
if (max_registers_per_loop > 0) {
optimizer->RegisterPass(
CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
return {OPT_CONTINUE, 0};
// NOTE: DO NOT ADD NEW FLAGS HERE.
//
// These flags are supported for backwards compatibility. When adding new
// passes that need extra arguments in its command-line flag, please make them
// use the syntax "--pass_name[=pass_arg].
if (0 == strcmp(cur_arg, "--set-spec-const-default-value") ||
0 == strcmp(cur_arg, "--loop-fission") ||
0 == strcmp(cur_arg, "--loop-fusion") ||
0 == strcmp(cur_arg, "--loop-unroll-partial") ||
0 == strcmp(cur_arg, "--loop-peeling-threshold")) {
if (next_arg) {
canonical_arg << "=" << next_arg;
++(*argi);
}
}
fprintf(stderr,
"error: --loop-loop-fusion must be followed by a positive "
"integer\n");
return {OPT_STOP, 1};
return canonical_arg.str();
}
OptStatus ParseLoopUnrollPartialArg(int argc, const char** argv, int argi,
Optimizer* optimizer) {
if (argi < argc) {
int factor = atoi(argv[argi]);
if (factor != 0) {
optimizer->RegisterPass(CreateLoopUnrollPass(false, factor));
return {OPT_CONTINUE, 0};
}
}
fprintf(stderr,
"error: --loop-unroll-partial must be followed by a non-0 "
"integer\n");
return {OPT_STOP, 1};
}
OptStatus ParseLoopPeelingThresholdArg(int argc, const char** argv, int argi) {
if (argi < argc) {
int factor = atoi(argv[argi]);
if (factor > 0) {
opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
return {OPT_CONTINUE, 0};
}
}
fprintf(
stderr,
"error: --loop-peeling-threshold must be followed by a non-0 integer\n");
return {OPT_STOP, 1};
}
// Parses command-line flags. |argc| contains the number of command-line flags.
// |argv| points to an array of strings holding the flags. |optimizer| is the
// Optimizer instance used to optimize the program.
// the number of command-line flags. |argv| points to an array of strings
// holding the flags. |optimizer| is the Optimizer instance used to optimize the
// program.
//
// On return, this function stores the name of the input program in |in_file|.
// The name of the output file in |out_file|. The return value indicates whether
@ -489,11 +482,13 @@ OptStatus ParseLoopPeelingThresholdArg(int argc, const char** argv, int argi) {
OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
const char** in_file, const char** out_file,
spv_validator_options options, bool* skip_validator) {
std::vector<std::string> pass_flags;
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());
Logf(opt_diagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
spvSoftwareVersionDetailsString());
return {OPT_STOP, 0};
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
PrintUsage(argv[0]);
@ -505,182 +500,53 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
PrintUsage(argv[0]);
return {OPT_STOP, 1};
}
} else if (0 == strcmp(cur_arg, "--strip-debug")) {
optimizer->RegisterPass(CreateStripDebugInfoPass());
} else if (0 == strcmp(cur_arg, "--strip-reflect")) {
optimizer->RegisterPass(CreateStripReflectInfoPass());
} else if (0 == strcmp(cur_arg, "--set-spec-const-default-value")) {
if (++argi < argc) {
auto spec_ids_vals =
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
argv[argi]);
if (!spec_ids_vals) {
fprintf(stderr,
"error: Invalid argument for "
"--set-spec-const-default-value: %s\n",
argv[argi]);
return {OPT_STOP, 1};
}
optimizer->RegisterPass(
CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
} else if ('\0' == cur_arg[1]) {
// Setting a filename of "-" to indicate stdin.
if (!*in_file) {
*in_file = cur_arg;
} else {
fprintf(
stderr,
"error: Expected a string of <spec id>:<default value> pairs.");
Error(opt_diagnostic, nullptr, {},
"More than one input file specified");
return {OPT_STOP, 1};
}
} else if (0 == strcmp(cur_arg, "--if-conversion")) {
optimizer->RegisterPass(CreateIfConversionPass());
} else if (0 == strcmp(cur_arg, "--freeze-spec-const")) {
optimizer->RegisterPass(CreateFreezeSpecConstantValuePass());
} else if (0 == strcmp(cur_arg, "--inline-entry-points-exhaustive")) {
optimizer->RegisterPass(CreateInlineExhaustivePass());
} else if (0 == strcmp(cur_arg, "--inline-entry-points-opaque")) {
optimizer->RegisterPass(CreateInlineOpaquePass());
} else if (0 == strcmp(cur_arg, "--convert-local-access-chains")) {
optimizer->RegisterPass(CreateLocalAccessChainConvertPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-code-aggressive")) {
optimizer->RegisterPass(CreateAggressiveDCEPass());
} else if (0 == strcmp(cur_arg, "--eliminate-insert-extract")) {
optimizer->RegisterPass(CreateInsertExtractElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-block")) {
optimizer->RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-local-single-store")) {
optimizer->RegisterPass(CreateLocalSingleStoreElimPass());
} else if (0 == strcmp(cur_arg, "--merge-blocks")) {
optimizer->RegisterPass(CreateBlockMergePass());
} else if (0 == strcmp(cur_arg, "--merge-return")) {
optimizer->RegisterPass(CreateMergeReturnPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-branches")) {
optimizer->RegisterPass(CreateDeadBranchElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-functions")) {
optimizer->RegisterPass(CreateEliminateDeadFunctionsPass());
} else if (0 == strcmp(cur_arg, "--eliminate-local-multi-store")) {
optimizer->RegisterPass(CreateLocalMultiStoreElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-common-uniform")) {
optimizer->RegisterPass(CreateCommonUniformElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-const")) {
optimizer->RegisterPass(CreateEliminateDeadConstantPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-inserts")) {
optimizer->RegisterPass(CreateDeadInsertElimPass());
} else if (0 == strcmp(cur_arg, "--eliminate-dead-variables")) {
optimizer->RegisterPass(CreateDeadVariableEliminationPass());
} else if (0 == strcmp(cur_arg, "--fold-spec-const-op-composite")) {
optimizer->RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
} else if (0 == strcmp(cur_arg, "--loop-unswitch")) {
optimizer->RegisterPass(CreateLoopUnswitchPass());
} else if (0 == strcmp(cur_arg, "--scalar-replacement")) {
optimizer->RegisterPass(CreateScalarReplacementPass());
} else if (0 == strncmp(cur_arg, "--scalar-replacement=", 21)) {
uint32_t limit = atoi(cur_arg + 21);
optimizer->RegisterPass(CreateScalarReplacementPass(limit));
} else if (0 == strcmp(cur_arg, "--strength-reduction")) {
optimizer->RegisterPass(CreateStrengthReductionPass());
} else if (0 == strcmp(cur_arg, "--unify-const")) {
optimizer->RegisterPass(CreateUnifyConstantPass());
} else if (0 == strcmp(cur_arg, "--flatten-decorations")) {
optimizer->RegisterPass(CreateFlattenDecorationPass());
} else if (0 == strcmp(cur_arg, "--compact-ids")) {
optimizer->RegisterPass(CreateCompactIdsPass());
} else if (0 == strcmp(cur_arg, "--cfg-cleanup")) {
optimizer->RegisterPass(CreateCFGCleanupPass());
} else if (0 == strcmp(cur_arg, "--local-redundancy-elimination")) {
optimizer->RegisterPass(CreateLocalRedundancyEliminationPass());
} else if (0 == strcmp(cur_arg, "--loop-invariant-code-motion")) {
optimizer->RegisterPass(CreateLoopInvariantCodeMotionPass());
} else if (0 == strcmp(cur_arg, "--reduce-load-size")) {
optimizer->RegisterPass(CreateReduceLoadSizePass());
} else if (0 == strcmp(cur_arg, "--redundancy-elimination")) {
optimizer->RegisterPass(CreateRedundancyEliminationPass());
} else if (0 == strcmp(cur_arg, "--private-to-local")) {
optimizer->RegisterPass(CreatePrivateToLocalPass());
} else if (0 == strcmp(cur_arg, "--remove-duplicates")) {
optimizer->RegisterPass(CreateRemoveDuplicatesPass());
} else if (0 == strcmp(cur_arg, "--workaround-1209")) {
optimizer->RegisterPass(CreateWorkaround1209Pass());
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options->relax_struct_store = true;
} else if (0 == strcmp(cur_arg, "--replace-invalid-opcode")) {
optimizer->RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (0 == strcmp(cur_arg, "--simplify-instructions")) {
optimizer->RegisterPass(CreateSimplificationPass());
} else if (0 == strcmp(cur_arg, "--ssa-rewrite")) {
optimizer->RegisterPass(CreateSSARewritePass());
} else if (0 == strcmp(cur_arg, "--copy-propagate-arrays")) {
optimizer->RegisterPass(CreateCopyPropagateArraysPass());
} else if (0 == strcmp(cur_arg, "--loop-fission")) {
OptStatus status = ParseLoopFissionArg(argc, argv, ++argi, optimizer);
if (status.action != OPT_CONTINUE) {
return status;
}
} else if (0 == strcmp(cur_arg, "--loop-fusion")) {
OptStatus status = ParseLoopFusionArg(argc, argv, ++argi, optimizer);
if (status.action != OPT_CONTINUE) {
return status;
}
} else if (0 == strcmp(cur_arg, "--loop-unroll")) {
optimizer->RegisterPass(CreateLoopUnrollPass(true));
} else if (0 == strcmp(cur_arg, "--vector-dce")) {
optimizer->RegisterPass(CreateVectorDCEPass());
} else if (0 == strcmp(cur_arg, "--loop-unroll-partial")) {
OptStatus status =
ParseLoopUnrollPartialArg(argc, argv, ++argi, optimizer);
if (status.action != OPT_CONTINUE) {
return status;
}
} else if (0 == strcmp(cur_arg, "--loop-peeling")) {
optimizer->RegisterPass(CreateLoopPeelingPass());
} else if (0 == strcmp(cur_arg, "--loop-peeling-threshold")) {
OptStatus status = ParseLoopPeelingThresholdArg(argc, argv, ++argi);
if (status.action != OPT_CONTINUE) {
return status;
}
} else if (0 == strcmp(cur_arg, "--skip-validation")) {
*skip_validator = true;
} else if (0 == strcmp(cur_arg, "-O")) {
optimizer->RegisterPerformancePasses();
} else if (0 == strcmp(cur_arg, "-Os")) {
optimizer->RegisterSizePasses();
} else if (0 == strcmp(cur_arg, "--legalize-hlsl")) {
*skip_validator = true;
optimizer->RegisterLegalizationPasses();
} else if (0 == strncmp(cur_arg, "-Oconfig=", sizeof("-Oconfig=") - 1)) {
OptStatus status =
ParseOconfigFlag(argv[0], cur_arg, optimizer, in_file, out_file);
if (status.action != OPT_CONTINUE) {
return status;
}
} else if (0 == strcmp(cur_arg, "--ccp")) {
optimizer->RegisterPass(CreateCCPPass());
} else if (0 == strcmp(cur_arg, "--skip-validation")) {
*skip_validator = true;
} 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' == 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 {OPT_STOP, 1};
}
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options->relax_struct_store = true;
} else {
fprintf(
stderr,
"error: Unknown flag '%s'. Use --help for a list of valid flags\n",
cur_arg);
return {OPT_STOP, 1};
// Some passes used to accept the form '--pass arg', canonicalize them
// to '--pass=arg'.
pass_flags.push_back(CanonicalizeFlag(argv, argc, &argi));
// If we were requested to legalize SPIR-V generated from the HLSL
// front-end, skip validation.
if (0 == strcmp(cur_arg, "--legalize-hlsl")) *skip_validator = true;
}
} else {
if (!*in_file) {
*in_file = cur_arg;
} else {
fprintf(stderr, "error: More than one input file specified\n");
Error(opt_diagnostic, nullptr, {},
"More than one input file specified");
return {OPT_STOP, 1};
}
}
}
if (!optimizer->RegisterPassesFromFlags(pass_flags)) {
return {OPT_STOP, 1};
}
return {OPT_CONTINUE, 0};
}
@ -710,7 +576,7 @@ int main(int argc, const char** argv) {
}
if (out_file == nullptr) {
fprintf(stderr, "error: -o required\n");
Error(opt_diagnostic, nullptr, {}, "-o required");
return 1;
}