Skip to content

Commit a2f1c8e

Browse files
randall77ianlancetaylor
authored andcommitted
[release-branch.go1.11] reflect: use correct write barrier operations for method funcs
Fix the code to use write barriers on heap memory, and no write barriers on stack memory. These errors were discovered as part of fixing #27695. They may have something to do with that issue, but hard to be sure. The core cause is different, so this fix is a separate CL. Update #27867 Change-Id: Ib005f6b3308de340be83c3d07d049d5e316b1e3c Reviewed-on: https://go-review.googlesource.com/137438 Reviewed-by: Austin Clements <[email protected]> (cherry picked from commit e35a412) Reviewed-on: https://go-review.googlesource.com/138581 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 34e5a85 commit a2f1c8e

File tree

3 files changed

+41
-13
lines changed

3 files changed

+41
-13
lines changed

src/reflect/type.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3066,6 +3066,8 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
30663066
// space no matter how big they actually are.
30673067
if ifaceIndir(rcvr) || rcvr.pointers() {
30683068
ptrmap.append(1)
3069+
} else {
3070+
ptrmap.append(0)
30693071
}
30703072
offset += ptrSize
30713073
}

src/reflect/value.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -453,15 +453,14 @@ func (v Value) call(op string, in []Value) []Value {
453453

454454
var ret []Value
455455
if nout == 0 {
456-
// This is untyped because the frame is really a
457-
// stack, even though it's a heap object.
458-
memclrNoHeapPointers(args, frametype.size)
456+
typedmemclr(frametype, args)
459457
framePool.Put(args)
460458
} else {
461459
// Zero the now unused input area of args,
462460
// because the Values returned by this function contain pointers to the args object,
463461
// and will thus keep the args object alive indefinitely.
464-
memclrNoHeapPointers(args, retOffset)
462+
typedmemclrpartial(frametype, args, 0, retOffset)
463+
465464
// Wrap Values around return values in args.
466465
ret = make([]Value, nout)
467466
off = retOffset
@@ -472,6 +471,10 @@ func (v Value) call(op string, in []Value) []Value {
472471
if tv.Size() != 0 {
473472
fl := flagIndir | flag(tv.Kind())
474473
ret[i] = Value{tv.common(), add(args, off, "tv.Size() != 0"), fl}
474+
// Note: this does introduce false sharing between results -
475+
// if any result is live, they are all live.
476+
// (And the space for the args is live as well, but as we've
477+
// cleared that space it isn't as big a deal.)
475478
} else {
476479
// For zero-sized return value, args+off may point to the next object.
477480
// In this case, return the zero value instead.
@@ -660,6 +663,8 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
660663
}
661664

662665
// Call.
666+
// Call copies the arguments from args to the stack, calls fn,
667+
// and then copies the results back into args.
663668
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
664669

665670
// Copy return values. On amd64p32, the beginning of return values
@@ -673,16 +678,14 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
673678
if runtime.GOARCH == "amd64p32" {
674679
callerRetOffset = align(argSize-ptrSize, 8)
675680
}
676-
typedmemmovepartial(frametype,
677-
add(frame, callerRetOffset, "frametype.size > retOffset"),
681+
// This copies to the stack. Write barriers are not needed.
682+
memmove(add(frame, callerRetOffset, "frametype.size > retOffset"),
678683
add(args, retOffset, "frametype.size > retOffset"),
679-
retOffset,
680684
frametype.size-retOffset)
681685
}
682686

683-
// This is untyped because the frame is really a stack, even
684-
// though it's a heap object.
685-
memclrNoHeapPointers(args, frametype.size)
687+
// Put the args scratch space back in the pool.
688+
typedmemclr(frametype, args)
686689
framePool.Put(args)
687690

688691
// See the comment in callReflect.
@@ -2569,6 +2572,10 @@ func call(argtype *rtype, fn, arg unsafe.Pointer, n uint32, retoffset uint32)
25692572

25702573
func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer)
25712574

2575+
// memmove copies size bytes to dst from src. No write barriers are used.
2576+
//go:noescape
2577+
func memmove(dst, src unsafe.Pointer, size uintptr)
2578+
25722579
// typedmemmove copies a value of type t to dst from src.
25732580
//go:noescape
25742581
func typedmemmove(t *rtype, dst, src unsafe.Pointer)
@@ -2578,14 +2585,20 @@ func typedmemmove(t *rtype, dst, src unsafe.Pointer)
25782585
//go:noescape
25792586
func typedmemmovepartial(t *rtype, dst, src unsafe.Pointer, off, size uintptr)
25802587

2588+
// typedmemclr zeros the value at ptr of type t.
2589+
//go:noescape
2590+
func typedmemclr(t *rtype, ptr unsafe.Pointer)
2591+
2592+
// typedmemclrpartial is like typedmemclr but assumes that
2593+
// dst points off bytes into the value and only clears size bytes.
2594+
//go:noescape
2595+
func typedmemclrpartial(t *rtype, ptr unsafe.Pointer, off, size uintptr)
2596+
25812597
// typedslicecopy copies a slice of elemType values from src to dst,
25822598
// returning the number of elements copied.
25832599
//go:noescape
25842600
func typedslicecopy(elemType *rtype, dst, src sliceHeader) int
25852601

2586-
//go:noescape
2587-
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
2588-
25892602
// Dummy annotation marking that the value x escapes,
25902603
// for use in cases where the reflect code is so clever that
25912604
// the compiler cannot follow.

src/runtime/mbarrier.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,19 @@ func typedmemclr(typ *_type, ptr unsafe.Pointer) {
320320
memclrNoHeapPointers(ptr, typ.size)
321321
}
322322

323+
//go:linkname reflect_typedmemclr reflect.typedmemclr
324+
func reflect_typedmemclr(typ *_type, ptr unsafe.Pointer) {
325+
typedmemclr(typ, ptr)
326+
}
327+
328+
//go:linkname reflect_typedmemclrpartial reflect.typedmemclrpartial
329+
func reflect_typedmemclrpartial(typ *_type, ptr unsafe.Pointer, off, size uintptr) {
330+
if typ.kind&kindNoPointers == 0 {
331+
bulkBarrierPreWrite(uintptr(ptr), 0, size)
332+
}
333+
memclrNoHeapPointers(ptr, size)
334+
}
335+
323336
// memclrHasPointers clears n bytes of typed memory starting at ptr.
324337
// The caller must ensure that the type of the object at ptr has
325338
// pointers, usually by checking typ.kind&kindNoPointers. However, ptr

0 commit comments

Comments
 (0)