1.0.41.27: ppc: Calling convention fixes for assembly-routines calling static-funs.
[sbcl.git] / src / runtime / mips-assem.S
1 /*
2  * very-low-level utilities for runtime support
3  */
4
5 /*
6  * This software is part of the SBCL system. See the README file for
7  * more information.
8  *
9  * This software is derived from the CMU CL system, which was
10  * written at Carnegie Mellon University and released into the
11  * public domain. The software is in the public domain and is
12  * provided with absolutely no warranty. See the COPYING and CREDITS
13  * files for more information.
14  */
15 \f
16 #include "sbcl.h"
17 #include "lispregs.h"
18 #include "globals.h"
19 #include "genesis/fdefn.h"
20 #include "genesis/closure.h"
21 #include "genesis/funcallable-instance.h"
22 #include "genesis/return-pc.h"
23 #include "genesis/simple-fun.h"
24 #include "genesis/static-symbols.h"
25                 
26 #define zero $0
27 #define AT $1
28 #define v0 $2
29 #define v1 $3
30 #define a0 $4
31 #define a1 $5
32 #define a2 $6
33 #define a3 $7
34 #define t0 $8
35 #define t1 $9
36 #define t2 $10
37 #define t3 $11
38 #define t4 $12
39 #define t5 $13
40 #define t6 $14
41 #define t7 $15
42 #define s0 $16
43 #define s1 $17
44 #define s2 $18
45 #define s3 $19
46 #define s4 $20
47 #define s5 $21
48 #define s6 $22
49 #define s7 $23
50 #define t8 $24
51 #define t9 $25
52 #define k0 $26
53 #define k1 $27
54 #define gp $28
55 #define sp $29
56 #define s8 $30
57 #define ra $31
58
59 /*
60  * LEAF - declare leaf routine
61  */
62 #define LEAF(symbol)                                    \
63                 .globl  symbol;                         \
64                 .align  2;                              \
65                 .type   symbol,@function;               \
66                 .ent    symbol,0;                       \
67 symbol:         .frame  sp,0,ra
68
69 /*
70  * NESTED - declare nested routine entry point
71  */
72 #define NESTED(symbol, framesize, rpc)                  \
73                 .globl  symbol;                         \
74                 .align  2;                              \
75                 .type   symbol,@function;               \
76                 .ent    symbol,0;                       \
77 symbol:         .frame  sp, framesize, rpc
78
79 /*
80  * END - mark end of function
81  */
82 #define END(function)                                   \
83                 .end    function;                       \
84                 .size   function,.-function
85
86 /*
87  * EXPORT - export definition of symbol
88  */
89 #define EXPORT(symbol)                                  \
90                 .globl  symbol;                         \
91 symbol:
92
93 /*
94  * FEXPORT - export definition of a function symbol
95  */
96 #define FEXPORT(symbol)                                 \
97                 .globl  symbol;                         \
98                 .type   symbol,@function;               \
99 symbol:
100
101
102         .text
103         
104 /*
105  * Function to transfer control into lisp.
106  */
107 #define framesize 16*4
108         NESTED(call_into_lisp, framesize, ra)
109         .set    noreorder
110         .cpload t9
111         .set    reorder
112         subu    sp, framesize
113
114         /* Save all the C regs. */
115         .mask   0xc0ff0000, -8
116         sw      ra, framesize-8(sp)
117         sw      s8, framesize-12(sp)
118         /* No .cprestore, we don't want automatic gp restauration. */
119         sw      gp, framesize-16(sp)
120         sw      s7, framesize-20(sp)
121         sw      s6, framesize-24(sp)
122         sw      s5, framesize-28(sp)
123         sw      s4, framesize-32(sp)
124         sw      s3, framesize-36(sp)
125         sw      s2, framesize-40(sp)
126         sw      s1, framesize-44(sp)
127         sw      s0, framesize-48(sp)
128
129         li      reg_NIL, NIL
130
131         /* Clear unsaved boxed descriptor regs */
132         li      reg_FDEFN, 0            # t6
133         li      reg_L1, 0               # t8
134
135         /* Turn on pseudo-atomic. */
136         .set    noreorder
137         li      reg_NL4, 0
138         li      reg_ALLOC, 1
139         .set    reorder
140
141         /* Load the allocation pointer, preserving the low-bit of alloc */
142         lw      reg_BSP, dynamic_space_free_pointer
143         addu    reg_ALLOC, reg_BSP
144
145         /* Load the rest of the LISP state. */
146         lw      reg_BSP, current_binding_stack_pointer
147         lw      reg_CSP, current_control_stack_pointer
148         lw      reg_OCFP, current_control_frame_pointer
149
150         /* Check for interrupt */
151         .set    noreorder
152         bgez    reg_NL4, 1f
153          subu   reg_ALLOC, 1
154         break   0x0, 0x10
155 1:      .set    reorder
156
157         /* Pass in args */
158         move    reg_LEXENV, a0
159         move    reg_CFP, a1
160         sll     reg_NARGS, a2, 2
161         lw      reg_A0, 0(reg_CFP)
162         lw      reg_A1, 4(reg_CFP)
163         lw      reg_A2, 8(reg_CFP)
164         lw      reg_A3, 12(reg_CFP)
165         lw      reg_A4, 16(reg_CFP)
166         lw      reg_A5, 20(reg_CFP)
167
168         /* Calculate LRA */
169         la      reg_LRA, lra - RETURN_PC_RETURN_POINT_OFFSET
170
171         /* Indirect closure */
172         lw      reg_CODE, CLOSURE_FUN_OFFSET(reg_LEXENV)
173
174         addu    reg_LIP, reg_CODE, SIMPLE_FUN_CODE_OFFSET
175
176         /* Mark us as in Lisp land. */
177         sw      zero, foreign_function_call_active
178
179         /* Jump into lisp land. */
180         jr      reg_LIP
181
182         .align  3
183         .set    noreorder
184 lra:    .word   RETURN_PC_HEADER_WIDETAG
185
186         /* Multiple value return spot, clear stack. */
187         move    reg_CSP, reg_OCFP
188         nop
189
190         /* Single value return spot. */
191
192         /* Nested lisp -> C calls may have clobbered gp. */
193         lw      gp, framesize-16(sp)
194
195         /* Mark us as in C land. */
196         sw      reg_CSP, foreign_function_call_active
197
198         /* Set the pseudo-atomic flag. */
199         li      reg_NL4, 0
200         addu    reg_ALLOC, 1
201         .set    reorder
202
203         /* Save LISP state. */
204         subu    reg_NL0, reg_ALLOC, 1
205         sw      reg_NL0, dynamic_space_free_pointer
206         sw      reg_BSP, current_binding_stack_pointer
207         sw      reg_CSP, current_control_stack_pointer
208         sw      reg_CFP, current_control_frame_pointer
209
210         /* Check for interrupt */
211         .set    noreorder
212         bgez    reg_NL4, 1f
213          subu   reg_ALLOC, 1
214         break   0x0, 0x10
215 1:      .set    reorder
216
217         /* Pass one return value back to C land. For a 64bit value, we may
218            need to clobber v1 aka reg_NL4. */
219         move    v0, reg_A0      # reg_CFUNC
220         move    v1, reg_A1      # reg_NL4
221
222         /* Restore C regs */
223         lw      ra, framesize-8(sp)
224         lw      s8, framesize-12(sp)
225         lw      s7, framesize-20(sp)
226         lw      s6, framesize-24(sp)
227         lw      s5, framesize-28(sp)
228         lw      s4, framesize-32(sp)
229         lw      s3, framesize-36(sp)
230         lw      s2, framesize-40(sp)
231         lw      s1, framesize-44(sp)
232         lw      s0, framesize-48(sp)
233
234         /* Restore C stack. */
235         addu    sp, framesize
236
237         /* Back we go. */
238         jr      ra
239
240         END(call_into_lisp)
241
242 /*
243  * Transfering control from Lisp into C
244  */
245         NESTED(call_into_c, 0, ra)
246         /* The C stack frame was already set up from lisp, and the
247            argument registers as well. We have to fake the correct
248            gp value for this function, though. */
249         .set    noreorder
250         /* reg_NL3 is AT. */
251         .set    noat
252         lui     gp, %hi(_gp_disp)
253         addiu   gp, %lo(_gp_disp)
254         lui     reg_NL3, %hi(call_into_c)
255         addiu   reg_NL3, %lo(call_into_c)
256         addu    gp, reg_NL3
257         .set    at
258         .set    reorder
259
260         /* Setup the lisp stack. */
261         move    reg_OCFP, reg_CFP
262         move    reg_CFP, reg_CSP
263         addu    reg_CSP, reg_CFP, 32
264
265         /* Mark us as in C land. */
266         sw      reg_CSP, foreign_function_call_active
267
268         /* Set the pseudo-atomic flag. */
269         .set    noreorder
270         li      reg_NL4, 0
271         addu    reg_ALLOC, 1
272         .set    reorder
273
274         /* Convert the return address to an offset and save it on the stack. */
275         subu    reg_NFP, reg_LIP, reg_CODE
276         addu    reg_NFP, OTHER_POINTER_LOWTAG
277         sw      reg_LRA, (reg_CFP)
278         sw      reg_CODE, 4(reg_CFP)
279         sw      gp, 8(reg_CFP)
280
281         /* Save LISP state. */
282         subu    reg_A0, reg_ALLOC, 1
283         sw      reg_A0, dynamic_space_free_pointer
284         sw      reg_BSP, current_binding_stack_pointer
285         sw      reg_CSP, current_control_stack_pointer
286         sw      reg_CFP, current_control_frame_pointer
287
288         /* Check for interrupt */
289         .set    noreorder
290         bgez    reg_NL4, 1f
291          subu   reg_ALLOC, 1
292         break   0x0, 0x10
293 1:      .set    reorder
294
295         /* Into C land we go. */
296         move    t9, reg_CFUNC           # reg_ALLOC
297         jalr    t9
298
299         lw      gp, 8(reg_CFP)
300
301         /* Pass 64bit return value to lisp land. */ 
302         move    reg_NL0, v0             # reg_CFUNC
303         move    reg_NL1, v1             # reg_NL4
304
305         /*
306          * Clear boxed descriptor registers before allowing an interrupt.
307          * We can't rely on C saving some of those registers, they might
308          * have been GCed in the meanwhile.
309          */
310         li      reg_A0, 0               # t0
311         li      reg_A1, 0               # t1
312         li      reg_A2, 0               # t2
313         li      reg_A3, 0               # t3
314         li      reg_A4, 0               # t4
315         li      reg_A5, 0               # t5
316         li      reg_FDEFN, 0            # t6
317         li      reg_LEXENV, 0           # t7
318         /*
319          * reg_NFP and reg_OCFP are pointing to fixed locations and are
320          * preserved by C.
321          */
322         li      reg_LRA, 0              # s2
323         li      reg_L0, 0               # s3
324         li      reg_L1, 0               # t8
325         li      reg_CODE, 0             # s8
326         li      reg_LIP, 0              # ra
327
328         /* Turn on pseudo-atomic. */
329         .set    noreorder
330         li      reg_NL4, 0
331         li      reg_ALLOC, 1
332         .set    reorder
333
334         /* Load the allocation pointer, preserving the low-bit of alloc */
335         lw      reg_BSP, dynamic_space_free_pointer
336         addu    reg_ALLOC, reg_BSP
337
338         lw      reg_BSP, current_binding_stack_pointer
339
340         /* Restore LRA & CODE */
341         lw      reg_LRA, (reg_CFP)
342         lw      reg_CODE, 4(reg_CFP)
343         subu    reg_LIP, reg_NFP, OTHER_POINTER_LOWTAG
344         addu    reg_LIP, reg_CODE
345
346         /* Check for interrupt */
347         .set    noreorder
348         bgez    reg_NL4, 1f
349          subu   reg_ALLOC, 1
350         break   0x0, 0x10
351 1:      .set    reorder
352
353         /* Reset the lisp stack. */
354         /* Note: OCFP and CFP are in saved regs. */
355         move    reg_CSP, reg_CFP
356         move    reg_CFP, reg_OCFP
357
358         /* Mark us as in Lisp land. */
359         sw      zero, foreign_function_call_active
360
361         /* Return to LISP. */
362         jr      reg_LIP
363         END(call_into_c)
364
365 /*
366  * Trampolines follow the Lisp calling convention.
367  *
368  * The undefined-function trampoline.
369  */
370         .align  3 /* minimum alignment for a lisp object */
371         .word   SIMPLE_FUN_HEADER_WIDETAG /* header */
372         .word   undefined_tramp - SIMPLE_FUN_CODE_OFFSET /* self */
373         .word   NIL /* next */
374         .word   NIL /* name */
375         .word   NIL /* arglist */
376         .word   NIL /* type */
377         .word   NIL /* xrefs */
378         LEAF(undefined_tramp)
379         /* Point reg_CODE to the header and tag it as function, since
380            the debugger regards a function pointer in reg_CODE which
381            doesn't point to a code object as undefined function.  */
382         lui     reg_CODE, %hi(undefined_tramp)
383         addiu   reg_CODE, %lo(undefined_tramp)
384         addiu   reg_CODE, -SIMPLE_FUN_CODE_OFFSET
385         .set    noreorder
386         b       1f
387          break  0x0, trap_Cerror
388         /* Error data length. */
389         .byte   4
390         /* Error number. */
391         .byte   UNDEFINED_FUN_ERROR
392         /* Magic value 254 means a 16bit little endian value follows.
393            See debug-var-io.lisp. */
394         .byte   254
395         /* reg_FDEFN is #14. */
396         .byte   ((14 << 5) + sc_DescriptorReg) % 0x100
397         .byte   ((14 << 5) + sc_DescriptorReg) / 0x100
398         .align  2
399         .set    reorder
400 1:      lw      reg_CODE, FDEFN_FUN_OFFSET(reg_FDEFN)
401         lw      reg_LIP, SIMPLE_FUN_CODE_OFFSET(reg_CODE)
402         jr      reg_LIP
403         END(undefined_tramp)
404
405 /*
406  * The closure trampoline.
407  */
408         .align  5 /* common MIPS cacheline size */
409         .word   0 /* pad 1 */
410         .word   0 /* pad 2 */
411         .word   SIMPLE_FUN_HEADER_WIDETAG /* header */
412         .word   closure_tramp - SIMPLE_FUN_CODE_OFFSET /* self */
413         .word   NIL /* next */
414         .word   NIL /* name */
415         .word   NIL /* arglist */
416         .word   NIL /* type */
417         .word   NIL /* xrefs */
418         LEAF(closure_tramp)
419         lw      reg_LEXENV, FDEFN_FUN_OFFSET(reg_FDEFN)
420         lw      reg_CODE, CLOSURE_FUN_OFFSET(reg_LEXENV)
421         addu    reg_LIP, reg_CODE, SIMPLE_FUN_CODE_OFFSET
422         jr      reg_LIP
423         END(closure_tramp)
424
425 /*
426  * The trampoline for funcallable instances
427  */
428         .globl funcallable_instance_tramp
429         .align  3
430         .word   SIMPLE_FUN_HEADER_WIDETAG
431 funcallable_instance_tramp = . + 1
432         .word   funcallable_instance_tramp
433         .word   NIL
434         .word   NIL
435         .word   NIL
436         .word   NIL
437         .word   NIL
438
439         lw      reg_LEXENV, FUNCALLABLE_INSTANCE_FUNCTION_OFFSET(reg_LEXENV)
440         lw      reg_CODE, CLOSURE_FUN_OFFSET(reg_LEXENV)
441         addu    reg_LIP, reg_CODE, SIMPLE_FUN_CODE_OFFSET
442         jr      reg_LIP
443         nop
444
445 /*
446  * Function-end breakpoint magic. This is truely magic, the code is
447  * copied and has to be relocatable. It also needs a properly aligned
448  * header tag after the fun_end_breakpoint_guts symbol.
449  */
450         .align  3 /* minimum alignment for a lisp object */
451         LEAF(fun_end_breakpoint_guts)
452         .set    noreorder
453         .word   RETURN_PC_HEADER_WIDETAG
454         b       multiple_value_return
455          nop
456         .set    reorder
457
458         /* single value return */
459
460         move    reg_OCFP, reg_CSP
461         addu    reg_CSP, 4
462         li      reg_NARGS, 4
463         move    reg_A1, reg_NIL
464         move    reg_A2, reg_NIL
465         move    reg_A3, reg_NIL
466         move    reg_A4, reg_NIL
467         move    reg_A5, reg_NIL
468
469 multiple_value_return:
470
471         FEXPORT(fun_end_breakpoint_trap)
472         .set    noreorder
473         b       multiple_value_return
474          break  0x0, trap_FunEndBreakpoint
475         .set    reorder
476         EXPORT(fun_end_breakpoint_end)
477         END(fun_end_breakpoint_guts)
478
479
480         .align  3 /* minimum alignment for a lisp object */
481         LEAF(do_pending_interrupt)
482         break   0x0, trap_PendingInterrupt
483         jr      reg_LIP
484         END(do_pending_interrupt)