summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/passes/remove-unused-brs-gc.wast449
-rw-r--r--test/lit/passes/remove-unused-brs_all-features.wast121
2 files changed, 497 insertions, 73 deletions
diff --git a/test/lit/passes/remove-unused-brs-gc.wast b/test/lit/passes/remove-unused-brs-gc.wast
index 94dbabc6b..0d0a94cd8 100644
--- a/test/lit/passes/remove-unused-brs-gc.wast
+++ b/test/lit/passes/remove-unused-brs-gc.wast
@@ -6,9 +6,11 @@
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (struct ))
- (type $struct (struct))
+ (type $struct (sub (struct)))
;; CHECK: (type $struct2 (struct ))
(type $struct2 (struct))
+ ;; CHECK: (type $substruct (sub $struct (struct )))
+ (type $substruct (sub $struct (struct)))
)
;; CHECK: (func $br_on-if (type $ref|struct|_=>_none) (param $0 (ref struct))
@@ -43,21 +45,49 @@
)
;; CHECK: (func $br_on_cast (type $none_=>_ref|$struct|) (result (ref $struct))
+ ;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (block $block (result (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result nullref)
+ ;; CHECK-NEXT: (br_on_non_null $block
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $block (ref $struct) (ref $substruct)
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast (result (ref $struct))
+ (local $struct (ref null $struct))
(block $block (result (ref $struct))
(drop
- ;; This static cast can be computed at compile time: it will definitely be
- ;; taken, so we can turn it into a normal br.
+ ;; This cast can be computed at compile time: it will definitely be taken,
+ ;; so we can turn it into a normal br.
+ (br_on_cast $block anyref (ref $struct)
+ (struct.new $struct)
+ )
+ )
+ (drop
+ ;; This cast can be partially computed at compile time, but we still need to
+ ;; do a null check.
(br_on_cast $block anyref (ref $struct)
+ (local.get $struct)
+ )
+ )
+ (drop
+ ;; This cast cannot be optimized at all.
+ (br_on_cast $block anyref (ref $substruct)
(struct.new $struct)
)
)
@@ -65,6 +95,69 @@
)
)
+ ;; CHECK: (func $br_on_cast-fallthrough (type $none_=>_ref|$struct|) (result (ref $struct))
+ ;; CHECK-NEXT: (local $struct (ref null $struct))
+ ;; CHECK-NEXT: (local $any anyref)
+ ;; CHECK-NEXT: (block $block (result (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br $block
+ ;; CHECK-NEXT: (ref.cast $struct
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result nullref)
+ ;; CHECK-NEXT: (br_on_non_null $block
+ ;; CHECK-NEXT: (ref.cast null $struct
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $block anyref (ref $substruct)
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast-fallthrough (result (ref $struct))
+ ;; Same as above, but now the type information comes from fallthrough values.
+ (local $struct (ref null $struct))
+ (local $any anyref)
+ (block $block (result (ref $struct))
+ (drop
+ ;; Definitely taken, but will need a cast for validity.
+ (br_on_cast $block anyref (ref $struct)
+ (local.tee $any (struct.new $struct))
+ )
+ )
+ (drop
+ ;; Needs a null check and cast for validity.
+ (br_on_cast $block anyref (ref $struct)
+ (local.tee $any (local.get $struct))
+ )
+ )
+ (drop
+ ;; This cannot be optimized, but at least it still doesn't need an
+ ;; additional cast.
+ (br_on_cast $block anyref (ref $substruct)
+ (local.tee $any (struct.new $struct))
+ )
+ )
+ (unreachable)
+ )
+ )
+
;; CHECK: (func $nested_br_on_cast (type $none_=>_i31ref) (result i31ref)
;; CHECK-NEXT: (block $label$1 (result (ref i31))
;; CHECK-NEXT: (drop
@@ -136,8 +229,9 @@
)
)
(drop
- ;; But if both are nullable, then we can't optimize because the cast would
- ;; succeed if the value is a null.
+ ;; But if both are nullable, then the cast will succeed only if the value is
+ ;; null, so we can partially optimize.
+ ;; TODO: Optimize this.
(br_on_cast $block anyref (ref null $struct)
(local.get $nullable-struct2)
)
@@ -146,6 +240,172 @@
)
)
+ ;; CHECK: (func $br_on_cast_unrelated-fallthrough (type $none_=>_ref?|$struct|) (result (ref null $struct))
+ ;; CHECK-NEXT: (local $any anyref)
+ ;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
+ ;; CHECK-NEXT: (block $block (result nullref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $block anyref nullref
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_unrelated-fallthrough (result (ref null $struct))
+ ;; Same as above, but now all the type information comes from fallthrough values.
+ (local $any anyref)
+ (local $nullable-struct2 (ref null $struct2))
+ (block $block (result (ref null $struct))
+ (drop
+ ;; Definitely not taken.
+ (br_on_cast $block anyref (ref $struct)
+ (local.tee $any (struct.new $struct2))
+ )
+ )
+ (drop
+ ;; Still not taken.
+ (br_on_cast $block anyref (ref null $struct)
+ (local.tee $any (struct.new $struct2))
+ )
+ )
+ (drop
+ ;; Also not taken.
+ (br_on_cast $block anyref (ref $struct)
+ (local.tee $any (local.get $nullable-struct2))
+ )
+ )
+ (drop
+ ;; Taken only if null.
+ ;; TODO: Optimize this.
+ (br_on_cast $block anyref (ref null $struct)
+ (local.tee $any (local.get $nullable-struct2))
+ )
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_cast_fail (type $none_=>_anyref) (result anyref)
+ ;; CHECK-NEXT: (local $struct (ref null $struct))
+ ;; CHECK-NEXT: (block $block (result (ref null $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $block (ref null $struct) (ref $struct)
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $block (ref $struct) (ref $substruct)
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_fail (result anyref)
+ (local $struct (ref null $struct))
+ (block $block (result anyref)
+ (drop
+ ;; This cast can be computed at compile time: it will definitely succeed so
+ ;; the branch will not be taken.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (struct.new $struct)
+ )
+ )
+ (drop
+ ;; This cast can be partially computed at compile time, but we still need to
+ ;; do a null check.
+ ;; TODO: optimize this.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (local.get $struct)
+ )
+ )
+ (drop
+ ;; This cast cannot be optimized at all.
+ (br_on_cast_fail $block anyref (ref $substruct)
+ (struct.new $struct)
+ )
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_cast_fail-fallthrough (type $none_=>_anyref) (result anyref)
+ ;; CHECK-NEXT: (local $any anyref)
+ ;; CHECK-NEXT: (local $struct (ref null $struct))
+ ;; CHECK-NEXT: (block $block (result anyref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast $struct
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $block anyref (ref $struct)
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $block anyref (ref $substruct)
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_fail-fallthrough (result anyref)
+ ;; Same as above, but now the type information comes from fallthrough values.
+ (local $any anyref)
+ (local $struct (ref null $struct))
+ (block $block (result anyref)
+ (drop
+ ;; This cast will succeed. We will need a cast for validity.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (local.tee $any (struct.new $struct))
+ )
+ )
+ (drop
+ ;; We will still need a null check.
+ ;; TODO: optimize this.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (local.tee $any (local.get $struct))
+ )
+ )
+ (drop
+ ;; This cast cannot be optimized at all.
+ (br_on_cast_fail $block anyref (ref $substruct)
+ (local.tee $any (struct.new $struct))
+ )
+ )
+ (unreachable)
+ )
+ )
+
;; CHECK: (func $br_on_cast_fail_unrelated (type $none_=>_anyref) (result anyref)
;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
;; CHECK-NEXT: (block $block (result (ref null $struct2))
@@ -165,8 +425,11 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_cast_fail $block (ref null $struct2) nullref
- ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: (block (result nullref)
+ ;; CHECK-NEXT: (br_on_non_null $block
+ ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
@@ -195,8 +458,8 @@
)
)
(drop
- ;; But if both are nullable, then we can't optimize because the cast would
- ;; succeed if the value is a null.
+ ;; But if both are nullable, then we can only partially optimize because we
+ ;; still have to do a null check.
(br_on_cast_fail $block anyref (ref null $struct)
(local.get $nullable-struct2)
)
@@ -205,70 +468,130 @@
)
)
- ;; CHECK: (func $br_on_cast_no (type $none_=>_ref|$struct|) (result (ref $struct))
- ;; CHECK-NEXT: (local $struct (ref null $struct))
- ;; CHECK-NEXT: (block $block (result (ref $struct))
+ ;; CHECK: (func $br_on_cast_fail_unrelated-fallthrough (type $none_=>_anyref) (result anyref)
+ ;; CHECK-NEXT: (local $any anyref)
+ ;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
+ ;; CHECK-NEXT: (block $block (result anyref)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_cast $block (ref null $struct) (ref $struct)
- ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (br $block
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br $block
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (struct.new_default $struct2)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $br_on_cast_no (result (ref $struct))
- (local $struct (ref null $struct))
- (block $block (result (ref $struct))
- (drop
- (br_on_cast $block anyref (ref $struct)
- ;; As above, but now the type is nullable, so we cannot infer anything.
- (local.get $struct)
- )
- )
- (unreachable)
- )
- )
-
- ;; CHECK: (func $br_on_cast_nullable (type $none_=>_ref?|$struct|) (result (ref null $struct))
- ;; CHECK-NEXT: (block $block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result nullref)
+ ;; CHECK-NEXT: (br_on_non_null $block
+ ;; CHECK-NEXT: (local.tee $any
+ ;; CHECK-NEXT: (local.get $nullable-struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $br_on_cast_nullable (result (ref null $struct))
- (block $block (result (ref null $struct))
+ (func $br_on_cast_fail_unrelated-fallthrough (result anyref)
+ ;; Same as above, but now type information comes from fallthrough values.
+ (local $any anyref)
+ (local $nullable-struct2 (ref null $struct2))
+ (block $block (result anyref)
(drop
- (br_on_cast $block anyref (ref null $struct)
- ;; As above, but now the cast allows nulls, so we can optimize.
- (ref.null $struct)
+ ;; Will definitely take the branch.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (local.tee $any (struct.new $struct2))
+ )
+ )
+ (drop
+ ;; Ditto.
+ (br_on_cast_fail $block anyref (ref null $struct)
+ (local.tee $any (struct.new $struct2))
+ )
+ )
+ (drop
+ ;; Ditto.
+ (br_on_cast_fail $block anyref (ref $struct)
+ (local.tee $any (local.get $nullable-struct2))
+ )
+ )
+ (drop
+ ;; Still has to do a null check.
+ (br_on_cast_fail $block anyref (ref null $struct)
+ (local.tee $any (local.get $nullable-struct2))
)
)
(unreachable)
)
)
- ;; CHECK: (func $br_on_cast_fail (type $none_=>_ref|$struct|) (result (ref $struct))
+ ;; CHECK: (func $br_on_cast-unreachable (type $i31ref_=>_anyref) (param $i31ref i31ref) (result anyref)
;; CHECK-NEXT: (block $block
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref none))
+ ;; CHECK-NEXT: (ref.cast none
+ ;; CHECK-NEXT: (block (result i31ref)
+ ;; CHECK-NEXT: (local.get $i31ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref none))
+ ;; CHECK-NEXT: (ref.cast none
+ ;; CHECK-NEXT: (block (result i31ref)
+ ;; CHECK-NEXT: (local.get $i31ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $br_on_cast_fail (result (ref $struct))
- (block $block (result (ref $struct))
+ (func $br_on_cast-unreachable (param $i31ref i31ref) (result anyref)
+ ;; Optimize out br_on_cast* where the input is uninhabitable.
+ (block $block (result anyref)
(drop
- ;; As $br_on_cast, but this checks for a failing cast, so we know it will
- ;; *not* be taken.
- (br_on_cast_fail $block anyref (ref $struct)
- (struct.new $struct)
+ (br_on_cast $block anyref (ref i31)
+ (block (result anyref)
+ (ref.cast struct
+ (block (result anyref)
+ (local.get $i31ref)
+ )
+ )
+ )
+ )
+ )
+ (br_on_cast_fail $block anyref (ref i31)
+ (block (result anyref)
+ (ref.cast struct
+ (block (result anyref)
+ (local.get $i31ref)
+ )
+ )
)
)
- (unreachable)
)
)
@@ -284,7 +607,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (if (result anyref)
+ ;; CHECK-NEXT: (if (result nullref)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (ref.cast null none
@@ -293,26 +616,34 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (if (result anyref)
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: (block $something (result anyref)
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_cast $something (ref null $struct) (ref $struct)
- ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (select (result (ref null $struct))
+ ;; CHECK-NEXT: (block (result (ref null $struct))
+ ;; CHECK-NEXT: (block $something (result (ref null $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result nullref)
+ ;; CHECK-NEXT: (br_on_non_null $something
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (select (result anyref)
- ;; CHECK-NEXT: (block (result anyref)
+ ;; CHECK-NEXT: (select (result nullref)
+ ;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (block $nothing
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_null $nothing
- ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $nothing)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
diff --git a/test/lit/passes/remove-unused-brs_all-features.wast b/test/lit/passes/remove-unused-brs_all-features.wast
index c93f25df2..3482afc29 100644
--- a/test/lit/passes/remove-unused-brs_all-features.wast
+++ b/test/lit/passes/remove-unused-brs_all-features.wast
@@ -10,8 +10,6 @@
(type $struct (struct (field (ref null $vector))))
;; CHECK: (type $i32_=>_none (func (param i32)))
- ;; CHECK: (type $none_=>_funcref (func (result funcref)))
-
;; CHECK: (type $none_=>_ref?|$struct| (func (result (ref null $struct))))
;; CHECK: (type $none_=>_f64 (func (result f64)))
@@ -20,8 +18,14 @@
;; CHECK: (type $i32_=>_funcref (func (param i32) (result funcref)))
+ ;; CHECK: (type $funcref_=>_none (func (param funcref)))
+
;; CHECK: (type $none_=>_none (func))
+ ;; CHECK: (type $funcref_=>_funcref (func (param funcref) (result funcref)))
+
+ ;; CHECK: (type $none_=>_funcref (func (result funcref)))
+
;; CHECK: (import "out" "log" (func $log (type $i32_=>_none) (param i32)))
(import "out" "log" (func $log (param i32)))
;; CHECK: (elem declare func $br_on_non_null $br_on_null $i32_=>_none $none_=>_i32)
@@ -118,22 +122,29 @@
)
)
- ;; CHECK: (func $br_on_null (type $none_=>_none)
+ ;; CHECK: (func $br_on_null (type $funcref_=>_none) (param $x funcref)
;; CHECK-NEXT: (block $null
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_null $null
- ;; CHECK-NEXT: (ref.null nofunc)
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null nofunc)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $null)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $br_on_null)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_null $null
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $br_on_null
+ (func $br_on_null (param $x funcref)
(block $null
;; A null reference to bottom is definitely null, and the br is always taken.
- ;; TODO: Optimize this.
(drop
(br_on_null $null (ref.null nofunc))
)
@@ -142,21 +153,66 @@
(drop
(br_on_null $null (ref.func $br_on_null))
)
+ ;; If we don't know whether the input is null, we can't optimize.
+ (drop
+ (br_on_null $null (local.get $x))
+ )
)
)
- ;; CHECK: (func $br_on_non_null (type $none_=>_funcref) (result funcref)
- ;; CHECK-NEXT: (block $non-null (result (ref $none_=>_funcref))
+ ;; CHECK: (func $br_on_null-fallthrough (type $none_=>_none)
+ ;; CHECK-NEXT: (local $x funcref)
+ ;; CHECK-NEXT: (block $null
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $x
+ ;; CHECK-NEXT: (ref.null nofunc)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $null)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.tee $x
+ ;; CHECK-NEXT: (ref.func $br_on_null)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_null-fallthrough
+ ;; This is the same as above, but now the necessary type information comes
+ ;; from fallthrough values.
+ (local $x funcref)
+ (block $null
+ ;; Definitely taken.
+ (drop
+ (br_on_null $null (local.tee $x (ref.null nofunc)))
+ )
+ ;; Definitely not taken. Optimizable, but still requires a cast for validity.
+ (drop
+ (br_on_null $null (local.tee $x (ref.func $br_on_null)))
+ )
+ )
+ )
+
+ ;; CHECK: (func $br_on_non_null (type $funcref_=>_funcref) (param $x funcref) (result funcref)
+ ;; CHECK-NEXT: (block $non-null (result (ref func))
;; CHECK-NEXT: (br $non-null
;; CHECK-NEXT: (ref.func $br_on_non_null)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (br_on_non_null $non-null
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null nofunc)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.func $br_on_non_null)
+ ;; CHECK-NEXT: (br_on_non_null $non-null
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- (func $br_on_non_null (result funcref)
+ (func $br_on_non_null (param $x funcref) (result funcref)
(block $non-null (result (ref func))
;; A non-null reference is not null, and the br is always taken.
(br_on_non_null $non-null
@@ -164,11 +220,48 @@
)
;; On the other hand, if we know the input is null, the branch will never be
;; taken.
- ;; TODO: Optimize this.
(br_on_non_null $non-null
(ref.null nofunc)
)
- (ref.func $br_on_non_null)
+ ;; If we don't know whether the input is null, we can't optimize.
+ (br_on_non_null $non-null
+ (local.get $x)
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_non_null-fallthrough (type $none_=>_funcref) (result funcref)
+ ;; CHECK-NEXT: (local $x funcref)
+ ;; CHECK-NEXT: (block $non-null (result (ref func))
+ ;; CHECK-NEXT: (br $non-null
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.tee $x
+ ;; CHECK-NEXT: (ref.func $br_on_non_null)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $x
+ ;; CHECK-NEXT: (ref.null nofunc)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_non_null-fallthrough (result funcref)
+ ;; Same as above, but now using fallthrough values.
+ (local $x funcref)
+ (block $non-null (result (ref func))
+ ;; Definitely taken. Requires cast.
+ (br_on_non_null $non-null
+ (local.tee $x (ref.func $br_on_non_null))
+ )
+ ;; Definitely not taken.
+ (br_on_non_null $non-null
+ (local.tee $x (ref.null nofunc))
+ )
+ (unreachable)
)
)
)