diff options
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 41 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 46 |
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)) |