to use from CLI and JVM style systems.
+2004-05-24 Rhys Weatherley <rweather@southern-storm.com.au>
+
+ * include/jit/jit-insn.h, include/jit/jit-opcode.h, jit/jit-block.c,
+ jit/jit-dump.c, jit/jit-except.c, jit/jit-function.c, jit/jit-insn.c,
+ jit/jit-internal.h, jit/jit-interp.c, jit/jit-interp.h,
+ jit/jit-opcode.c, jit/jit-rules-arm.c, jit/jit-rules-interp.c,
+ jit/jit-rules-x86.c, jit/jit-rules.h, jit/jit-setjmp.h:
+ rewrite the exception region routines to make them easier
+ to use from CLI and JVM style systems.
+
2004-05-22 Rhys Weatherley <rweather@southern-storm.com.au>
* jit/jit-block.c (_jit_block_peephole_branch): don't allow
(jit_function_t func, jit_value_t value, jit_label_t *label) JIT_NOTHROW;
jit_value_t jit_insn_address_of
(jit_function_t func, jit_value_t value1) JIT_NOTHROW;
+jit_value_t jit_insn_address_of_label
+ (jit_function_t func, jit_label_t *label) JIT_NOTHROW;
jit_value_t jit_insn_convert
(jit_function_t func, jit_value_t value,
jit_type_t type, int overflow_check) JIT_NOTHROW;
int jit_insn_default_return(jit_function_t func) JIT_NOTHROW;
int jit_insn_throw(jit_function_t func, jit_value_t value) JIT_NOTHROW;
jit_value_t jit_insn_get_call_stack(jit_function_t func) JIT_NOTHROW;
-int jit_insn_start_try
- (jit_function_t func, jit_label_t *catch_label,
- jit_label_t *finally_label, int finally_on_fault);
-jit_value_t jit_insn_start_catch
- (jit_function_t func, jit_label_t *catch_label);
-int jit_insn_end_try(jit_function_t func);
-int jit_insn_start_finally(jit_function_t func, jit_label_t *finally_label);
-int jit_insn_return_from_finally(jit_function_t func);
+
+jit_value_t jit_insn_thrown_exception(jit_function_t func) JIT_NOTHROW;
+int jit_insn_uses_catcher(jit_function_t func) JIT_NOTHROW;
+jit_value_t jit_insn_start_catcher(jit_function_t func) JIT_NOTHROW;
+int jit_insn_branch_if_pc_not_in_range
+ (jit_function_t func, jit_label_t start_label,
+ jit_label_t end_label, jit_label_t *label) JIT_NOTHROW;
+int jit_insn_rethrow_unhandled(jit_function_t func) JIT_NOTHROW;
+int jit_insn_start_finally
+ (jit_function_t func, jit_label_t *finally_label) JIT_NOTHROW;
+int jit_insn_return_from_finally(jit_function_t func) JIT_NOTHROW;
+int jit_insn_call_finally
+ (jit_function_t func, jit_label_t *finally_label) JIT_NOTHROW;
jit_value_t jit_insn_start_filter
- (jit_function_t func, jit_label_t *label, jit_type_t type);
-int jit_insn_return_from_filter(jit_function_t func, jit_value_t value);
+ (jit_function_t func, jit_label_t *label, jit_type_t type) JIT_NOTHROW;
+int jit_insn_return_from_filter
+ (jit_function_t func, jit_value_t value) JIT_NOTHROW;
jit_value_t jit_insn_call_filter
(jit_function_t func, jit_label_t *label,
- jit_value_t value, jit_type_t type);
+ jit_value_t value, jit_type_t type) JIT_NOTHROW;
int jit_insn_memcpy
(jit_function_t func, jit_value_t dest,
* Exception handling.
*/
#define JIT_OP_THROW 0x014C
-#define JIT_OP_LOAD_PC 0x014D
-#define JIT_OP_ENTER_CATCH 0x014E
-#define JIT_OP_ENTER_FINALLY 0x014F
-#define JIT_OP_LEAVE_FINALLY 0x0150
-#define JIT_OP_ENTER_FILTER 0x0151
-#define JIT_OP_LEAVE_FILTER 0x0152
-#define JIT_OP_CALL_FILTER 0x0153
-#define JIT_OP_CALL_FILTER_RETURN 0x0154
-#define JIT_OP_PREPARE_FOR_LEAVE 0x0155
-#define JIT_OP_PREPARE_FOR_RETURN 0x0156
-#define JIT_OP_JUMP_TO_CATCHER 0x0157
+#define JIT_OP_RETHROW 0x014D
+#define JIT_OP_LOAD_PC 0x014E
+#define JIT_OP_LOAD_EXCEPTION_PC 0x014F
+#define JIT_OP_ENTER_FINALLY 0x0150
+#define JIT_OP_LEAVE_FINALLY 0x0151
+#define JIT_OP_CALL_FINALLY 0x0152
+#define JIT_OP_ENTER_FILTER 0x0153
+#define JIT_OP_LEAVE_FILTER 0x0154
+#define JIT_OP_CALL_FILTER 0x0155
+#define JIT_OP_CALL_FILTER_RETURN 0x0156
+#define JIT_OP_ADDRESS_OF_LABEL 0x0157
/*
* Data manipulation.
#define JIT_OPCODE_IS_CALL 0x00002000
#define JIT_OPCODE_IS_CALL_EXTERNAL 0x00004000
#define JIT_OPCODE_IS_REG 0x00008000
+#define JIT_OPCODE_IS_ADDROF_LABEL 0x00010000
#define JIT_OPCODE_OPER_MASK 0x01F00000
#define JIT_OPCODE_OPER_NONE 0x00000000
#define JIT_OPCODE_OPER_ADD 0x00100000
{
jit_block_t current = func->builder->first_block;
jit_block_t next;
- jit_block_eh_t current_eh;
- jit_block_eh_t next_eh;
while(current != 0)
{
next = current->next;
jit_free(current);
current = next;
}
- current_eh = func->builder->exception_handlers;
- while(current_eh != 0)
- {
- next_eh = current_eh->next;
- jit_free(current_eh);
- current_eh = next_eh;
- }
func->builder->first_block = 0;
func->builder->last_block = 0;
func->builder->entry = 0;
func->builder->current_block = 0;
- func->builder->exception_handlers = 0;
- func->builder->current_handler = 0;
}
jit_block_t _jit_block_create(jit_function_t func, jit_label_t *label)
block->label = jit_label_undefined;
}
- /* Set the exception handling context for this block */
- block->block_eh = func->builder->current_handler;
-
/* Add the block to the end of the function's list */
block->next = 0;
block->prev = func->builder->last_block;
{
break;
}
- if(new_block->block_eh != block->block_eh && insn->opcode != JIT_OP_BR)
- {
- /* Conditional branches must never cross an exception context */
- break;
- }
if(new_block->first_insn < new_block->last_insn)
{
/* There is more than one instruction in this block,
putc(')', stream);
return;
}
+ else if((flags & JIT_OPCODE_IS_ADDROF_LABEL) != 0)
+ {
+ fprintf(stream, "address_of_label .L%ld",
+ (long)(jit_insn_get_label(insn)));
+ return;
+ }
/* Output the destination information */
if((flags & JIT_OPCODE_DEST_MASK) != JIT_OPCODE_DEST_EMPTY &&
default:
{
- if((info->flags & JIT_OPCODE_IS_BRANCH) != 0)
+ if((info->flags & (JIT_OPCODE_IS_BRANCH |
+ JIT_OPCODE_IS_ADDROF_LABEL)) != 0)
{
fprintf(stream, " %08lX",
(long)(jit_nint)((pc - 1) + (jit_nint)(*pc)));
if(control)
{
jbuf->trace = control->backtrace_head;
- jbuf->catcher = 0;
+ jbuf->catch_pc = 0;
jbuf->parent = control->setjmp_head;
control->setjmp_head = jbuf;
}
}
}
-/*
- * Call "finally" blocks that are relevant for a transition from
- * "src" to "dest". "dest" should be NULL for a return instruction.
- */
-static void call_finally_blocks(jit_gencode_t gen, jit_function_t func,
- jit_block_eh_t src, jit_block_eh_t dest)
-{
- jit_block_eh_t temp;
-
- /* Bail out if we are branching to an inner scope, because we
- don't need to call "finally" clauses to go further in */
- temp = dest;
- while(temp != 0)
- {
- if(temp == src)
- {
- return;
- }
- temp = temp->parent;
- }
-
- /* Find the common ancestor of "src" and "dest" */
- while(dest != 0)
- {
- temp = src;
- while(temp != 0 && temp != dest)
- {
- temp = temp->parent;
- }
- if(temp != 0)
- {
- break;
- }
- dest = dest->parent;
- }
-
- /* Output all finally blocks between "src" and "dest" */
- while(src != dest)
- {
- if(src->catch_label != jit_label_undefined &&
- !(src->finally_on_fault))
- {
- /* Call "finally" from the body of the "try" */
- if(src->finally_label != jit_label_undefined)
- {
- _jit_gen_call_finally(gen, func, src->finally_label);
- }
- }
- else if(src->catch_label == jit_label_undefined)
- {
- /* Call "finally" from the body of the "catch" */
- if(src->finally_label != jit_label_undefined)
- {
- _jit_gen_call_finally(gen, func, src->finally_label);
- }
- }
- src = src->parent;
- }
-}
-
/*
* Compile a single basic block within a function.
*/
{
jit_insn_iter_t iter;
jit_insn_t insn;
- jit_block_t branch_block;
/* Iterate over all blocks in the function */
jit_insn_iter_init(&iter, block);
}
break;
- case JIT_OP_PREPARE_FOR_LEAVE:
- {
- /* Call the finally clauses that are relevant to the
- "JIT_OP_BR" instruction that follows */
- _jit_regs_spill_all(gen);
- insn = jit_insn_iter_next(&iter);
- branch_block = jit_block_from_label
- (func, (jit_label_t)(insn->dest));
- call_finally_blocks
- (gen, func, block->block_eh, branch_block->block_eh);
- _jit_gen_insn(gen, func, block, insn);
- }
- break;
-
- case JIT_OP_PREPARE_FOR_RETURN:
- {
- /* Call all finally clauses on the way out of the function */
- _jit_regs_spill_all(gen);
- call_finally_blocks(gen, func, block->block_eh, 0);
- }
- break;
-
case JIT_OP_INCOMING_REG:
{
/* Assign a register to an incoming value */
void *recompilable_start = 0;
void *end;
jit_block_t block;
- jit_block_eh_t eh;
- jit_block_eh_t new_eh;
- jit_cache_eh_t cache_eh;
- jit_cache_eh_t cache_new_eh;
int result;
#ifdef JIT_PROLOG_SIZE
int have_prolog;
/* Generate code for the blocks in the function */
block = 0;
- eh = 0;
- cache_eh = 0;
while((block = jit_block_next(func, block)) != 0)
{
/* If this block is never entered, then discard it */
continue;
}
- /* Start a new exception region in the cache if necessary */
- new_eh = block->block_eh;
- if(new_eh && new_eh->catch_label == jit_label_undefined)
- {
- new_eh = 0;
- }
- if(new_eh != eh)
- {
- eh = new_eh;
- cache_new_eh = _jit_cache_alloc
- (&(gen.posn), sizeof(struct jit_cache_eh));
- if(cache_new_eh)
- {
- if(eh)
- {
- cache_new_eh->handler_label = eh->catch_label;
- }
- else
- {
- cache_new_eh->handler_label = jit_label_undefined;
- }
- cache_new_eh->handler = 0;
- cache_new_eh->previous = cache_eh;
- _jit_cache_new_region(&(gen.posn), cache_new_eh);
- cache_eh = cache_new_eh;
- }
- }
-
/* Notify the back end that the block is starting */
_jit_gen_start_block(&gen, block);
_jit_regs_init_for_block(&gen);
}
- /* Fix up the labels for the exception regions */
- while(cache_eh != 0)
- {
- if(cache_eh->handler_label != jit_label_undefined)
- {
- block = jit_block_from_label(func, cache_eh->handler_label);
- if(block)
- {
- cache_eh->handler = (unsigned char *)(block->address);
- }
- }
- cache_eh = cache_eh->previous;
- }
-
/* Output the function epilog. All return paths will jump to here */
_jit_gen_epilog(&gen, func);
end = gen.posn.ptr;
{
return (jit_label_t)(insn->dest);
}
+ else if(insn && (insn->flags & JIT_INSN_VALUE1_IS_LABEL) != 0)
+ {
+ /* "address_of_label" instruction */
+ return (jit_label_t)(insn->value1);
+ }
else
{
return 0;
}
}
-/*
- * Output instructions to handle "finally" clauses in the current context.
- */
-static int handle_finally(jit_function_t func, int opcode)
-{
- jit_block_eh_t eh = func->builder->current_handler;
- while(eh != 0)
- {
- if(eh->finally_on_fault)
- {
- /* The "finally" clause only applies to the "catch" block */
- if(!(eh->in_try_body) &&
- eh->finally_label != jit_label_undefined)
- {
- return create_noarg_note(func, opcode);
- }
- }
- else if(eh->finally_label != jit_label_undefined)
- {
- /* The "finally" clause applies to "try" body and the "catch" */
- return create_noarg_note(func, opcode);
- }
- eh = eh->parent;
- }
- return 1;
-}
-
/*@
* @deftypefun int jit_insn_branch (jit_function_t func, {jit_label_t *} label)
* Terminate the current block by branching unconditionally
{
return 0;
}
- if(!handle_finally(func, JIT_OP_PREPARE_FOR_LEAVE))
- {
- return 0;
- }
insn = _jit_block_add_insn(func->builder->current_block);
if(!insn)
{
return result;
}
+/*@
+ * @deftypefun jit_value_t jit_insn_address_of_label (jit_function_t func, {jit_label_t *} label)
+ * Get the address of @code{label} into a new temporary. This is typically
+ * used for exception handling, to track where in a function an exception
+ * was actually thrown.
+ * @end deftypefun
+@*/
+jit_value_t jit_insn_address_of_label(jit_function_t func, jit_label_t *label)
+{
+ jit_value_t dest;
+ jit_insn_t insn;
+ if(!_jit_function_ensure_builder(func) || !label)
+ {
+ return 0;
+ }
+ if(*label == jit_label_undefined)
+ {
+ *label = (func->builder->next_label)++;
+ }
+ insn = _jit_block_add_insn(func->builder->current_block);
+ if(!insn)
+ {
+ return 0;
+ }
+ dest = jit_value_create(func, jit_type_void_ptr);
+ if(!dest)
+ {
+ return 0;
+ }
+ insn->opcode = (short)JIT_OP_ADDRESS_OF_LABEL;
+ insn->flags = JIT_INSN_VALUE1_IS_LABEL;
+ insn->dest = dest;
+ insn->value1 = (jit_value_t)label;
+ return dest;
+}
+
/*
* Information about the opcodes for a particular conversion.
*/
jit_type_free(type);
#endif
- /* Are we currently within a "try" context covered by a "catch"? */
- block = func->builder->current_block;
- if(block->block_eh &&
- block->block_eh->catch_label != jit_label_undefined &&
- func->builder->setjmp_value != 0)
+ /* Update the "catch_pc" value to reflect the current context */
+ if(func->builder->setjmp_value != 0)
{
- /* Set the "catcher" field within "setjmp_value" to the catcher */
args[0] = jit_value_create(func, jit_type_void_ptr);
if(!(args[0]))
{
{
return 0;
}
- jit_value_ref(func, args[1]);
- insn->opcode = JIT_OP_LOAD_CATCHER_PC;
- insn->flags |= JIT_INSN_VALUE1_IS_LABEL;
+ jit_value_ref(func, args[0]);
+ insn->opcode = JIT_OP_LOAD_PC;
insn->dest = args[0];
- insn->value1 = (jit_value_t)(block->block_eh->catch_label);
if(!jit_insn_store_relative
(func, jit_insn_address_of(func->builder->setjmp_value),
- jit_jmp_catcher_offset, args[0]))
+ jit_jmp_catch_pc_offset, args[0]))
{
return 0;
}
jit_type_free(type);
#endif
- /* Are we currently within a "try" context covered by a "catch"? */
- block = func->builder->current_block;
- if(block->block_eh &&
- block->block_eh->catch_label != jit_label_undefined &&
- func->builder->setjmp_value != 0)
+ /* Clear the "catch_pc" value for the current context */
+ if(func->builder->setjmp_value != 0)
{
- /* Set the "catcher" field within "setjmp_value" to NULL */
args[0] = jit_value_create_nint_constant(func, jit_type_void_ptr, 0);
if(!(args[0]))
{
}
if(!jit_insn_store_relative
(func, jit_insn_address_of(func->builder->setjmp_value),
- jit_jmp_catcher_offset, args[0]))
+ jit_jmp_catch_pc_offset, args[0]))
{
return 0;
}
type = jit_type_promote_int(type);
if(!value || type == jit_type_void)
{
- /* Handle "finally" clauses if necessary */
- if(!handle_finally(func, JIT_OP_PREPARE_FOR_RETURN))
- {
- return 0;
- }
-
/* This function returns "void" */
if(!create_noarg_note(func, JIT_OP_RETURN))
{
return 0;
}
- /* Handle "finally" clauses if necessary */
- if(!handle_finally(func, JIT_OP_PREPARE_FOR_RETURN))
- {
- return 0;
- }
-
/* Create the "return" instruction */
switch(type->kind)
{
case JIT_TYPE_STRUCT:
case JIT_TYPE_UNION:
{
- /* Handle "finally" clauses if necessary */
- if(!handle_finally(func, JIT_OP_PREPARE_FOR_RETURN))
- {
- return 0;
- }
-
/* Determine the kind of structure return to use */
return_ptr = jit_value_get_struct_pointer(func);
if(return_ptr)
return value;
}
+/*@
+ * @deftypefun jit_value_t jit_insn_thrown_exception (jit_function_t func)
+ * Get the value that holds the most recent thrown exception. This is
+ * typically used in @code{catch} clauses.
+ * @end deftypefun
+@*/
+jit_value_t jit_insn_thrown_exception(jit_function_t func)
+{
+ if(!_jit_function_ensure_builder(func))
+ {
+ return 0;
+ }
+ if(!(func->builder->thrown_exception))
+ {
+ func->builder->thrown_exception =
+ jit_value_create(func, jit_type_void_ptr);
+ }
+ return func->builder->thrown_exception;
+}
+
/*
* Initialize the "setjmp" setup block that is needed to catch exceptions
* thrown back to this level of execution. The block looks like this:
* _jit_unwind_push_setjmp(&jbuf);
* if(setjmp(&jbuf.buf))
* {
- * catcher = jbuf.catcher;
- * if(catcher)
+ * catch_pc = jbuf.catch_pc;
+ * if(catch_pc)
* {
- * jbuf.catcher = 0;
+ * jbuf.catch_pc = 0;
* goto *catcher;
* }
* else
* }
* }
*
- * The field "jbuf.catcher" will be set to the address of the relevant
+ * The field "jbuf.catch_pc" will be set to the address of the relevant
* "catch" block just before a subroutine call that may involve exceptions.
* It will be reset to NULL after such subroutine calls.
*
return 1;
}
func->builder->longjmp_label = jit_label_undefined;
+ func->builder->catcher_label = jit_label_undefined;
/* Force the start of a new block to mark the start of the init code */
if(!jit_insn_label(func, &start_label))
return 0;
}
- /* Get the value of "catcher" from within "setjmp_value". This indicates
- which catcher we should use to handle the thrown exception. If the
- catcher is NULL, then we need to rethrow the exception higher up
- because it isn't covered by any of the catch blocks that we have */
+ /* We need a value to hold the location of the thrown exception */
+ if((func->builder->thrown_pc =
+ jit_value_create(func, jit_type_void_ptr)) == 0)
+ {
+ return 0;
+ }
+
+ /* Get the value of "catch_pc" from within "setjmp_value" and store it
+ into the current frame. This indicates where the exception occurred */
value = jit_insn_load_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
- jit_jmp_catcher_offset, jit_type_void_ptr);
+ jit_jmp_catch_pc_offset, jit_type_void_ptr);
if(!value)
{
return 0;
}
+ if(!jit_insn_store(func, func->builder->thrown_pc, value))
+ {
+ return 0;
+ }
if(!jit_insn_branch_if_not(func, value, &rethrow_label))
{
return 0;
}
- /* Clear the original "catcher" value within "setjmp_value" */
+ /* Clear the original "catch_pc" value within "setjmp_value" */
if(!jit_insn_store_relative
(func, jit_insn_address_of(func, func->builder->setjmp_value),
jit_jmp_catcher_offset, jit_value_create_nint_constant
return 0;
}
- /* Jump to the address indicated by the catcher */
- if(!create_unary_note(func, JIT_OP_JUMP_TO_CATCHER, value))
+ /* Jump to this function's exception catcher */
+ if(!jit_insn_branch(func, &(func->builder->catcher_label))
{
return 0;
}
- func->builder->current_block->ends_in_dead = 1;
/* Mark the position of the rethrow label */
if(!jit_insn_label(func, &rethrow_label))
return jit_insn_move_blocks_to_start(func, start_label, end_label);
#else
/* The interpreter doesn't need the "setjmp" setup block */
+ func->builder->catcher_label = jit_label_undefined;
return 1;
#endif
}
/*@
- * @deftypefun int jit_insn_start_try (jit_function_t func, {jit_label_t *} catch_label, {jit_label_t *} finally_label, int finally_on_fault)
- * Start an exception-handling @code{try} block at the current position
- * within @code{func}.
- *
- * Whenever an exception occurs in the block, execution will jump to
- * @code{catch_label}. When execution exits the @code{try} block's scope,
- * @code{finally_label} will be called. If @code{finally_on_fault} is
- * non-zero, then @code{finally_label} will be called for exceptions,
- * but not when control exits the @code{try} block normally.
- *
- * The @code{finally_label} parameter may be NULL if the @code{try} block
- * does not have a @code{finally} clause associated with it.
- *
- * All of the blocks between @code{jit_insn_start_try} and
- * @code{jit_insn_start_catch} are covered by the @code{catch}
- * clause and the @code{finally} clause. Blocks between
- * @code{jit_insn_start_catch} and @code{jit_insn_end_try} are
- * covered by the @code{finally} clause.
- *
- * Calls to @code{jit_insn_branch}, @code{jit_insn_return},
- * @code{jit_insn_throw} and @code{jit_insn_rethrow} will cause
- * additional code to be output to invoke the relevant @code{finally}
- * clauses.
- *
- * The destinations for @code{jit_insn_branch_if} and
- * @code{jit_insn_branch_if_not} must never be outside the current
- * @code{finally} context. You should always use @code{jit_insn_branch}
- * to branch out of @code{finally} contexts.
- *
- * Note: you don't need to output calls to @code{finally} clauses
- * yourself as @code{libjit} can detect when it is necessary on its own.
+ * @deftypefun int jit_insn_uses_catcher (jit_function_t func)
+ * Notify the function building process that @code{func} contains
+ * some form of @code{catch} clause for catching exceptions. This must
+ * be called before any instruction that is covered by a @code{try},
+ * ideally at the start of the function output process.
* @end deftypefun
@*/
-int jit_insn_start_try
- (jit_function_t func, jit_label_t *catch_label,
- jit_label_t *finally_label, int finally_on_fault)
+int jit_insn_uses_catcher(jit_function_t func)
{
- jit_block_eh_t eh;
- jit_block_t block;
-
- /* Ensure that we have a function builder */
- if(!_jit_function_ensure_builder(func) || !catch_label)
+ if(!_jit_function_ensure_builder(func))
{
return 0;
}
-
- /* Allocate the label numbers */
- if(*catch_label == jit_label_undefined)
- {
- *catch_label = (func->builder->next_label)++;
- }
- if(finally_label && *finally_label == jit_label_undefined)
+ if(func->has_try)
{
- *finally_label = (func->builder->next_label)++;
+ return 1;
}
-
- /* This function has a "try" block. This flag helps native
- back ends know when they must be careful about global
- register allocation */
func->has_try = 1;
+ func->builder->may_throw = 1;
+ func->builder->non_leaf = 1;
+ return initialize_setjmp_block(func);
+}
- /* Make sure that the "setjmp" setup block is present in this function */
- if(!initialize_setjmp_block(func))
+/*@
+ * @deftypefun jit_value_t jit_insn_start_catcher (jit_function_t func)
+ * Start the catcher block for @code{func}. There should be exactly one
+ * catcher block for any function that involves a @code{try}. All
+ * exceptions that are thrown within the function will cause control
+ * to jump to this point. Returns a value that holds the exception
+ * that was thrown.
+ * @end deftypefun
+@*/
+jit_value_t jit_insn_start_catcher(jit_function_t func)
+{
+ jit_value_t value;
+ if(!_jit_function_ensure_builder(func))
{
return 0;
}
-
- /* Anything with a finally handler makes the function not a leaf,
- because we may need to do a native "call" to invoke the handler */
- if(finally_label)
+ if(!jit_insn_label(func, &(func->builder->catcher_label)))
{
- func->builder->non_leaf = 1;
+ return 0;
}
-
- /* Create a new exception handling context and populate it */
- eh = jit_cnew(struct jit_block_eh);
- if(!eh)
+ value = jit_insn_thrown_exception(func);
+ if(!value)
{
return 0;
}
- eh->parent = func->builder->current_handler;
- eh->next = func->builder->exception_handlers;
- func->builder->exception_handlers = eh;
- eh->catch_label = *catch_label;
- eh->finally_label = (finally_label ? *finally_label : jit_label_undefined);
- eh->finally_on_fault = finally_on_fault;
- eh->in_try_body = 1;
- func->builder->current_handler = eh;
-
- /* Start a new block, for the body of the "try" */
- block = _jit_block_create(func, 0);
- if(!block)
+#if defined(JIT_BACKEND_INTERP)
+ /* In the interpreter, the exception object will be on the top of
+ the operand stack when control reaches the catcher */
+ if(!jit_insn_incoming_reg(func, value, 0))
{
return 0;
}
- block->entered_via_top = 1;
- return 1;
+#endif
+ return value;
}
/*@
- * @deftypefun jit_value_t jit_insn_start_catch (jit_function_t func, {jit_label_t *} catch_label)
- * Start the @code{catch} clause for the currently active @code{try}
- * block. Returns a pointer @code{value} that indicates the exception
- * that is thrown.
- *
- * There can be only one @code{catch} clause per @code{try} block.
- * The front end is responsible for outputting instructions that
- * inspect the exception object, determine if it is appropriate to
- * the clause, and then either handle it or rethrow it.
- *
- * Every @code{try} block must have an associated @code{catch} clause,
- * even if all it does is rethrow the exception immediately. Without a
- * @code{catch} clause, the correct @code{finally} logic will not be
- * performed for thrown exceptions.
+ * @deftypefun int jit_insn_branch_if_pc_not_in_range (jit_function_t func, jit_label_t start_label, jit_label_t end_label, {jit_label_t *} label)
+ * Branch to @code{label} if the program counter where an exception occurred
+ * does not fall between @code{start_label} and @code{end_label}.
* @end deftypefun
@*/
-jit_value_t jit_insn_start_catch
- (jit_function_t func, jit_label_t *catch_label)
+int jit_insn_branch_if_pc_not_in_range
+ (jit_function_t func, jit_label_t start_label,
+ jit_label_t end_label, jit_label_t *label)
{
- jit_block_eh_t eh;
- jit_block_eh_t new_eh;
+ jit_value_t value1;
+ jit_value_t value2;
- /* Ensure that we have a function builder */
+ /* Ensure that we have a function builder and a try block */
if(!_jit_function_ensure_builder(func))
{
return 0;
}
+ if(!(func->has_try))
+ {
+ return 0;
+ }
- /* Create a new exception handler to indicate that we are in a "catch" */
- eh = func->builder->current_handler;
- if(!eh)
+ /* Get the location where the exception occurred in this function */
+#if defined(JIT_BACKEND_INTERP)
+ value1 = create_dest_note
+ (func, JIT_OP_LOAD_EXCEPTION_PC, jit_type_void_ptr);
+#else
+ value1 = func->builder->thrown_pc;
+#endif
+ if(!value1)
{
return 0;
}
- new_eh = jit_cnew(struct jit_block_eh);
- if(!new_eh)
+
+ /* Compare the location against the start and end labels */
+ value2 = jit_insn_address_of_label(func, &start_label);
+ if(!value2)
{
return 0;
}
- new_eh->parent = eh->parent;
- new_eh->next = func->builder->exception_handlers;
- func->builder->exception_handlers = new_eh;
- if(eh->finally_label != jit_label_undefined)
+ if(!jit_insn_branch_if(func, jit_insn_lt(func, value1, value2), label))
{
- /* We will need a mini "catch" block to call the "finally"
- clause when an exception occurs within the "catch" */
- new_eh->catch_label = (func->builder->next_label)++;
+ return 0;
}
- else
+ value2 = jit_insn_address_of_label(func, &end_label);
+ if(!value2)
{
- new_eh->catch_label = jit_label_undefined;
+ return 0;
}
- new_eh->finally_label = eh->finally_label;
- new_eh->finally_on_fault = eh->finally_on_fault;
- new_eh->in_try_body = 0;
- func->builder->current_handler = new_eh;
-
- /* Start a new block for the "catch" clause */
- if(!jit_insn_label(func, catch_label))
+ if(!jit_insn_branch_if(func, jit_insn_ge(func, value1, value2), label))
{
return 0;
}
- /* Output an instruction to notice the caught value */
- return create_dest_note(func, JIT_OP_ENTER_CATCH, jit_type_void_ptr);
+ /* If control gets here, then we have a location match */
+ return 1;
}
/*@
- * @deftypefun int jit_insn_end_try (jit_function_t func)
- * Mark the end of the @code{try} block and its associated @code{catch}
- * clause. This is normally followed by a call to
- * @code{jit_insn_start_finally} to define the @code{finally} clause.
+ * @deftypefun int jit_insn_rethrow_unhandled (jit_function_t func)
+ * Rethrow the current exception because it cannot be handled by
+ * any of the @code{catch} blocks in the current function.
+ *
+ * Note: this is intended for use within catcher blocks. It should not
+ * be used to rethrow exceptions in response to programmer requests
+ * (e.g. @code{throw;} in C#). The @code{jit_insn_throw} function
+ * should be used for that purpose.
* @end deftypefun
@*/
-int jit_insn_end_try(jit_function_t func)
+int jit_insn_rethrow_unhandled(jit_function_t func)
{
- jit_block_eh_t eh;
jit_block_t block;
jit_value_t value;
+#if !defined(JIT_BACKEND_INTERP)
+ jit_type_t type;
+#endif
/* Ensure that we have a function builder */
if(!_jit_function_ensure_builder(func))
return 0;
}
- /* Get the current exception handling context */
- eh = func->builder->current_handler;
- if(!eh)
+ /* Get the current exception value to be thrown */
+ value = jit_insn_thrown_exception(func);
+ if(!value)
{
return 0;
}
- /* We may need another mini "catch" block to invoke the "finally"
- clause for exceptions that happen during the "catch" */
- if(eh->catch_label != jit_label_undefined)
+#if defined(JIT_BACKEND_INTERP)
+
+ /* Rethrow the current exception (interpreter version) */
+ if(!create_unary_note(func, JIT_OP_RETHROW, value))
{
- /* TODO: not quite right */
- if(!jit_insn_label(func, &(eh->catch_label)))
- {
- return 0;
- }
- value = create_dest_note(func, JIT_OP_ENTER_CATCH, jit_type_void_ptr);
- if(!value)
- {
- return 0;
- }
- if(!jit_insn_throw(func, value))
- {
- return 0;
- }
+ return 0;
+ }
+
+#else /* !JIT_BACKEND_INTERP */
+
+ /* Call "_jit_unwind_pop_setjmp" to remove the current exception catcher */
+ if(!_jit_function_ensure_builder(func))
+ {
+ return 0;
+ }
+ type = jit_type_create_signature
+ (jit_abi_cdecl, jit_type_void, 0, 0, 1);
+ if(!type)
+ {
+ return 0;
+ }
+ jit_insn_call_native
+ (func, "_jit_unwind_pop_setjmp",
+ (void *)_jit_unwind_pop_setjmp, type, 0, 0, JIT_CALL_NOTHROW);
+ jit_type_free(type);
+
+ /* Call the "jit_exception_throw" function to effect the rethrow */
+ type = jit_type_void_ptr;
+ type = jit_type_create_signature
+ (jit_abi_cdecl, jit_type_void, &type, 1, 1);
+ if(!type)
+ {
+ return 0;
}
+ jit_insn_call_native
+ (func, "jit_exception_throw",
+ (void *)jit_exception_throw, type, &value, 1,
+ JIT_CALL_NOTHROW | JIT_CALL_NORETURN);
+ jit_type_free(type);
- /* Pop the current exception context */
- func->builder->current_handler = eh->parent;
+#endif /* !JIT_BACKEND_INTERP */
- /* Start a new block, covered by the next outer "try" context */
+ /* The current block ends in dead and we need to start a new block */
+ func->builder->current_block->ends_in_dead = 1;
block = _jit_block_create(func, 0);
if(!block)
{
return 0;
}
+ func->builder->current_block = block;
return 1;
}
/*@
* @deftypefun int jit_insn_start_finally (jit_function_t func, {jit_label_t *} finally_label)
- * Start the @code{finally} clause for the preceding @code{try} block.
+ * Start a @code{finally} clause.
* @end deftypefun
@*/
int jit_insn_start_finally(jit_function_t func, jit_label_t *finally_label)
return 1;
}
+/*@
+ * @deftypefun int jit_insn_call_finally (jit_function_t func, {jit_label_t *} finally_label)
+ * Call a @code{finally} clause.
+ * @end deftypefun
+@*/
+int jit_insn_call_finally(jit_function_t func, jit_label_t *finally_label)
+{
+ if(!jit_insn_label(func, finally_label))
+ {
+ return 0;
+ }
+ return create_noarg_note(func, JIT_OP_CALL_FINALLY);
+}
+
/*@
* @deftypefun jit_value_t jit_insn_start_filter (jit_function_t func, {jit_label_t *} label, jit_type_t type)
* Define the start of a filter. Filters are embedded subroutines within
jit_function_t pool_owner;
};
-/*
- * Exception handling information that is attached to blocks.
- */
-typedef struct jit_block_eh *jit_block_eh_t;
-struct jit_block_eh
-{
- jit_block_eh_t parent;
- jit_block_eh_t next;
- jit_label_t catch_label;
- jit_label_t finally_label;
- int finally_on_fault : 1;
- int in_try_body : 1;
-};
-
/*
* Internal structure of a block.
*/
jit_block_t next;
jit_block_t prev;
jit_meta_t meta;
- jit_block_eh_t block_eh;
int entered_via_top : 1;
int entered_via_branch : 1;
int ends_in_dead : 1;
#define JIT_INSN_DEST_IS_NATIVE 0x0100
#define JIT_INSN_DEST_OTHER_FLAGS 0x01C0
#define JIT_INSN_VALUE1_IS_NAME 0x0200
-#define JIT_INSN_VALUE1_OTHER_FLAGS 0x0200
-#define JIT_INSN_VALUE2_IS_SIGNATURE 0x0400
-#define JIT_INSN_VALUE2_OTHER_FLAGS 0x0400
-#define JIT_INSN_DEST_IS_VALUE 0x0800
+#define JIT_INSN_VALUE1_IS_LABEL 0x0400
+#define JIT_INSN_VALUE1_OTHER_FLAGS 0x0600
+#define JIT_INSN_VALUE2_IS_SIGNATURE 0x0800
+#define JIT_INSN_VALUE2_OTHER_FLAGS 0x0800
+#define JIT_INSN_DEST_IS_VALUE 0x1000
/*
* Information that is associated with a function for building
jit_block_t init_block;
int init_insn;
- /* Exception handlers for the function */
- jit_block_eh_t exception_handlers;
- jit_block_eh_t current_handler;
+ /* Exception handling definitions for the function */
jit_value_t setjmp_value;
jit_label_t longjmp_label;
+ jit_value_t thrown_exception;
+ jit_value_t thrown_pc;
+ jit_label_t catcher_label;
+ jit_value_t eh_frame_info;
/* Flag that is set to indicate that this function is not a leaf */
int non_leaf : 1;
jit_value_t struct_return;
jit_value_t parent_frame;
- /* The value that holds the exception frame information for a callout */
- jit_value_t eh_frame_info;
-
/* Metadata that is stored only while the function is being built */
jit_meta_t meta;
struct jit_backtrace call_trace;
void *entry;
void *exception_object = 0;
+ void *exception_pc = 0;
void *handler;
jit_jmp_buf *jbuf;
/* Throw an exception, which may be handled in this function */
exception_object = VM_STK_PTR0;
handle_exception:
+ exception_pc = pc;
if(jit_function_from_pc(func->func->context, pc, &handler)
== func->func && handler != 0)
{
}
VMBREAK;
+ VMCASE(JIT_OP_RETHROW):
+ {
+ /* Rethrow an exception to the caller */
+ _jit_unwind_pop_setjmp();
+ jit_exception_throw(VM_STK_PTR0);
+ }
+ VMBREAK;
+
VMCASE(JIT_OP_LOAD_PC):
{
/* Load the current program counter onto the stack */
}
VMBREAK;
+ VMCASE(JIT_OP_LOAD_EXCEPTION_PC):
+ {
+ /* Load the address where the exception occurred onto the stack */
+ VM_STK_PTRP = (void *)exception_pc;
+ VM_MODIFY_PC_AND_STACK(1, -1);
+ }
+ VMBREAK;
+
VMCASE(JIT_OP_LEAVE_FINALLY):
{
/* Return from a "finally" handler */
}
VMBREAK;
+ VMCASE(JIT_OP_ADDRESS_OF_LABEL):
+ {
+ /* Load the address of a label onto the stack */
+ VM_STK_PTRP = VM_BR_TARGET;
+ VM_MODIFY_PC_AND_STACK(2, -1);
+ }
+ VMBREAK;
+
/******************************************************************
* Pointer-relative loads and stores.
******************************************************************/
VMCASE(JIT_OP_PUSH_FLOAT64):
VMCASE(JIT_OP_PUSH_NFLOAT):
VMCASE(JIT_OP_FLUSH_SMALL_STRUCT):
- VMCASE(JIT_OP_ENTER_CATCH):
VMCASE(JIT_OP_ENTER_FINALLY):
VMCASE(JIT_OP_ENTER_FILTER):
VMCASE(JIT_OP_CALL_FILTER_RETURN):
- VMCASE(JIT_OP_PREPARE_FOR_LEAVE):
- VMCASE(JIT_OP_PREPARE_FOR_RETURN):
- VMCASE(JIT_OP_JUMP_TO_CATCHER):
{
/* Shouldn't happen, but skip the instruction anyway */
VM_MODIFY_PC_AND_STACK(1, 0);
#define JIT_OP_PUSH_CONST_FLOAT64 (JIT_OP_NUM_OPCODES + 0x0036)
#define JIT_OP_PUSH_CONST_NFLOAT (JIT_OP_NUM_OPCODES + 0x0037)
-/*
- * Exception handling (interpreter-only).
- */
-#define JIT_OP_CALL_FINALLY (JIT_OP_NUM_OPCODES + 0x0038)
-
/*
* Marker opcode for the end of the interpreter-specific opcodes.
*/
* Exception handling.
*/
{"throw", F_(EMPTY, PTR, EMPTY)},
+ {"rethrow", F_(EMPTY, PTR, EMPTY)},
{"load_pc", F_(PTR, EMPTY, EMPTY)},
- {"enter_catch", F_(PTR, EMPTY, EMPTY)},
+ {"load_exception_pc", F_(PTR, EMPTY, EMPTY)},
{"enter_finally", F_(EMPTY, EMPTY, EMPTY)},
{"leave_finally", F_(EMPTY, EMPTY, EMPTY)},
+ {"call_finally", B_(EMPTY, EMPTY)},
{"enter_filter", F_(ANY, EMPTY, EMPTY)},
{"leave_filter", F_(EMPTY, ANY, EMPTY)},
{"call_filter", B_(ANY, EMPTY)},
{"call_filter_return", F_(ANY, EMPTY, EMPTY)},
- {"prepare_for_leave", F_(EMPTY, EMPTY, EMPTY)},
- {"prepare_for_return", F_(EMPTY, EMPTY, EMPTY)},
- {"jump_to_catcher", F_(EMPTY, PTR, EMPTY)},
+ {"address_of_label", JIT_OPCODE_IS_ADDROF_LABEL},
/*
* Data manipulation.
{"push_const_float64", JIT_OPCODE_CONST_FLOAT64},
{"push_const_nfloat", JIT_OPCODE_CONST_NFLOAT},
- /*
- * Exception handling (interpreter only).
- */
- {"call_finally", B_(EMPTY, EMPTY)},
-
/*
* Marker opcode for the end of a function.
*/
/* Nothing to do here for ARM */
}
-void _jit_gen_call_finally
- (jit_gencode_t gen, jit_function_t func, jit_label_t label)
-{
- /* TODO */
-}
-
-void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object)
-{
- /* TODO */
-}
-
#endif /* JIT_BACKEND_ARM */
switch(insn->opcode)
{
case JIT_OP_BR:
+ case JIT_OP_CALL_FINALLY:
{
/* Unconditional branch */
_jit_regs_spill_all(gen);
}
/* Not reached */
+ case JIT_OP_ADDRESS_OF_LABEL:
+ {
+ /* Get the address of a particular label */
+ if(_jit_regs_num_used(gen, 0) >= JIT_NUM_REGS)
+ {
+ _jit_regs_spill_all(gen);
+ }
+ reg = _jit_regs_new_top(gen, insn->dest, 0);
+ adjust_working(gen, 1);
+ label = (jit_label_t)(insn->value1);
+ pc = (void **)(gen->posn.ptr);
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ block = jit_block_from_label(func, label);
+ if(!block)
+ {
+ break;
+ }
+ if(block->address)
+ {
+ /* We already know the address of the block */
+ jit_cache_native
+ (&(gen->posn), ((void **)(block->address)) - pc);
+ }
+ else
+ {
+ /* Record this position on the block's fixup list */
+ jit_cache_native(&(gen->posn), block->fixup_list);
+ block->fixup_list = (void *)pc;
+ }
+ }
+ break;
+
case JIT_OP_CALL:
case JIT_OP_CALL_TAIL:
{
break;
case JIT_OP_LOAD_PC:
+ case JIT_OP_LOAD_EXCEPTION_PC:
{
/* Load the current program counter onto the stack */
if(_jit_regs_num_used(gen, 0) >= JIT_NUM_REGS)
}
break;
- case JIT_OP_ENTER_CATCH:
case JIT_OP_CALL_FILTER_RETURN:
{
/* The top of stack currently contains "dest" */
gen->working_area = 0;
}
-/*@
- * @deftypefun void _jit_gen_call_finally (jit_gencode_t gen, jit_function_t func, jit_label_t label)
- * Call a @code{finally} clause at @code{label}.
- * @end deftypefun
-@*/
-void _jit_gen_call_finally
- (jit_gencode_t gen, jit_function_t func, jit_label_t label)
-{
- jit_block_t block;
- void **pc;
- _jit_regs_spill_all(gen);
- pc = (void **)(gen->posn.ptr);
- jit_cache_opcode(&(gen->posn), JIT_OP_CALL_FINALLY);
- block = jit_block_from_label(func, label);
- if(!block)
- {
- return;
- }
- if(block->address)
- {
- /* We already know the address of the block */
- jit_cache_native
- (&(gen->posn), ((void **)(block->address)) - pc);
- }
- else
- {
- /* Record this position on the block's fixup list */
- jit_cache_native(&(gen->posn), block->fixup_list);
- block->fixup_list = (void *)pc;
- }
-}
-
-/*@
- * @deftypefun void _jit_gen_unwind_stack ({void *} stacktop, {void *} catch_pc, {void *} object)
- * Unwind the stack back to @code{stacktop}, restore the frame, and
- * jump to @code{catch_pc}. The registers are set up to arrange for
- * @code{object} to be in the right place for a @code{catch} clause.
- * @end deftypefun
-@*/
-void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object)
-{
- /* Not used by the interpreted back end */
-}
-
#endif /* JIT_BACKEND_INTERP */
/* Nothing to do here for x86 */
}
-void _jit_gen_call_finally
- (jit_gencode_t gen, jit_function_t func, jit_label_t label)
-{
- /* TODO */
-}
-
-void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object)
-{
- void *frame;
-
- /* Fetch the proper EBP value from the stack, just before
- where we need to unwind back to */
- frame = ((void **)stacktop)[-2];
-
- /* Unwind the stack and jump to "catch_pc" */
-#if defined(__GNUC__)
- __asm__ (
- "movl %0, %%edx\n\t"
- "movl %1, %%eax\n\t"
- "movl %2, %%ecx\n\t"
- "movl %3, %%ebx\n\t"
- "movl %%ebx, %%ebp\n\t"
- "movl %%edx, %%esp\n\t"
- "jmp *(%%eax)\n\t"
- : : "m"(stacktop), "m"(catch_pc), "m"(object), "m"(frame)
- : "eax", "ebx", "ecx", "edx"
- );
-#elif defined(_MSC_VER)
- __asm {
- mov edx, dword ptr stacktop
- mov eax, dword ptr catch_pc
- mov ecx, dword ptr object
- mov ebx, dword ptr frame
- mov ebp, ebx
- mov esp, edx
- jmp [eax]
- }
-#else
- #error "Don't know how to unwind the stack under x86"
-#endif
-}
-
-void _jit_unwind_stack(void *frame, void *pc, void *object)
-{
-#if defined(__GNUC__)
- __asm__ (
- "movl %0, %%edx\n\t"
- "movl %1, %%eax\n\t"
- "movl %2, %%ecx\n\t"
- "movl %%edx, %%ebp\n\t"
- "popl %%ebp\n\t"
- "popl %%edx\n\t"
- "jmp *%%eax\n\t"
- : : "m"(frame), "m"(pc), "m"(object)
- : "eax", "ecx", "edx"
- );
-#elif defined(_MSC_VER)
- __asm {
- mov edx, dword ptr frame
- mov eax, dword ptr pc
- mov ecx, dword ptr object
- mov ebp, edx
- pop ebp
- pop edx
- jmp eax
- }
-#else
- #error "Don't know how to unwind the stack on x86 platforms"
-#endif
-}
-
#endif /* JIT_BACKEND_X86 */
jit_block_t block, jit_insn_t insn);
void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block);
void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block);
-void _jit_gen_call_finally
- (jit_gencode_t gen, jit_function_t func, jit_label_t label);
-void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object);
/*
* Determine the byte number within a "jit_int" where the low
{
jmp_buf buf;
jit_backtrace_t trace;
- void *catcher;
+ void *catch_pc;
struct jit_jmp_buf *parent;
} jit_jmp_buf;
-#define jit_jmp_catcher_offset \
- ((jit_nint)&(((jit_jmp_buf *)0)->catcher))
+#define jit_jmp_catch_pc_offset \
+ ((jit_nint)&(((jit_jmp_buf *)0)->catch_pc))
/*
* Push a "setjmp" buffer onto the current thread's unwind stck.