]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/runtime/malloc.go
[dev.garbage] Merge remote-tracking branch 'origin/master' into HEAD
[gostls13.git] / src / runtime / malloc.go
index 081d1419cb67082d90e957ddeb8e628c5529a21b..6fe46566033143a0a85196885f62b7e969845e0a 100644 (file)
@@ -94,6 +94,9 @@ const (
        pageShift = _PageShift
        pageSize  = _PageSize
        pageMask  = _PageMask
+       // By construction, single page spans of the smallest object class
+       // have the most objects per span.
+       maxObjsPerSpan = pageSize / 8
 
        mSpanInUse = _MSpanInUse
 
@@ -484,6 +487,71 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer {
 // base address for all 0-byte allocations
 var zerobase uintptr
 
+// nextFreeFast returns the next free object if one is quickly available.
+// Otherwise it returns 0.
+func (c *mcache) nextFreeFast(sizeclass int8) gclinkptr {
+       s := c.alloc[sizeclass]
+       ctzIndex := uint8(s.allocCache & 0xff)
+       if ctzIndex != 0 {
+               theBit := uint64(ctzVals[ctzIndex])
+               freeidx := s.freeindex // help the pre ssa compiler out here with cse.
+               result := freeidx + uintptr(theBit)
+               if result < s.nelems {
+                       s.allocCache >>= (theBit + 1)
+                       freeidx = result + 1
+                       if freeidx%64 == 0 && freeidx != s.nelems {
+                               // We just incremented s.freeindex so it isn't 0
+                               // so we are moving to the next aCache.
+                               whichByte := freeidx / 8
+                               s.refillAllocCache(whichByte)
+                       }
+                       s.freeindex = freeidx
+                       v := gclinkptr(result*s.elemsize + s.base())
+                       s.allocCount++
+                       return v
+               }
+       }
+       return 0
+}
+
+// nextFree returns the next free object from the cached span if one is available.
+// Otherwise it refills the cache with a span with an available object and
+// returns that object along with a flag indicating that this was a heavy
+// weight allocation. If it is a heavy weight allocation the caller must
+// determine whether a new GC cycle needs to be started or if the GC is active
+// whether this goroutine needs to assist the GC.
+func (c *mcache) nextFree(sizeclass int8) (v gclinkptr, shouldhelpgc bool) {
+       s := c.alloc[sizeclass]
+       shouldhelpgc = false
+       freeIndex := s.nextFreeIndex()
+       if freeIndex == s.nelems {
+               // The span is full.
+               if uintptr(s.allocCount) != s.nelems {
+                       println("runtime: s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
+                       throw("s.allocCount != s.nelems && freeIndex == s.nelems")
+               }
+               systemstack(func() {
+                       c.refill(int32(sizeclass))
+               })
+               shouldhelpgc = true
+               s = c.alloc[sizeclass]
+
+               freeIndex = s.nextFreeIndex()
+       }
+
+       if freeIndex >= s.nelems {
+               throw("freeIndex is not valid")
+       }
+
+       v = gclinkptr(freeIndex*s.elemsize + s.base())
+       s.allocCount++
+       if uintptr(s.allocCount) > s.nelems {
+               println("s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
+               throw("s.allocCount > s.nelems")
+       }
+       return
+}
+
 // Allocate an object of size bytes.
 // Small objects are allocated from the per-P cache's free lists.
 // Large objects (> 32 kB) are allocated straight from the heap.
@@ -538,7 +606,6 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
        shouldhelpgc := false
        dataSize := size
        c := gomcache()
-       var s *mspan
        var x unsafe.Pointer
        noscan := typ == nil || typ.kind&kindNoPointers != 0
        if size <= maxSmallSize {
@@ -591,20 +658,11 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
                                return x
                        }
                        // Allocate a new maxTinySize block.
-                       s = c.alloc[tinySizeClass]
-                       v := s.freelist
-                       if v.ptr() == nil {
-                               systemstack(func() {
-                                       c.refill(tinySizeClass)
-                               })
-                               shouldhelpgc = true
-                               s = c.alloc[tinySizeClass]
-                               v = s.freelist
+                       var v gclinkptr
+                       v = c.nextFreeFast(tinySizeClass)
+                       if v == 0 {
+                               v, shouldhelpgc = c.nextFree(tinySizeClass)
                        }
-                       s.freelist = v.ptr().next
-                       s.ref++
-                       // prefetchnta offers best performance, see change list message.
-                       prefetchnta(uintptr(v.ptr().next))
                        x = unsafe.Pointer(v)
                        (*[2]uint64)(x)[0] = 0
                        (*[2]uint64)(x)[1] = 0
@@ -623,26 +681,15 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
                                sizeclass = size_to_class128[(size-1024+127)>>7]
                        }
                        size = uintptr(class_to_size[sizeclass])
-                       s = c.alloc[sizeclass]
-                       v := s.freelist
-                       if v.ptr() == nil {
-                               systemstack(func() {
-                                       c.refill(int32(sizeclass))
-                               })
-                               shouldhelpgc = true
-                               s = c.alloc[sizeclass]
-                               v = s.freelist
+                       var v gclinkptr
+                       v = c.nextFreeFast(sizeclass)
+                       if v == 0 {
+                               v, shouldhelpgc = c.nextFree(sizeclass)
                        }
-                       s.freelist = v.ptr().next
-                       s.ref++
-                       // prefetchnta offers best performance, see change list message.
-                       prefetchnta(uintptr(v.ptr().next))
                        x = unsafe.Pointer(v)
                        if needzero {
-                               v.ptr().next = 0
-                               if size > 2*sys.PtrSize && ((*[2]uintptr)(x))[1] != 0 {
-                                       memclr(unsafe.Pointer(v), size)
-                               }
+                               memclr(unsafe.Pointer(v), size)
+                               // TODO:(rlh) Only clear if object is not known to be zeroed.
                        }
                }
        } else {
@@ -651,13 +698,14 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
                systemstack(func() {
                        s = largeAlloc(size, needzero)
                })
-               x = unsafe.Pointer(uintptr(s.start << pageShift))
+               s.freeindex = 1
+               x = unsafe.Pointer(s.base())
                size = s.elemsize
        }
 
        var scanSize uintptr
        if noscan {
-               // All objects are pre-marked as noscan. Nothing to do.
+               heapBitsSetTypeNoScan(uintptr(x), size)
        } else {
                // If allocating a defer+arg block, now that we've picked a malloc size
                // large enough to hold everything, cut the "asked for" size down to
@@ -698,9 +746,27 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
                gcmarknewobject(uintptr(x), size, scanSize)
        }
 
+       // The object x is about to be reused but tracefree and msanfree
+       // need to be informed.
+       // TODO:(rlh) It is quite possible that this object is being allocated
+       // out of a fresh span and that there is no preceding call to
+       // tracealloc with this object. If this is an issue then initialization
+       // of the fresh span needs to leave some crumbs around that can be used to
+       // avoid these calls. Furthermore these crumbs a likely the same as
+       // those needed to determine if the object needs to be zeroed.
+       // In the case of msanfree it does not make sense to call msanfree
+       // followed by msanmalloc. msanfree indicates that the bytes are not
+       // initialized but msanmalloc is about to indicate that they are.
+       // It makes no difference whether msanmalloc has been called on these
+       // bytes or not.
+       if debug.allocfreetrace != 0 {
+               tracefree(unsafe.Pointer(x), size)
+       }
+
        if raceenabled {
                racemalloc(x, size)
        }
+
        if msanenabled {
                msanmalloc(x, size)
        }
@@ -755,8 +821,8 @@ func largeAlloc(size uintptr, needzero bool) *mspan {
        if s == nil {
                throw("out of memory")
        }
-       s.limit = uintptr(s.start)<<_PageShift + size
-       heapBitsForSpan(s.base()).initSpan(s.layout())
+       s.limit = s.base() + size
+       heapBitsForSpan(s.base()).initSpan(s)
        return s
 }