1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime
6
7import (
8	"internal/abi"
9	"internal/goarch"
10	"runtime/internal/math"
11	"runtime/internal/sys"
12	"unsafe"
13)
14
15type slice struct {
16	array unsafe.Pointer
17	len   int
18	cap   int
19}
20
21// A notInHeapSlice is a slice backed by go:notinheap memory.
22type notInHeapSlice struct {
23	array *notInHeap
24	len   int
25	cap   int
26}
27
28func panicmakeslicelen() {
29	panic(errorString("makeslice: len out of range"))
30}
31
32func panicmakeslicecap() {
33	panic(errorString("makeslice: cap out of range"))
34}
35
36// makeslicecopy allocates a slice of "tolen" elements of type "et",
37// then copies "fromlen" elements of type "et" into that new allocation from "from".
38func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer {
39	var tomem, copymem uintptr
40	if uintptr(tolen) > uintptr(fromlen) {
41		var overflow bool
42		tomem, overflow = math.MulUintptr(et.size, uintptr(tolen))
43		if overflow || tomem > maxAlloc || tolen < 0 {
44			panicmakeslicelen()
45		}
46		copymem = et.size * uintptr(fromlen)
47	} else {
48		// fromlen is a known good length providing and equal or greater than tolen,
49		// thereby making tolen a good slice length too as from and to slices have the
50		// same element width.
51		tomem = et.size * uintptr(tolen)
52		copymem = tomem
53	}
54
55	var to unsafe.Pointer
56	if et.ptrdata == 0 {
57		to = mallocgc(tomem, nil, false)
58		if copymem < tomem {
59			memclrNoHeapPointers(add(to, copymem), tomem-copymem)
60		}
61	} else {
62		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
63		to = mallocgc(tomem, et, true)
64		if copymem > 0 && writeBarrier.enabled {
65			// Only shade the pointers in old.array since we know the destination slice to
66			// only contains nil pointers because it has been cleared during alloc.
67			bulkBarrierPreWriteSrcOnly(uintptr(to), uintptr(from), copymem)
68		}
69	}
70
71	if raceenabled {
72		callerpc := getcallerpc()
73		pc := abi.FuncPCABIInternal(makeslicecopy)
74		racereadrangepc(from, copymem, callerpc, pc)
75	}
76	if msanenabled {
77		msanread(from, copymem)
78	}
79	if asanenabled {
80		asanread(from, copymem)
81	}
82
83	memmove(to, from, copymem)
84
85	return to
86}
87
88func makeslice(et *_type, len, cap int) unsafe.Pointer {
89	mem, overflow := math.MulUintptr(et.size, uintptr(cap))
90	if overflow || mem > maxAlloc || len < 0 || len > cap {
91		// NOTE: Produce a 'len out of range' error instead of a
92		// 'cap out of range' error when someone does make([]T, bignumber).
93		// 'cap out of range' is true too, but since the cap is only being
94		// supplied implicitly, saying len is clearer.
95		// See golang.org/issue/4085.
96		mem, overflow := math.MulUintptr(et.size, uintptr(len))
97		if overflow || mem > maxAlloc || len < 0 {
98			panicmakeslicelen()
99		}
100		panicmakeslicecap()
101	}
102
103	return mallocgc(mem, et, true)
104}
105
106func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
107	len := int(len64)
108	if int64(len) != len64 {
109		panicmakeslicelen()
110	}
111
112	cap := int(cap64)
113	if int64(cap) != cap64 {
114		panicmakeslicecap()
115	}
116
117	return makeslice(et, len, cap)
118}
119
120func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
121	if len < 0 {
122		panicunsafeslicelen()
123	}
124
125	mem, overflow := math.MulUintptr(et.size, uintptr(len))
126	if overflow || mem > -uintptr(ptr) {
127		if ptr == nil {
128			panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
129		}
130		panicunsafeslicelen()
131	}
132}
133
134func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
135	len := int(len64)
136	if int64(len) != len64 {
137		panicunsafeslicelen()
138	}
139	unsafeslice(et, ptr, len)
140}
141
142func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) {
143	unsafeslice64(et, ptr, len64)
144
145	// Check that underlying array doesn't straddle multiple heap objects.
146	// unsafeslice64 has already checked for overflow.
147	if checkptrStraddles(ptr, uintptr(len64)*et.size) {
148		throw("checkptr: unsafe.Slice result straddles multiple allocations")
149	}
150}
151
152func panicunsafeslicelen() {
153	panic(errorString("unsafe.Slice: len out of range"))
154}
155
156// growslice handles slice growth during append.
157// It is passed the slice element type, the old slice, and the desired new minimum capacity,
158// and it returns a new slice with at least that capacity, with the old data
159// copied into it.
160// The new slice's length is set to the old slice's length,
161// NOT to the new requested capacity.
162// This is for codegen convenience. The old slice's length is used immediately
163// to calculate where to write new values during an append.
164// TODO: When the old backend is gone, reconsider this decision.
165// The SSA backend might prefer the new length or to return only ptr/cap and save stack space.
166func growslice(et *_type, old slice, cap int) slice {
167	if raceenabled {
168		callerpc := getcallerpc()
169		racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice))
170	}
171	if msanenabled {
172		msanread(old.array, uintptr(old.len*int(et.size)))
173	}
174	if asanenabled {
175		asanread(old.array, uintptr(old.len*int(et.size)))
176	}
177
178	if cap < old.cap {
179		panic(errorString("growslice: cap out of range"))
180	}
181
182	if et.size == 0 {
183		// append should not create a slice with nil pointer but non-zero len.
184		// We assume that append doesn't need to preserve old.array in this case.
185		return slice{unsafe.Pointer(&zerobase), old.len, cap}
186	}
187
188	newcap := old.cap
189	doublecap := newcap + newcap
190	if cap > doublecap {
191		newcap = cap
192	} else {
193		const threshold = 256
194		if old.cap < threshold {
195			newcap = doublecap
196		} else {
197			// Check 0 < newcap to detect overflow
198			// and prevent an infinite loop.
199			for 0 < newcap && newcap < cap {
200				// Transition from growing 2x for small slices
201				// to growing 1.25x for large slices. This formula
202				// gives a smooth-ish transition between the two.
203				newcap += (newcap + 3*threshold) / 4
204			}
205			// Set newcap to the requested cap when
206			// the newcap calculation overflowed.
207			if newcap <= 0 {
208				newcap = cap
209			}
210		}
211	}
212
213	var overflow bool
214	var lenmem, newlenmem, capmem uintptr
215	// Specialize for common values of et.size.
216	// For 1 we don't need any division/multiplication.
217	// For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
218	// For powers of 2, use a variable shift.
219	switch {
220	case et.size == 1:
221		lenmem = uintptr(old.len)
222		newlenmem = uintptr(cap)
223		capmem = roundupsize(uintptr(newcap))
224		overflow = uintptr(newcap) > maxAlloc
225		newcap = int(capmem)
226	case et.size == goarch.PtrSize:
227		lenmem = uintptr(old.len) * goarch.PtrSize
228		newlenmem = uintptr(cap) * goarch.PtrSize
229		capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
230		overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
231		newcap = int(capmem / goarch.PtrSize)
232	case isPowerOfTwo(et.size):
233		var shift uintptr
234		if goarch.PtrSize == 8 {
235			// Mask shift for better code generation.
236			shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
237		} else {
238			shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
239		}
240		lenmem = uintptr(old.len) << shift
241		newlenmem = uintptr(cap) << shift
242		capmem = roundupsize(uintptr(newcap) << shift)
243		overflow = uintptr(newcap) > (maxAlloc >> shift)
244		newcap = int(capmem >> shift)
245	default:
246		lenmem = uintptr(old.len) * et.size
247		newlenmem = uintptr(cap) * et.size
248		capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
249		capmem = roundupsize(capmem)
250		newcap = int(capmem / et.size)
251	}
252
253	// The check of overflow in addition to capmem > maxAlloc is needed
254	// to prevent an overflow which can be used to trigger a segfault
255	// on 32bit architectures with this example program:
256	//
257	// type T [1<<27 + 1]int64
258	//
259	// var d T
260	// var s []T
261	//
262	// func main() {
263	//   s = append(s, d, d, d, d)
264	//   print(len(s), "\n")
265	// }
266	if overflow || capmem > maxAlloc {
267		panic(errorString("growslice: cap out of range"))
268	}
269
270	var p unsafe.Pointer
271	if et.ptrdata == 0 {
272		p = mallocgc(capmem, nil, false)
273		// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
274		// Only clear the part that will not be overwritten.
275		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
276	} else {
277		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
278		p = mallocgc(capmem, et, true)
279		if lenmem > 0 && writeBarrier.enabled {
280			// Only shade the pointers in old.array since we know the destination slice p
281			// only contains nil pointers because it has been cleared during alloc.
282			bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)
283		}
284	}
285	memmove(p, old.array, lenmem)
286
287	return slice{p, old.len, newcap}
288}
289
290func isPowerOfTwo(x uintptr) bool {
291	return x&(x-1) == 0
292}
293
294// slicecopy is used to copy from a string or slice of pointerless elements into a slice.
295func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
296	if fromLen == 0 || toLen == 0 {
297		return 0
298	}
299
300	n := fromLen
301	if toLen < n {
302		n = toLen
303	}
304
305	if width == 0 {
306		return n
307	}
308
309	size := uintptr(n) * width
310	if raceenabled {
311		callerpc := getcallerpc()
312		pc := abi.FuncPCABIInternal(slicecopy)
313		racereadrangepc(fromPtr, size, callerpc, pc)
314		racewriterangepc(toPtr, size, callerpc, pc)
315	}
316	if msanenabled {
317		msanread(fromPtr, size)
318		msanwrite(toPtr, size)
319	}
320	if asanenabled {
321		asanread(fromPtr, size)
322		asanwrite(toPtr, size)
323	}
324
325	if size == 1 { // common case worth about 2x to do here
326		// TODO: is this still worth it with new memmove impl?
327		*(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer
328	} else {
329		memmove(toPtr, fromPtr, size)
330	}
331	return n
332}
333