diff options
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r-- | libgo/go/reflect/all_test.go | 24 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc.go | 95 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc_dummy.c | 11 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc_ffi.go | 88 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc_ffi_c.c | 135 |
5 files changed, 290 insertions, 63 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 799bbea4a8a..f888d648c19 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 a46e1d86497..736ac36ade7 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<<flagKindShift) | flagIndir} } @@ -90,12 +101,6 @@ func makeMethodValue(op string, v Value) Value { panic("reflect: internal error: invalid use of makeMethodValue") } - switch runtime.GOARCH { - case "amd64", "386": - default: - panic("reflect.makeMethodValue not implemented for " + runtime.GOARCH) - } - // Ignoring the flagMethod bit, v describes the receiver, not the method type. fl := v.flag & (flagRO | flagAddr | flagIndir) fl |= flag(v.typ.Kind()) << flagKindShift @@ -104,22 +109,37 @@ func makeMethodValue(op string, v Value) Value { // v.Type returns the actual type of the method value. ft := v.Type().(*rtype) - // 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)) - // Cause panic if method is not appropriate. // The panic would still happen during the call if we omit this, // but we want Interface() and other operations to fail early. _, t, _ := methodReceiver(op, rcvr, int(v.flag)>>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)<<flagKindShift | flagIndir} @@ -137,26 +157,31 @@ func makeValueMethod(v Value) Value { panic("reflect: call of makeValueMethod with non-MethodFn") } + 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.makeValueMethod not implemented for " + runtime.GOARCH) + code, ffi = makeFuncFFI(ftyp, + func(in []Value) []Value { + return v.Call(in) + }) } - 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, method: -2, rcvr: v, + ffi: ffi, } return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} diff --git a/libgo/go/reflect/makefunc_dummy.c b/libgo/go/reflect/makefunc_dummy.c index aba48df3eb8..8eff0c1dfac 100644 --- a/libgo/go/reflect/makefunc_dummy.c +++ b/libgo/go/reflect/makefunc_dummy.c @@ -2,11 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !amd64 +#include "runtime.h" -// Dummy function for processors without makefunc support. +/* Dummy function for processors that implement MakeFunc using FFI + rather than having builtin support. */ -void makeFuncStub () __asm__ ("reflect.makeFuncStub"); -void makeFuncStub () +void makeFuncStub (void) __asm__ ("reflect.makeFuncStub"); + +void makeFuncStub (void) { + runtime_throw ("impossible call to makeFuncStub"); } diff --git a/libgo/go/reflect/makefunc_ffi.go b/libgo/go/reflect/makefunc_ffi.go new file mode 100644 index 00000000000..a13ef179f5d --- /dev/null +++ b/libgo/go/reflect/makefunc_ffi.go @@ -0,0 +1,88 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +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) { + in := make([]Value, 0, len(ftyp.in)) + ap := params + for _, rt := range ftyp.in { + p := unsafe_New(rt) + memmove(p, *(*unsafe.Pointer)(ap), rt.size) + v := Value{rt, p, flag(rt.Kind()<<flagKindShift) | flagIndir} + in = append(in, v) + ap = (unsafe.Pointer)(uintptr(ap) + ptrSize) + } + + out := fn(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) + + " 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) + + " returned value obtained from unexported field") + } + + off = align(off, uintptr(typ.fieldAlign)) + addr := unsafe.Pointer(uintptr(results) + off) + if v.flag&flagIndir == 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { + *(*unsafe.Pointer)(addr) = v.ptr + } else { + memmove(addr, v.ptr, typ.size) + } + off += typ.size + } +} diff --git a/libgo/go/reflect/makefunc_ffi_c.c b/libgo/go/reflect/makefunc_ffi_c.c new file mode 100644 index 00000000000..fba269dcb58 --- /dev/null +++ b/libgo/go/reflect/makefunc_ffi_c.c @@ -0,0 +1,135 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "go-type.h" +#include "go-panic.h" + +#ifdef USE_LIBFFI + +#include "go-ffi.h" + +#if FFI_CLOSURES +#define USE_LIBFFI_CLOSURES +#endif + +#endif /* defined(USE_LIBFFI) */ + +/* 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"); + +#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. */ + +static void +ffi_callback (ffi_cif* cif __attribute__ ((unused)), void *results, + void **args, void *user_data) +{ + Location locs[6]; + int n; + int i; + const void *pc; + 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 pass the PC of the Go + function to makefunc_can_recover. Look up the stack for a + function that is definitely not an FFI function. */ + n = runtime_callers (1, &locs[0], sizeof locs / sizeof locs[0], true); + for (i = 0; i < n; i++) + { + const byte *name; + + if (locs[i].function.len == 0) + continue; + if (locs[i].function.len < 4) + break; + name = locs[i].function.str; + if (*name == '_') + { + if (locs[i].function.len < 5) + break; + ++name; + } + if (name[0] != 'f' || name[1] != 'f' || name[2] != 'i' || name[3] != '_') + break; + } + if (i < n) + pc = (const void *) locs[i].pc; + else + pc = __builtin_return_address (0); + + __go_makefunc_can_recover (pc); + + fv = (FuncVal *) user_data; + __go_set_closure (fv); + f = (void *) fv->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 |