Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple noinline instances with different arguments. #6687

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

DirtyHairy
Copy link

Allow multiple no-inline - passes as suggested in #6646 .

@DirtyHairy DirtyHairy changed the title Allow multiple noinline instances with different arguments. Resolves #6646 Allow multiple noinline instances with different arguments. Jun 20, 2024
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Nice!

name, "Usage usage: wasm-opt --" + name + "=WILDCARD");
std::string pattern =
passArg ? *passArg
: getPassOptions().getArgument(
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we can simplify this? Specifically that it accepts two mechanisms seems a little more complex than it would ideally be.

In fact --passname=ARG sets both of them, so I think we only need to check the former? But I might be missing something.

Copy link
Author

Choose a reason for hiding this comment

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

The reason why I kept the global mechanism is --pass-arg.

Copy link
Member

@kripken kripken Jun 21, 2024

Choose a reason for hiding this comment

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

Ah, yes, I agree we want the global mechanism too. And that makes sense: there can be a global flag that multiple passes (even of different operations, not just instances of the same Pass) care about, but also each pass instance can have its own. But here, I think this can always read passArg rather than check passArg and maybe read getPassOptions().getArgument(..).

src/pass.h Outdated
@@ -47,6 +51,7 @@ struct PassRegistry {
std::vector<std::string> getRegisteredNames();
std::string getPassDescription(std::string name);
bool isPassHidden(std::string name);
bool doesPassAllowMultipleInstancesWithArgs(std::string name);
Copy link
Member

Choose a reason for hiding this comment

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

Can we make this the behavior of all passes? I can't think of a problem that would occur, and that seems simpler, but again I might be missing something.

Copy link
Author

Choose a reason for hiding this comment

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

Sure. I introduced the flag only for strict backwards compatibility for the other passes (bail out if the pass is specified multiple times with different arguments), but I'd be more than happy to remove it 😏

Copy link
Member

Choose a reason for hiding this comment

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

Ok good, I don't think we need to bother with the case of passes passed different arguments before now (that was never meant to work). So if no tests break, then let's make that change.

Copy link
Author

Choose a reason for hiding this comment

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

OK. Before I go ahead and make those changes, just to make sure we have the same picture of how the result should look like: All passes will use their own private value of the option that shares their name, and only options that have a different name and thus are set via --pass-arg will be read from the global map.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that sounds right.

Copy link
Member

@kripken kripken Jun 26, 2024

Choose a reason for hiding this comment

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

Ah, I think I wasn't clear enough before, or I've misunderstood you. To make sure we are on the same page, the issue is that we need the fallback from the pass's argument to the global argument, because of the two forms --foopass=FOO and --pass-arg=foopass@FOO?

I think we can get around that. It looks like optimization-options handles the first form, and tool-options the second, but if they were coordinated this could be fixed, and passes would only read from the one place.

Concretely, I'm suggesting this:

  • When we see --foopass=FOO then we add it to the pass instance as an argument.
  • When we see --pass-arg=foopass@FOO then we check if there is a pass for it.
    • If there is, we write it to that pass instance as an argument. If there is more than one appearance of the pass, set it for them all.
    • If there is no such pass, write it to the global store.

There is no need for a fallback here, since we always write to the pass instance when it has the name of a pass, and always to the global store when not.

That means some more processing in tool-options/optimization-options, but I think it's worth it. It would be simpler, and also backwards compatible except for corner cases with multiple invocations of the same pass (which I am ok with altering).

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, I think we managed to misunderstand each other 😏

To make sure we are on the same page, the issue is that we need the fallback from the pass's argument to the global argument, because of the two forms --foopass=FOO and --pass-arg=foopass@FOO?

Exactly.

I think your suggestion would work well, although I'd suggest a minor change:

  • When we see --foopass=FOO we set the argument on the pass instance
  • When we see --pass-arg=foopass@FOO we check the pass list for foopass
    • If we find an instance of foopass we set the argument there and do not continue to look for other, earlier instances
    • If there is no instance we store the argument in the global list, take it from there when we add foopass after that and remove it from the global list afterwards

This way, --pass-arg=foopass will always affect the previous instance of foopass on the command line, except when it occurs before the first --foopass, in which case it will affect the first instance. To me, this is a little more consistent with how I would expect the UI to behave (instead of a single --pass-arg=foopass overriding all previous instances).

Copy link
Member

Choose a reason for hiding this comment

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

Interesting. I like that it affects just one instance. Maybe we can make that even simpler, though: --pass-arg=foopass@FOO can always "attach" to the last instance, as you suggested, but also (1) it is an error if that pass already has an arg, and (2) it is an error if there is no pass instance before. Those avoid some possible ambiguity I think?

Copy link
Author

Choose a reason for hiding this comment

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

I like that even better 😏 I'll make the necessary changes.

Copy link
Author

Choose a reason for hiding this comment

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

OK, done. I also rebased my code on the current main head. If we agree that this is the behaviour we want to stick with I can also add a few tests.

src/binaryen-c.cpp Outdated Show resolved Hide resolved
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Nice work!

src/binaryen-c.cpp Show resolved Hide resolved
src/pass.h Outdated Show resolved Hide resolved
src/pass.h Outdated Show resolved Hide resolved
src/passes/pass.cpp Outdated Show resolved Hide resolved
src/passes/pass.cpp Outdated Show resolved Hide resolved
src/passes/pass.cpp Outdated Show resolved Hide resolved
src/tools/optimization-options.h Outdated Show resolved Hide resolved
src/tools/optimization-options.h Show resolved Hide resolved
@kripken
Copy link
Member

kripken commented Jul 2, 2024

Also, the behavior we decided on should be documented somewhere - did I miss that in the code?

@DirtyHairy
Copy link
Author

Also, the behavior we decided on should be documented somewhere - did I miss that in the code?

No, there isn't anything yet, but I agree, some documentation would be nice and helpful. Do you have a pointer what a good place would be?

@kripken
Copy link
Member

kripken commented Jul 3, 2024

The description of --pass-arg would be one user-facing place. For internal code documentation, in pass.h where the pass has its argument could be a good place.

@DirtyHairy
Copy link
Author

Thanks 😏 I've added a few comments.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Great lgtm with one suggestion.

"in the form KEY@VALUE",
"in the form KEY@VALUE. If a pass --somepass is specified "
"multiple times, --pass-arg=somepass will apply to the closest "
"instance to the left. All other options apply to all instances.",
Copy link
Member

Choose a reason for hiding this comment

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

This should also say "if KEY is the name of a pass, then [..]". Overall maybe:

in the form KEY@VALUE. If KEY is the name of a pass then it applies to the closest instance of that pass before us. If KEY is not the name of a pass then it is a global option that applies to all pass instances that read it (later appearances of such global options override earlier ones).

Copy link
Author

Choose a reason for hiding this comment

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

I like you suggestion better than mine and adopted it, although I removed the last part in parenthesis as the text already is quite long, especially when formatted into a column.

Copy link
Member

Choose a reason for hiding this comment

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

Looks good!

@kripken
Copy link
Member

kripken commented Jul 8, 2024

Oh, also please add something to CHANGELOG.md

@DirtyHairy
Copy link
Author

Oh, also please add something to CHANGELOG.md

Done!

@@ -38,6 +38,12 @@ Current Trunk
- The build-time option to use legacy WasmGC opcodes is removed.
- The strings in `string.const` instructions must now be valid WTF-8.
- The `TraverseCalls` flag for `ExpressionRunner` is removed.
- Passes can now be specified several times on the command line. The "main"
Copy link
Member

Choose a reason for hiding this comment

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

Passes could already be specified more than once - what changes here is that each such instance can get its own argument?

How about

Passes can now receive individual pass arguments, that is --foo=A --foo=B for a pass foo will run the pass twice (which was possible before) and will now run it first with argument A and second with B. --pass-arg=foo@BAR will now apply to the most recent --foo pass on the commandline, if foo is a pass (while global pass arguments - that are not the name of a pass - remain, as before, global for all passes).

@kripken
Copy link
Member

kripken commented Jul 10, 2024

I started the tests here. I think for them to pass you need to update the help text outputs, which you can do by running ./scripts/update_help_checks.py and committing that output.

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.

None yet

2 participants