Skip to content
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/passes/Precompute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,70 @@ struct Precompute
}
}

void visitBlock(Block* curr) {
// When block precomputation fails, it can lead to quadratic slowness due to
// the "tower of blocks" pattern used to implement switches:
//
// (block
// (block
// ...
// (block
// (br_table ..
//
// If we try to precompute each block here, and fail on each, then we end up
// doing quadratic work. This is also wasted work as once a nested block
// fails to precompute there is not really a chance to succeed on the
// parent. If we do *not* fail to precompute, however, then we do want to
// precompute such nested blocks, e.g.:
//
// (block $out
// (block
// (br $out)
// )
// )
//
// Here we *can* precompute the inner block, so when we get to the outer one
// we see this:
//
// (block $out
// (br $out)
// )
//
// And that precomputes to nothing. Therefore when we see a child of the
// block that is another block (it failed to precompute to something
// simpler) then we leave early here.
//
// Note that in theory we could still precompute here if wasm had
// instructions that allow such things, e.g.:
//
// (block $out
// (block
// (cause side effect)
// (br $out)
// )
// (undo that side effect exactly, and nothing more)
Copy link
Member

Choose a reason for hiding this comment

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

This br would skip over the undo of the side effects, so I don't think this example makes sense.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, fixed.

// )
//
// In this situation we can remove the outer block and everything inside it,
// because it has no side effects at all, when seen as a whole (but not when
// considering the inner block by itself). However, wasm does not atm have
// such instructions: side effects like locals would persist outside of the
// block. In theory we could set a local and unset it before leaving the
// block, but tracking that level of effects is not something that this pass
// does.
if (!curr->list.empty() && curr->list[0]->is<Block>()) {
// The first child is a block, that is, it could not be simplified, so
// this looks like the "tower of blocks" pattern. Avoid quadratic time
// here as explained above. (We could also look at other children of the
// block, but the only real-world pattern identified so far is on the
// first child, so keep things simple here.)
return;
}

// Otherwise, precompute normally like all other expressions.
visitExpression(curr);
}

// If we failed to precompute a constant, perhaps we can still precompute part
// of an expression. Specifically, consider this case:
//
Expand Down