Merge pull request #238 from KubaKaszycki/master
[libffi.git] / src / mips / o32.S
1 /* -----------------------------------------------------------------------
2    o32.S - Copyright (c) 1996, 1998, 2005  Red Hat, Inc.
3    
4    MIPS 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
31 /* Only build this code if we are compiling for o32 */  
32
33 #if defined(FFI_MIPS_O32)
34         
35 #define callback a0
36 #define bytes    a2
37 #define flags    a3
38                 
39 #define SIZEOF_FRAME    (4 * FFI_SIZEOF_ARG + 2 * FFI_SIZEOF_ARG)
40 #define A3_OFF          (SIZEOF_FRAME + 3 * FFI_SIZEOF_ARG)
41 #define FP_OFF          (SIZEOF_FRAME - 2 * FFI_SIZEOF_ARG)
42 #define RA_OFF          (SIZEOF_FRAME - 1 * FFI_SIZEOF_ARG)
43
44         .abicalls
45         .text
46         .align  2
47         .globl  ffi_call_O32
48         .ent    ffi_call_O32
49 ffi_call_O32:   
50 $LFB0:
51         # Prologue
52         SUBU    $sp, SIZEOF_FRAME       # Frame size
53 $LCFI00:
54         REG_S   $fp, FP_OFF($sp)        # Save frame pointer
55 $LCFI01:
56         REG_S   ra, RA_OFF($sp)         # Save return address
57 $LCFI02:
58         move    $fp, $sp
59
60 $LCFI03:
61         move    t9, callback            # callback function pointer
62         REG_S   flags, A3_OFF($fp)      # flags
63
64         # Allocate at least 4 words in the argstack
65         LI      v0, 4 * FFI_SIZEOF_ARG
66         blt     bytes, v0, sixteen
67
68         ADDU    v0, bytes, 7    # make sure it is aligned 
69         and     v0, -8          # to an 8 byte boundry
70
71 sixteen:
72         SUBU    $sp, v0         # move the stack pointer to reflect the
73                                 # arg space
74
75         ADDU    a0, $sp, 4 * FFI_SIZEOF_ARG
76
77         jalr    t9
78         
79         REG_L   t0, A3_OFF($fp)         # load the flags word
80         SRL     t2, t0, 4               # shift our arg info
81         and     t0, ((1<<4)-1)          # mask out the return type
82                 
83         ADDU    $sp, 4 * FFI_SIZEOF_ARG         # adjust $sp to new args
84
85 #ifndef __mips_soft_float
86         bnez    t0, pass_d                      # make it quick for int
87 #endif
88         REG_L   a0, 0*FFI_SIZEOF_ARG($sp)       # just go ahead and load the
89         REG_L   a1, 1*FFI_SIZEOF_ARG($sp)       # four regs.
90         REG_L   a2, 2*FFI_SIZEOF_ARG($sp)
91         REG_L   a3, 3*FFI_SIZEOF_ARG($sp)
92         b       call_it
93
94 #ifndef __mips_soft_float
95 pass_d:
96         bne     t0, FFI_ARGS_D, pass_f
97         l.d     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
98         REG_L   a2,   2*FFI_SIZEOF_ARG($sp)     # passing a double
99         REG_L   a3,   3*FFI_SIZEOF_ARG($sp)
100         b       call_it
101
102 pass_f: 
103         bne     t0, FFI_ARGS_F, pass_d_d
104         l.s     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
105         REG_L   a1,   1*FFI_SIZEOF_ARG($sp)     # passing a float
106         REG_L   a2,   2*FFI_SIZEOF_ARG($sp)
107         REG_L   a3,   3*FFI_SIZEOF_ARG($sp)
108         b       call_it         
109
110 pass_d_d:               
111         bne     t0, FFI_ARGS_DD, pass_f_f
112         l.d     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
113         l.d     $f14, 2*FFI_SIZEOF_ARG($sp)     # passing two doubles
114         b       call_it
115
116 pass_f_f:       
117         bne     t0, FFI_ARGS_FF, pass_d_f
118         l.s     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
119         l.s     $f14, 1*FFI_SIZEOF_ARG($sp)     # passing two floats
120         REG_L   a2,   2*FFI_SIZEOF_ARG($sp)
121         REG_L   a3,   3*FFI_SIZEOF_ARG($sp)
122         b       call_it
123
124 pass_d_f:               
125         bne     t0, FFI_ARGS_DF, pass_f_d
126         l.d     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
127         l.s     $f14, 2*FFI_SIZEOF_ARG($sp)     # passing double and float
128         REG_L   a3,   3*FFI_SIZEOF_ARG($sp)
129         b       call_it
130
131 pass_f_d:               
132  # assume that the only other combination must be float then double
133  #      bne     t0, FFI_ARGS_F_D, call_it
134         l.s     $f12, 0*FFI_SIZEOF_ARG($sp)     # load $fp regs from args
135         l.d     $f14, 2*FFI_SIZEOF_ARG($sp)     # passing double and float
136 #endif
137
138 call_it:        
139         # Load the static chain pointer
140         REG_L   t7, SIZEOF_FRAME + 6*FFI_SIZEOF_ARG($fp)
141
142         # Load the function pointer
143         REG_L   t9, SIZEOF_FRAME + 5*FFI_SIZEOF_ARG($fp)
144
145         # If the return value pointer is NULL, assume no return value.
146         REG_L   t1, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
147         beqz    t1, noretval
148
149         bne     t2, FFI_TYPE_INT, retlonglong
150         jalr    t9
151         REG_L   t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
152         REG_S   v0, 0(t0)
153         b       epilogue
154
155 retlonglong:
156         # Really any 64-bit int, signed or not.
157         bne     t2, FFI_TYPE_UINT64, retfloat
158         jalr    t9
159         REG_L   t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
160         REG_S   v1, 4(t0)
161         REG_S   v0, 0(t0)
162         b       epilogue
163
164 retfloat:
165         bne     t2, FFI_TYPE_FLOAT, retdouble
166         jalr    t9
167         REG_L   t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
168 #ifndef __mips_soft_float
169         s.s     $f0, 0(t0)
170 #else
171         REG_S   v0, 0(t0)
172 #endif
173         b       epilogue
174
175 retdouble:      
176         bne     t2, FFI_TYPE_DOUBLE, noretval
177         jalr    t9
178         REG_L   t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
179 #ifndef __mips_soft_float
180         s.d     $f0, 0(t0)
181 #else
182         REG_S   v1, 4(t0)
183         REG_S   v0, 0(t0)
184 #endif
185         b       epilogue
186         
187 noretval:       
188         jalr    t9
189         
190         # Epilogue
191 epilogue:       
192         move    $sp, $fp        
193         REG_L   $fp, FP_OFF($sp)        # Restore frame pointer
194         REG_L   ra, RA_OFF($sp)         # Restore return address
195         ADDU    $sp, SIZEOF_FRAME       # Fix stack pointer
196         j       ra
197
198 $LFE0:
199         .end    ffi_call_O32
200
201
202 /* ffi_closure_O32. Expects address of the passed-in ffi_closure
203         in t4 ($12). Stores any arguments passed in registers onto the
204         stack, then calls ffi_closure_mips_inner_O32, which
205         then decodes them.
206         
207         Stack layout:
208
209          3 - a3 save
210          2 - a2 save
211          1 - a1 save
212          0 - a0 save, original sp
213         -1 - ra save
214         -2 - fp save
215         -3 - $16 (s0) save
216         -4 - cprestore
217         -5 - return value high (v1)
218         -6 - return value low (v0)
219         -7 - f14 (le high, be low)
220         -8 - f14 (le low, be high)
221         -9 - f12 (le high, be low)
222        -10 - f12 (le low, be high)
223        -11 - Called function a5 save
224        -12 - Called function a4 save
225        -13 - Called function a3 save
226        -14 - Called function a2 save
227        -15 - Called function a1 save
228        -16 - Called function a0 save, our sp and fp point here
229          */
230         
231 #define SIZEOF_FRAME2   (16 * FFI_SIZEOF_ARG)
232 #define A3_OFF2         (SIZEOF_FRAME2 + 3 * FFI_SIZEOF_ARG)
233 #define A2_OFF2         (SIZEOF_FRAME2 + 2 * FFI_SIZEOF_ARG)
234 #define A1_OFF2         (SIZEOF_FRAME2 + 1 * FFI_SIZEOF_ARG)
235 #define A0_OFF2         (SIZEOF_FRAME2 + 0 * FFI_SIZEOF_ARG)
236 #define RA_OFF2         (SIZEOF_FRAME2 - 1 * FFI_SIZEOF_ARG)
237 #define FP_OFF2         (SIZEOF_FRAME2 - 2 * FFI_SIZEOF_ARG)
238 #define S0_OFF2         (SIZEOF_FRAME2 - 3 * FFI_SIZEOF_ARG)
239 #define GP_OFF2         (SIZEOF_FRAME2 - 4 * FFI_SIZEOF_ARG)
240 #define V1_OFF2         (SIZEOF_FRAME2 - 5 * FFI_SIZEOF_ARG)
241 #define V0_OFF2         (SIZEOF_FRAME2 - 6 * FFI_SIZEOF_ARG)
242 #define FA_1_1_OFF2     (SIZEOF_FRAME2 - 7 * FFI_SIZEOF_ARG)
243 #define FA_1_0_OFF2     (SIZEOF_FRAME2 - 8 * FFI_SIZEOF_ARG)
244 #define FA_0_1_OFF2     (SIZEOF_FRAME2 - 9 * FFI_SIZEOF_ARG)
245 #define FA_0_0_OFF2     (SIZEOF_FRAME2 - 10 * FFI_SIZEOF_ARG)
246 #define CALLED_A5_OFF2  (SIZEOF_FRAME2 - 11 * FFI_SIZEOF_ARG)
247 #define CALLED_A4_OFF2  (SIZEOF_FRAME2 - 12 * FFI_SIZEOF_ARG)
248
249         .text
250
251         .align  2
252         .globl  ffi_go_closure_O32
253         .ent    ffi_go_closure_O32
254 ffi_go_closure_O32:
255 $LFB1:
256         # Prologue
257         .frame  $fp, SIZEOF_FRAME2, ra
258         .set    noreorder
259         .cpload t9
260         .set    reorder
261         SUBU    $sp, SIZEOF_FRAME2
262         .cprestore GP_OFF2
263 $LCFI10:
264
265         REG_S   $16, S0_OFF2($sp)        # Save s0
266         REG_S   $fp, FP_OFF2($sp)        # Save frame pointer
267         REG_S   ra, RA_OFF2($sp)         # Save return address
268 $LCFI11:
269
270         move    $fp, $sp
271 $LCFI12:
272
273         REG_S   a0, A0_OFF2($fp)
274         REG_S   a1, A1_OFF2($fp)
275         REG_S   a2, A2_OFF2($fp)
276         REG_S   a3, A3_OFF2($fp)
277
278         # Load ABI enum to s0
279         REG_L   $16, 4($15)     # cif 
280         REG_L   $16, 0($16)     # abi is first member.
281
282         li      $13, 1          # FFI_O32
283         bne     $16, $13, 1f    # Skip fp save if FFI_O32_SOFT_FLOAT
284         
285         # Store all possible float/double registers.
286         s.d     $f12, FA_0_0_OFF2($fp)
287         s.d     $f14, FA_1_0_OFF2($fp)
288 1:
289         # prepare arguments for ffi_closure_mips_inner_O32
290         REG_L   a0, 4($15)       # cif 
291         REG_L   a1, 8($15)       # fun
292         move    a2, $15          # user_data = go closure
293         addu    a3, $fp, V0_OFF2 # rvalue
294
295         addu    t9, $fp, A0_OFF2 # ar
296         REG_S   t9, CALLED_A4_OFF2($fp)
297
298         addu    t9, $fp, FA_0_0_OFF2 #fpr
299         REG_S   t9, CALLED_A5_OFF2($fp)
300
301         b $do_closure
302
303 $LFE1:
304         .end ffi_go_closure_O32
305
306         .align  2
307         .globl  ffi_closure_O32
308         .ent    ffi_closure_O32
309 ffi_closure_O32:
310 $LFB2:
311         # Prologue
312         .frame  $fp, SIZEOF_FRAME2, ra
313         .set    noreorder
314         .cpload t9
315         .set    reorder
316         SUBU    $sp, SIZEOF_FRAME2
317         .cprestore GP_OFF2
318 $LCFI20:
319         REG_S   $16, S0_OFF2($sp)        # Save s0
320         REG_S   $fp, FP_OFF2($sp)        # Save frame pointer
321         REG_S   ra, RA_OFF2($sp)         # Save return address
322 $LCFI21:
323         move    $fp, $sp
324
325 $LCFI22:
326         # Store all possible argument registers. If there are more than
327         # four arguments, then they are stored above where we put a3.
328         REG_S   a0, A0_OFF2($fp)
329         REG_S   a1, A1_OFF2($fp)
330         REG_S   a2, A2_OFF2($fp)
331         REG_S   a3, A3_OFF2($fp)
332
333         # Load ABI enum to s0
334         REG_L   $16, 20($12)    # cif pointer follows tramp.
335         REG_L   $16, 0($16)     # abi is first member.
336
337         li      $13, 1          # FFI_O32
338         bne     $16, $13, 1f    # Skip fp save if FFI_O32_SOFT_FLOAT
339         
340 #ifndef __mips_soft_float
341         # Store all possible float/double registers.
342         s.d     $f12, FA_0_0_OFF2($fp)
343         s.d     $f14, FA_1_0_OFF2($fp)
344 #endif
345 1:      
346         # prepare arguments for ffi_closure_mips_inner_O32
347         REG_L   a0, 20($12)      # cif pointer follows tramp.
348         REG_L   a1, 24($12)      # fun
349         REG_L   a2, 28($12)      # user_data
350         addu    a3, $fp, V0_OFF2 # rvalue
351
352         addu    t9, $fp, A0_OFF2 # ar
353         REG_S   t9, CALLED_A4_OFF2($fp)
354
355         addu    t9, $fp, FA_0_0_OFF2 #fpr
356         REG_S   t9, CALLED_A5_OFF2($fp)
357
358 $do_closure:
359         la      t9, ffi_closure_mips_inner_O32
360         # Call ffi_closure_mips_inner_O32 to do the work.
361         jalr    t9
362
363         # Load the return value into the appropriate register.
364         move    $8, $2
365         li      $9, FFI_TYPE_VOID
366         beq     $8, $9, closure_done
367
368         li      $13, 1          # FFI_O32
369         bne     $16, $13, 1f    # Skip fp restore if FFI_O32_SOFT_FLOAT
370
371 #ifndef __mips_soft_float
372         li      $9, FFI_TYPE_FLOAT
373         l.s     $f0, V0_OFF2($fp)
374         beq     $8, $9, closure_done
375
376         li      $9, FFI_TYPE_DOUBLE
377         l.d     $f0, V0_OFF2($fp)
378         beq     $8, $9, closure_done
379 #endif
380 1:      
381         REG_L   $3, V1_OFF2($fp)
382         REG_L   $2, V0_OFF2($fp)
383
384 closure_done:
385         # Epilogue
386         move    $sp, $fp
387         REG_L   $16, S0_OFF2($sp)        # Restore s0
388         REG_L   $fp, FP_OFF2($sp)        # Restore frame pointer
389         REG_L   ra,  RA_OFF2($sp)        # Restore return address
390         ADDU    $sp, SIZEOF_FRAME2
391         j       ra
392 $LFE2:
393         .end    ffi_closure_O32
394
395 /* DWARF-2 unwind info. */
396
397         .section        .eh_frame,"a",@progbits
398 $Lframe0:
399         .4byte  $LECIE0-$LSCIE0  # Length of Common Information Entry
400 $LSCIE0:
401         .4byte  0x0      # CIE Identifier Tag
402         .byte   0x1      # CIE Version
403         .ascii "zR\0"    # CIE Augmentation
404         .uleb128 0x1     # CIE Code Alignment Factor
405         .sleb128 4       # CIE Data Alignment Factor
406         .byte   0x1f     # CIE RA Column
407         .uleb128 0x1     # Augmentation size
408         .byte   0x00     # FDE Encoding (absptr)
409         .byte   0xc      # DW_CFA_def_cfa
410         .uleb128 0x1d
411         .uleb128 0x0
412         .align  2
413 $LECIE0:
414
415 $LSFDE0:
416         .4byte  $LEFDE0-$LASFDE0         # FDE Length
417 $LASFDE0:
418         .4byte  $LASFDE0-$Lframe0        # FDE CIE offset
419         .4byte  $LFB0    # FDE initial location
420         .4byte  $LFE0-$LFB0      # FDE address range
421         .uleb128 0x0     # Augmentation size
422         .byte   0x4      # DW_CFA_advance_loc4
423         .4byte  $LCFI00-$LFB0
424         .byte   0xe      # DW_CFA_def_cfa_offset
425         .uleb128 0x18
426         .byte   0x4      # DW_CFA_advance_loc4
427         .4byte  $LCFI01-$LCFI00
428         .byte   0x11     # DW_CFA_offset_extended_sf
429         .uleb128 0x1e    # $fp
430         .sleb128 -2      # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
431         .byte   0x11     # DW_CFA_offset_extended_sf
432         .uleb128 0x1f    # $ra
433         .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
434         .byte   0x4      # DW_CFA_advance_loc4
435         .4byte  $LCFI02-$LCFI01
436         .byte   0xc      # DW_CFA_def_cfa
437         .uleb128 0x1e
438         .uleb128 0x18
439         .align  2
440 $LEFDE0:
441
442 $LSFDE1:
443         .4byte  $LEFDE1-$LASFDE1         # FDE Length
444 $LASFDE1:
445         .4byte  $LASFDE1-$Lframe0        # FDE CIE offset
446         .4byte  $LFB1    # FDE initial location
447         .4byte  $LFE1-$LFB1      # FDE address range
448         .uleb128 0x0     # Augmentation size
449         .byte   0x4      # DW_CFA_advance_loc4
450         .4byte  $LCFI10-$LFB1
451         .byte   0xe      # DW_CFA_def_cfa_offset
452         .uleb128 SIZEOF_FRAME2
453         .byte   0x4      # DW_CFA_advance_loc4
454         .4byte  $LCFI11-$LCFI10
455         .byte   0x11     # DW_CFA_offset_extended_sf
456         .uleb128 0x10    # $16
457         .sleb128 -3      # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
458         .byte   0x11     # DW_CFA_offset_extended_sf
459         .uleb128 0x1e    # $fp
460         .sleb128 -2      # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
461         .byte   0x11     # DW_CFA_offset_extended_sf
462         .uleb128 0x1f    # $ra
463         .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
464         .byte   0x4      # DW_CFA_advance_loc4
465         .4byte  $LCFI12-$LCFI11
466         .byte   0xc      # DW_CFA_def_cfa
467         .uleb128 0x1e
468         .uleb128 SIZEOF_FRAME2
469         .align  2
470 $LEFDE1:
471
472 $LSFDE2:
473         .4byte  $LEFDE2-$LASFDE2         # FDE Length
474 $LASFDE2:
475         .4byte  $LASFDE2-$Lframe0        # FDE CIE offset
476         .4byte  $LFB2    # FDE initial location
477         .4byte  $LFE2-$LFB2      # FDE address range
478         .uleb128 0x0     # Augmentation size
479         .byte   0x4      # DW_CFA_advance_loc4
480         .4byte  $LCFI20-$LFB2
481         .byte   0xe      # DW_CFA_def_cfa_offset
482         .uleb128 SIZEOF_FRAME2
483         .byte   0x4      # DW_CFA_advance_loc4
484         .4byte  $LCFI21-$LCFI20
485         .byte   0x11     # DW_CFA_offset_extended_sf
486         .uleb128 0x10    # $16
487         .sleb128 -3      # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
488         .byte   0x11     # DW_CFA_offset_extended_sf
489         .uleb128 0x1e    # $fp
490         .sleb128 -2      # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
491         .byte   0x11     # DW_CFA_offset_extended_sf
492         .uleb128 0x1f    # $ra
493         .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
494         .byte   0x4      # DW_CFA_advance_loc4
495         .4byte  $LCFI22-$LCFI21
496         .byte   0xc      # DW_CFA_def_cfa
497         .uleb128 0x1e
498         .uleb128 SIZEOF_FRAME2
499         .align  2
500 $LEFDE2:
501
502 #endif