summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/OptimizeInstructions.cpp41
-rw-r--r--test/lit/passes/optimize-instructions-gc.wast46
2 files changed, 53 insertions, 34 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index ad51d7cd4..6b2a5a1cd 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1595,7 +1595,10 @@ struct OptimizeInstructions
}
}
- if (ref->type.isNull()) {
+ auto fallthrough =
+ Properties::getFallthrough(ref, getPassOptions(), *getModule());
+
+ if (fallthrough->type.isNull()) {
replaceCurrent(
getDroppedChildrenAndAppend(curr, builder.makeUnreachable()));
// Propagate the unreachability.
@@ -1864,6 +1867,10 @@ struct OptimizeInstructions
return;
}
+ if (curr->type.isNonNullable() && trapOnNull(curr, curr->ref)) {
+ return;
+ }
+
Builder builder(*getModule());
auto& passOptions = getPassOptions();
@@ -1873,27 +1880,21 @@ struct OptimizeInstructions
auto intendedType = curr->type.getHeapType();
// If the value is a null, then we know a nullable cast will succeed and a
- // non-nullable cast will fail. Either way, we do not need the cast.
- // However, we have to avoid changing the type when replacing a cast with
+ // non-nullable cast will fail. Either way, we do not need the cast. We've
+ // already handled the non-nullable case above, so all we have left is a
+ // nullable one.
+ // Note that we have to avoid changing the type when replacing a cast with
// its potentially more refined child, e.g.
// (ref.cast null (ref.as_non_null (.. (ref.null)))
- if (fallthrough->is<RefNull>()) {
- if (curr->type.isNullable()) {
- // Replace the expression to drop the input and directly produce the
- // null.
- replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
- builder.makeRefNull(intendedType)));
- return;
- // TODO: The optimal ordering of this and the other ref.as_non_null
- // stuff later down in this functions is unclear and may be worth
- // looking into.
- } else {
- // The cast will trap on the null, so replace it with an unreachable
- // wrapped in a block of the original type.
- replaceCurrent(builder.makeSequence(
- builder.makeDrop(curr->ref), builder.makeUnreachable(), curr->type));
- return;
- }
+ if (fallthrough->is<RefNull>() && curr->type.isNullable()) {
+ // Replace the expression to drop the input and directly produce the
+ // null.
+ replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
+ builder.makeRefNull(intendedType)));
+ return;
+ // TODO: The optimal ordering of this and the other ref.as_non_null
+ // stuff later down in this functions is unclear and may be worth
+ // looking into.
}
// For the cast to be able to succeed, the value being cast must be a
diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast
index fe18c015b..592ec80cd 100644
--- a/test/lit/passes/optimize-instructions-gc.wast
+++ b/test/lit/passes/optimize-instructions-gc.wast
@@ -1791,15 +1791,10 @@
;; CHECK: (func $incompatible-cast-of-null (type $void)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $array))
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.null none)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $array))
+ ;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null none)
@@ -1811,15 +1806,10 @@
;; CHECK-NEXT: )
;; NOMNL: (func $incompatible-cast-of-null (type $void)
;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (block (result (ref $array))
- ;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (ref.null none)
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (unreachable)
- ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (unreachable)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (block (result (ref $array))
+ ;; NOMNL-NEXT: (block
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (ref.as_non_null
;; NOMNL-NEXT: (ref.null none)
@@ -2178,6 +2168,16 @@
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $a
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NOMNL: (func $ref-cast-static-null (type $void)
;; NOMNL-NEXT: (local $a (ref null $A))
@@ -2215,6 +2215,16 @@
;; NOMNL-NEXT: (ref.null none)
;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (block
+ ;; NOMNL-NEXT: (drop
+ ;; NOMNL-NEXT: (local.tee $a
+ ;; NOMNL-NEXT: (ref.null none)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: (unreachable)
+ ;; NOMNL-NEXT: )
+ ;; NOMNL-NEXT: )
;; NOMNL-NEXT: )
(func $ref-cast-static-null
(local $a (ref null $A))
@@ -2242,6 +2252,14 @@
)
)
)
+ ;; A non-null cast of a falling-though null will trap.
+ (drop
+ (ref.cast $A
+ (local.tee $a
+ (ref.null $A)
+ )
+ )
+ )
)
;; CHECK: (func $ref-cast-static-general (type $ref?|$A|_ref?|$B|_=>_none) (param $a (ref null $A)) (param $b (ref null $B))