reflect: correct alignment of call arguments on amd64p32.

Changes adapted from original CL 15680044.

LGTM=iant
R=rsc, iant, dave
CC=golang-codereviews
https://golang.org/cl/76150044
This commit is contained in:
Rémy Oudompheng 2014-03-20 22:22:07 +01:00
parent ceb346685d
commit ea7d801130
2 changed files with 42 additions and 15 deletions

View file

@ -1814,9 +1814,15 @@ type layoutKey struct {
rcvr *rtype // receiver type, or nil if none
}
type layoutType struct {
t *rtype
argSize uintptr // size of arguments
retOffset uintptr // offset of return values.
}
var layoutCache struct {
sync.RWMutex
m map[layoutKey]*rtype
m map[layoutKey]layoutType
}
// funcLayout computes a struct type representing the layout of the
@ -1825,21 +1831,21 @@ var layoutCache struct {
// The returned type exists only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in
// the name for possible debugging use.
func funcLayout(t *rtype, rcvr *rtype) *rtype {
func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr) {
if t.Kind() != Func {
panic("reflect: funcSignature of non-func type")
}
k := layoutKey{t, rcvr}
layoutCache.RLock()
if x := layoutCache.m[k]; x != nil {
if x := layoutCache.m[k]; x.t != nil {
layoutCache.RUnlock()
return x
return x.t, x.argSize, x.retOffset
}
layoutCache.RUnlock()
layoutCache.Lock()
if x := layoutCache.m[k]; x != nil {
if x := layoutCache.m[k]; x.t != nil {
layoutCache.Unlock()
return x
return x.t, x.argSize, x.retOffset
}
tt := (*funcType)(unsafe.Pointer(t))
@ -1868,7 +1874,12 @@ func funcLayout(t *rtype, rcvr *rtype) *rtype {
}
offset += arg.size
}
argSize = offset
if runtime.GOARCH == "amd64p32" {
offset = align(offset, 8)
}
offset = align(offset, ptrSize)
retOffset = offset
for _, res := range tt.out {
offset = align(offset, uintptr(res.align))
if res.pointers() {
@ -1893,9 +1904,13 @@ func funcLayout(t *rtype, rcvr *rtype) *rtype {
// cache result for future callers
if layoutCache.m == nil {
layoutCache.m = make(map[layoutKey]*rtype)
layoutCache.m = make(map[layoutKey]layoutType)
}
layoutCache.m[k] = layoutType{
t: x,
argSize: argSize,
retOffset: retOffset,
}
layoutCache.m[k] = x
layoutCache.Unlock()
return x
return x, argSize, retOffset
}

View file

@ -532,7 +532,7 @@ func (v Value) call(op string, in []Value) []Value {
}
// Compute frame type, allocate a chunk of memory for frame
frametype := funcLayout(t, rcvrtype)
frametype, _, retOffset := funcLayout(t, rcvrtype)
args := unsafe_New(frametype)
off := uintptr(0)
@ -558,13 +558,13 @@ func (v Value) call(op string, in []Value) []Value {
}
off += n
}
off = (off + ptrSize - 1) &^ (ptrSize - 1)
// Call.
call(fn, args, uint32(frametype.size))
// Copy return values out of args.
ret := make([]Value, nout)
off = retOffset
for i := 0; i < nout; i++ {
tv := t.Out(i)
a := uintptr(tv.Align())
@ -628,6 +628,9 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
// Copy results back into argument frame.
if len(ftyp.out) > 0 {
off += -off & (ptrSize - 1)
if runtime.GOARCH == "amd64p32" {
off = align(off, 8)
}
for i, arg := range ftyp.out {
typ := arg
v := out[i]
@ -738,20 +741,29 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
rcvr := ctxt.rcvr
rcvrtype := rcvr.typ
t, fn := methodReceiver("call", rcvr, ctxt.method)
frametype := funcLayout(t, rcvrtype)
frametype, argSize, retOffset := funcLayout(t, rcvrtype)
// 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)
memmove(unsafe.Pointer(uintptr(args)+ptrSize), frame, argSize-ptrSize)
// Call.
call(fn, args, uint32(frametype.size))
// Copy return values.
memmove(frame, unsafe.Pointer(uintptr(args)+ptrSize), frametype.size-ptrSize)
// Copy return values. On amd64p32, the beginning of return values
// is 64-bit aligned, so the caller's frame layout (which doesn't have
// a receiver) is different from the layout of the fn call, which has
// a receiver.
// Ignore any changes to args and just copy return values.
callerRetOffset := retOffset - ptrSize
if runtime.GOARCH == "amd64p32" {
callerRetOffset = align(argSize-ptrSize, 8)
}
memmove(unsafe.Pointer(uintptr(frame)+callerRetOffset),
unsafe.Pointer(uintptr(args)+retOffset), frametype.size-retOffset)
}
// funcName returns the name of f, for use in error messages.