-
Notifications
You must be signed in to change notification settings - Fork 787
Simplify BinaryenIRWriter #3110
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
Simplify BinaryenIRWriter #3110
Conversation
BinaryenIRWriter was previously inconsistent about whether or not it emitted an instruction if that instruction was not reachable. Instructions that produced values were not emitted if they were unreachable, but instructions that did not produce values were always emitted. Additionally, blocks continued to emit their children even after emitting an unreachable child. Since it was not possible to tell whether an unreachable instruction's parent would be emitted, BinaryenIRWriter had to be very defensive and emit many extra `unreachable` instructions around unreachable code to avoid type errors. This PR unifies the logic for emitting all non-control flow instructions and changes the behavior of BinaryenIRWriter so that it never emits instructions that cannot be reached due to having unreachable children. This means that extra `unreachable` instructions now only need to be emitted after unreachable control flow constructs. BinaryenIRWriter now also stops emitting instructions inside blocks after the first unreachable instruction as an extra optimization. This change will also simplify Poppy IR stackification (see WebAssembly#3059) by guaranteeing that instructions with unreachable children will not be emitted into the stackifier. This makes satisfying the Poppy IR rule against unreachable Pops trivial, whereas previously satisfying this rule would have required about about 700 additional lines of code to recompute the types of all unreachable children for any instruction.
So far this has done 7000 iterations on the fuzzer, but I will leave it running over night. |
Alright, I just stopped the fuzzer at 67892 iterations, so I'd say this is stable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
Two things though:
- Does this have a performance impact? Using an Iterator instead of a direct walk may be slower. Worth measuring.
- Might be good to run the emscripten test suite before landing (maybe at least
other, wasm0, wasm2
).
src/ir/iteration.h
Outdated
if (Properties::isControlFlowStructure(curr)) { | ||
// If conditions are the only stack children of control flow structures | ||
if (auto* if_ = curr->dynCast<If>()) { | ||
self->pushTask(SubType::scan, &if_->condition); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we use iff
for this type of thing in more places
// similar to in visitBlock, here we could skip emitting the block itself, | ||
// but must still end the 'block' (the contents, really) with an unreachable | ||
emitUnreachable(); | ||
if (child->type == Type::unreachable) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a comment here about why we do this and why it is valid? specifically that this is enough because an unreachable block must have an unreachable child.
src/ir/iteration.h
Outdated
class ChildIterator { | ||
// ChildIterator - Iterates over all children | ||
// | ||
// StackChildIterator - Iterates over all children that produce values used by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Stack" seems not clear enough to me. If this is just for Poppy IR then "Poppy" (or "Stacky" if we decided on that name?). But that doesn't seem clear enough either. How about PoppedChildIterator
- indicating that these are children that are popped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't particular to Poppy IR, and I'm concerned that having "Popped" in the name might give the wrong impression. Maybe ValueChildIterator
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, maybe "value child" vs "structural child" or such is good terminology. +1 for ValueChildIterator
I checked the performance of
So we are taking a 10% hit on our baseline parse + emit performance. This is not great, but I think it is worth it for the simplicity wins. I don't feel strongly about that, though, and I would be willing to make the same behavioral change without unifying all the logic. Let me know what you think. Also, the emscripten tests passed. |
(What's the SmallVector change? In the Iterator?) I think 10% might be acceptable here. We are moving to make unoptimized builds not run binaryen at all, and in optimized builds binary writing is pretty small compared to optimization work. But might be worth a TODO to look into optimizing this more, as in theory the iterator could be as fast as a walk. |
Yeah, sorry, I should have explained the SmallVector change. That's with a What we really need is a kind of |
Late to the review, but it is really a nice simplifying change! |
This test seems to be added in WebAssembly#2266 to test custom unreachable generation in `BinaryenIRWriter`, but given that the `fromBinary` files only contain a single `unreaachable` for the whole function, I don't think this tests serves a lot of purpose. Also the custom unreachable generation logic in WebAssembly#2266 was largely replaced in WebAssembly#3110.
This test seems to be added in WebAssembly#2266 to test custom unreachable generation in `BinaryenIRWriter`, but given that the `fromBinary` files only contain a single `unreaachable` for the whole function, I don't think this test serves a lot of purpose. Also the custom unreachable generation logic in WebAssembly#2266 was largely replaced in WebAssembly#3110.
This test seems to be added in WebAssembly#2266 to test custom unreachable generation in `BinaryenIRWriter`, but given that the `fromBinary` files only contain a single `unreaachable` for the whole function, I don't think this test serves a lot of purpose. Also the custom unreachable generation logic in WebAssembly#2266 was largely replaced in WebAssembly#3110.
This test seems to be added in #2266 to test custom unreachable generation in `BinaryenIRWriter`, but given that the `fromBinary` files only contain a single `unreachable` for the whole function, I don't think this test serves a lot of purpose. Also the custom unreachable generation logic in #2266 was largely replaced in #3110.
BinaryenIRWriter was previously inconsistent about whether or not it
emitted an instruction if that instruction was not reachable.
Instructions that produced values were not emitted if they were
unreachable, but instructions that did not produce values were always
emitted. Additionally, blocks continued to emit their children even
after emitting an unreachable child.
Since it was not possible to tell whether an unreachable instruction's
parent would be emitted, BinaryenIRWriter had to be very defensive and
emit many extra
unreachable
instructions around unreachable code toavoid type errors.
This PR unifies the logic for emitting all non-control flow
instructions and changes the behavior of BinaryenIRWriter so that it
never emits instructions that cannot be reached due to having
unreachable children. This means that extra
unreachable
instructionsnow only need to be emitted after unreachable control flow
constructs. BinaryenIRWriter now also stops emitting instructions
inside blocks after the first unreachable instruction as an extra
optimization.
This change will also simplify Poppy IR stackification (see #3059) by
guaranteeing that instructions with unreachable children will not be
emitted into the stackifier. This makes satisfying the Poppy IR rule
against unreachable Pops trivial, whereas previously satisfying this
rule would have required about about 700 additional lines of code to
recompute the types of all unreachable children for any instruction.