Skip to content

Commit 613fadc

Browse files
authored
Avoid emitting a block in the binary format when it has no name (#4912)
We already did this if the block was a child of a control flow structure, which is the common case (see the new added comment around that code, which clarifies why). This does the same for all other blocks. This is simple to do and a minor optimization, but the main benefit from this is just to make our handling of blocks uniform: after this, we never emit a block with no name. This will make 1a non- nullable locals easier to handle (since they will be able to assume that property; and not emitting such blocks avoids some work to handle non-nullable locals in them).
1 parent 2d86d1f commit 613fadc

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

src/wasm-stack.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,21 @@ template<typename SubType> void BinaryenIRWriter<SubType>::write() {
200200
emitFunctionEnd();
201201
}
202202

203-
// emits a node, but if it is a block with no name, emit a list of its contents
203+
// Emits a node in a position that can contain a list of contents, like an if
204+
// arm. This will emit the node, but if it is a block with no name, just emit
205+
// its contents. This is ok to do because a list of contents is ok in the wasm
206+
// binary format in such positions anyhow. When we read such code in Binaryen
207+
// we will end up creating a block for it (note that while doing so we create a
208+
// block without a name, since nothing branches to it, which makes it easy to
209+
// handle in optimization passes and when writing the binary out again).
204210
template<typename SubType>
205211
void BinaryenIRWriter<SubType>::visitPossibleBlockContents(Expression* curr) {
206212
auto* block = curr->dynCast<Block>();
213+
// Even if the block has a name, check if the name is necessary (if it has no
214+
// uses, it is equivalent to not having one). Scanning the children of the
215+
// block means that this takes quadratic time, but it will be N^2 for a fairly
216+
// small N since the number of nested non-block control flow structures tends
217+
// to be very reasonable.
207218
if (!block || BranchUtils::BranchSeeker::has(block, block->name)) {
208219
visit(curr);
209220
return;
@@ -264,6 +275,26 @@ void BinaryenIRWriter<SubType>::visitBlock(Block* curr) {
264275
}
265276
};
266277

278+
// A block with no name never needs to be emitted: we can just emit its
279+
// contents. In some cases that will end up as "stacky" code, which is valid
280+
// in wasm but not in Binaryen IR. This is similar to what we do in
281+
// visitPossibleBlockContents(), and like there, when we reload such a binary
282+
// we'll end up creating a block for it then.
283+
//
284+
// Note that in visitPossibleBlockContents() we also optimize the case of a
285+
// block with a name but the name actually has no uses - that handles more
286+
// cases, but it requires more work. It is reasonable to do it in
287+
// visitPossibleBlockContents() which handles the common cases of blocks that
288+
// are children of control flow structures (like an if arm); doing it here
289+
// would affect every block, including highly-nested block stacks, which would
290+
// end up as quadratic time. In optimized code the name will not exist if it's
291+
// not used anyhow, so a minor optimization for the unoptimized case that
292+
// leads to potential quadratic behavior is not worth it here.
293+
if (!curr->name.is()) {
294+
visitChildren(curr, 0);
295+
return;
296+
}
297+
267298
auto afterChildren = [this](Block* curr) {
268299
emitScopeEnd(curr);
269300
if (curr->type == Type::unreachable) {

test/example/c-api-unused-mem.txt

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
(call $main)
3737
)
3838
)
39-
145
39+
133
4040
(module
4141
(type $none_=>_none (func))
4242
(memory $0 1024 1024)
@@ -53,20 +53,13 @@
5353
(i32.const 0)
5454
)
5555
)
56-
(block $label$2
57-
(br $label$1)
58-
)
56+
(br $label$1)
5957
)
60-
(block $label$3
61-
(block $label$4
62-
(i32.store
63-
(i32.const 0)
64-
(local.get $0)
65-
)
66-
(return)
67-
)
68-
(unreachable)
58+
(i32.store
59+
(i32.const 0)
60+
(local.get $0)
6961
)
62+
(return)
7063
)
7164
(func $__wasm_start
7265
(i32.store

0 commit comments

Comments
 (0)