1.0.5.27: Stepper support for MIPS.
[sbcl.git] / src / runtime / mips-arch.c
index a65a381..6da8579 100644 (file)
@@ -1,7 +1,5 @@
 /*
 
 /*
 
- $Header$
-
  This code was written as part of the CMU Common Lisp project at
  Carnegie Mellon University, and has been placed in the public domain.
 
  This code was written as part of the CMU Common Lisp project at
  Carnegie Mellon University, and has been placed in the public domain.
 
@@ -9,9 +7,9 @@
 
 #include <stdio.h>
 
 
 #include <stdio.h>
 
+#include "sbcl.h"
 #include "runtime.h"
 #include "arch.h"
 #include "runtime.h"
 #include "arch.h"
-#include "sbcl.h"
 #include "globals.h"
 #include "validate.h"
 #include "os.h"
 #include "globals.h"
 #include "validate.h"
 #include "os.h"
 #include "interrupt.h"
 #include "interr.h"
 #include "breakpoint.h"
 #include "interrupt.h"
 #include "interr.h"
 #include "breakpoint.h"
-#include "monitor.h"
 
 
-void arch_init()
+#include "genesis/constants.h"
+
+#define INSN_LEN sizeof(unsigned int)
+
+void
+arch_init(void)
 {
     return;
 }
 
 {
     return;
 }
 
-os_vm_address_t arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
+os_vm_address_t
+arch_get_bad_addr(int signam, siginfo_t *siginfo, os_context_t *context)
 {
     /* Classic CMUCL comment:
 
        Finding the bad address on the mips is easy. */
 {
     /* Classic CMUCL comment:
 
        Finding the bad address on the mips is easy. */
-    return (os_vm_address_t) siginfo->si_addr;
+    return (os_vm_address_t)siginfo->si_addr;
+}
+
+static inline unsigned int
+os_context_register(os_context_t *context, int offset)
+{
+    return (unsigned int)(*os_context_register_addr(context, offset));
+}
+
+static inline unsigned int
+os_context_pc(os_context_t *context)
+{
+    return (unsigned int)(*os_context_pc_addr(context));
 }
 
 }
 
-unsigned long 
-emulate_branch(os_context_t *context, unsigned long inst)
+static inline unsigned int
+os_context_insn(os_context_t *context)
 {
 {
-    long opcode = inst >> 26;
-    long r1 = (inst >> 21) & 0x1f;
-    long r2 = (inst >> 16) & 0x1f;
-    long bdisp = (inst&(1<<15)) ? inst | (-1 << 16) : inst&0xffff;
-    long jdisp = (inst&(1<<25)) ? inst | (-1 << 26) : inst&0xffff;
-    long disp = 0;
+    if (os_context_bd_cause(context))
+        return *(unsigned int *)(os_context_pc(context) + INSN_LEN);
+    else
+        return *(unsigned int *)(os_context_pc(context));
+}
+
+boolean
+arch_insn_with_bdelay_p(unsigned int insn)
+{
+    switch (insn >> 26) {
+    case 0x0:
+        switch (insn & 0x3f) {
+        /* register jumps */
+        case 0x08:
+        case 0x09:
+            return 1;
+        }
+        break;
+    /* branches and immediate jumps */
+    case 0x1:
+        switch ((insn >> 16) & 0x1f) {
+        case 0x00:
+        case 0x01:
+        case 0x02:
+        case 0x03:
+        case 0x10:
+        case 0x11:
+        case 0x12:
+        case 0x13:
+            return 1;
+        }
+        break;
+    case 0x2:
+    case 0x3:
+    case 0x4:
+    case 0x5:
+    case 0x6:
+    case 0x7:
+        return 1;
+    case 0x10:
+    case 0x11:
+    case 0x12:
+        switch ((insn >> 21) & 0x1f) {
+         /* CP0/CP1/CP2 branches */
+        case 0x08:
+            return 1;
+        }
+        break;
+    /* branch likely (MIPS II) */
+    case 0x14:
+    case 0x15:
+    case 0x16:
+    case 0x17:
+        return 1;
+    }
+    return 0;
+}
+
+/* Find the next instruction in the control flow. For a instruction
+   with branch delay slot, this is the branch/jump target if the branch
+   is taken, and PC + 8 if it is not taken. For other instructions it
+   is PC + 4. */
+static unsigned int
+next_insn_addr(os_context_t *context, unsigned int inst)
+{
+    unsigned int opcode = inst >> 26;
+    unsigned int r1 = (inst >> 21) & 0x1f;
+    unsigned int r2 = (inst >> 16) & 0x1f;
+    unsigned int r3 = (inst >> 11) & 0x1f;
+    unsigned int disp = ((inst&(1<<15)) ? inst | (-1 << 16) : inst&0x7fff) << 2;
+    unsigned int jtgt = (os_context_pc(context) & ~0x0fffffff) | (inst&0x3ffffff) << 2;
+    unsigned int tgt = os_context_pc(context);
 
     switch(opcode) {
 
     switch(opcode) {
-    case 0x1: /* bltz, bgez, bltzal, bgezal */
-       switch((inst >> 16) & 0x1f) {
-       case 0x00: /* bltz */
-           if(*os_context_register_addr(context, r1) < 0)
-               disp = bdisp;
-           break;
-       case 0x01: /* bgez */
-           if(*os_context_register_addr(context, r1) >= 0)
-               disp = bdisp;
-           break;
-       case 0x10: /* bltzal */
-           if(*os_context_register_addr(context, r1) < 0)
-               disp = bdisp;
-           *os_context_register_addr(context, 31) = *os_context_pc_addr(context) + 4;
-           break;
-       case 0x11: /* bgezal */
-           if(*os_context_register_addr(context, r1) >= 0)
-               disp = bdisp;
-           *os_context_register_addr(context, 31) = *os_context_pc_addr(context) + 4;
-           break;
-       }
-       break;
+    case 0x0: /* jr, jalr */
+        switch(inst & 0x3f) {
+        case 0x08: /* jr */
+            tgt = os_context_register(context, r1);
+            break;
+        case 0x09: /* jalr */
+            tgt = os_context_register(context, r1);
+            *os_context_register_addr(context, r3)
+                = os_context_pc(context) + INSN_LEN;
+            break;
+        default:
+            tgt += INSN_LEN;
+            break;
+        }
+        break;
+    case 0x1: /* bltz, bgez, bltzal, bgezal, ... */
+        switch(r2) {
+        case 0x00: /* bltz */
+        case 0x02: /* bltzl */
+            if(os_context_register(context, r1) < 0)
+                tgt += disp;
+            else
+                tgt += INSN_LEN;
+            break;
+        case 0x01: /* bgez */
+        case 0x03: /* bgezl */
+            if(os_context_register(context, r1) >= 0)
+                tgt += disp;
+            else
+                tgt += INSN_LEN;
+            break;
+        case 0x10: /* bltzal */
+        case 0x12: /* bltzall */
+            if(os_context_register(context, r1) < 0) {
+                tgt += disp;
+                *os_context_register_addr(context, 31)
+                    = os_context_pc(context) + INSN_LEN;
+            } else
+                tgt += INSN_LEN;
+            break;
+        case 0x11: /* bgezal */
+        case 0x13: /* bgezall */
+            if(os_context_register(context, r1) >= 0) {
+                tgt += disp;
+                *os_context_register_addr(context, 31)
+                    = os_context_pc(context) + INSN_LEN;
+            } else
+                tgt += INSN_LEN;
+            break;
+        default:
+            tgt += INSN_LEN;
+            break;
+        }
+        break;
+    case 0x2: /* j */
+        tgt = jtgt;
+        break;
+    case 0x3: /* jal */
+        tgt = jtgt;
+        *os_context_register_addr(context, 31)
+            = os_context_pc(context) + INSN_LEN;
+        break;
     case 0x4: /* beq */
     case 0x4: /* beq */
-       if(*os_context_register_addr(context, r1)
-          == *os_context_register_addr(context, r2))
-           disp = bdisp;
-       break;
+    case 0x14: /* beql */
+        if(os_context_register(context, r1)
+           == os_context_register(context, r2))
+            tgt += disp;
+        else
+            tgt += INSN_LEN;
+        break;
     case 0x5: /* bne */
     case 0x5: /* bne */
-       if(*os_context_register_addr(context, r1) 
-          != *os_context_register_addr(context, r2))
-           disp = bdisp;
-       break;
-    case 0x6: /* ble */
-       if(*os_context_register_addr(context, r1)
-          /* FIXME: One has to assume that the CMUCL gods of old have
-              got the sign issues right... but it might be worth
-              checking, someday */
-          <= *os_context_register_addr(context, r2))
-           disp = bdisp;
-       break;
+    case 0x15: /* bnel */
+        if(os_context_register(context, r1)
+           != os_context_register(context, r2))
+            tgt += disp;
+        else
+            tgt += INSN_LEN;
+        break;
+    case 0x6: /* blez */
+    case 0x16: /* blezl */
+        if(os_context_register(context, r1)
+           <= os_context_register(context, r2))
+            tgt += disp;
+        else
+            tgt += INSN_LEN;
+        break;
     case 0x7: /* bgtz */
     case 0x7: /* bgtz */
-       if(*os_context_register_addr(context, r1)
-          >= *os_context_register_addr(context, r2))
-           disp = bdisp;
-       break;
-    case 0x2: /* j */
-       disp = jdisp;
-       break;
-    case 0x3: /* jal */
-       disp = jdisp;
-       *os_context_register_addr(context, 31) = *os_context_pc_addr(context) + 4;
-       break;
+    case 0x17: /* bgtzl */
+        if(os_context_register(context, r1)
+           > os_context_register(context, r2))
+            tgt += disp;
+        else
+            tgt += INSN_LEN;
+        break;
+    case 0x10:
+    case 0x11:
+    case 0x12:
+        switch (r1) {
+         /* CP0/CP1/CP2 branches */
+        case 0x08:
+            /* FIXME */
+            tgt += INSN_LEN;
+            break;
+        }
+        break;
+    default:
+        tgt += INSN_LEN;
+        break;
     }
     }
-    return (*os_context_pc_addr(context) + disp * 4);
+    return tgt;
 }
 
 }
 
-void arch_skip_instruction(os_context_t *context)
+void
+arch_skip_instruction(os_context_t *context)
 {
 {
-    /* Skip the offending instruction */
-    if (os_context_bd_cause(context))
-        *os_context_pc_addr(context) = 
-           emulate_branch(context, 
-                          *(unsigned long *) *os_context_pc_addr(context));
-    else
-        *os_context_pc_addr(context) += 4;
-
-    os_flush_icache((os_vm_address_t) *os_context_pc_addr(context), sizeof(unsigned long));
+    /* Skip the offending instruction. Don't use os_context_insn here,
+       since in case of a branch we want the branch insn, not the delay
+       slot. */
+    *os_context_pc_addr(context)
+        = (os_context_register_t)
+            next_insn_addr(context,
+                *(unsigned int *)(os_context_pc(context)));
 }
 
 }
 
-unsigned char *arch_internal_error_arguments(os_context_t *context)
+unsigned char *
+arch_internal_error_arguments(os_context_t *context)
 {
     if (os_context_bd_cause(context))
 {
     if (os_context_bd_cause(context))
-       return (unsigned char *)(*os_context_pc_addr(context) + 8);
+        return (unsigned char *)(os_context_pc(context) + (INSN_LEN * 2));
     else
     else
-       return (unsigned char *)(*os_context_pc_addr(context) + 4);
+        return (unsigned char *)(os_context_pc(context) + INSN_LEN);
+}
+
+boolean
+arch_pseudo_atomic_atomic(os_context_t *context)
+{
+    return os_context_register(context, reg_ALLOC) & 1;
 }
 
 }
 
-boolean arch_pseudo_atomic_atomic(os_context_t *context)
+void
+arch_set_pseudo_atomic_interrupted(os_context_t *context)
 {
 {
-    return *os_context_register_addr(context, reg_ALLOC) & 1;
+    *os_context_register_addr(context, reg_NL4) |= -1LL<<31;
 }
 
 }
 
-#define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
+void
+arch_clear_pseudo_atomic_interrupted(os_context_t *context)
+{
+    *os_context_register_addr(context, reg_NL4) &= ~(-1LL<<31);
+}
 
 
-void arch_set_pseudo_atomic_interrupted(os_context_t *context)
+unsigned int
+arch_install_breakpoint(void *pc)
 {
 {
-    *os_context_register_addr(context, reg_NL4) |= 1<<31;
+    unsigned int *ptr = (unsigned int *)pc;
+    unsigned int insn;
+
+    /* Don't install over a branch/jump with delay slot. */
+    if (arch_insn_with_bdelay_p(*ptr))
+        ptr++;
+
+    insn = *ptr;
+    *ptr = (trap_Breakpoint << 6) | 0xd;
+    os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
+
+    return insn;
 }
 
 }
 
-unsigned long arch_install_breakpoint(void *pc)
+static inline unsigned int
+arch_install_after_breakpoint(void *pc)
 {
 {
-    unsigned long *ptr = (unsigned long *)pc;
-    unsigned long result = *ptr;
-    *ptr = (trap_Breakpoint << 16) | 0xd;
+    unsigned int *ptr = (unsigned int *)pc;
+    unsigned int insn;
+
+    /* Don't install over a branch/jump with delay slot. */
+    if (arch_insn_with_bdelay_p(*ptr))
+        ptr++;
 
 
-    os_flush_icache((os_vm_address_t)ptr, sizeof(unsigned long));
+    insn = *ptr;
+    *ptr = (trap_AfterBreakpoint << 6) | 0xd;
+    os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
 
 
-    return result;
+    return insn;
 }
 
 }
 
-void arch_remove_breakpoint(void *pc, unsigned long orig_inst)
+void
+arch_remove_breakpoint(void *pc, unsigned int orig_inst)
 {
 {
-    *(unsigned long *)pc = orig_inst;
+    unsigned int *ptr = (unsigned int *)pc;
 
 
-    os_flush_icache((os_vm_address_t)pc, sizeof(unsigned long));
+    /* We may remove from a branch delay slot. */
+    if (arch_insn_with_bdelay_p(*ptr))
+        ptr++;
+
+    *ptr = orig_inst;
+    os_flush_icache((os_vm_address_t)ptr, INSN_LEN);
 }
 
 }
 
-static unsigned long *skipped_break_addr, displaced_after_inst;
+/* Perform the instruction that we overwrote with a breakpoint. As we
+   don't have a single-step facility, this means we have to:
+   - put the instruction back
+   - put a second breakpoint at the following instruction,
+     set after_breakpoint and continue execution.
+
+   When the second breakpoint is hit (very shortly thereafter, we hope)
+   sigtrap_handler gets called again, but follows the AfterBreakpoint
+   arm, which
+   - puts a bpt back in the first breakpoint place (running across a
+     breakpoint shouldn't cause it to be uninstalled)
+   - replaces the second bpt with the instruction it was meant to be
+   - carries on
+
+   Clear? */
+
+static unsigned int *skipped_break_addr, displaced_after_inst;
 static sigset_t orig_sigmask;
 
 static sigset_t orig_sigmask;
 
-void arch_do_displaced_inst(os_context_t *context,
-                           unsigned int orig_inst)
+void
+arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
 {
 {
-    unsigned long *pc = (unsigned long *)*os_context_pc_addr(context);
-    unsigned long *break_pc, *next_pc;
-    unsigned long next_inst;
-    int opcode;
+    unsigned int *pc = (unsigned int *)os_context_pc(context);
+    unsigned int *next_pc;
 
     orig_sigmask = *os_context_sigmask_addr(context);
     sigaddset_blockable(os_context_sigmask_addr(context));
 
 
     orig_sigmask = *os_context_sigmask_addr(context);
     sigaddset_blockable(os_context_sigmask_addr(context));
 
-    /* Figure out where the breakpoint is, and what happens next. */
-    if (os_context_bd_cause(context)) {
-       break_pc = pc+1;
-       next_inst = *pc;
-    }
-    else {
-       break_pc = pc;
-       next_inst = orig_inst;
-    }
-
     /* Put the original instruction back. */
     /* Put the original instruction back. */
-    *break_pc = orig_inst;
-    os_flush_icache((os_vm_address_t)break_pc, sizeof(unsigned long));
-    skipped_break_addr = break_pc;
+    arch_remove_breakpoint(pc, orig_inst);
+    skipped_break_addr = pc;
 
     /* Figure out where it goes. */
 
     /* Figure out where it goes. */
-    opcode = next_inst >> 26;
-    if (opcode == 1 || ((opcode & 0x3c) == 0x4) || ((next_inst & 0xf00e0000) == 0x80000000)) {
-        
-        next_pc = emulate_branch(context, next_inst);
-    }
-    else
-       next_pc = pc+1;
+    next_pc = (unsigned int *)next_insn_addr(context, *pc);
+    displaced_after_inst = arch_install_after_breakpoint(next_pc);
+}
 
 
-    displaced_after_inst = *next_pc;
-    *next_pc = (trap_AfterBreakpoint << 16) | 0xd;
-    os_flush_icache((os_vm_address_t)next_pc, sizeof(unsigned long));
+void
+arch_handle_breakpoint(os_context_t *context)
+{
+    handle_breakpoint(context);
 }
 
 }
 
-static void sigtrap_handler(int signal, siginfo_t *info, void *void_context)
+void
+arch_handle_fun_end_breakpoint(os_context_t *context)
 {
 {
-    os_context_t *context = arch_os_get_context(&void_context);
-    sigset_t *mask;
-    int code;
-    /* Don't disallow recursive breakpoint traps.  Otherwise, we can't */
-    /* use debugger breakpoints anywhere in here. */
-    mask = os_context_sigmask_addr(context);
-    sigsetmask(mask);
-    code = ((*(int *) (*os_context_pc_addr(context))) >> 16) & 0x1f;
-
-    switch (code) {
-    case trap_PendingInterrupt:
-       arch_skip_instruction(context);
-       interrupt_handle_pending(context);
-       break;
-       
-    case trap_Halt:
-       fake_foreign_function_call(context);
-       lose("%%primitive halt called; the party is over.\n");
-       
-    case trap_Error:
-    case trap_Cerror:
-       interrupt_internal_error(signal, info, context, code==trap_Cerror);
-       break;
-       
-    case trap_Breakpoint:
-       handle_breakpoint(signal, info, context);
-       break;
-       
-    case trap_FunEndBreakpoint:
-       *os_context_pc_addr(context) = (int)handle_fun_end_breakpoint(signal, info, context);
-       break;
-       
-    case trap_AfterBreakpoint:
-       *skipped_break_addr = (trap_Breakpoint << 16) | 0xd;
-       os_flush_icache((os_vm_address_t)skipped_break_addr,
-                       sizeof(unsigned long));
-       skipped_break_addr = NULL;
-       *(unsigned long *)(*os_context_pc_addr(context)) = displaced_after_inst;
-       os_flush_icache((os_vm_address_t) *os_context_pc_addr(context), sizeof(unsigned long));
-       *os_context_sigmask_addr(context) = orig_sigmask;
-       break;
+    *os_context_pc_addr(context)
+        = (os_context_register_t)(unsigned int)
+        handle_fun_end_breakpoint(context);
+}
 
 
-    case 0x10:
-       /* Clear the flag */
-       *os_context_register_addr(context, reg_NL4) &= 0x7fffffff;
-       arch_skip_instruction(context);
-       interrupt_handle_pending(context);
-       return;
-       
-    default:
-       interrupt_handle_now(signal, info, context);
-       break;
-    }
+void
+arch_handle_after_breakpoint(os_context_t *context)
+{
+    arch_install_breakpoint(skipped_break_addr);
+    arch_remove_breakpoint((unsigned int *)os_context_pc(context),
+                           displaced_after_inst);
+    *os_context_sigmask_addr(context) = orig_sigmask;
 }
 
 }
 
-/* FIXME: We must have one of these somewhere. Also, export
-   N-FIXNUM-TAG-BITS from Lispland and use it rather than 2 here. */
-#define FIXNUM_VALUE(lispobj) (((int)lispobj)>>2)
+void
+arch_handle_single_step_trap(os_context_t *context, int trap)
+{
+    unsigned int code = *((u32 *)(*os_context_pc_addr(context)));
+    int register_offset = code >> 11 & 0x1f;
+    handle_single_step_trap(context, trap, register_offset);
+    arch_skip_instruction(context);
+}
 
 
-void sigfpe_handler(int signal, siginfo_t *info, void *void_context)
+static void
+sigtrap_handler(int signal, siginfo_t *info, void *void_context)
 {
 {
-    unsigned long bad_inst;
-    unsigned int op, rs, rt, rd, funct, dest;
-    int immed;
-    long result;
     os_context_t *context = arch_os_get_context(&void_context);
     os_context_t *context = arch_os_get_context(&void_context);
+    unsigned int code = (os_context_insn(context) >> 6) & 0xfffff;
+    /* FIXME: This magic number is pseudo-atomic-trap from parms.lisp.
+     * Genesis should provide the proper #define, but it specialcases
+     * pseudo-atomic-trap to work around some oddity on SPARC.
+     * Eventually this should go into handle_trap. */
+    if (code==0x10) {
+        arch_clear_pseudo_atomic_interrupted(context);
+        arch_skip_instruction(context);
+        interrupt_handle_pending(context);
+    } else
+        handle_trap(context,code & 0x1f);
+}
 
 
-    if (os_context_bd_cause(context))
-        bad_inst = *(unsigned long *)(*os_context_pc_addr(context) + 4);
-    else
-        bad_inst = *(unsigned long *)(*os_context_pc_addr(context));
+#define FIXNUM_VALUE(lispobj) (((int)lispobj) >> N_FIXNUM_TAG_BITS)
+
+static void
+sigfpe_handler(int signal, siginfo_t *info, void *void_context)
+{
+    os_context_t *context = arch_os_get_context(&void_context);
+    unsigned int bad_inst = os_context_insn(context);
+    unsigned int op, rs, rt, rd, funct, dest = 32;
+    int immed;
+    int result;
 
     op = (bad_inst >> 26) & 0x3f;
     rs = (bad_inst >> 21) & 0x1f;
 
     op = (bad_inst >> 26) & 0x3f;
     rs = (bad_inst >> 21) & 0x1f;
@@ -280,108 +432,92 @@ void sigfpe_handler(int signal, siginfo_t *info, void *void_context)
 
     switch (op) {
     case 0x0: /* SPECIAL */
 
     switch (op) {
     case 0x0: /* SPECIAL */
-       switch (funct) {
-       case 0x20: /* ADD */
-           /* FIXME: Hopefully, this whole section can just go away,
-               with the rewrite of pseudo-atomic and the deletion of
-               overflow VOPs */
-           /* Check to see if this is really a pa_interrupted hit */
-           if (rs == reg_ALLOC && rt == reg_NL4) {
-               *os_context_register_addr(context, reg_ALLOC)
-                   += (*os_context_register_addr(context, reg_NL4)
-                       - PSEUDO_ATOMIC_INTERRUPTED_BIAS);
-               arch_skip_instruction(context);
-               interrupt_handle_pending(context);
-               return;
-           }
-           result = FIXNUM_VALUE(*os_context_register_addr(context, rs))
-               + FIXNUM_VALUE(*os_context_register_addr(context, rt));
-           dest = rd;
-           break;
-           
-       case 0x22: /* SUB */
-           result = FIXNUM_VALUE(*os_context_register_addr(context, rs))
-               - FIXNUM_VALUE(*os_context_register_addr(context, rt));
-           dest = rd;
-           break;
-           
-       default:
-           dest = 32;
-           break;
-       }
-       break;
-       
+        switch (funct) {
+        case 0x20: /* ADD */
+            result = FIXNUM_VALUE(os_context_register(context, rs))
+                + FIXNUM_VALUE(os_context_register(context, rt));
+            dest = rd;
+            break;
+
+        case 0x22: /* SUB */
+            result = FIXNUM_VALUE(os_context_register(context, rs))
+                - FIXNUM_VALUE(os_context_register(context, rt));
+            dest = rd;
+            break;
+
+        default:
+            interrupt_handle_now(signal, info, context);
+            return;
+        }
+        break;
+
     case 0x8: /* ADDI */
     case 0x8: /* ADDI */
-       result = FIXNUM_VALUE(*os_context_register_addr(context,rs)) + (immed>>2);
-       dest = rt;
-       break;
-       
+        result = FIXNUM_VALUE(os_context_register(context,rs))
+                    + (immed >> N_FIXNUM_TAG_BITS);
+        dest = rt;
+        break;
+
     default:
     default:
-       dest = 32;
-       break;
+        interrupt_handle_now(signal, info, context);
+        return;
     }
     }
-    
-    if (dest < 32) {
-        dynamic_space_free_pointer =
-            (lispobj *) *os_context_register_addr(context,reg_ALLOC);
 
 
-        *os_context_register_addr(context,dest) = alloc_number(result);
+    dynamic_space_free_pointer =
+        (lispobj *)(unsigned int)*os_context_register_addr(context,reg_ALLOC);
 
 
-       *os_context_register_addr(context, reg_ALLOC) =
-           (unsigned long) dynamic_space_free_pointer;
-       
-        arch_skip_instruction(context);
-       
-    }
-    else
-        interrupt_handle_now(signal, info, context);
+    *os_context_register_addr(context,dest) = alloc_number(result);
+
+    *os_context_register_addr(context, reg_ALLOC) =
+        (unsigned int) dynamic_space_free_pointer;
+
+    arch_skip_instruction(context);
 }
 
 }
 
-void arch_install_interrupt_handlers()
-{    
+void
+arch_install_interrupt_handlers(void)
+{
     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
 }
 
     undoably_install_low_level_interrupt_handler(SIGTRAP,sigtrap_handler);
     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
 }
 
-extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
+#ifdef LISP_FEATURE_LINKAGE_TABLE
 
 
-lispobj funcall0(lispobj function)
-{
-    lispobj *args = current_control_stack_pointer;
-
-    return call_into_lisp(function, args, 0);
-}
-
-lispobj funcall1(lispobj function, lispobj arg0)
-{
-    lispobj *args = current_control_stack_pointer;
+/* Linkage tables for MIPS
 
 
-    current_control_stack_pointer += 1;
-    args[0] = arg0;
+   Linkage entry size is 16, because we need 4 instructions to implement
+   a jump. The entry size constant is defined in parms.lisp.
 
 
-    return call_into_lisp(function, args, 1);
-}
+   Define the register to use in the linkage jump table. For MIPS this
+   has to be the PIC call register $25 aka t9 aka reg_ALLOC. */
+#define LINKAGE_TEMP_REG        reg_ALLOC
 
 
-lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1)
+/* Insert the necessary jump instructions at the given address. */
+void
+arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
 {
 {
-    lispobj *args = current_control_stack_pointer;
-
-    current_control_stack_pointer += 2;
-    args[0] = arg0;
-    args[1] = arg1;
-
-    return call_into_lisp(function, args, 2);
+  /* Make JMP to function entry. The instruction sequence is:
+       lui    $25, 0, %hi(addr)
+       addiu  $25, $25, %lo(addr)
+       jr     $25
+        nop */
+  unsigned int *insn = (unsigned int *)reloc_addr;
+  unsigned int addr = (unsigned int)target_addr;
+  unsigned int hi = ((addr + 0x8000) >> 16) & 0xffff;
+  unsigned int lo = addr & 0xffff;
+
+  *insn++ = (15 << 26) | (LINKAGE_TEMP_REG << 16) | hi;
+  *insn++ = ((9 << 26) | (LINKAGE_TEMP_REG << 21)
+                 | (LINKAGE_TEMP_REG << 16) | lo);
+  *insn++ = (LINKAGE_TEMP_REG << 21) | 8;
+  *insn = 0;
+
+  os_flush_icache((os_vm_address_t)reloc_addr, LINKAGE_TABLE_ENTRY_SIZE);
 }
 
 }
 
-lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
+void
+arch_write_linkage_table_ref(void *reloc_addr, void *target_addr)
 {
 {
-    lispobj *args = current_control_stack_pointer;
-
-    current_control_stack_pointer += 3;
-    args[0] = arg0;
-    args[1] = arg1;
-    args[2] = arg2;
-
-    return call_into_lisp(function, args, 3);
+    *(unsigned int *)reloc_addr = (unsigned int)target_addr;
 }
 
 }
 
+#endif