Mercurial > pub > dyncall > dyncall
diff dyncall/dyncall_aggregate_x64.c @ 533:71c884e610f0
- integration of patches from Raphael Luba, Thekla, Inc.:
* integration of aggregate-by-value (struct, union) support patch for x64 (win and sysv)
* windows/x64 asm additions to specify how stack unwinds (help for debuggers, exception handling, etc.)
* see Changelog for details
- new calling convention modes for thiscalls (platform agnostic, was specific before)
* new signature character for platform agnostic thiscalls ('*' / DC_SIGCHAR_CC_THISCALL)
- dcCallF(), dcVCallF(), dcArgF() and dcVArgF():
* added support for aggregates-by-value (wasn't part of patch)
* change that those functions don't implicitly call dcReset() anymore, which was unflexible (breaking change)
- added macros to feature test implementation for aggregate-by-value and syscall support
- changed libdyncall_s.lib and libdyncallback_s.lib order in callback test makefiles, as some toolchains are picky about order
- doc:
* man page updates to describe aggregate interface
* manual overview changes to highlight platforms with aggregate-by-value support
- test/plain: replaced tests w/ old/stale sctruct interface with new aggregate one
author | Tassilo Philipp |
---|---|
date | Thu, 21 Apr 2022 13:35:47 +0200 |
parents | |
children | a73a5cd50c19 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dyncall/dyncall_aggregate_x64.c Thu Apr 21 13:35:47 2022 +0200 @@ -0,0 +1,195 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_aggregate_x64.c + Description: + License: + + Copyright (c) 2021-2022 Tassilo Philipp <tphilipp@potion-studios.com> + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#if defined(DC_UNIX) + +#define DC_ONE_8BYTE 8 +#define DC_TWO_8BYTES 2*8 +#define DC_EIGHT_8BYTES 8*8 + +/* helper - long long mask with each byte being X */ +#define LLBYTE(X) ((X)&0xFFULL) +#define SYSVC_CHECK_ALL_CLASSES(X) ((LLBYTE(X)<<56)|(LLBYTE(X)<<48)|(LLBYTE(X)<<40)|(LLBYTE(X)<<32)|(LLBYTE(X)<<24)|(LLBYTE(X)<<16)|(LLBYTE(X)<<8)|LLBYTE(X)) + + +static DCuchar dc_get_sysv_class_for_8byte(const DCaggr *ag, int index, int base_offset) +{ + int qword_offset = index * DC_ONE_8BYTE, i; + DCuchar clz = SYSVC_NONE; + + for(i = 0; i < ag->n_fields; i++) { + const DCfield *f = ag->fields + i; + DCsize offset = base_offset + f->offset; + + /* field outside of qword at index? */ + if(offset >= (qword_offset + DC_ONE_8BYTE) || (offset + f->size * f->array_len) <= qword_offset) + continue; + + DCuchar new_class = SYSVC_NONE; + + switch (f->type) { + case DC_SIGCHAR_BOOL: + case DC_SIGCHAR_CHAR: + case DC_SIGCHAR_UCHAR: + case DC_SIGCHAR_SHORT: + case DC_SIGCHAR_USHORT: + case DC_SIGCHAR_INT: + case DC_SIGCHAR_UINT: + case DC_SIGCHAR_LONG: + case DC_SIGCHAR_ULONG: + case DC_SIGCHAR_LONGLONG: + case DC_SIGCHAR_ULONGLONG: + case DC_SIGCHAR_STRING: + case DC_SIGCHAR_POINTER: + new_class = SYSVC_INTEGER; + break; + case DC_SIGCHAR_FLOAT: + case DC_SIGCHAR_DOUBLE: + new_class = SYSVC_SSE; + break; + case DC_SIGCHAR_AGGREGATE: + new_class = dc_get_sysv_class_for_8byte(f->sub_aggr, index, offset); + break; + /*case DClongdouble, DCcomplexfloat DCcomplexdouble DCcomplexlongdouble etc... -> x87/x87up/complexx87 classes @@@AGGR implement */ + } + + if (clz == new_class) + continue; + + if (clz == SYSVC_NONE) + clz = new_class; + else if (new_class == SYSVC_NONE) + continue; + else if (clz == SYSVC_MEMORY || new_class == SYSVC_MEMORY) + clz = SYSVC_MEMORY; + else if (clz == SYSVC_INTEGER || new_class == SYSVC_INTEGER) + clz = SYSVC_INTEGER; + /* @@@AGGR implement when implementing x87 types + else if ((clz & (SYSVC_X87|SYSVC_X87UP|SYSVC_COMPLEX_X87)) || (new_class & (SYSVC_X87|SYSVC_X87UP|SYSVC_COMPLEX_X87))) + clz = SYSVC_MEMORY;*/ + else + clz = SYSVC_SSE; + } + + return clz; +} + + +static void dc_get_sysv_classes_for_aggr(const DCaggr *ag, DCuchar *classes) +{ + int i; + +#if 1 /* this is the optimized version that only respects types supported by dyncall */ + + if(ag->size > DC_TWO_8BYTES) { /* @@@AGGR not checking if a field is unaligned */ + classes[0] = SYSVC_MEMORY; + return; + } + + /* abi doc: "If one of the classes is MEMORY, the whole argument is passed in memory." */ + classes[0] = dc_get_sysv_class_for_8byte(ag, 0, 0); + if(classes[0] != SYSVC_MEMORY) { + classes[1] = dc_get_sysv_class_for_8byte(ag, 1, 0); + if(classes[1] == SYSVC_MEMORY) + classes[0] = SYSVC_MEMORY; + else + classes[2] = SYSVC_NONE; + } + /* @@@AGGR what would happen with alignment-enforced padding >= 8? Then no field would cover the eightbyte @@@test */ + +#else /* this would be the version following the ABI more closely, to be implemented fully or partly when those types get supported by dyncall */ + + /* abi doc: "If the size of an object is larger than eight qwords, or it + * contains unaligned fields, it has class MEMORY." + * note: ABI specs <v1.0 (2018) specify "four qwords", instead (b/c _m512 was added, later) */ /* @@@AGGR not checking if a field is unaligned */ + if(ag->size > DC_EIGHT_8BYTES) { + classes[0] = SYSVC_MEMORY; + return; + } + + /* classify fields according to each of max 8 qwords */ + for(i = 0; i < DC_SYSV_MAX_NUM_CLASSES; ++i) { + classes[i] = dc_get_sysv_class_for_8byte(ag, i, 0); + + /* abi doc: "If one of the classes is MEMORY, the whole argument is passed in memory." */ + if(classes[i] == SYSVC_MEMORY) { + classes[0] = SYSVC_MEMORY; + return; + } + + /* stop eightbyte classification on first SYSVC_NONE returned */ + /* @@@AGGR what would happen with alignment-enforced padding >= 8? Then no field would cover the eightbyte @@@test */ + if(classes[i] == SYSVC_NONE) + break; + } + + /* Do post merger cleanup */ + + /* abi doc: "If X87UP is not preceded by X87, the whole argument is passed in memory." */ + for(i = 1; i < DC_SYSV_MAX_NUM_CLASSES; ++i) { + if (classes[i-1] == SYSVC_X87 && classes[i] != SYSVC_X87UP) { + classes[0] = SYSVC_MEMORY; + return; + } + } + + /* abi doc: "If the size of the aggregate exceeds two qwords and the first eightbyte isn't + * SSE or any other eightbyte isn't SSEUP, the whole argument is passed in memory." */ + if(ag->size > DC_TWO_8BYTES) { + DClonglong mask = SYSVC_CHECK_ALL_CLASSES(SYSVC_SSEUP|SYSVC_NONE) ^ (LLBYTE(SYSVC_SSE|SYSVC_SSEUP|SYSVC_NONE)<<56); + if((*(DClonglong*)ag->sysv_classes & mask) != *(DClonglong*)ag->sysv_classes) { + classes[0] = SYSVC_MEMORY; + return; + } + } + + /* abi doc: "If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE." */ + for(i = 1; i < DC_SYSV_MAX_NUM_CLASSES; ++i) { + DCuchar clz = classes[i]; + if(classes[i] == SYSVC_SSEUP && !(classes[i-1] & (SYSVC_SSE|SYSVC_SSEUP))) + classes[i] = SYSVC_SSE; + } + +#endif +} + + +void dcFinishAggr(DCaggr *ag) +{ + dc_get_sysv_classes_for_aggr(ag, ag->sysv_classes); + + /* @@@AGGR implement when implementing x87 types + for(i=0; ag->sysv_classes[i] && i<DC_SYSV_MAX_NUM_CLASSES; ++i) + assert((ag->sysv_classes[i] & (SYSVC_MEMORY|SYSVC_INTEGER|SYSVC_SSE)) && "Unsupported System V class detected in struct");*/ +} + +#else + +void dcFinishAggr(DCaggr *ag) +{ +} + +#endif +