Skip to content

Add -O, -Os and -Oconfig flags.#817

Closed
dnovillo wants to merge 1 commit intoKhronosGroup:masterfrom
dnovillo:opt-pipeline
Closed

Add -O, -Os and -Oconfig flags.#817
dnovillo wants to merge 1 commit intoKhronosGroup:masterfrom
dnovillo:opt-pipeline

Conversation

@dnovillo
Copy link
Copy Markdown
Contributor

NOTE: This is still WIP. I wanted to provide an early patch to give an idea of where I intend to go with this. I would appreciate feedback on the direction this is going, so I can adjust it accordingly.

Major missing bits:

1- Proper expansion for -O. I added some transformations mostly to have something to debug,
but I am sure that list is incomplete and/or makes little sense.
2- Similarly for -Os.
3- Unit tests
4- There is some debugging code that still needs to be taken out.

======================================================================

These flags are expanded to a series of spirv-opt flags with the
following semantics:

-O: expands to passes that attempt to improve the performance of the
generated code.

-Os: expands to passes that attempt to reduce the size of the generated
code.

-Oconfig= expands to the sequence of passes determined by the
flags specified in the user-provided file.

Copy link
Copy Markdown
Contributor

@greg-lunarg greg-lunarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have commented on the documentation and content of the -O and -Os options. I leave infrastructure and style to those that are closer to those considerations.


namespace spvtools {

// Size optimization flags expanded by -Os.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend at least these as a first approximation to what we want to include for size reduction:

--inline-entry-points-exhaustive
--convert-local-access-chains
--eliminate-local-single-block
--eliminate-local-single-store
--eliminate-insert-extract
--eliminate-dead-code-aggressive
--eliminate-dead-branches
--merge-blocks
--eliminate-local-multi-store
--eliminate-insert-extract
--eliminate-dead-code-aggressive
--eliminate-common-uniform

Of course, the exhaustive inlining is controversial at best, but we really don't have any other inlining at the moment and the rest of the options are not as effective for most shaders with function calls without some inlining. If we feel we want to make -Os available now, then I would include exhaustive inlining.

If you haven't seen this yet, you may want to read this as it will give you an idea how these passes work and how they are designed to work together: https://www.lunarg.com/wp-content/uploads/2017/08/SPIR-V-Shader-Size-Reduction-Using-spirv-opt_v1.0.pdf

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't given this data point before about exhaustive inlining: In my work on clspv, I'm working on some codebases originating from the OpenCL world. Some of the code I've seen has a large handful of entry points in the same compilation units, and they call down into common functions. Exhaustive inlining for all entry points would increase size.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I've added these passes for now. We'll be testing them internally and we'll see how they work. I'm particularly perplexed at the exhaustive inliner, but I don't really have a good idea of why it's having this effect.

// Using the -Os flag in spirv-opt's command line is equivalent to specifying
// the following flags in this exact sequence.
std::vector<std::string> SizePasses = {
"--strip-debug",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we should include this as some users may wish to reduce the size of their SPIR-V but keep debugging info in.

"--eliminate-dead-code-aggressive",
"--eliminate-dead-branches",
"--merge-blocks",
"--flatten-decorations",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure --flatten-decorations will increase size. I don't think we want to include it here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Also, I don't know of any tools that produce grouped decorations. :-)

"--eliminate-dead-branches",
"--merge-blocks",
"--flatten-decorations",
"--compact-ids"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this does not reduce size. We should probably remove it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree on this one. spirv-remap works hard to set Ids so they look similar-ish across modules.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, folks. But I really didn't put any thought in these strings. They're very literally options I just picked at random. I would like the dev community to give an initial list of flags you think might be good for -O and -Os. I very literally do not care what we put here for now. I don't have a good set of benchmarks to try it for now.

first as its only predecessor. Performed only on entry point
call tree functions.
-h, --help
-O
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not advertise -O and -Os until we are fairly confident they are close to what we want. We want to make sure initial experience with these options is positive. If you follow my recommendations for what to include for them, then I would be fine with advertising here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That make some sense, especially while you've got this as a work in progress. But we also need a way to converge: How do we know we have a good set?

Alternately, once we've settled on an initial sane config, you could advertise this as EXPERIMENTAL.

It should probably always be listed a subject to change from one version of the software to the next.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think marking it [experimental] is the way to go. No point hiding it if we want users to try it out.

// Using the -O flag in spirv-opt's command line is equivalent to specifying
// the following flags in this exact sequence.
std::vector<std::string> OptimizationPasses = {
"--inline-entry-points-opaque",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my recommendations for -Os. I would include those here as well. They are designed to eliminate all loads and stores to function scope variable which is a performance win besides a size win.

I would not use opaque inlining here. It was not designed with performance in mind (and it doesn't even quite solve the problem it was originally designed for). I am currently writing a "default" inliner which I think might be more acceptable here, but until that is ready I think the exhaustive inliner is going to be our best bet. Without some form of inlining, the other size-reduction passes will be much less effective for shaders with function calls.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this list will change over time. It's always subject to tuning.
Suggest adding a comment saying so.

Exhaustive inlining is probably the right thing for graphics shaders since there's only one entry point.
That's true for GLSL, and HLSL compilation via Glslang and dxc.
On the othe rhand, a module-combiner tool/API is in code review.
In OpenCL it's common to have multiple entry points in the same module.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. As a starting point, I've made both lists identical for now. I've added comments stating that they're in flux.

@dneto0 dneto0 changed the title Add -O, -Os and -Oconfig flags. WIP: Add -O, -Os and -Oconfig flags. Sep 20, 2017
Copy link
Copy Markdown
Collaborator

@dneto0 dneto0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feedback you asked for: High level design looks good to me.

I've got some lower level comments as well, embedded.

And yes, the various lists will need tuning. Should state that they are always subject to revision. Say so in the comments and any online help you provide.


// Registers passes that attempt to improve performance of generated code.
// The sequence of passes is defined in the PerformancePasses vector.
void RegisterPerformancePasses();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a small thing, but I'd prefer this to return a reference to *this. That way we can stack calls, e.g.

  RegsiterPerformancePasses().RegisterPass(blah blah)

Copy link
Copy Markdown
Contributor Author

@dnovillo dnovillo Oct 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Using the -O flag in spirv-opt's command line is equivalent to specifying
// the following flags in this exact sequence.
std::vector<std::string> OptimizationPasses = {
"--inline-entry-points-opaque",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this list will change over time. It's always subject to tuning.
Suggest adding a comment saying so.

Exhaustive inlining is probably the right thing for graphics shaders since there's only one entry point.
That's true for GLSL, and HLSL compilation via Glslang and dxc.
On the othe rhand, a module-combiner tool/API is in code review.
In OpenCL it's common to have multiple entry points in the same module.

using namespace spvtools;

std::string Flags2Str(const std::vector<std::string>& flags) {
std::string s;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of habit we tend to accumulate on a std::ostringstream and then call the .str() method get the final result.
That avoids quadratic reallocation (in theory), which matters if the vector is large.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

first as its only predecessor. Performed only on entry point
call tree functions.
-h, --help
-O
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That make some sense, especially while you've got this as a work in progress. But we also need a way to converge: How do we know we have a good set?

Alternately, once we've settled on an initial sane config, you could advertise this as EXPERIMENTAL.

It should probably always be listed a subject to change from one version of the software to the next.

-Os
Optimize for size. Apply a sequence of transformations in an
attempt to minimize the size of the generated code. This flag
is equivalent to specifying the following flags: %s
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly should say this is subject to change from one revision of the code to the next.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

flags.insert(flags.end(), SizePasses.begin(), SizePasses.end());
} else if (0 == strncmp(opt_flag, "-Oconfig=", sizeof("-Oconfig=") - 1)) {
std::vector<std::string> file_flags;
if (!ReadFlagsFromFile(opt_flag, &file_flags)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I guess -Oconfig=foo inside a flags file does not work? That's fine, I'm sure. :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was debating whether to allow it or not. I'm disallowing it for now, but we can review this later. It's possible to have -O and -Os inside the configuration file, of course. I figured a -Oconfig= inside a configuration file is just asking for trouble.

//
// Using the -Os flag in spirv-opt's command line is equivalent to specifying
// the following flags in this exact sequence.
std::vector<std::string> SizePasses = {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a function to return this.

"Variables of class type with static storage duration are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I've reworked the whole -O and -Os handling. They are now part of the Optimizer API. There are two new methods: RegisterPerformancePasses and RegisterSizePasses. These do the direct instantiation of the related passes. Having them inside the Optimizer API allows us to invoke them from places other than spirv-opt.

"--eliminate-dead-code-aggressive",
"--eliminate-dead-branches",
"--merge-blocks",
"--flatten-decorations",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Also, I don't know of any tools that produce grouped decorations. :-)

// Size optimization flags expanded by -Os.
//
// Using the -Os flag in spirv-opt's command line is equivalent to specifying
// the following flags in this exact sequence.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend a note this list is subject to continual tuning.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"--eliminate-dead-branches",
"--merge-blocks",
"--flatten-decorations",
"--compact-ids"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree on this one. spirv-remap works hard to set Ids so they look similar-ish across modules.

--eliminate-dead-code-aggressive program.spv

Note that the flags -O, -Os and -Oconfig=<file> are also
affected by other flags present on the command line. For
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we improve the "affected" phrasing? How about:

Is this better?

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Collaborator

@dneto0 dneto0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level concern: I wonder if the lists of passes be available via public API. That way an external tool (such as glslc) could reuse the list when performing the optimizations. That way we could keep the list (and tuning knowledge) in one place rather than replicating it.

@dnovillo dnovillo changed the title WIP: Add -O, -Os and -Oconfig flags. Add -O, -Os and -Oconfig flags. Sep 29, 2017
@dnovillo dnovillo force-pushed the opt-pipeline branch 4 times, most recently from 2e0a7fb to d544bdb Compare October 5, 2017 14:45
@dnovillo
Copy link
Copy Markdown
Contributor Author

dnovillo commented Oct 5, 2017

I've addressed all the requested changes. PTAL. Thanks.

Copy link
Copy Markdown
Collaborator

@dneto0 dneto0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low level item: Method comment on GetPassNames should talk about lifetime of the char pointers.

High level item: The name advertised by a particular pass via its name() method is often different from what we use on the command line. So that will be confusing.
Two options:

  1. Explain the difference. (Yucky)
  2. Change the internal name to the public name (without leading double-hyphen). And forever catch a particular discrepancy in code review.

I prefer option 2. I don't think anybody really depends on the specific result of the name() method yet.

What do you think?

std::vector<uint32_t>* optimized_binary) const;

// Returns a vector of strings with all the pass names added to this
// optimizer's pass manager.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since they are const char* you should say how long the pointers are valid. They are valid until the pass manager is destroyed.

(Or, change it to return std::string)

These flags are expanded to a series of spirv-opt flags with the
following semantics:

-O: expands to passes that attempt to improve the performance of the
    generated code.

-Os: expands to passes that attempt to reduce the size of the generated
     code.

-Oconfig=<file> expands to the sequence of passes determined by the
                flags specified in the user-provided file.
@dnovillo
Copy link
Copy Markdown
Contributor Author

dnovillo commented Oct 6, 2017 via email

// 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).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@dneto0
Copy link
Copy Markdown
Collaborator

dneto0 commented Oct 10, 2017

Rebased and pushed into master as c90d730

@dneto0 dneto0 closed this Oct 10, 2017
@dnovillo dnovillo deleted the opt-pipeline branch October 17, 2017 18:56
dneto0 pushed a commit to dneto0/SPIRV-Tools that referenced this pull request Sep 14, 2024
Roll third_party/glslang/ 973d0e5..7bc0473 (1 commit)

KhronosGroup/glslang@973d0e5...7bc0473

$ git log 973d0e5..7bc0473 --date=short --no-merges --format='%ad %ae %s'
2019-09-18 laddoc Reflection will crash when the VS input symbol defines the same name with FS output symbol

Roll third_party/googletest/ f2fb48c3b..dc1ca9ae4 (7 commits)

google/googletest@f2fb48c...dc1ca9a

$ git log f2fb48c3b..dc1ca9ae4 --date=short --no-merges --format='%ad %ae %s'
2019-09-29 misterg Googletest export
2019-09-27 misterg Googletest export
2019-09-25 absl-team Googletest export
2019-09-25 absl-team Googletest export
2019-09-24 absl-team Googletest export
2019-09-19 absl-team Googletest export
2019-09-27 gennadiycivil Bump llvm version to 4 so brew can work again

Roll third_party/spirv-headers/ 601d738..842ec90 (4 commits)

KhronosGroup/SPIRV-Headers@601d738...842ec90

$ git log 601d738..842ec90 --date=short --no-merges --format='%ad %ae %s'
2019-09-24 ehsannas Improve the doc on using Bazel.
2019-09-24 rex.xu Bump the SPIR-V version to 1.5
2019-09-23 ehsannas Update documentation.
2019-09-18 ehsannas Add a Bazel build file.

Roll third_party/spirv-tools/ f62ee4a..9eb1c9a (22 commits)

KhronosGroup/SPIRV-Tools@f62ee4a...9eb1c9a

$ git log f62ee4a..9eb1c9a --date=short --no-merges --format='%ad %ae %s'
2019-10-01 stevenperron Add continue construct analysis to struct cfg analysis (KhronosGroup#2922)
2019-09-27 stevenperron Record trailing line dbg instructions (KhronosGroup#2926)
2019-09-27 rharrison Add removing references to debug instructions when removing them (KhronosGroup#2923)
2019-09-27 paulthomson spirv-fuzz: allow interestingness script arguments (KhronosGroup#2925)
2019-09-27 ehsannas Add Kokoro bots for building with Bazel. (KhronosGroup#2914)
2019-09-27 alanbaker Refactor the InstructionPass (KhronosGroup#2924)
2019-09-26 afdx spirv-fuzz: do not allow a dead break to target an unreachable block (KhronosGroup#2917)
2019-09-26 afdx spirv-fuzz: preserve some analyses when permuting blocks (KhronosGroup#2918)
2019-09-25 alanbaker Only allow previously declared forward refs in structs (KhronosGroup#2920)
2019-09-25 stevenperron Handle id overflow in wrap-opkill (KhronosGroup#2916)
2019-09-25 afdx spirv-fuzz: do not replace struct indices with synonyms (KhronosGroup#2915)
2019-09-25 afdx spirv-fuzz: Fixes to preconditions for adding dead break/continue edges (KhronosGroup#2904)
2019-09-25 afdx spirv-fuzz: do not replace a pointer argument to a function call with a synonym (KhronosGroup#2901)
2019-09-25 afdx spirv-fuzz: do not replace boolean constant argument to OpPhi instruction (KhronosGroup#2903)
2019-09-24 alanbaker Remove validate_datarules.cpp (KhronosGroup#2911)
2019-09-24 stevenperron Handle extract with no indexes (KhronosGroup#2910)
2019-09-24 ehsannas Add Bazel build configuration. (KhronosGroup#2891)
2019-09-24 stevenperron Handle id overflow in convert local access chains (KhronosGroup#2908)
2019-09-24 dsinclair Add OpCopyMemory test to SVA. (KhronosGroup#2885)
2019-09-23 dsinclair Add missing GN dependency (KhronosGroup#2899)
2019-09-23 afdx Employ the "swarm testing" idea in spirv-fuzz (KhronosGroup#2890)
2019-09-23 afdx Fix operand index in spirv-fuzz (KhronosGroup#2895)

Created with:
  roll-dep third_party/effcee third_party/glslang third_party/googletest third_party/re2 third_party/spirv-cross third_party/spirv-headers third_party/spirv-tools
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants