Refresh from GCC
[libffi.git] / src / arm / sysv.S
1 /* -----------------------------------------------------------------------
2    sysv.S - Copyright (c) 1998, 2008 Red Hat, Inc.
3    
4    ARM Foreign Function Interface 
5
6    Permission is hereby granted, free of charge, to any person obtaining
7    a copy of this software and associated documentation files (the
8    ``Software''), to deal in the Software without restriction, including
9    without limitation the rights to use, copy, modify, merge, publish,
10    distribute, sublicense, and/or sell copies of the Software, and to
11    permit persons to whom the Software is furnished to do so, subject to
12    the following conditions:
13
14    The above copyright notice and this permission notice shall be included
15    in all copies or substantial portions of the Software.
16
17    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
18    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20    NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24    DEALINGS IN THE SOFTWARE.
25    ----------------------------------------------------------------------- */
26
27 #define LIBFFI_ASM      
28 #include <fficonfig.h>
29 #include <ffi.h>
30 #ifdef HAVE_MACHINE_ASM_H
31 #include <machine/asm.h>
32 #else
33 #ifdef __USER_LABEL_PREFIX__
34 #define CONCAT1(a, b) CONCAT2(a, b)
35 #define CONCAT2(a, b) a ## b
36
37 /* Use the right prefix for global labels.  */
38 #define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
39 #else
40 #define CNAME(x) x
41 #endif
42 #define ENTRY(x) .globl CNAME(x); .type CNAME(x),%function; CNAME(x):
43 #endif
44
45 #ifdef __ELF__
46 #define LSYM(x) .x
47 #else
48 #define LSYM(x) x
49 #endif
50
51 /* We need a better way of testing for this, but for now, this is all 
52    we can do.  */
53 @ This selects the minimum architecture level required.
54 #define __ARM_ARCH__ 3
55
56 #if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
57 # undef __ARM_ARCH__
58 # define __ARM_ARCH__ 4
59 #endif
60         
61 #if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
62         || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
63         || defined(__ARM_ARCH_5TEJ__)
64 # undef __ARM_ARCH__
65 # define __ARM_ARCH__ 5
66 #endif
67
68 #if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
69         || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
70         || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
71         || defined(__ARM_ARCH_6M__)
72 # undef __ARM_ARCH__
73 # define __ARM_ARCH__ 6
74 #endif
75
76 #if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
77         || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
78         || defined(__ARM_ARCH_7EM__)
79 # undef __ARM_ARCH__
80 # define __ARM_ARCH__ 7
81 #endif
82
83 #if __ARM_ARCH__ >= 5
84 # define call_reg(x)    blx     x
85 #elif defined (__ARM_ARCH_4T__)
86 # define call_reg(x)    mov     lr, pc ; bx     x
87 # if defined(__thumb__) || defined(__THUMB_INTERWORK__)
88 #  define __INTERWORKING__
89 # endif
90 #else
91 # define call_reg(x)    mov     lr, pc ; mov    pc, x
92 #endif
93
94 /* Conditionally compile unwinder directives.  */
95 #ifdef __ARM_EABI__
96 #define UNWIND
97 #else
98 #define UNWIND @
99 #endif  
100
101         
102 #if defined(__thumb__) && !defined(__THUMB_INTERWORK__)
103 .macro  ARM_FUNC_START name
104         .text
105         .align 0
106         .thumb
107         .thumb_func
108         ENTRY(\name)
109         bx      pc
110         nop
111         .arm
112         UNWIND .fnstart
113 /* A hook to tell gdb that we've switched to ARM mode.  Also used to call
114    directly from other local arm routines.  */
115 _L__\name:              
116 .endm
117 #else
118 .macro  ARM_FUNC_START name
119         .text
120         .align 0
121         .arm
122         ENTRY(\name)
123         UNWIND .fnstart
124 .endm
125 #endif
126
127 .macro  RETLDM  regs=, cond=, dirn=ia
128 #if defined (__INTERWORKING__)
129         .ifc "\regs",""
130         ldr\cond        lr, [sp], #4
131         .else
132         ldm\cond\dirn   sp!, {\regs, lr}
133         .endif
134         bx\cond lr
135 #else
136         .ifc "\regs",""
137         ldr\cond        pc, [sp], #4
138         .else
139         ldm\cond\dirn   sp!, {\regs, pc}
140         .endif
141 #endif
142 .endm
143
144
145         @ r0:   fn
146         @ r1:   &ecif
147         @ r2:   cif->bytes
148         @ r3:   fig->flags
149         @ sp+0: ecif.rvalue
150
151         @ This assumes we are using gas.
152 ARM_FUNC_START ffi_call_SYSV
153         @ Save registers
154         stmfd   sp!, {r0-r3, fp, lr}
155         UNWIND .save    {r0-r3, fp, lr}
156         mov     fp, sp
157
158         UNWIND .setfp   fp, sp
159
160         @ Make room for all of the new args.
161         sub     sp, fp, r2
162
163         @ Place all of the ffi_prep_args in position
164         mov     r0, sp
165         @     r1 already set
166
167         @ Call ffi_prep_args(stack, &ecif)
168         bl      ffi_prep_args
169
170         @ move first 4 parameters in registers
171         ldmia   sp, {r0-r3}
172
173         @ and adjust stack
174         sub     lr, fp, sp      @ cif->bytes == fp - sp
175         ldr     ip, [fp]        @ load fn() in advance
176         cmp     lr, #16
177         movhs   lr, #16
178         add     sp, sp, lr
179
180         @ call (fn) (...)
181         call_reg(ip)
182         
183         @ Remove the space we pushed for the args
184         mov     sp, fp
185
186         @ Load r2 with the pointer to storage for the return value
187         ldr     r2, [sp, #24]
188
189         @ Load r3 with the return type code 
190         ldr     r3, [sp, #12]
191
192         @ If the return value pointer is NULL, assume no return value.
193         cmp     r2, #0
194         beq     LSYM(Lepilogue)
195
196 @ return INT
197         cmp     r3, #FFI_TYPE_INT
198 #if defined(__SOFTFP__) || defined(__ARM_EABI__)
199         cmpne   r3, #FFI_TYPE_FLOAT
200 #endif
201         streq   r0, [r2]
202         beq     LSYM(Lepilogue)
203
204         @ return INT64
205         cmp     r3, #FFI_TYPE_SINT64
206 #if defined(__SOFTFP__) || defined(__ARM_EABI__)
207         cmpne   r3, #FFI_TYPE_DOUBLE
208 #endif
209         stmeqia r2, {r0, r1}
210
211 #if !defined(__SOFTFP__) && !defined(__ARM_EABI__)
212         beq     LSYM(Lepilogue)
213
214 @ return FLOAT
215         cmp     r3, #FFI_TYPE_FLOAT
216         stfeqs  f0, [r2]
217         beq     LSYM(Lepilogue)
218
219 @ return DOUBLE or LONGDOUBLE
220         cmp     r3, #FFI_TYPE_DOUBLE
221         stfeqd  f0, [r2]
222 #endif
223
224 LSYM(Lepilogue):
225         RETLDM  "r0-r3,fp"
226
227 .ffi_call_SYSV_end:
228         UNWIND .fnend
229         .size    CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
230
231
232 /*
233         unsigned int FFI_HIDDEN
234         ffi_closure_SYSV_inner (closure, respp, args)
235              ffi_closure *closure;
236              void **respp;
237              void *args;
238 */
239
240 ARM_FUNC_START ffi_closure_SYSV
241         UNWIND .pad #16
242         add     ip, sp, #16
243         stmfd   sp!, {ip, lr}
244         UNWIND .save    {r0, lr}
245         add     r2, sp, #8
246         UNWIND .pad #16
247         sub     sp, sp, #16
248         str     sp, [sp, #8]
249         add     r1, sp, #8
250         bl      ffi_closure_SYSV_inner
251         cmp     r0, #FFI_TYPE_INT
252         beq     .Lretint
253
254         cmp     r0, #FFI_TYPE_FLOAT
255 #if defined(__SOFTFP__) || defined(__ARM_EABI__)
256         beq     .Lretint
257 #else
258         beq     .Lretfloat
259 #endif
260
261         cmp     r0, #FFI_TYPE_DOUBLE
262 #if defined(__SOFTFP__) || defined(__ARM_EABI__)
263         beq     .Lretlonglong
264 #else
265         beq     .Lretdouble
266 #endif
267
268         cmp     r0, #FFI_TYPE_LONGDOUBLE
269 #if defined(__SOFTFP__) || defined(__ARM_EABI__)
270         beq     .Lretlonglong
271 #else
272         beq     .Lretlongdouble
273 #endif
274
275         cmp     r0, #FFI_TYPE_SINT64
276         beq     .Lretlonglong
277 .Lclosure_epilogue:
278         add     sp, sp, #16
279         ldmfd   sp, {sp, pc}
280 .Lretint:
281         ldr     r0, [sp]
282         b       .Lclosure_epilogue
283 .Lretlonglong:
284         ldr     r0, [sp]
285         ldr     r1, [sp, #4]
286         b       .Lclosure_epilogue
287
288 #if !defined(__SOFTFP__) && !defined(__ARM_EABI__)
289 .Lretfloat:
290         ldfs    f0, [sp]
291         b       .Lclosure_epilogue
292 .Lretdouble:
293         ldfd    f0, [sp]
294         b       .Lclosure_epilogue
295 .Lretlongdouble:
296         ldfd    f0, [sp]
297         b       .Lclosure_epilogue
298 #endif
299
300 .ffi_closure_SYSV_end:
301         UNWIND .fnend
302         .size    CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
303
304
305 /* Below are VFP hard-float ABI call and closure implementations.
306    Add VFP FPU directive here. */
307         .fpu    vfp
308
309         @ r0:   fn
310         @ r1:   &ecif
311         @ r2:   cif->bytes
312         @ r3:   fig->flags
313         @ sp+0: ecif.rvalue
314
315 ARM_FUNC_START ffi_call_VFP
316         @ Save registers
317         stmfd   sp!, {r0-r3, fp, lr}
318         UNWIND .save    {r0-r3, fp, lr}
319         mov     fp, sp
320         UNWIND .setfp   fp, sp
321
322         @ Make room for all of the new args.
323         sub     sp, sp, r2
324
325         @ Make room for loading VFP args
326         sub     sp, sp, #64
327
328         @ Place all of the ffi_prep_args in position
329         mov     r0, sp
330         @     r1 already set
331         sub     r2, fp, #64   @ VFP scratch space
332
333         @ Call ffi_prep_args(stack, &ecif, vfp_space)
334         bl      ffi_prep_args
335
336         @ Load VFP register args if needed
337         cmp     r0, #0
338         beq     LSYM(Lbase_args)
339
340         @ Load only d0 if possible
341         cmp     r0, #3
342         sub     ip, fp, #64
343         flddle  d0, [ip]
344         fldmiadgt       ip, {d0-d7}
345
346 LSYM(Lbase_args):
347         @ move first 4 parameters in registers
348         ldmia   sp, {r0-r3}
349
350         @ and adjust stack
351         sub     lr, ip, sp      @ cif->bytes == (fp - 64) - sp
352         ldr     ip, [fp]        @ load fn() in advance
353         cmp     lr, #16
354         movhs   lr, #16
355         add     sp, sp, lr
356
357         @ call (fn) (...)
358         call_reg(ip)
359
360         @ Remove the space we pushed for the args
361         mov     sp, fp
362
363         @ Load r2 with the pointer to storage for
364         @ the return value
365         ldr     r2, [sp, #24]
366
367         @ Load r3 with the return type code 
368         ldr     r3, [sp, #12]
369
370         @ If the return value pointer is NULL,
371         @ assume no return value.
372         cmp     r2, #0
373         beq     LSYM(Lepilogue_vfp)
374         
375         cmp     r3, #FFI_TYPE_INT
376         streq   r0, [r2]
377         beq     LSYM(Lepilogue_vfp)
378
379         cmp     r3, #FFI_TYPE_SINT64
380         stmeqia r2, {r0, r1}
381         beq     LSYM(Lepilogue_vfp)
382
383         cmp     r3, #FFI_TYPE_FLOAT
384         fstseq  s0, [r2]
385         beq     LSYM(Lepilogue_vfp)
386         
387         cmp     r3, #FFI_TYPE_DOUBLE
388         fstdeq  d0, [r2]
389         beq     LSYM(Lepilogue_vfp)
390
391         cmp     r3, #FFI_TYPE_STRUCT_VFP_FLOAT
392         cmpne   r3, #FFI_TYPE_STRUCT_VFP_DOUBLE
393         fstmiadeq       r2, {d0-d3}
394
395 LSYM(Lepilogue_vfp):
396         RETLDM  "r0-r3,fp"
397
398 .ffi_call_VFP_end:
399         UNWIND .fnend
400         .size    CNAME(ffi_call_VFP),.ffi_call_VFP_end-CNAME(ffi_call_VFP)
401
402
403 ARM_FUNC_START ffi_closure_VFP
404         fstmfdd sp!, {d0-d7}
405         @ r0-r3, then d0-d7
406         UNWIND .pad #80
407         add     ip, sp, #80
408         stmfd   sp!, {ip, lr}
409         UNWIND .save    {r0, lr}
410         add     r2, sp, #72
411         add     r3, sp, #8
412         UNWIND .pad #72
413         sub     sp, sp, #72
414         str     sp, [sp, #64]
415         add     r1, sp, #64
416         bl      ffi_closure_SYSV_inner
417
418         cmp     r0, #FFI_TYPE_INT
419         beq     .Lretint_vfp
420
421         cmp     r0, #FFI_TYPE_FLOAT
422         beq     .Lretfloat_vfp
423
424         cmp     r0, #FFI_TYPE_DOUBLE
425         cmpne   r0, #FFI_TYPE_LONGDOUBLE
426         beq     .Lretdouble_vfp
427
428         cmp     r0, #FFI_TYPE_SINT64
429         beq     .Lretlonglong_vfp
430
431         cmp     r0, #FFI_TYPE_STRUCT_VFP_FLOAT
432         beq     .Lretfloat_struct_vfp
433
434         cmp     r0, #FFI_TYPE_STRUCT_VFP_DOUBLE
435         beq     .Lretdouble_struct_vfp
436         
437 .Lclosure_epilogue_vfp:
438         add     sp, sp, #72
439         ldmfd   sp, {sp, pc}
440
441 .Lretfloat_vfp:
442         flds    s0, [sp]
443         b       .Lclosure_epilogue_vfp
444 .Lretdouble_vfp:
445         fldd    d0, [sp]
446         b       .Lclosure_epilogue_vfp
447 .Lretint_vfp:
448         ldr     r0, [sp]
449         b       .Lclosure_epilogue_vfp
450 .Lretlonglong_vfp:
451         ldmia   sp, {r0, r1}
452         b       .Lclosure_epilogue_vfp
453 .Lretfloat_struct_vfp:
454         fldmiad sp, {d0-d1}
455         b       .Lclosure_epilogue_vfp
456 .Lretdouble_struct_vfp:
457         fldmiad sp, {d0-d3}
458         b       .Lclosure_epilogue_vfp
459
460 .ffi_closure_VFP_end:
461         UNWIND .fnend
462         .size    CNAME(ffi_closure_VFP),.ffi_closure_VFP_end-CNAME(ffi_closure_VFP)
463
464 #if defined __ELF__ && defined __linux__
465         .section        .note.GNU-stack,"",%progbits
466 #endif