reflect: add precise GC info for Call argument frame.

Give proper types to the argument/return areas
allocated for reflect calls.  Avoid use of iword to
manipulate receivers, which may or may not be pointers.

Update #6490

R=rsc
CC=golang-codereviews
https://golang.org/cl/52110044
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 30b5e2a..916e219 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -473,11 +473,14 @@
 	// Get function pointer, type.
 	t := v.typ
 	var (
-		fn   unsafe.Pointer
-		rcvr iword
+		fn       unsafe.Pointer
+		rcvr     Value
+		rcvrtype *rtype
 	)
 	if v.flag&flagMethod != 0 {
-		t, fn, rcvr = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
+		rcvrtype = t
+		rcvr = v
+		t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
 	} else if v.flag&flagIndir != 0 {
 		fn = *(*unsafe.Pointer)(v.ptr)
 	} else {
@@ -556,23 +559,26 @@
 	}
 	nout := t.NumOut()
 
-	// Compute arg size & allocate.
-	// This computation is 5g/6g/8g-dependent
-	// and probably wrong for gccgo, but so
-	// is most of this function.
-	size, _, _, _ := frameSize(t, v.flag&flagMethod != 0)
+	// If the target is methodValueCall, do its work here: add the receiver
+	// argument and call the real target directly.
+	// We need to do this here because otherwise we have a situation where
+	// reflect.callXX calls methodValueCall, neither of which knows the
+	// layout of the args.  That's bad for precise gc & stack copying.
+	y := (*methodValue)(fn)
+	if y.fn == methodValueCallCode {
+		rcvr = y.rcvr
+		rcvrtype = rcvr.typ
+		t, fn = methodReceiver("call", rcvr, y.method)
+	}
 
-	// Copy into args.
-	//
-	// TODO(rsc): This will need to be updated for any new garbage collector.
-	// For now make everything look like a pointer by allocating
-	// a []unsafe.Pointer.
-	args := make([]unsafe.Pointer, size/ptrSize)
-	ptr := unsafe.Pointer(&args[0])
+	// Compute frame type, allocate a chunk of memory for frame
+	frametype := funcLayout(t, rcvrtype)
+	args := unsafe_New(frametype)
 	off := uintptr(0)
-	if v.flag&flagMethod != 0 {
-		// Hard-wired first argument.
-		*(*iword)(ptr) = rcvr
+
+	// Copy inputs into args.
+	if rcvrtype != nil {
+		storeRcvr(rcvr, args)
 		off = ptrSize
 	}
 	for i, v := range in {
@@ -581,7 +587,7 @@
 		a := uintptr(targ.align)
 		off = (off + a - 1) &^ (a - 1)
 		n := targ.size
-		addr := unsafe.Pointer(uintptr(ptr) + off)
+		addr := unsafe.Pointer(uintptr(args) + off)
 		v = v.assignTo("reflect.Value.Call", targ, (*interface{})(addr))
 		if v.flag&flagIndir != 0 {
 			memmove(addr, v.ptr, n)
@@ -594,35 +600,17 @@
 	}
 	off = (off + ptrSize - 1) &^ (ptrSize - 1)
 
-	// If the target is methodValueCall, do its work here: add the receiver
-	// argument and call the real target directly.
-	// We need to do this here because otherwise we have a situation where
-	// reflect.callXX calls methodValueCall, neither of which knows the
-	// layout of the args.  That's bad for precise gc & stack copying.
-	y := (*methodValue)(fn)
-	if y.fn == methodValueCallCode {
-		_, fn, rcvr = methodReceiver("call", y.rcvr, y.method)
-		args = append(args, unsafe.Pointer(nil))
-		copy(args[1:], args)
-		args[0] = unsafe.Pointer(rcvr)
-		ptr = unsafe.Pointer(&args[0])
-		off += ptrSize
-		size += ptrSize
-	}
-
 	// Call.
-	call(fn, ptr, uint32(size))
+	call(fn, args, uint32(frametype.size))
 
 	// Copy return values out of args.
-	//
-	// TODO(rsc): revisit like above.
 	ret := make([]Value, nout)
 	for i := 0; i < nout; i++ {
 		tv := t.Out(i)
 		a := uintptr(tv.Align())
 		off = (off + a - 1) &^ (a - 1)
 		fl := flagIndir | flag(tv.Kind())<<flagKindShift
-		ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(ptr) + off), 0, fl}
+		ret[i] = Value{tv.common(), unsafe.Pointer(uintptr(args) + off), 0, fl}
 		off += tv.Size()
 	}
 
@@ -710,7 +698,9 @@
 // described by v. The Value v may or may not have the
 // flagMethod bit set, so the kind cached in v.flag should
 // not be used.
-func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Pointer, rcvr iword) {
+// The return value t gives the method type signature (without the receiver).
+// The return value fn is a pointer to the method code.
+func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Pointer) {
 	i := methodIndex
 	if v.typ.Kind() == Interface {
 		tt := (*interfaceType)(unsafe.Pointer(v.typ))
@@ -721,13 +711,12 @@
 		if m.pkgPath != nil {
 			panic("reflect: " + op + " of unexported method")
 		}
-		t = m.typ
 		iface := (*nonEmptyInterface)(v.ptr)
 		if iface.itab == nil {
 			panic("reflect: " + op + " of method on nil interface value")
 		}
 		fn = unsafe.Pointer(&iface.itab.fun[i])
-		rcvr = iface.word
+		t = m.typ
 	} else {
 		ut := v.typ.uncommon()
 		if ut == nil || i < 0 || i >= len(ut.methods) {
@@ -739,58 +728,41 @@
 		}
 		fn = unsafe.Pointer(&m.ifn)
 		t = m.mtyp
-		rcvr = v.iword()
 	}
 	return
 }
 
+// v is a method receiver.  Store at p the word which is used to
+// encode that receiver at the start of the argument list.
+// Reflect uses the "interface" calling convention for
+// methods, which always uses one word to record the receiver.
+func storeRcvr(v Value, p unsafe.Pointer) {
+	t := v.typ
+	if t.Kind() == Interface {
+		// the interface data word becomes the receiver word
+		iface := (*nonEmptyInterface)(v.ptr)
+		*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
+	} else if v.flag&flagIndir != 0 {
+		if t.size > ptrSize {
+			*(*unsafe.Pointer)(p) = v.ptr
+		} else if t.pointers() {
+			*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
+		} else {
+			*(*uintptr)(p) = loadScalar(v.ptr, t.size)
+		}
+	} else if t.pointers() {
+		*(*unsafe.Pointer)(p) = v.ptr
+	} else {
+		*(*uintptr)(p) = v.scalar
+	}
+}
+
 // align returns the result of rounding x up to a multiple of n.
 // n must be a power of two.
 func align(x, n uintptr) uintptr {
 	return (x + n - 1) &^ (n - 1)
 }
 
-// frameSize returns the sizes of the argument and result frame
-// for a function of the given type. The rcvr bool specifies whether
-// a one-word receiver should be included in the total.
-func frameSize(t *rtype, rcvr bool) (total, in, outOffset, out uintptr) {
-	if rcvr {
-		// extra word for receiver interface word
-		total += ptrSize
-	}
-
-	nin := t.NumIn()
-	in = -total
-	for i := 0; i < nin; i++ {
-		tv := t.In(i)
-		total = align(total, uintptr(tv.Align()))
-		total += tv.Size()
-	}
-	in += total
-	total = align(total, ptrSize)
-	nout := t.NumOut()
-	outOffset = total
-	out = -total
-	for i := 0; i < nout; i++ {
-		tv := t.Out(i)
-		total = align(total, uintptr(tv.Align()))
-		total += tv.Size()
-	}
-	out += total
-
-	// total must be > 0 in order for &args[0] to be valid.
-	// the argument copying is going to round it up to
-	// a multiple of ptrSize anyway, so make it ptrSize to begin with.
-	if total < ptrSize {
-		total = ptrSize
-	}
-
-	// round to pointer
-	total = align(total, ptrSize)
-
-	return
-}
-
 // callMethod is the call implementation used by a function returned
 // by makeMethodValue (used by v.Method(i).Interface()).
 // It is a streamlined version of the usual reflect call: the caller has
@@ -803,24 +775,23 @@
 // so that the linker can make it work correctly for panic and recover.
 // The gc compilers know to do that for the name "reflect.callMethod".
 func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
-	t, fn, rcvr := methodReceiver("call", ctxt.rcvr, ctxt.method)
-	total, in, outOffset, out := frameSize(t, true)
+	rcvr := ctxt.rcvr
+	rcvrtype := rcvr.typ
+	t, fn := methodReceiver("call", rcvr, ctxt.method)
+	frametype := funcLayout(t, rcvrtype)
 
-	// Copy into args.
-	//
-	// TODO(rsc): This will need to be updated for any new garbage collector.
-	// For now make everything look like a pointer by allocating
-	// a []unsafe.Pointer.
-	args := make([]unsafe.Pointer, total/ptrSize)
-	args[0] = unsafe.Pointer(rcvr)
-	base := unsafe.Pointer(&args[0])
-	memmove(unsafe.Pointer(uintptr(base)+ptrSize), frame, in)
+	// Make a new frame that is one word bigger so we can store the receiver.
+	args := unsafe_New(frametype)
+
+	// Copy in receiver and rest of args.
+	storeRcvr(rcvr, args)
+	memmove(unsafe.Pointer(uintptr(args)+ptrSize), frame, frametype.size-ptrSize)
 
 	// Call.
-	call(fn, unsafe.Pointer(&args[0]), uint32(total))
+	call(fn, args, uint32(frametype.size))
 
 	// Copy return values.
-	memmove(unsafe.Pointer(uintptr(frame)+outOffset-ptrSize), unsafe.Pointer(uintptr(base)+outOffset), out)
+	memmove(frame, unsafe.Pointer(uintptr(args)+ptrSize), frametype.size-ptrSize)
 }
 
 // funcName returns the name of f, for use in error messages.