Passing exception object using native stack is problematic. It causes inefficiency in unwind code. Let exception reference be stored in a per-thread variable 'exception_holder'. This variable will be also used to determine whether exception has been signalled or not. This solution is correct because there can be only one exception processed by given thread.
Signed-off-by: Tomek Grabiec <[email protected]> --- arch/x86/emit-code_32.c | 12 ------ arch/x86/exception.c | 15 +++---- arch/x86/include/arch/instruction.h | 6 --- arch/x86/insn-selector_32.brg | 27 +++++++++++++- arch/x86/unwind_32.S | 30 ++------------ include/jit/exception.h | 24 ++++++++--- jit/compiler.c | 4 -- jit/exception.c | 70 +++++++++++----------------------- vm/jato.c | 6 ++- 9 files changed, 81 insertions(+), 113 deletions(-) diff --git a/arch/x86/emit-code_32.c b/arch/x86/emit-code_32.c index e5d42a1..21853ef 100644 --- a/arch/x86/emit-code_32.c +++ b/arch/x86/emit-code_32.c @@ -507,17 +507,8 @@ static void emit_leave(struct buffer *buf) emit(buf, 0xc9); } -/* - * Emitted code must not write to ECX register because it may hold - * exception object reference when in unwind block - */ static void __emit_epilog(struct buffer *buf) { - /* - * Always emit 'leave' even if method has no local variables - * because exception object reference might have been pushed - * on stack. - */ emit_leave(buf); /* Restore callee saved registers */ @@ -541,9 +532,6 @@ static void __emit_jmp(struct buffer *buf, unsigned long addr) void emit_unwind(struct buffer *buf) { - /* save exception object in ECX */ - __emit_pop_reg(buf, REG_ECX); - __emit_epilog(buf); __emit_jmp(buf, (unsigned long)&unwind); } diff --git a/arch/x86/exception.c b/arch/x86/exception.c index 2a8d6c3..b640778 100644 --- a/arch/x86/exception.c +++ b/arch/x86/exception.c @@ -42,7 +42,9 @@ throw_exception(struct compilation_unit *cu, struct object *exception) native_ptr = __builtin_return_address(0) - 1; frame = __builtin_frame_address(1); - return throw_exception_from(cu, frame, native_ptr, exception); + signal_exception(exception); + + return throw_exception_from(cu, frame, native_ptr); } void throw_exception_from_signal(void *ctx, struct object *exception) @@ -50,23 +52,18 @@ void throw_exception_from_signal(void *ctx, struct object *exception) struct jit_stack_frame *frame; struct compilation_unit *cu; unsigned long source_addr; - unsigned long *stack; ucontext_t *uc; void *eh; + signal_exception(exception); + uc = ctx; source_addr = uc->uc_mcontext.gregs[REG_IP]; cu = get_cu_from_native_addr(source_addr); frame = (struct jit_stack_frame*)uc->uc_mcontext.gregs[REG_BP]; - eh = throw_exception_from(cu, frame, (unsigned char*)source_addr, - exception); + eh = throw_exception_from(cu, frame, (unsigned char*)source_addr); uc->uc_mcontext.gregs[REG_IP] = (unsigned long)eh; - - /* push exception object reference on stack */ - uc->uc_mcontext.gregs[REG_SP] -= sizeof(exception); - stack = (unsigned long*)uc->uc_mcontext.gregs[REG_SP]; - *stack = (unsigned long)exception; } diff --git a/arch/x86/include/arch/instruction.h b/arch/x86/include/arch/instruction.h index 8201d84..86c8492 100644 --- a/arch/x86/include/arch/instruction.h +++ b/arch/x86/include/arch/instruction.h @@ -169,12 +169,6 @@ reload_insn(struct stack_slot *slot, struct var_info *var) return memlocal_reg_insn(INSN_MOV_MEMLOCAL_REG, slot, var); } -static inline struct insn * -exception_spill_insn(struct stack_slot *slot) -{ - return memlocal_insn(INSN_POP_MEMLOCAL, slot); -} - struct insn *alloc_insn(enum insn_type); void free_insn(struct insn *); diff --git a/arch/x86/insn-selector_32.brg b/arch/x86/insn-selector_32.brg index 5cbb562..3d64ada 100644 --- a/arch/x86/insn-selector_32.brg +++ b/arch/x86/insn-selector_32.brg @@ -1145,7 +1145,7 @@ stmt: STMT_ATHROW(reg) select_insn(s, tree, reg_insn(INSN_PUSH_REG, state->left->reg1)); select_insn(s, tree, imm_insn(INSN_PUSH_IMM, (unsigned long)s->b_parent)); select_insn(s, tree, rel_insn(INSN_CALL_REL, (unsigned long)throw_exception)); - method_args_cleanup(s, tree, 1); + method_args_cleanup(s, tree, 2); /* Jump where throw_exception() told us to jump */ select_insn(s, tree, reg_insn(INSN_PUSH_REG, reg_eax)); @@ -1464,6 +1464,28 @@ static void free_state(MBState *state) g_free(state); } +static void eh_add_insn(struct basic_block *bb, struct insn *insn) +{ + insn->bytecode_offset = bb->start; + bb_add_insn(bb, insn); +} + +static void select_eh_prologue(struct basic_block *bb) +{ + struct var_info *reg_eax; + + reg_eax = get_fixed_var(bb->b_parent, REG_EAX); + + /* Save exception from exception_holder to exception stack slot */ + eh_add_insn(bb, imm_reg_insn(INSN_MOV_THREAD_LOCAL_MEMDISP_REG, + get_thread_local_offset(&exception_holder), reg_eax)); + eh_add_insn(bb, reg_memlocal_insn(INSN_MOV_REG_MEMLOCAL, reg_eax, + bb->b_parent->exception_spill_slot)); + + /* Clear exception */ + eh_add_insn(bb, rel_insn(INSN_CALL_REL, (unsigned long)clear_exception)); +} + static void insn_select(struct basic_block *bb) { struct statement *stmt; @@ -1471,6 +1493,9 @@ static void insn_select(struct basic_block *bb) mono_burg_init(); + if (bb->is_eh) + select_eh_prologue(bb); + for_each_stmt(stmt, &bb->stmt_list) { state = mono_burg_label(&stmt->node, bb); emit_code(bb, state, MB_NTERM_stmt); diff --git a/arch/x86/unwind_32.S b/arch/x86/unwind_32.S index 0455c7d..85a3794 100644 --- a/arch/x86/unwind_32.S +++ b/arch/x86/unwind_32.S @@ -14,48 +14,28 @@ unwind: /* * Lookup pointer to compilation unit. */ - push %ecx push %edx call get_cu_from_native_addr + movl %eax, %ecx pop %edx - pop %ecx /* * Restore stack pointer. We need to restore space for locals too. */ - push %ecx push %edx - - push %eax + push %ecx call cu_frame_locals_offset - addl $4, %esp - - pop %edx pop %ecx + pop %edx movl %ebp, %esp subl %eax, %esp - /* - * Lookup pointer to compilation unit. - */ - push %ecx - push %edx - call get_cu_from_native_addr - pop %edx - pop %ecx - - /* - * Push exception object reference to handler - */ - pushl %ecx - - pushl %ecx # exception pushl %edx # native ptr pushl %ebp # frame - pushl %eax # cu + pushl %ecx # cu call throw_exception_from - addl $16, %esp + addl $12, %esp pushl %eax ret diff --git a/include/jit/exception.h b/include/jit/exception.h index 5c0b0e0..42bc2bb 100644 --- a/include/jit/exception.h +++ b/include/jit/exception.h @@ -9,15 +9,22 @@ struct jit_stack_frame; /* * This is a per-thread pointer to a memory location which should be - * polled by JIT code to check whether exception occurred. When - * exception is set this pointer will point to a hidden guard page - * which will trigger SIGSEGV on access. The signal handler will throw - * the exception then. + * polled by JIT code to check for asynchronous exception + * occurrance. When exception is set this pointer will point to a + * hidden guard page which will trigger SIGSEGV on access. The signal + * handler will throw the exception then. */ extern __thread void *exception_guard; extern void *exceptions_guard_page; +/* + * Holds a reference to exception that has been signalled. This + * pointer is cleared when handler is executed or + * clear_exception() is called. + */ +extern __thread struct object *exception_holder; + struct exception_table_entry *exception_find_entry(struct methodblock *, unsigned long); @@ -29,8 +36,7 @@ static inline bool exception_covers(struct exception_table_entry *eh, unsigned char *throw_exception_from(struct compilation_unit *cu, struct jit_stack_frame *frame, - unsigned char *native_ptr, - struct object *exception); + unsigned char *native_ptr); int insert_exception_spill_insns(struct compilation_unit *cu); /* This should be called only by JIT compiled native code */ @@ -41,8 +47,12 @@ void unwind(void); void signal_exception(struct object *obj); void signal_new_exception(char *class_name, char *msg); void clear_exception(void); -struct object *exception_occurred(void); void init_exceptions(void); void thread_init_exceptions(void); +static inline struct object *exception_occurred(void) +{ + return exception_holder; +} + #endif /* JATO_JIT_EXCEPTION_H */ diff --git a/jit/compiler.c b/jit/compiler.c index 83c31c8..3302748 100644 --- a/jit/compiler.c +++ b/jit/compiler.c @@ -51,10 +51,6 @@ int compile(struct compilation_unit *cu) if (opt_trace_tree_ir) trace_tree_ir(cu); - err = insert_exception_spill_insns(cu); - if (err) - goto out; - err = select_instructions(cu); if (err) goto out; diff --git a/jit/exception.c b/jit/exception.c index 2237dbe..e9e9c4b 100644 --- a/jit/exception.c +++ b/jit/exception.c @@ -39,7 +39,9 @@ #include <arch/instruction.h> #include <errno.h> +__thread struct object *exception_holder = NULL; __thread void *exception_guard = NULL; + void *exceptions_guard_page; void init_exceptions(void) @@ -62,23 +64,21 @@ void thread_init_exceptions(void) /** * signal_exception - used for signaling that exception has occurred - * in jato functions. Exception will be thrown as soon as - * controll is returned back to JIT method code. If another - * exception is already signalled no action is done. + * in jato functions. Exception will be thrown when controll + * is returned to JIT code. * * @exception: exception object to be thrown. */ void signal_exception(struct object *exception) { - if (getExecEnv()->exception) + if (exception_holder) return; if (exception == NULL) die("%s: exception is NULL.", __func__); - getExecEnv()->exception = exception; - - exception_guard = exceptions_guard_page; + exception_guard = exceptions_guard_page; + exception_holder = exception; } void signal_new_exception(char *class_name, char *msg) @@ -89,15 +89,10 @@ void signal_new_exception(char *class_name, char *msg) signal_exception(e); } -struct object *exception_occurred(void) -{ - return getExecEnv()->exception; -} - void clear_exception(void) { - exception_guard = &exception_guard; - getExecEnv()->exception = NULL; + exception_guard = &exception_guard; + exception_holder = NULL; } struct exception_table_entry *exception_find_entry(struct methodblock *method, @@ -172,61 +167,42 @@ static unsigned char *find_handler(struct compilation_unit *cu, * unwind can't be done because the method's caller * is not a jitted method). * + * @cu: compilation unit * @frame: frame pointer of method throwing exception * @native_ptr: pointer to instruction that caused exception - * @exception: exception object to throw. */ unsigned char *throw_exception_from(struct compilation_unit *cu, struct jit_stack_frame *frame, - unsigned char *native_ptr, - struct object *exception) + unsigned char *native_ptr) { - struct object *ee_exception; + struct object *exception; unsigned long bc_offset; unsigned char *eh_ptr; eh_ptr = NULL; - ee_exception = exception_occurred(); - if (ee_exception) { - exception = ee_exception; - clear_exception(); - } + exception = exception_occurred(); + assert(exception != NULL); + + clear_exception(); bc_offset = native_ptr_to_bytecode_offset(cu, native_ptr); if (bc_offset != BC_OFFSET_UNKNOWN) { eh_ptr = find_handler(cu, exception->class, bc_offset); - if (eh_ptr != NULL) + if (eh_ptr != NULL) { + signal_exception(exception); return eh_ptr; + } } - if (!is_jit_method(frame->return_address)) { + signal_exception(exception); + + if (!is_jit_method(frame->return_address)) /* * No handler found within jitted method call chain. - * Signal exception and return to previous (not jit) method. + * Return to previous (not jit) method. */ - signal_exception(exception); return bb_native_ptr(cu->exit_bb); - } return bb_native_ptr(cu->unwind_bb); } - -int insert_exception_spill_insns(struct compilation_unit *cu) -{ - struct insn *insn; - struct basic_block *bb; - - for_each_basic_block(bb, &cu->bb_list) { - if (bb->is_eh) { - insn = exception_spill_insn(cu->exception_spill_slot); - if (insn == NULL) - return -ENOMEM; - - insn->bytecode_offset = bb->start; - bb_add_insn(bb, insn); - } - } - - return 0; -} diff --git a/vm/jato.c b/vm/jato.c index 34125dc..f22dc10 100644 --- a/vm/jato.c +++ b/vm/jato.c @@ -361,8 +361,10 @@ int main(int argc, char *argv[]) { /* ExceptionOccured returns the exception or NULL, which is OK for normal conditionals, but not here... */ - if((status = exceptionOccured() ? 1 : 0)) - printException(); + if((status = exception_occurred() ? 1 : 0)) { + getExecEnv()->exception = exception_occurred(); + printException(); + } /* Wait for all but daemon threads to die */ mainThreadWaitToExitVM(); -- 1.6.0.6 ------------------------------------------------------------------------------ OpenSolaris 2009.06 is a cutting edge operating system for enterprises looking to deploy the next generation of Solaris that includes the latest innovations from Sun and the OpenSource community. Download a copy and enjoy capabilities such as Networking, Storage and Virtualization. Go to: http://p.sf.net/sfu/opensolaris-get _______________________________________________ Jatovm-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/jatovm-devel
