summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-05-06 13:22:03 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2020-05-06 13:22:03 -0400
commit38d57cdce42115fac4eb48bb441ac31f0fd81a7a (patch)
treee8606a24408d00c750301360ead6f768b591651f
parentcb75407e5ebd05edf3779b4ab389afcd70d7b3c8 (diff)
system v amd64 calling convention
-rw-r--r--Makefile6
-rw-r--r--README.md10
-rwxr-xr-xbuild.sh22
-rw-r--r--foreign64.c (renamed from foreign_msvc64.c)46
-rw-r--r--foreign_avcall.c42
-rw-r--r--foreign_msvc.c8
-rw-r--r--foreign_msvc32.c2
-rw-r--r--foreign_unix.c51
-rw-r--r--main.c1
-rw-r--r--systemv64call.asm267
-rw-r--r--systemv64call_test.c149
-rw-r--r--test.toc1
-rw-r--r--toc.c6
13 files changed, 543 insertions, 68 deletions
diff --git a/Makefile b/Makefile
index f568f2a..efc2fca 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-toc: *.[ch]
+toc: *.[ch] build.sh
./build.sh
-release: *.[ch]
+release: *.[ch] build.sh
./build.sh release
clean:
- rm toc
+ rm toc *.o
diff --git a/README.md b/README.md
index b230bea..f6e3f57 100644
--- a/README.md
+++ b/README.md
@@ -35,8 +35,14 @@ https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line .
You can run `build32.bat release` or `build64.bat release` depending on whether you want a 32-bit or 64-bit version of toc.
On other systems, you can just compile main.c with a C compiler.
-By default, toc will use libdl and libffcall. If you don't have these installed, add the preprocessor define `COMPILE_TIME_FOREIGN_FN_SUPPORT=0`.
-This is usually done with `-DCOMPILE_TIME_FOREIGN_FN_SUPPORT=0`.
+By default, on Unix x86\_64 systems, you will need to link in an assembled version of `systemv64call.asm` (this is for foreign function support at compile time).
+You can do this with:
+```
+nasm -f elf64 systemv64call.asm
+cc main.c systemv64call.o -o toc
+```
+If you want to diable foreign function support at compile time, add your equivalent of `-DCOMPILE_TIME_FOREIGN_FN_SUPPORT=0` to the compile command.
+
`toc` uses several C99 and a couple of C11 features, so it might not work on all compilers. But it does compile on quite a few, including `clang`, `gcc`, `tcc`, and MSVC. It can also be compiled as if it were C++, so and `g++` can also compile it (it does rely on implicit casting of `void *` though). The *outputted* code should be C99-compliant.
diff --git a/build.sh b/build.sh
index 49b3728..06dc638 100755
--- a/build.sh
+++ b/build.sh
@@ -1,4 +1,6 @@
#!/bin/sh
+[ "$NASM" = "" ] && NASM=nasm
+
if [ "$CC" = "" ]; then
if [ "$1" = "release" ]; then
CC=clang
@@ -27,12 +29,12 @@ else
WARNINGS=''
fi
+[ "$ARCH" = "" ] && ARCH="$(uname -m)"
+
+
if [ "$COMPILE_TIME_FOREIGN_FN_SUPPORT" != "no" ]; then
- if uname | grep -qi bsd; then
- LIBRARIES='-lavcall'
- else
- LIBRARIES='-ldl -lavcall'
- fi
+ uname | grep -qi bsd || LIBRARIES="$LIBRARIES -ldl"
+ [ "$ARCH" = "x86_64" ] || LIBRARIES="$LIBRARIES -lavcall"
ADDITIONAL_FLAGS="$ADDITIONAL_FLAGS $LIBRARIES"
else
ADDITIONAL_FLAGS="$ADDITIONAL_FLAGS -DCOMPILE_TIME_FOREIGN_FN_SUPPORT=0"
@@ -53,6 +55,10 @@ else
FLAGS="$DEBUG_FLAGS $ADDITIONAL_FLAGS"
fi
-COMMAND="$CC $FLAGS -o toc main.c"
-echo $COMMAND
-$COMMAND || exit 1
+c() {
+ echo "$1" && $1 || exit 1
+}
+
+c "$NASM -f elf64 systemv64call.asm"
+c "$CC $FLAGS -o toc main.c systemv64call.o"
+
diff --git a/foreign_msvc64.c b/foreign64.c
index b797c86..7b577d3 100644
--- a/foreign_msvc64.c
+++ b/foreign64.c
@@ -4,13 +4,43 @@
You should have received a copy of the GNU General Public License along with toc. If not, see <https://www.gnu.org/licenses/>.
*/
#if SIZE_MAX != U64_MAX
-#error "What's going on? The 64-bit Windows file was included, but size_t isn't 64 bits!"
+#error "What's going on? The 64-bit #foreign file was included, but size_t isn't 64 bits!"
#endif
+#ifdef _WIN64
extern U64 win64_call(FnPtr fn, U64 *args, I64 nargs);
extern float win64_callf(FnPtr fn, U64 *args, I64 nargs);
extern double win64_calld(FnPtr fn, U64 *args, I64 nargs);
+static inline U64 foreign_calli(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ (void)is_float;
+ return win64_call(fn, args, nargs);
+}
+
+static inline float foreign_callf(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ (void)is_float;
+ return win64_callf(fn, args, nargs);
+}
+
+static inline double foreign_calld(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ (void)is_float;
+ return win64_calld(fn, args, nargs);
+}
+#else
+extern U64 systemv64_call(FnPtr fn, U64 *args, I64 nargs, bool *is_float);
+extern float systemv64_callf(FnPtr fn, U64 *args, I64 nargs, bool *is_float);
+extern double systemv64_calld(FnPtr fn, U64 *args, I64 nargs, bool *is_float);
+static inline U64 foreign_calli(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ return systemv64_call(fn, args, nargs, is_float);
+}
+static inline float foreign_callf(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ return systemv64_callf(fn, args, nargs, is_float);
+}
+static inline double foreign_calld(FnPtr fn, U64 *args, I64 nargs, bool *is_float) {
+ return systemv64_calld(fn, args, nargs, is_float);
+}
+#endif
+
static Status val_to_word(Value v, Type *t, Location where, U64 *w) {
switch (t->kind) {
case TYPE_BUILTIN:
@@ -49,16 +79,19 @@ static Status val_to_word(Value v, Type *t, Location where, U64 *w) {
}
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(double) == 8); /* if either of these assertions fails, you'll need to use libffcall */
possibly_static_assert(sizeof(float) == 4);
- FnPtr fn_ptr = msvc_get_fn_ptr(ffmgr, fn, call_where);
+ FnPtr fn_ptr = foreign_get_fn_ptr(ffmgr, fn, call_where);
+ /* @OPTIM: use alloca/_malloca if available */
U64 *words = err_malloc(nargs * sizeof *words);
+ bool *is_float = err_malloc(nargs);
U64 *word = words;
char *type = (char *)arg_types;
for (size_t i = 0; i < nargs; ++i) {
if (!val_to_word(args[i], (Type *)type, call_where, word))
return false;
+ is_float[i] = type_is_float((Type *)type);
type += arg_types_stride;
++word;
}
@@ -102,7 +135,7 @@ static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type,
switch (kind) {
case 0: {
- U64 r = win64_call(fn_ptr, words, (I64)nargs);
+ U64 r = foreign_calli(fn_ptr, words, (I64)nargs, is_float);
switch (ret_type->kind) {
case TYPE_BUILTIN:
switch (ret_type->builtin) {
@@ -127,12 +160,13 @@ static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type,
}
} break;
case 1:
- ret->f32 = win64_callf(fn_ptr, words, (I64)nargs);
+ ret->f32 = foreign_callf(fn_ptr, words, (I64)nargs, is_float);
break;
case 2:
- ret->f64 = win64_calld(fn_ptr, words, (I64)nargs);
+ ret->f64 = foreign_calld(fn_ptr, words, (I64)nargs, is_float);
break;
}
free(words);
+ free(is_float);
return true;
}
diff --git a/foreign_avcall.c b/foreign_avcall.c
index cce52a1..8c0fe40 100644
--- a/foreign_avcall.c
+++ b/foreign_avcall.c
@@ -5,10 +5,6 @@
*/
/* 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
@@ -24,7 +20,6 @@ typedef struct {
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#include <avcall.h>
-#include <dlfcn.h>
#if SCHAR_MAX != 127
@@ -295,42 +290,7 @@ static Status arg_list_add(av_alist *arg_list, Value val, Type *type, Location w
#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;
- }
-
+ FnPtr fn_ptr = foreign_get_fn_ptr(ffmgr, fn, call_where);
av_alist arg_list;
if (!arg_list_start(&arg_list, fn_ptr, ret, ret_type, call_where))
return false;
diff --git a/foreign_msvc.c b/foreign_msvc.c
index 86f5519..6a8bb7f 100644
--- a/foreign_msvc.c
+++ b/foreign_msvc.c
@@ -9,7 +9,7 @@ typedef struct {
HMODULE handle;
} Library;
-static FnPtr msvc_get_fn_ptr(ForeignFnManager *ffmgr, FnExpr *fn, Location call_where) {
+static FnPtr foreign_get_fn_ptr(ForeignFnManager *ffmgr, FnExpr *fn, Location call_where) {
FnPtr fn_ptr = fn->foreign.fn_ptr;
if (!fn_ptr) {
assert(fn->flags & FN_EXPR_FOREIGN);
@@ -41,8 +41,10 @@ static FnPtr msvc_get_fn_ptr(ForeignFnManager *ffmgr, FnExpr *fn, Location call_
return fn_ptr;
}
-#ifdef _WIN64
-#include "foreign_msvc64.c"
+#ifdef FOREIGN_USE_AVCALL
+#include "foreign_avcall.c"
+#elif defined _WIN64
+#include "foreign64.c"
#else
#include "foreign_msvc32.c"
#endif
diff --git a/foreign_msvc32.c b/foreign_msvc32.c
index d2a116d..35b09db 100644
--- a/foreign_msvc32.c
+++ b/foreign_msvc32.c
@@ -169,7 +169,7 @@ static double (*const msvc_callf[11])(FnPtr fn, Word *w) = {
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 = msvc_get_fn_ptr(ffmgr, fn, call_where);
+ FnPtr fn_ptr = foreign_get_fn_ptr(ffmgr, fn, call_where);
Word words[10];
Word *word = words;
diff --git a/foreign_unix.c b/foreign_unix.c
new file mode 100644
index 0000000..3b4222a
--- /dev/null
+++ b/foreign_unix.c
@@ -0,0 +1,51 @@
+#include <dlfcn.h>
+typedef struct {
+ void *handle;
+} Library;
+
+static FnPtr foreign_get_fn_ptr(ForeignFnManager *ffmgr, FnExpr *fn, Location call_where) {
+ 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;
+ }
+ return fn_ptr;
+}
+
+#ifdef FOREIGN_USE_AVCALL
+#include "foreign_avcall.c"
+#elif defined __x86_64__
+#include "foreign64.c"
+#else
+#include "foreign_avcall.c"
+#endif
diff --git a/main.c b/main.c
index de2c818..1b2dbc0 100644
--- a/main.c
+++ b/main.c
@@ -8,7 +8,6 @@
/*
@TODO:
-win64 #foreign
allow
#include "foo.toc", foo;
#include "foo.toc", foo;
diff --git a/systemv64call.asm b/systemv64call.asm
new file mode 100644
index 0000000..e12f304
--- /dev/null
+++ b/systemv64call.asm
@@ -0,0 +1,267 @@
+;;; Call SystemV x64 functions dynamically
+;;; Written in NASM
+;;; This implements the SystemV calling convention (which is used by Linux and OS X) so that
+;;; you can call functions with a variable (i.e. not known at compile time) number of arguments.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; This is free and unencumbered software released into the public domain.
+;;
+;; Anyone is free to copy, modify, publish, use, compile, sell, or
+;; distribute this software, either in source code form or as a compiled
+;; binary, for any purpose, commercial or non-commercial, and by any
+;; means.
+;;
+;; In jurisdictions that recognize copyright laws, the author or authors
+;; of this software dedicate any and all copyright interest in the
+;; software to the public domain. We make this dedication for the benefit
+;; of the public at large and to the detriment of our heirs and
+;; successors. We intend this dedication to be an overt act of
+;; relinquishment in perpetuity of all present and future rights to this
+;; software under copyright law.
+;;
+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+;; OTHER DEALINGS IN THE SOFTWARE.
+;;
+;; For more information, please refer to <http://unlicense.org/>
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;To compile this:
+;; first get nasm: https://nasm.us/
+;; add it to your path, then do:
+;; nasm -f elf64 systemv64call.asm
+;; You will get systemv64call.o
+;;You can use it like this (in C/C++):
+;; typedef void (*FnPtr)();
+;; extern unsigned long systemv64_call(FnPtr fn, void *args, long nargs, bool *is_float);
+;; extern float systemv64_callf(FnPtr fn, void *args, long nargs, bool *is_float);
+;; extern double systemv64_calld(FnPtr fn, void *args, long nargs, bool *is_float);
+;; extern SomeType systemv64_call_other(FnPtr fn, void *args, long nargs, bool *is_float);
+;; (all of these refer to the same actual function)
+;; Let's say you want to call the function:
+;; float foo(double a, float b, int c, unsigned long long d) {
+;; return (float)a + b*(float)c - (float)d;
+;; }
+;; with the arguments a = 3.4, b = 3.5f, c = -73, d = 46.
+;; First, you need to convert all the arguments to a single data type, so that
+;; they can call be stored in one array. You should probably convert them all to
+;; as unsigned long/uint64_ts (you can also do all doubles). For integers, you can just
+;; cast them to unsigned long/uint64_t. For floating point numbers, you need to do something
+;; a bit dubious:
+;; double x = 3.4;
+;; uint64_t num = *(uint64_t *)&x;
+;; float y = 3.5f;
+;; uint64_t num2 = (uint64_t) *(uint32_t *)&y;
+;; (The cast to uint64_t on that last line is unnecessary, unless you have very strict warnings on.)
+;; Now store them all in an array:
+;; uint64_t args[4] = {num, num2, (uint64_t)-73, 46};
+;; Finally, you will need an is_float array. is_float[i] is true if the ith argument is floating-point,
+;; and false if it's an integer/pointer. So in this case it would be:
+;; bool is_float[4] = {true, true, false, false};
+;; (if you are using C89, you can use unsigned char instead of bool, 0 instead of false, and 1 instead of true)
+;; Now use systemv64_callf (since the function returns a float).
+;; float ret = systemv64_callf((FnPtr)foo, args, is_float, 4);
+;; printf("foo returned: %f\n", ret);
+;; Here's the full code:
+;; extern float systemv64_callf(FnPtr fn, void *args, bool *is_float, long nargs);
+;; float foo(double a, float b, int c, unsigned long long d) {
+;; return (float)a + b*(float)c - (float)d;
+;; }
+;; int main(void) {
+;; double x = 3.4;
+;; uint64_t num = *(uint64_t *)&x;
+;; float y = 3.5f;
+;; uint64_t num2 = (uint64_t) *(uint32_t *)&y;
+;; uint64_t args[4] = {num, num2, (uint64_t)-73, 46};
+;; bool is_float[4] = {true, true, false, false};
+;; float ret = systemv64_callf((FnPtr)foo, args, 4, is_float);
+;; printf("foo returned: %f\n", ret);
+;; return 0;
+;; }
+;;Why might you need this?
+;; Sometimes you don't know how many arguments you're gonna call a function with at compile time.
+;; For example, maybe you're making an interpreted programming language which calls out to C.
+;; Usually it will be with a function pointer you got from dlsym.
+;;If you find a bug,
+;; Please email pommicket@pommicket.com
+global systemv64_call
+global systemv64_callf
+global systemv64_calld
+global systemv64_call_other
+
+; rdi - fn - function pointer
+; rsi - args - arguments to the function
+; rdx - nargs - number of arguments
+; rcx - is_float - is each argument floating point?
+systemv64_call:
+systemv64_callf:
+systemv64_calld:
+systemv64_call_other:
+ sub rsp, 32
+ mov [rsp+24], rbx ; save non-volatile register values
+ mov [rsp+16], rbp
+ mov [rsp+8], r12
+ mov [rsp], r13
+
+ mov rbp, rsp ; save stack pointer
+ mov r13, rdi ; save function pointer
+
+ ; this section here calculates the number of floating point and integer arguments
+ mov r10, 0 ; integer index
+ mov r11, 0 ; floating point index
+ mov rbx, 0 ; arg index
+.fp_loop:
+ cmp byte [rcx], 0
+ jne .fp_loop_float
+ ; integer argument
+ add r10, 1
+ jmp .fp_continue
+ .fp_loop_float:
+ add r11, 1
+ .fp_continue:
+ inc rbx ; ++arg_index
+ inc rcx ; ++is_float
+ cmp rbx, rdx ; if arg_index < nargs
+ jl .fp_loop
+
+ ; r10 now holds the number of integer arguments, and r11 holds the number of floating point arguments
+
+ ; we need to calculate the number of stack arguments so we can align the stack properly
+ mov rbx, 0 ; num_stack_args
+ lea r12, [r10-6] ; number of integer args
+ cmp r12, 0
+ jle .skip_int
+ add rbx, r12 ; add int stack args
+ .skip_int:
+ lea r12, [r11-8] ; number of float args
+ cmp r12, 0
+ jle .skip_flt
+ add rbx, r12 ; add float stack args
+ .skip_flt:
+
+ ; align the stack
+ lea rbx, [rsp+8*rbx] ; where rsp will be after we push the arguments
+ and rbx, 0xf ; calculate future alignment
+ sub rsp, rbx ; align the stack
+
+ mov rax, r11 ; save number of floating point arguments (needs to be in rax, at least for varargs. I'm not sure why)
+ ; we go right to left because that's the order stuff's put on the stack
+ lea r12, [rsi+8*rdx] ; arg = &args[nargs]
+ mov rbx, rcx ; is_arg_fp = &is_fp[nargs]
+.loop:
+ ; if r10 (int index) and r11 (float index) are both 0, ...
+ cmp r10, 0
+ jne .skip_fp_check
+ cmp r11, 0
+ je .loop_end ; ... break out of the loop
+.skip_fp_check:
+ sub r12, 8 ; --arg
+ dec rbx ; --is_arg_fp
+
+ cmp byte [rbx], 0 ; is it float?
+ jne .float
+
+ dec r10 ; --int_arg_index
+
+ cmp r10, 0
+ jg .after_1st
+ ; 1st integer argument
+ mov rdi, qword [r12]
+ jmp .loop
+.after_1st:
+ cmp r10, 1
+ jg .after_2nd
+ ; 2nd int argument
+ mov rsi, qword [r12]
+ jmp .loop
+.after_2nd:
+ cmp r10, 2
+ jg .after_3rd
+ ; 3rd int argument
+ mov rdx, qword [r12]
+ jmp .loop
+.after_3rd:
+ cmp r10, 3
+ jg .after_4th
+ ; 4th int argument
+ mov rcx, qword [r12]
+ jmp .loop
+.after_4th:
+ cmp r10, 4
+ jg .after_5th
+ ; 5th int argument
+ mov r8, qword [r12]
+ jmp .loop
+.after_5th:
+ cmp r10, 5
+ jg .stack_arg
+ ; 6th int argument
+ mov r9, qword [r12]
+ jmp .loop
+
+.float:
+ dec r11 ; --float_arg_index
+ cmp r11, 0
+ jg .after_1stf
+ ; 1st float argument
+ movsd xmm0, qword [r12]
+ jmp .loop
+.after_1stf:
+ cmp r11, 1
+ jg .after_2ndf
+ ; 2nd float argument
+ movsd xmm1, qword [r12]
+ jmp .loop
+.after_2ndf:
+ cmp r11, 2
+ jg .after_3rdf
+ ; 3rd float argument
+ movsd xmm2, qword [r12]
+ jmp .loop
+.after_3rdf:
+ cmp r11, 3
+ jg .after_4thf
+ ; 4th float argument
+ movsd xmm3, qword [r12]
+ jmp .loop
+.after_4thf:
+ cmp r11, 4
+ jg .after_5thf
+ ; 5th float argument
+ movsd xmm4, qword [r12]
+ jmp .loop
+.after_5thf:
+ cmp r11, 5
+ jg .after_6thf
+ ; 6th float argument
+ movsd xmm5, qword [r12]
+ jmp .loop
+.after_6thf:
+ cmp r11, 6
+ jg .after_7thf
+ ; 7th float argument
+ movsd xmm6, qword [r12]
+ jmp .loop
+.after_7thf:
+ cmp r11, 7
+ jg .stack_arg
+ ; 8th float argument
+ movsd xmm7, qword [r12]
+ jmp .loop
+.stack_arg:
+ ; argument pushed on the stack (>6th integer, >8th float argument)
+ push qword [r12]
+ jmp .loop
+.loop_end:
+
+ call r13
+
+ mov rsp, rbp ; restore stack pointer (stored here at top of function)
+ mov r13, [rsp]; restore non-volatile register values
+ mov r12, [rsp+8]
+ mov rbp, [rsp+16]
+ mov rbx, [rsp+24]
+ add rsp, 32
+ ret
diff --git a/systemv64call_test.c b/systemv64call_test.c
new file mode 100644
index 0000000..3febbba
--- /dev/null
+++ b/systemv64call_test.c
@@ -0,0 +1,149 @@
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ For more information, please refer to <http://unlicense.org/>
+
+should output:
+123 456 789 101 112 -131 -415 -161 -718 19 999 888 2626183732628
+2626183734690
+1.414214
+Hello 3.000000 -2848239.700000 -123 456 789 43873243.234982 111.100000 222.200000 333.300000 444.400000 555.500000 666.600000
+126
+foo returned: -298.100006
+*/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h>
+
+
+typedef void (*FnPtr)();
+
+extern uint64_t systemv64_call(FnPtr fn, void *args, int64_t nargs, bool *is_fp);
+extern float systemv64_callf(FnPtr fn, void *args, int64_t nargs, bool *is_fp);
+extern double systemv64_calld(FnPtr fn, void *args, int64_t nargs, bool *is_fp);
+typedef struct {
+ double x,y,z;
+} Point;
+extern Point systemv64_call_other(FnPtr fn, void *args, bool *is_fp, int64_t nargs);
+
+#define arr_sz(x) (sizeof (x) / sizeof *(x))
+
+
+float foo(double a, float b, int c, unsigned long long d) {
+ return (float)a + b*(float)c - (float)d;
+}
+int main2(void) {
+ double x = 3.4;
+ uint64_t num = *(uint64_t *)&x;
+ float y = 3.5f;
+ uint64_t num2 = (uint64_t) *(uint32_t *)&y;
+ uint64_t args[4] = {num, num2, (uint64_t)-73, 46};
+ bool is_float[4] = {true, true, false, false};
+ float ret = systemv64_callf((FnPtr)foo, args, 4, is_float);
+ printf("foo returned: %f\n", ret);
+ return 0;
+}
+
+long long bar(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, long long m) {
+ printf("%d %d %d %d %d %d %d %d %d %d %d %d %lld\n", a,b,c,d,e,f,g,h,i,j,k,l,m);
+ return a+b+c+d+e+f+g+h+i+j+k+l+m;
+}
+
+Point mkpoint(int a, int b, int c) {
+ Point ret = {a,b,c};
+ return ret;
+}
+
+int main(void) {
+ uint64_t params[13] = {
+ 123,
+ 456,
+ 789,
+ 101,
+ 112,
+ -131,
+ -415,
+ -161,
+ -718,
+ 19,
+ 999,
+ 888,
+ 2626183732628LL
+ };
+ bool is_fp[arr_sz(params)] = {
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0
+ };
+ long long ret = (long long)systemv64_call((FnPtr)bar, params, arr_sz(params), is_fp);
+ printf("%lld\n",ret);
+
+ float two = 2.0f;
+ uint64_t params2[] = {
+ *(uint32_t *)&two
+ };
+ bool is_fp2[arr_sz(params)] = {1};
+ float ret2 = systemv64_callf((FnPtr)sqrtf, params2, arr_sz(params2), is_fp2);
+ printf("%f\n",ret2);
+
+
+ double nums[] = {
+ 3.0,
+ -2848239.7,
+ 43873243.234982,
+ 111.1,
+ 222.2,
+ 333.3,
+ 444.4,
+ 555.5,
+ 666.6,
+ 777.7,
+ 888.8,
+ 999.9
+ };
+
+ uint64_t params3[] = {
+ (uint64_t)"Hello %f %f %d %d %d %f %f %f %f %f %f %f\n",
+ *(uint64_t *)&nums[0],
+ *(uint64_t *)&nums[1],
+ -123,
+ 456,
+ 789,
+ *(uint64_t *)&nums[2],
+ *(uint64_t *)&nums[3],
+ *(uint64_t *)&nums[4],
+ *(uint64_t *)&nums[5],
+ *(uint64_t *)&nums[6],
+ *(uint64_t *)&nums[7],
+ *(uint64_t *)&nums[8],
+
+ };
+
+ bool is_fp3[arr_sz(params3)] = {0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1};
+ int ret3 = systemv64_call((FnPtr)printf, params3, arr_sz(params3), is_fp3);
+ printf("%d\n",ret3);
+ main2();
+ return 0;
+}
diff --git a/test.toc b/test.toc
index 5117906..b63bb2f 100644
--- a/test.toc
+++ b/test.toc
@@ -21,6 +21,7 @@ main ::= fn() {
io.puti(f.b.f.k);
io.puti(f.b.f.b.f.k);
}
+main();
slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) {
head = slice[0];
diff --git a/toc.c b/toc.c
index 6a84170..b5660f9 100644
--- a/toc.c
+++ b/toc.c
@@ -153,10 +153,10 @@ static Location token_location(File *file, Token *t);
#include "parse.c"
#if COMPILE_TIME_FOREIGN_FN_SUPPORT
-#if defined _MSC_VER && !defined COMPILE_TIME_FOREIGN_FN_AVCALL
+#if defined _MSC_VER
#include "foreign_msvc.c"
-#else
-#include "foreign_avcall.c"
+#elif defined __unix__ || defined __OSX__
+#include "foreign_unix.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) {