From 8319869a30e4bea4085e494ec59aa9c5d9984d27 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 21 Sep 2026 10:24:28 -0700 Subject: [PATCH] Handle new block-nested pops in Unsubtyping --- src/passes/Unsubtyping.cpp | 9 +++++ test/lit/passes/unsubtyping-desc.wast | 52 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index 50fcb54f10d..c4bd9885e48 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -19,6 +19,7 @@ #include #include "ir/effects.h" +#include "ir/eh-utils.h" #include "ir/js-utils.h" #include "ir/localize.h" #include "ir/module-utils.h" @@ -1073,6 +1074,7 @@ struct Unsubtyping : Pass, Noter { // collecting and iterating over all the types, though. struct Rewriter : WalkerPass> { const TypeTree& types; + bool needEHFixups = false; // Allocations that might trap that have been removed from module-level // initializers. These need to be placed in new globals to preserve any @@ -1114,6 +1116,7 @@ struct Unsubtyping : Pass, Noter { block->list.push_back(curr); block->type = curr->type; replaceCurrent(block); + needEHFixups = true; } else { // We are dropping this descriptor, but it might have a potential trap // nested inside it. In that case we need to preserve the trap by @@ -1125,6 +1128,12 @@ struct Unsubtyping : Pass, Noter { } curr->desc = nullptr; } + + void visitFunction(Function* curr) { + if (needEHFixups) { + EHUtils::handleBlockNestedPops(curr, *getModule()); + } + } }; Rewriter rewriter(types); diff --git a/test/lit/passes/unsubtyping-desc.wast b/test/lit/passes/unsubtyping-desc.wast index 95a42735a4e..787d9338335 100644 --- a/test/lit/passes/unsubtyping-desc.wast +++ b/test/lit/passes/unsubtyping-desc.wast @@ -1240,3 +1240,55 @@ ) ) ) + +;; Removing a descriptor from a struct.new_desc uses ChildLocalizer, which wraps +;; the allocation in a block. Ensure any nested `pop` inside a catch block is +;; hoisted out of the new block so the IR remains valid. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct)) + (type $A (descriptor $A.desc) (struct)) + ;; CHECK: (type $A.desc (struct)) + (type $A.desc (describes $A) (struct)) + ) + ;; CHECK: (type $2 (func (param (ref (exact $A.desc))))) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (tag $e (type $2) (param (ref (exact $A.desc)))) + (tag $e (param (ref (exact $A.desc)))) + ;; CHECK: (func $pop-in-struct-new-desc (type $3) + ;; CHECK-NEXT: (local $0 (ref (exact $A.desc))) + ;; CHECK-NEXT: (local $1 (ref (exact $A.desc))) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop (ref (exact $A.desc))) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref (exact $A))) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.new_default $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $pop-in-struct-new-desc + (try + (do) + (catch $e + (drop + (struct.new_desc $A + (pop (ref (exact $A.desc))) + ) + ) + ) + ) + ) +)