mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 08:20:27 +00:00
Add support for struct return values.
This commit is contained in:
parent
9ecb5b4791
commit
a5def77bfe
21
ffitest/so.c
21
ffitest/so.c
@ -56,3 +56,24 @@ int intintint_fn(double x, intintint iii) {
|
|||||||
printf("double: %g\n", x);
|
printf("double: %g\n", x);
|
||||||
return iii.a + iii.b + iii.c;
|
return iii.a + iii.b + iii.c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intint return_struct(int i) {
|
||||||
|
intint ret;
|
||||||
|
ret.a = i;
|
||||||
|
ret.b = i * i;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int64_t a;
|
||||||
|
int64_t b;
|
||||||
|
int64_t c;
|
||||||
|
} big;
|
||||||
|
|
||||||
|
big struct_big(int i, double d) {
|
||||||
|
big ret;
|
||||||
|
ret.a = i;
|
||||||
|
ret.b = (int64_t) d;
|
||||||
|
ret.c = ret.a + ret.b + 1000;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -42,6 +42,11 @@
|
|||||||
[x ii]
|
[x ii]
|
||||||
(native-call intint-fn-pointer intint-fn-sig x ii))
|
(native-call intint-fn-pointer intint-fn-sig x ii))
|
||||||
|
|
||||||
|
(def return-struct-sig (native-signature :default [:int :int] :int))
|
||||||
|
(def return-struct-pointer (native-lookup module "return_struct"))
|
||||||
|
(defn return-struct-fn
|
||||||
|
[i]
|
||||||
|
(native-call return-struct-pointer return-struct-sig i))
|
||||||
|
|
||||||
(def intintint (native-struct :int :int :int))
|
(def intintint (native-struct :int :int :int))
|
||||||
(def intintint-fn-sig (native-signature :default :int :double intintint))
|
(def intintint-fn-sig (native-signature :default :int :double intintint))
|
||||||
@ -50,6 +55,13 @@
|
|||||||
[x iii]
|
[x iii]
|
||||||
(native-call intintint-fn-pointer intintint-fn-sig x iii))
|
(native-call intintint-fn-pointer intintint-fn-sig x iii))
|
||||||
|
|
||||||
|
(def big (native-struct :s64 :s64 :s64))
|
||||||
|
(def struct-big-fn-sig (native-signature :default big :int :double))
|
||||||
|
(def struct-big-fn-pointer (native-lookup module "struct_big"))
|
||||||
|
(defn struct-big-fn
|
||||||
|
[i d]
|
||||||
|
(native-call struct-big-fn-pointer struct-big-fn-sig i d))
|
||||||
|
|
||||||
#
|
#
|
||||||
# Call functions
|
# Call functions
|
||||||
#
|
#
|
||||||
@ -61,6 +73,8 @@
|
|||||||
(pp (float-fn 8 4 17))
|
(pp (float-fn 8 4 17))
|
||||||
(pp (intint-fn 123.456 [10 20]))
|
(pp (intint-fn 123.456 [10 20]))
|
||||||
(pp (intintint-fn 123.456 [10 20 30]))
|
(pp (intintint-fn 123.456 [10 20 30]))
|
||||||
|
(pp (return-struct-fn 42))
|
||||||
|
(pp (struct-big-fn 11 99.5))
|
||||||
|
|
||||||
(assert (= 60 (int-fn 10 20)))
|
(assert (= 60 (int-fn 10 20)))
|
||||||
(assert (= 42 (double-fn 1.5 2.5 3.5)))
|
(assert (= 42 (double-fn 1.5 2.5 3.5)))
|
||||||
|
111
src/core/ffi.c
111
src/core/ffi.c
@ -32,6 +32,8 @@
|
|||||||
#define alloca _alloca
|
#define alloca _alloca
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define JANET_FFI_MAX_RECUR 64
|
||||||
|
|
||||||
typedef struct JanetFFIType JanetFFIType;
|
typedef struct JanetFFIType JanetFFIType;
|
||||||
typedef struct JanetFFIStruct JanetFFIStruct;
|
typedef struct JanetFFIStruct JanetFFIStruct;
|
||||||
|
|
||||||
@ -124,7 +126,7 @@ typedef struct {
|
|||||||
uint32_t variant;
|
uint32_t variant;
|
||||||
uint32_t stack_count;
|
uint32_t stack_count;
|
||||||
JanetFFICallingConvention cc;
|
JanetFFICallingConvention cc;
|
||||||
JanetFFIType ret_type;
|
JanetFFIMapping ret;
|
||||||
JanetFFIMapping args[JANET_FFI_MAX_ARGS];
|
JanetFFIMapping args[JANET_FFI_MAX_ARGS];
|
||||||
} JanetFFISignature;
|
} JanetFFISignature;
|
||||||
|
|
||||||
@ -373,21 +375,20 @@ static Janet janet_ffi_read_one(const uint8_t *from, JanetFFIType type, int recu
|
|||||||
default:
|
default:
|
||||||
case JANET_FFI_TYPE_VOID:
|
case JANET_FFI_TYPE_VOID:
|
||||||
return janet_wrap_nil();
|
return janet_wrap_nil();
|
||||||
case JANET_FFI_TYPE_STRUCT:
|
case JANET_FFI_TYPE_STRUCT: {
|
||||||
{
|
JanetFFIStruct *st = type.st;
|
||||||
JanetFFIStruct *st = type.st;
|
Janet *tup = janet_tuple_begin(st->field_count);
|
||||||
Janet *tup = janet_tuple_begin(st->field_count);
|
size_t cursor = 0;
|
||||||
size_t cursor = 0;
|
for (uint32_t i = 0; i < st->field_count; i++) {
|
||||||
for (uint32_t i = 0; i < st->field_count; i++) {
|
JanetFFIType tp = st->fields[i];
|
||||||
JanetFFIType tp = st->fields[i];
|
size_t align = type_align(tp);
|
||||||
size_t align = type_align(tp);
|
size_t size = type_size(tp);
|
||||||
size_t size = type_size(tp);
|
cursor = ((cursor + align - 1) / align) * align;
|
||||||
cursor = ((cursor + align - 1) / align) * align;
|
tup[i] = janet_ffi_read_one(from + cursor, tp, recur - 1);
|
||||||
tup[i] = janet_ffi_read_one(from + cursor, tp, recur - 1);
|
cursor += size;
|
||||||
cursor += size;
|
|
||||||
}
|
|
||||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
|
||||||
}
|
}
|
||||||
|
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||||
|
}
|
||||||
case JANET_FFI_TYPE_DOUBLE:
|
case JANET_FFI_TYPE_DOUBLE:
|
||||||
return janet_wrap_number(((double *)(from))[0]);
|
return janet_wrap_number(((double *)(from))[0]);
|
||||||
case JANET_FFI_TYPE_FLOAT:
|
case JANET_FFI_TYPE_FLOAT:
|
||||||
@ -422,10 +423,6 @@ static Janet janet_ffi_read_one(const uint8_t *from, JanetFFIType type, int recu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_fp_type(JanetFFIType type) {
|
|
||||||
return type.prim == JANET_FFI_TYPE_DOUBLE || type.prim == JANET_FFI_TYPE_FLOAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JanetFFIMapping void_mapping(void) {
|
static JanetFFIMapping void_mapping(void) {
|
||||||
JanetFFIMapping m;
|
JanetFFIMapping m;
|
||||||
m.type = prim_type(JANET_FFI_TYPE_VOID);
|
m.type = prim_type(JANET_FFI_TYPE_VOID);
|
||||||
@ -493,6 +490,11 @@ JANET_CORE_FN(cfun_ffi_signature,
|
|||||||
uint32_t stack_count = 0;
|
uint32_t stack_count = 0;
|
||||||
JanetFFICallingConvention cc = decode_ffi_cc(janet_getkeyword(argv, 0));
|
JanetFFICallingConvention cc = decode_ffi_cc(janet_getkeyword(argv, 0));
|
||||||
JanetFFIType ret_type = decode_ffi_type(argv[1]);
|
JanetFFIType ret_type = decode_ffi_type(argv[1]);
|
||||||
|
JanetFFIMapping ret = {
|
||||||
|
ret_type,
|
||||||
|
JANET_SYSV64_NO_CLASS,
|
||||||
|
0
|
||||||
|
};
|
||||||
JanetFFIMapping mappings[JANET_FFI_MAX_ARGS];
|
JanetFFIMapping mappings[JANET_FFI_MAX_ARGS];
|
||||||
for (int i = 0; i < JANET_FFI_MAX_ARGS; i++) mappings[i] = void_mapping();
|
for (int i = 0; i < JANET_FFI_MAX_ARGS; i++) mappings[i] = void_mapping();
|
||||||
switch (cc) {
|
switch (cc) {
|
||||||
@ -500,19 +502,23 @@ JANET_CORE_FN(cfun_ffi_signature,
|
|||||||
janet_panicf("calling convention %v unsupported", argv[0]);
|
janet_panicf("calling convention %v unsupported", argv[0]);
|
||||||
break;
|
break;
|
||||||
case JANET_FFI_CC_SYSV_64: {
|
case JANET_FFI_CC_SYSV_64: {
|
||||||
if (is_fp_type(ret_type)) variant = 1;
|
JanetFFIWordSpec ret_spec = sysv64_classify(ret.type);
|
||||||
for (uint32_t i = 0; i < arg_count; i++) {
|
if (ret_spec == JANET_SYSV64_SSE) variant = 1;
|
||||||
mappings[i].type = decode_ffi_type(argv[i + 2]);
|
ret.spec = ret_spec;
|
||||||
mappings[i].offset = 0;
|
|
||||||
mappings[i].spec = sysv64_classify(mappings[i].type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Spill register overflow to memory */
|
/* Spill register overflow to memory */
|
||||||
uint32_t next_register = 0;
|
uint32_t next_register = 0;
|
||||||
uint32_t next_fp_register = 0;
|
uint32_t next_fp_register = 0;
|
||||||
const uint32_t max_regs = 6;
|
const uint32_t max_regs = 6;
|
||||||
const uint32_t max_fp_regs = 8;
|
const uint32_t max_fp_regs = 8;
|
||||||
|
if (ret_spec == JANET_SYSV64_MEMORY) {
|
||||||
|
/* First integer reg is pointer. */
|
||||||
|
next_register = 1;
|
||||||
|
}
|
||||||
for (uint32_t i = 0; i < arg_count; i++) {
|
for (uint32_t i = 0; i < arg_count; i++) {
|
||||||
|
mappings[i].type = decode_ffi_type(argv[i + 2]);
|
||||||
|
mappings[i].offset = 0;
|
||||||
|
mappings[i].spec = sysv64_classify(mappings[i].type);
|
||||||
size_t el_size = (type_size(mappings[i].type) + 7) / 8;
|
size_t el_size = (type_size(mappings[i].type) + 7) / 8;
|
||||||
switch (mappings[i].spec) {
|
switch (mappings[i].spec) {
|
||||||
default:
|
default:
|
||||||
@ -560,7 +566,7 @@ JANET_CORE_FN(cfun_ffi_signature,
|
|||||||
JanetFFISignature *abst = janet_abstract(&janet_signature_type, sizeof(JanetFFISignature));
|
JanetFFISignature *abst = janet_abstract(&janet_signature_type, sizeof(JanetFFISignature));
|
||||||
abst->frame_size = frame_size;
|
abst->frame_size = frame_size;
|
||||||
abst->cc = cc;
|
abst->cc = cc;
|
||||||
abst->ret_type = ret_type;
|
abst->ret = ret;
|
||||||
abst->arg_count = arg_count;
|
abst->arg_count = arg_count;
|
||||||
abst->variant = variant;
|
abst->variant = variant;
|
||||||
abst->stack_count = stack_count;
|
abst->stack_count = stack_count;
|
||||||
@ -569,15 +575,15 @@ JANET_CORE_FN(cfun_ffi_signature,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
|
static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
|
||||||
uint64_t ret, rethi;
|
uint64_t ret[2];
|
||||||
(void) rethi; /* at some point we will support more complex return types */
|
|
||||||
union {
|
|
||||||
float f;
|
|
||||||
double d;
|
|
||||||
uint64_t reg;
|
|
||||||
} u;
|
|
||||||
uint64_t regs[6];
|
uint64_t regs[6];
|
||||||
uint64_t fp_regs[8];
|
uint64_t fp_regs[8];
|
||||||
|
JanetFFIWordSpec ret_spec = signature->ret.spec;
|
||||||
|
void *ret_mem = ret;
|
||||||
|
if (ret_spec == JANET_SYSV64_MEMORY) {
|
||||||
|
ret_mem = alloca(type_size(signature->ret.type));
|
||||||
|
regs[0] = (uint64_t) ret_mem;
|
||||||
|
}
|
||||||
uint64_t *stack = alloca(sizeof(uint64_t) * signature->stack_count);
|
uint64_t *stack = alloca(sizeof(uint64_t) * signature->stack_count);
|
||||||
for (uint32_t i = 0; i < signature->arg_count; i++) {
|
for (uint32_t i = 0; i < signature->arg_count; i++) {
|
||||||
uint64_t *to;
|
uint64_t *to;
|
||||||
@ -596,7 +602,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
|
|||||||
to = stack + arg.offset;
|
to = stack + arg.offset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
janet_ffi_write_one(to, argv, n, arg.type, 64);
|
janet_ffi_write_one(to, argv, n, arg.type, JANET_FFI_MAX_RECUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* !!ACHTUNG!! */
|
/* !!ACHTUNG!! */
|
||||||
@ -616,7 +622,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
|
|||||||
"movq %14, %%xmm5\n\t" \
|
"movq %14, %%xmm5\n\t" \
|
||||||
"movq %15, %%xmm6\n\t" \
|
"movq %15, %%xmm6\n\t" \
|
||||||
"movq %16, %%xmm7\n\t"
|
"movq %16, %%xmm7\n\t"
|
||||||
#define FFI_ASM_OUTPUTS "=g" (ret), "=g" (rethi)
|
#define FFI_ASM_OUTPUTS "=g" (ret[0]), "=g" (ret[1])
|
||||||
#define FFI_ASM_INPUTS \
|
#define FFI_ASM_INPUTS \
|
||||||
"g"(function_pointer), \
|
"g"(function_pointer), \
|
||||||
"g"(regs[0]), \
|
"g"(regs[0]), \
|
||||||
@ -661,36 +667,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
|
|||||||
#undef FFI_ASM_OUTPUTS
|
#undef FFI_ASM_OUTPUTS
|
||||||
#undef FFI_ASM_INPUTS
|
#undef FFI_ASM_INPUTS
|
||||||
|
|
||||||
/* TODO - compound type returns */
|
return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
|
||||||
switch (signature->ret_type.prim) {
|
|
||||||
default:
|
|
||||||
janet_panic("nyi");
|
|
||||||
return janet_wrap_nil();
|
|
||||||
case JANET_FFI_TYPE_FLOAT:
|
|
||||||
u.reg = ret;
|
|
||||||
return janet_wrap_number(u.f);
|
|
||||||
case JANET_FFI_TYPE_DOUBLE:
|
|
||||||
u.reg = ret;
|
|
||||||
return janet_wrap_number(u.d);
|
|
||||||
case JANET_FFI_TYPE_VOID:
|
|
||||||
return janet_wrap_nil();
|
|
||||||
case JANET_FFI_TYPE_PTR:
|
|
||||||
return janet_wrap_pointer((void *) ret);
|
|
||||||
case JANET_FFI_TYPE_BOOL:
|
|
||||||
return janet_wrap_boolean(ret);
|
|
||||||
case JANET_FFI_TYPE_INT8:
|
|
||||||
case JANET_FFI_TYPE_INT16:
|
|
||||||
case JANET_FFI_TYPE_INT32:
|
|
||||||
return janet_wrap_integer((int32_t) ret);
|
|
||||||
case JANET_FFI_TYPE_INT64:
|
|
||||||
return janet_wrap_integer((int64_t) ret);
|
|
||||||
case JANET_FFI_TYPE_UINT8:
|
|
||||||
case JANET_FFI_TYPE_UINT16:
|
|
||||||
case JANET_FFI_TYPE_UINT32:
|
|
||||||
case JANET_FFI_TYPE_UINT64:
|
|
||||||
/* TODO - fix 64 bit unsigned return */
|
|
||||||
return janet_wrap_number(ret);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JANET_CORE_FN(cfun_ffi_call,
|
JANET_CORE_FN(cfun_ffi_call,
|
||||||
@ -720,7 +697,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
|
|||||||
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
|
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
|
||||||
janet_buffer_extra(buffer, el_size);
|
janet_buffer_extra(buffer, el_size);
|
||||||
memset(buffer->data, 0, el_size);
|
memset(buffer->data, 0, el_size);
|
||||||
janet_ffi_write_one(buffer->data, argv, 1, type, 64);
|
janet_ffi_write_one(buffer->data, argv, 1, type, JANET_FFI_MAX_RECUR);
|
||||||
buffer->count += el_size;
|
buffer->count += el_size;
|
||||||
return janet_wrap_buffer(buffer);
|
return janet_wrap_buffer(buffer);
|
||||||
}
|
}
|
||||||
@ -736,7 +713,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read,
|
|||||||
JanetByteView bytes = janet_getbytes(argv, 1);
|
JanetByteView bytes = janet_getbytes(argv, 1);
|
||||||
size_t offset = (size_t) janet_optnat(argv, argc, 2, 0);
|
size_t offset = (size_t) janet_optnat(argv, argc, 2, 0);
|
||||||
if ((size_t) bytes.len < offset + el_size) janet_panic("read out of range");
|
if ((size_t) bytes.len < offset + el_size) janet_panic("read out of range");
|
||||||
return janet_ffi_read_one(bytes.bytes, type, 64);
|
return janet_ffi_read_one(bytes.bytes, type, JANET_FFI_MAX_RECUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void janet_lib_ffi(JanetTable *env) {
|
void janet_lib_ffi(JanetTable *env) {
|
||||||
|
Loading…
Reference in New Issue
Block a user