]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/internal/atomic: add 32-bit And/Or
authorMichael Pratt <mpratt@google.com>
Thu, 8 Oct 2020 18:38:39 +0000 (14:38 -0400)
committerMichael Pratt <mpratt@google.com>
Fri, 23 Oct 2020 14:17:57 +0000 (14:17 +0000)
These will be used in a following CL to perform larger bit clear and bit
set than And8/Or8.

Change-Id: I60f7b1099e29b69eb64add77564faee862880a8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/260977
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Michael Pratt <mpratt@google.com>

20 files changed:
src/runtime/internal/atomic/asm_386.s
src/runtime/internal/atomic/asm_amd64.s
src/runtime/internal/atomic/asm_mips64x.s
src/runtime/internal/atomic/asm_mipsx.s
src/runtime/internal/atomic/asm_ppc64x.s
src/runtime/internal/atomic/asm_s390x.s
src/runtime/internal/atomic/atomic_386.go
src/runtime/internal/atomic/atomic_amd64.go
src/runtime/internal/atomic/atomic_arm.go
src/runtime/internal/atomic/atomic_arm64.go
src/runtime/internal/atomic/atomic_arm64.s
src/runtime/internal/atomic/atomic_mips64x.go
src/runtime/internal/atomic/atomic_mipsx.go
src/runtime/internal/atomic/atomic_ppc64x.go
src/runtime/internal/atomic/atomic_riscv64.go
src/runtime/internal/atomic/atomic_riscv64.s
src/runtime/internal/atomic/atomic_s390x.go
src/runtime/internal/atomic/atomic_test.go
src/runtime/internal/atomic/atomic_wasm.go
src/runtime/internal/atomic/bench_test.go

index 7ebf675ac5f8f829ee953057fe01010697c60ac1..d82faef1f01b0e8d18159722b03450066b265fbc 100644 (file)
@@ -243,3 +243,19 @@ TEXT ·Store8(SB), NOSPLIT, $0-5
        MOVB    val+4(FP), AX
        XCHGB   AX, 0(BX)
        RET
+
+// func Or(addr *uint32, v uint32)
+TEXT ·Or(SB), NOSPLIT, $0-8
+       MOVL    ptr+0(FP), AX
+       MOVL    val+4(FP), BX
+       LOCK
+       ORL     BX, (AX)
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT ·And(SB), NOSPLIT, $0-8
+       MOVL    ptr+0(FP), AX
+       MOVL    val+4(FP), BX
+       LOCK
+       ANDL    BX, (AX)
+       RET
index 80fb31285decf8eb7f5a5408e0aaab27db165ed8..2cf7c5587067a4121a7a51e611cfafb6dead9dea 100644 (file)
@@ -169,3 +169,19 @@ TEXT runtime∕internal∕atomic·And8(SB), NOSPLIT, $0-9
        LOCK
        ANDB    BX, (AX)
        RET
+
+// func Or(addr *uint32, v uint32)
+TEXT runtime∕internal∕atomic·Or(SB), NOSPLIT, $0-12
+       MOVQ    ptr+0(FP), AX
+       MOVL    val+8(FP), BX
+       LOCK
+       ORL     BX, (AX)
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT runtime∕internal∕atomic·And(SB), NOSPLIT, $0-12
+       MOVQ    ptr+0(FP), AX
+       MOVL    val+8(FP), BX
+       LOCK
+       ANDL    BX, (AX)
+       RET
index 03fb82292946dce612ef197f5dbc8f2cf9933737..a515683ebb4bb92ab28ed14237648631a9575e22 100644 (file)
@@ -243,3 +243,29 @@ TEXT ·And8(SB), NOSPLIT, $0-9
        BEQ     R4, -4(PC)
        SYNC
        RET
+
+// func Or(addr *uint32, v uint32)
+TEXT ·Or(SB), NOSPLIT, $0-12
+       MOVV    ptr+0(FP), R1
+       MOVW    val+8(FP), R2
+
+       SYNC
+       LL      (R1), R3
+       OR      R2, R3
+       SC      R3, (R1)
+       BEQ     R3, -4(PC)
+       SYNC
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT ·And(SB), NOSPLIT, $0-12
+       MOVV    ptr+0(FP), R1
+       MOVW    val+8(FP), R2
+
+       SYNC
+       LL      (R1), R3
+       AND     R2, R3
+       SC      R3, (R1)
+       BEQ     R3, -4(PC)
+       SYNC
+       RET
index 63bb54882566cf4a39a76049ceb40463fc2f6044..2b2cfabe08918b50187899b600d448cf6d6d9015 100644 (file)
@@ -172,3 +172,29 @@ try_and8:
        BEQ     R4, try_and8
        SYNC
        RET
+
+// func Or(addr *uint32, v uint32)
+TEXT ·Or(SB), NOSPLIT, $0-8
+       MOVW    ptr+0(FP), R1
+       MOVW    val+4(FP), R2
+
+       SYNC
+       LL      (R1), R3
+       OR      R2, R3
+       SC      R3, (R1)
+       BEQ     R3, -4(PC)
+       SYNC
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT ·And(SB), NOSPLIT, $0-8
+       MOVW    ptr+0(FP), R1
+       MOVW    val+4(FP), R2
+
+       SYNC
+       LL      (R1), R3
+       AND     R2, R3
+       SC      R3, (R1)
+       BEQ     R3, -4(PC)
+       SYNC
+       RET
index c0237de4d05df048116b1ff14d5ee567c6d19278..bb009ab34da5f300aa7c53020b02a06480595a60 100644 (file)
@@ -222,8 +222,32 @@ TEXT runtime∕internal∕atomic·And8(SB), NOSPLIT, $0-9
        MOVBZ   val+8(FP), R4
        LWSYNC
 again:
-       LBAR    (R3),R6
-       AND     R4,R6
-       STBCCC  R6,(R3)
+       LBAR    (R3), R6
+       AND     R4, R6
+       STBCCC  R6, (R3)
+       BNE     again
+       RET
+
+// func Or(addr *uint32, v uint32)
+TEXT runtime∕internal∕atomic·Or(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R3
+       MOVW    val+8(FP), R4
+       LWSYNC
+again:
+       LWAR    (R3), R6
+       OR      R4, R6
+       STWCCC  R6, (R3)
+       BNE     again
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT runtime∕internal∕atomic·And(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R3
+       MOVW    val+8(FP), R4
+       LWSYNC
+again:
+       LWAR    (R3),R6
+       AND     R4, R6
+       STWCCC  R6, (R3)
        BNE     again
        RET
index 9a19bc0ecebc5cc86f79cb75d4731a20966609e0..daf1f3cc9f17f805b6ac636733485b0311ece944 100644 (file)
@@ -174,8 +174,8 @@ TEXT ·Xchguintptr(SB), NOSPLIT, $0-24
 
 // func Or8(addr *uint8, v uint8)
 TEXT ·Or8(SB), NOSPLIT, $0-9
-       MOVD    ptr+0(FP), R3
-       MOVBZ   val+8(FP), R4
+       MOVD    ptr+0(FP), R3
+       MOVBZ   val+8(FP), R4
        // We don't have atomic operations that work on individual bytes so we
        // need to align addr down to a word boundary and create a mask
        // containing v to OR with the entire word atomically.
@@ -188,8 +188,8 @@ TEXT ·Or8(SB), NOSPLIT, $0-9
 
 // func And8(addr *uint8, v uint8)
 TEXT ·And8(SB), NOSPLIT, $0-9
-       MOVD    ptr+0(FP), R3
-       MOVBZ   val+8(FP), R4
+       MOVD    ptr+0(FP), R3
+       MOVBZ   val+8(FP), R4
        // We don't have atomic operations that work on individual bytes so we
        // need to align addr down to a word boundary and create a mask
        // containing v to AND with the entire word atomically.
@@ -200,3 +200,17 @@ TEXT ·And8(SB), NOSPLIT, $0-9
        RLL     R5, R4, R4           // R4 = rotl(R4, R5)
        LAN     R4, R6, 0(R3)        // R6 = *R3; *R3 &= R4; (atomic)
        RET
+
+// func Or(addr *uint32, v uint32)
+TEXT ·Or(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R3
+       MOVW    val+8(FP), R4
+       LAO     R4, R6, 0(R3)        // R6 = *R3; *R3 |= R4; (atomic)
+       RET
+
+// func And(addr *uint32, v uint32)
+TEXT ·And(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R3
+       MOVW    val+8(FP), R4
+       LAN     R4, R6, 0(R3)        // R6 = *R3; *R3 &= R4; (atomic)
+       RET
index 06ce6a5356f0f590f85dc3382378ceec131624d8..1bfcb1143d3ec8fdd8fa1fcfb3e94d438bb70cdd 100644 (file)
@@ -69,6 +69,12 @@ func And8(ptr *uint8, val uint8)
 //go:noescape
 func Or8(ptr *uint8, val uint8)
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
 //go:noescape
index 1b71a16d94875bd5a2ae30e56cad50a58ba039c7..e36eb83a113da773a2cf91ea1119597bac2f9421 100644 (file)
@@ -77,6 +77,12 @@ func And8(ptr *uint8, val uint8)
 //go:noescape
 func Or8(ptr *uint8, val uint8)
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
 //go:noescape
index 67d529c1cb3476dd1124810d7e84b5e6385f31b0..546b3d6120752f23d9f9355043b65d9dfff7f6b2 100644 (file)
@@ -182,6 +182,26 @@ func And8(addr *uint8, v uint8) {
        }
 }
 
+//go:nosplit
+func Or(addr *uint32, v uint32) {
+       for {
+               old := *addr
+               if Cas(addr, old, old|v) {
+                       return
+               }
+       }
+}
+
+//go:nosplit
+func And(addr *uint32, v uint32) {
+       for {
+               old := *addr
+               if Cas(addr, old, old&v) {
+                       return
+               }
+       }
+}
+
 //go:nosplit
 func armcas(ptr *uint32, old, new uint32) bool
 
index c9b4322fe93902fdf514d32326bba838b717bfed..d49bee8936f63e3295b5bdc13a043df24fb0f3d9 100644 (file)
@@ -53,6 +53,12 @@ func Or8(ptr *uint8, val uint8)
 //go:noescape
 func And8(ptr *uint8, val uint8)
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Cas64(ptr *uint64, old, new uint64) bool
 
index 36c7698b189286a546daaf2a8db0908ffecf6b44..0cf3c40223079a75ecf61a9b87273e3bbb156f54 100644 (file)
@@ -164,3 +164,22 @@ TEXT ·Or8(SB), NOSPLIT, $0-9
        CBNZ    R3, -3(PC)
        RET
 
+// func And(addr *uint32, v uint32)
+TEXT ·And(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R0
+       MOVW    val+8(FP), R1
+       LDAXRW  (R0), R2
+       AND     R1, R2
+       STLXRW  R2, (R0), R3
+       CBNZ    R3, -3(PC)
+       RET
+
+// func Or(addr *uint32, v uint32)
+TEXT ·Or(SB), NOSPLIT, $0-12
+       MOVD    ptr+0(FP), R0
+       MOVW    val+8(FP), R1
+       LDAXRW  (R0), R2
+       ORR     R1, R2
+       STLXRW  R2, (R0), R3
+       CBNZ    R3, -3(PC)
+       RET
index fca2242514656cce873c6b63bf14550da02f1077..b0109d72b06835d5a4dbd91b57c314629b41cc2c 100644 (file)
@@ -55,6 +55,12 @@ func Or8(ptr *uint8, val uint8)
 
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Cas64(ptr *uint64, old, new uint64) bool
 
index be1e6a038b118cc8527cacba3d16bfae73dc5a91..1336b501214263f60f910d5ae639c5a6ee9c7c6d 100644 (file)
@@ -141,6 +141,12 @@ func And8(ptr *uint8, val uint8)
 //go:noescape
 func Or8(ptr *uint8, val uint8)
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Store(ptr *uint32, val uint32)
 
index e759bb27a293cc741bed6341a8507947c8e40d4d..e4b109f0ec1e77de9209a1b7c1af859b70301ecb 100644 (file)
@@ -55,6 +55,12 @@ func Or8(ptr *uint8, val uint8)
 
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Cas64(ptr *uint64, old, new uint64) bool
 
index 617bc1a3eb247874e4d16902681ee59d1ad99361..8f24d616259c6530c1219302db0a866a7099cffc 100644 (file)
@@ -51,6 +51,12 @@ func Or8(ptr *uint8, val uint8)
 //go:noescape
 func And8(ptr *uint8, val uint8)
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Cas64(ptr *uint64, old, new uint64) bool
 
index db139d690ae4ee49c7196f45d8790e21e95644a4..74c896cea6c8a2bda37ca27ecdec8d9e4a7875f9 100644 (file)
@@ -242,3 +242,17 @@ TEXT ·Or8(SB), NOSPLIT, $0-9
        SLL     A2, A1
        AMOORW  A1, (A0), ZERO
        RET
+
+// func And(ptr *uint32, val uint32)
+TEXT ·And(SB), NOSPLIT, $0-12
+       MOV     ptr+0(FP), A0
+       MOVW    val+8(FP), A1
+       AMOANDW A1, (A0), ZERO
+       RET
+
+// func Or(ptr *uint32, val uint32)
+TEXT ·Or(SB), NOSPLIT, $0-12
+       MOV     ptr+0(FP), A0
+       MOVW    val+8(FP), A1
+       AMOORW  A1, (A0), ZERO
+       RET
index b649caa39f2a4c78a4df4ba274fa46e87cd8eb76..a058d601029c98427ef725acbc61016e1120ad2d 100644 (file)
@@ -91,6 +91,12 @@ func Or8(ptr *uint8, val uint8)
 
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
+//go:noescape
+func And(ptr *uint32, val uint32)
+
+//go:noescape
+func Or(ptr *uint32, val uint32)
+
 //go:noescape
 func Xadd(ptr *uint32, delta int32) uint32
 
index a9f95077c0efc9186ca55fb3aa7c2c911f3b6a5a..c9c2eba248cf4221b3c5cad1d4d3b577117a60f9 100644 (file)
@@ -150,6 +150,45 @@ func TestAnd8(t *testing.T) {
        }
 }
 
+func TestAnd(t *testing.T) {
+       // Basic sanity check.
+       x := uint32(0xffffffff)
+       for i := uint32(0); i < 32; i++ {
+               atomic.And(&x, ^(1 << i))
+               if r := uint32(0xffffffff) << (i + 1); x != r {
+                       t.Fatalf("clearing bit %#x: want %#x, got %#x", uint32(1<<i), r, x)
+               }
+       }
+
+       // 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 TestOr8(t *testing.T) {
        // Basic sanity check.
        x := uint8(0)
@@ -186,7 +225,43 @@ func TestOr8(t *testing.T) {
        }
 }
 
-func TestBitwiseContended(t *testing.T) {
+func TestOr(t *testing.T) {
+       // Basic sanity check.
+       x := uint32(0)
+       for i := uint32(0); i < 32; i++ {
+               atomic.Or(&x, 1<<i)
+               if r := (uint32(1) << (i + 1)) - 1; x != r {
+                       t.Fatalf("setting bit %#x: want %#x, got %#x", uint32(1)<<i, r, x)
+               }
+       }
+
+       // 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.Or(&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 TestBitwiseContended8(t *testing.T) {
        // Start with every bit in array set to 0.
        a := make([]uint8, 16)
 
@@ -228,6 +303,48 @@ func TestBitwiseContended(t *testing.T) {
        }
 }
 
+func TestBitwiseContended(t *testing.T) {
+       // Start with every bit in array set to 0.
+       a := make([]uint32, 16)
+
+       // Iterations to try.
+       N := 1 << 16
+       if testing.Short() {
+               N = 1 << 10
+       }
+
+       // Set and then clear every bit in the array bit-by-bit in different goroutines.
+       done := make(chan bool)
+       for i := 0; i < 32; i++ {
+               m := uint32(1 << i)
+               go func() {
+                       for n := 0; n < N; n++ {
+                               for i := range a {
+                                       atomic.Or(&a[i], m)
+                                       if atomic.Load(&a[i])&m != m {
+                                               t.Errorf("a[%v] bit %#x not set", i, m)
+                                       }
+                                       atomic.And(&a[i], ^m)
+                                       if atomic.Load(&a[i])&m != 0 {
+                                               t.Errorf("a[%v] bit %#x not clear", 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 TestStorepNoWB(t *testing.T) {
        var p [2]*int
        for i := range p {
index 60a4942884f9802637ff017e9f094c3f63c8e71a..b05d98ed515a226d4f0f233e122d58786b1d84c9 100644 (file)
@@ -131,6 +131,18 @@ func Or8(ptr *uint8, val uint8) {
 
 // NOTE: Do not add atomicxor8 (XOR is not idempotent).
 
+//go:nosplit
+//go:noinline
+func And(ptr *uint32, val uint32) {
+       *ptr = *ptr & val
+}
+
+//go:nosplit
+//go:noinline
+func Or(ptr *uint32, val uint32) {
+       *ptr = *ptr | val
+}
+
 //go:nosplit
 //go:noinline
 func Cas64(ptr *uint64, old, new uint64) bool {
index de71b0f2c7b5c13621caadde90598165791f16c0..434aa6d43404ac95aadf1849762965271fca232e 100644 (file)
@@ -51,6 +51,14 @@ func BenchmarkAnd8(b *testing.B) {
        }
 }
 
+func BenchmarkAnd(b *testing.B) {
+       var x [128]uint32 // give x its own cache line
+       sink = &x
+       for i := 0; i < b.N; i++ {
+               atomic.And(&x[63], uint32(i))
+       }
+}
+
 func BenchmarkAnd8Parallel(b *testing.B) {
        var x [512]uint8 // give byte its own cache line
        sink = &x
@@ -63,6 +71,18 @@ func BenchmarkAnd8Parallel(b *testing.B) {
        })
 }
 
+func BenchmarkAndParallel(b *testing.B) {
+       var x [128]uint32 // give x its own cache line
+       sink = &x
+       b.RunParallel(func(pb *testing.PB) {
+               i := uint32(0)
+               for pb.Next() {
+                       atomic.And(&x[63], i)
+                       i++
+               }
+       })
+}
+
 func BenchmarkOr8(b *testing.B) {
        var x [512]uint8 // give byte its own cache line
        sink = &x
@@ -71,6 +91,14 @@ func BenchmarkOr8(b *testing.B) {
        }
 }
 
+func BenchmarkOr(b *testing.B) {
+       var x [128]uint32 // give x its own cache line
+       sink = &x
+       for i := 0; i < b.N; i++ {
+               atomic.Or(&x[63], uint32(i))
+       }
+}
+
 func BenchmarkOr8Parallel(b *testing.B) {
        var x [512]uint8 // give byte its own cache line
        sink = &x
@@ -83,6 +111,18 @@ func BenchmarkOr8Parallel(b *testing.B) {
        })
 }
 
+func BenchmarkOrParallel(b *testing.B) {
+       var x [128]uint32 // give x its own cache line
+       sink = &x
+       b.RunParallel(func(pb *testing.PB) {
+               i := uint32(0)
+               for pb.Next() {
+                       atomic.Or(&x[63], i)
+                       i++
+               }
+       })
+}
+
 func BenchmarkXadd(b *testing.B) {
        var x uint32
        ptr := &x