From 787bfef178cfdfa44f80a125723bb78753afd2c0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 15 Apr 2026 10:21:32 -0700 Subject: [PATCH 1/2] fix --- src/passes/Heap2Local.cpp | 7 +++ test/lit/passes/heap2local-desc.wast | 65 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) 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..e6aeaf0bc8a 100644 --- a/test/lit/passes/heap2local-desc.wast +++ b/test/lit/passes/heap2local-desc.wast @@ -1360,3 +1360,68 @@ ) ) ) + +(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) + ) + (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) + ) + ) + ) + ) +) + From 9269945034f8f9d8dcc8bd53d88f701289dfc3f3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 15 Apr 2026 10:23:30 -0700 Subject: [PATCH 2/2] comment --- test/lit/passes/heap2local-desc.wast | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lit/passes/heap2local-desc.wast b/test/lit/passes/heap2local-desc.wast index e6aeaf0bc8a..b6467beb544 100644 --- a/test/lit/passes/heap2local-desc.wast +++ b/test/lit/passes/heap2local-desc.wast @@ -1410,6 +1410,9 @@ (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)