1 /* vacall function for x86_64 CPU with the Windows ABI ('gcc -mabi=ms') */
2
3 /*
4 * Copyright 1995-2021 Bruno Haible <bruno@clisp.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "vacall-internal.h"
21
22 #ifdef REENTRANT
23 #define vacall_receiver callback_receiver
24 register struct { void (*vacall_function) (void*,va_alist); void* arg; }
25 * env __asm__("r10");
26 #endif
27
28 /*register __varword iarg1 __asm__("rcx");*/
29 /*register __varword iarg2 __asm__("rdx");*/
30 /*register __varword iarg3 __asm__("r8");*/
31 /*register __varword iarg4 __asm__("r9");*/
32
33 register float farg1 __asm__("xmm0");
34 register float farg2 __asm__("xmm1");
35 register float farg3 __asm__("xmm2");
36 register float farg4 __asm__("xmm3");
37
38 register double darg1 __asm__("xmm0");
39 register double darg2 __asm__("xmm1");
40 register double darg3 __asm__("xmm2");
41 register double darg4 __asm__("xmm3");
42
43 register __varword iret __asm__("rax");
44 register float fret __asm__("xmm0");
45 register double dret __asm__("xmm0");
46
47 /*
48 * Tell gcc to not use the call-saved registers %rbx, %rsi, %rdi.
49 * This ensures that the return sequence does not need to restore registers
50 * from the stack.
51 */
52 register void* dummy1 __asm__("%rbx");
53 register void* dummy2 __asm__("%rsi");
54 register void* dummy3 __asm__("%rdi");
55
56 #ifdef REENTRANT
57 static
58 #endif
59 void /* the return type is variable, not void! */
vacall_receiver(__vaword word1,__vaword word2,__vaword word3,__vaword word4,__vaword firstword)60 vacall_receiver (__vaword word1, __vaword word2, __vaword word3, __vaword word4,
61 __vaword firstword)
62 {
63 __va_alist list;
64 /* Move the arguments passed in registers to their stack locations. */
65 (&firstword)[-4] = word1;
66 (&firstword)[-3] = word2;
67 (&firstword)[-2] = word3;
68 (&firstword)[-1] = word4;
69 /* Move the floating-point arguments passed in registers to temp storage. */
70 list.farg[0] = farg1;
71 list.farg[1] = farg2;
72 list.farg[2] = farg3;
73 list.farg[3] = farg4;
74 list.darg[0] = darg1;
75 list.darg[1] = darg2;
76 list.darg[2] = darg3;
77 list.darg[3] = darg4;
78 /* Prepare the va_alist. */
79 list.flags = 0;
80 list.aptr = (long)(&firstword - 4);
81 list.raddr = (void*)0;
82 list.rtype = __VAvoid;
83 list.anum = 0;
84 /* Call vacall_function. The macros do all the rest. */
85 #ifndef REENTRANT
86 (*vacall_function) (&list);
87 #else /* REENTRANT */
88 (*env->vacall_function) (env->arg,&list);
89 #endif
90 /* Put return value into proper register. */
91 if (list.rtype == __VAvoid) {
92 } else
93 if (list.rtype == __VAchar) {
94 iret = list.tmp._char;
95 } else
96 if (list.rtype == __VAschar) {
97 iret = list.tmp._schar;
98 } else
99 if (list.rtype == __VAuchar) {
100 iret = list.tmp._uchar;
101 } else
102 if (list.rtype == __VAshort) {
103 iret = list.tmp._short;
104 } else
105 if (list.rtype == __VAushort) {
106 iret = list.tmp._ushort;
107 } else
108 if (list.rtype == __VAint) {
109 iret = list.tmp._int;
110 } else
111 if (list.rtype == __VAuint) {
112 iret = list.tmp._uint;
113 } else
114 if (list.rtype == __VAlong) {
115 iret = list.tmp._long;
116 } else
117 if (list.rtype == __VAulong) {
118 iret = list.tmp._ulong;
119 } else
120 if (list.rtype == __VAlonglong) {
121 iret = list.tmp._long;
122 } else
123 if (list.rtype == __VAulonglong) {
124 iret = list.tmp._ulong;
125 } else
126 if (list.rtype == __VAfloat) {
127 fret = list.tmp._float;
128 } else
129 if (list.rtype == __VAdouble) {
130 dret = list.tmp._double;
131 } else
132 if (list.rtype == __VAvoidp) {
133 iret = (long)list.tmp._ptr;
134 } else
135 if (list.rtype == __VAstruct) {
136 if (list.flags & __VA_REGISTER_STRUCT_RETURN) {
137 /* Return structs of size 1, 2, 4, 8 in registers. */
138 #if 0 /* Unoptimized */
139 if (list.rsize == 1) {
140 iret = (__varword)((unsigned char *) list.raddr)[0];
141 } else
142 if (list.rsize == 2) {
143 iret = (__varword)((unsigned char *) list.raddr)[0]
144 | ((__varword)((unsigned char *) list.raddr)[1] << 8);
145 } else
146 if (list.rsize == 4) {
147 iret = (__varword)((unsigned char *) list.raddr)[0]
148 | ((__varword)((unsigned char *) list.raddr)[1] << 8)
149 | ((__varword)((unsigned char *) list.raddr)[2] << 16)
150 | ((__varword)((unsigned char *) list.raddr)[3] << 24);
151 } else
152 if (list.rsize == 8) {
153 iret = (__varword)((unsigned char *) list.raddr)[0]
154 | ((__varword)((unsigned char *) list.raddr)[1] << 8)
155 | ((__varword)((unsigned char *) list.raddr)[2] << 16)
156 | ((__varword)((unsigned char *) list.raddr)[3] << 24)
157 | ((__varword)((unsigned char *) list.raddr)[4] << 32)
158 | ((__varword)((unsigned char *) list.raddr)[5] << 40)
159 | ((__varword)((unsigned char *) list.raddr)[6] << 48)
160 | ((__varword)((unsigned char *) list.raddr)[7] << 56);
161 }
162 #else /* Optimized: fewer conditional jumps, fewer memory accesses */
163 uintptr_t count = list.rsize; /* > 0, ≤ sizeof(__varword) */
164 if (count == 1 || count == 2 || count == 4 || count == 8) {
165 __varword* wordaddr = (__varword*)((uintptr_t)list.raddr & ~(uintptr_t)(sizeof(__varword)-1));
166 uintptr_t start_offset = (uintptr_t)list.raddr & (uintptr_t)(sizeof(__varword)-1); /* ≥ 0, < sizeof(__varword) */
167 uintptr_t end_offset = start_offset + count; /* > 0, < 2*sizeof(__varword) */
168 if (end_offset <= sizeof(__varword)) {
169 /* 0 < end_offset ≤ sizeof(__varword) */
170 __varword mask0 = ((__varword)2 << (end_offset*8-1)) - 1;
171 iret = (wordaddr[0] & mask0) >> (start_offset*8);
172 } else {
173 /* sizeof(__varword) < end_offset < 2*sizeof(__varword), start_offset > 0 */
174 __varword mask1 = ((__varword)2 << (end_offset*8-sizeof(__varword)*8-1)) - 1;
175 iret = (wordaddr[0] >> (start_offset*8)) | ((wordaddr[1] & mask1) << (sizeof(__varword)*8-start_offset*8));
176 }
177 }
178 #endif
179 } else {
180 iret = (long)list.raddr;
181 }
182 }
183 }
184
185 #ifdef REENTRANT
186 __vacall_r_t
callback_get_receiver(void)187 callback_get_receiver (void)
188 {
189 return (__vacall_r_t)(void*)&callback_receiver;
190 }
191 #endif
192