diff options
-rw-r--r-- | build.bat | 1 | ||||
-rw-r--r-- | build32.bat | 2 | ||||
-rw-r--r-- | foreign_msvc.c | 43 | ||||
-rw-r--r-- | foreign_msvc32.c | 46 | ||||
-rw-r--r-- | foreign_msvc64.c | 133 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | test.toc | 48 | ||||
-rw-r--r-- | toc.c | 2 | ||||
-rw-r--r-- | win64call.asm | 174 | ||||
-rw-r--r-- | win64call_test.c | 162 |
10 files changed, 563 insertions, 49 deletions
diff --git a/build.bat b/build.bat deleted file mode 100644 index 50f2fbc..0000000 --- a/build.bat +++ /dev/null @@ -1 +0,0 @@ -cl /DTOC_DEBUG /W3 /wd4146 /D_CRT_SECURE_NO_WARNINGS /Od /Fe:toc.exe /DEBUG /Zi main.c diff --git a/build32.bat b/build32.bat new file mode 100644 index 0000000..287e2a5 --- /dev/null +++ b/build32.bat @@ -0,0 +1,2 @@ +if x%1 == xrelease cl /W3 /wd4146 /D_CRT_SECURE_NO_WARNINGS /O2 /Fe:toc.exe main.c +if x%1 == x cl /DTOC_DEBUG /W3 /wd4146 /D_CRT_SECURE_NO_WARNINGS /Od /Fe:toc.exe /DEBUG /Zi main.c diff --git a/foreign_msvc.c b/foreign_msvc.c new file mode 100644 index 0000000..1e1cf7c --- /dev/null +++ b/foreign_msvc.c @@ -0,0 +1,43 @@ +#include <windows.h> + +typedef struct { + HMODULE handle; +} Library; + +static FnPtr msvc_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); + 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; + } + return fn_ptr; +} + +#ifdef _WIN64 +#include "foreign_msvc64.c" +#else +#include "foreign_msvc32.c" +#endif diff --git a/foreign_msvc32.c b/foreign_msvc32.c index 8ab61a2..89928e4 100644 --- a/foreign_msvc32.c +++ b/foreign_msvc32.c @@ -1,24 +1,13 @@ /* 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 +but I don't think there's any other way that doesn't involve assembly */ -#include <windows.h> - typedef size_t Word; - #if SIZE_MAX != U32_MAX -/* -@TODO -x64 has its own calling convention -*/ -#error "Calling #foreign functitons at compile time only works on 32-bit Windows, not 64-bit." +#error "What's going on? The 32-bit Windows file was included, but size_t isn't 32 bits!" #endif -typedef struct { - HMODULE handle; -} Library; - /* f64s take 2 words */ static Status val_to_words(Value v, Type *t, Location where, Word *w) { switch (t->kind) { @@ -171,37 +160,12 @@ static double (*const msvc_callf[11])(FnPtr fn, Word *w) = { 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; - } + FnPtr fn_ptr = msvc_get_fn_ptr(ffmgr, fn, call_where); + Word words[10]; Word *word = words; char *type = (char *)arg_types; diff --git a/foreign_msvc64.c b/foreign_msvc64.c new file mode 100644 index 0000000..4bbb643 --- /dev/null +++ b/foreign_msvc64.c @@ -0,0 +1,133 @@ +#if SIZE_MAX != U64_MAX +#error "What's going on? The 64-bit Windows file was included, but size_t isn't 64 bits!" +#endif + +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 Status val_to_word(Value v, Type *t, Location where, U64 *w) { + switch (t->kind) { + case TYPE_BUILTIN: + switch (t->builtin) { + case BUILTIN_I8: *w = (U64)v.i8; break; + case BUILTIN_I16: *w = (U64)v.i16; break; + case BUILTIN_I32: *w = (U64)v.i32; break; + case BUILTIN_U8: *w = (U64)v.u8; break; + case BUILTIN_U16: *w = (U64)v.u16; break; + case BUILTIN_U32: *w = (U64)v.u32; break; + case BUILTIN_I64: *w = (U64)v.i64; break; + case BUILTIN_U64: *w = v.u64; break; + case BUILTIN_F32: *w = (U64)*(U32 *)&v.f32; break; + case BUILTIN_F64: *w = *(U64 *)&v.f64; break; + case BUILTIN_CHAR: *w = (U64)v.charv; break; + case BUILTIN_BOOL: *w = (U64)v.boolv; break; + case BUILTIN_TYPE: + case BUILTIN_VARARGS: + case BUILTIN_NMS: + case BUILTIN_VOID: + goto unsupported; + } + break; + case TYPE_PTR: + *w = (U64)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; +} + +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); + + U64 *words = err_malloc(nargs * sizeof *words); + U64 *word = words; + char *type = (char *)arg_types; + for (size_t i = 0; i < nargs; ++i) { + if (!val_to_words(args[i], (Type *)type, call_where, word)) + return false; + type += arg_types_stride; + ++word; + } + int kind = 0; /* 0=>integer, 1=>f32, 2=>f64 */ + 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: + kind = 1; + break; + case BUILTIN_F64: + kind = 2; + 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; + } + } + + switch (kind) { + case 0: { + U64 r = win64_call(fn_ptr, words, (I64)nargs); + switch (ret_type->kind) { + case TYPE_BUILTIN: + switch (ret_type->builtin) { + case BUILTIN_I8: ret->i8 = (I8)r; break; + case BUILTIN_I16: ret->i16 = (I16)r; break; + case BUILTIN_I32: ret->i32 = (I32)r; break; + case BUILTIN_I64: ret->i64 = (I64)r; break; + case BUILTIN_U8: ret->u8 = (U8)r; break; + case BUILTIN_U16: ret->u16 = (U16)r; break; + case BUILTIN_U32: ret->u32 = (U32)r; break; + case BUILTIN_U64: ret->u64 = (U64)r; break; + case BUILTIN_BOOL: ret->boolv = (bool)r; break; + case BUILTIN_CHAR: ret->charv = (char)r; break; + case BUILTIN_VOID: (void)r; break; + default: assert(0); break; + } + break; + case TYPE_PTR: + ret->ptr = (void *)r; + break; + default: assert(0); break; + } + } break; + case 1: + ret->f32 = win64_callf(fn_ptr, words, (I64)nargs); + break; + case 2: + ret->f64 = win64_calld(fn_ptr, words, (I64)nargs); + break; + } + free(words); + return true; +} @@ -8,7 +8,6 @@ /* @TODO: -fix puti(puti(x)) win64 #foreign allow #include "foo.toc", foo; @@ -1,9 +1,47 @@ -foo ::= fn(x: int) int { - 2*x +#include "std/io.toc", io; +#include "std/mem.toc"; + +main ::= fn() { + nums := news(int, 10); + for x, i := &nums { + *x = i*i; + } + l := slice_to_ll(nums); + p := &l; + while p { + io.puti(p.head); + p = p.tail; + } + f: Foo; + f.k = -173; + f.b = new(Bar); + f.b.f.b = new(Bar); + f.b.f.b.f.k = 9; + io.puti(f.k); + io.puti(f.b.f.k); + io.puti(f.b.f.b.f.k); +} + +slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) { + head = slice[0]; + if slice.len == 1 { + tail = null; + } else { + tail = new(LinkedList(t)); + *tail = slice_to_ll(slice[1:]); + } } -main ::=fn(){ - foo(foo(10)); +LinkedList ::= struct (of :: Type) { + head: of; + tail: &LinkedList(of); +} + +Foo ::= struct { + k: int; + b: &Bar; +} +Bar ::= struct { + f: Foo; } -main(); @@ -154,7 +154,7 @@ static Location token_location(File *file, Token *t); #if COMPILE_TIME_FOREIGN_FN_SUPPORT #if defined _MSC_VER && !defined COMPILE_TIME_FOREIGN_FN_AVCALL -#include "foreign_msvc32.c" +#include "foreign_msvc.c" #else #include "foreign_avcall.c" #endif diff --git a/win64call.asm b/win64call.asm new file mode 100644 index 0000000..5c1c76a --- /dev/null +++ b/win64call.asm @@ -0,0 +1,174 @@ +;;; Call C functions with a dynamic number of arguments x64 MSVC ;;; +;;; Written in NASM ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 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 win64 win64call.asm +;; You will get win64call.lib +;;To use this in a C/C++ program: +;; typedef void (*FnPtr)(); +;; extern unsigned long long win64_call(FnPtr fn, void *args, long long nargs); +;; extern float win64_callf(FnPtr fn, void *args, long long nargs); +;; extern double win64_calld(FnPtr fn, void *args, long long nargs); +;; extern SomeType win64_call_other(FnPtr fn, void *args, long long nargs); +;; (all of these refer to the same actual function) +;; With MSVC's calling convention, all arguments are treated as if they were 64-bit. +;; This means you need to convert integer arguments to unsigned long long/uint64_t before using them. +;; So if you have integer arguments, you probably want to pass an unsigned long long * for args. +;; Floating point arguments can either be "reinterpreted" as unsigned long longs (see 2nd example), or +;; you can pass a double *for args instead. +;; &((unsigned long long *)args)[i] should be a pointer to the ith argument. +;; If you have a 1, 2, 4, or 8 byte struct argument, convert it to an integer, then pass it (keep in mind that +;; if your struct is 8 bytes but not aligned to 8 bytes, the *(uint64_t *)&x trick will cause an unaligned read). +;; Otherwise, pass it by pointer. +;; For returning structs: if your type is 1, 2, 4, or 8 bytes and is POD, it will be returned as an integer. +;; Otherwise, you need to pass a pointer to the struct as the first argument. +;; for more info see https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention +;;So, if you want to call it: +;; simple example (integer arguments): +;; int foo(int a, int b, int c) { +;; return a+b+c; +;; } +;; int main() { +;; FnPtr fn = (FnPtr)foo; +;; unsigned long long args[3] = { +;; -1000, -3, 65 +;; }; +;; int ret = (int)win64_call(fn, args, 3); +;; printf("%d\n", ret); +;; } +;; more involved example (with floating-point numbers): +;; float bar(float a, double b, int c, double d, long e) { +;; return a-(float)b + sinf((float)c) - (float)cos(d) + (float)e; +;; } +;; int main() { +;; FnPtr fn = (FnPtr)bar; +;; float a = -1.6f; +;; double b = 3.0, d = 33.7; +;; unsigned long long args[5] = { +;; *(uint32_t *)&a, *(uint64_t *)&b, -12, *(uint64_t *)&d, 4 +;; }; +;; float ret = win64_callf(fn, args, 5); +;; printf("%f\n", ret); +;; } +;;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. +;; (python uses libffi but that's not easy to compile...) +;; Usually it will be with a function pointer you got from GetProcAddress. +;;If you find a bug... +;; Please let me know by emailing pommicket@pommicket.com. + +global win64_call +global win64_callf +global win64_calld +global win64_call_other + +section .text + +; takes: +; rcx - fn - pointer to function +; rdx - args - pointer to arguments +; r8 - nargs - number of arguments +win64_call: +win64_callf: +win64_calld: +win64_call_other: + ; use "shadow store" to save rsi + mov [rsp+24], rsi + mov rax, rcx ; function pointer (rcx may be overwritten) + mov r11, rdx ; args (rdx may be overwritten) + mov r10, r8 ; index_of_argument + mov rsi, rsp ; save original stack pointer + + ; we need to make sure the stack pointer is aligned to 16 bytes when the function is called. + ; for some reason, even though we have to align it or stuff breaks, sometimes when + ; our function is called, it's not 16-byte aligned :/ + ; find number of stack arguments: + cmp r8, 4 + jg .align_stack + mov r8, 0 ; if there are <=4 arguments, set the number of stack arguments to 0 +.align_stack: + and r8, 1 ; is the number of stack arguments even or odd? + lea r8, [rsp+8*r8] + ; r8 is now equivalent to where the stack pointer will be (mod 16) when we call the function + and r8, 0xf ; take r8 mod 16 + sub rsp, r8 ; align the stack pointer so when we call the function it's 16-byte aligned + lea r11, [r11+8*r10] ; go to end of arguments--we go from right to left + ; because that's the order things are pushed onto the stack + + cmp r10, 0 + je .loop_end ; no arguments +.loop: + dec r10 ; --index_of_argument + sub r11, 8 ; --arg + cmp r10, 0 + jg .after_1st +; NOTE: we have to set both the integer and floating-point register for every argument because +; a. we don't know if it's integer or floating point +; b. varargs expects to have the value in both registers + ; 1st argument + mov rcx, qword [r11] + movsd xmm0, qword [r11] + jmp .continue +.after_1st: + cmp r10, 1 + jg .after_2nd + ; 2nd argument + mov rdx, qword [r11] + movsd xmm1, qword [r11] + jmp .continue +.after_2nd: + cmp r10, 2 + jg .after_3rd + ; 3rd argument + mov r8, qword [r11] + movsd xmm2, qword [r11] + jmp .continue +.after_3rd: + cmp r10, 3 + jg .after_4th + ; 4th argument + mov r9, qword [r11] + movsd xmm3, qword [r11] + jmp .continue +.after_4th: + ; additional argument + push qword [r11] +.continue: + cmp r10, 0 ; if index_of_argument > 0 + jg .loop +.loop_end: + sub rsp, 32 ; "shadow store" + call rax ; function pointer stored here before + + mov rsp, rsi ; restore original stack pointer + ; restore rsi + mov rsi, [rsp+24] + ret diff --git a/win64call_test.c b/win64call_test.c new file mode 100644 index 0000000..f1950b5 --- /dev/null +++ b/win64call_test.c @@ -0,0 +1,162 @@ +/* +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/> + +Output should be: +-12387387222 54873482123 4598345 -4 +1 returned: 54873482123 +Hello 5 6 7 8 9 +Hello 5 6 7 8 9 hey 19249488282934 -1 +2 returned: 38 +3 returned: 1.414214 +4 returned: 1.414214 +5.600000 -1.300000 2.000000 -6 2.000000 55 1200.000000 +5 returned: 4.300000 +6 returned: -1.600000 +7 returned: -938 +8 returned: 0.590889 +*/ + + +#include <stdlib.h> +#include <stdio.h> +#include <windows.h> +#include <stdint.h> +#include <stdbool.h> +#include <math.h> + +typedef void (*FnPtr)(); +extern uint64_t win64_call(FnPtr fn, void *args, int64_t nargs); +extern float win64_callf(FnPtr fn, void *args, int64_t nargs); +extern double win64_calld(FnPtr fn, void *args, int64_t nargs); +float asdf(float a, double b, int c, double d, long e, float f) { + return (float)f; +} +void foobar(void) { + FnPtr fn = (FnPtr)asdf; + float a = -1.6f; + double b = 3.0, d = 33.7; + unsigned long long args[6] = { + *(uint32_t *)&a, *(uint64_t *)&b, -12, *(uint64_t *)&d, 4, *(uint32_t *)&a + }; + float ret = win64_callf(fn, args, 6); + printf("6 returned: %f\n", ret); +} + +uint64_t test_fn(long long a, uint64_t b, uint64_t c, int d) { + printf("%lld %llu %llu %d\n",a,b,c,d); + return b; +} + +double test_fp(double a, double b, float c, int d, float e, int f, double g) { + printf("%f %f %f %d %f %d %f\n", a,b,c,d,e,f,g); + return a+b; +} +#define arr_size(a) (sizeof (a) / sizeof *(a)) + + +int foo(int a, int b, int c) { + return a+b+c; +} +void main1(void) { + FnPtr fn = (FnPtr)foo; + unsigned long long args[3] = { + -1000, -3, 65 + }; + int ret = (int)win64_call(fn, args, 3); + printf("7 returned: %d\n", ret); +} +float bar(float a, double b, int c, double d, long e) { + return a-(float)b + sinf((float)c) - (float)cos(d) + (float)e; +} +void main2(void) { + FnPtr fn = (FnPtr)bar; + float a = -1.6f; + double b = 3.0, d = 33.7; + unsigned long long args[5] = { + *(uint32_t *)&a, *(uint64_t *)&b, -12, *(uint64_t *)&d, 4 + }; + float ret = win64_callf(fn, args, 5); + printf("8 returned: %f\n", ret); +} + +int main(void) { + uint64_t args[] = {-12387387222, 54873482123, 4598345, -4}; + uint64_t x = win64_call(test_fn, args, arr_size(args)); + printf("1 returned: %llu\n", x); + + printf("Hello %d %d %d %d %d\n",5,6,7,8,9); + + + uint64_t args2[] = {(uint64_t)"Hello %d %d %d %d %d %s %llu %d\n", 5, 6, 7, 8, 9, (uint64_t)"hey", 19249488282934, -1}; + uint64_t x2 = win64_call(printf, args2, arr_size(args2)); + printf("2 returned: %llu\n",x2); + +#if 0 + { + float a = 3.7f; + int b = 1; + int c = 2; + int d = 3; + float e = 4.5f; + double f = -1.2; + float ret = test_fp(a,b,c,d,e,f); + printf("%f\n",ret); + } +#endif + + double args3[] = {2.0}; + double x3 = win64_calld(sqrt, args3, arr_size(args3)); + printf("3 returned: %f\n",x3); + + float two = 2.0f; + + uint64_t args4[] = {*(uint32_t *)&two}; + float x4 = win64_callf(sqrtf, args4, arr_size(args4)); + printf("4 returned: %f\n",x4); + + double num1 = 5.6; + double num2 = -1.3; + double num3 = 1200; + uint64_t args5[] = { + *(uint64_t *)&num1, + *(uint64_t *)&num2, + *(uint32_t *)&two, + -6, + *(uint32_t *)&two, + 55, + *(uint64_t *)&num3 + + }; + double x5 = win64_calld(test_fp, args5, arr_size(args5)); + printf("5 returned: %f\n",x5); + + foobar(); + main1(); + main2(); + + return 0; + +} |