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