Mercurial > pub > dyncall > dyncall
comparison dyncallback/dyncall_callback_x64.S @ 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 | 41e2a01cad32 |
children | 951cbfb5020a |
comparison
equal
deleted
inserted
replaced
532:d4bf63ab9164 | 533:71c884e610f0 |
---|---|
4 Library: dyncallback | 4 Library: dyncallback |
5 File: dyncallback/dyncall_callback_x64.S | 5 File: dyncallback/dyncall_callback_x64.S |
6 Description: Callback Thunk entry for x64 (portasm version) | 6 Description: Callback Thunk entry for x64 (portasm version) |
7 License: | 7 License: |
8 | 8 |
9 Copyright (c) 2011-2018 Daniel Adler <dadler@uni-goettingen.de> | 9 Copyright (c) 2011-2022 Daniel Adler <dadler@uni-goettingen.de>, |
10 Tassilo Philipp <tphilipp@potion-studios.com> | |
10 | 11 |
11 Permission to use, copy, modify, and distribute this software for any | 12 Permission to use, copy, modify, and distribute this software for any |
12 purpose with or without fee is hereby granted, provided that the above | 13 purpose with or without fee is hereby granted, provided that the above |
13 copyright notice and this permission notice appear in all copies. | 14 copyright notice and this permission notice appear in all copies. |
14 | 15 |
27 #include "../portasm/portasm-x64.S" | 28 #include "../portasm/portasm-x64.S" |
28 | 29 |
29 /* structure sizes */ | 30 /* structure sizes */ |
30 | 31 |
31 SET(DCThunk_size,24) | 32 SET(DCThunk_size,24) |
32 SET(DCArgs_size_win64,80) | 33 SET(DCArgs_size_win64,96) /* 8 (stack_ptr) + 4 (reg_count) + 4 (pad_w) + 4 (aggr_return_register) + 4 (pad) + 8*4 (int regs) + 8 (aggrs) + 8*4 (float regs) */ |
33 SET(DCArgs_size_sysv,128) | 34 SET(DCArgs_size_sysv,144) /* 8 (stack_ptr) + 8 (reg_count) + 4 (aggr_return_register) + 4 (pad) + 8*6 (int regs) + 8 (aggrs) + 8*8 (float regs) */ |
34 SET(DCValue_size,8) | 35 SET(DCValue_size,8) |
35 | 36 SET(DCRetRegs_SystemV_size,32) |
36 /* frame local variable offsets relative to %rbp*/ | 37 |
38 /* frame local variable offsets relative to %rbp */ | |
37 | 39 |
38 SET(FRAME_arg0_win64,48) | 40 SET(FRAME_arg0_win64,48) |
39 SET(FRAME_arg0_sysv,16) | 41 SET(FRAME_arg0_sysv,16) |
40 SET(FRAME_return,8) | 42 SET(FRAME_return,8) |
41 SET(FRAME_parent,0) | 43 SET(FRAME_parent,0) |
42 SET(FRAME_DCArgs_sysv,-128) | 44 |
43 SET(FRAME_DCValue_sysv,-136) | 45 /* struct DCCallback layout, relative to ptr passed to functions below via RAX */ |
44 SET(FRAME_DCArgs_win64,-80) | |
45 SET(FRAME_DCValue_win64,-80) | |
46 | |
47 /* struct DCCallback */ | |
48 | 46 |
49 SET(CTX_thunk,0) | 47 SET(CTX_thunk,0) |
50 SET(CTX_handler,24) | 48 SET(CTX_handler,24) |
51 SET(CTX_userdata,32) | 49 SET(CTX_userdata,32) |
52 SET(DCCallback_size,40) | 50 SET(CTX_aggr_ret_reg,40) |
51 SET(CTX_pad,44) | |
52 SET(CTX_aggrs_pp,48) | |
53 SET(DCCallback_size,56) | |
53 | 54 |
54 | 55 |
55 | 56 |
56 GLOBAL(dcCallback_x64_sysv) | 57 GLOBAL(dcCallback_x64_sysv) |
57 BEGIN_PROC(dcCallback_x64_sysv) | 58 BEGIN_PROC(dcCallback_x64_sysv) |
59 PUSH(RBP) | 60 PUSH(RBP) |
60 MOV(RSP,RBP) | 61 MOV(RSP,RBP) |
61 | 62 |
62 /* initialize DCArgs */ | 63 /* initialize DCArgs */ |
63 | 64 |
64 /* float parameters (8 registers spill to DCArgs) */ | 65 /* float parameters (8 registers spill to DCArgs.reg_data) */ |
65 | |
66 SUB(LIT(8*8),RSP) | 66 SUB(LIT(8*8),RSP) |
67 | 67 MOVSD(XMM7, QWORD(RSP,8*7)) /* DCArgs offset 136: float parameter 7 */ |
68 MOVSD(XMM7, QWORD(RSP,8*7)) /* struct offset 120: float parameter 7 */ | 68 MOVSD(XMM6, QWORD(RSP,8*6)) /* DCArgs offset 128: float parameter 6 */ |
69 MOVSD(XMM6, QWORD(RSP,8*6)) /* struct offset 112: float parameter 6 */ | 69 MOVSD(XMM5, QWORD(RSP,8*5)) /* DCArgs offset 120: float parameter 5 */ |
70 MOVSD(XMM5, QWORD(RSP,8*5)) /* struct offset 104: float parameter 5 */ | 70 MOVSD(XMM4, QWORD(RSP,8*4)) /* DCArgs offset 112: float parameter 4 */ |
71 MOVSD(XMM4, QWORD(RSP,8*4)) /* struct offset 96: float parameter 4 */ | 71 MOVSD(XMM3, QWORD(RSP,8*3)) /* DCArgs offset 104: float parameter 3 */ |
72 MOVSD(XMM3, QWORD(RSP,8*3)) /* struct offset 88: float parameter 3 */ | 72 MOVSD(XMM2, QWORD(RSP,8*2)) /* DCArgs offset 96: float parameter 2 */ |
73 MOVSD(XMM2, QWORD(RSP,8*2)) /* struct offset 80: float parameter 2 */ | 73 MOVSD(XMM1, QWORD(RSP,8*1)) /* DCArgs offset 88: float parameter 1 */ |
74 MOVSD(XMM1, QWORD(RSP,8*1)) /* struct offset 72: float parameter 1 */ | 74 MOVSD(XMM0, QWORD(RSP,8*0)) /* DCArgs offset 80: float parameter 0 */ |
75 MOVSD(XMM0, QWORD(RSP,8*0)) /* struct offset 64: float parameter 0 */ | 75 |
76 | 76 /* integer parameters (6 registers spill to DCArgs.reg_data) */ |
77 /* integer parameters (6 registers spill to DCArgs) */ | 77 PUSH(R9) /* DCArgs offset 72: parameter 5 */ |
78 | 78 PUSH(R8) /* DCArgs offset 64: parameter 4 */ |
79 PUSH(R9) /* struct offset 56: parameter 5 */ | 79 PUSH(RCX) /* DCArgs offset 56: parameter 3 */ |
80 PUSH(R8) /* struct offset 48: parameter 4 */ | 80 PUSH(RDX) /* DCArgs offset 48: parameter 2 */ |
81 PUSH(RCX) /* struct offset 40: parameter 3 */ | 81 PUSH(RSI) /* DCArgs offset 40: parameter 1 */ |
82 PUSH(RDX) /* struct offset 32: parameter 2 */ | 82 PUSH(RDI) /* DCArgs offset 32: parameter 0 */ |
83 PUSH(RSI) /* struct offset 24: parameter 1 */ | 83 |
84 PUSH(RDI) /* struct offset 16: parameter 0 */ | 84 MOV(QWORD(RAX, CTX_aggrs_pp), R8) |
85 | 85 PUSH(R8) /* DCArgs offset 24: **aggrs */ |
86 | |
87 /* get val of aggr_return_register from DCCallback struct into r8's LSBs, clear */ | |
88 /* MSBs, write DCarg's pad and aggr_return_register at once */ | |
89 MOVL(DWORD(RAX, CTX_aggr_ret_reg), R8D) /* implicitly zeroes the high bits of R8 */ | |
90 PUSH(R8) /* DCArgs offset 16: pad=0, aggr_return_register=DCCallback.aggr_return_register */ | |
86 /* register counts for integer/pointer and float regs */ | 91 /* register counts for integer/pointer and float regs */ |
87 | 92 PUSH(LIT(0)) /* DCArgs offset 12: fcount */ |
88 PUSH(LIT(0)) /* struct offset 12: fcount */ | 93 /* DCArgs offset 8: icount */ |
89 /* struct offset 8: icount */ | 94 |
90 | 95 LEA(QWORD(RBP,FRAME_arg0_sysv),RDX) /* DCArgs offset 0: *stack_ptr */ |
91 LEA(QWORD(RBP,FRAME_arg0_sysv),RDX) /* struct offset 0: stack pointer */ | |
92 PUSH(RDX) | 96 PUSH(RDX) |
93 | 97 |
94 MOV(RSP,RSI) /* arg 1 RSI : DCArgs* */ | 98 MOV(RSP,RSI) /* arg 1 RSI : DCArgs* */ |
95 | 99 |
96 /* initialize DCValue */ | 100 /* stack space for DCValue or DCRetRegs_SysV (passed to handler as DCValue*) and padding */ |
97 | 101 SUB(LIT(4*8),RSP) /* 4 qwords for DCRetRegs_SysV */ |
98 PUSH(LIT(0)) /* struct offset 0: return value (max long long) */ | 102 |
99 | 103 /* call handler(*ctx, *args, *value, *userdata) - stack must be 16b aligned, here */ |
100 /* call handler( *ctx, *args, *value, *userdata) */ | |
101 | |
102 MOV(RAX,RDI) /* arg 0 RDI : DCCallback* (RAX) */ | 104 MOV(RAX,RDI) /* arg 0 RDI : DCCallback* (RAX) */ |
103 MOV(QWORD(RDI,CTX_userdata),RCX) /* arg 3 RCX : userdata* */ | 105 MOV(QWORD(RDI,CTX_userdata),RCX) /* arg 3 RCX : userdata* */ |
104 MOV(RSP,RDX) /* arg 2 RDX : DCValue* */ | 106 MOV(RSP,RDX) /* arg 2 RDX : DCValue* */ |
105 PUSH(LIT(0)) /* align to 16 bytes */ | 107 |
106 CALL_REG(QWORD(RAX,CTX_handler)) | 108 CALL_REG(QWORD(RAX,CTX_handler)) |
107 | 109 |
108 /* pass return type via registers, handle ints and floats */ | 110 /* get info about return type, use to select how to store reg-based retval */ |
109 | 111 CMPL(LIT(-2/*see C*/), DWORD(RSP, 48)) /* rsp+48 = where r8 (aggr_return_register) was pushed */ |
110 MOV(QWORD(RBP,FRAME_DCValue_sysv),RAX) | 112 |
111 MOVD(RAX,XMM0) | 113 /* if retval is small aggregate via regs */ |
114 JE(scalar_retval) | |
115 | |
116 MOV(QWORD(RSP,0),RAX) | |
117 MOV(QWORD(RSP,8),RDX) | |
118 MOVSD(QWORD(RSP,16),XMM0) /* @@@AGGR needed to be put in xmm in this case? @@@ also not what doc/appendix says, actually */ | |
119 MOVSD(QWORD(RSP,24),XMM1) /* @@@AGGR needed to be put in xmm in this case? @@@ also not what doc/appendix says, actually */ | |
120 | |
121 /* else (retval is int, float, or ptr to aggregate) */ | |
122 JMP(epilog) | |
123 CSYM(scalar_retval): | |
124 | |
125 /* pass return type via registers, handle ints and floats */ | |
126 MOV(QWORD(RSP,0),RAX) | |
127 MOVD(RAX,XMM0) | |
128 | |
129 CSYM(epilog): | |
112 | 130 |
113 MOV(RBP,RSP) | 131 MOV(RBP,RSP) |
114 POP(RBP) | 132 POP(RBP) |
115 RET() | 133 RET() |
116 | 134 |
117 END_PROC(dcCallback_x64_sysv) | 135 END_PROC(dcCallback_x64_sysv) |
118 | 136 |
119 GLOBAL(dcCallback_x64_win64) | 137 |
120 BEGIN_PROC(dcCallback_x64_win64) | 138 GLOBAL_FRAME(dcCallback_x64_win64) |
139 FRAME_BEGIN_PROC(dcCallback_x64_win64) | |
121 | 140 |
122 PUSH(RBP) | 141 PUSH(RBP) |
142 FRAME_PUSH_REG(RBP) | |
123 MOV(RSP,RBP) | 143 MOV(RSP,RBP) |
144 FRAME_SET(0, RBP) | |
145 FRAME_ENDPROLOG() | |
124 | 146 |
125 /* initialize DCArgs */ | 147 /* initialize DCArgs */ |
126 | 148 |
127 /* float parameters (4 registers spill to DCArgs) */ | 149 /* float parameters (4 registers spill to DCArgs.reg_data) */ |
128 | |
129 SUB(LIT(4*8),RSP) | 150 SUB(LIT(4*8),RSP) |
130 | 151 MOVSD(XMM3, QWORD(RSP,8*3)) /* DCArgs offset 88: float parameter 3 */ |
131 MOVSD(XMM3, QWORD(RSP,8*3)) /* struct offset 72: float parameter 3 */ | 152 MOVSD(XMM2, QWORD(RSP,8*2)) /* DCArgs offset 80: float parameter 2 */ |
132 MOVSD(XMM2, QWORD(RSP,8*2)) /* struct offset 64: float parameter 2 */ | 153 MOVSD(XMM1, QWORD(RSP,8*1)) /* DCArgs offset 72: float parameter 1 */ |
133 MOVSD(XMM1, QWORD(RSP,8*1)) /* struct offset 56: float parameter 1 */ | 154 MOVSD(XMM0, QWORD(RSP,8*0)) /* DCArgs offset 64: float parameter 0 */ |
134 MOVSD(XMM0, QWORD(RSP,8*0)) /* struct offset 48: float parameter 0 */ | 155 |
135 | 156 /* integer parameters (4 registers spill to DCArgs.reg_data) */ |
136 /* integer parameters (4 registers spill to DCArgs) */ | 157 PUSH(R9) /* DCArgs offset 56: parameter 3 */ |
137 | 158 PUSH(R8) /* DCArgs offset 48: parameter 2 */ |
138 PUSH(R9) /* struct offset 40: parameter 3 */ | 159 PUSH(RDX) /* DCArgs offset 40: parameter 1 */ |
139 PUSH(R8) /* struct offset 32: parameter 2 */ | 160 PUSH(RCX) /* DCArgs offset 32: parameter 0 */ |
140 PUSH(RDX) /* struct offset 24: parameter 1 */ | 161 |
141 PUSH(RCX) /* struct offset 16: parameter 0 */ | 162 MOV(QWORD(RAX, CTX_aggrs_pp), R8) |
142 | 163 PUSH(R8) /* DCArgs offset 24: **aggrs */ |
164 | |
165 /* get val of aggr_return_register from DCCallback struct into r8's LSBs, clear */ | |
166 /* MSBs, write DCarg's pad and aggr_return_register at once */ | |
167 MOVL(DWORD(RAX, CTX_aggr_ret_reg), R8D) /* implicitly zeroes the high bits of R8 */ | |
168 PUSH(R8) /* DCArgs offset 16: pad=0, aggr_return_register=DCCallback.aggr_return_register */ | |
169 | |
143 /* register counts for integer/pointer and float regs */ | 170 /* register counts for integer/pointer and float regs */ |
144 | 171 PUSH(LIT(0)) /* DCArgs offset 12: pad_w */ |
145 PUSH(LIT(0)) /* struct offset 12: fcount */ | 172 /* DCArgs offset 8: reg_count */ |
146 /* struct offset 8: icount */ | 173 |
147 | 174 LEA(QWORD(RBP,FRAME_arg0_win64),RDX) /* DCArgs offset 0: *stack_ptr */ |
148 LEA(QWORD(RBP,FRAME_arg0_win64),RDX) /* struct offset 0: stack pointer */ | |
149 PUSH(RDX) | 175 PUSH(RDX) |
150 | 176 |
151 MOV(RSP,RDX) /* arg 1 RDX : DCArgs* */ | 177 MOV(RSP,RDX) /* arg 1 RDX : DCArgs* */ |
152 | 178 |
153 /* initialize DCValue */ | 179 /* space for retval (also aligns stack to 16b) */ |
154 | 180 SUB(LIT(2*8),RSP) |
155 // PUSHQ(LIT(0)) /* struct offset 0: return value (max long long) */ | 181 |
156 | 182 /* call handler(*ctx, *args, *value, *userdata) - stack must be 16b aligned, here */ |
157 /* call handler( *ctx, *args, *value, *userdata) */ | |
158 | |
159 MOV(RAX,RCX) /* arg 0 RCX : DCCallback* (RAX) */ | 183 MOV(RAX,RCX) /* arg 0 RCX : DCCallback* (RAX) */ |
160 MOV(QWORD(RAX,CTX_userdata),R9) /* arg 3 R9 : userdata* */ | 184 MOV(QWORD(RAX,CTX_userdata),R9) /* arg 3 R9 : userdata* */ |
161 MOV(RSP,R8) /* arg 2 R8 : DCValue* */ | 185 MOV(RSP,R8) /* arg 2 R8 : DCValue* */ |
162 SUB(LIT(4*8),RSP) /* make room for spill area and call */ | 186 |
187 /* spill area */ | |
188 SUB(LIT(4*8),RSP) /* 4 qwords for spill area */ | |
189 | |
163 CALL_REG(QWORD(RAX,CTX_handler)) | 190 CALL_REG(QWORD(RAX,CTX_handler)) |
164 | 191 |
165 /* pass return type via registers, handle ints and floats */ | 192 /* pass return type via registers, handle ints and floats */ |
166 | 193 MOV(QWORD(RSP,4*8),RAX) |
167 MOV(QWORD(RBP,FRAME_DCValue_win64),RAX) | |
168 MOVD(RAX,XMM0) | 194 MOVD(RAX,XMM0) |
169 | 195 |
170 MOV(RBP,RSP) | 196 MOV(RBP,RSP) |
171 POP(RBP) | 197 POP(RBP) |
172 RET() | 198 RET() |
173 | 199 |
174 END_PROC(dcCallback_x64_win64) | 200 END_PROC(dcCallback_x64_win64) |
175 | 201 |
202 | |
176 END_ASM | 203 END_ASM |
177 | 204 |
205 /* vim: set ts=8: */ | |
206 |