aboutsummaryrefslogtreecommitdiff
path: root/libgo/go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2016-10-28 22:34:47 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-10-28 22:34:47 +0000
commit94f56408db96e2e12f6e1322ed2c1c465df934f6 (patch)
treef5eaac76bc47cc2637e5e30b9ff6c27499fac2d9 /libgo/go
parent21f1031d6cc228e2b468338b3dfa4303d54ac207 (diff)
downloadgcc-94f56408db96e2e12f6e1322ed2c1c465df934f6.zip
gcc-94f56408db96e2e12f6e1322ed2c1c465df934f6.tar.gz
gcc-94f56408db96e2e12f6e1322ed2c1c465df934f6.tar.bz2
compiler, runtime: copy slice code from Go 1.7 runtime
Change the compiler handle append as the gc compiler does: call a function to grow the slice, but otherwise assign the new elements directly to the final slice. For the current gccgo memory allocator the slice code has to call runtime_newarray, not mallocgc directly, so that the allocator sets the TypeInfo_Array bit in the type pointer. Rename the static function cnew to runtime_docnew, so that the stack trace ignores it when ignoring runtime functions. This was needed to fix the runtime/pprof tests on 386. Reviewed-on: https://go-review.googlesource.com/32218 From-SVN: r241667
Diffstat (limited to 'libgo/go')
-rw-r--r--libgo/go/runtime/slice.go212
-rw-r--r--libgo/go/runtime/stubs.go25
2 files changed, 232 insertions, 5 deletions
diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go
new file mode 100644
index 0000000..4548a5b
--- /dev/null
+++ b/libgo/go/runtime/slice.go
@@ -0,0 +1,212 @@
+// Copyright 2009 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 runtime
+
+import (
+ "unsafe"
+)
+
+// For gccgo, use go:linkname to rename compiler-called functions to
+// themselves, so that the compiler will export them.
+//
+//go:linkname makeslice runtime.makeslice
+//go:linkname growslice runtime.growslice
+//go:linkname slicecopy runtime.slicecopy
+//go:linkname slicestringcopy runtime.slicestringcopy
+
+type slice struct {
+ array unsafe.Pointer
+ len int
+ cap int
+}
+
+// maxElems is a lookup table containing the maximum capacity for a slice.
+// The index is the size of the slice element.
+var maxElems = [...]uintptr{
+ ^uintptr(0),
+ _MaxMem / 1, _MaxMem / 2, _MaxMem / 3, _MaxMem / 4,
+ _MaxMem / 5, _MaxMem / 6, _MaxMem / 7, _MaxMem / 8,
+ _MaxMem / 9, _MaxMem / 10, _MaxMem / 11, _MaxMem / 12,
+ _MaxMem / 13, _MaxMem / 14, _MaxMem / 15, _MaxMem / 16,
+ _MaxMem / 17, _MaxMem / 18, _MaxMem / 19, _MaxMem / 20,
+ _MaxMem / 21, _MaxMem / 22, _MaxMem / 23, _MaxMem / 24,
+ _MaxMem / 25, _MaxMem / 26, _MaxMem / 27, _MaxMem / 28,
+ _MaxMem / 29, _MaxMem / 30, _MaxMem / 31, _MaxMem / 32,
+}
+
+// maxSliceCap returns the maximum capacity for a slice.
+func maxSliceCap(elemsize uintptr) uintptr {
+ if elemsize < uintptr(len(maxElems)) {
+ return maxElems[elemsize]
+ }
+ return _MaxMem / elemsize
+}
+
+// TODO: take uintptrs instead of int64s?
+func makeslice(et *_type, len64, cap64 int64) slice {
+ // NOTE: The len > maxElements check here is not strictly necessary,
+ // but it produces a 'len out of range' error instead of a 'cap out of range' error
+ // when someone does make([]T, bignumber). 'cap out of range' is true too,
+ // but since the cap is only being supplied implicitly, saying len is clearer.
+ // See issue 4085.
+ maxElements := maxSliceCap(et.size)
+ len := int(len64)
+ if len64 < 0 || int64(len) != len64 || uintptr(len) > maxElements {
+ panic(errorString("makeslice: len out of range"))
+ }
+
+ cap := int(cap64)
+ if cap < len || int64(cap) != cap64 || uintptr(cap) > maxElements {
+ panic(errorString("makeslice: cap out of range"))
+ }
+
+ // gccgo's current garbage collector requires using newarray,
+ // not mallocgc here. This can change back to mallocgc when
+ // we port the garbage collector.
+ p := newarray(et, cap)
+ return slice{p, len, cap}
+}
+
+// growslice handles slice growth during append.
+// It is passed the slice element type, the old slice, and the desired new minimum capacity,
+// and it returns a new slice with at least that capacity, with the old data
+// copied into it.
+// The new slice's length is set to the requested capacity.
+func growslice(et *_type, old slice, cap int) slice {
+ if raceenabled {
+ callerpc := getcallerpc(unsafe.Pointer(&et))
+ racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
+ }
+ if msanenabled {
+ msanread(old.array, uintptr(old.len*int(et.size)))
+ }
+
+ if et.size == 0 {
+ if cap < old.cap {
+ panic(errorString("growslice: cap out of range"))
+ }
+ // append should not create a slice with nil pointer but non-zero len.
+ // We assume that append doesn't need to preserve old.array in this case.
+ return slice{unsafe.Pointer(&zerobase), cap, cap}
+ }
+
+ newcap := old.cap
+ doublecap := newcap + newcap
+ if cap > doublecap {
+ newcap = cap
+ } else {
+ if old.len < 1024 {
+ newcap = doublecap
+ } else {
+ for newcap < cap {
+ newcap += newcap / 4
+ }
+ }
+ }
+
+ var lenmem, capmem uintptr
+ const ptrSize = unsafe.Sizeof((*byte)(nil))
+ switch et.size {
+ case 1:
+ lenmem = uintptr(old.len)
+ capmem = roundupsize(uintptr(newcap))
+ newcap = int(capmem)
+ case ptrSize:
+ lenmem = uintptr(old.len) * ptrSize
+ capmem = roundupsize(uintptr(newcap) * ptrSize)
+ newcap = int(capmem / ptrSize)
+ default:
+ lenmem = uintptr(old.len) * et.size
+ capmem = roundupsize(uintptr(newcap) * et.size)
+ newcap = int(capmem / et.size)
+ }
+
+ if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) {
+ panic(errorString("growslice: cap out of range"))
+ }
+
+ var p unsafe.Pointer
+ if et.kind&kindNoPointers != 0 {
+ // gccgo's current GC requires newarray, not mallocgc.
+ p = newarray(et, newcap)
+ memmove(p, old.array, lenmem)
+ // The call to memclr is not needed for gccgo since
+ // the newarray function will zero the memory.
+ // Calling memclr is also wrong since we allocated
+ // newcap*et.size bytes, which is not the same as capmem.
+ // memclr(add(p, lenmem), capmem-lenmem)
+ } else {
+ // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
+ // gccgo's current GC requires newarray, not mallocgc.
+ p = newarray(et, newcap)
+ if !writeBarrier.enabled {
+ memmove(p, old.array, lenmem)
+ } else {
+ for i := uintptr(0); i < lenmem; i += et.size {
+ typedmemmove(et, add(p, i), add(old.array, i))
+ }
+ }
+ }
+
+ return slice{p, cap, newcap}
+}
+
+func slicecopy(to, fm slice, width uintptr) int {
+ if fm.len == 0 || to.len == 0 {
+ return 0
+ }
+
+ n := fm.len
+ if to.len < n {
+ n = to.len
+ }
+
+ if width == 0 {
+ return n
+ }
+
+ if raceenabled {
+ callerpc := getcallerpc(unsafe.Pointer(&to))
+ pc := funcPC(slicecopy)
+ racewriterangepc(to.array, uintptr(n*int(width)), callerpc, pc)
+ racereadrangepc(fm.array, uintptr(n*int(width)), callerpc, pc)
+ }
+ if msanenabled {
+ msanwrite(to.array, uintptr(n*int(width)))
+ msanread(fm.array, uintptr(n*int(width)))
+ }
+
+ size := uintptr(n) * width
+ if size == 1 { // common case worth about 2x to do here
+ // TODO: is this still worth it with new memmove impl?
+ *(*byte)(to.array) = *(*byte)(fm.array) // known to be a byte pointer
+ } else {
+ memmove(to.array, fm.array, size)
+ }
+ return n
+}
+
+func slicestringcopy(to []byte, fm string) int {
+ if len(fm) == 0 || len(to) == 0 {
+ return 0
+ }
+
+ n := len(fm)
+ if len(to) < n {
+ n = len(to)
+ }
+
+ if raceenabled {
+ callerpc := getcallerpc(unsafe.Pointer(&to))
+ pc := funcPC(slicestringcopy)
+ racewriterangepc(unsafe.Pointer(&to[0]), uintptr(n), callerpc, pc)
+ }
+ if msanenabled {
+ msanwrite(unsafe.Pointer(&to[0]), uintptr(n))
+ }
+
+ memmove(unsafe.Pointer(&to[0]), stringStructOf(&fm).str, uintptr(n))
+ return n
+}
diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go
index 5924ee6..755933d 100644
--- a/libgo/go/runtime/stubs.go
+++ b/libgo/go/runtime/stubs.go
@@ -253,11 +253,18 @@ func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
memmove(dst, src, typ.size)
}
-// Here for gccgo unless and until we port slice.go.
-type slice struct {
- array unsafe.Pointer
- len int
- cap int
+// Temporary for gccgo until we port mbarrier.go.
+//go:linkname typedslicecopy runtime.typedslicecopy
+func typedslicecopy(typ *_type, dst, src slice) int {
+ n := dst.len
+ if n > src.len {
+ n = src.len
+ }
+ if n == 0 {
+ return 0
+ }
+ memmove(dst.array, src.array, uintptr(n)*typ.size)
+ return n
}
// Here for gccgo until we port malloc.go.
@@ -474,3 +481,11 @@ func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
func writebarrierptr(dst *uintptr, src uintptr) {
*dst = src
}
+
+// Temporary for gccgo until we port malloc.go
+var zerobase uintptr
+
+//go:linkname getZerobase runtime.getZerobase
+func getZerobase() *uintptr {
+ return &zerobase
+}