// 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 ( "internal/abi" "internal/goarch" "runtime/internal/math" "runtime/internal/sys" "unsafe" ) type slice struct { array unsafe.Pointer len int cap int } // A notInHeapSlice is a slice backed by runtime/internal/sys.NotInHeap memory. type notInHeapSlice struct { array *notInHeap len int cap int } func panicmakeslicelen() { panic(errorString("makeslice: len out of range")) } func panicmakeslicecap() { panic(errorString("makeslice: cap out of range")) } // makeslicecopy allocates a slice of "tolen" elements of type "et", // then copies "fromlen" elements of type "et" into that new allocation from "from". func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer { var tomem, copymem uintptr if uintptr(tolen) > uintptr(fromlen) { var overflow bool tomem, overflow = math.MulUintptr(et.size, uintptr(tolen)) if overflow || tomem > maxAlloc || tolen < 0 { panicmakeslicelen() } copymem = et.size * uintptr(fromlen) } else { // fromlen is a known good length providing and equal or greater than tolen, // thereby making tolen a good slice length too as from and to slices have the // same element width. tomem = et.size * uintptr(tolen) copymem = tomem } var to unsafe.Pointer if et.ptrdata == 0 { to = mallocgc(tomem, nil, false) if copymem < tomem { memclrNoHeapPointers(add(to, copymem), tomem-copymem) } } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. to = mallocgc(tomem, et, true) if copymem > 0 && writeBarrier.enabled { // Only shade the pointers in old.array since we know the destination slice to // only contains nil pointers because it has been cleared during alloc. bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem) } } if raceenabled { callerpc := getcallerpc() pc := abi.FuncPCABIInternal(makeslicecopy) racereadrangepc(from, copymem, callerpc, pc) } if msanenabled { msanread(from, copymem) } if asanenabled { asanread(from, copymem) } memmove(to, from, copymem) return to } func makeslice(et *_type, len, cap int) unsafe.Pointer { mem, overflow := math.MulUintptr(et.size, uintptr(cap)) if overflow || mem > maxAlloc || len < 0 || len > cap { // NOTE: Produce 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 golang.org/issue/4085. mem, overflow := math.MulUintptr(et.size, uintptr(len)) if overflow || mem > maxAlloc || len < 0 { panicmakeslicelen() } panicmakeslicecap() } return mallocgc(mem, et, true) } func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer { len := int(len64) if int64(len) != len64 { panicmakeslicelen() } cap := int(cap64) if int64(cap) != cap64 { panicmakeslicecap() } return makeslice(et, len, cap) } // This is a wrapper over runtime/internal/math.MulUintptr, // so the compiler can recognize and treat it as an intrinsic. func mulUintptr(a, b uintptr) (uintptr, bool) { return math.MulUintptr(a, b) } // growslice allocates new backing store for a slice. // // arguments: // // oldPtr = pointer to the slice's backing array // newLen = new length (= oldLen + num) // oldCap = original slice's capacity. // num = number of elements being added // et = element type // // return values: // // newPtr = pointer to the new backing store // newLen = same value as the argument // newCap = capacity of the new backing store // // Requires that uint(newLen) > uint(oldCap). // Assumes the original slice length is newLen - num // // A new backing store is allocated with space for at least newLen elements. // Existing entries [0, oldLen) are copied over to the new backing store. // Added entries [oldLen, newLen) are not initialized by growslice // (although for pointer-containing element types, they are zeroed). They // must be initialized by the caller. // Trailing entries [newLen, newCap) are zeroed. // // growslice's odd calling convention makes the generated code that calls // this function simpler. In particular, it accepts and returns the // new length so that the old length is not live (does not need to be // spilled/restored) and the new length is returned (also does not need // to be spilled/restored). func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice { oldLen := newLen - num if raceenabled { callerpc := getcallerpc() racereadrangepc(oldPtr, uintptr(oldLen*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice)) } if msanenabled { msanread(oldPtr, uintptr(oldLen*int(et.size))) } if asanenabled { asanread(oldPtr, uintptr(oldLen*int(et.size))) } if newLen < 0 { panic(errorString("growslice: len out of range")) } if et.size == 0 { // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve oldPtr in this case. return slice{unsafe.Pointer(&zerobase), newLen, newLen} } newcap := oldCap doublecap := newcap + newcap if newLen > doublecap { newcap = newLen } else { const threshold = 256 if oldCap < threshold { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < newLen { // Transition from growing 2x for small slices // to growing 1.25x for large slices. This formula // gives a smooth-ish transition between the two. newcap += (newcap + 3*threshold) / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = newLen } } } var overflow bool var lenmem, newlenmem, capmem uintptr // Specialize for common values of et.size. // For 1 we don't need any division/multiplication. // For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant. // For powers of 2, use a variable shift. switch { case et.size == 1: lenmem = uintptr(oldLen) newlenmem = uintptr(newLen) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.size == goarch.PtrSize: lenmem = uintptr(oldLen) * goarch.PtrSize newlenmem = uintptr(newLen) * goarch.PtrSize capmem = roundupsize(uintptr(newcap) * goarch.PtrSize) overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize newcap = int(capmem / goarch.PtrSize) case isPowerOfTwo(et.size): var shift uintptr if goarch.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.TrailingZeros64(uint64(et.size))) & 63 } else { shift = uintptr(sys.TrailingZeros32(uint32(et.size))) & 31 } lenmem = uintptr(oldLen) << shift newlenmem = uintptr(newLen) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) capmem = uintptr(newcap) << shift default: lenmem = uintptr(oldLen) * et.size newlenmem = uintptr(newLen) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem = roundupsize(capmem) newcap = int(capmem / et.size) capmem = uintptr(newcap) * et.size } // The check of overflow in addition to capmem > maxAlloc is needed // to prevent an overflow which can be used to trigger a segfault // on 32bit architectures with this example program: // // type T [1<<27 + 1]int64 // // var d T // var s []T // // func main() { // s = append(s, d, d, d, d) // print(len(s), "\n") // } if overflow || capmem > maxAlloc { panic(errorString("growslice: len out of range")) } var p unsafe.Pointer if et.ptrdata == 0 { p = mallocgc(capmem, nil, false) // The append() that calls growslice is going to overwrite from oldLen to newLen. // Only clear the part that will not be overwritten. // The reflect_growslice() that calls growslice will manually clear // the region not cleared here. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) if lenmem > 0 && writeBarrier.enabled { // Only shade the pointers in oldPtr since we know the destination slice p // only contains nil pointers because it has been cleared during alloc. bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldPtr), lenmem-et.size+et.ptrdata) } } memmove(p, oldPtr, lenmem) return slice{p, newLen, newcap} } //go:linkname reflect_growslice reflect.growslice func reflect_growslice(et *_type, old slice, num int) slice { // Semantically equivalent to slices.Grow, except that the caller // is responsible for ensuring that old.len+num > old.cap. num -= old.cap - old.len // preserve memory of old[old.len:old.cap] new := growslice(old.array, old.cap+num, old.cap, num, et) // growslice does not zero out new[old.cap:new.len] since it assumes that // the memory will be overwritten by an append() that called growslice. // Since the caller of reflect_growslice is not append(), // zero out this region before returning the slice to the reflect package. if et.ptrdata == 0 { oldcapmem := uintptr(old.cap) * et.size newlenmem := uintptr(new.len) * et.size memclrNoHeapPointers(add(new.array, oldcapmem), newlenmem-oldcapmem) } new.len = old.len // preserve the old length return new } func isPowerOfTwo(x uintptr) bool { return x&(x-1) == 0 } // slicecopy is used to copy from a string or slice of pointerless elements into a slice. func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int { if fromLen == 0 || toLen == 0 { return 0 } n := fromLen if toLen < n { n = toLen } if width == 0 { return n } size := uintptr(n) * width if raceenabled { callerpc := getcallerpc() pc := abi.FuncPCABIInternal(slicecopy) racereadrangepc(fromPtr, size, callerpc, pc) racewriterangepc(toPtr, size, callerpc, pc) } if msanenabled { msanread(fromPtr, size) msanwrite(toPtr, size) } if asanenabled { asanread(fromPtr, size) asanwrite(toPtr, size) } if size == 1 { // common case worth about 2x to do here // TODO: is this still worth it with new memmove impl? *(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer } else { memmove(toPtr, fromPtr, size) } return n }