Skip to content

Conversation

@tlively
Copy link
Member

@tlively tlively commented Dec 13, 2024

Since multivalue was standardized, WebAssembly has supported not only
multiple results but also an arbitrary number of inputs on control flow
structures, but until now Binaryen did not support control flow input.
Binaryen IR still has no way to represent control flow input, so lower
it away using scratch locals in IRBuilder. Since both the text and
binary parsers use IRBuilder, this gives us full support for parsing
control flow inputs.

The lowering scheme is mostly simple. A local.set writing the control
flow inputs to a scratch local is inserted immediately before the
control flow structure begins and a local.get retrieving those inputs is
inserted inside the control flow structure before the rest of its body.
The only complications come from ifs, in which the inputs must be
retrieved at the beginning of both arms, and from loops, where branches
to the beginning of the loop must be transformed so their values are
written to the scratch local along the way.

Resolves #6407.

Since multivalue was standardized, WebAssembly has supported not only
multiple results but also an arbitrary number of inputs on control flow
structures, but until now Binaryen did not support control flow input.
Binaryen IR still has no way to represent control flow input, so lower
it away using scratch locals in IRBuilder. Since both the text and
binary parsers use IRBuilder, this gives us full support for parsing
control flow inputs.

The lowering scheme is mostly simple. A local.set writing the control
flow inputs to a scratch local is inserted immediately before the
control flow structure begins and a local.get retrieving those inputs is
inserted inside the control flow structure before the rest of its body.
The only complications come from ifs, in which the inputs must be
retrieved at the beginning of both arms, and from loops, where branches
to the beginning of the loop must be transformed so their values are
written to the scratch local along the way.

Resolves #6407.
@tlively tlively requested a review from kripken December 13, 2024 18:56

bool getBasicType(int32_t code, Type& out);
bool getBasicHeapType(int64_t code, HeapType& out);
// Get the signature of control flow structure.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Get the signature of control flow structure.
// Get the signature of a control flow structure.


- BinaryenSelect no longer takes a type parameter.
- AutoDrop APIs have been removed.
- Binaryen now supports parsing control flow structures with parameter types.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- Binaryen now supports parsing control flow structures with parameter types.
- Binaryen now supports parsing control flow structures with parameter types
(this is not fully optimized, however, and is therefore not recommended).

Copy link
Member Author

Choose a reason for hiding this comment

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

I would expect it to make basically no difference after optimizations, so I'm not sure we need to be this conservative in the changelog note. If producers can get simpler unoptimized modules by using control flow inputs and it basically makes no change to their optimized output, then it's a still a net win.

Copy link
Member

Choose a reason for hiding this comment

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

True, yeah, so my wording wasn't good. But still, I can imagine someone does a bunch of work to use inputs to control flow, and expect Binaryen to preserve that benefit, which seems surprising when it doesn't. Maybe a note that we do not emit such shapes?

Copy link
Member Author

Choose a reason for hiding this comment

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

I expanded the note to say that we lower the input parameters away, which I think is the important information here.

Copy link
Member

Choose a reason for hiding this comment

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

sgtm though I don't see that pushed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, my bad. Will fix.

}
WASM_UNREACHABLE("unexpected scope kind");
}
bool isDelimiter() { return getElse() || getCatch() || getCatchAll(); }
Copy link
Member

Choose a reason for hiding this comment

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

is Delegate not a delimiter? (if not, what does that term mean here?)

Copy link
Member Author

Choose a reason for hiding this comment

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

By delimiter here, I mean a separator between different sections of a control flow structure with multiple bodies. Delegate does not introduce a new control flow body, so it is more like an extra fancy end.

// Normally an if without an else must have type none, but if there is an
// input parameter, the empty else arm must propagate its value.
// Synthesize an else arm that loads the value from the scratch local.
iff->ifFalse = builder.makeLocalGet(scope.inputLocal, scope.inputType);
Copy link
Member

Choose a reason for hiding this comment

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

I don't follow this. There is an input type, but this makes the second arm have an output type. Can this arm simply not read the scratch local?

Copy link
Member Author

Choose a reason for hiding this comment

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

Any if without an else must have equivalent input and output types.

In the specified WebAssembly semantics, there's actually no such thing as an if without an else arm. There are only ifs with empty else arms instead. It might be worth looking into removing the ability for ifFalse arms to be null in Binaryen IR; that might be a significant simplification.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see... right, if the input isn't read in an arm, it has to flow out.

Copy link
Member

Choose a reason for hiding this comment

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

About disallowing null in our IR, interesting idea. It would have some memory usage downsides though. I don't have a good guess if it would help enough to justify that.

tlively added a commit that referenced this pull request Dec 13, 2024
Now that #7149 added support for parsing block parameters, we can run
additional spec tests that previously failed.
@kripken
Copy link
Member

kripken commented Dec 13, 2024

(lgtm aside from open discussions)

@tlively tlively merged commit 8a88ba2 into main Dec 13, 2024
13 checks passed
@tlively tlively deleted the control-flow-inputs branch December 13, 2024 21:23
tlively added a commit that referenced this pull request Dec 13, 2024
The PR was accidentally merged without these fixes included.
tlively added a commit that referenced this pull request Dec 13, 2024
The PR was accidentally merged without these fixes included.
tlively added a commit that referenced this pull request Dec 16, 2024
Now that #7149 added support for parsing block parameters, we can run
additional spec tests that previously failed.
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.

Support block-type parameters

3 participants