merged volatile memory barriers
[cacao.git] / src / vm / jit / patcher-common.cpp
index a33aba7893d350bfda472c2d7669d7935c0bc709..6acd9687c493440c8548b4509a9c584a8642b4d6 100644 (file)
@@ -1,6 +1,6 @@
 /* src/vm/jit/patcher-common.cpp - architecture independent code patching stuff
 
-   Copyright (C) 2007, 2008
+   Copyright (C) 2007, 2008, 2009
    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
    Copyright (C) 2008 Theobroma Systems Ltd.
 
 #include <assert.h>
 #include <stdint.h>
 
+#include <algorithm>
+#include <functional>
+
 #include "codegen.h"                   /* for PATCHER_NOPS */
 #include "md.h"
+#include "trap.hpp"
 
-#include "mm/memory.h"
+#include "mm/memory.hpp"
 
-#include "native/native.h"
+#include "native/native.hpp"
 
-#include "toolbox/list.h"
-#include "toolbox/logging.h"           /* XXX remove me! */
+#include "toolbox/list.hpp"
+#include "toolbox/logging.hpp"           /* XXX remove me! */
 
+#include "vm/breakpoint.hpp"
 #include "vm/exceptions.hpp"
-#include "vm/initialize.h"
+#include "vm/hook.hpp"
+#include "vm/initialize.hpp"
 #include "vm/options.h"
-#include "vm/resolve.h"
-#include "vm/vm.hpp"                     /* for vm_abort */
+#include "vm/os.hpp"
+#include "vm/resolve.hpp"
 
 #include "vm/jit/code.hpp"
 #include "vm/jit/disass.h"
@@ -67,11 +73,14 @@ typedef struct patcher_function_list_t {
 
 static patcher_function_list_t patcher_function_list[] = {
        { PATCHER_initialize_class,              "initialize_class" },
+#ifdef ENABLE_VERIFIER
        { PATCHER_resolve_class,                 "resolve_class" },
+#endif /* ENABLE_VERIFIER */
        { PATCHER_resolve_native_function,       "resolve_native_function" },
        { PATCHER_invokestatic_special,          "invokestatic_special" },
        { PATCHER_invokevirtual,                 "invokevirtual" },
        { PATCHER_invokeinterface,               "invokeinterface" },
+       { PATCHER_breakpoint,                    "breakpoint" },
        { NULL,                                  "-UNKNOWN PATCHER FUNCTION-" }
 };
 #endif
@@ -85,7 +94,7 @@ static patcher_function_list_t patcher_function_list[] = {
 
 void patcher_list_create(codeinfo *code)
 {
-       code->patchers = list_create(OFFSET(patchref_t, linkage));
+       code->patchers = new LockedList<patchref_t>();
 }
 
 
@@ -98,20 +107,13 @@ void patcher_list_create(codeinfo *code)
 
 void patcher_list_reset(codeinfo *code)
 {
-       patchref_t *pr;
-
-       /* free all elements of the list */
-
-       while((pr = (patchref_t*) list_first(code->patchers)) != NULL) {
-               list_remove(code->patchers, pr);
-
-               FREE(pr, patchref_t);
-
 #if defined(ENABLE_STATISTICS)
-               if (opt_stat)
-                       size_patchref -= sizeof(patchref_t);
+       if (opt_stat)
+               size_patchref -= sizeof(patchref_t) * code->patchers->size();
 #endif
-       }
+
+       // Free all elements of the list.
+       code->patchers->clear();
 }
 
 /* patcher_list_free ***********************************************************
@@ -122,92 +124,136 @@ void patcher_list_reset(codeinfo *code)
 
 void patcher_list_free(codeinfo *code)
 {
-       /* free all elements of the list */
-
+       // Free all elements of the list.
        patcher_list_reset(code);
 
-       /* free the list itself */
-
-       list_free(code->patchers);
+       // Free the list itself.
+       delete code->patchers;
 }
 
 
-/* patcher_list_find ***********************************************************
-
-   Find an entry inside the patcher list for the given codeinfo
-   by specifying the program counter of the patcher position.
-
-   NOTE: Caller should hold the patcher list lock or maintain
-   exclusive access otherwise.
+/**
+ * Find an entry inside the patcher list for the given codeinfo by
+ * specifying the program counter of the patcher position.
+ *
+ * NOTE: Caller should hold the patcher list lock or maintain
+ * exclusive access otherwise.
+ *
+ * @param pc Program counter to find.
+ *
+ * @return Pointer to patcher.
+ */
 
-*******************************************************************************/
+struct foo : public std::binary_function<patchref_t, void*, bool> {
+       bool operator() (const patchref_t& pr, const void* pc) const
+       {
+               return (pr.mpc == (uintptr_t) pc);
+       }
+};
 
-static patchref_t *patcher_list_find(codeinfo *code, void *pc)
+static patchref_t* patcher_list_find(codeinfo* code, void* pc)
 {
-       patchref_t *pr;
+       // Search for a patcher with the given PC.
+       List<patchref_t>::iterator it = std::find_if(code->patchers->begin(), code->patchers->end(), std::bind2nd(foo(), pc));
 
-       /* walk through all patcher references for the given codeinfo */
+       if (it == code->patchers->end())
+               return NULL;
 
-       pr = (patchref_t*) list_first(code->patchers);
+       return &(*it);
+}
 
-       while (pr) {
 
-/*#define TRACE_PATCHER_FIND*/
-#ifdef TRACE_PATCHER_FIND
-               log_println("patcher_list_find: %p == %p", pr->mpc, pc);
-#endif
+/**
+ * Show the content of the whole patcher reference list for
+ * debugging purposes.
+ *
+ * @param code The codeinfo containing the patcher list.
+ */
+#if !defined(NDEBUG)
+void patcher_list_show(codeinfo *code)
+{
+       for (List<patchref_t>::iterator it = code->patchers->begin(); it != code->patchers->end(); it++) {
+               patchref_t& pr = *it;
 
-               if (pr->mpc == (ptrint) pc)
-                       return pr;
+               // Lookup name in patcher function list.
+               patcher_function_list_t* l;
+               for (l = patcher_function_list; l->patcher != NULL; l++)
+                       if (l->patcher == pr.patcher)
+                               break;
 
-               pr = (patchref_t*) list_next(code->patchers, pr);
+               // Display information about patcher.
+               printf("\tpatcher pc:"PRINTF_FORMAT_INTPTR_T, pr.mpc);
+               printf(" datap:"PRINTF_FORMAT_INTPTR_T, pr.datap);
+               printf(" ref:"PRINTF_FORMAT_INTPTR_T, (intptr_t) pr.ref);
+#if PATCHER_CALL_SIZE == 4
+               printf(" mcode:%08x", (uint32_t) pr.mcode);
+#elif PATCHER_CALL_SIZE == 2
+               printf(" mcode:%04x", (uint16_t) pr.mcode);
+#else
+# error Unknown PATCHER_CALL_SIZE
+#endif
+               printf(" type:%s\n", l->name);
+
+               // Display machine code of patched position.
+#if 0 && defined(ENABLE_DISASSEMBLER)
+               printf("\t\tcurrent -> ");
+               disassinstr((uint8_t*) pr.mpc);
+               printf("\t\tapplied -> ");
+               disassinstr((uint8_t*) &(pr.mcode));
+#endif
        }
-
-       return NULL;
 }
+#endif
 
 
 /* patcher_add_patch_ref *******************************************************
 
    Appends a new patcher reference to the list of patching positions.
 
+   Returns a pointer to the newly created patchref_t.
+
 *******************************************************************************/
 
-void patcher_add_patch_ref(jitdata *jd, functionptr patcher, void* ref, s4 disp)
+patchref_t *patcher_add_patch_ref(jitdata *jd, functionptr patcher, void* ref, s4 disp)
 {
-       codegendata *cd;
-       codeinfo    *code;
-       patchref_t  *pr;
-       s4           patchmpc;
+       codegendata *cd   = jd->cd;
+       codeinfo    *code = jd->code;
+
+#if defined(ALIGN_PATCHER_TRAP)
+       emit_patcher_alignment(cd);
+#endif
 
-       cd       = jd->cd;
-       code     = jd->code;
-       patchmpc = cd->mcodeptr - cd->mcodebase;
+       int32_t patchmpc = cd->mcodeptr - cd->mcodebase;
 
 #if !defined(NDEBUG)
        if (patcher_list_find(code, (void*) (intptr_t) patchmpc) != NULL)
-               vm_abort("patcher_add_patch_ref: different patchers at same position.");
+               os::abort("patcher_add_patch_ref: different patchers at same position.");
 #endif
 
-       /* allocate patchref on heap (at least freed together with codeinfo) */
+#if defined(USES_PATCHABLE_MEMORY_BARRIER)
+       PATCHER_NOPS;
+#endif
+
+       // Set patcher information (mpc is resolved later).
+       patchref_t pr;
 
-       pr = NEW(patchref_t);
-       list_add_first(code->patchers, pr);
+       pr.mpc     = patchmpc;
+       pr.datap   = 0;
+       pr.disp    = disp;
+       pr.disp_mb = 0;
+       pr.patcher = patcher;
+       pr.ref     = ref;
+       pr.mcode   = 0;
+       pr.done    = false;
+
+       // Store patcher in the list (NOTE: structure is copied).
+       code->patchers->push_back(pr);
 
 #if defined(ENABLE_STATISTICS)
        if (opt_stat)
                size_patchref += sizeof(patchref_t);
 #endif
 
-       /* set patcher information (mpc is resolved later) */
-
-       pr->mpc     = patchmpc;
-       pr->disp    = disp;
-       pr->patcher = patcher;
-       pr->ref     = ref;
-       pr->mcode   = 0;
-       pr->done    = false;
-
 #if defined(ENABLE_JIT) && (defined(__I386__) || defined(__M68K__) || defined(__SPARC_64__) || defined(__X86_64__))
 
        /* XXX We can remove that when we don't use UD2 anymore on i386
@@ -222,6 +268,8 @@ void patcher_add_patch_ref(jitdata *jd, functionptr patcher, void* ref, s4 disp)
 
        cd->lastmcodeptr = cd->mcodeptr + PATCHER_CALL_SIZE;
 #endif
+
+       return &code->patchers->back();
 }
 
 
@@ -232,16 +280,14 @@ void patcher_add_patch_ref(jitdata *jd, functionptr patcher, void* ref, s4 disp)
  */
 void patcher_resolve(jitdata* jd)
 {
-       codeinfo*   code;
-       patchref_t* pr;
+       // Get required compiler data.
+       codeinfo* code = jd->code;
 
-       /* Get required compiler data. */
+       for (List<patchref_t>::iterator it = code->patchers->begin(); it != code->patchers->end(); it++) {
+               patchref_t& pr = *it;
 
-       code = jd->code;
-
-       for (pr = (patchref_t*) list_first(code->patchers); pr != NULL; pr = (patchref_t*) list_next(code->patchers, pr)) {
-               pr->mpc   += (intptr_t) code->entrypoint;
-               pr->datap  = (intptr_t) (pr->disp + code->entrypoint);
+               pr.mpc   += (intptr_t) code->entrypoint;
+               pr.datap  = (intptr_t) (pr.disp + code->entrypoint);
        }
 }
 
@@ -260,7 +306,13 @@ bool patcher_is_patched(patchref_t* pr)
        // instruction as the patcher structure contains.
        uint32_t mcode = *((uint32_t*) pr->mpc);
 
+#if PATCHER_CALL_SIZE == 4
        if (mcode != pr->mcode) {
+#elif PATCHER_CALL_SIZE == 2
+       if ((uint16_t) mcode != (uint16_t) pr->mcode) {
+#else
+#error Unknown PATCHER_CALL_SIZE
+#endif
                // The code differs.
                return false;
        }
@@ -309,12 +361,11 @@ static int patcher_depth = 0;
 #define TRACE_PATCHER_INDENT for (i=0; i<patcher_depth; i++) printf("\t")
 #endif /* !defined(NDEBUG) */
 
-java_handle_t *patcher_handler(u1 *pc)
+bool patcher_handler(u1 *pc)
 {
        codeinfo      *code;
        patchref_t    *pr;
        bool           result;
-       java_handle_t *e;
 #if !defined(NDEBUG)
        patcher_function_list_t *l;
        int                      i;
@@ -329,16 +380,15 @@ java_handle_t *patcher_handler(u1 *pc)
        code = code_find_codeinfo_for_pc(pc);
        assert(code);
 
-       /* enter a monitor on the patcher list */
-
-       list_lock(code->patchers);
+       // Enter a mutex on the patcher list.
+       code->patchers->lock();
 
        /* search the patcher information for the given PC */
 
        pr = patcher_list_find(code, pc);
 
        if (pr == NULL)
-               vm_abort("patcher_handler: Unable to find patcher reference.");
+               os::abort("patcher_handler: Unable to find patcher reference.");
 
        if (pr->done) {
 #if !defined(NDEBUG)
@@ -346,8 +396,8 @@ java_handle_t *patcher_handler(u1 *pc)
                        log_println("patcher_handler: double-patching detected!");
                }
 #endif
-               list_unlock(code->patchers);
-               return NULL;
+               code->patchers->unlock();
+               return true;
        }
 
 #if !defined(NDEBUG)
@@ -401,21 +451,17 @@ java_handle_t *patcher_handler(u1 *pc)
        }
 #endif
 
-       /* check for return value and exit accordingly */
+       // Check return value and mangle the pending exception.
+       if (result == false)
+               resolve_handle_pending_exception(true);
 
-       if (result == false) {
-               e = exceptions_get_and_clear_exception();
+       // XXX This is only preliminary to prevent double-patching.
+       else
+               pr->done = true;
 
-               list_unlock(code->patchers);
-
-               return e;
-       }
+       code->patchers->unlock();
 
-       pr->done = true; /* XXX this is only preliminary to prevent double-patching */
-
-       list_unlock(code->patchers);
-
-       return NULL;
+       return result;
 }
 
 
@@ -489,7 +535,6 @@ bool patcher_resolve_native_function(patchref_t *pr)
 {
        methodinfo  *m;
        uint8_t     *datap;
-       functionptr  f;
 
        /* get stuff from the patcher reference */
 
@@ -498,12 +543,15 @@ bool patcher_resolve_native_function(patchref_t *pr)
 
        /* resolve native function */
 
-       if (!(f = native_method_resolve(m)))
+       NativeMethods& nm = VM::get_current()->get_nativemethods();
+       void* f = nm.resolve_method(m);
+
+       if (f == NULL)
                return false;
 
        /* patch native function pointer */
 
-       *((intptr_t *) datap) = (intptr_t) f;
+       *((intptr_t*) datap) = (intptr_t) f;
 
        /* synchronize data cache */
 
@@ -517,6 +565,31 @@ bool patcher_resolve_native_function(patchref_t *pr)
 }
 
 
+/**
+ * Deals with breakpoint instructions (ICMD_BREAKPOINT) compiled
+ * into a JIT method. This patcher might never patch back the
+ * original machine code because breakpoints are kept active.
+ */
+bool patcher_breakpoint(patchref_t *pr)
+{
+       // Get stuff from the patcher reference.
+       Breakpoint* breakp = (Breakpoint*) pr->ref;
+
+       // Hook point when a breakpoint was triggered.
+       Hook::breakpoint(breakp);
+
+       // In case the breakpoint wants to be kept active, we simply
+       // fail to "patch" at this point.
+       if (!breakp->is_oneshot)
+               return false;
+
+       // Patch back original code.
+       patcher_patch_code(pr);
+
+       return true;
+}
+
+
 /*
  * 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