diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index e513120f9..a7611d503 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -77,17 +77,32 @@ class Optimizer { // method. Optimizer& RegisterPass(PassToken&& pass); + // Registers passes that attempt to improve performance of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterPerformancePasses(); + + // Registers passes that attempt to improve the size of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterSizePasses(); + // 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 // modified. Returns false if errors occur when processing |original_binary| // using any of the registered passes. In that case, no further passes are - // excuted and the contents in |optimized_binary| may be invalid. + // executed and the contents in |optimized_binary| may be invalid. // // It's allowed to alias |original_binary| to the start of |optimized_binary|. bool Run(const uint32_t* original_binary, size_t original_binary_size, std::vector* optimized_binary) const; + // Returns a vector of strings with all the pass names added to this + // optimizer's pass manager. These strings are valid until the associated + // pass manager is destroyed. + std::vector GetPassNames() const; + private: struct Impl; // Opaque struct for holding internal data. std::unique_ptr impl_; // Unique pointer to internal data. diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index 94eb049e3..651d2fc0a 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -42,7 +42,7 @@ class AggressiveDCEPass : public MemPass { std::function*(const ir::BasicBlock*)>; AggressiveDCEPass(); - const char* name() const override { return "aggressive-dce"; } + const char* name() const override { return "eliminate-dead-code-aggressive"; } Status Process(ir::Module*) override; private: diff --git a/source/opt/block_merge_pass.h b/source/opt/block_merge_pass.h index ee68617f5..25cfa5927 100644 --- a/source/opt/block_merge_pass.h +++ b/source/opt/block_merge_pass.h @@ -36,7 +36,7 @@ namespace opt { class BlockMergePass : public Pass { public: BlockMergePass(); - const char* name() const override { return "sroa"; } + const char* name() const override { return "merge-blocks"; } Status Process(ir::Module*) override; private: diff --git a/source/opt/common_uniform_elim_pass.h b/source/opt/common_uniform_elim_pass.h index 1e332eb0f..217763ddd 100644 --- a/source/opt/common_uniform_elim_pass.h +++ b/source/opt/common_uniform_elim_pass.h @@ -41,7 +41,7 @@ class CommonUniformElimPass : public Pass { std::function*(const ir::BasicBlock*)>; CommonUniformElimPass(); - const char* name() const override { return "common-uniform-elim"; } + const char* name() const override { return "eliminate-common-uniform"; } Status Process(ir::Module*) override; private: diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h index 01e84b237..3a18ddcaf 100644 --- a/source/opt/dead_branch_elim_pass.h +++ b/source/opt/dead_branch_elim_pass.h @@ -43,7 +43,7 @@ class DeadBranchElimPass : public MemPass { std::function*(const ir::BasicBlock*)>; DeadBranchElimPass(); - const char* name() const override { return "dead-branch-elim"; } + const char* name() const override { return "eliminate-dead-branches"; } Status Process(ir::Module*) override; private: diff --git a/source/opt/inline_exhaustive_pass.h b/source/opt/inline_exhaustive_pass.h index 71e090299..e4773d81c 100644 --- a/source/opt/inline_exhaustive_pass.h +++ b/source/opt/inline_exhaustive_pass.h @@ -37,7 +37,7 @@ class InlineExhaustivePass : public InlinePass { InlineExhaustivePass(); Status Process(ir::Module*) override; - const char* name() const override { return "inline-exhaustive"; } + const char* name() const override { return "inline-entry-points-exhaustive"; } private: // Exhaustively inline all function calls in func as well as in diff --git a/source/opt/inline_opaque_pass.h b/source/opt/inline_opaque_pass.h index d17420906..e166617aa 100644 --- a/source/opt/inline_opaque_pass.h +++ b/source/opt/inline_opaque_pass.h @@ -37,7 +37,7 @@ class InlineOpaquePass : public InlinePass { InlineOpaquePass(); Status Process(ir::Module*) override; - const char* name() const override { return "inline-opaque"; } + const char* name() const override { return "inline-entry-points-opaque"; } private: // Return true if |typeId| is or contains opaque type diff --git a/source/opt/insert_extract_elim.h b/source/opt/insert_extract_elim.h index cf3be2361..4f7509cbf 100644 --- a/source/opt/insert_extract_elim.h +++ b/source/opt/insert_extract_elim.h @@ -36,7 +36,7 @@ namespace opt { class InsertExtractElimPass : public Pass { public: InsertExtractElimPass(); - const char* name() const override { return "insert_extract_elim"; } + const char* name() const override { return "eliminate-insert-extract"; } Status Process(ir::Module*) override; private: diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 66fbad538..546bb3d05 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -66,6 +66,34 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) { return *this; } +Optimizer& Optimizer::RegisterPerformancePasses() { + return RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateInsertExtractElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateInsertExtractElimPass()) + .RegisterPass(CreateCommonUniformElimPass()); +} + +Optimizer& Optimizer::RegisterSizePasses() { + return RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateInsertExtractElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateInsertExtractElimPass()) + .RegisterPass(CreateCommonUniformElimPass()); +} + bool Optimizer::Run(const uint32_t* original_binary, const size_t original_binary_size, std::vector* optimized_binary) const { @@ -95,13 +123,11 @@ Optimizer::PassToken CreateStripDebugInfoPass() { MakeUnique()); } - Optimizer::PassToken CreateEliminateDeadFunctionsPass() { return MakeUnique( MakeUnique()); } - Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( const std::unordered_map& id_value_map) { return MakeUnique( @@ -153,17 +179,17 @@ Optimizer::PassToken CreateInlineExhaustivePass() { return MakeUnique( MakeUnique()); } - + Optimizer::PassToken CreateInlineOpaquePass() { return MakeUnique( MakeUnique()); } - + Optimizer::PassToken CreateLocalAccessChainConvertPass() { return MakeUnique( MakeUnique()); } - + Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() { return MakeUnique( MakeUnique()); @@ -204,4 +230,12 @@ Optimizer::PassToken CreateCompactIdsPass() { MakeUnique()); } +std::vector Optimizer::GetPassNames() const { + std::vector v; + for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) { + v.push_back(impl_->pass_manager.GetPass(i)->name()); + } + return v; +} + } // namespace spvtools diff --git a/source/opt/pass.h b/source/opt/pass.h index 78622705f..ee04ae5d5 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h @@ -56,6 +56,11 @@ class Pass { virtual ~Pass() = default; // Returns a descriptive name for this pass. + // + // NOTE: When deriving a new pass class, make sure you make the name + // compatible with the corresponding spirv-opt command-line flag. For example, + // if you add the flag --my-pass to spirv-opt, make this function return + // "my-pass" (no leading hyphens). virtual const char* name() const = 0; // Sets the message consumer to the given |consumer|. |consumer| which will be diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index ac037c51f..956f76c47 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include +#include +#include +#include #include #include #include @@ -27,6 +29,36 @@ using namespace spvtools; +namespace { + +// Status and actions to perform after parsing command-line arguments. +enum OptActions { OPT_CONTINUE, OPT_STOP }; + +struct OptStatus { + OptActions action; + int code; +}; + +std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { + std::stringstream ss; + for (const auto& name : optimizer.GetPassNames()) { + ss << "\n\t\t" << name; + } + return ss.str(); +} + +std::string GetOptimizationPasses() { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_2); + optimizer.RegisterPerformancePasses(); + return GetListOfPassesAsString(optimizer); +} + +std::string GetSizePasses() { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_2); + optimizer.RegisterSizePasses(); + return GetListOfPassesAsString(optimizer); +} + void PrintUsage(const char* program) { printf( R"(%s - Optimize a SPIR-V binary file. @@ -117,15 +149,257 @@ Options: call tree functions. --strength-reduction Replaces instructions with equivalent and less expensive ones. - -h, --help + -O + Optimize for performance. Apply a sequence of transformations + in an attempt to improve the performance of the generated + code. For this version of the optimizer, this flag is equivalent + to specifying the following optimization code names: + %s + -Os + Optimize for size. Apply a sequence of transformations in an + attempt to minimize the size of the generated code. For this + version of the optimizer, this flag is equivalent to specifying + the following optimization code names: + %s + + NOTE: The specific transformations done by -O and -Os change + from release to release. + -Oconfig= + Apply the sequence of transformations indicated in . + This file contains a sequence of strings separated by whitespace + (tabs, newlines or blanks). Each string is one of the flags + accepted by spirv-opt. Optimizations will be applied in the + sequence they appear in the file. This is equivalent to + specifying all the flags on the command line. For example, + given the file opts.cfg with the content: + + --inline-entry-points-exhaustive + --eliminate-dead-code-aggressive + + The following two invocations to spirv-opt are equivalent: + + $ spirv-opt -Oconfig=opts.cfg program.spv + + $ spirv-opt --inline-entry-points-exhaustive \ + --eliminate-dead-code-aggressive program.spv + + Lines starting with the character '#' in the configuration + file indicate a comment and will be ignored. + + The -O, -Os, and -Oconfig flags act as macros. Using one of them + is equivalent to explicitly inserting the underlying flags at + that position in the command line. For example, the invocation + 'spirv-opt --merge-blocks -O ...' applies the transformation + --merge-blocks followed by all the transformations implied by + -O. + -h, --help Print this help. - --version + --version Display optimizer version information. )", - program, program); + program, program, GetOptimizationPasses().c_str(), + GetSizePasses().c_str()); } -int main(int argc, char** argv) { +// Reads command-line flags the file specified in |oconfig_flag|. This string +// is assumed to have the form "-Oconfig=FILENAME". This function parses the +// string and extracts the file name after the '=' sign. +// +// Flags found in |FILENAME| are pushed at the end of the vector |file_flags|. +// +// This function returns true on success, false on failure. +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); + return false; + } + fname++; + + std::ifstream input_file; + input_file.open(fname); + if (input_file.fail()) { + fprintf(stderr, "error: Could not open file '%s'\n", fname); + return false; + } + + while (!input_file.eof()) { + std::string flag; + input_file >> flag; + if (flag.length() > 0 && flag[0] != '#') { + file_flags->push_back(flag); + } + } + + return true; +} + +OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer, + const char** in_file, const char** out_file); + +// Parses and handles the -Oconfig flag. |prog_name| contains the name of +// the spirv-opt binary (used to build a new argv vector for the recursive +// invocation to ParseFlags). |opt_flag| contains the -Oconfig=FILENAME flag. +// |optimizer|, |in_file| and |out_file| are as in ParseFlags. +// +// This returns the same OptStatus instance returned by ParseFlags. +OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag, + Optimizer* optimizer, const char** in_file, + const char** out_file) { + std::vector flags; + flags.push_back(prog_name); + + std::vector file_flags; + if (!ReadFlagsFromFile(opt_flag, &file_flags)) { + fprintf(stderr, + "error: Could not read optimizer flags from configuration file\n"); + return {OPT_STOP, 1}; + } + flags.insert(flags.end(), file_flags.begin(), file_flags.end()); + + 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"); + return {OPT_STOP, 1}; + } + new_argv[i] = flags[i].c_str(); + } + + return ParseFlags(static_cast(flags.size()), new_argv, optimizer, + in_file, out_file); +} + +// 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. +// +// 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 +// optimization should continue and a status code indicating an error or +// success. +OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer, + const char** in_file, const char** out_file) { + 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()); + return {OPT_STOP, 0}; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + PrintUsage(argv[0]); + return {OPT_STOP, 0}; + } else if (0 == strcmp(cur_arg, "-o")) { + if (!*out_file && argi + 1 < argc) { + *out_file = argv[++argi]; + } else { + PrintUsage(argv[0]); + return {OPT_STOP, 1}; + } + } else if (0 == strcmp(cur_arg, "--strip-debug")) { + optimizer->RegisterPass(CreateStripDebugInfoPass()); + } 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 { + fprintf( + stderr, + "error: Expected a string of : pairs."); + return {OPT_STOP, 1}; + } + } 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, "--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, "--fold-spec-const-op-composite")) { + optimizer->RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); + } 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, "-O")) { + optimizer->RegisterPerformancePasses(); + } else if (0 == strcmp(cur_arg, "-Os")) { + optimizer->RegisterSizePasses(); + } 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' == 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 { + fprintf( + stderr, + "error: Unknown flag '%s'. Use --help for a list of valid flags\n", + cur_arg); + return {OPT_STOP, 1}; + } + } else { + if (!*in_file) { + *in_file = cur_arg; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return {OPT_STOP, 1}; + } + } + } + + return {OPT_CONTINUE, 0}; +} + +} // namespace + +int main(int argc, const char** argv) { const char* in_file = nullptr; const char* out_file = nullptr; @@ -139,102 +413,9 @@ int main(int argc, char** argv) { << std::endl; }); - 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()); - return 0; - } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { - PrintUsage(argv[0]); - return 0; - } else if (0 == strcmp(cur_arg, "-o")) { - if (!out_file && argi + 1 < argc) { - out_file = argv[++argi]; - } else { - PrintUsage(argv[0]); - return 1; - } - } else if (0 == strcmp(cur_arg, "--strip-debug")) { - optimizer.RegisterPass(CreateStripDebugInfoPass()); - } 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 1; - } - optimizer.RegisterPass( - CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals))); - } else { - fprintf( - stderr, - "error: Expected a string of : pairs."); - return 1; - } - } 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, "--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, "--fold-spec-const-op-composite")) { - optimizer.RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); - } 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' == 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 1; - } - } else { - PrintUsage(argv[0]); - return 1; - } - } else { - if (!in_file) { - in_file = cur_arg; - } else { - fprintf(stderr, "error: More than one input file specified\n"); - return 1; - } - } + OptStatus status = ParseFlags(argc, argv, &optimizer, &in_file, &out_file); + if (status.action == OPT_STOP) { + return status.code; } if (out_file == nullptr) { @@ -243,7 +424,9 @@ int main(int argc, char** argv) { } std::vector binary; - if (!ReadFile(in_file, "rb", &binary)) return 1; + if (!ReadFile(in_file, "rb", &binary)) { + return 1; + } // Let's do validation first. spv_context context = spvContextCreate(target_env);