summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/gc-type-utils.h14
-rw-r--r--test/lit/passes/remove-unused-brs-gc.wast105
2 files changed, 116 insertions, 3 deletions
diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h
index 80a935462..584cde83c 100644
--- a/src/ir/gc-type-utils.h
+++ b/src/ir/gc-type-utils.h
@@ -52,9 +52,19 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) {
// We don't check nullability here.
case BrOnNull:
case BrOnNonNull:
- // Casts can only be known at runtime using RTTs.
- case BrOnCast:
case BrOnCastFail:
+ flip = true;
+ [[fallthrough]];
+ case BrOnCast:
+ if (!br->rtt) {
+ // This is a static cast check, which we may be able to resolve at
+ // compile time. Note that the type must be non-nullable for us to
+ // succeed at that inference, as otherwise a null can make us fail.
+ if (Type::isSubType(br->ref->type,
+ Type(br->intendedType, NonNullable))) {
+ return flip ? Failure : Success;
+ }
+ }
return Unknown;
case BrOnNonFunc:
flip = true;
diff --git a/test/lit/passes/remove-unused-brs-gc.wast b/test/lit/passes/remove-unused-brs-gc.wast
index 10b316900..458958d62 100644
--- a/test/lit/passes/remove-unused-brs-gc.wast
+++ b/test/lit/passes/remove-unused-brs-gc.wast
@@ -111,5 +111,108 @@
(unreachable)
)
)
-)
+ ;; CHECK: (func $br_on_cast_static (result (ref $struct))
+ ;; CHECK-NEXT: (local $temp (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: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_static (result (ref $struct))
+ (local $temp (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.
+ (br_on_cast_static $block $struct
+ (struct.new $struct)
+ )
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_cast_static_no (result (ref $struct))
+ ;; CHECK-NEXT: (local $temp (ref null $struct))
+ ;; CHECK-NEXT: (block $block (result (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_static $block $struct
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_static_no (result (ref $struct))
+ (local $temp (ref null $struct))
+ (block $block (result (ref $struct))
+ (drop
+ (br_on_cast_static $block $struct
+ ;; As above, but now the type is nullable, so we cannot infer anything.
+ (ref.null $struct)
+ )
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_cast_fail_static (result (ref $struct))
+ ;; CHECK-NEXT: (local $temp (ref null $struct))
+ ;; CHECK-NEXT: (block $block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_fail_static (result (ref $struct))
+ (local $temp (ref null $struct))
+ (block $block (result (ref $struct))
+ (drop
+ ;; As $br_on_cast_static, but this checks for a failing cast, so we know it will
+ ;; *not* be taken.
+ (br_on_cast_static_fail $block $struct
+ (struct.new $struct)
+ )
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $br_on_cast_dynamic (result (ref $struct))
+ ;; CHECK-NEXT: (local $temp (ref null $struct))
+ ;; CHECK-NEXT: (block $block (result (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $block
+ ;; CHECK-NEXT: (struct.new_default_with_rtt $struct
+ ;; CHECK-NEXT: (rtt.canon $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (rtt.canon $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br_on_cast_dynamic (result (ref $struct))
+ (local $temp (ref null $struct))
+ (block $block (result (ref $struct))
+ (drop
+ ;; This dynamic cast happens to be optimizable since we see both sides use
+ ;; rtt.canon, but we do not inspect things that closely, and leave such
+ ;; dynamic casts to runtime.
+ (br_on_cast $block
+ (struct.new_with_rtt $struct
+ (rtt.canon $struct)
+ )
+ (rtt.canon $struct)
+ )
+ )
+ (unreachable)
+ )
+ )
+)