diff --git a/src/passes/GlobalTypeOptimization.cpp b/src/passes/GlobalTypeOptimization.cpp index b2ba23b0feb..3d006468fc7 100644 --- a/src/passes/GlobalTypeOptimization.cpp +++ b/src/passes/GlobalTypeOptimization.cpp @@ -22,6 +22,7 @@ // * Fields that are never read from can be removed entirely. // +#include "ir/eh-utils.h" #include "ir/localize.h" #include "ir/module-utils.h" #include "ir/ordering.h" @@ -453,6 +454,8 @@ struct GlobalTypeOptimization : public Pass { return std::make_unique(parent); } + bool needEHFixups = false; + void visitStructNew(StructNew* curr) { if (curr->type == Type::unreachable) { return; @@ -476,6 +479,8 @@ struct GlobalTypeOptimization : public Pass { ChildLocalizer localizer( curr, getFunction(), *getModule(), getPassOptions()); replaceCurrent(localizer.getReplacement()); + // Adding a block here requires EH fixups. + needEHFixups = true; // Remove and reorder operands. Index removed = 0; @@ -519,6 +524,7 @@ struct GlobalTypeOptimization : public Pass { getFunction(), getModule(), getPassOptions()); + needEHFixups = true; Expression* replacement = builder.makeDrop(builder.makeRefAs(RefAsNonNull, flipped)); if (curr->order == MemoryOrder::SeqCst) { @@ -545,6 +551,12 @@ struct GlobalTypeOptimization : public Pass { curr->index = newIndex; } + void visitFunction(Function* curr) { + if (needEHFixups) { + EHUtils::handleBlockNestedPops(curr, *getModule()); + } + } + private: Index getNewIndex(HeapType type, Index index) { auto iter = parent.indexesAfterRemovals.find(type); diff --git a/test/lit/passes/gto-removals.wast b/test/lit/passes/gto-removals.wast index 6ab126611ea..e02430a3634 100644 --- a/test/lit/passes/gto-removals.wast +++ b/test/lit/passes/gto-removals.wast @@ -1627,3 +1627,55 @@ ) ) ) + +;; A struct with a pop, which requires EH fixups to avoid popping in a nested +;; block. +(module + (type $i32 (func (param i32))) + + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (struct)) + (type $struct (struct (field (mut i32)))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (type $i32 (func (param i32))) + + ;; CHECK: (tag $tag (type $i32) (param i32)) + (tag $tag (type $i32) (param i32)) + + ;; CHECK: (func $func (type $1) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $tag + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref $struct)) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.new_default $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $func + (try + (do + ) + (catch $tag + (drop + (struct.new $struct + (pop i32) + ) + ) + ) + ) + ) +)