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:
Родитель
a114e1f30c
Коммит
99fe61e724
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче