diff options
-rw-r--r-- | src/ir/gc-type-utils.h | 14 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-brs-gc.wast | 105 |
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) + ) + ) +) |