mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 09:33:02 +00:00 
			
		
		
		
	Add support for struct return values.
This commit is contained in:
		
							
								
								
									
										21
									
								
								ffitest/so.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								ffitest/so.c
									
									
									
									
									
								
							@@ -56,3 +56,24 @@ int intintint_fn(double x, intintint iii) {
 | 
			
		||||
    printf("double: %g\n", x);
 | 
			
		||||
    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]
 | 
			
		||||
  (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-fn-sig (native-signature :default :int :double intintint))
 | 
			
		||||
@@ -50,6 +55,13 @@
 | 
			
		||||
  [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
 | 
			
		||||
#
 | 
			
		||||
@@ -61,6 +73,8 @@
 | 
			
		||||
(pp (float-fn 8 4 17))
 | 
			
		||||
(pp (intint-fn 123.456 [10 20]))
 | 
			
		||||
(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 (= 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
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define JANET_FFI_MAX_RECUR 64
 | 
			
		||||
 | 
			
		||||
typedef struct JanetFFIType JanetFFIType;
 | 
			
		||||
typedef struct JanetFFIStruct JanetFFIStruct;
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +126,7 @@ typedef struct {
 | 
			
		||||
    uint32_t variant;
 | 
			
		||||
    uint32_t stack_count;
 | 
			
		||||
    JanetFFICallingConvention cc;
 | 
			
		||||
    JanetFFIType ret_type;
 | 
			
		||||
    JanetFFIMapping ret;
 | 
			
		||||
    JanetFFIMapping args[JANET_FFI_MAX_ARGS];
 | 
			
		||||
} JanetFFISignature;
 | 
			
		||||
 | 
			
		||||
@@ -373,21 +375,20 @@ static Janet janet_ffi_read_one(const uint8_t *from, JanetFFIType type, int recu
 | 
			
		||||
        default:
 | 
			
		||||
        case JANET_FFI_TYPE_VOID:
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        case JANET_FFI_TYPE_STRUCT:
 | 
			
		||||
            {
 | 
			
		||||
                JanetFFIStruct *st = type.st;
 | 
			
		||||
                Janet *tup = janet_tuple_begin(st->field_count);
 | 
			
		||||
                size_t cursor = 0;
 | 
			
		||||
                for (uint32_t i = 0; i < st->field_count; i++) {
 | 
			
		||||
                    JanetFFIType tp = st->fields[i];
 | 
			
		||||
                    size_t align = type_align(tp);
 | 
			
		||||
                    size_t size = type_size(tp);
 | 
			
		||||
                    cursor = ((cursor + align - 1) / align) * align;
 | 
			
		||||
                    tup[i] = janet_ffi_read_one(from + cursor, tp, recur - 1);
 | 
			
		||||
                    cursor += size;
 | 
			
		||||
                }
 | 
			
		||||
                return janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
        case JANET_FFI_TYPE_STRUCT: {
 | 
			
		||||
            JanetFFIStruct *st = type.st;
 | 
			
		||||
            Janet *tup = janet_tuple_begin(st->field_count);
 | 
			
		||||
            size_t cursor = 0;
 | 
			
		||||
            for (uint32_t i = 0; i < st->field_count; i++) {
 | 
			
		||||
                JanetFFIType tp = st->fields[i];
 | 
			
		||||
                size_t align = type_align(tp);
 | 
			
		||||
                size_t size = type_size(tp);
 | 
			
		||||
                cursor = ((cursor + align - 1) / align) * align;
 | 
			
		||||
                tup[i] = janet_ffi_read_one(from + cursor, tp, recur - 1);
 | 
			
		||||
                cursor += size;
 | 
			
		||||
            }
 | 
			
		||||
            return janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FFI_TYPE_DOUBLE:
 | 
			
		||||
            return janet_wrap_number(((double *)(from))[0]);
 | 
			
		||||
        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) {
 | 
			
		||||
    JanetFFIMapping m;
 | 
			
		||||
    m.type = prim_type(JANET_FFI_TYPE_VOID);
 | 
			
		||||
@@ -493,6 +490,11 @@ JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
    uint32_t stack_count = 0;
 | 
			
		||||
    JanetFFICallingConvention cc = decode_ffi_cc(janet_getkeyword(argv, 0));
 | 
			
		||||
    JanetFFIType ret_type = decode_ffi_type(argv[1]);
 | 
			
		||||
    JanetFFIMapping ret = {
 | 
			
		||||
        ret_type,
 | 
			
		||||
        JANET_SYSV64_NO_CLASS,
 | 
			
		||||
        0
 | 
			
		||||
    };
 | 
			
		||||
    JanetFFIMapping mappings[JANET_FFI_MAX_ARGS];
 | 
			
		||||
    for (int i = 0; i < JANET_FFI_MAX_ARGS; i++) mappings[i] = void_mapping();
 | 
			
		||||
    switch (cc) {
 | 
			
		||||
@@ -500,19 +502,23 @@ JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
            janet_panicf("calling convention %v unsupported", argv[0]);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_FFI_CC_SYSV_64: {
 | 
			
		||||
            if (is_fp_type(ret_type)) variant = 1;
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
            JanetFFIWordSpec ret_spec = sysv64_classify(ret.type);
 | 
			
		||||
            if (ret_spec == JANET_SYSV64_SSE) variant = 1;
 | 
			
		||||
            ret.spec = ret_spec;
 | 
			
		||||
 | 
			
		||||
            /* Spill register overflow to memory */
 | 
			
		||||
            uint32_t next_register = 0;
 | 
			
		||||
            uint32_t next_fp_register = 0;
 | 
			
		||||
            const uint32_t max_regs = 6;
 | 
			
		||||
            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++) {
 | 
			
		||||
                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;
 | 
			
		||||
                switch (mappings[i].spec) {
 | 
			
		||||
                    default:
 | 
			
		||||
@@ -560,7 +566,7 @@ JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
    JanetFFISignature *abst = janet_abstract(&janet_signature_type, sizeof(JanetFFISignature));
 | 
			
		||||
    abst->frame_size = frame_size;
 | 
			
		||||
    abst->cc = cc;
 | 
			
		||||
    abst->ret_type = ret_type;
 | 
			
		||||
    abst->ret = ret;
 | 
			
		||||
    abst->arg_count = arg_count;
 | 
			
		||||
    abst->variant = variant;
 | 
			
		||||
    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) {
 | 
			
		||||
    uint64_t ret, rethi;
 | 
			
		||||
    (void) rethi; /* at some point we will support more complex return types */
 | 
			
		||||
    union {
 | 
			
		||||
        float f;
 | 
			
		||||
        double d;
 | 
			
		||||
        uint64_t reg;
 | 
			
		||||
    } u;
 | 
			
		||||
    uint64_t ret[2];
 | 
			
		||||
    uint64_t regs[6];
 | 
			
		||||
    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);
 | 
			
		||||
    for (uint32_t i = 0; i < signature->arg_count; i++) {
 | 
			
		||||
        uint64_t *to;
 | 
			
		||||
@@ -596,7 +602,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
 | 
			
		||||
                to = stack + arg.offset;
 | 
			
		||||
                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!! */
 | 
			
		||||
@@ -616,7 +622,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
 | 
			
		||||
    "movq %14, %%xmm5\n\t" \
 | 
			
		||||
    "movq %15, %%xmm6\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 \
 | 
			
		||||
    "g"(function_pointer), \
 | 
			
		||||
    "g"(regs[0]), \
 | 
			
		||||
@@ -661,36 +667,7 @@ static Janet janet_ffi_sysv64(JanetFFISignature *signature, void *function_point
 | 
			
		||||
#undef FFI_ASM_OUTPUTS
 | 
			
		||||
#undef FFI_ASM_INPUTS
 | 
			
		||||
 | 
			
		||||
    /* TODO - compound type returns */
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    janet_buffer_extra(buffer, 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;
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
@@ -736,7 +713,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read,
 | 
			
		||||
    JanetByteView bytes = janet_getbytes(argv, 1);
 | 
			
		||||
    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");
 | 
			
		||||
    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) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user