summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-05-03 01:23:38 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2020-05-03 01:23:38 -0400
commit6be24a8a51106e373406081ecd088bd1a2db4040 (patch)
tree5be60b3d2b02b4cb3d17cea5abfe58150f37d63b
parent8c2327e430ca63349c28816b47d04147873c1aca (diff)
started windows #foreign support -- floating point not working yet--also found bug with eval EXPR_CALL
-rw-r--r--build.bat1
-rw-r--r--foreign_avcall.c (renamed from foreign.c)31
-rw-r--r--foreign_msvc.c276
-rw-r--r--infer.c2
-rw-r--r--parse.c8
-rw-r--r--test.toc29
-rw-r--r--toc.c23
-rw-r--r--types.c2
-rw-r--r--types.h21
9 files changed, 331 insertions, 62 deletions
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_avcall.c
index 65de87b..69146bb 100644
--- a/foreign.c
+++ b/foreign_avcall.c
@@ -1,6 +1,9 @@
/* WARNING: In this file, you will find crazy macros and dubious usage of avcall. Beware! */
-#if COMPILE_TIME_FOREIGN_FN_SUPPORT
+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
@@ -87,7 +90,7 @@
#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) {
+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.");
@@ -195,7 +198,7 @@ static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val,
return true;
}
-static bool arg_list_add(av_alist *arg_list, Value val, Type *type, Location where) {
+static Status arg_list_add(av_alist *arg_list, Value val, Type *type, Location where) {
switch (type->kind) {
case TYPE_TUPLE:
case TYPE_UNKNOWN:
@@ -285,12 +288,8 @@ static bool arg_list_add(av_alist *arg_list, Value val, Type *type, Location whe
#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;
+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;
@@ -339,17 +338,3 @@ static bool foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Ty
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_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 <windows.h>
+
+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 <inttypes.h>
#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;