Correct compiliation of throwing and catching java constructions.

When no exception handler is found in current method the control is
transfered to the unwind block. This block restores calle saved
registers much like the exit block but instead of returning to caller
it jumps to the 'unwind' routine which restores the caller stack and
rethrows the exception.

When exception is not caught by any jitted method then 'exception'
field of current execution environment is set and the first jitted
method returns to caller.

Signed-off-by: Tomek Grabiec <[email protected]>
---
 arch/x86/Makefile_32              |    1 +
 arch/x86/emit-code_32.c           |   39 ++++++++++++---
 arch/x86/exception_32.c           |    9 +++-
 arch/x86/include/arch/emit-code.h |    3 +-
 arch/x86/include/arch/exception.h |    1 +
 arch/x86/unwind_32.S              |   41 +++++++++++++++
 include/jit/compilation-unit.h    |    1 +
 include/jit/exception.h           |    5 ++
 jit/compilation-unit.c            |    6 ++
 jit/emit.c                        |    3 +
 jit/exception.c                   |   99 +++++++++++++++++++++++++++++++++++++
 scripts/build/test.mk             |    4 ++
 test/arch-x86/Makefile            |    4 ++
 test/include/arch/exception.h     |    8 +++
 test/jamvm/thread-stub.c          |    8 +++
 test/jit/Makefile                 |    3 +
 16 files changed, 226 insertions(+), 9 deletions(-)
 create mode 100644 arch/x86/unwind_32.S
 create mode 100644 test/include/arch/exception.h
 create mode 100644 test/jamvm/thread-stub.c

diff --git a/arch/x86/Makefile_32 b/arch/x86/Makefile_32
index c65c9f0..81bb5ad 100644
--- a/arch/x86/Makefile_32
+++ b/arch/x86/Makefile_32
@@ -8,6 +8,7 @@ ARCH_OBJS = \
        arch/x86/stack-frame.o          \
        arch/x86/use-def.o              \
        arch/x86/exception_32.o         \
+       arch/x86/unwind_32.o            \
        jamvm/os/$(OS)/i386/dll_md.o    \
        jamvm/os/$(OS)/i386/init.o      \
        jamvm/os/$(OS)/os.o
diff --git a/arch/x86/emit-code_32.c b/arch/x86/emit-code_32.c
index 62f2a1c..a9c1ce7 100644
--- a/arch/x86/emit-code_32.c
+++ b/arch/x86/emit-code_32.c
@@ -11,6 +11,8 @@
 #include <jit/statement.h>
 #include <jit/compilation-unit.h>
 #include <jit/compiler.h>
+#include <jit/exception.h>
+#include <jit/stack-slot.h>
 
 #include <vm/list.h>
 #include <vm/buffer.h>
@@ -19,6 +21,7 @@
 #include <arch/emit-code.h>
 #include <arch/instruction.h>
 #include <arch/memory.h>
+#include <arch/exception.h>
 
 #include <assert.h>
 #include <errno.h>
@@ -27,6 +30,12 @@
 #include <stdbool.h>
 #include <string.h>
 
+#define PREFIX_SIZE 1
+#define BRANCH_INSN_SIZE 5
+#define BRANCH_TARGET_OFFSET 1
+
+#define CALL_INSN_SIZE 5
+
 static void __emit_add_imm_reg(struct buffer *buf, long imm, enum machine_reg 
reg);
 static void __emit_push_imm(struct buffer *buf, long imm);
 
@@ -438,8 +447,6 @@ static void emit_push_imm(struct buffer *buf, struct 
operand *operand)
        __emit_push_imm(buf, operand->imm);
 }
 
-#define CALL_INSN_SIZE 5
-
 static void __emit_call(struct buffer *buf, void *call_target)
 {
        int disp = call_target - buffer_current(buf) - CALL_INSN_SIZE;
@@ -469,7 +476,11 @@ static void emit_jmp_reg_direct(struct buffer *buf, struct 
operand *operand)
        __emit_jmp_reg_direct(buf, mach_reg(&operand->reg));
 }
 
-void emit_epilog(struct buffer *buf, unsigned long nr_locals)
+/*
+ * 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, unsigned long nr_locals)
 {
        if (nr_locals)
                emit(buf, 0xc9);
@@ -483,10 +494,28 @@ void emit_epilog(struct buffer *buf, unsigned long 
nr_locals)
 
        /* Discard compilation unit pointer */
        __emit_add_imm_reg(buf, 0x04, REG_ESP);
+}
 
+void emit_epilog(struct buffer *buf, unsigned long nr_locals)
+{
+       __emit_epilog(buf, nr_locals);
        emit_ret(buf);
 }
 
+static void __emit_jmp(struct buffer *buf, unsigned long addr)
+{
+       unsigned long current = (unsigned long)buffer_current(buf);
+
+       emit(buf, 0xE9);
+       emit_imm32(buf, addr - current - BRANCH_INSN_SIZE);
+}
+
+void emit_unwind(struct buffer *buf, unsigned long nr_locals)
+{
+       __emit_epilog(buf, nr_locals);
+       __emit_jmp(buf, (unsigned long)&unwind);
+}
+
 static void emit_adc_reg_reg(struct buffer *buf,
                              struct operand *src, struct operand *dest)
 {
@@ -702,10 +731,6 @@ void emit_branch_rel(struct buffer *buf, unsigned char 
prefix,
        emit_imm32(buf, rel32);
 }
 
-#define PREFIX_SIZE 1
-#define BRANCH_INSN_SIZE 5
-#define BRANCH_TARGET_OFFSET 1
-
 static long branch_rel_addr(struct insn *insn, unsigned long target_offset)
 {
        long ret;
diff --git a/arch/x86/exception_32.c b/arch/x86/exception_32.c
index c703859..c2c155e 100644
--- a/arch/x86/exception_32.c
+++ b/arch/x86/exception_32.c
@@ -24,9 +24,16 @@
  * Please refer to the file LICENSE for details.
  */
 
+#include <jit/exception.h>
 #include <arch/exception.h>
 
 unsigned char *throw_exception(struct object *exception)
 {
-       return NULL; /* TODO */
+       unsigned char *native_ptr;
+       struct jit_stack_frame *frame_ptr;
+
+       native_ptr = __builtin_return_address(0) - 1;
+       frame_ptr  = __builtin_frame_address(1);
+
+       return throw_exception_from(frame_ptr, native_ptr, exception);
 }
diff --git a/arch/x86/include/arch/emit-code.h 
b/arch/x86/include/arch/emit-code.h
index 1e8eb16..51fff75 100644
--- a/arch/x86/include/arch/emit-code.h
+++ b/arch/x86/include/arch/emit-code.h
@@ -10,5 +10,6 @@ void emit_prolog(struct compilation_unit *, struct buffer *, 
unsigned long);
 void emit_epilog(struct buffer *, unsigned long);
 void emit_body(struct basic_block *, struct buffer *);
 void emit_trampoline(struct compilation_unit *, void *, struct 
jit_trampoline*);
-    
+void emit_unwind(struct buffer *, unsigned long);
+
 #endif /* __X86_EMIT_CODE */
diff --git a/arch/x86/include/arch/exception.h 
b/arch/x86/include/arch/exception.h
index 422321e..32fde50 100644
--- a/arch/x86/include/arch/exception.h
+++ b/arch/x86/include/arch/exception.h
@@ -5,5 +5,6 @@
 
 /* This should be called only by JIT compiled native code */
 unsigned char *throw_exception(struct object *exception);
+extern void unwind();
 
 #endif
diff --git a/arch/x86/unwind_32.S b/arch/x86/unwind_32.S
new file mode 100644
index 0000000..1e144f9
--- /dev/null
+++ b/arch/x86/unwind_32.S
@@ -0,0 +1,41 @@
+.global unwind
+.text
+
+/*
+ * unwind - Performs the following:
+ *          1) Resets the stack pointer to point at the end of
+ *             caller's stack frame
+ *          2) Rethrows the exception in caller's context
+ */
+unwind:
+       popl %edx         /* method's return address */
+       decl %edx
+
+       /*
+        * Restore stack pointer.
+        * We need to restore space for locals too.
+        */
+       push %ecx
+       push %edx
+       push %ebp
+       call jit_frame_locals_offset
+       addl $4, %esp
+       pop %edx
+       pop %ecx
+
+       movl %ebp, %esp
+       subl %eax, %esp
+
+       /*
+        * throw_exception_from(frame, native_ptr, exception);
+        */
+       pushl %ecx
+       pushl %edx
+       pushl %ebp
+       call throw_exception_from
+       movl 8(%esp), %ecx  /* restore exception object pointer */
+       addl $12, %esp
+
+       /* Jump where throw_exception_from() told us to jump */
+       pushl %eax
+       ret
diff --git a/include/jit/compilation-unit.h b/include/jit/compilation-unit.h
index 9de65d7..1cd9c46 100644
--- a/include/jit/compilation-unit.h
+++ b/include/jit/compilation-unit.h
@@ -18,6 +18,7 @@ struct compilation_unit {
        struct methodblock *method;
        struct list_head bb_list;
        struct basic_block *exit_bb;
+       struct basic_block *unwind_bb;
        struct var_info *var_infos;
        unsigned long nr_vregs;
        struct buffer *objcode;
diff --git a/include/jit/exception.h b/include/jit/exception.h
index 6ea47f0..ad8cb87 100644
--- a/include/jit/exception.h
+++ b/include/jit/exception.h
@@ -3,6 +3,7 @@
 
 #include <jit/compilation-unit.h>
 #include <vm/vm.h>
+#include <arch/stack-frame.h>
 
 struct exception_table_entry *exception_find_entry(struct methodblock *,
                                                   unsigned long);
@@ -13,4 +14,8 @@ static inline bool exception_covers(struct 
exception_table_entry *eh,
        return eh->start_pc <= offset && offset < eh->end_pc;
 }
 
+unsigned char *throw_exception_from(struct jit_stack_frame *frame,
+                                   unsigned char *native_ptr,
+                                   struct object *exception);
+
 #endif
diff --git a/jit/compilation-unit.c b/jit/compilation-unit.c
index 0848f69..356b114 100644
--- a/jit/compilation-unit.c
+++ b/jit/compilation-unit.c
@@ -46,6 +46,11 @@ struct compilation_unit *alloc_compilation_unit(struct 
methodblock *method)
                cu->exit_bb = alloc_basic_block(cu, 0, 0);
                if (!cu->exit_bb)
                        goto out_of_memory;
+
+               cu->unwind_bb = alloc_basic_block(cu, 0, 0);
+               if (!cu->unwind_bb)
+                       goto out_of_memory;
+
                pthread_mutex_init(&cu->mutex, NULL);
                cu->stack_frame = alloc_stack_frame(method->args_count,
                                                    method->max_locals);
@@ -84,6 +89,7 @@ void free_compilation_unit(struct compilation_unit *cu)
 
        pthread_mutex_destroy(&cu->mutex);
        free_basic_block(cu->exit_bb);
+       free_basic_block(cu->unwind_bb);
        free_buffer(cu->objcode);
        free_var_infos(cu->var_infos);
        free_stack_frame(cu->stack_frame);
diff --git a/jit/emit.c b/jit/emit.c
index b36df07..ffef8f0 100644
--- a/jit/emit.c
+++ b/jit/emit.c
@@ -44,6 +44,9 @@ int emit_machine_code(struct compilation_unit *cu)
        emit_body(cu->exit_bb, cu->objcode);
        emit_epilog(cu->objcode, frame_size);
 
+       emit_body(cu->unwind_bb, cu->objcode);
+       emit_unwind(cu->objcode, frame_size);
+
        return 0;
 }
 
diff --git a/jit/exception.c b/jit/exception.c
index b3d3dd5..055ea7e 100644
--- a/jit/exception.c
+++ b/jit/exception.c
@@ -26,6 +26,9 @@
 
 #include <jit/exception.h>
 #include <jit/compilation-unit.h>
+#include <jit/bc-offset-mapping.h>
+#include <vm/buffer.h>
+#include <arch/exception.h>
 
 struct exception_table_entry *exception_find_entry(struct methodblock *method,
                                                   unsigned long target)
@@ -41,3 +44,99 @@ struct exception_table_entry *exception_find_entry(struct 
methodblock *method,
 
        return NULL;
 }
+
+static unsigned char *eh_native_ptr(struct compilation_unit *cu,
+                                   struct exception_table_entry *eh)
+{
+       struct basic_block *bb = find_bb(cu, eh->handler_pc);
+
+       assert(bb != NULL);
+
+       return bb_native_ptr(bb);
+}
+
+/**
+ * find_handler - return native pointer to exception handler for given
+ *                @exception_class and @bc_offset of source.
+ */
+static unsigned char *find_handler(struct compilation_unit *cu,
+                                  Class *exception_class,
+                                  unsigned long bc_offset)
+{
+       struct exception_table_entry *eh;
+       struct methodblock *method = cu->method;
+       int size = method->exception_table_size;
+       int i;
+
+       for (i = 0; i < size; i++) {
+               eh = &method->exception_table[i];
+
+               if (exception_covers(eh, bc_offset)) {
+                       Class *catch_class;
+
+                       if (eh->catch_type == 0)
+                               break; /* It's a finally block */
+
+                       catch_class = resolveClass(method->class,
+                                                  eh->catch_type,
+                                                  false);
+
+                       if (isInstanceOf(catch_class, exception_class))
+                               break;
+               }
+       }
+
+       if (i < size)
+               return eh_native_ptr(cu, eh);
+
+       return NULL;
+}
+
+/**
+ * throw_exception_from - returns native pointer inside jitted method
+ *                        that sould be executed to handle exception.
+ *                        This can be one of the following:
+ *                        1) registered exception handler (catch/finally block)
+ *                        2) method's unwind block (when no handler is found)
+ *                        3) method's exit block (when no handler is found and
+ *                           unwind can't be done because the method's caller
+ *                           is not a jitted method).
+ *
+ * @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 jit_stack_frame *frame,
+                                   unsigned char *native_ptr,
+                                   struct object *exception)
+{
+       struct compilation_unit *cu;
+       unsigned char *eh_ptr = NULL;
+       unsigned long bc_offset;
+
+       if (getExecEnv()->exception != NULL) {
+               /* Looks like we've caught some asynchronous exception,
+                  which must have precedence. */
+               exception = getExecEnv()->exception;
+               getExecEnv()->exception = NULL;
+       }
+
+       cu = get_cu_from_stack_frame(frame);
+
+       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)
+                       return eh_ptr;
+       }
+
+       if (!called_from_jitted(frame)) {
+               /* No handler found within jitted method call
+                  chain. Signal exception and return to previous
+                  (non-jitted) method. */
+               getExecEnv()->exception = exception;
+               return bb_native_ptr(cu->exit_bb);
+       }
+
+       return bb_native_ptr(cu->unwind_bb);
+}
diff --git a/scripts/build/test.mk b/scripts/build/test.mk
index e3f1b1a..a4afcdb 100644
--- a/scripts/build/test.mk
+++ b/scripts/build/test.mk
@@ -14,6 +14,10 @@ endif
        $(E) "  CC      " $@
        $(Q) $(CC) $(ARCH_CFLAGS) $(DEFAULT_CFLAGS) $(CFLAGS) $(INCLUDE) 
$(DEFINES) -c $< -o `basename $...@`
 
+%.o: %.S
+       $(E) "  AS      " $@
+       $(Q) $(AS) $(AFLAGS) $< -o `basename $...@`
+
 test: $(RUNNER)
 
 $(RUNNER): $(SUITE) $(OBJS)
diff --git a/test/arch-x86/Makefile b/test/arch-x86/Makefile
index 8e3b6ad..8f84cb6 100644
--- a/test/arch-x86/Makefile
+++ b/test/arch-x86/Makefile
@@ -24,6 +24,8 @@ OBJS = \
        ../jamvm/alloc-stub.o \
        ../jamvm/cast-stub.o \
        ../jamvm/lock.o \
+       ../jamvm/thread-stub.o \
+       ../jamvm/resolve-stub.o \
        ../../arch/x86/backtrace.o \
        ../../vm/buffer.o \
        ../../vm/list.o \
@@ -44,12 +46,14 @@ OBJS = \
        ../../jit/tree-printer.o \
        ../../jit/fixup-site.o \
        ../../jit/bc-offset-mapping.o \
+       ../../jit/exception.o \
        ../../arch/x86/emit-code$(ARCH_POSTFIX).o \
        ../../arch/x86/instruction.o \
        ../../arch/x86/insn-selector$(ARCH_POSTFIX).o \
        ../../arch/x86/stack-frame.o \
        ../../arch/x86/use-def.o \
        ../../arch/x86/exception$(ARCH_POSTFIX).o \
+       ../../arch/x86/unwind$(ARCH_POSTFIX).o \
        $(TESTS)
 
 TESTS = \
diff --git a/test/include/arch/exception.h b/test/include/arch/exception.h
new file mode 100644
index 0000000..7bf52aa
--- /dev/null
+++ b/test/include/arch/exception.h
@@ -0,0 +1,8 @@
+#ifndef __ARCH_EXCEPTION_H
+#define __ARCH_EXCEPTION_H
+
+struct object;
+
+unsigned char *throw_exception(struct object *exception);
+
+#endif /* __ARCH_EXCEPTION_H */
diff --git a/test/jamvm/thread-stub.c b/test/jamvm/thread-stub.c
new file mode 100644
index 0000000..8eeb0aa
--- /dev/null
+++ b/test/jamvm/thread-stub.c
@@ -0,0 +1,8 @@
+#include <vm/vm.h>
+
+ExecEnv *getExecEnv()
+{
+       static ExecEnv ee;
+
+       return &ee;
+}
diff --git a/test/jit/Makefile b/test/jit/Makefile
index 89d1489..ceeb0aa 100644
--- a/test/jit/Makefile
+++ b/test/jit/Makefile
@@ -38,8 +38,11 @@ OBJS = \
        ../libharness/libharness.o \
        ../jamvm/alloc-stub.o \
        ../jamvm/resolve-stub.o \
+       ../jamvm/thread-stub.o \
+       ../jamvm/cast-stub.o \
        ../arch-mmix/instruction.o \
        ../arch-mmix/use-def.o \
+       ../arch-mmix/stack-frame.o \
        arithmetic-bc-test.o \
        basic-block-test.o \
        bc-test-utils.o \
-- 
1.6.0.6


------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image 
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Jatovm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jatovm-devel

Reply via email to