| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904 |
- // Copyright (c) 2016 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <algorithm>
- #include <cassert>
- #include <cstring>
- #include <fstream>
- #include <iostream>
- #include <memory>
- #include <sstream>
- #include <string>
- #include <vector>
- #include "source/opt/log.h"
- #include "source/spirv_target_env.h"
- #include "source/util/string_utils.h"
- #include "spirv-tools/libspirv.hpp"
- #include "spirv-tools/optimizer.hpp"
- #include "tools/io.h"
- #include "tools/util/cli_consumer.h"
- namespace {
- // Status and actions to perform after parsing command-line arguments.
- enum OptActions { OPT_CONTINUE, OPT_STOP };
- struct OptStatus {
- OptActions action;
- 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& /*position*/, 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()) {
- ss << "\n\t\t" << name;
- }
- return ss.str();
- }
- const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
- std::string GetLegalizationPasses() {
- spvtools::Optimizer optimizer(kDefaultEnvironment);
- optimizer.RegisterLegalizationPasses();
- return GetListOfPassesAsString(optimizer);
- }
- std::string GetOptimizationPasses() {
- spvtools::Optimizer optimizer(kDefaultEnvironment);
- optimizer.RegisterPerformancePasses();
- return GetListOfPassesAsString(optimizer);
- }
- std::string GetSizePasses() {
- spvtools::Optimizer optimizer(kDefaultEnvironment);
- optimizer.RegisterSizePasses();
- return GetListOfPassesAsString(optimizer);
- }
- void PrintUsage(const char* program) {
- std::string target_env_list = spvTargetEnvList(16, 80);
- // NOTE: Please maintain flags in lexicographical order.
- printf(
- R"(%s - Optimize a SPIR-V binary file.
- USAGE: %s [options] [<input>] -o <output>
- The SPIR-V binary is read from <input>. If no file is specified,
- or if <input> is "-", then the binary is read from standard input.
- if <output> is "-", then the optimized output is written to
- standard output.
- NOTE: The optimizer is a work in progress.
- Options (in lexicographical order):)",
- program, program);
- printf(R"(
- --amd-ext-to-khr
- Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader,
- and VK_AMD_shader_trinary_minmax with equivalent code using core
- instructions and capabilities.)");
- printf(R"(
- --before-hlsl-legalization
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --ccp
- Apply the conditional constant propagation transform. This will
- propagate constant values throughout the program, and simplify
- expressions and conditional jumps with known predicate
- values. Performed on entry point call tree functions and
- exported functions.)");
- printf(R"(
- --cfg-cleanup
- Cleanup the control flow graph. This will remove any unnecessary
- code from the CFG like unreachable code. Performed on entry
- point call tree functions and exported functions.)");
- printf(R"(
- --combine-access-chains
- Combines chained access chains to produce a single instruction
- where possible.)");
- printf(R"(
- --compact-ids
- Remap result ids to a compact range starting from %%1 and without
- any gaps.)");
- printf(R"(
- --convert-local-access-chains
- Convert constant index access chain loads/stores into
- equivalent load/stores with inserts and extracts. Performed
- on function scope variables referenced only with load, store,
- and constant index access chains in entry point call tree
- functions.)");
- printf(R"(
- --convert-relaxed-to-half
- Convert all RelaxedPrecision arithmetic operations to half
- precision, inserting conversion operations where needed.
- Run after function scope variable load and store elimination
- for better results. Simplify-instructions, redundancy-elimination
- and DCE should be run after this pass to eliminate excess
- conversions. This conversion is useful when the target platform
- does not support RelaxedPrecision or ignores it. This pass also
- removes all RelaxedPrecision decorations.)");
- printf(R"(
- --convert-to-sampled-image "<descriptor set>:<binding> ..."
- convert images and/or samplers with the given pairs of descriptor
- set and binding to sampled images. If a pair of an image and a
- sampler have the same pair of descriptor set and binding that is
- one of the given pairs, they will be converted to a sampled
- image. In addition, if only an image or a sampler has the
- descriptor set and binding that is one of the given pairs, it
- will be converted to a sampled image.)");
- printf(R"(
- --copy-propagate-arrays
- Does propagation of memory references when an array is a copy of
- another. It will only propagate an array if the source is never
- written to, and the only store to the target is the copy.)");
- printf(R"(
- --replace-desc-array-access-using-var-index
- Replaces accesses to descriptor arrays based on a variable index
- with a switch that has a case for every possible value of the
- index.)");
- printf(R"(
- --spread-volatile-semantics
- Spread Volatile semantics to variables with SMIDNV, WarpIDNV,
- SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask,
- SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask
- BuiltIn decorations or OpLoad for them when the shader model is
- ray generation, closest hit, miss, intersection, or callable.
- For the SPIR-V version is 1.6 or above, it also spreads Volatile
- semantics to a variable with HelperInvocation BuiltIn decoration
- in the fragement shader.)");
- printf(R"(
- --descriptor-scalar-replacement
- Replaces every array variable |desc| that has a DescriptorSet
- and Binding decorations with a new variable for each element of
- the array. Suppose |desc| was bound at binding |b|. Then the
- variable corresponding to |desc[i]| will have binding |b+i|.
- The descriptor set will be the same. All accesses to |desc|
- must be in OpAccessChain instructions with a literal index for
- the first index.)");
- printf(R"(
- --descriptor-composite-scalar-replacement
- Same as descriptor-scalar-replacement, but only impacts composite/structs.
- For details, see --descriptor-scalar-replacement help.)");
- printf(R"(
- --descriptor-array-scalar-replacement
- Same as descriptor-scalar-replacement, but only impacts arrays.
- For details, see --descriptor-scalar-replacement help.)");
- printf(R"(
- --eliminate-dead-branches
- Convert conditional branches with constant condition to the
- indicated unconditional branch. Delete all resulting dead
- code. Performed only on entry point call tree functions.)");
- printf(R"(
- --eliminate-dead-code-aggressive
- Delete instructions which do not contribute to a function's
- output. Performed only on entry point call tree functions.)");
- printf(R"(
- --eliminate-dead-const
- Eliminate dead constants.)");
- printf(R"(
- --eliminate-dead-functions
- Deletes functions that cannot be reached from entry points or
- exported functions.)");
- printf(R"(
- --eliminate-dead-inserts
- Deletes unreferenced inserts into composites, most notably
- unused stores to vector components, that are not removed by
- aggressive dead code elimination.)");
- printf(R"(
- --eliminate-dead-input-components
- Deletes unused components from input variables. Currently
- deletes trailing unused elements from input arrays.)");
- printf(R"(
- --eliminate-dead-variables
- Deletes module scope variables that are not referenced.)");
- printf(R"(
- --eliminate-insert-extract
- DEPRECATED. This pass has been replaced by the simplification
- pass, and that pass will be run instead.
- See --simplify-instructions.)");
- printf(R"(
- --eliminate-local-multi-store
- Replace stores and loads of function scope variables that are
- stored multiple times. Performed on variables referenceed only
- with loads and stores. Performed only on entry point call tree
- functions.)");
- printf(R"(
- --eliminate-local-single-block
- Perform single-block store/load and load/load elimination.
- Performed only on function scope variables in entry point
- call tree functions.)");
- printf(R"(
- --eliminate-local-single-store
- Replace stores and loads of function scope variables that are
- only stored once. Performed on variables referenceed only with
- loads and stores. Performed only on entry point call tree
- functions.)");
- printf(R"(
- --fix-func-call-param
- fix non memory argument for the function call, replace
- accesschain pointer argument with a variable.)");
- printf(R"(
- --flatten-decorations
- Replace decoration groups with repeated OpDecorate and
- OpMemberDecorate instructions.)");
- printf(R"(
- --fold-spec-const-op-composite
- Fold the spec constants defined by OpSpecConstantOp or
- OpSpecConstantComposite instructions to front-end constants
- when possible.)");
- printf(R"(
- --freeze-spec-const
- Freeze the values of specialization constants to their default
- values.)");
- printf(R"(
- --graphics-robust-access
- Clamp indices used to access buffers and internal composite
- values, providing guarantees that satisfy Vulkan's
- robustBufferAccess rules.)");
- printf(R"(
- --if-conversion
- Convert if-then-else like assignments into OpSelect.)");
- printf(R"(
- --inline-entry-points-exhaustive
- Exhaustively inline all function calls in entry point call tree
- functions. Currently does not inline calls to functions with
- early return in a loop.)");
- printf(R"(
- --legalize-hlsl
- Runs a series of optimizations that attempts to take SPIR-V
- generated by an HLSL front-end and generates legal Vulkan SPIR-V.
- The optimizations are:
- %s
- Note this does not guarantee legal code. This option passes the
- option --relax-logical-pointer to the validator.)",
- GetLegalizationPasses().c_str());
- printf(R"(
- --local-redundancy-elimination
- Looks for instructions in the same basic block that compute the
- same value, and deletes the redundant ones.)");
- printf(R"(
- --loop-fission
- Splits any top level loops in which the register pressure has
- exceeded a given threshold. The threshold must follow the use of
- this flag and must be a positive integer value.)");
- printf(R"(
- --loop-fusion
- Identifies adjacent loops with the same lower and upper bound.
- If this is legal, then merge the loops into a single loop.
- Includes heuristics to ensure it does not increase number of
- registers too much, while reducing the number of loads from
- memory. Takes an additional positive integer argument to set
- the maximum number of registers.)");
- printf(R"(
- --loop-invariant-code-motion
- Identifies code in loops that has the same value for every
- iteration of the loop, and move it to the loop pre-header.)");
- printf(R"(
- --loop-unroll
- Fully unrolls loops marked with the Unroll flag)");
- printf(R"(
- --loop-unroll-partial
- Partially unrolls loops marked with the Unroll flag. Takes an
- additional non-0 integer argument to set the unroll factor, or
- how many times a loop body should be duplicated)");
- printf(R"(
- --loop-peeling
- Execute few first (respectively last) iterations before
- (respectively after) the loop if it can elide some branches.)");
- printf(R"(
- --loop-peeling-threshold
- Takes a non-0 integer argument to set the loop peeling code size
- growth threshold. The threshold prevents the loop peeling
- from happening if the code size increase created by
- the optimization is above the threshold.)");
- printf(R"(
- --max-id-bound=<n>
- Sets the maximum value for the id bound for the module. The
- default is the minimum value for this limit, 0x3FFFFF. See
- section 2.17 of the Spir-V specification.)");
- printf(R"(
- --merge-blocks
- Join two blocks into a single block if the second has the
- first as its only predecessor. Performed only on entry point
- call tree functions.)");
- printf(R"(
- --merge-return
- Changes functions that have multiple return statements so they
- have a single return statement.
- For structured control flow it is assumed that the only
- unreachable blocks in the function are trivial merge and continue
- blocks.
- A trivial merge block contains the label and an OpUnreachable
- instructions, nothing else. A trivial continue block contain a
- label and an OpBranch to the header, nothing else.
- These conditions are guaranteed to be met after running
- dead-branch elimination.)");
- printf(R"(
- --modify-maximal-reconvergence=[add|remove]
- Add or remove the MaximallyReconvergesKHR execution mode to all
- entry points in the module.
- Note: when adding the execution mode, no attempt is made to
- determine if any ray tracing repack instructions are used.)");
- printf(R"(
- --loop-unswitch
- Hoists loop-invariant conditionals out of loops by duplicating
- the loop on each branch of the conditional and adjusting each
- copy of the loop.)");
- printf(R"(
- -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)",
- GetOptimizationPasses().c_str());
- printf(R"(
- -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.)",
- GetSizePasses().c_str());
- printf(R"(
- -Oconfig=<file>
- Apply the sequence of transformations indicated in <file>.
- 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.)");
- printf(R"(
- --preserve-bindings
- Ensure that the optimizer preserves all bindings declared within
- the module, even when those bindings are unused.)");
- printf(R"(
- --preserve-interface
- Ensure that input and output variables are not removed from the
- shader, even if they are unused. Note that this option applies to
- all passes that will be run regardless of the order of the flags.)");
- printf(R"(
- --preserve-spec-constants
- Ensure that the optimizer preserves all specialization constants declared
- within the module, even when those constants are unused.)");
- printf(R"(
- --print-all
- Print SPIR-V assembly to standard error output before each pass
- and after the last pass.)");
- printf(R"(
- --private-to-local
- Change the scope of private variables that are used in a single
- function to that function.)");
- printf(R"(
- --reduce-load-size[=<threshold>]
- Replaces loads of composite objects where not every component is
- used by loads of just the elements that are used. If the ratio
- of the used components of the load is less than the <threshold>,
- we replace the load. <threshold> is a double type number. If
- it is bigger than 1.0, we always replaces the load.)");
- printf(R"(
- --redundancy-elimination
- Looks for instructions in the same function that compute the
- same value, and deletes the redundant ones.)");
- printf(R"(
- --relax-block-layout
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --relax-float-ops
- Decorate all float operations with RelaxedPrecision if not already
- so decorated. This does not decorate types or variables.)");
- printf(R"(
- --relax-logical-pointer
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --relax-struct-store
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --remove-duplicates
- Removes duplicate types, decorations, capabilities and extension
- instructions.)");
- printf(R"(
- --remove-unused-interface-variables
- Removes variables referenced on the |OpEntryPoint| instruction
- that are not referenced in the entry point function or any function
- in its call tree. Note that this could cause the shader interface
- to no longer match other shader stages.)");
- printf(R"(
- --replace-invalid-opcode
- Replaces instructions whose opcode is valid for shader modules,
- but not for the current shader stage. To have an effect, all
- entry points must have the same execution model.)");
- printf(R"(
- --resolve-binding-conflicts
- Renumber bindings to avoid conflicts.
- When an image and sampler share the same desriptor set and binding,
- increment the binding number of the sampler. Recursively ripple
- to higher-numbered bindings until all conflicts resolved resolved.)");
- printf(R"(
- --ssa-rewrite
- Replace loads and stores to function local variables with
- operations on SSA IDs.)");
- printf(R"(
- --scalar-block-layout
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --scalar-replacement[=<n>]
- Replace aggregate function scope variables that are only accessed
- via their elements with new function variables representing each
- element. <n> is a limit on the size of the aggregates that will
- be replaced. 0 means there is no limit. The default value is
- 100.)");
- printf(R"(
- --set-spec-const-default-value "<spec id>:<default value> ..."
- Set the default values of the specialization constants with
- <spec id>:<default value> pairs specified in a double-quoted
- string. <spec id>:<default value> pairs must be separated by
- blank spaces, and in each pair, spec id and default value must
- be separated with colon ':' without any blank spaces in between.
- e.g.: --set-spec-const-default-value "1:100 2:400")");
- printf(R"(
- --simplify-instructions
- Will simplify all instructions in the function as much as
- possible.)");
- printf(R"(
- --skip-block-layout
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --skip-validation
- Will not validate the SPIR-V before optimizing. If the SPIR-V
- is invalid, the optimizer may fail or generate incorrect code.
- This options should be used rarely, and with caution.)");
- printf(R"(
- --split-combined-image-sampler
- Replace combined image sampler variables and parameters into
- pairs of images and samplers. New variables have the same
- bindings as the original variable.)");
- printf(R"(
- --strength-reduction
- Replaces instructions with equivalent and less expensive ones.)");
- printf(R"(
- --strip-debug
- Remove all debug instructions.)");
- printf(R"(
- --strip-nonsemantic
- Remove all reflection and nonsemantic information.)");
- printf(R"(
- --strip-reflect
- DEPRECATED. Remove all reflection information. For now, this
- covers reflection information defined by
- SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)");
- printf(R"(
- --struct-packing=name:rule
- Re-assign layout offsets to a given struct according to
- its packing rules.)");
- printf(R"(
- --switch-descriptorset=<from>:<to>
- Switch any DescriptoSet decorations using the value <from> to
- the new value <to>.)");
- printf(R"(
- --target-env=<env>
- Set the target environment. Without this flag the target
- environment defaults to spv1.5. <env> must be one of
- {%s})",
- target_env_list.c_str());
- printf(R"(
- --time-report
- Print the resource utilization of each pass (e.g., CPU time,
- RSS) to standard error output. Currently it supports only Unix
- systems. This option is the same as -ftime-report in GCC. It
- prints CPU/WALL/USR/SYS time (and RSS if possible), but note that
- USR/SYS time are returned by getrusage() and can have a small
- error.)");
- printf(R"(
- --trim-capabilities
- Remove unnecessary capabilities and extensions declared within the
- module.)");
- printf(R"(
- --upgrade-memory-model
- Upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
- Transforms memory, image, atomic and barrier operations to conform
- to that model's requirements.)");
- printf(R"(
- --vector-dce
- This pass looks for components of vectors that are unused, and
- removes them from the vector. Note this would still leave around
- lots of dead code that a pass of ADCE will be able to remove.)");
- printf(R"(
- --workaround-1209
- Rewrites instructions for which there are known driver bugs to
- avoid triggering those bugs.
- Current workarounds: Avoid OpUnreachable in loops.)");
- printf(R"(
- --workgroup-scalar-block-layout
- Forwards this option to the validator. See the validator help
- for details.)");
- printf(R"(
- --wrap-opkill
- Replaces all OpKill instructions in functions that can be called
- from a continue construct with a function call to a function
- whose only instruction is an OpKill. This is done to enable
- inlining on these functions.
- )");
- printf(R"(
- --unify-const
- Remove the duplicated constants.)");
- printf(R"(
- --validate-after-all
- Validate the module after each pass is performed.)");
- printf(R"(
- -h, --help
- Print this help.)");
- printf(R"(
- --version
- Display optimizer version information.
- )");
- }
- // 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<std::string>* file_flags) {
- const char* fname = strchr(oconfig_flag, '=');
- if (fname == nullptr || fname[0] != '=') {
- spvtools::Errorf(opt_diagnostic, nullptr, {}, "Invalid -Oconfig flag %s",
- oconfig_flag);
- return false;
- }
- fname++;
- std::ifstream input_file;
- input_file.open(fname);
- if (input_file.fail()) {
- spvtools::Errorf(opt_diagnostic, nullptr, {}, "Could not open file '%s'",
- fname);
- return false;
- }
- std::string line;
- while (std::getline(input_file, line)) {
- // Ignore empty lines and lines starting with the comment marker '#'.
- if (line.length() == 0 || line[0] == '#') {
- continue;
- }
- // Tokenize the line. Add all found tokens to the list of found flags. This
- // mimics the way the shell will parse whitespace on the command line. NOTE:
- // This does not support quoting and it is not intended to.
- std::istringstream iss(line);
- while (!iss.eof()) {
- std::string flag;
- iss >> flag;
- file_flags->push_back(flag);
- }
- }
- return true;
- }
- OptStatus ParseFlags(int argc, const char** argv,
- spvtools::Optimizer* optimizer, const char** in_file,
- const char** out_file,
- spvtools::ValidatorOptions* validator_options,
- spvtools::OptimizerOptions* optimizer_options);
- // Parses and handles the -Oconfig flag. |prog_name| contains the name of
- // the spirv-opt binary (used to build a new argv vector for the recursive
- // invocation to ParseFlags). |opt_flag| contains the -Oconfig=FILENAME flag.
- // |optimizer|, |in_file|, |out_file|, |validator_options|, and
- // |optimizer_options| are as in ParseFlags.
- //
- // This returns the same OptStatus instance returned by ParseFlags.
- OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag,
- spvtools::Optimizer* optimizer, const char** in_file,
- const char** out_file,
- spvtools::ValidatorOptions* validator_options,
- spvtools::OptimizerOptions* optimizer_options) {
- std::vector<std::string> flags;
- flags.push_back(prog_name);
- std::vector<std::string> file_flags;
- if (!ReadFlagsFromFile(opt_flag, &file_flags)) {
- spvtools::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());
- 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) {
- spvtools::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();
- }
- auto ret_val =
- ParseFlags(static_cast<int>(flags.size()), new_argv, optimizer, in_file,
- out_file, validator_options, optimizer_options);
- delete[] new_argv;
- return ret_val;
- }
- // 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;
- // 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);
- }
- }
- return canonical_arg.str();
- }
- // 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,
- spvtools::Optimizer* optimizer, const char** in_file,
- const char** out_file,
- spvtools::ValidatorOptions* validator_options,
- spvtools::OptimizerOptions* optimizer_options) {
- std::vector<std::string> pass_flags;
- bool preserve_interface = true;
- for (int argi = 1; argi < argc; ++argi) {
- const char* cur_arg = argv[argi];
- if ('-' == cur_arg[0]) {
- if (0 == strcmp(cur_arg, "--version")) {
- spvtools::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]);
- 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' == cur_arg[1]) {
- // Setting a filename of "-" to indicate stdin.
- if (!*in_file) {
- *in_file = cur_arg;
- } else {
- spvtools::Error(opt_diagnostic, nullptr, {},
- "More than one input file specified");
- return {OPT_STOP, 1};
- }
- } else if (0 == strncmp(cur_arg, "-Oconfig=", sizeof("-Oconfig=") - 1)) {
- OptStatus status =
- ParseOconfigFlag(argv[0], cur_arg, optimizer, in_file, out_file,
- validator_options, optimizer_options);
- if (status.action != OPT_CONTINUE) {
- return status;
- }
- } else if (0 == strcmp(cur_arg, "--skip-validation")) {
- optimizer_options->set_run_validator(false);
- } else if (0 == strcmp(cur_arg, "--print-all")) {
- optimizer->SetPrintAll(&std::cerr);
- } else if (0 == strcmp(cur_arg, "--preserve-bindings")) {
- optimizer_options->set_preserve_bindings(true);
- } else if (0 == strcmp(cur_arg, "--preserve-spec-constants")) {
- optimizer_options->set_preserve_spec_constants(true);
- } else if (0 == strcmp(cur_arg, "--time-report")) {
- optimizer->SetTimeReport(&std::cerr);
- } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
- validator_options->SetRelaxStructStore(true);
- } else if (0 == strncmp(cur_arg, "--max-id-bound=",
- sizeof("--max-id-bound=") - 1)) {
- auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
- // Will not allow values in the range [2^31,2^32).
- uint32_t max_id_bound =
- static_cast<uint32_t>(atoi(split_flag.second.c_str()));
- // That SPIR-V mandates the minimum value for max id bound but
- // implementations may allow higher minimum bounds.
- if (max_id_bound < kDefaultMaxIdBound) {
- spvtools::Error(opt_diagnostic, nullptr, {},
- "The max id bound must be at least 0x3FFFFF");
- return {OPT_STOP, 1};
- }
- optimizer_options->set_max_id_bound(max_id_bound);
- validator_options->SetUniversalLimit(spv_validator_limit_max_id_bound,
- max_id_bound);
- } else if (0 == strncmp(cur_arg,
- "--target-env=", sizeof("--target-env=") - 1)) {
- const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
- const auto target_env_str = split_flag.second.c_str();
- spv_target_env target_env;
- if (!spvParseTargetEnv(target_env_str, &target_env)) {
- spvtools::Error(opt_diagnostic, nullptr, {},
- "Invalid value passed to --target-env");
- return {OPT_STOP, 1};
- }
- optimizer->SetTargetEnv(target_env);
- } else if (0 == strcmp(cur_arg, "--validate-after-all")) {
- optimizer->SetValidateAfterAll(true);
- } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
- validator_options->SetBeforeHlslLegalization(true);
- } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) {
- validator_options->SetRelaxLogicalPointer(true);
- } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
- validator_options->SetRelaxBlockLayout(true);
- } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
- validator_options->SetScalarBlockLayout(true);
- } else if (0 == strcmp(cur_arg, "--workgroup-scalar-block-layout")) {
- validator_options->SetWorkgroupScalarBlockLayout(true);
- } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
- validator_options->SetSkipBlockLayout(true);
- } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
- validator_options->SetRelaxStructStore(true);
- } else if (0 == strcmp(cur_arg, "--preserve-interface")) {
- preserve_interface = true;
- } else {
- // 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")) {
- validator_options->SetBeforeHlslLegalization(true);
- }
- }
- } else {
- if (!*in_file) {
- *in_file = cur_arg;
- } else {
- spvtools::Error(opt_diagnostic, nullptr, {},
- "More than one input file specified");
- return {OPT_STOP, 1};
- }
- }
- }
- if (!optimizer->RegisterPassesFromFlags(pass_flags, preserve_interface)) {
- 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;
- spv_target_env target_env = kDefaultEnvironment;
- spvtools::Optimizer optimizer(target_env);
- optimizer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
- spvtools::ValidatorOptions validator_options;
- spvtools::OptimizerOptions optimizer_options;
- OptStatus status = ParseFlags(argc, argv, &optimizer, &in_file, &out_file,
- &validator_options, &optimizer_options);
- optimizer_options.set_validator_options(validator_options);
- if (status.action == OPT_STOP) {
- return status.code;
- }
- if (out_file == nullptr) {
- spvtools::Error(opt_diagnostic, nullptr, {}, "-o required");
- return 1;
- }
- std::vector<uint32_t> binary;
- if (!ReadBinaryFile(in_file, &binary)) {
- return 1;
- }
- // By using the same vector as input and output, we save time in the case
- // that there was no change.
- bool ok =
- optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
- if (!WriteFile<uint32_t>(out_file, "wb", binary.data(), binary.size())) {
- return 1;
- }
- return ok ? 0 : 1;
- }
|