* Removed all Id tags.
[cacao.git] / src / vm / jit / intrp / dynamic-super.c
index e826ecd212d3b56f2cc3880eb2cf151abdbaf4b9..923a9f510a7f2339d4a6dfe21e0f6811824ddae5 100644 (file)
@@ -3,10 +3,10 @@
    Copyright (C) 1995,1996,1997,1998,2000,2003,2004 Free Software Foundation, Inc.
    Taken from Gforth.
 
-   Copyright (C) 1996-2005 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
+   Copyright (C) 1996-2005, 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
 
    This file is part of CACAO.
 
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.
-
-   Contact: cacao@complang.tuwien.ac.at
-
-   Authors: Christian Thalinger
-            Anton Ertl
-
-   Changes:
-
-   $Id: dynamic-super.c 3895 2005-12-07 16:03:37Z anton $
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
 */
 
 
 #include <stdlib.h>
 #include <assert.h>
 
-#include "mm/memory.h"
-#include "vm/options.h"
 #include "vm/types.h"
+
+#include "mm/memory.h"
+
+#if defined(ENABLE_THREADS)
+# include "threads/native/lock.h"
+#else
+# include "threads/none/lock.h"
+#endif
+
+#include "toolbox/hashtable.h"
+#include "toolbox/logging.h"
+
+#include "vm/jit/disass.h"
 #include "vm/jit/intrp/intrp.h"
 
+#include "vmcore/options.h"
+
 
 s4 no_super=0;   /* option: just use replication, but no dynamic superinsts */
 
 static char MAYBE_UNUSED superend[]={
-#include "java-superend.i"
+#include <java-superend.i>
 };
 
 const char * const prim_names[]={
-#include "java-names.i"
+#include <java-names.i>
 };
 
 enum {
 #define INST_ADDR(_inst) N_##_inst
-#include "java-labels.i"
+#include <java-labels.i>
 #undef INST_ADDR
 };
 
@@ -87,12 +91,60 @@ Label before_goto;
 u4 goto_len;
 
 typedef struct superstart {
-  struct superstartlist *next;
+  struct superstart *next;
+  s4 patcherm;  /* offset of patcher, -1 if super has no patcher */
+  u4 length;    /* length of superinstruction */
+  u1 *oldsuper; /* reused superinstruction: NULL if there is none */
   s4 dynsuperm;
   s4 dynsupern;
+  u1 *mcodebase;
+  u1 *ncodebase;
 } superstart;
 
+static hashtable hashtable_patchersupers;
+#define HASHTABLE_PATCHERSUPERS_BITS 14
+
+#if defined(ENABLE_THREADS)
+static java_objectheader *lock_hashtable_patchersupers;
+#endif
+
+/* stuff for -no-replication */
+typedef struct superreuse {
+  struct superreuse *next;
+  u1 *code;
+  u4 length;
+} superreuse;
+
+static hashtable hashtable_superreuse;
+#define HASHTABLE_SUPERREUSE_BITS 14
+
+#if defined(ENABLE_THREADS)
+static java_objectheader *lock_hashtable_superreuse;
+#endif
+
 # define debugp(x...) if (opt_verbose) fprintf(x)
+#define debugp1(x...)
+
+/* statistics variables */
+
+u4 count_supers        = 0; /* dynamic superinstructions, including replicas */
+u4 count_supers_unique = 0; /* dynamic superinstructions, without replicas */
+u4 count_supers_reused = 0; /* reused dynamic superinstructions */
+u4 count_patchers_exec = 0; /* executed patchers */
+u4 count_patchers_last = 0; /* executed last patchers */
+u4 count_patchers_ins  = 0; /* patchers inserted in patchersupers table */
+u4 count_patchers      = 0; /* superstarts with patcherm!=-1 */
+u4 count_supers_nopatch= 0; /* superinstructions for code without patchers */
+u4 count_supers_patch  = 0; /* superinstructions for code with patchers */
+u4 count_dispatches    = 0; /* dynamic superinstructions generated */
+u4 count_disp_nonreloc = 0; /* */
+u4 count_insts_reloc   = 0; /* relocatable insts compiled (append_prim) */
+u4 count_insts         = 0; /* compiled insts (gen_inst) */
+u4 count_native_code   = 0; /* native code bytes */
+u4 count_native_saved  = 0; /* reclaimed native code */
+
+/* determine priminfos */
+
 
 java_objectheader *engine2(Inst *ip0, Cell * sp, Cell * fp);
 
@@ -152,7 +204,7 @@ static Label bsearch_next(Label key, Label *a, u4 n)
 
 #endif
 
-void check_prims(Label symbols1[])
+static void check_prims(Label symbols1[])
 {
   int i;
   Label *symbols2, *symbols3, *js1, *ks1, *ks1sorted;
@@ -189,7 +241,7 @@ void check_prims(Label symbols1[])
 
   /* check whether the "goto *" is relocatable */
   goto_len = after_goto-before_goto;
-  debugp(stderr, "goto * %p %p len=%ld\n",
+  debugp(stderr, "goto * %p %p len=%d\n",
         before_goto,before_goto2,goto_len);
   if (memcmp(before_goto, before_goto2, goto_len)!=0) { /* unequal */
     opt_no_dynamic = true;
@@ -260,22 +312,23 @@ void check_prims(Label symbols1[])
 #endif
 }
 
-static bool is_relocatable(u4 p)
+static bool is_relocatable(ptrint p)
 {
   return !opt_no_dynamic && priminfos[p].start != NULL;
 }
 
 static
-void append_prim(codegendata *cd, u4 p)
+void append_prim(codegendata *cd, ptrint p)
 {
   PrimInfo *pi = &priminfos[p];
-  debugp(stderr,"append_prim %p %s\n",cd->mcodeptr, prim_names[p]);
+  debugp1(stderr,"append_prim %p %s\n",cd->lastmcodeptr, prim_names[p]);
   if (cd->ncodeptr + pi->length + pi->restlength + goto_len >
       cd->ncodebase + cd->ncodesize) {
     cd->ncodeptr = codegen_ncode_increase(cd, cd->ncodeptr);
   }
   memcpy(cd->ncodeptr, pi->start, pi->length);
   cd->ncodeptr += pi->length;
+  count_insts_reloc++;
 }
 
 static void init_dynamic_super(codegendata *cd)
@@ -285,16 +338,137 @@ static void init_dynamic_super(codegendata *cd)
   cd->dynsupern = cd->ncodeptr - cd->ncodebase;
 }
 
+
+/******************* -no-replication stuff **********************/
+
+/* bugs: superinstructions are inserted into the table only after the
+   end of a method, so there may be replication within a method even
+   with -no-replication.  Moreover, there is a race condition that can
+   cause varying amounts of replication between different threads;
+   this could only be avoided by eliminating the bug above and having
+   more locking. */
+
+static u4 hash_superreuse(u1 *code, u4 length)
+     /* calculates a hash value for given code length */
+{
+  u4 r=0;
+  u4 i;
+
+  for (i=0; i<(length&(~3)); i+=sizeof(u4)) {
+    r += *(s4 *)(code+i); /* !! align each superinstruction */
+  }
+  return (r+(r>>HASHTABLE_SUPERREUSE_BITS))&((1<<HASHTABLE_SUPERREUSE_BITS)-1);
+}
+
+static void superreuse_insert(u1 *code, u4 length)
+{
+  u4 slot = hash_superreuse(code, length);
+  superreuse **listp = (superreuse **)&hashtable_superreuse.ptr[slot];
+  superreuse *sr = NEW(superreuse);
+  sr->code = code;
+  sr->length = length;
+
+  LOCK_MONITOR_ENTER(lock_hashtable_superreuse);
+
+  sr->next = *listp;
+  *listp = sr;
+
+  LOCK_MONITOR_EXIT(lock_hashtable_superreuse);
+
+  count_supers_unique++;
+}
+
+static u1 *superreuse_lookup(u1 *code, u4 length)
+     /* returns earlier instances code address, or NULL */
+{
+  u4 slot = hash_superreuse(code, length);
+  superreuse *sr = (superreuse *)hashtable_superreuse.ptr[slot];
+  /* since there is another race condition anyway, we don't bother
+     locking here; both race conditions don't affect the correctness */
+  for (; sr != NULL; sr = sr->next)
+    if (length == sr->length && memcmp(code, sr->code, length)==0)
+      return sr->code;
+  return NULL;
+}
+
+/******************* patcher stuff *******************************/
+
+void patchersuper_rewrite(Inst *p)
+     /* p is the address of a patcher; if this is the last patcher in
+        a dynamic superinstruction, rewrite the threaded code to use
+        the dynamic superinstruction; the data for that comes from
+        hashtable_patchersupers */
+{
+  ptrint key = (ptrint)p;
+  u4 slot = ((key + (key>>HASHTABLE_PATCHERSUPERS_BITS)) & 
+             ((1<<HASHTABLE_PATCHERSUPERS_BITS)-1));
+  superstart **listp = (superstart **)&hashtable_patchersupers.ptr[slot];
+  superstart *ss;
+  count_patchers_exec++;
+
+  LOCK_MONITOR_ENTER(lock_hashtable_patchersupers);
+
+  for (; ss=*listp,  ss!=NULL; listp = &(ss->next)) {
+    if (p == ((Inst *)(ss->mcodebase + ss->patcherm))) {
+      Inst target;
+      if (ss->oldsuper != NULL)
+        target = (Inst)ss->oldsuper;
+      else
+        target = (Inst)(ss->ncodebase + ss->dynsupern);
+      *(Inst *)(ss->mcodebase + ss->dynsuperm) = target;
+      debugp1(stderr, "patcher rewrote %p into %p\n", 
+             ss->mcodebase + ss->dynsuperm, target);
+      *listp = ss->next;
+      FREE(ss, superstart);
+      count_patchers_last++;
+      break;
+    }
+  }
+
+  LOCK_MONITOR_EXIT(lock_hashtable_patchersupers);
+}
+
+static void hashtable_patchersupers_insert(superstart *ss)
+{
+  ptrint key = (ptrint)(ss->mcodebase + ss->patcherm);
+  u4 slot = ((key + (key>>HASHTABLE_PATCHERSUPERS_BITS)) & 
+             ((1<<HASHTABLE_PATCHERSUPERS_BITS)-1));
+  void **listp = &hashtable_patchersupers.ptr[slot];
+
+  LOCK_MONITOR_ENTER(lock_hashtable_patchersupers);
+
+  ss->next = (superstart *)*listp;
+  *listp = (void *)ss;
+
+  LOCK_MONITOR_EXIT(lock_hashtable_patchersupers);
+
+  count_patchers_ins++;
+}
+
 void dynamic_super_rewrite(codegendata *cd)
      /* rewrite the threaded code in the superstarts list to point to
         the dynamic superinstructions */
 {
   superstart *ss = cd->superstarts;
-  for (; ss != NULL; ss = ss->next) {
-    *(Inst *)(cd->mcodebase + ss->dynsuperm) =
-      (Inst)(cd->ncodebase + ss->dynsupern);
-    debugp(stderr, "rewrote %p into %p\n", 
-            cd->mcodebase + ss->dynsuperm, cd->ncodebase + ss->dynsupern); 
+  superstart *ssnext;
+  for (; ss != NULL; ss = ssnext) {
+    ssnext = ss->next;
+    if (opt_no_replication && ss->oldsuper == NULL)
+      superreuse_insert(cd->ncodebase + ss->dynsupern, ss->length);
+    if (ss->patcherm == -1) {
+      assert(ss->oldsuper == NULL);
+      *(Inst *)(cd->mcodebase + ss->dynsuperm) =
+        (Inst)(cd->ncodebase + ss->dynsupern);
+      debugp1(stderr, "rewrote %p into %p\n", 
+             cd->mcodebase + ss->dynsuperm, cd->ncodebase + ss->dynsupern);
+      count_supers_nopatch++;
+    } else {
+      /* fprintf(stderr,"%p stage2\n", ss); */
+      ss->mcodebase = cd->mcodebase;
+      ss->ncodebase = cd->ncodebase;
+      hashtable_patchersupers_insert(ss);
+      count_supers_patch++;
+    }
   }
 }
 
@@ -304,19 +478,45 @@ static void new_dynamic_super(codegendata *cd)
         is no patcher), or when the last patcher is executed (if there
         is a patcher). */
 {
-  if (cd->lastpatcheroffset==-1) {
-    superstart *ss = DNEW(superstart);
-    ss->dynsuperm = cd->dynsuperm;
-    ss->dynsupern = cd->dynsupern;
-    ss->next = cd->superstarts;
-    cd->superstarts = ss;
+  superstart *ss;
+  u1 *oldsuper = NULL;
+  u1 *code = cd->ncodebase + cd->dynsupern;
+  u4 length = cd->ncodeptr - code;
+  count_supers++;
+  if (opt_no_replication) {
+    oldsuper = superreuse_lookup(code, length);
+    if (oldsuper != NULL) {
+      count_supers_reused++;
+      count_native_saved += length;
+      cd->ncodeptr = code;
+      if (cd->lastpatcheroffset == -1) {
+        *(Inst *)(cd->mcodebase + cd->dynsuperm) = (Inst)oldsuper;
+        debugp1(stderr, "rewrote %p into %p (reused)\n", 
+                cd->mcodebase + ss->dynsuperm, oldsuper);
+        return;
+      }
+    }
+  }
+  if (cd->lastpatcheroffset == -1) {
+    ss = DNEW(superstart);
   } else {
+    ss = NEW(superstart);
+    count_patchers++;
+    /* fprintf(stderr,"%p stage1\n", ss); */
   }
+  ss->patcherm = cd->lastpatcheroffset;
+  ss->oldsuper = oldsuper;
+  ss->length   = length;
+  ss->dynsuperm = cd->dynsuperm;
+  ss->dynsupern = cd->dynsupern;
+  ss->next = cd->superstarts;
+  cd->superstarts = ss;
+  count_native_code += cd->ncodeptr - code;
 }
 
 void append_dispatch(codegendata *cd)
 {
-  debugp(stderr,"append_dispatch %p\n",cd->mcodeptr);
+  debugp1(stderr,"append_dispatch %p\n",cd->lastmcodeptr);
   if (cd->lastinstwithoutdispatch != ~0) {
     PrimInfo *pi = &priminfos[cd->lastinstwithoutdispatch];
     
@@ -326,22 +526,25 @@ void append_dispatch(codegendata *cd)
     cd->ncodeptr += goto_len;
     cd->lastinstwithoutdispatch = ~0;
     new_dynamic_super(cd);
+    count_dispatches++;
   }
   init_dynamic_super(cd);
 }
 
-static void compile_prim_dyn(codegendata *cd, u4 p)
+static void compile_prim_dyn(codegendata *cd, ptrint p)
      /* compile prim #p dynamically (mod flags etc.)  */
 {
   if (opt_no_dynamic)
     return;
   assert(p<npriminfos);
   if (!is_relocatable(p)) {
+    if (cd->lastinstwithoutdispatch != ~0) /* only count real dispatches */
+      count_disp_nonreloc++;
     append_dispatch(cd); /* to previous dynamic superinstruction */
     return;
   }
   if (cd->dynsuperm == -1)
-    cd->dynsuperm = cd->mcodeptr - cd->mcodebase;
+    cd->dynsuperm = cd->lastmcodeptr - cd->mcodebase;
   append_prim(cd, p);
   cd->lastinstwithoutdispatch = p;
   if (priminfos[p].superend)
@@ -349,19 +552,23 @@ static void compile_prim_dyn(codegendata *cd, u4 p)
   return;
 }
 
-static void replace_patcher(codegendata *cd, u4 p)
+static void replace_patcher(codegendata *cd, ptrint p)
      /* compile p dynamically, and note that there is a patcher here */
 {
-  cd->lastpatcheroffset = cd->mcodeptr - cd->mcodebase;
-  compile_prim_dyn(cd, p);
+  if (opt_no_quicksuper) {
+    append_dispatch(cd);
+  } else {
+    cd->lastpatcheroffset = cd->lastmcodeptr - cd->mcodebase;
+    compile_prim_dyn(cd, p);
+  }
 }
 
-void gen_inst(codegendata *cd, u4 instr)
+void gen_inst1(codegendata *cd, ptrint instr)
 {
   /* actually generate the threaded code instruction */
 
-  debugp(stderr, "gen_inst %p, %s\n", cd->mcodeptr, prim_names[instr]);
-  *((Inst *) cd->mcodeptr) = vm_prim[instr];
+  debugp1(stderr, "gen_inst %p, %s\n", cd->lastmcodeptr, prim_names[instr]);
+  *((Inst *) cd->lastmcodeptr) = vm_prim[instr];
   switch (instr) {
   case N_PATCHER_ACONST:         replace_patcher(cd, N_ACONST1); break;
   case N_PATCHER_ARRAYCHECKCAST: replace_patcher(cd, N_ARRAYCHECKCAST); break;
@@ -385,7 +592,7 @@ void gen_inst(codegendata *cd, u4 instr)
   case N_PATCHER_CHECKCAST:      replace_patcher(cd, N_CHECKCAST); break;
   case N_PATCHER_INSTANCEOF:     replace_patcher(cd, N_INSTANCEOF); break;
   case N_PATCHER_NATIVECALL:     
-    if (runverbose)
+    if (opt_verbosecall)
       replace_patcher(cd, N_TRACENATIVECALL);
     else
       replace_patcher(cd, N_NATIVECALL);
@@ -396,6 +603,115 @@ void gen_inst(codegendata *cd, u4 instr)
     compile_prim_dyn(cd, instr);
     break;
   }
-  cd->mcodeptr += sizeof(Inst); /* cd->mcodeptr used in compile_prim_dyn() */
+  count_insts++;
+}
+
+void finish_ss(codegendata *cd)
+     /* conclude the last static super and compile it dynamically */
+{
+#if 1
+  if (cd->lastmcodeptr != NULL) {
+    gen_inst1(cd, *(ptrint *)(cd->lastmcodeptr));
+    cd->lastmcodeptr = NULL;
+  }
+#endif
+}
+
+#if 0
+void gen_inst(codegendata *cd, ptrint instr)
+{
+  cd->lastmcodeptr = cd->mcodeptr;
+  gen_inst1(cd, instr);
+  cd->mcodeptr += sizeof(Inst);
+  cd->lastmcodeptr = NULL;
+}
+#else
+void gen_inst(codegendata *cd, ptrint instr)
+{
+  /* vmgen-0.6.2 generates gen_... calls with Inst ** as first
+     parameter, but we need to pass in cd to make lastmcodeptr
+     thread-safe */
+  ptrint *lastmcodeptr = (ptrint *)cd->lastmcodeptr;
+  
+  if (lastmcodeptr != NULL) {
+    ptrint combo;
+
+    assert((u1 *) lastmcodeptr < cd->mcodeptr &&
+           cd->mcodeptr < (u1 *) (lastmcodeptr + 40));
+
+    combo = peephole_opt(*lastmcodeptr, instr, peeptable);
+
+    if (combo != -1) {
+      *lastmcodeptr = combo;
+      return;
+    }
+    finish_ss(cd);
+  }
+
+  /* actually generate the threaded code instruction */
+
+  *((Inst *) cd->mcodeptr) = (Inst) instr;
+
+  cd->lastmcodeptr = cd->mcodeptr;
+  cd->mcodeptr += sizeof(Inst);
+}
+#endif
+
+void print_dynamic_super_statistics(void)
+{
+  dolog("count_supers        = %d", count_supers        );
+  dolog("count_supers_unique = %d", count_supers_unique );
+  dolog("count_supers_reused = %d", count_supers_reused );
+  dolog("count_patchers_exec = %d", count_patchers_exec );
+  dolog("count_patchers_last = %d", count_patchers_last );
+  dolog("count_patchers_ins  = %d", count_patchers_ins  );
+  dolog("count_patchers      = %d", count_patchers      );
+  dolog("count_supers_nopatch= %d", count_supers_nopatch);
+  dolog("count_supers_patch  = %d", count_supers_patch  );
+  dolog("count_dispatches    = %d", count_dispatches    );
+  dolog("count_disp_nonreloc = %d", count_disp_nonreloc );
+  dolog("count_insts_reloc   = %d", count_insts_reloc   );
+  dolog("count_insts         = %d", count_insts         );
+  dolog("count_native_code   = %d", count_native_code   );
+  dolog("count_native_saved  = %d", count_native_saved  );
+}
+
+#if defined(ENABLE_DISASSEMBLER)
+void disassemble_prim(int n)
+{
+  PrimInfo *p = &(priminfos[n]);
+  u1 *start = vm_prim[n];
+
+#if defined(ENABLE_JIT)
+  printf("%s (len=%d, restlen=%d):\n",prim_names[n],p->length, p->restlength);
+  disassemble(start, start + p->length + p->restlength);
+#endif
 }
+#endif /* defined(ENABLE_DISASSEMBLER) */
 
+void dynamic_super_init(void)
+{
+  check_prims(vm_prim);
+
+#if defined(ENABLE_DISASSEMBLER)
+  if (opt_verbose) {
+    disassemble_prim(N_IADD);
+    disassemble_prim(N_ILOAD);
+    disassemble_prim(N_GETFIELD_INT);
+  }
+#endif
+
+  hashtable_create(&hashtable_patchersupers, 1<<HASHTABLE_PATCHERSUPERS_BITS);
+  if (opt_no_replication)
+    hashtable_create(&hashtable_superreuse,  1<<HASHTABLE_SUPERREUSE_BITS);
+
+#if defined(ENABLE_THREADS)
+  /* create patchersupers hashtable lock object */
+
+  lock_hashtable_patchersupers = NEW(java_objectheader);
+  lock_hashtable_superreuse  = NEW(java_objectheader);
+
+  lock_init_object_lock(lock_hashtable_patchersupers);
+  lock_init_object_lock(lock_hashtable_superreuse);
+#endif
+}