-
Notifications
You must be signed in to change notification settings - Fork 792
Track indirect call types in RemoveUnusedModuleElements #7728
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
Conversation
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.
What's the difference between note
/ use
/ reference
?
I think I understand what use
and reference
are... use
means we use it so we have to preserve it, while reference
means we may or may not use it but its name is referenced somewhere so we at least have to keep its shell (even if we empty out the contents). Not sure what note
is... Can note
possibly be merged with use
or reference
?
;; CHECK: (type $B (sub (func (param f64)))) | ||
(type $B (sub (func (param f64)))) |
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.
Any reason we don't have (type $C
spelled out as well?
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.
Hmm, it validates without it (it is defined implicitly), so I didn't think there was a need? Also I guess it adds coverage for implicitly-defined types.
@@ -12768,13 +12768,6 @@ function asmFunc(imports) { | |||
return $1_1 | 0; | |||
} | |||
|
|||
function f($0_1, $1_1, $2_1) { |
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.
Is this change for something else?
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.
No, this is an optimization unlocked by this PR. It is defined here:
binaryen/test/wasm2js/br_table_temp.wast
Line 971 in 76ab43f
(func $f (param i32 i32 i32) (result i32) (i32.const -1)) |
It has a few direct calls, but they get optimized out in this optimized test output. Previously, I guess it remained alive because of this table reference, when we can now see has no corresponding call_indirect,
binaryen/test/wasm2js/br_table_temp.wast
Line 995 in 76ab43f
(table funcref (elem $f)) |
|
Co-authored-by: Heejin Ahn <[email protected]>
Co-authored-by: Heejin Ahn <[email protected]>
The only thing those binaryen/src/passes/RemoveUnusedModuleElements.cpp Lines 83 to 90 in bb7b7a1
And here in binaryen/src/passes/RemoveUnusedModuleElements.cpp Lines 286 to 297 in bb7b7a1
Then can't we just replace those |
Hmm, good point, but I'd actually prefer to rename the latter, so |
|
Makes sense. I agree it's good to try to merge where possible. Here, I think it is clearer to separate module elements which can be used and referenced, from specific things that need special processing. I added a few comments for that now, and renamed to |
Does the PR now currently contain the the merging? It doesn't seem to, so.. By the way I think this looks good and don't want to hold up the landing for this. That can be done later as a follow-up (or not). |
Sorry, I am saying that I don't think we can merge in this case. The terms are
So I would prefer not to apply the terms |
What I asked was, like, for example, we currently have this: binaryen/src/passes/RemoveUnusedModuleElements.cpp Lines 154 to 164 in 7489b27
I was wondering if we can just do this. So "merging" was maybe not the correct term after all... void visitCallIndirect(CallIndirect* curr) {
reference({ModuleElementKind::Table, curr->table});
useIndirectCall({curr->table, curr->heapType});
useRefFunc(curr->heaptype);
} because this is what we do anyway in But while |
@aheejin Ok, fair enough, maybe I was making things more complicated by trying to use those terms in different ways. I merged things now, avoiding |
I mean, what I asked was whether we were able to remove those previous |
Oh, sorry, I guess I misread you then. Maybe we can merge this actually. I thought the idea was to eagerly scan in parallel, then combine on a single thread, but reading the code now, it looks like it works entirely on a single-thread, but lazily. That is, the separation between the I'm not sure if it would be faster the other way, but it might be worth checking. |
…7748) wasm-metadce does a graph analysis to find unreached things, and then cleans up using RemoveUnusedModuleElements. That pass become more powerful in #7728, which led to a situation where an import was removed from the wasm, but wasm-metadce did not report that it had removed it. This led to unneeded code in the JS (it kept sending that import, unnecessarily). This was a harmless minor waste of JS size, but it did cause a test error on Emscripten (#7747), as it parses that JS to check some things, and it found an import in JS without a use in wasm. To fix that, check if that pass removed imports, and report them.
#24783) Followup to https://github.com/emscripten-core/emscripten/pull/24777/files#diff-59f6b346090f49748193fef565b0c7e223f93ecef5bb116fa370d0b864af9493 after WebAssembly/binaryen#7748 landed. Code size savings are from WebAssembly/binaryen#7728
An indirect call to a type in a table now only forces functions of
that type to be marked as used: functions of other types are
left alone, potentially leaving them unreached.
This is more precise than assuming any indirect call can
reach anywhere, which is more or less what we did before.
There is a downside to this: the pass is around 10% slower. This is one
of our faster passes, so this may be acceptable, however.
This has some benefits, here is the Emscripten diff:
Example
diff --git a/test/code_size/embind_val_wasm.json b/test/code_size/embind_val_wasm.json index 939ad737b3..1c8b8a1546 100644 --- a/test/code_size/embind_val_wasm.json +++ b/test/code_size/embind_val_wasm.json @@ -1,10 +1,10 @@ { "a.html": 552, "a.html.gz": 373, "a.js": 5356, "a.js.gz": 2526, - "a.wasm": 7468, - "a.wasm.gz": 3461, - "total": 13376, - "total_gz": 6360 + "a.wasm": 5831, + "a.wasm.gz": 2713, + "total": 11739, + "total_gz": 5612 } diff --git a/test/code_size/random_printf_wasm.json b/test/code_size/random_printf_wasm.json index 89da22d7c8..9685b59d93 100644 --- a/test/code_size/random_printf_wasm.json +++ b/test/code_size/random_printf_wasm.json @@ -1,6 +1,6 @@ { - "a.html": 12511, - "a.html.gz": 6848, - "total": 12511, - "total_gz": 6848 + "a.html": 12507, + "a.html.gz": 6822, + "total": 12507, + "total_gz": 6822 } diff --git a/test/code_size/random_printf_wasm2js.json b/test/code_size/random_printf_wasm2js.json index 5b21705c95..7d168dbd6a 100644 --- a/test/code_size/random_printf_wasm2js.json +++ b/test/code_size/random_printf_wasm2js.json @@ -1,6 +1,6 @@ { - "a.html": 17224, - "a.html.gz": 7551, - "total": 17224, - "total_gz": 7551 + "a.html": 17229, + "a.html.gz": 7542, + "total": 17229, + "total_gz": 7542 } diff --git a/test/other/codesize/test_codesize_files_wasmfs.size b/test/other/codesize/test_codesize_files_wasmfs.size index 82b16397a9..20191f896a 100644 --- a/test/other/codesize/test_codesize_files_wasmfs.size +++ b/test/other/codesize/test_codesize_files_wasmfs.size @@ -1 +1 @@ -50314 +50233 diff --git a/test/other/codesize/test_codesize_hello_O3.size b/test/other/codesize/test_codesize_hello_O3.size index b0539e90d9..b339887848 100644 --- a/test/other/codesize/test_codesize_hello_O3.size +++ b/test/other/codesize/test_codesize_hello_O3.size @@ -1 +1 @@ -1735 +1733 diff --git a/test/other/codesize/test_codesize_hello_Os.size b/test/other/codesize/test_codesize_hello_Os.size index 1c38c9071a..9b5f360cc2 100644 --- a/test/other/codesize/test_codesize_hello_Os.size +++ b/test/other/codesize/test_codesize_hello_Os.size @@ -1 +1 @@ -1725 +1723 diff --git a/test/other/codesize/test_codesize_hello_Oz.size b/test/other/codesize/test_codesize_hello_Oz.size index 771034cb6a..6bbc2a3cd4 100644 --- a/test/other/codesize/test_codesize_hello_Oz.size +++ b/test/other/codesize/test_codesize_hello_Oz.size @@ -1 +1 @@ -1259 +1257 diff --git a/test/other/codesize/test_codesize_hello_single_file.jssize b/test/other/codesize/test_codesize_hello_single_file.jssize index 4cd877762a..8755c7be20 100644 --- a/test/other/codesize/test_codesize_hello_single_file.jssize +++ b/test/other/codesize/test_codesize_hello_single_file.jssize @@ -1 +1 @@ -6615 +6611 diff --git a/test/other/codesize/test_codesize_hello_wasmfs.size b/test/other/codesize/test_codesize_hello_wasmfs.size index b0539e90d9..b339887848 100644 --- a/test/other/codesize/test_codesize_hello_wasmfs.size +++ b/test/other/codesize/test_codesize_hello_wasmfs.size @@ -1 +1 @@ -1735 +1733 diff --git a/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.funcs b/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.funcs index 86fd2dc144..7f12daaeba 100644 --- a/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.funcs +++ b/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.funcs @@ -1,2 +1 @@ -$__wasm_call_ctors $_start diff --git a/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.size b/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.size index 7296f257eb..94361d49fd 100644 --- a/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.size +++ b/test/other/codesize/test_codesize_libcxxabi_message_O3_standalone.size @@ -1 +1 @@ -136 +132 diff --git a/test/other/codesize/test_codesize_mem_O3_grow_standalone.funcs b/test/other/codesize/test_codesize_mem_O3_grow_standalone.funcs index 8a606d1279..19dd45693e 100644 --- a/test/other/codesize/test_codesize_mem_O3_grow_standalone.funcs +++ b/test/other/codesize/test_codesize_mem_O3_grow_standalone.funcs @@ -1,3 +1,2 @@ -$__wasm_call_ctors $_start $sbrk diff --git a/test/other/codesize/test_codesize_mem_O3_grow_standalone.size b/test/other/codesize/test_codesize_mem_O3_grow_standalone.size index ab5b9efed7..848ef7c501 100644 --- a/test/other/codesize/test_codesize_mem_O3_grow_standalone.size +++ b/test/other/codesize/test_codesize_mem_O3_grow_standalone.size @@ -1 +1 @@ -5553 +5549 diff --git a/test/other/codesize/test_codesize_mem_O3_standalone.funcs b/test/other/codesize/test_codesize_mem_O3_standalone.funcs index 8a606d1279..19dd45693e 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone.funcs +++ b/test/other/codesize/test_codesize_mem_O3_standalone.funcs @@ -1,3 +1,2 @@ -$__wasm_call_ctors $_start $sbrk diff --git a/test/other/codesize/test_codesize_mem_O3_standalone.size b/test/other/codesize/test_codesize_mem_O3_standalone.size index 7bcda5ba23..7e9732ae43 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone.size +++ b/test/other/codesize/test_codesize_mem_O3_standalone.size @@ -1 +1 @@ -5478 +5474 diff --git a/test/other/codesize/test_codesize_mem_O3_standalone_narg.funcs b/test/other/codesize/test_codesize_mem_O3_standalone_narg.funcs index 8a606d1279..19dd45693e 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone_narg.funcs +++ b/test/other/codesize/test_codesize_mem_O3_standalone_narg.funcs @@ -1,3 +1,2 @@ -$__wasm_call_ctors $_start $sbrk diff --git a/test/other/codesize/test_codesize_mem_O3_standalone_narg.size b/test/other/codesize/test_codesize_mem_O3_standalone_narg.size index 05112f24d5..b54c900141 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone_narg.size +++ b/test/other/codesize/test_codesize_mem_O3_standalone_narg.size @@ -1 +1 @@ -5271 +5267 diff --git a/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.funcs b/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.funcs index 8a606d1279..19dd45693e 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.funcs +++ b/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.funcs @@ -1,3 +1,2 @@ -$__wasm_call_ctors $_start $sbrk diff --git a/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.size b/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.size index 603c2df295..bbdd8cef02 100644 --- a/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.size +++ b/test/other/codesize/test_codesize_mem_O3_standalone_narg_flto.size @@ -1 +1 @@ -4084 +4080
One lucky embind test shrinks by 20%, but all other changes are just
a few bytes, far less than 1%. I looked at real-world codebases, and
see no real benefit there. My hunch is that this is expected because of
signature overlap: when you generate random graphs of size
n
andchance for each edge to exist
p
, then even ifp
decreases to 0 thegraph will tend to end up fully connected [1]. And, in wasm,
p
does not even decrease to 0:
{i32} -> {}
(i32 param, no result).q>0
for that signature to be called,and some chance
r>0
for that signature to exist in the code.p >= O(rq) > 0
because all it takes for a connection to exist is that thatsignature exists on one side and is called on the other.
That is, in large codebases there is an overlap in signatures, and
statistically this means that all the code will end up reachable, at
least in the limit. In small programs you may get lucky, but not in
the long run. And even in the mid run, you will quickly see weird
stuff like a game engine's physics code seeming to be able to call
networking or audio (impossible in general, but they can overlap
on signatures).
To really fix that we need more than structural typing of indirect
calls, something like knowing the possible targets at each callsite.
Devirtualization can provide this, based on source language info.
Still, this PR may be of some benefit in some cases.
[1] https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model#Properties_of_G(n,_p)