1 /**
2   Copyright 1993 Bill Triggs <Bill.Triggs@inrialpes.fr>
3   Copyright 1995-2021 Bruno Haible <bruno@clisp.org>
4 
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 **/
18 /*----------------------------------------------------------------------
19   Foreign function interface for a Linux riscv32 with ILP32 ABI.
20 
21   This calls a C function with an argument list built up using macros
22   defined in avcall.h.
23 
24   RISC-V Argument Passing Conventions are documented in
25   https://people.eecs.berkeley.edu/~krste/papers/riscv-spec-v2.1.pdf
26   chapter 20.
27   ----------------------------------------------------------------------*/
28 #include "avcall-internal.h"
29 
30 #define RETURN(TYPE,VAL)	(*(TYPE*)l->raddr = (TYPE)(VAL))
31 
32 register __avrword iarg1 __asm__("a0");
33 register __avrword iarg2 __asm__("a1");
34 register __avrword iarg3 __asm__("a2");
35 register __avrword iarg4 __asm__("a3");
36 register __avrword iarg5 __asm__("a4");
37 register __avrword iarg6 __asm__("a5");
38 register __avrword iarg7 __asm__("a6");
39 register __avrword iarg8 __asm__("a7");
40 
41 register float farg1 __asm__("fa0");
42 register float farg2 __asm__("fa1");
43 register float farg3 __asm__("fa2");
44 register float farg4 __asm__("fa3");
45 register float farg5 __asm__("fa4");
46 register float farg6 __asm__("fa5");
47 register float farg7 __asm__("fa6");
48 register float farg8 __asm__("fa7");
49 
50 register double darg1 __asm__("fa0");
51 register double darg2 __asm__("fa1");
52 register double darg3 __asm__("fa2");
53 register double darg4 __asm__("fa3");
54 register double darg5 __asm__("fa4");
55 register double darg6 __asm__("fa5");
56 register double darg7 __asm__("fa6");
57 register double darg8 __asm__("fa7");
58 
59 int
avcall_call(av_alist * list)60 avcall_call(av_alist* list)
61 {
62   register __avrword	iretreg	 __asm__("a0");
63   register __avrword	iret2reg __asm__("a1");
64   register double	dret	__asm__("fa0");
65 
66   __av_alist* l = &AV_LIST_INNER(list);
67 
68   __avword* argframe = __builtin_alloca(__AV_ALIST_WORDS * sizeof(__avword)); /* make room for argument list */
69   int arglen = l->aptr - l->args;
70   unsigned int fanum = l->fanum;
71 
72   {
73     int i;
74     for (i = 8; i < arglen; i++)	/* push function args onto stack */
75       argframe[i-8] = l->args[i];
76   }
77 
78   /* Put up to 8 integer args into registers. */
79   if (arglen >= 1) {
80     iarg1 = l->args[0];
81     if (arglen >= 2) {
82       iarg2 = l->args[1];
83       if (arglen >= 3) {
84         iarg3 = l->args[2];
85         if (arglen >= 4) {
86           iarg4 = l->args[3];
87           if (arglen >= 5) {
88             iarg5 = l->args[4];
89             if (arglen >= 6) {
90               iarg6 = l->args[5];
91               if (arglen >= 7) {
92                 iarg7 = l->args[6];
93                 if (arglen >= 8) {
94                   iarg8 = l->args[7];
95                 }
96               }
97             }
98           }
99         }
100       }
101     }
102   }
103 
104   /* Put upto 8 floating-point args into registers. */
105   if (fanum >= 1) {
106     if (l->darg_mask & (1 << 0)) darg1 = l->dargs[0];
107     else if (l->farg_mask & (1 << 0)) farg1 = l->fargs[0];
108     if (fanum >= 2) {
109       if (l->darg_mask & (1 << 1)) darg2 = l->dargs[1];
110       else if (l->farg_mask & (1 << 1)) farg2 = l->fargs[1];
111       if (fanum >= 3) {
112         if (l->darg_mask & (1 << 2)) darg3 = l->dargs[2];
113         else if (l->farg_mask & (1 << 2)) farg3 = l->fargs[2];
114         if (fanum >= 4) {
115           if (l->darg_mask & (1 << 3)) darg4 = l->dargs[3];
116           else if (l->farg_mask & (1 << 3)) farg4 = l->fargs[3];
117           if (fanum >= 5) {
118             if (l->darg_mask & (1 << 4)) darg5 = l->dargs[4];
119             else if (l->farg_mask & (1 << 4)) farg5 = l->fargs[4];
120             if (fanum >= 6) {
121               if (l->darg_mask & (1 << 5)) darg6 = l->dargs[5];
122               else if (l->farg_mask & (1 << 5)) farg6 = l->fargs[5];
123               if (fanum >= 7) {
124                 if (l->darg_mask & (1 << 6)) darg7 = l->dargs[6];
125                 else if (l->farg_mask & (1 << 6)) farg7 = l->fargs[6];
126                 if (fanum >= 8) {
127                   if (l->darg_mask & (1 << 7)) darg8 = l->dargs[7];
128                   else if (l->farg_mask & (1 << 7)) farg8 = l->fargs[7];
129                 }
130               }
131             }
132           }
133         }
134       }
135     }
136   }
137 
138   /* Call function. */
139   if (l->rtype == __AVfloat) {
140     *(float*)l->raddr = (*(float(*)())l->func)();
141   } else
142   if (l->rtype == __AVdouble) {
143     *(double*)l->raddr = (*(double(*)())l->func)();
144   } else {
145     __avrword iret, iret2;
146 
147     iret = (*l->func)();
148     iret2 = iret2reg;
149 
150     /* save return value */
151     if (l->rtype == __AVvoid) {
152     } else
153     if (l->rtype == __AVchar) {
154       RETURN(char, iret);
155     } else
156     if (l->rtype == __AVschar) {
157       RETURN(signed char, iret);
158     } else
159     if (l->rtype == __AVuchar) {
160       RETURN(unsigned char, iret);
161     } else
162     if (l->rtype == __AVshort) {
163       RETURN(short, iret);
164     } else
165     if (l->rtype == __AVushort) {
166       RETURN(unsigned short, iret);
167     } else
168     if (l->rtype == __AVint) {
169       RETURN(int, iret);
170     } else
171     if (l->rtype == __AVuint) {
172       RETURN(unsigned int, iret);
173     } else
174     if (l->rtype == __AVlong) {
175       RETURN(long, iret);
176     } else
177     if (l->rtype == __AVulong) {
178       RETURN(unsigned long, iret);
179     } else
180     if (l->rtype == __AVlonglong || l->rtype == __AVulonglong) {
181       void* raddr = l->raddr;
182       ((__avrword*)raddr)[0] = iret;
183       ((__avrword*)raddr)[1] = iret2;
184     } else
185   /* see above
186     if (l->rtype == __AVfloat) {
187     } else
188     if (l->rtype == __AVdouble) {
189     } else
190   */
191     if (l->rtype == __AVvoidp) {
192       RETURN(void*, iret);
193     } else
194     if (l->rtype == __AVstruct) {
195       if (l->flags & __AV_SMALL_STRUCT_RETURN) {
196         /* Return structs of size <= 8 in registers. */
197         if (l->rsize > 0 && l->rsize <= 8) {
198           void* raddr = l->raddr;
199           #if 0 /* Unoptimized */
200           if (l->rsize == 1) {
201             ((unsigned char *)raddr)[0] = (unsigned char)(iret);
202           } else
203           if (l->rsize == 2) {
204             ((unsigned char *)raddr)[0] = (unsigned char)(iret);
205             ((unsigned char *)raddr)[1] = (unsigned char)(iret>>8);
206           } else
207           if (l->rsize == 3) {
208             ((unsigned char *)raddr)[0] = (unsigned char)(iret);
209             ((unsigned char *)raddr)[1] = (unsigned char)(iret>>8);
210             ((unsigned char *)raddr)[2] = (unsigned char)(iret>>16);
211           } else
212           if (l->rsize >= 4 && l->rsize <= 8) {
213             ((unsigned char *)raddr)[0] = (unsigned char)(iret);
214             ((unsigned char *)raddr)[1] = (unsigned char)(iret>>8);
215             ((unsigned char *)raddr)[2] = (unsigned char)(iret>>16);
216             ((unsigned char *)raddr)[3] = (unsigned char)(iret>>24);
217             if (l->rsize == 4) {
218             } else
219             if (l->rsize == 5) {
220               ((unsigned char *)raddr)[4+0] = (unsigned char)(iret2);
221             } else
222             if (l->rsize == 6) {
223               ((unsigned char *)raddr)[4+0] = (unsigned char)(iret2);
224               ((unsigned char *)raddr)[4+1] = (unsigned char)(iret2>>8);
225             } else
226             if (l->rsize == 7) {
227               ((unsigned char *)raddr)[4+0] = (unsigned char)(iret2);
228               ((unsigned char *)raddr)[4+1] = (unsigned char)(iret2>>8);
229               ((unsigned char *)raddr)[4+2] = (unsigned char)(iret2>>16);
230             } else
231             if (l->rsize == 8) {
232               ((unsigned char *)raddr)[4+0] = (unsigned char)(iret2);
233               ((unsigned char *)raddr)[4+1] = (unsigned char)(iret2>>8);
234               ((unsigned char *)raddr)[4+2] = (unsigned char)(iret2>>16);
235               ((unsigned char *)raddr)[4+3] = (unsigned char)(iret2>>24);
236             }
237           }
238           #else /* Optimized: fewer conditional jumps, fewer memory accesses */
239           uintptr_t count = l->rsize; /* > 0, ≤ 2*sizeof(__avrword) */
240           __avrword* wordaddr = (__avrword*)((uintptr_t)raddr & ~(uintptr_t)(sizeof(__avrword)-1));
241           uintptr_t start_offset = (uintptr_t)raddr & (uintptr_t)(sizeof(__avrword)-1); /* ≥ 0, < sizeof(__avrword) */
242           uintptr_t end_offset = start_offset + count; /* > 0, < 3*sizeof(__avrword) */
243           if (count <= sizeof(__avrword)) {
244             /* Use iret. */
245             if (end_offset <= sizeof(__avrword)) {
246               /* 0 < end_offset ≤ sizeof(__avrword) */
247               __avrword mask0 = ((__avrword)2 << (end_offset*8-1)) - ((__avrword)1 << (start_offset*8));
248               wordaddr[0] ^= (wordaddr[0] ^ (iret << (start_offset*8))) & mask0;
249             } else {
250               /* sizeof(__avrword) < end_offset < 2*sizeof(__avrword), start_offset > 0 */
251               __avrword mask0 = - ((__avrword)1 << (start_offset*8));
252               __avrword mask1 = ((__avrword)2 << (end_offset*8-sizeof(__avrword)*8-1)) - 1;
253               wordaddr[0] ^= (wordaddr[0] ^ (iret << (start_offset*8))) & mask0;
254               wordaddr[1] ^= (wordaddr[1] ^ (iret >> (sizeof(__avrword)*8-start_offset*8))) & mask1;
255             }
256           } else {
257             /* Use iret, iret2. */
258             __avrword mask0 = - ((__avrword)1 << (start_offset*8));
259             wordaddr[0] ^= (wordaddr[0] ^ (iret << (start_offset*8))) & mask0;
260             if (end_offset <= 2*sizeof(__avrword)) {
261               /* sizeof(__avrword) < end_offset ≤ 2*sizeof(__avrword) */
262               __avrword mask1 = ((__avrword)2 << (end_offset*8-sizeof(__avrword)*8-1)) - 1;
263               wordaddr[1] ^= (wordaddr[1] ^ ((iret >> (sizeof(__avrword)*4-start_offset*4) >> (sizeof(__avrword)*4-start_offset*4)) | (iret2 << (start_offset*8)))) & mask1;
264             } else {
265               /* 2*sizeof(__avrword) < end_offset < 3*sizeof(__avrword), start_offset > 0 */
266               __avrword mask2 = ((__avrword)2 << (end_offset*8-2*sizeof(__avrword)*8-1)) - 1;
267               wordaddr[1] = (iret >> (sizeof(__avrword)*8-start_offset*8)) | (iret2 << (start_offset*8));
268               wordaddr[2] ^= (wordaddr[2] ^ (iret2 >> (sizeof(__avrword)*8-start_offset*8))) & mask2;
269             }
270           }
271           #endif
272         }
273       }
274     }
275   }
276   return 0;
277 }
278