summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-05-16 11:03:45 -0700
committerGitHub <noreply@github.com>2023-05-16 11:03:45 -0700
commit972e659bf59740c3ee44129812f95bec143d01a6 (patch)
treef86d70fa692a45e3dfbf951b0d1af06204d4ecf7 /test
parent44cd751d9feda7c4b4b6c9d6af1e71541b90abac (diff)
downloadbinaryen-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')
-rw-r--r--test/lit/help/wasm-merge.test149
-rw-r--r--test/lit/merge/cycle.wat84
-rw-r--r--test/lit/merge/cycle.wat.second24
-rw-r--r--test/lit/merge/cycle.wat.third24
-rw-r--r--test/lit/merge/export_options.wat59
-rw-r--r--test/lit/merge/export_options.wat.second15
-rw-r--r--test/lit/merge/export_options_default.wat15
-rw-r--r--test/lit/merge/export_options_default.wat.second7
-rw-r--r--test/lit/merge/fusing.wat94
-rw-r--r--test/lit/merge/fusing.wat.second28
-rw-r--r--test/lit/merge/memory_data.wat39
-rw-r--r--test/lit/merge/memory_data.wat.second13
-rw-r--r--test/lit/merge/renamings.wat350
-rw-r--r--test/lit/merge/renamings.wat.second123
-rw-r--r--test/lit/merge/start.flip.wat30
-rw-r--r--test/lit/merge/start.flip.wat.second8
-rw-r--r--test/lit/merge/start.wat33
-rw-r--r--test/lit/merge/start.wat.second15
-rw-r--r--test/lit/merge/start3.wat36
-rw-r--r--test/lit/merge/start3.wat.second16
-rw-r--r--test/lit/merge/start3.wat.third9
-rw-r--r--test/lit/merge/table_elem.wat114
-rw-r--r--test/lit/merge/table_elem.wat.second37
23 files changed, 1322 insertions, 0 deletions
diff --git a/test/lit/help/wasm-merge.test b/test/lit/help/wasm-merge.test
new file mode 100644
index 000000000..541f37d5a
--- /dev/null
+++ b/test/lit/help/wasm-merge.test
@@ -0,0 +1,149 @@
+;; RUN: wasm-merge --help | filecheck %s
+;; CHECK: ================================================================================
+;; CHECK-NEXT: wasm-merge INFILE1 NAME1 INFILE2 NAME2 [..]
+;; CHECK-NEXT:
+;; CHECK-NEXT: Merge wasm files into one.
+;; CHECK-NEXT:
+;; CHECK-NEXT: For example,
+;; CHECK-NEXT:
+;; CHECK-NEXT: wasm-merge foo.wasm foo bar.wasm bar -o merged.wasm
+;; CHECK-NEXT:
+;; CHECK-NEXT: will read foo.wasm and bar.wasm, with names 'foo' and 'bar' respectively, so if
+;; CHECK-NEXT: the second imports from 'foo', we will see that as an import from the first
+;; CHECK-NEXT: module after the merge. The merged output will be written to merged.wasm.
+;; CHECK-NEXT:
+;; CHECK-NEXT: Note that filenames and modules names are interleaved (which is hopefully less
+;; CHECK-NEXT: confusing).
+;; CHECK-NEXT: ================================================================================
+;; CHECK-NEXT:
+;; CHECK-NEXT:
+;; CHECK-NEXT: wasm-merge options:
+;; CHECK-NEXT: -------------------
+;; CHECK-NEXT:
+;; CHECK-NEXT: --output,-o Output file (stdout if not specified)
+;; CHECK-NEXT:
+;; CHECK-NEXT: --rename-export-conflicts,-rec Rename exports to avoid conflicts (rather
+;; CHECK-NEXT: than error)
+;; CHECK-NEXT:
+;; CHECK-NEXT: --skip-export-conflicts,-sec Skip exports that conflict with previous
+;; CHECK-NEXT: ones
+;; CHECK-NEXT:
+;; CHECK-NEXT: --emit-text,-S Emit text instead of binary for the
+;; CHECK-NEXT: output file
+;; CHECK-NEXT:
+;; CHECK-NEXT: --debuginfo,-g Emit names section and debug info
+;; CHECK-NEXT:
+;; CHECK-NEXT:
+;; CHECK-NEXT: Tool options:
+;; CHECK-NEXT: -------------
+;; CHECK-NEXT:
+;; CHECK-NEXT: --mvp-features,-mvp Disable all non-MVP features
+;; CHECK-NEXT:
+;; CHECK-NEXT: --all-features,-all Enable all features
+;; CHECK-NEXT:
+;; CHECK-NEXT: --detect-features (deprecated - this flag does nothing)
+;; CHECK-NEXT:
+;; CHECK-NEXT: --quiet,-q Emit less verbose output and hide trivial
+;; CHECK-NEXT: warnings.
+;; CHECK-NEXT:
+;; CHECK-NEXT: --experimental-poppy Parse wast files as Poppy IR for testing
+;; CHECK-NEXT: purposes.
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-sign-ext Enable sign extension operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-sign-ext Disable sign extension operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-threads Enable atomic operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-threads Disable atomic operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-mutable-globals Enable mutable globals
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-mutable-globals Disable mutable globals
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-nontrapping-float-to-int Enable nontrapping float-to-int
+;; CHECK-NEXT: operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-nontrapping-float-to-int Disable nontrapping float-to-int
+;; CHECK-NEXT: operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-simd Enable SIMD operations and types
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-simd Disable SIMD operations and types
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-bulk-memory Enable bulk memory operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-bulk-memory Disable bulk memory operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-exception-handling Enable exception handling operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-exception-handling Disable exception handling operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-tail-call Enable tail call operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-tail-call Disable tail call operations
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-reference-types Enable reference types
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-reference-types Disable reference types
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-multivalue Enable multivalue functions
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-multivalue Disable multivalue functions
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-gc Enable garbage collection
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-gc Disable garbage collection
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-memory64 Enable memory64
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-memory64 Disable memory64
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-gc-nn-locals Enable GC non-null locals
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-gc-nn-locals Disable GC non-null locals
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-relaxed-simd Enable relaxed SIMD
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-relaxed-simd Disable relaxed SIMD
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-extended-const Enable extended const expressions
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-extended-const Disable extended const expressions
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-strings Enable strings
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-strings Disable strings
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-multi-memories Enable multi-memories
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-multi-memories Disable multi-memories
+;; CHECK-NEXT:
+;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag
+;; CHECK-NEXT:
+;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag
+;; CHECK-NEXT:
+;; CHECK-NEXT: --no-validation,-n Disables validation, assumes inputs are
+;; CHECK-NEXT: correct
+;; CHECK-NEXT:
+;; CHECK-NEXT: --pass-arg,-pa An argument passed along to optimization
+;; CHECK-NEXT: passes being run. Must be in the form
+;; CHECK-NEXT: KEY@VALUE
+;; CHECK-NEXT:
+;; CHECK-NEXT: --closed-world,-cw Assume code outside of the module does
+;; CHECK-NEXT: not inspect or interact with GC and
+;; CHECK-NEXT: function references, even if they are
+;; CHECK-NEXT: passed out. The outside may hold on to
+;; CHECK-NEXT: them and pass them back in, but not
+;; CHECK-NEXT: inspect their contents or call them.
+;; CHECK-NEXT:
+;; CHECK-NEXT:
+;; CHECK-NEXT: General options:
+;; CHECK-NEXT: ----------------
+;; CHECK-NEXT:
+;; CHECK-NEXT: --version Output version information and exit
+;; CHECK-NEXT:
+;; CHECK-NEXT: --help,-h Show this help message and exit
+;; CHECK-NEXT:
+;; CHECK-NEXT: --debug,-d Print debug information to stderr
+;; CHECK-NEXT:
diff --git a/test/lit/merge/cycle.wat b/test/lit/merge/cycle.wat
new file mode 100644
index 000000000..9862b3355
--- /dev/null
+++ b/test/lit/merge/cycle.wat
@@ -0,0 +1,84 @@
+;; 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 %s.third third --rename-export-conflicts -all -S -o - | filecheck %s
+
+;; Test a cycle of imports: the first module imports from the second, which
+;; imports from the third, and we have a reverse cycle as well.
+
+(module
+ (import "second" "forward" (func $second.forward))
+
+ (import "second" "reverse" (func $second.reverse))
+
+ (import "third" "forward" (func $third.forward))
+
+ (import "third" "reverse" (func $third.reverse))
+
+
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (export "forward" (func $forward))
+
+ ;; CHECK: (export "reverse" (func $reverse))
+
+ ;; CHECK: (export "forward_2" (func $forward_6))
+
+ ;; CHECK: (export "reverse_3" (func $reverse_6))
+
+ ;; CHECK: (export "forward_4" (func $forward_12))
+
+ ;; CHECK: (export "reverse_5" (func $reverse_12))
+
+ ;; CHECK: (func $forward (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $forward_6)
+ ;; CHECK-NEXT: )
+ (func $forward (export "forward")
+ (drop
+ (i32.const 1)
+ )
+ (call $second.forward)
+ )
+
+ ;; CHECK: (func $reverse (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const -1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $reverse_12)
+ ;; CHECK-NEXT: )
+ (func $reverse (export "reverse")
+ (drop
+ (i32.const -1)
+ )
+ (call $third.reverse)
+ )
+)
+;; CHECK: (func $forward_6 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (call $forward_12)
+;; CHECK-NEXT: )
+
+;; CHECK: (func $reverse_6 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const -2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (call $reverse)
+;; CHECK-NEXT: )
+
+;; CHECK: (func $forward_12 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (call $forward)
+;; CHECK-NEXT: )
+
+;; CHECK: (func $reverse_12 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const -3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (call $reverse_6)
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/cycle.wat.second b/test/lit/merge/cycle.wat.second
new file mode 100644
index 000000000..b110043b4
--- /dev/null
+++ b/test/lit/merge/cycle.wat.second
@@ -0,0 +1,24 @@
+(module
+ (import "first" "forward" (func $first.forward))
+
+ (import "first" "reverse" (func $first.reverse))
+
+ (import "third" "forward" (func $third.forward))
+
+ (import "third" "reverse" (func $third.reverse))
+
+ (func $forward (export "forward")
+ (drop
+ (i32.const 2)
+ )
+ (call $third.forward)
+ )
+
+ (func $reverse (export "reverse")
+ (drop
+ (i32.const -2)
+ )
+ (call $first.reverse)
+ )
+)
+
diff --git a/test/lit/merge/cycle.wat.third b/test/lit/merge/cycle.wat.third
new file mode 100644
index 000000000..03cc08f04
--- /dev/null
+++ b/test/lit/merge/cycle.wat.third
@@ -0,0 +1,24 @@
+(module
+ (import "first" "forward" (func $first.forward))
+
+ (import "first" "reverse" (func $first.reverse))
+
+ (import "second" "forward" (func $second.forward))
+
+ (import "second" "reverse" (func $second.reverse))
+
+ (func $forward (export "forward")
+ (drop
+ (i32.const 3)
+ )
+ (call $first.forward)
+ )
+
+ (func $reverse (export "reverse")
+ (drop
+ (i32.const -3)
+ )
+ (call $second.reverse)
+ )
+)
+
diff --git a/test/lit/merge/export_options.wat b/test/lit/merge/export_options.wat
new file mode 100644
index 000000000..26bc9d382
--- /dev/null
+++ b/test/lit/merge/export_options.wat
@@ -0,0 +1,59 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; If asked to, we rename the conflicts. The second "func" export will become
+;; "func_1".
+;; RUN: wasm-merge %s first %s.second second --rename-export-conflicts -S -o - | filecheck %s --check-prefix RENAME
+
+;; If asked to, we can skip conflicting exports from later modules. The second
+;; "func" export will not exist.
+;; RUN: wasm-merge %s first %s.second second --skip-export-conflicts -S -o - | filecheck %s --check-prefix SKIP_C
+
+(module
+ ;; RENAME: (type $none_=>_none (func))
+
+ ;; RENAME: (export "func" (func $func0))
+
+ ;; RENAME: (export "func_1" (func $func1))
+
+ ;; RENAME: (export "other" (func $func2))
+
+ ;; RENAME: (func $func0
+ ;; RENAME-NEXT: (drop
+ ;; RENAME-NEXT: (i32.const 0)
+ ;; RENAME-NEXT: )
+ ;; RENAME-NEXT: )
+ ;; SKIP_C: (type $none_=>_none (func))
+
+ ;; SKIP_C: (export "func" (func $func0))
+
+ ;; SKIP_C: (export "other" (func $func2))
+
+ ;; SKIP_C: (func $func0
+ ;; SKIP_C-NEXT: (drop
+ ;; SKIP_C-NEXT: (i32.const 0)
+ ;; SKIP_C-NEXT: )
+ ;; SKIP_C-NEXT: )
+ (func $func0 (export "func")
+ ;; This export also appears in the second module.
+ (drop
+ (i32.const 0)
+ )
+ )
+)
+;; RENAME: (func $func1
+;; RENAME-NEXT: (drop
+;; RENAME-NEXT: (i32.const 1)
+;; RENAME-NEXT: )
+;; RENAME-NEXT: )
+
+;; RENAME: (func $func2
+;; RENAME-NEXT: (drop
+;; RENAME-NEXT: (i32.const 2)
+;; RENAME-NEXT: )
+;; RENAME-NEXT: )
+
+;; SKIP_C: (func $func2
+;; SKIP_C-NEXT: (drop
+;; SKIP_C-NEXT: (i32.const 2)
+;; SKIP_C-NEXT: )
+;; SKIP_C-NEXT: )
diff --git a/test/lit/merge/export_options.wat.second b/test/lit/merge/export_options.wat.second
new file mode 100644
index 000000000..adcc24bd2
--- /dev/null
+++ b/test/lit/merge/export_options.wat.second
@@ -0,0 +1,15 @@
+(module
+ (func $func1 (export "func")
+ ;; This export will conflict.
+ (drop
+ (i32.const 1)
+ )
+ )
+
+ (func $func2 (export "other")
+ ;; This export will not conflict.
+ (drop
+ (i32.const 2)
+ )
+ )
+)
diff --git a/test/lit/merge/export_options_default.wat b/test/lit/merge/export_options_default.wat
new file mode 100644
index 000000000..bb1dea30e
--- /dev/null
+++ b/test/lit/merge/export_options_default.wat
@@ -0,0 +1,15 @@
+;; By default we error on an export name conflict.
+;; (This is the same as export_options.wat, but a "not" test and a test with
+;; automatic updates cannot be in the same file.)
+
+;; RUN: not wasm-merge %s first %s.second second 2>&1 | filecheck %s
+;; CHECK: Fatal: Export name conflict: func (consider --rename-export-conflicts or --skip-export-conflicts)
+
+(module
+ (func $func0 (export "func")
+ ;; This export also appears in the second module.
+ (drop
+ (i32.const 0)
+ )
+ )
+)
diff --git a/test/lit/merge/export_options_default.wat.second b/test/lit/merge/export_options_default.wat.second
new file mode 100644
index 000000000..6e045c7dd
--- /dev/null
+++ b/test/lit/merge/export_options_default.wat.second
@@ -0,0 +1,7 @@
+(module
+ (func $func1 (export "func")
+ (drop
+ (i32.const 1)
+ )
+ )
+)
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: )
diff --git a/test/lit/merge/fusing.wat.second b/test/lit/merge/fusing.wat.second
new file mode 100644
index 000000000..387e57bb4
--- /dev/null
+++ b/test/lit/merge/fusing.wat.second
@@ -0,0 +1,28 @@
+(module
+ ;; Use the same internal name as in first, so $other.foo will need to be
+ ;; deduplicated.
+ (import "first" "foo" (func $other.foo))
+
+ ;; Use a different prefix than in first ($main instead of $other).
+ (import "first" "bar" (func $main.bar))
+
+ (memory $second.mem 2)
+
+ (export "mem" (memory $second.mem))
+
+ (func $second.foo (export "foo")
+ (call $other.foo)
+ (drop
+ (i32.const 3)
+ )
+ )
+
+ ;; Use the same internal name as in first, so this will need to be
+ ;; deduplicated.
+ (func $bar (export "bar")
+ (call $main.bar)
+ (drop
+ (i32.const 4)
+ )
+ )
+)
diff --git a/test/lit/merge/memory_data.wat b/test/lit/merge/memory_data.wat
new file mode 100644
index 000000000..d796dc322
--- /dev/null
+++ b/test/lit/merge/memory_data.wat
@@ -0,0 +1,39 @@
+;; 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 we rename memories and data segments properly at the module scope.
+;; Memory $bar has a name collision, and both of the element segments' names.
+;; This test verifies that data segments refer to the right tables even after
+;; such name changes.
+
+(module
+ ;; CHECK: (memory $foo 1)
+ (memory $foo 1)
+
+ ;; CHECK: (memory $bar 10)
+ (memory $bar 10)
+
+ ;; CHECK: (memory $other 100)
+
+ ;; CHECK: (memory $bar_2 1000)
+
+ ;; CHECK: (data $a (i32.const 0) "a")
+ (data $a (memory $foo) (i32.const 0) "a")
+
+ ;; CHECK: (data $b (memory $bar) (i32.const 0) "b")
+ (data $b (memory $bar) (i32.const 0) "b")
+
+ ;; CHECK: (data $a_2 (memory $other) (i32.const 0) "a2")
+
+ ;; CHECK: (data $b_2 (memory $bar_2) (i32.const 0) "b2")
+
+ ;; CHECK: (export "keepalive" (memory $foo))
+ (export "keepalive" (memory $foo))
+
+ ;; CHECK: (export "keepalive1" (memory $bar))
+ (export "keepalive1" (memory $bar))
+)
+;; CHECK: (export "keepalive_2" (memory $other))
+
+;; CHECK: (export "keepalive1_3" (memory $bar_2))
diff --git a/test/lit/merge/memory_data.wat.second b/test/lit/merge/memory_data.wat.second
new file mode 100644
index 000000000..0ea34dbcd
--- /dev/null
+++ b/test/lit/merge/memory_data.wat.second
@@ -0,0 +1,13 @@
+(module
+ (memory $other 100)
+
+ (memory $bar 1000)
+
+ (data $a (memory $other) (i32.const 0) "a2")
+
+ (data $b (memory $bar) (i32.const 0) "b2")
+
+ (export "keepalive" (memory $other))
+
+ (export "keepalive1" (memory $bar))
+)
diff --git a/test/lit/merge/renamings.wat b/test/lit/merge/renamings.wat
new file mode 100644
index 000000000..fd01803f2
--- /dev/null
+++ b/test/lit/merge/renamings.wat
@@ -0,0 +1,350 @@
+;; 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 rename items in the second module to avoid name collisions.
+
+(module
+ ;; CHECK: (type $array (array (mut funcref)))
+ (type $array (array (mut (ref null func))))
+
+ ;; This tag has a conflict in second.wat, and so second.wat's $foo
+ ;; will be renamed.
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $ref|$array|_=>_none (func (param (ref $array))))
+
+ ;; CHECK: (type $i32_=>_none (func (param i32)))
+
+ ;; CHECK: (type $i64_=>_none (func (param i64)))
+
+ ;; CHECK: (type $f32_=>_none (func (param f32)))
+
+ ;; CHECK: (type $f64_=>_none (func (param f64)))
+
+ ;; CHECK: (global $foo i32 (i32.const 1))
+ (global $foo i32 (i32.const 1))
+
+ ;; This global has a conflict in second.wat, and so second.wat's $bar
+ ;; will be renamed.
+ ;; CHECK: (global $bar i32 (i32.const 2))
+ (global $bar i32 (i32.const 2))
+
+ ;; This memory has a conflict in second.wat, and so second.wat's $foo
+ ;; will be renamed.
+ ;; CHECK: (global $other i32 (i32.const 3))
+
+ ;; CHECK: (global $bar_2 i32 (i32.const 4))
+
+ ;; CHECK: (memory $foo 10 20)
+ (memory $foo 10 20)
+
+ ;; CHECK: (memory $bar 30 40)
+ (memory $bar 30 40)
+
+ ;; CHECK: (memory $foo_2 50 60)
+
+ ;; CHECK: (memory $other 70 80)
+
+ ;; CHECK: (data $foo (i32.const 1) "abc")
+ (data $foo (i32.const 1) "abc")
+
+ ;; This data segment has a conflict in second.wat, and so second.wat's $bar
+ ;; will be renamed.
+ ;; CHECK: (data $bar (i32.const 2) "def")
+ (data $bar (i32.const 2) "def")
+
+ ;; This table has a conflict in second.wat, and so second.wat's $foo
+ ;; will be renamed.
+ ;; CHECK: (data $other (memory $foo_2) (i32.const 3) "ghi")
+
+ ;; CHECK: (data $bar_2 (memory $foo_2) (i32.const 4) "jkl")
+
+ ;; CHECK: (table $foo 10 20 funcref)
+ (table $foo 10 20 funcref)
+
+ ;; CHECK: (table $bar 30 40 funcref)
+ (table $bar 30 40 funcref)
+
+ ;; CHECK: (table $foo_2 50 60 funcref)
+
+ ;; CHECK: (table $other 70 80 funcref)
+
+ ;; CHECK: (elem $foo func $foo $bar)
+ (elem $foo (ref null func) $foo $bar)
+
+ ;; This elem has a conflict in second.wat, and so second.wat's $bar
+ ;; will be renamed.
+ ;; CHECK: (elem $bar func $bar $foo)
+ (elem $bar (ref null func) $bar $foo)
+
+ ;; CHECK: (elem $other func $foo_3 $other)
+
+ ;; CHECK: (elem $bar_2 func $other $foo_3)
+
+ ;; CHECK: (tag $foo (param i32))
+ (tag $foo (param i32))
+
+ ;; CHECK: (tag $bar (param i64))
+ (tag $bar (param i64))
+
+ ;; This export has a conflict in second.wat, and so second.wat's $foo
+ ;; will be renamed.
+ ;; CHECK: (tag $foo_2 (param f32))
+
+ ;; CHECK: (tag $other (param f64))
+
+ ;; CHECK: (export "foo" (func $foo))
+ (export "foo" (func $foo))
+
+ ;; CHECK: (export "bar" (func $bar))
+ (export "bar" (func $bar))
+
+ ;; CHECK: (export "keepalive" (func $uses))
+ (export "keepalive" (func $uses))
+
+ ;; CHECK: (export "foo_3" (func $foo_3))
+
+ ;; CHECK: (export "other" (func $other))
+
+ ;; CHECK: (export "keepalive_5" (func $uses.second))
+
+ ;; CHECK: (export "other-b" (func $other))
+
+ ;; CHECK: (func $foo (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo
+ ;; This function has a conflict in second.wat, and so second.wat's $foo
+ ;; will be renamed.
+ (drop
+ (i32.const 1)
+ )
+ )
+
+ ;; CHECK: (func $bar (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $bar
+ (drop
+ (i32.const 2)
+ )
+ )
+
+ ;; CHECK: (func $uses (type $ref|$array|_=>_none) (param $array (ref $array))
+ ;; CHECK-NEXT: (try $try
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $foo
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (pop i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (try $try0
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $bar
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (pop i64)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.load $foo
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.load $bar
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (data.drop $foo)
+ ;; CHECK-NEXT: (data.drop $bar)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (table.get $foo
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (table.get $bar
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (array.init_elem $array $foo
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (array.init_elem $array $bar
+ ;; CHECK-NEXT: (local.get $array)
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: (i32.const 5)
+ ;; CHECK-NEXT: (i32.const 6)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (global.get $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $bar)
+ ;; CHECK-NEXT: )
+ (func $uses (param $array (ref $array))
+ ;; Tags.
+ (try
+ (do)
+ (catch $foo
+ (drop
+ (pop i32)
+ )
+ )
+ )
+ (try
+ (do)
+ (catch $bar
+ (drop
+ (pop i64)
+ )
+ )
+ )
+
+ ;; Memories
+ (drop
+ (i32.load $foo
+ (i32.const 1)
+ )
+ )
+ (drop
+ (i32.load $bar
+ (i32.const 2)
+ )
+ )
+
+ ;; Data segments
+ (data.drop $foo)
+ (data.drop $bar)
+
+ ;; Tables
+ (drop
+ (table.get $foo
+ (i32.const 1)
+ )
+ )
+ (drop
+ (table.get $bar
+ (i32.const 2)
+ )
+ )
+
+ ;; Element segments
+ (array.init_elem $array $foo
+ (local.get $array)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ )
+ (array.init_elem $array $bar
+ (local.get $array)
+ (i32.const 4)
+ (i32.const 5)
+ (i32.const 6)
+ )
+
+ ;; Globals
+ (drop
+ (global.get $foo)
+ )
+ (drop
+ (global.get $bar)
+ )
+
+ ;; Functions.
+ (call $foo)
+ (call $bar)
+ )
+)
+;; CHECK: (func $foo_3 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $other (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 4)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $uses.second (type $ref|$array|_=>_none) (param $array (ref $array))
+;; CHECK-NEXT: (try $try
+;; CHECK-NEXT: (do
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (catch $foo_2
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (pop f32)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (try $try0
+;; CHECK-NEXT: (do
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (catch $other
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (pop f64)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.load $foo_2
+;; CHECK-NEXT: (i32.const 3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.load $other
+;; CHECK-NEXT: (i32.const 4)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (data.drop $other)
+;; CHECK-NEXT: (data.drop $bar_2)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $foo_2
+;; CHECK-NEXT: (i32.const 3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $other
+;; CHECK-NEXT: (i32.const 4)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (array.init_elem $array $other
+;; CHECK-NEXT: (local.get $array)
+;; CHECK-NEXT: (i32.const 7)
+;; CHECK-NEXT: (i32.const 8)
+;; CHECK-NEXT: (i32.const 9)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (array.init_elem $array $bar_2
+;; CHECK-NEXT: (local.get $array)
+;; CHECK-NEXT: (i32.const 10)
+;; CHECK-NEXT: (i32.const 11)
+;; CHECK-NEXT: (i32.const 12)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (global.get $other)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (global.get $bar_2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (call $foo_3)
+;; CHECK-NEXT: (call $other)
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/renamings.wat.second b/test/lit/merge/renamings.wat.second
new file mode 100644
index 000000000..da02e0438
--- /dev/null
+++ b/test/lit/merge/renamings.wat.second
@@ -0,0 +1,123 @@
+(module
+ (type $array (array (mut (ref null func))))
+
+ (tag $foo (param f32))
+
+ (tag $other (param f64))
+
+ (memory $foo 50 60)
+
+ (memory $other 70 80)
+
+ (data $other (i32.const 3) "ghi")
+
+ (data $bar (i32.const 4) "jkl")
+
+ (table $foo 50 60 funcref)
+
+ (table $other 70 80 funcref)
+
+ (elem $other (ref null func) $foo $other)
+
+ (elem $bar (ref null func) $other $foo)
+
+ (global $other i32 (i32.const 3))
+
+ (global $bar i32 (i32.const 4))
+
+ (export "foo" (func $foo))
+
+ (export "other" (func $other))
+
+ (export "keepalive" (func $uses.second))
+
+ ;; Also test having a different name for the export as for the internal
+ ;; thing it refers to, to test we don't assume they are identical.
+ (export "other-b" (func $other))
+
+ (func $foo
+ (drop
+ (i32.const 3)
+ )
+ )
+
+ (func $other
+ (drop
+ (i32.const 4)
+ )
+ )
+
+ (func $uses.second (param $array (ref $array))
+ ;; Tags.
+ (try
+ (do)
+ (catch $foo
+ (drop
+ (pop f32)
+ )
+ )
+ )
+ (try
+ (do)
+ (catch $other
+ (drop
+ (pop f64)
+ )
+ )
+ )
+
+ ;; Memories
+ (drop
+ (i32.load $foo
+ (i32.const 3)
+ )
+ )
+ (drop
+ (i32.load $other
+ (i32.const 4)
+ )
+ )
+
+ ;; Data segments
+ (data.drop $other)
+ (data.drop $bar)
+
+ ;; Tables
+ (drop
+ (table.get $foo
+ (i32.const 3)
+ )
+ )
+ (drop
+ (table.get $other
+ (i32.const 4)
+ )
+ )
+
+ ;; Element segments
+ (array.init_elem $array $other
+ (local.get $array)
+ (i32.const 7)
+ (i32.const 8)
+ (i32.const 9)
+ )
+ (array.init_elem $array $bar
+ (local.get $array)
+ (i32.const 10)
+ (i32.const 11)
+ (i32.const 12)
+ )
+
+ ;; Globals
+ (drop
+ (global.get $other)
+ )
+ (drop
+ (global.get $bar)
+ )
+
+ ;; Functions.
+ (call $foo)
+ (call $other)
+ )
+)
diff --git a/test/lit/merge/start.flip.wat b/test/lit/merge/start.flip.wat
new file mode 100644
index 000000000..cf7d93ca5
--- /dev/null
+++ b/test/lit/merge/start.flip.wat
@@ -0,0 +1,30 @@
+;; 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
+
+;; Like start, but flipped - now only the first module has a start.
+
+(module
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (export "start" (func $start_1))
+
+ ;; CHECK: (start $start)
+ (start $start)
+
+ ;; CHECK: (func $start (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $start
+ (drop
+ (i32.const 0)
+ )
+ )
+)
+
+;; CHECK: (func $start_1 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/start.flip.wat.second b/test/lit/merge/start.flip.wat.second
new file mode 100644
index 000000000..89dcf4f97
--- /dev/null
+++ b/test/lit/merge/start.flip.wat.second
@@ -0,0 +1,8 @@
+(module
+ (func $start (export "start")
+ ;; Not a start function, but the name overlaps so it will get deduplicated.
+ (drop
+ (i32.const 1)
+ )
+ )
+)
diff --git a/test/lit/merge/start.wat b/test/lit/merge/start.wat
new file mode 100644
index 000000000..19a9bcb91
--- /dev/null
+++ b/test/lit/merge/start.wat
@@ -0,0 +1,33 @@
+;; 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 merge start functions. The first module here has none, but the
+;; second does, so we'll refer to its start function in the merged module.
+
+(module
+ (func $start
+ ;; This function has the name start, but is *not* the start function. The
+ ;; other module's start will need to get a new deduplicated name.
+ (drop
+ (i32.const 0)
+ )
+ )
+)
+;; CHECK: (type $none_=>_none (func))
+
+;; CHECK: (export "start" (func $start_1))
+
+;; CHECK: (export "user" (func $user))
+
+;; CHECK: (start $start_1)
+
+;; CHECK: (func $start_1 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $user (type $none_=>_none)
+;; CHECK-NEXT: (call $start_1)
+;; CHECK-NEXT: (call $start_1)
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/start.wat.second b/test/lit/merge/start.wat.second
new file mode 100644
index 000000000..0f00feb7a
--- /dev/null
+++ b/test/lit/merge/start.wat.second
@@ -0,0 +1,15 @@
+(module
+ (start $start)
+
+ (func $start (export "start")
+ (drop
+ (i32.const 1)
+ )
+ )
+
+ (func $user (export "user")
+ ;; These calls must go to the function $start here (with body "1").
+ (call $start)
+ (call $start)
+ )
+)
diff --git a/test/lit/merge/start3.wat b/test/lit/merge/start3.wat
new file mode 100644
index 000000000..ab4c555ab
--- /dev/null
+++ b/test/lit/merge/start3.wat
@@ -0,0 +1,36 @@
+;; 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 %s.third third -all -S -o - | filecheck %s
+
+;; Test that we merge start functions. The first module here has none, but the
+;; second and third do, so we'll first copy in the second's and then merge in
+;; the third's.
+
+(module
+)
+;; CHECK: (type $none_=>_none (func))
+
+;; CHECK: (export "start" (func $start))
+
+;; CHECK: (export "user" (func $user))
+
+;; CHECK: (start $merged.start)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $user (type $none_=>_none)
+;; CHECK-NEXT: (call $start)
+;; CHECK-NEXT: (call $start)
+;; CHECK-NEXT: )
+
+;; CHECK: (func $merged.start (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (i32.const 2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/start3.wat.second b/test/lit/merge/start3.wat.second
new file mode 100644
index 000000000..ec66c2f5b
--- /dev/null
+++ b/test/lit/merge/start3.wat.second
@@ -0,0 +1,16 @@
+(module
+ (start $start)
+
+ (func $start (export "start")
+ (drop
+ (i32.const 1)
+ )
+ )
+
+ (func $user (export "user")
+ ;; These calls must go to the function $start here (with body "1") and not
+ ;; to the modified start that has the third module's content merged in.
+ (call $start)
+ (call $start)
+ )
+)
diff --git a/test/lit/merge/start3.wat.third b/test/lit/merge/start3.wat.third
new file mode 100644
index 000000000..508ffdb2a
--- /dev/null
+++ b/test/lit/merge/start3.wat.third
@@ -0,0 +1,9 @@
+(module
+ (start $start)
+
+ (func $start
+ (drop
+ (i32.const 2)
+ )
+ )
+)
diff --git a/test/lit/merge/table_elem.wat b/test/lit/merge/table_elem.wat
new file mode 100644
index 000000000..3fec4d865
--- /dev/null
+++ b/test/lit/merge/table_elem.wat
@@ -0,0 +1,114 @@
+;; 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 we rename tables and element segments properly at the module scope.
+;; Table $foo has a name collision, and both of the element segments' names do
+;; as well. This test verifies that element segments refer to the right tables
+;; even after such name changes.
+
+(module
+ ;; CHECK: (type $vec (array funcref))
+ (type $vec (array funcref))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (table $foo 1 funcref)
+ (table $foo 1 funcref)
+
+ ;; CHECK: (table $bar 10 funcref)
+ (table $bar 10 funcref)
+
+ ;; CHECK: (table $foo_2 100 funcref)
+
+ ;; CHECK: (table $other 1000 funcref)
+
+ ;; CHECK: (elem $a (table $foo) (i32.const 0) func)
+ (elem $a (table $foo) (i32.const 0) func)
+
+ ;; CHECK: (elem $b (table $bar) (i32.const 0) func)
+ (elem $b (table $bar) (i32.const 0) func)
+
+ (func "keepalive2"
+ (drop
+ (table.get $foo
+ (i32.const 1)
+ )
+ )
+ (drop
+ (table.get $bar
+ (i32.const 1)
+ )
+ )
+ ;; GC operations are the only things that can keep alive an elem segment.
+ (drop
+ (array.new_elem $vec $a
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (drop
+ (array.new_elem $vec $b
+ (i32.const 3)
+ (i32.const 4)
+ )
+ )
+ )
+)
+;; CHECK: (elem $a_2 (table $foo_2) (i32.const 0) func)
+
+;; CHECK: (elem $b_2 (table $other) (i32.const 0) func)
+
+;; CHECK: (export "keepalive2" (func $0))
+
+;; CHECK: (export "keepalive2_1" (func $0_1))
+
+;; CHECK: (func $0 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $foo
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $bar
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (array.new_elem $vec $a
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: (i32.const 2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (array.new_elem $vec $b
+;; CHECK-NEXT: (i32.const 3)
+;; CHECK-NEXT: (i32.const 4)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $0_1 (type $none_=>_none)
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $foo_2
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (table.get $other
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (array.new_elem $vec $a_2
+;; CHECK-NEXT: (i32.const 5)
+;; CHECK-NEXT: (i32.const 6)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (array.new_elem $vec $b_2
+;; CHECK-NEXT: (i32.const 7)
+;; CHECK-NEXT: (i32.const 8)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
diff --git a/test/lit/merge/table_elem.wat.second b/test/lit/merge/table_elem.wat.second
new file mode 100644
index 000000000..7aa960c7f
--- /dev/null
+++ b/test/lit/merge/table_elem.wat.second
@@ -0,0 +1,37 @@
+(module
+ (type $vec (array funcref))
+
+ (table $foo 100 funcref)
+
+ (table $other 1000 funcref)
+
+ (elem $a (table $foo) (i32.const 0) func)
+
+ (elem $b (table $other) (i32.const 0) func)
+
+ (func "keepalive2"
+ (drop
+ (table.get $foo
+ (i32.const 1)
+ )
+ )
+ (drop
+ (table.get $other
+ (i32.const 1)
+ )
+ )
+ ;; GC operations are the only things that can keep alive an elem segment.
+ (drop
+ (array.new_elem $vec $a
+ (i32.const 5)
+ (i32.const 6)
+ )
+ )
+ (drop
+ (array.new_elem $vec $b
+ (i32.const 7)
+ (i32.const 8)
+ )
+ )
+ )
+)