Source file
src/runtime/malloc_stubs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package runtime
22
23 import (
24 "internal/goarch"
25 "internal/goexperiment"
26 "internal/runtime/sys"
27 "unsafe"
28 )
29
30
31
32
33
34
35
36 const elemsize_ = 8
37 const sizeclass_ = 0
38 const noscanint_ = 0
39 const isNoScan_ = false
40 const size_ = 0
41 const isTiny_ = false
42 const isSlowPath_ = false
43
44 func malloc0(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
45 if doubleCheckMalloc {
46 if gcphase == _GCmarktermination {
47 throw("mallocgc called with gcphase == _GCmarktermination")
48 }
49 }
50
51
52 return unsafe.Pointer(&zerobase)
53 }
54
55 func mallocPanic(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
56 panic("not defined for sizeclass")
57 }
58
59 func mallocgcSlowPathStub(size uintptr, typ *_type, needzero bool, spc spanClass, elemsize uintptr) unsafe.Pointer {
60 return mallocStub(size, typ, needzero)
61 }
62
63
64
65 func mallocStub(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
66 if doubleCheckMalloc {
67 if gcphase == _GCmarktermination {
68 throw("mallocgc called with gcphase == _GCmarktermination")
69 }
70 }
71
72 var mp *m
73 if !isSlowPath_ {
74
75
76
77
78
79 mp = acquirem()
80
81
82 forceSlowPath := debug.malloc || gcBlackenEnabled != 0 || (goexperiment.RuntimeSecret && getg().secret > 0)
83
84 if forceSlowPath {
85 releasem(mp)
86 if isTiny_ {
87 return mallocgcTinySlowPath(size, typ, needzero)
88 } else {
89 const spc = spanClass(sizeclass_<<1) | spanClass(noscanint_)
90 const elemsize = uintptr(elemsize_)
91 return mallocgcSlowPathStub(size, typ, needzero, spc, elemsize)
92 }
93 }
94
95
96
97
98 lockRankMayQueueFinalizer()
99 } else {
100
101 if isTiny_ {
102
103
104
105
106
107
108 gp := getg()
109 if goexperiment.RuntimeSecret && gp.secret > 0 {
110 return mallocgcSmallNoScanSC2(size, typ, needzero)
111 }
112 }
113
114
115
116
117 lockRankMayQueueFinalizer()
118
119
120 if debug.malloc {
121 if x := preMallocgcDebug(size, typ); x != nil {
122 return x
123 }
124 }
125
126
127
128
129 if gcBlackenEnabled != 0 {
130 deductAssistCredit(size)
131 }
132
133 mp = acquirem()
134 }
135
136
137 return inlinedMalloc(mp, size, typ, needzero)
138 }
139
140 func postMallocgc(x unsafe.Pointer, typ *_type, size uintptr, elemsize uintptr) {
141 if isSlowPath_ && !isTiny_ {
142 gp := getg()
143 if goexperiment.RuntimeSecret && gp.secret > 0 {
144
145
146 addSecret(x, size)
147 }
148 }
149
150
151 if isSlowPath_ && gcBlackenEnabled != 0 && elemsize != 0 {
152 if assistG := getg().m.curg; assistG != nil {
153 assistG.gcAssistBytes -= int64(elemsize - size)
154 }
155 }
156
157
158 if isSlowPath_ && debug.malloc {
159 postMallocgcDebug(x, elemsize, typ)
160 }
161 }
162
163
164
165
166
167
168
169 func deductAssistCredit(size uintptr) {
170 assistG := getg()
171 if assistG.m.curg != nil {
172 assistG = assistG.m.curg
173 }
174 assistG.gcAssistBytes -= int64(size)
175 if assistG.gcAssistBytes < 0 {
176 gcAssistAlloc(assistG)
177 }
178 }
179
180
181
182
183
184
185
186
187
188
189 func inlinedMalloc(mp *m, size uintptr, typ *_type, needzero bool) unsafe.Pointer {
190 return unsafe.Pointer(uintptr(0))
191 }
192
193 func doubleCheckSmallScanNoHeader(size uintptr, typ *_type, mp *m) {
194 if mp.mallocing != 0 {
195 throw("malloc deadlock")
196 }
197 if mp.gsignal == getg() {
198 throw("malloc during signal")
199 }
200 if typ == nil || !typ.Pointers() {
201 throw("noscan allocated in scan-only path")
202 }
203 if !heapBitsInSpan(size) {
204 throw("heap bits in not in span for non-header-only path")
205 }
206 }
207
208
209
210 func smallStub(mp *m, size uintptr, typ *_type, needzero bool) unsafe.Pointer {
211 const sizeclass = sizeclass_
212 const elemsize = elemsize_
213
214
215 if doubleCheckMalloc {
216 if isNoScan_ {
217 doubleCheckSmallNoScan(typ, mp)
218 }
219 if !isNoScan_ {
220 doubleCheckSmallScanNoHeader(size, typ, mp)
221 }
222 }
223 mp.mallocing = 1
224
225 checkGCTrigger := false
226 c := getMCache(mp)
227 const spc = spanClass(sizeclass<<1) | spanClass(noscanint_)
228 span := c.alloc[spc]
229
230 var v gclinkptr
231 var x unsafe.Pointer
232 if isNoScan_ {
233
234 if runtimeFreegcEnabled && c.hasReusableNoscan(spc) {
235
236 x = mallocgcSmallNoscanReuse(c, span, spc, elemsize, needzero)
237 mp.mallocing = 0
238 releasem(mp)
239 if isSlowPath_ {
240
241 goto post
242 } else {
243 return x
244 }
245 }
246 }
247
248
249
250 {
251 v = nextFreeFastStub(span, elemsize)
252 if v == 0 {
253 v, span, checkGCTrigger = c.nextFree(spc)
254 }
255 x = unsafe.Pointer(v)
256 }
257 if isNoScan_ {
258 if needzero && span.needzero != 0 {
259 memclrNoHeapPointers(x, elemsize)
260 }
261 }
262 if !isNoScan_ {
263 if span.needzero != 0 {
264 memclrNoHeapPointers(x, elemsize)
265 }
266 if goarch.PtrSize == 8 && elemsize == 8 {
267
268
269 c.scanAlloc += 8
270 } else {
271 dataSize := size
272 x := uintptr(x)
273 scanSize := heapSetTypeNoHeaderStub(x, dataSize, typ, span)
274 c.scanAlloc += scanSize
275 }
276 }
277
278
279
280
281
282
283
284 publicationBarrier()
285
286 if isSlowPath_ && writeBarrier.enabled {
287
288
289
290
291 gcmarknewobject(span, uintptr(x))
292 } else {
293
294
295
296
297
298
299
300
301 span.freeIndexForScan = span.freeindex
302 }
303
304
305
306
307
308
309
310
311
312
313 c.nextSample -= int64(elemsize)
314 if c.nextSample < 0 || MemProfileRate != c.memProfRate {
315 profilealloc(mp, x, elemsize)
316 }
317 mp.mallocing = 0
318 releasem(mp)
319
320 if checkGCTrigger {
321 if t := (gcTrigger{kind: gcTriggerHeap}); t.test() {
322 gcStart(t)
323 }
324 }
325
326 post:
327 if isSlowPath_ {
328 postMallocgc(x, typ, size, elemsize)
329 }
330
331 return x
332 }
333
334 func doubleCheckSmallNoScan(typ *_type, mp *m) {
335 if mp.mallocing != 0 {
336 throw("malloc deadlock")
337 }
338 if mp.gsignal == getg() {
339 throw("malloc during signal")
340 }
341 if typ != nil && typ.Pointers() {
342 throw("expected noscan type for noscan alloc")
343 }
344 }
345
346 func doubleCheckTiny(size uintptr, typ *_type, mp *m) {
347 if mp.mallocing != 0 {
348 throw("malloc deadlock")
349 }
350 if mp.gsignal == getg() {
351 throw("malloc during signal")
352 }
353 if typ != nil && typ.Pointers() {
354 throw("expected noscan for tiny alloc")
355 }
356 }
357
358
359
360 func tinyStub(mp *m, size uintptr, typ *_type, needzero bool) unsafe.Pointer {
361 const elemsize = elemsize_
362
363
364 if doubleCheckMalloc {
365 doubleCheckTiny(size, typ, mp)
366 }
367 mp.mallocing = 1
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398 c := getMCache(mp)
399 off := c.tinyoffset
400
401 if size&7 == 0 {
402 off = alignUp(off, 8)
403 } else if goarch.PtrSize == 4 && size == 12 {
404
405
406
407
408
409
410 off = alignUp(off, 8)
411 } else if size&3 == 0 {
412 off = alignUp(off, 4)
413 } else if size&1 == 0 {
414 off = alignUp(off, 2)
415 }
416 if off+size <= maxTinySize && c.tiny != 0 {
417
418 x := unsafe.Pointer(c.tiny + off)
419 c.tinyoffset = off + size
420 c.tinyAllocs++
421 mp.mallocing = 0
422 releasem(mp)
423 const elemsize = 0
424 postMallocgc(x, typ, size, elemsize)
425 return x
426 }
427
428 checkGCTrigger := false
429 span := c.alloc[tinySpanClass]
430 v := nextFreeFastTiny(span)
431 if v == 0 {
432 v, span, checkGCTrigger = c.nextFree(tinySpanClass)
433 }
434 x := unsafe.Pointer(v)
435 (*[2]uint64)(x)[0] = 0
436 (*[2]uint64)(x)[1] = 0
437
438
439 if !raceenabled && (size < c.tinyoffset || c.tiny == 0) {
440
441 c.tiny = uintptr(x)
442 c.tinyoffset = size
443 }
444
445
446
447
448
449
450
451 publicationBarrier()
452
453 if isSlowPath_ && writeBarrier.enabled {
454
455
456
457
458 gcmarknewobject(span, uintptr(x))
459 } else {
460
461
462
463
464
465
466
467
468 span.freeIndexForScan = span.freeindex
469 }
470
471
472
473
474
475
476
477
478
479
480 c.nextSample -= int64(elemsize)
481 if c.nextSample < 0 || MemProfileRate != c.memProfRate {
482 profilealloc(mp, x, elemsize)
483 }
484 mp.mallocing = 0
485 releasem(mp)
486
487 if checkGCTrigger {
488 if t := (gcTrigger{kind: gcTriggerHeap}); t.test() {
489 gcStart(t)
490 }
491 }
492 postMallocgc(x, typ, size, elemsize)
493
494 return x
495 }
496
497
498
499 func nextFreeFastTiny(span *mspan) gclinkptr {
500 const nbytes = 8192
501 const nelems = uint16((nbytes - unsafe.Sizeof(spanInlineMarkBits{})) / elemsize_)
502 var nextFreeFastResult gclinkptr
503 if span.allocCache != 0 {
504 theBit := sys.TrailingZeros64(span.allocCache)
505 result := span.freeindex + uint16(theBit)
506 if result < nelems {
507 freeidx := result + 1
508 if !(freeidx%64 == 0 && freeidx != nelems) {
509 span.allocCache >>= uint(theBit + 1)
510 span.freeindex = freeidx
511 span.allocCount++
512 nextFreeFastResult = gclinkptr(uintptr(result)*elemsize_ + span.base())
513 }
514 }
515 }
516 return nextFreeFastResult
517 }
518
519 func nextFreeFastStub(span *mspan, elemsize uintptr) gclinkptr {
520 var nextFreeFastResult gclinkptr
521 if span.allocCache != 0 {
522 theBit := sys.TrailingZeros64(span.allocCache)
523 result := span.freeindex + uint16(theBit)
524 if result < span.nelems {
525 freeidx := result + 1
526 if !(freeidx%64 == 0 && freeidx != span.nelems) {
527 span.allocCache >>= uint(theBit + 1)
528 span.freeindex = freeidx
529 span.allocCount++
530 nextFreeFastResult = gclinkptr(uintptr(result)*elemsize + span.base())
531 }
532 }
533 }
534 return nextFreeFastResult
535 }
536
537 func heapSetTypeNoHeaderStub(x, dataSize uintptr, typ *_type, span *mspan) uintptr {
538 if doubleCheckHeapSetType && (!heapBitsInSpan(dataSize) || !heapBitsInSpan(elemsize_)) {
539 throw("tried to write heap bits, but no heap bits in span")
540 }
541 scanSize := writeHeapBitsSmallStub(span, x, dataSize, typ)
542 if doubleCheckHeapSetType {
543 doubleCheckHeapType(x, dataSize, typ, nil, span)
544 }
545 return scanSize
546 }
547
548
549
550
551
552
553
554
555 func writeHeapBitsSmallStub(span *mspan, x, dataSize uintptr, typ *_type) uintptr {
556
557 src0 := readUintptr(getGCMask(typ))
558
559 const elemsize = elemsize_
560
561
562 var scanSize uintptr
563 src := src0
564 if typ.Size_ == goarch.PtrSize {
565 src = (1 << (dataSize / goarch.PtrSize)) - 1
566
567 scanSize = dataSize
568 } else {
569
570
571
572 if doubleCheckHeapSetType && !asanenabled && dataSize%typ.Size_ != 0 {
573 throw("runtime: (*mspan).writeHeapBitsSmall: dataSize is not a multiple of typ.Size_")
574 }
575 scanSize = typ.PtrBytes
576 for i := typ.Size_; i < dataSize; i += typ.Size_ {
577 src |= src0 << (i / goarch.PtrSize)
578 scanSize += typ.Size_
579 }
580 }
581
582
583
584 dstBase, _ := spanHeapBitsRange(span.base(), pageSize, elemsize)
585 dst := unsafe.Pointer(dstBase)
586 o := (x - span.base()) / goarch.PtrSize
587 i := o / ptrBits
588 j := o % ptrBits
589 var bits uintptr = elemsize / goarch.PtrSize
590
591
592
593
594
595
596
597
598 var bitsIsPowerOfTwo = bits&(bits-1) == 0
599 if bits > ptrBits || (!bitsIsPowerOfTwo && j+bits > ptrBits) {
600
601 bits0 := ptrBits - j
602 bits1 := bits - bits0
603 dst0 := (*uintptr)(add(dst, (i+0)*goarch.PtrSize))
604 dst1 := (*uintptr)(add(dst, (i+1)*goarch.PtrSize))
605 *dst0 = (*dst0)&(^uintptr(0)>>bits0) | (src << j)
606 *dst1 = (*dst1)&^((1<<bits1)-1) | (src >> bits0)
607 } else {
608
609 dst := (*uintptr)(add(dst, i*goarch.PtrSize))
610 *dst = (*dst)&^(((1<<(min(bits, ptrBits)))-1)<<j) | (src << j)
611 }
612
613 const doubleCheck = false
614 if doubleCheck {
615 writeHeapBitsDoubleCheck(span, x, dataSize, src, src0, i, j, bits, typ)
616 }
617 return scanSize
618 }
619
620 func writeHeapBitsDoubleCheck(span *mspan, x, dataSize, src, src0, i, j, bits uintptr, typ *_type) {
621 srcRead := span.heapBitsSmallForAddr(x)
622 if srcRead != src {
623 print("runtime: x=", hex(x), " i=", i, " j=", j, " bits=", bits, "\n")
624 print("runtime: dataSize=", dataSize, " typ.Size_=", typ.Size_, " typ.PtrBytes=", typ.PtrBytes, "\n")
625 print("runtime: src0=", hex(src0), " src=", hex(src), " srcRead=", hex(srcRead), "\n")
626 throw("bad pointer bits written for small object")
627 }
628 }
629
View as plain text