#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
/*
* Data manipulation.
*/
-#define JIT_OP_COPY_LOAD_SBYTE 0x0157
-#define JIT_OP_COPY_LOAD_UBYTE 0x0158
-#define JIT_OP_COPY_LOAD_SHORT 0x0159
-#define JIT_OP_COPY_LOAD_USHORT 0x015A
-#define JIT_OP_COPY_INT 0x015B
-#define JIT_OP_COPY_LONG 0x015C
-#define JIT_OP_COPY_FLOAT32 0x015D
-#define JIT_OP_COPY_FLOAT64 0x015E
-#define JIT_OP_COPY_NFLOAT 0x015F
-#define JIT_OP_COPY_STRUCT 0x0160
-#define JIT_OP_COPY_STORE_BYTE 0x0161
-#define JIT_OP_COPY_STORE_SHORT 0x0162
-#define JIT_OP_ADDRESS_OF 0x0163
+#define JIT_OP_COPY_LOAD_SBYTE 0x0158
+#define JIT_OP_COPY_LOAD_UBYTE 0x0159
+#define JIT_OP_COPY_LOAD_SHORT 0x015A
+#define JIT_OP_COPY_LOAD_USHORT 0x015B
+#define JIT_OP_COPY_INT 0x015C
+#define JIT_OP_COPY_LONG 0x015D
+#define JIT_OP_COPY_FLOAT32 0x015E
+#define JIT_OP_COPY_FLOAT64 0x015F
+#define JIT_OP_COPY_NFLOAT 0x0160
+#define JIT_OP_COPY_STRUCT 0x0161
+#define JIT_OP_COPY_STORE_BYTE 0x0162
+#define JIT_OP_COPY_STORE_SHORT 0x0163
+#define JIT_OP_ADDRESS_OF 0x0164
/*
* Incoming registers, outgoing registers, and stack pushes.
*/
-#define JIT_OP_INCOMING_REG 0x0164
-#define JIT_OP_INCOMING_FRAME_POSN 0x0165
-#define JIT_OP_OUTGOING_REG 0x0166
-#define JIT_OP_RETURN_REG 0x0167
-#define JIT_OP_PUSH_INT 0x0168
-#define JIT_OP_PUSH_LONG 0x0169
-#define JIT_OP_PUSH_FLOAT32 0x016A
-#define JIT_OP_PUSH_FLOAT64 0x016B
-#define JIT_OP_PUSH_NFLOAT 0x016C
-#define JIT_OP_PUSH_STRUCT 0x016D
-#define JIT_OP_POP_STACK 0x016E
-#define JIT_OP_FLUSH_SMALL_STRUCT 0x016F
+#define JIT_OP_INCOMING_REG 0x0165
+#define JIT_OP_INCOMING_FRAME_POSN 0x0166
+#define JIT_OP_OUTGOING_REG 0x0167
+#define JIT_OP_RETURN_REG 0x0168
+#define JIT_OP_PUSH_INT 0x0169
+#define JIT_OP_PUSH_LONG 0x016A
+#define JIT_OP_PUSH_FLOAT32 0x016B
+#define JIT_OP_PUSH_FLOAT64 0x016C
+#define JIT_OP_PUSH_NFLOAT 0x016D
+#define JIT_OP_PUSH_STRUCT 0x016E
+#define JIT_OP_POP_STACK 0x016F
+#define JIT_OP_FLUSH_SMALL_STRUCT 0x0170
/*
* Pointer-relative loads and stores.
*/
-#define JIT_OP_LOAD_RELATIVE_SBYTE 0x0170
-#define JIT_OP_LOAD_RELATIVE_UBYTE 0x0171
-#define JIT_OP_LOAD_RELATIVE_SHORT 0x0172
-#define JIT_OP_LOAD_RELATIVE_USHORT 0x0173
-#define JIT_OP_LOAD_RELATIVE_INT 0x0174
-#define JIT_OP_LOAD_RELATIVE_LONG 0x0175
-#define JIT_OP_LOAD_RELATIVE_FLOAT32 0x0176
-#define JIT_OP_LOAD_RELATIVE_FLOAT64 0x0177
-#define JIT_OP_LOAD_RELATIVE_NFLOAT 0x0178
-#define JIT_OP_LOAD_RELATIVE_STRUCT 0x0179
-#define JIT_OP_STORE_RELATIVE_BYTE 0x017A
-#define JIT_OP_STORE_RELATIVE_SHORT 0x017B
-#define JIT_OP_STORE_RELATIVE_INT 0x017C
-#define JIT_OP_STORE_RELATIVE_LONG 0x017D
-#define JIT_OP_STORE_RELATIVE_FLOAT32 0x017E
-#define JIT_OP_STORE_RELATIVE_FLOAT64 0x017F
-#define JIT_OP_STORE_RELATIVE_NFLOAT 0x0180
-#define JIT_OP_STORE_RELATIVE_STRUCT 0x0181
-#define JIT_OP_ADD_RELATIVE 0x0182
+#define JIT_OP_LOAD_RELATIVE_SBYTE 0x0171
+#define JIT_OP_LOAD_RELATIVE_UBYTE 0x0172
+#define JIT_OP_LOAD_RELATIVE_SHORT 0x0173
+#define JIT_OP_LOAD_RELATIVE_USHORT 0x0174
+#define JIT_OP_LOAD_RELATIVE_INT 0x0175
+#define JIT_OP_LOAD_RELATIVE_LONG 0x0176
+#define JIT_OP_LOAD_RELATIVE_FLOAT32 0x0177
+#define JIT_OP_LOAD_RELATIVE_FLOAT64 0x0178
+#define JIT_OP_LOAD_RELATIVE_NFLOAT 0x0179
+#define JIT_OP_LOAD_RELATIVE_STRUCT 0x017A
+#define JIT_OP_STORE_RELATIVE_BYTE 0x017B
+#define JIT_OP_STORE_RELATIVE_SHORT 0x017C
+#define JIT_OP_STORE_RELATIVE_INT 0x017D
+#define JIT_OP_STORE_RELATIVE_LONG 0x017E
+#define JIT_OP_STORE_RELATIVE_FLOAT32 0x017F
+#define JIT_OP_STORE_RELATIVE_FLOAT64 0x0180
+#define JIT_OP_STORE_RELATIVE_NFLOAT 0x0181
+#define JIT_OP_STORE_RELATIVE_STRUCT 0x0182
+#define JIT_OP_ADD_RELATIVE 0x0183
/*
* Array element loads and stores.
*/
-#define JIT_OP_LOAD_ELEMENT_SBYTE 0x0183
-#define JIT_OP_LOAD_ELEMENT_UBYTE 0x0184
-#define JIT_OP_LOAD_ELEMENT_SHORT 0x0185
-#define JIT_OP_LOAD_ELEMENT_USHORT 0x0186
-#define JIT_OP_LOAD_ELEMENT_INT 0x0187
-#define JIT_OP_LOAD_ELEMENT_UINT 0x0188
-#define JIT_OP_LOAD_ELEMENT_LONG 0x0189
-#define JIT_OP_LOAD_ELEMENT_ULONG 0x018A
-#define JIT_OP_LOAD_ELEMENT_FLOAT32 0x018B
-#define JIT_OP_LOAD_ELEMENT_FLOAT64 0x018C
-#define JIT_OP_LOAD_ELEMENT_NFLOAT 0x018D
-#define JIT_OP_STORE_ELEMENT_BYTE 0x018E
-#define JIT_OP_STORE_ELEMENT_SHORT 0x018F
-#define JIT_OP_STORE_ELEMENT_INT 0x0190
-#define JIT_OP_STORE_ELEMENT_LONG 0x0191
-#define JIT_OP_STORE_ELEMENT_FLOAT32 0x0192
-#define JIT_OP_STORE_ELEMENT_FLOAT64 0x0193
-#define JIT_OP_STORE_ELEMENT_NFLOAT 0x0194
+#define JIT_OP_LOAD_ELEMENT_SBYTE 0x0184
+#define JIT_OP_LOAD_ELEMENT_UBYTE 0x0185
+#define JIT_OP_LOAD_ELEMENT_SHORT 0x0186
+#define JIT_OP_LOAD_ELEMENT_USHORT 0x0187
+#define JIT_OP_LOAD_ELEMENT_INT 0x0188
+#define JIT_OP_LOAD_ELEMENT_UINT 0x0189
+#define JIT_OP_LOAD_ELEMENT_LONG 0x018A
+#define JIT_OP_LOAD_ELEMENT_ULONG 0x018B
+#define JIT_OP_LOAD_ELEMENT_FLOAT32 0x018C
+#define JIT_OP_LOAD_ELEMENT_FLOAT64 0x018D
+#define JIT_OP_LOAD_ELEMENT_NFLOAT 0x018E
+#define JIT_OP_STORE_ELEMENT_BYTE 0x018F
+#define JIT_OP_STORE_ELEMENT_SHORT 0x0190
+#define JIT_OP_STORE_ELEMENT_INT 0x0191
+#define JIT_OP_STORE_ELEMENT_LONG 0x0192
+#define JIT_OP_STORE_ELEMENT_FLOAT32 0x0193
+#define JIT_OP_STORE_ELEMENT_FLOAT64 0x0194
+#define JIT_OP_STORE_ELEMENT_NFLOAT 0x0195
/*
* Block operations.
*/
-#define JIT_OP_MEMCPY 0x0195
-#define JIT_OP_MEMMOVE 0x0196
-#define JIT_OP_MEMSET 0x0197
+#define JIT_OP_MEMCPY 0x0196
+#define JIT_OP_MEMMOVE 0x0197
+#define JIT_OP_MEMSET 0x0198
/*
* Allocate memory from the stack.
*/
-#define JIT_OP_ALLOCA 0x0198
+#define JIT_OP_ALLOCA 0x0199
/*
* The number of opcodes in the above list.
*/
-#define JIT_OP_NUM_OPCODES 0x0199
+#define JIT_OP_NUM_OPCODES 0x019A
/*
* Opcode information.
#include "jit-internal.h"
#include "jit-rules.h"
+#include "jit-setjmp.h"
#include <config.h>
#if HAVE_ALLOCA_H
#include <alloca.h>
jit_type_t params[2];
jit_value_t struct_return;
- /* If the "nothrow" or "tail" flags are set, then we don't
- need to worry about this */
+ /* If "tail" is set, then we need to pop the "setjmp" context */
+ if((flags & JIT_CALL_TAIL) != 0 && func->has_try)
+ {
+ 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);
+ }
+
+ /* If "nothrow" or "tail" is set, then there is no more to do */
if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) != 0)
{
return 1;
/* This function may throw an exception */
func->builder->may_throw = 1;
+#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
/* Get the value that holds the exception frame information */
if((eh_frame_info = func->builder->eh_frame_info) == 0)
{
return 0;
}
- /* Set up to call the "_jit_backtrace_push" intrinsic */
- if(!_jit_create_call_setup_insns
- (func, type, args, 2, 0, 0, &struct_return))
- {
- jit_type_free(type);
- return 0;
- }
-
- /* Terminate the current block and then call "_jit_backtrace_push" */
- block = _jit_block_create(func, 0);
- if(!block)
- {
- jit_type_free(type);
- return 0;
- }
- block->entered_via_top = 1;
- func->builder->current_block = block;
- insn = _jit_block_add_insn(block);
- if(!insn)
- {
- jit_type_free(type);
- return 0;
- }
- insn->opcode = JIT_OP_CALL_EXTERNAL;
- insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME;
- insn->dest = (jit_value_t)(void *)_jit_backtrace_push;
- insn->value1 = (jit_value_t)"_jit_backtrace_push";
+ /* Call the "_jit_backtrace_push" function */
+ jit_insn_call_native
+ (func, "_jit_backtrace_push",
+ (void *)_jit_backtrace_push, type, args, 2, JIT_CALL_NOTHROW);
+ jit_type_free(type);
+#endif
- /* Clean up after the function call */
- if(!_jit_create_call_return_insns(func, type, args, 2, 0, 0))
+ /* 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)
{
- jit_type_free(type);
- return 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;
+ }
+ insn = _jit_block_add_insn(func->builder->current_block);
+ if(!insn)
+ {
+ return 0;
+ }
+ jit_value_ref(func, args[1]);
+ insn->opcode = JIT_OP_LOAD_CATCHER_PC;
+ insn->flags |= JIT_INSN_VALUE1_IS_LABEL;
+ 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]))
+ {
+ return 0;
+ }
}
/* We are now ready to make the actual function call */
- jit_type_free(type);
return 1;
#else /* JIT_BACKEND_INTERP */
/* The interpreter handles exception frames for us */
return 1;
}
+#if JIT_APPLY_BROKEN_FRAME_BUILTINS != 0
/* Create the signature prototype "void (void)" */
type = jit_type_create_signature
(jit_abi_cdecl, jit_type_void, 0, 0, 0);
return 0;
}
- /* Set up to call the "_jit_backtrace_pop" intrinsic */
- if(!_jit_create_call_setup_insns
- (func, type, 0, 0, 0, 0, &struct_return))
- {
- jit_type_free(type);
- return 0;
- }
-
- /* Terminate the current block and then call "_jit_backtrace_pop" */
- block = _jit_block_create(func, 0);
- if(!block)
- {
- jit_type_free(type);
- return 0;
- }
- block->entered_via_top = 1;
- func->builder->current_block = block;
- insn = _jit_block_add_insn(block);
- if(!insn)
- {
- jit_type_free(type);
- return 0;
- }
- insn->opcode = JIT_OP_CALL_EXTERNAL;
- insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME;
- insn->dest = (jit_value_t)(void *)_jit_backtrace_pop;
- insn->value1 = (jit_value_t)"_jit_backtrace_pop";
+ /* Call the "_jit_backtrace_pop" function */
+ jit_insn_call_native
+ (func, "_jit_backtrace_pop",
+ (void *)_jit_backtrace_pop, type, 0, 0, JIT_CALL_NOTHROW);
+ jit_type_free(type);
+#endif
- /* Clean up after the function call */
- if(!_jit_create_call_return_insns(func, type, 0, 0, 0, 0))
+ /* 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)
{
- jit_type_free(type);
- return 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]))
+ {
+ return 0;
+ }
+ if(!jit_insn_store_relative
+ (func, jit_insn_address_of(func->builder->setjmp_value),
+ jit_jmp_catcher_offset, args[0]))
+ {
+ return 0;
+ }
}
/* Everything is back to where it should be */
- jit_type_free(type);
return 1;
#else /* JIT_BACKEND_INTERP */
/* The interpreter handles exception frames for us */
it will be eliminated during later code generation */
if((flags & JIT_CALL_NORETURN) != 0)
{
- block = _jit_block_create(func, 0);
- if(!block)
+ func->builder->current_block->ends_in_dead = 1;
+ if(!jit_insn_label(func, 0))
{
return 0;
}
- func->builder->current_block = block;
}
/* Create space for the return value, if we don't already have one */
it will be eliminated during later code generation */
if((flags & JIT_CALL_NORETURN) != 0)
{
- block = _jit_block_create(func, 0);
- if(!block)
+ func->builder->current_block->ends_in_dead = 1;
+ if(!jit_insn_label(func, 0))
{
return 0;
}
- func->builder->current_block = block;
}
/* Create space for the return value, if we don't already have one */
it will be eliminated during later code generation */
if((flags & JIT_CALL_NORETURN) != 0)
{
- block = _jit_block_create(func, 0);
- if(!block)
+ func->builder->current_block->ends_in_dead = 1;
+ if(!jit_insn_label(func, 0))
{
return 0;
}
- func->builder->current_block = block;
}
/* Create space for the return value, if we don't already have one */
it will be eliminated during later code generation */
if((flags & JIT_CALL_NORETURN) != 0)
{
- block = _jit_block_create(func, 0);
- if(!block)
+ func->builder->current_block->ends_in_dead = 1;
+ if(!jit_insn_label(func, 0))
{
return 0;
}
- func->builder->current_block = block;
}
/* Create space for the return value, if we don't already have one */
return value;
}
+/*
+ * Initialize the "setjmp" setup block that is needed to catch exceptions
+ * thrown back to this level of execution. The block looks like this:
+ *
+ * jit_jmp_buf jbuf;
+ * void *catcher;
+ *
+ * _jit_unwind_push_setjmp(&jbuf);
+ * if(setjmp(&jbuf.buf))
+ * {
+ * catcher = jbuf.catcher;
+ * if(catcher)
+ * {
+ * jbuf.catcher = 0;
+ * goto *catcher;
+ * }
+ * else
+ * {
+ * _jit_unwind_pop_and_rethrow();
+ * }
+ * }
+ *
+ * The field "jbuf.catcher" 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.
+ *
+ * Native back ends are responsible for outputting a call to the function
+ * "_jit_unwind_pop_setjmp()" just before "return" instructions if the
+ * "has_try" flag is set on the function.
+ */
+static int initialize_setjmp_block(jit_function_t func)
+{
+#if !defined(JIT_BACKEND_INTERP)
+ jit_label_t start_label = jit_label_undefined;
+ jit_label_t end_label = jit_label_undefined;
+ jit_label_t rethrow_label = jit_label_undefined;
+ jit_type_t type;
+ jit_value_t args[1];
+ jit_value_t value;
+
+ /* Bail out if we have already done this before */
+ if(func->builder->setjmp_value)
+ {
+ return 1;
+ }
+ func->builder->longjmp_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;
+ }
+
+ /* Create a value to hold an item of type "jit_jmp_buf" */
+ type = jit_type_create_struct(0, 0, 1);
+ if(!type)
+ {
+ return 0;
+ }
+ jit_type_set_size_and_alignment
+ (type, sizeof(jit_jmp_buf), JIT_BEST_ALIGNMENT);
+ if((func->builder->setjmp_value = jit_value_create(func, type)) == 0)
+ {
+ jit_type_free(type);
+ return 0;
+ }
+ jit_type_free(type);
+
+ /* Call "_jit_unwind_push_setjmp" with "&setjmp_value" as its argument */
+ type = jit_type_void_ptr;
+ type = jit_type_create_signature
+ (jit_abi_cdecl, jit_type_void, &type, 1, 1);
+ if(!type)
+ {
+ return 0;
+ }
+ args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
+ jit_insn_call_native
+ (func, "_jit_unwind_push_setjmp",
+ (void *)_jit_unwind_push_setjmp, type, args, 1, JIT_CALL_NOTHROW);
+ jit_type_free(type);
+
+ /* Call "setjmp" with "&setjmp_value" as its argument */
+ type = jit_type_void_ptr;
+ type = jit_type_create_signature
+ (jit_abi_cdecl, jit_type_int, &type, 1, 1);
+ if(!type)
+ {
+ return 0;
+ }
+ args[0] = jit_insn_address_of(func, func->builder->setjmp_value);
+ value = jit_insn_call_native
+ (func, "setjmp", (void *)setjmp, type, args, 1, JIT_CALL_NOTHROW);
+ jit_type_free(type);
+ if(!value)
+ {
+ return 0;
+ }
+
+ /* Branch to the end of the init code if "setjmp" returned zero */
+ if(!jit_insn_branch_if_not(func, value, &end_label))
+ {
+ return 0;
+ }
+
+ /* The current point in the code is where "longjmp" will resume from */
+ if(!jit_insn_label(func, &(func->builder->longjmp_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 */
+ value = jit_insn_load_relative
+ (func, jit_insn_address_of(func, func->builder->setjmp_value),
+ jit_jmp_catcher_offset, jit_type_void_ptr);
+ if(!value)
+ {
+ return 0;
+ }
+ if(!jit_insn_branch_if_not(func, value, &rethrow_label))
+ {
+ return 0;
+ }
+
+ /* Clear the original "catcher" 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
+ (func, jit_type_void_ptr, 0)))
+ {
+ return 0;
+ }
+
+ /* Jump to the address indicated by the catcher */
+ if(!create_unary_note(func, JIT_OP_JUMP_TO_CATCHER, value))
+ {
+ 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 0;
+ }
+
+ /* Call "_jit_unwind_pop_and_rethrow" to pop the current
+ "setjmp" context and then rethrow the current exception */
+ 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_and_rethrow",
+ (void *)_jit_unwind_pop_and_rethrow, type, 0, 0,
+ JIT_CALL_NOTHROW | JIT_CALL_NORETURN);
+ jit_type_free(type);
+
+ /* Force the start of a new block to mark the end of the init code */
+ if(!jit_insn_label(func, &end_label))
+ {
+ return 0;
+ }
+
+ /* Move the initialization code to the head of the function so that
+ it is performed once upon entry to the function */
+ return jit_insn_move_blocks_to_start(func, start_label, end_label);
+#else
+ /* The interpreter doesn't need the "setjmp" setup block */
+ 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
register allocation */
func->has_try = 1;
+ /* Make sure that the "setjmp" setup block is present in this function */
+ if(!initialize_setjmp_block(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)