From 337fa50b7b0d2964b6ebe2373224b5c1bbb61efb Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 10 Sep 2016 12:21:59 +0000 Subject: [PATCH] runtime/internal/atomic: new package, API copied from Go 1.7 Copy over the Go 1.7 runtime/internal/atomic package, but implement the functions in C using __atomic functions rather than using the processor-specific assembler code. Reviewed-on: https://go-review.googlesource.com/29010 From-SVN: r240070 --- gcc/go/gofrontend/MERGE | 2 +- libgo/Makefile.am | 17 ++ libgo/Makefile.in | 18 ++ libgo/go/runtime/internal/atomic/atomic.c | 237 ++++++++++++++++++ .../go/runtime/internal/atomic/atomic_test.go | 67 +++++ libgo/go/runtime/internal/atomic/gccgo.go | 59 +++++ libgo/go/runtime/internal/atomic/stubs.go | 33 +++ 7 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 libgo/go/runtime/internal/atomic/atomic.c create mode 100644 libgo/go/runtime/internal/atomic/atomic_test.go create mode 100644 libgo/go/runtime/internal/atomic/gccgo.go create mode 100644 libgo/go/runtime/internal/atomic/stubs.go diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index daa4bcf8bc6..487adbe8e50 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -6e791d2f3c183d55fdcabe3df20f01c029fd6d54 +04fe765560107e5d4c5f98c1022765930a1069f9 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 0a54a8972c5..1fde5086812 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -843,6 +843,8 @@ libgo_go_objs = \ net/rpc/jsonrpc.lo \ runtime/debug.lo \ runtime/pprof.lo \ + runtime/internal/atomic.lo \ + runtime/internal/atomic_c.lo \ sync/atomic.lo \ sync/atomic_c.lo \ text/scanner.lo \ @@ -2365,6 +2367,18 @@ runtime/pprof/check: $(CHECK_DEPS) # Also use -fno-inline to get better results from the memory profiler. runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline +@go_include@ runtime/internal/atomic.lo.dep +runtime/internal/atomic.lo.dep: $(srcdir)/go/runtime/internal/atomic/*.go + $(BUILDDEPS) +runtime_internal_atomic_lo_GOCFLAGS = -fgo-compiling-runtime +runtime/internal/atomic.lo: + $(BUILDPACKAGE) +runtime/internal/atomic_c.lo: go/runtime/internal/atomic/atomic.c runtime.inc runtime/internal/atomic.lo + $(LTCOMPILE) -c -o $@ $(srcdir)/go/runtime/internal/atomic/atomic.c +runtime/internal/atomic/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: runtime/internal/atomic/check + @go_include@ sync/atomic.lo.dep sync/atomic.lo.dep: $(srcdir)/go/sync/atomic/*.go $(BUILDDEPS) @@ -2800,6 +2814,8 @@ runtime/debug.gox: runtime/debug.lo $(BUILDGOX) runtime/pprof.gox: runtime/pprof.lo $(BUILDGOX) +runtime/internal/atomic.gox: runtime/internal/atomic.lo + $(BUILDGOX) sync/atomic.gox: sync/atomic.lo $(BUILDGOX) @@ -2954,6 +2970,7 @@ TEST_PACKAGES = \ path/filepath/check \ regexp/syntax/check \ runtime/pprof/check \ + runtime/internal/atomic/check \ sync/atomic/check \ text/scanner/check \ text/tabwriter/check \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index d9c510028b6..931a6c9e0ca 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -212,6 +212,7 @@ am__DEPENDENCIES_1 = bufio.lo bytes.lo bytes/index.lo context.lo \ old/regexp.lo old/template.lo os/exec.lo os/signal.lo \ os/user.lo path/filepath.lo regexp/syntax.lo \ net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \ + runtime/internal/atomic.lo runtime/internal/atomic_c.lo \ sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ text/tabwriter.lo text/template.lo text/template/parse.lo \ testing/iotest.lo testing/quick.lo unicode/utf16.lo \ @@ -1096,6 +1097,8 @@ libgo_go_objs = \ net/rpc/jsonrpc.lo \ runtime/debug.lo \ runtime/pprof.lo \ + runtime/internal/atomic.lo \ + runtime/internal/atomic_c.lo \ sync/atomic.lo \ sync/atomic_c.lo \ text/scanner.lo \ @@ -1247,6 +1250,7 @@ runtime_go_lo_GOCFLAGS = -fgo-c-header=runtime.inc.tmp -fgo-compiling-runtime # otherwise we can't get the line numbers. # Also use -fno-inline to get better results from the memory profiler. runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline +runtime_internal_atomic_lo_GOCFLAGS = -fgo-compiling-runtime # How to build a .gox file from a .lo file. BUILDGOX = \ @@ -1384,6 +1388,7 @@ TEST_PACKAGES = \ path/filepath/check \ regexp/syntax/check \ runtime/pprof/check \ + runtime/internal/atomic/check \ sync/atomic/check \ text/scanner/check \ text/tabwriter/check \ @@ -4987,6 +4992,17 @@ runtime/pprof/check: $(CHECK_DEPS) @$(CHECK) .PHONY: runtime/pprof/check +@go_include@ runtime/internal/atomic.lo.dep +runtime/internal/atomic.lo.dep: $(srcdir)/go/runtime/internal/atomic/*.go + $(BUILDDEPS) +runtime/internal/atomic.lo: + $(BUILDPACKAGE) +runtime/internal/atomic_c.lo: go/runtime/internal/atomic/atomic.c runtime.inc runtime/internal/atomic.lo + $(LTCOMPILE) -c -o $@ $(srcdir)/go/runtime/internal/atomic/atomic.c +runtime/internal/atomic/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: runtime/internal/atomic/check + @go_include@ sync/atomic.lo.dep sync/atomic.lo.dep: $(srcdir)/go/sync/atomic/*.go $(BUILDDEPS) @@ -5415,6 +5431,8 @@ runtime/debug.gox: runtime/debug.lo $(BUILDGOX) runtime/pprof.gox: runtime/pprof.lo $(BUILDGOX) +runtime/internal/atomic.gox: runtime/internal/atomic.lo + $(BUILDGOX) sync/atomic.gox: sync/atomic.lo $(BUILDGOX) diff --git a/libgo/go/runtime/internal/atomic/atomic.c b/libgo/go/runtime/internal/atomic/atomic.c new file mode 100644 index 00000000000..3393fbef44e --- /dev/null +++ b/libgo/go/runtime/internal/atomic/atomic.c @@ -0,0 +1,237 @@ +// Copyright 2016 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. + +#include + +#include "runtime.h" + +uint32_t Load (uint32_t *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Load") + __attribute__ ((no_split_stack)); + +uint32_t +Load (uint32_t *ptr) +{ + return __atomic_load_n (ptr, __ATOMIC_ACQUIRE); +} + +void *Loadp (void *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loadp") + __attribute__ ((no_split_stack)); + +void * +Loadp (void *ptr) +{ + return __atomic_load_n ((void **) ptr, __ATOMIC_ACQUIRE); +} + +uint64_t Load64 (uint64_t *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Load64") + __attribute__ ((no_split_stack)); + +uint64_t +Load64 (uint64_t *ptr) +{ + return __atomic_load_n (ptr, __ATOMIC_ACQUIRE); +} + +uintptr_t Loaduintptr (uintptr_t *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loaduintptr") + __attribute__ ((no_split_stack)); + +uintptr_t +Loaduintptr (uintptr_t *ptr) +{ + return __atomic_load_n (ptr, __ATOMIC_ACQUIRE); +} + +uintgo Loaduint (uintgo *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loaduint") + __attribute__ ((no_split_stack)); + +uintgo +Loaduint (uintgo *ptr) +{ + return __atomic_load_n (ptr, __ATOMIC_ACQUIRE); +} + +int64_t Loadint64 (int64_t *ptr) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loadint64") + __attribute__ ((no_split_stack)); + +int64_t +Loadint64 (int64_t *ptr) +{ + return __atomic_load_n (ptr, __ATOMIC_ACQUIRE); +} + +uint32_t Xadd (uint32_t *ptr, int32_t delta) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadd") + __attribute__ ((no_split_stack)); + +uint32_t +Xadd (uint32_t *ptr, int32_t delta) +{ + return __atomic_add_fetch (ptr, (uint32_t) delta, __ATOMIC_SEQ_CST); +} + +uint64_t Xadd64 (uint64_t *ptr, int64_t delta) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadd64") + __attribute__ ((no_split_stack)); + +uint64_t +Xadd64 (uint64_t *ptr, int64_t delta) +{ + return __atomic_add_fetch (ptr, (uint64_t) delta, __ATOMIC_SEQ_CST); +} + +uintptr_t Xadduintptr (uintptr_t *ptr, uintptr_t delta) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadduintptr") + __attribute__ ((no_split_stack)); + +uintptr_t +Xadduintptr (uintptr_t *ptr, uintptr_t delta) +{ + return __atomic_add_fetch (ptr, delta, __ATOMIC_SEQ_CST); +} + +int64_t Xaddint64 (int64_t *ptr, int64_t delta) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xaddint64") + __attribute__ ((no_split_stack)); + +int64_t +Xaddint64 (int64_t *ptr, int64_t delta) +{ + return __atomic_add_fetch (ptr, delta, __ATOMIC_SEQ_CST); +} + +uint32_t Xchg (uint32_t *ptr, uint32_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchg") + __attribute__ ((no_split_stack)); + +uint32_t +Xchg (uint32_t *ptr, uint32_t new) +{ + return __atomic_exchange_n (ptr, new, __ATOMIC_SEQ_CST); +} + +uint64_t Xchg64 (uint64_t *ptr, uint64_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchg64") + __attribute__ ((no_split_stack)); + +uint64_t +Xchg64 (uint64_t *ptr, uint64_t new) +{ + return __atomic_exchange_n (ptr, new, __ATOMIC_SEQ_CST); +} + +uintptr_t Xchguintptr (uintptr_t *ptr, uintptr_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchguintptr") + __attribute__ ((no_split_stack)); + +uintptr_t +Xchguintptr (uintptr_t *ptr, uintptr_t new) +{ + return __atomic_exchange_n (ptr, new, __ATOMIC_SEQ_CST); +} + +void And8 (uint8_t *ptr, uint8_t val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.And8") + __attribute__ ((no_split_stack)); + +void +And8 (uint8_t *ptr, uint8_t val) +{ + __atomic_and_fetch (ptr, val, __ATOMIC_SEQ_CST); +} + +void Or8 (uint8_t *ptr, uint8_t val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Or8") + __attribute__ ((no_split_stack)); + +void +Or8 (uint8_t *ptr, uint8_t val) +{ + __atomic_or_fetch (ptr, val, __ATOMIC_SEQ_CST); +} + +_Bool Cas (uint32_t *ptr, uint32_t old, uint32_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Cas") + __attribute__ ((no_split_stack)); + +_Bool +Cas (uint32_t *ptr, uint32_t old, uint32_t new) +{ + return __atomic_compare_exchange_n (ptr, &old, new, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); +} + +_Bool Cas64 (uint64_t *ptr, uint64_t old, uint64_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Cas64") + __attribute__ ((no_split_stack)); + +_Bool +Cas64 (uint64_t *ptr, uint64_t old, uint64_t new) +{ + return __atomic_compare_exchange_n (ptr, &old, new, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); +} + +_Bool Casp1 (void **ptr, void *old, void *new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Casp1") + __attribute__ ((no_split_stack)); + +_Bool +Casp1 (void **ptr, void *old, void *new) +{ + return __atomic_compare_exchange_n (ptr, &old, new, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); +} + +_Bool Casuintptr (uintptr_t *ptr, uintptr_t old, uintptr_t new) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Casuintptr") + __attribute__ ((no_split_stack)); + +_Bool +Casuintptr (uintptr_t *ptr, uintptr_t old, uintptr_t new) +{ + return __atomic_compare_exchange_n (ptr, &old, new, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); +} + +void Store (uint32_t *ptr, uint32_t val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Store") + __attribute__ ((no_split_stack)); + +void +Store (uint32_t *ptr, uint32_t val) +{ + __atomic_store_n (ptr, val, __ATOMIC_SEQ_CST); +} + +void Store64 (uint64_t *ptr, uint64_t val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Store64") + __attribute__ ((no_split_stack)); + +void +Store64 (uint64_t *ptr, uint64_t val) +{ + __atomic_store_n (ptr, val, __ATOMIC_SEQ_CST); +} + +void Storeuintptr (uintptr_t *ptr, uintptr_t val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Storeuintptr") + __attribute__ ((no_split_stack)); + +void +Storeuintptr (uintptr_t *ptr, uintptr_t val) +{ + __atomic_store_n (ptr, val, __ATOMIC_SEQ_CST); +} + +void StorepNoWB (void *ptr, void *val) + __asm__ (GOSYM_PREFIX "runtime_internal_atomic.StorepNoWB") + __attribute__ ((no_split_stack)); + +void +StorepNoWB (void *ptr, void *val) +{ + __atomic_store_n ((void**) ptr, val, __ATOMIC_SEQ_CST); +} diff --git a/libgo/go/runtime/internal/atomic/atomic_test.go b/libgo/go/runtime/internal/atomic/atomic_test.go new file mode 100644 index 00000000000..d5dc552b952 --- /dev/null +++ b/libgo/go/runtime/internal/atomic/atomic_test.go @@ -0,0 +1,67 @@ +// Copyright 2015 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. + +package atomic_test + +import ( + "runtime" + "runtime/internal/atomic" + "testing" + "unsafe" +) + +func runParallel(N, iter int, f func()) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(int(N))) + done := make(chan bool) + for i := 0; i < N; i++ { + go func() { + for j := 0; j < iter; j++ { + f() + } + done <- true + }() + } + for i := 0; i < N; i++ { + <-done + } +} + +func TestXadduintptr(t *testing.T) { + const N = 20 + const iter = 100000 + inc := uintptr(100) + total := uintptr(0) + runParallel(N, iter, func() { + atomic.Xadduintptr(&total, inc) + }) + if want := uintptr(N * iter * inc); want != total { + t.Fatalf("xadduintpr error, want %d, got %d", want, total) + } + total = 0 + runParallel(N, iter, func() { + atomic.Xadduintptr(&total, inc) + atomic.Xadduintptr(&total, uintptr(-int64(inc))) + }) + if total != 0 { + t.Fatalf("xadduintpr total error, want %d, got %d", 0, total) + } +} + +// Tests that xadduintptr correctly updates 64-bit values. The place where +// we actually do so is mstats.go, functions mSysStat{Inc,Dec}. +func TestXadduintptrOnUint64(t *testing.T) { + /* if runtime.BigEndian != 0 { + // On big endian architectures, we never use xadduintptr to update + // 64-bit values and hence we skip the test. (Note that functions + // mSysStat{Inc,Dec} in mstats.go have explicit checks for + // big-endianness.) + return + }*/ + const inc = 100 + val := uint64(0) + atomic.Xadduintptr((*uintptr)(unsafe.Pointer(&val)), inc) + if inc != val { + t.Fatalf("xadduintptr should increase lower-order bits, want %d, got %d", inc, val) + } +} diff --git a/libgo/go/runtime/internal/atomic/gccgo.go b/libgo/go/runtime/internal/atomic/gccgo.go new file mode 100644 index 00000000000..696736465fd --- /dev/null +++ b/libgo/go/runtime/internal/atomic/gccgo.go @@ -0,0 +1,59 @@ +// Copyright 2016 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. + +package atomic + +// Stubs for atomic functions that in gccgo are implemented in C. + +import "unsafe" + +//go:noescape +func Load(ptr *uint32) uint32 + +//go:noescape +func Loadp(ptr unsafe.Pointer) unsafe.Pointer + +//go:noescape +func Load64(ptr *uint64) uint64 + +//go:noescape +func Xadd(ptr *uint32, delta int32) uint32 + +//go:noescape +func Xadd64(ptr *uint64, delta int64) uint64 + +//go:noescape +func Xadduintptr(ptr *uintptr, delta uintptr) uintptr + +//go:noescape +func Xchg(ptr *uint32, new uint32) uint32 + +//go:noescape +func Xchg64(ptr *uint64, new uint64) uint64 + +//go:noescape +func Xchguintptr(ptr *uintptr, new uintptr) uintptr + +//go:noescape +func And8(ptr *uint8, val uint8) + +//go:noescape +func Or8(ptr *uint8, val uint8) + +// NOTE: Do not add atomicxor8 (XOR is not idempotent). + +//go:noescape +func Cas64(ptr *uint64, old, new uint64) bool + +//go:noescape +func Store(ptr *uint32, val uint32) + +//go:noescape +func Store64(ptr *uint64, val uint64) + +// StorepNoWB performs *ptr = val atomically and without a write +// barrier. +// +// NO go:noescape annotation; see atomic_pointer.go. +func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) diff --git a/libgo/go/runtime/internal/atomic/stubs.go b/libgo/go/runtime/internal/atomic/stubs.go new file mode 100644 index 00000000000..497b98046d8 --- /dev/null +++ b/libgo/go/runtime/internal/atomic/stubs.go @@ -0,0 +1,33 @@ +// Copyright 2015 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. + +package atomic + +import "unsafe" + +//go:noescape +func Cas(ptr *uint32, old, new uint32) bool + +// NO go:noescape annotation; see atomic_pointer.go. +func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool + +//go:noescape +func Casuintptr(ptr *uintptr, old, new uintptr) bool + +//go:noescape +func Storeuintptr(ptr *uintptr, new uintptr) + +//go:noescape +func Loaduintptr(ptr *uintptr) uintptr + +//go:noescape +func Loaduint(ptr *uint) uint + +// TODO(matloob): Should these functions have the go:noescape annotation? + +//go:noescape +func Loadint64(ptr *int64) int64 + +//go:noescape +func Xaddint64(ptr *int64, delta int64) int64 -- 2.30.2