diff options
Diffstat (limited to 'test')
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) + ) + ) + ) +) |