AM_CONDITIONAL([ENABLE_SSA], test x"${ENABLE_SSA}" = "xyes")
AM_CONDITIONAL([ENABLE_ESCAPE], test x"${ENABLE_SSA}" = "xyes")
AM_CONDITIONAL([ENABLE_ESCAPE_CHECK], test x"${ENABLE_SSA}" = "xyes")
+AM_CONDITIONAL([ENABLE_TLH], 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])
AC_DEFINE([ENABLE_ESCAPE_CHECK], 1, [enable generating code to validate escape analysis results])
+ AC_DEFINE([ENABLE_TLH], 1, [enable thread local heap])
ENABLE_LSRA="no"
fi
libmm_la_LIBADD = \
$(GC_LIB)
+if ENABLE_TLH
+libmm_la_SOURCES += \
+ tlh.h \
+ tlh.c
+endif
## Local variables:
## mode: Makefile
--- /dev/null
+/* src/mm/tlh.c
+
+ Copyright (C) 2008
+ CACAOVM - Verein zu Foerderung der freien virtuellen Machine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include "config.h"
+
+#include "mm/memory.h"
+#include "mm/tlh.h"
+
+#include "vm/global.h"
+
+#include <assert.h>
+#include <sys/mman.h>
+
+static const int TLH_MAX_SIZE = (20 * 1024 * 1024);
+
+static inline bool tlh_avail(tlh_t *tlh, unsigned n) {
+ /*
+ --- --- --- ---
+ ^ end
+ ^ top
+ ^ top + 2
+ */
+ return (tlh->top + n) <= tlh->end;
+}
+
+void tlh_init(tlh_t *tlh) {
+
+ void *heap = mmap(
+ NULL,
+ TLH_MAX_SIZE,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE,
+ -1,
+ 0
+ );
+
+ if (heap == MAP_FAILED) {
+ /* The top pointer points to end, so all allocations will fail. */
+ tlh->start = NULL;
+ tlh->end = NULL;
+ tlh->top = NULL;
+ tlh->base = NULL;
+ } else {
+ tlh->start = (uint8_t *)heap;
+ tlh->top = tlh->start;
+ tlh->base = tlh->start;
+ tlh->end = tlh->start + TLH_MAX_SIZE;
+ }
+
+ tlh->overflows = 0;
+}
+
+void tlh_destroy(tlh_t *tlh) {
+ int res = munmap(tlh->start, TLH_MAX_SIZE);
+ if (res == -1) {
+ /* TODO */
+ assert(0);
+ }
+ tlh->start = NULL;
+ tlh->end = NULL;
+ tlh->top = NULL;
+ tlh->base = NULL;
+}
+
+void tlh_add_frame(tlh_t *tlh) {
+ if (tlh_avail(tlh, SIZEOF_VOID_P)) {
+ *(uint8_t **)tlh->top = tlh->base;
+ tlh->base = tlh->top;
+ tlh->top += SIZEOF_VOID_P;
+ } else {
+ tlh->overflows += 1;
+ }
+}
+
+void tlh_remove_frame(tlh_t *tlh) {
+ if (tlh->overflows > 0) {
+ tlh->overflows -= 1;
+ } else {
+ tlh->top = tlh->base;
+ tlh->base = *(uint8_t **)tlh->top;
+ }
+}
+
+void *tlh_alloc(tlh_t *tlh, size_t size) {
+ void *ret;
+ if (tlh_avail(tlh, size)) {
+ ret = tlh->top;
+ tlh->top += size;
+ MZERO(ret, char, size);
+ } else {
+ ret = NULL;
+ }
+ return ret;
+}
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
--- /dev/null
+/* src/mm/tlh.h
+
+ Copyright (C) 2008
+ CACAOVM - Verein zu Foerderung der freien virtuellen Machine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef _MM_TLH_H
+#define _MM_TLH_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+ uint8_t *start;
+ uint8_t *end;
+ uint8_t *top;
+ uint8_t *base;
+ unsigned overflows;
+} tlh_t;
+
+void tlh_init(tlh_t *tlh);
+
+void tlh_destroy(tlh_t *tlh);
+
+void tlh_add_frame(tlh_t *tlh);
+
+void tlh_remove_frame(tlh_t *tlh);
+
+void *tlh_alloc(tlh_t *tlh, size_t size);
+
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
t->suspendmutex = Mutex_new();
t->suspendcond = Condition_new();
+
+#if defined(ENABLE_TLH)
+ tlh_init(&(t->tlh));
+#endif
}
/* threads_impl_thread_clear ***************************************************
/* not really needed */
t->flc_object = NULL;
+
+#if defined(ENABLE_TLH)
+ tlh_destroy(&(t->tlh));
+ tlh_init(&(t->tlh));
+#endif
}
sched_yield();
}
+#if defined(ENABLE_TLH)
+
+void threads_tlh_add_frame() {
+ tlh_add_frame(&(THREADOBJECT->tlh));
+}
+
+void threads_tlh_remove_frame() {
+ tlh_remove_frame(&(THREADOBJECT->tlh));
+}
+
+#endif
/*
* These are local overrides for various environment variables in Emacs.
#include "mm/memory.h"
+#if defined(ENABLE_TLH)
+#include "mm/tlh.h"
+#endif
+
#include "native/localref.h"
#include "threads/condition.hpp"
#endif
+
/* current threadobject *******************************************************/
#if defined(HAVE___THREAD)
u4 tracejavacallcount;
#endif
+#if defined(ENABLE_TLH)
+ tlh_t tlh;
+#endif
+
+#if defined(ENABLE_ESCAPE_REASON)
+ void *escape_reasons;
+#endif
+
listnode_t linkage; /* threads-list */
listnode_t linkage_free; /* free-list */
};
void threads_thread_interrupt(threadobject *thread);
+#if defined(ENABLE_TLH)
+void threads_tlh_add_frame();
+void threads_tlh_remove_frame();
+#endif
+
#endif /* _THREAD_POSIX_H */
return o;
}
+#if defined(ENABLE_ESCAPE_REASON)
+java_handle_t *builtin_escape_reason_new(classinfo *c) {
+ print_escape_reasons();
+ return builtin_java_new(c);
+}
+#endif
+
+#if defined(ENABLE_TLH)
+java_handle_t *builtin_tlh_new(classinfo *c)
+{
+ java_handle_t *o;
+#if defined(ENABLE_RT_TIMING)
+ struct timespec time_start, time_end;
+#endif
+#if defined(ENABLE_CYCLES_STATS)
+ u8 cycles_start, cycles_end;
+#endif
+
+ RT_TIMING_GET_TIME(time_start);
+ CYCLES_STATS_GET(cycles_start);
+
+ /* is the class loaded */
+
+ assert(c->state & CLASS_LOADED);
+
+ /* check if we can instantiate this class */
+
+ if (c->flags & ACC_ABSTRACT) {
+ exceptions_throw_instantiationerror(c);
+ return NULL;
+ }
+
+ /* is the class linked */
+
+ if (!(c->state & CLASS_LINKED))
+ if (!link_class(c))
+ return NULL;
+
+ if (!(c->state & CLASS_INITIALIZED)) {
+#if !defined(NDEBUG)
+ if (initverbose)
+ log_message_class("Initialize class (from builtin_new): ", c);
+#endif
+
+ if (!initialize_class(c))
+ return NULL;
+ }
+
+ /*
+ o = tlh_alloc(&(THREADOBJECT->tlh), c->instancesize);
+ */
+ o = NULL;
+
+ if (o == NULL) {
+ o = heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS,
+ c->finalizer, true);
+ }
+
+ if (!o)
+ return NULL;
+
+#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
+ /* XXX this is only a dirty hack to make Boehm work with handles */
+
+ o = LLNI_WRAP((java_object_t *) o);
+#endif
+
+ LLNI_vftbl_direct(o) = c->vftbl;
+
+#if defined(ENABLE_THREADS)
+ lock_init_object_lock(LLNI_DIRECT(o));
+#endif
+
+ CYCLES_STATS_GET(cycles_end);
+ RT_TIMING_GET_TIME(time_end);
+
+/*
+ CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start);
+ RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT);
+*/
+
+ return o;
+#endif
+}
/* builtin_java_new ************************************************************
/* NOT AN OP */
java_handle_t *builtin_java_new(java_handle_t *c);
#define BUILTIN_new (functionptr) builtin_java_new
+
+#if defined(ENABLE_TLH)
+#define BUILTIN_tlh_new (functionptr) builtin_tlh_new
+java_handle_t *builtin_tlh_new(classinfo *c);
+#endif
+
+#if defined(ENABLE_ESCAPE_REASON)
+#define BUILTIN_escape_reason_new (functionptr)builtin_escape_reason_new
+java_handle_t *builtin_escape_reason_new(classinfo *c);
+#endif
+
java_object_t *builtin_fast_new(classinfo *c);
#define BUILTIN_FAST_new (functionptr) builtin_fast_new
NULL,
NULL
},
+
+#if defined(ENABLE_TLH)
+ {
+ ICMD_NEW,
+ BUILTINTABLE_FLAG_STUB,
+ BUILTIN_tlh_new,
+ NULL,
+ NULL,
+ "tlh_new",
+ "(Ljava/lang/Class;)Ljava/lang/Object;",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ },
+#endif
+
+#if defined(ENABLE_ESCAPE_REASON)
+ {
+ ICMD_NEW,
+ BUILTINTABLE_FLAG_STUB,
+ BUILTIN_escape_reason_new,
+ NULL,
+ NULL,
+ "escape_reason_new",
+ "(Ljava/lang/Class;)Ljava/lang/Object;",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ },
+#endif
+
{
ICMD_NEW,
0,
#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 */
+#define ACC_METHOD_MONOMORPHY_USED \
+ 0x00100000
+#define ACC_METHOD_PARENT_MONOMORPHY_USED \
+ 0x00200000
/* data structures of the runtime system **************************************/
}
}
+void cfg_remove_root(jitdata *jd) {
+ basicblock *root, *zero, *it;
+
+ root = jd->basicblocks;
+ zero = root->next;
+
+ zero->predecessorcount -= 1;
+
+ jd->basicblocks = zero;
+
+ for (it = zero; it; it = it->next) {
+ it->nr -= 1;
+ }
+}
+
#if defined(ENABLE_SSA)
static void cfg_eliminate_edges_to_unreachable(jitdata *jd);
#define CODE_FLAG_INVALID 0x0001
#define CODE_FLAG_LEAFMETHOD 0x0002
#define CODE_FLAG_SYNCHRONIZED 0x0004
+#define CODE_FLAG_TLH 0x0008
/* codeinfo *******************************************************************
void emit_exception_check(codegendata *cd, instruction *iptr);
void emit_trap_compiler(codegendata *cd);
+void emit_trap_countdown(codegendata *cd, s4 *counter);
uint32_t emit_trap(codegendata *cd);
void emit_patcher_traps(jitdata *jd);
if (bptr->bitflags & BBFLAG_REPLACEMENT) {
if (cd->replacementpoint[-1].flags & RPLPOINT_FLAG_COUNTDOWN) {
MCODECHECK(32);
- disp = (s4) &(m->hitcountdown);
- M_ISUB_IMM_MEMABS(1, disp);
- M_BS(0);
+ emit_trap_countdown(cd, &(m->hitcountdown));
}
}
#endif
break;
case ICMD_PUTSTATIC: /* ..., value ==> ... */
-
+
if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
uf = iptr->sx.s23.s3.uf;
fieldtype = uf->fieldref->parseddesc.fd->type;
bte = iptr->sx.s23.s3.bte;
md = bte->md;
+
+#if defined(ENABLE_ESCAPE_REASON)
+ if (bte->fp == BUILTIN_escape_reason_new) {
+ void set_escape_reasons(void *);
+ M_ASUB_IMM(8, REG_SP);
+ M_MOV_IMM(iptr->escape_reasons, REG_ITMP1);
+ M_AST(EDX, REG_SP, 4);
+ M_AST(REG_ITMP1, REG_SP, 0);
+ M_MOV_IMM(set_escape_reasons, REG_ITMP1);
+ M_CALL(REG_ITMP1);
+ M_ALD(EDX, REG_SP, 4);
+ M_AADD_IMM(8, REG_SP);
+ }
+#endif
+
goto gen_method;
case ICMD_INVOKESTATIC: /* ..., [arg1, [arg2 ...]] ==> ... */
M_ALD_MEM(REG_METHODPTR, TRAP_COMPILER);
}
+/* emit_trap_countdown *********************************************************
+
+ Emit a countdown trap.
+
+ counter....absolute address of the counter variable
+
+*******************************************************************************/
+
+void emit_trap_countdown(codegendata *cd, s4 *counter)
+{
+ M_ISUB_IMM_MEMABS(1, (s4) counter);
+ M_BNS(6);
+ M_ALD_MEM(REG_METHODPTR, TRAP_COUNTDOWN);
+}
/* emit_trap *******************************************************************
_mc->gregs[REG_EIP] = (uintptr_t) p;
}
}
+#if defined(ENABLE_REPLACEMENT)
+ else if (type == TRAP_COUNTDOWN) {
+ /* context has been written by md_replace_executionstate_write */
+ }
+#endif
else {
_mc->gregs[REG_EAX] = (uintptr_t) p;
_mc->gregs[REG_ECX] = (uintptr_t) xpc; /* REG_ITMP2_XPC */
/* Don't use 8 (could be a normal load offset). */
TRAP_COMPILER = 9,
+ TRAP_COUNTDOWN = 10,
TRAP_END
};
jd->flags |= JITDATA_FLAG_VERBOSECALL;
#if defined(ENABLE_REPLACEMENT) && defined(ENABLE_INLINING)
- if (opt_Inline)
+ if (opt_Inline && (jd->m->hitcountdown > 0) && (jd->code->optlevel == 0)) {
jd->flags |= JITDATA_FLAG_COUNTDOWN;
+ }
#endif
#if defined(ENABLE_JIT)
#endif
RT_TIMING_GET_TIME(time_typecheck);
-#if defined(ENABLE_SSA)
- if (opt_lsra) {
- fix_exception_handlers(jd);
- }
-#endif
-
- /* Build the CFG. This has to be done after stack_analyse, as
- there happens the JSR elimination. */
-
- if (!cfg_build(jd))
- return NULL;
-
#if defined(ENABLE_LOOP)
if (opt_loops) {
depthFirst(jd);
/* inlining */
-#if defined(ENABLE_INLINING)
+#if defined(ENABLE_INLINING) && (!defined(ENABLE_ESCAPE) || 1)
if (JITDATA_HAS_FLAG_INLINE(jd)) {
if (!inline_inline(jd))
return NULL;
}
#endif
+#if defined(ENABLE_SSA)
+ if (opt_lsra) {
+ fix_exception_handlers(jd);
+ }
+#endif
+
+ /* Build the CFG. This has to be done after stack_analyse, as
+ there happens the JSR elimination. */
+
+ if (!cfg_build(jd))
+ return NULL;
+
#if defined(ENABLE_PROFILING)
/* Basic block reordering. I think this should be done after
if-conversion, as we could lose the ability to do the
#if defined(ENABLE_SSA)
/* allocate registers */
if (
- (opt_lsra)
- /*&& strncmp(jd->m->name->text, "banana", 6) == 0*/
+ (opt_lsra &&
+ jd->code->optlevel > 0)
+ /* strncmp(jd->m->name->text, "hottie", 6) == 0*/
/*&& jd->exceptiontablelength == 0*/
) {
- /* printf("=== %s ===\n", jd->m->name->text); */
+ /*printf("=== %s ===\n", jd->m->name->text);*/
jd->ls = DNEW(lsradata);
jd->ls = NULL;
ssa(jd);
/*lsra(jd);*/ regalloc(jd);
- eliminate_subbasicblocks(jd);
+ /*eliminate_subbasicblocks(jd);*/
STATISTICS(count_methods_allocated_by_lsra++);
} else
#if SIZEOF_VOID_P == 4
flags_operand_t flags; /* 4 bytes */
#endif
+#if defined(ENABLE_ESCAPE_REASON)
+ void *escape_reasons;
+#endif
};
#define FOR_EACH_INSTRUCTION(bptr, it) \
for ((it) = (bptr)->iinstr; (it) != (bptr)->iinstr + (bptr)->icount; ++(it))
+#define FOR_EACH_INSTRUCTION_REV(bptr, it) \
+ for ((it) = (bptr)->iinstr + (bptr)->icount - 1; (it) != (bptr)->iinstr - 1; --(it))
+
#if defined(ENABLE_SSA)
#define FOR_EACH_EXHANDLER(bptr, it) \
op_stack_slot_t *ptr;
op_stack_slot_t *bottom;
unsigned max;
+ bool *perror_flag;
} 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) {
+static void op_stack_init(op_stack_t *stack, unsigned max, bool *perror_flag) {
op_stack_slot_t *it;
stack->elements = DMNEW(op_stack_slot_t, max * 2);
stack->ptr = stack->start;
stack->bottom = stack->start;
+
+ stack->perror_flag = perror_flag;
+}
+
+static void op_stack_set_error(op_stack_t *stack) {
+ *(stack->perror_flag) = true;
+#if BC_ESCAPE_VERBOSE
+ printf("%s: error.\n", __FUNCTION__);
+#endif
+}
+
+static bool op_stack_test_position(op_stack_t *stack, op_stack_slot_t *pos) {
+ if (!(stack->elements <= pos)) {
+ op_stack_set_error(stack);
+ return false;
+ } else if (!(pos < stack->end)) {
+ op_stack_set_error(stack);
+ return false;
+ } else {
+ return true;
+ }
}
static void op_stack_reset(op_stack_t *stack) {
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);
+ if (! op_stack_test_position(stack, stack->ptr)) {
+ return OP_STACK_SLOT_UNKNOWN;
+ }
ret = *(stack->ptr);
if (stack->ptr < stack->bottom) {
stack->bottom = stack->ptr;
}
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;
+ if (op_stack_test_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);
+ if (op_stack_test_position(stack, stack->ptr - offset)) {
+ return *(stack->ptr - offset);
+ } else {
+ return OP_STACK_SLOT_UNKNOWN;
+ }
}
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;
+ if (op_stack_test_position(stack, stack->ptr - offset)) {
+ *(stack->ptr - offset) = value;
+ }
}
static inline void op_stack_push_unknown(op_stack_t *stack) {
u1 *pos;
u1 *instruction_start;
s4 offset;
+ bool *perror_flag;
} jcode_t;
-static void jcode_init(jcode_t *jc, u1 *start, s4 length, s4 offset) {
+static void jcode_init(jcode_t *jc, u1 *start, s4 length, s4 offset, bool *perror_flag) {
jc->start = start;
jc->end = jc->start + length;
jc->pos = jc->start;
jc->offset = offset;
+ jc->perror_flag = perror_flag;
+}
+
+static void jcode_set_error(jcode_t *jc) {
+ *(jc->perror_flag) = true;
+#if BC_ESCAPE_VERBOSE
+ printf("%s: error.\n", __FUNCTION__);
+#endif
}
static void jcode_move_to_index(jcode_t *jc, s4 index) {
return jc->offset + (jc->pos - jc->start);
}
-#define jcode_assert_has_bytes(jc, n) \
- assert((jc->pos + n) <= jc->end)
+bool jcode_test_has_bytes(jcode_t *jc, s4 n) {
+ if ((jc->pos + n) <= jc->end) {
+ return true;
+ } else {
+ jcode_set_error(jc);
+ return false;
+ }
+}
static u1 jcode_get_u1(jcode_t *jc) {
u1 ret;
- jcode_assert_has_bytes(jc, 1);
- ret = jc->pos[0];
- jc->pos += 1;
+ if (jcode_test_has_bytes(jc, 1)) {
+ ret = jc->pos[0];
+ jc->pos += 1;
+ } else {
+ ret = 0;
+ }
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;
+ if (jcode_test_has_bytes(jc, 2)) {
+ ret = (jc->pos[0] << 8) | (jc->pos[1]);
+ jc->pos += 2;
+ } else {
+ ret = 0;
+ }
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;
+ if (jcode_test_has_bytes(jc, 2)) {
+ ret = (jc->pos[0] << 8) | (jc->pos[1]);
+ jc->pos += 2;
+ } else {
+ ret = 0;
+ }
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;
+ if (jcode_test_has_bytes(jc, 4)) {
+ ret = (jc->pos[0] << 24) | (jc->pos[1] << 16) | (jc->pos[2] << 8) | (jc->pos[3]);
+ jc->pos += 4;
+ } else {
+ ret = 0;
+ }
return ret;
}
static s4 jcode_get_fall_through_target(jcode_t *jc) {
int length = bytecode[*jc->instruction_start].length;
- assert(length > 0);
+ if (length <= 0) {
+ jcode_set_error(jc);
+ }
return jc->offset + (jc->instruction_start - jc->start) + length;
}
bool verbose;
#endif
int depth;
+
+ bool fatal_error;
} bc_escape_analysis_t;
static void bc_escape_analysis_perform_intern(methodinfo *m, int depth);
int a;
u1 *ite;
u1 t;
- int ret_adr;
unsigned n;
+ int ret_val_is_adr;
be->method = m;
be->stack = DNEW(op_stack_t);
- op_stack_init(be->stack, m->maxstack);
+ op_stack_init(be->stack, m->maxstack, &(be->fatal_error));
be->basicblocks = DNEW(basicblock_work_list_t);
basicblock_work_list_init(be->basicblocks);
assert(l == be->local_to_adr_param_size);
- /* Determine whether return type is address */
-
- ret_adr = m->parseddesc->returntype.type == TYPE_ADR ? 1 : 0;
+ ret_val_is_adr = m->parseddesc->returntype.type == TYPE_ADR ? 1 : 0;
/* Allocate param_escape on heap. */
be->param_escape_size = a;
- n = a + ret_adr;
+ n = a + ret_val_is_adr;
if (n == 0) {
/* Use some non-NULL value. */
be->param_escape = (u1 *)1;
} else {
be->param_escape = MNEW(u1, n);
+ be->param_escape += ret_val_is_adr;
}
for (ite = be->param_escape; ite != be->param_escape + n; ++ite) {
- *ite = (u1)ESCAPE_NONE;
+ *ite = escape_state_to_u1(ESCAPE_NONE);
}
- be->param_escape += ret_adr;
+ if (ret_val_is_adr) {
+ be->param_escape[-1] = escape_state_to_u1(ESCAPE_NONE);
+ }
be->adr_param_dirty = DNEW(bit_vector_t);
bit_vector_init(be->adr_param_dirty, a);
#endif
be->depth = depth;
+
+ be->fatal_error = false;
}
static void bc_escape_analysis_branch_target(bc_escape_analysis_t *be, s4 branch_target) {
parameters. */
if (
- old < ESCAPE_GLOBAL_THROUGH_METHOD &&
- escape_state >= ESCAPE_GLOBAL_THROUGH_METHOD
+ old < ESCAPE_GLOBAL &&
+ escape_state >= ESCAPE_GLOBAL
) {
be->non_escaping_adr_params -= 1;
}
bc_escape_analysis_dirty(be, local + 1);
}
-static void bc_escape_analyisis_returned(bc_escape_analysis_t *be, op_stack_slot_t value) {
+static void bc_escape_analysis_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. */
+ /* The escape state of the return value will be adjusted later. */
+ } else {
+ /* Adjust escape state of return value. */
if (be->method->parseddesc->returntype.type == TYPE_ADR) {
- be->param_escape[-1] = (u1)ESCAPE_GLOBAL;
+ be->param_escape[-1] = escape_state_to_u1(ESCAPE_GLOBAL);
}
+ bc_escape_analysis_adjust_state(be, value, ESCAPE_GLOBAL);
}
}
}
}
+static void bc_escape_analysis_push_return_value(
+ bc_escape_analysis_t *be,
+ methoddesc *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_adjust_invoke_parameters(
bc_escape_analysis_t *be,
methodinfo *mi
methoddesc *md = mi->parseddesc;
u1 *paramescape = mi->paramescape;
s4 stack_depth = md->paramslots;
+ unsigned num_params_returned = 0;
+ op_stack_slot_t param_returned;
/* 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:
+ if (*paramescape & 0x80) {
+ num_params_returned += 1;
+ param_returned = op_stack_get(be->stack, stack_depth);
+ }
bc_escape_analysis_adjust_state(
be,
op_stack_get(be->stack, stack_depth),
- (escape_state_t)*(paramescape++)
+ escape_state_from_u1(*paramescape++)
);
stack_depth -= 1;
break;
for (i = 0; i < md->paramslots; ++i) {
op_stack_pop(be->stack);
}
+
+ /* Push return value. */
+ if (md->returntype.type == TYPE_ADR) {
+ if ((num_params_returned == 1) && (mi->paramescape[-1] < ESCAPE_GLOBAL)) {
+ /* Only a single argument can be returned by the method,
+ and the retun value does not escape otherwise. */
+ op_stack_push(be->stack, param_returned);
+ } else {
+ op_stack_push_unknown(be->stack);
+ }
+ } else {
+ bc_escape_analysis_push_return_value(be, md);
+ }
}
static void bc_escape_analysis_escape_invoke_parameters(
for (i = 0; i < md->paramslots; ++i) {
bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
}
+
+ bc_escape_analysis_push_return_value(be, md);
}
static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *jc) {
or recurse into callee.
Otherwise we must assume, that all parameters escape. */
- if (mi != NULL) {
+ if (mi != NULL && escape_is_monomorphic(be->method, mi)) {
if (mi->paramescape == NULL) {
bc_escape_analysis_perform_intern(mi, be->depth + 1);
} 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(
/* TODO end if all parameters escape */
/* TODO move code into process_instruction or the like */
- while ((! jcode_end(jc)) && (! bb_end)) {
+ while ((! jcode_end(jc)) && (! bb_end) && (! be->fatal_error)) {
jcode_record_instruction_start(jc);
case BC_areturn:
/* FIXME */
- bc_escape_analyisis_returned(be, op_stack_pop(be->stack));
+ bc_escape_analysis_returned(be, op_stack_pop(be->stack));
bb_end = true;
break;
}
#endif
- while (! op_stack_is_empty(be->stack)) {
+ while ((! op_stack_is_empty(be->stack)) && (! be->fatal_error)) {
#if BC_ESCAPE_VERBOSE
if (be->verbose) {
dprintf(be->depth, "Stack element: ");
}
#endif
}
+
+ if (be->fatal_error) {
+#if BC_ESCAPE_VERBOSE
+ if (be->verbose) {
+ printf("Fatal error while processing basic block. Aborting.\n");
+ }
+#endif
+ assert(0);
+ }
}
static void bc_escape_analysis_adjust_return_value(bc_escape_analysis_t *be) {
- escape_state_t e, pe;
+ escape_state_t re, pe;
int i;
/* Only calculate, if return value is of type address. */
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[i] |= 0x80;
+
+ pe = escape_state_from_u1(be->param_escape[i]);
+ re = escape_state_from_u1(be->param_escape[-1]);
+
+ if (pe > re) {
+ be->param_escape[-1] = escape_state_to_u1(pe);
}
}
}
-
- be->param_escape[-1] = (u1)e;
}
static void bc_escape_analysis_analyze(bc_escape_analysis_t *be) {
&jc,
be->method->jcode,
be->method->jcodelength,
- 0
+ 0,
+ &(be->fatal_error)
);
/* Process basicblock by basicblock. */
}
#endif
+ if (depth >= 3) {
+ return;
+ }
+
if (m->paramescape != NULL) {
#if BC_ESCAPE_VERBOSE
if (verbose) {
return;
}
- /* TODO threshold */
+ if (m->jcodelength > 250) {
+#if BC_ESCAPE_VERBOSE
+ if (verbose) {
+ dprintf(depth, "Bytecode too long: %d.\n", m->jcodelength);
+ }
+#endif
+ return;
+ }
be = DNEW(bc_escape_analysis_t);
bc_escape_analysis_init(be, m, verbose, depth);
-/* src/vm/optimizing/escape.c
+/* src/vm/optimizing/e&scape.c
Copyright (C) 2008
CACAOVM - Verein zu Foerderung der freien virtuellen Machine CACAO
#include "vm/jit/jit.h"
#include "vmcore/class.h"
+#include "vmcore/classcache.h"
#include "vm/jit/optimizing/escape.h"
+#include <stdarg.h>
+
+#if defined(ENABLE_ESCAPE_REASON)
+#define ENABLE_REASON
+#endif
+
+#if defined(ENABLE_REASON)
+#define I2(why, tov, es) escape_analysis_record_reason(e, why, iptr, tov, es);
+#else
+#define I2(why, tov, es)
+#endif
+#define I(why, to, from) I2(why, instruction_ ## to (iptr), escape_analysis_get_state(e, instruction_ ## from (iptr)))
+#define E2(why, var) I2(why, var, ESCAPE_GLOBAL)
+#define E(why, which) E2(why, instruction_ ## which (iptr))
+
+typedef enum {
+ RED = 31,
+ GREEN,
+ YELLOW,
+ BLUE,
+ MAGENTA,
+ CYAN,
+ WHITE,
+ COLOR_END
+} color_t;
+
+#define ENABLE_COLOR
+
+static void color_start(color_t color) {
+#if defined(ENABLE_COLOR)
+ if (RED <= color && color < COLOR_END) {
+ printf("\033[%dm", color);
+ }
+#endif
+}
+
+static void color_end() {
+#if defined(ENABLE_COLOR)
+ printf("\033[m");
+ fflush(stdout);
+#endif
+}
+
+static void color_printf(color_t color, const char *fmt, ...) {
+ va_list ap;
+ color_start(color);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ color_end();
+}
+
+
/*** escape_state *************************************************************/
const char *escape_state_to_string(escape_state_t escape_state) {
str(ESCAPE_UNKNOWN)
str(ESCAPE_NONE)
str(ESCAPE_METHOD)
- str(ESCAPE_GLOBAL_THROUGH_METHOD)
+ str(ESCAPE_METHOD_RETURN)
str(ESCAPE_GLOBAL)
default: return "???";
}
instruction_list_t *allocations;
instruction_list_t *getfields;
+ instruction_list_t *monitors;
+ instruction_list_t *returns;
struct var_extra **var;
/*** var_extra ***************************************************************/
+#if defined(ENABLE_REASON)
+typedef struct reason {
+ const char *why;
+ instruction *iptr;
+ struct reason *next;
+} reason_t;
+#endif
+
typedef struct var_extra {
instruction *allocation;
escape_state_t escape_state;
s4 representant;
dependency_list_t *dependency_list;
- bool is_arg; /* TODO optimize */
+ unsigned contains_arg:1;
+ unsigned contains_only_args:1;
+ /*signed adr_arg_num:30;*/
+#if defined(ENABLE_REASON)
+ reason_t *reasons;
+#endif
} var_extra_t;
static void var_extra_init(var_extra_t *ve) {
ve->escape_state = ESCAPE_NONE;
ve->representant = -1;
ve->dependency_list = NULL;
- ve->is_arg = false;
+ ve->contains_arg = false;
+ ve->contains_only_args = false;
+ /*ve->adr_arg_num = -1;*/
+#if defined(ENABLE_REASON)
+ ve->reasons = NULL;
+#endif
}
static inline var_extra_t *var_extra_get_no_alloc(const escape_analysis_t *e, s4 var) {
e->getfields = DNEW(instruction_list_t);
instruction_list_init(e->getfields);
+ e->monitors = DNEW(instruction_list_t);
+ instruction_list_init(e->monitors);
+
+ e->returns = DNEW(instruction_list_t);
+ instruction_list_init(e->returns);
+
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;
+ e->verbose = strcmp(jd->m->name->text, "<init>") == 0;
+ e->verbose = getenv("EV") != NULL;
+}
+
+#if defined(ENABLE_REASON)
+static void escape_analysis_record_reason(escape_analysis_t *e, const char *why, instruction *iptr, s4 var, escape_state_t es) {
+ var_extra_t *ve;
+ reason_t *re;
+ if (es == ESCAPE_GLOBAL || es == ESCAPE_METHOD_RETURN) {
+ var = var_extra_get_representant(e, var);
+ ve = var_extra_get(e, var);
+ re = NEW(reason_t);
+ re->why = why;
+ re->iptr= iptr;
+ re->next = ve->reasons;
+ ve->reasons = re;
+ if (e->verbose) {
+ printf("%d escapes because %s\n", var, why);
+ }
+ }
}
+#endif
static void escape_analysis_set_allocation(escape_analysis_t *e, s4 var, instruction *iptr) {
var_extra_get(e, var)->allocation = iptr;
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 void escape_analysis_set_contains_argument(escape_analysis_t *e, s4 var) {
+ var = var_extra_get_representant(e, var);
+ var_extra_get(e, var)->contains_arg = true;
+}
+
+static bool escape_analysis_get_contains_argument(escape_analysis_t *e, s4 var) {
+ var = var_extra_get_representant(e, var);
+ return var_extra_get(e, var)->contains_arg;
+}
+
+static void escape_analysis_set_contains_only_arguments(escape_analysis_t *e, s4 var) {
+ var = var_extra_get_representant(e, var);
+ var_extra_get(e, var)->contains_only_args = true;
+}
+
+static bool escape_analysis_get_contains_only_arguments(escape_analysis_t *e, s4 var) {
+ var = var_extra_get_representant(e, var);
+ return var_extra_get(e, var)->contains_only_args;
+}
+
+/*
+static void escape_analysis_set_adr_arg_num(escape_analysis_t *e, s4 var, s4 num) {
+ var_extra_get(e, var)->adr_arg_num = num;
+}
+
+static s4 escape_analysis_get_adr_arg_num(escape_analysis_t *e, s4 var) {
+ return var_extra_get(e, var)->adr_arg_num;
}
+*/
-static bool escape_analysis_get_is_argument(escape_analysis_t *e, s4 var) {
- return var_extra_get(e, var)->is_arg;
+static bool escape_analysis_in_same_set(escape_analysis_t *e, s4 var1, s4 var2) {
+ return var_extra_get_representant(e, var1) == var_extra_get_representant(e, var2);
}
static void escape_analysis_ensure_state(escape_analysis_t *e, s4 var, escape_state_t escape_state) {
dependency_list_item_get_dependency(it),
escape_state
);
+ {
+ instruction *iptr = NULL;
+ I2("propagated by dependency", dependency_list_item_get_dependency(it), escape_state);
+ }
}
}
}
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);
return;
}
+ if (e->verbose) printf("Merging (%d,%d)\n", var1, var2);
+
ve1 = var_extra_get(e, var1);
ve2 = var_extra_get(e, var2);
ve2->representant = var1;
- /* Adjust is_argument to logical or. */
+ /* Adjust is_arg to logical or. */
- has_become_arg = ve1->is_arg != ve2->is_arg;
- ve1->is_arg = ve1->is_arg || ve2->is_arg;
+ has_become_arg = ve1->contains_arg != ve2->contains_arg;
+ ve1->contains_arg = ve1->contains_arg || ve2->contains_arg;
if (e->verbose && has_become_arg) printf("(%d,%d) has become arg.\n", var1, var2);
dependency_list_item_get_dependency(itd),
ESCAPE_GLOBAL
);
+ {
+ instruction *iptr = NULL;
+ E2("has become arg", dependency_list_item_get_dependency(itd));
+ }
}
}
+
+ /* Adjust contains_only_args to logical and. */
+
+ ve1->contains_only_args = ve1->contains_only_args && ve2->contains_only_args;
+
+ /* Adjust address argument number contained in this var. */
+
+ /*
+ if (ve1->adr_arg_num != ve2->adr_arg_num) {
+ ve1->adr_arg_num = -1;
+ }
+ */
+#if defined(ENABLE_REASON)
+ if (ve1->reasons) {
+ reason_t *re = ve1->reasons;
+ while (re->next != NULL) {
+ re = re->next;
+ }
+ re->next = ve2->reasons;
+ } else {
+ ve1->reasons = ve2->reasons;
+ }
+#endif
}
static void escape_analysis_add_dependency(escape_analysis_t *e, instruction *store) {
dependency_list_add(dl, store);
if (e->verbose) {
- printf("dependency_list_add\n");
+ printf("dependency_list_add: %d.dependency_list.add( { ", obj);
+ show_icmd(e->jd, store, 0, 3);
+ printf(" } )\n");
}
}
u1 *paramescape;
unsigned i;
instruction **iarg;
- constant_FMIref *fmi;
methodinfo *mi;
- resolve_result_t result;
+ escape_state_t es;
+ const char *why;
if (e->verbose) {
- printf("processing %s@%d\n", icmd_table[iptr->opc].name, iptr->line);
+ color_start(CYAN);
+ printf("%d: ", iptr->line);
+ show_icmd(e->jd, iptr, 0, 3);
+ color_end();
+ printf("\n");
}
switch (instruction_get_opcode(iptr)) {
if (c == NULL) {
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
- if (e->verbose) printf("1\n");
+ E("unresolved class", dst)
} else if (c->finalizer != NULL) {
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
- if (e->verbose) printf("3\n");
+ E("finalizer", dst)
} 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_MONITORENTER:
+ case ICMD_MONITOREXIT:
+
+ instruction_list_add(e->monitors, 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);
-
+ E("untracked array", dst)
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);
+ E("putstatic", s1)
}
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))) {
+ if (escape_analysis_get_contains_argument(e, instruction_s1(iptr))) {
escape_analysis_ensure_state(e, instruction_s2(iptr), ESCAPE_GLOBAL);
+ /* If s1 is currently not an argument, but can contain one later because
+ of a phi function, the merge function takes care to make all
+ dependencies escape globally. */
+ E("putfield into argument", s2)
} else {
+ I("putfield inherit", s2, s1);
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))) {
+ if (escape_analysis_get_contains_argument(e, instruction_s1(iptr))) {
+ if (e->verbose) printf("Contains argument.\n");
escape_analysis_ensure_state(e, instruction_s3(iptr), ESCAPE_GLOBAL);
+ E("aastore into argument", s3)
} else {
+ if (e->verbose) printf("Contains no argument.\n");
+ I("aastore", s3, s1)
escape_analysis_ensure_state(e, instruction_s3(iptr), escape_analysis_get_state(e, instruction_s1(iptr)));
escape_analysis_add_dependency(e, iptr);
}
case ICMD_GETSTATIC:
if (instruction_field_type(iptr) == TYPE_ADR) {
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+ E("loaded from static var", dst)
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))) {
+ if (escape_analysis_get_contains_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);
+ E("loaded from arg", dst)
} else {
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
}
break;
case ICMD_ARRAYLENGTH:
- /*
- escape_analysis_assert_has_escape(e, instruction_s1(iptr));
- */
+ escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
break;
case ICMD_AALOAD:
- /*
- escape_analysis_assert_has_escape(e, instruction_s1(iptr));
- */
- if (escape_analysis_get_is_argument(e, instruction_s1(iptr))) {
+ if (escape_analysis_get_contains_argument(e, instruction_s1(iptr))) {
/* If store into argument, escapes globally. See ICMD_GETFIELD. */
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+ E("aaload from argument", dst)
} else {
escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_NONE);
}
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:
+ escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+ break;
+
case ICMD_CHECKNULL:
- /*
- escape_analysis_assert_has_escape(e, instruction_s1(iptr));
- */
escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD);
+ escape_analysis_merge(e, instruction_s1(iptr), instruction_dst(iptr));
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;
count = instruction_arg_count(iptr);
mi = instruction_local_methodinfo(iptr);
paramescape = NULL;
+ why = "???";
if (mi != NULL) {
/* If the method could be resolved, it already is. */
paramescape = mi->paramescape;
+ if (e->verbose) {
+ if (paramescape) {
+ printf("Paramescape for callee available.\n");
+ }
+ }
+
+ if (paramescape) why = "Available param escape";
+
if (paramescape == NULL) {
if (e->verbose) {
printf("BC escape analyzing callee.\n");
}
+ why = "BC param escape";
bc_escape_analysis_perform(mi);
paramescape = mi->paramescape;
}
if (e->verbose) {
printf("Unresolved callee.\n");
}
+ why = "Unresolved callee";
+ }
+
+ if (iptr->opc == ICMD_INVOKEVIRTUAL || iptr->opc == ICMD_INVOKEINTERFACE) {
+ if (mi != NULL && !escape_is_monomorphic(e->jd->m, mi)) {
+ if (e->verbose) {
+ printf("Not monomorphic.\n");
+ }
+ why = "Polymorphic";
+ paramescape = NULL;
+ }
}
+ /* Set the escape state of the return value.
+ This is: global if we down have information of the callee, or the callee
+ supplied escape state. */
+
+ if (instruction_return_type(iptr) == TYPE_ADR) {
+ if (paramescape == NULL) {
+ escape_analysis_ensure_state(e, instruction_dst(iptr), ESCAPE_GLOBAL);
+ E(why, dst);
+ } else {
+ es = escape_state_from_u1(paramescape[-1]);
+ I2(why, instruction_dst(iptr), es)
+ escape_analysis_ensure_state(e, instruction_dst(iptr), es);
+ }
+ escape_analysis_set_allocation(e, instruction_dst(iptr), iptr);
+ }
+
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
+ ESCAPE_GLOBAL
);
- } else if ((escape_state_t)*paramescape < ESCAPE_METHOD) {
- escape_analysis_ensure_state(e, instruction_arg(iptr, i), ESCAPE_METHOD);
+ E2(why, instruction_arg(iptr, i));
+ } else if (escape_state_from_u1(*paramescape) <= ESCAPE_METHOD) {
+ es = escape_state_from_u1(*paramescape);
+
+ if (es < ESCAPE_METHOD) {
+ es = ESCAPE_METHOD;
+ }
+
+ I2(why, instruction_arg(iptr, i), es);
+ escape_analysis_ensure_state(e, instruction_arg(iptr, i), es);
+
+ if (*paramescape & 0x80) {
+ /* Parameter can be returned from method.
+ This creates an alias to the retur value.
+ If the return value escapes, the ES of the parameter needs
+ to be adjusted. */
+ escape_analysis_merge(e, instruction_arg(iptr, i), instruction_dst(iptr));
+ I2("return alias", instruction_arg(iptr, i), instruction_dst(iptr));
+ }
} else {
- escape_analysis_ensure_state(e, instruction_arg(iptr, i), (escape_state_t)*paramescape);
+ escape_analysis_ensure_state(e, instruction_arg(iptr, i), ESCAPE_GLOBAL);
+ E2(why, instruction_arg(iptr, i));
}
if (paramescape != NULL) {
}
}
- 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);
+ E("throw", s1)
break;
case ICMD_ARETURN:
- /*
- escape_analysis_assert_has_escape(e, instruction_s1(iptr));
- */
- escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_GLOBAL);
+ /* If we return only arguments, the return value escapes only the method.
+ ESCAPE_METHOD for now, and check later, if a different value than an
+ argument is possibly returned. */
+ escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_METHOD_RETURN);
+ E("return", s1)
+ instruction_list_add(e->returns, iptr);
break;
case ICMD_ALOAD:
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);
}
FOR_EACH_BASICBLOCK(e->jd, bptr) {
+ if (e->verbose) {
+ color_printf(CYAN, "=== BB %d ===\n", bptr->nr);
+ }
+
for (iptr = bptr->phis; iptr != bptr->phis + bptr->phicount; ++iptr) {
escape_analysis_process_instruction(e, iptr);
}
}
}
+static void escape_analysis_post_process_returns(escape_analysis_t *e) {
+ instruction_list_item_t *iti;
+ instruction *iptr;
+
+ if (e->verbose) printf("Post processing returns:\n");
+
+ FOR_EACH_INSTRUCTION_LIST(e->getfields, iti) {
+ iptr = iti->instr;
+
+ if (! escape_analysis_get_contains_only_arguments(e, instruction_s1(iptr))) {
+ escape_analysis_ensure_state(e, instruction_s1(iptr), ESCAPE_GLOBAL);
+ E("return of not argument", s1)
+ }
+ }
+}
+
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;
+ if (e->verbose) printf("Post processing getfields:\n");
+
FOR_EACH_INSTRUCTION_LIST(e->getfields, iti) {
iptr = iti->instr;
dependency_list_item_get_dependency(itd),
escape_analysis_get_state(e, instruction_dst(iptr))
);
+ I2("post process getfield", 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;
+static void escape_analysis_mark_monitors(escape_analysis_t *e) {
+ instruction_list_item_t *iti;
+ instruction *iptr;
+
+ FOR_EACH_INSTRUCTION_LIST(e->monitors, iti) {
+ iptr = iti->instr;
- if (instruction_get_opcode(iptr) == ICMD_NEW) {
- c = escape_analysis_classinfo_in_var(e, instruction_arg(iptr, 0));
- if (c) {
- cl = c->name->text;
+ /* TODO if argument does not escape, mark. */
+ if (escape_analysis_get_state(e, instruction_arg(iptr, 0)) != ESCAPE_GLOBAL) {
+ if (e->verbose) {
+ printf("Monitor on thread local object!\n");
+ }
}
}
-
-
- 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;
+ instruction *iptr;
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);
+ iptr = iti->instr;
+ es = escape_analysis_get_state(e, instruction_dst(iptr));
+
+#if defined(ENABLE_REASON)
+ if (instruction_get_opcode(iptr) == ICMD_NEW) {
+ var_extra_t *ve;
+ iptr->sx.s23.s3.bte = builtintable_get_internal(BUILTIN_escape_reason_new);
+ ve = var_extra_get(e, var_extra_get_representant(e, instruction_dst(iptr)));
+ iptr->escape_reasons = ve->reasons;
+ if (es < ESCAPE_METHOD_RETURN) {
+ assert(!ve->reasons);
+ reason_t *r = NEW(reason_t);
+ r->why = "No escape\n";
+ r->iptr = NULL;
+ r->next = NULL;
+ iptr->escape_reasons = r;
+ } else {
+ assert(iptr->escape_reasons);
+ }
}
- if (es == ESCAPE_GLOBAL_THROUGH_METHOD) {
- display_allocation(e, "!!!!", iti->instr, es);
+#endif
+
+/*
+ if (instruction_get_opcode(iptr) == ICMD_NEW) {
+ es = escape_analysis_get_state(e, instruction_dst(iptr));
+ if (es < ESCAPE_METHOD_RETURN) {
+ iptr->sx.s23.s3.bte = builtintable_get_internal(BUILTIN_tlh_new);
+ e->jd->code->flags |= CODE_FLAG_TLH;
+ }
}
- }
*/
+ }
}
static void escape_analysis_process_arguments(escape_analysis_t *e) {
if (t == TYPE_ADR) {
if (varindex != UNUSED) {
escape_analysis_ensure_state(e, varindex, ESCAPE_NONE);
- escape_analysis_set_is_argument(e, varindex);
+ escape_analysis_set_contains_argument(e, varindex);
+ escape_analysis_set_contains_only_arguments(e, varindex);
+ /*escape_analysis_set_adr_arg_num(e, varindex, e->adr_args_count);*/
}
e->adr_args_count += 1;
}
s4 varindex;
methoddesc *md;
u1 *paramescape;
+ instruction_list_item_t *iti;
+ instruction *iptr;
+ escape_state_t es, re;
+ int ret_val_is_adr;
md = e->jd->m->parseddesc;
- paramescape = MNEW(u1, e->adr_args_count);
- e->jd->m->paramescape = paramescape;
+ ret_val_is_adr = (md->returntype.type == TYPE_ADR) ? 1 : 0;
+
+ paramescape = MNEW(u1, e->adr_args_count + ret_val_is_adr);
+
+ e->jd->m->paramescape = paramescape + ret_val_is_adr;
for (p = 0, l = 0; p < md->paramcount; ++p) {
t = md->paramtypes[p].type;
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));
+ es = escape_analysis_get_state(e, varindex);
+
+ if (es == ESCAPE_METHOD_RETURN) {
+ *paramescape = escape_state_to_u1(ESCAPE_METHOD) | 0x80;
+ if (e->verbose) {
+ printf("non-escaping adr parameter returned: %d\n", p);
+ }
+ } else {
+ *paramescape = escape_state_to_u1(es);
+ }
+
}
paramescape += 1;
}
l += IS_2_WORD_TYPE(t) ? 2 : 1;
}
+
+ if (ret_val_is_adr) {
+ /* Calculate escape state of return value as maximum escape state of all
+ values returned. */
+
+ re = ESCAPE_NONE;
+
+ FOR_EACH_INSTRUCTION_LIST(e->returns, iti) {
+ iptr = iti->instr;
+ es = escape_analysis_get_state(e, instruction_s1(iptr));
+
+ if (es > re) {
+ re = es;
+ }
+ }
+
+ e->jd->m->paramescape[-1] = escape_state_to_u1(re);
+ }
+}
+
+#if defined(ENABLE_REASON)
+void print_escape_reasons() {
+ reason_t *re = THREADOBJECT->escape_reasons;
+
+ fprintf(stderr, "DYN_REASON");
+
+ for (; re; re = re->next) {
+ fprintf(stderr,":%s", re->why);
+ }
+
+ fprintf(stderr, "\n");
+}
+
+void set_escape_reasons(void *vp) {
+ THREADOBJECT->escape_reasons = vp;
}
+#endif
static void escape_analysis_display(escape_analysis_t *e) {
instruction_list_item_t *iti;
FOR_EACH_INSTRUCTION_LIST(e->allocations, iti) {
iptr = iti->instr;
- ve = var_extra_get(e, instruction_dst(iptr));
+ ve = var_extra_get(e, var_extra_get_representant(e, instruction_dst(iptr)));
+ show_icmd(e->jd, iptr-1, 0, 3);
+ printf("\n");
+ show_icmd(e->jd, iptr, 0, 3);
+ printf("\n");
printf(
- "%s@%d: %s\n",
+ "%s@%d: --%s-- %d\n\n",
icmd_table[iptr->opc].name,
iptr->line,
- escape_state_to_string(ve->escape_state)
+ escape_state_to_string(ve->escape_state),
+ ve->representant
);
+#if defined(ENABLE_REASON)
+ {
+ reason_t *re;
+ for (re = ve->reasons; re; re = re->next) {
+ printf("ESCAPE_REASON: %s\n", re->why);
+ }
+ }
+#endif
}
}
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);
+ color_printf(RED, "\n\n==== %s/%s ====\n\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_post_process_returns(e);
+
escape_analysis_export_arguments(e);
if (e->verbose) escape_analysis_display(e);
+
+ if (e->verbose) {
+ int i, j, r;
+ for (i = 0; i < jd->vartop; ++i) {
+ r = var_extra_get_representant(e, i);
+ if (i == r) {
+ printf("EES of %d: ", i);
+ for (j = 0; j < jd->vartop; ++j) {
+ if (var_extra_get_representant(e, j) == r) {
+ printf("%d, ", j);
+ }
+ }
+ printf("\n");
+ }
+ }
+ printf("\n");
+ }
+
escape_analysis_mark_allocations(e);
+ escape_analysis_mark_monitors(e);
jd->m->flags &= ~ACC_METHOD_EA;
}
void escape_analysis_escape_check(void *vp) {
}
+
+/*** monomorphic *************************************************************/
+
+bool escape_is_monomorphic(methodinfo *caller, methodinfo *callee) {
+
+ /* Non-speculative case */
+
+ if (callee->flags & (ACC_STATIC | ACC_FINAL | ACC_PRIVATE)) {
+ return true;
+ }
+
+ if (
+ (callee->flags & (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED| ACC_ABSTRACT))
+ == (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED)
+ ) {
+
+ /* Mark that we have used the information about monomorphy. */
+
+ callee->flags |= ACC_METHOD_MONOMORPHY_USED;
+
+ /* We assume the callee is monomorphic. */
+
+ method_add_assumption_monomorphic(caller, callee);
+
+ return true;
+ }
+
+ return false;
+}
+
ESCAPE_UNKNOWN,
ESCAPE_NONE,
ESCAPE_METHOD,
- ESCAPE_GLOBAL_THROUGH_METHOD,
+ ESCAPE_METHOD_RETURN,
ESCAPE_GLOBAL
} escape_state_t;
+static inline escape_state_t escape_state_from_u1(u1 x) {
+ return (escape_state_t)(x & ~0x80);
+}
+
+static inline u1 escape_state_to_u1(escape_state_t x) {
+ return (u1)x;
+}
+
void escape_analysis_perform(jitdata *jd);
void escape_analysis_escape_check(void *vp);
void bc_escape_analysis_perform(methodinfo *m);
+bool escape_is_monomorphic(methodinfo *caller, methodinfo *callee);
+
#endif
yssa(jd);
}
/*pythonpass_run(jd, "foo", "after");*/
+ cfg_remove_root(jd);
return;
ls = jd->ls;
#include <limits.h>
#include <stdio.h>
-#define ELIMINATE_NOP_LOAD_STORE
+/*#define ELIMINATE_NOP_LOAD_STORE*/
#define FIXME(x) x
#define SSA_VERIFY
-/* #define SSA_VERBOSE */
+/*#define SSA_VERBOSE */
/*
__attribute__((always_inline))
/*** vars *******************************************************************/
-#define VARS_CATEGORY_SHIFT 29
-#define VARS_INDEX_MASK 0x1FFFFFFF
+#define VARS_CATEGORY_SHIFT 28
+#define VARS_INDEX_MASK 0x0FFFFFFF
#define VARS_CATEGORY_LOCAL 0
#define VARS_CATEGORY_STACK 1
#define VAR_TYPE_SUBSTITUED 666
+#define OLD_INDEX_UNUSED -2
+
+typedef struct {
+ varinfo v;
+ s4 old_index;
+} vars_item_t;
+
typedef struct {
- varinfo items[9000]; /* XXX hardcoded max */
+ vars_item_t items[9000]; /* XXX hardcoded max */
unsigned max;
unsigned count;
unsigned category;
unsigned i = vs->count;
assert(i < vs->max);
vs->count += 1;
- vs->items[i] = *item;
+ vs->items[i].v = *item;
+ vs->items[i].old_index = OLD_INDEX_UNUSED;
return (vs->category << VARS_CATEGORY_SHIFT) | i;
}
static inline varinfo *vars_back(vars_t *vs) {
assert(vs->count > 0);
- return vs->items + (vs->count - 1);
+ return &(vs->items[vs->count - 1].v);
}
static inline void vars_init(vars_t *vs, unsigned category) {
varindex = vars_get_index(varindex);
replacementindex = vars_get_index(replacementindex);
- vs->items[varindex].type = VAR_TYPE_SUBSTITUED;
- vs->items[varindex].vv.regoff = replacementindex;
+ vs->items[varindex].v.type = VAR_TYPE_SUBSTITUED;
+ vs->items[varindex].v.vv.ii[1] = replacementindex;
}
static unsigned vars_resolve_subst(const vars_t *vs, unsigned varindex) {
#endif
varindex = vars_get_index(varindex);
- if (vs->items[varindex].type == VAR_TYPE_SUBSTITUED) /*fprintf(stderr, "*")*/;
+ if (vs->items[varindex].v.type == VAR_TYPE_SUBSTITUED) /*fprintf(stderr, "*")*/;
- while (vs->items[varindex].type == VAR_TYPE_SUBSTITUED) {
+ while (vs->items[varindex].v.type == VAR_TYPE_SUBSTITUED) {
assert(loop_ctr++ != vs->count);
- varindex = vs->items[varindex].vv.regoff;
+ varindex = vs->items[varindex].v.vv.ii[1];
}
return (vs->category << VARS_CATEGORY_SHIFT) | varindex ;
}
static void vars_copy_to_final(vars_t *vs, varinfo *dst) {
- const varinfo *it;
+ const vars_item_t *it;
unsigned subst;
for (it = vs->items; it != vs->items + vs->count; ++it, ++dst) {
/* Copy variable. */
- *dst = *it;
+ *dst = it->v;
/* Eliminate VAR_TYPE_SUBSTITUED as it leads to problems. */
if (dst->type == VAR_TYPE_SUBSTITUED) {
subst = vars_get_index(vars_resolve_subst(vs, it - vs->items));
- dst->type = vs->items[subst].type;
+ dst->type = vs->items[subst].v.type;
}
}
}
+static void vars_import(vars_t *vs, varinfo *v, unsigned count, s4 old_index) {
+ vars_item_t *it;
+
+ assert((vs->count + count) <= vs->max);
+
+ it = vs->items + vs->count;
+ vs->count += count;
+
+ while (count-- > 0) {
+ it->v = *v;
+ it->old_index = old_index;
+ it += 1;
+ v += 1;
+ old_index += 1;
+ }
+}
+
+static inline void vars_record_old_index(vars_t *vs, unsigned varindex, s4 old_index) {
+ vars_item_t *item;
+ varindex = vars_get_index(varindex);
+
+ assert(varindex < vs->count);
+
+ item = vs->items + varindex;
+
+ assert(
+ item->old_index == OLD_INDEX_UNUSED ||
+ item->old_index == old_index
+ );
+
+ item->old_index = old_index;
+}
+
+static inline s4 vars_get_old_index(vars_t *vs, unsigned varindex) {
+ s4 old;
+
+ varindex = vars_get_index(varindex);
+
+ assert(varindex < vs->count);
+ old = vs->items[varindex].old_index;
+ assert(old != OLD_INDEX_UNUSED);
+
+ return old;
+}
/*** phis *******************************************************************/
}
#endif
-static inline void phis_copy_to(const phis_t *ps, instruction *dst) {
- MCOPY(
- dst,
- ps->items,
- instruction,
- ps->count
- );
+static inline unsigned phis_copy_to(const phis_t *ps, instruction *dst) {
+ instruction *it;
+ instruction *out = dst;
+
+ FOR_EACH_PHI_FUNCTION(ps, it) {
+ *(out++) = *it;
+ }
+
+ return (out - dst);
}
/*** state_array ************************************************************/
state_array_set(t->state_array, index, phi);
+ vars_record_old_index(v, phi->dst.varindex, index);
+
return phi;
}
static void traversal_rename_def(traversal_t *t, vars_t *vars, instruction *iptr) {
const varinfo *v;
unsigned index;
+ s4 old;
state_array_assert_items(t->state_array);
v = t->ops->var_num_to_varinfo(t->ops_vp, iptr->dst.varindex);
index = t->ops->var_num_to_index(t->ops_vp, iptr->dst.varindex);
+ old = iptr->dst.varindex;
iptr->dst.varindex = vars_add_item(vars, v);
state_array_set(t->state_array, index, iptr);
+
+ vars_record_old_index(vars, iptr->dst.varindex, index);
}
-static void traversal_rename_use(traversal_t *t, s4 *puse) {
+static void traversal_rename_use(traversal_t *t, vars_t *vars, s4 *puse) {
unsigned index;
+ s4 old;
state_array_assert_items(t->state_array);
index = t->ops->var_num_to_index(t->ops_vp, *puse);
assert(state_array_get(t->state_array, index));
+ old = *puse;
*puse = state_array_get_var(t->state_array, index);
+
+ vars_record_old_index(vars, *puse, index);
}
static inline unsigned traversal_variables_count(traversal_t *t) {
basicblock_chain_t *new_blocks;
+ struct {
+ s4 maxlocals;
+ s4 maxinterfaces;
+ s4 *local_map;
+ varinfo *var;
+ s4 vartop;
+ s4 varcount;
+ s4 localcount;
+ } original;
+
+ unsigned keep_in_out:1;
+
} ssa_info, ssa_info_t;
void ssa_info_init(ssa_info_t *ssa, jitdata *jd) {
vars_init(ssa->locals, VARS_CATEGORY_LOCAL);
/* Initialize first version of locals, that is always available. */
- MCOPY(ssa->locals->items, jd->var, varinfo, jd->localcount);
- ssa->locals->count = jd->localcount;
+ vars_import(ssa->locals, jd->var, jd->localcount, 0);
ssa->stack = DNEW(vars_t);
vars_init(ssa->stack, VARS_CATEGORY_STACK);
ssa->new_blocks = DNEW(basicblock_chain_t);
basicblock_chain_init(ssa->new_blocks);
+
+ ssa->original.maxlocals = jd->maxlocals;
+ ssa->original.maxinterfaces = jd->maxinterfaces;
+ ssa->original.local_map = jd->local_map;
+ ssa->original.var = jd->var;
+ ssa->original.vartop = jd->vartop;
+ ssa->original.varcount = jd->varcount;
+ ssa->original.localcount = jd->localcount;
+
+ ssa->keep_in_out = false;
}
/*** others_mapping *********************************************************/
static inline void others_mapping_set(ssa_info *ssa, s4 var, s4 new_var) {
- ssa->jd->var[var].vv.regoff = new_var;
+ ssa->jd->var[var].vv.ii[1] = new_var;
}
static inline s4 others_mapping_get(const ssa_info *ssa, s4 var) {
- return ssa->jd->var[var].vv.regoff;
+ return ssa->jd->var[var].vv.ii[1];
}
/*** code *******************************************************************/
basicblock_chain_t chain;
basicblock *last = NULL;
basicblock *marker = NULL;
+ s4 vartop;
+ unsigned i;
if (jd->exceptiontablelength == 0) {
return;
basicblock_chain_init(&chain);
- /* We need to allocate new iovars */
+ /* Remember, where we started adding IO variables. */
- avail_vars = (jd->varcount - jd->vartop);
- add_vars = jd->exceptiontablelength;
-
- if (add_vars > avail_vars) {
- add_vars -= avail_vars;
- jd->var = DMREALLOC(jd->var, varinfo, jd->varcount, jd->varcount + add_vars);
- jd->varcount += add_vars;
- }
+ vartop = jd->vartop;
/* For each exception handler block, create one block with a prologue block */
FOR_EACH_BASICBLOCK(jd, bptr) {
if (bptr->type == BBTYPE_EXH) {
+ /*
+
+ +---- EXH (exh)-------+
+ | in0 in1 in2 exc |
+ | ..... |
+ +---------------------+
+
+ === TRANSFORMED TO ===>
+
+ +---- PROL (exh) -------+
+ | in0 in1 in2 |
+ | GETEXECEPTION => OU3 |
+ | GOTO REAL_EXH |
+ | in0 in1 in2 OU3 |
+ +-----------------------+
+
+ +---- REAL_EXH (std) -+
+ | in0 in1 in2 exc |
+ | ...... |
+ +---------------------+
+
+ */
+
bptr->type = BBTYPE_STD;
bptr->predecessorcount = 0; /* legacy */
iptr = DMNEW(instruction, 2);
MZERO(iptr, instruction, 2);
+
+ /* Create outvars */
+
+ exh->outdepth = bptr->indepth;
+
+ if (exh->outdepth > 0) {
+ exh->outvars = DMNEW(s4, exh->outdepth);
+ for (i = 0; i < exh->outdepth; ++i) {
+ exh->outvars[i] = vartop++;
+ }
+ }
+
+ /* Create invars */
+ exh->indepth = exh->outdepth - 1;
+ exh->invars = exh->outvars;
+
+#if 0
/* Create new outvar */
assert(jd->vartop < jd->varcount);
jd->vartop += 1;
jd->var[v].type = TYPE_ADR;
jd->var[v].flags = INOUT;
+#endif
exh->nr = jd->basicblockcount;
jd->basicblockcount += 1;
exh->type = BBTYPE_EXH;
exh->icount = 2;
exh->iinstr = iptr;
+/*
exh->outdepth = 1;
exh->outvars = DNEW(s4);
exh->outvars[0] = v;
+*/
exh->predecessorcount = -1; /* legacy */
exh->flags = BBFINISHED;
exh->method = jd->m;
/* Get exception */
iptr->opc = ICMD_GETEXCEPTION;
- iptr->dst.varindex = v;
+ iptr->dst.varindex = exh->outvars[exh->outdepth - 1];
iptr += 1;
/* Goto real exception handler */
}
}
+ /* We need to allocate the new iovars in the var array */
+
+ avail_vars = (jd->varcount - jd->vartop);
+ add_vars = (vartop - jd->vartop);
+
+ if (add_vars > avail_vars) {
+ add_vars -= avail_vars;
+ jd->var = DMREALLOC(jd->var, varinfo, jd->varcount, jd->varcount + add_vars);
+ jd->varcount += add_vars;
+ }
+
+ jd->vartop = vartop;
+
/* Put the chain of exception handlers between just before the last
basic block (end marker). */
for (ee = jd->exceptiontable; ee; ee = ee->down) {
assert(ee->handler->vp);
- ee->handler = ee->handler->vp;
+
+ bptr = ee->handler;
+ exh = (basicblock *)ee->handler->vp;
+
+ ee->handler = exh;
+
+ /* Set up IO variables in newly craeted exception handlers. */
+
+ for (i = 0; i < exh->outdepth; ++i) {
+ v = exh->outvars[i];
+
+ jd->var[v].flags = INOUT;
+ jd->var[v].type = jd->var[ bptr->invars[i] ].type;
+ }
+ }
+
+}
+
+void unfix_exception_handlers(jitdata *jd) {
+ basicblock *bptr, *exh;
+ unsigned i;
+ exception_entry *ee;
+#if !defined(NDEBUG)
+ bool found = false;
+#endif
+
+ FOR_EACH_BASICBLOCK(jd, bptr) {
+ if (bptr->type == BBTYPE_EXH) {
+ assert(bptr->iinstr[1].opc == ICMD_GOTO);
+ exh = bptr->iinstr[1].dst.block;
+
+ bptr->type = BBDELETED;
+ bptr->icount = 0;
+ bptr->indepth = 0;
+ bptr->outdepth = 0;
+ exh->type = BBTYPE_EXH;
+ bptr->vp = exh;
+
+ /* bptr is no more a predecessor of exh */
+
+ for (i = 0; i < exh->predecessorcount; ++i) {
+ if (exh->predecessors[i] == bptr) {
+ exh->predecessors[i] = exh->predecessors[exh->predecessorcount - 1];
+ exh->predecessorcount -= 1;
+#if !defined(NDEBUG)
+ found = true;
+#endif
+ break;
+ }
+ }
+
+ assert(found);
+
+ } else {
+ bptr->vp = NULL;
+ }
}
+ for (ee = jd->exceptiontable; ee; ee = ee->down) {
+ assert(ee->handler->vp);
+ ee->handler = ee->handler->vp;
+ }
}
/*** ssa_enter ***************************************************************/
basicblock *bptr;
basicblock_info_t *bbi;
instruction *itph;
+
+ /* XXX */
+ return;
+
FOR_EACH_BASICBLOCK(ssa->jd, bptr) {
if (basicblock_reached(bptr)) {
bbi = bb_info(bptr);
basicblock_get_ex_predecessor_index(bb, pei, *itsucc),
ssa->locals
);
+
+ ssa_enter_merge(
+ bbi->stack,
+ succi->stack,
+ *itsucc,
+ basicblock_get_ex_predecessor_index(bb, pei, *itsucc),
+ ssa->stack
+ );
}
}
instruction *itph;
bool ret = false;
+ /* XXX */
+ return;
+
FOR_EACH_PHI_FUNCTION(t->phis, itph) {
phi_calculate_redundancy(itph);
state_array_allocate_items(bbi->stack->state_array);
}
+#if 0
/* Exception handlers have a clean stack. */
+ /* Not true with inlining. */
+
if (bb->type == BBTYPE_EXH) {
state_array_assert_no_items(bbi->stack->state_array);
state_array_allocate_items(bbi->stack->state_array);
}
+#endif
/* Some in/out vars get marked as INOUT in simplereg,
and are not marked at this point.
Mark them manually. */
for (ituse = bb->invars; ituse != bb->invars + bb->indepth; ++ituse) {
+ if (ssa->keep_in_out && ssa->jd->var[*ituse].type == TYPE_RET) {
+ continue;
+ }
ssa->jd->var[*ituse].flags |= INOUT;
ssa->jd->var[*ituse].flags &= ~PREALLOC;
}
for (ituse = bb->outvars; ituse != bb->outvars + bb->outdepth; ++ituse) {
+ if (ssa->keep_in_out && ssa->jd->var[*ituse].type == TYPE_RET) {
+ continue;
+ }
ssa->jd->var[*ituse].flags |= INOUT;
ssa->jd->var[*ituse].flags &= ~PREALLOC;
}
if (var_is_local(ssa->jd, *ituse)) {
traversal_rename_use(
bbi->locals,
+ ssa->locals,
ituse
);
} else if (var_is_inout(ssa->jd, *ituse)) {
traversal_rename_use(
bbi->stack,
+ ssa->stack,
ituse
);
} else {
ssa->others,
ssa->jd->var + iptr->dst.varindex
);
+ vars_record_old_index(ssa->others, iptr->dst.varindex, old);
others_mapping_set(ssa, old, iptr->dst.varindex);
}
}
s4 *it;
unsigned i, j;
jitdata *jd = ssa->jd;
+ s4 *local_map;
vartop = ssa->locals->count + ssa->stack->count + ssa->others->count;
vars = DMNEW(varinfo, vartop);
/* Grow local map to accomodate all new locals and iovars.
But keep the local map for version 1 of locals, that contains the holes. */
- jd->local_map = DMREALLOC(
- jd->local_map,
- s4,
- 5 * jd->maxlocals,
+ local_map = DMNEW(
+ s4,
5 * (jd->maxlocals + ssa->locals->count + ssa->stack->count - jd->localcount)
);
+ MCOPY(local_map, jd->local_map, s4, 5 * jd->maxlocals);
+
+ jd->local_map = local_map;
+
it = jd->local_map + (jd->maxlocals * 5); /* start adding entries here */
/* Add version > 1 of all locals */
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);
+ bptr->phis = DMNEW(instruction, bbi->locals->phis->count + bbi->stack->phis->count);
dst = bptr->phis;
- phis_copy_to(bbi->locals->phis, dst);
+ dst += phis_copy_to(bbi->locals->phis, dst);
- dst += bbi->locals->phis->count;
+ dst += phis_copy_to(bbi->stack->phis, dst);
- phis_copy_to(bbi->stack->phis, dst);
+ bptr->phicount = dst - bptr->phis;
}
}
}
bbi = bb_info(bb);
- bb->indepth = 0;
- bb->outdepth = 0;
+ if (! ssa->keep_in_out) {
+ bb->indepth = 0;
+ bb->outdepth = 0;
+ }
if (bbi != NULL) {
FOR_EACH_PHI_FUNCTION(bbi->locals->phis, itph) {
}
}
+void ssa_simple_leave_restore(ssa_info_t *ssa, basicblock *bptr, s4 *pvar) {
+ s4 var = *pvar;
+ s4 index;
+ basicblock_info_t *bbi;
+
+ if (var < ssa->locals->count) {
+ *pvar = vars_get_old_index(ssa->locals, var);
+ } else if (var < ssa->locals->count + ssa->stack->count) {
+
+ index = vars_get_old_index(
+ ssa->stack,
+ var - ssa->locals->count
+ );
+
+ bbi = bb_info(bptr);
+
+ /* We have to determine whether to take an invar or an outvar for
+ the stack depth ``index''.
+ The state array contains the last definition of the stack element
+ at the given depth.
+ */
+
+ if (state_array_get_var(bbi->stack->state_array, index) == var) {
+ /* The last definition of a stack depth inside the basicblock.
+ This is the outvar at the given depth.
+ If there is no outvar at the given depth, it must be an invar.
+ */
+ if (index < bptr->outdepth) {
+ *pvar = bptr->outvars[index];
+ } else if (index < bptr->indepth) {
+ *pvar = bptr->invars[index];
+ } else {
+ assert(0);
+ }
+ } else {
+ /* A different than the last definition of a stack depth.
+ This must be an invar.
+ */
+ assert(index < bptr->indepth);
+ *pvar = bptr->invars[index];
+ }
+ } else {
+ *pvar = vars_get_old_index(
+ ssa->others,
+ var - ssa->locals->count - ssa->stack->count
+ );
+ }
+}
+
+void ssa_simple_leave(ssa_info_t *ssa) {
+ basicblock *bptr;
+ instruction *iptr;
+ s4 *ituse, *uses;
+ unsigned uses_count;
+
+ FOR_EACH_BASICBLOCK(ssa->jd, bptr) {
+ if (bptr->type == BBTYPE_EXH) {
+ /* (Aritifical) exception handler blocks will be eliminated. */
+ continue;
+ }
+ /* In reverse order. We need to rename the definition after any use! */
+ FOR_EACH_INSTRUCTION_REV(bptr, iptr) {
+ if (instruction_has_dst(iptr)) {
+ ssa_simple_leave_restore(ssa, bptr, &(iptr->dst.varindex));
+ }
+ instruction_get_uses(iptr, ssa->s_buf, &uses, &uses_count);
+ for (ituse = uses; ituse != uses + uses_count; ++ituse) {
+ ssa_simple_leave_restore(ssa, bptr, ituse);
+ }
+ instruction_set_uses(iptr, ssa->s_buf, uses, uses_count);
+ }
+ bptr->phicount = 0;
+ }
+
+ unfix_exception_handlers(ssa->jd);
+
+ ssa->jd->maxlocals = ssa->original.maxlocals;
+ ssa->jd->maxinterfaces = ssa->original.maxinterfaces;
+ ssa->jd->local_map =ssa->original.local_map;
+ ssa->jd->var = ssa->original.var;
+ ssa->jd->vartop = ssa->original.vartop;
+ ssa->jd->varcount = ssa->original.varcount;
+ ssa->jd->localcount = ssa->original.localcount;
+}
+
+#include "vmcore/rt-timing.h"
+
void yssa(jitdata *jd) {
basicblock *it;
basicblock_info_t *iti;
ssa_info *ssa;
+ struct timespec bs, es, be, ee;
+
+ RT_TIMING_GET_TIME(bs);
+
#ifdef SSA_VERBOSE
- printf("=============== [ before %s ] =========================\n", jd->m->name->text);
- show_method(jd, 3);
- printf("=============== [ /before ] =========================\n");
+ bool verb = true;
+ if (verb) {
+ printf("=============== [ before %s ] =========================\n", jd->m->name->text);
+ show_method(jd, 3);
+ printf("=============== [ /before ] =========================\n");
+ }
#endif
ssa = DNEW(ssa_info);
ssa_info_init(ssa, jd);
+ ssa->keep_in_out = true;
FOR_EACH_BASICBLOCK(jd, it) {
if (basicblock_reached(it)) {
/*ssa_enter_create_phi_graph(ssa);*/
+ RT_TIMING_GET_TIME(be);
escape_analysis_perform(ssa->jd);
+ RT_TIMING_GET_TIME(ee);
+ /*
ssa_leave_create_phi_moves(ssa);
ssa_leave_create_exceptional_phi_moves(ssa);
+ */
+
+#ifdef SSA_VERBOSE
+ if (verb) {
+ printf("=============== [ mid ] =========================\n");
+ show_method(jd, 3);
+ printf("=============== [ /mid ] =========================\n");
+ }
+#endif
+
+ ssa_simple_leave(ssa);
#ifdef SSA_VERBOSE
- printf("=============== [ after ] =========================\n");
- show_method(jd, 3);
- printf("=============== [ /after ] =========================\n");
+ if (verb) {
+ printf("=============== [ after ] =========================\n");
+ show_method(jd, 3);
+ printf("=============== [ /after ] =========================\n");
+ }
#endif
+ RT_TIMING_GET_TIME(es);
+
+ RT_TIMING_TIME_DIFF(bs, es, RT_TIMING_1);
+ RT_TIMING_TIME_DIFF(be, ee, RT_TIMING_2);
}
void eliminate_subbasicblocks(jitdata *jd) {
float f;
double d;
basicblock *retaddr;
+ s4 ii[2];
} vv;
#if defined(ENABLE_VERIFIER)
typeinfo_t typeinfo; /* type info for reference types */
#include "vmcore/options.h"
#include "vmcore/classcache.h"
-
+#if defined(ENABLE_RT_TIMING)
+#include "vmcore/rt-timing.h"
+#endif
#define REPLACE_PATCH_DYNAMIC_CALL
/*#define REPLACE_PATCH_ALL*/
#undef REPLACE_RA_TOP_OF_FRAME
#undef REPLACE_RA_LINKAGE_AREA
#undef REPLACE_LEAFMETHODS_RA_REGISTER
-#undef REPLACE_REG_RA
/* i386, x86_64 and m68k */
#if defined(__I386__) || defined(__X86_64__) || defined(__M68K__)
#elif defined(__ALPHA__)
#define REPLACE_RA_TOP_OF_FRAME
#define REPLACE_LEAFMETHODS_RA_REGISTER
-#define REPLACE_REG_RA REG_RA
/* powerpc */
#elif defined(__POWERPC__)
#define REPLACE_RA_LINKAGE_AREA
#define REPLACE_LEAFMETHODS_RA_REGISTER
-#define REPLACE_REG_RA REG_ITMP3 /* the execution state has the LR in itmp3 */
/* s390 */
#elif defined(__S390__)
#define REPLACE_RA_TOP_OF_FRAME
-#define REPLACE_REG_RA REG_ITMP3
#endif
replace_read_value(es, &instra, &(frame->instance));
#endif
}
+#if defined(__I386__)
+ else if (!(rp->method->flags & ACC_STATIC)) {
+ /* On i386 we always pass the first argument on stack. */
+ frame->instance.a = *(java_object_t **)(basesp + 1);
+ }
+#endif
#endif /* defined(REPLACE_PATCH_DYNAMIC_CALL) */
/* read stack slots */
if (!topframe && ra->index == RPLALLOC_PARAM) {
/* skip it */
+ /*
+ ra->index = RPLALLOC_PARAM;
+ replace_val_t v;
+ v.l = 0;
+ replace_write_value(es,ra,&v);
+ */
}
else {
assert(i < frame->javastackdepth);
}
-/* replace_pop_activation_record ***********************************************
+/* md_pop_stackframe ***********************************************************
+
+ Restore callee-saved registers (including the RA register),
+ set the stack pointer to the next stackframe,
+ set the PC to the return address of the popped frame.
- Peel a stack frame from the execution state.
-
*** This function imitates the effects of the method epilog ***
*** and returning from the method call. ***
IN:
- es...............execution state
- frame............source frame, receives synchronization slots
+ es...............execution state
OUT:
*es..............the execution state after popping the stack frame
-
+ NOTE: es->code and es->pv are NOT updated.
+
*******************************************************************************/
-u1* replace_pop_activation_record(executionstate_t *es,
- sourceframe_t *frame)
+void md_pop_stackframe(executionstate_t *es)
{
u1 *ra;
- u1 *pv;
+ s4 ra_align_off;
s4 reg;
s4 i;
- s4 count;
- codeinfo *code;
stackslot_t *basesp;
stackslot_t *sp;
assert(es->code);
- assert(frame);
+
+ /* alignment offset of RA */
+
+ ra_align_off = 0;
+#if defined(REPLACE_RA_BETWEEN_FRAMES)
+ if (es->code->stackframesize)
+ ra_align_off = SIZE_OF_STACKSLOT - SIZEOF_VOID_P;
+#endif
/* read the return address */
#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
if (code_is_leafmethod(es->code))
- ra = (u1*) (ptrint) es->intregs[REPLACE_REG_RA];
+ ra = es->ra;
else
#endif
ra = md_stacktrace_get_returnaddress(es->sp,
- SIZE_OF_STACKSLOT * es->code->stackframesize);
-
- DOLOG( printf("RA = %p\n", (void*)ra); );
-
- assert(ra);
+ SIZE_OF_STACKSLOT * es->code->stackframesize + ra_align_off);
/* calculate the base of the stack frame */
sp = (stackslot_t *) es->sp;
basesp = sp + es->code->stackframesize;
- /* read slots used for synchronization */
-
- assert(frame->syncslotcount == 0);
- assert(frame->syncslots == NULL);
- count = code_get_sync_slot_count(es->code);
- frame->syncslotcount = count;
- frame->syncslots = DMNEW(replace_val_t, count);
- for (i=0; i<count; ++i) {
- frame->syncslots[i].p = sp[es->code->memuse + i]; /* XXX */
- }
-
/* restore return address, if part of frame */
#if defined(REPLACE_RA_TOP_OF_FRAME)
#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
if (!code_is_leafmethod(es->code))
#endif
- es->intregs[REPLACE_REG_RA] = *--basesp;
+ es->ra = (u1*) (ptrint) *--basesp;
#endif /* REPLACE_RA_TOP_OF_FRAME */
#if defined(REPLACE_RA_LINKAGE_AREA)
#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
if (!code_is_leafmethod(es->code))
#endif
- es->intregs[REPLACE_REG_RA] = basesp[LA_LR_OFFSET / sizeof(stackslot_t)];
+ es->ra = (u1*) (ptrint) basesp[LA_LR_OFFSET / sizeof(stackslot_t)];
#endif /* REPLACE_RA_LINKAGE_AREA */
/* restore saved int registers */
es->sp += SIZE_OF_STACKSLOT * es->code->stackframesize;
#if defined(REPLACE_RA_BETWEEN_FRAMES)
- es->sp += SIZE_OF_STACKSLOT; /* skip return address */
+ es->sp += ra_align_off + SIZEOF_VOID_P; /* skip return address */
#endif
- /* Set the new pc. Subtract one so we do not hit the replacement point */
- /* of the instruction following the call, if there is one. */
-
- es->pc = ra - 1;
-
- /* find the new codeinfo */
-
- pv = md_codegen_get_pv_from_pc(ra);
- DOLOG( printf("PV = %p\n", (void*) pv); );
-
- code = code_get_codeinfo_for_pv(pv);
- DOLOG( printf("CODE = %p\n", (void*) code); );
-
- /* return NULL if we reached native code */
+ /* set the program counter to the return address */
- es->pv = pv;
- es->code = code;
+ es->pc = ra;
/* in debugging mode clobber non-saved registers */
#if !defined(NDEBUG)
/* for debugging */
for (i=0; i<INT_REG_CNT; ++i)
- if ((nregdescint[i] != REG_SAV)
-#if defined(REG_RA)
- && (i != REPLACE_REG_RA)
-#endif
- )
+ if (nregdescint[i] != REG_SAV)
es->intregs[i] = (ptrint) 0x33dead3333dead33ULL;
for (i=0; i<FLT_REG_CNT; ++i)
if (nregdescfloat[i] != REG_SAV)
es->adrregs[i] = (ptrint) 0x33dead3333dead33ULL;
# endif
#endif /* !defined(NDEBUG) */
+}
+
+
+/* md_push_stackframe **********************************************************
+
+ Save the given return address, build the new stackframe,
+ and store callee-saved registers.
+
+ *** This function imitates the effects of a call and the ***
+ *** method prolog of the callee. ***
+
+ IN:
+ es...............execution state
+ calleecode.......the code we are "calling"
+ ra...............the return address to save
+
+ OUT:
+ *es..............the execution state after pushing the stack frame
+ NOTE: es->pc, es->code, and es->pv are NOT updated.
+
+*******************************************************************************/
+
+void md_push_stackframe(executionstate_t *es, codeinfo *calleecode, u1 *ra)
+{
+ s4 reg;
+ s4 i;
+ stackslot_t *basesp;
+ stackslot_t *sp;
+
+ assert(es);
+ assert(calleecode);
+
+ /* write the return address */
+
+#if defined(REPLACE_RA_BETWEEN_FRAMES)
+ es->sp -= SIZEOF_VOID_P;
+ *((void **)es->sp) = (void *) ra;
+ if (calleecode->stackframesize)
+ es->sp -= (SIZE_OF_STACKSLOT - SIZEOF_VOID_P);
+#endif /* REPLACE_RA_BETWEEN_FRAMES */
+
+ es->ra = (u1*) (ptrint) ra;
+
+ /* build the stackframe */
+
+ DOLOG( printf("building stackframe of %d words at %p\n",
+ calleecode->stackframesize, (void*)es->sp); );
+
+ sp = (stackslot_t *) es->sp;
+ basesp = sp;
+
+ sp -= calleecode->stackframesize;
+ es->sp = (u1*) sp;
+
+ /* in debug mode, invalidate stack frame first */
+
+ /* XXX may not invalidate linkage area used by native code! */
+
+#if !defined(NDEBUG) && 0
+ for (i=0; i< (basesp - sp) && i < 1; ++i) {
+ sp[i] = 0xdeaddeadU;
+ }
+#endif
+
+#if defined(__I386__)
+ /* Stackslot 0 may contain the object instance for vftbl patching.
+ Destroy it, so there's no undefined value used. */
+ if ((basesp - sp) > 0) {
+ sp[0] = 0;
+ }
+#endif
+
+ /* save the return address register */
+
+#if defined(REPLACE_RA_TOP_OF_FRAME)
+#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
+ if (!code_is_leafmethod(calleecode))
+#endif
+ *--basesp = (ptrint) ra;
+#endif /* REPLACE_RA_TOP_OF_FRAME */
+
+#if defined(REPLACE_RA_LINKAGE_AREA)
+#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
+ if (!code_is_leafmethod(calleecode))
+#endif
+ basesp[LA_LR_OFFSET / sizeof(stackslot_t)] = (ptrint) ra;
+#endif /* REPLACE_RA_LINKAGE_AREA */
+
+ /* save int registers */
+
+ reg = INT_REG_CNT;
+ for (i=0; i<calleecode->savedintcount; ++i) {
+ while (nregdescint[--reg] != REG_SAV)
+ ;
+ *--basesp = es->intregs[reg];
+
+ /* XXX may not clobber saved regs used by native code! */
+#if !defined(NDEBUG) && 0
+ es->intregs[reg] = (ptrint) 0x44dead4444dead44ULL;
+#endif
+ }
+
+ /* save flt registers */
+
+ /* XXX align? */
+ reg = FLT_REG_CNT;
+ for (i=0; i<calleecode->savedfltcount; ++i) {
+ while (nregdescfloat[--reg] != REG_SAV)
+ ;
+ basesp -= STACK_SLOTS_PER_FLOAT;
+ *(double*)basesp = es->fltregs[reg];
+
+ /* XXX may not clobber saved regs used by native code! */
+#if !defined(NDEBUG) && 0
+ *(u8*)&(es->fltregs[reg]) = 0x44dead4444dead44ULL;
+#endif
+ }
+
+#if defined(HAS_ADDRESS_REGISTER_FILE)
+ /* save adr registers */
+
+ reg = ADR_REG_CNT;
+ for (i=0; i<calleecode->savedadrcount; ++i) {
+ while (nregdescadr[--reg] != REG_SAV)
+ ;
+ *--basesp = es->adrregs[reg];
+
+ /* XXX may not clobber saved regs used by native code! */
+#if !defined(NDEBUG) && 0
+ es->adrregs[reg] = (ptrint) 0x44dead4444dead44ULL;
+#endif
+ }
+#endif
+}
+
+
+/* replace_pop_activation_record ***********************************************
+
+ Peel a stack frame from the execution state.
+
+ *** This function imitates the effects of the method epilog ***
+ *** and returning from the method call. ***
+
+ IN:
+ es...............execution state
+ frame............source frame, receives synchronization slots
+
+ OUT:
+ *es..............the execution state after popping the stack frame
+
+ RETURN VALUE:
+ the return address of the poped activation record
+
+*******************************************************************************/
+
+u1* replace_pop_activation_record(executionstate_t *es,
+ sourceframe_t *frame)
+{
+ u1 *ra;
+ u1 *pv;
+ s4 i;
+ s4 count;
+ codeinfo *code;
+ stackslot_t *sp;
+
+ assert(es->code);
+ assert(frame);
+
+ /* calculate the base of the stack frame */
+
+ sp = (stackslot_t *) es->sp;
+ assert(frame->syncslotcount == 0);
+ assert(frame->syncslots == NULL);
+ count = code_get_sync_slot_count(es->code);
+ frame->syncslotcount = count;
+ frame->syncslots = DMNEW(replace_val_t, count);
+ for (i=0; i<count; ++i) {
+ frame->syncslots[i].p = sp[es->code->memuse + i]; /* XXX md_ function */
+ }
+
+ /* pop the stackframe */
+
+ md_pop_stackframe(es);
+
+ ra = es->pc;
+
+ DOLOG( printf("RA = %p\n", (void*)ra); );
+
+ /* Subtract one from the PC so we do not hit the replacement point */
+ /* of the instruction following the call, if there is one. */
+
+ es->pc--;
+
+ /* find the new codeinfo */
+
+ pv = md_codegen_get_pv_from_pc(ra);
+ DOLOG( printf("PV = %p\n", (void*) pv); );
+
+ code = code_get_codeinfo_for_pv(pv);
+ DOLOG( printf("CODE = %p\n", (void*) code); );
+
+ /* return NULL if we reached native code */
+
+ es->pv = pv;
+ es->code = code;
return (code) ? ra : NULL;
}
/* we can only patch such calls if we are at the entry point */
+#if !defined(__I386__)
+ /* On i386 we always know the instance argument. */
if (!atentry)
return;
+#endif
assert((calleem->flags & ACC_STATIC) == 0);
else {
/* the call was statically bound */
+#if defined(__I386__)
+ /* It happens that there is a patcher trap. (pm) */
+ if (*(u2 *)(patchpos - 1) == 0x0b0f) {
+ } else
+#endif
replace_patch_method_pointer((methodptr *) patchpos, entrypoint, "static ");
}
}
sourceframe_t *callerframe,
sourceframe_t *calleeframe)
{
- s4 reg;
s4 i;
s4 count;
- stackslot_t *basesp;
stackslot_t *sp;
u1 *ra;
codeinfo *calleecode;
else
ra = es->pc + 1 /* XXX this is ugly */;
- /* write the return address */
-
-#if defined(REPLACE_RA_BETWEEN_FRAMES)
- es->sp -= SIZE_OF_STACKSLOT;
-
- *((stackslot_t *)es->sp) = (stackslot_t) ra;
-#endif /* REPLACE_RA_BETWEEN_FRAMES */
+ /* push the stackframe */
-#if defined(REPLACE_REG_RA)
- es->intregs[REPLACE_REG_RA] = (ptrint) ra;
-#endif
+ md_push_stackframe(es, calleecode, ra);
- /* we move into a new code unit */
+ /* we move into a new code unit, set code, PC, PV */
es->code = calleecode;
-
- /* set the new pc XXX not needed? */
-
- es->pc = calleecode->entrypoint;
-
- /* build the stackframe */
-
- DOLOG( printf("building stackframe of %d words at %p\n",
- calleecode->stackframesize, (void*)es->sp); );
-
- sp = (stackslot_t *) es->sp;
- basesp = sp;
-
- sp -= calleecode->stackframesize;
- es->sp = (u1*) sp;
-
- /* in debug mode, invalidate stack frame first */
-
- /* XXX may not invalidate linkage area used by native code! */
-#if !defined(NDEBUG) && 0
- for (i=0; i<(basesp - sp); ++i) {
- sp[i] = 0xdeaddeadU;
- }
-#endif
-
- /* save the return address register */
-
-#if defined(REPLACE_RA_TOP_OF_FRAME)
-#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
- if (!code_is_leafmethod(calleecode))
-#endif
- *--basesp = (ptrint) ra;
-#endif /* REPLACE_RA_TOP_OF_FRAME */
-
-#if defined(REPLACE_RA_LINKAGE_AREA)
-#if defined(REPLACE_LEAFMETHODS_RA_REGISTER)
- if (!code_is_leafmethod(calleecode))
-#endif
- basesp[LA_LR_OFFSET / sizeof(stackslot_t)] = (ptrint) ra;
-#endif /* REPLACE_RA_LINKAGE_AREA */
-
- /* save int registers */
-
- reg = INT_REG_CNT;
- for (i=0; i<calleecode->savedintcount; ++i) {
- while (nregdescint[--reg] != REG_SAV)
- ;
- *--basesp = es->intregs[reg];
-
- /* XXX may not clobber saved regs used by native code! */
-#if !defined(NDEBUG) && 0
- es->intregs[reg] = (ptrint) 0x44dead4444dead44ULL;
-#endif
- }
-
- /* save flt registers */
-
- /* XXX align? */
- reg = FLT_REG_CNT;
- for (i=0; i<calleecode->savedfltcount; ++i) {
- while (nregdescfloat[--reg] != REG_SAV)
- ;
- basesp -= STACK_SLOTS_PER_FLOAT;
- *(double*)basesp = es->fltregs[reg];
-
- /* XXX may not clobber saved regs used by native code! */
-#if !defined(NDEBUG) && 0
- *(u8*)&(es->fltregs[reg]) = 0x44dead4444dead44ULL;
-#endif
- }
-
-#if defined(HAS_ADDRESS_REGISTER_FILE)
- /* save adr registers */
-
- reg = ADR_REG_CNT;
- for (i=0; i<calleecode->savedadrcount; ++i) {
- while (nregdescadr[--reg] != REG_SAV)
- ;
- *--basesp = es->adrregs[reg];
-
- /* XXX may not clobber saved regs used by native code! */
-#if !defined(NDEBUG) && 0
- es->adrregs[reg] = (ptrint) 0x44dead4444dead44ULL;
-#endif
- }
-#endif
+ es->pc = calleecode->entrypoint; /* XXX not needed? */
+ es->pv = calleecode->entrypoint;
/* write slots used for synchronization */
+ sp = (stackslot_t *) es->sp;
count = code_get_sync_slot_count(calleecode);
assert(count == calleeframe->syncslotcount);
for (i=0; i<count; ++i) {
sp[calleecode->memuse + i] = calleeframe->syncslots[i].p;
}
- /* set the PV */
-
- es->pv = calleecode->entrypoint;
-
/* redirect future invocations */
if (callerframe && rpcall) {
*******************************************************************************/
-rplpoint *replace_find_replacement_point_for_pc(codeinfo *code, u1 *pc)
+rplpoint *replace_find_replacement_point_for_pc(codeinfo *code, u1 *pc, unsigned desired_flags)
{
rplpoint *found;
rplpoint *rp;
rp = code->rplpoints;
for (i=0; i<code->rplpointcount; ++i, ++rp) {
DOLOG( replace_replacement_point_println(rp, 2); );
- if (rp->pc <= pc && rp->pc + rp->callsize >= pc)
- found = rp;
+ if (rp->pc <= pc && rp->pc + rp->callsize >= pc) {
+ if (desired_flags) {
+ if (rp->flags & desired_flags) {
+ found = rp;
+ }
+ } else {
+ found = rp;
+ }
+ }
}
return found;
}
-
/* replace_pop_native_frame ****************************************************
Unroll a native frame in the execution state and create a source frame
/* find the replacement point at the call site */
after_machine_frame:
- rp = replace_find_replacement_point_for_pc(es->code, es->pc);
+ rp = replace_find_replacement_point_for_pc(es->code, es->pc, 0);
if (rp == NULL)
vm_abort("could not find replacement point while unrolling call");
origcode = es->code;
origrp = rp;
+#if defined(ENABLE_TLH)
+ printf("Replacing in %s/%s\n", rp->method->clazz->name->text, rp->method->name->text);
+#endif
+
+ /*if (strcmp(rp->method->clazz->name->text, "antlr/AlternativeElement") == 0 && strcmp(rp->method->name->text, "getAutoGenType") ==0) opt_TraceReplacement = 2; else opt_TraceReplacement = 0;*/
+
DOLOG_SHORT( printf("REPLACING(%d %p): (id %d %p) ",
stat_replacements, (void*)THREADOBJECT,
rp->id, (void*)rp);
codeinfo *code;
rplpoint *rp;
executionstate_t es;
+#if defined(ENABLE_RT_TIMING)
+ struct timespec time_start, time_end;
+#endif
/* search the codeinfo for the given PC */
/* search for a replacement point at the given PC */
- rp = replace_find_replacement_point_for_pc(code, pc);
+ rp = replace_find_replacement_point_for_pc(code, pc, (RPLPOINT_FLAG_ACTIVE | RPLPOINT_FLAG_COUNTDOWN));
/* check if the replacement point belongs to given PC and is active */
- if ((rp != NULL) && (rp->pc == pc) && (rp->flags & RPLPOINT_FLAG_ACTIVE)) {
+ if ((rp != NULL) && (rp->pc == pc)
+ && (rp->flags & (RPLPOINT_FLAG_ACTIVE | RPLPOINT_FLAG_COUNTDOWN))) {
+
+ DOLOG( printf("valid replacement point\n"); );
#if !defined(NDEBUG)
executionstate_sanity_check(context);
/* do the actual replacement */
+#if defined(ENABLE_RT_TIMING)
+ RT_TIMING_GET_TIME(time_start);
+#endif
+
replace_me(rp, &es);
+#if defined(ENABLE_RT_TIMING)
+ RT_TIMING_GET_TIME(time_end);
+ RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_REPLACE);
+#endif
+
/* write execution state to current context */
md_executionstate_write(&es, context);
}
#endif /* defined(ENABLE_INLINING) */
+#if defined(ENABLE_SSA)
+
+ iptr = bptr->phis;
+
+ for (i = 0; i < bptr->phicount; i++, iptr++) {
+ printf("%4d:%4d: ", iptr->line, iptr->flags.bits >> INS_FLAG_ID_SHIFT);
+
+ show_icmd(jd, iptr, deadcode, irstage);
+ printf("\n");
+ }
+#endif
+
iptr = bptr->iinstr;
for (i = 0; i < bptr->icount; i++, iptr++) {
case ICMD_GETEXCEPTION:
SHOW_DST(iptr);
break;
+#if defined(ENABLE_SSA)
+ case ICMD_PHI:
+ printf("[ ");
+ for (i = 0; i < iptr->s1.argcount; ++i) {
+ SHOW_VARIABLE(iptr->sx.s23.s2.iargs[i]->dst.varindex);
+ }
+ printf("] ");
+ SHOW_DST(iptr);
+ if (iptr->flags.bits & (1 << 0)) printf("used ");
+ if (iptr->flags.bits & (1 << 1)) printf("redundantAll ");
+ if (iptr->flags.bits & (1 << 2)) printf("redundantOne ");
+ break;
+#endif
}
fflush(stdout);
}
p = jit_compile_handle(m, sfi.pv, ra, (void *) val);
break;
+#if defined(ENABLE_REPLACEMENT)
+ case TRAP_COUNTDOWN:
+#if defined(__I386__)
+ replace_me_wrapper((char*)xpc - 13, context);
+#endif
+ p = NULL;
+ break;
+#endif
+
default:
/* Let's try to get a backtrace. */
if ((ms->flags & ACC_METHOD_IMPLEMENTED) && ms->name != utf_init) {
do {
+
+#if defined(ENABLE_TLH)
+ if (mg->flags & ACC_METHOD_MONOMORPHY_USED) {
+ printf("%s/%s is evil! the siner is %s/%s\n", mg->clazz->name->text, mg->name->text,
+ ms->clazz->name->text, ms->name->text);
+ ms->flags |= ACC_METHOD_PARENT_MONOMORPHY_USED;
+ }
+#endif
+
if (mg->flags & ACC_METHOD_IMPLEMENTED) {
/* this adds another implementation */
m->assumptions = as;
}
-
/* method_break_assumption_monomorphic *****************************************
Break the assumption that this method is monomorphic. All callers that
);
method_add_to_worklist(as->context, wl);
+
+#if defined(ENABLE_TLH) && 0
+ /* XXX hack */
+ method_assumption *as2;
+ as2 = m->assumptions;
+ m->assumptions = NULL;
+ method_break_assumption_monomorphic(as->context, wl);
+ /*
+ assert(m->assumptions == NULL);
+ m->assumptions = as2;*/
+#endif
+
}
}
-
/* method_printflags ***********************************************************
Prints the flags of a method to stdout like.
{ RT_TIMING_GC_COMPACT ,RT_TIMING_GC_TOTAL , "gc: compaction phase" },
{ RT_TIMING_GC_ROOTSET2 ,RT_TIMING_GC_TOTAL , "gc: rootset writeback" },
{ RT_TIMING_GC_TOTAL ,-1 , "total garbage collection time" },
+#endif
+ { -1 ,-1 , "" },
+
+#if defined(ENABLE_REPLACEMENT)
+ { RT_TIMING_REPLACE ,-1 , "replacement" },
{ -1 ,-1 , "" },
#endif
+ { RT_TIMING_1 ,-1 , "temporary timer 1" },
+ { RT_TIMING_2 ,-1 , "temporary timer 2" },
+ { RT_TIMING_3 ,-1 , "temporary timer 3" },
+ { RT_TIMING_4 ,-1 , "temporary timer 4" },
+ { -1 ,-1 , "" },
+
{ 0 ,-1 , NULL }
};
#define RT_TIMING_GC_ROOTSET2 53
#define RT_TIMING_GC_TOTAL 54
-#define RT_TIMING_N 55
+#define RT_TIMING_REPLACE 55
+
+#define RT_TIMING_1 56
+#define RT_TIMING_2 57
+#define RT_TIMING_3 58
+#define RT_TIMING_4 59
+
+#define RT_TIMING_N 60
void rt_timing_gettime(struct timespec *ts);