From 6be24a8a51106e373406081ecd088bd1a2db4040 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sun, 3 May 2020 01:23:38 -0400 Subject: started windows #foreign support -- floating point not working yet--also found bug with eval EXPR_CALL --- build.bat | 1 + foreign.c | 355 ------------------------------------------------------- foreign_avcall.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++ foreign_msvc.c | 276 ++++++++++++++++++++++++++++++++++++++++++ infer.c | 2 +- parse.c | 8 +- test.toc | 29 ++--- toc.c | 23 +++- types.c | 2 +- types.h | 21 ++-- 10 files changed, 663 insertions(+), 394 deletions(-) create mode 100644 build.bat delete mode 100644 foreign.c create mode 100644 foreign_avcall.c create mode 100644 foreign_msvc.c diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..1d6a338 --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +cl /DTOC_DEBUG /W3 /wd4146 /D_CRT_SECURE_NO_WARNINGS /Fe:toc.exe /DEBUG /Zi main.c diff --git a/foreign.c b/foreign.c deleted file mode 100644 index 65de87b..0000000 --- a/foreign.c +++ /dev/null @@ -1,355 +0,0 @@ -/* WARNING: In this file, you will find crazy macros and dubious usage of avcall. Beware! */ - -#if COMPILE_TIME_FOREIGN_FN_SUPPORT -#if CHAR_BIT != 8 -#error "Compile-time foreign functions can only be used on systems where CHAR_BIT is 8." -#endif - -/* avcall has some sign conversion problems on BSD */ -/* (the macros it defines cause problems too, which is why this is ignored for so long) */ - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#elif defined __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif -#include -#include - - -#if SCHAR_MAX != 127 -#error "Foreign function support requires an 8-bit signed type." -#else -#define av_type_with_limit_127 schar -#endif - -#if UCHAR_MAX != 255 -#error "Foreign function support requires an 8-bit unsigned type." -#else -#define av_type_with_limit_255 uchar -#endif - -#if SHRT_MAX != 32767 -#error "Foreign function support requires a 16-bit signed type." -#else -#define av_type_with_limit_32767 short -#endif - -#if USHRT_MAX != 65535 -#error "Foreign function support requires an 16-bit unsigned type." -#else -#define av_type_with_limit_65535 ushort -#endif - -#if INT_MAX == 2147483647 -#define av_type_with_limit_2147483647 int -#elif LONG_MAX == 2147483647 -#define av_type_with_limit_2147483647 long -#else -#error "Foreign function support requires an 32-bit signed type." -#endif - -#if UINT_MAX == 4294967295 -#define av_type_with_limit_4294967295 uint -#elif ULONG_MAX == 4294967295 -#define av_type_with_limit_4294967295 ulong -#else -#error "Foreign function support requires an 32-bit unsigned type." -#endif - -#if LONG_MAX == 9223372036854775807 -#define av_type_with_limit_9223372036854775807 long -#elif LLONG_MAX == 9223372036854775807 -#define av_type_with_limit_9223372036854775807 longlong -#else -#error "Foreign function support requires a 64-bit signed type." -#endif - -#if ULONG_MAX == 18446744073709551615ULL -#define av_type_with_limit_18446744073709551615 ulong -#elif ULLONG_MAX == 18446744073709551615ULL -#define av_type_with_limit_18446744073709551615 ulonglong -#else -#error "Foreign function support requires a 64-bit unsigned type." -#endif - - - -#define av_type_with_limit(x) join(av_type_with_limit_, x) - -#define toc_av_start(limit) join(av_start_, av_type_with_limit(limit)) -#define toc_av_add(limit) join(av_, av_type_with_limit(limit)) - -#define toc_av_start_f32 av_start_float -#define toc_av_start_f64 av_start_double -#define toc_av_f32 av_float -#define toc_av_f64 av_double - -static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val, Type *return_type, Location where) { - switch (return_type->kind) { - case TYPE_UNKNOWN: - err_print(where, "Cannot call foreign function with unknown return type."); - return false; - case TYPE_TUPLE: - err_print(where, "Cannot call foreign function with tuple return type."); - return false; - case TYPE_ARR: - err_print(where, "Foreign functions cannot return arrays."); - return false; - case TYPE_FN: - warn_print(where, "Foreign function returns function pointer. If it returns a C-style function pointer, it won't be called properly by toc."); - av_start_ptr(*arg_list, fn, FnExpr *, &return_val->fn); - break; - case TYPE_PTR: - av_start_ptr(*arg_list, fn, void *, &return_val->ptr); - break; - case TYPE_BUILTIN: - switch (return_type->builtin) { - case BUILTIN_I8: - toc_av_start(127)(*arg_list, fn, &return_val->i8); - break; - case BUILTIN_U8: - toc_av_start(255)(*arg_list, fn, &return_val->u8); - break; - case BUILTIN_I16: - toc_av_start(32767)(*arg_list, fn, &return_val->i16); - break; - case BUILTIN_U16: - toc_av_start(65535)(*arg_list, fn, &return_val->u16); - break; - case BUILTIN_I32: - toc_av_start(2147483647)(*arg_list, fn, &return_val->i32); - break; - case BUILTIN_U32: - toc_av_start(4294967295)(*arg_list, fn, &return_val->u32); - break; - case BUILTIN_I64: - toc_av_start(9223372036854775807)(*arg_list, fn, &return_val->i64); - break; - case BUILTIN_U64: - toc_av_start(18446744073709551615)(*arg_list, fn, &return_val->u64); - break; - case BUILTIN_BOOL: - /* bool is probably just unsigned char.... hopefully... */ - av_start_uchar(*arg_list, fn, &return_val->u8); - break; - case BUILTIN_CHAR: - av_start_char(*arg_list, fn, &return_val->charv); - break; - case BUILTIN_F32: - toc_av_start_f32(*arg_list, fn, &return_val->f32); - break; - case BUILTIN_F64: - toc_av_start_f64(*arg_list, fn, &return_val->f64); - break; - case BUILTIN_TYPE: - av_start_ptr(*arg_list, fn, Type *, &return_val->type); - break; - case BUILTIN_NMS: - av_start_ptr(*arg_list, fn, Namespace *, &return_val->nms); - break; - case BUILTIN_VOID: - av_start_void(*arg_list, fn); - break; - case BUILTIN_VARARGS: - assert(0); - break; - } - break; - case TYPE_STRUCT: { - size_t struct_size = compiler_sizeof(return_type); - StructDef *struc = return_type->struc; - return_val->struc = err_calloc(1, struct_size); - bool splittable; - /* hopefully this is right! */ - if (struct_size <= sizeof(long)) { - splittable = true; - } else if (struct_size > 2*sizeof(long)) { - splittable = false; - } else if (arr_len(struc->fields) > 4) { - splittable = false; - } else { - /* NOTE: this warning is not because splittable is being computed incorrectly! it doesn't handle it right with *either* splittable = 0 or splittable = 1 */ - warn_print(where, "Dynamically calling function which returns a struct. avcall seems to not handle structs of size ~2*sizeof(long) correctly."); - splittable = true; - size_t word_size = sizeof(__avword); - arr_foreach(struc->fields, Field, f) { - if (f->offset / word_size != (f->offset + compiler_sizeof(f->type) - 1) / word_size) { - splittable = false; - break; - } - } - } - /* getting warning on Debian stretch about splittable being set but not used */ - _av_start_struct(*arg_list, fn, struct_size, splittable, return_val->struc); (void)splittable; - } break; - case TYPE_SLICE: - av_start_struct(*arg_list, fn, Slice, av_word_splittable_2(I64, void *), &return_val->slice); - break; - case TYPE_EXPR: - assert(0); - return false; - } - return true; -} - -static bool arg_list_add(av_alist *arg_list, Value val, Type *type, Location where) { - switch (type->kind) { - case TYPE_TUPLE: - case TYPE_UNKNOWN: - case TYPE_ARR: { /* @TODO: maybe just pass pointer for arr? */ - char *s = type_to_str(type); - err_print(where, "Cannot pass type %s to foreign function.", s); - free(s); - return false; - } - case TYPE_PTR: - av_ptr(*arg_list, void *, val.ptr); - break; - case TYPE_FN: - warn_print(where, "Passing toc function pointer to foreign function. This will not work if the function expects a C-style function pointer."); - av_ptr(*arg_list, FnExpr *, val.fn); - break; - case TYPE_BUILTIN: - switch (type->builtin) { - case BUILTIN_I8: - toc_av_add(127)(*arg_list, val.i8); - break; - case BUILTIN_U8: - toc_av_add(255)(*arg_list, val.u8); - break; - case BUILTIN_I16: - toc_av_add(32767)(*arg_list, val.i16); - break; - case BUILTIN_U16: - toc_av_add(65535)(*arg_list, val.u16); - break; - case BUILTIN_I32: - toc_av_add(2147483647)(*arg_list, val.i32); - break; - case BUILTIN_U32: - toc_av_add(4294967295)(*arg_list, val.u32); - break; - case BUILTIN_I64: - toc_av_add(9223372036854775807)(*arg_list, val.i64); - break; - case BUILTIN_U64: - toc_av_add(18446744073709551615)(*arg_list, val.u64); - break; - case BUILTIN_CHAR: - av_char(*arg_list, val.charv); - break; - case BUILTIN_BOOL: - av_uchar(*arg_list, val.boolv); - break; - case BUILTIN_F32: - toc_av_f32(*arg_list, val.f32); - break; - case BUILTIN_F64: - toc_av_f64(*arg_list, val.f64); - break; - case BUILTIN_TYPE: - av_ptr(*arg_list, Type *, val.type); - break; - case BUILTIN_NMS: - av_ptr(*arg_list, Namespace *, val.nms); - break; - case BUILTIN_VARARGS: - arr_foreach(val.varargs, VarArg, arg) { - arg_list_add(arg_list, arg->val, arg->type, where); - } - break; - case BUILTIN_VOID: - err_print(where, "Cannot pass type void to foreign function."); - return false; - } - break; - case TYPE_SLICE: - av_struct(*arg_list, Slice, val.slice); - break; - case TYPE_STRUCT: - _av_struct(*arg_list, compiler_sizeof(type), compiler_alignof(type), val.struc); - break; - case TYPE_EXPR: - assert(0); - return false; - } - return true; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif defined __GNUC__ -#pragma GCC diagnostic pop -#endif - -static void ffmgr_create(ForeignFnManager *ffmgr, Allocator *allocr) { - str_hash_table_create(&ffmgr->libs_loaded, sizeof(Library), allocr); -} - -static bool foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { - void (*fn_ptr)() = fn->foreign.fn_ptr; - if (!fn_ptr) { - assert(fn->flags & FN_EXPR_FOREIGN); - const char *libname = fn->foreign.lib; - if (!libname) { - err_print(call_where, "Attempt to call function at compile time which does not have an associated library."); - info_print(fn->where, "Function was declared here."); - return false; - } - Library *lib = str_hash_table_get(&ffmgr->libs_loaded, libname, strlen(libname)); - if (!lib) { - void *handle = dlopen(libname, RTLD_LAZY); - if (!handle) { - err_print(call_where, "Could not open dynamic library: %s.", libname); - return false; - } - lib = str_hash_table_insert(&ffmgr->libs_loaded, libname, strlen(libname)); - lib->handle = handle; - } - const char *name = fn->foreign.name; -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -#endif - fn_ptr = dlsym(lib->handle, name); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - - if (!fn_ptr) { - err_print(call_where, "Could not get function from dynamic library: %s.", name); - return false; - } - fn->foreign.fn_ptr = fn_ptr; - } - - av_alist arg_list; - if (!arg_list_start(&arg_list, fn_ptr, ret, ret_type, call_where)) - return false; - char *type = (char *)arg_types; - for (size_t i = 0; i < nargs; ++i) { - if (!arg_list_add(&arg_list, args[i], (Type *)type, call_where)) - return false; - type += arg_types_stride; - } - av_call(arg_list); - return true; -} - -#else -static void ffmgr_create(ForeignFnManager *ffmgr, Allocator *allocr) { - (void)ffmgr; - (void)allocr; -} - -static bool foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { - (void)ffmgr; (void)fn; (void)ret_type; (void)arg_types; (void)arg_types_stride; (void)args; (void)nargs; (void)ret; - err_print(call_where, "You have not compiled toc with compile time foreign function support."); - return false; -} - -#endif - diff --git a/foreign_avcall.c b/foreign_avcall.c new file mode 100644 index 0000000..69146bb --- /dev/null +++ b/foreign_avcall.c @@ -0,0 +1,340 @@ +/* WARNING: In this file, you will find crazy macros and dubious usage of avcall. Beware! */ + +typedef struct { + void *handle; +} Library; + +#if CHAR_BIT != 8 +#error "Compile-time foreign functions can only be used on systems where CHAR_BIT is 8." +#endif + +/* avcall has some sign conversion problems on BSD */ +/* (the macros it defines cause problems too, which is why this is ignored for so long) */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#elif defined __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +#include +#include + + +#if SCHAR_MAX != 127 +#error "Foreign function support requires an 8-bit signed type." +#else +#define av_type_with_limit_127 schar +#endif + +#if UCHAR_MAX != 255 +#error "Foreign function support requires an 8-bit unsigned type." +#else +#define av_type_with_limit_255 uchar +#endif + +#if SHRT_MAX != 32767 +#error "Foreign function support requires a 16-bit signed type." +#else +#define av_type_with_limit_32767 short +#endif + +#if USHRT_MAX != 65535 +#error "Foreign function support requires an 16-bit unsigned type." +#else +#define av_type_with_limit_65535 ushort +#endif + +#if INT_MAX == 2147483647 +#define av_type_with_limit_2147483647 int +#elif LONG_MAX == 2147483647 +#define av_type_with_limit_2147483647 long +#else +#error "Foreign function support requires an 32-bit signed type." +#endif + +#if UINT_MAX == 4294967295 +#define av_type_with_limit_4294967295 uint +#elif ULONG_MAX == 4294967295 +#define av_type_with_limit_4294967295 ulong +#else +#error "Foreign function support requires an 32-bit unsigned type." +#endif + +#if LONG_MAX == 9223372036854775807 +#define av_type_with_limit_9223372036854775807 long +#elif LLONG_MAX == 9223372036854775807 +#define av_type_with_limit_9223372036854775807 longlong +#else +#error "Foreign function support requires a 64-bit signed type." +#endif + +#if ULONG_MAX == 18446744073709551615ULL +#define av_type_with_limit_18446744073709551615 ulong +#elif ULLONG_MAX == 18446744073709551615ULL +#define av_type_with_limit_18446744073709551615 ulonglong +#else +#error "Foreign function support requires a 64-bit unsigned type." +#endif + + + +#define av_type_with_limit(x) join(av_type_with_limit_, x) + +#define toc_av_start(limit) join(av_start_, av_type_with_limit(limit)) +#define toc_av_add(limit) join(av_, av_type_with_limit(limit)) + +#define toc_av_start_f32 av_start_float +#define toc_av_start_f64 av_start_double +#define toc_av_f32 av_float +#define toc_av_f64 av_double + +static Status arg_list_start(av_alist *arg_list, FnPtr fn, Value *return_val, Type *return_type, Location where) { + switch (return_type->kind) { + case TYPE_UNKNOWN: + err_print(where, "Cannot call foreign function with unknown return type."); + return false; + case TYPE_TUPLE: + err_print(where, "Cannot call foreign function with tuple return type."); + return false; + case TYPE_ARR: + err_print(where, "Foreign functions cannot return arrays."); + return false; + case TYPE_FN: + warn_print(where, "Foreign function returns function pointer. If it returns a C-style function pointer, it won't be called properly by toc."); + av_start_ptr(*arg_list, fn, FnExpr *, &return_val->fn); + break; + case TYPE_PTR: + av_start_ptr(*arg_list, fn, void *, &return_val->ptr); + break; + case TYPE_BUILTIN: + switch (return_type->builtin) { + case BUILTIN_I8: + toc_av_start(127)(*arg_list, fn, &return_val->i8); + break; + case BUILTIN_U8: + toc_av_start(255)(*arg_list, fn, &return_val->u8); + break; + case BUILTIN_I16: + toc_av_start(32767)(*arg_list, fn, &return_val->i16); + break; + case BUILTIN_U16: + toc_av_start(65535)(*arg_list, fn, &return_val->u16); + break; + case BUILTIN_I32: + toc_av_start(2147483647)(*arg_list, fn, &return_val->i32); + break; + case BUILTIN_U32: + toc_av_start(4294967295)(*arg_list, fn, &return_val->u32); + break; + case BUILTIN_I64: + toc_av_start(9223372036854775807)(*arg_list, fn, &return_val->i64); + break; + case BUILTIN_U64: + toc_av_start(18446744073709551615)(*arg_list, fn, &return_val->u64); + break; + case BUILTIN_BOOL: + /* bool is probably just unsigned char.... hopefully... */ + av_start_uchar(*arg_list, fn, &return_val->u8); + break; + case BUILTIN_CHAR: + av_start_char(*arg_list, fn, &return_val->charv); + break; + case BUILTIN_F32: + toc_av_start_f32(*arg_list, fn, &return_val->f32); + break; + case BUILTIN_F64: + toc_av_start_f64(*arg_list, fn, &return_val->f64); + break; + case BUILTIN_TYPE: + av_start_ptr(*arg_list, fn, Type *, &return_val->type); + break; + case BUILTIN_NMS: + av_start_ptr(*arg_list, fn, Namespace *, &return_val->nms); + break; + case BUILTIN_VOID: + av_start_void(*arg_list, fn); + break; + case BUILTIN_VARARGS: + assert(0); + break; + } + break; + case TYPE_STRUCT: { + size_t struct_size = compiler_sizeof(return_type); + StructDef *struc = return_type->struc; + return_val->struc = err_calloc(1, struct_size); + bool splittable; + /* hopefully this is right! */ + if (struct_size <= sizeof(long)) { + splittable = true; + } else if (struct_size > 2*sizeof(long)) { + splittable = false; + } else if (arr_len(struc->fields) > 4) { + splittable = false; + } else { + /* NOTE: this warning is not because splittable is being computed incorrectly! it doesn't handle it right with *either* splittable = 0 or splittable = 1 */ + warn_print(where, "Dynamically calling function which returns a struct. avcall seems to not handle structs of size ~2*sizeof(long) correctly."); + splittable = true; + size_t word_size = sizeof(__avword); + arr_foreach(struc->fields, Field, f) { + if (f->offset / word_size != (f->offset + compiler_sizeof(f->type) - 1) / word_size) { + splittable = false; + break; + } + } + } + /* getting warning on Debian stretch about splittable being set but not used */ + _av_start_struct(*arg_list, fn, struct_size, splittable, return_val->struc); (void)splittable; + } break; + case TYPE_SLICE: + av_start_struct(*arg_list, fn, Slice, av_word_splittable_2(I64, void *), &return_val->slice); + break; + case TYPE_EXPR: + assert(0); + return false; + } + return true; +} + +static Status arg_list_add(av_alist *arg_list, Value val, Type *type, Location where) { + switch (type->kind) { + case TYPE_TUPLE: + case TYPE_UNKNOWN: + case TYPE_ARR: { /* @TODO: maybe just pass pointer for arr? */ + char *s = type_to_str(type); + err_print(where, "Cannot pass type %s to foreign function.", s); + free(s); + return false; + } + case TYPE_PTR: + av_ptr(*arg_list, void *, val.ptr); + break; + case TYPE_FN: + warn_print(where, "Passing toc function pointer to foreign function. This will not work if the function expects a C-style function pointer."); + av_ptr(*arg_list, FnExpr *, val.fn); + break; + case TYPE_BUILTIN: + switch (type->builtin) { + case BUILTIN_I8: + toc_av_add(127)(*arg_list, val.i8); + break; + case BUILTIN_U8: + toc_av_add(255)(*arg_list, val.u8); + break; + case BUILTIN_I16: + toc_av_add(32767)(*arg_list, val.i16); + break; + case BUILTIN_U16: + toc_av_add(65535)(*arg_list, val.u16); + break; + case BUILTIN_I32: + toc_av_add(2147483647)(*arg_list, val.i32); + break; + case BUILTIN_U32: + toc_av_add(4294967295)(*arg_list, val.u32); + break; + case BUILTIN_I64: + toc_av_add(9223372036854775807)(*arg_list, val.i64); + break; + case BUILTIN_U64: + toc_av_add(18446744073709551615)(*arg_list, val.u64); + break; + case BUILTIN_CHAR: + av_char(*arg_list, val.charv); + break; + case BUILTIN_BOOL: + av_uchar(*arg_list, val.boolv); + break; + case BUILTIN_F32: + toc_av_f32(*arg_list, val.f32); + break; + case BUILTIN_F64: + toc_av_f64(*arg_list, val.f64); + break; + case BUILTIN_TYPE: + av_ptr(*arg_list, Type *, val.type); + break; + case BUILTIN_NMS: + av_ptr(*arg_list, Namespace *, val.nms); + break; + case BUILTIN_VARARGS: + arr_foreach(val.varargs, VarArg, arg) { + arg_list_add(arg_list, arg->val, arg->type, where); + } + break; + case BUILTIN_VOID: + err_print(where, "Cannot pass type void to foreign function."); + return false; + } + break; + case TYPE_SLICE: + av_struct(*arg_list, Slice, val.slice); + break; + case TYPE_STRUCT: + _av_struct(*arg_list, compiler_sizeof(type), compiler_alignof(type), val.struc); + break; + case TYPE_EXPR: + assert(0); + return false; + } + return true; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined __GNUC__ +#pragma GCC diagnostic pop +#endif + +static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { + FnPtr fn_ptr = fn->foreign.fn_ptr; + if (!fn_ptr) { + assert(fn->flags & FN_EXPR_FOREIGN); + const char *libname = fn->foreign.lib; + if (!libname) { + err_print(call_where, "Attempt to call function at compile time which does not have an associated library."); + info_print(fn->where, "Function was declared here."); + return false; + } + Library *lib = str_hash_table_get(&ffmgr->libs_loaded, libname, strlen(libname)); + if (!lib) { + void *handle = dlopen(libname, RTLD_LAZY); + if (!handle) { + err_print(call_where, "Could not open dynamic library: %s.", libname); + return false; + } + lib = str_hash_table_insert(&ffmgr->libs_loaded, libname, strlen(libname)); + lib->handle = handle; + } + const char *name = fn->foreign.name; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + fn_ptr = dlsym(lib->handle, name); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + if (!fn_ptr) { + err_print(call_where, "Could not get function from dynamic library: %s.", name); + return false; + } + fn->foreign.fn_ptr = fn_ptr; + } + + av_alist arg_list; + if (!arg_list_start(&arg_list, fn_ptr, ret, ret_type, call_where)) + return false; + char *type = (char *)arg_types; + for (size_t i = 0; i < nargs; ++i) { + if (!arg_list_add(&arg_list, args[i], (Type *)type, call_where)) + return false; + type += arg_types_stride; + } + av_call(arg_list); + return true; +} + diff --git a/foreign_msvc.c b/foreign_msvc.c new file mode 100644 index 0000000..f78f275 --- /dev/null +++ b/foreign_msvc.c @@ -0,0 +1,276 @@ +/* +this code is not standard-compliant in the slightest, and a bit dubious, +but I don't think there's any other way that doesn't involve inline assembly +*/ + +#include + +typedef size_t Word; + +typedef struct { + HMODULE handle; +} Library; + + +static Status val_to_word(Value v, Type *t, Location where, Word *w) { + switch (t->kind) { + case TYPE_BUILTIN: + switch (t->builtin) { + case BUILTIN_I8: *w = (Word)v.i8; break; + case BUILTIN_I16: *w = (Word)v.i16; break; + case BUILTIN_I32: *w = (Word)v.i32; break; + case BUILTIN_I64: *w = (Word)v.i64; break; + case BUILTIN_U8: *w = (Word)v.u8; break; + case BUILTIN_U16: *w = (Word)v.u16; break; + case BUILTIN_U32: *w = (Word)v.u32; break; + case BUILTIN_U64: *w = (Word)v.u64; break; + case BUILTIN_F32: *w = (Word)*(uint32_t *)&v.f32; break; + case BUILTIN_F64: *w = (Word)*(uint64_t *)&v.f64; break; + case BUILTIN_CHAR: *w = (Word)v.charv; break; + case BUILTIN_BOOL: *w = (Word)v.boolv; break; + case BUILTIN_TYPE: + case BUILTIN_VARARGS: + case BUILTIN_NMS: + case BUILTIN_VOID: + goto unsupported; + } + break; + case TYPE_PTR: + *w = (Word)v.ptr; break; + default: + unsupported: { + /* @TODO(eventually) */ + char *s = type_to_str(t); + err_print(where, "#foreign functions can't take arguments of type %s at compile time on Windows.", s); + free(s); + return false; + } + } + return true; +} + + +/* +because of the way the MSVC "cdecl" calling convention works, the only things that affect the way a function is called are +the number of arguments and whether the function returns an integer (or pointer), floating-point number, +or struct (struct return values are not supported yet). + +this means we can get away with these twenty functions--GCC passes most arguments in registers, so there would need to be +a lot more functions to support different combinations of integer and floating-point arguments (since they use different +registers) +*/ + +/* call function with 0 arguments, returning some sort of integer (or pointer) */ +static Word msvc_call0i(FnPtr fn, Word *w) { + (void)w; + return ((Word(*)(void))fn)(); +} +static Word msvc_call1i(FnPtr fn, Word *w) { + return ((Word(*)(Word))fn)(w[0]); +} +static Word msvc_call2i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word))fn)(w[0], w[1]); +} +static Word msvc_call3i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word))fn)(w[0], w[1], w[2]); +} +static Word msvc_call4i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3]); +} +static Word msvc_call5i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4]); +} +static Word msvc_call6i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5]); +} +static Word msvc_call7i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6]); +} +static Word msvc_call8i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); +} +static Word msvc_call9i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]); +} +static Word msvc_call10i(FnPtr fn, Word *w) { + return ((Word(*)(Word, Word, Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], w[9]); +} +static Word (*const msvc_calli[11])(FnPtr fn, Word *w) = { + msvc_call0i, + msvc_call1i, + msvc_call2i, + msvc_call3i, + msvc_call4i, + msvc_call5i, + msvc_call6i, + msvc_call7i, + msvc_call8i, + msvc_call9i, + msvc_call10i +}; + + +/* call function with 0 arguments, returning a float or double */ +static double msvc_call0f(FnPtr fn, Word *w) { + (void)w; + return ((double(*)(void))fn)(); +} +static double msvc_call1f(FnPtr fn, Word *w) { + return ((double(*)(Word))fn)(w[0]); +} +static double msvc_call2f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word))fn)(w[0], w[1]); +} +static double msvc_call3f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word))fn)(w[0], w[1], w[2]); +} +static double msvc_call4f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3]); +} +static double msvc_call5f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4]); +} +static double msvc_call6f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5]); +} +static double msvc_call7f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6]); +} +static double msvc_call8f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); +} +static double msvc_call9f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]); +} +static double msvc_call10f(FnPtr fn, Word *w) { + return ((double(*)(Word, Word, Word, Word, Word, Word, Word, Word, Word, Word))fn)(w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], w[9]); +} +static double (*const msvc_callf[11])(FnPtr fn, Word *w) = { + msvc_call0f, + msvc_call1f, + msvc_call2f, + msvc_call3f, + msvc_call4f, + msvc_call5f, + msvc_call6f, + msvc_call7f, + msvc_call8f, + msvc_call9f, + msvc_call10f +}; + +static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { + possibly_static_assert(sizeof(double) == 8); + possibly_static_assert(sizeof(float) == 4); + FnPtr fn_ptr = fn->foreign.fn_ptr; + if (!fn_ptr) { + assert(fn->flags & FN_EXPR_FOREIGN); + char const *libname = fn->foreign.lib; + if (!libname) { + err_print(call_where, "Attempt to call function at compile time which does not have an associated library."); + info_print(fn->where, "Function was declared here."); + return false; + } + Library *lib = str_hash_table_get(&ffmgr->libs_loaded, libname, strlen(libname)); + if (!lib) { + HMODULE handle = LoadLibraryA(libname); + if (!handle) { + DWORD err = GetLastError(); + err_print(call_where, "Could not open dynamic library %s (error code %ld).", libname, err); + return false; + } + lib = str_hash_table_insert(&ffmgr->libs_loaded, libname, strlen(libname)); + lib->handle = handle; + } + const char *name = fn->foreign.name; + fn_ptr = (FnPtr)GetProcAddress(lib->handle, name); + if (!fn_ptr) { + err_print(call_where, "Could not get function %s from dynamic library.", name); + return false; + } + fn->foreign.fn_ptr = fn_ptr; + } + Word words[10]; + char *type = (char *)arg_types; + for (size_t i = 0; i < nargs; ++i) { + val_to_word(args[i], (Type *)type, call_where, &words[i]); + type += arg_types_stride; + } + bool is_float = false; + switch (ret_type->kind) { + case TYPE_BUILTIN: + switch (ret_type->builtin) { + case BUILTIN_I8: + case BUILTIN_I16: + case BUILTIN_I32: + case BUILTIN_I64: + case BUILTIN_U8: + case BUILTIN_U16: + case BUILTIN_U32: + case BUILTIN_U64: + case BUILTIN_BOOL: + case BUILTIN_CHAR: + case BUILTIN_VOID: + break; + case BUILTIN_F32: + case BUILTIN_F64: + is_float = true; + break; + default: + goto unsupported; + } + break; + case TYPE_PTR: + break; + default: + unsupported: { + char *s = type_to_str(ret_type); + /* @TODO(eventually) */ + err_print(call_where, "You can't call functions which return type %s at compile time on Windows.", s); + free(s); + return false; + } + } + + if (nargs > 10) { + err_print(call_where, "You can only call functions with up to 10 arguments on Windows at compile time. This call has %ld.", (long)nargs); + return false; + } + + if (is_float) { + double d = msvc_callf[nargs](fn_ptr, words); + assert(ret_type->kind == TYPE_BUILTIN); + if (ret_type->builtin == BUILTIN_F32) { + U64 lower_bits = (U32)*(U64 *)&d; + ret->f32 = *(float *)&lower_bits; + } else { + assert(ret_type->builtin == BUILTIN_F64); + ret->f64 = d; + } + } else { + Word w = msvc_calli[nargs](fn_ptr, words); + switch (ret_type->kind) { + case TYPE_BUILTIN: + switch (ret_type->builtin) { + case BUILTIN_I8: ret->i8 = (I8)w; break; + case BUILTIN_I16: ret->i16 = (I16)w; break; + case BUILTIN_I32: ret->i32 = (I32)w; break; + case BUILTIN_I64: ret->i64 = (I64)w; break; + case BUILTIN_U8: ret->u8 = (U8)w; break; + case BUILTIN_U16: ret->u16 = (U16)w; break; + case BUILTIN_U32: ret->u32 = (U32)w; break; + case BUILTIN_U64: ret->u64 = (U64)w; break; + case BUILTIN_BOOL: ret->boolv = (bool)w; break; + case BUILTIN_CHAR: ret->charv = (char)w; break; + case BUILTIN_VOID: (void)w; break; + default: assert(0); break; + } + break; + case TYPE_PTR: + ret->ptr = (void *)w; + break; + default: assert(0); break; + } + } + return true; +} diff --git a/infer.c b/infer.c index 0bf5e0f..9816e2e 100644 --- a/infer.c +++ b/infer.c @@ -18,7 +18,7 @@ static bool infer_from_expr(Typer *tr, Expression *match, Value to, Type *to_typ /* an identifier! maybe it's one of idents... */ arr_foreach(idents, Identifier, ident) { if (ident_eq_string(*ident, match->ident_str)) { - long idx = ident - idents; + long idx = (long)(ident - idents); types[idx] = *to_type; vals[idx] = to; break; diff --git a/parse.c b/parse.c index dd3a4b7..b066708 100644 --- a/parse.c +++ b/parse.c @@ -146,14 +146,18 @@ static bool type_builtin_is_float(BuiltinType b) { } } -static bool type_builtin_is_numerical(BuiltinType b) { +static inline bool type_builtin_is_numerical(BuiltinType b) { return type_builtin_is_int(b) || type_builtin_is_float(b); } -static bool type_is_int(Type *t) { +static inline bool type_is_int(Type *t) { return t->kind == TYPE_BUILTIN && type_builtin_is_int(t->builtin); } +static inline bool type_is_float(Type *t) { + return t->kind == TYPE_BUILTIN && type_builtin_is_float(t->builtin); +} + /* returns -1 on failure */ static int kw_to_builtin_type(Keyword kw) { switch (kw) { diff --git a/test.toc b/test.toc index b181972..44917ac 100644 --- a/test.toc +++ b/test.toc @@ -1,22 +1,13 @@ -#include "std/io.toc"; - -foo ::= fn() total := 0{ - if "foo" || (1/0) { - total += 1; - } - if !"foo" && (1/0) { - total += 100; - } - if "foo" || "bar" && 0 { - total += 10; - } - if !"foo" || !"bar" { - total += 1000; - } +stdc ::= "msvcrt.dll"; +printf ::= #foreign("printf",stdc) fn (#C &"char const", #C ..) #C int; +puti ::= fn(i: i32) i32 { + fmt := "number: %d\n\0"; + printf(&fmt[0], i) as i32 } +// BUG: puti(puti(x)) +sqrtf ::= #foreign("sqrt",stdc) fn(f64) f64; + main ::= fn() { - a := foo(); - b ::= foo(); - puti(a); - puti(b); + f ::= sqrtf(2.0); } +main(); diff --git a/toc.c b/toc.c index 954f357..bebb9b1 100644 --- a/toc.c +++ b/toc.c @@ -17,7 +17,7 @@ #include #ifndef COMPILE_TIME_FOREIGN_FN_SUPPORT -#define COMPILE_TIME_FOREIGN_FN_SUPPORT 0 +#define COMPILE_TIME_FOREIGN_FN_SUPPORT 1 #endif @@ -132,7 +132,26 @@ static Location token_location(File *file, Token *t); #include "copy.c" #include "tokenizer.c" #include "parse.c" -#include "foreign.c" + +#if COMPILE_TIME_FOREIGN_FN_SUPPORT +#if defined _MSC_VER && !defined COMPILE_TIME_FOREIGN_FN_AVCALL +#include "foreign_msvc.c" +#else +#include "foreign_avcall.c" +#endif +#else +static bool foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) { + (void)ffmgr; (void)fn; (void)ret_type; (void)arg_types; (void)arg_types_stride; (void)args; (void)nargs; (void)ret; + err_print(call_where, "You have not compiled toc with compile time foreign function support."); + return false; +} +typedef char Library; +#endif +static void ffmgr_create(ForeignFnManager *ffmgr, Allocator *allocr) { + ffmgr->allocr = allocr; + str_hash_table_create(&ffmgr->libs_loaded, sizeof(Library), allocr); +} + #include "infer.c" #include "types.c" #include "eval.c" diff --git a/types.c b/types.c index 536ddf6..1001e07 100644 --- a/types.c +++ b/types.c @@ -2433,7 +2433,7 @@ static Status types_expr(Typer *tr, Expression *e) { nvarargs += nvarargs_here; --nvarargs; - long arg_out_idx = arg_out - arg_exprs; /* save and restore arg_out to prevent realloc from causing problems */ + long arg_out_idx = (long)(arg_out - arg_exprs); /* save and restore arg_out to prevent realloc from causing problems */ /* add more room (or if nvarargs_here == 0, remove room) for more varargs */ arr_set_lena(arg_exprs, narg_exprs, tr->allocr); arg_out = arg_exprs + arg_out_idx; diff --git a/types.h b/types.h index 07d9bfb..22dfe56 100644 --- a/types.h +++ b/types.h @@ -42,13 +42,15 @@ typedef unsigned long ulonglong; #define ULONGLONG_FMT "%lu" #endif +/* generic function pointer */ +typedef void (*FnPtr)(void); /* try to find the type with the strictest alignment */ typedef union { double floating; void *ptr; longlong integer; - void (*fn_ptr)(void); + FnPtr fn_ptr; } MaxAlign; typedef uint8_t U8; @@ -695,7 +697,7 @@ typedef struct FnExpr { const char *lib; struct Expression *lib_expr; }; - void (*fn_ptr)(); + FnPtr fn_ptr; } foreign; }; HashTable *instances; /* for fns with constant parameters. the key is a tuple where @@ -998,18 +1000,9 @@ typedef struct Parser { ParsedFile *parsed_file; } Parser; -#if COMPILE_TIME_FOREIGN_FN_SUPPORT typedef struct { - void *handle; -} Library; -#endif - -typedef struct { -#if COMPILE_TIME_FOREIGN_FN_SUPPORT - StrHashTable libs_loaded; /* of Library */ -#else - char unused; -#endif + Allocator *allocr; + StrHashTable libs_loaded; /* of Library (NOTE: Library is defined in foreign_something.c) */ } ForeignFnManager; typedef struct Evaluator { @@ -1053,5 +1046,5 @@ typedef struct CGenerator { FnExpr *fn; /* which function are we in? (NULL for none) - not used during decls */ Identifier main_ident; Identifiers *globals; - const char **nms_prefixes; /* dynamic (null-terminated) array of namespace prefixes */ + char **nms_prefixes; /* dynamic (null-terminated) array of namespace prefixes */ } CGenerator; -- cgit v1.2.3