* configure.ac: Define ENABLE_ESCAPE if ENABLE_SSA is defined.
authorPeter Molnar <pm@complang.tuwien.ac.at>
Sun, 1 Jun 2008 11:52:18 +0000 (13:52 +0200)
committerPeter Molnar <pm@complang.tuwien.ac.at>
Sun, 1 Jun 2008 11:52:18 +0000 (13:52 +0200)
* src/vm/global.h (ACC_METHOD_EA): new symbol.
* src/vm/jit/cfg.c (cfg_add_root): correctly initialize method member of basicblock.
* src/vm/jit/jit.c (basicblock) [ENABLE_SSA]: new members phis and phicount.
* src/vm/jit/jit.h (jit_compile_intern): Transform exception handlers only if -lsra is given.
* src/vm/jit/optimizing/Makefile.am: Adpated.
* src/vm/jit/optimizing/bytecode_escape.c: New file. Implementation of escape analysis on bytecode.
* src/vm/jit/optimizing/escape.c: Changed a lot.
* src/vm/jit/optimizing/ssa3.c: Changed a lot.
* src/vmcore/method.h (methodinfo) [ENABLE_ESCAPE]: New member paramescape.

configure.ac
src/vm/global.h
src/vm/jit/cfg.c
src/vm/jit/jit.c
src/vm/jit/jit.h
src/vm/jit/optimizing/Makefile.am
src/vm/jit/optimizing/bytecode_escape.c [new file with mode: 0644]
src/vm/jit/optimizing/escape.c [new file with mode: 0644]
src/vm/jit/optimizing/ssa3.c
src/vmcore/method.h

index a1febed28ac4937bb4c4e152a6abc5544bab7336..642f98bb2a7c772f571f4e9eb2b5f477e219b5a0 100644 (file)
@@ -666,9 +666,11 @@ AC_ARG_ENABLE([ssa],
               [ENABLE_SSA=no])
 AC_MSG_RESULT(${ENABLE_SSA})
 AM_CONDITIONAL([ENABLE_SSA], test x"${ENABLE_SSA}" = "xyes")
+AM_CONDITIONAL([ENABLE_ESCAPE], test x"${ENABLE_SSA}" = "xyes")
 
 if test x"${ENABLE_SSA}" = "xyes"; then
     AC_DEFINE([ENABLE_SSA], 1, [enable lsra with ssa])
+    AC_DEFINE([ENABLE_ESCAPE], 1, [enable escape analysis with ssa])
     ENABLE_LSRA="no"
 fi
 
@@ -695,7 +697,6 @@ if test x"${ENABLE_LSRA}" = "xyes"; then
     AC_DEFINE([ENABLE_LSRA], 1, [enable lsra])
 fi
 
-
 dnl check if profiling should be supported
 AC_MSG_CHECKING(whether profiling should be supported)
 AC_ARG_ENABLE([profiling],
index c0fe462a21d6ecd52c6f96c0b7ec65e1d86e1ded..aa4d400ea9ae512fac1d78066a9cc2b66f9d83d1 100644 (file)
@@ -210,6 +210,7 @@ typedef struct java_objectarray_t java_objectarray_t;
 #define ACC_METHOD_BUILTIN     0x00010000     /* use for descriptor parsing   */
 #define ACC_METHOD_IMPLEMENTED 0x00020000     /* there is an implementation   */
 #define ACC_METHOD_MONOMORPHIC 0x00040000     /* currently monomorphic method */
+#define ACC_METHOD_EA          0x00080000     /* method being escape analyzed */
 
 
 /* data structures of the runtime system **************************************/
index ab74f93191916df27626a28c60fec8cb74cd87c4..652e200970461fc6d6531c460f30a62cd6935182 100644 (file)
@@ -483,6 +483,7 @@ void cfg_add_root(jitdata *jd) {
        root->next = zero;
        root->nr = 0;
        root->type = BBTYPE_STD;
+       root->method = jd->m;
 
        if (zero->predecessorcount == 0) {
                zero->predecessors = DNEW(basicblock *);
index eaf8672745da0ed93dbe229dee54e41bec022c4c..e6f17c2d83f5266996607b181bd4b0c5b4682654 100644 (file)
@@ -701,7 +701,9 @@ static u1 *jit_compile_intern(jitdata *jd)
                RT_TIMING_GET_TIME(time_typecheck);
 
 #if defined(ENABLE_SSA)
-               fix_exception_handlers(jd);
+               if (opt_lsra) {
+                       fix_exception_handlers(jd);
+               }
 #endif
 
                /* Build the CFG.  This has to be done after stack_analyse, as
index a09c01e122e2b8a2aa703422ce3be833f8623594..448f0cc539d6206c4c5493caef05f0586a6e3f32 100644 (file)
@@ -529,6 +529,9 @@ struct basicblock {
        s4            expredecessorcount;
        s4            exouts;       /* Number of exceptional exits */
 
+       instruction  *phis;         /* Phi functions */
+       s4            phicount;     /* Number of phi functions */
+
        void         *vp;           /* Freely used by different passes            */
 #endif
 };
index b0e3fba34acdc6158e806733aae1ae5ce5fb585e..b7c937c13cec6cf9c5c3476d9433de7330937387 100644 (file)
@@ -66,7 +66,14 @@ SSA_SOURCES = \
        lifetimes.c \
        lifetimes.h \
        ssa2.c \
-       ssa3.c
+       ssa3.c 
+endif
+
+if ENABLE_ESCAPE
+ESCAPE_SOURCES = \
+       bytecode_escape.c \
+       escape.h \
+       escape.c
 endif
 
 noinst_LTLIBRARIES = \
@@ -77,7 +84,8 @@ liboptimizing_la_SOURCES = \
        $(PROFILE_SOURCES) \
        $(RECOMPILE_SOURCES) \
        $(REORDER_SOURCES) \
-       $(SSA_SOURCES)
+       $(SSA_SOURCES) \
+       $(ESCAPE_SOURCES)
 
 
 ## Local variables:
diff --git a/src/vm/jit/optimizing/bytecode_escape.c b/src/vm/jit/optimizing/bytecode_escape.c
new file mode 100644 (file)
index 0000000..4deae2c
--- /dev/null
@@ -0,0 +1,1737 @@
+#include "mm/dumpmemory.h"
+#include "mm/memory.h"
+
+#include "toolbox/bitvector.h"
+
+#include "vm/global.h"
+#include "vm/jit/ir/bytecode.h"
+#include "vm/jit/optimizing/escape.h"
+#include "vm/resolve.h"
+
+#include "vmcore/class.h"
+#include "vmcore/descriptor.h"
+#include "vmcore/references.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+#define BC_ESCAPE_VERBOSE !defined(NDEBUG)
+
+/*** dprintf *****************************************************************/
+
+#if BC_ESCAPE_VERBOSE && 0
+void dprintf(int depth, const char *fmt, ...) {
+       va_list ap;
+
+       while (depth-- > 0) {
+               printf("    ");
+       }
+       printf("| ");
+
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       va_end(ap);
+}
+#endif
+
+#define dprintf(x, ...) printf(__VA_ARGS__)
+
+/*** op_stack_slot  **********************************************************/
+
+typedef enum {
+       OP_STACK_SLOT_TYPE_PARAM = 0,
+       OP_STACK_SLOT_TYPE_UNKNOWN = 1
+} op_stack_slot_type_t;
+
+typedef struct {
+       unsigned type:1;
+       unsigned index:31;
+} op_stack_slot_t;
+
+const op_stack_slot_t OP_STACK_SLOT_UNKNOWN = { 
+       OP_STACK_SLOT_TYPE_UNKNOWN,
+       0
+};
+
+static inline op_stack_slot_t op_stack_slot_create_param(s4 index) {
+       op_stack_slot_t res;
+       res.type = OP_STACK_SLOT_TYPE_PARAM;
+       res.index = index;
+       return res;
+}
+
+static inline bool op_stack_slot_is_unknown(const op_stack_slot_t slot) {
+       return slot.type == OP_STACK_SLOT_TYPE_UNKNOWN;
+}
+
+static inline bool op_stack_slot_is_param(const op_stack_slot_t slot) {
+       return slot.type == OP_STACK_SLOT_TYPE_PARAM;
+}
+
+/*** op_stack *****************************************************************/
+
+/*
+
++---+---+---+---+  push 1
+        ^
+
++---+---+-1-+---+  push 2
+            ^
+
++---+---+-1-+-2-+  pop 2
+                ^
+
++---+---+-1-+---+  pop 1
+            ^        
+
++---+---+---+---+  
+        ^
+*/
+
+typedef struct {
+       op_stack_slot_t *elements;
+       op_stack_slot_t *start;
+       op_stack_slot_t *end;
+       op_stack_slot_t *ptr;
+       op_stack_slot_t *bottom;
+       unsigned max;
+} op_stack_t;
+
+#define stack_assert_position(stack, pos) \
+       do { \
+               assert((stack)->elements <= (pos)); \
+               assert((pos) < (stack)->end); \
+       } while (0)
+
+static void op_stack_init(op_stack_t *stack, unsigned max) {
+       op_stack_slot_t *it;
+
+       stack->elements = DMNEW(op_stack_slot_t, max * 2);
+       stack->max = max;
+       stack->start = stack->elements + max;
+       stack->end = stack->elements + max + max;
+
+       for (it = stack->elements; it != stack->start; ++it) {
+               *it = OP_STACK_SLOT_UNKNOWN;
+       }
+
+       stack->ptr = stack->start;
+       stack->bottom = stack->start;
+}
+
+static void op_stack_reset(op_stack_t *stack) {
+       op_stack_slot_t *it;
+
+       /* Clear bottom half. */
+
+       for (it = stack->bottom; it != stack->elements + stack->max; ++it) {
+               *it = OP_STACK_SLOT_UNKNOWN;
+       }
+
+       /* Reset pointers. */
+
+       stack->ptr = stack->start;
+       stack->bottom = stack->start;
+}
+
+static op_stack_slot_t op_stack_pop(op_stack_t *stack) {
+       op_stack_slot_t ret;
+       stack->ptr -= 1;
+       stack_assert_position(stack, stack->ptr);
+       ret = *(stack->ptr);
+       if (stack->ptr < stack->bottom) {
+               stack->bottom = stack->ptr;
+       }
+       return ret;
+}
+
+static void op_stack_push(op_stack_t *stack, op_stack_slot_t element) {
+       stack_assert_position(stack, stack->ptr);
+       *(stack->ptr) = element;
+       stack->ptr += 1;
+}
+
+static op_stack_slot_t op_stack_get(const op_stack_t *stack, int offset) {
+       stack_assert_position(stack, stack->ptr - offset);
+       return *(stack->ptr - offset);
+}
+
+static void op_stack_set(op_stack_t *stack, int offset, op_stack_slot_t value) {
+       stack_assert_position(stack, stack->ptr - offset);
+       *(stack->ptr - offset) = value;
+}
+
+static inline void op_stack_push_unknown(op_stack_t *stack) {
+       op_stack_push(stack, OP_STACK_SLOT_UNKNOWN);
+}
+
+static void op_stack_print(const op_stack_t *stack, FILE *f) {
+       op_stack_slot_t *it;
+       char sep;
+       
+       for (it = stack->bottom; it < stack->ptr; ++it) {
+               if (it == stack->start) {
+                       sep = '!';
+               } else {
+                       sep = '|';
+               }
+               if (op_stack_slot_is_unknown(*it)) {
+                       fprintf(f, "%c----", sep);
+               } else {
+                       fprintf(f, "%cP%3d", sep, it->index);
+               }
+       }
+
+       fprintf(f, "|");
+}
+
+static bool op_stack_is_empty(const op_stack_t *stack) {
+       return !(stack->bottom < stack->ptr);
+}
+
+static s4 op_stack_element_count(const op_stack_t *stack) {
+       return (stack->ptr - stack->bottom);
+}
+
+/*** bit_vector **************************************************************/
+
+typedef struct {
+       bitvector bv;
+       s4 size;
+} bit_vector_t;
+
+static void bit_vector_init(bit_vector_t *bv, s4 size) {
+       bv->bv = bv_new(size);
+       bv->size = size;
+}
+
+static s4 bit_vector_size(const bit_vector_t *bv) {
+       return bv->size;
+}
+
+static void bit_vector_set(bit_vector_t *bv, s4 bit) {
+       assert(0 <= bit && bit < bv->size);
+       bv_set_bit(bv->bv, bit);
+}
+
+static bool bit_vector_get(const bit_vector_t *bv, s4 bit) {
+       assert(0 <= bit && bit < bv->size);
+       return bv_get_bit(bv->bv, bit);
+}
+
+/*** basicblock_work_list ***********************************************/
+
+typedef struct basicblock_work_item {
+       s4 bytecode_index;
+       struct basicblock_work_item *next;
+} basicblock_work_item_t;
+
+typedef struct {
+       basicblock_work_item_t *first;
+       basicblock_work_item_t *last;
+} basicblock_work_list_t;
+
+void basicblock_work_list_init(basicblock_work_list_t *lst) {
+       lst->first = NULL;
+       lst->last = NULL;
+}
+
+#define FOR_EACH_BASICBLOCK_WORK_LIST(lst, it) \
+       for ((it) = (lst)->first; (it); (it) = (it)->next)
+
+void basicblock_work_list_insert(basicblock_work_list_t *lst, s4 bytecode_index) {
+       basicblock_work_item_t *it, *item;
+
+       /* If the destination is already present in the list, do nothing. */
+
+       FOR_EACH_BASICBLOCK_WORK_LIST(lst, it) {
+               if (it->bytecode_index == bytecode_index) {
+                       return;
+               }
+       }
+
+       item = DNEW(basicblock_work_item_t);
+       item->bytecode_index = bytecode_index;
+       item->next = NULL;
+
+       if (lst->first == NULL) {
+               lst->first = item;
+               lst->last = item;
+       } else {
+               lst->last->next = item;
+               lst->last = item;
+       }
+}
+
+/*** value_category *****************************************************/
+
+typedef enum {
+       VALUE_CATEGORY_1,
+       VALUE_CATEGORY_2
+} value_category_t;
+
+/*** jcode **************************************************************/
+
+typedef struct {
+       u1 *start;
+       u1 *end;
+       u1 *pos;
+       u1 *instruction_start;
+       s4 offset;
+} jcode_t;
+
+static void jcode_init(jcode_t *jc, u1 *start, s4 length, s4 offset) {
+       jc->start = start;
+       jc->end = jc->start + length;
+       jc->pos = jc->start;
+       jc->offset = offset;
+}
+
+static void jcode_move_to_index(jcode_t *jc, s4 index) {
+       jc->pos = jc->start + (index - jc->offset);
+}
+
+static bool jcode_end(const jcode_t *jc) {
+       return (jc->pos >= jc->end);
+}
+
+static void jcode_record_instruction_start(jcode_t *jc) {
+       jc->instruction_start = jc->pos;
+}
+
+static void jcode_rewind_instruction(jcode_t *jc) {
+       jc->pos = jc->instruction_start;
+}
+
+static s4 jcode_get_instruction_length(const jcode_t *jc) {
+       return (jc->pos - jc->instruction_start);
+}
+
+static void jcode_align_bytecode_index(jcode_t *jc, s4 align) {
+       s4 idx, aidx;
+
+       idx = jc->offset + (jc->pos - jc->start);
+       aidx = MEMORY_ALIGN(idx, align);
+
+       jc->pos += (aidx - idx);
+}
+
+static void jcode_forward_instruction_relative(jcode_t *jc, s4 n) {
+       jc->pos = jc->instruction_start + n;
+}
+
+static s4 jcode_get_index(const jcode_t *jc) {
+       return jc->offset + (jc->pos - jc->start);
+}
+
+#define jcode_assert_has_bytes(jc, n) \
+       assert((jc->pos + n) <= jc->end)
+
+static u1 jcode_get_u1(jcode_t *jc) {
+       u1 ret;
+       jcode_assert_has_bytes(jc, 1);
+       ret = jc->pos[0];
+       jc->pos += 1;
+       return ret;
+}
+
+static s2 jcode_get_s2(jcode_t *jc) {
+       s2 ret;
+       jcode_assert_has_bytes(jc, 2);
+       ret = (jc->pos[0] << 8) | (jc->pos[1]);
+       jc->pos += 2;
+       return ret;
+}
+
+static u2 jcode_get_u2(jcode_t *jc) {
+       u2 ret;
+       jcode_assert_has_bytes(jc, 2);
+       ret = (jc->pos[0] << 8) | (jc->pos[1]);
+       jc->pos += 2;
+       return ret;
+}
+
+static s4 jcode_get_s4(jcode_t *jc) {
+       s4 ret;
+       jcode_assert_has_bytes(jc, 4);
+       ret = (jc->pos[0] << 24) | (jc->pos[1] << 16) | (jc->pos[2] << 8) | (jc->pos[3]);
+       jc->pos += 4;
+       return ret;
+}
+
+static s4 jcode_get_branch_target(jcode_t *jc) {
+       s2 off = jcode_get_s2(jc);
+       return jc->offset + (jc->instruction_start - jc->start) + off;
+}
+
+static s4 jcode_get_branch_target_wide(jcode_t *jc) {
+       s4 off = jcode_get_s4(jc);
+       return jc->offset + (jc->instruction_start - jc->start) + off;
+}
+
+static s4 jcode_get_fall_through_target(jcode_t *jc) {
+       int length = bytecode[*jc->instruction_start].length;
+       assert(length > 0);
+       return jc->offset + (jc->instruction_start - jc->start) + length;
+}
+
+/*** bc_escape_analysis *************************************************/
+
+typedef struct {
+       methodinfo *method;
+       op_stack_t *stack;
+       basicblock_work_list_t *basicblocks;
+
+       op_stack_slot_t *local_to_adr_param;
+       s4 local_to_adr_param_size;
+
+       u1 *param_escape;
+       s4 param_escape_size;
+
+       bit_vector_t *adr_param_dirty;
+       bit_vector_t *adr_param_returned;
+
+       s4 non_escaping_adr_params;
+
+#if BC_ESCAPE_VERBOSE
+       bool verbose;
+#endif
+       int depth;
+} bc_escape_analysis_t;
+
+static void bc_escape_analysis_perform_intern(methodinfo *m, int depth);
+
+static void bc_escape_analysis_init(bc_escape_analysis_t *be, methodinfo *m, bool verbose, int depth) {
+       u2 p;
+       int l;
+       int a;
+       u1 *ite;
+       u1 t;
+       int ret_adr;
+       unsigned n;
+
+       be->method = m;
+
+       be->stack = DNEW(op_stack_t);
+       op_stack_init(be->stack, m->maxstack);
+
+       be->basicblocks = DNEW(basicblock_work_list_t);
+       basicblock_work_list_init(be->basicblocks);
+
+       be->local_to_adr_param_size = m->parseddesc->paramslots;
+       be->local_to_adr_param = DMNEW(op_stack_slot_t, m->parseddesc->paramslots);
+
+       /* Count number of address parameters a. */
+
+       for (p = 0, l = 0, a = 0; p < m->parseddesc->paramcount; ++p) {
+               t = m->parseddesc->paramtypes[p].type;
+               if (t == TYPE_ADR) {
+                       be->local_to_adr_param[l] = op_stack_slot_create_param(a);
+                       a += 1;
+                       l += 1;
+               } else if (IS_2_WORD_TYPE(t)) {
+                       be->local_to_adr_param[l] = OP_STACK_SLOT_UNKNOWN;
+                       be->local_to_adr_param[l + 1] = OP_STACK_SLOT_UNKNOWN;
+                       l += 2;
+               } else {
+                       be->local_to_adr_param[l] = OP_STACK_SLOT_UNKNOWN;
+                       l += 1;
+               }
+       }
+
+       assert(l == be->local_to_adr_param_size);
+
+       /* Determine whether return type is address */
+
+       ret_adr = m->parseddesc->returntype.type == TYPE_ADR ? 1 : 0;
+
+       /* Allocate param_escape on heap. */
+
+       be->param_escape_size = a;
+       n = a + ret_adr;
+
+       if (n == 0) {
+               /* Use some non-NULL value. */
+               be->param_escape = (u1 *)1;
+       } else {
+               be->param_escape = MNEW(u1, n);
+       }
+
+       for (ite = be->param_escape; ite != be->param_escape + n; ++ite) {
+               *ite = (u1)ESCAPE_NONE;
+       }
+
+       be->param_escape += ret_adr;
+
+       be->adr_param_dirty = DNEW(bit_vector_t);
+       bit_vector_init(be->adr_param_dirty, a);
+
+       be->adr_param_returned= DNEW(bit_vector_t);
+       bit_vector_init(be->adr_param_returned, a);
+
+       be->non_escaping_adr_params = be->param_escape_size;
+
+#if BC_ESCAPE_VERBOSE
+       be->verbose = verbose;
+#endif
+
+       be->depth = depth;
+}
+
+static void bc_escape_analysis_branch_target(bc_escape_analysis_t *be, s4 branch_target) {
+       basicblock_work_list_insert(be->basicblocks, branch_target);
+}
+
+static void bc_escape_analysis_adjust_state(
+       bc_escape_analysis_t *be, 
+       op_stack_slot_t adr_param,
+       escape_state_t escape_state
+) {
+       escape_state_t old;
+
+       if (op_stack_slot_is_param(adr_param)) {
+               if (0 <= adr_param.index && adr_param.index < be->param_escape_size) {
+                       old = (escape_state_t)be->param_escape[adr_param.index];
+                       if (old < escape_state) {
+
+                               /* Adjust escape state. */
+                               be->param_escape[adr_param.index] = (u1)escape_state;
+
+                               /* If the parameter has just escaped, decrement number of non-escaping
+                                  parameters. */
+
+                               if (
+                                       old < ESCAPE_GLOBAL_THROUGH_METHOD && 
+                                       escape_state >= ESCAPE_GLOBAL_THROUGH_METHOD
+                               ) {
+                                       be->non_escaping_adr_params -= 1;
+                               }
+                       }
+               }
+       }
+}
+
+static void bc_escape_analysis_dirty(bc_escape_analysis_t *be, s4 local) {
+       op_stack_slot_t adr_param;
+
+       if (0 <= local && local < be->local_to_adr_param_size) {
+               adr_param = be->local_to_adr_param[local];
+               if (op_stack_slot_is_param(adr_param)) {
+                       bit_vector_set(be->adr_param_dirty, adr_param.index);
+               }
+       }
+}
+
+static void bc_escape_analysis_dirty_2(bc_escape_analysis_t *be, s4 local) {
+       bc_escape_analysis_dirty(be, local);
+       bc_escape_analysis_dirty(be, local + 1);
+}
+
+static void bc_escape_analyisis_returned(bc_escape_analysis_t *be, op_stack_slot_t value) {
+       if (op_stack_slot_is_param(value)) {
+               /* A parameter is returned, mark it as being returned. */
+               bit_vector_set(be->adr_param_returned, value.index);
+       } else if (op_stack_slot_is_unknown(value)) {
+               /* An untracked value is returned.
+                  Conservatively asume a globally escaping value is returned. */
+               if (be->method->parseddesc->returntype.type == TYPE_ADR) {
+                       be->param_escape[-1] = (u1)ESCAPE_GLOBAL;
+               }
+       }
+}
+
+op_stack_slot_t bc_escape_analysis_address_local(bc_escape_analysis_t *be, s4 local) {
+       if (0 <= local && local < be->local_to_adr_param_size) {
+               return be->local_to_adr_param[local];
+       } else {
+               return OP_STACK_SLOT_UNKNOWN;
+       }
+}
+
+value_category_t bc_escape_analysis_value_category(bc_escape_analysis_t *be, s4 index) {
+       constant_FMIref *fmi;
+       fmi = class_getconstant(be->method->clazz, index, CONSTANT_Fieldref);
+
+       if (fmi == NULL) {
+               /* TODO */
+               assert(0);
+       }
+
+       switch (fmi->parseddesc.fd->type) {
+               case TYPE_LNG:
+               case TYPE_DBL:
+                       return VALUE_CATEGORY_2;
+               default:
+                       return VALUE_CATEGORY_1;
+       }
+}
+
+static void bc_escape_analysis_adjust_invoke_parameters(
+       bc_escape_analysis_t *be,
+       methodinfo *mi
+) {
+       int i;
+       methoddesc *md = mi->parseddesc;
+       u1 *paramescape = mi->paramescape;
+       s4 stack_depth = md->paramslots;
+
+       /* Process parameters. 
+        * The first parameter is at the highest depth on the stack.
+        */
+
+       for (i = 0; i < md->paramcount; ++i) {
+               switch (md->paramtypes[i].type) {
+                       case TYPE_ADR:
+                               bc_escape_analysis_adjust_state(
+                                       be,
+                                       op_stack_get(be->stack, stack_depth),
+                                       (escape_state_t)*(paramescape++)
+                               );
+                               stack_depth -= 1;
+                               break;
+                       case TYPE_LNG:
+                       case TYPE_DBL:
+                               stack_depth -= 2;
+                               break;
+                       default:
+                               stack_depth -= 1;
+                               break;
+               }
+       }
+
+       /* Pop all parameters. */
+
+       for (i = 0; i < md->paramslots; ++i) {
+               op_stack_pop(be->stack);
+       }
+
+}
+
+static void bc_escape_analysis_escape_invoke_parameters(
+       bc_escape_analysis_t *be,
+       methoddesc *md
+) {
+       s4 i;
+       for (i = 0; i < md->paramslots; ++i) {
+               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+       }
+}
+
+static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *jc) {
+       constant_FMIref *fmi;
+       methoddesc *md;
+       u1 opc;
+       u2 cp_index;
+       s2 i;
+       resolve_result_t result;
+       methodinfo *mi;
+
+       opc = jcode_get_u1(jc);
+       cp_index = jcode_get_u2(jc);
+
+       /* Get method reference */
+
+       if (opc == BC_invokeinterface) {
+               fmi = class_getconstant(be->method->clazz, cp_index, CONSTANT_InterfaceMethodref);
+       } else {
+               fmi = class_getconstant(be->method->clazz, cp_index, CONSTANT_Methodref);
+       }
+
+       if (fmi == NULL) {
+               /* TODO */
+               assert(0);
+       }
+
+       md = fmi->parseddesc.md;
+
+       assert(md != NULL);
+
+       /* Parse parameters if not done yet. */
+
+       if (md->params == NULL) {
+               if (! descriptor_params_from_paramtypes(md, opc == BC_invokestatic ? ACC_STATIC : 0)) {
+                       /* TODO */
+                       assert(0);
+               }
+       }
+
+       /* Try to lazyly resolve method. */
+
+       result = resolve_method_lazy(be->method, fmi, opc == BC_invokespecial);
+
+       if (result == resolveSucceeded) {
+               mi = fmi->p.method;
+
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       dprintf(
+                               be->depth,
+                               "Succefully resolved callee %s/%s. Recursing.\n",
+                               mi->clazz->name->text,
+                               mi->name->text
+                       );
+               }
+#endif
+       } else {
+               mi = NULL;
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       dprintf(
+                               be->depth,
+                               "Failed to resolve callee %s/%s.\n", 
+                               (IS_FMIREF_RESOLVED(fmi) ? "ERR" : fmi->p.classref->name->text),
+                               fmi->name->text
+                       );
+               }
+#endif
+       }
+
+       /* If we could resolve the method, either reuse available escape inormation
+          or recurse into callee. 
+          Otherwise we must assume, that all parameters escape. */
+
+       if (mi != NULL) {
+
+               if (mi->paramescape == NULL) {
+                       bc_escape_analysis_perform_intern(mi, be->depth + 1);
+               }
+
+               if (mi->paramescape == NULL) {
+                       /* No escape information. */
+                       bc_escape_analysis_escape_invoke_parameters(be, md);
+               } else {
+                       /* Adjust escape state of parameters. */
+                       bc_escape_analysis_adjust_invoke_parameters(be, mi);
+               }
+
+       } else {
+               bc_escape_analysis_escape_invoke_parameters(be, md);
+       }
+
+       switch (md->returntype.type) {
+               case TYPE_LNG:
+               case TYPE_DBL:
+                       op_stack_push_unknown(be->stack);
+                       op_stack_push_unknown(be->stack);
+                       break;
+               case TYPE_VOID:
+                       /* Do nothing */
+                       break;
+               default:
+                       op_stack_push_unknown(be->stack);
+                       break;
+       }
+}
+
+static void bc_escape_analysis_parse_tableswitch(
+       bc_escape_analysis_t *be, 
+       jcode_t *jc
+) {
+       s4 high, low, def;
+       s4 i;
+
+       jcode_get_u1(jc); /* opcode */
+
+       jcode_align_bytecode_index(jc, 4);
+
+       def = jcode_get_s4(jc);
+       low = jcode_get_s4(jc);
+       high = jcode_get_s4(jc);
+
+       if (low <= high) {
+               for (i = 0; i < (high - low + 1); ++i) {
+                       bc_escape_analysis_branch_target(
+                               be,
+                               jcode_get_branch_target_wide(jc)
+                       );
+               }
+       }
+
+}
+
+static void bc_escape_analysis_parse_lookupswitch(
+       bc_escape_analysis_t *be, 
+       jcode_t *jc
+) {
+       s4 npairs;
+       s4 i;
+
+       jcode_get_u1(jc); /* opcode */
+
+       jcode_align_bytecode_index(jc, 4);
+
+       /* default */
+
+       bc_escape_analysis_branch_target(
+               be,
+               jcode_get_branch_target_wide(jc)
+       );
+
+       /* npairs */
+
+       npairs = jcode_get_s4(jc);
+
+       for (i = 0; i < npairs; ++i) {
+               /* Match */
+               jcode_get_s4(jc);
+
+               /* Offset */
+               bc_escape_analysis_branch_target(
+                       be,
+                       jcode_get_branch_target_wide(jc)
+               );
+       }
+
+}
+
+static void bc_escape_analysis_process_basicblock(bc_escape_analysis_t *be, jcode_t *jc) {
+       u1 opc;
+       op_stack_slot_t value1, value2, value3, value4;
+       u1 dim;
+       int length;
+       bool bb_end = false;
+
+#if BC_ESCAPE_VERBOSE
+       if (be->verbose) {
+               dprintf(be->depth, "Processing basicblock at offset %d.\n", jcode_get_index(jc));
+       }
+#endif
+
+       op_stack_reset(be->stack);
+
+       /* TODO end if all parameters escape */
+       /* TODO move code into process_instruction or the like */
+
+       while ((! jcode_end(jc)) && (! bb_end)) {
+
+               jcode_record_instruction_start(jc);
+
+               opc = jcode_get_u1(jc);
+
+               length = bytecode[opc].length;
+
+#if BC_ESCAPE_VERBOSE  
+               if (be->verbose) {
+                       dprintf(be->depth, "* %s, ", bytecode[opc].mnemonic);
+                       op_stack_print(be->stack, stdout);
+                       printf(" => ");
+               }
+#endif
+
+               switch (opc) {
+                       case BC_nop:
+                               break;
+
+                       case BC_aconst_null:
+                       case BC_iconst_m1:
+                       case BC_iconst_0:
+                       case BC_iconst_1:
+                       case BC_iconst_2:
+                       case BC_iconst_3:
+                       case BC_iconst_4:
+                       case BC_iconst_5:
+                       case BC_fconst_0:
+                       case BC_fconst_1:
+                       case BC_fconst_2:
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_dconst_0:
+                       case BC_dconst_1:
+                       case BC_lconst_0:
+                       case BC_lconst_1:
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_bipush:
+                       case BC_sipush:
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ldc1:
+                       case BC_ldc2:
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ldc2w:
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_iload:
+                       case BC_fload:
+                       case BC_iload_0:
+                       case BC_iload_1:
+                       case BC_iload_2:
+                       case BC_iload_3:
+                       case BC_fload_0:
+                       case BC_fload_1:
+                       case BC_fload_2:
+                       case BC_fload_3:
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_dload:
+                       case BC_lload:
+                       case BC_lload_0:
+                       case BC_lload_1:
+                       case BC_lload_2:
+                       case BC_lload_3:
+                       case BC_dload_0:
+                       case BC_dload_1:
+                       case BC_dload_2:
+                       case BC_dload_3:
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_aload:
+                               op_stack_push(be->stack, bc_escape_analysis_address_local(be, jcode_get_u1(jc)));
+                               break;
+
+                       case BC_aload_0:
+                               op_stack_push(be->stack, bc_escape_analysis_address_local(be, 0));
+                               break;
+
+                       case BC_aload_1:
+                               op_stack_push(be->stack, bc_escape_analysis_address_local(be, 1));
+                               break;
+
+                       case BC_aload_2:
+                               op_stack_push(be->stack, bc_escape_analysis_address_local(be, 2));
+                               break;
+
+                       case BC_aload_3:
+                               op_stack_push(be->stack, bc_escape_analysis_address_local(be, 3));
+                               break;
+
+                       case BC_iaload:
+                       case BC_faload:
+                       case BC_baload:
+                       case BC_caload:
+                       case BC_saload:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_laload:
+                       case BC_daload:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_aaload:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_istore:
+                       case BC_fstore:
+                               bc_escape_analysis_dirty(be, jcode_get_u1(jc));
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_istore_0:
+                       case BC_fstore_0:
+                               bc_escape_analysis_dirty(be, 0);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_istore_1:
+                       case BC_fstore_1:
+                               bc_escape_analysis_dirty(be, 1);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_istore_2:
+                       case BC_fstore_2:
+                               bc_escape_analysis_dirty(be, 2);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_istore_3:
+                       case BC_fstore_3:
+                               bc_escape_analysis_dirty(be, 3);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lstore:
+                       case BC_dstore:
+                               bc_escape_analysis_dirty_2(be, jcode_get_u1(jc));
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lstore_0:
+                       case BC_dstore_0:
+                               bc_escape_analysis_dirty_2(be, 0);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lstore_1:
+                       case BC_dstore_1:
+                               bc_escape_analysis_dirty_2(be, 1);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lstore_2:
+                       case BC_dstore_2:
+                               bc_escape_analysis_dirty_2(be, 2);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lstore_3:
+                       case BC_dstore_3:
+                               bc_escape_analysis_dirty_2(be, 3);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_astore:
+                               bc_escape_analysis_dirty(be, jcode_get_u1(jc));
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_astore_0:
+                               bc_escape_analysis_dirty(be, 0);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_astore_1:
+                               bc_escape_analysis_dirty(be, 1);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_astore_2:
+                               bc_escape_analysis_dirty(be, 2);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_astore_3:
+                               bc_escape_analysis_dirty(be, 3);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_iastore:
+                       case BC_fastore:
+                       case BC_bastore:
+                       case BC_castore:
+                       case BC_sastore:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_lastore:
+                       case BC_dastore:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_aastore:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               break;
+
+                       case BC_pop:
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_pop2:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               break;
+
+                       case BC_dup:
+                               value1 = op_stack_get(be->stack, 1);
+                               op_stack_push(be->stack, value1);
+                               break;
+
+                       case BC_dup_x1:
+                               value1 = op_stack_pop(be->stack);
+                               value2 = op_stack_pop(be->stack);
+                               op_stack_push(be->stack, value1);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               break;
+
+                       case BC_dup_x2:
+                               value1 = op_stack_pop(be->stack);
+                               value2 = op_stack_pop(be->stack);
+                               value3 = op_stack_pop(be->stack);
+                               op_stack_push(be->stack, value1);
+                               op_stack_push(be->stack, value3);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               break;
+                               
+                       case BC_dup2:
+                               value1 = op_stack_get(be->stack, 1);
+                               value2 = op_stack_get(be->stack, 2);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               break;
+
+                       case BC_dup2_x1:
+                               value1 = op_stack_pop(be->stack);
+                               value2 = op_stack_pop(be->stack);
+                               value3 = op_stack_pop(be->stack);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               op_stack_push(be->stack, value3);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               break;
+
+                       case BC_dup2_x2:
+                               value1 = op_stack_pop(be->stack);
+                               value2 = op_stack_pop(be->stack);
+                               value3 = op_stack_pop(be->stack);
+                               value4 = op_stack_pop(be->stack);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               op_stack_push(be->stack, value4);
+                               op_stack_push(be->stack, value3);
+                               op_stack_push(be->stack, value2);
+                               op_stack_push(be->stack, value1);
+                               break;
+
+                       case BC_swap:
+                               value1 = op_stack_get(be->stack, 1);
+                               value2 = op_stack_get(be->stack, 2);
+                               op_stack_set(be->stack, 1, value2);
+                               op_stack_set(be->stack, 2, value1);
+                               break;
+
+                       case BC_iadd:
+                       case BC_fadd:
+
+                       case BC_isub:
+                       case BC_fsub:
+
+                       case BC_imul:
+                       case BC_fmul:
+
+                       case BC_idiv:
+                       case BC_fdiv:
+
+                       case BC_irem:
+                       case BC_frem:
+
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ladd:
+                       case BC_dadd:
+
+                       case BC_lsub:
+                       case BC_dsub:
+
+                       case BC_ldiv:
+                       case BC_ddiv:
+
+                       case BC_lmul:
+                       case BC_dmul:
+
+                       case BC_lrem:
+                       case BC_drem:
+
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ineg:
+                       case BC_lneg:
+                       case BC_fneg:
+                       case BC_dneg:
+
+                               /* Nothing */
+                               break;
+
+                       case BC_ishl:
+                       case BC_ishr:
+                       case BC_iushr:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_lshl:
+                       case BC_lshr:
+                       case BC_lushr:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               /* Second operand is int. */
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_iand:
+                       case BC_ior:
+                       case BC_ixor:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_land:
+                       case BC_lor:
+                       case BC_lxor:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_iinc:
+                               /* Not stack operation. */
+                               bc_escape_analysis_dirty(be, jcode_get_u1(jc));
+                               break;
+
+                       case BC_i2l:
+                       case BC_i2d:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_i2f:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_l2i:
+                       case BC_l2f:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_l2d:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_f2i:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+                               
+                       case BC_f2l:
+                       case BC_f2d:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_d2i:
+                       case BC_d2f:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_d2l:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_int2byte:
+                       case BC_int2char:
+                       case BC_int2short:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_fcmpl:
+                       case BC_fcmpg:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_lcmp:
+                       case BC_dcmpl:
+                       case BC_dcmpg:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ifeq:
+                       case BC_ifne:
+                       case BC_iflt:
+                       case BC_ifge:
+                       case BC_ifgt:
+                       case BC_ifle:
+                               op_stack_pop(be->stack);
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bc_escape_analysis_branch_target(be, jcode_get_fall_through_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_if_icmpeq:
+                       case BC_if_icmpne:
+                       case BC_if_icmplt:
+                       case BC_if_icmpge:
+                       case BC_if_icmpgt:
+                       case BC_if_icmple:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bc_escape_analysis_branch_target(be, jcode_get_fall_through_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_if_acmpeq:
+                       case BC_if_acmpne:
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_METHOD);
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_METHOD);
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bc_escape_analysis_branch_target(be, jcode_get_fall_through_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_goto:
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_jsr:
+                               op_stack_push_unknown(be->stack);
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_ret:
+                               break;
+
+                       case BC_tableswitch:
+                               op_stack_pop(be->stack);
+                               jcode_rewind_instruction(jc);
+                               bc_escape_analysis_parse_tableswitch(be, jc);
+                               length = jcode_get_instruction_length(jc);
+                               bb_end = 1;
+                               break;
+
+                       case BC_lookupswitch:
+                               op_stack_pop(be->stack);
+                               jcode_rewind_instruction(jc);
+                               bc_escape_analysis_parse_lookupswitch(be, jc);
+                               length = jcode_get_instruction_length(jc);
+                               bb_end = 1;
+                               break;
+
+                       case BC_return:
+                               bb_end = true;
+                               break;
+
+                       case BC_ireturn:
+                       case BC_freturn:
+                               op_stack_pop(be->stack);
+                               bb_end = true;
+                               break;
+
+                       case BC_lreturn:
+                       case BC_dreturn:
+                               op_stack_pop(be->stack);
+                               op_stack_pop(be->stack);
+                               bb_end = true;
+                               break;
+
+                       case BC_areturn:
+                               /* FIXME */
+                               bc_escape_analyisis_returned(be, op_stack_pop(be->stack));
+                               bb_end = true;
+                               break;
+
+                       case BC_getfield:
+                               op_stack_pop(be->stack);
+                               /* Fall through. */
+
+                       case BC_getstatic:
+                               if (
+                                       bc_escape_analysis_value_category(be, jcode_get_s2(jc)) == 
+                                       VALUE_CATEGORY_2
+                               ) {
+                                       op_stack_push_unknown(be->stack);
+                               }
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       
+                       case BC_putfield:
+                               if (
+                                       bc_escape_analysis_value_category(be, jcode_get_u2(jc)) == 
+                                       VALUE_CATEGORY_2
+                               ) {
+                                       op_stack_pop(be->stack);
+
+                                       op_stack_pop(be->stack);
+                                       op_stack_pop(be->stack);
+                               } else {
+                                       value2 = op_stack_pop(be->stack);
+                                       value1 = op_stack_pop(be->stack);
+                                       bc_escape_analysis_adjust_state(be, value2, ESCAPE_GLOBAL);
+                               }
+                               break;
+
+                       case BC_putstatic:
+                               if (
+                                       bc_escape_analysis_value_category(be, jcode_get_u2(jc)) == 
+                                       VALUE_CATEGORY_2
+                               ) {
+                                       op_stack_pop(be->stack);
+                                       op_stack_pop(be->stack);
+                               } else {
+                                       value1 = op_stack_pop(be->stack);
+                                       bc_escape_analysis_adjust_state(be, value1, ESCAPE_GLOBAL);
+                               }
+                               break;
+
+                       case BC_invokevirtual:
+                       case BC_invokespecial:
+                       case BC_invokestatic:
+                       case BC_invokeinterface:
+                               jcode_rewind_instruction(jc);
+                               bc_escape_analysis_parse_invoke(be, jc);
+                               break;
+
+                       case BC_new:
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_newarray:
+                       case BC_anewarray:
+                               op_stack_pop(be->stack);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_arraylength:
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_METHOD);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_athrow:
+                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                               bb_end = true;
+                               break;
+
+                       case BC_checkcast:
+                               value1 = op_stack_get(be->stack, 1);
+                               bc_escape_analysis_adjust_state(be, value1, ESCAPE_METHOD);
+                               break;
+
+                       case BC_instanceof:
+                               value1 = op_stack_pop(be->stack);
+                               bc_escape_analysis_adjust_state(be, value1, ESCAPE_METHOD);
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_monitorenter:
+                       case BC_monitorexit:
+                               value1 = op_stack_pop(be->stack);
+                               bc_escape_analysis_adjust_state(be, value1, ESCAPE_METHOD);
+                               break;
+
+                       case BC_wide:
+
+                               /* All except iinc have a length of 4. */
+                               length = 4;
+
+                               switch (jcode_get_u1(jc)) {
+                                       case BC_iload:
+                                       case BC_fload:
+                                               op_stack_push_unknown(be->stack);
+                                               break;
+
+                                       case BC_lload:
+                                       case BC_dload:
+                                               op_stack_push_unknown(be->stack);
+                                               op_stack_push_unknown(be->stack);
+                                               break;
+
+                                       case BC_aload:
+                                               op_stack_push(
+                                                       be->stack, 
+                                                       bc_escape_analysis_address_local(
+                                                               be, 
+                                                               jcode_get_u1(jc)
+                                                       )
+                                               );
+                                               break;
+
+                                       case BC_istore:
+                                       case BC_fstore:
+                                               bc_escape_analysis_dirty(be, jcode_get_u2(jc));
+                                               op_stack_pop(be->stack);
+                                               break;
+
+                                       case BC_lstore:
+                                       case BC_dstore:
+                                               bc_escape_analysis_dirty_2(be, jcode_get_u2(jc));
+                                               op_stack_pop(be->stack);
+                                               op_stack_pop(be->stack);
+                                               break;
+
+                                       case BC_astore:
+                                               bc_escape_analysis_dirty(be, jcode_get_u2(jc));
+                                               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+                                               break;
+                                               
+                                       case BC_ret:
+                                               /* Do nothing. */
+                                               break;
+
+                                       case BC_iinc:
+                                               bc_escape_analysis_dirty(be, jcode_get_u2(jc));
+                                               length = 6;
+                                               /* Do nothing. */
+                                               break;
+                               }
+                               break;
+
+                       case BC_multianewarray:
+                               jcode_get_u2(jc);
+                               dim = jcode_get_u1(jc);
+                               while (dim-- > 0) {
+                                       op_stack_pop(be->stack);
+                               }
+                               op_stack_push_unknown(be->stack);
+                               break;
+
+                       case BC_ifnull:
+                       case BC_ifnonnull:
+                               value1 = op_stack_pop(be->stack);
+                               bc_escape_analysis_adjust_state(be, value1, ESCAPE_METHOD);
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target(jc));
+                               bc_escape_analysis_branch_target(be, jcode_get_fall_through_target(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_goto_w:
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target_wide(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_jsr_w:
+                               bc_escape_analysis_branch_target(be, jcode_get_branch_target_wide(jc));
+                               bb_end = true;
+                               break;
+
+                       case BC_breakpoint:
+                       case BC_impdep1:
+                       case BC_impdep2:
+                               break;
+
+                       default:
+                               break;
+               }
+               assert(length > 0);
+               jcode_forward_instruction_relative(jc, length);
+
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       op_stack_print(be->stack, stdout);
+                       printf("\n");
+               }
+#endif
+       }
+
+#if BC_ESCAPE_VERBOSE
+       if (be->verbose) {
+               dprintf(be->depth, "Elements left on stack: %d\n", op_stack_element_count(be->stack));
+       }
+#endif
+
+       while (! op_stack_is_empty(be->stack)) {
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       dprintf(be->depth, "Stack element: ");
+                       op_stack_print(be->stack, stdout);
+                       printf(" => ");
+               }
+#endif
+               bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       op_stack_print(be->stack, stdout);
+                       printf("\n");
+               }
+#endif
+       }
+}
+
+static void    bc_escape_analysis_adjust_return_value(bc_escape_analysis_t *be) {
+       escape_state_t e, pe;
+       int i;
+
+       /* Only calculate, if return value is of type address. */
+
+       if (be->method->parseddesc->returntype.type != TYPE_ADR) {
+               return ;
+       }
+
+       /* Get current escape state of return value. */
+
+       e = (escape_state_t)be->param_escape[-1];
+
+       /* If a parameter can be returned, adjust to its escape state. */
+
+       for (i = 0; i < be->param_escape_size; ++i) {
+               if (bit_vector_get(be->adr_param_returned, i)) {
+                       pe = (escape_state_t)be->param_escape[i];
+                       if (pe > e) {
+                               e = pe;
+                       }
+               }
+       }
+
+       be->param_escape[-1] = (u1)e;
+}
+
+static void bc_escape_analysis_analyze(bc_escape_analysis_t *be) {
+       raw_exception_entry *itee;
+       basicblock_work_item_t *bb;
+       jcode_t jc;
+
+       /* Add root as basic block. */
+
+       bc_escape_analysis_branch_target(be, 0);
+
+       /* Add each exception handler as basic block. */
+
+       for (
+               itee = be->method->rawexceptiontable;
+               itee != be->method->rawexceptiontable + be->method->rawexceptiontablelength;
+               ++itee
+       ) {
+               bc_escape_analysis_branch_target(be, itee->handlerpc);
+       }
+
+       /* Init jcode parser. */
+
+       jcode_init(
+               &jc,
+               be->method->jcode,
+               be->method->jcodelength,
+               0
+       );
+
+       /* Process basicblock by basicblock. */
+
+       for (bb = be->basicblocks->first; bb; bb = bb->next) {
+               jcode_move_to_index(&jc, bb->bytecode_index);
+               bc_escape_analysis_process_basicblock(
+                       be,
+                       &jc
+               );
+       }
+
+       /* Calculate escape of return value. */
+
+       bc_escape_analysis_adjust_return_value(be);
+
+       /* Export escape of parameters. */
+
+       be->method->paramescape = be->param_escape;
+}
+
+static void bc_escape_analysis_perform_intern(methodinfo *m, int depth) {
+       bc_escape_analysis_t *be;
+       bool verbose = false;
+
+#if BC_ESCAPE_VERBOSE
+       if (verbose) {
+               dprintf(
+                       depth,
+                       "=== BC escape analysis of %s/%s at depth %d ===\n",
+                       m->clazz->name->text,
+                       m->name->text,
+                       depth
+               );
+       }
+#endif
+
+       if (m->paramescape != NULL) {
+#if BC_ESCAPE_VERBOSE
+               if (verbose) {  
+                       dprintf(depth, "Escape info for method already available.\n");
+               }
+#endif
+               return;
+       }
+
+       if ((m->flags & ACC_METHOD_EA) != 0) {
+#if BC_ESCAPE_VERBOSE
+               if (verbose) {  
+                       dprintf(depth, "Detected recursion, aborting.\n");
+               }
+#endif
+               return;
+       }
+
+       if (m->jcode == NULL) {
+#if BC_ESCAPE_VERBOSE
+               if (verbose) {
+                       dprintf(depth, "No bytecode for callee.\n");
+               }
+#endif
+               return;
+       }
+
+       /* TODO threshold */
+
+       be = DNEW(bc_escape_analysis_t);
+       bc_escape_analysis_init(be, m, verbose, depth);
+
+       m->flags |= ACC_METHOD_EA;
+
+       bc_escape_analysis_analyze(be);
+
+#if BC_ESCAPE_VERBOSE
+       if (be->verbose) {
+               dprintf(
+                       depth,
+                       "%s/%s: Non-escaping params: %d\n", 
+                       m->clazz->name->text,
+                       m->name->text,
+                       be->non_escaping_adr_params
+               );
+       }
+#endif
+
+       m->flags &= ~ACC_METHOD_EA;
+}
+
+void bc_escape_analysis_perform(methodinfo *m) {
+       bc_escape_analysis_perform_intern(m, 0);
+}
+
+
diff --git a/src/vm/jit/optimizing/escape.c b/src/vm/jit/optimizing/escape.c
new file mode 100644 (file)
index 0000000..71cf200
--- /dev/null
@@ -0,0 +1,982 @@
+#include "vm/jit/jit.h"
+#include "vmcore/class.h"
+#include "vm/jit/optimizing/escape.h"
+
+/*** escape_state *************************************************************/
+
+const char *escape_state_to_string(escape_state_t escape_state) {
+#      define str(x) case x: return #x;
+       switch (escape_state) {
+               str(ESCAPE_UNKNOWN)
+               str(ESCAPE_NONE)
+               str(ESCAPE_METHOD)
+               str(ESCAPE_GLOBAL_THROUGH_METHOD)
+               str(ESCAPE_GLOBAL)
+               default: return "???";
+       }
+#      undef str
+}
+
+/*** instruction **************************************************************/
+
+static inline s2 instruction_get_opcode(const instruction *iptr) {
+       if (iptr->opc == ICMD_BUILTIN) {
+               return iptr->sx.s23.s3.bte->opcode;
+       } else {
+               return iptr->opc;
+       }
+}
+
+static inline bool instruction_is_unresolved(const instruction *iptr) {
+       return iptr->flags.bits & INS_FLAG_UNRESOLVED;
+}
+
+static inline s4 instruction_field_type(const instruction *iptr) {
+       if (instruction_is_unresolved(iptr)) {
+               return iptr->sx.s23.s3.uf->fieldref->parseddesc.fd->type;
+       } else {
+               return iptr->sx.s23.s3.fmiref->p.field->type;
+       }
+}
+
+static inline s4 instruction_s1(const instruction *iptr) {
+       return iptr->s1.varindex;
+}
+
+static inline s4 instruction_s2(const instruction *iptr) {
+       return iptr->sx.s23.s2.varindex;
+}
+
+static inline s4 instruction_s3(const instruction *iptr) {
+       return iptr->sx.s23.s3.varindex;
+}
+
+static inline s4 instruction_dst(const instruction *iptr) {
+       return iptr->dst.varindex;
+}
+
+static inline s4 instruction_arg(const instruction *iptr, int arg) {
+       return iptr->sx.s23.s2.args[arg];
+}
+
+static inline bool instruction_is_class_constant(const instruction *iptr) {
+       return iptr->flags.bits & INS_FLAG_CLASS;
+}
+
+static inline classinfo *instruction_classinfo(const instruction *iptr) {
+       return iptr->sx.val.c.cls;
+}
+
+static inline methodinfo *instruction_local_methodinfo(const instruction *iptr) {
+       if (instruction_is_unresolved(iptr)) {
+               return NULL;
+       } else {
+               return iptr->sx.s23.s3.fmiref->p.method;
+       }
+}
+
+static inline int instruction_dst_type(const instruction *iptr, jitdata *jd) {
+       return VAROP(iptr->dst)->type;
+}
+
+static inline int instruction_return_type(const instruction *iptr) {
+       return instruction_call_site(iptr)->returntype.type;
+}
+
+static inline s4 instruction_arg_type(const instruction *iptr, int arg) {
+       methoddesc *md = instruction_call_site(iptr);
+       assert(0 <= arg && arg < md->paramcount);
+       return md->paramtypes[arg].type;
+}
+
+static inline int instruction_arg_count(const instruction *iptr) {
+       return instruction_call_site(iptr)->paramcount;
+}
+
+/*** instruction_list ********************************************************/
+
+typedef struct instruction_list_item {
+       instruction *instr;
+       struct instruction_list_item *next;
+} instruction_list_item_t;
+
+typedef struct {
+       instruction_list_item_t *first;
+} instruction_list_t;
+
+void instruction_list_init(instruction_list_t *list) {
+       list->first = NULL;
+}
+
+void instruction_list_add(instruction_list_t *list, instruction *instr) {
+       instruction_list_item_t *item = DNEW(instruction_list_item_t);
+       item->instr = instr;
+       item->next = list->first;
+       list->first = item;
+}
+
+#define FOR_EACH_INSTRUCTION_LIST(list, it) \
+       for ((it) = (list)->first; (it) != NULL; (it) = (it)->next)
+
+/*** escape_analysis *********************************************************/
+
+struct var_extra;
+
+typedef struct {
+       jitdata *jd;
+
+       instruction_list_t *allocations;
+       instruction_list_t *getfields;
+
+       struct var_extra **var;
+
+       unsigned adr_args_count;
+
+       bool verbose;
+
+} escape_analysis_t;
+
+/*** dependency_list_item ****************************************************/
+
+typedef struct dependency_list_item {
+       instruction *store;
+       struct dependency_list_item *next;
+} dependency_list_item_t;
+
+bool dependency_list_item_compare(const dependency_list_item_t *item, const instruction *load) {
+
+       instruction *store = item->store;
+       utf *storen;
+       const utf *loadn;
+
+       if (load->opc == ICMD_AALOAD) {
+
+               if (store->opc != ICMD_AASTORE) {
+                       return false;
+               }
+
+               return true;
+
+       } else {
+
+               if (store->opc != ICMD_PUTFIELD) {
+                       return false;
+               }
+
+               if (
+                       instruction_is_unresolved(store) !=
+                       instruction_is_unresolved(load)
+               ) {
+                       return false;
+               }
+
+               if (instruction_is_unresolved(store)) {
+                       storen = store->sx.s23.s3.uf->fieldref->name;
+                       loadn = load->sx.s23.s3.uf->fieldref->name;
+               } else {
+                       storen = store->sx.s23.s3.fmiref->name;
+                       loadn = load->sx.s23.s3.fmiref->name;
+               }
+
+               /* TODO pointer equality ? */   
+
+               if (storen->blength != loadn->blength) {
+                       return false;
+               }
+
+               return (strcmp(storen->text, loadn->text) == 0);
+       }
+}
+
+/* TODO rename */
+s4 dependency_list_item_get_dependency(const dependency_list_item_t *item) {
+       switch (item->store->opc) {
+               case ICMD_AASTORE:
+                       return instruction_s3(item->store);
+               case ICMD_PUTFIELD:
+                       return instruction_s2(item->store);
+               default:
+                       assert(0);
+                       return 0;
+       }
+}
+
+/*** dependency_list *********************************************************/
+
+typedef struct {
+       dependency_list_item_t *first;
+       dependency_list_item_t *last;
+} dependency_list_t;
+
+void dependency_list_init(dependency_list_t *dl) {
+       dl->first = NULL;
+       dl->last = NULL;
+}
+
+void dependency_list_add(dependency_list_t *dl, instruction *store) {
+       dependency_list_item_t *item = DNEW(dependency_list_item_t);
+
+       item->store = store;
+       item->next = NULL;
+       
+       if (dl->first == NULL) {
+               dl->first = item;
+               dl->last = item;
+       } else {
+               dl->last->next = item;
+               dl->last = item;
+       }
+}
+
+void dependenCy_list_import(dependency_list_t *dl, dependency_list_t *other) {
+
+       if (other == NULL) {
+               return;
+       }
+
+       if (dl->first == NULL) {
+               *dl = *other;
+       } else {
+               dl->last->next = other->first;
+               dl->last = other->last;
+       }
+
+       other->first = NULL;
+       other->last = NULL;
+       
+}
+
+#define FOR_EACH_DEPENDENCY_LIST(dl, it) \
+       for ((it) = (dl)->first; (it) != NULL; (it) = (it)->next)
+
+/*** var_extra ***************************************************************/
+
+typedef struct var_extra {
+       instruction *allocation;
+       escape_state_t escape_state;
+       s4 representant;
+       dependency_list_t *dependency_list;
+       bool is_arg; /* TODO optimize */
+} var_extra_t;
+
+static void var_extra_init(var_extra_t *ve) {
+       ve->allocation = NULL;
+       ve->escape_state = ESCAPE_NONE;
+       ve->representant = -1;
+       ve->dependency_list = NULL;
+       ve->is_arg = false;
+}
+
+static inline var_extra_t *var_extra_get_no_alloc(const escape_analysis_t *e, s4 var) {
+       return e->var[var];
+}
+
+static var_extra_t* var_extra_get(escape_analysis_t *e, s4 var) {
+       var_extra_t *ve;
+
+       assert(0 <= var && var <= e->jd->vartop);
+
+       ve = var_extra_get_no_alloc(e, var);
+
+       if (ve == NULL) {
+               ve = DNEW(var_extra_t);
+               var_extra_init(ve);
+               e->var[var] = ve;
+       }
+
+       return ve;
+}
+
+static s4 var_extra_get_representant(escape_analysis_t *e, s4 var) {
+       var_extra_t *ve;
+#if !defined(NDEBUG)
+       int ctr = 0;
+#endif
+
+       ve = var_extra_get(e, var);
+
+       while (ve->representant != -1) {
+               assert(ctr++ < 10000);
+               var = ve->representant;
+               ve = var_extra_get_no_alloc(e, var);
+               assert(ve);
+       }
+
+       return var;
+}
+
+static escape_state_t var_extra_get_escape_state(escape_analysis_t *e, s4 var) {
+       var_extra_t *ve;
+       
+       var = var_extra_get_representant(e, var);
+       ve = var_extra_get(e, var);
+
+       return ve->escape_state;
+}
+
+static void var_extra_set_escape_state(escape_analysis_t *e, s4 var, escape_state_t escape_state) {
+       var_extra_t *ve;
+       
+       var = var_extra_get_representant(e, var);
+       ve = var_extra_get(e, var);
+
+       ve->escape_state = escape_state;
+}
+
+static dependency_list_t *var_extra_get_dependency_list(escape_analysis_t *e, s4 var) {
+       var_extra_t *ve;
+       
+       var = var_extra_get_representant(e, var);
+       ve = var_extra_get(e, var);
+
+       if (ve->dependency_list == NULL) {
+               ve->dependency_list = DNEW(dependency_list_t);
+               dependency_list_init(ve->dependency_list);
+       }
+
+       return ve->dependency_list;
+}
+
+/*** escape_analysis *********************************************************/
+
+static void escape_analysis_init(escape_analysis_t *e, jitdata *jd) {
+       e->jd = jd;
+
+       e->allocations = DNEW(instruction_list_t);
+       instruction_list_init(e->allocations);
+
+       e->getfields = DNEW(instruction_list_t);
+       instruction_list_init(e->getfields);
+
+       e->var = DMNEW(var_extra_t *, jd->vartop);
+       MZERO(e->var, var_extra_t *, jd->vartop);
+
+       e->adr_args_count = 0;
+
+       e->verbose = (
+               strcmp(e->jd->m->clazz->name->text, "gnu/java/util/regex/RESyntax") == 0 
+               && strcmp(e->jd->m->name->text, "<clinit>") == 0
+       );
+       e->verbose = 1;
+}
+
+static void escape_analysis_set_allocation(escape_analysis_t *e, s4 var, instruction *iptr) {
+       var_extra_get(e, var)->allocation = iptr;
+}
+
+static instruction *escape_analysis_get_allocation(const escape_analysis_t *e, s4 var) {
+       var_extra_t *ve = var_extra_get_no_alloc(e, var);
+
+       assert(ve != NULL);
+       assert(ve->allocation != NULL);
+
+       return ve->allocation;
+}
+
+static void escape_analysis_set_is_argument(escape_analysis_t *e, s4 var) {
+       var_extra_get(e, var)->is_arg = true;
+}
+
+static bool escape_analysis_get_is_argument(escape_analysis_t *e, s4 var) {
+       return var_extra_get(e, var)->is_arg;
+}
+
+static void escape_analysis_ensure_state(escape_analysis_t *e, s4 var, escape_state_t escape_state) {
+       
+       var_extra_t *ve;
+       dependency_list_item_t *it;
+
+       var = var_extra_get_representant(e, var);
+       ve = var_extra_get(e, var);
+
+       if (ve->escape_state < escape_state) {
+               if (e->verbose) {
+                       printf(
+                               "escape state of %d %s => %s\n", 
+                               var, 
+                               escape_state_to_string(ve->escape_state), 
+                               escape_state_to_string(escape_state)
+                       );
+               }
+               ve->escape_state = escape_state;
+               if (ve->dependency_list != NULL) {
+                       FOR_EACH_DEPENDENCY_LIST(ve->dependency_list, it) {
+                               if (e->verbose) {
+                                       printf("propagating to %s@%d\n", icmd_table[it->store->opc].name, it->store->line);
+                               }
+                               escape_analysis_ensure_state(
+                                       e, 
+                                       dependency_list_item_get_dependency(it),
+                                       escape_state
+                               );
+                       }
+               }
+       }
+}
+
+static escape_state_t escape_analysis_get_state(escape_analysis_t *e, s4 var) {
+       return var_extra_get_escape_state(e, var);
+}
+
+#define escape_analysis_assert_has_escape(e, var) \
+       assert( \
+               var_extra_get_no_alloc(e, var) && \
+               (var_extra_get_no_alloc(e, var)->escape_state > ESCAPE_UNKNOWN) \
+       )
+
+static classinfo *escape_analysis_classinfo_in_var(escape_analysis_t *e, s4 var) {
+       instruction *iptr = escape_analysis_get_allocation(e, var);
+
+       if (iptr == NULL) {
+               return NULL;
+       }
+
+       if (! instruction_is_class_constant(iptr)) {
+               return NULL;
+       }
+
+       if (instruction_dst(iptr) != var) {
+               return NULL;
+       }
+
+       if (instruction_is_unresolved(iptr)) {
+               return NULL;
+       }
+
+       return instruction_classinfo(iptr);
+}
+
+static void escape_analysis_merge(escape_analysis_t *e, s4 var1, s4 var2) {
+
+       var_extra_t *ve1, *ve2;
+       dependency_list_item_t *itd;
+       bool has_become_arg;
+
+       var1 = var_extra_get_representant(e, var1);
+       var2 = var_extra_get_representant(e, var2);
+
+       /* Don't merge the same escape sets. */
+
+       if (var1 == var2) {
+               return;
+       }
+
+       ve1 = var_extra_get(e, var1);
+       ve2 = var_extra_get(e, var2);
+
+       /* Adjust escape state to maximal escape state. */
+
+       escape_analysis_ensure_state(e, var1, ve2->escape_state);
+       escape_analysis_ensure_state(e, var2, ve1->escape_state);
+
+       /* Representant of var1 becomes the representant of var2. */
+
+       ve2->representant = var1;
+
+       /* Adjust is_argument to logical or. */
+       
+       has_become_arg = ve1->is_arg != ve2->is_arg;
+       ve1->is_arg = ve1->is_arg || ve2->is_arg;
+
+       if (e->verbose && has_become_arg) printf("(%d,%d) has become arg.\n", var1, var2);
+
+       /* Merge list of dependencies. */
+
+       if (ve1->dependency_list == NULL) {
+               ve1->dependency_list = ve2->dependency_list;
+       } else {
+               dependenCy_list_import(ve1->dependency_list, ve2->dependency_list);
+       }
+
+       /* If one of the merged values is an argument but the other not,
+          all dependencies of the newly created value escape globally. */
+
+       if (has_become_arg && ve1->dependency_list != NULL) {
+               FOR_EACH_DEPENDENCY_LIST(ve1->dependency_list, itd) {
+                       escape_analysis_ensure_state(
+                               e,
+                               dependency_list_item_get_dependency(itd),
+                               ESCAPE_GLOBAL
+                       );
+               }
+       }
+}
+
+static void escape_analysis_add_dependency(escape_analysis_t *e, instruction *store) {
+       s4 obj = instruction_s1(store);
+       dependency_list_t *dl = var_extra_get_dependency_list(e, obj);
+
+       assert(store->opc == ICMD_PUTFIELD || store->opc == ICMD_AASTORE);
+
+       dependency_list_add(dl, store);
+
+       if (e->verbose) {
+               printf("dependency_list_add\n");
+       }
+}
+
+static void escape_analysis_process_instruction(escape_analysis_t *e, instruction *iptr) {
+       jitdata *jd = e->jd;
+       classinfo *c;
+       s4 count;
+       u1 *paramescape;
+       unsigned i;
+       instruction **iarg;
+       constant_FMIref *fmi;
+       methodinfo *mi;
+       resolve_result_t result;
+
+       if (e->verbose) {
+               printf("processing %s@%d\n", icmd_table[iptr->opc].name, iptr->line);
+       }
+
+       switch (instruction_get_opcode(iptr)) {
+               case ICMD_ACONST:
+
+                       escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+
+                       break;
+
+               case ICMD_NEW:
+                       c = escape_analysis_classinfo_in_var(e, instruction_arg(iptr, 0));
+
+                       escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+
+                       if (c == NULL) {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                               if (e->verbose) printf("1\n");
+                       } else if (c->finalizer != NULL) {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                               if (e->verbose) printf("3\n");
+                       } else {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+                               if (e->verbose) printf("2\n");
+                       }
+
+                       instruction_list_add(e->allocations, iptr);
+
+                       break;
+
+               case ICMD_NEWARRAY:
+               case ICMD_ANEWARRAY:
+                       
+                       escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                       escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       instruction_list_add(e->allocations, iptr);
+
+                       break;
+
+               case ICMD_PUTSTATIC:
+                       if (instruction_field_type(iptr) == TYPE_ADR) {
+                               /*
+                               escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                               */
+                               escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_GLOBAL);
+                       }
+                       break;
+
+               case ICMD_PUTFIELD:
+                       if (instruction_field_type(iptr) == TYPE_ADR) {
+                               /*
+                               escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                               escape_analysis_assert_has_escape(e, instruction_s2(iptr));
+                               */
+                               if (escape_analysis_get_is_argument(e, instruction_s1(iptr))) {
+                                       escape_analysis_ensure_state(e, instruction_s2(iptr), ESCAPE_GLOBAL);
+                               } else {
+                                       escape_analysis_ensure_state(e, instruction_s2(iptr), escape_analysis_get_state(e, instruction_s1(iptr)));
+                                       escape_analysis_add_dependency(e, iptr);
+                               }
+                       }
+                       break;
+
+               case ICMD_AASTORE:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       escape_analysis_assert_has_escape(e, instruction_s3(iptr));
+                       */
+                       if (escape_analysis_get_is_argument(e, instruction_s1(iptr))) {
+                               escape_analysis_ensure_state(e, instruction_s3(iptr), ESCAPE_GLOBAL);
+                       } else {
+                               escape_analysis_ensure_state(e, instruction_s3(iptr), escape_analysis_get_state(e, instruction_s1(iptr)));
+                               escape_analysis_add_dependency(e, iptr);
+                       }
+                       break;
+
+               case ICMD_GETSTATIC:
+                       if (instruction_field_type(iptr) == TYPE_ADR) {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                               escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       }
+                       break;
+
+               case ICMD_GETFIELD:
+                       if (instruction_field_type(iptr) == TYPE_ADR) {
+                               /*
+                               escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                               */
+
+                               if (escape_analysis_get_is_argument(e, instruction_s1(iptr))) {
+                                       /* Fields loaded from arguments escape globally.
+                                          x = arg.foo;
+                                          x.bar = y;
+                                          => y escapes globally. */
+                                       escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                               } else {
+                                       escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+                               }
+
+                               escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+
+                               instruction_list_add(e->getfields, iptr);
+                       }
+                       break;
+
+               case ICMD_ARRAYLENGTH:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       break;
+
+               case ICMD_AALOAD:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+
+                       if (escape_analysis_get_is_argument(e, instruction_s1(iptr))) {
+                               /* If store into argument, escapes globally. See ICMD_GETFIELD. */
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+                       } else {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+                       }
+
+                       escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+
+                       instruction_list_add(e->getfields, iptr);
+                       break;
+
+               case ICMD_IF_ACMPEQ:
+               case ICMD_IF_ACMPNE:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       escape_analysis_assert_has_escape(e, instruction_s2(iptr));
+                       */
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+                       escape_analysis_ensure_state(e, instruction_s2(iptr), ESCAPE_METHOD);
+                       break;
+
+               case ICMD_IFNULL:
+               case ICMD_IFNONNULL:
+               case ICMD_CHECKNULL:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+                       break;
+
+               case ICMD_CHECKCAST:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       escape_analysis_merge(e, instruction_s1(iptr), instruction_dst(iptr));
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+                       escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       break;
+
+               case ICMD_INSTANCEOF:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+                       break;
+
+               case ICMD_INVOKESPECIAL:
+               case ICMD_INVOKEVIRTUAL:
+               case ICMD_INVOKEINTERFACE:
+               case ICMD_INVOKESTATIC:
+                       count = instruction_arg_count(iptr);
+                       mi = instruction_local_methodinfo(iptr);
+                       paramescape = NULL;
+
+                       if (mi != NULL) {
+                               /* If the method could be resolved, it already is. */
+                               paramescape = mi->paramescape;
+
+                               if (paramescape == NULL) {
+                                       if (e->verbose) {
+                                               printf("BC escape analyzing callee.\n");
+                                       }
+                                       bc_escape_analysis_perform(mi);
+                                       paramescape = mi->paramescape;
+                               }
+                       } else {
+                               if (e->verbose) {
+                                       printf("Unresolved callee.\n");
+                               }
+                       }
+
+                       for (i = 0; i < count; ++i) {
+                               if (instruction_arg_type(iptr, i) == TYPE_ADR) {
+
+                                       /*
+                                       escape_analysis_assert_has_escape(e, instruction_arg(iptr, i));
+                                       */
+                                       if (paramescape == NULL) {
+                                               escape_analysis_ensure_state(
+                                                       e, 
+                                                       instruction_arg(iptr, i), 
+                                                       instruction_local_methodinfo(iptr) && instruction_local_methodinfo(iptr)->jcode ?
+                                                               ESCAPE_GLOBAL_THROUGH_METHOD :
+                                                               ESCAPE_GLOBAL
+                                               );
+                                       } else if ((escape_state_t)*paramescape < ESCAPE_METHOD) {
+                                               escape_analysis_ensure_state(e, instruction_arg(iptr, i), ESCAPE_METHOD);
+                                       } else {
+                                               escape_analysis_ensure_state(e, instruction_arg(iptr, i), (escape_state_t)*paramescape);
+                                       }
+
+                                       if (paramescape != NULL) {
+                                               ++paramescape;
+                                       }
+                               }
+                       }
+
+                       if (instruction_return_type(iptr) == TYPE_ADR) {
+                               escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+                               escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       }
+
+                       break;
+
+               case ICMD_ATHROW:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_GLOBAL);
+                       break;
+
+               case ICMD_ARETURN:
+                       /*
+                       escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                       */
+                       escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_GLOBAL);
+                       break;
+
+               case ICMD_ALOAD:
+               case ICMD_ASTORE:
+               case ICMD_MOVE:
+               case ICMD_COPY:
+                       if (instruction_dst_type(iptr, jd) == TYPE_ADR) {
+                               /*
+                               escape_analysis_assert_has_escape(e, instruction_s1(iptr));
+                               */
+                               escape_analysis_merge(e, instruction_s1(iptr), instruction_dst(iptr));
+                               escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+                       }
+                       break;
+
+               case ICMD_PHI:
+                       for (
+                               iarg = iptr->sx.s23.s2.iargs;
+                               iarg != iptr->sx.s23.s2.iargs + iptr->s1.argcount;
+                               ++iarg
+                       ) {
+                               escape_analysis_merge(e, instruction_dst(iptr), instruction_dst(*iarg));
+                       }
+                       break;
+
+               case ICMD_GETEXCEPTION:
+                       escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
+                       break;
+       }
+}
+
+static void escape_analysis_process_instructions(escape_analysis_t *e) {
+       basicblock *bptr;
+       instruction *iptr;
+
+       FOR_EACH_BASICBLOCK(e->jd, bptr) {
+
+               for (iptr = bptr->phis; iptr != bptr->phis + bptr->phicount; ++iptr) {
+                       escape_analysis_process_instruction(e, iptr);
+               }
+
+               FOR_EACH_INSTRUCTION(bptr, iptr) {
+                       escape_analysis_process_instruction(e, iptr);
+               }
+
+       }
+}
+
+static void escape_analysis_post_process_getfields(escape_analysis_t *e) {
+       instruction_list_item_t *iti;
+       dependency_list_item_t *itd;
+       instruction *iptr;
+       dependency_list_t *dl;
+
+       FOR_EACH_INSTRUCTION_LIST(e->getfields, iti) {
+
+               iptr = iti->instr;
+
+               /* Get the object the field/element is loaded from. */
+
+               dl = var_extra_get_dependency_list(e, instruction_s1(iptr));
+
+               /* Adjust escape state of all objects in the dependency list,
+                  referenced via the field of this getfield/arraystore. */
+
+               if (dl != NULL) {
+                       FOR_EACH_DEPENDENCY_LIST(dl, itd) {
+                               if (dependency_list_item_compare(itd, iptr)) {
+
+                                       /* Fields match. Adjust escape state. */
+
+                                       escape_analysis_ensure_state(
+                                               e, 
+                                               dependency_list_item_get_dependency(itd),
+                                               escape_analysis_get_state(e, instruction_dst(iptr))
+                                       );
+                               }
+                       }
+               }
+
+       }
+}
+
+static void display_allocation(escape_analysis_t *e, const char *prefix, const instruction *iptr, escape_state_t es) {
+       const char *cl = "WTF";
+       classinfo *c;
+
+       if (instruction_get_opcode(iptr) == ICMD_NEW) {
+               c = escape_analysis_classinfo_in_var(e, instruction_arg(iptr, 0));
+               if (c) {
+                       cl = c->name->text;
+               }
+       }
+       
+
+       printf(
+               " %s %s %s: %s %s @%d %s\n", 
+               prefix,
+               e->jd->m->clazz->name->text,
+               e->jd->m->name->text,
+               icmd_table[iptr->opc].name,
+               cl,
+               iptr->line,
+               escape_state_to_string(es)
+       );
+}
+
+static void escape_analysis_mark_allocations(escape_analysis_t *e) {
+       instruction_list_item_t *iti;
+       escape_state_t es;
+/*
+       FOR_EACH_INSTRUCTION_LIST(e->allocations, iti) {
+               es = escape_analysis_get_state(e, instruction_dst(iti->instr));
+               if (es < ESCAPE_GLOBAL_THROUGH_METHOD) {
+                       display_allocation(e, "****", iti->instr, es);
+               }
+               if (es == ESCAPE_GLOBAL_THROUGH_METHOD) {
+                       display_allocation(e, "!!!!", iti->instr, es);
+               }
+       }
+*/
+}
+
+static void escape_analysis_process_arguments(escape_analysis_t *e) {
+       s4 p;
+       s4 t;
+       s4 l;
+       s4 varindex;
+       methoddesc *md;
+       
+       md = e->jd->m->parseddesc;
+
+       for (p = 0, l = 0; p < md->paramcount; ++p) {
+               t = md->paramtypes[p].type;
+               varindex = e->jd->local_map[l * 5 + t];
+               if (t == TYPE_ADR) {
+                       if (varindex != UNUSED) {
+                               escape_analysis_ensure_state(e, varindex, ESCAPE_NONE);
+                               escape_analysis_set_is_argument(e, varindex);
+                       }
+                       e->adr_args_count += 1;
+               }
+               l += IS_2_WORD_TYPE(t) ? 2 : 1;
+       }
+}
+
+static void escape_analysis_export_arguments(escape_analysis_t *e) {
+       s4 p;
+       s4 t;
+       s4 l;
+       s4 varindex;
+       methoddesc *md;
+       u1 *paramescape;
+       
+       md = e->jd->m->parseddesc;
+
+       paramescape = MNEW(u1, e->adr_args_count);
+       e->jd->m->paramescape = paramescape;
+
+       for (p = 0, l = 0; p < md->paramcount; ++p) {
+               t = md->paramtypes[p].type;
+               varindex = e->jd->local_map[l * 5 + t];
+               if (t == TYPE_ADR) {
+                       if (varindex == UNUSED) {
+                               *paramescape = (u1)ESCAPE_NONE;
+                       } else {
+                               *paramescape = (u1)escape_analysis_get_state(e, varindex);
+                       }
+                       if (e->verbose) {
+                               printf("adr parameter %d: %s\n", p, escape_state_to_string((escape_state_t)*paramescape));
+                       }
+                       paramescape += 1;
+               }
+               l += IS_2_WORD_TYPE(t) ? 2 : 1;
+       }
+}
+
+static void escape_analysis_display(escape_analysis_t *e) {
+       instruction_list_item_t *iti;
+       var_extra_t *ve;
+       instruction *iptr;
+
+       FOR_EACH_INSTRUCTION_LIST(e->allocations, iti) {
+               iptr = iti->instr;
+               ve = var_extra_get(e, instruction_dst(iptr));
+               printf(
+                       "%s@%d: %s\n", 
+                       icmd_table[iptr->opc].name, 
+                       iptr->line, 
+                       escape_state_to_string(ve->escape_state)
+               );
+       }
+}
+
+void escape_analysis_perform(jitdata *jd) {
+       escape_analysis_t *e;
+
+       jd->m->flags |= ACC_METHOD_EA;
+
+       /*bc_escape_analysis_perform(jd->m);*/
+
+       e = DNEW(escape_analysis_t);
+       escape_analysis_init(e, jd);
+
+       if (e->verbose) 
+               printf("==== %s/%s ====\n", e->jd->m->clazz->name->text, e->jd->m->name->text);
+               
+       /*fprintf(stderr, ".");*/
+
+       escape_analysis_process_arguments(e);
+       escape_analysis_process_instructions(e);
+       escape_analysis_post_process_getfields(e);
+       escape_analysis_export_arguments(e);
+       if (e->verbose) escape_analysis_display(e);
+       escape_analysis_mark_allocations(e);
+
+       jd->m->flags &= ~ACC_METHOD_EA;
+}
+
index fb48421e21335dcce7765f9380b2df740c99f38d..0f8c1306dcb754a625a7291c8dda1c008fb1b983 100644 (file)
@@ -435,6 +435,7 @@ static void vars_copy_to_final(vars_t *vs, varinfo *dst) {
                if (dst->type == VAR_TYPE_SUBSTITUED) {
                        subst = vars_get_index(vars_resolve_subst(vs, it - vs->items));
                        dst->type = vs->items[subst].type;
+
                }
        }
 }
@@ -485,6 +486,15 @@ FIXME() inline void phis_print(const phis_t *ps) {
 }
 #endif
 
+static inline void phis_copy_to(const phis_t *ps, instruction *dst) {
+       MCOPY(
+               dst,
+               ps->items,
+               instruction,
+               ps->count
+       );
+}
+
 /*** state_array ************************************************************/
 
 typedef struct {
@@ -950,6 +960,7 @@ void fix_exception_handlers(jitdata *jd) {
                        exh->outvars[0] = v;
                        exh->predecessorcount = -1; /* legacy */
                        exh->flags = BBFINISHED;
+                       exh->method = jd->m;
 
                        basicblock_chain_add(&chain, exh);
 
@@ -1505,6 +1516,31 @@ static void ssa_enter_export_variables(ssa_info *ssa) {
 
 }
 
+static void ssa_enter_export_phis(ssa_info_t *ssa) {
+       basicblock *bptr;
+       basicblock_info_t *bbi;
+       instruction *dst;
+
+       FOR_EACH_BASICBLOCK(ssa->jd, bptr) {
+               bbi = bb_info(bptr);
+               if (bbi != NULL) {
+                       bptr->phicount = 
+                               bbi->locals->phis->count + 
+                               bbi->stack->phis->count;
+
+                       bptr->phis = DMNEW(instruction, bptr->phicount);
+
+                       dst = bptr->phis;
+
+                       phis_copy_to(bbi->locals->phis, dst);
+
+                       dst += bbi->locals->phis->count;
+
+                       phis_copy_to(bbi->stack->phis, dst);
+               }
+       }
+}
+
 /* TODO rename */
 static inline void ssa_enter_eliminate_category(ssa_info_t *ssa, s4 *pvar) {
        switch (vars_get_category(*pvar)) {
@@ -1622,6 +1658,7 @@ static basicblock *ssa_leave_create_transition_block_intern(
        bb->nr = ssa->jd->basicblockcount;
        ssa->jd->basicblockcount += 1;
        bb->mpc = -1;
+       bb->method = ssa->jd->m;
        bb->type = BBTYPE_STD;
        bb->icount = 
                reserved_insns + 
@@ -2051,10 +2088,14 @@ void yssa(jitdata *jd) {
 
        ssa_enter_export_variables(ssa);
 
+       ssa_enter_export_phis(ssa);
+
        ssa_enter_verify_no_redundant_phis(ssa);
 
        /*ssa_enter_create_phi_graph(ssa);*/
 
+       escape_analysis_perform(ssa->jd);
+
        ssa_leave_create_phi_moves(ssa);
 
        ssa_leave_create_exceptional_phi_moves(ssa);
index 34b529ba2d3b34d294f6a9f24ff6650e81903968..ff28286f477164a7b09e64f1c485a882b5ef8c6f 100644 (file)
@@ -107,8 +107,11 @@ struct methodinfo {                 /* method structure                       */
 #if defined(ENABLE_DEBUG_FILTER)
        u1            filtermatches;    /* flags indicating which filters the method matches */
 #endif
-};
 
+#if defined(ENABLE_ESCAPE)
+       u1           *paramescape;
+#endif
+};
 
 /* method_assumption ***********************************************************