diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 21d59a88bfb..1e121cedce2 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -867,11 +867,11 @@ template struct ChildTyper : OverriddenVisitor { void visitCallRef(CallRef* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->target->type.isRef()) { + if (!curr->target->type.isSignature()) { self().noteUnknown(); return; } - ht = curr->target->type.getHeapType().getSignature(); + ht = curr->target->type.getHeapType(); } auto params = ht->getSignature().params; handleCall(curr, params); @@ -891,11 +891,17 @@ template struct ChildTyper : OverriddenVisitor { } target = curr->type; } + std::optional desc; + if (curr->desc) { + desc = target->getHeapType().getDescriptorType(); + if (!desc) { + self().noteUnknown(); + return; + } + } auto top = target->getHeapType().getTop(); note(&curr->ref, Type(top, Nullable)); if (curr->desc) { - auto desc = target->getHeapType().getDescriptorType(); - assert(desc); note(&curr->desc, Type(*desc, Nullable, target->getExactness())); } } @@ -903,7 +909,7 @@ template struct ChildTyper : OverriddenVisitor { void visitRefGetDesc(RefGetDesc* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isRef() || curr->ref->type.isBottomRef()) { self().noteUnknown(); return; } @@ -936,11 +942,17 @@ template struct ChildTyper : OverriddenVisitor { out = curr->castType; in = Type(out->getHeapType().getTop(), Nullable); } - note(&curr->ref, *in); + std::optional desc; if (curr->op == BrOnCastDescEq || curr->op == BrOnCastDescEqFail) { - auto desc = out->getHeapType().getDescriptorType(); - assert(desc); - note(&curr->desc, Type(*desc, Nullable)); + desc = out->getHeapType().getDescriptorType(); + if (!desc) { + self().noteUnknown(); + return; + } + } + note(&curr->ref, *in); + if (desc) { + note(&curr->desc, Type(*desc, Nullable, out->getExactness())); } return; } @@ -949,7 +961,7 @@ template struct ChildTyper : OverriddenVisitor { } void visitStructNew(StructNew* curr) { - if (!curr->type.isRef()) { + if (!curr->type.isStruct()) { self().noteUnknown(); return; } @@ -968,7 +980,7 @@ template struct ChildTyper : OverriddenVisitor { void visitStructGet(StructGet* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isStruct()) { self().noteUnknown(); return; } @@ -980,7 +992,7 @@ template struct ChildTyper : OverriddenVisitor { void visitStructSet(StructSet* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isStruct()) { self().noteUnknown(); return; } @@ -995,7 +1007,7 @@ template struct ChildTyper : OverriddenVisitor { void visitStructRMW(StructRMW* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isStruct()) { self().noteUnknown(); return; } @@ -1010,7 +1022,7 @@ template struct ChildTyper : OverriddenVisitor { void visitStructCmpxchg(StructCmpxchg* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isStruct()) { self().noteUnknown(); return; } @@ -1063,7 +1075,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayNew(ArrayNew* curr) { if (!curr->isWithDefault()) { - if (!curr->type.isRef()) { + if (!curr->type.isArray()) { self().noteUnknown(); return; } @@ -1083,7 +1095,7 @@ template struct ChildTyper : OverriddenVisitor { } void visitArrayNewFixed(ArrayNewFixed* curr) { - if (!curr->type.isRef()) { + if (!curr->type.isArray()) { self().noteUnknown(); return; } @@ -1096,7 +1108,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayGet(ArrayGet* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1109,7 +1121,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArraySet(ArraySet* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1124,7 +1136,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayLoad(ArrayLoad* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1138,7 +1150,7 @@ template struct ChildTyper : OverriddenVisitor { std::optional ht = std::nullopt, std::optional valueType = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1162,14 +1174,14 @@ template struct ChildTyper : OverriddenVisitor { std::optional dest = std::nullopt, std::optional src = std::nullopt) { if (!dest) { - if (!curr->destRef->type.isRef()) { + if (!curr->destRef->type.isArray()) { self().noteUnknown(); return; } dest = curr->destRef->type.getHeapType(); } if (!src) { - if (!curr->srcRef->type.isRef()) { + if (!curr->srcRef->type.isArray()) { self().noteUnknown(); return; } @@ -1185,7 +1197,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayFill(ArrayFill* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1201,7 +1213,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayInitData(ArrayInitData* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1216,7 +1228,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayInitElem(ArrayInitElem* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1231,7 +1243,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayRMW(ArrayRMW* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1246,7 +1258,7 @@ template struct ChildTyper : OverriddenVisitor { void visitArrayCmpxchg(ArrayCmpxchg* curr, std::optional ht = std::nullopt) { if (!ht) { - if (!curr->ref->type.isRef()) { + if (!curr->ref->type.isArray()) { self().noteUnknown(); return; } @@ -1350,20 +1362,27 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->end, Type::i32); } - void visitContNew(ContNew* curr) { note(&curr->func, curr->type); } + void visitContNew(ContNew* curr) { + if (!curr->type.isContinuation()) { + self().noteUnknown(); + return; + } + note(&curr->func, + Type(curr->type.getHeapType().getContinuation().type, Nullable)); + } void visitContBind(ContBind* curr, std::optional src = std::nullopt, std::optional dest = std::nullopt) { if (!src) { - if (!curr->cont->type.isRef()) { + if (!curr->cont->type.isContinuation()) { self().noteUnknown(); return; } src = curr->cont->type.getHeapType(); } if (!dest) { - if (!curr->type.isRef()) { + if (!curr->type.isContinuation()) { self().noteUnknown(); return; } @@ -1390,7 +1409,7 @@ template struct ChildTyper : OverriddenVisitor { void visitResume(Resume* curr, std::optional ct = std::nullopt) { if (!ct) { - if (!curr->cont->type.isRef()) { + if (!curr->cont->type.isContinuation()) { self().noteUnknown(); return; } @@ -1408,7 +1427,7 @@ template struct ChildTyper : OverriddenVisitor { void visitResumeThrow(ResumeThrow* curr, std::optional ct = std::nullopt) { if (!ct) { - if (!curr->cont->type.isRef()) { + if (!curr->cont->type.isContinuation()) { self().noteUnknown(); return; } @@ -1432,7 +1451,7 @@ template struct ChildTyper : OverriddenVisitor { void visitStackSwitch(StackSwitch* curr, std::optional ct = std::nullopt) { if (!ct) { - if (!curr->cont->type.isRef()) { + if (!curr->cont->type.isContinuation()) { self().noteUnknown(); return; } diff --git a/src/passes/TypeSSA.cpp b/src/passes/TypeSSA.cpp index a913faa6c2b..02796310626 100644 --- a/src/passes/TypeSSA.cpp +++ b/src/passes/TypeSSA.cpp @@ -51,6 +51,7 @@ #include "ir/module-utils.h" #include "ir/names.h" #include "ir/possible-constant.h" +#include "ir/properties.h" #include "ir/utils.h" #include "pass.h" #include "support/hash.h" @@ -136,26 +137,26 @@ struct Analyzer } } void visitRefTest(RefTest* curr) { visitCast(curr); } - void visitRefCast(RefCast* curr) { visitCast(curr); } + void visitRefCast(RefCast* curr) { + visitCast(curr); + visitExpression(curr); + } void visitBrOn(BrOn* curr) { - if (curr->op == BrOnCast || curr->op == BrOnCastFail) { + if (curr->op != BrOnNull && curr->op != BrOnNonNull) { visitCast(curr); } + visitExpression(curr); } void visitExpression(Expression* curr) { // Look at the constraints on this expression's operands to see if it // requires an exact operand. If it does, we cannot optimize allocations of // that type. As an optimization, do not let control flow structures or - // branches inhibit optimization since they can safely be refinalized to use + // branches inhibit optimization. They can safely be refinalized to use // new types as long as no other instruction expected the original exact - // type. Also allow optimizing if the instruction that would inhibit - // optimizing will not be written in the final output. Skipping further - // analysis for these instructions also ensures that the ChildTyper below - // sees the type information it expects in the instructions it analyzes. + // type. if (Properties::isControlFlowStructure(curr) || - Properties::isBranch(curr) || - Properties::hasUnwritableTypeImmediate(curr)) { + Properties::isBranch(curr)) { return; } diff --git a/test/lit/passes/type-ssa-child-typer.wast b/test/lit/passes/type-ssa-child-typer.wast new file mode 100644 index 00000000000..58f6aa3046e --- /dev/null +++ b/test/lit/passes/type-ssa-child-typer.wast @@ -0,0 +1,375 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all --type-ssa --preserve-type-order -S -o - | filecheck %s + +;; These tests are for ChildTyper more than TypeSSA. They check that +;; ChildTyper does not crash when visiting instructions that require type +;; immediate information, whether or not that information is available. These +;; instructions are not sources of exactness for non-descriptor types that +;; TypeSSA would consider optimizing. + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $desc-struct (sub (descriptor $desc) (struct (field anyref)))) + (type $desc-struct (sub (descriptor $desc) (struct (field anyref)))) + ;; CHECK: (type $desc (sub (describes $desc-struct) (struct))) + (type $desc (sub (describes $desc-struct) (struct))) + ) + ;; CHECK: (type $i8-array (array (mut i8))) + (type $i8-array (array (field (mut i8)))) + ;; CHECK: (type $any-array (array (mut anyref))) + (type $any-array (array (field (mut anyref)))) + ;; CHECK: (type $func-empty (func)) + (type $func-empty (func)) + ;; CHECK: (type $cont-empty (cont $func-empty)) + (type $cont-empty (cont $func-empty)) + + ;; CHECK: (type $6 (func (param anyref (ref null (exact $desc))))) + + ;; CHECK: (type $7 (func (param anyref))) + + ;; CHECK: (type $8 (func (param (ref null $i8-array)))) + + ;; CHECK: (type $9 (func (param (ref null $any-array)))) + + ;; CHECK: (type $10 (func (param (ref null $func-empty)))) + + ;; CHECK: (data $data "") + (data $data "") + ;; CHECK: (elem $elem anyref) + (elem $elem anyref) + + ;; CHECK: (func $ref-cast-desc-eq (type $6) (param $any anyref) (param $desc-ref (ref null (exact $desc))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast_desc_eq (ref (exact $desc-struct)) + ;; CHECK-NEXT: (local.get $any) + ;; CHECK-NEXT: (local.get $desc-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-desc-eq (param $any anyref) (param $desc-ref (ref null (exact $desc))) + (drop + (ref.cast_desc_eq (ref (exact $desc-struct)) + (local.get $any) + (local.get $desc-ref) + ) + ) + ) + + ;; CHECK: (func $ref-cast-desc-eq-bot (type $7) (param $any anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-desc-eq-bot (param $any anyref) + (drop + (ref.cast_desc_eq (ref (exact $desc-struct)) + (local.get $any) + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-get (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.get_u $i8-array + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-get (param $i8-ref (ref null $i8-array)) + (drop + (array.get_u $i8-array + (local.get $i8-ref) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $array-get-bot (type $func-empty) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayGet we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-get-bot + (drop + (array.get_u $i8-array + (ref.null none) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $array-load (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load (type $i8-array) + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-load (param $i8-ref (ref null $i8-array)) + (drop + (i32.load (type $i8-array) + (local.get $i8-ref) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $array-load-bot (type $func-empty) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayLoad we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-load-bot + (drop + (i32.load (type $i8-array) + (ref.null none) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $array-store (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (i32.store (type $i8-array) + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-store (param $i8-ref (ref null $i8-array)) + (i32.store (type $i8-array) + (local.get $i8-ref) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-store-bot (type $func-empty) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayStore we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-store-bot + (i32.store (type $i8-array) + (ref.null none) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-copy-bot-dest (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-copy-bot-dest (param $i8-ref (ref null $i8-array)) + (array.copy $i8-array $i8-array + (ref.null none) + (i32.const 0) + (local.get $i8-ref) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-copy-bot-src (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCopy we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-copy-bot-src (param $i8-ref (ref null $i8-array)) + (array.copy $i8-array $i8-array + (local.get $i8-ref) + (i32.const 0) + (ref.null none) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-init-data (type $8) (param $i8-ref (ref null $i8-array)) + ;; CHECK-NEXT: (array.init_data $i8-array $data + ;; CHECK-NEXT: (local.get $i8-ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-init-data (param $i8-ref (ref null $i8-array)) + (array.init_data $i8-array $data + (local.get $i8-ref) + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-init-data-bot (type $func-empty) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitData we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-init-data-bot + (array.init_data $i8-array $data + (ref.null none) + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-init-elem (type $9) (param $any-arr-ref (ref null $any-array)) + ;; CHECK-NEXT: (array.init_elem $any-array $elem + ;; CHECK-NEXT: (local.get $any-arr-ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-init-elem (param $any-arr-ref (ref null $any-array)) + (array.init_elem $any-array $elem + (local.get $any-arr-ref) + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $array-init-elem-bot (type $func-empty) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayInitElem we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-init-elem-bot + (array.init_elem $any-array $elem + (ref.null none) + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + ) + + ;; CHECK: (func $cont-new (type $10) (param $func (ref null $func-empty)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (cont.new $cont-empty + ;; CHECK-NEXT: (local.get $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-new (param $func (ref null $func-empty)) + (drop + (cont.new $cont-empty + (local.get $func) + ) + ) + ) + + ;; CHECK: (func $cont-new-unreachable (type $func-empty) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ContNew we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-new-unreachable + (drop + (cont.new $cont-empty + (unreachable) + ) + ) + ) +) diff --git a/test/lit/passes/type-ssa-exact-rmw.wast b/test/lit/passes/type-ssa-exact-rmw.wast index d658a34dc6c..a32e1dc8b30 100644 --- a/test/lit/passes/type-ssa-exact-rmw.wast +++ b/test/lit/passes/type-ssa-exact-rmw.wast @@ -8,42 +8,106 @@ (type $used-in-struct-rmw (sub (struct (field anyref)))) ;; CHECK: (type $used-in-struct-rmw-ok (sub (struct (field anyref)))) (type $used-in-struct-rmw-ok (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-struct-rmw-bot (sub (struct (field anyref)))) + (type $used-in-struct-rmw-bot (sub (struct (field anyref)))) ;; CHECK: (type $expected-in-struct-cmpxchg (sub (struct (field anyref)))) (type $expected-in-struct-cmpxchg (sub (struct (field anyref)))) ;; CHECK: (type $used-in-struct-cmpxchg (sub $expected-in-struct-cmpxchg (struct (field anyref)))) (type $used-in-struct-cmpxchg (sub $expected-in-struct-cmpxchg (struct (field anyref)))) ;; CHECK: (type $used-in-struct-cmpxchg-ok (sub $expected-in-struct-cmpxchg (struct (field anyref)))) (type $used-in-struct-cmpxchg-ok (sub $expected-in-struct-cmpxchg (struct (field anyref)))) + ;; CHECK: (type $used-in-struct-cmpxchg-bot (sub $expected-in-struct-cmpxchg (struct (field anyref)))) + (type $used-in-struct-cmpxchg-bot (sub $expected-in-struct-cmpxchg (struct (field anyref)))) + ;; CHECK: (type $used-in-array-rmw (sub (struct (field anyref)))) + (type $used-in-array-rmw (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-array-rmw-ok (sub (struct (field anyref)))) + (type $used-in-array-rmw-ok (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-array-rmw-bot (sub (struct (field anyref)))) + (type $used-in-array-rmw-bot (sub (struct (field anyref)))) + ;; CHECK: (type $expected-in-array-cmpxchg (sub (struct (field anyref)))) + (type $expected-in-array-cmpxchg (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-array-cmpxchg (sub $expected-in-array-cmpxchg (struct (field anyref)))) + (type $used-in-array-cmpxchg (sub $expected-in-array-cmpxchg (struct (field anyref)))) + ;; CHECK: (type $used-in-array-cmpxchg-ok (sub $expected-in-array-cmpxchg (struct (field anyref)))) + (type $used-in-array-cmpxchg-ok (sub $expected-in-array-cmpxchg (struct (field anyref)))) + ;; CHECK: (type $used-in-array-cmpxchg-bot (sub $expected-in-array-cmpxchg (struct (field anyref)))) + (type $used-in-array-cmpxchg-bot (sub $expected-in-array-cmpxchg (struct (field anyref)))) ) ;; CHECK: (type $struct-rmw-exact (struct (field (mut (ref (exact $used-in-struct-rmw)))))) (type $struct-rmw-exact (struct (field (mut (ref (exact $used-in-struct-rmw)))))) ;; CHECK: (type $struct-rmw-inexact (struct (field (mut (ref $used-in-struct-rmw-ok))))) (type $struct-rmw-inexact (struct (field (mut (ref $used-in-struct-rmw-ok))))) + (type $struct-rmw-bot (struct (field (mut (ref (exact $used-in-struct-rmw-bot)))))) ;; CHECK: (type $struct-cmpxchg-exact (struct (field (mut (ref (exact $used-in-struct-cmpxchg)))))) (type $struct-cmpxchg-exact (struct (field (mut (ref (exact $used-in-struct-cmpxchg)))))) ;; CHECK: (type $struct-cmpxchg-inexact (struct (field (mut (ref $used-in-struct-cmpxchg-ok))))) (type $struct-cmpxchg-inexact (struct (field (mut (ref $used-in-struct-cmpxchg-ok))))) + (type $struct-cmpxchg-bot (struct (field (mut (ref (exact $used-in-struct-cmpxchg-bot)))))) - ;; CHECK: (type $9 (func (param (ref (exact $used-in-struct-rmw)) (ref $struct-rmw-exact)))) + ;; CHECK: (type $array-rmw-exact (array (mut (ref (exact $used-in-array-rmw))))) + (type $array-rmw-exact (array (field (mut (ref (exact $used-in-array-rmw)))))) + ;; CHECK: (type $array-rmw-inexact (array (mut (ref $used-in-array-rmw-ok)))) + (type $array-rmw-inexact (array (field (mut (ref $used-in-array-rmw-ok))))) + (type $array-rmw-bot (array (field (mut (ref (exact $used-in-array-rmw-bot)))))) - ;; CHECK: (type $10 (func (param (ref (exact $used-in-struct-rmw-ok)) (ref $struct-rmw-inexact)))) + ;; CHECK: (type $array-cmpxchg-exact (array (mut (ref (exact $used-in-array-cmpxchg))))) + (type $array-cmpxchg-exact (array (field (mut (ref (exact $used-in-array-cmpxchg)))))) + ;; CHECK: (type $array-cmpxchg-inexact (array (mut (ref $used-in-array-cmpxchg-ok)))) + (type $array-cmpxchg-inexact (array (field (mut (ref $used-in-array-cmpxchg-ok))))) + (type $array-cmpxchg-bot (array (field (mut (ref (exact $used-in-array-cmpxchg-bot)))))) - ;; CHECK: (type $11 (func (param (ref (exact $used-in-struct-cmpxchg)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-exact)))) + ;; CHECK: (type $22 (func (param (ref (exact $used-in-struct-rmw)) (ref $struct-rmw-exact)))) - ;; CHECK: (type $12 (func (param (ref (exact $used-in-struct-cmpxchg-ok)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-inexact)))) + ;; CHECK: (type $23 (func (param (ref (exact $used-in-struct-rmw-ok)) (ref $struct-rmw-inexact)))) + + ;; CHECK: (type $24 (func (param (ref (exact $used-in-struct-rmw-bot))))) + + ;; CHECK: (type $25 (func (param (ref (exact $used-in-struct-cmpxchg)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-exact)))) + + ;; CHECK: (type $26 (func (param (ref (exact $used-in-struct-cmpxchg-ok)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-inexact)))) + + ;; CHECK: (type $27 (func (param (ref (exact $used-in-struct-cmpxchg-bot)) (ref (exact $expected-in-struct-cmpxchg))))) + + ;; CHECK: (type $28 (func (param (ref (exact $used-in-array-rmw)) (ref $array-rmw-exact)))) + + ;; CHECK: (type $29 (func (param (ref (exact $used-in-array-rmw-ok)) (ref $array-rmw-inexact)))) + + ;; CHECK: (type $30 (func (param (ref (exact $used-in-array-rmw-bot))))) + + ;; CHECK: (type $31 (func (param (ref (exact $used-in-array-cmpxchg)) (ref (exact $expected-in-array-cmpxchg)) (ref $array-cmpxchg-exact)))) + + ;; CHECK: (type $32 (func (param (ref (exact $used-in-array-cmpxchg-ok)) (ref (exact $expected-in-array-cmpxchg)) (ref $array-cmpxchg-inexact)))) + + ;; CHECK: (type $33 (func (param (ref (exact $used-in-array-cmpxchg-bot)) (ref (exact $expected-in-array-cmpxchg))))) ;; CHECK: (rec ;; CHECK-NEXT: (type $used-in-struct-rmw-ok_1 (sub $used-in-struct-rmw-ok (struct (field anyref)))) - ;; CHECK: (type $expected-in-struct-cmpxchg_2 (sub $expected-in-struct-cmpxchg (struct (field anyref)))) + ;; CHECK: (type $used-in-struct-rmw-bot_2 (sub $used-in-struct-rmw-bot (struct (field anyref)))) ;; CHECK: (type $expected-in-struct-cmpxchg_3 (sub $expected-in-struct-cmpxchg (struct (field anyref)))) - ;; CHECK: (type $used-in-struct-cmpxchg-ok_4 (sub $used-in-struct-cmpxchg-ok (struct (field anyref)))) + ;; CHECK: (type $expected-in-struct-cmpxchg_4 (sub $expected-in-struct-cmpxchg (struct (field anyref)))) + + ;; CHECK: (type $used-in-struct-cmpxchg-ok_5 (sub $used-in-struct-cmpxchg-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-struct-cmpxchg-bot_6 (sub $used-in-struct-cmpxchg-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-rmw-ok_7 (sub $used-in-array-rmw-ok (struct (field anyref)))) - ;; CHECK: (func $struct-rmw (type $9) (param $used (ref (exact $used-in-struct-rmw))) (param $ref (ref $struct-rmw-exact)) + ;; CHECK: (type $used-in-array-rmw-bot_8 (sub $used-in-array-rmw-bot (struct (field anyref)))) + + ;; CHECK: (type $expected-in-array-cmpxchg_9 (sub $expected-in-array-cmpxchg (struct (field anyref)))) + + ;; CHECK: (type $expected-in-array-cmpxchg_10 (sub $expected-in-array-cmpxchg (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-cmpxchg-ok_11 (sub $used-in-array-cmpxchg-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-cmpxchg-bot_12 (sub $used-in-array-cmpxchg-bot (struct (field anyref)))) + + ;; CHECK: (func $struct-rmw (type $22) (param $used (ref (exact $used-in-struct-rmw))) (param $ref (ref $struct-rmw-exact)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.atomic.rmw.xchg $struct-rmw-exact 0 ;; CHECK-NEXT: (local.get $ref) @@ -73,7 +137,7 @@ ) ) - ;; CHECK: (func $struct-rmw-ok (type $10) (param $used (ref (exact $used-in-struct-rmw-ok))) (param $ref (ref $struct-rmw-inexact)) + ;; CHECK: (func $struct-rmw-ok (type $23) (param $used (ref (exact $used-in-struct-rmw-ok))) (param $ref (ref $struct-rmw-inexact)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.atomic.rmw.xchg $struct-rmw-inexact 0 ;; CHECK-NEXT: (local.get $ref) @@ -102,7 +166,41 @@ ) ) - ;; CHECK: (func $struct-cmpxchg (type $11) (param $used (ref (exact $used-in-struct-cmpxchg))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-exact)) + ;; CHECK: (func $struct-rmw-bot (type $24) (param $used (ref (exact $used-in-struct-rmw-bot))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable StructRMW we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-struct-rmw-bot_2 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $struct-rmw-bot (param $used (ref (exact $used-in-struct-rmw-bot))) + ;; This RMW has a null reference, so it will not be emitted and does not + ;; inhibit optimization. + (drop + (struct.atomic.rmw.xchg $struct-rmw-bot 0 + (ref.null none) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-struct-rmw-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $struct-cmpxchg (type $25) (param $used (ref (exact $used-in-struct-cmpxchg))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-exact)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $struct-cmpxchg-exact 0 ;; CHECK-NEXT: (local.get $ref) @@ -111,7 +209,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new $expected-in-struct-cmpxchg_2 + ;; CHECK-NEXT: (struct.new $expected-in-struct-cmpxchg_3 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -142,7 +240,7 @@ ) ) - ;; CHECK: (func $struct-cmpxchg-ok (type $12) (param $used (ref (exact $used-in-struct-cmpxchg-ok))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-inexact)) + ;; CHECK: (func $struct-cmpxchg-ok (type $26) (param $used (ref (exact $used-in-struct-cmpxchg-ok))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-inexact)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $struct-cmpxchg-inexact 0 ;; CHECK-NEXT: (local.get $ref) @@ -151,12 +249,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new $expected-in-struct-cmpxchg_3 + ;; CHECK-NEXT: (struct.new $expected-in-struct-cmpxchg_4 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new $used-in-struct-cmpxchg-ok_4 + ;; CHECK-NEXT: (struct.new $used-in-struct-cmpxchg-ok_5 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -181,4 +279,264 @@ ) ) ) + + ;; CHECK: (func $struct-cmpxchg-bot (type $27) (param $used (ref (exact $used-in-struct-cmpxchg-bot))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable StructCmpxchg we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $expected) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-struct-cmpxchg-bot_6 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $struct-cmpxchg-bot (param $used (ref (exact $used-in-struct-cmpxchg-bot))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) + ;; This cmpxchg has a null reference, so it will not be emitted and does not + ;; inhibit optimization. + (drop + (struct.atomic.rmw.cmpxchg $struct-cmpxchg-bot 0 + (ref.null none) + (local.get $expected) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-struct-cmpxchg-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-rmw (type $28) (param $used (ref (exact $used-in-array-rmw))) (param $ref (ref $array-rmw-exact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.atomic.rmw.xchg $array-rmw-exact + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-rmw + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-rmw (param $used (ref (exact $used-in-array-rmw))) (param $ref (ref $array-rmw-exact)) + ;; This requires and observes an exact $used-in-array-rmw. + (drop + (array.atomic.rmw.xchg $array-rmw-exact + (local.get $ref) + (i32.const 0) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-array-rmw + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-rmw-ok (type $29) (param $used (ref (exact $used-in-array-rmw-ok))) (param $ref (ref $array-rmw-inexact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.atomic.rmw.xchg $array-rmw-inexact + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-rmw-ok_7 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-rmw-ok (param $used (ref (exact $used-in-array-rmw-ok))) (param $ref (ref $array-rmw-inexact)) + ;; The array element type is inexact, so this can be optimized. + (drop + (array.atomic.rmw.xchg $array-rmw-inexact + (local.get $ref) + (i32.const 0) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-array-rmw-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-rmw-bot (type $30) (param $used (ref (exact $used-in-array-rmw-bot))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayRMW we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-rmw-bot_8 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-rmw-bot (param $used (ref (exact $used-in-array-rmw-bot))) + ;; A null array reference does not inhibit optimization. + (drop + (array.atomic.rmw.xchg $array-rmw-bot + (ref.null none) + (i32.const 0) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-array-rmw-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-cmpxchg (type $31) (param $used (ref (exact $used-in-array-cmpxchg))) (param $expected (ref (exact $expected-in-array-cmpxchg))) (param $ref (ref $array-cmpxchg-exact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.atomic.rmw.cmpxchg $array-cmpxchg-exact + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $expected) + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $expected-in-array-cmpxchg_9 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-cmpxchg + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-cmpxchg (param $used (ref (exact $used-in-array-cmpxchg))) (param $expected (ref (exact $expected-in-array-cmpxchg))) (param $ref (ref $array-cmpxchg-exact)) + ;; Only the replacement value is constrained to be exact. + (drop + (array.atomic.rmw.cmpxchg $array-cmpxchg-exact + (local.get $ref) + (i32.const 0) + (local.get $expected) + (local.get $used) + ) + ) + (drop + (struct.new $expected-in-array-cmpxchg + (ref.null none) + ) + ) + (drop + (struct.new $used-in-array-cmpxchg + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-cmpxchg-ok (type $32) (param $used (ref (exact $used-in-array-cmpxchg-ok))) (param $expected (ref (exact $expected-in-array-cmpxchg))) (param $ref (ref $array-cmpxchg-inexact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (array.atomic.rmw.cmpxchg $array-cmpxchg-inexact + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $expected) + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $expected-in-array-cmpxchg_10 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-cmpxchg-ok_11 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-cmpxchg-ok (param $used (ref (exact $used-in-array-cmpxchg-ok))) (param $expected (ref (exact $expected-in-array-cmpxchg))) (param $ref (ref $array-cmpxchg-inexact)) + ;; Now both can be optimized. + (drop + (array.atomic.rmw.cmpxchg $array-cmpxchg-inexact + (local.get $ref) + (i32.const 0) + (local.get $expected) + (local.get $used) + ) + ) + (drop + (struct.new $expected-in-array-cmpxchg + (ref.null none) + ) + ) + (drop + (struct.new $used-in-array-cmpxchg-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-cmpxchg-bot (type $33) (param $used (ref (exact $used-in-array-cmpxchg-bot))) (param $expected (ref (exact $expected-in-array-cmpxchg))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayCmpxchg we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $expected) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-cmpxchg-bot_12 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-cmpxchg-bot (param $used (ref (exact $used-in-array-cmpxchg-bot))) (param $expected (ref (exact $expected-in-array-cmpxchg))) + ;; A null array reference does not inhibit optimization. + (drop + (array.atomic.rmw.cmpxchg $array-cmpxchg-bot + (ref.null none) + (i32.const 0) + (local.get $expected) + (local.get $used) + ) + ) + (drop + (struct.new $used-in-array-cmpxchg-bot + (ref.null none) + ) + ) + ) ) diff --git a/test/lit/passes/type-ssa-exact.wast b/test/lit/passes/type-ssa-exact.wast index 3d9d1d6205b..094a164e524 100644 --- a/test/lit/passes/type-ssa-exact.wast +++ b/test/lit/passes/type-ssa-exact.wast @@ -1738,3 +1738,934 @@ (table $table 1 1 (ref (exact $sub)) (struct.new $sub)) ) +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $used-in-br-on-null (sub (struct (field anyref)))) + (type $used-in-br-on-null (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-br-on-non-null (sub (struct (field anyref)))) + (type $used-in-br-on-non-null (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-ref-cast-unreachable (sub (struct (field anyref)))) + (type $used-in-ref-cast-unreachable (sub (struct (field anyref)))) + ;; CHECK: (type $desc-struct (sub (descriptor $desc) (struct (field anyref)))) + (type $desc-struct (sub (descriptor $desc) (struct (field anyref)))) + ;; CHECK: (type $desc (sub (describes $desc-struct) (struct))) + (type $desc (sub (describes $desc-struct) (struct))) + ;; CHECK: (type $struct-get-type (sub (struct (field i32)))) + (type $struct-get-type (sub (struct (field i32)))) + ;; CHECK: (type $used-in-struct-set-bot (sub (struct (field anyref)))) + (type $used-in-struct-set-bot (sub (struct (field anyref)))) + ;; CHECK: (type $struct-set-bot (struct (field (mut (ref (exact $used-in-struct-set-bot)))))) + (type $struct-set-bot (struct (field (mut (ref (exact $used-in-struct-set-bot)))))) + ;; CHECK: (type $used-in-array-new-unreachable (sub (struct (field anyref)))) + (type $used-in-array-new-unreachable (sub (struct (field anyref)))) + ;; CHECK: (type $array-new-unreachable (array (ref (exact $used-in-array-new-unreachable)))) + (type $array-new-unreachable (array (field (ref (exact $used-in-array-new-unreachable))))) + ;; CHECK: (type $used-in-array-new-fixed-unreachable (sub (struct (field anyref)))) + (type $used-in-array-new-fixed-unreachable (sub (struct (field anyref)))) + ;; CHECK: (type $array-new-fixed-unreachable (array (ref (exact $used-in-array-new-fixed-unreachable)))) + (type $array-new-fixed-unreachable (array (field (ref (exact $used-in-array-new-fixed-unreachable))))) + ;; CHECK: (type $used-in-array-set-bot (sub (struct (field anyref)))) + (type $used-in-array-set-bot (sub (struct (field anyref)))) + ;; CHECK: (type $array-set-bot (array (mut (ref (exact $used-in-array-set-bot))))) + (type $array-set-bot (array (field (mut (ref (exact $used-in-array-set-bot)))))) + ;; CHECK: (type $used-in-array-fill-bot (sub (struct (field anyref)))) + (type $used-in-array-fill-bot (sub (struct (field anyref)))) + ;; CHECK: (type $array-fill-bot (array (mut (ref (exact $used-in-array-fill-bot))))) + (type $array-fill-bot (array (field (mut (ref (exact $used-in-array-fill-bot)))))) + ;; CHECK: (type $func-empty (func)) + (type $func-empty (func)) + ;; CHECK: (type $cont-empty (cont $func-empty)) + (type $cont-empty (cont $func-empty)) + ;; CHECK: (type $used-in-cont-bind (sub (struct (field anyref)))) + (type $used-in-cont-bind (sub (struct (field anyref)))) + ;; CHECK: (type $func-cont-bind-exact (func (param (ref (exact $used-in-cont-bind))))) + (type $func-cont-bind-exact (func (param (ref (exact $used-in-cont-bind))))) + ;; CHECK: (type $cont-bind-exact (cont $func-cont-bind-exact)) + (type $cont-bind-exact (cont $func-cont-bind-exact)) + ;; CHECK: (type $used-in-cont-bind-ok (sub (struct (field anyref)))) + (type $used-in-cont-bind-ok (sub (struct (field anyref)))) + ;; CHECK: (type $func-cont-bind-inexact (func (param (ref $used-in-cont-bind-ok)))) + (type $func-cont-bind-inexact (func (param (ref $used-in-cont-bind-ok)))) + ;; CHECK: (type $cont-bind-inexact (cont $func-cont-bind-inexact)) + (type $cont-bind-inexact (cont $func-cont-bind-inexact)) + ;; CHECK: (type $used-in-cont-bind-bot (sub (struct (field anyref)))) + (type $used-in-cont-bind-bot (sub (struct (field anyref)))) + ;; CHECK: (type $func-cont-bind-bot (func (param (ref (exact $used-in-cont-bind-bot))))) + (type $func-cont-bind-bot (func (param (ref (exact $used-in-cont-bind-bot))))) + ;; CHECK: (type $cont-bind-bot (cont $func-cont-bind-bot)) + (type $cont-bind-bot (cont $func-cont-bind-bot)) + ;; CHECK: (type $used-in-resume (sub (struct (field anyref)))) + (type $used-in-resume (sub (struct (field anyref)))) + ;; CHECK: (type $func-resume-exact (func (param (ref (exact $used-in-resume))))) + (type $func-resume-exact (func (param (ref (exact $used-in-resume))))) + ;; CHECK: (type $cont-resume-exact (cont $func-resume-exact)) + (type $cont-resume-exact (cont $func-resume-exact)) + ;; CHECK: (type $used-in-resume-ok (sub (struct (field anyref)))) + (type $used-in-resume-ok (sub (struct (field anyref)))) + ;; CHECK: (type $func-resume-inexact (func (param (ref $used-in-resume-ok)))) + (type $func-resume-inexact (func (param (ref $used-in-resume-ok)))) + ;; CHECK: (type $cont-resume-inexact (cont $func-resume-inexact)) + (type $cont-resume-inexact (cont $func-resume-inexact)) + ;; CHECK: (type $used-in-resume-bot (sub (struct (field anyref)))) + (type $used-in-resume-bot (sub (struct (field anyref)))) + ;; CHECK: (type $func-resume-bot (func (param (ref (exact $used-in-resume-bot))))) + (type $func-resume-bot (func (param (ref (exact $used-in-resume-bot))))) + ;; CHECK: (type $cont-resume-bot (cont $func-resume-bot)) + (type $cont-resume-bot (cont $func-resume-bot)) + ;; CHECK: (type $used-in-resume-throw (sub (struct (field anyref)))) + (type $used-in-resume-throw (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-resume-throw-ok (sub (struct (field anyref)))) + (type $used-in-resume-throw-ok (sub (struct (field anyref)))) + ;; CHECK: (type $used-in-resume-throw-bot (sub (struct (field anyref)))) + (type $used-in-resume-throw-bot (sub (struct (field anyref)))) + ;; CHECK: (type $func-switch-target (func)) + (type $func-switch-target (func)) + ;; CHECK: (type $cont-switch-target (cont $func-switch-target)) + (type $cont-switch-target (cont $func-switch-target)) + ;; CHECK: (type $used-in-switch (sub (struct (field anyref)))) + (type $used-in-switch (sub (struct (field anyref)))) + ;; CHECK: (type $func-switch-exact (func (param (ref (exact $used-in-switch)) (ref null $cont-switch-target)))) + (type $func-switch-exact (func (param (ref (exact $used-in-switch)) (ref null $cont-switch-target)))) + ;; CHECK: (type $cont-switch-exact (cont $func-switch-exact)) + (type $cont-switch-exact (cont $func-switch-exact)) + ;; CHECK: (type $used-in-switch-ok (sub (struct (field anyref)))) + (type $used-in-switch-ok (sub (struct (field anyref)))) + ;; CHECK: (type $func-switch-inexact (func (param (ref $used-in-switch-ok) (ref null $cont-switch-target)))) + (type $func-switch-inexact (func (param (ref $used-in-switch-ok) (ref null $cont-switch-target)))) + ;; CHECK: (type $cont-switch-inexact (cont $func-switch-inexact)) + (type $cont-switch-inexact (cont $func-switch-inexact)) + ;; CHECK: (type $used-in-switch-bot (sub (struct (field anyref)))) + (type $used-in-switch-bot (sub (struct (field anyref)))) + ;; CHECK: (type $func-switch-bot (func (param (ref (exact $used-in-switch-bot)) (ref null $cont-switch-target)))) + (type $func-switch-bot (func (param (ref (exact $used-in-switch-bot)) (ref null $cont-switch-target)))) + ;; CHECK: (type $cont-switch-bot (cont $func-switch-bot)) + (type $cont-switch-bot (cont $func-switch-bot)) + ) + + ;; CHECK: (type $50 (func (param (ref (exact $used-in-resume-throw))))) + + ;; CHECK: (type $51 (func (param (ref $used-in-resume-throw-ok)))) + + ;; CHECK: (type $52 (func (param (ref (exact $used-in-resume-throw-bot))))) + + ;; CHECK: (type $53 (func)) + + ;; CHECK: (type $54 (func (param (ref null (exact $used-in-br-on-null))))) + + ;; CHECK: (type $55 (func (param (ref null (exact $used-in-br-on-non-null))))) + + ;; CHECK: (type $56 (func (param (ref null $desc-struct)))) + + ;; CHECK: (type $57 (func (param anyref (ref null (exact $desc))))) + + ;; CHECK: (type $58 (func (param anyref))) + + ;; CHECK: (type $59 (func (param (ref null $struct-get-type)))) + + ;; CHECK: (type $60 (func (param (ref (exact $used-in-struct-set-bot))))) + + ;; CHECK: (type $61 (func (param (ref (exact $used-in-array-new-unreachable))))) + + ;; CHECK: (type $62 (func (param (ref (exact $used-in-array-new-fixed-unreachable))))) + + ;; CHECK: (type $63 (func (param (ref (exact $used-in-array-set-bot))))) + + ;; CHECK: (type $64 (func (param (ref (exact $used-in-array-fill-bot))))) + + ;; CHECK: (type $65 (func (param (ref (exact $used-in-cont-bind)) (ref null $cont-bind-exact)))) + + ;; CHECK: (type $66 (func (param (ref (exact $used-in-cont-bind-ok)) (ref null $cont-bind-inexact)))) + + ;; CHECK: (type $67 (func (param (ref (exact $used-in-cont-bind-bot))))) + + ;; CHECK: (type $68 (func (param (ref null $cont-bind-inexact)))) + + ;; CHECK: (type $69 (func (param (ref (exact $used-in-resume)) (ref null $cont-resume-exact)))) + + ;; CHECK: (type $70 (func (param (ref (exact $used-in-resume-ok)) (ref null $cont-resume-inexact)))) + + ;; CHECK: (type $71 (func (param (ref (exact $used-in-resume-bot))))) + + ;; CHECK: (type $72 (func (param (ref (exact $used-in-resume-throw)) (ref null $cont-empty)))) + + ;; CHECK: (type $73 (func (param (ref (exact $used-in-resume-throw-ok)) (ref null $cont-empty)))) + + ;; CHECK: (type $74 (func (param (ref (exact $used-in-switch)) (ref null $cont-switch-exact)))) + + ;; CHECK: (type $75 (func (param (ref (exact $used-in-switch-ok)) (ref null $cont-switch-inexact)))) + + ;; CHECK: (type $76 (func (param (ref (exact $used-in-switch-bot))))) + + ;; CHECK: (rec + ;; CHECK-NEXT: (type $used-in-br-on-null_1 (sub $used-in-br-on-null (struct (field anyref)))) + + ;; CHECK: (type $used-in-br-on-non-null_2 (sub $used-in-br-on-non-null (struct (field anyref)))) + + ;; CHECK: (type $used-in-ref-cast-unreachable_3 (sub $used-in-ref-cast-unreachable (struct (field anyref)))) + + ;; CHECK: (type $used-in-struct-set-bot_4 (sub $used-in-struct-set-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-new-unreachable_5 (sub $used-in-array-new-unreachable (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-new-fixed-unreachable_6 (sub $used-in-array-new-fixed-unreachable (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-set-bot_7 (sub $used-in-array-set-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-array-fill-bot_8 (sub $used-in-array-fill-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-cont-bind-ok_9 (sub $used-in-cont-bind-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-cont-bind-bot_10 (sub $used-in-cont-bind-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-resume-ok_11 (sub $used-in-resume-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-resume-bot_12 (sub $used-in-resume-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-resume-throw-ok_13 (sub $used-in-resume-throw-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-resume-throw-bot_14 (sub $used-in-resume-throw-bot (struct (field anyref)))) + + ;; CHECK: (type $used-in-switch-ok_15 (sub $used-in-switch-ok (struct (field anyref)))) + + ;; CHECK: (type $used-in-switch-bot_16 (sub $used-in-switch-bot (struct (field anyref)))) + + ;; CHECK: (tag $tag-exact (type $50) (param (ref (exact $used-in-resume-throw)))) + (tag $tag-exact (param (ref (exact $used-in-resume-throw)))) + ;; CHECK: (tag $tag-inexact (type $51) (param (ref $used-in-resume-throw-ok))) + (tag $tag-inexact (param (ref $used-in-resume-throw-ok))) + ;; CHECK: (tag $tag-bot (type $52) (param (ref (exact $used-in-resume-throw-bot)))) + (tag $tag-bot (param (ref (exact $used-in-resume-throw-bot)))) + ;; CHECK: (tag $switch-tag (type $53)) + (tag $switch-tag) + + ;; CHECK: (func $br-on-null (type $54) (param $used (ref null (exact $used-in-br-on-null))) + ;; CHECK-NEXT: (block $l + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_on_null $l + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-br-on-null_1 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-null (param $used (ref null (exact $used-in-br-on-null))) + ;; br_on_null does not inhibit optimization. + (block $l + (drop + (br_on_null $l + (local.get $used) + ) + ) + ) + (drop + (struct.new $used-in-br-on-null + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $br-on-non-null (type $55) (param $used (ref null (exact $used-in-br-on-non-null))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $l (result (ref (exact $used-in-br-on-non-null))) + ;; CHECK-NEXT: (br_on_non_null $l + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-br-on-non-null_2 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-non-null (param $used (ref null (exact $used-in-br-on-non-null))) + ;; br_on_non_null does not inhibit optimization. + (drop + (block $l (result (ref (exact $used-in-br-on-non-null))) + (br_on_non_null $l + (local.get $used) + ) + (unreachable) + ) + ) + (drop + (struct.new $used-in-br-on-non-null + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $ref-cast-unreachable (type $53) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-ref-cast-unreachable_3 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-unreachable + ;; Unreachable ref.cast does not inhibit optimization. + (drop + (ref.cast (ref (exact $used-in-ref-cast-unreachable)) + (unreachable) + ) + ) + (drop + (struct.new $used-in-ref-cast-unreachable + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $ref-get-desc (type $56) (param $desc-struct-ref (ref null $desc-struct)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.get_desc $desc-struct + ;; CHECK-NEXT: (local.get $desc-struct-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-get-desc (param $desc-struct-ref (ref null $desc-struct)) + (drop + (ref.get_desc $desc-struct + (local.get $desc-struct-ref) + ) + ) + ) + + ;; CHECK: (func $ref-get-desc-unreachable (type $53) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable RefGetDesc we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-get-desc-unreachable + (drop + (ref.get_desc $desc-struct + (unreachable) + ) + ) + ) + + ;; CHECK: (func $ref-get-desc-bot (type $53) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable RefGetDesc we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-get-desc-bot + (drop + (ref.get_desc $desc-struct + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $br-on-cast-desc-eq (type $57) (param $any anyref) (param $desc-ref (ref null (exact $desc))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $l (result (ref (exact $desc-struct))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_on_cast_desc_eq $l anyref (ref (exact $desc-struct)) + ;; CHECK-NEXT: (local.get $any) + ;; CHECK-NEXT: (local.get $desc-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-desc-eq (param $any anyref) (param $desc-ref (ref null (exact $desc))) + (drop + (block $l (result (ref (exact $desc-struct))) + (drop + (br_on_cast_desc_eq $l anyref (ref (exact $desc-struct)) + (local.get $any) + (local.get $desc-ref) + ) + ) + (unreachable) + ) + ) + ) + + ;; CHECK: (func $br-on-cast-desc-eq-bot (type $58) (param $any anyref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $l (result (ref none)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable BrOn we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-desc-eq-bot (param $any anyref) + (drop + (block $l (result (ref (exact $desc-struct))) + (drop + (br_on_cast_desc_eq $l anyref (ref (exact $desc-struct)) + (local.get $any) + (ref.null none) + ) + ) + (unreachable) + ) + ) + ) + + ;; CHECK: (func $struct-get (type $59) (param $struct-ref (ref null $struct-get-type)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct-get-type 0 + ;; CHECK-NEXT: (local.get $struct-ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $struct-get (param $struct-ref (ref null $struct-get-type)) + (drop + (struct.get $struct-get-type 0 + (local.get $struct-ref) + ) + ) + ) + + ;; CHECK: (func $struct-get-bot (type $53) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $struct-get-bot + (drop + (struct.get $struct-get-type 0 + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $struct-set-bot (type $60) (param $used (ref (exact $used-in-struct-set-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable StructSet we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-struct-set-bot_4 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $struct-set-bot (param $used (ref (exact $used-in-struct-set-bot))) + ;; A null struct reference does not inhibit optimization. + (struct.set $struct-set-bot 0 + (ref.null none) + (local.get $used) + ) + (drop + (struct.new $used-in-struct-set-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-new-unreachable (type $61) (param $used (ref (exact $used-in-array-new-unreachable))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayNew we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-new-unreachable_5 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-new-unreachable (param $used (ref (exact $used-in-array-new-unreachable))) + ;; An unreachable array.new does not inhibit optimization. + (drop + (array.new $array-new-unreachable + (local.get $used) + (unreachable) + ) + ) + (drop + (struct.new $used-in-array-new-unreachable + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-new-fixed-unreachable (type $62) (param $used (ref (exact $used-in-array-new-fixed-unreachable))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayNewFixed we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-new-fixed-unreachable_6 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-new-fixed-unreachable (param $used (ref (exact $used-in-array-new-fixed-unreachable))) + ;; An unreachable array.new_fixed does not inhibit optimization. + (drop + (array.new_fixed $array-new-fixed-unreachable 2 + (local.get $used) + (unreachable) + ) + ) + (drop + (struct.new $used-in-array-new-fixed-unreachable + (ref.null none) + ) + ) + ) + + + ;; CHECK: (func $array-set-bot (type $63) (param $used (ref (exact $used-in-array-set-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArraySet we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-set-bot_7 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-set-bot (param $used (ref (exact $used-in-array-set-bot))) + ;; A null array reference does not inhibit optimization. + (array.set $array-set-bot + (ref.null none) + (i32.const 0) + (local.get $used) + ) + (drop + (struct.new $used-in-array-set-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $array-fill-bot (type $64) (param $used (ref (exact $used-in-array-fill-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable ArrayFill we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-array-fill-bot_8 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-fill-bot (param $used (ref (exact $used-in-array-fill-bot))) + ;; A null array reference does not inhibit optimization. + (array.fill $array-fill-bot + (ref.null none) + (i32.const 0) + (local.get $used) + (i32.const 0) + ) + (drop + (struct.new $used-in-array-fill-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $cont-bind (type $65) (param $used (ref (exact $used-in-cont-bind))) (param $cont (ref null $cont-bind-exact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (cont.bind $cont-bind-exact $cont-empty + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-cont-bind + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-bind (param $used (ref (exact $used-in-cont-bind))) (param $cont (ref null $cont-bind-exact)) + (drop + (cont.bind $cont-bind-exact $cont-empty + (local.get $used) + (local.get $cont) + ) + ) + (drop + (struct.new $used-in-cont-bind + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $cont-bind-ok (type $66) (param $used (ref (exact $used-in-cont-bind-ok))) (param $cont (ref null $cont-bind-inexact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (cont.bind $cont-bind-inexact $cont-empty + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-cont-bind-ok_9 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-bind-ok (param $used (ref (exact $used-in-cont-bind-ok))) (param $cont (ref null $cont-bind-inexact)) + (drop + (cont.bind $cont-bind-inexact $cont-empty + (local.get $used) + (local.get $cont) + ) + ) + (drop + (struct.new $used-in-cont-bind-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $cont-bind-bot (type $67) (param $used (ref (exact $used-in-cont-bind-bot))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ContBind we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nocont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-cont-bind-bot_10 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-bind-bot (param $used (ref (exact $used-in-cont-bind-bot))) + (drop + (cont.bind $cont-bind-bot $cont-empty + (local.get $used) + (ref.null nocont) + ) + ) + (drop + (struct.new $used-in-cont-bind-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $cont-bind-unreachable (type $68) (param $cont (ref null $cont-bind-inexact)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces unreachable ContBind we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cont-bind-unreachable (param $cont (ref null $cont-bind-inexact)) + (drop + (cont.bind $cont-bind-inexact $cont-empty + (unreachable) + (local.get $cont) + ) + ) + ) + + ;; CHECK: (func $resume (type $69) (param $used (ref (exact $used-in-resume))) (param $cont (ref null $cont-resume-exact)) + ;; CHECK-NEXT: (resume $cont-resume-exact + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume (param $used (ref (exact $used-in-resume))) (param $cont (ref null $cont-resume-exact)) + (resume $cont-resume-exact + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-resume + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $resume-ok (type $70) (param $used (ref (exact $used-in-resume-ok))) (param $cont (ref null $cont-resume-inexact)) + ;; CHECK-NEXT: (resume $cont-resume-inexact + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume-ok_11 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume-ok (param $used (ref (exact $used-in-resume-ok))) (param $cont (ref null $cont-resume-inexact)) + (resume $cont-resume-inexact + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-resume-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $resume-bot (type $71) (param $used (ref (exact $used-in-resume-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable Resume we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nocont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume-bot_12 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume-bot (param $used (ref (exact $used-in-resume-bot))) + (resume $cont-resume-bot + (local.get $used) + (ref.null nocont) + ) + (drop + (struct.new $used-in-resume-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $resume-throw (type $72) (param $used (ref (exact $used-in-resume-throw))) (param $cont (ref null $cont-empty)) + ;; CHECK-NEXT: (resume_throw $cont-empty $tag-exact + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume-throw + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume-throw (param $used (ref (exact $used-in-resume-throw))) (param $cont (ref null $cont-empty)) + (resume_throw $cont-empty $tag-exact + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-resume-throw + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $resume-throw-ok (type $73) (param $used (ref (exact $used-in-resume-throw-ok))) (param $cont (ref null $cont-empty)) + ;; CHECK-NEXT: (resume_throw $cont-empty $tag-inexact + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume-throw-ok_13 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume-throw-ok (param $used (ref (exact $used-in-resume-throw-ok))) (param $cont (ref null $cont-empty)) + (resume_throw $cont-empty $tag-inexact + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-resume-throw-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $resume-throw-bot (type $52) (param $used (ref (exact $used-in-resume-throw-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable ResumeThrow we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nocont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-resume-throw-bot_14 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $resume-throw-bot (param $used (ref (exact $used-in-resume-throw-bot))) + (resume_throw $cont-empty $tag-bot + (local.get $used) + (ref.null nocont) + ) + (drop + (struct.new $used-in-resume-throw-bot + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $switch (type $74) (param $used (ref (exact $used-in-switch))) (param $cont (ref null $cont-switch-exact)) + ;; CHECK-NEXT: (switch $cont-switch-exact $switch-tag + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-switch + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $switch (param $used (ref (exact $used-in-switch))) (param $cont (ref null $cont-switch-exact)) + (switch $cont-switch-exact $switch-tag + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-switch + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $switch-ok (type $75) (param $used (ref (exact $used-in-switch-ok))) (param $cont (ref null $cont-switch-inexact)) + ;; CHECK-NEXT: (switch $cont-switch-inexact $switch-tag + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: (local.get $cont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-switch-ok_15 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $switch-ok (param $used (ref (exact $used-in-switch-ok))) (param $cont (ref null $cont-switch-inexact)) + (switch $cont-switch-inexact $switch-tag + (local.get $used) + (local.get $cont) + ) + (drop + (struct.new $used-in-switch-ok + (ref.null none) + ) + ) + ) + + ;; CHECK: (func $switch-bot (type $76) (param $used (ref (exact $used-in-switch-bot))) + ;; CHECK-NEXT: (block ;; (replaces unreachable StackSwitch we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $used) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nocont) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $used-in-switch-bot_16 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $switch-bot (param $used (ref (exact $used-in-switch-bot))) + (switch $cont-switch-bot $switch-tag + (local.get $used) + (ref.null nocont) + ) + (drop + (struct.new $used-in-switch-bot + (ref.null none) + ) + ) + ) +)