diff options
-rw-r--r-- | src/cfg/liveness-traversal.h | 22 | ||||
-rw-r--r-- | test/lit/passes/coalesce-locals-gc-nn.wast | 23 | ||||
-rw-r--r-- | test/lit/passes/coalesce-locals-gc.wast | 4 |
3 files changed, 47 insertions, 2 deletions
diff --git a/src/cfg/liveness-traversal.h b/src/cfg/liveness-traversal.h index 6deab2fd6..ee56a9925 100644 --- a/src/cfg/liveness-traversal.h +++ b/src/cfg/liveness-traversal.h @@ -118,7 +118,27 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { auto* curr = (*currp)->cast<LocalGet>(); // if in unreachable code, ignore if (!self->currBasicBlock) { - *currp = Builder(*self->getModule()).replaceWithIdenticalType(curr); + Builder builder(*self->getModule()); + auto* rep = builder.replaceWithIdenticalType(curr); + if (rep->is<LocalGet>()) { + // We failed to replace the node with something simpler. This can happen + // if the local is non-nullable, for example. We still need to remove + // this node entirely, however, as it is unreachable and so we will not + // see it in the analysis we perform (for example, we may be changing + // local indexes). Replace it with something completely different, even + // if it is larger, a block with a forced type that has unreachable + // contents, + // + // (block (result X) + // (unreachable)) + // + // That pattern lets us set any type we wish. + // + // TODO: Make a helper function for this, if it is useful in other + // places. + rep = builder.makeBlock({builder.makeUnreachable()}, curr->type); + } + *currp = rep; return; } self->currBasicBlock->contents.actions.emplace_back( diff --git a/test/lit/passes/coalesce-locals-gc-nn.wast b/test/lit/passes/coalesce-locals-gc-nn.wast index 21b6d2a33..69e96ac62 100644 --- a/test/lit/passes/coalesce-locals-gc-nn.wast +++ b/test/lit/passes/coalesce-locals-gc-nn.wast @@ -81,4 +81,27 @@ ) ) ) + + ;; CHECK: (func $unreachable-get-of-non-nullable + ;; CHECK-NEXT: (local $0 (ref any)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref any)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $unreachable-get-of-non-nullable + ;; One local is unused entirely, the other is used but only in unreachable + ;; code. It does not really matter what we do here (coalesce, or not), but we + ;; should emit valid IR. Normally we would apply a constant to replace the + ;; local.get, however, the types here are non-nullable, so we must do + ;; something else. + (local $unused (ref any)) + (local $used-in-unreachable (ref any)) + (unreachable) + (drop + (local.get $used-in-unreachable) + ) + ) ) diff --git a/test/lit/passes/coalesce-locals-gc.wast b/test/lit/passes/coalesce-locals-gc.wast index 2c0fc3f03..52243ebc8 100644 --- a/test/lit/passes/coalesce-locals-gc.wast +++ b/test/lit/passes/coalesce-locals-gc.wast @@ -11,7 +11,9 @@ ;; CHECK: (func $test-dead-get-non-nullable (param $0 dataref) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (block (result dataref) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-dead-get-non-nullable (param $func (ref data)) |