--- /dev/null
+#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);
+}
+
+
--- /dev/null
+#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;
+}
+