From 54aee1672f84bd455054870a543d65aa8dc3beb6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 17 Sep 2026 11:48:11 -0700 Subject: [PATCH 1/4] fix --- src/passes/TypeRefining.cpp | 27 ++++++++-- test/lit/passes/type-refining-gufa-rmw.wast | 59 +++++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp index 720233cf9ae..210779616d9 100644 --- a/src/passes/TypeRefining.cpp +++ b/src/passes/TypeRefining.cpp @@ -523,8 +523,14 @@ struct TypeRefining : public Pass { } void visitStructSet(StructSet* curr) { - if (curr->type == Type::unreachable) { - // Ignore unreachable code. + if (curr->ref->type == Type::unreachable) { + // Ignore unreachable code. Note that we check curr->ref, not curr, + // as curr may be unreachable because of another operand than the ref, + // and in that case we must still validate, i.e., we must call fixType + // to fix things up. No actual problem can happen here, as the only + // other operand is the value (and it needs no fixing if it is + // unreachable), but we do this for consistency with the cases below + // that do have other operands. return; } auto type = curr->ref->type.getHeapType(); @@ -538,7 +544,7 @@ struct TypeRefining : public Pass { } void visitStructRMW(StructRMW* curr) { - if (curr->type == Type::unreachable) { + if (curr->ref->type == Type::unreachable) { return; } auto type = curr->ref->type.getHeapType(); @@ -551,7 +557,7 @@ struct TypeRefining : public Pass { } void visitStructCmpxchg(StructCmpxchg* curr) { - if (curr->type == Type::unreachable) { + if (curr->ref->type == Type::unreachable) { return; } auto type = curr->ref->type.getHeapType(); @@ -563,6 +569,19 @@ 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/test/lit/passes/type-refining-gufa-rmw.wast b/test/lit/passes/type-refining-gufa-rmw.wast index 24aec709a98..760dd5cd75f 100644 --- a/test/lit/passes/type-refining-gufa-rmw.wast +++ b/test/lit/passes/type-refining-gufa-rmw.wast @@ -415,3 +415,62 @@ ) ) ) + +(module + ;; NRML: (type $struct (sub (struct (field (mut (ref null $struct)))))) + ;; GUFA: (rec + ;; GUFA-NEXT: (type $struct (sub (struct (field (mut nullref))))) + (type $struct (sub (struct (field (mut (ref null $struct)))))) + + ;; NRML: (type $1 (func (param (ref null $struct)))) + + ;; NRML: (func $test (type $1) (param $struct (ref null $struct)) + ;; NRML-NEXT: (drop + ;; NRML-NEXT: (struct.new_default $struct) + ;; NRML-NEXT: ) + ;; NRML-NEXT: (drop + ;; NRML-NEXT: (struct.atomic.rmw.cmpxchg acqrel acqrel $struct 0 + ;; NRML-NEXT: (local.get $struct) + ;; NRML-NEXT: (unreachable) + ;; NRML-NEXT: (local.get $struct) + ;; NRML-NEXT: ) + ;; NRML-NEXT: ) + ;; NRML-NEXT: ) + ;; GUFA: (type $1 (func (param (ref null $struct)))) + + ;; GUFA: (func $test (type $1) (param $struct (ref null $struct)) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (struct.new_default $struct) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (struct.atomic.rmw.cmpxchg acqrel acqrel $struct 0 + ;; GUFA-NEXT: (local.get $struct) + ;; GUFA-NEXT: (unreachable) + ;; GUFA-NEXT: (block (result nullref) + ;; GUFA-NEXT: (drop + ;; GUFA-NEXT: (local.get $struct) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: (ref.null none) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: ) + ;; GUFA-NEXT: ) + (func $test (param $struct (ref null $struct)) + ;; Use the type, writing only nulls. + (drop + (struct.new_default $struct) + ) + ;; An unreachable cmpxchg, but where the ref and value are not unreachable. + ;; The value must still validate as if it were written, so we will fix it up + ;; with a null, as the field is refined to a null (in GUFA mode, which + ;; does that refining). + (drop + (struct.atomic.rmw.cmpxchg acqrel acqrel $struct 0 + (local.get $struct) + (unreachable) + (local.get $struct) + ) + ) + ) +) + From 9bb153ba711379b571dd03a954110ea134474adf Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 17 Sep 2026 11:52:06 -0700 Subject: [PATCH 2/4] simpl --- src/passes/TypeRefining.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp index 210779616d9..dcbde4fb958 100644 --- a/src/passes/TypeRefining.cpp +++ b/src/passes/TypeRefining.cpp @@ -523,14 +523,8 @@ struct TypeRefining : public Pass { } void visitStructSet(StructSet* curr) { - if (curr->ref->type == Type::unreachable) { - // Ignore unreachable code. Note that we check curr->ref, not curr, - // as curr may be unreachable because of another operand than the ref, - // and in that case we must still validate, i.e., we must call fixType - // to fix things up. No actual problem can happen here, as the only - // other operand is the value (and it needs no fixing if it is - // unreachable), but we do this for consistency with the cases below - // that do have other operands. + if (curr->type == Type::unreachable) { + // Ignore unreachable code. return; } auto type = curr->ref->type.getHeapType(); @@ -544,7 +538,7 @@ struct TypeRefining : public Pass { } void visitStructRMW(StructRMW* curr) { - if (curr->ref->type == Type::unreachable) { + if (curr->type == Type::unreachable) { return; } auto type = curr->ref->type.getHeapType(); @@ -558,6 +552,10 @@ struct TypeRefining : public Pass { void visitStructCmpxchg(StructCmpxchg* curr) { if (curr->ref->type == Type::unreachable) { + // Ignore unreachable code. Note that we check curr->ref, not curr, + // as curr may be unreachable because of another operand than the ref + // and the replacement value (the expected value). In that case, the + // replacement must still validate as if it were written. return; } auto type = curr->ref->type.getHeapType(); @@ -571,6 +569,7 @@ struct TypeRefining : public Pass { void visitStructWait(StructWait* curr) { if (curr->ref->type == Type::unreachable) { + // See visitStructCmpxchg for why we check curr->ref. return; } auto type = curr->ref->type.getHeapType(); From 78cf9ed2f3fc105e3e3f2ea34b80fc6f0206edf9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 17 Sep 2026 11:56:16 -0700 Subject: [PATCH 3/4] todos --- src/ir/possible-contents.cpp | 1 + src/ir/struct-utils.h | 2 ++ src/passes/GlobalTypeOptimization.cpp | 2 ++ src/passes/TypeRefining.cpp | 14 +------------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 549ec7169ef..381d4e49441 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1776,6 +1776,7 @@ void TNHOracle::scan(Function* func, void visitStructCmpxchg(StructCmpxchg* curr) { notePossibleTrap(curr->ref); } + // TODO: visitStructWait void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); } void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); } void visitArrayLoad(ArrayLoad* curr) { notePossibleTrap(curr->ref); } diff --git a/src/ir/struct-utils.h b/src/ir/struct-utils.h index 471a580a9d4..c90fbb85348 100644 --- a/src/ir/struct-utils.h +++ b/src/ir/struct-utils.h @@ -287,6 +287,8 @@ struct StructScanner : public WalkerPass> { noteExpressionOrCopy(curr->replacement, type, index, info); } + // TODO: visitStructWait + void visitRefCast(RefCast* curr) { if (curr->desc) { // We may try to read a descriptor from anything arriving in |curr->ref|, diff --git a/src/passes/GlobalTypeOptimization.cpp b/src/passes/GlobalTypeOptimization.cpp index 8cfa4960cc9..b0bb31edbd9 100644 --- a/src/passes/GlobalTypeOptimization.cpp +++ b/src/passes/GlobalTypeOptimization.cpp @@ -838,6 +838,8 @@ struct GlobalTypeOptimization : public Pass { assert(newIndex != RemovedField); curr->index = newIndex; } + + // TODO: visitStructWait }; PassRunner runner(getPassRunner()); diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp index dcbde4fb958..cc0772555ea 100644 --- a/src/passes/TypeRefining.cpp +++ b/src/passes/TypeRefining.cpp @@ -567,19 +567,7 @@ struct TypeRefining : public Pass { curr->replacement = fixType(curr->replacement, fieldType); } - void visitStructWait(StructWait* curr) { - if (curr->ref->type == Type::unreachable) { - // See visitStructCmpxchg for why we check curr->ref. - 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); - } + // TODO: visitStructWait bool refinalize = false; From 93f48a76efdfb9426f61efe47502f66a260ac182 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 17 Sep 2026 11:58:50 -0700 Subject: [PATCH 4/4] fix --- src/passes/TypeRefining.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/passes/TypeRefining.cpp b/src/passes/TypeRefining.cpp index cc0772555ea..1d4fad7d85d 100644 --- a/src/passes/TypeRefining.cpp +++ b/src/passes/TypeRefining.cpp @@ -554,8 +554,9 @@ struct TypeRefining : public Pass { if (curr->ref->type == Type::unreachable) { // Ignore unreachable code. Note that we check curr->ref, not curr, // as curr may be unreachable because of another operand than the ref - // and the replacement value (the expected value). In that case, the - // replacement must still validate as if it were written. + // and the replacement value (the expected value may be the + // unreachable one). In that case, the replacement must still validate + // as if it were written, so we must fix it up below. return; } auto type = curr->ref->type.getHeapType();