1
0
mirror of https://github.com/golang/go synced 2024-11-18 15:24:41 -07:00

runtime/internal/atomic: add wasm And/Or operators

In the WebAssembly version of these operators we avoid using
a CAS loop since the Go wasm implementation is single-threaded.

A new test file has been added that has build tags in order to
only test this feature on implemented architectures.

This is part of a series of CLs aimed to add the primitives
for And/Or atomic operations that will be used by the public
sync/atomic apis.

For #61395

Change-Id: Ic67ffefc9cfb626915ea86b6b21b500117710327
GitHub-Last-Rev: bbec3a5f35
GitHub-Pull-Request: golang/go#62517
Reviewed-on: https://go-review.googlesource.com/c/go/+/526656
Run-TryBot: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
This commit is contained in:
Mauri de Souza Meneguzzo 2023-09-09 13:41:35 +00:00 committed by Gopher Robot
parent bfa8a8586f
commit 6ecd5f7504
2 changed files with 217 additions and 0 deletions

View File

@ -0,0 +1,169 @@
// +build wasm
//
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(61395): move these tests to atomic_test.go once And/Or have
// implementations for all architectures.
package atomic_test
import (
"testing"
"runtime/internal/atomic"
)
func TestAnd32(t *testing.T) {
// Basic sanity check.
x := uint32(0xffffffff)
for i := uint32(0); i < 32; i++ {
old := x
v := atomic.And32(&x, ^(1 << i))
if r := uint32(0xffffffff) << (i + 1); x != r || v != old {
t.Fatalf("clearing bit %#x: want %#x, got new %#x and old %#v", uint32(1<<i), r, x, v)
}
}
// Set every bit in array to 1.
a := make([]uint32, 1<<12)
for i := range a {
a[i] = 0xffffffff
}
// Clear array bit-by-bit in different goroutines.
done := make(chan bool)
for i := 0; i < 32; i++ {
m := ^uint32(1 << i)
go func() {
for i := range a {
atomic.And(&a[i], m)
}
done <- true
}()
}
for i := 0; i < 32; i++ {
<-done
}
// Check that the array has been totally cleared.
for i, v := range a {
if v != 0 {
t.Fatalf("a[%v] not cleared: want %#x, got %#x", i, uint32(0), v)
}
}
}
func TestAnd64(t *testing.T) {
// Basic sanity check.
x := uint64(0xffffffffffffffff)
for i := uint64(0); i < 64; i++ {
old := x
v := atomic.And64(&x, ^(1 << i))
if r := uint64(0xffffffffffffffff) << (i + 1); x != r || v != old {
t.Fatalf("clearing bit %#x: want %#x, got new %#x and old %#v", uint64(1<<i), r, x, v)
}
}
// Set every bit in array to 1.
a := make([]uint64, 1<<12)
for i := range a {
a[i] = 0xffffffffffffffff
}
// Clear array bit-by-bit in different goroutines.
done := make(chan bool)
for i := 0; i < 64; i++ {
m := ^uint64(1 << i)
go func() {
for i := range a {
atomic.And64(&a[i], m)
}
done <- true
}()
}
for i := 0; i < 64; i++ {
<-done
}
// Check that the array has been totally cleared.
for i, v := range a {
if v != 0 {
t.Fatalf("a[%v] not cleared: want %#x, got %#x", i, uint64(0), v)
}
}
}
func TestOr32(t *testing.T) {
// Basic sanity check.
x := uint32(0)
for i := uint32(0); i < 32; i++ {
old := x
v := atomic.Or32(&x, 1<<i)
if r := (uint32(1) << (i + 1)) - 1; x != r || v != old {
t.Fatalf("setting bit %#x: want %#x, got new %#x and old %#v", uint32(1<<i), r, x, v)
}
}
// Start with every bit in array set to 0.
a := make([]uint32, 1<<12)
// Set every bit in array bit-by-bit in different goroutines.
done := make(chan bool)
for i := 0; i < 32; i++ {
m := uint32(1 << i)
go func() {
for i := range a {
atomic.Or32(&a[i], m)
}
done <- true
}()
}
for i := 0; i < 32; i++ {
<-done
}
// Check that the array has been totally set.
for i, v := range a {
if v != 0xffffffff {
t.Fatalf("a[%v] not fully set: want %#x, got %#x", i, uint32(0xffffffff), v)
}
}
}
func TestOr64(t *testing.T) {
// Basic sanity check.
x := uint64(0)
for i := uint64(0); i < 64; i++ {
old := x
v := atomic.Or64(&x, 1<<i)
if r := (uint64(1) << (i + 1)) - 1; x != r || v != old {
t.Fatalf("setting bit %#x: want %#x, got new %#x and old %#v", uint64(1<<i), r, x, v)
}
}
// Start with every bit in array set to 0.
a := make([]uint64, 1<<12)
// Set every bit in array bit-by-bit in different goroutines.
done := make(chan bool)
for i := 0; i < 64; i++ {
m := uint64(1 << i)
go func() {
for i := range a {
atomic.Or64(&a[i], m)
}
done <- true
}()
}
for i := 0; i < 64; i++ {
<-done
}
// Check that the array has been totally set.
for i, v := range a {
if v != 0xffffffffffffffff {
t.Fatalf("a[%v] not fully set: want %#x, got %#x", i, uint64(0xffffffffffffffff), v)
}
}
}

View File

@ -339,3 +339,51 @@ func Xaddint64(ptr *int64, delta int64) int64 {
*ptr = new
return new
}
//go:nosplit
//go:noinline
func And32(ptr *uint32, val uint32) uint32 {
old := *ptr
*ptr = old & val
return old
}
//go:nosplit
//go:noinline
func And64(ptr *uint64, val uint64) uint64 {
old := *ptr
*ptr = old & val
return old
}
//go:nosplit
//go:noinline
func Anduintptr(ptr *uintptr, val uintptr) uintptr {
old := *ptr
*ptr = old & val
return old
}
//go:nosplit
//go:noinline
func Or32(ptr *uint32, val uint32) uint32 {
old := *ptr
*ptr = old | val
return old
}
//go:nosplit
//go:noinline
func Or64(ptr *uint64, val uint64) uint64 {
old := *ptr
*ptr = old | val
return old
}
//go:nosplit
//go:noinline
func Oruintptr(ptr *uintptr, val uintptr) uintptr {
old := *ptr
*ptr = old | val
return old
}