summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-11-09 16:43:04 -0800
committerGitHub <noreply@github.com>2021-11-09 16:43:04 -0800
commitb260f1cc65096a7784da8ef8ad25a067e0480e5b (patch)
treee470790d9d230191660a7d96541da81e8ee3c77b /test
parenta0325162a4b54f795d17550bff2b565c379dde51 (diff)
downloadbinaryen-b260f1cc65096a7784da8ef8ad25a067e0480e5b.tar.gz
binaryen-b260f1cc65096a7784da8ef8ad25a067e0480e5b.tar.bz2
binaryen-b260f1cc65096a7784da8ef8ad25a067e0480e5b.zip
OptimizeInstructions: Fix static cast optimizations (#4311)
We found one cast that has another as its input, and forgot that the child was possibly a fallthrough value. That is, there might be more code that needs to be kept around. Rather than fix the middle of the three cases there - the one with HeapType::isSubType(childIntendedType, intendedType) - I noticed it is not actually needed. That case checks if the child's type is more specific than the parent's, and if so, then the parent is not needed. But we already handle that earlier above in the same function: regardless of what the child of a static cast is, if the cast is static and the input is the proper type already, the cast is unneeded (see lines 1565-1566).
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/optimize-instructions-gc.wast211
1 files changed, 207 insertions, 4 deletions
diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast
index 691d259d3..01bee7a76 100644
--- a/test/lit/passes/optimize-instructions-gc.wast
+++ b/test/lit/passes/optimize-instructions-gc.wast
@@ -18,12 +18,14 @@
;; NOMNL: (type $A (struct_subtype (field i32) data))
(type $A (struct (field i32)))
+ ;; CHECK: (type $B (struct (field i32) (field i32) (field f32)))
+
;; CHECK: (type $array (array (mut i8)))
+ ;; NOMNL: (type $B (struct_subtype (field i32) (field i32) (field f32) $A))
+
;; NOMNL: (type $array (array_subtype (mut i8) data))
(type $array (array (mut i8)))
- ;; CHECK: (type $B (struct (field i32) (field i32) (field f32)))
- ;; NOMNL: (type $B (struct_subtype (field i32) (field i32) (field f32) $A))
(type $B (struct_subtype (field i32) (field i32) (field f32) $A))
;; CHECK: (type $B-child (struct (field i32) (field i32) (field f32) (field i64)))
@@ -2414,6 +2416,199 @@
)
)
+ ;; CHECK: (func $ref-cast-static-fallthrough-remaining (param $x eqref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref null $B))
+ ;; CHECK-NEXT: (call $ref-cast-static-fallthrough-remaining
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.cast_static $B
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $ref-cast-static-fallthrough-remaining (param $x eqref)
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (block (result (ref null $B))
+ ;; NOMNL-NEXT: (call $ref-cast-static-fallthrough-remaining
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (ref.cast_static $B
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $ref-cast-static-fallthrough-remaining (param $x eqref)
+ (drop
+ (ref.cast_static $A
+ (block (result (ref null $B))
+ ;; Additional contents in between redundant casts must be preserved.
+ ;; That is, when we see that the casts are redundant, by seeing that
+ ;; the fallthrough value reaching the outer cast is already cast, we
+ ;; can avoid a duplicate cast, but we do still need to keep any code
+ ;; in the middle, as it may have side effects.
+ ;;
+ ;; In this first testcase, the outer cast is not needed as the inside
+ ;; is already a more specific type.
+ (call $ref-cast-static-fallthrough-remaining
+ (local.get $x)
+ )
+ (ref.cast_static $B
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $ref-cast-static-fallthrough-remaining-child (param $x eqref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast_static $B
+ ;; CHECK-NEXT: (block (result eqref)
+ ;; CHECK-NEXT: (call $ref-cast-static-fallthrough-remaining-child
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $ref-cast-static-fallthrough-remaining-child (param $x eqref)
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (ref.cast_static $B
+ ;; NOMNL-NEXT: (block (result eqref)
+ ;; NOMNL-NEXT: (call $ref-cast-static-fallthrough-remaining-child
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (ref.cast_static $A
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $ref-cast-static-fallthrough-remaining-child (param $x eqref)
+ (drop
+ ;; As above, but with $A and $B flipped. Now the inner cast is not needed.
+ ;; However, we do not remove it, as it may be necessary for validation,
+ ;; and we hope other opts help out here.
+ (ref.cast_static $B
+ (block (result (eqref))
+ (call $ref-cast-static-fallthrough-remaining-child
+ (local.get $x)
+ )
+ (ref.cast_static $A
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $ref-cast-static-fallthrough-remaining-impossible (param $x (ref eq))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref $array))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref eq))
+ ;; CHECK-NEXT: (call $ref-cast-static-fallthrough-remaining-impossible
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.cast_static $struct
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $ref-cast-static-fallthrough-remaining-impossible (param $x (ref eq))
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (block (result (ref $array))
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (block (result (ref eq))
+ ;; NOMNL-NEXT: (call $ref-cast-static-fallthrough-remaining-impossible
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (ref.cast_static $struct
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (unreachable)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $ref-cast-static-fallthrough-remaining-impossible (param $x (ref eq))
+ (drop
+ ;; As above, but with an impossible cast of an array to a struct. The
+ ;; block with the side effects and the inner cast must be kept around and
+ ;; dropped, and then we replace the outer cast with an unreachable.
+ (ref.cast_static $array
+ (block (result (ref eq))
+ (call $ref-cast-static-fallthrough-remaining-impossible
+ (local.get $x)
+ )
+ (ref.cast_static $struct
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $ref-cast-static-fallthrough-remaining-nonnull (param $x (ref eq))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast_static $A
+ ;; CHECK-NEXT: (block (result (ref eq))
+ ;; CHECK-NEXT: (call $ref-cast-static-fallthrough-remaining
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.cast_static $B
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; NOMNL: (func $ref-cast-static-fallthrough-remaining-nonnull (param $x (ref eq))
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (ref.cast_static $A
+ ;; NOMNL-NEXT: (block (result (ref eq))
+ ;; NOMNL-NEXT: (call $ref-cast-static-fallthrough-remaining
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (ref.cast_static $B
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ (func $ref-cast-static-fallthrough-remaining-nonnull (param $x (ref eq))
+ ;; The input is non-nullable here, and the middle block is of a simpler
+ ;; type than either the parent or the child. This checks that we do not
+ ;; mis-optimize this case: In general the outer cast is not needed, but
+ ;; the middle block prevents us from seeing that (after other opts run,
+ ;; however, we would).
+ (drop
+ (ref.cast_static $A
+ (block (result (ref eq))
+ (call $ref-cast-static-fallthrough-remaining
+ (local.get $x)
+ )
+ (ref.cast_static $B
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
+
;; CHECK: (func $ref-cast-static-squared-impossible (param $x eqref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast_static $struct
@@ -2425,7 +2620,11 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.cast_static $array
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
@@ -2442,7 +2641,11 @@
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (block (result (ref $struct))
;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: (ref.as_non_null
+ ;; NOMNL-NEXT: (ref.cast_static $array
+ ;; NOMNL-NEXT: (local.get $x)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (unreachable)
;; NOMNL-NEXT: )