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