This does drop support for targets whose libffi hasn't been updated, but if we go this way that should be fairly easy to do. --- libgo/go/reflect/makefunc.go | 49 ++++++++++------------------ libgo/go/reflect/makefunc_ffi.go | 67 ++++++++++++-------------------------- libgo/go/reflect/makefunc_ffi_c.c | 68 +++++++++------------------------------ libgo/go/reflect/value.go | 3 ++ libgo/runtime/go-reflect-call.c | 10 ++---- 5 files changed, 59 insertions(+), 138 deletions(-)
diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index 977aacf..23c63a7 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -14,7 +14,11 @@ import ( // makeFuncImpl is the closure value implementing the function // returned by MakeFunc. type makeFuncImpl struct { - code uintptr + // These first three words are layed out like ffi_go_closure. + code uintptr + ffi_cif unsafe.Pointer + ffi_fun func(unsafe.Pointer, unsafe.Pointer) + typ *funcType fn func([]Value) []Value @@ -22,10 +26,6 @@ type makeFuncImpl struct { // method values. method int rcvr Value - - // When using FFI, hold onto the FFI closure for the garbage - // collector. - ffi *ffiData } // MakeFunc returns a new function of the given Type @@ -58,25 +58,18 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { t := typ.common() ftyp := (*funcType)(unsafe.Pointer(t)) - var code uintptr - var ffi *ffiData - switch runtime.GOARCH { - case "amd64", "386": - // Indirect Go func value (dummy) to obtain actual - // code address. (A Go func value is a pointer to a C - // function pointer. http://golang.org/s/go11func.) - dummy := makeFuncStub - code = **(**uintptr)(unsafe.Pointer(&dummy)) - default: - code, ffi = makeFuncFFI(ftyp, fn) - } - impl := &makeFuncImpl{ - code: code, typ: ftyp, fn: fn, method: -1, - ffi: ffi, + } + + switch runtime.GOARCH { + case "amd64", "386": + impl.code = makeFuncStubCode + default: + impl.fn = fn + makeFuncFFI(ftyp, impl) } return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} @@ -125,13 +118,9 @@ func makeMethodValue(op string, v Value) Value { switch runtime.GOARCH { case "amd64", "386": - // Indirect Go func value (dummy) to obtain actual - // code address. (A Go func value is a pointer to a C - // function pointer. http://golang.org/s/go11func.) - dummy := makeFuncStub - fv.code = **(**uintptr)(unsafe.Pointer(&dummy)) + fv.code = makeFuncStubCode; default: - fv.code, fv.ffi = makeFuncFFI(ftyp, fv.call) + makeFuncFFI(ftyp, fv) } return Value{ft, unsafe.Pointer(&fv), v.flag&flagRO | flag(Func)<<flagKindShift | flagIndir} @@ -160,13 +149,9 @@ func makeValueMethod(v Value) Value { switch runtime.GOARCH { case "amd64", "386": - // Indirect Go func value (dummy) to obtain actual - // code address. (A Go func value is a pointer to a C - // function pointer. http://golang.org/s/go11func.) - dummy := makeFuncStub - impl.code = **(**uintptr)(unsafe.Pointer(&dummy)) + impl.code = makeFuncStubCode default: - impl.code, impl.ffi = makeFuncFFI(ftyp, impl.call) + makeFuncFFI(ftyp, impl) } return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} diff --git a/libgo/go/reflect/makefunc_ffi.go b/libgo/go/reflect/makefunc_ffi.go index a13ef17..5c764e3 100644 --- a/libgo/go/reflect/makefunc_ffi.go +++ b/libgo/go/reflect/makefunc_ffi.go @@ -5,52 +5,27 @@ package reflect import ( - "runtime" "unsafe" ) -// The ffi function, written in C, allocates an FFI closure. It -// returns the code and data pointers. When the code pointer is -// called, it will call callback. CIF is an FFI data structure -// allocated as part of the closure, and is returned to ensure that -// the GC retains it. -func ffi(ftyp *funcType, callback func(unsafe.Pointer, unsafe.Pointer)) (code uintptr, data uintptr, cif unsafe.Pointer) - -// The ffiFree function, written in C, releases the FFI closure. -func ffiFree(uintptr) - -// An ffiData holds the information needed to preserve an FFI closure -// for the garbage collector. -type ffiData struct { - code uintptr - data uintptr - cif unsafe.Pointer - callback func(unsafe.Pointer, unsafe.Pointer) -} - -// The makeFuncFFI function uses libffi closures to implement -// reflect.MakeFunc. This is used for processors for which we don't -// have more efficient support. -func makeFuncFFI(ftyp *funcType, fn func(args []Value) (results []Value)) (uintptr, *ffiData) { - callback := func(params, results unsafe.Pointer) { - ffiCall(ftyp, fn, params, results) - } - - code, data, cif := ffi(ftyp, callback) - - c := &ffiData{code: code, data: data, cif: cif, callback: callback} - - runtime.SetFinalizer(c, - func(p *ffiData) { - ffiFree(p.data) - }) - - return code, c -} - -// ffiCall takes pointers to the parameters, calls the function, and -// stores the results back into memory. -func ffiCall(ftyp *funcType, fn func([]Value) []Value, params unsafe.Pointer, results unsafe.Pointer) { +// The makeFuncFFI function, written in C, fills in an FFI closure. +// It arranges for ffiCall to be invoked directly from FFI. +func makeFuncFFI(ftyp *funcType, impl *makeFuncImpl) + +// FFICallbackGo implements the Go side of the libffi callback. +// It is exported so that C code can call it. +// +// The call chain arriving here looks like +// some_go_caller +// ->some_ffi_internals +// ->ffi_callback (in C) +// ->FFICallbackGo +// +// The ffi_callback handles __go_makefunc_can_recover, and +// then passes off the data as received from ffi here. + +func FFICallbackGo(results unsafe.Pointer, params unsafe.Pointer, impl *makeFuncImpl) { + ftyp := impl.typ in := make([]Value, 0, len(ftyp.in)) ap := params for _, rt := range ftyp.in { @@ -61,18 +36,18 @@ func ffiCall(ftyp *funcType, fn func([]Value) []Value, params unsafe.Pointer, re ap = (unsafe.Pointer)(uintptr(ap) + ptrSize) } - out := fn(in) + out := impl.call(in) off := uintptr(0) for i, typ := range ftyp.out { v := out[i] if v.typ != typ { - panic("reflect: function created by MakeFunc using " + funcName(fn) + + panic("reflect: function created by MakeFunc using " + funcName(impl.fn) + " returned wrong type: have " + out[i].typ.String() + " for " + typ.String()) } if v.flag&flagRO != 0 { - panic("reflect: function created by MakeFunc using " + funcName(fn) + + panic("reflect: function created by MakeFunc using " + funcName(impl.fn) + " returned value obtained from unexported field") } diff --git a/libgo/go/reflect/makefunc_ffi_c.c b/libgo/go/reflect/makefunc_ffi_c.c index a3dfd4a..727ae81 100644 --- a/libgo/go/reflect/makefunc_ffi_c.c +++ b/libgo/go/reflect/makefunc_ffi_c.c @@ -10,7 +10,7 @@ #include "go-ffi.h" -#if FFI_CLOSURES +#if FFI_GO_CLOSURES #define USE_LIBFFI_CLOSURES #endif @@ -18,36 +18,28 @@ /* Declare C functions with the names used to call from Go. */ -struct ffi_ret { - void *code; - void *data; - void *cif; -}; - -struct ffi_ret ffi(const struct __go_func_type *ftyp, FuncVal *callback) - __asm__ (GOSYM_PREFIX "reflect.ffi"); - -void ffiFree(void *data) - __asm__ (GOSYM_PREFIX "reflect.ffiFree"); +void makeFuncFFI(const struct __go_func_type *ftyp, ffi_go_closure *impl) + __asm__ (GOSYM_PREFIX "reflect.makeFuncFFI"); #ifdef USE_LIBFFI_CLOSURES -/* The function that we pass to ffi_prep_closure_loc. This calls the - Go callback function (passed in user_data) with the pointer to the - arguments and the results area. */ +/* The function that we pass to ffi_prep_closure_loc. This calls the Go + function ffiCall with the pointer to the arguments, the results area, + and the closure structure. */ + +void FFICallbackGo(void *result, void **args, ffi_go_closure *closure) + __asm__ (GOSYM_PREFIX "reflect.FFICallbackGo"); static void ffi_callback (ffi_cif *, void *, void **, void *) __asm__ ("reflect.ffi_callback"); static void ffi_callback (ffi_cif* cif __attribute__ ((unused)), void *results, - void **args, void *user_data) + void **args, void *closure) { Location locs[8]; int n; int i; - FuncVal *fv; - void (*f) (void *, void *); /* This function is called from some series of FFI closure functions called by a Go function. We want to see whether the caller of @@ -69,10 +61,7 @@ ffi_callback (ffi_cif* cif __attribute__ ((unused)), void *results, if (i < n) __go_makefunc_ffi_can_recover (locs + i, n - i); - fv = (FuncVal *) user_data; - __go_set_closure (fv); - f = (void *) fv->fn; - f (args, results); + FFICallbackGo(results, args, closure); if (i < n) __go_makefunc_returning (); @@ -80,46 +69,21 @@ ffi_callback (ffi_cif* cif __attribute__ ((unused)), void *results, /* Allocate an FFI closure and arrange to call ffi_callback. */ -struct ffi_ret -ffi (const struct __go_func_type *ftyp, FuncVal *callback) +void +makeFuncFFI(const struct __go_func_type *ftyp, ffi_go_closure *impl) { ffi_cif *cif; - void *code; - void *data; - struct ffi_ret ret; cif = (ffi_cif *) __go_alloc (sizeof (ffi_cif)); __go_func_to_cif (ftyp, 0, 0, cif); - data = ffi_closure_alloc (sizeof (ffi_closure), &code); - if (data == NULL) - runtime_panicstring ("ffi_closure_alloc failed"); - if (ffi_prep_closure_loc (data, cif, ffi_callback, callback, code) - != FFI_OK) - runtime_panicstring ("ffi_prep_closure_loc failed"); - ret.code = code; - ret.data = data; - ret.cif = cif; - return ret; -} - -/* Free the FFI closure. */ -void -ffiFree (void *data) -{ - ffi_closure_free (data); + ffi_prep_go_closure(impl, cif, ffi_callback); } #else /* !defined(USE_LIBFFI_CLOSURES) */ -struct ffi_ret -ffi(const struct __go_func_type *ftyp, FuncVal *callback) -{ - runtime_panicstring ("libgo built without FFI does not support " - "reflect.MakeFunc"); -} - -void ffiFree(void *data) +void +makeFuncFFI(const struct __go_func_type *ftyp, ffi_go_closure *impl) { runtime_panicstring ("libgo built without FFI does not support " "reflect.MakeFunc"); diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index c390b8e..1e0b537 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -427,6 +427,9 @@ func (v Value) CallSlice(in []Value) []Value { var callGC bool // for testing; see TestCallMethodJump +// Indirect Go func value (dummy) to obtain actual +// code address. (A Go func value is a pointer to a C +// function pointer. http://golang.org/s/go11func.) var makeFuncStubFn = makeFuncStub var makeFuncStubCode = **(**uintptr)(unsafe.Pointer(&makeFuncStubFn)) diff --git a/libgo/runtime/go-reflect-call.c b/libgo/runtime/go-reflect-call.c index dfc703e..692c8cc 100644 --- a/libgo/runtime/go-reflect-call.c +++ b/libgo/runtime/go-reflect-call.c @@ -202,11 +202,7 @@ go_set_results (const struct __go_func_type *func, unsigned char *call_result, If IS_METHOD is true this is a call to a method expression. The first argument is the receiver. It is described in FUNC_TYPE, but - regardless of FUNC_TYPE, it is passed as a pointer. - - If neither IS_INTERFACE nor IS_METHOD is true then we are calling a - function indirectly, and we must pass a closure pointer via - __go_set_closure. The pointer to pass is simply FUNC_VAL. */ + regardless of FUNC_TYPE, it is passed as a pointer. */ void reflect_call (const struct __go_func_type *func_type, FuncVal *func_val, @@ -221,9 +217,7 @@ reflect_call (const struct __go_func_type *func_type, FuncVal *func_val, call_result = (unsigned char *) malloc (go_results_size (func_type)); - if (!is_interface && !is_method) - __go_set_closure (func_val); - ffi_call (&cif, func_val->fn, call_result, params); + ffi_call_go (&cif, func_val->fn, call_result, params, func_val); /* Some day we may need to free result values if RESULTS is NULL. */ -- 1.9.3