diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/type-merging-tnh.wast | 6 | ||||
-rw-r--r-- | test/lit/passes/type-merging.wast | 350 |
2 files changed, 285 insertions, 71 deletions
diff --git a/test/lit/passes/type-merging-tnh.wast b/test/lit/passes/type-merging-tnh.wast index 5099760de..fefdb00e3 100644 --- a/test/lit/passes/type-merging-tnh.wast +++ b/test/lit/passes/type-merging-tnh.wast @@ -88,12 +88,10 @@ ) ) -;; call_indirect should not inhibit merging if traps never happen, but we -;; haven't implemented function type merging yet TODO. +;; call_indirect should not inhibit merging if traps never happen. (module ;; CHECK: (type $A (func)) (type $A (func)) - ;; CHECK: (type $B (func_subtype $A)) (type $B (func_subtype $A)) (table 1 1 (ref null $A)) @@ -101,7 +99,7 @@ ;; CHECK: (table $0 1 1 (ref null $A)) ;; CHECK: (func $test (type $A) - ;; CHECK-NEXT: (call_indirect $0 (type $B) + ;; CHECK-NEXT: (call_indirect $0 (type $A) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/type-merging.wast b/test/lit/passes/type-merging.wast index b4b84f94a..578d84ebb 100644 --- a/test/lit/passes/type-merging.wast +++ b/test/lit/passes/type-merging.wast @@ -1,30 +1,41 @@ ;; 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-merging -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --hybrid --closed-world --type-merging --remove-unused-types -all -S -o - | filecheck %s (module - ;; CHECK: (type $A (struct (field i32))) - (type $A (struct_subtype (field i32) data)) - (type $B (struct_subtype (field i32) $A)) - ;; CHECK: (type $D (struct_subtype (field i32) $A)) - - ;; CHECK: (type $none_=>_none (func)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field anyref))) + (type $A (struct_subtype (field anyref) data)) + (type $B (struct_subtype (field anyref) $A)) + ;; CHECK: (type $F (struct_subtype (field anyref) $A)) + + ;; CHECK: (type $E (struct_subtype (field eqref) $A)) + + ;; CHECK: (type $D (struct_subtype (field (ref any)) $A)) + + ;; CHECK: (type $C (struct_subtype (field anyref) (field f64) $A)) + (type $C (struct_subtype (field anyref) (field f64) $A)) + (type $D (struct_subtype (field (ref any)) $A)) + (type $E (struct_subtype (field eqref) $A)) + (type $F (struct_subtype (field anyref) $A)) + ) - ;; CHECK: (type $C (struct_subtype (field i32) (field f64) $A)) - (type $C (struct_subtype (field i32) (field f64) $A)) - (type $D (struct_subtype (field i32) $A)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $a (ref null $A)) ;; CHECK-NEXT: (local $b (ref null $A)) ;; CHECK-NEXT: (local $c (ref null $C)) ;; CHECK-NEXT: (local $d (ref null $D)) + ;; CHECK-NEXT: (local $e (ref null $E)) + ;; CHECK-NEXT: (local $f (ref null $F)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.cast null $A ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast null $D + ;; CHECK-NEXT: (ref.cast null $F ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -36,8 +47,12 @@ (local $b (ref null $B)) ;; $C cannot because it adds a field. (local $c (ref null $C)) - ;; $D cannot because it has a cast. + ;; $D cannot because it refines a field's nullability. (local $d (ref null $D)) + ;; $E cannot because it refines a field's heap type. + (local $e (ref null $E)) + ;; $F cannot because it has a cast. + (local $f (ref null $F)) ;; A cast of $A has no effect. (drop @@ -45,9 +60,9 @@ (local.get $a) ) ) - ;; A cast of $D prevents it from being merged. + ;; A cast of $F prevents it from being merged. (drop - (ref.cast null $D + (ref.cast null $F (local.get $a) ) ) @@ -56,17 +71,18 @@ ;; Multiple levels of merging. (module - ;; CHECK: (type $A (struct (field i32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field i32))) (type $A (struct_subtype (field i32) data)) (type $B (struct_subtype (field i32) $A)) (type $C (struct_subtype (field i32) $B)) - ;; CHECK: (type $D (struct_subtype (field i32) (field f64) $A)) + ;; CHECK: (type $D (struct_subtype (field i32) (field f64) $A)) (type $D (struct_subtype (field i32) (field f64) $A)) (type $E (struct_subtype (field i32) (field f64) $D)) (type $F (struct_subtype (field i32) (field f64) $E)) (type $G (struct_subtype (field i32) (field f64) $F)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $a (ref null $A)) @@ -99,19 +115,18 @@ ;; in which we have two "merge points" that things get merged into. The results ;; should remain the same as before, everything merged into either $A or $D. (module - ;; CHECK: (type $A (struct (field i32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field i32))) (type $A (struct_subtype (field i32) data)) - ;; CHECK: (type $B (struct_subtype (field i32) $A)) (type $B (struct_subtype (field i32) $A)) - ;; CHECK: (type $C (struct_subtype (field i32) $B)) (type $C (struct_subtype (field i32) $B)) - ;; CHECK: (type $D (struct_subtype (field i32) (field f64) $C)) + ;; CHECK: (type $D (struct_subtype (field i32) (field f64) $A)) (type $D (struct_subtype (field i32) (field f64) $C)) ;; this line changed (type $E (struct_subtype (field i32) (field f64) $D)) (type $F (struct_subtype (field i32) (field f64) $E)) (type $G (struct_subtype (field i32) (field f64) $F)) - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $a (ref null $A)) @@ -135,13 +150,17 @@ ) (module - ;; CHECK: (type $A (struct (field (ref null $A)))) - (type $A (struct_subtype (field (ref null $A)) data)) - (type $B (struct_subtype (field (ref null $A)) $A)) - ;; CHECK: (type $none_=>_none (func)) - - ;; CHECK: (type $C (struct_subtype (field (ref null $A)) $A)) - (type $C (struct_subtype (field (ref null $B)) $A)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $X (struct )) + (type $X (struct)) + (type $Y (struct_subtype $X)) + ;; CHECK: (type $A (struct (field (ref null $X)))) + (type $A (struct (field (ref null $X)))) + (type $B (struct_subtype (field (ref null $Y)) $A)) + ;; CHECK: (type $C (struct_subtype (field (ref $X)) $A)) + (type $C (struct_subtype (field (ref $Y)) $A)) + + ;; CHECK: (type $none_=>_none (func)) ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $a (ref null $A)) @@ -150,24 +169,136 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $foo - ;; $A will remain the same. + ;; B can be merged into A even though it refines A's field because that + ;; refinement will no longer happen after X and Y are also merged. + (local $a (ref null $A)) + (local $b (ref null $B)) + ;; C cannot be merged because it refines the field's nullability. + (local $c (ref null $C)) + ) +) + +(module + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (ref null $A)))) + (type $A (struct (ref null $A))) + (type $B (struct_subtype (ref null $B) $A)) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (local $a (ref null $A)) + ;; CHECK-NEXT: (local $b (ref null $A)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + ;; A recursive subtype can be merged even though its field is a refinement + ;; as well. + (local $a (ref null $A)) + (local $b (ref null $B)) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $X (struct (field (ref null $A)) (field f32))) + + ;; CHECK: (type $A (struct (field (ref null $X)) (field i32))) + (type $A (struct (ref null $X) i32)) + (type $B (struct_subtype (ref null $Y) i32 $A)) + (type $X (struct (ref null $A) f32)) + (type $Y (struct_subtype (ref null $B) f32 $X)) + ) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (local $a (ref null $A)) + ;; CHECK-NEXT: (local $b (ref null $A)) + ;; CHECK-NEXT: (local $x (ref null $X)) + ;; CHECK-NEXT: (local $y (ref null $X)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + ;; Two mutually referential chains, A->B and X->Y, can be merged into a pair + ;; of mutually referential types. + (local $a (ref null $A)) + (local $b (ref null $B)) + (local $x (ref null $X)) + (local $y (ref null $Y)) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $X (struct (field (ref null $A)))) + + ;; CHECK: (type $A (struct (field (ref null $X)))) + (type $A (struct (ref null $X))) + (type $B (struct_subtype (ref null $Y) $A)) + (type $X (struct (ref null $A))) + (type $Y (struct_subtype (ref null $B) $X)) + ) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (local $a (ref null $A)) + ;; CHECK-NEXT: (local $b (ref null $A)) + ;; CHECK-NEXT: (local $x (ref null $X)) + ;; CHECK-NEXT: (local $y (ref null $X)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + ;; As above, but now the A->B and X->Y chains are not differentiated by the + ;; i32 and f32, so all four types can be merged into a single type. + ;; TODO: This is not yet implemented. Merge the top level types. + (local $a (ref null $A)) + (local $b (ref null $B)) + (local $x (ref null $X)) + (local $y (ref null $Y)) + ) +) + +(module + ;; CHECK: (rec + ;; CHECK-NEXT: (type $X (struct (field anyref))) + (type $X (struct anyref)) + ;; CHECK: (type $Y (struct_subtype (field eqref) $X)) + (type $Y (struct_subtype eqref $X)) + ;; CHECK: (type $A (struct (field (ref null $X)))) + (type $A (struct (ref null $X))) + ;; CHECK: (type $B (struct_subtype (field (ref null $Y)) $A)) + (type $B (struct_subtype (ref null $Y) $A)) + (type $C (struct_subtype (ref null $Y) $A)) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (local $a (ref null $A)) + ;; CHECK-NEXT: (local $b (ref null $B)) + ;; CHECK-NEXT: (local $c (ref null $B)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + ;; B and C cannot be merged into A because they refine A's field, but B and + ;; C can still be merged with each other even though they are siblings. (local $a (ref null $A)) - ;; $B can be merged into $A. (local $b (ref null $B)) - ;; $C refines the field, so it cannot be merged. However, separately, in - ;; the type definition of $C, its field of type $B should become $A. That - ;; is, $B should no longer be used anywhere. (local $c (ref null $C)) ) ) ;; Check that we refinalize properly. (module - ;; CHECK: (type $A (struct )) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct )) (type $A (struct)) (type $B (struct_subtype $A)) - ;; CHECK: (type $none_=>_ref?|$A| (func (result (ref null $A)))) + ;; CHECK: (type $none_=>_ref?|$A| (func (result (ref null $A)))) ;; CHECK: (func $returner (type $none_=>_ref?|$A|) (result (ref null $A)) ;; CHECK-NEXT: (local $local (ref null $A)) @@ -189,24 +320,29 @@ ;; into $D. While doing so we must update the fields and the expressions that ;; they appear in, and not error. (module - ;; CHECK: (type $C (struct (field (mut i32)))) - - ;; CHECK: (type $D (struct_subtype (field (mut i32)) (field (mut i32)) $C)) - - ;; CHECK: (type $I (array (mut (ref null $C)))) - (type $I (array (mut (ref null $C)))) - (type $C (struct (field (mut i32)))) - (type $D (struct_subtype (field (mut i32)) (field (mut i32)) $C)) - (type $E (struct_subtype (field (mut i32)) (field (mut i32)) $D)) - (type $F (struct_subtype (field (mut i32)) (field (mut i32)) $E)) - (type $D$to-merge (struct_subtype (field (mut i32)) (field (mut i32)) $F)) - ;; CHECK: (type $G (func (param (ref $C)) (result (ref $D)))) - (type $G (func (param (ref $C)) (result (ref $D)))) - ;; CHECK: (type $H (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $D))) $D)) - (type $H (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) $D)) - ;; CHECK: (type $A (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $D))) (field (mut i64)) (field (mut (ref null $I))) $H)) - (type $A (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) (field (mut i64)) (field (mut (ref null $I))) $H)) - (type $A$to-merge (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) (field (mut i64)) (field (mut (ref null $I))) $A)) + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $C (struct (field (mut i32)))) + + ;; CHECK: (type $D (struct_subtype (field (mut i32)) (field (mut i32)) $C)) + + ;; CHECK: (type $H (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $D))) $D)) + + ;; CHECK: (type $A (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $D))) (field (mut i64)) (field (mut (ref null $I))) $H)) + + ;; CHECK: (type $I (array (mut (ref null $C)))) + (type $I (array (mut (ref null $C)))) + (type $C (struct (field (mut i32)))) + (type $D (struct_subtype (field (mut i32)) (field (mut i32)) $C)) + (type $E (struct_subtype (field (mut i32)) (field (mut i32)) $D)) + (type $F (struct_subtype (field (mut i32)) (field (mut i32)) $E)) + (type $D$to-merge (struct_subtype (field (mut i32)) (field (mut i32)) $F)) + ;; CHECK: (type $G (func (param (ref $C)) (result (ref $D)))) + (type $G (func (param (ref $C)) (result (ref $D)))) + (type $H (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) $D)) + (type $A (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) (field (mut i64)) (field (mut (ref null $I))) $H)) + (type $A$to-merge (struct_subtype (field (mut i32)) (field (mut i32)) (field (mut (ref null $E))) (field (mut i64)) (field (mut (ref null $I))) $A)) + ) ;; CHECK: (global $global$0 (ref $D) (struct.new $D ;; CHECK-NEXT: (i32.const 1705) @@ -238,18 +374,21 @@ ;; Arrays (module - ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $refarray (array anyref)) - ;; CHECK: (type $intarray (array (mut i32))) + ;; CHECK: (type $sub-refarray-nn (array_subtype (ref any) $refarray)) + + ;; CHECK: (type $intarray (array (mut i32))) (type $intarray (array (mut i32))) (type $sub-intarray (array_subtype (mut i32) $intarray)) - ;; CHECK: (type $refarray (array anyref)) (type $refarray (array (ref null any))) (type $sub-refarray (array_subtype (ref null any) $refarray)) - ;; CHECK: (type $sub-refarray-nn (array_subtype (ref any) $refarray)) (type $sub-refarray-nn (array_subtype (ref any) $refarray)) + ;; CHECK: (type $none_=>_none (func)) + ;; CHECK: (func $foo (type $none_=>_none) ;; CHECK-NEXT: (local $a (ref null $intarray)) ;; CHECK-NEXT: (local $b (ref null $intarray)) @@ -277,15 +416,90 @@ ) ) -;; Check that a ref.test inhibits merging (ref.cast is already checked above). +;; Function types (module - ;; CHECK: (type $ref|$A|_=>_i32 (func (param (ref $A)) (result i32))) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $func (func (param eqref))) + (type $func (func (param eqref))) + (type $sub-func (func_subtype (param eqref) $func)) + ;; CHECK: (type $sub-func-refined (func_subtype (param anyref) $func)) + (type $sub-func-refined (func_subtype (param anyref) $func)) + + ;; CHECK: (type $none_=>_none (func)) - ;; CHECK: (type $A (struct )) + ;; CHECK: (func $foo (type $none_=>_none) + ;; CHECK-NEXT: (local $a (ref null $func)) + ;; CHECK-NEXT: (local $b (ref null $func)) + ;; CHECK-NEXT: (local $c (ref null $sub-func-refined)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + ;; $func will remain the same. + (local $a (ref null $func)) + ;; $sub-func will be merged into $func. + (local $b (ref null $sub-func)) + ;; $sub-func-refined will not be merged into $func because it refines a result. + (local $c (ref null $sub-func-refined)) + ) +) + +;; Check that public types are not merged. +(module + ;; CHECK: (type $A (func)) + (type $A (func)) ;; public + ;; CHECK: (type $B (func_subtype $A)) + (type $B (func_subtype $A)) ;; public + (type $C (func_subtype $B)) ;; private + + ;; CHECK: (type $ref|$A|_ref|$B|_ref|$B|_=>_none (func (param (ref $A) (ref $B) (ref $B)))) + + ;; CHECK: (export "foo" (func $foo)) + (export "foo" (func $foo)) + ;; CHECK: (export "bar" (func $bar)) + (export "bar" (func $bar)) + + ;; A stays the same. + ;; CHECK: (func $foo (type $A) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $foo (type $A) + (unreachable) + ) + + ;; B is not merged because it is public. + ;; CHECK: (func $bar (type $B) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $bar (type $B) + (unreachable) + ) + + ;; C can be merged into B because it is private. + ;; CHECK: (func $baz (type $B) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $baz (type $C) + (unreachable) + ) + + ;; CHECK: (func $quux (type $ref|$A|_ref|$B|_ref|$B|_=>_none) (param $0 (ref $A)) (param $1 (ref $B)) (param $2 (ref $B)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $quux (param (ref $A) (ref $B) (ref $C)) + (unreachable) + ) +) + +;; Check that a ref.test inhibits merging (ref.cast is already checked above). +(module + ;; 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) @@ -300,12 +514,13 @@ ;; Check that a br_on_cast inhibits merging. (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)) @@ -346,9 +561,10 @@ ;; Check that a call_indirect inhibits merging. (module - ;; CHECK: (type $A (func)) + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (func)) (type $A (func)) - ;; CHECK: (type $B (func_subtype $A)) + ;; CHECK: (type $B (func_subtype $A)) (type $B (func_subtype $A)) (table 1 1 (ref null $A)) |