summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/help/wasm-opt.test3
-rw-r--r--test/lit/help/wasm2js.test3
-rw-r--r--test/lit/passes/signature-pruning.wast597
3 files changed, 603 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index caf6437fc..d786bfdfc 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -346,6 +346,9 @@
;; CHECK-NEXT: --set-globals sets specified globals to
;; CHECK-NEXT: specified values
;; CHECK-NEXT:
+;; CHECK-NEXT: --signature-pruning remove params from function
+;; CHECK-NEXT: signature types where possible
+;; CHECK-NEXT:
;; CHECK-NEXT: --signature-refining apply more specific subtypes to
;; CHECK-NEXT: signature types where possible
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index ab169a1a3..504e08726 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -308,6 +308,9 @@
;; CHECK-NEXT: --set-globals sets specified globals to
;; CHECK-NEXT: specified values
;; CHECK-NEXT:
+;; CHECK-NEXT: --signature-pruning remove params from function
+;; CHECK-NEXT: signature types where possible
+;; CHECK-NEXT:
;; CHECK-NEXT: --signature-refining apply more specific subtypes to
;; CHECK-NEXT: signature types where possible
;; CHECK-NEXT:
diff --git a/test/lit/passes/signature-pruning.wast b/test/lit/passes/signature-pruning.wast
new file mode 100644
index 000000000..86a26130c
--- /dev/null
+++ b/test/lit/passes/signature-pruning.wast
@@ -0,0 +1,597 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --nominal --signature-pruning -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32 f64) func))
+ (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $sig) (param $0 i32) (param $1 f64)
+ ;; CHECK-NEXT: (local $2 f32)
+ ;; CHECK-NEXT: (local $3 i64)
+ ;; CHECK-NEXT: (i32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f64.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32) (param $i64 i64) (param $f32 f32) (param $f64 f64)
+ ;; Use the first and last parameter. The middle parameters will be removed
+ ;; both from the function and from $sig, and also in the calls below.
+ (i32.store
+ (i32.const 0)
+ (local.get $i32)
+ )
+ (f64.store
+ (i32.const 0)
+ (local.get $f64)
+ )
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (f64.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: (f64.const 7)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ (i64.const 1)
+ (f32.const 2)
+ (f64.const 3)
+ )
+ (call_ref
+ (i32.const 4)
+ (i64.const 5)
+ (f32.const 6)
+ (f64.const 7)
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype (param i64 f32) func))
+ (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $sig) (param $0 i64) (param $1 f32)
+ ;; CHECK-NEXT: (local $2 f64)
+ ;; CHECK-NEXT: (local $3 i32)
+ ;; CHECK-NEXT: (i64.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32) (param $i64 i64) (param $f32 f32) (param $f64 f64)
+ ;; Use the middle two parameters.
+ (i64.store
+ (i32.const 0)
+ (local.get $i64)
+ )
+ (f32.store
+ (i32.const 0)
+ (local.get $f32)
+ )
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: (f32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (i64.const 5)
+ ;; CHECK-NEXT: (f32.const 6)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ (i64.const 1)
+ (f32.const 2)
+ (f64.const 3)
+ )
+ (call_ref
+ (i32.const 4)
+ (i64.const 5)
+ (f32.const 6)
+ (f64.const 7)
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32 i64 f32) func))
+ (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $sig) (param $0 i32) (param $1 i64) (param $2 f32)
+ ;; CHECK-NEXT: (local $3 f64)
+ ;; CHECK-NEXT: (i64.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32) (param $i64 i64) (param $f32 f32) (param $f64 f64)
+ ;; Use the middle two parameters.
+ (i64.store
+ (i32.const 0)
+ (local.get $i64)
+ )
+ (f32.store
+ (i32.const 0)
+ (local.get $f32)
+ )
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (block $block (result i32)
+ ;; CHECK-NEXT: (call $caller)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: (f32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: (i64.const 5)
+ ;; CHECK-NEXT: (f32.const 6)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ ;; As above, but now one of the unused parameters has a side effect which
+ ;; prevents us from removing it (flattening the IR first would avoid this
+ ;; limitation). We only end up removing a single unused param, the last.
+ (call $foo
+ (block (result i32)
+ (call $caller)
+ (i32.const 0)
+ )
+ (i64.const 1)
+ (f32.const 2)
+ (f64.const 3)
+ )
+ (call_ref
+ (i32.const 4)
+ (i64.const 5)
+ (f32.const 6)
+ (f64.const 7)
+ (ref.func $foo)
+ )
+ )
+)
+
+;; As above, but with the effects on a call_ref. Once more, we can only optimize
+;; away the very last param.
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32 i64 f32) func))
+ (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $sig) (param $0 i32) (param $1 i64) (param $2 f32)
+ ;; CHECK-NEXT: (local $3 f64)
+ ;; CHECK-NEXT: (i64.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (f32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32) (param $i64 i64) (param $f32 f32) (param $f64 f64)
+ (i64.store
+ (i32.const 0)
+ (local.get $i64)
+ )
+ (f32.store
+ (i32.const 0)
+ (local.get $f32)
+ )
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i64.const 1)
+ ;; CHECK-NEXT: (f32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (block $block (result i32)
+ ;; CHECK-NEXT: (call $caller)
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i64.const 5)
+ ;; CHECK-NEXT: (f32.const 6)
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ (i64.const 1)
+ (f32.const 2)
+ (f64.const 3)
+ )
+ (call_ref
+ (block (result i32)
+ (call $caller)
+ (i32.const 4)
+ )
+ (i64.const 5)
+ (f32.const 6)
+ (f64.const 7)
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype func))
+ (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $foo)
+
+ ;; CHECK: (func $foo (type $sig)
+ ;; CHECK-NEXT: (local $0 f64)
+ ;; CHECK-NEXT: (local $1 f32)
+ ;; CHECK-NEXT: (local $2 i64)
+ ;; CHECK-NEXT: (local $3 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32) (param $i64 i64) (param $f32 f32) (param $f64 f64)
+ ;; Use nothing at all: all params can be removed.
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ (i64.const 1)
+ (f32.const 2)
+ (f64.const 3)
+ )
+ (call_ref
+ (i32.const 4)
+ (i64.const 5)
+ (f32.const 6)
+ (f64.const 7)
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype func))
+ (type $sig (func_subtype (param i32) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (func $foo (type $sig)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ ;; Use the parameters' index, but not its value. We can still remove it,
+ ;; and the value set in the function is then set to a local and not a param,
+ ;; which works just as well.
+ (local.set $i32
+ (i32.const 1)
+ )
+ (i32.store
+ (i32.const 0)
+ (local.get $i32)
+ )
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype func))
+ (type $sig (func_subtype (param i32) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (func $foo (type $sig)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ ;; This function does not use the parameter. It also has no calls, but that
+ ;; is not a problem - we can still remove the parameter.
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32) func))
+ (type $sig (func_subtype (param i32) func))
+
+ ;; As above, but now an import also uses this signature, which prevents us
+ ;; from changing anything.
+ ;; CHECK: (import "out" "func" (func $import (param i32)))
+ (import "out" "func" (func $import (type $sig) (param i32)))
+
+ (memory 1 1)
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (func $foo (type $sig) (param $i32 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32) func))
+ (type $sig (func_subtype (param i32) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (func $foo (type $sig) (param $i32 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ )
+
+ ;; CHECK: (func $bar (type $sig) (param $i32 i32)
+ ;; CHECK-NEXT: (i32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $bar (type $sig) (param $i32 i32)
+ ;; As above, but now there is a second (non-imported) function using this
+ ;; signature, and it does use the param, so we cannot optimize.
+ (i32.store
+ (i32.const 0)
+ (local.get $i32)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype func))
+ (type $sig (func_subtype (param i32) func))
+
+ ;; CHECK: (type $sig2 (func_subtype (param i32) func))
+ (type $sig2 (func_subtype (param i32) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (func $foo (type $sig)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ )
+
+ ;; CHECK: (func $bar (type $sig2) (param $i32 i32)
+ ;; CHECK-NEXT: (i32.store
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $bar (type $sig2) (param $i32 i32)
+ ;; As above, but now the second function has a different signature, so we
+ ;; can optimize one while not modifying the other.
+ (i32.store
+ (i32.const 0)
+ (local.get $i32)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (type $sig (func_subtype func))
+ (type $sig (func_subtype (param i32) func))
+
+ (memory 1 1)
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (memory $0 1 1)
+
+ ;; CHECK: (elem declare func $bar $foo)
+
+ ;; CHECK: (func $foo (type $sig)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ )
+
+ ;; CHECK: (func $bar (type $sig)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $bar (type $sig) (param $i32 i32)
+ ;; As above, but the second function also does not use the parameter, and
+ ;; has the same type. We can optimize both at once.
+ )
+
+ ;; CHECK: (func $caller (type $none_=>_none)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $bar)
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (ref.func $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller
+ (call $foo
+ (i32.const 0)
+ )
+ (call $bar
+ (i32.const 1)
+ )
+ (call_ref
+ (i32.const 2)
+ (ref.func $foo)
+ )
+ (call_ref
+ (i32.const 2)
+ (ref.func $bar)
+ )
+ )
+
+ ;; CHECK: (func $caller-2 (type $none_=>_none)
+ ;; CHECK-NEXT: (call $bar)
+ ;; CHECK-NEXT: (call_ref
+ ;; CHECK-NEXT: (ref.func $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $caller-2
+ ;; Also add some more calls to see they are updated too.
+ (call $bar
+ (i32.const 1)
+ )
+ (call_ref
+ (i32.const 2)
+ (ref.func $foo)
+ )
+ )
+)
+
+(module
+ ;; The presence of a table prevents us from doing any optimizations.
+ (table 1 1 anyref)
+
+ ;; CHECK: (type $sig (func_subtype (param i32) func))
+ (type $sig (func_subtype (param i32) func))
+
+ ;; CHECK: (table $0 1 1 anyref)
+
+ ;; CHECK: (func $foo (type $sig) (param $i32 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (type $sig) (param $i32 i32)
+ )
+)
+
+;; Exports cannot be optimized.
+(module
+ ;; CHECK: (type $sig (func_subtype (param i32) func))
+ (type $sig (func_subtype (param i32) func))
+
+ ;; CHECK: (export "foo" (func $foo))
+
+ ;; CHECK: (func $foo (type $sig) (param $i32 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo (export "foo") (type $sig) (param $i32 i32)
+ )
+)
+
+;; Two functions with two different types have an unused parameter. After
+;; removing the parameter from each, they both have no parameters. They should
+;; *not* have the same type, however: the type should be different nominally
+;; even though after the pruning they are identical structurally.
+(module
+ ;; CHECK: (type $sig1 (func_subtype func))
+ (type $sig1 (func_subtype (param i32) func))
+ ;; CHECK: (type $sig2 (func_subtype func))
+ (type $sig2 (func_subtype (param f64) func))
+
+ ;; CHECK: (func $foo1 (type $sig1)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo1 (type $sig1) (param $i32 i32)
+ )
+
+ ;; CHECK: (func $foo2 (type $sig2)
+ ;; CHECK-NEXT: (local $0 f64)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo2 (type $sig2) (param $f64 f64)
+ )
+)
+