diff options
-rw-r--r-- | test/lit/passes/gufa-vs-cfp.wast | 32 | ||||
-rw-r--r-- | test/lit/passes/monomorphize.wast | 12 | ||||
-rw-r--r-- | test/lit/passes/optimize-casts.wast | 3 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-call_ref-roundtrip.wast | 17 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc-tnh.wast | 4 | ||||
-rw-r--r-- | test/lit/passes/precompute-gc-immutable.wast | 30 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-module-elements-refs.wast | 50 | ||||
-rw-r--r-- | test/lit/passes/signature-pruning.wast | 124 | ||||
-rw-r--r-- | test/lit/passes/signature-refining.wast | 156 | ||||
-rw-r--r-- | test/lit/passes/signature-refining_gto.wat | 11 | ||||
-rw-r--r-- | test/lit/passes/type-merging-tnh.wast | 23 | ||||
-rw-r--r-- | test/lit/passes/type-refining.wast | 244 | ||||
-rw-r--r-- | test/lit/passes/type-ssa_and_merging.wast | 18 |
13 files changed, 409 insertions, 315 deletions
diff --git a/test/lit/passes/gufa-vs-cfp.wast b/test/lit/passes/gufa-vs-cfp.wast index 4c3d2e25c..73253aaa7 100644 --- a/test/lit/passes/gufa-vs-cfp.wast +++ b/test/lit/passes/gufa-vs-cfp.wast @@ -1,5 +1,7 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --nominal --remove-unused-names --gufa -all -S -o - | filecheck %s + +;; RUN: foreach %s %t wasm-opt --remove-unused-names --gufa -all -S -o - | filecheck %s + ;; (remove-unused-names is added to test fallthrough values without a block ;; name getting in the way) @@ -653,10 +655,10 @@ ;; Subtyping: Create a subtype and get a supertype. The get must receive a ;; reference to the subtype and so we can infer the value of the get. (module - (type $substruct (struct_subtype i32 f64 $struct)) - (type $struct (struct i32)) + (type $substruct (struct_subtype i32 f64 $struct)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $test (type $none_=>_none) @@ -785,12 +787,11 @@ ;; one value, so we can optimize. (module ;; CHECK: (type $struct (struct (field i32))) + (type $struct (struct i32)) ;; CHECK: (type $substruct (struct_subtype (field i32) (field f64) $struct)) (type $substruct (struct_subtype i32 f64 $struct)) - (type $struct (struct i32)) - ;; CHECK: (type $none_=>_i32 (func (result i32))) ;; CHECK: (type $none_=>_none (func)) @@ -2015,16 +2016,14 @@ ;; apply to it, preventing optimization. (module ;; CHECK: (type $A (struct (field (mut i32)))) + (type $A (struct (mut i32))) ;; CHECK: (type $B (struct_subtype (field (mut i32)) $A)) + (type $B (struct_subtype (mut i32) $A)) ;; CHECK: (type $C (struct_subtype (field (mut i32)) $B)) (type $C (struct_subtype (mut i32) $B)) - (type $A (struct (mut i32))) - - (type $B (struct_subtype (mut i32) $A)) - ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (type $none_=>_ref|$C| (func (result (ref $C)))) @@ -2668,12 +2667,17 @@ ;; Test we handle packed fields properly. (module - (type $A_8 (struct (field i8))) - (type $A_16 (struct (field i16))) - ;; CHECK: (type $B_16 (struct (field i16))) - (type $B_16 (struct (field i16))) + (rec + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A_8 (struct (field i8))) + (type $A_8 (struct (field i8))) + ;; CHECK: (type $A_16 (struct (field i16))) + (type $A_16 (struct (field i16))) + ;; CHECK: (type $B_16 (struct (field i16))) + (type $B_16 (struct (field i16))) + ) ;; CHECK: (import "a" "b" (global $g i32)) (import "a" "b" (global $g i32)) diff --git a/test/lit/passes/monomorphize.wast b/test/lit/passes/monomorphize.wast index d171291e2..22326fb43 100644 --- a/test/lit/passes/monomorphize.wast +++ b/test/lit/passes/monomorphize.wast @@ -3,8 +3,8 @@ ;; Test in both "always" mode, which always monomorphizes, and in "careful" ;; mode which does it only when it appears to actually help. -;; RUN: foreach %s %t wasm-opt --nominal --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS -;; RUN: foreach %s %t wasm-opt --nominal --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL +;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS +;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL (module ;; ALWAYS: (type $A (struct )) @@ -558,14 +558,14 @@ (module ;; Test that we avoid recursive calls. - ;; ALWAYS: (type $ref|$A|_=>_none (func (param (ref $A)))) - ;; ALWAYS: (type $A (struct )) - ;; CAREFUL: (type $ref|$A|_=>_none (func (param (ref $A)))) - ;; CAREFUL: (type $A (struct )) (type $A (struct_subtype data)) + ;; ALWAYS: (type $ref|$A|_=>_none (func (param (ref $A)))) + ;; ALWAYS: (type $B (struct_subtype $A)) + ;; CAREFUL: (type $ref|$A|_=>_none (func (param (ref $A)))) + ;; CAREFUL: (type $B (struct_subtype $A)) (type $B (struct_subtype $A)) diff --git a/test/lit/passes/optimize-casts.wast b/test/lit/passes/optimize-casts.wast index 6faca88d7..527c2d157 100644 --- a/test/lit/passes/optimize-casts.wast +++ b/test/lit/passes/optimize-casts.wast @@ -1,6 +1,5 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: wasm-opt %s --optimize-casts -all --nominal -S -o - \ -;; RUN: | filecheck %s +;; RUN: wasm-opt %s --optimize-casts -all -S -o - | filecheck %s (module ;; CHECK: (type $A (struct )) diff --git a/test/lit/passes/optimize-instructions-call_ref-roundtrip.wast b/test/lit/passes/optimize-instructions-call_ref-roundtrip.wast index de4d0da6f..0e59d6df3 100644 --- a/test/lit/passes/optimize-instructions-call_ref-roundtrip.wast +++ b/test/lit/passes/optimize-instructions-call_ref-roundtrip.wast @@ -1,7 +1,7 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. -;; RUN: wasm-opt %s --optimize-instructions --nominal --roundtrip --all-features -S -o - | filecheck %s +;; RUN: wasm-opt %s --optimize-instructions --roundtrip --all-features -S -o - | filecheck %s ;; roundtrip to see the effects on heap types in the binary format, specifically ;; regarding nominal heap types @@ -10,14 +10,17 @@ ;; distinct nominally. The three tables will use different ones, and the ;; emitted call_indirects should use the corresponding ones. - ;; CHECK: (type $v1 (func)) - (type $v1 (func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $v1 (func)) + (type $v1 (func)) - ;; CHECK: (type $v2 (func)) - (type $v2 (func)) + ;; CHECK: (type $v2 (func)) + (type $v2 (func)) - ;; CHECK: (type $v3 (func)) - (type $v3 (func)) + ;; CHECK: (type $v3 (func)) + (type $v3 (func)) + ) ;; CHECK: (type $i32_=>_none (func (param i32))) diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index 3c7494bac..11627d90c 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: wasm-opt %s --optimize-instructions --traps-never-happen -all --nominal -S -o - | filecheck %s --check-prefix TNH -;; RUN: wasm-opt %s --optimize-instructions -all --nominal -S -o - | filecheck %s --check-prefix NO_TNH +;; RUN: wasm-opt %s --optimize-instructions --traps-never-happen -all -S -o - | filecheck %s --check-prefix TNH +;; RUN: wasm-opt %s --optimize-instructions -all -S -o - | filecheck %s --check-prefix NO_TNH (module ;; TNH: (type $struct (struct (field (mut i32)))) diff --git a/test/lit/passes/precompute-gc-immutable.wast b/test/lit/passes/precompute-gc-immutable.wast index 980276ebb..32e2d5719 100644 --- a/test/lit/passes/precompute-gc-immutable.wast +++ b/test/lit/passes/precompute-gc-immutable.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: foreach %s %t wasm-opt --precompute-propagate -all --nominal -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --precompute-propagate -all -S -o - | filecheck %s (module ;; CHECK: (type $struct-imm (struct (field i32))) @@ -410,10 +410,9 @@ ;; Create an immutable vtable in an immutable field, which lets us read from ;; it. - ;; CHECK: (type $object (struct (field (ref $vtable)))) - ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $object (struct (field (ref $vtable)))) (type $object (struct (ref $vtable))) ;; CHECK: (func $nested-creations (type $none_=>_none) @@ -458,10 +457,9 @@ (module ;; As above, but make $vtable not immutable, which prevents optimization. - ;; CHECK: (type $object (struct (field (ref $vtable)))) - ;; CHECK: (type $vtable (struct (field (mut funcref)))) (type $vtable (struct (mut funcref))) + ;; CHECK: (type $object (struct (field (ref $vtable)))) (type $object (struct (ref $vtable))) ;; CHECK: (func $nested-creations (type $none_=>_none) @@ -511,10 +509,9 @@ (module ;; As above, but make $object not immutable, which prevents optimization. - ;; CHECK: (type $object (struct (field (mut (ref $vtable))))) - ;; CHECK: (type $vtable (struct (field funcref))) (type $vtable (struct funcref)) + ;; CHECK: (type $object (struct (field (mut (ref $vtable))))) (type $object (struct (mut (ref $vtable)))) ;; CHECK: (func $nested-creations (type $none_=>_none) @@ -739,17 +736,20 @@ ;; data that is filled with vtables of different types. On usage, we do a ;; cast of the vtable type. - ;; CHECK: (type $itable (array structref)) - (type $itable (array (ref null struct))) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $itable (array structref)) + (type $itable (array (ref null struct))) - ;; CHECK: (type $object (struct (field (ref $itable)))) - (type $object (struct (ref $itable))) + ;; CHECK: (type $object (struct (field (ref $itable)))) + (type $object (struct (ref $itable))) - ;; CHECK: (type $vtable-0 (struct (field funcref))) - (type $vtable-0 (struct funcref)) + ;; CHECK: (type $vtable-0 (struct (field funcref))) + (type $vtable-0 (struct funcref)) - ;; CHECK: (type $vtable-1 (struct (field funcref))) - (type $vtable-1 (struct funcref)) + ;; CHECK: (type $vtable-1 (struct (field funcref))) + (type $vtable-1 (struct funcref)) + ) ;; CHECK: (global $itable (ref $itable) (array.new_fixed $itable ;; CHECK-NEXT: (struct.new $vtable-0 diff --git a/test/lit/passes/remove-unused-module-elements-refs.wast b/test/lit/passes/remove-unused-module-elements-refs.wast index 61ccb4daf..61315384d 100644 --- a/test/lit/passes/remove-unused-module-elements-refs.wast +++ b/test/lit/passes/remove-unused-module-elements-refs.wast @@ -1,33 +1,39 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --closed-world --nominal -all -S -o - | filecheck %s -;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --nominal -all -S -o - | filecheck %s --check-prefix OPEN_WORLD +;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --closed-world -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -all -S -o - | filecheck %s --check-prefix OPEN_WORLD ;; Test both open world (default) and closed world. In a closed world we can do ;; more with function refs, as we assume nothing calls them on the outside, so ;; if no calls exist to the right type, the function is not reached. (module - ;; CHECK: (type $A-super (func)) - ;; OPEN_WORLD: (type $A-super (func)) - (type $A-super (func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A-super (func)) + ;; OPEN_WORLD: (rec + ;; OPEN_WORLD-NEXT: (type $A-super (func)) + (type $A-super (func)) - ;; CHECK: (type $A (func_subtype $A-super)) - ;; OPEN_WORLD: (type $A (func_subtype $A-super)) - (type $A (func_subtype $A-super)) + ;; CHECK: (type $A (func_subtype $A-super)) + ;; OPEN_WORLD: (type $A (func_subtype $A-super)) + (type $A (func_subtype $A-super)) - ;; CHECK: (type $A-sub (func_subtype $A)) - ;; OPEN_WORLD: (type $A-sub (func_subtype $A)) - (type $A-sub (func_subtype $A)) + ;; CHECK: (type $A-sub (func_subtype $A)) + ;; OPEN_WORLD: (type $A-sub (func_subtype $A)) + (type $A-sub (func_subtype $A)) - ;; CHECK: (type $B (func)) - ;; OPEN_WORLD: (type $B (func)) - (type $B (func)) + ;; CHECK: (type $B (func)) + ;; OPEN_WORLD: (type $B (func)) + (type $B (func)) + ) + + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (elem declare func $target-A $target-A-sub $target-A-super $target-B) ;; CHECK: (export "foo" (func $foo)) - ;; CHECK: (func $foo (type $A-super) + ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $A (ref null $A)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.func $target-A) @@ -51,11 +57,13 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (type $none_=>_none (func)) + ;; OPEN_WORLD: (elem declare func $target-A $target-A-sub $target-A-super $target-B) ;; OPEN_WORLD: (export "foo" (func $foo)) - ;; OPEN_WORLD: (func $foo (type $A-super) + ;; OPEN_WORLD: (func $foo (type $none_=>_none) ;; OPEN_WORLD-NEXT: (local $A (ref null $A)) ;; OPEN_WORLD-NEXT: (drop ;; OPEN_WORLD-NEXT: (ref.func $target-A) @@ -1161,11 +1169,13 @@ ;; Cycles. (module (rec - ;; CHECK: (type $vtable-func (func (param (ref $vtable)))) - ;; OPEN_WORLD: (type $vtable-func (func (param (ref $vtable)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $vtable-func (func (param (ref $vtable)))) + ;; OPEN_WORLD: (rec + ;; OPEN_WORLD-NEXT: (type $vtable-func (func (param (ref $vtable)))) (type $vtable-func (func (param (ref $vtable)))) - ;; CHECK: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) - ;; OPEN_WORLD: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) + ;; CHECK: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) + ;; OPEN_WORLD: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) (type $vtable (struct_subtype (field (ref $vtable-func)) (field (ref $vtable-func)) data)) ) diff --git a/test/lit/passes/signature-pruning.wast b/test/lit/passes/signature-pruning.wast index 76a2666ab..55010a250 100644 --- a/test/lit/passes/signature-pruning.wast +++ b/test/lit/passes/signature-pruning.wast @@ -1,14 +1,15 @@ ;; 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 --closed-world -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --signature-pruning --closed-world -all -S -o - | filecheck %s (module - ;; CHECK: (type $sig (func (param i32 f64))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param i32 f64))) (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $foo) @@ -67,13 +68,14 @@ ) (module - ;; CHECK: (type $sig (func (param i64 f32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param i64 f32))) (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $foo) @@ -131,13 +133,14 @@ ) (module - ;; CHECK: (type $sig (func (param i32 i64 f32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param i32 i64 f32))) (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $foo) @@ -207,13 +210,14 @@ ;; 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 (param i32 i64 f32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param i32 i64 f32))) (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $foo) @@ -277,13 +281,14 @@ ) (module - ;; CHECK: (type $sig (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func)) (type $sig (func_subtype (param i32) (param i64) (param f32) (param f64) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $foo) @@ -323,13 +328,14 @@ ) (module - ;; CHECK: (type $sig (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func)) (type $sig (func_subtype (param i32) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (func $foo (type $sig) @@ -437,11 +443,15 @@ ) (module - ;; CHECK: (type $sig (func)) - (type $sig (func_subtype (param i32) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $sig2 (func (param i32))) - ;; CHECK: (type $sig2 (func (param i32))) - (type $sig2 (func_subtype (param i32) func)) + ;; CHECK: (type $sig (func)) + (type $sig (func_subtype (param i32) func)) + + (type $sig2 (func_subtype (param i32) func)) + ) (memory 1 1) @@ -471,13 +481,14 @@ ) (module - ;; CHECK: (type $sig (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func)) (type $sig (func_subtype (param i32) func)) (memory 1 1) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (memory $0 1 1) ;; CHECK: (elem declare func $bar $foo) @@ -600,10 +611,14 @@ ;; *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)) - (type $sig1 (func_subtype (param i32) func)) - ;; CHECK: (type $sig2 (func)) - (type $sig2 (func_subtype (param f64) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $sig2 (func)) + + ;; CHECK: (type $sig1 (func)) + (type $sig1 (func_subtype (param i32) func)) + (type $sig2 (func_subtype (param f64) func)) + ) ;; CHECK: (func $foo1 (type $sig1) ;; CHECK-NEXT: (local $0 i32) @@ -621,10 +636,14 @@ ) (module - ;; CHECK: (type $sig-foo (func)) - (type $sig-foo (func_subtype (param i32) func)) - ;; CHECK: (type $sig-bar (func (param i32))) - (type $sig-bar (func_subtype (param i32) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $sig-bar (func (param i32))) + + ;; CHECK: (type $sig-foo (func)) + (type $sig-foo (func_subtype (param i32) func)) + (type $sig-bar (func_subtype (param i32) func)) + ) (memory 1 1) @@ -685,10 +704,14 @@ ;; value that we can optimize in the case of $foo (but not $bar, again, as $bar ;; is not always sent a constant value). (module - ;; CHECK: (type $sig-foo (func)) - (type $sig-foo (func_subtype (param funcref) func)) - ;; CHECK: (type $sig-bar (func (param funcref))) - (type $sig-bar (func_subtype (param funcref) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $sig-bar (func (param funcref))) + + ;; CHECK: (type $sig-foo (func)) + (type $sig-foo (func_subtype (param funcref) func)) + (type $sig-bar (func_subtype (param funcref) func)) + ) (memory 1 1) @@ -737,10 +760,14 @@ ;; As above, but the values are now ref.nulls. (module - ;; CHECK: (type $sig-foo (func)) - (type $sig-foo (func_subtype (param anyref) func)) - ;; CHECK: (type $sig-bar (func (param anyref))) - (type $sig-bar (func_subtype (param anyref) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $sig-bar (func (param anyref))) + + ;; CHECK: (type $sig-foo (func)) + (type $sig-foo (func_subtype (param anyref) func)) + (type $sig-bar (func_subtype (param anyref) func)) + ) (memory 1 1) @@ -855,23 +882,20 @@ ;; relationship. Atm we do not prune such "cycles" so we do not optimize here. ;; TODO (module - ;; CHECK: (type $array.A (array (ref $struct.A))) - - ;; CHECK: (type $array.B (array_subtype (ref $struct.B) $array.A)) - - ;; CHECK: (type $func.A (func (param (ref $array.B)) (result (ref $array.A)))) - - ;; CHECK: (type $func.B (func_subtype (param (ref $array.A)) (result (ref $array.B)) $func.A)) - ;; CHECK: (type $struct.A (struct (field i32))) (type $struct.A (struct (field i32))) + ;; CHECK: (type $array.A (array (ref $struct.A))) + ;; CHECK: (type $struct.B (struct_subtype (field i32) (field i64) $struct.A)) (type $struct.B (struct_subtype (field i32) (field i64) $struct.A)) (type $array.A (array (ref $struct.A))) + ;; CHECK: (type $array.B (array_subtype (ref $struct.B) $array.A)) (type $array.B (array_subtype (ref $struct.B) $array.A)) + ;; CHECK: (type $func.A (func (param (ref $array.B)) (result (ref $array.A)))) (type $func.A (func (param (ref $array.B)) (result (ref $array.A)))) + ;; CHECK: (type $func.B (func_subtype (param (ref $array.A)) (result (ref $array.B)) $func.A)) (type $func.B (func_subtype (param (ref $array.A)) (result (ref $array.B)) $func.A)) ;; CHECK: (func $func.A (type $func.A) (param $0 (ref $array.B)) (result (ref $array.A)) diff --git a/test/lit/passes/signature-refining.wast b/test/lit/passes/signature-refining.wast index d8da19a64..dea74451a 100644 --- a/test/lit/passes/signature-refining.wast +++ b/test/lit/passes/signature-refining.wast @@ -1,5 +1,5 @@ ;; 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-refining -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --signature-refining -all -S -o - | filecheck %s (module ;; $func is defined with an anyref parameter but always called with a $struct, @@ -7,13 +7,14 @@ ;; heap type's definition as well as the types of the parameters as printed ;; on the function (which are derived from the heap type). - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) (type $struct (struct)) - ;; CHECK: (type $sig (func (param (ref $struct)))) - (type $sig (func_subtype (param anyref) func)) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $sig (func (param (ref $struct)))) + (type $sig (func_subtype (param anyref) func)) ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) ;; CHECK-NEXT: (nop) @@ -36,14 +37,14 @@ (module ;; As above, but the call is via call_ref. - ;; CHECK: (type $sig (func (param (ref $struct)))) - - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) (type $struct (struct)) - (type $sig (func_subtype (param anyref) func)) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $sig (func (param (ref $struct)))) + (type $sig (func_subtype (param anyref) func)) ;; CHECK: (elem declare func $func) @@ -72,13 +73,13 @@ ;; call uses a nullable $struct, the other a non-nullable i31, so the LUB ;; is a nullable eqref. - ;; CHECK: (type $sig (func (param eqref))) - - ;; CHECK: (type $none_=>_none (func)) - - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) (type $struct (struct)) + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param eqref))) (type $sig (func_subtype (param anyref) func)) ;; CHECK: (elem declare func $func) @@ -118,20 +119,25 @@ ;; Multiple functions with the same heap type. Again, the LUB is in the ;; middle, this time the parent $struct and not a subtype. - ;; CHECK: (type $sig (func (param (ref $struct)))) - (type $sig (func_subtype (param anyref) func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $struct-sub2 (struct_subtype $struct)) - ;; CHECK: (type $struct (struct )) + ;; CHECK: (type $struct-sub1 (struct_subtype $struct)) - ;; CHECK: (type $struct-sub1 (struct_subtype $struct)) - (type $struct-sub1 (struct_subtype $struct)) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $struct-sub2 (struct_subtype $struct)) - (type $struct-sub2 (struct_subtype $struct)) + ;; CHECK: (type $sig (func (param (ref $struct)))) + (type $sig (func_subtype (param anyref) func)) - (type $struct (struct)) + (type $struct (struct)) + + (type $struct-sub1 (struct_subtype $struct)) + + (type $struct-sub2 (struct_subtype $struct)) + ) ;; CHECK: (func $func-1 (type $sig) (param $x (ref $struct)) ;; CHECK-NEXT: (nop) @@ -167,14 +173,16 @@ ;; As above, but now only one of the functions is called. The other is still ;; updated, though, as they share a heap type. - ;; CHECK: (type $sig (func (param (ref $struct)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param (ref $struct)))) (type $sig (func_subtype (param anyref) func)) - ;; CHECK: (type $struct (struct )) (type $struct (struct)) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (func $func-1 (type $sig) (param $x (ref $struct)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) @@ -203,14 +211,16 @@ ;; Define a field in the struct of the signature type that will be updated, ;; to check for proper validation after the update. - ;; CHECK: (type $sig (func (param (ref $struct) (ref $sig)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (ref $sig)))) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param (ref $struct) (ref $sig)))) (type $sig (func_subtype (param anyref funcref) func)) - ;; CHECK: (type $struct (struct (field (ref $sig)))) (type $struct (struct_subtype (field (ref $sig)) data)) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (elem declare func $func) ;; CHECK: (func $func (type $sig) (param $x (ref $struct)) (param $f (ref $sig)) @@ -266,14 +276,14 @@ ;; An unreachable value does not prevent optimization: we will update the ;; param to be $struct. - ;; CHECK: (type $sig (func (param (ref $struct)))) - - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) (type $struct (struct)) - (type $sig (func_subtype (param anyref) func)) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $sig (func (param (ref $struct)))) + (type $sig (func_subtype (param anyref) func)) ;; CHECK: (elem declare func $func) @@ -354,16 +364,19 @@ (module ;; Test multiple fields in multiple types. - ;; CHECK: (type $struct (struct )) - (type $struct (struct)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) + (type $struct (struct)) - ;; CHECK: (type $sig-2 (func (param eqref (ref $struct)))) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $sig-1 (func (param structref anyref))) - (type $sig-1 (func_subtype (param anyref) (param anyref) func)) - (type $sig-2 (func_subtype (param anyref) (param anyref) func)) + ;; CHECK: (type $sig-2 (func (param eqref (ref $struct)))) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $sig-1 (func (param structref anyref))) + (type $sig-1 (func_subtype (param anyref) (param anyref) func)) + (type $sig-2 (func_subtype (param anyref) (param anyref) func)) + ) ;; CHECK: (elem declare func $func-2) @@ -463,15 +476,16 @@ ;; Pass a null in one call to the function. The null can be updated which ;; allows us to refine (but the new type must be nullable). - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) - ;; CHECK: (type $sig (func (param (ref null $struct)))) + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $sig (func (param (ref null $struct)))) (type $sig (func_subtype (param anyref) func)) (type $struct (struct)) - ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (func $func (type $sig) (param $x (ref null $struct)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) @@ -497,23 +511,28 @@ ) (module - ;; CHECK: (type $struct (struct )) - (type $struct (struct)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $none_=>_none (func)) - ;; This signature has a single function using it, which returns a more - ;; refined type, and we can refine to that. - ;; CHECK: (type $sig-can-refine (func (result (ref $struct)))) - (type $sig-can-refine (func_subtype (result anyref) func)) + ;; CHECK: (type $sig-unreachable (func (result anyref))) - ;; Also a single function, but no refinement is possible. - ;; CHECK: (type $sig-cannot-refine (func (result (ref func)))) - (type $sig-cannot-refine (func_subtype (result (ref func)) func)) + ;; CHECK: (type $sig-cannot-refine (func (result (ref func)))) - ;; The single function never returns, so no refinement is possible. - ;; CHECK: (type $sig-unreachable (func (result anyref))) - (type $sig-unreachable (func_subtype (result anyref) func)) + ;; CHECK: (type $struct (struct )) + (type $struct (struct)) - ;; CHECK: (type $none_=>_none (func)) + ;; This signature has a single function using it, which returns a more + ;; refined type, and we can refine to that. + ;; CHECK: (type $sig-can-refine (func (result (ref $struct)))) + (type $sig-can-refine (func_subtype (result anyref) func)) + + ;; Also a single function, but no refinement is possible. + (type $sig-cannot-refine (func_subtype (result (ref func)) func)) + + ;; The single function never returns, so no refinement is possible. + (type $sig-unreachable (func_subtype (result anyref) func)) + ) ;; CHECK: (elem declare func $func-can-refine $func-cannot-refine) @@ -589,13 +608,13 @@ ) (module - ;; CHECK: (type $sig (func (result (ref null $struct)))) - - ;; CHECK: (type $struct (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct )) (type $struct (struct)) ;; This signature has multiple functions using it, and some of them have nulls ;; which should be updated when we refine. + ;; CHECK: (type $sig (func (result (ref null $struct)))) (type $sig (func_subtype (result anyref) func)) ;; CHECK: (func $func-1 (type $sig) (result (ref null $struct)) @@ -689,11 +708,11 @@ ) (module - ;; CHECK: (type $ref|${}|_i32_=>_none (func (param (ref ${}) i32))) - ;; CHECK: (type ${} (struct )) (type ${} (struct)) + ;; CHECK: (type $ref|${}|_i32_=>_none (func (param (ref ${}) i32))) + ;; CHECK: (func $foo (type $ref|${}|_i32_=>_none) (param $ref (ref ${})) (param $i32 i32) ;; CHECK-NEXT: (local $2 eqref) ;; CHECK-NEXT: (local.set $2 @@ -763,9 +782,10 @@ ;; skip such optimizations. TODO (module (rec - ;; CHECK: (type $A (func (param (ref null $B)) (result (ref null $A)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (func (param (ref null $B)) (result (ref null $A)))) (type $A (func (param (ref null $B)) (result (ref null $A)))) - ;; CHECK: (type $B (func_subtype (param (ref null $A)) (result (ref null $B)) $A)) + ;; CHECK: (type $B (func_subtype (param (ref null $A)) (result (ref null $B)) $A)) (type $B (func_subtype (param (ref null $A)) (result (ref null $B)) $A)) ) diff --git a/test/lit/passes/signature-refining_gto.wat b/test/lit/passes/signature-refining_gto.wat index 8e034ef3e..0d0e4c22c 100644 --- a/test/lit/passes/signature-refining_gto.wat +++ b/test/lit/passes/signature-refining_gto.wat @@ -1,20 +1,17 @@ -;; RUN: foreach %s %t wasm-opt --closed-world --nominal --signature-refining --gto --roundtrip -all -S -o - | filecheck %s -;; RUN: foreach %s %t wasm-opt --closed-world --signature-refining --gto --remove-unused-types --roundtrip -all -S -o - | filecheck %s --check-prefix ISOREC +;; RUN: wasm-opt %s --closed-world --signature-refining --gto --remove-unused-types --roundtrip -all -S -o - | filecheck %s ;; Check that type $A is not included in the final binary after the signature -;; refining optimization. For isorecursive types, this requires an additional -;; --remove-unused-types pass after signature refining. +;; refining optimization and an additional --remove-unused-types pass. (module ;; The type $A should not be emitted at all (see below). ;; CHECK-NOT: (type $A - ;; ISOREC-NOT: (type $A (type $A (struct (field (mut (ref null $A))))) - ;; CHECK: (type $ref|none|_=>_none (func (param (ref none)))) - ;; CHECK: (type $funcref_i32_=>_none (func (param funcref i32))) + ;; CHECK: (type $ref|none|_=>_none (func (param (ref none)))) + ;; CHECK: (func $struct.get (type $ref|none|_=>_none) (param $0 (ref none)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) diff --git a/test/lit/passes/type-merging-tnh.wast b/test/lit/passes/type-merging-tnh.wast index fefdb00e3..c84e31815 100644 --- a/test/lit/passes/type-merging-tnh.wast +++ b/test/lit/passes/type-merging-tnh.wast @@ -1,13 +1,14 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --nominal --closed-world -tnh --type-merging -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --closed-world -tnh --type-merging --remove-unused-types -all -S -o - | filecheck %s ;; ref.cast does not inhibit merging if traps never happen. (module - ;; CHECK: (type $A (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct )) (type $A (struct)) (type $B (struct_subtype $A)) - ;; CHECK: (type $ref|$A|_=>_ref|$A| (func (param (ref $A)) (result (ref $A)))) + ;; CHECK: (type $ref|$A|_=>_ref|$A| (func (param (ref $A)) (result (ref $A)))) ;; CHECK: (func $test (type $ref|$A|_=>_ref|$A|) (param $a (ref $A)) (result (ref $A)) ;; CHECK-NEXT: (ref.cast $A @@ -23,13 +24,14 @@ ;; Check that a ref.test still inhibits merging with -tnh. (module - ;; CHECK: (type $ref|$A|_=>_i32 (func (param (ref $A)) (result i32))) - - ;; CHECK: (type $A (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct )) (type $A (struct)) - ;; CHECK: (type $B (struct_subtype $A)) + ;; CHECK: (type $B (struct_subtype $A)) (type $B (struct_subtype $A)) + ;; CHECK: (type $ref|$A|_=>_i32 (func (param (ref $A)) (result i32))) + ;; CHECK: (func $test (type $ref|$A|_=>_i32) (param $a (ref $A)) (result i32) ;; CHECK-NEXT: (ref.test $B ;; CHECK-NEXT: (local.get $a) @@ -44,12 +46,13 @@ ;; Check that a br_on_cast still inhibits merging with -tnh. (module - ;; CHECK: (type $A (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct )) (type $A (struct)) - ;; CHECK: (type $B (struct_subtype $A)) + ;; CHECK: (type $B (struct_subtype $A)) (type $B (struct_subtype $A)) - ;; CHECK: (type $ref|$A|_=>_ref|$B| (func (param (ref $A)) (result (ref $B)))) + ;; CHECK: (type $ref|$A|_=>_ref|$B| (func (param (ref $A)) (result (ref $B)))) ;; CHECK: (func $test (type $ref|$A|_=>_ref|$B|) (param $a (ref $A)) (result (ref $B)) ;; CHECK-NEXT: (block $__binaryen_fake_return (result (ref $B)) diff --git a/test/lit/passes/type-refining.wast b/test/lit/passes/type-refining.wast index 870e3d7cb..ca9b98590 100644 --- a/test/lit/passes/type-refining.wast +++ b/test/lit/passes/type-refining.wast @@ -1,14 +1,15 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --nominal --closed-world --type-refining -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --closed-world --type-refining -all -S -o - | filecheck %s (module ;; A struct with three fields. The first will have no writes, the second one ;; write of the same type, and the last a write of a subtype, which will allow ;; us to specialize that one. - ;; CHECK: (type $struct (struct (field (mut anyref)) (field (mut (ref i31))) (field (mut (ref i31))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut anyref)) (field (mut (ref i31))) (field (mut (ref i31))))) (type $struct (struct_subtype (field (mut anyref)) (field (mut (ref i31))) (field (mut anyref)) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (struct.set $struct 1 @@ -53,10 +54,11 @@ ;; must keep the type nullable, unlike in the previous module, due to the ;; default value being null. - ;; CHECK: (type $struct (struct (field (mut i31ref)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut i31ref)))) (type $struct (struct_subtype (field (mut anyref)) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (drop @@ -86,16 +88,20 @@ ;; that we do so in all three types, not just the parent to which we write ;; (the children have no writes, but must still be updated). - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) - (type $struct (struct_subtype (field (mut structref)) data)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) + (type $struct (struct_subtype (field (mut structref)) data)) + + ;; CHECK: (type $child-B (struct_subtype (field (mut (ref $struct))) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child-A|_ref|$child-B|_=>_none (func (param (ref $struct) (ref $child-A) (ref $child-B)))) + ;; CHECK: (type $child-A (struct_subtype (field (mut (ref $struct))) $struct)) + (type $child-A (struct_subtype (field (mut structref)) $struct)) - ;; CHECK: (type $child-A (struct_subtype (field (mut (ref $struct))) $struct)) - (type $child-A (struct_subtype (field (mut structref)) $struct)) + (type $child-B (struct_subtype (field (mut structref)) $struct)) + ) - ;; CHECK: (type $child-B (struct_subtype (field (mut (ref $struct))) $struct)) - (type $child-B (struct_subtype (field (mut structref)) $struct)) + ;; CHECK: (type $ref|$struct|_ref|$child-A|_ref|$child-B|_=>_none (func (param (ref $struct) (ref $child-A) (ref $child-B)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child-A|_ref|$child-B|_=>_none) (param $struct (ref $struct)) (param $child-A (ref $child-A)) (param $child-B (ref $child-B)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -123,19 +129,21 @@ ;; As above, but all writes are of $child-A, which allows more optimization ;; up to that type. - ;; CHECK: (type $struct (struct (field (mut (ref $child-A))))) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $child-A))))) + (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child-A (struct_subtype (field (mut (ref $child-A))) $struct)) - (type $child-A (struct_subtype (field (mut structref)) $struct)) + ;; CHECK: (type $child-A (struct_subtype (field (mut (ref $child-A))) $struct)) + (type $child-A (struct_subtype (field (mut structref)) $struct)) - (type $struct (struct_subtype (field (mut structref)) data)) - - ;; CHECK: (type $ref|$struct|_ref|$child-A|_=>_none (func (param (ref $struct) (ref $child-A)))) + ;; CHECK: (type $child-B (struct_subtype (field (mut (ref $child-A))) $struct)) + (type $child-B (struct_subtype (field (mut structref)) $struct)) + ) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $child-B (struct_subtype (field (mut (ref $child-A))) $struct)) - (type $child-B (struct_subtype (field (mut structref)) $struct)) + ;; CHECK: (type $ref|$struct|_ref|$child-A|_=>_none (func (param (ref $struct) (ref $child-A)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child-A|_=>_none) (param $struct (ref $struct)) (param $child-A (ref $child-A)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -175,13 +183,14 @@ ;; child prevents specialization even in the parent and we only improve up to ;; $struct but not to $child. - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) (type $child (struct_subtype (field (mut structref)) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -208,14 +217,14 @@ (module ;; As above, but both writes are of $child, so we can optimize. - ;; CHECK: (type $struct (struct (field (mut (ref $child))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $child))))) + (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref $child))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref $child))) $struct)) (type $child (struct_subtype (field (mut structref)) $struct)) - (type $struct (struct_subtype (field (mut structref)) data)) - - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -245,13 +254,14 @@ ;; info, however, we can't make the parent field more specific than the ;; child's. - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) (type $child (struct_subtype (field (mut structref)) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (drop @@ -289,13 +299,14 @@ ;; imply the fields are mutable, which limits optimization, see the next ;; testcase after this.) - ;; CHECK: (type $struct (struct (field (ref $struct)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (ref $struct)))) (type $struct (struct_subtype (field structref) data)) - ;; CHECK: (type $child (struct_subtype (field (ref $child)) $struct)) + ;; CHECK: (type $child (struct_subtype (field (ref $child)) $struct)) (type $child (struct_subtype (field structref) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (drop @@ -328,13 +339,14 @@ ;; different types in this case, and both will become $struct (still an ;; improvement!) - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) $struct)) (type $child (struct_subtype (field (mut structref)) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (drop @@ -367,13 +379,14 @@ ;; that case there is nothing stopping us from specializing that new field ;; to $child. - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) (field (mut (ref $child))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref $struct))) (field (mut (ref $child))) $struct)) (type $child (struct_subtype (field (mut structref)) (field (mut structref)) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $work (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (drop @@ -407,10 +420,11 @@ ;; A copy of a field does not prevent optimization (even though it assigns ;; the old type). - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct_subtype (field (mut structref)) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -439,24 +453,27 @@ ) (module - ;; CHECK: (type $X (struct )) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (ref $Y)))) - ;; CHECK: (type $Y (struct_subtype $X)) - (type $Y (struct_subtype $X)) + ;; CHECK: (type $B (struct_subtype (field (ref $Y)) $A)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $X (struct )) + (type $X (struct)) - ;; CHECK: (type $A (struct (field (ref $Y)))) + ;; CHECK: (type $Y (struct_subtype $X)) + (type $Y (struct_subtype $X)) - ;; CHECK: (type $C (struct_subtype (field (ref $Y)) $A)) - (type $C (struct_subtype (field (ref $X)) $A)) + (type $A (struct_subtype (field (ref $X)) data)) - ;; CHECK: (type $B (struct_subtype (field (ref $Y)) $A)) - (type $B (struct_subtype (field (ref $X)) $A)) + ;; CHECK: (type $C (struct_subtype (field (ref $Y)) $A)) + (type $C (struct_subtype (field (ref $X)) $A)) - (type $A (struct_subtype (field (ref $X)) data)) + (type $B (struct_subtype (field (ref $X)) $A)) + ) - (type $X (struct)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $unused (ref null $C)) @@ -490,23 +507,25 @@ ;; As above, but remove the struct.new to $B, which means $A, $B, $C all have ;; no writes to them. There are no optimizations to do here. - ;; CHECK: (type $X (struct )) - (type $X (struct)) - - ;; CHECK: (type $none_=>_none (func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $X (struct )) + (type $X (struct)) - ;; CHECK: (type $A (struct (field (ref $X)))) + ;; CHECK: (type $Y (struct_subtype $X)) + (type $Y (struct_subtype $X)) - ;; CHECK: (type $C (struct_subtype (field (ref $X)) $A)) - (type $C (struct_subtype (field (ref $X)) $A)) + ;; CHECK: (type $A (struct (field (ref $X)))) + (type $A (struct_subtype (field (ref $X)) data)) - ;; CHECK: (type $B (struct_subtype (field (ref $X)) $A)) - (type $B (struct_subtype (field (ref $X)) $A)) + ;; CHECK: (type $B (struct_subtype (field (ref $X)) $A)) + (type $B (struct_subtype (field (ref $X)) $A)) - ;; CHECK: (type $Y (struct_subtype $X)) - (type $Y (struct_subtype $X)) + ;; CHECK: (type $C (struct_subtype (field (ref $X)) $A)) + (type $C (struct_subtype (field (ref $X)) $A)) + ) - (type $A (struct_subtype (field (ref $X)) data)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $unused1 (ref null $C)) @@ -525,17 +544,18 @@ (module ;; CHECK: (type $X (struct )) (type $X (struct)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (type $A (struct (field (ref $X)))) - ;; CHECK: (type $B (struct_subtype (field (ref $Y)) $A)) - (type $B (struct_subtype (field (ref $Y)) $A)) + ;; CHECK: (type $Y (struct_subtype $X)) + (type $Y (struct_subtype $X)) (type $A (struct_subtype (field (ref $X)) data)) - ;; CHECK: (type $Y (struct_subtype $X)) - (type $Y (struct_subtype $X)) + ;; CHECK: (type $B (struct_subtype (field (ref $Y)) $A)) + (type $B (struct_subtype (field (ref $Y)) $A)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $unused2 (ref null $B)) @@ -561,10 +581,11 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $update-null (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -594,12 +615,13 @@ ;; As above, but now the null is in a child. The result should be the same: ;; refine the field to nullable $struct. - ;; CHECK: (type $struct (struct (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct)) (type $child (struct_subtype (field (mut (ref null struct))) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -626,12 +648,13 @@ (module ;; As above, but now the null is in a parent. The result should be the same. - ;; CHECK: (type $struct (struct (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref null $struct))) $struct)) (type $child (struct_subtype (field (mut (ref null struct))) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (struct.set $struct 0 @@ -656,10 +679,11 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut nullref)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut nullref)))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (drop @@ -676,10 +700,11 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (drop @@ -704,10 +729,11 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) data)) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (drop @@ -736,13 +762,14 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut (ref null $child))) (field (mut (ref null $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref null $child))) (field (mut (ref null $struct))))) (type $struct (struct_subtype (field (mut (ref null struct))) (field (mut (ref null struct))) data)) - ;; CHECK: (type $child (struct_subtype (field (mut (ref null $child))) (field (mut (ref null $struct))) $struct)) + ;; CHECK: (type $child (struct_subtype (field (mut (ref null $child))) (field (mut (ref null $struct))) $struct)) (type $child (struct_subtype (field (mut (ref null struct))) (field (mut (ref null struct))) $struct)) - ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) + ;; CHECK: (type $ref|$struct|_ref|$child|_=>_none (func (param (ref $struct) (ref $child)))) ;; CHECK: (func $update-null (type $ref|$struct|_ref|$child|_=>_none) (param $struct (ref $struct)) (param $child (ref $child)) ;; CHECK-NEXT: (drop @@ -791,27 +818,29 @@ ;; Root-Outer[Root-Inner] -> Leaf1-Outer[Leaf1-Inner] ;; -> Leaf2-Outer[Leaf2-Inner] - ;; CHECK: (type $Root-Inner (struct )) - - ;; CHECK: (type $Leaf2-Inner (struct_subtype $Root-Inner)) - (type $Leaf2-Inner (struct_subtype $Root-Inner)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $Root-Inner (struct )) + (type $Root-Inner (struct)) - ;; CHECK: (type $ref?|$Leaf1-Outer|_=>_none (func (param (ref null $Leaf1-Outer)))) + ;; CHECK: (type $Leaf1-Inner (struct_subtype (field i32) $Root-Inner)) + (type $Leaf1-Inner (struct_subtype (field i32) $Root-Inner)) - ;; CHECK: (type $Root-Outer (struct (field (ref $Leaf2-Inner)))) + ;; CHECK: (type $Root-Outer (struct (field (ref $Leaf2-Inner)))) - ;; CHECK: (type $Leaf2-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) + ;; CHECK: (type $Leaf1-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) - ;; CHECK: (type $Leaf1-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) - (type $Leaf1-Outer (struct_subtype (field (ref $Leaf1-Inner)) $Root-Outer)) + ;; CHECK: (type $Leaf2-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) - (type $Leaf2-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) + ;; CHECK: (type $Leaf2-Inner (struct_subtype $Root-Inner)) + (type $Leaf2-Inner (struct_subtype $Root-Inner)) (type $Root-Outer (struct_subtype (field (ref $Root-Inner)) data)) - (type $Root-Inner (struct)) + (type $Leaf1-Outer (struct_subtype (field (ref $Leaf1-Inner)) $Root-Outer)) - (type $Leaf1-Inner (struct_subtype (field i32) $Root-Inner)) + (type $Leaf2-Outer (struct_subtype (field (ref $Leaf2-Inner)) $Root-Outer)) + + ;; CHECK: (type $ref?|$Leaf1-Outer|_=>_none (func (param (ref null $Leaf1-Outer)))) ;; CHECK: (func $func (type $ref?|$Leaf1-Outer|_=>_none) (param $Leaf1-Outer (ref null $Leaf1-Outer)) ;; CHECK-NEXT: (drop @@ -931,12 +960,13 @@ ) (module - ;; CHECK: (type $A (struct (field (ref null $A)))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (ref null $A)))) (type $A (struct_subtype (field (ref null $A)) data)) - ;; CHECK: (type $B (struct_subtype (field (ref null $B)) $A)) + ;; CHECK: (type $B (struct_subtype (field (ref null $B)) $A)) (type $B (struct_subtype (field (ref null $A)) $A)) - ;; CHECK: (type $ref?|$B|_ref?|$A|_=>_none (func (param (ref null $B) (ref null $A)))) + ;; CHECK: (type $ref?|$B|_ref?|$A|_=>_none (func (param (ref null $B) (ref null $A)))) ;; CHECK: (func $heap-type (type $ref?|$B|_ref?|$A|_=>_none) (param $b (ref null $B)) (param $A (ref null $A)) ;; CHECK-NEXT: (local $a (ref null $A)) @@ -981,10 +1011,11 @@ ) (module - ;; CHECK: (type $A (struct (field (mut (ref $A))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut (ref $A))))) (type $A (struct_subtype (field (mut (ref null $A))) data)) - ;; CHECK: (type $ref|$A|_ref?|$A|_=>_none (func (param (ref $A) (ref null $A)))) + ;; CHECK: (type $ref|$A|_ref?|$A|_=>_none (func (param (ref $A) (ref null $A)))) ;; CHECK: (func $non-nullability-block (type $ref|$A|_ref?|$A|_=>_none) (param $nn (ref $A)) (param $A (ref null $A)) ;; CHECK-NEXT: (struct.set $A 0 @@ -1046,10 +1077,11 @@ ) (module - ;; CHECK: (type $struct (struct (field (mut (ref $struct))))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct (field (mut (ref $struct))))) (type $struct (struct (field (mut (ref null struct))))) - ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) + ;; CHECK: (type $ref|$struct|_=>_none (func (param (ref $struct)))) ;; CHECK: (func $work (type $ref|$struct|_=>_none) (param $struct (ref $struct)) ;; CHECK-NEXT: (struct.set $struct 0 diff --git a/test/lit/passes/type-ssa_and_merging.wast b/test/lit/passes/type-ssa_and_merging.wast index 291b37cc2..a642660db 100644 --- a/test/lit/passes/type-ssa_and_merging.wast +++ b/test/lit/passes/type-ssa_and_merging.wast @@ -1,25 +1,27 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --nominal --closed-world --gufa -Os -all -S -o - | filecheck %s --check-prefix NOP -;; RUN: foreach %s %t wasm-opt --nominal --closed-world --type-ssa --gufa -Os --type-merging -all -S -o - | filecheck %s --check-prefix YES +;; RUN: foreach %s %t wasm-opt --closed-world --gufa -Os -all -S -o - | filecheck %s --check-prefix NOP +;; RUN: foreach %s %t wasm-opt --closed-world --type-ssa --gufa -Os --type-merging -all -S -o - | filecheck %s --check-prefix YES ;; Show that the combination of type-ssa and type-merging can find things that ;; otherwise cannot be optimized. NOP will fail to optimize something that YES ;; can. (module - ;; NOP: (type $A (struct (field i32))) + ;; NOP: (rec + ;; NOP-NEXT: (type $ref|$A|_=>_i32 (func (param (ref $A)) (result i32))) + + ;; NOP: (type $A (struct (field i32))) ;; YES: (type $none_=>_i32 (func (result i32))) - ;; YES: (type $A (struct )) + ;; YES: (rec + ;; YES-NEXT: (type $ref|$A|_=>_none (func (param (ref $A)))) + + ;; YES: (type $A (struct )) (type $A (struct_subtype (field (mut i32)) data)) ;; NOP: (type $none_=>_i32 (func (result i32))) - ;; NOP: (type $ref|$A|_=>_i32 (func (param (ref $A)) (result i32))) - ;; NOP: (import "a" "b" (func $import (result i32))) - ;; YES: (type $ref|$A|_=>_none (func (param (ref $A)))) - ;; YES: (import "a" "b" (func $import (result i32))) (import "a" "b" (func $import (result i32))) |