From 2a9b853ffee0c7e79c60f55bac49d4c98d271a90 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 15:39:14 -0800 Subject: [PATCH 1/5] [AbstractTypeRefining] Skip unreachable casts Skip `Preoptimizer::visitRefCast`, `visitRefGetDesc`, and `visitBrOn` when an operand is unreachable (either already unreachable in the input or replaced with an unreachable block earlier in `Preoptimizer`), updating `curr->type` to `Type::unreachable` so parent expressions also see the unreachability and `ReFinalize` cleans up unreachable branches. This fixes a bug where Preoptimizer was replacing unreachable expressions with none-typed expressions, leading to validation failures. --- src/passes/AbstractTypeRefining.cpp | 14 ++ .../passes/abstract-type-refining-desc.wast | 206 ++++++++++++++++++ 2 files changed, 220 insertions(+) diff --git a/src/passes/AbstractTypeRefining.cpp b/src/passes/AbstractTypeRefining.cpp index b7b12733835..3e7017cd958 100644 --- a/src/passes/AbstractTypeRefining.cpp +++ b/src/passes/AbstractTypeRefining.cpp @@ -371,6 +371,11 @@ struct AbstractTypeRefining : public Pass { // This might cause the casts to incorrectly start succeeding. To avoid // that, optimize them out first. void visitRefCast(RefCast* curr) { + if (curr->ref->type == Type::unreachable || + (curr->desc && curr->desc->type == Type::unreachable)) { + curr->type = Type::unreachable; + return; + } auto optimized = getOptimized(curr->type); if (!optimized) { return; @@ -402,6 +407,10 @@ struct AbstractTypeRefining : public Pass { // If we optimize away a descriptor type, then we must fix up any // ref.get_desc of it, as ReFinalize would fix us up to return it. void visitRefGetDesc(RefGetDesc* curr) { + if (curr->ref->type == Type::unreachable) { + curr->type = Type::unreachable; + return; + } auto optimized = getOptimized(curr->type); if (!optimized) { return; @@ -433,6 +442,11 @@ struct AbstractTypeRefining : public Pass { if (curr->op == BrOnNull || curr->op == BrOnNonNull) { return; } + if (curr->ref->type == Type::unreachable || + (curr->desc && curr->desc->type == Type::unreachable)) { + curr->type = Type::unreachable; + return; + } auto optimized = getOptimized(curr->castType); if (!optimized) { return; diff --git a/test/lit/passes/abstract-type-refining-desc.wast b/test/lit/passes/abstract-type-refining-desc.wast index 7b0ab882917..fa79adb1cab 100644 --- a/test/lit/passes/abstract-type-refining-desc.wast +++ b/test/lit/passes/abstract-type-refining-desc.wast @@ -843,6 +843,57 @@ ) (unreachable) ) + + ;; YESTNH: (func $branch-fail-unreachable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (block $block + ;; YESTNH-NEXT: (ref.cast_desc_eq (ref $struct) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (ref.null none) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-unreachable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (block $block + ;; NO_TNH-NEXT: (ref.cast_desc_eq (ref $struct) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-fail-unreachable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + ;; This cast is unreachable because its ref input is an unreachable drop, + ;; so it is skipped. During ReFinalize it requires its ref input to remain + ;; unreachable. + (ref.cast_desc_eq (ref $struct) + ;; This drop is only valid as the ref input to ref.cast_desc_eq because + ;; its child is unreachable. + (drop + ;; The descriptor operand is unreachable, so this branch is skipped + ;; and replaced by an unreachable block in ReFinalize. + (br_on_cast_desc_eq_fail $block (ref null $struct) (ref $struct) + (ref.null none) + (unreachable) + ) + ) + (local.get $desc) + ) + ) + ) ) (module @@ -1189,6 +1240,117 @@ ) ) +(module + ;; As above, but now $A is also uninstantiated and the inexact operand of + ;; ref.get_desc becomes unreachable during optimization. + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $A (sub (descriptor $B) (struct))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $A (sub (descriptor $B) (struct))) + (type $A (sub (descriptor $B) (struct))) + ;; YESTNH: (type $B (sub (describes $A) (struct))) + ;; NO_TNH: (type $B (sub (describes $A) (struct))) + (type $B (sub (describes $A) (struct))) + ;; YESTNH: (type $A.sub (sub $A (descriptor $B.sub) (struct))) + ;; NO_TNH: (type $A.sub (sub $A (descriptor $B.sub) (struct))) + (type $A.sub (sub $A (descriptor $B.sub) (struct))) + ;; YESTNH: (type $B.sub (sub $B (describes $A.sub) (struct))) + ;; NO_TNH: (type $B.sub (sub $B (describes $A.sub) (struct))) + (type $B.sub (sub $B (describes $A.sub) (struct))) + ) + + ;; YESTNH: (type $4 (func)) + + ;; YESTNH: (type $5 (func (param (ref $A.sub) (ref (exact $A.sub))) (result (ref $B.sub)))) + + ;; YESTNH: (func $create (type $4) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (struct.new_default_desc $A.sub + ;; YESTNH-NEXT: (struct.new_default $B.sub) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (type $4 (func)) + + ;; NO_TNH: (type $5 (func (param (ref $A) (ref (exact $A))) (result (ref $B)))) + + ;; NO_TNH: (func $create (type $4) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (struct.new_default_desc $A.sub + ;; NO_TNH-NEXT: (struct.new_default $B.sub) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $create + (drop + (struct.new_default_desc $A.sub + (struct.new_default $B.sub) + ) + ) + ) + + ;; YESTNH: (func $inexact-unreachable (type $5) (param $inexact (ref $A.sub)) (param $exact (ref (exact $A.sub))) (result (ref $B.sub)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block ;; (replaces unreachable RefGetDesc we can't emit) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (local.get $inexact) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (block + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (local.get $exact) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $inexact-unreachable (type $5) (param $inexact (ref $A)) (param $exact (ref (exact $A))) (result (ref $B)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result (ref (exact $A))) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (ref.get_desc $A + ;; NO_TNH-NEXT: (br_on_cast_desc_eq $block (ref $A) (ref (exact $A)) + ;; NO_TNH-NEXT: (local.get $inexact) + ;; NO_TNH-NEXT: (ref.get_desc $A + ;; NO_TNH-NEXT: (local.get $exact) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $inexact-unreachable (param $inexact (ref $A)) (param $exact (ref (exact $A))) (result (ref $B)) + (drop + (block $block (result (ref (exact $A))) + (return + (ref.get_desc $A + (br_on_cast_desc_eq $block (ref $A) (ref (exact $A)) + (local.get $inexact) + (ref.get_desc $A + (local.get $exact) + ) + ) + ) + ) + ) + ) + (unreachable) + ) +) + ;; Shared abstract described types. (module (rec @@ -1272,6 +1434,8 @@ ;; YESTNH: (type $8 (func (param (ref null $desc2)))) + ;; YESTNH: (type $9 (func (param (ref $struct) (ref null $desc2)))) + ;; YESTNH: (global $desc2 (ref $desc2) (struct.new_default $desc2)) ;; NO_TNH: (type $4 (func (param (ref none)))) @@ -1283,6 +1447,8 @@ ;; NO_TNH: (type $8 (func (param (ref null $desc2)))) + ;; NO_TNH: (type $9 (func (param (ref $struct) (ref null $desc2)))) + ;; NO_TNH: (global $desc2 (ref $desc2) (struct.new_default $desc2)) (global $desc2 (ref $desc2) (struct.new_default $desc2)) @@ -1452,6 +1618,46 @@ ) ) + ;; YESTNH: (func $ref-cast-desc-unreachable (type $9) (param $s (ref $struct)) (param $desc2 (ref null $desc2)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (ref.cast_desc_eq (ref $struct2) + ;; YESTNH-NEXT: (block + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (local.get $s) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.get $desc2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $ref-cast-desc-unreachable (type $9) (param $s (ref $struct)) (param $desc2 (ref null $desc2)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (ref.cast_desc_eq (ref $struct2) + ;; NO_TNH-NEXT: (block + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (local.get $s) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $desc2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $ref-cast-desc-unreachable (param $s (ref $struct)) (param $desc2 (ref null $desc2)) + ;; The inner ref.get_desc is replaced with an unreachable block before the + ;; enclosing ref.cast_desc_eq is visited, so the enclosing cast becomes + ;; unreachable and is skipped. + (drop + (ref.cast_desc_eq (ref $struct2) + (ref.get_desc $struct + (local.get $s) + ) + (local.get $desc2) + ) + ) + ) + ;; YESTNH: (func $br-on-cast-desc-pop (type $8) (param $desc (ref null $desc2)) ;; YESTNH-NEXT: (local $1 anyref) ;; YESTNH-NEXT: (local $2 (ref null $desc2)) From 9270ae18571766eaeb156d3cd1a3d4b234399df2 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 16:51:41 -0800 Subject: [PATCH 2/5] move checks earlier --- src/passes/AbstractTypeRefining.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/passes/AbstractTypeRefining.cpp b/src/passes/AbstractTypeRefining.cpp index 3e7017cd958..62aeb48027a 100644 --- a/src/passes/AbstractTypeRefining.cpp +++ b/src/passes/AbstractTypeRefining.cpp @@ -439,14 +439,14 @@ struct AbstractTypeRefining : public Pass { } void visitBrOn(BrOn* curr) { - if (curr->op == BrOnNull || curr->op == BrOnNonNull) { - return; - } if (curr->ref->type == Type::unreachable || (curr->desc && curr->desc->type == Type::unreachable)) { curr->type = Type::unreachable; return; } + if (curr->op == BrOnNull || curr->op == BrOnNonNull) { + return; + } auto optimized = getOptimized(curr->castType); if (!optimized) { return; From 7442085d3d2e0040dd1d593ef447260feddbabba Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 16:52:16 -0800 Subject: [PATCH 3/5] simplify test --- .../passes/abstract-type-refining-desc.wast | 124 ++++-------------- 1 file changed, 26 insertions(+), 98 deletions(-) diff --git a/test/lit/passes/abstract-type-refining-desc.wast b/test/lit/passes/abstract-type-refining-desc.wast index fa79adb1cab..1c0913ba53e 100644 --- a/test/lit/passes/abstract-type-refining-desc.wast +++ b/test/lit/passes/abstract-type-refining-desc.wast @@ -1166,6 +1166,8 @@ ;; YESTNH: (type $6 (func (param (ref $A)) (result (ref $B.sub)))) + ;; YESTNH: (type $7 (func (param (ref (exact $B.sub)) (ref $B.sub)) (result (ref $B.sub)))) + ;; YESTNH: (func $create (type $4) ;; YESTNH-NEXT: (drop ;; YESTNH-NEXT: (struct.new_default_desc $A.sub @@ -1179,6 +1181,8 @@ ;; NO_TNH: (type $6 (func (param (ref $A)) (result (ref $B)))) + ;; NO_TNH: (type $7 (func (param (ref (exact $B)) (ref $B)) (result (ref $B)))) + ;; NO_TNH: (func $create (type $4) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (struct.new_default_desc $A.sub @@ -1238,116 +1242,40 @@ (local.get $inexact) ) ) -) - -(module - ;; As above, but now $A is also uninstantiated and the inexact operand of - ;; ref.get_desc becomes unreachable during optimization. - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $A (sub (descriptor $B) (struct))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $A (sub (descriptor $B) (struct))) - (type $A (sub (descriptor $B) (struct))) - ;; YESTNH: (type $B (sub (describes $A) (struct))) - ;; NO_TNH: (type $B (sub (describes $A) (struct))) - (type $B (sub (describes $A) (struct))) - ;; YESTNH: (type $A.sub (sub $A (descriptor $B.sub) (struct))) - ;; NO_TNH: (type $A.sub (sub $A (descriptor $B.sub) (struct))) - (type $A.sub (sub $A (descriptor $B.sub) (struct))) - ;; YESTNH: (type $B.sub (sub $B (describes $A.sub) (struct))) - ;; NO_TNH: (type $B.sub (sub $B (describes $A.sub) (struct))) - (type $B.sub (sub $B (describes $A.sub) (struct))) - ) - - ;; YESTNH: (type $4 (func)) - - ;; YESTNH: (type $5 (func (param (ref $A.sub) (ref (exact $A.sub))) (result (ref $B.sub)))) - - ;; YESTNH: (func $create (type $4) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (struct.new_default_desc $A.sub - ;; YESTNH-NEXT: (struct.new_default $B.sub) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (type $4 (func)) - - ;; NO_TNH: (type $5 (func (param (ref $A) (ref (exact $A))) (result (ref $B)))) - - ;; NO_TNH: (func $create (type $4) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (struct.new_default_desc $A.sub - ;; NO_TNH-NEXT: (struct.new_default $B.sub) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $create - (drop - (struct.new_default_desc $A.sub - (struct.new_default $B.sub) - ) - ) - ) - ;; YESTNH: (func $inexact-unreachable (type $5) (param $inexact (ref $A.sub)) (param $exact (ref (exact $A.sub))) (result (ref $B.sub)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block ;; (replaces unreachable RefGetDesc we can't emit) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (local.get $inexact) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (block - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (local.get $exact) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) + ;; YESTNH: (func $inexact-unreachable (type $7) (param $exact (ref (exact $B.sub))) (param $inexact (ref $B.sub)) (result (ref $B.sub)) + ;; YESTNH-NEXT: (block ;; (replaces unreachable RefGetDesc we can't emit) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (ref.cast_desc_eq (ref $A.sub) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: (local.get $inexact) ;; YESTNH-NEXT: ) ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $inexact-unreachable (type $5) (param $inexact (ref $A)) (param $exact (ref (exact $A))) (result (ref $B)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result (ref (exact $A))) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (ref.get_desc $A - ;; NO_TNH-NEXT: (br_on_cast_desc_eq $block (ref $A) (ref (exact $A)) - ;; NO_TNH-NEXT: (local.get $inexact) - ;; NO_TNH-NEXT: (ref.get_desc $A - ;; NO_TNH-NEXT: (local.get $exact) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) + ;; NO_TNH: (func $inexact-unreachable (type $7) (param $exact (ref (exact $B))) (param $inexact (ref $B)) (result (ref $B)) + ;; NO_TNH-NEXT: (ref.get_desc $A + ;; NO_TNH-NEXT: (ref.cast_desc_eq (ref $A) + ;; NO_TNH-NEXT: (struct.new_default_desc $A + ;; NO_TNH-NEXT: (local.get $exact) ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $inexact) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) ;; NO_TNH-NEXT: ) - (func $inexact-unreachable (param $inexact (ref $A)) (param $exact (ref (exact $A))) (result (ref $B)) - (drop - (block $block (result (ref (exact $A))) - (return - (ref.get_desc $A - (br_on_cast_desc_eq $block (ref $A) (ref (exact $A)) - (local.get $inexact) - (ref.get_desc $A - (local.get $exact) - ) - ) - ) + (func $inexact-unreachable (param $exact (ref (exact $B))) (param $inexact (ref $B)) (result (ref $B)) + ;; The struct.new_default_desc is replaced with unreachable, which makes the + ;; inexact ref.cast_desc_eq unreachable, which in turn makes the + ;; ref.get_desc unreachable. + (ref.get_desc $A + (ref.cast_desc_eq (ref $A) + (struct.new_default_desc $A + (local.get $exact) ) + (local.get $inexact) ) ) - (unreachable) ) ) From e9a6af784d92163b7babb68b089ce0d69e083f13 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 17:01:14 -0800 Subject: [PATCH 4/5] comments --- src/passes/AbstractTypeRefining.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/passes/AbstractTypeRefining.cpp b/src/passes/AbstractTypeRefining.cpp index 62aeb48027a..ebaebeb9832 100644 --- a/src/passes/AbstractTypeRefining.cpp +++ b/src/passes/AbstractTypeRefining.cpp @@ -373,6 +373,8 @@ struct AbstractTypeRefining : public Pass { void visitRefCast(RefCast* curr) { if (curr->ref->type == Type::unreachable || (curr->desc && curr->desc->type == Type::unreachable)) { + // A child may have become unreachable earlier in Preoptimizer, before + // ReFinalize has run. Propagate unreachability and skip optimizing. curr->type = Type::unreachable; return; } @@ -408,6 +410,7 @@ struct AbstractTypeRefining : public Pass { // ref.get_desc of it, as ReFinalize would fix us up to return it. void visitRefGetDesc(RefGetDesc* curr) { if (curr->ref->type == Type::unreachable) { + // As in visitRefCast, propagate unreachability and skip optimizing. curr->type = Type::unreachable; return; } @@ -441,6 +444,7 @@ struct AbstractTypeRefining : public Pass { void visitBrOn(BrOn* curr) { if (curr->ref->type == Type::unreachable || (curr->desc && curr->desc->type == Type::unreachable)) { + // As in visitRefCast, propagate unreachability and skip optimizing. curr->type = Type::unreachable; return; } From 780b00da290248190a8599e3a61950370d94be42 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 17:01:28 -0800 Subject: [PATCH 5/5] more test coverage --- .../passes/abstract-type-refining-desc.wast | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/lit/passes/abstract-type-refining-desc.wast b/test/lit/passes/abstract-type-refining-desc.wast index 1c0913ba53e..c337b0dc20e 100644 --- a/test/lit/passes/abstract-type-refining-desc.wast +++ b/test/lit/passes/abstract-type-refining-desc.wast @@ -894,6 +894,56 @@ ) ) ) + + ;; YESTNH: (func $cast-unreachable-desc (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (block $block + ;; YESTNH-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-unreachable-desc (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (block $block + ;; NO_TNH-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-unreachable-desc (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + ;; Same as above, but with an unreachable ref operand in the branch and an + ;; unreachable descriptor operand in the cast. + (ref.cast_desc_eq (ref $struct) + (local.get $ref) + (br_on_cast_desc_eq_fail $block (ref null $struct) (ref $struct) + (unreachable) + (local.get $desc) + ) + ) + ) + ) ) (module