* src/vm/jit/trap.cpp: Finally switched s390 to the new trap decoding method.
[cacao.git] / src / vm / jit / trap.cpp
index c1dc1629559687898b64b3c393c09d42a278b246..d3781e33fcd793936481f19f9b0b33dcdeca4249 100644 (file)
@@ -1,7 +1,8 @@
 /* src/vm/jit/trap.cpp - hardware traps
 
-   Copyright (C) 2008
+   Copyright (C) 2008, 2009, 2010
    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+   Copyright (C) 2009 Theobroma Systems Ltd.
 
    This file is part of CACAO.
 
 #include "vm/jit/patcher-common.hpp"
 #include "vm/jit/replace.hpp"
 #include "vm/jit/stacktrace.hpp"
+#include "vm/jit/trap.hpp"
+
+#if defined(__S390__)
+#include "vm/jit/s390/codegen.h"
+#else
+#define N_PV_OFFSET 0
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -64,21 +72,15 @@ extern "C" {
  */
 void trap_init(void)
 {
-#if !(defined(__ARM__) && defined(__LINUX__))
-       /* On arm-linux the first memory page can't be mmap'ed, as it
-          contains the exception vectors. */
-
-       int pagesize;
-
-       /* mmap a memory page at address 0x0, so our hardware-exceptions
-          work. */
-
-       pagesize = os::getpagesize();
+       TRACESUBSYSTEMINITIALIZATION("trap_init");
 
-       (void) os::mmap_anonymous(NULL, pagesize, PROT_NONE, MAP_PRIVATE | MAP_FIXED);
-#endif
+       /* If requested we mmap a memory page at address 0x0,
+          so our hardware-exceptions work. */
 
-       TRACESUBSYSTEMINITIALIZATION("trap_init");
+       if (opt_AlwaysMmapFirstPage) {
+               int pagesize = os::getpagesize();
+               (void) os::mmap_anonymous(NULL, pagesize, PROT_NONE, MAP_PRIVATE | MAP_FIXED);
+       }
 
 #if !defined(TRAP_INSTRUCTION_IS_LOAD)
 # error TRAP_INSTRUCTION_IS_LOAD is not defined in your md-trap.h
@@ -97,29 +99,23 @@ void trap_init(void)
  * Handles the signal which is generated by trap instructions, caught
  * by a signal handler and calls the correct function.
  *
- * @param type trap number
- * @param 
+ * @param sig signal number
+ * @param xpc exception PC
+ * @param context pointer to OS dependent machine context
  */
-void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xpc, void *context)
+void trap_handle(int sig, void *xpc, void *context)
 {
        executionstate_t es;
        stackframeinfo_t sfi;
+       trapinfo_t       trp;
 
 #if !defined(NDEBUG)
-       if (opt_TraceTraps)
-               log_println("[signal_handle: trap %d]", type);
-#endif
-       
-#if defined(ENABLE_VMLOG)
-       vmlog_cacao_signl_type(type);
+       // Sanity checking the XPC.
+       if (xpc == NULL)
+               vm_abort("trap_handle: The program counter is NULL!");
 #endif
 
-       /* Prevent compiler warnings. */
-
-       java_handle_t* o = NULL;
-       methodinfo*    m = NULL;
-
-#if defined(__ALPHA__) || defined(__ARM__) || defined(__I386__) || defined(__POWERPC__) || defined(__POWERPC64__) || defined(__X86_64__)
+#if defined(__ALPHA__) || defined(__ARM__) || defined(__I386__) || defined(__MIPS__) || defined(__POWERPC__) || defined(__POWERPC64__) || defined(__S390__) || defined(__X86_64__)
 # if !defined(NDEBUG)
        /* Perform a sanity check on our execution state functions. */
 
@@ -130,12 +126,66 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
 
        es.code = NULL;
        md_executionstate_read(&es, context);
+
+//# define TRAP_TRACE_VERBOSE
+# if !defined(NDEBUG) && defined(TRAP_TRACE_VERBOSE)
+       /* Dump contents of execution state */
+
+       if (opt_TraceTraps) {
+               log_println("[trap_handle: dumping execution state BEFORE ...]");
+               executionstate_println(&es);
+       }
+# endif
+#endif
+
+       // Extract information from executionstate
+       void* pv = es.pv;  // Maybe null, resolved during stackframeinfo creation.
+       void* sp = es.sp;
+#if defined(__I386__) || defined(__X86_64__)
+       void* ra = xpc;  // Return address is equal to XPC.
+#else
+       void* ra = es.ra;  // This is correct for leafs.
 #endif
 
+       // Decode machine-dependent trap instruction.
+       bool decode_result = md_trap_decode(&trp, sig, xpc, &es);
+
+       // Check if the trap instruction is valid and was decoded
+       // successfully.
+       if (!decode_result) {
+               // Check if the PC has been patched during our way to this
+               // trap handler (see PR85).
+               // NOTE: Some archs use SIGILL for other traps too, but it's OK to
+               // do this check anyway because it will fail.
+               if (patcher_is_patched_at(xpc) == true) {
+                       if (opt_PrintWarnings)
+                               log_println("trap_handle: Detected patcher race condition (PR85) at %p", xpc);
+                       return;
+               }
+
+               // We have a problem...
+               vm_abort_disassemble(xpc, 1, "trap_handle: Unknown trap instruction at %p", xpc);
+       }
+
+       // For convenience only.
+       int      type = trp.type;
+       intptr_t val  = trp.value;
+
        /* Do some preparations before we enter the nativeworld. */
        /* BEFORE: creating stackframeinfo */
 
+       // Prevent compiler warnings.
+       int32_t        index = 0;
+       java_handle_t* o     = NULL;
+       methodinfo*    m     = NULL;
+
        switch (type) {
+       case TRAP_ArrayIndexOutOfBoundsException:
+               /* Get the index into the array causing the exception. */
+
+               index = (int32_t) val;
+               break;
+
        case TRAP_ClassCastException:
                /* Wrap the value into a handle, as it is a reference. */
 
@@ -143,6 +193,12 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                break;
 
        case TRAP_COMPILER:
+               /* We need to fixup the XPC, SP and RA here because they
+                  all might point into the compiler stub instead of the
+                  calling method. */
+
+               MD_TRAP_COMPILER_FIXUP(xpc, ra, sp, pv);
+
                /* In this case the passed PV points to the compiler stub.  We
                   get the methodinfo pointer here and set PV to NULL so
                   stacktrace_stackframeinfo_add determines the PV for the
@@ -150,6 +206,7 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
 
                m  = code_get_methodinfo_for_pv(pv);
                pv = NULL;
+
                break;
 
        default:
@@ -157,14 +214,28 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                break;
        }
 
+#if !defined(NDEBUG)
+       // Trace this trap.
+       if (opt_TraceTraps)
+               log_println("[trap_handle: sig=%d, type=%d, val=%p, pv=%p, sp=%p, ra=%p, xpc=%p]", sig, type, val, pv, sp, ra, xpc);
+#endif
+
+#if defined(ENABLE_VMLOG)
+       vmlog_cacao_signl_type(type);
+#endif
+
        /* Fill and add a stackframeinfo. */
 
        stacktrace_stackframeinfo_add(&sfi, pv, sp, ra, xpc);
 
        /* Get resulting exception (or pointer to compiled method). */
 
-       int32_t        index;
        java_handle_t* p;
+       void*          entry;
+       bool           was_patched;
+#if defined(ENABLE_REPLACEMENT)
+       bool           was_replaced;
+#endif
 
        switch (type) {
        case TRAP_NullPointerException:
@@ -176,7 +247,6 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                break;
 
        case TRAP_ArrayIndexOutOfBoundsException:
-               index = (int32_t) val;
                p = exceptions_new_arrayindexoutofboundsexception(index);
                break;
 
@@ -193,25 +263,25 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                break;
 
        case TRAP_PATCHER:
+               p = NULL;
 #if defined(ENABLE_REPLACEMENT)
-               if (replace_me_wrapper((uint8_t*) xpc, context)) {
-                       p = NULL;
+               was_replaced = replace_handler((uint8_t*) xpc, &es);
+               if (was_replaced)
                        break;
-               }
 #endif
-               p = patcher_handler((uint8_t*) xpc);
+               was_patched = patcher_handler((uint8_t*) xpc);
                break;
 
        case TRAP_COMPILER:
-               p = (java_handle_t*) jit_compile_handle(m, sfi.pv, ra, (void*) val);
+               p = NULL;
+               entry = jit_compile_handle(m, sfi.pv, ra, (void*) val);
                break;
 
-#if defined(ENABLE_REPLACEMENT)
+#if defined(__I386__) && defined(ENABLE_REPLACEMENT)
+# warning Port the below stuff to use the patching subsystem.
        case TRAP_COUNTDOWN:
-# if defined(__I386__)
-               replace_me_wrapper((char*)xpc - 13, context);
-# endif
                p = NULL;
+               (void) replace_handler((uint8_t*) xpc - 13, &es);
                break;
 #endif
 
@@ -222,20 +292,7 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
 
                /* If that does not work, print more debug info. */
 
-               log_println("signal_handle: unknown hardware exception type %d", type);
-
-#if SIZEOF_VOID_P == 8
-               log_println("PC=0x%016lx", xpc);
-#else
-               log_println("PC=0x%08x", xpc);
-#endif
-
-#if defined(ENABLE_DISASSEMBLER)
-               log_println("machine instruction at PC:");
-               disassinstr((uint8_t*) xpc);
-#endif
-
-               vm_abort("Exiting...");
+               vm_abort_disassemble(xpc, 1, "trap_handle: Unknown hardware exception type %d", type);
 
                /* keep compiler happy */
 
@@ -246,7 +303,7 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
 
        stacktrace_stackframeinfo_remove(&sfi);
 
-#if defined(__ALPHA__) || defined(__ARM__) || defined(__I386__) || defined(__POWERPC__) || defined(__POWERPC64__) || defined(__X86_64__)
+#if defined(__ALPHA__) || defined(__ARM__) || defined(__I386__) || defined(__MIPS__) || defined(__POWERPC__) || defined(__POWERPC64__) || defined(__S390__) || defined(__X86_64__)
        /* Update execution state and set registers. */
        /* AFTER: removing stackframeinfo */
 
@@ -255,9 +312,11 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                // The normal case for a compiler trap is to jump directly to
                // the newly compiled method.
 
-               if (p != NULL) {
-                       es.pc = (uint8_t *) (uintptr_t) p;
-                       es.pv = (uint8_t *) (uintptr_t) p;
+               if (entry != NULL) {
+                       es.pc = (uint8_t *) (uintptr_t) entry;
+                       // The s390 executionstate offsets pv, so we need to
+                       // compensate here.
+                       es.pv = (uint8_t *) (uintptr_t) entry - N_PV_OFFSET;
                        break;
                }
 
@@ -267,21 +326,60 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
                p = exceptions_get_and_clear_exception();
                assert(p != NULL);
 
+               // Remove RA from stack on some archs.
+
+               es.sp = (uint8_t*) sp;
+
                // Get and set the PV from the parent Java method.
 
-               es.pv = (uint8_t *) md_codegen_get_pv_from_pc(ra);
+               es.pv = (uint8_t*) md_codegen_get_pv_from_pc(ra) - N_PV_OFFSET;
 
                // Now fall-through to default exception handling.
 
                goto trap_handle_exception;
 
        case TRAP_PATCHER:
+#if defined(ENABLE_REPLACEMENT)
+               // If on-stack-replacement suceeded, we are not allowed to touch
+               // the execution state. We assume that there was no exception.
+
+               if (was_replaced) {
+                       assert(exceptions_get_exception() == NULL);
+                       break;
+               }
+#endif
+
                // The normal case for a patcher trap is to continue execution at
                // the trap instruction. On some archs the PC may point after the
                // trap instruction, so we reset it here.
 
+               if (was_patched) {
+                       assert(exceptions_get_exception() == NULL);
+                       es.pc = (uint8_t *) (uintptr_t) xpc;
+                       break;
+               }
+
+               // In case patching was not successful, we try to fetch the pending
+               // exception here.
+
+               p = exceptions_get_and_clear_exception();
+
+               // If there is no pending exception, we continue execution behind
+               // the position still in need of patching. Normally this would
+               // indicate an error in the patching subsystem, but others might
+               // want to piggyback patchers and we want to be able to provide
+               // "reusable trap points" and avoid inifinite loops here. This is
+               // especially useful to implement breakpoints or profiling points
+               // of any kind. So before changing the trap logic, think about
+               // utilizing the patching subsystem on your quest. :)
+
                if (p == NULL) {
+#if !defined(NDEBUG)
+                       if (opt_PrintWarnings)
+                               log_println("trap_handle: Detected reusable trap at %p", xpc);
+#endif
                        es.pc = (uint8_t *) (uintptr_t) xpc;
+                       es.pc += REPLACEMENT_PATCH_SIZE;
                        break;
                }
 
@@ -290,24 +388,35 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
        trap_handle_exception:
        default:
                if (p != NULL) {
+#if defined(__ALPHA__) || defined(__I386__) || defined(__X86_64__)
+                       // Perform stack unwinding for exceptions on execution state.
+                       es.pc = (uint8_t *) (uintptr_t) xpc;
+                       es.pv = (uint8_t *) (uintptr_t) sfi.pv;
+                       executionstate_unwind_exception(&es, p);
+
+                       // Pass the exception object to the exception handler.
+                       es.intregs[REG_ITMP1_XPTR] = (uintptr_t) LLNI_DIRECT(p);
+#else
                        es.intregs[REG_ITMP1_XPTR] = (uintptr_t) LLNI_DIRECT(p);
                        es.intregs[REG_ITMP2_XPC]  = (uintptr_t) xpc;
                        es.pc                      = (uint8_t *) (uintptr_t) asm_handle_exception;
+#endif
                }
        }
 
        /* Write back execution state to current context. */
 
        md_executionstate_write(&es, context);
-#endif
 
-       /* Unwrap and return the exception object. */
-       /* AFTER: removing stackframeinfo */
+# if !defined(NDEBUG) && defined(TRAP_TRACE_VERBOSE)
+       /* Dump contents of execution state */
 
-       if (type == TRAP_COMPILER)
-               return p;
-       else
-               return LLNI_UNWRAP(p);
+       if (opt_TraceTraps) {
+               log_println("[trap_handle: dumping execution state AFTER ...]");
+               executionstate_println(&es);
+       }
+# endif
+#endif
 }
 
 #ifdef __cplusplus
@@ -326,4 +435,5 @@ void* trap_handle(int type, intptr_t val, void *pv, void *sp, void *ra, void *xp
  * c-basic-offset: 4
  * tab-width: 4
  * End:
+ * vim:noexpandtab:sw=4:ts=4:
  */