This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
[PATCH 08/13] libgo: Use the new libffi interfaces for Go
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: libffi-discuss at sourceware dot org, gofrontend-dev at googlegroups dot com
- Date: Fri, 10 Oct 2014 13:42:48 -0700
- Subject: [PATCH 08/13] libgo: Use the new libffi interfaces for Go
- Authentication-results: sourceware.org; auth=none
- References: <1412973773-3942-1-git-send-email-rth at redhat dot com>
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