# HG changeset patch # User Tassilo Philipp # Date 1661022255 -7200 # Node ID b36a738c8975d0dbd04a21c6bb02711196613a12 # Parent 49b60ca068c24356a970df1b0f1f78237384f17e - dyncallback: fix for calling back win/x64 C++ methods returning non-trivial aggregates (thanks Raphael!) - test/callback_plain_c++: * added test code for C++ method callbacks returning non-trivial aggregates * makefile linker command fix (was linking assuming c and not c++) - test/plain_c++: comments for completeness diff -r 49b60ca068c2 -r b36a738c8975 dyncallback/dyncall_args_x64.c --- a/dyncallback/dyncall_args_x64.c Mon Jul 11 23:17:50 2022 +0200 +++ b/dyncallback/dyncall_args_x64.c Sat Aug 20 21:04:15 2022 +0200 @@ -149,30 +149,28 @@ } -/* A 16 byte struct would be sufficient for System V (because at most two of the four registers can be full). */ -/* But then it's much more complicated to copy the result to the correct registers in assembly. */ -typedef struct { - DClonglong r[2]; /* rax, rdx */ - DCdouble x[2]; /* xmm0, xmm1 */ -} DCRetRegs_SysV; - void dcbReturnAggr(DCArgs *args, DCValue *result, DCpointer ret) { int i; DCaggr *ag = *(args->aggrs++); - if(!ag) { - /* non-trivial aggr: all we can do is to provide the ptr to the output space, user has to make copy */ - result->p = (DCpointer) args->reg_data.i[0]; - return; - } - - if (args->aggr_return_register >= 0) { + if(args->aggr_return_register >= 0) { DCpointer dest = (DCpointer) args->reg_data.i[args->aggr_return_register]; - memcpy(dest, ret, ag->size); + if(ag) + memcpy(dest, ret, ag->size); + else { + /* non-trivial aggr: all we can do is to provide the ptr to the output space, user has to make copy */ + } result->p = dest; } else { #if defined(DC_UNIX) + /* A 16 byte struct would be sufficient for System V (because at most two of the four registers can be full). */ + /* But then it's much more complicated to copy the result to the correct registers in assembly. */ + typedef struct { + DClonglong r[2]; /* rax, rdx */ + DCdouble x[2]; /* xmm0, xmm1 */ + } DCRetRegs_SysV; + /* a max of 2 regs are used in this case, out of rax, rdx, xmm0 and xmm1 */ /* space for 4 qwords is pointed to by (DCRetRegs_SysV*)result */ DClonglong *intRegs = ((DCRetRegs_SysV*)result)->r; diff -r 49b60ca068c2 -r b36a738c8975 test/callback_plain_c++/Makefile.embedded --- a/test/callback_plain_c++/Makefile.embedded Mon Jul 11 23:17:50 2022 +0200 +++ b/test/callback_plain_c++/Makefile.embedded Sat Aug 20 21:04:15 2022 +0200 @@ -11,8 +11,8 @@ .PHONY: all clean install -${APP}: ${OBJS} - ${CC} ${OBJS} ${LDFLAGS} ${LDLIBS} -o ${APP} +${APP}: ${OBJS} + ${CXX} ${OBJS} ${LDFLAGS} ${LDLIBS} -o ${APP} clean: rm -f ${APP} ${OBJS} diff -r 49b60ca068c2 -r b36a738c8975 test/callback_plain_c++/Makefile.generic --- a/test/callback_plain_c++/Makefile.generic Mon Jul 11 23:17:50 2022 +0200 +++ b/test/callback_plain_c++/Makefile.generic Sat Aug 20 21:04:15 2022 +0200 @@ -7,7 +7,7 @@ .PHONY: all clean install all: ${APP} ${APP}: ${OBJS} - ${CC} ${CFLAGS} ${LDFLAGS} ${OBJS} ${LDLIBS} -o ${APP} + ${CXX} ${CFLAGS} ${LDFLAGS} ${OBJS} ${LDLIBS} -o ${APP} clean: rm -f ${APP} ${OBJS} install: diff -r 49b60ca068c2 -r b36a738c8975 test/callback_plain_c++/test_main.cc --- a/test/callback_plain_c++/test_main.cc Mon Jul 11 23:17:50 2022 +0200 +++ b/test/callback_plain_c++/test_main.cc Sat Aug 20 21:04:15 2022 +0200 @@ -111,7 +111,7 @@ char cbNonTrivAggrReturnHandler(DCCallback* cb, DCArgs* args, DCValue* result, void* userdata) { - printf("reached callback\n"); + printf("reached callback (for sig \"%s\")\n", *(const char**)userdata); dcbReturnAggr(args, result, NULL); // this sets result->p to the non-triv aggr space allocated by the calling convention *(NonTriv*)result->p = NonTriv(1, 3); // explicit non-copy ctor and assignment operator, so not using NonTriv's statics a and b @@ -119,31 +119,49 @@ } +// class with single vtable entry, used to get the compiler to generate a +// method call; entry is at beginning w/o offset (see plain_c++/test_main.cc +// about potential vtable entry offsets) +struct Dummy { virtual NonTriv f() = 0; }; + + int testNonTrivAggrReturnCallback() { int ret = 1; + const char *sigs[] = { + ")A", // standard call + "_*p)A" // thiscall w/ this-ptr arg (special retval register handling in win/x64 calling conv) + }; + + + for(int i=0; if() : ((NonTriv(*)())cb)(); int a = NonTriv::a-1; int b = NonTriv::b-1; - printf("successfully returned from callback 1/2\n"); + printf("successfully returned from callback 1/2 of \"%s\"\n", sigs[i]); printf("retval w/ potential retval optimization and copy-init (should be %d %d for init or %d %d for copy, both allowed by C++): %d %d\n", 1, 3, a, b, result.i, result.j); ret = ((result.i == 1 && result.j == 3) || (result.i == a && result.j == b)) && ret; - // avoid copy elision on construction - result.i = result.j = -77; - result = ((NonTriv(*)())cb)(); // potential copy elision + // avoid copy elision on construction + result.i = result.j = -77; + result = is_method ? ((Dummy*)&fakeClass)->f() : ((NonTriv(*)())cb)(); // potential copy elision a = NonTriv::a-1; b = NonTriv::b-1; - printf("successfully returned from callback 2/2\n"); + printf("successfully returned from callback 2/2 of \"%s\"\n", sigs[i]); printf("retval w/ potential retval optimization and copy-init (should be %d %d for init or %d %d for copy, both allowed by C++): %d %d\n", 1, 3, a, b, result.i, result.j); dcbFreeCallback(cb); diff -r 49b60ca068c2 -r b36a738c8975 test/plain_c++/test_main.cc --- a/test/plain_c++/test_main.cc Mon Jul 11 23:17:50 2022 +0200 +++ b/test/plain_c++/test_main.cc Sat Aug 20 21:04:15 2022 +0200 @@ -87,11 +87,16 @@ /* * the layout of the VTable is non-standard and it is not clear what is the initial real first method index. - * so far it turns out that: - * on vc/x86 : 1 - * on GCC/x86 : 2 + * so far it turns out, *iff* dtor is defined, that: + * on msvc/x86 : 1 + * on msvc/x64 : 1 + * on gcc/x86 : 2 + * on gcc/x64 : 2 + * on clang/x86 : 2 + * on clang/x64 : 2 */ +// vtable offset to first func of class Value and class ValueMS, skipping dtor #if defined DC__C_MSVC #define VTBI_BASE 1 #else