1.0.0.2: TRACE :ENCAPSULATE NIL, plus other minor Windows improvements
[sbcl.git] / src / runtime / mips-arch.c
1 /*
2
3  This code was written as part of the CMU Common Lisp project at
4  Carnegie Mellon University, and has been placed in the public domain.
5
6 */
7
8 #include <stdio.h>
9
10 #include "sbcl.h"
11 #include "runtime.h"
12 #include "arch.h"
13 #include "globals.h"
14 #include "validate.h"
15 #include "os.h"
16 #include "lispregs.h"
17 #include "signal.h"
18 #include "alloc.h"
19 #include "interrupt.h"
20 #include "interr.h"
21 #include "breakpoint.h"
22
23 #include "genesis/constants.h"
24
25 #define INSN_LEN sizeof(unsigned int)
26
27 void
28 arch_init()
29 {
30     return;
31 }
32
33 os_vm_address_t
34 arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
35 {
36     /* Classic CMUCL comment:
37
38        Finding the bad address on the mips is easy. */
39     return (os_vm_address_t)siginfo->si_addr;
40 }
41
42 static inline unsigned int
43 os_context_register(os_context_t *context, int offset)
44 {
45     return (unsigned int)(*os_context_register_addr(context, offset));
46 }
47
48 static inline unsigned int
49 os_context_pc(os_context_t *context)
50 {
51     return (unsigned int)(*os_context_pc_addr(context));
52 }
53
54 static inline unsigned int
55 os_context_insn(os_context_t *context)
56 {
57     if (os_context_bd_cause(context))
58         return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
59     else
60         return *(unsigned int *)(os_context_pc(context));
61 }
62
63 boolean
64 arch_insn_with_bdelay_p(unsigned int insn)
65 {
66     switch (insn >> 26) {
67     case 0x0:
68         switch (insn & 0x3f) {
69         /* register jumps */
70         case 0x08:
71         case 0x09:
72             return 1;
73         }
74         break;
75     /* branches and immediate jumps */
76     case 0x1:
77         switch ((insn >> 16) & 0x1f) {
78         case 0x00:
79         case 0x01:
80         case 0x02:
81         case 0x03:
82         case 0x10:
83         case 0x11:
84         case 0x12:
85         case 0x13:
86             return 1;
87         }
88         break;
89     case 0x2:
90     case 0x3:
91     case 0x4:
92     case 0x5:
93     case 0x6:
94     case 0x7:
95         return 1;
96     case 0x10:
97     case 0x11:
98     case 0x12:
99         switch ((insn >> 21) & 0x1f) {
100          /* CP0/CP1/CP2 branches */
101         case 0x08:
102             return 1;
103         }
104         break;
105     /* branch likely (MIPS II) */
106     case 0x14:
107     case 0x15:
108     case 0x16:
109     case 0x17:
110         return 1;
111     }
112     return 0;
113 }
114
115 /* Find the next instruction in the control flow. For a instruction
116    with branch delay slot, this is the branch/jump target if the branch
117    is taken, and PC + 8 if it is not taken. For other instructions it
118    is PC + 4. */
119 static unsigned int
120 next_insn_addr(os_context_t *context, unsigned int inst)
121 {
122     unsigned int opcode = inst >> 26;
123     unsigned int r1 = (inst >> 21) & 0x1f;
124     unsigned int r2 = (inst >> 16) & 0x1f;
125     unsigned int r3 = (inst >> 11) & 0x1f;
126     unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
127     unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
128     unsigned int tgt = os_context_pc(context);
129
130     switch(opcode) {
131     case 0x0: /* jr, jalr */
132         switch(inst & 0x3f) {
133         case 0x08: /* jr */
134             tgt = os_context_register(context, r1);
135             break;
136         case 0x09: /* jalr */
137             tgt = os_context_register(context, r1);
138             *os_context_register_addr(context, r3)
139                 = os_context_pc(context) + INSN_LEN;
140             break;
141         default:
142             tgt += INSN_LEN;
143             break;
144         }
145         break;
146     case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
147         switch(r2) {
148         case 0x00: /* bltz */
149         case 0x02: /* bltzl */
150             if(os_context_register(context, r1) < 0)
151                 tgt += disp;
152             else
153                 tgt += INSN_LEN;
154             break;
155         case 0x01: /* bgez */
156         case 0x03: /* bgezl */
157             if(os_context_register(context, r1) >= 0)
158                 tgt += disp;
159             else
160                 tgt += INSN_LEN;
161             break;
162         case 0x10: /* bltzal */
163         case 0x12: /* bltzall */
164             if(os_context_register(context, r1) < 0) {
165                 tgt += disp;
166                 *os_context_register_addr(context, 31)
167                     = os_context_pc(context) + INSN_LEN;
168             } else
169                 tgt += INSN_LEN;
170             break;
171         case 0x11: /* bgezal */
172         case 0x13: /* bgezall */
173             if(os_context_register(context, r1) >= 0) {
174                 tgt += disp;
175                 *os_context_register_addr(context, 31)
176                     = os_context_pc(context) + INSN_LEN;
177             } else
178                 tgt += INSN_LEN;
179             break;
180         default:
181             tgt += INSN_LEN;
182             break;
183         }
184         break;
185     case 0x2: /* j */
186         tgt = jtgt;
187         break;
188     case 0x3: /* jal */
189         tgt = jtgt;
190         *os_context_register_addr(context, 31)
191             = os_context_pc(context) + INSN_LEN;
192         break;
193     case 0x4: /* beq */
194     case 0x14: /* beql */
195         if(os_context_register(context, r1)
196            == os_context_register(context, r2))
197             tgt += disp;
198         else
199             tgt += INSN_LEN;
200         break;
201     case 0x5: /* bne */
202     case 0x15: /* bnel */
203         if(os_context_register(context, r1)
204            != os_context_register(context, r2))
205             tgt += disp;
206         else
207             tgt += INSN_LEN;
208         break;
209     case 0x6: /* blez */
210     case 0x16: /* blezl */
211         if(os_context_register(context, r1)
212            <= os_context_register(context, r2))
213             tgt += disp;
214         else
215             tgt += INSN_LEN;
216         break;
217     case 0x7: /* bgtz */
218     case 0x17: /* bgtzl */
219         if(os_context_register(context, r1)
220            > os_context_register(context, r2))
221             tgt += disp;
222         else
223             tgt += INSN_LEN;
224         break;
225     case 0x10:
226     case 0x11:
227     case 0x12:
228         switch (r1) {
229          /* CP0/CP1/CP2 branches */
230         case 0x08:
231             /* FIXME */
232             tgt += INSN_LEN;
233             break;
234         }
235         break;
236     default:
237         tgt += INSN_LEN;
238         break;
239     }
240     return tgt;
241 }
242
243 void
244 arch_skip_instruction(os_context_t *context)
245 {
246     /* Skip the offending instruction. Don't use os_context_insn here,
247        since in case of a branch we want the branch insn, not the delay
248        slot. */
249     *os_context_pc_addr(context)
250         = (os_context_register_t)
251             next_insn_addr(context,
252                 *(unsigned int *)(os_context_pc(context)));
253 }
254
255 unsigned char *
256 arch_internal_error_arguments(os_context_t *context)
257 {
258     if (os_context_bd_cause(context))
259         return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
260     else
261         return (unsigned char *)(os_context_pc(context) + INSN_LEN);
262 }
263
264 boolean
265 arch_pseudo_atomic_atomic(os_context_t *context)
266 {
267     return os_context_register(context, reg_ALLOC) & 1;
268 }
269
270 void
271 arch_set_pseudo_atomic_interrupted(os_context_t *context)
272 {
273     *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
274 }
275
276 void
277 arch_clear_pseudo_atomic_interrupted(os_context_t *context)
278 {
279     *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
280 }
281
282 unsigned int
283 arch_install_breakpoint(void *pc)
284 {
285     unsigned int *ptr = (unsigned int *)pc;
286     unsigned int insn;
287
288     /* Don't install over a branch/jump with delay slot. */
289     if (arch_insn_with_bdelay_p(*ptr))
290         ptr++;
291
292     insn = *ptr;
293     *ptr = (trap_Breakpoint << 6) | 0xd;
294     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
295
296     return insn;
297 }
298
299 static inline unsigned int
300 arch_install_after_breakpoint(void *pc)
301 {
302     unsigned int *ptr = (unsigned int *)pc;
303     unsigned int insn;
304
305     /* Don't install over a branch/jump with delay slot. */
306     if (arch_insn_with_bdelay_p(*ptr))
307         ptr++;
308
309     insn = *ptr;
310     *ptr = (trap_AfterBreakpoint << 6) | 0xd;
311     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
312
313     return insn;
314 }
315
316 void
317 arch_remove_breakpoint(void *pc, unsigned int orig_inst)
318 {
319     unsigned int *ptr = (unsigned int *)pc;
320
321     /* We may remove from a branch delay slot. */
322     if (arch_insn_with_bdelay_p(*ptr))
323         ptr++;
324
325     *ptr = orig_inst;
326     os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
327 }
328
329 /* Perform the instruction that we overwrote with a breakpoint. As we
330    don't have a single-step facility, this means we have to:
331    - put the instruction back
332    - put a second breakpoint at the following instruction,
333      set after_breakpoint and continue execution.
334
335    When the second breakpoint is hit (very shortly thereafter, we hope)
336    sigtrap_handler gets called again, but follows the AfterBreakpoint
337    arm, which
338    - puts a bpt back in the first breakpoint place (running across a
339      breakpoint shouldn't cause it to be uninstalled)
340    - replaces the second bpt with the instruction it was meant to be
341    - carries on
342
343    Clear? */
344
345 static unsigned int *skipped_break_addr, displaced_after_inst;
346 static sigset_t orig_sigmask;
347
348 void
349 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
350 {
351     unsigned int *pc = (unsigned int *)os_context_pc(context);
352     unsigned int *next_pc;
353
354     orig_sigmask = *os_context_sigmask_addr(context);
355     sigaddset_blockable(os_context_sigmask_addr(context));
356
357     /* Put the original instruction back. */
358     arch_remove_breakpoint(pc, orig_inst);
359     skipped_break_addr = pc;
360
361     /* Figure out where it goes. */
362     next_pc = (unsigned int *)next_insn_addr(context, *pc);
363     displaced_after_inst = arch_install_after_breakpoint(next_pc);
364 }
365
366 static void
367 sigtrap_handler(int signal, siginfo_t *info, void *void_context)
368 {
369     os_context_t *context = arch_os_get_context(&void_context);
370     unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
371
372     switch (code) {
373     case trap_Halt:
374         fake_foreign_function_call(context);
375         lose("%%primitive halt called; the party is over.\n");
376
377     case trap_PendingInterrupt:
378         arch_skip_instruction(context);
379         interrupt_handle_pending(context);
380         break;
381
382     case trap_Error:
383     case trap_Cerror:
384         interrupt_internal_error(signal, info, context, code == trap_Cerror);
385         break;
386
387     case trap_Breakpoint:
388         handle_breakpoint(signal, info, context);
389         break;
390
391     case trap_FunEndBreakpoint:
392         *os_context_pc_addr(context)
393             = (os_context_register_t)(unsigned int)
394                 handle_fun_end_breakpoint(signal, info, context);
395         break;
396
397     case trap_AfterBreakpoint:
398         arch_install_breakpoint(skipped_break_addr);
399         arch_remove_breakpoint((unsigned int *)os_context_pc(context),
400                                displaced_after_inst);
401         *os_context_sigmask_addr(context) = orig_sigmask;
402         break;
403
404     case 0x10:
405         arch_clear_pseudo_atomic_interrupted(context);
406         arch_skip_instruction(context);
407         interrupt_handle_pending(context);
408         return;
409
410     default:
411         interrupt_handle_now(signal, info, context);
412         break;
413     }
414 }
415
416 #define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
417
418 static void
419 sigfpe_handler(int signal, siginfo_t *info, void *void_context)
420 {
421     os_context_t *context = arch_os_get_context(&void_context);
422     unsigned int bad_inst = os_context_insn(context);
423     unsigned int op, rs, rt, rd, funct, dest = 32;
424     int immed;
425     int result;
426
427     op = (bad_inst >> 26) & 0x3f;
428     rs = (bad_inst >> 21) & 0x1f;
429     rt = (bad_inst >> 16) & 0x1f;
430     rd = (bad_inst >> 11) & 0x1f;
431     funct = bad_inst & 0x3f;
432     immed = (((int)(bad_inst & 0xffff)) << 16) >> 16;
433
434     switch (op) {
435     case 0x0: /* SPECIAL */
436         switch (funct) {
437         case 0x20: /* ADD */
438             result = FIXNUM_VALUE(os_context_register(context, rs))
439                 + FIXNUM_VALUE(os_context_register(context, rt));
440             dest = rd;
441             break;
442
443         case 0x22: /* SUB */
444             result = FIXNUM_VALUE(os_context_register(context, rs))
445                 - FIXNUM_VALUE(os_context_register(context, rt));
446             dest = rd;
447             break;
448
449         default:
450             interrupt_handle_now(signal, info, context);
451             return;
452         }
453         break;
454
455     case 0x8: /* ADDI */
456         result = FIXNUM_VALUE(os_context_register(context,rs))
457                     + (immed >> N_FIXNUM_TAG_BITS);
458         dest = rt;
459         break;
460
461     default:
462         interrupt_handle_now(signal, info, context);
463         return;
464     }
465
466     dynamic_space_free_pointer =
467         (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
468
469     *os_context_register_addr(context,dest) = alloc_number(result);
470
471     *os_context_register_addr(context, reg_ALLOC) =
472         (unsigned int) dynamic_space_free_pointer;
473
474     arch_skip_instruction(context);
475 }
476
477 void
478 arch_install_interrupt_handlers()
479 {
480     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
481     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
482 }
483
484 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
485
486 lispobj
487 funcall0(lispobj function)
488 {
489     lispobj *args = current_control_stack_pointer;
490
491     return call_into_lisp(function, args, 0);
492 }
493
494 lispobj
495 funcall1(lispobj function, lispobj arg0)
496 {
497     lispobj *args = current_control_stack_pointer;
498
499     current_control_stack_pointer += 1;
500     args[0] = arg0;
501
502     return call_into_lisp(function, args, 1);
503 }
504
505 lispobj
506 funcall2(lispobj function, lispobj arg0, lispobj arg1)
507 {
508     lispobj *args = current_control_stack_pointer;
509
510     current_control_stack_pointer += 2;
511     args[0] = arg0;
512     args[1] = arg1;
513
514     return call_into_lisp(function, args, 2);
515 }
516
517 lispobj
518 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
519 {
520     lispobj *args = current_control_stack_pointer;
521
522     current_control_stack_pointer += 3;
523     args[0] = arg0;
524     args[1] = arg1;
525     args[2] = arg2;
526
527     return call_into_lisp(function, args, 3);
528 }
529
530 #ifdef LISP_FEATURE_LINKAGE_TABLE
531
532 /* Linkage tables for MIPS
533
534    Linkage entry size is 16, because we need 4 instructions to implement
535    a jump. The entry size constant is defined in parms.lisp.
536
537    Define the register to use in the linkage jump table. For MIPS this
538    has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
539 #define LINKAGE_TEMP_REG        reg_ALLOC
540
541 /* Insert the necessary jump instructions at the given address. */
542 void
543 arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
544 {
545   /* Make JMP to function entry. The instruction sequence is:
546        lui    $25, 0, %hi(addr)
547        addiu  $25, $25, %lo(addr)
548        jr     $25
549         nop */
550   unsigned int *insn = (unsigned int *)reloc_addr;
551   unsigned int addr = (unsigned int)target_addr;
552   unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
553   unsigned int lo = addr & 0xffff;
554
555   *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
556   *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
557                  | (LINKAGE_TEMP_REG << 16) | lo);
558   *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
559   *insn = 0;
560
561   os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
562 }
563
564 void
565 arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
566 {
567     *(unsigned int *)reloc_addr = (unsigned int)target_addr;
568 }
569
570 #endif