+2004-05-11 Rhys Weatherley <rweather@southern-storm.com.au>
+
+ * include/jit/jit-insn.h, jit/jit-insn.c, jit/jit-interp.cpp,
+ jit/jit-interp.h, jit/jit-opcode.c, jit/jit-rules-interp.c:
+ round out the function call handling opcodes for the interpreter.
+
2004-05-10 Rhys Weatherley <rweather@southern-storm.com.au>
* jit/jit-reg-alloc.c, jit/jit-reg-alloc.h, jit/jit-rules-interp.c:
jit_value_t jit_insn_import
(jit_function_t func, jit_value_t value) JIT_NOTHROW;
int jit_insn_push(jit_function_t func, jit_value_t value) JIT_NOTHROW;
+int jit_insn_push_ptr
+ (jit_function_t func, jit_value_t value, jit_type_t type) JIT_NOTHROW;
int jit_insn_pop_stack(jit_function_t func, jit_nint num_items) JIT_NOTHROW;
int jit_insn_return(jit_function_t func, jit_value_t value) JIT_NOTHROW;
int jit_insn_return_ptr
}
jit_value_ref(func, value);
insn->opcode = JIT_OP_CALL_INDIRECT;
+ insn->flags = JIT_INSN_VALUE2_IS_SIGNATURE;
insn->value1 = value;
+ insn->value2 = (jit_value_t)jit_type_copy(signature);
/* If the function does not return, then end the current block.
The next block does not have "entered_via_top" set so that
* @deftypefun jit_value_t jit_insn_import (jit_function_t func, jit_value_t value)
* Import @code{value} from an outer nested scope into @code{func}. Returns
* the effective address of the value for local access via a pointer.
- * If @code{value} is local to @code{func}, then it is returned as-is.
* Returns NULL if out of memory or the value is not accessible via a
* parent, grandparent, or other ancestor of @code{func}.
* @end deftypefun
return 0;
}
- /* If the value is already local, then there is nothing to do */
+ /* If the value is already local, then return the local address */
value_func = jit_value_get_function(value);
if(value_func == func)
{
- return value;
+ return jit_insn_address_of(func, value);
}
/* Find the nesting level of the value, where 1 is our parent */
case JIT_TYPE_STRUCT:
case JIT_TYPE_UNION:
{
- return create_unary_note(func, JIT_OP_PUSH_STRUCT, value);
+ /* We need the address of the value for "push_struct" */
+ value = jit_insn_address_of(func, value);
+ if(!value)
+ {
+ return 0;
+ }
+ return create_note
+ (func, JIT_OP_PUSH_STRUCT, value,
+ jit_value_create_nint_constant
+ (func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
}
/* Not reached */
}
return 1;
}
+/*@
+ * @deftypefun int jit_insn_push_ptr (jit_function_t func, jit_value_t value, jit_type_t type)
+ * Push @code{*value} onto the function call stack, in preparation for a call.
+ * This is normally used for returning @code{struct} and @code{union}
+ * values where you have the effective address of the structure, rather
+ * than the structure's contents, in @code{value}.
+ *
+ * You normally wouldn't call this yourself - it is used internally
+ * by the CPU back ends to set up the stack for a subroutine call.
+ * @end deftypefun
+@*/
+int jit_insn_push_ptr
+ (jit_function_t func, jit_value_t value, jit_type_t type)
+{
+ if(!value || !type)
+ {
+ return 0;
+ }
+ switch(jit_type_normalize(type)->kind)
+ {
+ case JIT_TYPE_STRUCT:
+ case JIT_TYPE_UNION:
+ {
+ /* Push the structure onto the stack by address */
+ return create_note
+ (func, JIT_OP_PUSH_STRUCT, value,
+ jit_value_create_nint_constant
+ (func, jit_type_nint, (jit_nint)jit_type_get_size(type)));
+ }
+ /* Not reached */
+
+ default:
+ {
+ /* Load the value from the address and push it normally */
+ return jit_insn_push
+ (func, jit_insn_load_relative(func, value, 0, type));
+ }
+ /* Not reached */
+ }
+}
+
/*@
* @deftypefun int jit_insn_pop_stack (jit_function_t func, jit_nint num_items)
* Pop @code{num_items} items from the function call stack. You normally
******************************************************************/
VMCASE(JIT_OP_CALL):
+ VMCASE(JIT_OP_CALL_TAIL):
{
/* Call a function that is under the control of the JIT */
call_func = (jit_function_t)VM_NINT_ARG;
{
/* Return from the current function, with a small structure */
#if JIT_APPLY_MAX_STRUCT_IN_REG != 0
- jit_memcpy(return_area->struct_value, VM_STK_PTR1,
- (unsigned int)VM_STK_NINT0);
+ jit_memcpy(return_area->struct_value, VM_STK_PTR0,
+ (unsigned int)VM_NINT_ARG);
#endif
return;
}
}
VMBREAK;
- VMCASE(JIT_OP_PUSH_PARENT_LOCALS):
+ VMCASE(JIT_OP_IMPORT_LOCAL):
{
- /* Push the pointer to the parent's local variables */
- VM_STK_PTRP = args[0].ptr_value;
- VM_MODIFY_PC_AND_STACK(1, -1);
+ /* Import the address of a local variable from an outer scope */
+ temparg = VM_NINT_ARG2;
+ tempptr = args[0].ptr_value;
+ tempptr2 = args[1].ptr_value;
+ while(temparg > 1)
+ {
+ tempptr = ((jit_item *)tempptr2)[0].ptr_value;
+ tempptr2 = ((jit_item *)tempptr2)[1].ptr_value;
+ --temparg;
+ }
+ VM_STK_PTRP = ((jit_item *)tempptr) + VM_NINT_ARG;
+ VM_MODIFY_PC_AND_STACK(3, -1);
}
VMBREAK;
- VMCASE(JIT_OP_PUSH_PARENT_ARGS):
+ VMCASE(JIT_OP_IMPORT_ARG):
{
- /* Push the pointer to the parent's argument variables */
- VM_STK_PTRP = args[1].ptr_value;
- VM_MODIFY_PC_AND_STACK(1, -1);
+ /* Import the address of an argument from an outer scope */
+ temparg = VM_NINT_ARG2;
+ tempptr = args[1].ptr_value;
+ while(temparg > 1)
+ {
+ tempptr = ((jit_item *)tempptr)[1].ptr_value;
+ --temparg;
+ }
+ VM_STK_PTRP = ((jit_item *)tempptr) + VM_NINT_ARG;
+ VM_MODIFY_PC_AND_STACK(3, -1);
+ }
+ VMBREAK;
+
+ VMCASE(JIT_OP_PUSH_STRUCT):
+ {
+ /* Push a structure value onto the stack, given a pointer to it */
+ tempptr = VM_STK_PTR0;
+ temparg = VM_NINT_ARG;
+ stacktop -= (JIT_NUM_ITEMS_IN_STRUCT(temparg) - 1);
+ jit_memcpy(stacktop, tempptr, (unsigned int)temparg);
+ VM_MODIFY_PC_AND_STACK(2, 0);
}
VMBREAK;
VMCASE(JIT_OP_PUSH_FLOAT32):
VMCASE(JIT_OP_PUSH_FLOAT64):
VMCASE(JIT_OP_PUSH_NFLOAT):
- VMCASE(JIT_OP_PUSH_STRUCT):
VMCASE(JIT_OP_FLUSH_SMALL_STRUCT):
VMCASE(JIT_OP_END_MARKER):
VMCASE(JIT_OP_ENTER_CATCH):
/*
* Nested function call handling.
*/
-#define JIT_OP_PUSH_PARENT_LOCALS (JIT_OP_NUM_OPCODES + 0x0031)
-#define JIT_OP_PUSH_PARENT_ARGS (JIT_OP_NUM_OPCODES + 0x0032)
+#define JIT_OP_IMPORT_LOCAL (JIT_OP_NUM_OPCODES + 0x0031)
+#define JIT_OP_IMPORT_ARG (JIT_OP_NUM_OPCODES + 0x0032)
/*
* Push constant values onto the stack.
/*
* Marker opcode for the end of the interpreter-specific opcodes.
*/
-#define JIT_OP_END_MARKER (JIT_OP_NUM_OPCODES + 0x0039)
+#define JIT_OP_END_MARKER (JIT_OP_NUM_OPCODES + 0x003B)
/*
* Number of interpreter-specific opcodes.
{"push_float32", F_(EMPTY, FLOAT32, EMPTY)},
{"push_float64", F_(EMPTY, FLOAT64, EMPTY)},
{"push_nfloat", F_(EMPTY, NFLOAT, EMPTY)},
- {"push_struct", F_(EMPTY, ANY, EMPTY)},
+ {"push_struct", F_(EMPTY, ANY, PTR) | NINT_ARG},
{"pop_stack", F_(EMPTY, INT, EMPTY) | NINT_ARG},
{"flush_small_struct", F_(EMPTY, ANY, EMPTY)},
/*
* Nested function call handling.
*/
- {"push_parent_locals", 0},
- {"push_parent_args", 0},
+ {"import_local", JIT_OPCODE_NINT_ARG_TWO},
+ {"import_arg", JIT_OPCODE_NINT_ARG_TWO},
/*
* Push constant values onto the stack.
int is_nested, int nested_level, jit_value_t *struct_return)
{
jit_type_t type;
+ jit_type_t vtype;
jit_value_t value;
/* Push all of the arguments in reverse order */
while(num_args > 0)
{
--num_args;
+ type = jit_type_normalize(jit_type_get_param(signature, num_args));
+ if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION)
+ {
+ /* If the value is a pointer, then we are pushing a structure
+ argument by pointer rather than by local variable */
+ vtype = jit_type_normalize(jit_value_get_type(args[num_args]));
+ if(vtype->kind <= JIT_TYPE_MAX_PRIMITIVE)
+ {
+ if(!jit_insn_push_ptr(func, args[num_args], type))
+ {
+ return 0;
+ }
+ }
+ }
if(!jit_insn_push(func, args[num_args]))
{
return 0;
/* Not reached */
case JIT_OP_CALL:
+ case JIT_OP_CALL_TAIL:
{
/* Call a function, whose pointer is supplied explicitly */
jit_cache_opcode(&(gen->posn), insn->opcode);
}
break;
- case JIT_OP_CALL_EXTERNAL:
+ case JIT_OP_CALL_INDIRECT:
{
- /* Call a native function, whose pointer is supplied explicitly */
+ /* Call a function, whose pointer is supplied on the stack */
if(!jit_type_return_via_pointer
(jit_type_get_return((jit_type_t)(insn->value2))))
{
jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_AREA_PTR);
}
+ reg = _jit_regs_load_to_top(gen, insn->value1, 0, 0);
jit_cache_opcode(&(gen->posn), insn->opcode);
jit_cache_native(&(gen->posn), (jit_nint)(insn->value2));
- jit_cache_native(&(gen->posn), (jit_nint)(insn->dest));
jit_cache_native(&(gen->posn), (jit_nint)
(jit_type_num_params((jit_type_t)(insn->value2))));
+ _jit_regs_free_reg(gen, reg, 1);
+ adjust_working(gen, -1);
}
break;
- case JIT_OP_CALL_INDIRECT:
case JIT_OP_CALL_VTABLE_PTR:
{
- /* Call a function, whose pointer is supplied on the stack */
- _jit_regs_load_to_top(gen, insn->value1, 0, 0);
+ /* Call a function, whose vtable pointer is supplied on the stack */
+ reg = _jit_regs_load_to_top(gen, insn->value1, 0, 0);
jit_cache_opcode(&(gen->posn), insn->opcode);
+ _jit_regs_free_reg(gen, reg, 1);
adjust_working(gen, -1);
}
break;
+ case JIT_OP_CALL_EXTERNAL:
+ {
+ /* Call a native function, whose pointer is supplied explicitly */
+ if(!jit_type_return_via_pointer
+ (jit_type_get_return((jit_type_t)(insn->value2))))
+ {
+ jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_AREA_PTR);
+ }
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ jit_cache_native(&(gen->posn), (jit_nint)(insn->value2));
+ jit_cache_native(&(gen->posn), (jit_nint)(insn->dest));
+ jit_cache_native(&(gen->posn), (jit_nint)
+ (jit_type_num_params((jit_type_t)(insn->value2))));
+ }
+ break;
+
case JIT_OP_RETURN:
{
/* Return from the current function with no result */
}
break;
+ case JIT_OP_RETURN_SMALL_STRUCT:
+ {
+ /* Return from current function with a small structure result */
+ if(!_jit_regs_is_top(gen, insn->value1) ||
+ _jit_regs_num_used(gen, 0) != 1)
+ {
+ _jit_regs_spill_all(gen);
+ }
+ reg = _jit_regs_load_to_top(gen, insn->value1, 0, 0);
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ jit_cache_native(&(gen->posn),
+ jit_value_get_nint_constant(insn->value2));
+ _jit_regs_free_reg(gen, reg, 1);
+ }
+ break;
+
+ case JIT_OP_SETUP_FOR_NESTED:
+ {
+ /* Set up to call a nested child */
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ adjust_working(gen, 2);
+ }
+ break;
+
+ case JIT_OP_SETUP_FOR_SIBLING:
+ {
+ /* Set up to call a nested sibling */
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ jit_cache_native(&(gen->posn),
+ jit_value_get_nint_constant(insn->value1));
+ adjust_working(gen, 2);
+ }
+ break;
+
+ case JIT_OP_IMPORT:
+ {
+ /* Import a local variable from an outer nested scope */
+ if(_jit_regs_num_used(gen, 0) >= JIT_NUM_REGS)
+ {
+ _jit_regs_spill_all(gen);
+ }
+ _jit_gen_fix_value(insn->value1);
+ if(insn->value1->frame_offset >= 0)
+ {
+ jit_cache_opcode(&(gen->posn), JIT_OP_IMPORT_LOCAL);
+ jit_cache_native(&(gen->posn), insn->value1->frame_offset);
+ jit_cache_native(&(gen->posn),
+ jit_value_get_nint_constant(insn->value2));
+ }
+ else
+ {
+ jit_cache_opcode(&(gen->posn), JIT_OP_IMPORT_ARG);
+ jit_cache_native
+ (&(gen->posn), -(insn->value1->frame_offset + 1));
+ jit_cache_native(&(gen->posn),
+ jit_value_get_nint_constant(insn->value2));
+ }
+ reg = _jit_regs_new_top(gen, insn->dest, 0);
+ adjust_working(gen, 1);
+ }
+ break;
+
case JIT_OP_RETURN_REG:
{
/* Push a function return value back onto the stack */
case JIT_OP_PUSH_STRUCT:
{
- /* TODO */
+ /* Load the pointer value to the top of the stack */
+ if(!_jit_regs_is_top(gen, insn->value1) ||
+ _jit_regs_num_used(gen, 0) != 1)
+ {
+ _jit_regs_spill_all(gen);
+ }
+ reg = _jit_regs_load_to_top
+ (gen, insn->value1,
+ (insn->flags & (JIT_INSN_VALUE1_NEXT_USE |
+ JIT_INSN_VALUE1_LIVE)), 0);
+ _jit_regs_free_reg(gen, reg, 1);
+
+ /* Push the structure at the designated pointer */
+ size = jit_value_get_nint_constant(insn->value2);
+ jit_cache_opcode(&(gen->posn), insn->opcode);
+ jit_cache_native(&(gen->posn), size);
+ adjust_working(gen, JIT_NUM_ITEMS_IN_STRUCT(size) - 1);
}
break;
case JIT_OP_POP_STACK:
{
/* Pop parameter values from the stack after a function returns */
- jit_nint size = jit_value_get_nint_constant(insn->value1);
+ size = jit_value_get_nint_constant(insn->value1);
if(size == 1)
{
jit_cache_opcode(&(gen->posn), JIT_OP_POP);