diff options
author | Alon Zakai <azakai@google.com> | 2023-05-16 11:03:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-16 11:03:45 -0700 |
commit | 972e659bf59740c3ee44129812f95bec143d01a6 (patch) | |
tree | f86d70fa692a45e3dfbf951b0d1af06204d4ecf7 /test/lit/merge/fusing.wat | |
parent | 44cd751d9feda7c4b4b6c9d6af1e71541b90abac (diff) | |
download | binaryen-972e659bf59740c3ee44129812f95bec143d01a6.tar.gz binaryen-972e659bf59740c3ee44129812f95bec143d01a6.tar.bz2 binaryen-972e659bf59740c3ee44129812f95bec143d01a6.zip |
Reintroduce wasm-merge (#5709)
We used to have a wasm-merge tool but removed it for a lack of use cases. Recently
use cases have been showing up in the wasm GC space and elsewhere, as people are
using more diverse toolchains together, for example a project might build some C++
code alongside some wasm GC code. Merging those wasm files together can allow
for nice optimizations like inlining and better DCE etc., so it makes sense to have a
tool for merging.
Background:
* Removal: #1969
* Requests:
* wasm-merge - why it has been deleted #2174
* Compiling and linking wat files #2276
* wasm-link? #2767
This PR is a compete rewrite of wasm-merge, not a restoration of the original
codebase. The original code was quite messy (my fault), and also, since then
we've added multi-memory and multi-table which makes things a lot simpler.
The linking semantics are as described in the "wasm-link" issue #2767 : all we do
is merge normal wasm files together and connect imports and export. That is, we
have a graph of modules and their names, and each import to a module name can
be resolved to that module. Basically, like a JS bundler would do for JS, or, in other
words, we do the same operations as JS code would do to glue wasm modules
together at runtime, but at compile time. See the README update in this PR for a
concrete example.
There are no plans to do more than that simple bundling, so this should not
really overlap with wasm-ld's use cases.
This should be fairly fast as it works in linear time on the total input code. However,
it won't be as fast as wasm-ld, of course, as it does build Binaryen IR for each
module. An advantage to working on Binaryen IR is that we can easily do some
global DCE after merging, and further optimizations are possible later.
Diffstat (limited to 'test/lit/merge/fusing.wat')
-rw-r--r-- | test/lit/merge/fusing.wat | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/test/lit/merge/fusing.wat b/test/lit/merge/fusing.wat new file mode 100644 index 000000000..645634b4d --- /dev/null +++ b/test/lit/merge/fusing.wat @@ -0,0 +1,94 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-merge %s first %s.second second --rename-export-conflicts -all -S -o - | filecheck %s + +;; Test that we fuse imports to exports across modules. +;; +;; We test functions and memories here, and not every possible entity in a +;; comprehensive way, since they all go through the same code path. (But we test +;; two to at least verify we differentiate them.) + +(module + ;; The first two imports here will be resolved to direct calls into the + ;; second module's merged contents. + (import "second" "foo" (func $other.foo)) + + (import "second" "bar" (func $other.bar)) + + (import "second" "mem" (memory $other.mem 1)) + + ;; This import will remain unresolved. + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $none_=>_i32 (func (result i32))) + + ;; CHECK: (import "third" "missing" (func $other.missing)) + (import "third" "missing" (func $other.missing)) + + ;; CHECK: (memory $second.mem 2) + + ;; CHECK: (export "foo" (func $first.foo)) + + ;; CHECK: (export "bar" (func $bar)) + + ;; CHECK: (export "keepalive" (func $keepalive)) + + ;; CHECK: (export "mem" (memory $second.mem)) + + ;; CHECK: (export "foo_4" (func $second.foo)) + + ;; CHECK: (export "bar_5" (func $bar_6)) + + ;; CHECK: (func $first.foo (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $second.foo) + ;; CHECK-NEXT: ) + (func $first.foo (export "foo") + (drop + (i32.const 1) + ) + (call $other.foo) + ) + + ;; CHECK: (func $bar (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar_6) + ;; CHECK-NEXT: (call $other.missing) + ;; CHECK-NEXT: ) + (func $bar (export "bar") + (drop + (i32.const 2) + ) + (call $other.bar) + (call $other.missing) + ) + + ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32) + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $keepalive (export "keepalive") (result i32) + ;; Load from the memory imported from the second module. + (i32.load $other.mem + (i32.const 10) + ) + ) +) +;; CHECK: (func $second.foo (type $none_=>_none) +;; CHECK-NEXT: (call $first.foo) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $bar_6 (type $none_=>_none) +;; CHECK-NEXT: (call $bar) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (i32.const 4) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) |