From dffa7328356257772dc7c00fe73aeb315229b373 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 19 Jul 2014 21:36:26 +0000 Subject: reflect, runtime: Use libffi closures to implement reflect.MakeFunc. Keep using the existing 386 and amd64 code on those archs, since it is more efficient. From-SVN: r212853 --- libgo/go/reflect/all_test.go | 24 ------- libgo/go/reflect/makefunc.go | 95 +++++++++++++++++---------- libgo/go/reflect/makefunc_dummy.c | 11 ++-- libgo/go/reflect/makefunc_ffi.go | 88 +++++++++++++++++++++++++ libgo/go/reflect/makefunc_ffi_c.c | 135 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+), 63 deletions(-) create mode 100644 libgo/go/reflect/makefunc_ffi.go create mode 100644 libgo/go/reflect/makefunc_ffi_c.c (limited to 'libgo/go') diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 799bbea..f888d64 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -1502,12 +1502,6 @@ func TestCallWithStruct(t *testing.T) { } func TestMakeFunc(t *testing.T) { - switch runtime.GOARCH { - case "amd64", "386": - default: - t.Skip("MakeFunc not implemented for " + runtime.GOARCH) - } - f := dummy fv := MakeFunc(TypeOf(f), func(in []Value) []Value { return in }) ValueOf(&f).Elem().Set(fv) @@ -1526,12 +1520,6 @@ func TestMakeFunc(t *testing.T) { } func TestMakeFuncInterface(t *testing.T) { - switch runtime.GOARCH { - case "amd64", "386": - default: - t.Skip("MakeFunc not implemented for " + runtime.GOARCH) - } - fn := func(i int) int { return i } incr := func(in []Value) []Value { return []Value{ValueOf(int(in[0].Int() + 1))} @@ -1676,12 +1664,6 @@ func TestMethod(t *testing.T) { } func TestMethodValue(t *testing.T) { - switch runtime.GOARCH { - case "amd64", "386": - default: - t.Skip("reflect method values not implemented for " + runtime.GOARCH) - } - p := Point{3, 4} var i int64 @@ -1853,12 +1835,6 @@ type Tm4 struct { func (t4 Tm4) M(x int, b byte) (byte, int) { return b, x + 40 } func TestMethod5(t *testing.T) { - switch runtime.GOARCH { - case "amd64", "386": - default: - t.Skip("reflect method values not implemented for " + runtime.GOARCH) - } - CheckF := func(name string, f func(int, byte) (byte, int), inc int) { b, x := f(1000, 99) if b != 99 || x != 1000+inc { diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index a46e1d8..736ac36 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -22,6 +22,10 @@ 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 @@ -51,22 +55,29 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { panic("reflect: call of MakeFunc with non-Func type") } + 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: - panic("reflect.MakeFunc not implemented for " + runtime.GOARCH) + code, ffi = makeFuncFFI(ftyp, fn) } - t := typ.common() - ftyp := (*funcType)(unsafe.Pointer(t)) - - // 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)) - - impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn, method: -1} + impl := &makeFuncImpl{ + code: code, + typ: ftyp, + fn: fn, + method: -1, + ffi: ffi, + } return Value{t, unsafe.Pointer(&impl), flag(Func<>flagMethodShift) + ftyp := (*funcType)(unsafe.Pointer(t)) + method := int(v.flag) >> flagMethodShift + + 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, + func(in []Value) []Value { + m := rcvr.Method(method) + return m.Call(in) + }) + } + fv := &makeFuncImpl{ code: code, - typ: (*funcType)(unsafe.Pointer(t)), - method: int(v.flag) >> flagMethodShift, + typ: ftyp, + method: method, rcvr: rcvr, + ffi: ffi, } return Value{ft, unsafe.Pointer(&fv), v.flag&flagRO | flag(Func)<fn; + f (args, results); + + __go_makefunc_returning (); +} + +/* Allocate an FFI closure and arrange to call ffi_callback. */ + +struct ffi_ret +ffi (const struct __go_func_type *ftyp, FuncVal *callback) +{ + 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); +} + +#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) +{ + runtime_panicstring ("libgo built without FFI does not support " + "reflect.MakeFunc"); +} + +#endif -- cgit v1.1