diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index e1eeedf5..6e193bdb 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -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& 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 diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 5cddbe4b..f91e80bd 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -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& 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 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 " + ": 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(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(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* optimized_binary) const { - std::unique_ptr context = - BuildModule(impl_->target_env, impl_->pass_manager.consumer(), - original_binary, original_binary_size); + std::unique_ptr context = BuildModule( + impl_->target_env, consumer(), original_binary, original_binary_size); if (context == nullptr) return false; auto status = impl_->pass_manager.Run(context.get()); diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index 59435a38..0c2a3741 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp @@ -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 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 diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index ca857c8b..4341c04e 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -22,6 +22,7 @@ #include #include +#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* 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 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(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(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 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 : 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; }