From 062437715a5e55f4eae583048abf843d5cc58bf5 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Tue, 22 Sep 2026 18:59:06 +0000 Subject: [PATCH 1/2] Fix allowing supertypes as expected value in struct.wait --- src/ir/subtype-exprs.h | 6 +++++- src/passes/TypeRefining.cpp | 13 ------------- src/wasm/wasm-validator.cpp | 5 ++++- test/lit/passes/type-refining-gufa-rmw.wast | 7 +------ 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 603a5a6ecfd..071ee2cdfe6 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -390,7 +390,11 @@ struct SubtypingDiscoverer : public OverriddenVisitor { return; } const auto& fields = curr->ref->type.getHeapType().getStruct().fields; - self()->noteSubtype(curr->expected, fields[curr->index].type); + auto expectedType = fields[curr->index].type; + if (expectedType.isRef()) { + expectedType = Type(HeapTypes::eq.getBasic(Shared), Nullable); + } + self()->noteSubtype(curr->expected, expectedType); } void visitWaitqueueNew(WaitqueueNew* curr) {} void visitWaitqueueNotify(WaitqueueNotify* curr) { diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp index 1afda6dbc4f..232aa63766d 100644 --- a/src/passes/TypeRefining.cpp +++ b/src/passes/TypeRefining.cpp @@ -568,19 +568,6 @@ struct TypeRefining : public Pass { curr->replacement = fixType(curr->replacement, fieldType); } - void visitStructWait(StructWait* curr) { - if (curr->ref->type == Type::unreachable) { - return; - } - auto type = curr->ref->type.getHeapType(); - if (type.isBottom()) { - return; - } - - auto fieldType = type.getStruct().fields[curr->index].type; - curr->expected = fixType(curr->expected, fieldType); - } - bool refinalize = false; // Fix up a given value so it fits into the type the location it is diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 03a445d33bd..9a1e9f048fc 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3739,8 +3739,11 @@ void FunctionValidator::visitStructWait(StructWait* curr) { return; } + auto expectedType = field.type.isRef() + ? Type(HeapTypes::eq.getBasic(Shared), Nullable) + : field.type; shouldBeSubType(curr->expected->type, - field.type, + expectedType, curr, "struct.wait expected value must match the field immediate"); } diff --git a/test/lit/passes/type-refining-gufa-rmw.wast b/test/lit/passes/type-refining-gufa-rmw.wast index 16e990b47d8..a362f41d50a 100644 --- a/test/lit/passes/type-refining-gufa-rmw.wast +++ b/test/lit/passes/type-refining-gufa-rmw.wast @@ -466,12 +466,7 @@ ;; GUFA-NEXT: (struct.wait $struct 0 ;; GUFA-NEXT: (local.get $struct) ;; GUFA-NEXT: (unreachable) - ;; GUFA-NEXT: (block (result (ref null (shared none))) - ;; GUFA-NEXT: (drop - ;; GUFA-NEXT: (local.get $struct) - ;; GUFA-NEXT: ) - ;; GUFA-NEXT: (ref.null (shared none)) - ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (local.get $struct) ;; GUFA-NEXT: (i64.const -1) ;; GUFA-NEXT: ) ;; GUFA-NEXT: ) From 541d85bbc72fa5167c71ab1f43caa844f82abf6f Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Wed, 23 Sep 2026 20:28:21 +0000 Subject: [PATCH 2/2] Update comments --- test/lit/passes/type-refining-gufa-rmw.wast | 42 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/type-refining-gufa-rmw.wast b/test/lit/passes/type-refining-gufa-rmw.wast index a362f41d50a..8e6da23bb35 100644 --- a/test/lit/passes/type-refining-gufa-rmw.wast +++ b/test/lit/passes/type-refining-gufa-rmw.wast @@ -436,6 +436,20 @@ ;; NRML-NEXT: ) ;; NRML-NEXT: ) ;; NRML-NEXT: (drop + ;; NRML-NEXT: (block ;; (replaces unreachable StructCmpxchg we can't emit) + ;; NRML-NEXT: (drop + ;; NRML-NEXT: (unreachable) + ;; NRML-NEXT: ) + ;; NRML-NEXT: (drop + ;; NRML-NEXT: (local.get $struct) + ;; NRML-NEXT: ) + ;; NRML-NEXT: (drop + ;; NRML-NEXT: (local.get $struct) + ;; NRML-NEXT: ) + ;; NRML-NEXT: (unreachable) + ;; NRML-NEXT: ) + ;; NRML-NEXT: ) + ;; NRML-NEXT: (drop ;; NRML-NEXT: (struct.wait $struct 0 ;; NRML-NEXT: (local.get $struct) ;; NRML-NEXT: (unreachable) @@ -463,6 +477,20 @@ ;; GUFA-NEXT: ) ;; GUFA-NEXT: ) ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (block ;; (replaces unreachable StructCmpxchg we can't emit) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (unreachable) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (local.get $struct) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (local.get $struct) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (unreachable) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (drop ;; GUFA-NEXT: (struct.wait $struct 0 ;; GUFA-NEXT: (local.get $struct) ;; GUFA-NEXT: (unreachable) @@ -487,7 +515,19 @@ (local.get $struct) ) ) - ;; Likewise with struct.wait. + ;; The fixup to null does *not* happen for the `expected` ref of a cmpxchg, + ;; because the expected value is allowed to be a supertype of the field's + ;; type. i.e. (local.get $struct) remains valid here even after the field's + ;; type is refined. + (drop + (struct.atomic.rmw.cmpxchg acqrel acqrel $struct 0 + (unreachable) + (local.get $struct) + (local.get $struct) + ) + ) + ;; Ditto for struct.wait, the `expected` ref may remain a supertype and + ;; doesn't need to be fixed up. (drop (struct.wait $struct 0 (local.get $struct)