1 /* vacall function for hppa 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 /*---------------------------------------------------------------------------
21 HPPA Argument Passing Conventions:
22
23 The calling conventions for anonymous functions and for explicitly named
24 functions are different. Only the convention for explicitly named functions
25 matters here.
26
27 All arguments, except the first 4 words, are passed on the stack
28 - growing down! - with word alignment. Doubles take two words and force
29 double alignment. Structures args are passed as true structures embedded
30 in the argument stack. They force double alignment and - if they don't
31 fit entirely in the 4 register words - are passed in memory.
32 The first 2 words are passed like this:
33 %r26 = first integer arg, %r25 = second integer arg, or
34 %fr4L = first float arg, %fr5L = second float arg, or
35 %fr5 = double arg.
36 Similarly for the next 2 words, passed in %r24 and %r23, or
37 %fr6L and %fr7L, or
38 %fr7.
39
40 To return a structure, the called function copies the return value to
41 the address supplied in register "%r28".
42 ---------------------------------------------------------------------------*/
43
44 #include "vacall-internal.h"
45
46 #ifdef REENTRANT
47 #define vacall_receiver callback_receiver
48 register struct { void (*vacall_function) (void*,va_alist); void* arg; }
49 * env __asm__("%r29");
50 #endif
51 register void* sret __asm__("%r28");
52 register __varword arg1 __asm__("%r26");
53 register __varword arg2 __asm__("%r25");
54 register __varword arg3 __asm__("%r24");
55 register __varword arg4 __asm__("%r23");
56 register float farg1 __asm__("%fr4"); /* fr4L */
57 register float farg2 __asm__("%fr5"); /* fr5L */
58 register float farg3 __asm__("%fr6"); /* fr6L */
59 register float farg4 __asm__("%fr7"); /* fr7L */
60 register double darg1 __asm__("%fr5");
61 register double darg2 __asm__("%fr7");
62 register int iret __asm__("%r28");
63 register float fret __asm__("%fr4"); /* fr4L */
64 register double dret __asm__("%fr4");
65 register __varword iret1 __asm__("%r28");
66 register __varword iret2 __asm__("%r29");
67
68 #ifdef REENTRANT
69 static
70 #endif
71 void /* the return type is variable, not void! */
vacall_receiver(__vaword word1,__vaword word2,__vaword word3,__vaword word4,__vaword firstword)72 vacall_receiver (__vaword word1, __vaword word2, __vaword word3, __vaword word4,
73 __vaword firstword)
74 {
75 /* gcc-2.6.3 source says: When a parameter is passed in a register,
76 * stack space is still allocated for it.
77 */
78 /* Note about stack offsets (see vacall-hppa.s):
79 * &firstword = %r30 - 244, &word4 = %r30 - 240, ..., &word1 = %r30 - 228,
80 */
81 __va_alist list;
82 /* Move the arguments passed in registers to their stack locations. */
83 (&firstword)[4] = word1;
84 (&firstword)[3] = word2;
85 (&firstword)[2] = word3;
86 (&firstword)[1] = word4;
87 list.darg[1] = darg1;
88 list.darg[0] = darg2;
89 list.farg[3] = farg1;
90 list.farg[2] = farg2;
91 list.farg[1] = farg3;
92 list.farg[0] = farg4;
93 /* Prepare the va_alist. */
94 list.flags = 0;
95 list.aptr = (long)(&firstword + 5);
96 list.raddr = (void*)0;
97 list.rtype = __VAvoid;
98 list.structraddr = sret;
99 list.memargptr = (long)(&firstword + 1);
100 list.farg_offset = (long)&list.farg[4] - list.aptr;
101 list.darg_offset = (long)&list.darg[2] - list.aptr;
102 /* Call vacall_function. The macros do all the rest. */
103 #ifndef REENTRANT
104 (*vacall_function) (&list);
105 #else /* REENTRANT */
106 (*env->vacall_function) (env->arg,&list);
107 #endif
108 /* Put return value into proper register. */
109 if (list.rtype == __VAvoid) {
110 } else
111 if (list.rtype == __VAchar) {
112 iret = list.tmp._char;
113 } else
114 if (list.rtype == __VAschar) {
115 iret = list.tmp._schar;
116 } else
117 if (list.rtype == __VAuchar) {
118 iret = list.tmp._uchar;
119 } else
120 if (list.rtype == __VAshort) {
121 iret = list.tmp._short;
122 } else
123 if (list.rtype == __VAushort) {
124 iret = list.tmp._ushort;
125 } else
126 if (list.rtype == __VAint) {
127 iret = list.tmp._int;
128 } else
129 if (list.rtype == __VAuint) {
130 iret = list.tmp._uint;
131 } else
132 if (list.rtype == __VAlong) {
133 iret = list.tmp._long;
134 } else
135 if (list.rtype == __VAulong) {
136 iret = list.tmp._ulong;
137 } else
138 if (list.rtype == __VAlonglong || list.rtype == __VAulonglong) {
139 iret1 = ((__varword *) &list.tmp._longlong)[0];
140 iret2 = ((__varword *) &list.tmp._longlong)[1];
141 } else
142 if (list.rtype == __VAfloat) {
143 fret = list.tmp._float;
144 iret1 = list.tmp._words[0]; /* HP cc generates a RTNVAL=GR call */
145 } else
146 if (list.rtype == __VAdouble) {
147 dret = list.tmp._double;
148 iret1 = list.tmp._words[0]; /* HP cc generates a RTNVAL=GR call */
149 iret2 = list.tmp._words[1]; /* i.e. result is expected in r28,r29 */
150 } else
151 if (list.rtype == __VAvoidp) {
152 iret = (long)list.tmp._ptr;
153 } else
154 if (list.rtype == __VAstruct) {
155 if (list.flags & __VA_SMALL_STRUCT_RETURN) {
156 /* cc, c89 and gcc >= 2.7 return structs of size <= 8 in registers. */
157 /* This is really weird code, unlike all other big-endian platforms. */
158 if (list.rsize > 0 && list.rsize <= 8) {
159 #if 0 /* Unoptimized */
160 if (list.rsize == 1) {
161 iret = ((unsigned char *) list.raddr)[0];
162 } else
163 if (list.rsize == 2) {
164 iret = (((unsigned char *) list.raddr)[0] << 8)
165 | ((unsigned char *) list.raddr)[1];
166 } else
167 if (list.rsize == 3) {
168 iret = (((unsigned char *) list.raddr)[0] << 16)
169 | (((unsigned char *) list.raddr)[1] << 8)
170 | ((unsigned char *) list.raddr)[2];
171 } else
172 if (list.rsize == 4) {
173 iret = (((unsigned char *) list.raddr)[0] << 24)
174 | (((unsigned char *) list.raddr)[1] << 16)
175 | (((unsigned char *) list.raddr)[2] << 8)
176 | ((unsigned char *) list.raddr)[3];
177 } else
178 if (list.rsize == 5) {
179 iret1 = ((unsigned char *) list.raddr)[0];
180 iret2 = (((unsigned char *) list.raddr)[1] << 24)
181 | (((unsigned char *) list.raddr)[2] << 16)
182 | (((unsigned char *) list.raddr)[3] << 8)
183 | ((unsigned char *) list.raddr)[4];
184 } else
185 if (list.rsize == 6) {
186 iret1 = (((unsigned char *) list.raddr)[0] << 8)
187 | ((unsigned char *) list.raddr)[1];
188 iret2 = (((unsigned char *) list.raddr)[2] << 24)
189 | (((unsigned char *) list.raddr)[3] << 16)
190 | (((unsigned char *) list.raddr)[4] << 8)
191 | ((unsigned char *) list.raddr)[5];
192 } else
193 if (list.rsize == 7) {
194 iret1 = (((unsigned char *) list.raddr)[0] << 16)
195 | (((unsigned char *) list.raddr)[1] << 8)
196 | ((unsigned char *) list.raddr)[2];
197 iret2 = (((unsigned char *) list.raddr)[3] << 24)
198 | (((unsigned char *) list.raddr)[4] << 16)
199 | (((unsigned char *) list.raddr)[5] << 8)
200 | ((unsigned char *) list.raddr)[6];
201 } else
202 if (list.rsize == 8) {
203 iret1 = (((unsigned char *) list.raddr)[0] << 24)
204 | (((unsigned char *) list.raddr)[1] << 16)
205 | (((unsigned char *) list.raddr)[2] << 8)
206 | ((unsigned char *) list.raddr)[3];
207 iret2 = (((unsigned char *) list.raddr)[4] << 24)
208 | (((unsigned char *) list.raddr)[5] << 16)
209 | (((unsigned char *) list.raddr)[6] << 8)
210 | ((unsigned char *) list.raddr)[7];
211 }
212 #else /* Optimized: fewer conditional jumps, fewer memory accesses */
213 uintptr_t count = list.rsize; /* > 0, ≤ 2*sizeof(__varword) */
214 __varword* wordaddr = (__varword*)((uintptr_t)list.raddr & ~(uintptr_t)(sizeof(__varword)-1));
215 uintptr_t start_offset = (uintptr_t)list.raddr & (uintptr_t)(sizeof(__varword)-1); /* ≥ 0, < sizeof(__varword) */
216 uintptr_t end_offset = start_offset + count; /* > 0, < 3*sizeof(__varword) */
217 if (count <= sizeof(__varword)) {
218 /* Assign iret. */
219 __varword mask0 = ((__varword)2 << (sizeof(__varword)*8-start_offset*8-1)) - 1;
220 if (end_offset <= sizeof(__varword)) {
221 /* 0 < end_offset ≤ sizeof(__varword) */
222 iret = (wordaddr[0] & mask0) >> (sizeof(__varword)*8-end_offset*8);
223 } else {
224 /* sizeof(__varword) < end_offset < 2*sizeof(__varword), start_offset > 0 */
225 iret = ((wordaddr[0] & mask0) << (end_offset*8-sizeof(__varword)*8))
226 | (wordaddr[1] >> (2*sizeof(__varword)*8-end_offset*8));
227 }
228 } else {
229 /* Assign iret, iret2. */
230 __varword mask0 = ((__varword)2 << (sizeof(__varword)*8-start_offset*8-1)) - 1;
231 if (end_offset <= 2*sizeof(__varword)) {
232 /* sizeof(__varword) < end_offset ≤ 2*sizeof(__varword) */
233 iret = (wordaddr[0] & mask0) >> (2*sizeof(__varword)*8-end_offset*8);
234 iret2 = ((wordaddr[0] & mask0) << (end_offset*4-sizeof(__varword)*4) << (end_offset*4-sizeof(__varword)*4))
235 | (wordaddr[1] >> (2*sizeof(__varword)*8-end_offset*8));
236 } else {
237 /* 2*sizeof(__varword) < end_offset < 3*sizeof(__varword), start_offset > 0 */
238 iret = ((wordaddr[0] & mask0) << (end_offset*8-2*sizeof(__varword)*8))
239 | (wordaddr[1] >> (3*sizeof(__varword)*8-end_offset*8));
240 iret2 = (wordaddr[1] << (end_offset*8-2*sizeof(__varword)*8))
241 | (wordaddr[2] >> (3*sizeof(__varword)*8-end_offset*8));
242 }
243 }
244 #endif
245 }
246 }
247 }
248 }
249
250 #ifdef REENTRANT
251 __vacall_r_t
callback_get_receiver(void)252 callback_get_receiver (void)
253 {
254 return (__vacall_r_t)(void*)&callback_receiver;
255 }
256 #endif
257