diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index d325e539a9a..3a0f9ea32cb 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -498,7 +498,9 @@ Type GlobalTypeRewriter::getTempTupleType(Tuple tuple) { namespace TypeUpdating { -void handleNonDefaultableLocals(Function* func, Module& wasm) { +void handleNonDefaultableLocals(Function* func, + Module& wasm, + Index firstLocal) { if (!wasm.features.hasReferenceTypes()) { // No references, so no non-nullable ones at all. return; @@ -524,6 +526,9 @@ void handleNonDefaultableLocals(Function* func, Module& wasm) { func, wasm, LocalStructuralDominance::NonNullableOnly); std::unordered_set badIndexes; for (auto index : info.nonDominatingIndices) { + if (index < firstLocal) { + continue; + } badIndexes.insert(index); // LocalStructuralDominance should have only looked at non-nullable indexes diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h index 7aaf02a8234..3db8cbdfba9 100644 --- a/src/ir/type-updating.h +++ b/src/ir/type-updating.h @@ -564,7 +564,10 @@ namespace TypeUpdating { // them. Atm this turns them into nullable ones, and adds ref.as_non_null on // their uses (which keeps the type of the users identical). // This may also handle other types of nondefaultable locals in the future. -void handleNonDefaultableLocals(Function* func, Module& wasm); +// Ignore locals before firstLocal, if specified. +void handleNonDefaultableLocals(Function* func, + Module& wasm, + Index firstLocal = 0); // Returns the type that a local should be, after handling of non- // defaultability. diff --git a/src/parser/parse-5-defs.cpp b/src/parser/parse-5-defs.cpp index 31048176c42..a3ff8029188 100644 --- a/src/parser/parse-5-defs.cpp +++ b/src/parser/parse-5-defs.cpp @@ -16,6 +16,8 @@ #include "wat-parser-internal.h" +#include "ir/type-updating.h" + namespace wasm::WATParser { Result<> parseDefinitions( @@ -46,6 +48,7 @@ Result<> parseDefinitions( WithPosition with(ctx, decls.funcDefs[i].pos); ctx.setSrcLoc(decls.funcDefs[i].annotations); ctx.in.setAnnotations(std::move(decls.funcDefs[i].annotations)); + auto firstScratchLocal = f->getNumLocals(); if (!f->imported()) { CHECK_ERR(ctx.visitFunctionStart(f)); } @@ -63,6 +66,8 @@ Result<> parseDefinitions( if (auto* err = end.getErr()) { return ctx.in.err(decls.funcDefs[i].pos, err->msg); } + TypeUpdating::handleNonDefaultableLocals( + f, decls.wasm, firstScratchLocal); } } diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 585a1c98f2a..cd83ea72289 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2302,7 +2302,7 @@ Result<> IRBuilder::makeBrOn(Index label, case BrOnCast: case BrOnCastDescEq: if (out->isNullable()) { - resultType = Type(in->getHeapType(), NonNullable); + resultType = in->with(NonNullable); } else { resultType = *in; } diff --git a/test/lit/basic/br-on-cast-extra-values.wast b/test/lit/basic/br-on-cast-extra-values.wast new file mode 100644 index 00000000000..6a209939009 --- /dev/null +++ b/test/lit/basic/br-on-cast-extra-values.wast @@ -0,0 +1,100 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s + +(module + ;; CHECK: (type $s (struct)) + (type $s (struct)) + ;; CHECK: (func $extra-values (type $1) (param $s (ref $s)) (result (ref $s) (ref $s)) + ;; CHECK-NEXT: (local $scratch (ref $s)) + ;; CHECK-NEXT: (local $scratch_3 (ref $s)) + ;; CHECK-NEXT: (local $scratch_4 (ref $s)) + ;; CHECK-NEXT: (local $scratch_5 (ref $s)) + ;; CHECK-NEXT: (local $scratch_2 (ref null $s)) + ;; CHECK-NEXT: (block $block1 (type $3) (result (ref $s) (ref $s)) + ;; CHECK-NEXT: (local.set $scratch_5 + ;; CHECK-NEXT: (block $block (result (ref $s)) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $s) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.get $s) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (local.get $scratch_3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_4 + ;; CHECK-NEXT: (br_on_cast $block (ref $s) (ref $s) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $block1 + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $extra-values (param $s (ref $s)) (result (ref $s) (ref $s)) + local.get $s + local.get $s + br_on_cast 0 (ref $s) (ref $s) + ) + ;; CHECK: (func $exact (type $2) (param $s (ref (exact $s))) (result (ref (exact $s)) (ref null (exact $s))) + ;; CHECK-NEXT: (local $scratch (ref (exact $s))) + ;; CHECK-NEXT: (local $scratch_3 (ref (exact $s))) + ;; CHECK-NEXT: (local $scratch_4 (ref (exact $s))) + ;; CHECK-NEXT: (local $scratch_2 (ref null (exact $s))) + ;; CHECK-NEXT: (local $scratch_5 (ref null (exact $s))) + ;; CHECK-NEXT: (block $block1 (type $4) (result (ref (exact $s)) (ref null (exact $s))) + ;; CHECK-NEXT: (local.set $scratch_5 + ;; CHECK-NEXT: (block $block (result (ref null (exact $s))) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $s) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.get $s) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (local.get $scratch_3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_4 + ;; CHECK-NEXT: (br_on_cast $block (ref (exact $s)) (ref (exact $s)) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $block1 + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact (param $s (ref (exact $s))) + (result (ref (exact $s)) (ref null (exact $s))) + local.get $s + local.get $s + br_on_cast 0 (ref (exact $s)) (ref null (exact $s)) + ) +)