summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cfg/liveness-traversal.h22
-rw-r--r--test/lit/passes/coalesce-locals-gc-nn.wast23
-rw-r--r--test/lit/passes/coalesce-locals-gc.wast4
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))