diff --git a/c/ffi/abi.go b/c/ffi/abi.go new file mode 100644 index 00000000..6009fa50 --- /dev/null +++ b/c/ffi/abi.go @@ -0,0 +1,7 @@ +//go:build ((freebsd || linux || darwin) && arm64) || (windows && (amd64 || arm64)) + +package ffi + +const ( + DefaultAbi Abi = 1 +) diff --git a/c/ffi/abi_amd64.go b/c/ffi/abi_amd64.go new file mode 100644 index 00000000..24567609 --- /dev/null +++ b/c/ffi/abi_amd64.go @@ -0,0 +1,7 @@ +//go:build freebsd || linux || darwin + +package ffi + +const ( + DefaultAbi Abi = 2 +) diff --git a/c/ffi/ffi.go b/c/ffi/ffi.go new file mode 100644 index 00000000..9b2266b9 --- /dev/null +++ b/c/ffi/ffi.go @@ -0,0 +1,98 @@ +package ffi + +import ( + "fmt" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/ffi/ffi" +) + +type Type = ffi.Type + +type Signature = ffi.Cif + +type Abi c.Uint + +type Error c.Uint + +const ( + OK Error = iota + BAD_TYPEDEF + BAD_ABI + BAD_ARGTYPE +) + +func (s Error) Error() string { + switch s { + case OK: + return "ok" + case BAD_TYPEDEF: + return "bad type def" + case BAD_ABI: + return "bad ABI" + case BAD_ARGTYPE: + return "bad argument type" + } + return fmt.Sprintf("invalid status: %v", int(s)) +} + +func NewSignature(ret *Type, args ...*Type) (*Signature, error) { + var cif ffi.Cif + var atype **Type + if len(args) > 0 { + atype = &args[0] + } + status := ffi.PrepCif(&cif, c.Uint(DefaultAbi), c.Uint(len(args)), ret, atype) + if status == 0 { + return &cif, nil + } + return nil, Error(status) +} + +func NewSignatureVar(ret *Type, fixed int, args ...*Type) (*Signature, error) { + var cif ffi.Cif + var atype **Type + if len(args) > 0 { + atype = &args[0] + } + status := ffi.PrepCifVar(&cif, c.Uint(DefaultAbi), c.Uint(fixed), c.Uint(len(args)), ret, atype) + if status == 0 { + return &cif, nil + } + return nil, Error(status) +} + +func Call(cif *Signature, fn unsafe.Pointer, ret unsafe.Pointer, args ...unsafe.Pointer) { + var avalues *unsafe.Pointer + if len(args) > 0 { + avalues = &args[0] + } + ffi.Call(cif, fn, ret, avalues) +} + +type Closure struct { + ptr unsafe.Pointer + Fn unsafe.Pointer +} + +func NewClosure() *Closure { + c := &Closure{} + c.ptr = ffi.ClosureAlloc(&c.Fn) + return c +} + +func (c *Closure) Free() { + if c != nil && c.ptr != nil { + ffi.ClosureFree(c.ptr) + c.ptr = nil + } +} + +func (c *Closure) Bind(cif *Signature, fn ffi.ClosureFunc, userdata unsafe.Pointer) error { + status := ffi.PreClosureLoc(c.ptr, cif, fn, userdata, c.Fn) + if status == 0 { + return nil + } + return Error(status) +} diff --git a/c/ffi/ffi/_wrap/libffi.c b/c/ffi/ffi/_wrap/libffi.c new file mode 100644 index 00000000..53fa1c70 --- /dev/null +++ b/c/ffi/ffi/_wrap/libffi.c @@ -0,0 +1,5 @@ +#include + +void *llog_ffi_closure_alloc(void **code) { + return ffi_closure_alloc(sizeof(ffi_closure), code); +} diff --git a/c/ffi/ffi/ffi.go b/c/ffi/ffi/ffi.go new file mode 100644 index 00000000..b500075c --- /dev/null +++ b/c/ffi/ffi/ffi.go @@ -0,0 +1,117 @@ +package ffi + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "link: $(pkg-config --libs libffi); -lffi" + LLGoFiles = "$(pkg-config --cflags libffi): _wrap/libffi.c" +) + +const ( + Void = iota + Int + Float + Double + LongDouble + Uint8 + Sint8 + Uint16 + Sint16 + Uint32 + Sint32 + Uint64 + Sint64 + Struct + Pointer + Complex +) + +type Type struct { + Size uintptr + Alignment uint16 + Type uint16 + Elements **Type +} + +/*typedef struct { + ffi_abi abi; + unsigned nargs; + ffi_type **arg_types; + ffi_type *rtype; + unsigned bytes; + unsigned flags; +#ifdef FFI_EXTRA_CIF_FIELDS + FFI_EXTRA_CIF_FIELDS; +#endif +} ffi_cif; +*/ + +type Cif struct { + Abi c.Uint + NArgs c.Uint + ArgTypes **Type + RType *Type + Bytes c.Uint + Flags c.Uint + //Extra c.Uint +} + +/* +ffi_status +ffi_prep_cif(ffi_cif *cif, + ffi_abi abi, + unsigned int nargs, + ffi_type *rtype, + ffi_type **atypes); +*/ +//go:linkname PrepCif C.ffi_prep_cif +func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint + +/* +ffi_status ffi_prep_cif_var(ffi_cif *cif, + ffi_abi abi, + unsigned int nfixedargs, + unsigned int ntotalargs, + ffi_type *rtype, + ffi_type **atypes); +*/ +//go:linkname PrepCifVar C.ffi_prep_cif_var +func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint + +/* +void ffi_call(ffi_cif *cif, + void (*fn)(void), + void *rvalue, + void **avalue); +*/ +//go:linkname Call C.ffi_call +func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer) + +// void *ffi_closure_alloc (size_t size, void **code); +// +//go:linkname ClosureAlloc C.llog_ffi_closure_alloc +func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer + +// void ffi_closure_free (void *); +// +//go:linkname ClosureFree C.ffi_closure_free +func ClosureFree(unsafe.Pointer) + +/* +ffi_status +ffi_prep_closure_loc (ffi_closure*, + ffi_cif *, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void *codeloc); +*/ + +//llgo:type C +type ClosureFunc func(cif *Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) + +//go:linkname PreClosureLoc C.ffi_prep_closure_loc +func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint diff --git a/c/ffi/type.go b/c/ffi/type.go new file mode 100644 index 00000000..2ef77657 --- /dev/null +++ b/c/ffi/type.go @@ -0,0 +1,124 @@ +package ffi + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/ffi/ffi" +) + +type BasicKind int + +const ( + Void BasicKind = iota // type is invalid + + // predeclared types + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + String + UnsafePointer + Interface + Slice + + // aliases + Byte = Uint8 + Rune = Int32 +) + +const ( + _64bit = 1 << (^uintptr(0) >> 63) / 2 + _Int = _64bit*ffi.Sint64 + (1-_64bit)*ffi.Sint32 + _Uint = _64bit*ffi.Uint64 + (1-_64bit)*ffi.Uint32 + _sizei = unsafe.Sizeof(0) + _aligni = uint16(unsafe.Alignof(0)) + _sizeci = unsafe.Sizeof(c.Int(0)) + _alignci = uint16(unsafe.Alignof(c.Int(0))) + _sizes = unsafe.Sizeof("") + _aligns = uint16(unsafe.Alignof("")) +) + +var ( + TypeVoid = &Type{1, 1, ffi.Void, nil} + TypeBool = &Type{1, 1, ffi.Uint8, nil} + TypeInt8 = &Type{1, 1, ffi.Sint8, nil} + TypeInt16 = &Type{2, 2, ffi.Sint16, nil} + TypeInt32 = &Type{4, 4, ffi.Sint32, nil} + TypeInt64 = &Type{8, 8, ffi.Sint64, nil} + TypeUint8 = &Type{1, 1, ffi.Uint8, nil} + TypeUint16 = &Type{2, 2, ffi.Uint16, nil} + TypeUint32 = &Type{4, 4, ffi.Uint32, nil} + TypeUint64 = &Type{8, 8, ffi.Uint64, nil} + TypeFloat32 = &Type{4, 4, ffi.Float, nil} + TypeFloat64 = &Type{8, 8, ffi.Double, nil} + TypeComplex64 = &Type{8, 4, ffi.Complex, &[]*Type{TypeFloat32, nil}[0]} + TypeComplex128 = &Type{16, 8, ffi.Complex, &[]*Type{TypeFloat64, nil}[0]} + TypeInt = &Type{_sizei, _aligni, _Int, nil} + TypeUint = &Type{_sizei, _aligni, _Uint, nil} + TypeUintptr = &Type{_sizei, _aligni, _Uint, nil} + TypePointer = &Type{_sizei, _aligni, ffi.Pointer, nil} + TypeString = StructOf(TypePointer, TypeInt) + TypeInterface = StructOf(TypePointer, TypePointer) + TypeSlice = StructOf(TypePointer, TypeInt, TypeInt) +) + +var Typ = []*Type{ + Void: TypeVoid, + Bool: TypeBool, + Int: TypeInt, + Int8: TypeInt8, + Int16: TypeInt16, + Int32: TypeInt32, + Int64: TypeInt64, + Uint: TypeUint, + Uint8: TypeUint8, + Uint16: TypeUint16, + Uint32: TypeUint32, + Uint64: TypeUint64, + Uintptr: TypeUintptr, + Float32: TypeFloat32, + Float64: TypeFloat64, + Complex64: TypeComplex64, + Complex128: TypeComplex128, + String: TypeString, + UnsafePointer: TypePointer, + Interface: TypeInterface, + Slice: TypeSlice, +} + +func ArrayOf(elem *Type, N int) *Type { + fs := make([]*Type, N+1) + for i := 0; i < N; i++ { + fs[i] = elem + } + return &Type{ + 0, + 0, + ffi.Struct, + &fs[0], + } +} + +func StructOf(fields ...*Type) *Type { + fs := make([]*Type, len(fields)+1) + copy(fs, fields) + return &Type{ + 0, + 0, + ffi.Struct, + &fs[0], + } +}