diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp index ca5a4a3413c..59c1e7e3fc5 100644 --- a/src/passes/Heap2Local.cpp +++ b/src/passes/Heap2Local.cpp @@ -862,6 +862,13 @@ struct Struct2Local : PostWalker { return; } + if (curr->type == Type::unreachable) { + // We must not modify unreachable code here, as we will replace it with a + // const, which has a concrete type (similar to the situation with + // local.get in other cases in this pass). + return; + } + // This test operates on the allocation, which means we can compute whether // it will succeed statically. We do not even need // GCTypeUtils::evaluateCastCheck because we know the allocation's type diff --git a/test/lit/passes/heap2local-desc.wast b/test/lit/passes/heap2local-desc.wast index a6673463504..b6467beb544 100644 --- a/test/lit/passes/heap2local-desc.wast +++ b/test/lit/passes/heap2local-desc.wast @@ -1360,3 +1360,71 @@ ) ) ) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (descriptor $desc) (struct)) + (type $struct (descriptor $desc) (struct)) + ;; CHECK: (type $desc (sub (describes $struct) (struct))) + (type $desc (sub (describes $struct) (struct))) + ) + + ;; CHECK: (type $2 (func)) + + ;; CHECK: (func $test (type $2) + ;; CHECK-NEXT: (local $temp (ref $desc)) + ;; CHECK-NEXT: (local $1 (ref none)) + ;; CHECK-NEXT: (local $2 (ref none)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.test (ref none) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (local $temp (ref $desc)) + (local.set $temp + (struct.new_default $desc) + ) + ;; The ref.test's input will become unreachable after we optimize. We should + ;; not emit a const for the test result, even though we know it, as this is + ;; unreachable code which would not validate. + (drop + (ref.test (ref none) + (ref.cast_desc_eq (ref $struct) + (struct.new_default_desc $struct + (ref.as_non_null + (ref.null none) + ) + ) + (local.get $temp) + ) + ) + ) + ) +) +