* Removed all Id tags.
[cacao.git] / src / vm / jit / s390 / md.c
index 42d16449bebbe51bab192e820ed6d22fe3a17cfa..936fa329ac997daaa88eeedf783871c5a1f503da 100644 (file)
@@ -1,6 +1,6 @@
-/* src/vm/jit/x86_64/md.c - machine dependent x86_64 Linux functions
+/* src/vm/jit/s390/md.c - machine dependent s390 Linux functions
 
-   Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+   Copyright (C) 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
    J. Wenninger, Institut f. Computersprachen - TU Wien
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Contact: cacao@cacaojvm.org
-
-   Authors: Christian Thalinger
-
-   Changes: Edwin Steiner
-
-   $Id: md.c 7367 2007-02-16 07:17:01Z pm $
-
 */
 
-#define REG_RSP 0
-#define REG_RIP 0
-#define REG_RAX 0
-#define REG_R10 0
-#define REG_ITMP2 0
-#define REG_RIP 0
-#define REG_RSP 0
-#define REG_RIP 0
-#define REG_RAX 0
-#define REG_R10 0
-#define REG_ITMP2 0
-#define REG_RIP 0
-#define REG_METHODPTR 0
-
 
 #define _GNU_SOURCE
 
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <ucontext.h>
 
 #include "vm/jit/s390/md-abi.h"
 
 #if defined(ENABLE_THREADS)
+# include "threads/threads-common.h"
 # include "threads/native/threads.h"
 #endif
 
 #include "vm/exceptions.h"
 #include "vm/signallocal.h"
 #include "vm/jit/asmpart.h"
+#include "vm/jit/abi.h"
+#include "vm/jit/methodheader.h"
 #include "vm/jit/stacktrace.h"
 
 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
 #include "vm/jit/disass.h" /* XXX debug */
 #endif
 
+#include "vm/jit/codegen-common.h"
+#include "vm/jit/s390/codegen.h"
+
 #include <assert.h>
 #define OOPS() assert(0);
 
+/* prototypes *****************************************************************/
+
+void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p);
+
+void md_dump_context(u1 *pc, mcontext_t *mc);
+
 /* md_init *********************************************************************
 
    Do some machine dependent initialization.
 
 void md_init(void)
 {
-       /* nothing to do */
 }
 
+/* md_dump_context ************************************************************
+   Logs the machine context
+  
+*******************************************************************************/
+
+void md_dump_context(u1 *pc, mcontext_t *mc) {
+       int i;
+       u1 *pv;
+       methodinfo *m;
+
+       union {
+               u8 l;
+               fpreg_t fr;
+       } freg;
+
+       log_println("Dumping context.");
+
+       log_println("Program counter: 0x%08X", pc);
+
+       pv = codegen_get_pv_from_pc_nocheck(pc);
+       if (pv == NULL) {
+               log_println("No java method found at location.");
+       } else {
+               m = (*(codeinfo **)(pv + CodeinfoPointer))->m;
+               log_println(
+                       "Java method: class %s, method %s, descriptor %s.",
+                       m->class->name->text, m->name->text, m->descriptor->text
+               );
+       }
+
+#if defined(ENABLE_DISASSEMBLER)
+       log_println("Printing instruction at program counter:");
+       disassinstr(pc);
+#endif
+
+       log_println("General purpose registers:");
+
+       for (i = 0; i < 16; i++) {
+               log_println("\tr%d:\t0x%08X\t%d", i, mc->gregs[i], mc->gregs[i]);
+       }
+
+       log_println("Floating point registers:");
+
+       for (i = 0; i < 16; i++) {
+               freg.fr.d = mc->fpregs.fprs[i].d;
+               log_println("\tf%d\t0x%016llX\t(double)%e\t(float)%f", i, freg.l, freg.fr.d, freg.fr.f);
+       }
+
+#if defined(ENABLE_THREADS)
+       log_println("Dumping the current stacktrace:");
+       threads_print_stacktrace();
+#endif
+
+}
 
 /* md_signal_handler_sigsegv ***************************************************
 
@@ -95,35 +140,138 @@ void md_init(void)
 
 void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
 {
-       ucontext_t *_uc;
-       mcontext_t *_mc;
-       u1         *sp;
-       u1         *ra;
-       u1         *xpc;
+       stackframeinfo  sfi;
+       ucontext_t     *_uc;
+       mcontext_t     *_mc;
+       u1             *pv;
+       u1             *sp;
+       u1             *ra;
+       u1             *xpc;
+       int             type;
+       intptr_t        val;
+       void           *p;
+       s4              base;
+       s4              is_null;
 
        _uc = (ucontext_t *) _p;
        _mc = &_uc->uc_mcontext;
 
-       /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
-          different to the ones in <ucontext.h>. */
+       xpc = (u1 *)_mc->psw.addr;
+
+       /* Check opcodes and verify it is a null pointer exception */
+
+       switch (xpc[0]) {
+               case 0x58: /* L */
+               case 0x50: /* ST */
+               case 0x55: /* CL (array size check on NULL array) */
+                       base = (xpc[2] >> 4) & 0xF;
+                       if (base == 0) {
+                               is_null = 1;
+                       } else if (_mc->gregs[base] == 0) {
+                               is_null = 1;
+                       } else {
+                               is_null = 0;
+                       }
+                       break;
+               default:
+                       is_null = 0;
+                       break;
+       }
+
+       if (! is_null) {
+#if !defined(NDEBUG)
+               md_dump_context(xpc, _mc);
+#endif
+               vm_abort("%s: segmentation fault at %p, aborting.", __FUNCTION__, xpc);
+       }
 
-       sp  = (u1 *) _mc->gregs[REG_RSP];
-       xpc = (u1 *) _mc->gregs[REG_RIP];
-       ra  = xpc;                          /* return address is equal to xpc     */
+       pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
+       sp = (u1 *)_mc->gregs[REG_SP];
+       ra = xpc;
+       type = EXCEPTION_HARDWARE_NULLPOINTER;
+       val = 0;
 
-#if 0
-       /* check for StackOverflowException */
+       /* create stackframeinfo */
 
-       threads_check_stackoverflow(sp);
-#endif
+       stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
+
+       /* Handle the type. */
 
-       _mc->gregs[REG_RAX] =
-               (ptrint) stacktrace_hardware_nullpointerexception(NULL, sp, ra, xpc);
+       p = signal_handle(xpc, type, val);
 
-       _mc->gregs[REG_R10] = (ptrint) xpc;                      /* REG_ITMP2_XPC */
-       _mc->gregs[REG_RIP] = (ptrint) asm_handle_exception;
+       /* remove stackframeinfo */
+
+       stacktrace_remove_stackframeinfo(&sfi);
+
+       if (p != NULL) {
+               _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
+               _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
+               _mc->psw.addr              = (intptr_t) asm_handle_exception;
+       }
+       else {
+               _mc->psw.addr              = (intptr_t) xpc;
+       }
 }
 
+void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p)
+{
+       stackframeinfo  sfi;
+       ucontext_t     *_uc;
+       mcontext_t     *_mc;
+       u1             *xpc;
+       u1             *ra;
+       u1             *pv;
+       u1             *sp;
+       int             type;
+       intptr_t        val;
+       void           *p;
+       s4              reg;
+
+       _uc = (ucontext_t *) _p;
+       _mc = &_uc->uc_mcontext;
+       xpc = ra = siginfo->si_addr;
+
+       /* Our trap instruction has the format: { 0x02, one_byte_of_data }. */
+
+       if ((siginfo->si_code == ILL_ILLOPC) && (xpc[0] == 0x02)) {
+
+               /* bits 7-4 contain a register holding a value */
+               reg = (xpc[1] >> 4) & 0xF;
+
+               /* bits 3-0 designate the exception type */
+               type = xpc[1] & 0xF;  
+
+               pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
+               sp = (u1 *)_mc->gregs[REG_SP];
+               val = (ptrint)_mc->gregs[reg];
+
+               /* create stackframeinfo */
+
+               stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
+
+               /* Handle the type. */
+
+               p = signal_handle(xpc, type, val);
+
+               /* remove stackframeinfo */
+
+               stacktrace_remove_stackframeinfo(&sfi);
+
+               if (p != NULL) {
+                       _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
+                       _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
+                       _mc->psw.addr              = (intptr_t) asm_handle_exception;
+               }
+               else {
+                       _mc->psw.addr              = (intptr_t) xpc;
+               }
+       } else {
+#if !defined(NDEBUG)
+               md_dump_context(xpc, _mc);
+#endif
+               vm_abort("%s: illegal instruction at %p, aborting.", __FUNCTION__, xpc);
+       }
+}
 
 /* md_signal_handler_sigfpe ****************************************************
 
@@ -134,27 +282,85 @@ void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
 
 void md_signal_handler_sigfpe(int sig, siginfo_t *siginfo, void *_p)
 {
-       ucontext_t  *_uc;
-       mcontext_t  *_mc;
-       u1          *sp;
-       u1          *ra;
-       u1          *xpc;
+       stackframeinfo  sfi;
+       ucontext_t     *_uc;
+       mcontext_t     *_mc;
+       u1             *pv;
+       u1             *sp;
+       u1             *ra;
+       u1             *xpc;
+       u1             *pc;
+       int             r1, r2;
+       int             type;
+       intptr_t        val;
+       void           *p;
 
        _uc = (ucontext_t *) _p;
        _mc = &_uc->uc_mcontext;
 
-       /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
-          different to the ones in <ucontext.h>. */
+       /* Instruction that raised signal */
+       xpc = siginfo->si_addr;
+
+       /* Check opcodes */
 
-       sp  = (u1 *) _mc->gregs[REG_RSP];
-       xpc = (u1 *) _mc->gregs[REG_RIP];
-       ra  = xpc;                          /* return address is equal to xpc     */
+       if (xpc[0] == 0x1D) { /* DR */
 
-       _mc->gregs[REG_RAX] =
-               (ptrint) stacktrace_hardware_arithmeticexception(NULL, sp, ra, xpc);
+               r1 = (xpc[1] >> 4) & 0xF;
+               r2 = xpc[1] & 0xF;
 
-       _mc->gregs[REG_R10] = (ptrint) xpc;                      /* REG_ITMP2_XPC */
-       _mc->gregs[REG_RIP] = (ptrint) asm_handle_exception;
+               if (
+                       (_mc->gregs[r1] == 0xFFFFFFFF) &&
+                       (_mc->gregs[r1 + 1] == 0x80000000) && 
+                       (_mc->gregs[r2] == 0xFFFFFFFF)
+               ) {
+                       /* handle special case 0x80000000 / 0xFFFFFFFF that fails on hardware */
+                       /* next instruction */
+                       pc = (u1 *)_mc->psw.addr;
+                       /* reminder */
+                       _mc->gregs[r1] = 0;
+                       /* quotient */
+                       _mc->gregs[r1 + 1] = 0x80000000;
+                       /* continue at next instruction */
+                       _mc->psw.addr = (ptrint) pc;
+
+                       return;
+               }
+               else if (_mc->gregs[r2] == 0) {
+                       /* division by 0 */
+
+                       pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
+                       sp = (u1 *)_mc->gregs[REG_SP];
+                       ra = xpc;
+
+                       type = EXCEPTION_HARDWARE_ARITHMETIC;
+                       val = 0;
+
+                       /* create stackframeinfo */
+
+                       stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
+
+                       /* Handle the type. */
+
+                       p = signal_handle(xpc, type, val);
+
+                       /* remove stackframeinfo */
+
+                       stacktrace_remove_stackframeinfo(&sfi);
+
+                       _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
+                       _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
+                       _mc->psw.addr              = (intptr_t) asm_handle_exception;
+
+                       return;
+               }
+       }
+
+       /* Could not handle signal */
+
+#if !defined(NDEBUG)
+       md_dump_context(xpc, _mc);
+#endif
+       vm_abort("%s: floating point exception at %p, aborting.", __FUNCTION__, xpc);
 }
 
 
@@ -180,7 +386,7 @@ void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
        /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
           different to the ones in <ucontext.h>. */
 
-       pc = (u1 *) _mc->gregs[REG_RIP];
+       pc = (u1 *) _mc->psw.addr;
 
        t->pc = pc;
 }
@@ -188,17 +394,22 @@ void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
 
 
 #if defined(ENABLE_THREADS)
-void thread_restartcriticalsection(ucontext_t *_uc)
+void md_critical_section_restart(ucontext_t *_uc)
 {
        mcontext_t *_mc;
-       void       *pc;
+       u1         *pc;
+       void       *npc;
 
        _mc = &_uc->uc_mcontext;
 
-       pc = critical_find_restart_point((void *) _mc->gregs[REG_RIP]);
+       pc = (u1 *)_mc->psw.addr;
+
+       npc = critical_find_restart_point(pc);
 
-       if (pc != NULL)
-               _mc->gregs[REG_RIP] = (ptrint) pc;
+       if (npc != NULL) {
+               log_println("%s: pc=%p, npc=%p", __FUNCTION__, pc, npc);
+               _mc->psw.addr = (ptrint) npc;
+       }
 }
 #endif
 
@@ -225,7 +436,7 @@ void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc)
        disp += 4; /* size of branch */
        disp /= 2; /* specified in halfwords */
 
-       /* TODO check for overflow */
+       ASSERT_VALID_BRANCH(disp);      
 
        /* patch the branch instruction before the mcodeptr */
 
@@ -246,7 +457,7 @@ u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize)
 
        /* on S390 the return address is located on the top of the stackframe */
 
-       ra = *((u1 **) (sp + framesize - SIZEOF_VOID_P));
+       ra = *((u1 **) (sp + framesize - 8));
 
        return ra;
 }
@@ -285,7 +496,7 @@ last 2 instructions the same as in invokevirtual
 
 u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
 {
-       u1  base;
+       u1  base, index;
        s4  offset;
        u1 *pa;                             /* patch address                      */
 
@@ -296,32 +507,53 @@ u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
        /* get the base register of the load */
 
        base = ra[2] >> 4;
+       index = ra[1] & 0xF;
 
        /* check for the different calls */
 
-       if (base == 0xd) { /* pv relative */
-               /* INVOKESTATIC/SPECIAL */
+       switch (base) {
+               case 0xd:
+                       /* INVOKESTATIC/SPECIAL */
 
-               /* the offset is in the load before the load */
+               
+                       switch (index) {
+                               case 0x0:
+                                       /* the offset is in the load instruction */
+                                       offset = ((*(u2 *)(ra + 2)) & 0xFFF) + N_PV_OFFSET;
+                                       break;
+                               case 0x1:
+                                       /* the offset is in the immediate load before the load */
+                                       offset = *((s2 *) (ra - 2));
+                                       break;
+                               default:
+                                       assert(0);
+                       }
 
-               offset = *((s2 *) (ra - 2));
+                       /* add the offset to the procedure vector */
 
-               /* add the offset to the procedure vector */
+                       pa = sfi->pv + offset;
 
-               pa = sfi->pv + offset;
-       }
-       else if (base == 0xc) { /* mptr relative */
-               /* INVOKEVIRTUAL/INTERFACE */
+                       break;
 
-               offset = *((u2 *)(ra + 2)) & 0xFFF;
+               case 0xc:
+                       /* mptr relative */
+                       /* INVOKEVIRTUAL/INTERFACE */
 
-               /* add offset to method pointer */
-               
-               pa = mptr + offset;
-       }
-       else {
-               /* catch any problems */
-               assert(0); 
+                       offset = *((u2 *)(ra + 2)) & 0xFFF;
+
+                       /* return NULL if no mptr was specified (used for replacement) */
+
+                       if (mptr == NULL)
+                               return NULL;
+
+                       /* add offset to method pointer */
+                       
+                       pa = mptr + offset;
+                       break;
+               default:
+                       /* catch any problems */
+                       assert(0); 
+                       break;
        }
 
        return pa;
@@ -390,39 +622,117 @@ void md_dcacheflush(u1 *addr, s4 nbytes)
    Patch the given replacement point.
 
 *******************************************************************************/
-#if 0
-void md_patch_replacement_point(rplpoint *rp)
+#if defined(ENABLE_REPLACEMENT)
+void md_patch_replacement_point(codeinfo *code, s4 index, rplpoint *rp, u1 *savedmcode)
 {
-    u8 mcode;
+       assert(0);
+}
+#endif
 
-       /* XXX this is probably unsafe! */
+void md_handle_exception(int32_t *regs, int64_t *fregs, int32_t *out) {
 
-       /* save the current machine code */
-       mcode = *(u8*)rp->pc;
+       uint8_t *xptr;
+       uint8_t *xpc;
+       uint8_t *sp;
+       uint8_t *pv;
+       uint8_t *ra;
+       uint8_t *handler;
+       int32_t framesize;
+       int32_t intsave;
+       int32_t fltsave;
+       int64_t *savearea;
+       int i;
+       int reg;
+       int loops = 0;
 
-       /* write spinning instruction */
-       *(u2*)(rp->pc) = 0xebfe;
+       /* get registers */
 
-       /* write 5th byte */
-       rp->pc[4] = (rp->mcode >> 32);
+       xptr = *(uint8_t **)(regs + REG_ITMP1_XPTR);
+       xpc = *(uint8_t **)(regs + REG_ITMP2_XPC);
+       sp = *(uint8_t **)(regs + REG_SP);
 
-       /* write first word */
-    *(u4*)(rp->pc) = (u4) rp->mcode;
 
-       /* store saved mcode */
-       rp->mcode = mcode;
-       
-#if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
-       {
-               u1* u1ptr = rp->pc;
-               DISASSINSTR(u1ptr);
-               fflush(stdout);
-       }
-#endif
-                       
-    /* XXX if required asm_cacheflush(rp->pc,8); */
+       /* initialize number of calle saved int regs to restore to 0 */
+       out[0] = 0;
+
+       /* initialize number of calle saved flt regs to restore to 0 */
+       out[1] = 0;
+
+       do {
+
+               ++loops;
+
+               pv = codegen_get_pv_from_pc(xpc);
+
+               handler = exceptions_handle_exception(xptr, xpc, pv, sp);
+
+               if (handler == NULL) {
+
+                       /* exception was not handled
+                        * get values of calee saved registers and remove stack frame 
+                        */
+
+                       /* read stuff from data segment */
+
+                       framesize = *(int32_t *)(pv + FrameSize);
+
+                       intsave = *(int32_t *)(pv + IntSave);
+                       if (intsave > out[0]) {
+                               out[0] = intsave;
+                       }
+
+                       fltsave = *(int32_t *)(pv + FltSave);
+                       if (fltsave > out[1]) {
+                               out[1] = fltsave;
+                       }
+
+                       /* pointer to register save area */
+
+                       savearea = (int64_t *)(sp + framesize - 8);
+
+                       /* return address */
+
+                       ra = *(uint8_t **)(sp + framesize - 8);
+
+                       /* restore saved registers */
+
+                       for (i = 0; i < intsave; ++i) {
+                               --savearea;
+                               reg = abi_registers_integer_saved[INT_SAV_CNT - 1 - i];
+                               regs[reg] = *(int32_t *)(savearea);
+                       }
+
+                       for (i = 0; i < fltsave; ++i) {
+                               --savearea;
+                               reg = abi_registers_float_saved[FLT_SAV_CNT - 1 - i];
+                               fregs[reg] = *savearea;
+                       }
+
+                       /* remove stack frame */
+
+                       sp += framesize;
+
+                       /* new xpc is call before return address */
+
+                       xpc = ra;
+
+               } else {
+                       xpc = handler;
+               }
+       } while (handler == NULL);
+
+       /* write new values for registers */
+
+       *(uint8_t **)(regs + REG_ITMP1_XPTR) = xptr;
+       *(uint8_t **)(regs + REG_ITMP2_XPC) = xpc;
+       *(uint8_t **)(regs + REG_SP) = sp;
+       *(uint8_t **)(regs + REG_PV) = pv - 0XFFC;
+
+       /* maybe leaf flag */
+
+       out[2] = (loops == 1);
 }
-#endif
+
 /*
  * These are local overrides for various environment variables in Emacs.
  * Please do not remove this and leave it at the end of the file, where