From dad066ce1eeec107aafed84eb6f1bf095b98e653 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 08:56:59 -0700 Subject: [PATCH 1/7] work --- src/wasm/wasm-validator.cpp | 17 ++++++++++++++--- test/spec/cont.wast | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 791d25c01bf..5b9023e2e85 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3979,10 +3979,21 @@ void FunctionValidator::visitContNew(ContNew* curr) { } shouldBeTrue(curr->type.isExact(), curr, "cont.new should be exact"); - shouldBeTrue(curr->type.isContinuation() && - curr->type.getHeapType().getContinuation().type.isSignature(), + if (!curr->type.isContinuation()) { + return; + } + + auto cont = curr->type.getHeapType().getContinuation(); + if (!shouldBeTrue(cont.type.isSignature(), + curr, + "cont.new must be annotated with a continuation type")) { + return; + } + + shouldBeTrue(HeapType::isSubType(curr->func->type.getHeapType(), + cont.type), curr, - "cont.new must be annotated with a continuation type"); + "cont.new function reference must be a subtype"); } void FunctionValidator::visitContBind(ContBind* curr) { diff --git a/test/spec/cont.wast b/test/spec/cont.wast index 8bfaf83c4d9..fc48d01ca2f 100644 --- a/test/spec/cont.wast +++ b/test/spec/cont.wast @@ -242,6 +242,26 @@ (type $c2 (cont $f2)) ) +(assert_invalid + (module + (rec + (type $fA (func)) + (type $fB (func)) + (type $cont (cont $fA)) + ) + (elem declare func $b) + (func $a + (drop + (cont.new $cont ;; expects a ref of $fA, not $fB + (ref.func $b) + ) + ) + ) + (func $b (type $fB) + ) + ) + "type mismatch") + ;; Simple state example (module $state From 1b6b0340fa82f8b4b720dd83ad819f1690007bb6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 09:12:59 -0700 Subject: [PATCH 2/7] format --- src/wasm/wasm-validator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 5b9023e2e85..77e4f8c5d77 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3990,8 +3990,7 @@ void FunctionValidator::visitContNew(ContNew* curr) { return; } - shouldBeTrue(HeapType::isSubType(curr->func->type.getHeapType(), - cont.type), + shouldBeTrue(HeapType::isSubType(curr->func->type.getHeapType(), cont.type), curr, "cont.new function reference must be a subtype"); } From f26dcca00bdbea05a243315c6a5285b68e1dc42b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 11:22:41 -0700 Subject: [PATCH 3/7] fix --- scripts/test/fuzzing.py | 1 + src/ir/type-updating.cpp | 12 +- src/ir/type-updating.h | 3 +- src/wasm-type.h | 7 +- src/wasm/wasm-type.cpp | 3 + .../passes/abstract-type-refining-cont.wast | 27 + .../passes/abstract-type-refining-desc.wast | 1037 +---------------- 7 files changed, 69 insertions(+), 1021 deletions(-) create mode 100644 test/lit/passes/abstract-type-refining-cont.wast diff --git a/scripts/test/fuzzing.py b/scripts/test/fuzzing.py index 7f951ce94cc..757cb78d2de 100644 --- a/scripts/test/fuzzing.py +++ b/scripts/test/fuzzing.py @@ -120,6 +120,7 @@ 'cont_export_throw.wast', 'type-merging-cont.wast', 'remove-unused-module-elements-cont.wast', + 'abstract-type-refining-cont.wast', # TODO: fix split_wast() on tricky escaping situations like a string ending # in \\" (the " is not escaped - there is an escaped \ before it) 'string-lifting-section.wast', diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index 87edc69f7c4..56c9470cf0c 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -359,9 +359,10 @@ Type GlobalTypeRewriter::getTempType(Type type) { } if (type.isRef()) { auto heapType = type.getHeapType(); - if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { + auto tempHeapType = getTempHeapType(heapType); + if (tempHeapType != heapType) { return typeBuilder.getTempRefType( - typeBuilder[it->second], type.getNullability(), type.getExactness()); + tempHeapType, type.getNullability(), type.getExactness()); } // This type is not one that is eligible for optimizing. That is fine; just // use it unmodified. @@ -377,6 +378,13 @@ Type GlobalTypeRewriter::getTempType(Type type) { WASM_UNREACHABLE("bad type"); } +HeapType GlobalTypeRewriter::getTempHeapType(HeapType type) { + if (auto it = typeIndices.find(type); it != typeIndices.end()) { + return typeBuilder[it->second]; + } + return type; +} + Type GlobalTypeRewriter::getTempTupleType(Tuple tuple) { return typeBuilder.getTempTupleType(tuple); } diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h index 5e55c460880..bd93857b07d 100644 --- a/src/ir/type-updating.h +++ b/src/ir/type-updating.h @@ -404,6 +404,7 @@ class GlobalTypeRewriter { // so that they can use a proper temp type of the TypeBuilder while modifying // things. Type getTempType(Type type); + HeapType getTempHeapType(HeapType type); Type getTempTupleType(Tuple tuple); using SignatureUpdates = std::unordered_map; @@ -496,7 +497,7 @@ class TypeMapper : public GlobalTypeRewriter { if (iter != mapping.end()) { return iter->second; } - return type; + return getTempHeapType(type); } Type getNewType(Type type) { diff --git a/src/wasm-type.h b/src/wasm-type.h index 6a0b9709f54..d90385b1c67 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -805,9 +805,12 @@ struct TypeBuilder { setHeapType(i, wasm::Array(elem)); return; } - case HeapTypeKind::Cont: - setHeapType(i, Continuation(map(type.getContinuation().type))); + case HeapTypeKind::Cont: { + auto cont = type.getContinuation(); + cont.type = map(cont.type); + setHeapType(i, cont); return; + } case HeapTypeKind::Basic: WASM_UNREACHABLE("unexpected kind"); } diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index ed5c360fb48..34d37da50d4 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -1324,6 +1324,9 @@ FeatureSet HeapType::getFeatures() const { // In addition, scan their non-ref children, to add dependencies on // things like SIMD. + // XXX This will not scan HeapType children that are not also children of + // Type children, which happens with Continuation (has a HeapType + // child that is not a Type). for (auto child : heapType.getTypeChildren()) { if (!child.isRef()) { feats |= child.getFeatures(); diff --git a/test/lit/passes/abstract-type-refining-cont.wast b/test/lit/passes/abstract-type-refining-cont.wast new file mode 100644 index 00000000000..b6dfa173f85 --- /dev/null +++ b/test/lit/passes/abstract-type-refining-cont.wast @@ -0,0 +1,27 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --abstract-type-refining -all --closed-world -S -o - | filecheck %s + +(module + (rec + (type $func (func)) + (type $cont (cont $func)) + + ;; This type is never created, so we optimize and rebuild types. While doing + ;; so we should not get confused as we copy the continuation type, which + ;; should end up referring properly to the corresponding func type. + (type $uncreated (struct)) + ) + + (func $func (type $func) + ) + + (func $test (type $func) + (drop + (cont.new $cont + (ref.func $func) + ) + ) + ) +) + diff --git a/test/lit/passes/abstract-type-refining-desc.wast b/test/lit/passes/abstract-type-refining-desc.wast index 6f590ff8d27..b6dfa173f85 100644 --- a/test/lit/passes/abstract-type-refining-desc.wast +++ b/test/lit/passes/abstract-type-refining-desc.wast @@ -1,1022 +1,27 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types --traps-never-happen \ -;; RUN: -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=YESTNH -;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types \ -;; RUN: -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=NO_TNH - -;; Run in both TNH and non-TNH mode. +;; RUN: foreach %s %t wasm-opt --abstract-type-refining -all --closed-world -S -o - | filecheck %s (module - ;; We should not try to generate invalid types by removing the subtype - ;; relation between $B.desc and $A.desc. - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $A (sub (descriptor $A.desc (struct)))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $A (sub (descriptor $A.desc (struct)))) - (type $A (sub (descriptor $A.desc (struct)))) - ;; YESTNH: (type $A.desc (sub (describes $A (struct)))) - ;; NO_TNH: (type $A.desc (sub (describes $A (struct)))) - (type $A.desc (sub (describes $A (struct)))) - ;; YESTNH: (type $B (sub $A (descriptor $B.desc (struct)))) - ;; NO_TNH: (type $B (sub $A (descriptor $B.desc (struct)))) - (type $B (sub $A (descriptor $B.desc (struct)))) - ;; YESTNH: (type $B.desc (sub $A.desc (describes $B (struct)))) - ;; NO_TNH: (type $B.desc (sub $A.desc (describes $B (struct)))) - (type $B.desc (sub $A.desc (describes $B (struct)))) - ) - ;; YESTNH: (global $g (ref (exact $B)) (struct.new_default $B - ;; YESTNH-NEXT: (ref.null none) - ;; YESTNH-NEXT: )) - ;; NO_TNH: (global $g (ref (exact $B)) (struct.new_default $B - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: )) - (global $g (ref (exact $B)) - (struct.new_default $B - (ref.null none) - ) - ) + (rec + (type $func (func)) + (type $cont (cont $func)) + + ;; This type is never created, so we optimize and rebuild types. While doing + ;; so we should not get confused as we copy the continuation type, which + ;; should end up referring properly to the corresponding func type. + (type $uncreated (struct)) + ) + + (func $func (type $func) + ) + + (func $test (type $func) + (drop + (cont.new $cont + (ref.func $func) + ) + ) + ) ) -(module - ;; Same as above, but now we should see references to $A.desc and $B.desc - ;; optimized to nullref because they are not instantiated. Similarly, $A is - ;; optimized to $B with TNH. - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $A (sub (descriptor $A.desc (struct (field (ref null $B)) (field (ref null $B)))))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $A (sub (descriptor $A.desc (struct (field (ref null $A)) (field (ref null $B)))))) - (type $A (sub (descriptor $A.desc (struct (field (ref null $A) (ref null $B)))))) - ;; YESTNH: (type $A.desc (sub (describes $A (struct (field nullref) (field nullref))))) - ;; NO_TNH: (type $A.desc (sub (describes $A (struct (field nullref) (field nullref))))) - (type $A.desc (sub (describes $A (struct (field (ref null $A.desc) (ref null $B.desc)))))) - ;; YESTNH: (type $B (sub $A (descriptor $B.desc (struct (field (ref null $B)) (field (ref null $B)))))) - ;; NO_TNH: (type $B (sub $A (descriptor $B.desc (struct (field (ref null $A)) (field (ref null $B)))))) - (type $B (sub $A (descriptor $B.desc (struct (field (ref null $A) (ref null $B)))))) - ;; YESTNH: (type $B.desc (sub $A.desc (describes $B (struct (field nullref) (field nullref))))) - ;; NO_TNH: (type $B.desc (sub $A.desc (describes $B (struct (field nullref) (field nullref))))) - (type $B.desc (sub $A.desc (describes $B (struct (field (ref null $A.desc) (ref null $B.desc)))))) - ) - - ;; YESTNH: (global $g (ref (exact $B)) (struct.new_default $B - ;; YESTNH-NEXT: (ref.null none) - ;; YESTNH-NEXT: )) - ;; NO_TNH: (global $g (ref (exact $B)) (struct.new_default $B - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: )) - (global $g (ref (exact $B)) - (struct.new_default $B - (ref.null none) - ) - ) -) - -(module - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $struct (descriptor $desc (struct))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $struct (descriptor $desc (struct))) - (type $struct (descriptor $desc (struct))) - ;; YESTNH: (type $desc (describes $struct (struct))) - ;; NO_TNH: (type $desc (describes $struct (struct))) - (type $desc (describes $struct (struct))) - ) - - ;; YESTNH: (type $2 (func (param anyref (ref null $desc)) (result nullref))) - - ;; YESTNH: (type $3 (func (param anyref (ref $desc)) (result nullref))) - - ;; YESTNH: (type $4 (func)) - - ;; YESTNH: (import "" "" (func $effect (type $4))) - ;; NO_TNH: (type $2 (func (param anyref (ref null $desc)) (result nullref))) - - ;; NO_TNH: (type $3 (func (param anyref (ref $desc)) (result nullref))) - - ;; NO_TNH: (type $4 (func)) - - ;; NO_TNH: (import "" "" (func $effect (type $4))) - (import "" "" (func $effect)) - - ;; YESTNH: (global $desc (ref $desc) (struct.new_default $desc)) - ;; NO_TNH: (global $desc (ref $desc) (struct.new_default $desc)) - (global $desc (ref $desc) (struct.new $desc)) - - ;; YESTNH: (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (ref.cast nullref - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.cast nullref - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $cast-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - ;; $struct will be optimized to nullref, but $desc will not. We must - ;; optimize out this cast because otherwise ReFinalization would make its - ;; result (ref $struct) again and it would not be valid for the optimized - ;; function result type. - (ref.cast_desc (ref null $struct) - (local.get $ref) - (local.get $desc) - ) - ) - - ;; YESTNH: (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref null $desc)) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref null $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (ref.cast nullref - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (block (result (ref null $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.cast nullref - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $cast-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - ;; Same, but with side effects we cannot drop. - (ref.cast_desc (ref null $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref null $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - - ;; YESTNH: (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (ref.cast (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.cast (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $cast-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - ;; Same, but now the cast does not admit null. - (ref.cast_desc (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - - ;; YESTNH: (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (ref.cast (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (ref.cast (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $cast-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - ;; Same, but now the descriptor is additionally non-null. - (ref.cast_desc (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - - ;; YESTNH: (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref $desc)) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (ref.cast (ref none) - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (block (result (ref $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (ref.cast (ref none) - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $cast-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - ;; Same, but with side effects we cannot drop. - (ref.cast_desc (ref $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - - ;; YESTNH: (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (block $block (result nullref) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result (ref any)) - ;; YESTNH-NEXT: (br_on_cast $block anyref nullref - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (block $block (result nullref) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result (ref any)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast $block anyref nullref - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $branch-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (block $block (result (ref null $struct)) - (drop - ;; Same, but with br_on_cast_desc. - (br_on_cast_desc $block anyref (ref null $struct) - (local.get $ref) - (local.get $desc) - ) - ) - (unreachable) - ) - ) - - ;; YESTNH: (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref null $desc)) - ;; YESTNH-NEXT: (block $block (result nullref) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result (ref any)) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref null $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (br_on_cast $block anyref nullref - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (block $block (result nullref) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result (ref any)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (block (result (ref null $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast $block anyref nullref - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $branch-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (block $block (result (ref null $struct)) - (drop - ;; Same, but with effects we cannot drop. - (br_on_cast_desc $block anyref (ref null $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref null $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - (unreachable) - ) - ) - - ;; YESTNH: (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (block $block (result (ref none)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (block $block (result (ref none)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $branch-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (block $block (result (ref null $struct)) - (drop - ;; Same, but now the cast does not admit nulls. - (br_on_cast_desc $block anyref (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - (unreachable) - ) - ) - - ;; YESTNH: (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (block $block (result (ref none)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (block $block (result (ref none)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $branch-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - (block $block (result (ref null $struct)) - (drop - ;; Same, but now the descriptor is additionally non-null. - (br_on_cast_desc $block anyref (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - (unreachable) - ) - ) - - ;; YESTNH: (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref $desc)) - ;; YESTNH-NEXT: (block $block (result (ref none)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (block $block (result (ref none)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (block (result (ref $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $branch-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - (block $block (result (ref null $struct)) - (drop - ;; Same, but with effects we cannot drop. - (br_on_cast_desc $block anyref (ref $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - (unreachable) - ) - ) - - ;; YESTNH: (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block (result (ref any)) - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block (result nullref) - ;; YESTNH-NEXT: (br_on_cast_fail $block anyref nullref - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result (ref any)) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (block (result nullref) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref nullref - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $branch-fail-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (drop - (block $block (result anyref) - (return - ;; Same, but with br_on_cast_desc_fail. - (br_on_cast_desc_fail $block anyref (ref null $struct) - (local.get $ref) - (local.get $desc) - ) - ) - ) - ) - (unreachable) - ) - - ;; YESTNH: (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref null $desc)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block (result (ref any)) - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block (result nullref) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref null $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (br_on_cast_fail $block anyref nullref - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result (ref any)) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (block (result nullref) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (block (result (ref null $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref nullref - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $branch-fail-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (drop - (block $block (result anyref) - (return - ;; Same, but with effects. - (br_on_cast_desc_fail $block anyref (ref null $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref null $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - ) - ) - (unreachable) - ) - - ;; YESTNH: (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block (result anyref) - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block (result (ref none)) - ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 (ref $desc)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result anyref) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (block (result (ref none)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (ref.as_non_null - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $branch-fail-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) - (drop - (block $block (result anyref) - (return - ;; Same, but now without admitting null. - (br_on_cast_desc_fail $block anyref (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - ) - ) - (unreachable) - ) - - ;; YESTNH: (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block (result anyref) - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block (result (ref none)) - ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result anyref) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (block (result (ref none)) - ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $branch-fail-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - (drop - (block $block (result anyref) - (return - ;; Same, but now the descriptor is additionally non-null. - (br_on_cast_desc_fail $block anyref (ref $struct) - (local.get $ref) - (local.get $desc) - ) - ) - ) - ) - (unreachable) - ) - - ;; YESTNH: (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; YESTNH-NEXT: (local $2 anyref) - ;; YESTNH-NEXT: (local $3 (ref $desc)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block $block (result anyref) - ;; YESTNH-NEXT: (return - ;; YESTNH-NEXT: (block (result (ref none)) - ;; YESTNH-NEXT: (local.set $2 - ;; YESTNH-NEXT: (block (result anyref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $ref) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (local.set $3 - ;; YESTNH-NEXT: (block (result (ref $desc)) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (local.get $desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; YESTNH-NEXT: (local.get $2) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) - ;; NO_TNH-NEXT: (local $2 anyref) - ;; NO_TNH-NEXT: (local $3 (ref $desc)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block $block (result anyref) - ;; NO_TNH-NEXT: (return - ;; NO_TNH-NEXT: (block (result (ref none)) - ;; NO_TNH-NEXT: (local.set $2 - ;; NO_TNH-NEXT: (block (result anyref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $ref) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (local.set $3 - ;; NO_TNH-NEXT: (block (result (ref $desc)) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (local.get $desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) - ;; NO_TNH-NEXT: (local.get $2) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $branch-fail-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) - (drop - (block $block (result anyref) - (return - ;; Same, but with effects. - (br_on_cast_desc_fail $block anyref (ref $struct) - (block (result anyref) - (call $effect) - (local.get $ref) - ) - (block (result (ref $desc)) - (call $effect) - (local.get $desc) - ) - ) - ) - ) - ) - (unreachable) - ) -) - -(module - ;; We will optimize the descriptor type, so we must take care not to leave - ;; invalid allocations of its described type. - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) - (type $struct (descriptor $uninstantiated (struct))) - ;; YESTNH: (type $uninstantiated (sub (describes $struct (struct)))) - ;; NO_TNH: (type $uninstantiated (sub (describes $struct (struct)))) - (type $uninstantiated (sub (describes $struct (struct)))) - ;; YESTNH: (type $other (descriptor $instantiated (struct))) - ;; NO_TNH: (type $other (descriptor $instantiated (struct))) - (type $other (descriptor $instantiated (struct))) - ;; YESTNH: (type $instantiated (sub $uninstantiated (describes $other (struct)))) - ;; NO_TNH: (type $instantiated (sub $uninstantiated (describes $other (struct)))) - (type $instantiated (sub $uninstantiated (describes $other (struct)))) - ) - - ;; YESTNH: (type $4 (func (result (ref $struct)))) - - ;; YESTNH: (type $5 (func)) - - ;; YESTNH: (import "" "" (func $effect (type $5))) - ;; NO_TNH: (type $4 (func (result (ref $struct)))) - - ;; NO_TNH: (type $5 (func)) - - ;; NO_TNH: (import "" "" (func $effect (type $5))) - (import "" "" (func $effect)) - - ;; YESTNH: (global $instantiated (ref $instantiated) (struct.new_default $instantiated)) - ;; NO_TNH: (global $instantiated (ref $instantiated) (struct.new_default $instantiated)) - (global $instantiated (ref $instantiated) (struct.new $instantiated)) - - ;; YESTNH: (global $fake-desc (ref null (exact $instantiated)) (ref.null none)) - ;; NO_TNH: (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) - (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) - - ;; YESTNH: (global $impossible (ref $struct) (struct.new_default $struct - ;; YESTNH-NEXT: (ref.null none) - ;; YESTNH-NEXT: )) - ;; NO_TNH: (global $impossible (ref $struct) (struct.new_default $struct - ;; NO_TNH-NEXT: (global.get $fake-desc) - ;; NO_TNH-NEXT: )) - (global $impossible (ref $struct) - (struct.new $struct - (global.get $fake-desc) - ) - ) - - ;; YESTNH: (func $impossible (type $4) (result (ref $struct)) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $impossible (type $4) (result (ref $struct)) - ;; NO_TNH-NEXT: (struct.new_default $struct - ;; NO_TNH-NEXT: (global.get $fake-desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $impossible (result (ref $struct)) - (struct.new $struct - (global.get $fake-desc) - ) - ) - - ;; YESTNH: (func $impossible-effect (type $4) (result (ref $struct)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result (ref null (exact $instantiated))) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (global.get $fake-desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $impossible-effect (type $4) (result (ref $struct)) - ;; NO_TNH-NEXT: (struct.new_default $struct - ;; NO_TNH-NEXT: (block (result (ref null (exact $uninstantiated))) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (global.get $fake-desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - (func $impossible-effect (result (ref $struct)) - (struct.new $struct - (block (result (ref null (exact $uninstantiated))) - (call $effect) - (global.get $fake-desc) - ) - ) - ) -) - -(module - ;; Same, but now we're optimizing the descriptor to bottom, so we don't need - ;; to do any preoptimization to ensure validity. We optimize anyway because - ;; we can. - (rec - ;; YESTNH: (rec - ;; YESTNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) - ;; NO_TNH: (rec - ;; NO_TNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) - (type $struct (descriptor $uninstantiated (struct))) - ;; YESTNH: (type $uninstantiated (describes $struct (struct))) - ;; NO_TNH: (type $uninstantiated (describes $struct (struct))) - (type $uninstantiated (describes $struct (struct))) - ) - - ;; YESTNH: (type $2 (func (result (ref $struct)))) - - ;; YESTNH: (type $3 (func)) - - ;; YESTNH: (import "" "" (func $effect (type $3))) - ;; NO_TNH: (type $2 (func (result (ref $struct)))) - - ;; NO_TNH: (type $3 (func)) - - ;; NO_TNH: (import "" "" (func $effect (type $3))) - (import "" "" (func $effect)) - - ;; YESTNH: (global $fake-desc nullref (ref.null none)) - ;; NO_TNH: (global $fake-desc nullref (ref.null none)) - (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) - - ;; YESTNH: (global $impossible (ref $struct) (struct.new_default $struct - ;; YESTNH-NEXT: (ref.null none) - ;; YESTNH-NEXT: )) - ;; NO_TNH: (global $impossible (ref $struct) (struct.new_default $struct - ;; NO_TNH-NEXT: (ref.null none) - ;; NO_TNH-NEXT: )) - (global $impossible (ref $struct) - (struct.new $struct - (global.get $fake-desc) - ) - ) - - ;; YESTNH: (func $impossible (type $2) (result (ref $struct)) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $impossible (type $2) (result (ref $struct)) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $impossible (result (ref $struct)) - (struct.new $struct - (global.get $fake-desc) - ) - ) - - ;; YESTNH: (func $impossible-effect (type $2) (result (ref $struct)) - ;; YESTNH-NEXT: (drop - ;; YESTNH-NEXT: (block (result nullref) - ;; YESTNH-NEXT: (call $effect) - ;; YESTNH-NEXT: (global.get $fake-desc) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: ) - ;; YESTNH-NEXT: (unreachable) - ;; YESTNH-NEXT: ) - ;; NO_TNH: (func $impossible-effect (type $2) (result (ref $struct)) - ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result nullref) - ;; NO_TNH-NEXT: (call $effect) - ;; NO_TNH-NEXT: (global.get $fake-desc) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (unreachable) - ;; NO_TNH-NEXT: ) - (func $impossible-effect (result (ref $struct)) - (struct.new $struct - (block (result (ref null (exact $uninstantiated))) - (call $effect) - (global.get $fake-desc) - ) - ) - ) -) From e212e1b633fed45564105b40873f328c94fee975 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 11:23:10 -0700 Subject: [PATCH 4/7] undo --- .../passes/abstract-type-refining-desc.wast | 1037 ++++++++++++++++- 1 file changed, 1016 insertions(+), 21 deletions(-) diff --git a/test/lit/passes/abstract-type-refining-desc.wast b/test/lit/passes/abstract-type-refining-desc.wast index b6dfa173f85..6f590ff8d27 100644 --- a/test/lit/passes/abstract-type-refining-desc.wast +++ b/test/lit/passes/abstract-type-refining-desc.wast @@ -1,27 +1,1022 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --abstract-type-refining -all --closed-world -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types --traps-never-happen \ +;; RUN: -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=YESTNH +;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types \ +;; RUN: -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=NO_TNH + +;; Run in both TNH and non-TNH mode. (module - (rec - (type $func (func)) - (type $cont (cont $func)) - - ;; This type is never created, so we optimize and rebuild types. While doing - ;; so we should not get confused as we copy the continuation type, which - ;; should end up referring properly to the corresponding func type. - (type $uncreated (struct)) - ) - - (func $func (type $func) - ) - - (func $test (type $func) - (drop - (cont.new $cont - (ref.func $func) - ) - ) - ) + ;; We should not try to generate invalid types by removing the subtype + ;; relation between $B.desc and $A.desc. + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $A (sub (descriptor $A.desc (struct)))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $A (sub (descriptor $A.desc (struct)))) + (type $A (sub (descriptor $A.desc (struct)))) + ;; YESTNH: (type $A.desc (sub (describes $A (struct)))) + ;; NO_TNH: (type $A.desc (sub (describes $A (struct)))) + (type $A.desc (sub (describes $A (struct)))) + ;; YESTNH: (type $B (sub $A (descriptor $B.desc (struct)))) + ;; NO_TNH: (type $B (sub $A (descriptor $B.desc (struct)))) + (type $B (sub $A (descriptor $B.desc (struct)))) + ;; YESTNH: (type $B.desc (sub $A.desc (describes $B (struct)))) + ;; NO_TNH: (type $B.desc (sub $A.desc (describes $B (struct)))) + (type $B.desc (sub $A.desc (describes $B (struct)))) + ) + ;; YESTNH: (global $g (ref (exact $B)) (struct.new_default $B + ;; YESTNH-NEXT: (ref.null none) + ;; YESTNH-NEXT: )) + ;; NO_TNH: (global $g (ref (exact $B)) (struct.new_default $B + ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: )) + (global $g (ref (exact $B)) + (struct.new_default $B + (ref.null none) + ) + ) ) +(module + ;; Same as above, but now we should see references to $A.desc and $B.desc + ;; optimized to nullref because they are not instantiated. Similarly, $A is + ;; optimized to $B with TNH. + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $A (sub (descriptor $A.desc (struct (field (ref null $B)) (field (ref null $B)))))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $A (sub (descriptor $A.desc (struct (field (ref null $A)) (field (ref null $B)))))) + (type $A (sub (descriptor $A.desc (struct (field (ref null $A) (ref null $B)))))) + ;; YESTNH: (type $A.desc (sub (describes $A (struct (field nullref) (field nullref))))) + ;; NO_TNH: (type $A.desc (sub (describes $A (struct (field nullref) (field nullref))))) + (type $A.desc (sub (describes $A (struct (field (ref null $A.desc) (ref null $B.desc)))))) + ;; YESTNH: (type $B (sub $A (descriptor $B.desc (struct (field (ref null $B)) (field (ref null $B)))))) + ;; NO_TNH: (type $B (sub $A (descriptor $B.desc (struct (field (ref null $A)) (field (ref null $B)))))) + (type $B (sub $A (descriptor $B.desc (struct (field (ref null $A) (ref null $B)))))) + ;; YESTNH: (type $B.desc (sub $A.desc (describes $B (struct (field nullref) (field nullref))))) + ;; NO_TNH: (type $B.desc (sub $A.desc (describes $B (struct (field nullref) (field nullref))))) + (type $B.desc (sub $A.desc (describes $B (struct (field (ref null $A.desc) (ref null $B.desc)))))) + ) + + ;; YESTNH: (global $g (ref (exact $B)) (struct.new_default $B + ;; YESTNH-NEXT: (ref.null none) + ;; YESTNH-NEXT: )) + ;; NO_TNH: (global $g (ref (exact $B)) (struct.new_default $B + ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: )) + (global $g (ref (exact $B)) + (struct.new_default $B + (ref.null none) + ) + ) +) + +(module + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $struct (descriptor $desc (struct))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $struct (descriptor $desc (struct))) + (type $struct (descriptor $desc (struct))) + ;; YESTNH: (type $desc (describes $struct (struct))) + ;; NO_TNH: (type $desc (describes $struct (struct))) + (type $desc (describes $struct (struct))) + ) + + ;; YESTNH: (type $2 (func (param anyref (ref null $desc)) (result nullref))) + + ;; YESTNH: (type $3 (func (param anyref (ref $desc)) (result nullref))) + + ;; YESTNH: (type $4 (func)) + + ;; YESTNH: (import "" "" (func $effect (type $4))) + ;; NO_TNH: (type $2 (func (param anyref (ref null $desc)) (result nullref))) + + ;; NO_TNH: (type $3 (func (param anyref (ref $desc)) (result nullref))) + + ;; NO_TNH: (type $4 (func)) + + ;; NO_TNH: (import "" "" (func $effect (type $4))) + (import "" "" (func $effect)) + + ;; YESTNH: (global $desc (ref $desc) (struct.new_default $desc)) + ;; NO_TNH: (global $desc (ref $desc) (struct.new_default $desc)) + (global $desc (ref $desc) (struct.new $desc)) + + ;; YESTNH: (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (ref.cast nullref + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (ref.cast nullref + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + ;; $struct will be optimized to nullref, but $desc will not. We must + ;; optimize out this cast because otherwise ReFinalization would make its + ;; result (ref $struct) again and it would not be valid for the optimized + ;; function result type. + (ref.cast_desc (ref null $struct) + (local.get $ref) + (local.get $desc) + ) + ) + + ;; YESTNH: (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref null $desc)) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref null $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (ref.cast nullref + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (block (result (ref null $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (ref.cast nullref + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + ;; Same, but with side effects we cannot drop. + (ref.cast_desc (ref null $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref null $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + + ;; YESTNH: (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (ref.cast (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (ref.cast (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + ;; Same, but now the cast does not admit null. + (ref.cast_desc (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + + ;; YESTNH: (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (ref.cast (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (ref.cast (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + ;; Same, but now the descriptor is additionally non-null. + (ref.cast_desc (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + + ;; YESTNH: (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref $desc)) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (ref.cast (ref none) + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (block (result (ref $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (ref.cast (ref none) + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $cast-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + ;; Same, but with side effects we cannot drop. + (ref.cast_desc (ref $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + + ;; YESTNH: (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (block $block (result nullref) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result (ref any)) + ;; YESTNH-NEXT: (br_on_cast $block anyref nullref + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (block $block (result nullref) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result (ref any)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast $block anyref nullref + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + (drop + ;; Same, but with br_on_cast_desc. + (br_on_cast_desc $block anyref (ref null $struct) + (local.get $ref) + (local.get $desc) + ) + ) + (unreachable) + ) + ) + + ;; YESTNH: (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref null $desc)) + ;; YESTNH-NEXT: (block $block (result nullref) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result (ref any)) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref null $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (br_on_cast $block anyref nullref + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (block $block (result nullref) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result (ref any)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (block (result (ref null $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast $block anyref nullref + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + (drop + ;; Same, but with effects we cannot drop. + (br_on_cast_desc $block anyref (ref null $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref null $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + (unreachable) + ) + ) + + ;; YESTNH: (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (block $block (result (ref none)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (block $block (result (ref none)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + (drop + ;; Same, but now the cast does not admit nulls. + (br_on_cast_desc $block anyref (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + (unreachable) + ) + ) + + ;; YESTNH: (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (block $block (result (ref none)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (block $block (result (ref none)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + (drop + ;; Same, but now the descriptor is additionally non-null. + (br_on_cast_desc $block anyref (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + (unreachable) + ) + ) + + ;; YESTNH: (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref $desc)) + ;; YESTNH-NEXT: (block $block (result (ref none)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (br_on_cast $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (block $block (result (ref none)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (block (result (ref $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $branch-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + (block $block (result (ref null $struct)) + (drop + ;; Same, but with effects we cannot drop. + (br_on_cast_desc $block anyref (ref $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + (unreachable) + ) + ) + + ;; YESTNH: (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block (result (ref any)) + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block (result nullref) + ;; YESTNH-NEXT: (br_on_cast_fail $block anyref nullref + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result (ref any)) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (block (result nullref) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref nullref + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $branch-fail-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (drop + (block $block (result anyref) + (return + ;; Same, but with br_on_cast_desc_fail. + (br_on_cast_desc_fail $block anyref (ref null $struct) + (local.get $ref) + (local.get $desc) + ) + ) + ) + ) + (unreachable) + ) + + ;; YESTNH: (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref null $desc)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block (result (ref any)) + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block (result nullref) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref null $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (br_on_cast_fail $block anyref nullref + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result (ref any)) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (block (result nullref) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (block (result (ref null $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref nullref + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $branch-fail-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (drop + (block $block (result anyref) + (return + ;; Same, but with effects. + (br_on_cast_desc_fail $block anyref (ref null $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref null $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + ) + ) + (unreachable) + ) + + ;; YESTNH: (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block (result anyref) + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block (result (ref none)) + ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 (ref $desc)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result anyref) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (block (result (ref none)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (ref.as_non_null + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $branch-fail-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct)) + (drop + (block $block (result anyref) + (return + ;; Same, but now without admitting null. + (br_on_cast_desc_fail $block anyref (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + ) + ) + (unreachable) + ) + + ;; YESTNH: (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block (result anyref) + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block (result (ref none)) + ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result anyref) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (block (result (ref none)) + ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $branch-fail-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + (drop + (block $block (result anyref) + (return + ;; Same, but now the descriptor is additionally non-null. + (br_on_cast_desc_fail $block anyref (ref $struct) + (local.get $ref) + (local.get $desc) + ) + ) + ) + ) + (unreachable) + ) + + ;; YESTNH: (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; YESTNH-NEXT: (local $2 anyref) + ;; YESTNH-NEXT: (local $3 (ref $desc)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block $block (result anyref) + ;; YESTNH-NEXT: (return + ;; YESTNH-NEXT: (block (result (ref none)) + ;; YESTNH-NEXT: (local.set $2 + ;; YESTNH-NEXT: (block (result anyref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $ref) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (local.set $3 + ;; YESTNH-NEXT: (block (result (ref $desc)) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (local.get $desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; YESTNH-NEXT: (local.get $2) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref) + ;; NO_TNH-NEXT: (local $2 anyref) + ;; NO_TNH-NEXT: (local $3 (ref $desc)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block $block (result anyref) + ;; NO_TNH-NEXT: (return + ;; NO_TNH-NEXT: (block (result (ref none)) + ;; NO_TNH-NEXT: (local.set $2 + ;; NO_TNH-NEXT: (block (result anyref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $ref) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.set $3 + ;; NO_TNH-NEXT: (block (result (ref $desc)) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (local.get $desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (br_on_cast_fail $block anyref (ref none) + ;; NO_TNH-NEXT: (local.get $2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $branch-fail-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct)) + (drop + (block $block (result anyref) + (return + ;; Same, but with effects. + (br_on_cast_desc_fail $block anyref (ref $struct) + (block (result anyref) + (call $effect) + (local.get $ref) + ) + (block (result (ref $desc)) + (call $effect) + (local.get $desc) + ) + ) + ) + ) + ) + (unreachable) + ) +) + +(module + ;; We will optimize the descriptor type, so we must take care not to leave + ;; invalid allocations of its described type. + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) + (type $struct (descriptor $uninstantiated (struct))) + ;; YESTNH: (type $uninstantiated (sub (describes $struct (struct)))) + ;; NO_TNH: (type $uninstantiated (sub (describes $struct (struct)))) + (type $uninstantiated (sub (describes $struct (struct)))) + ;; YESTNH: (type $other (descriptor $instantiated (struct))) + ;; NO_TNH: (type $other (descriptor $instantiated (struct))) + (type $other (descriptor $instantiated (struct))) + ;; YESTNH: (type $instantiated (sub $uninstantiated (describes $other (struct)))) + ;; NO_TNH: (type $instantiated (sub $uninstantiated (describes $other (struct)))) + (type $instantiated (sub $uninstantiated (describes $other (struct)))) + ) + + ;; YESTNH: (type $4 (func (result (ref $struct)))) + + ;; YESTNH: (type $5 (func)) + + ;; YESTNH: (import "" "" (func $effect (type $5))) + ;; NO_TNH: (type $4 (func (result (ref $struct)))) + + ;; NO_TNH: (type $5 (func)) + + ;; NO_TNH: (import "" "" (func $effect (type $5))) + (import "" "" (func $effect)) + + ;; YESTNH: (global $instantiated (ref $instantiated) (struct.new_default $instantiated)) + ;; NO_TNH: (global $instantiated (ref $instantiated) (struct.new_default $instantiated)) + (global $instantiated (ref $instantiated) (struct.new $instantiated)) + + ;; YESTNH: (global $fake-desc (ref null (exact $instantiated)) (ref.null none)) + ;; NO_TNH: (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) + (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) + + ;; YESTNH: (global $impossible (ref $struct) (struct.new_default $struct + ;; YESTNH-NEXT: (ref.null none) + ;; YESTNH-NEXT: )) + ;; NO_TNH: (global $impossible (ref $struct) (struct.new_default $struct + ;; NO_TNH-NEXT: (global.get $fake-desc) + ;; NO_TNH-NEXT: )) + (global $impossible (ref $struct) + (struct.new $struct + (global.get $fake-desc) + ) + ) + + ;; YESTNH: (func $impossible (type $4) (result (ref $struct)) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $impossible (type $4) (result (ref $struct)) + ;; NO_TNH-NEXT: (struct.new_default $struct + ;; NO_TNH-NEXT: (global.get $fake-desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $impossible (result (ref $struct)) + (struct.new $struct + (global.get $fake-desc) + ) + ) + + ;; YESTNH: (func $impossible-effect (type $4) (result (ref $struct)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result (ref null (exact $instantiated))) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (global.get $fake-desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $impossible-effect (type $4) (result (ref $struct)) + ;; NO_TNH-NEXT: (struct.new_default $struct + ;; NO_TNH-NEXT: (block (result (ref null (exact $uninstantiated))) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (global.get $fake-desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $impossible-effect (result (ref $struct)) + (struct.new $struct + (block (result (ref null (exact $uninstantiated))) + (call $effect) + (global.get $fake-desc) + ) + ) + ) +) + +(module + ;; Same, but now we're optimizing the descriptor to bottom, so we don't need + ;; to do any preoptimization to ensure validity. We optimize anyway because + ;; we can. + (rec + ;; YESTNH: (rec + ;; YESTNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) + ;; NO_TNH: (rec + ;; NO_TNH-NEXT: (type $struct (descriptor $uninstantiated (struct))) + (type $struct (descriptor $uninstantiated (struct))) + ;; YESTNH: (type $uninstantiated (describes $struct (struct))) + ;; NO_TNH: (type $uninstantiated (describes $struct (struct))) + (type $uninstantiated (describes $struct (struct))) + ) + + ;; YESTNH: (type $2 (func (result (ref $struct)))) + + ;; YESTNH: (type $3 (func)) + + ;; YESTNH: (import "" "" (func $effect (type $3))) + ;; NO_TNH: (type $2 (func (result (ref $struct)))) + + ;; NO_TNH: (type $3 (func)) + + ;; NO_TNH: (import "" "" (func $effect (type $3))) + (import "" "" (func $effect)) + + ;; YESTNH: (global $fake-desc nullref (ref.null none)) + ;; NO_TNH: (global $fake-desc nullref (ref.null none)) + (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none)) + + ;; YESTNH: (global $impossible (ref $struct) (struct.new_default $struct + ;; YESTNH-NEXT: (ref.null none) + ;; YESTNH-NEXT: )) + ;; NO_TNH: (global $impossible (ref $struct) (struct.new_default $struct + ;; NO_TNH-NEXT: (ref.null none) + ;; NO_TNH-NEXT: )) + (global $impossible (ref $struct) + (struct.new $struct + (global.get $fake-desc) + ) + ) + + ;; YESTNH: (func $impossible (type $2) (result (ref $struct)) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $impossible (type $2) (result (ref $struct)) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $impossible (result (ref $struct)) + (struct.new $struct + (global.get $fake-desc) + ) + ) + + ;; YESTNH: (func $impossible-effect (type $2) (result (ref $struct)) + ;; YESTNH-NEXT: (drop + ;; YESTNH-NEXT: (block (result nullref) + ;; YESTNH-NEXT: (call $effect) + ;; YESTNH-NEXT: (global.get $fake-desc) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: ) + ;; YESTNH-NEXT: (unreachable) + ;; YESTNH-NEXT: ) + ;; NO_TNH: (func $impossible-effect (type $2) (result (ref $struct)) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block (result nullref) + ;; NO_TNH-NEXT: (call $effect) + ;; NO_TNH-NEXT: (global.get $fake-desc) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + (func $impossible-effect (result (ref $struct)) + (struct.new $struct + (block (result (ref null (exact $uninstantiated))) + (call $effect) + (global.get $fake-desc) + ) + ) + ) +) From 02ed9190a8a91a28191df7964af0df613081c2d9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 11:27:03 -0700 Subject: [PATCH 5/7] feedbacl --- src/wasm/wasm-validator.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 77e4f8c5d77..9ed4027df94 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3979,17 +3979,15 @@ void FunctionValidator::visitContNew(ContNew* curr) { } shouldBeTrue(curr->type.isExact(), curr, "cont.new should be exact"); - if (!curr->type.isContinuation()) { - return; - } - - auto cont = curr->type.getHeapType().getContinuation(); - if (!shouldBeTrue(cont.type.isSignature(), + if (!shouldBeTrue(curr->type.isContinuation(), curr, "cont.new must be annotated with a continuation type")) { return; } + auto cont = curr->type.getHeapType().getContinuation(); + assert(cont.type.isSignature()); + shouldBeTrue(HeapType::isSubType(curr->func->type.getHeapType(), cont.type), curr, "cont.new function reference must be a subtype"); From 264ac13947307a19f29d62d3c7e931c080c8a295 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 12:24:59 -0700 Subject: [PATCH 6/7] test --- test/lit/passes/abstract-type-refining-cont.wast | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/lit/passes/abstract-type-refining-cont.wast b/test/lit/passes/abstract-type-refining-cont.wast index b6dfa173f85..13c96aaff43 100644 --- a/test/lit/passes/abstract-type-refining-cont.wast +++ b/test/lit/passes/abstract-type-refining-cont.wast @@ -2,14 +2,15 @@ ;; RUN: foreach %s %t wasm-opt --abstract-type-refining -all --closed-world -S -o - | filecheck %s +;; $uncreated is never created, so we optimize and rebuild types. While doing +;; so we should not get confused as we copy the continuation type, which +;; should end up referring properly to the corresponding func type. + (module (rec (type $func (func)) (type $cont (cont $func)) - ;; This type is never created, so we optimize and rebuild types. While doing - ;; so we should not get confused as we copy the continuation type, which - ;; should end up referring properly to the corresponding func type. (type $uncreated (struct)) ) From a0b2146cee04fe21970290d3d9bbe679a6a885ec Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 20 Aug 2025 12:26:52 -0700 Subject: [PATCH 7/7] fix --- test/lit/passes/abstract-type-refining-cont.wast | 15 +++++++++++++++ test/lit/passes/type-merging-cont.wast | 9 +++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/test/lit/passes/abstract-type-refining-cont.wast b/test/lit/passes/abstract-type-refining-cont.wast index 13c96aaff43..a33c88085a2 100644 --- a/test/lit/passes/abstract-type-refining-cont.wast +++ b/test/lit/passes/abstract-type-refining-cont.wast @@ -8,15 +8,30 @@ (module (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $cont (cont $func)) + + ;; CHECK: (type $func (func)) (type $func (func)) (type $cont (cont $func)) (type $uncreated (struct)) ) + ;; CHECK: (elem declare func $func) + + ;; CHECK: (func $func (type $func) + ;; CHECK-NEXT: ) (func $func (type $func) ) + ;; CHECK: (func $test (type $func) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (cont.new $cont + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $test (type $func) (drop (cont.new $cont diff --git a/test/lit/passes/type-merging-cont.wast b/test/lit/passes/type-merging-cont.wast index e8f059345b1..c7b57669d17 100644 --- a/test/lit/passes/type-merging-cont.wast +++ b/test/lit/passes/type-merging-cont.wast @@ -10,15 +10,12 @@ ;; CHECK: (rec ;; CHECK-NEXT: (type $f (func)) (type $f (func)) - ;; CHECK: (type $f_0 (func)) - - ;; CHECK: (type $k (cont $f_0)) + ;; CHECK: (type $k (cont $f)) (type $k (cont $f)) + ;; CHECK: (type $f-i32 (func (result i32))) (type $f-i32 (func (result i32))) - ;; CHECK: (type $f-i32_0 (func (result i32))) - - ;; CHECK: (type $k-i32 (cont $f-i32_0)) + ;; CHECK: (type $k-i32 (cont $f-i32)) (type $k-i32 (cont $f-i32)) (rec