From 325a8192b17777eef998748d5ba18650ecb46fb6 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 30 Mar 2026 13:52:25 -0700 Subject: [PATCH] [NFC] Handle atomic arrays in Heap2Local We previously had TODOs about handling atoimc arrays in the Array2Struct helper in the Heap2Local pass. However, we were already handling them correctly. When Array2Struct transformed an array get or set into a struct get or set, it did not previously preserve the memory order of the array access. But this tranformation only happens when the new struct access will also be subsequently optimized out, so the ordering does not matter. Update the code to preserve order anyway, since that is less surprising, and add some tests. --- src/passes/Heap2Local.cpp | 8 +-- test/lit/passes/heap2local.wast | 108 ++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp index c6660df7e12..1a52af2a92e 100644 --- a/src/passes/Heap2Local.cpp +++ b/src/passes/Heap2Local.cpp @@ -1387,9 +1387,8 @@ struct Array2Struct : PostWalker { } // Convert the ArraySet into a StructSet. - // TODO: Handle atomic array accesses. - replaceCurrent(builder.makeStructSet( - index, curr->ref, curr->value, MemoryOrder::Unordered)); + replaceCurrent( + builder.makeStructSet(index, curr->ref, curr->value, curr->order)); } void visitArrayGet(ArrayGet* curr) { @@ -1408,9 +1407,8 @@ struct Array2Struct : PostWalker { } // Convert the ArrayGet into a StructGet. - // TODO: Handle atomic array accesses. replaceCurrent(builder.makeStructGet( - index, curr->ref, MemoryOrder::Unordered, curr->type, curr->signed_)); + index, curr->ref, curr->order, curr->type, curr->signed_)); } void visitArrayRMW(ArrayRMW* curr) { diff --git a/test/lit/passes/heap2local.wast b/test/lit/passes/heap2local.wast index 9af0e1b1bb1..ae42ea45c1b 100644 --- a/test/lit/passes/heap2local.wast +++ b/test/lit/passes/heap2local.wast @@ -4615,8 +4615,10 @@ ;; CHECK: (type $struct (shared (struct (field (mut i32))))) (type $struct (shared (struct (field (mut i32))))) + ;; CHECK: (type $array (shared (array (mut i32)))) + (type $array (shared (array (field (mut i32))))) - ;; CHECK: (func $acqrel (type $0) + ;; CHECK: (func $acqrel-struct (type $0) ;; CHECK-NEXT: (local $0 (ref null $struct)) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop @@ -4644,7 +4646,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $acqrel + (func $acqrel-struct (local (ref null $struct)) (local.set 0 (struct.new_default $struct) @@ -4662,7 +4664,56 @@ ) ) - ;; CHECK: (func $seqcst (type $0) + ;; CHECK: (func $acqrel-array (type $0) + ;; CHECK-NEXT: (local $0 (ref null $array)) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref null (shared none))) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $acqrel-array + (local (ref null $array)) + (local.set 0 + (array.new_default $array + (i32.const 1) + ) + ) + ;; Same as with the struct. We can optimize. + (drop + (array.atomic.get acqrel $array + (local.get 0) + (i32.const 0) + ) + ) + (array.atomic.set acqrel $array + (local.get 0) + (i32.const 0) + (i32.const 1) + ) + ) + + ;; CHECK: (func $seqcst-struct (type $0) ;; CHECK-NEXT: (local $0 (ref null $struct)) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop @@ -4690,7 +4741,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $seqcst + (func $seqcst-struct (local (ref null $struct)) (local.set 0 (struct.new_default $struct) @@ -4707,4 +4758,53 @@ (i32.const 0) ) ) + + ;; CHECK: (func $seqcst-array (type $0) + ;; CHECK-NEXT: (local $0 (ref null $array)) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref null (shared none))) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $seqcst-array + (local (ref null $array)) + (local.set 0 + (array.new_default $array + (i32.const 1) + ) + ) + ;; Same as with the struct. We can optimize. + (drop + (array.atomic.get seqcst $array + (local.get 0) + (i32.const 0) + ) + ) + (array.atomic.set seqcst $array + (local.get 0) + (i32.const 0) + (i32.const 1) + ) + ) )