Skip to content

Commit f5ea49a

Browse files
authored
Fix missing unreachable checks in Heap2Local visitStructRMW/visitStructCmpxchg (WebAssembly#8304)
## Summary `visitStructRMW` and `visitStructCmpxchg` in `Struct2Local` are missing the `Type::unreachable` guard that all sibling visitor methods have (`visitRefGetDesc`, `visitStructGet`, `visitRefIsNull`, `visitRefEq`). When a `struct.atomic.rmw` or `struct.atomic.rmw.cmpxchg` has an unreachable operand (e.g., unreachable value), the expression type is `unreachable` but the code asserts it equals the concrete field type, causing an assertion failure: ``` Assertion failed: (type == field.type), function visitStructRMW, file Heap2Local.cpp, line 1043. ``` This is the same bug pattern that was fixed in WebAssembly#8283 for `visitRefGetDesc` — it was just missed in these two methods.
1 parent aebfa3c commit f5ea49a

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/passes/Heap2Local.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,11 @@ struct Struct2Local : PostWalker<Struct2Local> {
10381038
return;
10391039
}
10401040

1041+
if (curr->type == Type::unreachable) {
1042+
// As with RefGetDesc and StructGet, above.
1043+
return;
1044+
}
1045+
10411046
[[maybe_unused]] auto& field = fields[curr->index];
10421047
auto type = curr->type;
10431048
assert(type == field.type);
@@ -1108,6 +1113,11 @@ struct Struct2Local : PostWalker<Struct2Local> {
11081113
return;
11091114
}
11101115

1116+
if (curr->type == Type::unreachable) {
1117+
// As with RefGetDesc and StructGet, above.
1118+
return;
1119+
}
1120+
11111121
[[maybe_unused]] auto& field = fields[curr->index];
11121122
auto type = curr->type;
11131123
assert(type == field.type);

test/lit/passes/heap2local-rmw.wast

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,4 +693,61 @@
693693
(i32.const 2)
694694
)
695695
)
696+
697+
;; CHECK: (func $rmw-unreachable-value (type $1) (result i32)
698+
;; CHECK-NEXT: (local $0 i32)
699+
;; CHECK-NEXT: (block ;; (replaces unreachable StructRMW we can't emit)
700+
;; CHECK-NEXT: (drop
701+
;; CHECK-NEXT: (block (result nullref)
702+
;; CHECK-NEXT: (local.set $0
703+
;; CHECK-NEXT: (i32.const 0)
704+
;; CHECK-NEXT: )
705+
;; CHECK-NEXT: (ref.null none)
706+
;; CHECK-NEXT: )
707+
;; CHECK-NEXT: )
708+
;; CHECK-NEXT: (drop
709+
;; CHECK-NEXT: (unreachable)
710+
;; CHECK-NEXT: )
711+
;; CHECK-NEXT: (unreachable)
712+
;; CHECK-NEXT: )
713+
;; CHECK-NEXT: )
714+
(func $rmw-unreachable-value (result i32)
715+
;; When the value is unreachable, the whole expression is unreachable.
716+
;; We should not attempt to optimize this (it would hit an assertion
717+
;; on type == field.type since unreachable != i32).
718+
(struct.atomic.rmw.add $i32 0
719+
(struct.new_default $i32)
720+
(unreachable)
721+
)
722+
)
723+
724+
;; CHECK: (func $cmpxchg-unreachable-expected (type $1) (result i32)
725+
;; CHECK-NEXT: (local $0 i32)
726+
;; CHECK-NEXT: (block ;; (replaces unreachable StructCmpxchg we can't emit)
727+
;; CHECK-NEXT: (drop
728+
;; CHECK-NEXT: (block (result nullref)
729+
;; CHECK-NEXT: (local.set $0
730+
;; CHECK-NEXT: (i32.const 0)
731+
;; CHECK-NEXT: )
732+
;; CHECK-NEXT: (ref.null none)
733+
;; CHECK-NEXT: )
734+
;; CHECK-NEXT: )
735+
;; CHECK-NEXT: (drop
736+
;; CHECK-NEXT: (unreachable)
737+
;; CHECK-NEXT: )
738+
;; CHECK-NEXT: (drop
739+
;; CHECK-NEXT: (i32.const 1)
740+
;; CHECK-NEXT: )
741+
;; CHECK-NEXT: (unreachable)
742+
;; CHECK-NEXT: )
743+
;; CHECK-NEXT: )
744+
(func $cmpxchg-unreachable-expected (result i32)
745+
;; When the expected operand is unreachable, the whole expression is
746+
;; unreachable. We should not attempt to optimize this.
747+
(struct.atomic.rmw.cmpxchg $i32 0
748+
(struct.new_default $i32)
749+
(unreachable)
750+
(i32.const 1)
751+
)
752+
)
696753
)

0 commit comments

Comments
 (0)