diff options
Diffstat (limited to 'test/lit')
-rw-r--r-- | test/lit/passes/inlining-optimizing.wast | 10 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-call_ref.wast | 16 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc-iit.wast | 112 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 232 |
4 files changed, 317 insertions, 53 deletions
diff --git a/test/lit/passes/inlining-optimizing.wast b/test/lit/passes/inlining-optimizing.wast index 31e9aaa7a..ac29ba1f9 100644 --- a/test/lit/passes/inlining-optimizing.wast +++ b/test/lit/passes/inlining-optimizing.wast @@ -4,7 +4,6 @@ (module ;; CHECK: (type $none_=>_none (func)) (type $none_=>_none (func)) - ;; CHECK: (type $none_=>_i32 (func (result i32))) (type $none_=>_i32 (func (result i32))) ;; CHECK: (func $0 ;; CHECK-NEXT: (nop) @@ -15,10 +14,7 @@ ;; CHECK: (func $1 ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_ref - ;; CHECK-NEXT: (ref.cast - ;; CHECK-NEXT: (ref.func $0) - ;; CHECK-NEXT: (rtt.canon $none_=>_i32) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -28,7 +24,9 @@ ;; where it inlines, for efficiency). As part of the optimiziations, we will ;; try to precompute the cast here, which will try to look up $0. We should ;; not hit an assertion, rather we should skip precomputing it, the same as if - ;; we were optimizing $1 before $0 were added to the module. + ;; we were optimizing $1 before $0 were added to the module. (In fact, we will + ;; be able to see that the cast cannot succeed, and will optimize it into an + ;; unreachable.) (call $0) (drop (call_ref diff --git a/test/lit/passes/optimize-instructions-call_ref.wast b/test/lit/passes/optimize-instructions-call_ref.wast index 0a45b9e4d..b3b781b1d 100644 --- a/test/lit/passes/optimize-instructions-call_ref.wast +++ b/test/lit/passes/optimize-instructions-call_ref.wast @@ -143,18 +143,24 @@ ;; CHECK: (func $fallthrough-bad-type (result i32) ;; CHECK-NEXT: (call_ref - ;; CHECK-NEXT: (ref.cast - ;; CHECK-NEXT: (ref.func $return-nothing) - ;; CHECK-NEXT: (rtt.canon $none_=>_i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.func $return-nothing) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (rtt.canon $none_=>_i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $fallthrough-bad-type (result i32) ;; A fallthrough appears here, and we cast the function type to something else ;; in a way that is bad: the actual target function has a different return - ;; type than the cast type. The cast will fail at runtime, and we should not + ;; type than the cast type. The cast will definitely fail, and we should not ;; emit non-validating code here, which would happen if we replace the - ;; call_ref that returns nothing with a call that returns an i32. + ;; call_ref that returns nothing with a call that returns an i32. In fact, we + ;; end up optimizing the cast into an unreachable. (call_ref (ref.cast (ref.func $return-nothing) diff --git a/test/lit/passes/optimize-instructions-gc-iit.wast b/test/lit/passes/optimize-instructions-gc-iit.wast index cc1bb3b85..41d4e69d5 100644 --- a/test/lit/passes/optimize-instructions-gc-iit.wast +++ b/test/lit/passes/optimize-instructions-gc-iit.wast @@ -57,9 +57,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast - ;; CHECK-NEXT: (local.get $child) - ;; CHECK-NEXT: (local.get $other-rtt) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $child) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $other-rtt) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -87,9 +92,14 @@ ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.cast - ;; NOMNL-NEXT: (local.get $child) - ;; NOMNL-NEXT: (local.get $other-rtt) + ;; NOMNL-NEXT: (block + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $child) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $other-rtt) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) @@ -117,9 +127,14 @@ ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: (drop - ;; NOMNL-TNH-NEXT: (ref.cast - ;; NOMNL-TNH-NEXT: (local.get $child) - ;; NOMNL-TNH-NEXT: (local.get $other-rtt) + ;; NOMNL-TNH-NEXT: (block + ;; NOMNL-TNH-NEXT: (drop + ;; NOMNL-TNH-NEXT: (local.get $child) + ;; NOMNL-TNH-NEXT: ) + ;; NOMNL-TNH-NEXT: (drop + ;; NOMNL-TNH-NEXT: (local.get $other-rtt) + ;; NOMNL-TNH-NEXT: ) + ;; NOMNL-TNH-NEXT: (unreachable) ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) @@ -132,28 +147,31 @@ (param $child-rtt (rtt $child)) (param $other-rtt (rtt $other)) - ;; a cast of parent to an rtt of parent: static subtyping matches. + ;; a cast of parent to an rtt of parent: assuming no traps as we do, we can + ;; optimize this as the new type will be valid. (drop (ref.cast (local.get $parent) (local.get $parent-rtt) ) ) - ;; a cast of child to a supertype: static subtyping matches. + ;; a cast of child to a supertype: again, we replace with a valid type. (drop (ref.cast (local.get $child) (local.get $parent-rtt) ) ) - ;; a cast of parent to a subtype: static subtyping does not match. + ;; a cast of parent to a subtype: we cannot replace the original heap type + ;; $child with one that is not equal or more specific, like $parent, so we + ;; cannot optimize here. (drop (ref.cast (local.get $parent) (local.get $child-rtt) ) ) - ;; a cast of child to an unrelated type: static subtyping does not match. + ;; a cast of child to an unrelated type: it will trap anyhow (drop (ref.cast (local.get $child) @@ -163,15 +181,23 @@ ) ;; CHECK: (func $ref-cast-iit-bad (param $parent (ref $parent)) (param $parent-rtt (rtt $parent)) + ;; CHECK-NEXT: (local $2 (ref null $parent)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast - ;; CHECK-NEXT: (block $block (result (ref $parent)) - ;; CHECK-NEXT: (call $foo) - ;; CHECK-NEXT: (local.get $parent) + ;; CHECK-NEXT: (block (result (ref $parent)) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (block $block (result (ref $parent)) + ;; CHECK-NEXT: (call $foo) + ;; CHECK-NEXT: (local.get $parent) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $block0 (result (rtt $parent)) - ;; CHECK-NEXT: (call $foo) - ;; CHECK-NEXT: (local.get $parent-rtt) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $block0 (result (rtt $parent)) + ;; CHECK-NEXT: (call $foo) + ;; CHECK-NEXT: (local.get $parent-rtt) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -189,15 +215,23 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $ref-cast-iit-bad (param $parent (ref $parent)) (param $parent-rtt (rtt $parent)) + ;; NOMNL-NEXT: (local $2 (ref null $parent)) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.cast - ;; NOMNL-NEXT: (block $block (result (ref $parent)) - ;; NOMNL-NEXT: (call $foo) - ;; NOMNL-NEXT: (local.get $parent) + ;; NOMNL-NEXT: (block (result (ref $parent)) + ;; NOMNL-NEXT: (local.set $2 + ;; NOMNL-NEXT: (block $block (result (ref $parent)) + ;; NOMNL-NEXT: (call $foo) + ;; NOMNL-NEXT: (local.get $parent) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (block $block0 (result (rtt $parent)) - ;; NOMNL-NEXT: (call $foo) - ;; NOMNL-NEXT: (local.get $parent-rtt) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block $block0 (result (rtt $parent)) + ;; NOMNL-NEXT: (call $foo) + ;; NOMNL-NEXT: (local.get $parent-rtt) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (local.get $2) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) @@ -215,15 +249,23 @@ ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-TNH: (func $ref-cast-iit-bad (param $parent (ref $parent)) (param $parent-rtt (rtt $parent)) + ;; NOMNL-TNH-NEXT: (local $2 (ref null $parent)) ;; NOMNL-TNH-NEXT: (drop - ;; NOMNL-TNH-NEXT: (ref.cast - ;; NOMNL-TNH-NEXT: (block $block (result (ref $parent)) - ;; NOMNL-TNH-NEXT: (call $foo) - ;; NOMNL-TNH-NEXT: (local.get $parent) + ;; NOMNL-TNH-NEXT: (block (result (ref $parent)) + ;; NOMNL-TNH-NEXT: (local.set $2 + ;; NOMNL-TNH-NEXT: (block $block (result (ref $parent)) + ;; NOMNL-TNH-NEXT: (call $foo) + ;; NOMNL-TNH-NEXT: (local.get $parent) + ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) - ;; NOMNL-TNH-NEXT: (block $block0 (result (rtt $parent)) - ;; NOMNL-TNH-NEXT: (call $foo) - ;; NOMNL-TNH-NEXT: (local.get $parent-rtt) + ;; NOMNL-TNH-NEXT: (drop + ;; NOMNL-TNH-NEXT: (block $block0 (result (rtt $parent)) + ;; NOMNL-TNH-NEXT: (call $foo) + ;; NOMNL-TNH-NEXT: (local.get $parent-rtt) + ;; NOMNL-TNH-NEXT: ) + ;; NOMNL-TNH-NEXT: ) + ;; NOMNL-TNH-NEXT: (ref.as_non_null + ;; NOMNL-TNH-NEXT: (local.get $2) ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) ;; NOMNL-TNH-NEXT: ) @@ -244,7 +286,7 @@ (param $parent (ref $parent)) (param $parent-rtt (rtt $parent)) - ;; ignore due to the inability to reorder + ;; optimizing this cast away requires reordering. (drop (ref.cast (block (result (ref $parent)) diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index 27c966f52..2f6ae858d 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -14,22 +14,23 @@ (field $i64 (mut i64)) )) - ;; CHECK: (type $empty (struct )) - ;; NOMNL: (type $empty (struct )) - (type $empty (struct)) + ;; CHECK: (type $array (array (mut i8))) + ;; NOMNL: (type $array (array (mut i8))) + (type $array (array (mut i8))) ;; CHECK: (type $B (struct (field i32) (field i32) (field f32))) ;; NOMNL: (type $B (struct (field i32) (field i32) (field f32)) (extends $A)) (type $B (struct (field i32) (field i32) (field f32)) (extends $A)) + ;; CHECK: (type $empty (struct )) + ;; NOMNL: (type $empty (struct )) + (type $empty (struct)) + ;; CHECK: (type $C (struct (field i32) (field i32) (field f64))) ;; NOMNL: (type $C (struct (field i32) (field i32) (field f64)) (extends $A)) (type $C (struct (field i32) (field i32) (field f64)) (extends $A)) - ;; CHECK: (type $array (array (mut i8))) - ;; NOMNL: (type $array (array (mut i8))) - (type $array (array (mut i8))) - + ;; CHECK: (type $A (struct (field i32))) ;; NOMNL: (type $A (struct (field i32))) (type $A (struct (field i32))) @@ -1665,4 +1666,221 @@ ) ) ) + + ;; CHECK: (func $incompatible-cast-of-non-null (param $struct (ref $struct)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (rtt.canon $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $incompatible-cast-of-non-null (param $struct (ref $struct)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (rtt.canon $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $incompatible-cast-of-non-null (param $struct (ref $struct)) + (drop + (ref.cast + (local.get $struct) + (rtt.canon $array) + ) + ) + ) + + ;; CHECK: (func $incompatible-cast-of-null + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref null $array)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (rtt.canon $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (block (result (ref null $array)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (rtt.canon $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $incompatible-cast-of-null + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result (ref null $array)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.null $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (rtt.canon $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (ref.null $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (block (result (ref null $array)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (ref.null $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (rtt.canon $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (ref.null $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $incompatible-cast-of-null + (drop + (ref.cast + (ref.null $struct) + (rtt.canon $array) + ) + ) + (drop + (ref.cast + ;; The fallthrough is null, but the node's child's type is non-nullable, + ;; so we must add a ref.as_non_null on the outside to keep the type + ;; identical. + (ref.as_non_null + (ref.null $struct) + ) + (rtt.canon $array) + ) + ) + ) + + ;; CHECK: (func $incompatible-cast-of-unknown (param $struct (ref null $struct)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (rtt.canon $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $incompatible-cast-of-unknown (param $struct (ref null $struct)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.cast + ;; NOMNL-NEXT: (local.get $struct) + ;; NOMNL-NEXT: (rtt.canon $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $incompatible-cast-of-unknown (param $struct (ref null $struct)) + (drop + (ref.cast + (local.get $struct) + (rtt.canon $array) + ) + ) + ) + + ;; CHECK: (func $incompatible-test (param $struct (ref null $struct)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (rtt.canon $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $incompatible-test (param $struct (ref null $struct)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (rtt.canon $array) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 0) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $incompatible-test (param $struct (ref null $struct)) + (drop + ;; This test will definitely fail, so we can turn it into 0. + (ref.test + (local.get $struct) + (rtt.canon $array) + ) + ) + ) + + ;; CHECK: (func $subtype-compatible (param $A (ref null $A)) (param $B (ref null $B)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.test + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (rtt.canon $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.test + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (rtt.canon $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $subtype-compatible (param $A (ref null $A)) (param $B (ref null $B)) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.test + ;; NOMNL-NEXT: (local.get $A) + ;; NOMNL-NEXT: (rtt.canon $B) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.test + ;; NOMNL-NEXT: (local.get $B) + ;; NOMNL-NEXT: (rtt.canon $A) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $subtype-compatible (param $A (ref null $A)) (param $B (ref null $B)) + (drop + ;; B is a subtype of A, so this can work. + (ref.test + (local.get $A) + (rtt.canon $B) + ) + ) + (drop + ;; The other direction works too. + (ref.test + (local.get $B) + (rtt.canon $A) + ) + ) + ) ) |