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