]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/runtime/mbitmap.go
cmd/compile,runtime: dedup writeBarrier needed
[gostls13.git] / src / runtime / mbitmap.go
index dc99ba768bb4c160a58640e559f66f7610e27536..2bcf454797fbc57b870e2bb4a2b3a889dcf3d631 100644 (file)
@@ -46,6 +46,27 @@ import (
        "unsafe"
 )
 
+// heapArenaPtrScalar contains the per-heapArena pointer/scalar metadata for the GC.
+type heapArenaPtrScalar struct {
+       // bitmap stores the pointer/scalar bitmap for the words in
+       // this arena. See mbitmap.go for a description.
+       // This array uses 1 bit per word of heap, or 1.6% of the heap size (for 64-bit).
+       bitmap [heapArenaBitmapWords]uintptr
+
+       // If the ith bit of noMorePtrs is true, then there are no more
+       // pointers for the object containing the word described by the
+       // high bit of bitmap[i].
+       // In that case, bitmap[i+1], ... must be zero until the start
+       // of the next object.
+       // We never operate on these entries using bit-parallel techniques,
+       // so it is ok if they are small. Also, they can't be bigger than
+       // uint16 because at that size a single noMorePtrs entry
+       // represents 8K of memory, the minimum size of a span. Any larger
+       // and we'd have to worry about concurrent updates.
+       // This array uses 1 bit per word of bitmap, or .024% of the heap size (for 64-bit).
+       noMorePtrs [heapArenaBitmapWords / 8]uint8
+}
+
 // addb returns the byte pointer p+n.
 //
 //go:nowritebarrier
@@ -117,8 +138,8 @@ func (s *mspan) allocBitsForIndex(allocBitIndex uintptr) markBits {
 // and negates them so that ctz (count trailing zeros) instructions
 // can be used. It then places these 8 bytes into the cached 64 bit
 // s.allocCache.
-func (s *mspan) refillAllocCache(whichByte uintptr) {
-       bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(whichByte)))
+func (s *mspan) refillAllocCache(whichByte uint16) {
+       bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(uintptr(whichByte))))
        aCache := uint64(0)
        aCache |= uint64(bytes[0])
        aCache |= uint64(bytes[1]) << (1 * 8)
@@ -135,7 +156,7 @@ func (s *mspan) refillAllocCache(whichByte uintptr) {
 // or after s.freeindex.
 // There are hardware instructions that can be used to make this
 // faster if profiling warrants it.
-func (s *mspan) nextFreeIndex() uintptr {
+func (s *mspan) nextFreeIndex() uint16 {
        sfreeindex := s.freeindex
        snelems := s.nelems
        if sfreeindex == snelems {
@@ -163,7 +184,7 @@ func (s *mspan) nextFreeIndex() uintptr {
                // nothing available in cached bits
                // grab the next 8 bytes and try again.
        }
-       result := sfreeindex + uintptr(bitIndex)
+       result := sfreeindex + uint16(bitIndex)
        if result >= snelems {
                s.freeindex = snelems
                return snelems
@@ -191,7 +212,7 @@ func (s *mspan) nextFreeIndex() uintptr {
 // been no preemption points since ensuring this (which could allow a
 // GC transition, which would allow the state to change).
 func (s *mspan) isFree(index uintptr) bool {
-       if index < s.freeindex {
+       if index < uintptr(s.freeIndexForScan) {
                return false
        }
        bytep, mask := s.allocBits.bitp(index)
@@ -202,6 +223,10 @@ func (s *mspan) isFree(index uintptr) bool {
 // n must be within [0, s.npages*_PageSize),
 // or may be exactly s.npages*_PageSize
 // if s.elemsize is from sizeclasses.go.
+//
+// nosplit, because it is called by objIndex, which is nosplit
+//
+//go:nosplit
 func (s *mspan) divideByElemSize(n uintptr) uintptr {
        const doubleCheck = false
 
@@ -215,6 +240,9 @@ func (s *mspan) divideByElemSize(n uintptr) uintptr {
        return q
 }
 
+// nosplit, because it is called by other nosplit code like findObject
+//
+//go:nosplit
 func (s *mspan) objIndex(p uintptr) uintptr {
        return s.divideByElemSize(p - s.base())
 }
@@ -526,16 +554,16 @@ func (h heapBits) nextFast() (heapBits, uintptr) {
 // The pointer bitmap is not maintained for allocations containing
 // no pointers at all; any caller of bulkBarrierPreWrite must first
 // make sure the underlying allocation contains pointers, usually
-// by checking typ.ptrdata.
+// by checking typ.PtrBytes.
 //
-// Callers must perform cgo checks if writeBarrier.cgo.
+// Callers must perform cgo checks if goexperiment.CgoCheck2.
 //
 //go:nosplit
 func bulkBarrierPreWrite(dst, src, size uintptr) {
        if (dst|src|size)&(goarch.PtrSize-1) != 0 {
                throw("bulkBarrierPreWrite: unaligned arguments")
        }
-       if !writeBarrier.needed {
+       if !writeBarrier.enabled {
                return
        }
        if s := spanOf(dst); s == nil {
@@ -573,9 +601,8 @@ func bulkBarrierPreWrite(dst, src, size uintptr) {
                                break
                        }
                        dstx := (*uintptr)(unsafe.Pointer(addr))
-                       if !buf.putFast(*dstx, 0) {
-                               wbBufFlush(nil, 0)
-                       }
+                       p := buf.get1()
+                       p[0] = *dstx
                }
        } else {
                for {
@@ -585,9 +612,9 @@ func bulkBarrierPreWrite(dst, src, size uintptr) {
                        }
                        dstx := (*uintptr)(unsafe.Pointer(addr))
                        srcx := (*uintptr)(unsafe.Pointer(src + (addr - dst)))
-                       if !buf.putFast(*dstx, *srcx) {
-                               wbBufFlush(nil, 0)
-                       }
+                       p := buf.get2()
+                       p[0] = *dstx
+                       p[1] = *srcx
                }
        }
 }
@@ -606,7 +633,7 @@ func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) {
        if (dst|src|size)&(goarch.PtrSize-1) != 0 {
                throw("bulkBarrierPreWrite: unaligned arguments")
        }
-       if !writeBarrier.needed {
+       if !writeBarrier.enabled {
                return
        }
        buf := &getg().m.p.ptr().wbBuf
@@ -617,9 +644,8 @@ func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) {
                        break
                }
                srcx := (*uintptr)(unsafe.Pointer(addr - dst + src))
-               if !buf.putFast(0, *srcx) {
-                       wbBufFlush(nil, 0)
-               }
+               p := buf.get1()
+               p[0] = *srcx
        }
 }
 
@@ -650,14 +676,13 @@ func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) {
                if *bits&mask != 0 {
                        dstx := (*uintptr)(unsafe.Pointer(dst + i))
                        if src == 0 {
-                               if !buf.putFast(*dstx, 0) {
-                                       wbBufFlush(nil, 0)
-                               }
+                               p := buf.get1()
+                               p[0] = *dstx
                        } else {
                                srcx := (*uintptr)(unsafe.Pointer(src + i))
-                               if !buf.putFast(*dstx, *srcx) {
-                                       wbBufFlush(nil, 0)
-                               }
+                               p := buf.get2()
+                               p[0] = *dstx
+                               p[1] = *srcx
                        }
                }
                mask <<= 1
@@ -678,28 +703,28 @@ func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) {
 // Must not be preempted because it typically runs right before memmove,
 // and the GC must observe them as an atomic action.
 //
-// Callers must perform cgo checks if writeBarrier.cgo.
+// Callers must perform cgo checks if goexperiment.CgoCheck2.
 //
 //go:nosplit
 func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) {
        if typ == nil {
                throw("runtime: typeBitsBulkBarrier without type")
        }
-       if typ.size != size {
-               println("runtime: typeBitsBulkBarrier with type ", typ.string(), " of size ", typ.size, " but memory size", size)
+       if typ.Size_ != size {
+               println("runtime: typeBitsBulkBarrier with type ", toRType(typ).string(), " of size ", typ.Size_, " but memory size", size)
                throw("runtime: invalid typeBitsBulkBarrier")
        }
-       if typ.kind&kindGCProg != 0 {
-               println("runtime: typeBitsBulkBarrier with type ", typ.string(), " with GC prog")
+       if typ.Kind_&kindGCProg != 0 {
+               println("runtime: typeBitsBulkBarrier with type ", toRType(typ).string(), " with GC prog")
                throw("runtime: invalid typeBitsBulkBarrier")
        }
-       if !writeBarrier.needed {
+       if !writeBarrier.enabled {
                return
        }
-       ptrmask := typ.gcdata
+       ptrmask := typ.GCData
        buf := &getg().m.p.ptr().wbBuf
        var bits uint32
-       for i := uintptr(0); i < typ.ptrdata; i += goarch.PtrSize {
+       for i := uintptr(0); i < typ.PtrBytes; i += goarch.PtrSize {
                if i&(goarch.PtrSize*8-1) == 0 {
                        bits = uint32(*ptrmask)
                        ptrmask = addb(ptrmask, 1)
@@ -709,9 +734,9 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) {
                if bits&1 != 0 {
                        dstx := (*uintptr)(unsafe.Pointer(dst + i))
                        srcx := (*uintptr)(unsafe.Pointer(src + i))
-                       if !buf.putFast(*dstx, *srcx) {
-                               wbBufFlush(nil, 0)
-                       }
+                       p := buf.get2()
+                       p[0] = *dstx
+                       p[1] = *srcx
                }
        }
 }
@@ -747,7 +772,7 @@ func (s *mspan) initHeapBits(forceClear bool) {
 // scanning the allocation bitmap.
 func (s *mspan) countAlloc() int {
        count := 0
-       bytes := divRoundUp(s.nelems, 8)
+       bytes := divRoundUp(uintptr(s.nelems), 8)
        // Iterate over each 8-byte chunk and count allocations
        // with an intrinsic. Note that newMarkBits guarantees that
        // gcmarkBits will be 8-byte aligned, so we don't have to
@@ -918,7 +943,7 @@ func readUintptr(p *byte) uintptr {
 
 // heapBitsSetType records that the new allocation [x, x+size)
 // holds in [x, x+dataSize) one or more values of type typ.
-// (The number of values is given by dataSize / typ.size.)
+// (The number of values is given by dataSize / typ.Size.)
 // If dataSize < size, the fragment [x+dataSize, x+size) is
 // recorded as non-pointer data.
 // It is known that the type has pointers somewhere;
@@ -942,8 +967,8 @@ func readUintptr(p *byte) uintptr {
 func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
        const doubleCheck = false // slow but helpful; enable to test modifications to this code
 
-       if doubleCheck && dataSize%typ.size != 0 {
-               throw("heapBitsSetType: dataSize not a multiple of typ.size")
+       if doubleCheck && dataSize%typ.Size_ != 0 {
+               throw("heapBitsSetType: dataSize not a multiple of typ.Size")
        }
 
        if goarch.PtrSize == 8 && size == goarch.PtrSize {
@@ -968,12 +993,12 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
        h := writeHeapBitsForAddr(x)
 
        // Handle GC program.
-       if typ.kind&kindGCProg != 0 {
+       if typ.Kind_&kindGCProg != 0 {
                // Expand the gc program into the storage we're going to use for the actual object.
                obj := (*uint8)(unsafe.Pointer(x))
-               n := runGCProg(addb(typ.gcdata, 4), obj)
+               n := runGCProg(addb(typ.GCData, 4), obj)
                // Use the expanded program to set the heap bits.
-               for i := uintptr(0); true; i += typ.size {
+               for i := uintptr(0); true; i += typ.Size_ {
                        // Copy expanded program to heap bitmap.
                        p := obj
                        j := n
@@ -984,12 +1009,12 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
                        }
                        h = h.write(uintptr(*p), j)
 
-                       if i+typ.size == dataSize {
+                       if i+typ.Size_ == dataSize {
                                break // no padding after last element
                        }
 
                        // Pad with zeros to the start of the next element.
-                       h = h.pad(typ.size - n*goarch.PtrSize)
+                       h = h.pad(typ.Size_ - n*goarch.PtrSize)
                }
 
                h.flush(x, size)
@@ -1001,16 +1026,16 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 
        // Note about sizes:
        //
-       // typ.size is the number of words in the object,
-       // and typ.ptrdata is the number of words in the prefix
+       // typ.Size is the number of words in the object,
+       // and typ.PtrBytes is the number of words in the prefix
        // of the object that contains pointers. That is, the final
-       // typ.size - typ.ptrdata words contain no pointers.
+       // typ.Size - typ.PtrBytes words contain no pointers.
        // This allows optimization of a common pattern where
        // an object has a small header followed by a large scalar
        // buffer. If we know the pointers are over, we don't have
        // to scan the buffer's heap bitmap at all.
        // The 1-bit ptrmasks are sized to contain only bits for
-       // the typ.ptrdata prefix, zero padded out to a full byte
+       // the typ.PtrBytes prefix, zero padded out to a full byte
        // of bitmap. If there is more room in the allocated object,
        // that space is pointerless. The noMorePtrs bitmap will prevent
        // scanning large pointerless tails of an object.
@@ -1019,13 +1044,13 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
        // objects with scalar tails, all but the last tail does have to
        // be initialized, because there is no way to say "skip forward".
 
-       ptrs := typ.ptrdata / goarch.PtrSize
-       if typ.size == dataSize { // Single element
+       ptrs := typ.PtrBytes / goarch.PtrSize
+       if typ.Size_ == dataSize { // Single element
                if ptrs <= ptrBits { // Single small element
-                       m := readUintptr(typ.gcdata)
+                       m := readUintptr(typ.GCData)
                        h = h.write(m, ptrs)
                } else { // Single large element
-                       p := typ.gcdata
+                       p := typ.GCData
                        for {
                                h = h.write(readUintptr(p), ptrBits)
                                p = addb(p, ptrBits/8)
@@ -1038,10 +1063,10 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
                        h = h.write(m, ptrs)
                }
        } else { // Repeated element
-               words := typ.size / goarch.PtrSize // total words, including scalar tail
-               if words <= ptrBits {              // Repeated small element
-                       n := dataSize / typ.size
-                       m := readUintptr(typ.gcdata)
+               words := typ.Size_ / goarch.PtrSize // total words, including scalar tail
+               if words <= ptrBits {               // Repeated small element
+                       n := dataSize / typ.Size_
+                       m := readUintptr(typ.GCData)
                        // Make larger unit to repeat
                        for words <= ptrBits/2 {
                                if n&1 != 0 {
@@ -1061,8 +1086,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
                        }
                        h = h.write(m, ptrs)
                } else { // Repeated large element
-                       for i := uintptr(0); true; i += typ.size {
-                               p := typ.gcdata
+                       for i := uintptr(0); true; i += typ.Size_ {
+                               p := typ.GCData
                                j := ptrs
                                for j > ptrBits {
                                        h = h.write(readUintptr(p), ptrBits)
@@ -1071,11 +1096,11 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
                                }
                                m := readUintptr(p)
                                h = h.write(m, j)
-                               if i+typ.size == dataSize {
+                               if i+typ.Size_ == dataSize {
                                        break // don't need the trailing nonptr bits on the last element.
                                }
                                // Pad with zeros to the start of the next element.
-                               h = h.pad(typ.size - typ.ptrdata)
+                               h = h.pad(typ.Size_ - typ.PtrBytes)
                        }
                }
        }
@@ -1087,10 +1112,10 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
                        // Compute the pointer bit we want at offset i.
                        want := false
                        if i < dataSize {
-                               off := i % typ.size
-                               if off < typ.ptrdata {
+                               off := i % typ.Size_
+                               if off < typ.PtrBytes {
                                        j := off / goarch.PtrSize
-                                       want = *addb(typ.gcdata, j/8)>>(j%8)&1 != 0
+                                       want = *addb(typ.GCData, j/8)>>(j%8)&1 != 0
                                }
                        }
                        if want {
@@ -1400,15 +1425,6 @@ func dumpGCProg(p *byte) {
 
 // Testing.
 
-func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool {
-       target := (*stkframe)(ctxt)
-       if frame.sp <= target.sp && target.sp < frame.varp {
-               *target = *frame
-               return false
-       }
-       return true
-}
-
 // reflect_gcbits returns the GC type info for x, for testing.
 // The result is the bitmap entries (0 or 1), one entry per byte.
 //
@@ -1429,7 +1445,7 @@ func getgcmask(ep any) (mask []byte) {
                // data
                if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
                        bitmap := datap.gcdatamask.bytedata
-                       n := (*ptrtype)(unsafe.Pointer(t)).elem.size
+                       n := (*ptrtype)(unsafe.Pointer(t)).Elem.Size_
                        mask = make([]byte, n/goarch.PtrSize)
                        for i := uintptr(0); i < n; i += goarch.PtrSize {
                                off := (uintptr(p) + i - datap.data) / goarch.PtrSize
@@ -1441,7 +1457,7 @@ func getgcmask(ep any) (mask []byte) {
                // bss
                if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss {
                        bitmap := datap.gcbssmask.bytedata
-                       n := (*ptrtype)(unsafe.Pointer(t)).elem.size
+                       n := (*ptrtype)(unsafe.Pointer(t)).Elem.Size_
                        mask = make([]byte, n/goarch.PtrSize)
                        for i := uintptr(0); i < n; i += goarch.PtrSize {
                                off := (uintptr(p) + i - datap.bss) / goarch.PtrSize
@@ -1475,19 +1491,24 @@ func getgcmask(ep any) (mask []byte) {
 
        // stack
        if gp := getg(); gp.m.curg.stack.lo <= uintptr(p) && uintptr(p) < gp.m.curg.stack.hi {
-               var frame stkframe
-               frame.sp = uintptr(p)
-               gentraceback(gp.m.curg.sched.pc, gp.m.curg.sched.sp, 0, gp.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
-               if frame.fn.valid() {
-                       locals, _, _ := frame.getStackMap(nil, false)
+               found := false
+               var u unwinder
+               for u.initAt(gp.m.curg.sched.pc, gp.m.curg.sched.sp, 0, gp.m.curg, 0); u.valid(); u.next() {
+                       if u.frame.sp <= uintptr(p) && uintptr(p) < u.frame.varp {
+                               found = true
+                               break
+                       }
+               }
+               if found {
+                       locals, _, _ := u.frame.getStackMap(false)
                        if locals.n == 0 {
                                return
                        }
                        size := uintptr(locals.n) * goarch.PtrSize
-                       n := (*ptrtype)(unsafe.Pointer(t)).elem.size
+                       n := (*ptrtype)(unsafe.Pointer(t)).Elem.Size_
                        mask = make([]byte, n/goarch.PtrSize)
                        for i := uintptr(0); i < n; i += goarch.PtrSize {
-                               off := (uintptr(p) + i - frame.varp + size) / goarch.PtrSize
+                               off := (uintptr(p) + i - u.frame.varp + size) / goarch.PtrSize
                                mask[i/goarch.PtrSize] = locals.ptrbit(off)
                        }
                }
@@ -1499,3 +1520,98 @@ func getgcmask(ep any) (mask []byte) {
        // must not have pointers
        return
 }
+
+// userArenaHeapBitsSetType is the equivalent of heapBitsSetType but for
+// non-slice-backing-store Go values allocated in a user arena chunk. It
+// sets up the heap bitmap for the value with type typ allocated at address ptr.
+// base is the base address of the arena chunk.
+func userArenaHeapBitsSetType(typ *_type, ptr unsafe.Pointer, base uintptr) {
+       h := writeHeapBitsForAddr(uintptr(ptr))
+
+       // Our last allocation might have ended right at a noMorePtrs mark,
+       // which we would not have erased. We need to erase that mark here,
+       // because we're going to start adding new heap bitmap bits.
+       // We only need to clear one mark, because below we make sure to
+       // pad out the bits with zeroes and only write one noMorePtrs bit
+       // for each new object.
+       // (This is only necessary at noMorePtrs boundaries, as noMorePtrs
+       // marks within an object allocated with newAt will be erased by
+       // the normal writeHeapBitsForAddr mechanism.)
+       //
+       // Note that we skip this if this is the first allocation in the
+       // arena because there's definitely no previous noMorePtrs mark
+       // (in fact, we *must* do this, because we're going to try to back
+       // up a pointer to fix this up).
+       if uintptr(ptr)%(8*goarch.PtrSize*goarch.PtrSize) == 0 && uintptr(ptr) != base {
+               // Back up one pointer and rewrite that pointer. That will
+               // cause the writeHeapBits implementation to clear the
+               // noMorePtrs bit we need to clear.
+               r := heapBitsForAddr(uintptr(ptr)-goarch.PtrSize, goarch.PtrSize)
+               _, p := r.next()
+               b := uintptr(0)
+               if p == uintptr(ptr)-goarch.PtrSize {
+                       b = 1
+               }
+               h = writeHeapBitsForAddr(uintptr(ptr) - goarch.PtrSize)
+               h = h.write(b, 1)
+       }
+
+       p := typ.GCData // start of 1-bit pointer mask (or GC program)
+       var gcProgBits uintptr
+       if typ.Kind_&kindGCProg != 0 {
+               // Expand gc program, using the object itself for storage.
+               gcProgBits = runGCProg(addb(p, 4), (*byte)(ptr))
+               p = (*byte)(ptr)
+       }
+       nb := typ.PtrBytes / goarch.PtrSize
+
+       for i := uintptr(0); i < nb; i += ptrBits {
+               k := nb - i
+               if k > ptrBits {
+                       k = ptrBits
+               }
+               h = h.write(readUintptr(addb(p, i/8)), k)
+       }
+       // Note: we call pad here to ensure we emit explicit 0 bits
+       // for the pointerless tail of the object. This ensures that
+       // there's only a single noMorePtrs mark for the next object
+       // to clear. We don't need to do this to clear stale noMorePtrs
+       // markers from previous uses because arena chunk pointer bitmaps
+       // are always fully cleared when reused.
+       h = h.pad(typ.Size_ - typ.PtrBytes)
+       h.flush(uintptr(ptr), typ.Size_)
+
+       if typ.Kind_&kindGCProg != 0 {
+               // Zero out temporary ptrmask buffer inside object.
+               memclrNoHeapPointers(ptr, (gcProgBits+7)/8)
+       }
+
+       // Double-check that the bitmap was written out correctly.
+       //
+       // Derived from heapBitsSetType.
+       const doubleCheck = false
+       if doubleCheck {
+               size := typ.Size_
+               x := uintptr(ptr)
+               h := heapBitsForAddr(x, size)
+               for i := uintptr(0); i < size; i += goarch.PtrSize {
+                       // Compute the pointer bit we want at offset i.
+                       want := false
+                       off := i % typ.Size_
+                       if off < typ.PtrBytes {
+                               j := off / goarch.PtrSize
+                               want = *addb(typ.GCData, j/8)>>(j%8)&1 != 0
+                       }
+                       if want {
+                               var addr uintptr
+                               h, addr = h.next()
+                               if addr != x+i {
+                                       throw("userArenaHeapBitsSetType: pointer entry not correct")
+                               }
+                       }
+               }
+               if _, addr := h.next(); addr != 0 {
+                       throw("userArenaHeapBitsSetType: extra pointer")
+               }
+       }
+}