Browse Source

rework how nested values are accessed

The current way on how importing values from ancestors works mainly aims at
hiding how it works from the outside. The main problem was that it required
a complex implementation in each backend which only one of the backends (x86)
implemented.
This commit changes accessing nested values, making it completely frontend
based. Furthermore it allows the library users to retrieve the frame pointer
of a parent and pass it in any way they like instead of forcing them to use
jit_insn_call. This means the new way allows implementing things such as lambdas
as function pointer (closure) together with a pointer to the parent frame and
then call this lambda using jit_insn_call_indirect.
pull/15/head
Jakob Löw 7 years ago
parent
commit
a40e0bc422
  1. 2
      include/jit/jit-function.h
  2. 3
      include/jit/jit-insn.h
  3. 11
      jit/jit-compile.c
  4. 15
      jit/jit-function.c
  5. 63
      jit/jit-insn.c
  6. 3
      jit/jit-internal.h

2
include/jit/jit-function.h

@ -51,6 +51,8 @@ jit_function_t jit_function_previous
jit_block_t jit_function_get_entry(jit_function_t func) JIT_NOTHROW;
jit_block_t jit_function_get_current(jit_function_t func) JIT_NOTHROW;
jit_function_t jit_function_get_nested_parent(jit_function_t func) JIT_NOTHROW;
void jit_function_set_parent_frame(jit_function_t func,
jit_value_t parent_frame) JIT_NOTHROW;
int jit_function_compile(jit_function_t func) JIT_NOTHROW;
int jit_function_is_compiled(jit_function_t func) JIT_NOTHROW;
void jit_function_set_recompilable(jit_function_t func) JIT_NOTHROW;

3
include/jit/jit-insn.h

@ -255,6 +255,9 @@ int jit_insn_return_reg
int jit_insn_setup_for_nested
(jit_function_t func, int nested_level, int reg) JIT_NOTHROW;
int jit_insn_flush_struct(jit_function_t func, jit_value_t value) JIT_NOTHROW;
jit_value_t jit_insn_get_frame_pointer(jit_function_t func) JIT_NOTHROW;
jit_value_t jit_insn_get_parent_frame_of
(jit_function_t func, jit_value_t frame_pointer) JIT_NOTHROW;
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;

11
jit/jit-compile.c

@ -222,6 +222,15 @@ compile_block(jit_gencode_t gen, jit_function_t func, jit_block_t block)
break;
#endif
case JIT_OP_IMPORT:
_jit_gen_fix_value(insn->value2);
insn->opcode = JIT_OP_ADD_RELATIVE;
insn->value2 = jit_value_create_nint_constant(func, jit_type_nint,
insn->value2->frame_offset);
_jit_gen_insn(gen, func, block, insn);
break;
#ifndef JIT_BACKEND_INTERP
case JIT_OP_INCOMING_REG:
/* Assign a register to an incoming value */
@ -463,7 +472,7 @@ memory_start(_jit_compile_t *state)
/* Store the bounds of the available space */
state->gen.mem_start = _jit_memory_get_break(state->gen.context);
state->gen.mem_limit = _jit_memory_get_limit(state->gen.context);
state->gen.mem_limit = _jit_memory_get_limit(state->gen.context);
/* Align the function code start as required */
state->gen.ptr = state->gen.mem_start;

15
jit/jit-function.c

@ -500,6 +500,19 @@ jit_function_t jit_function_get_nested_parent(jit_function_t func)
}
}
/*@
* @deftypefun jit_function_t jit_function_get_nested_parent (jit_function_t @var{func}, jit_value_t @var{parent_frame})
* Set the frame pointer of the parent of a nested function
* @end deftypefun
@*/
void jit_function_set_parent_frame(jit_function_t func,
jit_value_t parent_frame)
{
func->parent_frame = parent_frame;
func->cached_parent = NULL;
func->cached_parent_frame = NULL;
}
/*
* Information that is stored for an exception region in the cache.
*/
@ -645,7 +658,7 @@ void *jit_function_to_closure(jit_function_t func)
* closure does not correspond to a function in the specified context.
* @end deftypefun
@*/
jit_function_t
jit_function_t
jit_function_from_closure(jit_context_t context, void *closure)
{
void *func_info;

63
jit/jit-insn.c

@ -23,6 +23,7 @@
#include "jit-internal.h"
#include "jit-rules.h"
#include "jit-setjmp.h"
#include "jit-apply-rules.h"
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
@ -3786,7 +3787,7 @@ jit_insn_branch_if_not(jit_function_t func, jit_value_t value, jit_label_t *labe
int opcode;
jit_value_t value1;
jit_value_t value2;
jit_block_t block = func->builder->current_block;
jit_block_t block = func->builder->current_block;
jit_insn_t prev = _jit_block_get_last(block);
if(value->is_temporary && prev && prev->dest == value)
{
@ -6315,6 +6316,43 @@ jit_insn_flush_struct(jit_function_t func, jit_value_t value)
return create_unary_note(func, JIT_OP_FLUSH_SMALL_STRUCT, value);
}
/*@
* @deftypefun jit_value_t jit_insn_get_frame_pointer (jit_function_t @var{func})
* Retrieve the frame pointer of function @var{func}
* Returns NULL if out of memory.
* @end deftypefun
@*/
jit_value_t
jit_insn_get_frame_pointer(jit_function_t func)
{
jit_value_t dummy;
dummy = jit_value_create(func, jit_type_sbyte);
if(!dummy)
{
return 0;
}
jit_value_set_addressable(dummy);
jit_insn_incoming_frame_posn(func, dummy, 0);
return jit_insn_address_of(func, dummy);
}
/*@
* @deftypefun jit_value_t jit_insn_get_parent_frame_of (jit_function_t @var{func}, jit_value_t @var{frame_pointer})
* Retrieve the parent frame of @var{frame_pointer}. Using this function
* multiple times allows importing values from grandparents. The initial
* @var{frame_pointer} can be obtained from @code{jit_insn_get_frame_pointer}.
* Returns NULL if out of memory.
* @end deftypefun
@*/
jit_value_t
jit_insn_get_parent_frame_of(jit_function_t func, jit_value_t frame_pointer)
{
return jit_insn_load_relative(func, frame_pointer,
JIT_APPLY_PARENT_FRAME_OFFSET, jit_type_void_ptr);
}
/*@
* @deftypefun jit_value_t jit_insn_import (jit_function_t @var{func}, jit_value_t @var{value})
* Import @var{value} from an outer nested scope into @var{func}. Returns
@ -6339,12 +6377,21 @@ jit_insn_import(jit_function_t func, jit_value_t value)
return jit_insn_address_of(func, value);
}
/* Find the nesting level of the value, where 1 is our parent */
int level = 1;
/* Often there are multiple values imported from the same ancestor in a row,
thats why the last ancestor a value was imported from is cached so its
frame can be reused as finding it would require multiple memory loads */
if(value_func == func->cached_parent)
{
return apply_binary(func, JIT_OP_IMPORT, func->cached_parent_frame,
value, jit_type_void_ptr);
}
/* Find the frame of the ancestor the value is from */
jit_value_t current_frame = func->parent_frame;
jit_function_t current_func = func->nested_parent;
while(current_func != 0 && current_func != value_func)
{
++level;
current_frame = jit_insn_get_parent_frame_of(func, current_frame);
current_func = current_func->nested_parent;
}
if(!current_func)
@ -6355,12 +6402,8 @@ jit_insn_import(jit_function_t func, jit_value_t value)
/* Output the relevant import instruction, which will also cause
it to be marked as a non-local addressable by "jit_value_ref" */
jit_value_t level_value = jit_value_create_nint_constant(func, jit_type_int, level);
if(!level_value)
{
return 0;
}
return apply_binary(func, JIT_OP_IMPORT, value, level_value, jit_type_void_ptr);
return apply_binary(func, JIT_OP_IMPORT, current_frame, value,
jit_type_void_ptr);
}
/*@

3
jit/jit-internal.h

@ -463,6 +463,9 @@ struct _jit_function
/* Containing function in a nested context */
jit_function_t nested_parent;
jit_value_t parent_frame;
jit_function_t cached_parent;
jit_value_t cached_parent_frame;
/* Metadata that survives once the builder is discarded */
jit_meta_t meta;

Loading…
Cancel
Save