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