1 /* vacall function for ia64 CPU */
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__("r15");
26 #endif
27 register double farg1 __asm__("f8");
28 register double farg2 __asm__("f9");
29 register double farg3 __asm__("f10");
30 register double farg4 __asm__("f11");
31 register double farg5 __asm__("f12");
32 register double farg6 __asm__("f13");
33 register double farg7 __asm__("f14");
34 register double farg8 __asm__("f15");
35 register __vaword* sret __asm__("r8");
36 register __varword iret __asm__("r8");
37 register __varword iret2 __asm__("r9");
38 register __varword iret3 __asm__("r10");
39 register __varword iret4 __asm__("r11");
40 register float fret __asm__("f8");
41 register double dret __asm__("f8");
42
43 /* The ABI requires that the first 8 general-purpose argument words are
44 being passed in registers, even if these words belong to a struct. No room
45 is allocated for these register words on the stack by the caller, but the
46 callee allocates room for them - at the right place in the stack frame,
47 that is, above the usual {fp, retaddr} combo - if and only if they are part
48 of a larger struct that extends to the stack and the address of this struct
49 is taken. */
50 struct gpargsequence {
51 __vaword word1; /* r32 */
52 __vaword word2; /* r33 */
53 __vaword word3; /* r34 */
54 __vaword word4; /* r35 */
55 __vaword word5; /* r36 */
56 __vaword word6; /* r37 */
57 __vaword word7; /* r38 */
58 __vaword word8; /* r39 */
59 __vaword firststackword;
60 };
61
62 #ifdef REENTRANT
63 static
64 #endif
65 void /* the return type is variable, not void! */
vacall_receiver(struct gpargsequence gpargs)66 vacall_receiver (struct gpargsequence gpargs)
67 {
68 __va_alist list;
69 /* Move the arguments passed in registers to their stack locations. */
70 list.farg[0] = farg1;
71 list.farg[1] = farg2;
72 list.farg[2] = farg3;
73 list.farg[3] = farg4;
74 list.farg[4] = farg5;
75 list.farg[5] = farg6;
76 list.farg[6] = farg7;
77 list.farg[7] = farg8;
78 /* Prepare the va_alist. */
79 list.flags = 0;
80 list.aptr = (long)&gpargs;
81 list.saptr = (__vaword*)&gpargs;
82 list.fanum = 0;
83 list.raddr = (void*)0;
84 list.rtype = __VAvoid;
85 list.structraddr = sret;
86 /* Call vacall_function. The macros do all the rest. */
87 #ifndef REENTRANT
88 (*vacall_function) (&list);
89 #else /* REENTRANT */
90 (*env->vacall_function) (env->arg,&list);
91 #endif
92 /* Put return value into proper register. */
93 if (list.rtype == __VAvoid) {
94 } else
95 if (list.rtype == __VAchar) {
96 iret = list.tmp._char;
97 } else
98 if (list.rtype == __VAschar) {
99 iret = list.tmp._schar;
100 } else
101 if (list.rtype == __VAuchar) {
102 iret = list.tmp._uchar;
103 } else
104 if (list.rtype == __VAshort) {
105 iret = list.tmp._short;
106 } else
107 if (list.rtype == __VAushort) {
108 iret = list.tmp._ushort;
109 } else
110 if (list.rtype == __VAint) {
111 iret = list.tmp._int;
112 } else
113 if (list.rtype == __VAuint) {
114 iret = list.tmp._uint;
115 } else
116 if (list.rtype == __VAlong) {
117 iret = list.tmp._long;
118 } else
119 if (list.rtype == __VAulong) {
120 iret = list.tmp._ulong;
121 } else
122 if (list.rtype == __VAlonglong) {
123 iret = list.tmp._long;
124 } else
125 if (list.rtype == __VAulonglong) {
126 iret = list.tmp._ulong;
127 } else
128 if (list.rtype == __VAfloat) {
129 fret = list.tmp._float;
130 } else
131 if (list.rtype == __VAdouble) {
132 dret = list.tmp._double;
133 } else
134 if (list.rtype == __VAvoidp) {
135 iret = (long)list.tmp._ptr;
136 } else
137 if (list.rtype == __VAstruct) {
138 if (list.flags & __VA_REGISTER_STRUCT_RETURN) {
139 /* Return structs of size <= 32 in registers. */
140 if (list.rsize > 0 && list.rsize <= 32) {
141 #if 0 /* Unoptimized */
142 iret = (__varword)((unsigned char *) list.raddr)[0];
143 if (list.rsize >= 2)
144 iret |= (__varword)((unsigned char *) list.raddr)[1] << 8;
145 if (list.rsize >= 3)
146 iret |= (__varword)((unsigned char *) list.raddr)[2] << 16;
147 if (list.rsize >= 4)
148 iret |= (__varword)((unsigned char *) list.raddr)[3] << 24;
149 if (list.rsize >= 5)
150 iret |= (__varword)((unsigned char *) list.raddr)[4] << 32;
151 if (list.rsize >= 6)
152 iret |= (__varword)((unsigned char *) list.raddr)[5] << 40;
153 if (list.rsize >= 7)
154 iret |= (__varword)((unsigned char *) list.raddr)[6] << 48;
155 if (list.rsize >= 8)
156 iret |= (__varword)((unsigned char *) list.raddr)[7] << 56;
157 if (list.rsize >= 9) {
158 iret2 = (__varword)((unsigned char *) list.raddr)[8];
159 if (list.rsize >= 10)
160 iret2 |= (__varword)((unsigned char *) list.raddr)[9] << 8;
161 if (list.rsize >= 11)
162 iret2 |= (__varword)((unsigned char *) list.raddr)[10] << 16;
163 if (list.rsize >= 12)
164 iret2 |= (__varword)((unsigned char *) list.raddr)[11] << 24;
165 if (list.rsize >= 13)
166 iret2 |= (__varword)((unsigned char *) list.raddr)[12] << 32;
167 if (list.rsize >= 14)
168 iret2 |= (__varword)((unsigned char *) list.raddr)[13] << 40;
169 if (list.rsize >= 15)
170 iret2 |= (__varword)((unsigned char *) list.raddr)[14] << 48;
171 if (list.rsize >= 16)
172 iret2 |= (__varword)((unsigned char *) list.raddr)[15] << 56;
173 if (list.rsize >= 17) {
174 iret3 = (__varword)((unsigned char *) list.raddr)[16];
175 if (list.rsize >= 18)
176 iret3 |= (__varword)((unsigned char *) list.raddr)[17] << 8;
177 if (list.rsize >= 19)
178 iret3 |= (__varword)((unsigned char *) list.raddr)[18] << 16;
179 if (list.rsize >= 20)
180 iret3 |= (__varword)((unsigned char *) list.raddr)[19] << 24;
181 if (list.rsize >= 21)
182 iret3 |= (__varword)((unsigned char *) list.raddr)[20] << 32;
183 if (list.rsize >= 22)
184 iret3 |= (__varword)((unsigned char *) list.raddr)[21] << 40;
185 if (list.rsize >= 23)
186 iret3 |= (__varword)((unsigned char *) list.raddr)[22] << 48;
187 if (list.rsize >= 24)
188 iret3 |= (__varword)((unsigned char *) list.raddr)[23] << 56;
189 if (list.rsize >= 25) {
190 iret4 = (__varword)((unsigned char *) list.raddr)[24];
191 if (list.rsize >= 26)
192 iret4 |= (__varword)((unsigned char *) list.raddr)[25] << 8;
193 if (list.rsize >= 27)
194 iret4 |= (__varword)((unsigned char *) list.raddr)[26] << 16;
195 if (list.rsize >= 28)
196 iret4 |= (__varword)((unsigned char *) list.raddr)[27] << 24;
197 if (list.rsize >= 29)
198 iret4 |= (__varword)((unsigned char *) list.raddr)[28] << 32;
199 if (list.rsize >= 30)
200 iret4 |= (__varword)((unsigned char *) list.raddr)[29] << 40;
201 if (list.rsize >= 31)
202 iret4 |= (__varword)((unsigned char *) list.raddr)[30] << 48;
203 if (list.rsize >= 32)
204 iret4 |= (__varword)((unsigned char *) list.raddr)[31] << 56;
205 }
206 }
207 }
208 #else /* Optimized: fewer conditional jumps, fewer memory accesses */
209 uintptr_t count = list.rsize; /* > 0, ≤ 4*sizeof(__varword) */
210 __varword* wordaddr = (__varword*)((uintptr_t)list.raddr & ~(uintptr_t)(sizeof(__varword)-1));
211 uintptr_t start_offset = (uintptr_t)list.raddr & (uintptr_t)(sizeof(__varword)-1); /* ≥ 0, < sizeof(__varword) */
212 uintptr_t end_offset = start_offset + count; /* > 0, < 5*sizeof(__varword) */
213 if (count <= sizeof(__varword)) {
214 /* Assign iret. */
215 if (end_offset <= sizeof(__varword)) {
216 /* 0 < end_offset ≤ sizeof(__varword) */
217 __varword mask0 = ((__varword)2 << (end_offset*8-1)) - 1;
218 iret = (wordaddr[0] & mask0) >> (start_offset*8);
219 } else {
220 /* sizeof(__varword) < end_offset < 2*sizeof(__varword), start_offset > 0 */
221 __varword mask1 = ((__varword)2 << (end_offset*8-sizeof(__varword)*8-1)) - 1;
222 iret = (wordaddr[0] >> (start_offset*8)) | ((wordaddr[1] & mask1) << (sizeof(__varword)*8-start_offset*8));
223 }
224 } else if (count <= 2*sizeof(__varword)) {
225 /* Assign iret, iret2. */
226 if (end_offset <= 2*sizeof(__varword)) {
227 /* sizeof(__varword) < end_offset ≤ 2*sizeof(__varword) */
228 __varword mask1 = ((__varword)2 << (end_offset*8-sizeof(__varword)*8-1)) - 1;
229 iret = (wordaddr[0] >> (start_offset*8)) | ((wordaddr[1] & mask1) << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
230 iret2 = (wordaddr[1] & mask1) >> (start_offset*8);
231 } else {
232 /* 2*sizeof(__varword) < end_offset < 3*sizeof(__varword), start_offset > 0 */
233 __varword mask2 = ((__varword)2 << (end_offset*8-2*sizeof(__varword)*8-1)) - 1;
234 iret = (wordaddr[0] >> (start_offset*8)) | (wordaddr[1] << (sizeof(__varword)*8-start_offset*8));
235 iret2 = (wordaddr[1] >> (start_offset*8)) | ((wordaddr[2] & mask2) << (sizeof(__varword)*8-start_offset*8));
236 }
237 } else if (count <= 3*sizeof(__varword)) {
238 /* Assign iret, iret2, iret3. */
239 if (end_offset <= 3*sizeof(__varword)) {
240 /* 2*sizeof(__varword) < end_offset ≤ 3*sizeof(__varword) */
241 __varword mask2 = ((__varword)2 << (end_offset*8-sizeof(__varword)*8-1)) - 1;
242 iret = (wordaddr[0] >> (start_offset*8)) | (wordaddr[1] << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
243 iret2 = (wordaddr[1] >> (start_offset*8)) | ((wordaddr[2] & mask2) << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
244 iret3 = (wordaddr[2] & mask2) >> (start_offset*8);
245 } else {
246 /* 3*sizeof(__varword) < end_offset < 4*sizeof(__varword), start_offset > 0 */
247 __varword mask3 = ((__varword)2 << (end_offset*8-2*sizeof(__varword)*8-1)) - 1;
248 iret = (wordaddr[0] >> (start_offset*8)) | (wordaddr[1] << (sizeof(__varword)*8-start_offset*8));
249 iret2 = (wordaddr[1] >> (start_offset*8)) | (wordaddr[2] << (sizeof(__varword)*8-start_offset*8));
250 iret3 = (wordaddr[2] >> (start_offset*8)) | ((wordaddr[3] & mask3) << (sizeof(__varword)*8-start_offset*8));
251 }
252 } else {
253 /* Assign iret, iret2, iret3, iret4. */
254 if (end_offset <= 4*sizeof(__varword)) {
255 /* 3*sizeof(__varword) < end_offset ≤ 4*sizeof(__varword) */
256 __varword mask3 = ((__varword)2 << (end_offset*8-sizeof(__varword)*8-1)) - 1;
257 iret = (wordaddr[0] >> (start_offset*8)) | (wordaddr[1] << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
258 iret2 = (wordaddr[1] >> (start_offset*8)) | (wordaddr[2] << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
259 iret3 = (wordaddr[2] >> (start_offset*8)) | ((wordaddr[3] & mask3) << (sizeof(__varword)*4-start_offset*4) << (sizeof(__varword)*4-start_offset*4));
260 iret4 = (wordaddr[3] & mask3) >> (start_offset*8);
261 } else {
262 /* 4*sizeof(__varword) < end_offset < 5*sizeof(__varword), start_offset > 0 */
263 __varword mask4 = ((__varword)2 << (end_offset*8-2*sizeof(__varword)*8-1)) - 1;
264 iret = (wordaddr[0] >> (start_offset*8)) | (wordaddr[1] << (sizeof(__varword)*8-start_offset*8));
265 iret2 = (wordaddr[1] >> (start_offset*8)) | (wordaddr[2] << (sizeof(__varword)*8-start_offset*8));
266 iret3 = (wordaddr[2] >> (start_offset*8)) | (wordaddr[3] << (sizeof(__varword)*8-start_offset*8));
267 iret4 = (wordaddr[3] >> (start_offset*8)) | ((wordaddr[4] & mask4) << (sizeof(__varword)*8-start_offset*8));
268 }
269 }
270 #endif
271 }
272 }
273 }
274 }
275
276 #ifdef REENTRANT
277 __vacall_r_t
callback_get_receiver(void)278 callback_get_receiver (void)
279 {
280 return (__vacall_r_t)(void*)&callback_receiver;
281 }
282 #endif
283