diff --git a/src/passes/GlobalTypeOptimization.cpp b/src/passes/GlobalTypeOptimization.cpp index 833ad433949..0124f4715d2 100644 --- a/src/passes/GlobalTypeOptimization.cpp +++ b/src/passes/GlobalTypeOptimization.cpp @@ -761,6 +761,18 @@ struct GlobalTypeOptimization : public Pass { curr->index = newIndex; } + void visitRefCast(RefCast* curr) { + // Unreachable ref.cast_desc instructions would not have been counted as + // reading the descriptor field, so their descriptor operands may no + // longer be descriptors. This is invalid, so replace such casts + // entirely. + if (curr->type == Type::unreachable && curr->desc && + curr->desc->type != Type::unreachable) { + assert(curr->ref->type == Type::unreachable); + replaceCurrent(curr->ref); + } + } + void visitFunction(Function* curr) { if (needEHFixups) { EHUtils::handleBlockNestedPops(curr, *getModule()); diff --git a/test/lit/passes/gto-desc.wast b/test/lit/passes/gto-desc.wast index 0a5e0a60c09..77b051db95b 100644 --- a/test/lit/passes/gto-desc.wast +++ b/test/lit/passes/gto-desc.wast @@ -1396,3 +1396,34 @@ ) ) +;; We can optimize away the descriptor since its only use is unreachable, but +;; we must update that use to remain valid despite being unreachable. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (sub (struct))) + (type $struct (sub (descriptor $desc (struct)))) + ;; CHECK: (type $desc (sub (struct))) + (type $desc (sub (describes $struct (struct)))) + ) + ;; CHECK: (type $2 (func)) + + ;; CHECK: (func $test (type $2) + ;; CHECK-NEXT: (local $struct (ref $struct)) + ;; CHECK-NEXT: (local $desc (ref $desc)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (local $struct (ref $struct)) + (local $desc (ref $desc)) + (drop + (ref.cast_desc (ref $struct) + (unreachable) + (struct.new $desc) + ) + ) + ) +) +