9218 lines
267 KiB
C
9218 lines
267 KiB
C
/*
|
|
MzScheme
|
|
Copyright (c) 2006-2009 PLT Scheme Inc.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/*
|
|
JIT limtations:
|
|
|
|
1) See "About short-jump mode" below.
|
|
|
|
2) Use jit_patchable_movi_p() when a constant needs to be
|
|
visible to the GC.
|
|
|
|
3) Immediate operands must be 32-bit values on x86_64, except with
|
|
jit_movi, jit_sti, jit_ld, jit_bXi, jit_calli, and jit_finishi.
|
|
|
|
4) Function calls are limited to 3 arguments (i.e., jit_prepare()
|
|
must never be called with a number greater than 3). This limit
|
|
is related to the way the x86_64 port shuffles arguments into
|
|
temporary registers.
|
|
|
|
5) On x86_64, arguments are delivered in JIT_V2, JIT_V3, and JIT_R2,
|
|
in that order. So don't set JIT_R2 before getting the third
|
|
argument, etc.
|
|
*/
|
|
|
|
#include "schpriv.h"
|
|
#include "schmach.h"
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
# include "unwind/libunwind.h"
|
|
#endif
|
|
|
|
#ifdef MZ_USE_JIT
|
|
|
|
#ifdef __APPLE__
|
|
# define _CALL_DARWIN
|
|
#endif
|
|
|
|
/* Separate JIT_PRECISE_GC lets us test some 3m support in non-3m mode: */
|
|
#ifdef MZ_PRECISE_GC
|
|
# define JIT_PRECISE_GC
|
|
#endif
|
|
|
|
/* IMPORTANT! 3m arithmetic checking disabled for the whole file! */
|
|
#ifdef MZ_PRECISE_GC
|
|
END_XFORM_ARITH;
|
|
#endif
|
|
|
|
#define JIT_USE_FP_OPS
|
|
|
|
#ifdef MZ_USE_JIT_X86_64
|
|
# define MZ_USE_JIT_I386
|
|
# define JIT_X86_64
|
|
#endif
|
|
|
|
#include "lightning/lightning.h"
|
|
|
|
#ifdef MZ_USE_JIT_X86_64
|
|
# define JIT_LOG_WORD_SIZE 3
|
|
#else
|
|
# define JIT_LOG_WORD_SIZE 2
|
|
#endif
|
|
#define JIT_WORD_SIZE (1 << JIT_LOG_WORD_SIZE)
|
|
#define WORDS_TO_BYTES(x) ((x) << JIT_LOG_WORD_SIZE)
|
|
#define MAX_TRY_SHIFT 30
|
|
|
|
/* a mzchar is an int: */
|
|
#define LOG_MZCHAR_SIZE 2
|
|
|
|
#if defined(MZ_USE_JIT_PPC) || defined(MZ_USE_JIT_X86_64)
|
|
# define NEED_LONG_JUMPS
|
|
#endif
|
|
#if defined(MZ_USE_JIT_I386) && !defined(MZ_USE_JIT_X86_64)
|
|
# define USE_TINY_JUMPS
|
|
#endif
|
|
|
|
#define JIT_NOT_RET JIT_R1
|
|
#if JIT_NOT_RET == JIT_RET
|
|
Fix me! See use.
|
|
#endif
|
|
|
|
#if 0
|
|
static void assert_failure(int where) { printf("JIT assert failed %d\n", where); }
|
|
#define JIT_ASSERT(v) if (!(v)) assert_failure(__LINE__);
|
|
#else
|
|
#define JIT_ASSERT(v) /* */
|
|
#endif
|
|
|
|
/* Used by vector-set-performance-stats!: */
|
|
int scheme_jit_malloced;
|
|
|
|
static int skip_checks = 0;
|
|
|
|
#define MAX_SHARED_CALL_RANDS 25
|
|
static void *shared_tail_code[4][MAX_SHARED_CALL_RANDS];
|
|
static void *shared_non_tail_code[4][MAX_SHARED_CALL_RANDS][2];
|
|
static void *shared_non_tail_retry_code[2];
|
|
static void *shared_non_tail_argc_code[2];
|
|
static void *shared_tail_argc_code;
|
|
|
|
#define MAX_SHARED_ARITY_CHECK 25
|
|
static void *shared_arity_check[MAX_SHARED_ARITY_CHECK][2][2];
|
|
|
|
static void *bad_result_arity_code;
|
|
static void *unbound_global_code;
|
|
static void *quote_syntax_code;
|
|
static void *call_original_unary_arith_code;
|
|
static void *call_original_binary_arith_code;
|
|
static void *call_original_binary_rev_arith_code;
|
|
static void *call_original_unary_arith_for_branch_code;
|
|
static void *call_original_binary_arith_for_branch_code;
|
|
static void *call_original_binary_rev_arith_for_branch_code;
|
|
static void *bad_car_code, *bad_cdr_code;
|
|
static void *bad_caar_code, *bad_cdar_code, *bad_cadr_code, *bad_cddr_code;
|
|
static void *bad_mcar_code, *bad_mcdr_code;
|
|
static void *bad_set_mcar_code, *bad_set_mcdr_code;
|
|
static void *bad_unbox_code;
|
|
static void *bad_vector_length_code;
|
|
static void *vector_ref_code, *vector_ref_check_index_code, *vector_set_code, *vector_set_check_index_code;
|
|
static void *string_ref_code, *string_ref_check_index_code, *string_set_code, *string_set_check_index_code;
|
|
static void *bytes_ref_code, *bytes_ref_check_index_code, *bytes_set_code, *bytes_set_check_index_code;
|
|
static void *syntax_e_code;
|
|
static void *on_demand_jit_code;
|
|
static void *on_demand_jit_arity_code;
|
|
static void *get_stack_pointer_code;
|
|
static void *stack_cache_pop_code;
|
|
static void *struct_pred_code, *struct_pred_multi_code;
|
|
static void *struct_pred_branch_code;
|
|
static void *struct_get_code, *struct_get_multi_code;
|
|
static void *struct_set_code, *struct_set_multi_code;
|
|
static void *struct_proc_extract_code;
|
|
static void *bad_app_vals_target;
|
|
static void *app_values_slow_code, *app_values_multi_slow_code, *app_values_tail_slow_code;
|
|
static void *finish_tail_call_code, *finish_tail_call_fixup_code;
|
|
|
|
typedef struct {
|
|
MZTAG_IF_REQUIRED
|
|
GC_CAN_IGNORE jit_state js;
|
|
char *limit;
|
|
int extra_pushed, max_extra_pushed;
|
|
int depth; /* the position of the closure's first value on the stack */
|
|
int max_depth;
|
|
int *mappings; /* For each element,
|
|
case 0x1 bit:
|
|
. 0 -> case 0x2 bit:
|
|
. 0 -> case rest bits:
|
|
. 0 -> save point
|
|
. 1 -> shift >>2 to get orig pushed count
|
|
. 1 -> shift >>4 to get arity for single orig pushed
|
|
. shift >>2 to get flags
|
|
. 1 -> shift >>1 to get new (native) pushed */
|
|
int num_mappings, mappings_size;
|
|
int retained;
|
|
int need_set_rs;
|
|
void **retain_start;
|
|
int local1_busy;
|
|
int log_depth;
|
|
int self_pos, self_closure_size, self_toplevel_pos;
|
|
int self_to_closure_delta, closure_to_args_delta;
|
|
int closure_self_on_runstack;
|
|
int example_argc;
|
|
Scheme_Object **example_argv;
|
|
void *self_restart_code;
|
|
void *self_nontail_code;
|
|
Scheme_Native_Closure *nc; /* for extract_globals and extract_closure_local, only */
|
|
Scheme_Closure_Data *self_data;
|
|
void *status_at_ptr;
|
|
int reg_status;
|
|
void *patch_depth;
|
|
int rs_virtual_offset;
|
|
} mz_jit_state;
|
|
|
|
#define mz_RECORD_STATUS(s) (jitter->status_at_ptr = _jit.x.pc, jitter->reg_status = (s))
|
|
#define mz_CURRENT_STATUS() ((jitter->status_at_ptr == _jit.x.pc) ? jitter->reg_status : 0)
|
|
|
|
#define mz_RS_R0_HAS_RUNSTACK0 0x1
|
|
|
|
typedef int (*Native_Check_Arity_Proc)(Scheme_Object *o, int argc, int dummy);
|
|
typedef Scheme_Object *(*Native_Get_Arity_Proc)(Scheme_Object *o, int dumm1, int dummy2);
|
|
static Native_Check_Arity_Proc check_arity_code;
|
|
static Native_Get_Arity_Proc get_arity_code;
|
|
|
|
static int generate_non_tail(Scheme_Object *obj, mz_jit_state *jitter, int multi_ok, int need_ends);
|
|
static int generate(Scheme_Object *obj, mz_jit_state *jitter, int tail_ok, int multi_ok, int target);
|
|
static void *generate_lambda_simple_arity_check(int num_params, int has_rest, int is_method, int permanent);
|
|
static void generate_case_lambda(Scheme_Case_Lambda *c, Scheme_Native_Closure_Data *ndata,
|
|
int is_method);
|
|
static void on_demand();
|
|
static int generate_non_tail_mark_pos_prefix(mz_jit_state *jitter);
|
|
static void generate_non_tail_mark_pos_suffix(mz_jit_state *jitter);
|
|
static void *generate_shared_call(int num_rands, mz_jit_state *old_jitter, int multi_ok, int is_tail,
|
|
int direct_prim, int direct_native, int nontail_self);
|
|
|
|
static int generate_two_args(Scheme_Object *rand1, Scheme_Object *rand2, mz_jit_state *jitter,
|
|
int order_matters, int skipped);
|
|
|
|
static int is_simple(Scheme_Object *obj, int depth, int just_markless, mz_jit_state *jitter, int stack_start);
|
|
static int lambda_has_been_jitted(Scheme_Native_Closure_Data *ndata);
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
static void register_traversers(void);
|
|
static void release_native_code(void *fnlized, void *p);
|
|
#endif
|
|
|
|
int scheme_direct_call_count, scheme_indirect_call_count;
|
|
|
|
#ifdef MZ_USE_SINGLE_FLOATS
|
|
# define SCHEME_FLOAT_TYPE scheme_float_type
|
|
#else
|
|
# define SCHEME_FLOAT_TYPE scheme_double_type
|
|
#endif
|
|
|
|
#define NATIVE_PRESERVES_MARKS 0x1
|
|
#define NATIVE_IS_SINGLE_RESULT 0x2
|
|
|
|
/* Tracking statistics: */
|
|
#if 0
|
|
# define NUM_CATEGORIES 23
|
|
int jit_sizes[NUM_CATEGORIES];
|
|
int jit_counts[NUM_CATEGORIES];
|
|
int jit_code_size;
|
|
# define START_JIT_DATA() void *__pos = jit_get_ip().ptr; unsigned long __total = 0
|
|
# define END_JIT_DATA(where) if (jitter->retain_start) { \
|
|
jit_sizes[where] += __total + ((unsigned long)jit_get_ip().ptr - (unsigned long)__pos); \
|
|
jit_counts[where]++; }
|
|
# define PAUSE_JIT_DATA() __total += ((unsigned long)jit_get_ip().ptr - (unsigned long)__pos)
|
|
# define RESUME_JIT_DATA() __pos = jit_get_ip().ptr
|
|
# define RECORD_CODE_SIZE(s) jit_code_size += s
|
|
#else
|
|
# define START_JIT_DATA() /* empty */
|
|
# define END_JIT_DATA(where) /* empty */
|
|
# define PAUSE_JIT_DATA() /* empty */
|
|
# define RESUME_JIT_DATA() /* empty */
|
|
# define RECORD_CODE_SIZE(s) /* empty */
|
|
#endif
|
|
|
|
typedef struct {
|
|
Scheme_Native_Closure_Data nc;
|
|
Scheme_Native_Closure_Data *case_lam;
|
|
} Scheme_Native_Closure_Data_Plus_Case;
|
|
|
|
/* This structure must be 4 words: */
|
|
typedef struct {
|
|
void *orig_return_address;
|
|
void *stack_frame;
|
|
Scheme_Object *cache;
|
|
void *filler;
|
|
} Stack_Cache_Elem;
|
|
|
|
#define STACK_CACHE_SIZE 32
|
|
static Stack_Cache_Elem stack_cache_stack[STACK_CACHE_SIZE];
|
|
long stack_cache_stack_pos = 0;
|
|
|
|
#define IS_NAMED_PRIM(p, nm) (!strcmp(((Scheme_Primitive_Proc *)p)->name, nm))
|
|
|
|
#include "codetab.inc"
|
|
|
|
static Scheme_Object **fixup_runstack_base;
|
|
static int fixup_already_in_place;
|
|
|
|
static Scheme_Object *_scheme_tail_apply_from_native_fixup_args(Scheme_Object *rator,
|
|
int argc,
|
|
Scheme_Object **argv)
|
|
{
|
|
int already = fixup_already_in_place, i;
|
|
Scheme_Object **base;
|
|
|
|
base = fixup_runstack_base XFORM_OK_MINUS argc XFORM_OK_MINUS already;
|
|
|
|
/* Need to shift argc to end of base: */
|
|
for (i = 0; i < argc; i++) {
|
|
base[already + i] = argv[i];
|
|
}
|
|
|
|
return _scheme_tail_apply_from_native(rator, argc + already, base);
|
|
}
|
|
static Scheme_Object *make_global_ref(Scheme_Object *var)
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *o;
|
|
|
|
o = scheme_alloc_small_object();
|
|
o->type = scheme_global_ref_type;
|
|
SCHEME_PTR_VAL(o) = var;
|
|
|
|
return o;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* JIT buffer */
|
|
/*========================================================================*/
|
|
|
|
#ifdef SIXTY_FOUR_BIT_INTEGERS
|
|
# define JIT_BUFFER_PAD_SIZE 200
|
|
#else
|
|
# define JIT_BUFFER_PAD_SIZE 100
|
|
#endif
|
|
|
|
#define _jit (jitter->js)
|
|
#define PAST_LIMIT() ((unsigned long)jit_get_ip().ptr > (unsigned long)jitter->limit)
|
|
#define CHECK_LIMIT() if (PAST_LIMIT()) return past_limit(jitter);
|
|
#if 1
|
|
# define past_limit(j) 0
|
|
#else
|
|
static int past_limit(mz_jit_state *jitter)
|
|
{
|
|
if (((unsigned long)jit_get_ip().ptr > (unsigned long)jitter->limit + JIT_BUFFER_PAD_SIZE)
|
|
|| (jitter->retain_start)) {
|
|
printf("way past\n");
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#define JIT_CACHE_SIZE_LIMIT 65536
|
|
#define JIT_BUFFER_INIT_SIZE 256
|
|
|
|
#define JIT_INIT_MAPPINGS_SIZE 32
|
|
|
|
static void *jit_buffer_cache;
|
|
static long jit_buffer_cache_size;
|
|
static int jit_buffer_cache_registered;
|
|
|
|
typedef int (*Generate_Proc)(mz_jit_state *j, void *data);
|
|
|
|
static void *get_end_pointer(mz_jit_state *jitter)
|
|
{
|
|
return jit_get_ip().ptr;
|
|
}
|
|
|
|
static int mz_retain_it(mz_jit_state *jitter, void *v)
|
|
{
|
|
if (jitter->retain_start) {
|
|
jitter->retain_start[jitter->retained] = v;
|
|
}
|
|
jitter->retained++;
|
|
return jitter->retained;
|
|
}
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
static void mz_load_retained(mz_jit_state *jitter, int rs, int retptr)
|
|
{
|
|
void *p;
|
|
p = jitter->retain_start + retptr - 1;
|
|
(void)jit_patchable_movi_p(rs, p);
|
|
jit_ldr_p(rs, rs);
|
|
}
|
|
#endif
|
|
|
|
static void *generate_one(mz_jit_state *old_jitter,
|
|
Generate_Proc generate,
|
|
void *data,
|
|
int gcable,
|
|
void *save_ptr,
|
|
Scheme_Native_Closure_Data *ndata)
|
|
{
|
|
mz_jit_state _jitter;
|
|
mz_jit_state *jitter = &_jitter;
|
|
void *buffer;
|
|
int mappings_buffer[JIT_INIT_MAPPINGS_SIZE];
|
|
int *mappings = mappings_buffer;
|
|
long size = JIT_BUFFER_INIT_SIZE, known_size = 0, size_pre_retained = 0, num_retained = 0, padding;
|
|
int mappings_size = JIT_INIT_MAPPINGS_SIZE;
|
|
int ok, max_extra_pushed = 0;
|
|
#ifdef MZ_PRECISE_GC
|
|
Scheme_Object *fnl_obj;
|
|
|
|
if (ndata) {
|
|
/* When fnl_obj becomes inaccessible, code generated
|
|
here can be freed. */
|
|
fnl_obj = scheme_box(scheme_false);
|
|
} else
|
|
fnl_obj = NULL;
|
|
#endif
|
|
|
|
if (!jit_buffer_cache_registered) {
|
|
jit_buffer_cache_registered = 1;
|
|
REGISTER_SO(jit_buffer_cache);
|
|
REGISTER_SO(stack_cache_stack);
|
|
#ifdef MZ_PRECISE_GC
|
|
register_traversers();
|
|
#endif
|
|
}
|
|
|
|
while (1) {
|
|
memset(jitter, 0, sizeof(_jitter));
|
|
#ifdef NEED_LONG_JUMPS
|
|
_jitl.long_jumps = 1;
|
|
#endif
|
|
#ifdef USE_TINY_JUMPS
|
|
_jitl.tiny_jumps = 0;
|
|
#endif
|
|
padding = JIT_BUFFER_PAD_SIZE;
|
|
if (known_size) {
|
|
size_pre_retained = known_size;
|
|
size = size_pre_retained + WORDS_TO_BYTES(num_retained);
|
|
padding = 0;
|
|
if (gcable) {
|
|
#ifdef MZ_PRECISE_GC
|
|
buffer = scheme_malloc_code(size);
|
|
scheme_jit_malloced += size_pre_retained;
|
|
#else
|
|
buffer = scheme_malloc_gcable_code(size);
|
|
#endif
|
|
} else {
|
|
buffer = scheme_malloc_code(size);
|
|
}
|
|
RECORD_CODE_SIZE(size);
|
|
} else if (old_jitter) {
|
|
/* this is a recursive generate, so use leftover space in
|
|
old_jitter's buffer */
|
|
buffer = get_end_pointer(old_jitter);
|
|
size = ((char *)old_jitter->limit - (char *)buffer);
|
|
if (size < JIT_BUFFER_INIT_SIZE) {
|
|
old_jitter = NULL;
|
|
buffer = NULL;
|
|
size = JIT_BUFFER_INIT_SIZE;
|
|
} else {
|
|
size_pre_retained = size;
|
|
}
|
|
} else
|
|
buffer = NULL;
|
|
|
|
if (!buffer) {
|
|
if (jit_buffer_cache && (jit_buffer_cache_size >= size)) {
|
|
buffer = jit_buffer_cache;
|
|
size = jit_buffer_cache_size;
|
|
jit_buffer_cache = NULL;
|
|
} else {
|
|
#ifdef MZ_PRECISE_GC
|
|
long minsz;
|
|
minsz = GC_malloc_stays_put_threshold();
|
|
if (size < minsz)
|
|
size = minsz;
|
|
buffer = (char *)scheme_malloc_atomic(size);
|
|
#else
|
|
buffer = scheme_malloc(size);
|
|
#endif
|
|
}
|
|
size_pre_retained = size;
|
|
}
|
|
|
|
(void)jit_set_ip(buffer).ptr;
|
|
jitter->limit = (char *)buffer + size_pre_retained - padding;
|
|
if (known_size) {
|
|
jitter->retain_start = (void *)jitter->limit;
|
|
#ifdef MZ_PRECISE_GC
|
|
if (ndata) {
|
|
memset(jitter->retain_start, 0, num_retained * sizeof(void*));
|
|
ndata->retained = (num_retained ? jitter->retain_start : NULL);
|
|
SCHEME_BOX_VAL(fnl_obj) = scheme_make_integer(size_pre_retained);
|
|
GC_set_finalizer(fnl_obj, 1, 3,
|
|
release_native_code, buffer,
|
|
NULL, NULL);
|
|
}
|
|
#endif
|
|
} else
|
|
jitter->retain_start = NULL;
|
|
|
|
jitter->mappings = mappings;
|
|
jitter->num_mappings = 0;
|
|
jitter->mappings_size = mappings_size;
|
|
mappings[0] = 0;
|
|
jitter->max_extra_pushed = max_extra_pushed;
|
|
jitter->self_pos = 1; /* beyond end of stack */
|
|
jitter->self_toplevel_pos = -1;
|
|
jitter->status_at_ptr = NULL;
|
|
|
|
/* Leave room for retained size on first pass,
|
|
install it if needed) on second pass:*/
|
|
if (!known_size || num_retained)
|
|
mz_retain_it(jitter, (void *)scheme_make_integer(num_retained));
|
|
|
|
ok = generate(jitter, data);
|
|
|
|
if (save_ptr) {
|
|
mz_retain_it(jitter, save_ptr);
|
|
}
|
|
#ifdef MZ_PRECISE_GC
|
|
if (fnl_obj) {
|
|
mz_retain_it(jitter, fnl_obj);
|
|
}
|
|
#endif
|
|
|
|
jitter->limit = (char *)jitter->limit + padding;
|
|
if (PAST_LIMIT() || (jitter->retain_start
|
|
&& (jitter->retained > num_retained))) {
|
|
scheme_console_printf("JIT buffer overflow: %p [%p,%p] (%d)!!\n",
|
|
jit_get_ip().ptr,
|
|
buffer, jitter->limit,
|
|
!!jitter->retain_start);
|
|
abort();
|
|
}
|
|
|
|
mappings_size = jitter->mappings_size;
|
|
mappings = jitter->mappings;
|
|
max_extra_pushed = jitter->max_extra_pushed;
|
|
|
|
if (ok) {
|
|
/* That was big enough: */
|
|
if (known_size) {
|
|
/* That was in the permanent area, so return: */
|
|
jit_flush_code(buffer, jit_get_ip().ptr);
|
|
return buffer;
|
|
} else {
|
|
/* Allocate permanent area and jit again: */
|
|
known_size = ((unsigned long)jit_get_ip().ptr) - (unsigned long)buffer;
|
|
if (known_size & (JIT_WORD_SIZE - 1)) {
|
|
known_size += (JIT_WORD_SIZE - (known_size & (JIT_WORD_SIZE - 1)));
|
|
}
|
|
num_retained = jitter->retained;
|
|
if (num_retained == 1) num_retained = 0;
|
|
/* Keep this buffer? Don't if it's too big, or if it's
|
|
a part of old_jitter, or if there's already a bigger
|
|
cache. */
|
|
if ((jit_buffer_cache_size < JIT_CACHE_SIZE_LIMIT)
|
|
&& !old_jitter
|
|
&& (!jit_buffer_cache
|
|
|| (jit_buffer_cache_size > size))) {
|
|
jit_buffer_cache = buffer;
|
|
jit_buffer_cache_size = size;
|
|
}
|
|
}
|
|
/* looping to try again... */
|
|
} else {
|
|
/* Need more room to try again: */
|
|
size = size * 2;
|
|
old_jitter = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
# define FOR_LOG(x) x
|
|
# define LOG_IT(args) if (jitter->retain_start) { emit_indentation(jitter); printf args; }
|
|
static void emit_indentation(mz_jit_state *jitter)
|
|
{
|
|
int i = jitter->log_depth;
|
|
while (i--) {
|
|
printf(" ");
|
|
}
|
|
}
|
|
#else
|
|
# define FOR_LOG(x) /* empty */
|
|
# define LOG_IT(args) /* empty */
|
|
#endif
|
|
|
|
/*========================================================================*/
|
|
/* run time */
|
|
/*========================================================================*/
|
|
|
|
static MZ_INLINE Scheme_Object *do_make_native_closure(Scheme_Native_Closure_Data *code, int size)
|
|
{
|
|
Scheme_Native_Closure *o;
|
|
|
|
o = (Scheme_Native_Closure *)scheme_malloc_tagged(sizeof(Scheme_Native_Closure)
|
|
+ ((size - 1) * sizeof(Scheme_Object *)));
|
|
o->so.type = scheme_native_closure_type;
|
|
o->code = code;
|
|
|
|
return (Scheme_Object *)o;
|
|
}
|
|
|
|
Scheme_Object *scheme_make_native_closure(Scheme_Native_Closure_Data *code)
|
|
{
|
|
return do_make_native_closure(code, code->closure_size);
|
|
}
|
|
|
|
Scheme_Object *scheme_make_native_case_closure(Scheme_Native_Closure_Data *code)
|
|
{
|
|
return do_make_native_closure(code, -(code->closure_size + 1));
|
|
}
|
|
|
|
static void call_set_global_bucket(Scheme_Bucket *b, Scheme_Object *val, int set_undef)
|
|
{
|
|
scheme_set_global_bucket("set!", b, val, set_undef);
|
|
}
|
|
|
|
static void lexical_binding_wrong_return_arity(int expected, int got, Scheme_Object **argv)
|
|
{
|
|
scheme_wrong_return_arity(NULL, expected, got, argv, "lexical binding");
|
|
}
|
|
|
|
static void call_wrong_return_arity(int expected, int got, Scheme_Object **argv)
|
|
|
|
{
|
|
scheme_wrong_return_arity(NULL, expected, got, argv, NULL);
|
|
}
|
|
|
|
static void wrong_argument_count(Scheme_Object *proc, int argc, Scheme_Object **argv)
|
|
{
|
|
scheme_wrong_count((char *)proc, -1, -1, argc, argv);
|
|
}
|
|
|
|
static void raise_bad_call_with_values(Scheme_Object *f)
|
|
{
|
|
Scheme_Object *a[1];
|
|
a[0] = f;
|
|
scheme_wrong_type("call-with-values", "procedure", -1, 1, a);
|
|
}
|
|
|
|
static Scheme_Object *call_with_values_from_multiple_result(Scheme_Object *f)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
if (SAME_OBJ(p->ku.multiple.array, p->values_buffer))
|
|
p->values_buffer = NULL;
|
|
return _scheme_apply(f, p->ku.multiple.count, p->ku.multiple.array);
|
|
}
|
|
|
|
static Scheme_Object *call_with_values_from_multiple_result_multi(Scheme_Object *f)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
if (SAME_OBJ(p->ku.multiple.array, p->values_buffer))
|
|
p->values_buffer = NULL;
|
|
return _scheme_apply_multi(f, p->ku.multiple.count, p->ku.multiple.array);
|
|
}
|
|
|
|
static Scheme_Object *tail_call_with_values_from_multiple_result(Scheme_Object *f)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
int num_rands = p->ku.multiple.count;
|
|
|
|
if (num_rands > p->tail_buffer_size) {
|
|
/* scheme_tail_apply will allocate */
|
|
if (SAME_OBJ(p->ku.multiple.array, p->values_buffer))
|
|
p->values_buffer = NULL;
|
|
}
|
|
return scheme_tail_apply(f, num_rands, p->ku.multiple.array);
|
|
}
|
|
|
|
static Scheme_Object *clear_runstack(long amt, Scheme_Object *sv)
|
|
{
|
|
int i;
|
|
for (i = 0; i < amt; i++) {
|
|
MZ_RUNSTACK[i] = NULL;
|
|
}
|
|
return sv;
|
|
}
|
|
|
|
static Scheme_Object *apply_checked_fail(Scheme_Object **args)
|
|
{
|
|
Scheme_Object *a[3];
|
|
|
|
a[0] = args[1];
|
|
a[1] = args[3];
|
|
a[2] = args[4];
|
|
|
|
return _scheme_apply(args[2], 3, a);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* code-gen utils */
|
|
/*========================================================================*/
|
|
|
|
#define JIT_RUNSTACK JIT_V0
|
|
#define JIT_RUNSTACK_BASE JIT_V2
|
|
|
|
#ifdef MZ_USE_JIT_PPC
|
|
# define JIT_STACK 1
|
|
# define JIT_STACK_FRAME 1
|
|
#else
|
|
# define JIT_STACK JIT_SP
|
|
# define JIT_STACK_FRAME JIT_FP
|
|
#endif
|
|
|
|
#define JIT_UPDATE_THREAD_RSPTR() jit_sti_p(&MZ_RUNSTACK, JIT_RUNSTACK)
|
|
#define JIT_UPDATE_THREAD_RSPTR_IF_NEEDED() \
|
|
if (jitter->need_set_rs) { \
|
|
JIT_UPDATE_THREAD_RSPTR(); \
|
|
jitter->need_set_rs = 0; \
|
|
}
|
|
#define JIT_UPDATE_THREAD_RSPTR_FOR_BRANCH_IF_NEEDED() \
|
|
if (jitter->need_set_rs) { \
|
|
JIT_UPDATE_THREAD_RSPTR(); \
|
|
}
|
|
|
|
#if 0
|
|
/* Debugging: checking for runstack overflow. A CHECK_RUNSTACK_OVERFLOW() should
|
|
be included after each decrement of JIT_RUNTACK. Failure is "reported" by
|
|
going into an immediate loop. */
|
|
static void *top;
|
|
static void *cr_tmp;
|
|
# define CHECK_RUNSTACK_OVERFLOW_NOCL() \
|
|
jit_sti_l(&cr_tmp, JIT_R0); jit_ldi_l(JIT_R0, &scheme_current_runstack_start); \
|
|
top = (_jit.x.pc); (void)jit_bltr_ul(top, JIT_RUNSTACK, JIT_R0); jit_ldi_l(JIT_R0, &cr_tmp)
|
|
# define CHECK_RUNSTACK_OVERFLOW() \
|
|
CHECK_LIMIT(); CHECK_RUNSTACK_OVERFLOW_NOCL()
|
|
#else
|
|
# define CHECK_RUNSTACK_OVERFLOW() /* empty */
|
|
# define CHECK_RUNSTACK_OVERFLOW_NOCL() /* empty */
|
|
#endif
|
|
|
|
#if 0
|
|
/* Debugging: ... */
|
|
static void *top4;
|
|
# define VALIDATE_RESULT(reg) top4 = (_jit.x.pc); (void)jit_beqi_ul(top4, reg, 0)
|
|
#else
|
|
# define VALIDATE_RESULT(reg) /* empty */
|
|
#endif
|
|
|
|
/* The mz_rs_... family of operations operate on a virtual
|
|
JIT_RUNSTACK register to perform a kind of peephole optimization.
|
|
The virtual register can be de-sync'd from the actual register, so
|
|
that multiple adjustments to the register can be collapsed; this
|
|
mostly improves code size, rather than speed. Functions that cause
|
|
the register to be de-sync'd are marked as such. Functions that can
|
|
accomodate a de-sync'd register on entry are marked as such. All
|
|
other fuctions can assume a sync'd regsiter and ensure a sync'd
|
|
register. Note that branches and calls normally require a sync'd
|
|
register. */
|
|
|
|
#if 1
|
|
# define mz_rs_dec(n) (jitter->rs_virtual_offset -= (n))
|
|
# define mz_rs_inc(n) (jitter->rs_virtual_offset += (n))
|
|
# define mz_rs_ldxi(reg, n) jit_ldxi_p(reg, JIT_RUNSTACK, WORDS_TO_BYTES(((n) + jitter->rs_virtual_offset)))
|
|
# define mz_rs_ldr(reg) mz_rs_ldxi(reg, 0)
|
|
# define mz_rs_stxi(n, reg) jit_stxi_p(WORDS_TO_BYTES(((n) + jitter->rs_virtual_offset)), JIT_RUNSTACK, reg)
|
|
# define mz_rs_str(reg) mz_rs_stxi(0, reg)
|
|
# define mz_rs_sync() (jitter->rs_virtual_offset \
|
|
? (jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(jitter->rs_virtual_offset)), \
|
|
jitter->rs_virtual_offset = 0) \
|
|
: 0)
|
|
# define mz_rs_sync_0() (jitter->rs_virtual_offset = 0)
|
|
#else
|
|
# define mz_rs_dec(n) jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(n))
|
|
# define mz_rs_inc(n) jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(n))
|
|
# define mz_rs_ldr(reg) jit_ldr_p(reg, JIT_RUNSTACK)
|
|
# define mz_rs_ldxi(reg, n) jit_ldxi_p(reg, JIT_RUNSTACK, WORDS_TO_BYTES(n))
|
|
# define mz_rs_str(reg) jit_str_p(JIT_RUNSTACK, reg)
|
|
# define mz_rs_stxi(n, reg) jit_stxi_p(WORDS_TO_BYTES(n), JIT_RUNSTACK, reg)
|
|
# define mz_rs_sync() /* empty */
|
|
# define mz_rs_sync_0() /* empty */
|
|
#endif
|
|
|
|
/* No need to sync if a branch just goes to an exception. */
|
|
# define mz_rs_sync_fail_branch() /* empty */
|
|
|
|
static void new_mapping(mz_jit_state *jitter)
|
|
{
|
|
jitter->num_mappings++;
|
|
if (jitter->num_mappings >= jitter->mappings_size) {
|
|
int *a;
|
|
a = (int *)scheme_malloc_atomic(jitter->mappings_size * 2 * sizeof(int));
|
|
memcpy(a, jitter->mappings, jitter->mappings_size * sizeof(int));
|
|
jitter->mappings = a;
|
|
jitter->mappings_size *= 2;
|
|
}
|
|
jitter->mappings[jitter->num_mappings] = 0;
|
|
}
|
|
|
|
static void mz_pushr_p_it(mz_jit_state *jitter, int reg)
|
|
/* de-sync's rs */
|
|
{
|
|
int v;
|
|
|
|
jitter->extra_pushed++;
|
|
if (jitter->extra_pushed > jitter->max_extra_pushed)
|
|
jitter->max_extra_pushed = jitter->extra_pushed;
|
|
|
|
if (!(jitter->mappings[jitter->num_mappings] & 0x1)
|
|
|| (jitter->mappings[jitter->num_mappings] < 0)) {
|
|
new_mapping(jitter);
|
|
}
|
|
v = (jitter->mappings[jitter->num_mappings]) >> 1;
|
|
v++;
|
|
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
|
|
|
|
mz_rs_dec(1);
|
|
CHECK_RUNSTACK_OVERFLOW_NOCL();
|
|
mz_rs_str(reg);
|
|
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
|
|
static void mz_popr_p_it(mz_jit_state *jitter, int reg)
|
|
/* de-sync's rs */
|
|
{
|
|
int v;
|
|
|
|
jitter->extra_pushed--;
|
|
|
|
JIT_ASSERT(jitter->mappings[jitter->num_mappings] & 0x1);
|
|
v = jitter->mappings[jitter->num_mappings] >> 1;
|
|
v--;
|
|
if (!v)
|
|
--jitter->num_mappings;
|
|
else
|
|
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
|
|
|
|
mz_rs_ldr(reg);
|
|
mz_rs_inc(1);
|
|
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
|
|
static void mz_runstack_skipped(mz_jit_state *jitter, int n)
|
|
{
|
|
int v;
|
|
|
|
if (!(jitter->mappings[jitter->num_mappings] & 0x1)
|
|
|| (jitter->mappings[jitter->num_mappings] > 0)) {
|
|
new_mapping(jitter);
|
|
}
|
|
v = (jitter->mappings[jitter->num_mappings]) >> 1;
|
|
JIT_ASSERT(v <= 0);
|
|
v -= n;
|
|
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
|
|
jitter->self_pos += n;
|
|
}
|
|
|
|
static void mz_runstack_unskipped(mz_jit_state *jitter, int n)
|
|
{
|
|
int v;
|
|
|
|
JIT_ASSERT(jitter->mappings[jitter->num_mappings] & 0x1);
|
|
v = (jitter->mappings[jitter->num_mappings]) >> 1;
|
|
JIT_ASSERT(v + n <= 0);
|
|
v += n;
|
|
if (!v)
|
|
--jitter->num_mappings;
|
|
else
|
|
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
|
|
jitter->self_pos -= n;
|
|
}
|
|
|
|
static void mz_runstack_pushed(mz_jit_state *jitter, int n)
|
|
{
|
|
jitter->depth += n;
|
|
if (jitter->depth > jitter->max_depth)
|
|
jitter->max_depth = jitter->depth;
|
|
jitter->self_pos += n;
|
|
if (!jitter->mappings[jitter->num_mappings]
|
|
|| (jitter->mappings[jitter->num_mappings] & 0x3)) {
|
|
new_mapping(jitter);
|
|
}
|
|
jitter->mappings[jitter->num_mappings] += (n << 2);
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
|
|
static void mz_runstack_closure_pushed(mz_jit_state *jitter, int a, int flags)
|
|
{
|
|
jitter->depth += 1;
|
|
if (jitter->depth > jitter->max_depth)
|
|
jitter->max_depth = jitter->depth;
|
|
jitter->self_pos += 1;
|
|
new_mapping(jitter);
|
|
jitter->mappings[jitter->num_mappings] = (a << 4) | (flags << 2) | 0x2;
|
|
jitter->need_set_rs = 1;
|
|
/* closures are never popped; they go away due to returns or tail calls */
|
|
}
|
|
|
|
static void mz_runstack_popped(mz_jit_state *jitter, int n)
|
|
{
|
|
int v;
|
|
jitter->depth -= n;
|
|
jitter->self_pos -= n;
|
|
|
|
v = jitter->mappings[jitter->num_mappings];
|
|
JIT_ASSERT(!(v & 0x1));
|
|
/* non-procedure slot */
|
|
v = v >> 2;
|
|
JIT_ASSERT(v >= n);
|
|
v -= n;
|
|
if (!v)
|
|
--jitter->num_mappings;
|
|
else
|
|
jitter->mappings[jitter->num_mappings] = (v << 2);
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
|
|
static int mz_try_runstack_pop(mz_jit_state *jitter, int n)
|
|
{
|
|
if (jitter->mappings[jitter->num_mappings] & 0x3)
|
|
return 0;
|
|
if ((jitter->mappings[jitter->num_mappings] >> 2) < n)
|
|
return 0;
|
|
mz_runstack_popped(jitter, n);
|
|
return 1;
|
|
}
|
|
|
|
static void mz_runstack_saved(mz_jit_state *jitter)
|
|
{
|
|
new_mapping(jitter);
|
|
/* 0 slot means "saved here" */
|
|
}
|
|
|
|
static int mz_runstack_restored(mz_jit_state *jitter)
|
|
{
|
|
/* pop down to 0 slot */
|
|
int amt = 0, c;
|
|
while ((c = jitter->mappings[jitter->num_mappings])) {
|
|
if (c & 0x1) {
|
|
/* native push */
|
|
c >>= 1;
|
|
if (c > 0)
|
|
amt += c;
|
|
} else if (c & 0x2) {
|
|
/* single procedure */
|
|
amt++;
|
|
jitter->self_pos--;
|
|
} else {
|
|
/* pushed N */
|
|
c = (c >> 2);
|
|
amt += c;
|
|
jitter->self_pos -= c;
|
|
}
|
|
--jitter->num_mappings;
|
|
}
|
|
--jitter->num_mappings;
|
|
if (amt)
|
|
jitter->need_set_rs = 1;
|
|
jitter->depth -= amt;
|
|
return amt;
|
|
}
|
|
|
|
static int mz_remap_it(mz_jit_state *jitter, int i)
|
|
{
|
|
int j = i, p = jitter->num_mappings, c;
|
|
while (p && (j >= 0)) {
|
|
c = jitter->mappings[p];
|
|
if (c & 0x1) {
|
|
/* native push */
|
|
c >>= 1;
|
|
i += c;
|
|
if (c < 0)
|
|
j += c;
|
|
} else if (c & 0x2) {
|
|
/* single procedure */
|
|
j--;
|
|
} else {
|
|
/* pushed N */
|
|
j -= (c >> 2);
|
|
}
|
|
--p;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int mz_is_closure(mz_jit_state *jitter, int i, int arity, int *_flags)
|
|
{
|
|
int j = i, p = jitter->num_mappings, c;
|
|
while (p && (j >= 0)) {
|
|
c = jitter->mappings[p];
|
|
if (c & 0x1) {
|
|
/* native push */
|
|
c >>= 1;
|
|
if (c < 0)
|
|
j += c;
|
|
} else if (c & 0x2) {
|
|
/* procedure */
|
|
if (!j) {
|
|
/* the one we're looking for */
|
|
if ((arity == (c >> 4)) || (arity == -1)) {
|
|
*_flags = (c >> 2) & 0x3;
|
|
return 1;
|
|
}
|
|
}
|
|
j--;
|
|
} else {
|
|
/* pushed N */
|
|
j -= (c >> 2);
|
|
}
|
|
--p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stack_safety(mz_jit_state *jitter, int cnt, int offset)
|
|
/* de-sync'd rs ok */
|
|
{
|
|
/* To preserve space safety, we must initialize any stack room
|
|
that we make, so that whatever happens to be there isn't
|
|
traversed in case of a GC. the value of JIT_RUNSTACK is
|
|
handy to use as a "clear" value. */
|
|
int i;
|
|
for (i = 0; i < cnt; i++) {
|
|
mz_rs_stxi(i+offset, JIT_RUNSTACK);
|
|
CHECK_LIMIT();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* de-sync's rs: */
|
|
#define mz_pushr_p(x) mz_pushr_p_it(jitter, x)
|
|
#define mz_popr_p(x) mz_popr_p_it(jitter, x)
|
|
|
|
#if 0
|
|
/* Debugging: at each _finish(), double-check that the runstack register has been
|
|
copied into scheme_current_runstack. This code assumes that mz_finishr() is not
|
|
used with JIT_R0. Failure is "reported" by going into an immediate loop, but
|
|
check_location is set to the source line number to help indicate where the
|
|
problem originated. */
|
|
static void *top;
|
|
int check_location;
|
|
# define CONFIRM_RUNSTACK() (jit_movi_l(JIT_R0, __LINE__), jit_sti_l(&check_location, JIT_R0), \
|
|
jit_ldi_p(JIT_R0, &MZ_RUNSTACK), top = (_jit.x.pc), jit_bner_p(top, JIT_RUNSTACK, JIT_R0))
|
|
#else
|
|
# define CONFIRM_RUNSTACK() 0
|
|
#endif
|
|
|
|
#define mz_prepare(x) jit_prepare(x)
|
|
#define mz_finish(x) ((void)CONFIRM_RUNSTACK(), jit_finish(x))
|
|
#define mz_finishr(x) ((void)CONFIRM_RUNSTACK(), jit_finishr(x))
|
|
|
|
#define mz_nonrs_finish(x) jit_finish(x)
|
|
|
|
#define mz_retain(x) mz_retain_it(jitter, x)
|
|
#define mz_remap(x) mz_remap_it(jitter, x)
|
|
|
|
/*
|
|
mz_prolog() and mz_epilog() bracket an internal "function" using a
|
|
lighter-weight ABI that keeps all Rx and Vx registers as-is on
|
|
entry and exit. Some of those functions are registered in a special
|
|
way with add_symbol() so that the backtrace function can follow the
|
|
lightweight ABI to get back to the calling code. The lightweight
|
|
ABI does not support nested calls (at least not on all platforms;
|
|
see LOCAL2 below).
|
|
|
|
LOCAL2 and LOCAL3 are available for temporary storage on the C
|
|
stack using mz_get_local() and mz_set_local() under certain
|
|
circumstances:
|
|
|
|
* They can only be used within a function (normally corresponding
|
|
to a Scheme lambda) where mz_push_locals() has been called after
|
|
jit_prolog(), and where mz_pop_locals() is called before
|
|
jit_ret().
|
|
|
|
* On some platforms, LOCAL2 and LOCAL3 are the same.
|
|
|
|
* On some platforms, a lightweight function created with
|
|
mz_prolog() and mz_epilog() uses LOCAL2 to save the return
|
|
address. On those platforms, though, LOCAL3 is dufferent from
|
|
LOCAL2. So, LOCAL3 can always be used for temporary storage in
|
|
such functions (assuming that they're called from a function that
|
|
pushes locals, and that nothing else is using LOCAL2).
|
|
*/
|
|
|
|
#ifdef MZ_USE_JIT_PPC
|
|
/* JIT_LOCAL1, JIT_LOCAL2, and JIT_LOCAL3 are offsets in the stack frame. */
|
|
# define JIT_LOCAL1 56
|
|
# define JIT_LOCAL2 60
|
|
# define JIT_LOCAL3 64
|
|
# define mz_set_local_p(x, l) jit_stxi_p(l, 1, x)
|
|
# define mz_get_local_p(x, l) jit_ldxi_p(x, 1, l)
|
|
# define mz_patch_branch_at(a, v) (_jitl.long_jumps ? (void)jit_patch_movei(a-4, a-3, v) : (void)jit_patch_branch(a-1, v))
|
|
# define mz_patch_ucbranch_at(a, v) (_jitl.long_jumps ? (void)jit_patch_movei(a-4, a-3, v) : (void)jit_patch_ucbranch(a-1, v))
|
|
# define mz_prolog(x) (MFLRr(x), mz_set_local_p(x, JIT_LOCAL2))
|
|
# define mz_epilog(x) (mz_get_local_p(x, JIT_LOCAL2), jit_jmpr(x))
|
|
# define mz_epilog_without_jmp() /* empty */
|
|
# define jit_shuffle_saved_regs() /* empty */
|
|
# define jit_unshuffle_saved_regs() /* empty */
|
|
# define mz_push_locals() /* empty */
|
|
# define mz_pop_locals() /* empty */
|
|
static void _jit_prolog_again(mz_jit_state *jitter, int n, int ret_addr_reg)
|
|
{
|
|
/* This must be consistent with _jit_prolog in many ways: */
|
|
int frame_size;
|
|
int ofs;
|
|
int first_saved_reg = JIT_AUX - n;
|
|
int num_saved_regs = 32 - first_saved_reg;
|
|
|
|
frame_size = 24 + 32 + 12 + num_saved_regs * 4; /* r27..r31 + args */
|
|
frame_size += 15; /* the stack must be quad-word */
|
|
frame_size &= ~15; /* aligned */
|
|
|
|
STWUrm(1, -frame_size, 1); /* stwu r1, -x(r1) */
|
|
|
|
/* We actually only need to save V0-V2, which are at
|
|
the end of the saved area: */
|
|
first_saved_reg = 29;
|
|
num_saved_regs = 3;
|
|
|
|
ofs = frame_size - num_saved_regs * 4;
|
|
STMWrm(first_saved_reg, ofs, 1); /* stmw rI, ofs(r1) */
|
|
#ifdef _CALL_DARWIN
|
|
STWrm(ret_addr_reg, frame_size + 8, 1); /* stw r0, x+8(r1) */
|
|
#else
|
|
STWrm(ret_addr_reg, frame_size + 4, 1); /* stw r0, x+4(r1) */
|
|
#endif
|
|
}
|
|
#else
|
|
# define JIT_LOCAL1 -(JIT_WORD_SIZE * 4)
|
|
# define JIT_LOCAL2 -(JIT_WORD_SIZE * 5)
|
|
# define mz_set_local_p(x, l) jit_stxi_p((l), JIT_FP, (x))
|
|
# define mz_get_local_p(x, l) jit_ldxi_p((x), JIT_FP, (l))
|
|
# define mz_patch_branch_at(a, v) jit_patch_branch_at(a, v)
|
|
# define mz_patch_ucbranch_at(a, v) jit_patch_ucbranch_at(a, v)
|
|
# ifdef _CALL_DARWIN
|
|
# define X86_ALIGN_STACK
|
|
# define STACK_ALIGN_WORDS 3
|
|
# endif
|
|
# ifdef JIT_X86_64
|
|
# define X86_ALIGN_STACK
|
|
# define STACK_ALIGN_WORDS 1
|
|
# endif
|
|
# ifdef X86_ALIGN_STACK
|
|
/* Maintain 4-byte stack alignment. */
|
|
# define mz_prolog(x) (ADDQiBr(-(STACK_ALIGN_WORDS * JIT_WORD_SIZE), JIT_SP))
|
|
# define mz_epilog_without_jmp() ADDQiBr((STACK_ALIGN_WORDS + 1) * JIT_WORD_SIZE, JIT_SP)
|
|
# define mz_epilog(x) (ADDQiBr(STACK_ALIGN_WORDS * JIT_WORD_SIZE, JIT_SP), RET_())
|
|
# define LOCAL_FRAME_SIZE 3
|
|
# define JIT_LOCAL3 -(JIT_WORD_SIZE * 6)
|
|
# else
|
|
# define mz_prolog(x) /* empty */
|
|
# define mz_epilog(x) RET_()
|
|
# define mz_epilog_without_jmp() ADDQir(JIT_WORD_SIZE, JIT_SP)
|
|
# define LOCAL_FRAME_SIZE 2
|
|
# define JIT_LOCAL3 JIT_LOCAL2
|
|
# endif
|
|
# define mz_push_locals() SUBQir((LOCAL_FRAME_SIZE << JIT_LOG_WORD_SIZE), JIT_SP)
|
|
# define mz_pop_locals() ADDQir((LOCAL_FRAME_SIZE << JIT_LOG_WORD_SIZE), JIT_SP)
|
|
#define _jit_prolog_again(jitter, n, ret_addr_reg) (PUSHQr(ret_addr_reg), jit_base_prolog())
|
|
# ifdef MZ_USE_JIT_X86_64
|
|
# define jit_shuffle_saved_regs() (MOVQrr(_ESI, _R12), MOVQrr(_EDI, _R13))
|
|
# define jit_unshuffle_saved_regs() (MOVQrr(_R12, _ESI), MOVQrr(_R13, _EDI))
|
|
# else
|
|
# define jit_shuffle_saved_regs() /* empty */
|
|
# define jit_unshuffle_saved_regs() /* empty */
|
|
# endif
|
|
#endif
|
|
|
|
#define mz_patch_branch(a) mz_patch_branch_at(a, (_jit.x.pc))
|
|
#define mz_patch_ucbranch(a) mz_patch_ucbranch_at(a, (_jit.x.pc))
|
|
|
|
#ifdef NEED_LONG_JUMPS
|
|
# define __START_SHORT_JUMPS__(cond) if (cond) { _jitl.long_jumps = 0; }
|
|
# define __END_SHORT_JUMPS__(cond) if (cond) { _jitl.long_jumps = 1; }
|
|
#else
|
|
# define __START_SHORT_JUMPS__(cond) /* empty */
|
|
# define __END_SHORT_JUMPS__(cond) /* empty */
|
|
#endif
|
|
|
|
#ifdef USE_TINY_JUMPS
|
|
/* A tiny jump has to be between -128 and 127 bytes. */
|
|
# define __START_TINY_JUMPS__(cond) if (cond) { _jitl.tiny_jumps = 1; }
|
|
# define __END_TINY_JUMPS__(cond) if (cond) { _jitl.tiny_jumps = 0; }
|
|
# define __START_INNER_TINY__(cond) __END_SHORT_JUMPS__(cond); __START_TINY_JUMPS__(1);
|
|
# define __END_INNER_TINY__(cond) __END_TINY_JUMPS__(1); __START_SHORT_JUMPS__(cond);
|
|
#else
|
|
# define __START_TINY_JUMPS__(cond) __START_SHORT_JUMPS__(cond)
|
|
# define __END_TINY_JUMPS__(cond) __END_SHORT_JUMPS__(cond)
|
|
# define __START_INNER_TINY__(cond) /* empty */
|
|
# define __END_INNER_TINY__(cond) /* empty */
|
|
#endif
|
|
|
|
#define __START_TINY_OR_SHORT_JUMPS__(tcond, cond) if (tcond) { __START_TINY_JUMPS__(1); } else { __START_SHORT_JUMPS__(cond); }
|
|
#define __END_TINY_OR_SHORT_JUMPS__(tcond, cond) if (tcond) { __END_TINY_JUMPS__(1); } else { __END_SHORT_JUMPS__(cond); }
|
|
|
|
/* mz_b..i_p supports 64-bit constants on x86_64: */
|
|
#ifdef MZ_USE_JIT_X86_64
|
|
# define mz_beqi_p(a, v, i) ((void)jit_patchable_movi_p(JIT_REXTMP, i), jit_beqr_p(a, v, JIT_REXTMP))
|
|
# define mz_bnei_p(a, v, i) ((void)jit_patchable_movi_p(JIT_REXTMP, i), jit_bner_p(a, v, JIT_REXTMP))
|
|
#else
|
|
# define mz_beqi_p(a, v, i) jit_beqi_p(a, v, i)
|
|
# define mz_bnei_p(a, v, i) jit_bnei_p(a, v, i)
|
|
#endif
|
|
|
|
/*
|
|
About short-jump mode:
|
|
|
|
In
|
|
jit_jmpi(code);
|
|
or
|
|
jit_blti_i(code, v);
|
|
the generated instructions can depend on the relative location
|
|
between the instruction address and the actual value. Do not enable
|
|
short jumps if the relative offset can change between the initial
|
|
sizing pass and the final pass. Of course, also don't enable short
|
|
jumps if the jump is potentially long (i.e. more than +/- 2^15
|
|
on PowerPC, or more than +/- 2^31 on x86_64). Otherwise, enable
|
|
short-jump mode as much as possible.
|
|
|
|
Tiny-jump mode is like short-jump mode, but the offset must be
|
|
within +/- 2^7. Favor tiny jumps over short jumps when possible.
|
|
|
|
All mz_finish() and jit_calli() are implicitly long jumps.
|
|
*/
|
|
|
|
/*========================================================================*/
|
|
/* inlined allocation */
|
|
/*========================================================================*/
|
|
|
|
#if defined(MZ_PRECISE_GC) && !defined(USE_COMPACT_3M_GC)
|
|
# define CAN_INLINE_ALLOC
|
|
#endif
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
extern THREAD_LOCAL unsigned long GC_gen0_alloc_page_ptr;
|
|
long GC_initial_word(int sizeb);
|
|
void GC_initial_words(char *buffer, int sizeb);
|
|
long GC_compute_alloc_size(long sizeb);
|
|
long GC_alloc_alignment(void);
|
|
|
|
static void *retry_alloc_code;
|
|
static void *retry_alloc_code_keep_r0_r1;
|
|
static void *retry_alloc_code_keep_fpr1;
|
|
|
|
static void *retry_alloc_r1; /* set by prepare_retry_alloc() */
|
|
|
|
static int generate_alloc_retry(mz_jit_state *jitter, int i);
|
|
|
|
#ifdef JIT_USE_FP_OPS
|
|
static double save_fp;
|
|
#endif
|
|
|
|
static void *prepare_retry_alloc(void *p, void *p2)
|
|
{
|
|
/* Alocate enough to trigger a new page */
|
|
long avail, algn;
|
|
|
|
algn = GC_alloc_alignment();
|
|
avail = algn - (GC_gen0_alloc_page_ptr & (algn - 1));
|
|
|
|
if (!avail)
|
|
avail = 1;
|
|
else if (avail == algn)
|
|
avail = 1;
|
|
|
|
if (avail > sizeof(long))
|
|
avail -= sizeof(long);
|
|
|
|
/* We assume that atomic memory and tagged go to the same nursery: */
|
|
scheme_malloc_atomic(avail);
|
|
|
|
retry_alloc_r1 = p2;
|
|
|
|
return p;
|
|
}
|
|
|
|
static long read_first_word(void *sp)
|
|
{
|
|
long foo;
|
|
memcpy(&foo, sp, sizeof(long));
|
|
return foo;
|
|
}
|
|
|
|
static long initial_tag_word(Scheme_Type tag, int immut)
|
|
{
|
|
GC_CAN_IGNORE Scheme_Small_Object sp;
|
|
memset(&sp, 0, sizeof(Scheme_Small_Object));
|
|
sp.iso.so.type = tag;
|
|
if (immut) SCHEME_SET_IMMUTABLE(&sp);
|
|
return read_first_word((void *)&sp);
|
|
}
|
|
|
|
static int inline_alloc(mz_jit_state *jitter, int amt, Scheme_Type ty, int immut,
|
|
int keep_r0_r1, int keep_fpr1, int inline_retry)
|
|
/* Puts allocated result at JIT_V1; first word is GC tag.
|
|
Uses JIT_R2 as temporary. The allocated memory is "dirty" (i.e., not 0ed).
|
|
Save FP0 when FP ops are enabled. */
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *reffail;
|
|
long a_word, sz, algn;
|
|
#if defined(MZ_USE_PLACES)
|
|
long a_words[2];
|
|
#endif
|
|
|
|
sz = GC_compute_alloc_size(amt);
|
|
algn = GC_alloc_alignment();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
reffail = _jit.x.pc;
|
|
jit_ldi_p(JIT_V1, &GC_gen0_alloc_page_ptr);
|
|
jit_subi_l(JIT_R2, JIT_V1, 1);
|
|
jit_andi_l(JIT_R2, JIT_R2, (algn - 1));
|
|
ref = jit_blti_l(jit_forward(), JIT_R2, (algn - sz));
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
/* Failure handling */
|
|
if (keep_r0_r1) {
|
|
if (inline_retry) {
|
|
generate_alloc_retry(jitter, 1);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
(void)jit_calli(retry_alloc_code_keep_r0_r1);
|
|
}
|
|
} else if (keep_fpr1) {
|
|
(void)jit_calli(retry_alloc_code_keep_fpr1);
|
|
} else {
|
|
(void)jit_calli(retry_alloc_code);
|
|
}
|
|
__START_TINY_JUMPS__(1);
|
|
(void)jit_jmpi(reffail);
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
jit_addi_ul(JIT_R2, JIT_V1, sz);
|
|
(void)jit_sti_l(&GC_gen0_alloc_page_ptr, JIT_R2);
|
|
#if !defined(MZ_USE_PLACES)
|
|
a_word = GC_initial_word(amt);
|
|
jit_movi_l(JIT_R2, a_word);
|
|
jit_str_l(JIT_V1, JIT_R2);
|
|
|
|
/*SchemeObject header*/
|
|
a_word = initial_tag_word(ty, immut);
|
|
jit_movi_l(JIT_R2, a_word);
|
|
jit_stxi_l(sizeof(long), JIT_V1, JIT_R2);
|
|
#else
|
|
GC_initial_words(a_words, amt);
|
|
jit_movi_l(JIT_R2, a_words[0]);
|
|
jit_str_l(JIT_V1, JIT_R2);
|
|
|
|
jit_movi_l(JIT_R2, a_words[1]);
|
|
jit_stxi_l(sizeof(long), JIT_V1, JIT_R2);
|
|
|
|
/*SchemeObject header*/
|
|
a_word = initial_tag_word(ty, immut);
|
|
jit_movi_l(JIT_R2, a_word);
|
|
jit_stxi_l(sizeof(long)*2, JIT_V1, JIT_R2);
|
|
#endif
|
|
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef JIT_USE_FP_OPS
|
|
# define INLINE_FP_COMP
|
|
# ifdef CAN_INLINE_ALLOC
|
|
# define INLINE_FP_OPS
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(INLINE_FP_OPS) && !defined(CAN_INLINE_ALLOC)
|
|
static double double_result;
|
|
static void *malloc_double(void)
|
|
{
|
|
return scheme_make_double(double_result);
|
|
}
|
|
#endif
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
# define cons GC_malloc_pair
|
|
#else
|
|
# define cons scheme_make_pair
|
|
#endif
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
static void *make_list_code, *make_list_star_code;
|
|
#else
|
|
static Scheme_Object *make_list(long n)
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object *l = scheme_null;
|
|
GC_CAN_IGNORE Scheme_Object **rs = MZ_RUNSTACK;
|
|
|
|
while (n--) {
|
|
l = cons(rs[n], l);
|
|
}
|
|
|
|
return l;
|
|
}
|
|
static Scheme_Object *make_list_star(long n)
|
|
{
|
|
GC_CAN_IGNORE Scheme_Object **rs = MZ_RUNSTACK;
|
|
GC_CAN_IGNORE Scheme_Object *l = rs[--n];
|
|
|
|
while (n--) {
|
|
l = cons(rs[n], l);
|
|
}
|
|
|
|
return l;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CAN_INLINE_ALLOC)
|
|
static Scheme_Object *make_vector(long n)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = scheme_make_vector(n, NULL);
|
|
return vec;
|
|
}
|
|
static Scheme_Object *make_ivector(long n)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = make_vector(n);
|
|
SCHEME_SET_IMMUTABLE(vec);
|
|
return vec;
|
|
}
|
|
static Scheme_Object *make_one_element_vector(Scheme_Object *a)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = scheme_make_vector(1, a);
|
|
return vec;
|
|
}
|
|
static Scheme_Object *make_one_element_ivector(Scheme_Object *a)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = make_one_element_vector(a);
|
|
SCHEME_SET_IMMUTABLE(vec);
|
|
return vec;
|
|
}
|
|
static Scheme_Object *make_two_element_vector(Scheme_Object *a, Scheme_Object *b)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = scheme_make_vector(2, a);
|
|
SCHEME_VEC_ELS(vec)[1] = b;
|
|
return vec;
|
|
}
|
|
static Scheme_Object *make_two_element_ivector(Scheme_Object *a, Scheme_Object *b)
|
|
{
|
|
Scheme_Object *vec;
|
|
vec = make_two_element_vector(a, b);
|
|
SCHEME_SET_IMMUTABLE(vec);
|
|
return vec;
|
|
}
|
|
#endif
|
|
|
|
/*========================================================================*/
|
|
/* bytecode properties */
|
|
/*========================================================================*/
|
|
|
|
#ifdef NEED_LONG_JUMPS
|
|
static int is_short(Scheme_Object *obj, int fuel)
|
|
{
|
|
Scheme_Type t;
|
|
|
|
if (fuel <= 0)
|
|
return fuel;
|
|
|
|
t = SCHEME_TYPE(obj);
|
|
|
|
switch (t) {
|
|
case scheme_syntax_type:
|
|
{
|
|
int t;
|
|
t = SCHEME_PINT_VAL(obj);
|
|
if (t == CASE_LAMBDA_EXPD)
|
|
return fuel - 1;
|
|
else
|
|
return 0;
|
|
}
|
|
break;
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)obj;
|
|
int i;
|
|
|
|
fuel -= app->num_args;
|
|
for (i = app->num_args + 1; i--; ) {
|
|
fuel = is_short(app->args[i], fuel);
|
|
}
|
|
return fuel;
|
|
}
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)obj;
|
|
fuel -= 2;
|
|
fuel = is_short(app->rator, fuel);
|
|
return is_short(app->rand, fuel);
|
|
}
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)obj;
|
|
fuel -= 3;
|
|
fuel = is_short(app->rator, fuel);
|
|
fuel = is_short(app->rand1, fuel);
|
|
return is_short(app->rand2, fuel);
|
|
}
|
|
case scheme_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)obj;
|
|
int i;
|
|
|
|
fuel -= seq->count;
|
|
for (i = seq->count; i--; ) {
|
|
fuel = is_short(seq->array[i], fuel);
|
|
}
|
|
return fuel;
|
|
}
|
|
break;
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *branch = (Scheme_Branch_Rec *)obj;
|
|
fuel -= 3;
|
|
fuel = is_short(branch->test, fuel);
|
|
fuel = is_short(branch->tbranch, fuel);
|
|
return is_short(branch->fbranch, fuel);
|
|
}
|
|
case scheme_toplevel_type:
|
|
case scheme_quote_syntax_type:
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
case scheme_unclosed_procedure_type:
|
|
return fuel - 1;
|
|
default:
|
|
if (t > _scheme_values_types_)
|
|
return fuel - 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int no_sync_change(Scheme_Object *obj, int fuel)
|
|
{
|
|
Scheme_Type t;
|
|
|
|
if (fuel <= 0)
|
|
return fuel;
|
|
|
|
t = SCHEME_TYPE(obj);
|
|
|
|
switch (t) {
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)obj;
|
|
if (SCHEME_PRIMP(app->rator)
|
|
&& (SCHEME_PRIM_PROC_FLAGS(app->rator) & SCHEME_PRIM_IS_UNARY_INLINED)
|
|
&& (IS_NAMED_PRIM(app->rator, "car")
|
|
|| IS_NAMED_PRIM(app->rator, "cdr")
|
|
|| IS_NAMED_PRIM(app->rator, "cadr")
|
|
|| IS_NAMED_PRIM(app->rator, "cdar")
|
|
|| IS_NAMED_PRIM(app->rator, "caar")
|
|
|| IS_NAMED_PRIM(app->rator, "cddr"))) {
|
|
return no_sync_change(app->rand, fuel - 1);
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
case scheme_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)obj;
|
|
int i;
|
|
|
|
fuel -= seq->count;
|
|
for (i = seq->count; i--; ) {
|
|
fuel = no_sync_change(seq->array[i], fuel);
|
|
}
|
|
return fuel;
|
|
}
|
|
break;
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *branch = (Scheme_Branch_Rec *)obj;
|
|
fuel -= 3;
|
|
fuel = no_sync_change(branch->test, fuel);
|
|
fuel = no_sync_change(branch->tbranch, fuel);
|
|
return no_sync_change(branch->fbranch, fuel);
|
|
}
|
|
case scheme_toplevel_type:
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
return fuel - 1;
|
|
default:
|
|
if (t > _scheme_values_types_)
|
|
return fuel - 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Scheme_Object *extract_global(Scheme_Object *o, Scheme_Native_Closure *nc)
|
|
{
|
|
/* GLOBAL ASSUMPTION: we assume that globals are the last thing
|
|
in the closure; grep for "GLOBAL ASSUMPTION" in fun.c. */
|
|
Scheme_Object **globs;
|
|
|
|
globs = (Scheme_Object **)nc->vals[nc->code->u2.orig_code->closure_size - 1];
|
|
return globs[SCHEME_TOPLEVEL_POS(o)];
|
|
}
|
|
|
|
Scheme_Object *extract_closure_local(Scheme_Object *obj, mz_jit_state *jitter, int extra_push)
|
|
{
|
|
int pos;
|
|
|
|
pos = SCHEME_LOCAL_POS(obj);
|
|
pos -= extra_push;
|
|
if (pos >= jitter->self_pos - jitter->self_to_closure_delta) {
|
|
pos -= (jitter->self_pos - jitter->self_to_closure_delta);
|
|
if (pos < jitter->nc->code->u2.orig_code->closure_size) {
|
|
/* in the closure */
|
|
return jitter->nc->vals[pos];
|
|
} else {
|
|
/* maybe an example argument... which is useful when
|
|
the enclosing function has been lifted, converting
|
|
a closure element into an argument */
|
|
pos -= jitter->closure_to_args_delta;
|
|
if (pos < jitter->example_argc)
|
|
return jitter->example_argv[pos];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int check_val_struct_prim(Scheme_Object *p, int arity)
|
|
{
|
|
if (p && SCHEME_PRIMP(p)) {
|
|
if (arity == 1) {
|
|
if (((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_IS_STRUCT_PRED)
|
|
return 1;
|
|
else if (((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_IS_STRUCT_INDEXED_GETTER)
|
|
return 2;
|
|
} else if (arity == 2) {
|
|
if ((((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_IS_STRUCT_OTHER)
|
|
&& ((((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_STRUCT_OTHER_TYPE_MASK)
|
|
== SCHEME_PRIM_STRUCT_TYPE_INDEXED_SETTER))
|
|
return 3;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int inlineable_struct_prim(Scheme_Object *o, mz_jit_state *jitter, int extra_push, int arity)
|
|
{
|
|
if (jitter->nc) {
|
|
if (SAME_TYPE(SCHEME_TYPE(o), scheme_toplevel_type)) {
|
|
Scheme_Object *p;
|
|
p = extract_global(o, jitter->nc);
|
|
p = ((Scheme_Bucket *)p)->val;
|
|
return check_val_struct_prim(p, arity);
|
|
} else if (SAME_TYPE(SCHEME_TYPE(o), scheme_local_type)) {
|
|
Scheme_Object *p;
|
|
p = extract_closure_local(o, jitter, extra_push);
|
|
return check_val_struct_prim(p, arity);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int inlined_unary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter)
|
|
{
|
|
if (SCHEME_PRIMP(o)
|
|
&& (SCHEME_PRIM_PROC_FLAGS(o) & SCHEME_PRIM_IS_UNARY_INLINED))
|
|
return 1;
|
|
|
|
if (inlineable_struct_prim(o, jitter, 1, 1))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int inlined_binary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter)
|
|
{
|
|
return ((SCHEME_PRIMP(o)
|
|
&& (SCHEME_PRIM_PROC_FLAGS(o) & SCHEME_PRIM_IS_BINARY_INLINED))
|
|
|| inlineable_struct_prim(o, jitter, 2, 2));
|
|
}
|
|
|
|
static int inlined_nary_prim(Scheme_Object *o, Scheme_Object *_app)
|
|
{
|
|
return (SCHEME_PRIMP(o)
|
|
&& (SCHEME_PRIM_PROC_FLAGS(o) & SCHEME_PRIM_IS_NARY_INLINED)
|
|
&& (((Scheme_App_Rec *)_app)->num_args >= ((Scheme_Primitive_Proc *)o)->mina)
|
|
&& (((Scheme_App_Rec *)_app)->num_args <= ((Scheme_Primitive_Proc *)o)->mu.maxa));
|
|
}
|
|
|
|
static int is_noncm(Scheme_Object *a, mz_jit_state *jitter, int depth, int stack_start)
|
|
{
|
|
if (SCHEME_PRIMP(a)) {
|
|
int opts;
|
|
opts = ((Scheme_Prim_Proc_Header *)a)->flags & SCHEME_PRIM_OPT_MASK;
|
|
if (opts >= SCHEME_PRIM_OPT_NONCM)
|
|
/* Structure-type predicates are handled specially, so don't claim NONCM: */
|
|
if (!(((Scheme_Prim_Proc_Header *)a)->flags & SCHEME_PRIM_IS_STRUCT_PRED))
|
|
return 1;
|
|
}
|
|
|
|
if (depth
|
|
&& jitter->nc
|
|
&& SAME_TYPE(SCHEME_TYPE(a), scheme_toplevel_type)
|
|
&& (SCHEME_TOPLEVEL_FLAGS(a) & SCHEME_TOPLEVEL_CONST)) {
|
|
Scheme_Object *p;
|
|
p = extract_global(a, jitter->nc);
|
|
p = ((Scheme_Bucket *)p)->val;
|
|
if (p && SAME_TYPE(SCHEME_TYPE(p), scheme_native_closure_type)) {
|
|
Scheme_Native_Closure_Data *ndata = ((Scheme_Native_Closure *)p)->code;
|
|
if (ndata->closure_size >= 0) { /* not case-lambda */
|
|
if (lambda_has_been_jitted(ndata)) {
|
|
if (SCHEME_NATIVE_CLOSURE_DATA_FLAGS(ndata) & NATIVE_PRESERVES_MARKS)
|
|
return 1;
|
|
} else {
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(ndata->u2.orig_code) & CLOS_PRESERVES_MARKS)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SAME_TYPE(SCHEME_TYPE(a), scheme_local_type)) {
|
|
int pos = SCHEME_LOCAL_POS(a) - stack_start;
|
|
if (pos >= 0) {
|
|
int flags;
|
|
if (mz_is_closure(jitter, pos, -1, &flags)) {
|
|
return (flags & NATIVE_PRESERVES_MARKS);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (depth && SAME_TYPE(SCHEME_TYPE(a), scheme_closure_type)) {
|
|
Scheme_Closure_Data *data;
|
|
|
|
data = ((Scheme_Closure *)a)->code;
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_PRESERVES_MARKS)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define INIT_SIMPLE_DEPTH 10
|
|
|
|
static int is_simple(Scheme_Object *obj, int depth, int just_markless, mz_jit_state *jitter, int stack_start)
|
|
{
|
|
/* Return 1 if evaluating `obj' doesn't change the runstack or cont-mark stack ---
|
|
or, if just_markless is 1, doesn't use the cont-mark stack.
|
|
If a form doesn't itself change/use the stack, then check all
|
|
expressions in tail position, up to some depth. The conservative
|
|
answer is always 0. */
|
|
Scheme_Type type;
|
|
|
|
type = SCHEME_TYPE(obj);
|
|
|
|
switch (type) {
|
|
case scheme_syntax_type:
|
|
{
|
|
int t;
|
|
t = SCHEME_PINT_VAL(obj);
|
|
return (t == CASE_LAMBDA_EXPD);
|
|
}
|
|
break;
|
|
|
|
case scheme_branch_type:
|
|
if (depth) {
|
|
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)obj;
|
|
return (is_simple(b->tbranch, depth - 1, just_markless, jitter, stack_start)
|
|
&& is_simple(b->fbranch, depth - 1, just_markless, jitter, stack_start));
|
|
}
|
|
break;
|
|
|
|
case scheme_let_value_type:
|
|
if (depth) {
|
|
return is_simple(((Scheme_Let_Value *)obj)->body, depth - 1, just_markless, jitter, stack_start);
|
|
}
|
|
break;
|
|
case scheme_let_one_type:
|
|
if (just_markless && depth) {
|
|
return is_simple(((Scheme_Let_One *)obj)->body, depth - 1, just_markless, jitter, stack_start + 1);
|
|
}
|
|
break;
|
|
case scheme_let_void_type:
|
|
if (just_markless && depth) {
|
|
return is_simple(((Scheme_Let_Void *)obj)->body, depth - 1, just_markless, jitter,
|
|
stack_start + ((Scheme_Let_Void *)obj)->count);
|
|
}
|
|
break;
|
|
case scheme_letrec_type:
|
|
if (just_markless && depth) {
|
|
return is_simple(((Scheme_Letrec *)obj)->body, depth - 1, just_markless, jitter,
|
|
stack_start + ((Scheme_Letrec *)obj)->count);
|
|
}
|
|
break;
|
|
|
|
case scheme_application_type:
|
|
if (inlined_nary_prim(((Scheme_App_Rec *)obj)->args[0], obj))
|
|
return 1;
|
|
if (just_markless) {
|
|
return is_noncm(((Scheme_App_Rec *)obj)->args[0], jitter, depth,
|
|
stack_start + ((Scheme_App_Rec *)obj)->num_args);
|
|
}
|
|
break;
|
|
case scheme_application2_type:
|
|
if (inlined_unary_prim(((Scheme_App2_Rec *)obj)->rator, obj, jitter))
|
|
return 1;
|
|
else if (just_markless) {
|
|
return is_noncm(((Scheme_App2_Rec *)obj)->rator, jitter, depth, stack_start + 1);
|
|
}
|
|
break;
|
|
case scheme_application3_type:
|
|
if (inlined_binary_prim(((Scheme_App2_Rec *)obj)->rator, obj, jitter))
|
|
return 1;
|
|
else if (just_markless) {
|
|
return is_noncm(((Scheme_App3_Rec *)obj)->rator, jitter, depth, stack_start + 2);
|
|
}
|
|
break;
|
|
|
|
case scheme_toplevel_type:
|
|
case scheme_quote_syntax_type:
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
case scheme_unclosed_procedure_type:
|
|
return 1;
|
|
break;
|
|
}
|
|
|
|
return (type > _scheme_values_types_);
|
|
}
|
|
|
|
static int is_non_gc(Scheme_Object *obj, int depth)
|
|
{
|
|
/* Return 1 if evaluating `obj' can't trigger a GC. */
|
|
Scheme_Type type;
|
|
|
|
type = SCHEME_TYPE(obj);
|
|
|
|
switch (type) {
|
|
case scheme_syntax_type:
|
|
break;
|
|
|
|
case scheme_branch_type:
|
|
if (depth) {
|
|
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)obj;
|
|
return (is_non_gc(b->test, depth - 1)
|
|
&& is_non_gc(b->tbranch, depth - 1)
|
|
&& is_non_gc(b->fbranch, depth - 1));
|
|
}
|
|
break;
|
|
|
|
case scheme_let_value_type:
|
|
if (depth) {
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)obj;
|
|
if (SCHEME_LET_AUTOBOX(lv))
|
|
return 0;
|
|
return is_non_gc(lv->body, depth - 1);
|
|
}
|
|
break;
|
|
case scheme_let_one_type:
|
|
if (depth) {
|
|
return (is_non_gc(((Scheme_Let_One *)obj)->value, depth - 1)
|
|
&& is_non_gc(((Scheme_Let_One *)obj)->body, depth - 1));
|
|
}
|
|
break;
|
|
case scheme_let_void_type:
|
|
if (depth) {
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)obj;
|
|
if (SCHEME_LET_AUTOBOX(lv))
|
|
return 0;
|
|
return is_non_gc(lv->body, depth - 1);
|
|
}
|
|
break;
|
|
case scheme_letrec_type:
|
|
break;
|
|
|
|
case scheme_application_type:
|
|
break;
|
|
case scheme_application2_type:
|
|
break;
|
|
case scheme_application3_type:
|
|
break;
|
|
|
|
case scheme_toplevel_type:
|
|
break;
|
|
case scheme_unclosed_procedure_type:
|
|
break;
|
|
|
|
case scheme_quote_syntax_type:
|
|
case scheme_local_type:
|
|
case scheme_local_unbox_type:
|
|
return 1;
|
|
break;
|
|
}
|
|
|
|
return (type > _scheme_values_types_);
|
|
}
|
|
|
|
static int ok_to_move_local(Scheme_Object *obj)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(obj), scheme_local_type)
|
|
&& !(SCHEME_LOCAL_FLAGS(obj) & SCHEME_LOCAL_CLEARING_MASK)) {
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
static int is_constant_and_avoids_r1(Scheme_Object *obj)
|
|
{
|
|
Scheme_Type t = SCHEME_TYPE(obj);
|
|
|
|
if (SAME_TYPE(t, scheme_toplevel_type)) {
|
|
return ((SCHEME_TOPLEVEL_FLAGS(obj) & SCHEME_TOPLEVEL_CONST)
|
|
? 1
|
|
: 0);
|
|
} else if (SAME_TYPE(t, scheme_local_type) && ok_to_move_local(obj)) {
|
|
return 1;
|
|
} else
|
|
return (t >= _scheme_compiled_values_types_);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* application codegen */
|
|
/*========================================================================*/
|
|
|
|
static jit_insn *generate_proc_struct_retry(mz_jit_state *jitter, int num_rands, GC_CAN_IGNORE jit_insn *refagain)
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref2, *refz1, *refz2, *refz3, *refz4, *refz5;
|
|
|
|
ref2 = jit_bnei_i(jit_forward(), JIT_R1, scheme_proc_struct_type);
|
|
jit_ldxi_p(JIT_R1, JIT_V1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldi_p(JIT_R2, &scheme_reduced_procedure_struct);
|
|
refz3 = jit_beqr_p(jit_forward(), JIT_R1, JIT_R2);
|
|
jit_ldxi_p(JIT_R1, JIT_R1, &((Scheme_Struct_Type *)0x0)->proc_attr);
|
|
refz1 = jit_bmci_i(jit_forward(), JIT_R1, 0x1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Proc is a field in the record */
|
|
jit_rshi_ul(JIT_R1, JIT_R1, 1);
|
|
jit_lshi_ul(JIT_R1, JIT_R1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_R1, JIT_R1, &((Scheme_Structure *)0x0)->slots);
|
|
jit_ldxr_p(JIT_R1, JIT_V1, JIT_R1);
|
|
|
|
/* JIT_R1 now has the wrapped procedure */
|
|
refz4 = jit_bmsi_i(jit_forward(), JIT_R1, 0x1);
|
|
jit_ldr_s(JIT_R2, JIT_R1);
|
|
refz2 = jit_bnei_i(jit_forward(), JIT_R2, scheme_native_closure_type);
|
|
CHECK_LIMIT();
|
|
|
|
/* It's a native closure, but we can't just jump to it, in case
|
|
the arity is wrong. */
|
|
mz_prepare(2);
|
|
jit_movi_i(JIT_R0, num_rands);
|
|
jit_pusharg_i(JIT_R0); /* argc */
|
|
jit_pusharg_p(JIT_R1); /* closure */
|
|
(void)mz_finish(scheme_native_arity_check);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
refz5 = jit_beqi_i(jit_forward(), JIT_R0, 0);
|
|
CHECK_LIMIT();
|
|
|
|
/* Extract proc again, then loop */
|
|
jit_ldxi_p(JIT_R1, JIT_V1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldxi_p(JIT_R1, JIT_R1, &((Scheme_Struct_Type *)0x0)->proc_attr);
|
|
jit_rshi_ul(JIT_R1, JIT_R1, 1);
|
|
jit_lshi_ul(JIT_R1, JIT_R1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_R1, JIT_R1, &((Scheme_Structure *)0x0)->slots);
|
|
jit_ldxr_p(JIT_V1, JIT_V1, JIT_R1);
|
|
(void)jit_jmpi(refagain);
|
|
CHECK_LIMIT();
|
|
|
|
mz_patch_branch(refz1);
|
|
mz_patch_branch(refz2);
|
|
mz_patch_branch(refz3);
|
|
mz_patch_branch(refz4);
|
|
mz_patch_branch(refz5);
|
|
|
|
return ref2;
|
|
}
|
|
|
|
static int generate_direct_prim_tail_call(mz_jit_state *jitter, int num_rands)
|
|
{
|
|
/* JIT_V1 must have the target function pointer.
|
|
Also, scheme_current_runstack must be up-to-date...
|
|
unless num-rands == 1, in which case JIT_R0 must
|
|
have the argument. */
|
|
if (num_rands == 1) {
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
jit_movi_i(JIT_R1, num_rands);
|
|
mz_prepare(2); /* a prim takes 3 args, but a NONCM prim ignores the 3rd */
|
|
CHECK_LIMIT();
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
mz_finishr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
/* Return: */
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_tail_call(mz_jit_state *jitter, int num_rands, int direct_native, int need_set_rs, int is_inline)
|
|
/* Proc is in V1, args are at RUNSTACK.
|
|
If num_rands < 0, then argc is in LOCAL2 and arguments are already below RUNSTACK_BASE.
|
|
If direct_native == 2, then some arguments are already in place (shallower in the runstack
|
|
than the arguments to move). */
|
|
{
|
|
int i;
|
|
GC_CAN_IGNORE jit_insn *refagain, *ref, *ref2, *ref4, *ref5;
|
|
|
|
__START_SHORT_JUMPS__(num_rands < 100);
|
|
|
|
/* First, try fast direct jump to native code: */
|
|
if (!direct_native) {
|
|
ref = jit_bmsi_ul(jit_forward(), JIT_V1, 0x1);
|
|
jit_ldr_s(JIT_R1, JIT_V1);
|
|
ref2 = jit_bnei_i(jit_forward(), JIT_R1, scheme_native_closure_type);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
ref = ref2 = NULL;
|
|
}
|
|
|
|
refagain = _jit.x.pc;
|
|
|
|
/* Right kind of function. Extract data and check stack depth: */
|
|
jit_ldxi_p(JIT_R0, JIT_V1, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_i(JIT_R2, JIT_R0, &((Scheme_Native_Closure_Data *)0x0)->max_let_depth);
|
|
jit_ldi_p(JIT_R1, &MZ_RUNSTACK_START);
|
|
jit_subr_ul(JIT_R1, JIT_RUNSTACK, JIT_R1);
|
|
ref4 = jit_bltr_ul(jit_forward(), JIT_R1, JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* Fast jump ok (proc will check argc).
|
|
At this point, V1 = closure and R0 = code. */
|
|
|
|
/* Check for thread swap: */
|
|
(void)jit_movi_p(JIT_R1, &scheme_fuel_counter);
|
|
jit_ldr_i(JIT_R2, JIT_R1);
|
|
ref5 = jit_blei_i(jit_forward(), JIT_R2, 0);
|
|
#ifndef FUEL_AUTODECEREMENTS
|
|
jit_subi_p(JIT_R2, JIT_R2, 0x1);
|
|
jit_str_i(JIT_R1, JIT_R2);
|
|
#endif
|
|
CHECK_LIMIT();
|
|
|
|
/* Copy args to runstack base: */
|
|
if (num_rands >= 0) {
|
|
/* Fixed argc: */
|
|
if (num_rands) {
|
|
jit_subi_p(JIT_R2, JIT_RUNSTACK_BASE, WORDS_TO_BYTES(num_rands));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
for (i = num_rands; i--; ) {
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(i));
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_R2, JIT_R1);
|
|
CHECK_LIMIT();
|
|
}
|
|
jit_movr_p(JIT_RUNSTACK, JIT_R2);
|
|
} else {
|
|
jit_movr_p(JIT_RUNSTACK, JIT_RUNSTACK_BASE);
|
|
}
|
|
if (direct_native > 1) { /* => some_args_already_in_place */
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL2);
|
|
jit_lshi_l(JIT_R1, JIT_R1, JIT_LOG_WORD_SIZE);
|
|
jit_subr_p(JIT_RUNSTACK, JIT_RUNSTACK, JIT_R1);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
}
|
|
} else {
|
|
/* Variable argc (in LOCAL2):
|
|
arguments are already in place. */
|
|
}
|
|
/* RUNSTACK, RUNSTACK_BASE, V1, and R0 are ready */
|
|
|
|
/* Extract function and data: */
|
|
jit_movr_p(JIT_R2, JIT_V1);
|
|
if (direct_native) {
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure_Data *)0x0)->u.tail_code);
|
|
} else {
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
|
|
}
|
|
/* Set up arguments; JIT_RUNSTACK and JIT_RUNSTACK_BASE must also be ready */
|
|
jit_movr_p(JIT_R0, JIT_R2);
|
|
if (num_rands >= 0) {
|
|
jit_movi_i(JIT_R1, num_rands);
|
|
if (direct_native > 1) { /* => some_args_already_in_place */
|
|
mz_get_local_p(JIT_R2, JIT_LOCAL2);
|
|
jit_addr_i(JIT_R1, JIT_R1, JIT_R2);
|
|
}
|
|
} else {
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL2);
|
|
}
|
|
jit_movr_p(JIT_R2, JIT_RUNSTACK);
|
|
if (need_set_rs) {
|
|
/* In case arity check fails, need to update runstack now: */
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
/* Now jump: */
|
|
jit_jmpr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
|
|
if (!direct_native && !is_inline && (num_rands >= 0)) {
|
|
/* Handle simple applicable struct: */
|
|
mz_patch_branch(ref2);
|
|
ref2 = generate_proc_struct_retry(jitter, num_rands, refagain);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
/* The slow way: */
|
|
/* V1 and RUNSTACK must be intact! */
|
|
if (!direct_native) {
|
|
mz_patch_branch(ref);
|
|
mz_patch_branch(ref2);
|
|
}
|
|
mz_patch_branch(ref4);
|
|
mz_patch_branch(ref5);
|
|
CHECK_LIMIT();
|
|
if (need_set_rs) {
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
if (direct_native > 1) { /* => some_args_already_in_place */
|
|
/* Need to shuffle argument lists. Since we can pass only
|
|
three arguments, use static variables for the others. */
|
|
jit_sti_p(&fixup_runstack_base, JIT_RUNSTACK_BASE);
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL2);
|
|
jit_sti_l(&fixup_already_in_place, JIT_R1);
|
|
}
|
|
if (num_rands >= 0) {
|
|
jit_movi_i(JIT_R0, num_rands);
|
|
} else {
|
|
mz_get_local_p(JIT_R0, JIT_LOCAL2);
|
|
}
|
|
/* Since we've overwritten JIT_RUNSTACK, if this is not shared
|
|
code, and if this is 3m, then the runstack no longer
|
|
has a pointer to the closure for this code. To ensure that
|
|
an appropriate return point exists, jump to static code
|
|
for the rest. (This is the slow path, anyway.) */
|
|
__END_SHORT_JUMPS__(num_rands < 100);
|
|
if (direct_native > 1) {
|
|
(void)jit_jmpi(finish_tail_call_fixup_code);
|
|
} else {
|
|
(void)jit_jmpi(finish_tail_call_code);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_finish_tail_call(mz_jit_state *jitter, int direct_native)
|
|
{
|
|
mz_prepare(3);
|
|
CHECK_LIMIT();
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R0);
|
|
jit_pusharg_p(JIT_V1);
|
|
if (direct_native > 1) { /* => some_args_already_in_place */
|
|
(void)mz_finish(_scheme_tail_apply_from_native_fixup_args);
|
|
} else {
|
|
(void)mz_finish(_scheme_tail_apply_from_native);
|
|
}
|
|
CHECK_LIMIT();
|
|
/* Return: */
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_direct_prim_non_tail_call(mz_jit_state *jitter, int num_rands, int multi_ok, int pop_and_jump)
|
|
{
|
|
/* See generate_prim_non_tail_call for assumptions. */
|
|
|
|
if (pop_and_jump) {
|
|
mz_prolog(JIT_R1);
|
|
}
|
|
|
|
if (num_rands == 1) {
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
|
|
jit_movi_i(JIT_R1, num_rands);
|
|
mz_prepare(2); /* a prim takes 3 args, but a NONCM prim ignores the 3rd */
|
|
CHECK_LIMIT();
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
mz_finishr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
/* No need to check for multi values or tail-call, because
|
|
we only use this for noncm primitives. */
|
|
|
|
if (num_rands == 1) {
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
|
|
if (pop_and_jump) {
|
|
mz_epilog(JIT_V1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_retry_call(mz_jit_state *jitter, int num_rands, int multi_ok, GC_CAN_IGNORE jit_insn *reftop)
|
|
/* If num_rands < 0, original argc is in V1, and we should
|
|
pop argc arguments off runstack before pushing more.
|
|
This function is called with short jumps enabled. */
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *refloop;
|
|
|
|
if (!reftop) {
|
|
reftop = shared_non_tail_retry_code[multi_ok ? 1 : 0];
|
|
}
|
|
|
|
/* Get new argc: */
|
|
(void)jit_ldi_p(JIT_R1, &scheme_current_thread);
|
|
jit_ldxi_l(JIT_R2, JIT_R1, &((Scheme_Thread *)0x0)->ku.apply.tail_num_rands);
|
|
if (num_rands >= 0) {
|
|
jit_movi_l(JIT_V1, 0);
|
|
}
|
|
/* Thread is in R1. New argc is in R2. Old argc to cancel is in V1. */
|
|
|
|
/* Enough room on runstack? */
|
|
jit_ldi_p(JIT_R0, &MZ_RUNSTACK_START);
|
|
jit_subr_ul(JIT_R0, JIT_RUNSTACK, JIT_R0); /* R0 is space left (in bytes) */
|
|
jit_subr_l(JIT_R2, JIT_R2, JIT_V1);
|
|
jit_lshi_l(JIT_R2, JIT_R2, JIT_LOG_WORD_SIZE);
|
|
ref = jit_bltr_ul(jit_forward(), JIT_R0, JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* Yes, there's enough room. Adjust the runstack. */
|
|
jit_subr_l(JIT_RUNSTACK, JIT_RUNSTACK, JIT_R2);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
|
|
/* Copy arguments to runstack, then jump to reftop. */
|
|
jit_ldxi_l(JIT_R2, JIT_R1, &((Scheme_Thread *)0x0)->ku.apply.tail_num_rands);
|
|
jit_ldxi_l(JIT_V1, JIT_R1, &((Scheme_Thread *)0x0)->ku.apply.tail_rands);
|
|
jit_lshi_l(JIT_R2, JIT_R2, JIT_LOG_WORD_SIZE);
|
|
CHECK_LIMIT();
|
|
refloop = _jit.x.pc;
|
|
ref2 = jit_blei_l(jit_forward(), JIT_R2, 0);
|
|
jit_subi_l(JIT_R2, JIT_R2, JIT_WORD_SIZE);
|
|
jit_ldxr_p(JIT_R0, JIT_V1, JIT_R2);
|
|
jit_stxr_p(JIT_R2, JIT_RUNSTACK, JIT_R0);
|
|
(void)jit_jmpi(refloop);
|
|
CHECK_LIMIT();
|
|
|
|
/* R1 is still the thread.
|
|
Put procedure and argc in place, then jump to apply: */
|
|
mz_patch_branch(ref2);
|
|
jit_ldxi_l(JIT_V1, JIT_R1, &((Scheme_Thread *)0x0)->ku.apply.tail_rator);
|
|
jit_ldxi_l(JIT_R0, JIT_R1, &((Scheme_Thread *)0x0)->ku.apply.tail_num_rands);
|
|
__END_SHORT_JUMPS__(1);
|
|
(void)jit_jmpi(reftop);
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
/* Slow path; restore R0 to SCHEME_TAIL_CALL_WAITING */
|
|
mz_patch_branch(ref);
|
|
jit_movi_l(JIT_R0, SCHEME_TAIL_CALL_WAITING);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_clear_previous_args(mz_jit_state *jitter, int num_rands)
|
|
{
|
|
if (num_rands >= 0) {
|
|
int i;
|
|
for (i = 0; i < num_rands; i++) {
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_RUNSTACK);
|
|
CHECK_LIMIT();
|
|
}
|
|
} else {
|
|
/* covered by generate_clear_slow_previous_args */
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int generate_clear_slow_previous_args(mz_jit_state *jitter)
|
|
{
|
|
CHECK_LIMIT();
|
|
mz_prepare(2);
|
|
jit_pusharg_p(JIT_R0);
|
|
jit_pusharg_l(JIT_V1);
|
|
mz_finish(clear_runstack);
|
|
jit_retval(JIT_R0);
|
|
return 1;
|
|
}
|
|
|
|
static int generate_non_tail_call(mz_jit_state *jitter, int num_rands, int direct_native, int need_set_rs,
|
|
int multi_ok, int nontail_self, int pop_and_jump, int is_inlined)
|
|
{
|
|
/* Non-tail call.
|
|
Proc is in V1, args are at RUNSTACK.
|
|
If nontail_self, then R0 has proc pointer, and R2 has max_let_depth.
|
|
If num_rands < 0, then argc is in R0, and need to pop runstack before returning.
|
|
If num_rands == -1, skip prolog. */
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref4, *ref5, *ref6, *ref7, *ref8, *ref9;
|
|
GC_CAN_IGNORE jit_insn *ref10, *reftop = NULL, *refagain;
|
|
#ifndef FUEL_AUTODECEREMENTS
|
|
GC_CAN_IGNORE jit_insn *ref11;
|
|
#endif
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
if (pop_and_jump) {
|
|
if (num_rands != -1) {
|
|
mz_prolog(JIT_R1);
|
|
} else {
|
|
reftop = _jit.x.pc;
|
|
}
|
|
}
|
|
|
|
/* Check for inlined native type */
|
|
if (!direct_native) {
|
|
ref = jit_bmsi_ul(jit_forward(), JIT_V1, 0x1);
|
|
jit_ldr_s(JIT_R1, JIT_V1);
|
|
ref2 = jit_bnei_i(jit_forward(), JIT_R1, scheme_native_closure_type);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
ref = ref2 = NULL;
|
|
}
|
|
|
|
refagain = _jit.x.pc;
|
|
|
|
/* Before inlined native, check max let depth */
|
|
if (!nontail_self) {
|
|
jit_ldxi_p(JIT_R2, JIT_V1, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_i(JIT_R2, JIT_R2, &((Scheme_Native_Closure_Data *)0x0)->max_let_depth);
|
|
}
|
|
jit_ldi_p(JIT_R1, &MZ_RUNSTACK_START);
|
|
jit_subr_ul(JIT_R1, JIT_RUNSTACK, JIT_R1);
|
|
ref4 = jit_bltr_ul(jit_forward(), JIT_R1, JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* Before inlined native, check stack depth: */
|
|
(void)jit_movi_p(JIT_R1, &scheme_jit_stack_boundary); /* assumes USE_STACK_BOUNDARY_VAR */
|
|
jit_ldr_l(JIT_R1, JIT_R1);
|
|
ref9 = jit_bltr_ul(jit_forward(), JIT_STACK, JIT_R1); /* assumes down-growing stack */
|
|
CHECK_LIMIT();
|
|
|
|
#ifndef FUEL_AUTODECEREMENTS
|
|
/* Finally, check for thread swap: */
|
|
(void)jit_movi_p(JIT_R1, &scheme_fuel_counter);
|
|
jit_ldr_i(JIT_R2, JIT_R1);
|
|
ref11 = jit_blei_i(jit_forward(), JIT_R2, 0);
|
|
jit_subi_p(JIT_R2, JIT_R2, 0x1);
|
|
jit_str_i(JIT_R1, JIT_R2);
|
|
#endif
|
|
|
|
/* Fast inlined-native jump ok (proc will check argc, if necessary) */
|
|
{
|
|
jit_insn *refr;
|
|
if (num_rands < 0) {
|
|
/* We need to save argc to manually pop the
|
|
runstack. So move V1 to R2 and move R0 to V1: */
|
|
jit_movr_p(JIT_R2, JIT_V1);
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
}
|
|
refr = jit_patchable_movi_p(JIT_R1, jit_forward());
|
|
jit_shuffle_saved_regs(); /* maybe copies V regsiters to be restored */
|
|
_jit_prolog_again(jitter, 3, JIT_R1); /* saves V registers (or copied V registers) */
|
|
if (num_rands >= 0) {
|
|
if (nontail_self) { jit_movr_p(JIT_R1, JIT_R0); }
|
|
jit_movr_p(JIT_R0, JIT_V1); /* closure */
|
|
if (!nontail_self) {
|
|
/* nontail_self is only enabled when there are no rest args: */
|
|
jit_movi_i(JIT_R1, num_rands); /* argc */
|
|
jit_movr_p(JIT_R2, JIT_RUNSTACK); /* argv */
|
|
}
|
|
jit_addi_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK, WORDS_TO_BYTES(num_rands));
|
|
} else {
|
|
/* R2 is closure, V1 is argc */
|
|
jit_lshi_l(JIT_R1, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addr_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK, JIT_R1);
|
|
jit_movr_p(JIT_R0, JIT_R2); /* closure */
|
|
jit_movr_i(JIT_R1, JIT_V1); /* argc */
|
|
jit_movr_p(JIT_R2, JIT_RUNSTACK); /* argv */
|
|
}
|
|
CHECK_LIMIT();
|
|
mz_push_locals();
|
|
if (!nontail_self) {
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
if (direct_native) {
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->u.tail_code);
|
|
} else {
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
|
|
if (need_set_rs) {
|
|
/* In case arity check fails, need to update runstack now: */
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
}
|
|
jit_jmpr(JIT_V1); /* callee restores (copied) V registers, etc. */
|
|
} else {
|
|
/* self-call function pointer is in R1 */
|
|
jit_jmpr(JIT_R1);
|
|
}
|
|
jit_patch_movi(refr, (_jit.x.pc));
|
|
jit_unshuffle_saved_regs(); /* maybe uncopies V registers */
|
|
/* If num_rands < 0, then V1 has argc */
|
|
}
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
|
|
/* Fast common-case return */
|
|
if (pop_and_jump) {
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(1);
|
|
refc = jit_blei_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
__END_INNER_TINY__(1);
|
|
if (num_rands < 0) {
|
|
/* At this point, argc must be in V1 */
|
|
jit_lshi_l(JIT_R1, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addr_p(JIT_RUNSTACK, JIT_RUNSTACK, JIT_R1);
|
|
}
|
|
if (pop_and_jump) {
|
|
mz_epilog(JIT_V1);
|
|
}
|
|
__START_INNER_TINY__(1);
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(1);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (!multi_ok) {
|
|
jit_insn *refm;
|
|
__END_SHORT_JUMPS__(1);
|
|
refm = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
mz_patch_branch_at(refm, bad_result_arity_code);
|
|
__START_SHORT_JUMPS__(1);
|
|
}
|
|
ref6 = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_TAIL_CALL_WAITING);
|
|
generate_clear_previous_args(jitter, num_rands);
|
|
CHECK_LIMIT();
|
|
if (pop_and_jump) {
|
|
/* Expects argc in V1 if num_rands < 0: */
|
|
generate_retry_call(jitter, num_rands, multi_ok, reftop);
|
|
}
|
|
CHECK_LIMIT();
|
|
if (need_set_rs) {
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
if (num_rands < 0) {
|
|
generate_clear_slow_previous_args(jitter);
|
|
CHECK_LIMIT();
|
|
}
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
if (multi_ok) {
|
|
(void)mz_finish(scheme_force_value_same_mark);
|
|
} else {
|
|
(void)mz_finish(scheme_force_one_value_same_mark);
|
|
}
|
|
ref5 = jit_jmpi(jit_forward());
|
|
CHECK_LIMIT();
|
|
|
|
/* Maybe it's a prim? */
|
|
if (!direct_native) {
|
|
mz_patch_branch(ref2);
|
|
ref2 = jit_bnei_i(jit_forward(), JIT_R1, scheme_prim_type);
|
|
/* It's a prim. Arity check... fast path when exactly equal to min, only: */
|
|
jit_ldxi_i(JIT_R2, JIT_V1, &((Scheme_Primitive_Proc *)0x0)->mina);
|
|
if (num_rands >= 0) {
|
|
ref7 = jit_bnei_i(jit_forward(), JIT_R2, num_rands);
|
|
} else {
|
|
ref7 = jit_bner_i(jit_forward(), JIT_R2, JIT_R0);
|
|
}
|
|
/* Fast prim application */
|
|
jit_ldxi_p(JIT_R1, JIT_V1, &((Scheme_Primitive_Proc *)0x0)->prim_val);
|
|
if (need_set_rs) {
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_V1);
|
|
if (num_rands < 0) { jit_movr_p(JIT_V1, JIT_R0); } /* save argc to manually pop runstack */
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R2);
|
|
(void)mz_finishr(JIT_R1);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
if (!multi_ok) {
|
|
jit_insn *refm;
|
|
__END_SHORT_JUMPS__(1);
|
|
refm = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
mz_patch_branch_at(refm, bad_result_arity_code);
|
|
__START_SHORT_JUMPS__(1);
|
|
}
|
|
ref10 = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_TAIL_CALL_WAITING);
|
|
generate_clear_previous_args(jitter, num_rands);
|
|
CHECK_LIMIT();
|
|
if (pop_and_jump) {
|
|
/* Expects argc in V1 if num_rands < 0: */
|
|
generate_retry_call(jitter, num_rands, multi_ok, reftop);
|
|
}
|
|
CHECK_LIMIT();
|
|
if (num_rands < 0) {
|
|
generate_clear_slow_previous_args(jitter);
|
|
CHECK_LIMIT();
|
|
}
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
if (multi_ok) {
|
|
(void)mz_finish(scheme_force_value_same_mark);
|
|
} else {
|
|
(void)mz_finish(scheme_force_one_value_same_mark);
|
|
}
|
|
CHECK_LIMIT();
|
|
ref8 = jit_jmpi(jit_forward());
|
|
|
|
/* Check for simple applicable struct wrapper */
|
|
if (!is_inlined && (num_rands >= 0)) {
|
|
mz_patch_branch(ref2);
|
|
ref2 = generate_proc_struct_retry(jitter, num_rands, refagain);
|
|
CHECK_LIMIT();
|
|
}
|
|
} else {
|
|
ref2 = ref7 = ref8 = ref10 = NULL;
|
|
}
|
|
|
|
/* The slow way: */
|
|
if (!direct_native) {
|
|
mz_patch_branch(ref);
|
|
mz_patch_branch(ref2);
|
|
mz_patch_branch(ref7);
|
|
}
|
|
mz_patch_branch(ref4);
|
|
mz_patch_branch(ref9);
|
|
#ifndef FUEL_AUTODECEREMENTS
|
|
mz_patch_branch(ref11);
|
|
#endif
|
|
if (need_set_rs) {
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
}
|
|
if (num_rands >= 0) {
|
|
jit_movi_i(JIT_R0, num_rands);
|
|
}
|
|
mz_prepare(3);
|
|
CHECK_LIMIT();
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R0);
|
|
jit_pusharg_p(JIT_V1);
|
|
if (num_rands < 0) { jit_movr_p(JIT_V1, JIT_R0); } /* save argc to manually pop runstack */
|
|
if (multi_ok) {
|
|
(void)mz_finish(_scheme_apply_multi_from_native);
|
|
} else {
|
|
(void)mz_finish(_scheme_apply_from_native);
|
|
}
|
|
CHECK_LIMIT();
|
|
mz_patch_ucbranch(ref5);
|
|
if (!direct_native) {
|
|
mz_patch_ucbranch(ref8);
|
|
}
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
mz_patch_branch(ref6);
|
|
if (!direct_native) {
|
|
mz_patch_branch(ref10);
|
|
}
|
|
/* Note: same return code is above for faster common-case return */
|
|
if (num_rands < 0) {
|
|
/* At this point, argc must be in V1 */
|
|
jit_lshi_l(JIT_R1, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addr_p(JIT_RUNSTACK, JIT_RUNSTACK, JIT_R1);
|
|
}
|
|
if (pop_and_jump) {
|
|
mz_epilog(JIT_V1);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_self_tail_call(Scheme_Object *rator, mz_jit_state *jitter, int num_rands, jit_insn *slow_code,
|
|
int args_already_in_place)
|
|
{
|
|
jit_insn *refslow;
|
|
int i, jmp_tiny, jmp_short;
|
|
int closure_size = jitter->self_closure_size;
|
|
|
|
/* Last argument is in R0 */
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
closure_size += 1; /* Skip procedure pointer, too */
|
|
#endif
|
|
|
|
jmp_tiny = num_rands < 5;
|
|
jmp_short = num_rands < 100;
|
|
|
|
__START_TINY_OR_SHORT_JUMPS__(jmp_tiny, jmp_short);
|
|
|
|
/* Check for thread swap: */
|
|
(void)jit_movi_p(JIT_R1, &scheme_fuel_counter);
|
|
jit_ldr_i(JIT_R2, JIT_R1);
|
|
refslow = jit_blei_i(jit_forward(), JIT_R2, 0);
|
|
#ifndef FUEL_AUTODECEREMENTS
|
|
jit_subi_p(JIT_R2, JIT_R2, 0x1);
|
|
jit_str_i(JIT_R1, JIT_R2);
|
|
#endif
|
|
|
|
__END_TINY_OR_SHORT_JUMPS__(jmp_tiny, jmp_short);
|
|
|
|
/* Copy args to runstack after closure data: */
|
|
jit_subi_p(JIT_R2, JIT_RUNSTACK_BASE, WORDS_TO_BYTES(num_rands + closure_size + args_already_in_place));
|
|
if (num_rands) {
|
|
jit_stxi_p(WORDS_TO_BYTES(num_rands - 1 + closure_size + args_already_in_place), JIT_R2, JIT_R0);
|
|
for (i = num_rands - 1; i--; ) {
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(i));
|
|
jit_stxi_p(WORDS_TO_BYTES(i + closure_size + args_already_in_place), JIT_R2, JIT_R1);
|
|
CHECK_LIMIT();
|
|
}
|
|
}
|
|
jit_movr_p(JIT_RUNSTACK, JIT_R2);
|
|
|
|
/* Now jump: */
|
|
(void)jit_jmpi(jitter->self_restart_code);
|
|
CHECK_LIMIT();
|
|
|
|
/* Slow path: */
|
|
__START_TINY_OR_SHORT_JUMPS__(jmp_tiny, jmp_short);
|
|
mz_patch_branch(refslow);
|
|
__END_TINY_OR_SHORT_JUMPS__(jmp_tiny, jmp_short);
|
|
|
|
if (args_already_in_place) {
|
|
jit_movi_l(JIT_R2, args_already_in_place);
|
|
mz_set_local_p(JIT_R2, JIT_LOCAL2);
|
|
}
|
|
|
|
jit_stxi_p(WORDS_TO_BYTES(num_rands - 1), JIT_RUNSTACK, JIT_R0);
|
|
generate(rator, jitter, 0, 0, JIT_V1);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
(void)jit_jmpi(slow_code);
|
|
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
int num_rands;
|
|
mz_jit_state *old_jitter;
|
|
int multi_ok;
|
|
int is_tail;
|
|
int direct_prim, direct_native, nontail_self;
|
|
} Generate_Call_Data;
|
|
|
|
static void register_sub_func(mz_jit_state *jitter, void *code, Scheme_Object *protocol)
|
|
{
|
|
void *code_end;
|
|
|
|
code_end = jit_get_ip().ptr;
|
|
if (jitter->retain_start)
|
|
add_symbol((unsigned long)code, (unsigned long)code_end - 1, protocol, 0);
|
|
}
|
|
|
|
static void register_helper_func(mz_jit_state *jitter, void *code)
|
|
{
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
/* Null indicates that there's no function name to report, but the
|
|
stack should be unwound manually using the JJIT-generated convention. */
|
|
register_sub_func(jitter, code, scheme_null);
|
|
#endif
|
|
}
|
|
|
|
int do_generate_shared_call(mz_jit_state *jitter, void *_data)
|
|
{
|
|
Generate_Call_Data *data = (Generate_Call_Data *)_data;
|
|
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jitter->js.jitl.nbArgs = data->old_jitter->js.jitl.nbArgs;
|
|
#endif
|
|
|
|
if (data->is_tail) {
|
|
int ok;
|
|
void *code;
|
|
|
|
code = jit_get_ip().ptr;
|
|
|
|
if (data->direct_prim)
|
|
ok = generate_direct_prim_tail_call(jitter, data->num_rands);
|
|
else
|
|
ok = generate_tail_call(jitter, data->num_rands, data->direct_native, 1, 0);
|
|
|
|
register_helper_func(jitter, code);
|
|
|
|
return ok;
|
|
} else {
|
|
int ok;
|
|
void *code;
|
|
|
|
code = jit_get_ip().ptr;
|
|
|
|
if (data->direct_prim)
|
|
ok = generate_direct_prim_non_tail_call(jitter, data->num_rands, data->multi_ok, 1);
|
|
else
|
|
ok = generate_non_tail_call(jitter, data->num_rands, data->direct_native, 1, data->multi_ok, data->nontail_self, 1, 0);
|
|
|
|
register_sub_func(jitter, code, scheme_false);
|
|
|
|
return ok;
|
|
}
|
|
}
|
|
|
|
static void *generate_shared_call(int num_rands, mz_jit_state *old_jitter, int multi_ok, int is_tail,
|
|
int direct_prim, int direct_native, int nontail_self)
|
|
{
|
|
Generate_Call_Data data;
|
|
|
|
data.num_rands = num_rands;
|
|
data.old_jitter = old_jitter;
|
|
data.multi_ok = multi_ok;
|
|
data.is_tail = is_tail;
|
|
data.direct_prim = direct_prim;
|
|
data.direct_native = direct_native;
|
|
data.nontail_self = nontail_self;
|
|
|
|
return generate_one(old_jitter, do_generate_shared_call, &data, 0, NULL, NULL);
|
|
}
|
|
|
|
static void ensure_retry_available(mz_jit_state *jitter, int multi_ok)
|
|
{
|
|
int mo = multi_ok ? 1 : 0;
|
|
if (!shared_non_tail_retry_code[mo]) {
|
|
void *code;
|
|
code = generate_shared_call(-1, jitter, multi_ok, 0, 0, 0, 0);
|
|
shared_non_tail_retry_code[mo] = code;
|
|
}
|
|
}
|
|
|
|
static int is_a_procedure(Scheme_Object *v, mz_jit_state *jitter)
|
|
{
|
|
Scheme_Type t;
|
|
|
|
if (SCHEME_PROCP(v))
|
|
return 1;
|
|
|
|
t = SCHEME_TYPE(v);
|
|
if (SAME_TYPE(t, scheme_closure_type)
|
|
|| SAME_TYPE(t, scheme_unclosed_procedure_type))
|
|
return 1;
|
|
else if (SAME_TYPE(t, scheme_syntax_type)) {
|
|
return (SCHEME_PINT_VAL(v) == CASE_LAMBDA_EXPD);
|
|
} else if (SAME_TYPE(t, scheme_local_type)) {
|
|
int flags;
|
|
return mz_is_closure(jitter, SCHEME_LOCAL_POS(v), -1, &flags);
|
|
} else if (t == scheme_toplevel_type) {
|
|
if (SCHEME_TOPLEVEL_FLAGS(v) & SCHEME_TOPLEVEL_CONST) {
|
|
if (jitter->nc) {
|
|
Scheme_Object *p;
|
|
|
|
p = extract_global(v, jitter->nc);
|
|
p = ((Scheme_Bucket *)p)->val;
|
|
return SAME_TYPE(SCHEME_TYPE(p), scheme_native_closure_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_nontail_self_setup(mz_jit_state *jitter)
|
|
{
|
|
void *pp, **pd;
|
|
pp = jit_patchable_movi_p(JIT_R2, jit_forward());
|
|
pd = (void **)scheme_malloc(2 * sizeof(void *));
|
|
pd[0] = pp;
|
|
pd[1] = jitter->patch_depth;
|
|
jitter->patch_depth = pd;
|
|
(void)jit_patchable_movi_p(JIT_R0, jitter->self_nontail_code);
|
|
#ifdef JIT_PRECISE_GC
|
|
if (jitter->closure_self_on_runstack) {
|
|
/* Get this closure's pointer from the run stack */
|
|
int depth = jitter->depth + jitter->extra_pushed - 1;
|
|
jit_ldxi_p(JIT_V1, JIT_RUNSTACK, WORDS_TO_BYTES(depth));
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int can_direct_native(Scheme_Object *p, int num_rands, long *extract_case)
|
|
{
|
|
if (SAME_TYPE(SCHEME_TYPE(p), scheme_native_closure_type)) {
|
|
if (((Scheme_Native_Closure *)p)->code->closure_size < 0) {
|
|
/* case-lambda */
|
|
int cnt, i;
|
|
mzshort *arities;
|
|
|
|
cnt = ((Scheme_Native_Closure *)p)->code->closure_size;
|
|
cnt = -(cnt + 1);
|
|
arities = ((Scheme_Native_Closure *)p)->code->u.arities;
|
|
for (i = 0; i < cnt; i++) {
|
|
if (arities[i] == num_rands) {
|
|
*extract_case = (long)&((Scheme_Native_Closure *)0x0)->vals[i];
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
/* not a case-lambda... */
|
|
if (scheme_native_arity_check(p, num_rands)
|
|
/* If it also accepts num_rands + 1, then it has a vararg,
|
|
so don't try direct_native. */
|
|
&& !scheme_native_arity_check(p, num_rands + 1)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_app(Scheme_App_Rec *app, Scheme_Object **alt_rands, int num_rands,
|
|
mz_jit_state *jitter, int is_tail, int multi_ok, int no_call)
|
|
/* de-sync'd ok
|
|
If no_call is 2, then rator is not necssarily evaluated.
|
|
If no_call is 1, then rator is left in V1 and arguments are on runstack. */
|
|
{
|
|
int i, offset, need_safety = 0;
|
|
int direct_prim = 0, need_non_tail = 0, direct_native = 0, direct_self = 0, nontail_self = 0;
|
|
int proc_already_in_place = 0;
|
|
Scheme_Object *rator, *v, *arg;
|
|
int reorder_ok = 0;
|
|
int args_already_in_place = 0;
|
|
long extract_case = 0; /* when direct_native, non-0 => offset to extract case-lambda case */
|
|
START_JIT_DATA();
|
|
|
|
rator = (alt_rands ? alt_rands[0] : app->args[0]);
|
|
|
|
if (SCHEME_PRIMP(rator)) {
|
|
if ((num_rands >= ((Scheme_Primitive_Proc *)rator)->mina)
|
|
&& ((num_rands <= ((Scheme_Primitive_Proc *)rator)->mu.maxa)
|
|
|| (((Scheme_Primitive_Proc *)rator)->mina < 0))
|
|
&& (is_noncm(rator, jitter, 0, 0)
|
|
/* It's also ok to directly call `values' if multiple values are ok: */
|
|
|| (multi_ok && SAME_OBJ(rator, scheme_values_func))))
|
|
direct_prim = 1;
|
|
} else {
|
|
Scheme_Type t;
|
|
t = SCHEME_TYPE(rator);
|
|
if ((t == scheme_local_type) && ok_to_move_local(rator)) {
|
|
/* We can re-order evaluation of the rator. */
|
|
reorder_ok = 1;
|
|
|
|
/* Call to known native, or even known self? */
|
|
{
|
|
int pos, flags;
|
|
pos = SCHEME_LOCAL_POS(rator) - num_rands;
|
|
if (mz_is_closure(jitter, pos, num_rands, &flags)) {
|
|
direct_native = 1;
|
|
if ((pos == jitter->self_pos)
|
|
&& (num_rands < MAX_SHARED_CALL_RANDS)) {
|
|
if (is_tail)
|
|
direct_self = 1;
|
|
else if (jitter->self_nontail_code)
|
|
nontail_self = 1;
|
|
}
|
|
}
|
|
}
|
|
} else if (t == scheme_toplevel_type) {
|
|
if (SCHEME_TOPLEVEL_FLAGS(rator) & SCHEME_TOPLEVEL_CONST) {
|
|
/* We can re-order evaluation of the rator. */
|
|
reorder_ok = 1;
|
|
|
|
if (jitter->nc) {
|
|
Scheme_Object *p;
|
|
|
|
p = extract_global(rator, jitter->nc);
|
|
p = ((Scheme_Bucket *)p)->val;
|
|
if (can_direct_native(p, num_rands, &extract_case)) {
|
|
direct_native = 1;
|
|
|
|
if ((SCHEME_TOPLEVEL_POS(rator) == jitter->self_toplevel_pos)
|
|
&& (num_rands < MAX_SHARED_CALL_RANDS)) {
|
|
if (is_tail)
|
|
direct_self = 1;
|
|
else if (jitter->self_nontail_code)
|
|
nontail_self = 1;
|
|
}
|
|
}
|
|
}
|
|
} else if (jitter->nc) {
|
|
Scheme_Object *p;
|
|
|
|
p = extract_global(rator, jitter->nc);
|
|
if (((Scheme_Bucket_With_Flags *)p)->flags & GLOB_IS_CONSISTENT) {
|
|
if (can_direct_native(((Scheme_Bucket *)p)->val, num_rands, &extract_case))
|
|
direct_native = 1;
|
|
}
|
|
}
|
|
} else if (SAME_TYPE(t, scheme_closure_type)) {
|
|
Scheme_Closure_Data *data;
|
|
data = ((Scheme_Closure *)rator)->code;
|
|
if ((data->num_params == num_rands)
|
|
&& !(SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST)) {
|
|
direct_native = 1;
|
|
|
|
if (SAME_OBJ(data->u.jit_clone, jitter->self_data)
|
|
&& (num_rands < MAX_SHARED_CALL_RANDS)) {
|
|
if (is_tail)
|
|
direct_self = 1;
|
|
else if (jitter->self_nontail_code)
|
|
nontail_self = 1;
|
|
}
|
|
}
|
|
reorder_ok = 1;
|
|
} else if (t > _scheme_values_types_) {
|
|
/* We can re-order evaluation of the rator. */
|
|
reorder_ok = 1;
|
|
}
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
if (jitter->closure_self_on_runstack) {
|
|
/* We can get this closure's pointer back from the Scheme stack. */
|
|
if (nontail_self)
|
|
direct_self = 1;
|
|
}
|
|
#endif
|
|
|
|
if (direct_self)
|
|
reorder_ok = 0; /* superceded by direct_self */
|
|
}
|
|
|
|
/* Direct native tail with same number of args as just received? */
|
|
if (direct_native && is_tail && num_rands
|
|
&& (num_rands == jitter->self_data->num_params)
|
|
&& !(SCHEME_CLOSURE_DATA_FLAGS(jitter->self_data) & CLOS_HAS_REST)) {
|
|
/* Check whether the actual arguments refer to Scheme-stack
|
|
locations that will be filled with argument values; that
|
|
is, check how many arguments are already in place for
|
|
the call. */
|
|
mz_runstack_skipped(jitter, num_rands);
|
|
for (i = 0; i < num_rands; i++) {
|
|
v = (alt_rands ? alt_rands[i+1] : app->args[i+1]);
|
|
if (SAME_TYPE(SCHEME_TYPE(v), scheme_local_type)
|
|
&& !(SCHEME_LOCAL_FLAGS(v) & SCHEME_LOCAL_OTHER_CLEARS)) {
|
|
int pos;
|
|
pos = mz_remap(SCHEME_LOCAL_POS(v));
|
|
if (pos == (jitter->depth + args_already_in_place))
|
|
args_already_in_place++;
|
|
else
|
|
break;
|
|
} else
|
|
break;
|
|
}
|
|
mz_runstack_unskipped(jitter, num_rands);
|
|
if (args_already_in_place) {
|
|
direct_native = 2;
|
|
mz_runstack_skipped(jitter, args_already_in_place);
|
|
num_rands -= args_already_in_place;
|
|
}
|
|
}
|
|
|
|
if (num_rands) {
|
|
if (!direct_prim || (num_rands > 1)) {
|
|
mz_rs_dec(num_rands);
|
|
need_safety = num_rands;
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, num_rands);
|
|
} else {
|
|
mz_runstack_skipped(jitter, 1);
|
|
}
|
|
}
|
|
|
|
for (i = num_rands + args_already_in_place + 1; i--; ) {
|
|
v = (alt_rands ? alt_rands[i] : app->args[i]);
|
|
if (!is_simple(v, INIT_SIMPLE_DEPTH, 1, jitter, 0)) {
|
|
need_non_tail = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (need_non_tail) {
|
|
offset = generate_non_tail_mark_pos_prefix(jitter);
|
|
CHECK_LIMIT();
|
|
} else
|
|
offset = 0;
|
|
|
|
if (!direct_prim && !reorder_ok && !direct_self) {
|
|
if (need_safety && !is_non_gc(rator, INIT_SIMPLE_DEPTH)) {
|
|
stack_safety(jitter, need_safety, offset);
|
|
CHECK_LIMIT();
|
|
need_safety = 0;
|
|
}
|
|
|
|
generate_non_tail(rator, jitter, 0, !need_non_tail); /* sync'd after args below */
|
|
CHECK_LIMIT();
|
|
|
|
if (num_rands) {
|
|
/* Save rator where GC can see it */
|
|
Scheme_Type t;
|
|
t = SCHEME_TYPE((alt_rands
|
|
? alt_rands[1+args_already_in_place]
|
|
: app->args[1+args_already_in_place]));
|
|
if ((num_rands == 1) && (SAME_TYPE(scheme_local_type, t)
|
|
|| (t >= _scheme_values_types_))) {
|
|
/* App of something complex to a local variable. We
|
|
can move the proc directly to V1. */
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
proc_already_in_place = 1;
|
|
} else {
|
|
mz_rs_stxi(num_rands - 1 + offset, JIT_R0);
|
|
if (need_safety)
|
|
need_safety--;
|
|
}
|
|
} else {
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
}
|
|
}
|
|
/* not sync'd...*/
|
|
|
|
for (i = 0; i < num_rands; i++) {
|
|
PAUSE_JIT_DATA();
|
|
arg = (alt_rands
|
|
? alt_rands[i+1+args_already_in_place]
|
|
: app->args[i+1+args_already_in_place]);
|
|
if (need_safety && !is_non_gc(arg, INIT_SIMPLE_DEPTH)) {
|
|
stack_safety(jitter, need_safety - i, offset + i);
|
|
CHECK_LIMIT();
|
|
need_safety = 0;
|
|
}
|
|
generate_non_tail(arg, jitter, 0, !need_non_tail); /* sync'd below */
|
|
RESUME_JIT_DATA();
|
|
CHECK_LIMIT();
|
|
if ((i == num_rands - 1) && !direct_prim && !reorder_ok && !direct_self && !proc_already_in_place) {
|
|
/* Move rator back to register: */
|
|
mz_rs_ldxi(JIT_V1, i + offset);
|
|
}
|
|
if ((!direct_prim || (num_rands > 1))
|
|
&& (!direct_self || !is_tail || no_call || (i + 1 < num_rands))) {
|
|
mz_rs_stxi(i + offset, JIT_R0);
|
|
}
|
|
}
|
|
/* not sync'd... */
|
|
|
|
if (need_non_tail) {
|
|
/* Uses JIT_R2: */
|
|
generate_non_tail_mark_pos_suffix(jitter);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (direct_prim) {
|
|
if (!no_call) {
|
|
(void)jit_movi_p(JIT_V1, ((Scheme_Primitive_Proc *)rator)->prim_val);
|
|
if (num_rands == 1) {
|
|
mz_runstack_unskipped(jitter, 1);
|
|
} else {
|
|
mz_rs_sync();
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
}
|
|
LOG_IT(("direct: %s\n", ((Scheme_Primitive_Proc *)rator)->name));
|
|
}
|
|
}
|
|
|
|
if (reorder_ok) {
|
|
if (no_call < 2) {
|
|
generate(rator, jitter, 0, 0, JIT_V1); /* sync'd below, or not */
|
|
}
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (!no_call)
|
|
mz_rs_sync();
|
|
|
|
END_JIT_DATA(20);
|
|
|
|
if (direct_prim || direct_native || direct_self || nontail_self)
|
|
scheme_direct_call_count++;
|
|
else
|
|
scheme_indirect_call_count++;
|
|
|
|
if (direct_native && extract_case) {
|
|
/* extract case from case-lambda */
|
|
jit_ldxi_p(JIT_V1, JIT_V1, extract_case);
|
|
}
|
|
|
|
if (no_call) {
|
|
/* leave actual call to inlining code */
|
|
} else if (!(direct_self && is_tail)
|
|
&& (num_rands >= MAX_SHARED_CALL_RANDS)) {
|
|
LOG_IT(("<-many args\n"));
|
|
if (is_tail) {
|
|
if (direct_prim) {
|
|
generate_direct_prim_tail_call(jitter, num_rands);
|
|
} else {
|
|
if (args_already_in_place) {
|
|
jit_movi_l(JIT_R2, args_already_in_place);
|
|
mz_set_local_p(JIT_R2, JIT_LOCAL2);
|
|
}
|
|
generate_tail_call(jitter, num_rands, direct_native, jitter->need_set_rs, 1);
|
|
}
|
|
} else {
|
|
if (direct_prim)
|
|
generate_direct_prim_non_tail_call(jitter, num_rands, multi_ok, 0);
|
|
else {
|
|
if (nontail_self) {
|
|
generate_nontail_self_setup(jitter);
|
|
}
|
|
generate_non_tail_call(jitter, num_rands, direct_native, jitter->need_set_rs, multi_ok, nontail_self, 0, 1);
|
|
}
|
|
}
|
|
} else {
|
|
/* Jump to code to implement a tail call for num_rands arguments */
|
|
void *code;
|
|
int dp = (direct_prim ? 1 : (direct_native ? (1 + direct_native + (nontail_self ? 1 : 0)) : 0));
|
|
if (is_tail) {
|
|
if (!shared_tail_code[dp][num_rands]) {
|
|
code = generate_shared_call(num_rands, jitter, multi_ok, is_tail, direct_prim, direct_native, 0);
|
|
shared_tail_code[dp][num_rands] = code;
|
|
}
|
|
code = shared_tail_code[dp][num_rands];
|
|
if (direct_self) {
|
|
LOG_IT(("<-self\n"));
|
|
generate_self_tail_call(rator, jitter, num_rands, code, args_already_in_place);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
LOG_IT(("<-tail\n"));
|
|
if (args_already_in_place) {
|
|
jit_movi_l(JIT_R2, args_already_in_place);
|
|
mz_set_local_p(JIT_R2, JIT_LOCAL2);
|
|
}
|
|
(void)jit_jmpi(code);
|
|
}
|
|
} else {
|
|
int mo = (multi_ok ? 1 : 0);
|
|
|
|
if (!shared_non_tail_code[dp][num_rands][mo]) {
|
|
ensure_retry_available(jitter, multi_ok);
|
|
code = generate_shared_call(num_rands, jitter, multi_ok, is_tail, direct_prim, direct_native, nontail_self);
|
|
shared_non_tail_code[dp][num_rands][mo] = code;
|
|
}
|
|
LOG_IT(("<-non-tail %d %d %d\n", dp, num_rands, mo));
|
|
code = shared_non_tail_code[dp][num_rands][mo];
|
|
|
|
if (nontail_self) {
|
|
generate_nontail_self_setup(jitter);
|
|
}
|
|
|
|
(void)jit_calli(code);
|
|
|
|
if (direct_prim) {
|
|
if (num_rands == 1) {
|
|
/* Popped single argument after return of prim: */
|
|
jitter->need_set_rs = 1;
|
|
} else {
|
|
/* Runstack is up-to-date: */
|
|
jitter->need_set_rs = 0;
|
|
}
|
|
} else {
|
|
/* Otherwise, we may have called native code, which may have left
|
|
the runstack register out of sync with scheme_current_runstack. */
|
|
jitter->need_set_rs = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
END_JIT_DATA(need_non_tail ? 22 : 4);
|
|
|
|
return is_tail ? 2 : 1;
|
|
}
|
|
|
|
static jit_insn *generate_arith_slow_path(mz_jit_state *jitter, Scheme_Object *rator,
|
|
jit_insn **_ref, jit_insn **_ref4,
|
|
jit_insn **for_branch,
|
|
int orig_args, int reversed, int arith, int use_v, int v)
|
|
/* *_ref is place to set for where to jump (for true case, if for_branch) after completing;
|
|
*_ref4 is place to set for where to jump for false if for_branch;
|
|
result is place to jump to start slow path if fixnum attempt fails */
|
|
{
|
|
jit_insn *ref, *ref4, *refslow;
|
|
|
|
refslow = _jit.x.pc;
|
|
|
|
(void)jit_movi_p(JIT_R2, ((Scheme_Primitive_Proc *)rator)->prim_val);
|
|
if (for_branch) {
|
|
ref4 = jit_patchable_movi_p(JIT_V1, jit_forward());
|
|
mz_set_local_p(JIT_V1, JIT_LOCAL2);
|
|
} else
|
|
ref4 = NULL;
|
|
ref = jit_patchable_movi_p(JIT_V1, jit_forward());
|
|
|
|
if (orig_args == 1) {
|
|
if (for_branch) {
|
|
(void)jit_jmpi(call_original_unary_arith_for_branch_code);
|
|
} else {
|
|
(void)jit_jmpi(call_original_unary_arith_code);
|
|
}
|
|
} else {
|
|
if (use_v) {
|
|
(void)jit_movi_p(JIT_R1, scheme_make_integer(v));
|
|
reversed = !reversed;
|
|
}
|
|
|
|
if (for_branch) {
|
|
if (reversed) {
|
|
(void)jit_jmpi(call_original_binary_rev_arith_for_branch_code);
|
|
} else {
|
|
(void)jit_jmpi(call_original_binary_arith_for_branch_code);
|
|
}
|
|
} else {
|
|
if (reversed) {
|
|
(void)jit_jmpi(call_original_binary_rev_arith_code);
|
|
} else {
|
|
(void)jit_jmpi(call_original_binary_arith_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
*_ref = ref;
|
|
*_ref4 = ref4;
|
|
|
|
if (arith == 6) {
|
|
/* Add tag back to first arg, just in case. See arithmetic-shift branch to refslow. */
|
|
ref = _jit.x.pc;
|
|
|
|
if (reversed || use_v) {
|
|
jit_ori_l(JIT_R0, JIT_R0, 0x1);
|
|
} else {
|
|
jit_ori_l(JIT_R1, JIT_R1, 0x1);
|
|
}
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
(void)jit_jmpi(refslow);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
return ref;
|
|
} else {
|
|
return refslow;
|
|
}
|
|
}
|
|
|
|
#ifdef SIXTY_FOUR_BIT_INTEGERS
|
|
# define SCHEME_INT_SMALL_ENOUGH(rand2) ((((long)rand2 & 0x7FFFFFFF) == (long)rand2) || (((long)rand2 & 0xFFFFFFFFF8000000) == 0xFFFFFFFFF8000000))
|
|
#else
|
|
# define SCHEME_INT_SMALL_ENOUGH(rand2) 1
|
|
#endif
|
|
|
|
static int can_fast_double(int arith, int cmp, int two_args)
|
|
{
|
|
#ifdef INLINE_FP_OPS
|
|
if ((arith == 1)
|
|
|| (arith == -1)
|
|
|| (arith == 2)
|
|
|| (arith == -2)
|
|
|| (arith == 11))
|
|
return 1;
|
|
#endif
|
|
#ifdef INLINE_FP_COMP
|
|
if (!arith
|
|
|| ((arith == 9) /* min */ && two_args)
|
|
|| ((arith == 10) /* max */ && two_args))
|
|
return 1;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The following FP-generation code is written to work both with a FP
|
|
stack (i387) and normal FP regsiters (everything else), though the
|
|
double-agent operations that end in _fppop() and _fppush(). In
|
|
FP-stack mode, the register names don't actually matter, but the
|
|
pushes and pops much balance. The popping branch operations pop
|
|
both arguments before branching. */
|
|
|
|
#if !defined(MZ_USE_JIT_I386)
|
|
/* Not FP stack, so use normal variants. */
|
|
#define jit_movi_d_fppush(rd,immd) jit_movi_d(rd,immd)
|
|
#define jit_ldi_d_fppush(rd, is) jit_ldi_d(rd, is)
|
|
#define jit_ldr_d_fppush(rd, rs) jit_ldr_d(rd, rs)
|
|
#define jit_ldxi_d_fppush(rd, rs, is) jit_ldxi_d(rd, rs, is)
|
|
#define jit_addr_d_fppop(rd,s1,s2) jit_addr_d(rd,s1,s2)
|
|
#define jit_subr_d_fppop(rd,s1,s2) jit_subr_d(rd,s1,s2)
|
|
#define jit_subrr_d_fppop(rd,s1,s2) jit_subrr_d(rd,s1,s2)
|
|
#define jit_mulr_d_fppop(rd,s1,s2) jit_mulr_d(rd,s1,s2)
|
|
#define jit_divr_d_fppop(rd,s1,s2) jit_divr_d(rd,s1,s2)
|
|
#define jit_divrr_d_fppop(rd,s1,s2) jit_divrr_d(rd,s1,s2)
|
|
#define jit_negr_d_fppop(rd,rs) jit_negr_d(rd,rs)
|
|
#define jit_abs_d_fppop(rd,rs) jit_abs_d(rd,rs)
|
|
#define jit_sti_d_fppop(id, rs) jit_sti_d(id, rs)
|
|
#define jit_stxi_d_fppop(id, rd, rs) jit_stxi_d(id, rd, rs)
|
|
#define jit_bger_d_fppop(d, s1, s2) jit_bger_d(d, s1, s2)
|
|
#define jit_bantiger_d_fppop(d, s1, s2) jit_bantiger_d(d, s1, s2)
|
|
#define jit_bler_d_fppop(d, s1, s2) jit_bler_d(d, s1, s2)
|
|
#define jit_bantiler_d_fppop(d, s1, s2) jit_bantiler_d(d, s1, s2)
|
|
#define jit_bgtr_d_fppop(d, s1, s2) jit_bgtr_d(d, s1, s2)
|
|
#define jit_bantigtr_d_fppop(d, s1, s2) jit_bantigtr_d(d, s1, s2)
|
|
#define jit_bltr_d_fppop(d, s1, s2) jit_bltr_d(d, s1, s2)
|
|
#define jit_bantiltr_d_fppop(d, s1, s2) jit_bantiltr_d(d, s1, s2)
|
|
#define jit_beqr_d_fppop(d, s1, s2) jit_beqr_d(d, s1, s2)
|
|
#define jit_bantieqr_d_fppop(d, s1, s2) jit_bantieqr_d(d, s1, s2)
|
|
#endif
|
|
|
|
static int generate_double_arith(mz_jit_state *jitter, int arith, int cmp, int reversed, int two_args, int second_const,
|
|
jit_insn **_refd, jit_insn **_refdt,
|
|
int branch_short)
|
|
{
|
|
#if defined(INLINE_FP_OPS) || defined(INLINE_FP_COMP)
|
|
GC_CAN_IGNORE jit_insn *ref8, *ref9, *ref10, *refd, *refdt;
|
|
int no_alloc = 0;
|
|
|
|
/* Maybe they're doubles */
|
|
__START_TINY_JUMPS__(1);
|
|
if (two_args) {
|
|
jit_orr_ul(JIT_R2, JIT_R0, JIT_R1);
|
|
ref8 = jit_bmsi_ul(jit_forward(), JIT_R2, 0x1);
|
|
} else
|
|
ref8 = NULL;
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
ref9 = jit_bnei_p(jit_forward(), JIT_R2, scheme_double_type);
|
|
if (two_args) {
|
|
jit_ldxi_s(JIT_R2, JIT_R1, &((Scheme_Object *)0x0)->type);
|
|
ref10 = jit_bnei_p(jit_forward(), JIT_R2, scheme_double_type);
|
|
} else
|
|
ref10 = NULL;
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
if (!two_args && !second_const && ((arith == 2) || ((arith == -2) && reversed))) {
|
|
/* Special case: multiplication by exact 0 */
|
|
(void)jit_movi_p(JIT_R0, scheme_make_integer(0));
|
|
} else {
|
|
/* Yes, they're doubles. */
|
|
jit_ldxi_d_fppush(JIT_FPR1, JIT_R0, &((Scheme_Double *)0x0)->double_val);
|
|
if (two_args) {
|
|
jit_ldxi_d_fppush(JIT_FPR0, JIT_R1, &((Scheme_Double *)0x0)->double_val);
|
|
} else if ((arith == -1) && !second_const && reversed) {
|
|
reversed = 0;
|
|
} else if (arith == 11) {
|
|
/* abs needs no extra number */
|
|
} else {
|
|
double d = second_const;
|
|
jit_movi_d_fppush(JIT_FPR0, d);
|
|
reversed = !reversed;
|
|
cmp = -cmp;
|
|
}
|
|
if (arith) {
|
|
switch (arith) {
|
|
case 1:
|
|
jit_addr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case 2:
|
|
jit_mulr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case -2:
|
|
if (reversed)
|
|
jit_divrr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
else
|
|
jit_divr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case -1:
|
|
{
|
|
if (!two_args && !second_const && !reversed) {
|
|
/* Need a special case to make sure that (- 0.0) => -0.0 */
|
|
jit_negr_d_fppop(JIT_FPR1, JIT_FPR1);
|
|
} else if (reversed)
|
|
jit_subrr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
else
|
|
jit_subr_d_fppop(JIT_FPR1, JIT_FPR0, JIT_FPR1);
|
|
}
|
|
break;
|
|
case 9: /* min */
|
|
case 10: /* max */
|
|
{
|
|
GC_CAN_IGNORE jit_insn *refc, *refn;
|
|
__START_TINY_JUMPS__(1);
|
|
/* If R0 is nan, then copy to R1, ensuring nan result */
|
|
refn = jit_beqr_d(jit_forward(), JIT_FPR1, JIT_FPR1);
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
mz_patch_branch(refn);
|
|
if (arith == 9) {
|
|
refc = jit_bger_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
} else {
|
|
refc = jit_bltr_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
}
|
|
jit_movr_p(JIT_R0, JIT_R1);
|
|
mz_patch_branch(refc);
|
|
__END_TINY_JUMPS__(1);
|
|
no_alloc = 1;
|
|
}
|
|
break;
|
|
case 11: /* abs */
|
|
jit_abs_d_fppop(JIT_FPR1, JIT_FPR1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
if (!no_alloc) {
|
|
#ifdef INLINE_FP_OPS
|
|
# ifdef CAN_INLINE_ALLOC
|
|
inline_alloc(jitter, sizeof(Scheme_Double), scheme_double_type, 0, 0, 1, 0);
|
|
CHECK_LIMIT();
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
(void)jit_stxi_d_fppop(&((Scheme_Double *)0x0)->double_val, JIT_R0, JIT_FPR1);
|
|
# else
|
|
(void)jit_sti_d_fppop(&double_result, JIT_FPR1);
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(0);
|
|
(void)mz_finish(malloc_double);
|
|
jit_retval(JIT_R0);
|
|
# endif
|
|
#endif
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
} else {
|
|
/* The "anti" variants below invert the branch. Unlike the "un"
|
|
variants, the "anti" variants invert the comparison result
|
|
after the layer where +nan.0 always generates false. */
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
switch (cmp) {
|
|
case -2:
|
|
refd = jit_bantiltr_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case -1:
|
|
refd = jit_bantiler_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case 0:
|
|
refd = jit_bantieqr_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case 1:
|
|
refd = jit_bantiger_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
case 2:
|
|
refd = jit_bantigtr_d_fppop(jit_forward(), JIT_FPR0, JIT_FPR1);
|
|
break;
|
|
default:
|
|
refd = NULL;
|
|
break;
|
|
}
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
*_refd = refd;
|
|
}
|
|
}
|
|
|
|
/* Jump to return result or true branch: */
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
refdt = jit_jmpi(jit_forward());
|
|
*_refdt = refdt;
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
/* No, they're not both doubles. */
|
|
__START_TINY_JUMPS__(1);
|
|
if (two_args) {
|
|
mz_patch_branch(ref8);
|
|
mz_patch_branch(ref10);
|
|
}
|
|
mz_patch_branch(ref9);
|
|
__END_TINY_JUMPS__(1);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_arith(mz_jit_state *jitter, Scheme_Object *rator, Scheme_Object *rand, Scheme_Object *rand2,
|
|
int orig_args, int arith, int cmp, int v, jit_insn **for_branch, int branch_short)
|
|
/* needs de-sync */
|
|
/* Either arith is non-zero or it's a cmp; the value of each determines the operation:
|
|
arith = 1 -> + or add1 (if !rand2)
|
|
arith = -1 -> - or sub1
|
|
arith = 2 -> *
|
|
arith = 3 -> bitwise-and
|
|
arith = 4 -> bitwise-ior
|
|
arith = 5 -> bitwise-xor
|
|
arith = 6 -> arithmetic-shift
|
|
arith = 7 -> bitwise-not
|
|
arith = 9 -> min
|
|
arith = 10 -> max
|
|
arith = 11 -> abs
|
|
cmp = 0 -> = or zero?
|
|
cmp = +/-1 -> >=/<=
|
|
cmp = +/-2 -> >/< or positive/negative?
|
|
cmp = 3 -> bitwise-bit-test?
|
|
*/
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4, *refd = NULL, *refdt = NULL, *refslow;
|
|
int skipped, simple_rand, simple_rand2, reversed = 0, has_fixnum_fast = 1;
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)rator)->name));
|
|
|
|
if (rand2) {
|
|
if (SCHEME_INTP(rand2)
|
|
&& SCHEME_INT_SMALL_ENOUGH(rand2)
|
|
&& ((arith != 6)
|
|
|| ((SCHEME_INT_VAL(rand2) <= MAX_TRY_SHIFT)
|
|
&& (SCHEME_INT_VAL(rand2) >= -MAX_TRY_SHIFT)))
|
|
&& ((cmp != 3)
|
|
|| ((SCHEME_INT_VAL(rand2) <= MAX_TRY_SHIFT)
|
|
&& (SCHEME_INT_VAL(rand2) >= 0)))) {
|
|
/* Second is constant, so use constant mode.
|
|
For arithmetic shift, only do this if the constant
|
|
is in range. */
|
|
v = SCHEME_INT_VAL(rand2);
|
|
rand2 = NULL;
|
|
} else if (SCHEME_INTP(rand)
|
|
&& SCHEME_INT_SMALL_ENOUGH(rand)
|
|
&& (arith != 6)
|
|
&& (cmp != 3)) {
|
|
/* First is constant; swap argument order and use constant mode. */
|
|
v = SCHEME_INT_VAL(rand);
|
|
cmp = -cmp;
|
|
rand = rand2;
|
|
rand2 = NULL;
|
|
reversed = 1;
|
|
} else if ((ok_to_move_local(rand2)
|
|
|| SCHEME_INTP(rand2))
|
|
&& !(ok_to_move_local(rand)
|
|
|| SCHEME_INTP(rand))) {
|
|
/* Second expression is side-effect-free, unlike the first;
|
|
swap order and use the fast path for when the first arg is
|
|
side-effect free. */
|
|
Scheme_Object *t = rand2;
|
|
rand2 = rand;
|
|
rand = t;
|
|
cmp = -cmp;
|
|
reversed = 1;
|
|
}
|
|
}
|
|
|
|
if ((arith == -1) && (orig_args == 1) && !v) {
|
|
/* Unary subtract */
|
|
reversed = 1;
|
|
}
|
|
|
|
if (rand2) {
|
|
simple_rand = (ok_to_move_local(rand)
|
|
|| SCHEME_INTP(rand));
|
|
if (!simple_rand)
|
|
simple_rand2 = SAME_TYPE(SCHEME_TYPE(rand2), scheme_local_type);
|
|
else
|
|
simple_rand2 = 0;
|
|
} else {
|
|
simple_rand = 0;
|
|
simple_rand2 = 0;
|
|
}
|
|
|
|
if (rand2 && !simple_rand && !simple_rand2)
|
|
skipped = orig_args - 1;
|
|
else
|
|
skipped = orig_args;
|
|
|
|
mz_runstack_skipped(jitter, skipped);
|
|
|
|
if (rand2 && !simple_rand && !simple_rand2) {
|
|
mz_runstack_skipped(jitter, 1);
|
|
generate_non_tail(rand, jitter, 0, 1); /* sync'd later */
|
|
CHECK_LIMIT();
|
|
mz_runstack_unskipped(jitter, 1);
|
|
mz_rs_dec(1);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, 1);
|
|
mz_rs_str(JIT_R0);
|
|
}
|
|
/* not sync'd... */
|
|
|
|
if (simple_rand2) {
|
|
if (SAME_TYPE(SCHEME_TYPE(rand), scheme_local_type))
|
|
generate(rand, jitter, 0, 0, JIT_R1); /* sync'd below */
|
|
else {
|
|
generate_non_tail(rand, jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
}
|
|
CHECK_LIMIT();
|
|
generate(rand2, jitter, 0, 0, JIT_R0); /* sync'd below */
|
|
} else {
|
|
generate_non_tail(rand2 ? rand2 : rand, jitter, 0, 1); /* sync'd below */
|
|
}
|
|
CHECK_LIMIT();
|
|
/* sync'd in three branches below */
|
|
|
|
if (arith == -2) {
|
|
if (rand2 || (v != 1) || reversed)
|
|
has_fixnum_fast = 0;
|
|
}
|
|
|
|
/* rand2 in R0, and rand in R1 unless it's simple */
|
|
|
|
if (simple_rand || simple_rand2) {
|
|
int pos, va;
|
|
|
|
if (simple_rand && SCHEME_INTP(rand)) {
|
|
(void)jit_movi_p(JIT_R1, rand);
|
|
va = JIT_R0;
|
|
} else {
|
|
if (simple_rand) {
|
|
pos = mz_remap(SCHEME_LOCAL_POS(rand));
|
|
mz_rs_ldxi(JIT_R1, pos);
|
|
}
|
|
/* check both fixnum bits at once by ANDing into R2: */
|
|
jit_andr_ul(JIT_R2, JIT_R0, JIT_R1);
|
|
va = JIT_R2;
|
|
}
|
|
|
|
mz_rs_sync();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
ref2 = jit_bmsi_ul(jit_forward(), va, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
if (!SCHEME_INTP(rand) && can_fast_double(arith, cmp, 1)) {
|
|
/* Maybe they're both doubles... */
|
|
generate_double_arith(jitter, arith, cmp, reversed, 1, 0, &refd, &refdt, branch_short);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (!has_fixnum_fast) {
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
|
|
/* Slow path */
|
|
refslow = generate_arith_slow_path(jitter, rator, &ref, &ref4, for_branch, orig_args, reversed, arith, 0, 0);
|
|
|
|
if (has_fixnum_fast) {
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
CHECK_LIMIT();
|
|
} else if (rand2) {
|
|
/* Move rand result back into R1 */
|
|
mz_rs_ldr(JIT_R1);
|
|
mz_rs_inc(1);
|
|
mz_runstack_popped(jitter, 1);
|
|
|
|
mz_rs_sync();
|
|
|
|
/* check both fixnum bits at once by ANDing into R2: */
|
|
jit_andr_ul(JIT_R2, JIT_R0, JIT_R1);
|
|
__START_TINY_JUMPS__(1);
|
|
ref2 = jit_bmsi_ul(jit_forward(), JIT_R2, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
CHECK_LIMIT();
|
|
|
|
if (can_fast_double(arith, cmp, 1)) {
|
|
/* Maybe they're both doubles... */
|
|
generate_double_arith(jitter, arith, cmp, reversed, 1, 0, &refd, &refdt, branch_short);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (!has_fixnum_fast) {
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
|
|
/* Slow path */
|
|
refslow = generate_arith_slow_path(jitter, rator, &ref, &ref4, for_branch, orig_args, reversed, arith, 0, 0);
|
|
|
|
if (has_fixnum_fast) {
|
|
/* Fixnum branch: */
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
CHECK_LIMIT();
|
|
} else {
|
|
mz_rs_sync();
|
|
/* Only one argument: */
|
|
__START_TINY_JUMPS__(1);
|
|
ref2 = jit_bmsi_ul(jit_forward(), JIT_R0, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
if ((orig_args != 2) /* <- heuristic: we could generate code when an exact argument is
|
|
given, but the extra FP code is probably not worthwhile. */
|
|
&& can_fast_double(arith, cmp, 0)
|
|
/* watch out: divide by 0 is special: */
|
|
&& ((arith != -2) || v || reversed)) {
|
|
/* Maybe it's a double... */
|
|
generate_double_arith(jitter, arith, cmp, reversed, 0, v, &refd, &refdt, branch_short);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (!has_fixnum_fast) {
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
|
|
/* Slow path */
|
|
refslow = generate_arith_slow_path(jitter, rator, &ref, &ref4, for_branch, orig_args, reversed, arith, 1, v);
|
|
|
|
if (has_fixnum_fast) {
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
}
|
|
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, skipped);
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
if (arith) {
|
|
if (rand2) {
|
|
/* First arg is in JIT_R1, second is in JIT_R0 */
|
|
if (arith == 1) {
|
|
jit_andi_ul(JIT_R2, JIT_R1, (~0x1));
|
|
(void)jit_boaddr_l(refslow, JIT_R2, JIT_R0);
|
|
jit_movr_p(JIT_R0, JIT_R2);
|
|
} else if (arith == -1) {
|
|
if (reversed) {
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
(void)jit_bosubr_l(refslow, JIT_R2, JIT_R1);
|
|
} else {
|
|
jit_movr_p(JIT_R2, JIT_R1);
|
|
(void)jit_bosubr_l(refslow, JIT_R2, JIT_R0);
|
|
}
|
|
jit_ori_ul(JIT_R0, JIT_R2, 0x1);
|
|
} else if (arith == 2) {
|
|
jit_andi_ul(JIT_R2, JIT_R1, (~0x1));
|
|
jit_rshi_l(JIT_V1, JIT_R0, 0x1);
|
|
(void)jit_bomulr_l(refslow, JIT_V1, JIT_R2);
|
|
jit_ori_ul(JIT_R0, JIT_V1, 0x1);
|
|
} else if (arith == -2) {
|
|
if (has_fixnum_fast) {
|
|
/* No fast path for fixnum division, yet */
|
|
(void)jit_jmpi(refslow);
|
|
}
|
|
} else if (arith == 3) {
|
|
/* and */
|
|
jit_andr_ul(JIT_R0, JIT_R1, JIT_R0);
|
|
} else if (arith == 4) {
|
|
/* ior */
|
|
jit_orr_ul(JIT_R0, JIT_R1, JIT_R0);
|
|
} else if (arith == 5) {
|
|
/* xor */
|
|
jit_andi_ul(JIT_R0, JIT_R0, (~0x1));
|
|
jit_xorr_ul(JIT_R0, JIT_R1, JIT_R0);
|
|
} else if (arith == 6) {
|
|
/* arithmetic-shift
|
|
This is a lot of code, but if you're using
|
|
arihtmetic-shift, then you probably want it. */
|
|
int v1 = (reversed ? JIT_R0 : JIT_R1);
|
|
int v2 = (reversed ? JIT_R1 : JIT_R0);
|
|
jit_insn *refi, *refc;
|
|
|
|
refi = jit_bgei_l(refslow, v2, (long)scheme_make_integer(0));
|
|
|
|
/* Right shift (always works for a small enough shift) */
|
|
(void)jit_blti_l(refslow, v2, scheme_make_integer(-MAX_TRY_SHIFT));
|
|
jit_notr_l(JIT_V1, v2);
|
|
jit_rshi_l(JIT_V1, JIT_V1, 0x1);
|
|
jit_addi_l(JIT_V1, JIT_V1, 0x1);
|
|
CHECK_LIMIT();
|
|
#ifdef MZ_USE_JIT_I386
|
|
/* Can't shift from _ECX */
|
|
jit_movr_l(JIT_R2, v1);
|
|
jit_rshr_l(JIT_R2, JIT_R2, JIT_V1);
|
|
#else
|
|
jit_rshr_l(JIT_R2, v1, JIT_V1);
|
|
#endif
|
|
jit_ori_l(JIT_R0, JIT_R2, 0x1);
|
|
refc = jit_jmpi(jit_forward());
|
|
CHECK_LIMIT();
|
|
|
|
/* Left shift */
|
|
mz_patch_branch(refi);
|
|
(void)jit_bgti_l(refslow, v2, (long)scheme_make_integer(MAX_TRY_SHIFT));
|
|
jit_rshi_l(JIT_V1, v2, 0x1);
|
|
jit_andi_l(v1, v1, (~0x1));
|
|
#ifdef MZ_USE_JIT_I386
|
|
/* Can't shift from _ECX */
|
|
jit_movr_l(JIT_R2, v1);
|
|
jit_lshr_l(JIT_R2, JIT_R2, JIT_V1);
|
|
#else
|
|
jit_lshr_l(JIT_R2, v1, JIT_V1);
|
|
#endif
|
|
CHECK_LIMIT();
|
|
/* If shifting back right produces a different result, that's overflow... */
|
|
jit_rshr_l(JIT_V1, JIT_R2, JIT_V1);
|
|
/* !! In case we go refslow, it nseed to add back tag to v1 !! */
|
|
(void)jit_bner_p(refslow, JIT_V1, v1);
|
|
/* No overflow. */
|
|
jit_ori_l(JIT_R0, JIT_R2, 0x1);
|
|
|
|
mz_patch_ucbranch(refc);
|
|
} else if (arith == 9) {
|
|
/* min */
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(branch_short);
|
|
refc = jit_bltr_l(jit_forward(), JIT_R0, JIT_R1);
|
|
jit_movr_l(JIT_R0, JIT_R1);
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(branch_short);
|
|
} else if (arith == 10) {
|
|
/* max */
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(branch_short);
|
|
refc = jit_bgtr_l(jit_forward(), JIT_R0, JIT_R1);
|
|
jit_movr_l(JIT_R0, JIT_R1);
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(branch_short);
|
|
}
|
|
} else {
|
|
/* Non-constant arg is in JIT_R0 */
|
|
if (arith == 1) {
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
(void)jit_boaddi_l(refslow, JIT_R2, v << 1);
|
|
jit_movr_p(JIT_R0, JIT_R2);
|
|
} else if (arith == -1) {
|
|
if (reversed) {
|
|
(void)jit_movi_p(JIT_R2, scheme_make_integer(v));
|
|
(void)jit_bosubr_l(refslow, JIT_R2, JIT_R0);
|
|
jit_addi_ul(JIT_R0, JIT_R2, 0x1);
|
|
} else {
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
(void)jit_bosubi_l(refslow, JIT_R2, v << 1);
|
|
jit_movr_p(JIT_R0, JIT_R2);
|
|
}
|
|
} else if (arith == 2) {
|
|
if (v == 1) {
|
|
/* R0 already is the answer */
|
|
} else if (v == 0) {
|
|
(void)jit_movi_p(JIT_R0, scheme_make_integer(0));
|
|
} else {
|
|
(void)jit_movi_p(JIT_R1, scheme_make_integer(v));
|
|
jit_andi_ul(JIT_R2, JIT_R1, (~0x1));
|
|
jit_rshi_l(JIT_V1, JIT_R0, 0x1);
|
|
(void)jit_bomulr_l(refslow, JIT_V1, JIT_R2);
|
|
jit_ori_ul(JIT_R0, JIT_V1, 0x1);
|
|
}
|
|
} else if (arith == -2) {
|
|
if ((v == 1) && !reversed) {
|
|
/* R0 already is the answer */
|
|
} else {
|
|
if (has_fixnum_fast) {
|
|
/* No general fast path for fixnum division, yet */
|
|
(void)jit_movi_p(JIT_R1, scheme_make_integer(v));
|
|
(void)jit_jmpi(refslow);
|
|
}
|
|
}
|
|
} else {
|
|
if (arith == 3) {
|
|
/* and */
|
|
long l = (long)scheme_make_integer(v);
|
|
jit_andi_ul(JIT_R0, JIT_R0, l);
|
|
} else if (arith == 4) {
|
|
/* ior */
|
|
long l = (long)scheme_make_integer(v);
|
|
jit_ori_ul(JIT_R0, JIT_R0, l);
|
|
} else if (arith == 5) {
|
|
/* xor */
|
|
jit_xori_ul(JIT_R0, JIT_R0, v << 1);
|
|
} else if (arith == 6) {
|
|
/* arithmetic-shift */
|
|
/* We only get here when v is between -MAX_TRY_SHIFT and MAX_TRY_SHIFT, inclusive */
|
|
if (v <= 0) {
|
|
jit_rshi_l(JIT_R0, JIT_R0, -v);
|
|
jit_ori_l(JIT_R0, JIT_R0, 0x1);
|
|
} else {
|
|
jit_andi_l(JIT_R0, JIT_R0, (~0x1));
|
|
jit_lshi_l(JIT_R2, JIT_R0, v);
|
|
/* If shifting back right produces a different result, that's overflow... */
|
|
jit_rshi_l(JIT_V1, JIT_R2, v);
|
|
/* !! In case we go refslow, it nseed to add back tag to JIT_R0 !! */
|
|
(void)jit_bner_p(refslow, JIT_V1, JIT_R0);
|
|
/* No overflow. */
|
|
jit_ori_l(JIT_R0, JIT_R2, 0x1);
|
|
}
|
|
} else if (arith == 7) {
|
|
jit_notr_ul(JIT_R0, JIT_R0);
|
|
jit_ori_ul(JIT_R0, JIT_R0, 0x1);
|
|
} else if (arith == 9) {
|
|
/* min */
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(branch_short);
|
|
refc = jit_blti_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
jit_movi_l(JIT_R0, (long)scheme_make_integer(v));
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(branch_short);
|
|
} else if (arith == 10) {
|
|
/* max */
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(branch_short);
|
|
refc = jit_bgti_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
jit_movi_l(JIT_R0, (long)scheme_make_integer(v));
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(branch_short);
|
|
} else if (arith == 11) {
|
|
/* abs */
|
|
jit_insn *refc;
|
|
__START_INNER_TINY__(branch_short);
|
|
refc = jit_bgei_l(jit_forward(), JIT_R0, (long)scheme_make_integer(0));
|
|
__END_INNER_TINY__(branch_short);
|
|
/* watch out for most negative fixnum! */
|
|
(void)jit_beqi_p(refslow, JIT_R0, (void *)(((long)1 << ((8 * JIT_WORD_SIZE) - 1)) | 0x1));
|
|
jit_rshi_l(JIT_R0, JIT_R0, 1);
|
|
jit_movi_l(JIT_R1, 0);
|
|
jit_subr_l(JIT_R0, JIT_R1, JIT_R0);
|
|
jit_lshi_l(JIT_R0, JIT_R0, 1);
|
|
jit_ori_l(JIT_R0, JIT_R0, 0x1);
|
|
__START_INNER_TINY__(branch_short);
|
|
mz_patch_branch(refc);
|
|
__END_INNER_TINY__(branch_short);
|
|
CHECK_LIMIT();
|
|
}
|
|
}
|
|
}
|
|
if (refdt)
|
|
mz_patch_ucbranch(refdt);
|
|
jit_patch_movi(ref, (_jit.x.pc));
|
|
} else {
|
|
/* If second is constant, first arg is in JIT_R0. */
|
|
/* Otherwise, first arg is in JIT_R1, second is in JIT_R0 */
|
|
/* Jump to ref3 to produce false */
|
|
switch (cmp) {
|
|
case -3:
|
|
if (rand2) {
|
|
(void)jit_blti_l(refslow, JIT_R1, 0);
|
|
(void)jit_bgti_l(refslow, JIT_R1, (long)scheme_make_integer(MAX_TRY_SHIFT));
|
|
jit_rshi_l(JIT_R1, JIT_R1, 1);
|
|
jit_addi_l(JIT_V1, JIT_R1, 1);
|
|
jit_movi_l(JIT_R2, 1);
|
|
jit_lshr_l(JIT_R2, JIT_R2, JIT_V1);
|
|
ref3 = jit_bmcr_l(jit_forward(), JIT_R0, JIT_R2);
|
|
} else {
|
|
/* shouldn't get here */
|
|
scheme_signal_error("bitwise-bit-test? constant in wrong position");
|
|
ref3 = NULL;
|
|
}
|
|
break;
|
|
case -2:
|
|
if (rand2) {
|
|
ref3 = jit_bger_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_bgei_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
}
|
|
break;
|
|
case -1:
|
|
if (rand2) {
|
|
ref3 = jit_bgtr_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_bgti_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
}
|
|
break;
|
|
case 0:
|
|
if (rand2) {
|
|
ref3 = jit_bner_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_bnei_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
}
|
|
break;
|
|
case 1:
|
|
if (rand2) {
|
|
ref3 = jit_bltr_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_blti_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
}
|
|
break;
|
|
case 2:
|
|
if (rand2) {
|
|
ref3 = jit_bler_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_blei_l(jit_forward(), JIT_R0, (long)scheme_make_integer(v));
|
|
}
|
|
break;
|
|
default:
|
|
case 3:
|
|
if (rand2) {
|
|
(void)jit_blti_l(refslow, JIT_R0, 0);
|
|
(void)jit_bgti_l(refslow, JIT_R0, (long)scheme_make_integer(MAX_TRY_SHIFT));
|
|
jit_rshi_l(JIT_R0, JIT_R0, 1);
|
|
jit_addi_l(JIT_R0, JIT_R0, 1);
|
|
jit_movi_l(JIT_V1, 1);
|
|
jit_lshr_l(JIT_R0, JIT_V1, JIT_R0);
|
|
ref3 = jit_bmcr_l(jit_forward(), JIT_R1, JIT_R0);
|
|
} else {
|
|
ref3 = jit_bmci_l(jit_forward(), JIT_R0, 1 << (v+1));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (refdt)
|
|
mz_patch_ucbranch(refdt);
|
|
|
|
if (for_branch) {
|
|
for_branch[0] = ref3;
|
|
for_branch[1] = refd;
|
|
for_branch[2] = ref;
|
|
jit_patch_movi(ref4, (_jit.x.pc));
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref3);
|
|
if (refd)
|
|
mz_patch_branch(refd);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
jit_patch_movi(ref, (_jit.x.pc));
|
|
}
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_inlined_constant_test(mz_jit_state *jitter, Scheme_App2_Rec *app,
|
|
Scheme_Object *cnst, Scheme_Object *cnst2,
|
|
jit_insn **for_branch, int branch_short, int need_sync)
|
|
/* de-sync'd ok */
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2;
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)app->rator)->name));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
if (need_sync) mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
if (cnst2) {
|
|
ref2 = mz_beqi_p(jit_forward(), JIT_R0, cnst);
|
|
ref = mz_bnei_p(jit_forward(), JIT_R0, cnst2);
|
|
mz_patch_branch(ref2);
|
|
} else {
|
|
ref = mz_bnei_p(jit_forward(), JIT_R0, cnst);
|
|
}
|
|
|
|
if (for_branch) {
|
|
for_branch[0] = ref;
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_inlined_type_test(mz_jit_state *jitter, Scheme_App2_Rec *app,
|
|
Scheme_Type lo_ty, Scheme_Type hi_ty,
|
|
jit_insn **for_branch, int branch_short, int need_sync)
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4;
|
|
int int_ok;
|
|
|
|
int_ok = ((lo_ty <= scheme_integer_type) && (scheme_integer_type <= hi_ty));
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)app->rator)->name));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
if (need_sync) mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
ref = jit_bmsi_ul(jit_forward(), JIT_R0, 0x1);
|
|
jit_ldxi_s(JIT_R0, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
if (lo_ty == hi_ty) {
|
|
ref3 = jit_bnei_p(jit_forward(), JIT_R0, lo_ty);
|
|
ref4 = NULL;
|
|
} else {
|
|
ref3 = jit_blti_p(jit_forward(), JIT_R0, lo_ty);
|
|
ref4 = jit_bgti_p(jit_forward(), JIT_R0, hi_ty);
|
|
}
|
|
if (int_ok) {
|
|
mz_patch_branch(ref);
|
|
}
|
|
if (for_branch) {
|
|
if (!int_ok) {
|
|
for_branch[0] = ref;
|
|
}
|
|
for_branch[1] = ref3;
|
|
for_branch[3] = ref4;
|
|
} else {
|
|
if ((lo_ty <= scheme_integer_type) && (scheme_integer_type <= hi_ty)) {
|
|
mz_patch_branch(ref);
|
|
}
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
if (!int_ok) {
|
|
mz_patch_branch(ref);
|
|
}
|
|
mz_patch_branch(ref3);
|
|
if (ref4) {
|
|
mz_patch_branch(ref4);
|
|
}
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_inlined_struct_op(int kind, mz_jit_state *jitter,
|
|
Scheme_Object *rator, Scheme_Object *rand, Scheme_Object *rand2,
|
|
jit_insn **for_branch, int branch_short,
|
|
int multi_ok)
|
|
/* de-sync'd ok; for branch, sync'd before */
|
|
{
|
|
LOG_IT(("inlined struct op\n"));
|
|
|
|
if (!rand2) {
|
|
generate_two_args(rator, rand, jitter, 1, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
} else {
|
|
Scheme_Object *args[3];
|
|
args[0] = rator;
|
|
args[1] = rand;
|
|
args[2] = rand2;
|
|
generate_app(NULL, args, 2, jitter, 0, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
jit_movr_p(JIT_R0, JIT_V1);
|
|
mz_rs_ldr(JIT_R1);
|
|
mz_rs_ldxi(JIT_V1, 1);
|
|
mz_rs_inc(2); /* sync'd below */
|
|
mz_runstack_popped(jitter, 2);
|
|
}
|
|
mz_rs_sync();
|
|
|
|
/* R0 is [potential] predicate/getter/setting, R1 is struct.
|
|
V1 is value for setting. */
|
|
|
|
if (for_branch) {
|
|
for_branch[2] = jit_patchable_movi_p(JIT_V1, jit_forward());
|
|
(void)jit_calli(struct_pred_branch_code);
|
|
} else if (kind == 1) {
|
|
if (multi_ok) {
|
|
(void)jit_calli(struct_pred_multi_code);
|
|
} else {
|
|
(void)jit_calli(struct_pred_code);
|
|
}
|
|
} else if (kind == 2) {
|
|
if (multi_ok) {
|
|
(void)jit_calli(struct_get_multi_code);
|
|
} else {
|
|
(void)jit_calli(struct_get_code);
|
|
}
|
|
} else {
|
|
if (multi_ok) {
|
|
(void)jit_calli(struct_set_multi_code);
|
|
} else {
|
|
(void)jit_calli(struct_set_code);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_cons_alloc(mz_jit_state *jitter, int rev, int inline_retry);
|
|
static int generate_vector_alloc(mz_jit_state *jitter, Scheme_Object *rator,
|
|
Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3);
|
|
|
|
static int generate_inlined_unary(mz_jit_state *jitter, Scheme_App2_Rec *app, int is_tail, int multi_ok,
|
|
jit_insn **for_branch, int branch_short, int need_sync)
|
|
/* de-sync's, unless branch */
|
|
{
|
|
Scheme_Object *rator = app->rator;
|
|
|
|
{
|
|
int k;
|
|
k = inlineable_struct_prim(rator, jitter, 1, 1);
|
|
if (k == 1) {
|
|
generate_inlined_struct_op(1, jitter, rator, app->rand, NULL, for_branch, branch_short, multi_ok);
|
|
scheme_direct_call_count++;
|
|
return 1;
|
|
} else if ((k == 2) && !for_branch) {
|
|
generate_inlined_struct_op(2, jitter, rator, app->rand, NULL, for_branch, branch_short, multi_ok);
|
|
scheme_direct_call_count++;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!SCHEME_PRIMP(rator))
|
|
return 0;
|
|
|
|
if (!(SCHEME_PRIM_PROC_FLAGS(rator) & SCHEME_PRIM_IS_UNARY_INLINED))
|
|
return 0;
|
|
|
|
scheme_direct_call_count++;
|
|
|
|
if (IS_NAMED_PRIM(rator, "not")) {
|
|
generate_inlined_constant_test(jitter, app, scheme_false, NULL, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "null?")) {
|
|
generate_inlined_constant_test(jitter, app, scheme_null, NULL, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "pair?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_pair_type, scheme_pair_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "mpair?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_mutable_pair_type, scheme_mutable_pair_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "symbol?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_symbol_type, scheme_symbol_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "syntax?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_stx_type, scheme_stx_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "char?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_char_type, scheme_char_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "boolean?")) {
|
|
generate_inlined_constant_test(jitter, app, scheme_false, scheme_true, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "number?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_integer_type, scheme_complex_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "real?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_integer_type, scheme_double_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "exact-integer?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_integer_type, scheme_bignum_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "fixnum?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_integer_type, scheme_integer_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "inexact-real?")) {
|
|
generate_inlined_type_test(jitter, app, SCHEME_FLOAT_TYPE, scheme_double_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "procedure?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_prim_type, scheme_native_closure_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "vector?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_vector_type, scheme_vector_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "box?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_box_type, scheme_box_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "string?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_char_string_type, scheme_char_string_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bytes?")) {
|
|
generate_inlined_type_test(jitter, app, scheme_byte_string_type, scheme_byte_string_type, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "eof-object?")) {
|
|
generate_inlined_constant_test(jitter, app, scheme_eof, NULL, for_branch, branch_short, need_sync);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "zero?")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 0, 0, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "negative?")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 0, -2, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "positive?")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 0, 2, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "exact-nonnegative-integer?")
|
|
|| IS_NAMED_PRIM(rator, "exact-positive-integer?")) {
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4;
|
|
|
|
LOG_IT(("inlined exact-nonnegative-integer?\n"));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
if (need_sync) mz_rs_sync();
|
|
|
|
/* Jump ahead if it's a fixnum: */
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bmsi_ul(jit_forward(), JIT_R0, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
/* Check for positive bignum: */
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
ref2 = jit_bnei_p(jit_forward(), JIT_R2, scheme_bignum_type);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &MZ_OPT_HASH_KEY(&((Scheme_Stx *)0x0)->iso));
|
|
ref3 = jit_bmci_ul(jit_forward(), JIT_R2, 0x1);
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
/* Ok bignum. Instead of jumping, install the fixnum 1: */
|
|
(void)jit_movi_p(JIT_R0, scheme_make_integer(1));
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
/* Check whether the fixnum is in range: */
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
jit_rshi_l(JIT_R0, JIT_R0, 0x1);
|
|
if (IS_NAMED_PRIM(rator, "exact-nonnegative-integer?")) {
|
|
ref4 = jit_blti_l(jit_forward(), JIT_R0, 0);
|
|
} else {
|
|
ref4 = jit_blei_l(jit_forward(), JIT_R0, 0);
|
|
}
|
|
|
|
/* Ok --- it's in range */
|
|
|
|
if (for_branch) {
|
|
for_branch[0] = ref2;
|
|
for_branch[1] = ref3;
|
|
for_branch[3] = ref4;
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref2);
|
|
mz_patch_branch(ref3);
|
|
mz_patch_branch(ref4);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
return 1;
|
|
} else if (!for_branch) {
|
|
if (IS_NAMED_PRIM(rator, "car")
|
|
|| IS_NAMED_PRIM(rator, "cdr")
|
|
|| IS_NAMED_PRIM(rator, "cadr")
|
|
|| IS_NAMED_PRIM(rator, "cdar")
|
|
|| IS_NAMED_PRIM(rator, "caar")
|
|
|| IS_NAMED_PRIM(rator, "cddr")) {
|
|
# define MAX_LEVELS 2
|
|
GC_CAN_IGNORE jit_insn *reffail = NULL, *ref;
|
|
int steps, i;
|
|
const char *name = ((Scheme_Primitive_Proc *)rator)->name;
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)rator)->name));
|
|
|
|
for (steps = 0; name[steps+1] != 'r'; steps++) {
|
|
}
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_sync_fail_branch();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
|
|
if (steps > 1) {
|
|
jit_movr_p(JIT_R2, JIT_R0); /* save original argument */
|
|
}
|
|
for (i = 0; i < steps; i++) {
|
|
if (!skip_checks) {
|
|
if (!i) {
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
reffail = _jit.x.pc;
|
|
__END_TINY_JUMPS__(1);
|
|
if (steps == 1) {
|
|
if (name[1] == 'a') {
|
|
(void)jit_calli(bad_car_code);
|
|
} else {
|
|
(void)jit_calli(bad_cdr_code);
|
|
}
|
|
} else {
|
|
if (name[1] == 'a') {
|
|
if (name[2] == 'a') {
|
|
(void)jit_calli(bad_caar_code);
|
|
} else {
|
|
(void)jit_calli(bad_cadr_code);
|
|
}
|
|
} else {
|
|
if (name[2] == 'a') {
|
|
(void)jit_calli(bad_cdar_code);
|
|
} else {
|
|
(void)jit_calli(bad_cddr_code);
|
|
}
|
|
}
|
|
}
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
} else {
|
|
(void)jit_bmsi_ul(reffail, JIT_R0, 0x1);
|
|
}
|
|
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R1, scheme_pair_type);
|
|
} else {
|
|
reffail = NULL;
|
|
}
|
|
if (name[steps - i] == 'a') {
|
|
(void)jit_ldxi_p(JIT_R0, JIT_R0, &((Scheme_Simple_Object *)0x0)->u.pair_val.car);
|
|
} else {
|
|
(void)jit_ldxi_p(JIT_R0, JIT_R0, &((Scheme_Simple_Object *)0x0)->u.pair_val.cdr);
|
|
}
|
|
VALIDATE_RESULT(JIT_R0);
|
|
CHECK_LIMIT();
|
|
}
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "mcar")
|
|
|| IS_NAMED_PRIM(rator, "mcdr")) {
|
|
GC_CAN_IGNORE jit_insn *reffail = NULL, *ref;
|
|
const char *name = ((Scheme_Primitive_Proc *)rator)->name;
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)rator)->name));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_sync_fail_branch();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
reffail = _jit.x.pc;
|
|
__END_TINY_JUMPS__(1);
|
|
if (name[2] == 'a') {
|
|
(void)jit_calli(bad_mcar_code);
|
|
} else {
|
|
(void)jit_calli(bad_mcdr_code);
|
|
}
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R1, scheme_mutable_pair_type);
|
|
if (name[2] == 'a') {
|
|
(void)jit_ldxi_p(JIT_R0, JIT_R0, &((Scheme_Simple_Object *)0x0)->u.pair_val.car);
|
|
} else {
|
|
(void)jit_ldxi_p(JIT_R0, JIT_R0, &((Scheme_Simple_Object *)0x0)->u.pair_val.cdr);
|
|
}
|
|
VALIDATE_RESULT(JIT_R0);
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "vector-length")) {
|
|
GC_CAN_IGNORE jit_insn *reffail, *ref;
|
|
|
|
LOG_IT(("inlined vector-length\n"));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_sync_fail_branch();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
reffail = _jit.x.pc;
|
|
(void)jit_calli(bad_vector_length_code);
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R1, scheme_vector_type);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
(void)jit_ldxi_i(JIT_R0, JIT_R0, &SCHEME_VEC_SIZE(0x0));
|
|
jit_lshi_l(JIT_R0, JIT_R0, 1);
|
|
jit_ori_l(JIT_R0, JIT_R0, 0x1);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "unbox")) {
|
|
GC_CAN_IGNORE jit_insn *reffail, *ref;
|
|
|
|
LOG_IT(("inlined unbox\n"));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_sync_fail_branch();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
reffail = _jit.x.pc;
|
|
(void)jit_calli(bad_unbox_code);
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R1, scheme_box_type);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
(void)jit_ldxi_p(JIT_R0, JIT_R0, &SCHEME_BOX_VAL(0x0));
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "syntax-e")) {
|
|
LOG_IT(("inlined syntax-e\n"));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_sync();
|
|
|
|
(void)jit_calli(syntax_e_code);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "add1")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 1, 0, 1, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "sub1")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, -1, 0, 1, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "-")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, -1, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "abs")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 11, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bitwise-not")) {
|
|
generate_arith(jitter, rator, app->rand, NULL, 1, 7, 0, 9, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "vector-immutable")
|
|
|| IS_NAMED_PRIM(rator, "vector")) {
|
|
return generate_vector_alloc(jitter, rator, NULL, app, NULL);
|
|
} else if (IS_NAMED_PRIM(rator, "list*")) {
|
|
/* on a single argument, `list*' is identity */
|
|
mz_runstack_skipped(jitter, 1);
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
mz_runstack_unskipped(jitter, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "list")) {
|
|
mz_runstack_skipped(jitter, 1);
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
mz_runstack_unskipped(jitter, 1);
|
|
(void)jit_movi_p(JIT_R1, &scheme_null);
|
|
return generate_cons_alloc(jitter, 0, 0);
|
|
} else if (IS_NAMED_PRIM(rator, "box")) {
|
|
mz_runstack_skipped(jitter, 1);
|
|
generate_non_tail(app->rand, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
mz_runstack_unskipped(jitter, 1);
|
|
mz_rs_sync();
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* Inlined alloc */
|
|
(void)jit_movi_p(JIT_R1, NULL); /* needed because R1 is marked during a GC */
|
|
inline_alloc(jitter, sizeof(Scheme_Small_Object), scheme_box_type, 0, 1, 0, 0);
|
|
CHECK_LIMIT();
|
|
|
|
jit_stxi_p((long)&SCHEME_BOX_VAL(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R0);
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
#else
|
|
/* Non-inlined */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_box);
|
|
jit_retval(JIT_R0);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!for_branch) {
|
|
scheme_console_printf("Inlining expected.\n");
|
|
abort();
|
|
}
|
|
|
|
--scheme_direct_call_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_two_args(Scheme_Object *rand1, Scheme_Object *rand2, mz_jit_state *jitter,
|
|
int order_matters, int skipped)
|
|
/* de-sync's rs.
|
|
Results go into R0 and R1. If !order_matters, and if only the
|
|
second is simple, then the arguments will be in reverse order. */
|
|
{
|
|
int simple1, simple2, direction = 1;
|
|
|
|
simple1 = is_constant_and_avoids_r1(rand1);
|
|
simple2 = is_constant_and_avoids_r1(rand2);
|
|
|
|
if (!simple1) {
|
|
if (simple2) {
|
|
mz_runstack_skipped(jitter, skipped);
|
|
|
|
generate_non_tail(rand1, jitter, 0, 1); /* no sync... */
|
|
CHECK_LIMIT();
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
|
|
generate(rand2, jitter, 0, 0, JIT_R0); /* no sync... */
|
|
CHECK_LIMIT();
|
|
|
|
if (order_matters) {
|
|
/* Swap arguments: */
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
jit_movr_p(JIT_R0, JIT_R1);
|
|
jit_movr_p(JIT_R1, JIT_R2);
|
|
} else
|
|
direction = -1;
|
|
|
|
mz_runstack_unskipped(jitter, skipped);
|
|
} else {
|
|
mz_runstack_skipped(jitter, skipped);
|
|
generate_non_tail(rand1, jitter, 0, 1); /* no sync... */
|
|
CHECK_LIMIT();
|
|
mz_runstack_unskipped(jitter, skipped);
|
|
|
|
mz_rs_dec(1);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, 1);
|
|
mz_rs_str(JIT_R0);
|
|
mz_runstack_skipped(jitter, skipped-1);
|
|
|
|
generate_non_tail(rand2, jitter, 0, 1); /* no sync... */
|
|
CHECK_LIMIT();
|
|
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
mz_rs_ldr(JIT_R0);
|
|
|
|
mz_runstack_unskipped(jitter, skipped-1);
|
|
mz_rs_inc(1);
|
|
mz_runstack_popped(jitter, 1);
|
|
}
|
|
} else {
|
|
mz_runstack_skipped(jitter, skipped);
|
|
|
|
if (simple2) {
|
|
generate(rand2, jitter, 0, 0, JIT_R1); /* no sync... */
|
|
CHECK_LIMIT();
|
|
} else {
|
|
generate_non_tail(rand2, jitter, 0, 1); /* no sync... */
|
|
CHECK_LIMIT();
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
}
|
|
|
|
generate(rand1, jitter, 0, 0, JIT_R0); /* no sync... */
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, skipped);
|
|
}
|
|
|
|
return direction;
|
|
}
|
|
|
|
static int generate_binary_char(mz_jit_state *jitter, Scheme_App3_Rec *app,
|
|
jit_insn **for_branch, int branch_short)
|
|
/* de-sync'd ok */
|
|
{
|
|
Scheme_Object *r1, *r2, *rator = app->rator;
|
|
GC_CAN_IGNORE jit_insn *reffail = NULL, *ref;
|
|
int direct = 0, direction;
|
|
|
|
LOG_IT(("inlined %s\n", ((Scheme_Primitive_Proc *)rator)->name));
|
|
|
|
r1 = app->rand1;
|
|
r2 = app->rand2;
|
|
direction = generate_two_args(r1, r2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
if (!SCHEME_CHARP(r1)) {
|
|
GC_CAN_IGNORE jit_insn *pref;
|
|
pref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
reffail = _jit.x.pc;
|
|
(void)jit_movi_p(JIT_R2, ((Scheme_Primitive_Proc *)rator)->prim_val);
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
if (direction > 0) {
|
|
(void)jit_jmpi(call_original_binary_rev_arith_code);
|
|
} else {
|
|
(void)jit_jmpi(call_original_binary_arith_code);
|
|
}
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
mz_patch_branch(pref);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, (int)&((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_char_type);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
if (!direct)
|
|
direct = (SCHEME_CHAR_VAL(r1) < 256);
|
|
}
|
|
if (!SCHEME_CHARP(r2)) {
|
|
if (!reffail) {
|
|
GC_CAN_IGNORE jit_insn *pref;
|
|
pref = jit_bmci_ul(jit_forward(), JIT_R1, 0x1);
|
|
reffail = _jit.x.pc;
|
|
(void)jit_movi_p(JIT_R2, ((Scheme_Primitive_Proc *)rator)->prim_val);
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
if (direction > 0) {
|
|
(void)jit_jmpi(call_original_binary_rev_arith_code);
|
|
} else {
|
|
(void)jit_jmpi(call_original_binary_arith_code);
|
|
}
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
mz_patch_branch(pref);
|
|
} else {
|
|
(void)jit_bmsi_ul(reffail, JIT_R1, 0x1);
|
|
}
|
|
jit_ldxi_s(JIT_R2, JIT_R1, (int)&((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_char_type);
|
|
CHECK_LIMIT();
|
|
} else {
|
|
if (!direct)
|
|
direct = (SCHEME_CHAR_VAL(r2) < 256);
|
|
}
|
|
|
|
if (!direct) {
|
|
/* Extract character value */
|
|
jit_ldxi_i(JIT_R0, JIT_R0, (int)&SCHEME_CHAR_VAL((Scheme_Object *)0x0));
|
|
jit_ldxi_i(JIT_R1, JIT_R1, (int)&SCHEME_CHAR_VAL((Scheme_Object *)0x0));
|
|
ref = jit_bner_i(jit_forward(), JIT_R0, JIT_R1);
|
|
} else {
|
|
ref = jit_bner_p(jit_forward(), JIT_R0, JIT_R1);
|
|
}
|
|
CHECK_LIMIT();
|
|
if (for_branch) {
|
|
for_branch[0] = ref;
|
|
} else {
|
|
GC_CAN_IGNORE jit_insn *ref2;
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_vector_op(mz_jit_state *jitter, int set, int int_ready)
|
|
/* if int_ready, JIT_R1 has num index and JIT_V1 has pre-computed offset,
|
|
otherwise JIT_R1 has fixnum index */
|
|
{
|
|
GC_CAN_IGNORE jit_insn *ref, *reffail;
|
|
|
|
if (!skip_checks) {
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
reffail = _jit.x.pc;
|
|
if (int_ready) {
|
|
jit_lshi_ul(JIT_R1, JIT_R1, 1);
|
|
jit_ori_l(JIT_R1, JIT_R1, 0x1);
|
|
}
|
|
if (set) {
|
|
(void)jit_calli(vector_set_check_index_code);
|
|
} else {
|
|
(void)jit_calli(vector_ref_check_index_code);
|
|
}
|
|
/* doesn't return */
|
|
CHECK_LIMIT();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
if (!int_ready)
|
|
(void)jit_bmci_ul(reffail, JIT_R1, 0x1);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_vector_type);
|
|
jit_ldxi_i(JIT_R2, JIT_R0, (int)&SCHEME_VEC_SIZE(0x0));
|
|
if (!int_ready) {
|
|
jit_rshi_ul(JIT_V1, JIT_R1, 1);
|
|
(void)jit_bler_ul(reffail, JIT_R2, JIT_V1);
|
|
} else {
|
|
(void)jit_bler_ul(reffail, JIT_R2, JIT_R1);
|
|
}
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
} else {
|
|
if (!int_ready)
|
|
jit_rshi_ul(JIT_V1, JIT_R1, 1);
|
|
}
|
|
|
|
if (!int_ready) {
|
|
jit_lshi_ul(JIT_V1, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_V1, JIT_V1, (int)&SCHEME_VEC_ELS(0x0));
|
|
}
|
|
if (set) {
|
|
jit_ldr_p(JIT_R2, JIT_RUNSTACK);
|
|
jit_stxr_p(JIT_V1, JIT_R0, JIT_R2);
|
|
(void)jit_movi_p(JIT_R0, scheme_void);
|
|
} else {
|
|
jit_ldxr_p(JIT_R0, JIT_R0, JIT_V1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_inlined_binary(mz_jit_state *jitter, Scheme_App3_Rec *app, int is_tail, int multi_ok,
|
|
jit_insn **for_branch, int branch_short, int need_sync)
|
|
/* de-sync's; for branch, sync'd before */
|
|
{
|
|
Scheme_Object *rator = app->rator;
|
|
|
|
if (!for_branch
|
|
&& inlineable_struct_prim(rator, jitter, 2, 2)) {
|
|
generate_inlined_struct_op(3, jitter, rator, app->rand1, app->rand2, for_branch, branch_short, multi_ok);
|
|
scheme_direct_call_count++;
|
|
return 1;
|
|
}
|
|
|
|
|
|
if (!SCHEME_PRIMP(rator))
|
|
return 0;
|
|
|
|
if (!(SCHEME_PRIM_PROC_FLAGS(rator) & SCHEME_PRIM_IS_BINARY_INLINED))
|
|
return 0;
|
|
|
|
scheme_direct_call_count++;
|
|
|
|
if (IS_NAMED_PRIM(rator, "eq?")) {
|
|
Scheme_Object *a1, *a2;
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2;
|
|
|
|
LOG_IT(("inlined eq?\n"));
|
|
|
|
a1 = app->rand1;
|
|
if (SCHEME_TYPE(a1) > _scheme_values_types_) {
|
|
a2 = app->rand2;
|
|
} else {
|
|
a1 = app->rand2;
|
|
a2 = app->rand1;
|
|
}
|
|
|
|
if (SCHEME_TYPE(a1) > _scheme_values_types_) {
|
|
/* Compare to constant: */
|
|
int retptr;
|
|
|
|
mz_runstack_skipped(jitter, 2);
|
|
|
|
generate_non_tail(a2, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
if (need_sync) mz_rs_sync();
|
|
|
|
mz_runstack_unskipped(jitter, 2);
|
|
|
|
if (!SCHEME_INTP(a1)
|
|
&& !SCHEME_FALSEP(a1)
|
|
&& !SCHEME_VOIDP(a1)
|
|
&& !SAME_OBJ(a1, scheme_true))
|
|
retptr = mz_retain(a1);
|
|
else
|
|
retptr = 0;
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
if (retptr) {
|
|
mz_load_retained(jitter, JIT_R1, retptr);
|
|
ref = jit_bner_p(jit_forward(), JIT_R0, JIT_R1);
|
|
} else
|
|
#endif
|
|
ref = mz_bnei_p(jit_forward(), JIT_R0, a1);
|
|
|
|
if (for_branch) {
|
|
for_branch[0] = ref;
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
} else {
|
|
/* Two complex expressions: */
|
|
generate_two_args(a2, a1, jitter, 0, 2);
|
|
CHECK_LIMIT();
|
|
|
|
if (need_sync) mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(branch_short);
|
|
|
|
ref = jit_bner_p(jit_forward(), JIT_R0, JIT_R1);
|
|
if (for_branch) {
|
|
for_branch[0] = ref;
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(ref);
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_patch_ucbranch(ref2);
|
|
}
|
|
|
|
__END_SHORT_JUMPS__(branch_short);
|
|
}
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "=")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, 0, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "<=")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, -1, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "<")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, -2, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, ">=")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, 1, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, ">")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, 2, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bitwise-bit-set?")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 0, 3, 0, for_branch, branch_short);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "char=?")) {
|
|
generate_binary_char(jitter, app, for_branch, branch_short);
|
|
return 1;
|
|
} else if (!for_branch) {
|
|
if (IS_NAMED_PRIM(rator, "+")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 1, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "-")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, -1, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "*")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 2, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "/")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, -2, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "min")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 9, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "max")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 10, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bitwise-and")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 3, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bitwise-ior")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 4, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "bitwise-xor")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 5, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "arithmetic-shift")) {
|
|
generate_arith(jitter, rator, app->rand1, app->rand2, 2, 6, 0, 0, NULL, 1);
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "vector-ref")
|
|
|| IS_NAMED_PRIM(rator, "string-ref")
|
|
|| IS_NAMED_PRIM(rator, "bytes-ref")) {
|
|
int simple;
|
|
int which;
|
|
|
|
if (IS_NAMED_PRIM(rator, "vector-ref"))
|
|
which = 0;
|
|
else if (IS_NAMED_PRIM(rator, "string-ref"))
|
|
which = 1;
|
|
else
|
|
which = 2;
|
|
|
|
LOG_IT(("inlined vector-/string-/bytes-ref\n"));
|
|
|
|
simple = (SCHEME_INTP(app->rand2)
|
|
&& (SCHEME_INT_VAL(app->rand2) >= 0));
|
|
|
|
if (!simple) {
|
|
generate_two_args(app->rand1, app->rand2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_sync();
|
|
|
|
if (!which) {
|
|
/* vector-ref is relatively simple and worth inlining */
|
|
generate_vector_op(jitter, 0, 0);
|
|
CHECK_LIMIT();
|
|
} else if (which == 1) {
|
|
(void)jit_calli(string_ref_check_index_code);
|
|
} else {
|
|
(void)jit_calli(bytes_ref_check_index_code);
|
|
}
|
|
} else {
|
|
long offset;
|
|
|
|
mz_runstack_skipped(jitter, 2);
|
|
|
|
generate_non_tail(app->rand1, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_sync();
|
|
|
|
offset = SCHEME_INT_VAL(app->rand2);
|
|
(void)jit_movi_p(JIT_R1, offset);
|
|
if (!which)
|
|
offset = ((int)&SCHEME_VEC_ELS(0x0)) + WORDS_TO_BYTES(offset);
|
|
else if (which == 1)
|
|
offset = offset << LOG_MZCHAR_SIZE;
|
|
jit_movi_l(JIT_V1, offset);
|
|
if (!which) {
|
|
/* vector-ref is relatively simple and worth inlining */
|
|
generate_vector_op(jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
} else if (which == 1) {
|
|
(void)jit_calli(string_ref_code);
|
|
} else {
|
|
(void)jit_calli(bytes_ref_code);
|
|
}
|
|
|
|
mz_runstack_unskipped(jitter, 2);
|
|
}
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "set-mcar!")
|
|
|| IS_NAMED_PRIM(rator, "set-mcdr!")) {
|
|
GC_CAN_IGNORE jit_insn *reffail, *ref;
|
|
int set_mcar;
|
|
|
|
set_mcar = IS_NAMED_PRIM(rator, "set-mcar!");
|
|
|
|
LOG_IT(("inlined set-mcar!\n"));
|
|
|
|
generate_two_args(app->rand1, app->rand2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync_fail_branch();
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
reffail = _jit.x.pc;
|
|
__END_TINY_JUMPS__(1);
|
|
if (set_mcar)
|
|
(void)jit_calli(bad_set_mcar_code);
|
|
else
|
|
(void)jit_calli(bad_set_mcdr_code);
|
|
__START_TINY_JUMPS__(1);
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_mutable_pair_type);
|
|
__END_TINY_JUMPS__(1);
|
|
CHECK_LIMIT();
|
|
|
|
if (set_mcar)
|
|
(void)jit_stxi_p(&((Scheme_Simple_Object *)0x0)->u.pair_val.car, JIT_R0, JIT_R1);
|
|
else
|
|
(void)jit_stxi_p(&((Scheme_Simple_Object *)0x0)->u.pair_val.cdr, JIT_R0, JIT_R1);
|
|
|
|
(void)jit_movi_p(JIT_R0, scheme_void);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "cons")
|
|
|| IS_NAMED_PRIM(rator, "list*")) {
|
|
LOG_IT(("inlined cons\n"));
|
|
|
|
generate_two_args(app->rand1, app->rand2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
return generate_cons_alloc(jitter, 0, 0);
|
|
} else if (IS_NAMED_PRIM(rator, "mcons")) {
|
|
LOG_IT(("inlined mcons\n"));
|
|
|
|
generate_two_args(app->rand1, app->rand2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* Inlined alloc */
|
|
inline_alloc(jitter, sizeof(Scheme_Simple_Object), scheme_mutable_pair_type, 0, 1, 0, 0);
|
|
CHECK_LIMIT();
|
|
|
|
jit_stxi_p((long)&SCHEME_MCAR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R0);
|
|
jit_stxi_p((long)&SCHEME_MCDR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R1);
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
#else
|
|
/* Non-inlined alloc */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(2);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_make_mutable_pair);
|
|
jit_retval(JIT_R0);
|
|
#endif
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "list")) {
|
|
LOG_IT(("inlined list\n"));
|
|
|
|
generate_two_args(app->rand1, app->rand2, jitter, 1, 2);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_dec(1);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, 1);
|
|
mz_rs_str(JIT_R0);
|
|
(void)jit_movi_p(JIT_R0, &scheme_null);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
generate_cons_alloc(jitter, 1, 0);
|
|
CHECK_LIMIT();
|
|
|
|
jit_ldr_p(JIT_R1, JIT_RUNSTACK);
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
mz_runstack_popped(jitter, 1);
|
|
CHECK_LIMIT();
|
|
|
|
return generate_cons_alloc(jitter, 1, 0);
|
|
} else if (IS_NAMED_PRIM(rator, "vector-immutable")
|
|
|| IS_NAMED_PRIM(rator, "vector")) {
|
|
return generate_vector_alloc(jitter, rator, NULL, NULL, app);
|
|
}
|
|
}
|
|
|
|
if (!for_branch) {
|
|
scheme_console_printf("Inlining expected.\n");
|
|
abort();
|
|
}
|
|
|
|
--scheme_direct_call_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_inlined_nary(mz_jit_state *jitter, Scheme_App_Rec *app, int is_tail, int multi_ok,
|
|
jit_insn **for_branch, int branch_short)
|
|
/* de-sync's; for branch, sync'd before */
|
|
{
|
|
Scheme_Object *rator = app->args[0];
|
|
|
|
if (!SCHEME_PRIMP(rator))
|
|
return 0;
|
|
|
|
if (!(SCHEME_PRIM_PROC_FLAGS(rator) & SCHEME_PRIM_IS_NARY_INLINED))
|
|
return 0;
|
|
|
|
if (app->num_args < ((Scheme_Primitive_Proc *)rator)->mina)
|
|
return 0;
|
|
if (app->num_args > ((Scheme_Primitive_Proc *)rator)->mu.maxa)
|
|
return 0;
|
|
|
|
scheme_direct_call_count++;
|
|
|
|
if (!for_branch) {
|
|
if (IS_NAMED_PRIM(rator, "vector-set!")
|
|
|| IS_NAMED_PRIM(rator, "string-set!")
|
|
|| IS_NAMED_PRIM(rator, "bytes-set!")) {
|
|
int simple, constval;
|
|
int which;
|
|
int pushed;
|
|
|
|
if (IS_NAMED_PRIM(rator, "vector-set!"))
|
|
which = 0;
|
|
else if (IS_NAMED_PRIM(rator, "string-set!"))
|
|
which = 1;
|
|
else
|
|
which = 2;
|
|
|
|
LOG_IT(("inlined vector-set!\n"));
|
|
|
|
simple = (SCHEME_INTP(app->args[2])
|
|
&& (SCHEME_INT_VAL(app->args[2]) >= 0));
|
|
|
|
constval = (SCHEME_TYPE(app->args[3]) > _scheme_values_types_);
|
|
|
|
if (constval && simple)
|
|
pushed = 1;
|
|
else
|
|
pushed = 2;
|
|
|
|
mz_runstack_skipped(jitter, 3 - pushed);
|
|
|
|
if (pushed) {
|
|
mz_rs_dec(pushed);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, pushed);
|
|
stack_safety(jitter, pushed, 0);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
generate_non_tail(app->args[1], jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
if (!constval || !simple) {
|
|
mz_rs_str(JIT_R0);
|
|
} else {
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
}
|
|
|
|
if (!simple) {
|
|
generate_non_tail(app->args[2], jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
if (!constval) {
|
|
mz_rs_stxi(1, JIT_R0);
|
|
} else {
|
|
jit_movr_p(JIT_R1, JIT_R0);
|
|
}
|
|
}
|
|
|
|
generate_non_tail(app->args[3], jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
if (!constval || !simple) {
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
jit_ldr_p(JIT_R0, JIT_RUNSTACK);
|
|
jit_str_p(JIT_RUNSTACK, JIT_R2);
|
|
if (!simple && !constval) {
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
}
|
|
} else {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_movr_p(JIT_R0, JIT_V1);
|
|
}
|
|
|
|
if (!simple) {
|
|
if (!which) {
|
|
/* vector-set! is relatively simple and worth inlining */
|
|
generate_vector_op(jitter, 1, 0);
|
|
CHECK_LIMIT();
|
|
} else if (which == 1) {
|
|
(void)jit_calli(string_set_check_index_code);
|
|
} else {
|
|
(void)jit_calli(bytes_set_check_index_code);
|
|
}
|
|
} else {
|
|
long offset;
|
|
offset = SCHEME_INT_VAL(app->args[2]);
|
|
(void)jit_movi_p(JIT_R1, offset);
|
|
if (!which)
|
|
offset = ((int)&SCHEME_VEC_ELS(0x0)) + WORDS_TO_BYTES(offset);
|
|
else if (which == 1)
|
|
offset = offset << LOG_MZCHAR_SIZE;
|
|
jit_movi_l(JIT_V1, offset);
|
|
if (!which) {
|
|
/* vector-set! is relatively simple and worth inlining */
|
|
generate_vector_op(jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
} else if (which == 1) {
|
|
(void)jit_calli(string_set_code);
|
|
} else {
|
|
(void)jit_calli(bytes_set_code);
|
|
}
|
|
}
|
|
|
|
mz_rs_inc(pushed); /* no sync */
|
|
mz_runstack_popped(jitter, pushed);
|
|
|
|
mz_runstack_unskipped(jitter, 3 - pushed);
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "vector-immutable")
|
|
|| IS_NAMED_PRIM(rator, "vector")) {
|
|
return generate_vector_alloc(jitter, rator, app, NULL, NULL);
|
|
} else if (IS_NAMED_PRIM(rator, "list")
|
|
|| IS_NAMED_PRIM(rator, "list*")) {
|
|
int c = app->num_args;
|
|
int star;
|
|
|
|
star = IS_NAMED_PRIM(rator, "list*");
|
|
|
|
if (c)
|
|
generate_app(app, NULL, c, jitter, 0, 0, 2);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
jit_movi_l(JIT_R2, c);
|
|
if (star)
|
|
(void)jit_calli(make_list_star_code);
|
|
else
|
|
(void)jit_calli(make_list_code);
|
|
#else
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
jit_movi_l(JIT_R0, c);
|
|
mz_prepare(1);
|
|
jit_pusharg_l(JIT_R0);
|
|
if (star)
|
|
(void)mz_finish(make_list_star);
|
|
else
|
|
(void)mz_finish(make_list);
|
|
jit_retval(JIT_R0);
|
|
#endif
|
|
|
|
if (c) {
|
|
mz_rs_inc(c); /* no sync */
|
|
mz_runstack_popped(jitter, c);
|
|
}
|
|
|
|
return 1;
|
|
} else if (IS_NAMED_PRIM(rator, "checked-procedure-check-and-extract")) {
|
|
generate_app(app, NULL, 5, jitter, 0, 0, 2); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
(void)jit_calli(struct_proc_extract_code);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_inc(5);
|
|
mz_runstack_popped(jitter, 5);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!for_branch) {
|
|
scheme_console_printf("Inlining expected.\n");
|
|
abort();
|
|
}
|
|
|
|
--scheme_direct_call_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_cons_alloc(mz_jit_state *jitter, int rev, int inline_retry)
|
|
{
|
|
/* Args should be in R0 (car) and R1 (cdr) */
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* Inlined alloc */
|
|
inline_alloc(jitter, sizeof(Scheme_Simple_Object), scheme_pair_type, 0, 1, 0, inline_retry);
|
|
CHECK_LIMIT();
|
|
|
|
if (rev) {
|
|
jit_stxi_p((long)&SCHEME_CAR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R1);
|
|
jit_stxi_p((long)&SCHEME_CDR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R0);
|
|
} else {
|
|
jit_stxi_p((long)&SCHEME_CAR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R0);
|
|
jit_stxi_p((long)&SCHEME_CDR(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R1);
|
|
}
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
#else
|
|
/* Non-inlined */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(2);
|
|
if (rev) {
|
|
jit_pusharg_p(JIT_R0);
|
|
jit_pusharg_p(JIT_R1);
|
|
} else {
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
}
|
|
(void)mz_finish(scheme_make_pair);
|
|
jit_retval(JIT_R0);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_vector_alloc(mz_jit_state *jitter, Scheme_Object *rator,
|
|
Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3)
|
|
/* de-sync'd ok */
|
|
{
|
|
int imm, i, c;
|
|
|
|
imm = IS_NAMED_PRIM(rator, "vector-immutable");
|
|
|
|
if (app2) {
|
|
mz_runstack_skipped(jitter, 1);
|
|
generate_non_tail(app2->rand, jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
mz_runstack_unskipped(jitter, 1);
|
|
c = 1;
|
|
} else if (app3) {
|
|
generate_two_args(app3->rand1, app3->rand2, jitter, 1, 2); /* sync'd below */
|
|
c = 2;
|
|
} else {
|
|
c = app->num_args;
|
|
if (c)
|
|
generate_app(app, NULL, c, jitter, 0, 0, 2); /* sync'd below */
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_sync();
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* Inlined alloc */
|
|
if (app2)
|
|
(void)jit_movi_p(JIT_R1, NULL); /* needed because R1 is marked during a GC */
|
|
inline_alloc(jitter, sizeof(Scheme_Vector) + ((c - 1) * sizeof(Scheme_Object*)), scheme_vector_type,
|
|
imm, app2 || app3, 0, 0);
|
|
CHECK_LIMIT();
|
|
|
|
if ((c == 2) || (c == 1)) {
|
|
jit_stxi_p((long)&SCHEME_VEC_ELS(0x0)[0] + GC_OBJHEAD_SIZE, JIT_V1, JIT_R0);
|
|
}
|
|
if (c == 2) {
|
|
jit_stxi_p((long)&SCHEME_VEC_ELS(0x0)[1] + GC_OBJHEAD_SIZE, JIT_V1, JIT_R1);
|
|
}
|
|
jit_movi_l(JIT_R1, c);
|
|
jit_stxi_i((long)&SCHEME_VEC_SIZE(0x0) + GC_OBJHEAD_SIZE, JIT_V1, JIT_R1);
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
#else
|
|
/* Non-inlined */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
if (c == 1) {
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
if (imm)
|
|
(void)mz_finish(make_one_element_ivector);
|
|
else
|
|
(void)mz_finish(make_one_element_vector);
|
|
} else if (c == 2) {
|
|
mz_prepare(2);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
if (imm)
|
|
(void)mz_finish(make_two_element_ivector);
|
|
else
|
|
(void)mz_finish(make_two_element_vector);
|
|
} else {
|
|
jit_movi_l(JIT_R1, c);
|
|
mz_prepare(1);
|
|
jit_pusharg_l(JIT_R1);
|
|
if (imm)
|
|
(void)mz_finish(make_ivector);
|
|
else
|
|
(void)mz_finish(make_vector);
|
|
}
|
|
jit_retval(JIT_R0);
|
|
#endif
|
|
|
|
CHECK_LIMIT();
|
|
|
|
if (app) {
|
|
for (i = 0; i < c; i++) {
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(i));
|
|
jit_stxi_p((long)&SCHEME_VEC_ELS(0x0)[i], JIT_R0, JIT_R1);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (c) {
|
|
/* could use mz_rs */
|
|
jit_addi_l(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(c));
|
|
mz_runstack_popped(jitter, c);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int generate_inlined_test(mz_jit_state *jitter, Scheme_Object *obj, int branch_short, jit_insn **refs, int need_sync)
|
|
/* de-sync'd ok; syncs before jump */
|
|
{
|
|
switch (SCHEME_TYPE(obj)) {
|
|
case scheme_application2_type:
|
|
return generate_inlined_unary(jitter, (Scheme_App2_Rec *)obj, 0, 0, refs, branch_short, need_sync);
|
|
case scheme_application3_type:
|
|
return generate_inlined_binary(jitter, (Scheme_App3_Rec *)obj, 0, 0, refs, branch_short, need_sync);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/* lambda codegen */
|
|
/*========================================================================*/
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
static Scheme_Object example_so = { scheme_native_closure_type, 0 };
|
|
#endif
|
|
|
|
static void ensure_closure_native(Scheme_Closure_Data *data,
|
|
Scheme_Native_Closure_Data *case_lam)
|
|
{
|
|
if (!data->u.native_code || SCHEME_FALSEP((Scheme_Object *)data->u.native_code)) {
|
|
Scheme_Native_Closure_Data *code;
|
|
code = scheme_generate_lambda(data, 0, case_lam);
|
|
data->u.native_code = code;
|
|
}
|
|
}
|
|
|
|
static int generate_closure(Scheme_Closure_Data *data,
|
|
mz_jit_state *jitter,
|
|
int immediately_filled)
|
|
{
|
|
Scheme_Native_Closure_Data *code;
|
|
int retptr;
|
|
|
|
ensure_closure_native(data, NULL);
|
|
code = data->u.native_code;
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
if (data->closure_size < 100) {
|
|
int sz;
|
|
long init_word;
|
|
sz = (sizeof(Scheme_Native_Closure)
|
|
+ ((data->closure_size - 1) * sizeof(Scheme_Object *)));
|
|
# ifdef CAN_INLINE_ALLOC
|
|
if (immediately_filled) {
|
|
/* Inlined alloc */
|
|
inline_alloc(jitter, sz, scheme_native_closure_type, 0, 0, 0, 0);
|
|
CHECK_LIMIT();
|
|
jit_addi_p(JIT_R0, JIT_V1, GC_OBJHEAD_SIZE);
|
|
} else
|
|
# endif
|
|
{
|
|
/* Non-inlined alloc */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
|
|
jit_movi_l(JIT_R0, sz);
|
|
mz_prepare(1);
|
|
jit_pusharg_l(JIT_R0);
|
|
if (immediately_filled) {
|
|
(void)mz_finish(GC_malloc_one_small_dirty_tagged);
|
|
} else {
|
|
(void)mz_finish(GC_malloc_one_small_tagged);
|
|
}
|
|
jit_retval(JIT_R0);
|
|
memcpy(&init_word, &example_so, sizeof(long));
|
|
jit_movi_l(JIT_R1, init_word);
|
|
jit_str_l(JIT_R0, JIT_R1);
|
|
}
|
|
retptr = mz_retain(code);
|
|
mz_load_retained(jitter, JIT_R1, retptr);
|
|
jit_stxi_p((long)&((Scheme_Native_Closure *)0x0)->code, JIT_R0, JIT_R1);
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
|
|
mz_prepare(1);
|
|
retptr = mz_retain(code);
|
|
#ifdef JIT_PRECISE_GC
|
|
mz_load_retained(jitter, JIT_R0, retptr);
|
|
#else
|
|
(void)jit_patchable_movi_p(JIT_R0, code); /* !! */
|
|
#endif
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_make_native_closure);
|
|
jit_retval(JIT_R0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int generate_closure_fill(Scheme_Closure_Data *data,
|
|
mz_jit_state *jitter)
|
|
{
|
|
/* Fill in closure */
|
|
int j, size, pos;
|
|
mzshort *map;
|
|
size = data->closure_size;
|
|
map = data->closure_map;
|
|
jit_addi_p(JIT_R2, JIT_R0, &((Scheme_Native_Closure *)0x0)->vals);
|
|
for (j = 0; j < size; j++) {
|
|
CHECK_LIMIT();
|
|
pos = mz_remap(map[j]);
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
|
|
jit_stxi_p(WORDS_TO_BYTES(j), JIT_R2, JIT_R1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
Scheme_Native_Closure_Data *scheme_generate_case_lambda(Scheme_Case_Lambda *c)
|
|
{
|
|
Scheme_Closure_Data *data;
|
|
Scheme_Native_Closure_Data *ndata;
|
|
Scheme_Object *name, *o;
|
|
int max_let_depth = 0, i, count, is_method = 0;
|
|
|
|
ndata = MALLOC_ONE_RT(Scheme_Native_Closure_Data);
|
|
#ifdef MZTAG_REQUIRED
|
|
ndata->iso.so.type = scheme_rt_native_code;
|
|
#endif
|
|
name = c->name;
|
|
if (name && SCHEME_BOXP(name)) {
|
|
name = SCHEME_BOX_VAL(name);
|
|
is_method = 1;
|
|
}
|
|
ndata->u2.name = name;
|
|
count = c->count;
|
|
for (i = 0; i < count; i++) {
|
|
o = c->array[i];
|
|
if (SCHEME_PROCP(o))
|
|
o = (Scheme_Object *)((Scheme_Closure *)o)->code;
|
|
data = (Scheme_Closure_Data *)o;
|
|
ensure_closure_native(data, ndata);
|
|
if (data->u.native_code->max_let_depth > max_let_depth)
|
|
max_let_depth = data->u.native_code->max_let_depth;
|
|
}
|
|
ndata->max_let_depth = max_let_depth;
|
|
ndata->closure_size = -(count + 1); /* Indicates case-lambda */
|
|
|
|
if (count) {
|
|
o = c->array[0];
|
|
if (SCHEME_PROCP(o))
|
|
o = (Scheme_Object *)((Scheme_Closure *)o)->code;
|
|
data = (Scheme_Closure_Data *)o;
|
|
is_method = ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_IS_METHOD) ? 1 : 0);
|
|
}
|
|
|
|
generate_case_lambda(c, ndata, is_method);
|
|
|
|
return ndata;
|
|
}
|
|
|
|
static void ensure_case_closure_native(Scheme_Case_Lambda *c)
|
|
{
|
|
if (!c->native_code || SCHEME_FALSEP((Scheme_Object *)c->native_code)) {
|
|
Scheme_Native_Closure_Data *ndata;
|
|
ndata = scheme_generate_case_lambda(c);
|
|
c->native_code = ndata;
|
|
}
|
|
}
|
|
|
|
static int generate_case_closure(Scheme_Object *obj, mz_jit_state *jitter, int target)
|
|
/* de-sync's */
|
|
{
|
|
Scheme_Case_Lambda *c = (Scheme_Case_Lambda *)obj;
|
|
Scheme_Native_Closure_Data *ndata;
|
|
Scheme_Closure_Data *data;
|
|
Scheme_Object *o;
|
|
int i, offset, count, retptr;
|
|
|
|
ensure_case_closure_native(c);
|
|
ndata = c->native_code;
|
|
|
|
mz_rs_sync();
|
|
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(1);
|
|
retptr = mz_retain(ndata);
|
|
#ifdef JIT_PRECISE_GC
|
|
mz_load_retained(jitter, JIT_R0, retptr);
|
|
#else
|
|
(void)jit_patchable_movi_p(JIT_R0, ndata); /* !! */
|
|
#endif
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_make_native_case_closure);
|
|
jit_retval(JIT_R1);
|
|
CHECK_LIMIT();
|
|
|
|
count = c->count;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
o = c->array[i];
|
|
if (SCHEME_PROCP(o))
|
|
o = (Scheme_Object *)((Scheme_Closure *)o)->code;
|
|
data = (Scheme_Closure_Data *)o;
|
|
mz_pushr_p(JIT_R1);
|
|
mz_rs_sync();
|
|
generate_closure(data, jitter, 1);
|
|
CHECK_LIMIT();
|
|
generate_closure_fill(data, jitter);
|
|
CHECK_LIMIT();
|
|
mz_popr_p(JIT_R1);
|
|
offset = WORDS_TO_BYTES(i) + (unsigned long)&((Scheme_Native_Closure *)0x0)->vals;
|
|
jit_stxi_p(offset, JIT_R1, JIT_R0);
|
|
CHECK_LIMIT();
|
|
}
|
|
jit_movr_p(target, JIT_R1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* non-tail codegen */
|
|
/*========================================================================*/
|
|
|
|
static int generate_non_tail_mark_pos_prefix(mz_jit_state *jitter)
|
|
{
|
|
/* dsync'd ok.
|
|
This part of a non-tail setup can be done once for a sequence
|
|
of non-tail calls. In that case, pass 0 for the `mark_pos_ends'
|
|
argument to generate_non_tail(), so that it can skip this prefix
|
|
and suffix. In case this prefix needs to adjust the runstack,
|
|
the result indicates the number of pushed values. */
|
|
jit_ldi_l(JIT_R2, &scheme_current_cont_mark_pos);
|
|
jit_addi_l(JIT_R2, JIT_R2, 2);
|
|
jit_sti_l(&scheme_current_cont_mark_pos, JIT_R2);
|
|
return 0 /* = number of pushed items */;
|
|
}
|
|
|
|
static void generate_non_tail_mark_pos_suffix(mz_jit_state *jitter)
|
|
/* dsync'd ok */
|
|
{
|
|
jit_ldi_l(JIT_R2, &scheme_current_cont_mark_pos);
|
|
jit_subi_l(JIT_R2, JIT_R2, 2);
|
|
jit_sti_l(&scheme_current_cont_mark_pos, JIT_R2);
|
|
}
|
|
|
|
static int generate_non_tail(Scheme_Object *obj, mz_jit_state *jitter, int multi_ok, int mark_pos_ends)
|
|
/* de-sync's rs */
|
|
{
|
|
if (is_simple(obj, INIT_SIMPLE_DEPTH, 0, jitter, 0)) {
|
|
/* Simple; doesn't change the stack or set marks: */
|
|
int v;
|
|
FOR_LOG(jitter->log_depth++);
|
|
v = generate(obj, jitter, 0, multi_ok, JIT_R0);
|
|
FOR_LOG(--jitter->log_depth);
|
|
return v;
|
|
}
|
|
|
|
{
|
|
int amt, need_ends = 1, using_local1 = 0;
|
|
START_JIT_DATA();
|
|
|
|
/* Might change the stack or marks: */
|
|
if (is_simple(obj, INIT_SIMPLE_DEPTH, 1, jitter, 0)) {
|
|
need_ends = 0;
|
|
} else {
|
|
LOG_IT(("non-tail\n"));
|
|
if (mark_pos_ends)
|
|
generate_non_tail_mark_pos_prefix(jitter);
|
|
jit_ldi_p(JIT_R2, &scheme_current_cont_mark_stack);
|
|
if (!jitter->local1_busy) {
|
|
using_local1 = 1;
|
|
jitter->local1_busy = 1;
|
|
mz_set_local_p(JIT_R2, JIT_LOCAL1);
|
|
} else {
|
|
/* mark stack is an integer... turn it into a pointer */
|
|
jit_lshi_l(JIT_R2, JIT_R2, 0x1);
|
|
jit_ori_l(JIT_R2, JIT_R2, 0x1);
|
|
mz_pushr_p(JIT_R2); /* no sync */
|
|
}
|
|
CHECK_LIMIT();
|
|
}
|
|
mz_runstack_saved(jitter);
|
|
CHECK_LIMIT();
|
|
|
|
PAUSE_JIT_DATA();
|
|
FOR_LOG(jitter->log_depth++);
|
|
|
|
generate(obj, jitter, 0, multi_ok, JIT_R0); /* no sync */
|
|
|
|
FOR_LOG(--jitter->log_depth);
|
|
RESUME_JIT_DATA();
|
|
CHECK_LIMIT();
|
|
|
|
amt = mz_runstack_restored(jitter);
|
|
if (amt) {
|
|
mz_rs_inc(amt);
|
|
}
|
|
if (need_ends) {
|
|
if (using_local1) {
|
|
mz_get_local_p(JIT_R2, JIT_LOCAL1);
|
|
jitter->local1_busy = 0;
|
|
} else {
|
|
mz_popr_p(JIT_R2); /* no sync */
|
|
jit_rshi_l(JIT_R2, JIT_R2, 0x1); /* pointer back to integer */
|
|
}
|
|
jit_sti_p(&scheme_current_cont_mark_stack, JIT_R2);
|
|
if (mark_pos_ends)
|
|
generate_non_tail_mark_pos_suffix(jitter);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
END_JIT_DATA(21);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* expression codegen */
|
|
/*========================================================================*/
|
|
|
|
static int generate_ignored_non_tail(Scheme_Object *obj, mz_jit_state *jitter, int multi_ok, int need_ends)
|
|
/* de-sync's */
|
|
{
|
|
Scheme_Type t = SCHEME_TYPE(obj);
|
|
|
|
if (SAME_TYPE(t, scheme_local_type)
|
|
|| SAME_TYPE(t, scheme_local_unbox_type)) {
|
|
/* Must be here to clear */
|
|
if (SCHEME_LOCAL_FLAGS(obj) & SCHEME_LOCAL_CLEAR_ON_READ) {
|
|
int pos;
|
|
START_JIT_DATA();
|
|
pos = mz_remap(SCHEME_LOCAL_POS(obj));
|
|
LOG_IT(("clear %d\n", pos));
|
|
mz_rs_stxi(pos, JIT_RUNSTACK);
|
|
END_JIT_DATA(2);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return generate_non_tail(obj, jitter, multi_ok, need_ends);
|
|
}
|
|
|
|
static Scheme_Object *generate_k(void)
|
|
{
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
Scheme_Object *obj = (Scheme_Object *)p->ku.k.p1;
|
|
mz_jit_state *jitter = (mz_jit_state *)p->ku.k.p2;
|
|
int v;
|
|
|
|
p->ku.k.p1 = NULL;
|
|
p->ku.k.p2 = NULL;
|
|
|
|
v = generate(obj, jitter, p->ku.k.i1, p->ku.k.i2, p->ku.k.i3);
|
|
|
|
return scheme_make_integer(v);
|
|
}
|
|
|
|
static int generate(Scheme_Object *obj, mz_jit_state *jitter, int is_tail, int multi_ok, int target)
|
|
/* de-sync's; result goes to target */
|
|
{
|
|
Scheme_Type type;
|
|
|
|
#ifdef DO_STACK_CHECK
|
|
# include "mzstkchk.h"
|
|
{
|
|
Scheme_Object *ok;
|
|
Scheme_Thread *p = scheme_current_thread;
|
|
mz_jit_state *jitter_copy;
|
|
|
|
jitter_copy = MALLOC_ONE_RT(mz_jit_state);
|
|
memcpy(jitter_copy, jitter, sizeof(mz_jit_state));
|
|
#ifdef MZTAG_REQUIRED
|
|
jitter_copy->type = scheme_rt_jitter_data;
|
|
#endif
|
|
|
|
p->ku.k.p1 = (void *)obj;
|
|
p->ku.k.p2 = (void *)jitter_copy;
|
|
p->ku.k.i1 = is_tail;
|
|
p->ku.k.i2 = multi_ok;
|
|
p->ku.k.i3 = target;
|
|
|
|
ok = scheme_handle_stack_overflow(generate_k);
|
|
|
|
memcpy(jitter, jitter_copy, sizeof(mz_jit_state));
|
|
|
|
return SCHEME_INT_VAL(ok);
|
|
}
|
|
#endif
|
|
|
|
type = SCHEME_TYPE(obj);
|
|
switch (type) {
|
|
case scheme_toplevel_type:
|
|
{
|
|
int pos;
|
|
/* Other parts of the JIT rely on this code not modifying R1 */
|
|
START_JIT_DATA();
|
|
LOG_IT(("top-level\n"));
|
|
mz_rs_sync_fail_branch();
|
|
/* Load global array: */
|
|
pos = mz_remap(SCHEME_TOPLEVEL_DEPTH(obj));
|
|
mz_rs_ldxi(JIT_R2, pos);
|
|
/* Load bucket: */
|
|
pos = SCHEME_TOPLEVEL_POS(obj);
|
|
jit_ldxi_p(JIT_R2, JIT_R2, WORDS_TO_BYTES(pos));
|
|
/* Extract bucket value */
|
|
jit_ldxi_p(target, JIT_R2, &(SCHEME_VAR_BUCKET(0x0)->val));
|
|
CHECK_LIMIT();
|
|
if (!(SCHEME_TOPLEVEL_FLAGS(obj)
|
|
& (SCHEME_TOPLEVEL_CONST | SCHEME_TOPLEVEL_READY))) {
|
|
/* Is it NULL? */
|
|
(void)jit_beqi_p(unbound_global_code, target, 0);
|
|
}
|
|
END_JIT_DATA(0);
|
|
return 1;
|
|
}
|
|
case scheme_local_type:
|
|
{
|
|
/* Other parts of the JIT rely on this code modifying the target register, only */
|
|
int pos;
|
|
START_JIT_DATA();
|
|
pos = mz_remap(SCHEME_LOCAL_POS(obj));
|
|
LOG_IT(("local %d [%d]\n", pos, SCHEME_LOCAL_FLAGS(obj)));
|
|
if (pos || (mz_CURRENT_STATUS() != mz_RS_R0_HAS_RUNSTACK0)) {
|
|
mz_rs_ldxi(target, pos);
|
|
VALIDATE_RESULT(target);
|
|
} else if (target != JIT_R0) {
|
|
jit_movr_p(target, JIT_R0);
|
|
}
|
|
if (SCHEME_LOCAL_FLAGS(obj) & SCHEME_LOCAL_CLEAR_ON_READ) {
|
|
mz_rs_stxi(pos, JIT_RUNSTACK);
|
|
}
|
|
END_JIT_DATA(2);
|
|
return 1;
|
|
}
|
|
case scheme_local_unbox_type:
|
|
{
|
|
int pos;
|
|
START_JIT_DATA();
|
|
LOG_IT(("unbox local\n"));
|
|
|
|
pos = mz_remap(SCHEME_LOCAL_POS(obj));
|
|
mz_rs_ldxi(JIT_R0, pos);
|
|
jit_ldr_p(target, JIT_R0);
|
|
if (SCHEME_LOCAL_FLAGS(obj) & SCHEME_LOCAL_CLEAR_ON_READ) {
|
|
LOG_IT(("clear-on-read\n"));
|
|
mz_rs_stxi(pos, JIT_RUNSTACK);
|
|
}
|
|
VALIDATE_RESULT(target);
|
|
|
|
END_JIT_DATA(3);
|
|
return 1;
|
|
}
|
|
case scheme_syntax_type:
|
|
{
|
|
int pos;
|
|
pos = SCHEME_PINT_VAL(obj);
|
|
switch (pos) {
|
|
case CASE_LAMBDA_EXPD:
|
|
{
|
|
START_JIT_DATA();
|
|
LOG_IT(("case-lambda\n"));
|
|
/* case-lambda */
|
|
generate_case_closure(SCHEME_IPTR_VAL(obj), jitter, target);
|
|
END_JIT_DATA(5);
|
|
}
|
|
break;
|
|
case BEGIN0_EXPD:
|
|
{
|
|
Scheme_Sequence *seq;
|
|
jit_insn *ref, *ref2;
|
|
int i;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("begin0\n"));
|
|
|
|
seq = (Scheme_Sequence *)SCHEME_IPTR_VAL(obj);
|
|
|
|
/* Evaluate first expression, and for consistency with bytecode
|
|
evaluation, allow multiple values. */
|
|
generate_non_tail(seq->array[0], jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Save value(s) */
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
mz_pushr_p(JIT_V1);
|
|
mz_pushr_p(JIT_V1);
|
|
mz_pushr_p(JIT_V1);
|
|
mz_rs_sync();
|
|
__START_SHORT_JUMPS__(1);
|
|
ref = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
CHECK_LIMIT();
|
|
/* Save away multiple values */
|
|
mz_popr_p(JIT_V1); /* sync'd below... */
|
|
mz_popr_p(JIT_V1);
|
|
mz_popr_p(JIT_V1);
|
|
jit_ldi_p(JIT_R0, &scheme_current_thread);
|
|
CHECK_LIMIT();
|
|
jit_ldxi_l(JIT_V1, JIT_R0, &((Scheme_Thread *)0x0)->ku.multiple.count);
|
|
jit_lshi_l(JIT_V1, JIT_V1, 0x1);
|
|
jit_ori_l(JIT_V1, JIT_V1, 0x1);
|
|
mz_pushr_p(JIT_V1); /* sync'd below */
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Thread *)0x0)->ku.multiple.array);
|
|
mz_pushr_p(JIT_V1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
(void)jit_movi_p(JIT_R1, 0x0);
|
|
mz_pushr_p(JIT_R1); /* pushing 0 indicates that multi-array follows */
|
|
/* If multi-value array is values buffer, zero out values buffer */
|
|
jit_ldxi_p(JIT_R2, JIT_R0, &((Scheme_Thread *)0x0)->values_buffer);
|
|
mz_rs_sync();
|
|
ref2 = jit_bner_p(jit_forward(), JIT_V1, JIT_R2);
|
|
jit_stxi_p(&((Scheme_Thread *)0x0)->values_buffer, JIT_R0, JIT_R1);
|
|
CHECK_LIMIT();
|
|
|
|
/* evaluate remaining expressions */
|
|
mz_patch_branch(ref);
|
|
mz_patch_branch(ref2);
|
|
__END_SHORT_JUMPS__(1);
|
|
for (i = 1; i < seq->count; i++) {
|
|
generate_ignored_non_tail(seq->array[i], jitter, 1, 1); /* sync's below */
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
/* Restore values, if necessary */
|
|
mz_popr_p(JIT_R0);
|
|
mz_popr_p(JIT_R1);
|
|
mz_popr_p(JIT_R2);
|
|
mz_rs_sync();
|
|
CHECK_LIMIT();
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_bnei_p(jit_forward(), JIT_R0, 0x0);
|
|
CHECK_LIMIT();
|
|
jit_ldi_p(JIT_R0, &scheme_current_thread);
|
|
jit_stxi_p(&((Scheme_Thread *)0x0)->ku.multiple.array, JIT_R0, JIT_R1);
|
|
jit_rshi_ul(JIT_R2, JIT_R2, 0x1);
|
|
jit_stxi_l(&((Scheme_Thread *)0x0)->ku.multiple.count, JIT_R0, JIT_R2);
|
|
(void)jit_movi_p(JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
|
|
mz_patch_branch(ref);
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
END_JIT_DATA(6);
|
|
}
|
|
break;
|
|
case SET_EXPD:
|
|
{
|
|
Scheme_Object *p, *v;
|
|
int pos, set_undef;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("set!\n"));
|
|
|
|
p = SCHEME_IPTR_VAL(obj);
|
|
v = SCHEME_CAR(p);
|
|
set_undef = SCHEME_TRUEP(v);
|
|
p = SCHEME_CDR(p);
|
|
v = SCHEME_CAR(p);
|
|
p = SCHEME_CDR(p);
|
|
|
|
generate_non_tail(p, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
/* Load global+stx array: */
|
|
pos = mz_remap(SCHEME_TOPLEVEL_DEPTH(v));
|
|
jit_ldxi_p(JIT_R2, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
|
|
/* Try already-renamed stx: */
|
|
pos = SCHEME_TOPLEVEL_POS(v);
|
|
jit_ldxi_p(JIT_R2, JIT_R2, WORDS_TO_BYTES(pos));
|
|
CHECK_LIMIT();
|
|
|
|
/* R0 has values, R2 has pos */
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(3);
|
|
(void)jit_movi_i(JIT_R1, set_undef);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
jit_pusharg_p(JIT_R2);
|
|
CHECK_LIMIT();
|
|
(void)mz_finish(call_set_global_bucket);
|
|
CHECK_LIMIT();
|
|
(void)jit_movi_p(target, scheme_void);
|
|
END_JIT_DATA(7);
|
|
}
|
|
break;
|
|
case APPVALS_EXPD:
|
|
{
|
|
Scheme_Object *p, *v;
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref5, *refloop;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("appvals\n"));
|
|
|
|
p = SCHEME_IPTR_VAL(obj);
|
|
v = SCHEME_CAR(p);
|
|
p = SCHEME_CDR(p);
|
|
|
|
generate_non_tail(v, jitter, 0, 1);
|
|
CHECK_LIMIT();
|
|
|
|
/* If v is not known to produce a procedure, then check result: */
|
|
if (!is_a_procedure(v, jitter)) {
|
|
mz_rs_sync();
|
|
(void)jit_bmsi_l(bad_app_vals_target, JIT_R0, 0x1);
|
|
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_blti_i(bad_app_vals_target, JIT_R1, scheme_prim_type);
|
|
(void)jit_bgti_i(bad_app_vals_target, JIT_R1, scheme_native_closure_type);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
mz_pushr_p(JIT_R0);
|
|
generate_non_tail(p, jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_popr_p(JIT_V1);
|
|
/* Function is in V1, argument(s) in R0 */
|
|
|
|
mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
ref = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
/* Single-value case: --------------- */
|
|
/* We definitely have stack space for one argument, because we
|
|
just used it for the rator. */
|
|
if (is_tail) {
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK_BASE, WORDS_TO_BYTES(1));
|
|
} else {
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
}
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_movi_l(JIT_R0, 1);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
CHECK_LIMIT();
|
|
|
|
/* Multiple-values case: ------------ */
|
|
mz_patch_branch(ref);
|
|
/* Get new argc: */
|
|
(void)jit_ldi_p(JIT_R1, &scheme_current_thread);
|
|
jit_ldxi_l(JIT_R2, JIT_R1, &((Scheme_Thread *)0x0)->ku.multiple.count);
|
|
/* Enough room on runstack? */
|
|
jit_ldi_p(JIT_R0, &MZ_RUNSTACK_START);
|
|
if (is_tail) {
|
|
jit_subr_ul(JIT_R0, JIT_RUNSTACK_BASE, JIT_R0);
|
|
} else {
|
|
jit_subr_ul(JIT_R0, JIT_RUNSTACK, JIT_R0);
|
|
}
|
|
CHECK_LIMIT();
|
|
/* R0 is space left (in bytes), R2 is argc */
|
|
jit_lshi_l(JIT_R2, JIT_R2, JIT_LOG_WORD_SIZE);
|
|
if (is_tail) {
|
|
__END_SHORT_JUMPS__(1);
|
|
(void)jit_bltr_ul(app_values_tail_slow_code, JIT_R0, JIT_R2);
|
|
__START_SHORT_JUMPS__(1);
|
|
ref5 = 0;
|
|
} else {
|
|
GC_CAN_IGNORE jit_insn *refok;
|
|
refok = jit_bger_ul(jit_forward(), JIT_R0, JIT_R2);
|
|
__END_SHORT_JUMPS__(1);
|
|
if (multi_ok) {
|
|
(void)jit_calli(app_values_multi_slow_code);
|
|
} else {
|
|
(void)jit_calli(app_values_slow_code);
|
|
}
|
|
__START_SHORT_JUMPS__(1);
|
|
ref5 = jit_jmpi(jit_forward());
|
|
mz_patch_branch(refok);
|
|
}
|
|
CHECK_LIMIT();
|
|
if (is_tail) {
|
|
jit_subr_ul(JIT_RUNSTACK, JIT_RUNSTACK_BASE, JIT_R2);
|
|
} else {
|
|
jit_subr_ul(JIT_RUNSTACK, JIT_RUNSTACK, JIT_R2);
|
|
}
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
/* Copy args: */
|
|
jit_ldxi_l(JIT_R1, JIT_R1, &((Scheme_Thread *)0x0)->ku.multiple.array);
|
|
refloop = _jit.x.pc;
|
|
ref3 = jit_blei_l(jit_forward(), JIT_R2, 0);
|
|
jit_subi_l(JIT_R2, JIT_R2, JIT_WORD_SIZE);
|
|
jit_ldxr_p(JIT_R0, JIT_R1, JIT_R2);
|
|
jit_stxr_p(JIT_R2, JIT_RUNSTACK, JIT_R0);
|
|
(void)jit_jmpi(refloop);
|
|
CHECK_LIMIT();
|
|
mz_patch_branch(ref3);
|
|
(void)jit_ldi_p(JIT_R0, &scheme_current_thread);
|
|
jit_ldxi_l(JIT_R0, JIT_R0, &((Scheme_Thread *)0x0)->ku.multiple.count);
|
|
|
|
/* Perform call --------------------- */
|
|
/* Function is in V1, argc in R0, args on RUNSTACK */
|
|
mz_patch_ucbranch(ref2);
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
if (is_tail) {
|
|
if (!shared_tail_argc_code) {
|
|
shared_tail_argc_code = generate_shared_call(-1, jitter, 1, 1, 0, 0, 0);
|
|
}
|
|
mz_set_local_p(JIT_R0, JIT_LOCAL2);
|
|
(void)jit_jmpi(shared_tail_argc_code);
|
|
} else {
|
|
int mo = multi_ok ? 1 : 0;
|
|
void *code;
|
|
if (!shared_non_tail_argc_code[mo]) {
|
|
ensure_retry_available(jitter, multi_ok);
|
|
code = generate_shared_call(-2, jitter, multi_ok, 0, 0, 0, 0);
|
|
shared_non_tail_argc_code[mo] = code;
|
|
}
|
|
code = shared_non_tail_argc_code[mo];
|
|
(void)jit_calli(code);
|
|
/* non-tail code pops args off runstack for us */
|
|
jitter->need_set_rs = 1;
|
|
mz_patch_ucbranch(ref5);
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
END_JIT_DATA(81);
|
|
|
|
if (is_tail)
|
|
return 2;
|
|
}
|
|
break;
|
|
case BOXENV_EXPD:
|
|
{
|
|
Scheme_Object *p, *v;
|
|
int pos;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("boxenv\n"));
|
|
|
|
mz_rs_sync();
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
|
|
p = (Scheme_Object *)SCHEME_IPTR_VAL(obj);
|
|
v = SCHEME_CAR(p);
|
|
pos = mz_remap(SCHEME_INT_VAL(v));
|
|
p = SCHEME_CDR(p);
|
|
|
|
jit_ldxi_p(JIT_R2, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R2);
|
|
(void)mz_finish(scheme_make_envunbox);
|
|
jit_retval(JIT_R0);
|
|
jit_stxi_p(WORDS_TO_BYTES(pos), JIT_RUNSTACK, JIT_R0);
|
|
CHECK_LIMIT();
|
|
|
|
generate(p, jitter, is_tail, multi_ok, target);
|
|
|
|
END_JIT_DATA(8);
|
|
}
|
|
break;
|
|
case REF_EXPD:
|
|
{
|
|
mz_rs_sync();
|
|
|
|
obj = SCHEME_IPTR_VAL(obj);
|
|
|
|
/* Load global array: */
|
|
pos = mz_remap(SCHEME_TOPLEVEL_DEPTH(obj));
|
|
jit_ldxi_p(JIT_R2, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
|
|
/* Load bucket: */
|
|
pos = SCHEME_TOPLEVEL_POS(obj);
|
|
jit_ldxi_p(JIT_R2, JIT_R2, WORDS_TO_BYTES(pos));
|
|
CHECK_LIMIT();
|
|
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R2);
|
|
(void)mz_finish(make_global_ref);
|
|
CHECK_LIMIT();
|
|
jit_retval(target);
|
|
VALIDATE_RESULT(target);
|
|
}
|
|
break;
|
|
case SPLICE_EXPD:
|
|
{
|
|
scheme_signal_error("cannot JIT a top-level splice form");
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
mz_rs_sync();
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
obj = SCHEME_IPTR_VAL(obj);
|
|
(void)jit_patchable_movi_p(JIT_R2, obj); /* !! */
|
|
CHECK_LIMIT();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R2);
|
|
(void)mz_finish(scheme_syntax_executers[pos]);
|
|
CHECK_LIMIT();
|
|
jit_retval(target);
|
|
VALIDATE_RESULT(target);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
case scheme_application_type:
|
|
{
|
|
Scheme_App_Rec *app = (Scheme_App_Rec *)obj;
|
|
int r;
|
|
|
|
LOG_IT(("app %d\n", app->num_args));
|
|
|
|
r = generate_inlined_nary(jitter, app, is_tail, multi_ok, NULL, 1);
|
|
CHECK_LIMIT();
|
|
if (r) {
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
return r;
|
|
}
|
|
|
|
r = generate_app(app, NULL, app->num_args, jitter, is_tail, multi_ok, 0);
|
|
|
|
CHECK_LIMIT();
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
|
|
return r;
|
|
}
|
|
case scheme_application2_type:
|
|
{
|
|
Scheme_App2_Rec *app = (Scheme_App2_Rec *)obj;
|
|
Scheme_Object *args[2];
|
|
int r;
|
|
|
|
r = generate_inlined_unary(jitter, app, is_tail, multi_ok, NULL, 1, 0);
|
|
CHECK_LIMIT();
|
|
if (r) {
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
return r;
|
|
}
|
|
|
|
LOG_IT(("app 2\n"));
|
|
|
|
args[0] = app->rator;
|
|
args[1] = app->rand;
|
|
|
|
r = generate_app(NULL, args, 1, jitter, is_tail, multi_ok, 0);
|
|
|
|
CHECK_LIMIT();
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
|
|
return r;
|
|
}
|
|
case scheme_application3_type:
|
|
{
|
|
Scheme_App3_Rec *app = (Scheme_App3_Rec *)obj;
|
|
Scheme_Object *args[3];
|
|
int r;
|
|
|
|
r = generate_inlined_binary(jitter, app, is_tail, multi_ok, NULL, 1, 0);
|
|
CHECK_LIMIT();
|
|
if (r) {
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
return r;
|
|
}
|
|
|
|
LOG_IT(("app 3\n"));
|
|
|
|
args[0] = app->rator;
|
|
args[1] = app->rand1;
|
|
args[2] = app->rand2;
|
|
|
|
r = generate_app(NULL, args, 2, jitter, is_tail, multi_ok, 0);
|
|
|
|
CHECK_LIMIT();
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
|
|
return r;
|
|
}
|
|
case scheme_sequence_type:
|
|
{
|
|
Scheme_Sequence *seq = (Scheme_Sequence *)obj;
|
|
int cnt = seq->count, i;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("begin\n"));
|
|
|
|
for (i = 0; i < cnt - 1; i++) {
|
|
generate_ignored_non_tail(seq->array[i], jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
END_JIT_DATA(11);
|
|
|
|
return generate(seq->array[cnt - 1], jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_branch_type:
|
|
{
|
|
Scheme_Branch_Rec *branch = (Scheme_Branch_Rec *)obj;
|
|
jit_insn *refs[6], *ref2;
|
|
int nsrs, nsrs1, g1, g2, amt, need_sync;
|
|
#ifdef NEED_LONG_JUMPS
|
|
int then_short_ok, else_short_ok;
|
|
#else
|
|
int then_short_ok = 1;
|
|
#endif
|
|
START_JIT_DATA();
|
|
|
|
#ifdef NEED_LONG_JUMPS
|
|
/* It's possible that the code for a then
|
|
or else branch will be so large that we might
|
|
need a long jump. Conservatively analyze the
|
|
`then' and `else' expressions. */
|
|
then_short_ok = (is_short(branch->tbranch, 32) > 0);
|
|
else_short_ok = (is_short(branch->fbranch, 32) > 0);
|
|
#endif
|
|
|
|
LOG_IT(("if...\n"));
|
|
|
|
/* Places to patch for the false branch: */
|
|
refs[0] = NULL;
|
|
refs[1] = NULL;
|
|
refs[2] = NULL; /* a movi patch, instead of a branch */
|
|
refs[3] = NULL;
|
|
refs[4] = NULL;
|
|
refs[5] = NULL; /* a jmpi instead of a test branch */
|
|
|
|
/* Avoid rs_sync if neither branch changes the sync state. */
|
|
if ((no_sync_change(branch->tbranch, 32) > 0)
|
|
&& (no_sync_change(branch->fbranch, 32) > 0))
|
|
need_sync = 0;
|
|
else
|
|
need_sync = 1;
|
|
|
|
if (!generate_inlined_test(jitter, branch->test, then_short_ok, refs, need_sync)) {
|
|
CHECK_LIMIT();
|
|
generate_non_tail(branch->test, jitter, 0, 1);
|
|
if (need_sync) mz_rs_sync();
|
|
CHECK_LIMIT();
|
|
__START_SHORT_JUMPS__(then_short_ok);
|
|
refs[0] = jit_beqi_p(jit_forward(), JIT_R0, scheme_false);
|
|
__END_SHORT_JUMPS__(then_short_ok);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
/* True branch */
|
|
mz_runstack_saved(jitter);
|
|
nsrs = jitter->need_set_rs;
|
|
PAUSE_JIT_DATA();
|
|
LOG_IT(("...then...\n"));
|
|
FOR_LOG(++jitter->log_depth);
|
|
g1 = generate(branch->tbranch, jitter, is_tail, multi_ok, target);
|
|
RESUME_JIT_DATA();
|
|
CHECK_LIMIT();
|
|
amt = mz_runstack_restored(jitter);
|
|
if (g1 != 2) {
|
|
if (!is_tail) {
|
|
if (amt)
|
|
mz_rs_inc(amt);
|
|
if (need_sync) mz_rs_sync();
|
|
}
|
|
__START_SHORT_JUMPS__(else_short_ok);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
__END_SHORT_JUMPS__(else_short_ok);
|
|
nsrs1 = jitter->need_set_rs;
|
|
} else {
|
|
ref2 = 0;
|
|
nsrs1 = 0;
|
|
}
|
|
jitter->need_set_rs = nsrs;
|
|
if (need_sync) mz_rs_sync_0();
|
|
|
|
/* False branch */
|
|
mz_runstack_saved(jitter);
|
|
__START_SHORT_JUMPS__(then_short_ok);
|
|
if (refs[0]) {
|
|
mz_patch_branch(refs[0]);
|
|
}
|
|
if (refs[1]) {
|
|
mz_patch_branch(refs[1]);
|
|
}
|
|
if (refs[2]) {
|
|
jit_patch_movi(refs[2], (_jit.x.pc));
|
|
}
|
|
if (refs[3]) {
|
|
mz_patch_branch(refs[3]);
|
|
}
|
|
if (refs[4]) {
|
|
mz_patch_branch(refs[4]);
|
|
}
|
|
if (refs[5]) {
|
|
mz_patch_ucbranch(refs[5]);
|
|
}
|
|
__END_SHORT_JUMPS__(then_short_ok);
|
|
PAUSE_JIT_DATA();
|
|
FOR_LOG(jitter->log_depth--);
|
|
LOG_IT(("...else\n"));
|
|
FOR_LOG(++jitter->log_depth);
|
|
g2 = generate(branch->fbranch, jitter, is_tail, multi_ok, target);
|
|
RESUME_JIT_DATA();
|
|
CHECK_LIMIT();
|
|
amt = mz_runstack_restored(jitter);
|
|
if (g2 != 2) {
|
|
if (!is_tail) {
|
|
if (amt)
|
|
mz_rs_inc(amt);
|
|
if (need_sync) mz_rs_sync();
|
|
}
|
|
} else {
|
|
jitter->need_set_rs = 0;
|
|
}
|
|
if (g1 != 2) {
|
|
__START_SHORT_JUMPS__(else_short_ok);
|
|
mz_patch_ucbranch(ref2);
|
|
__END_SHORT_JUMPS__(else_short_ok);
|
|
}
|
|
FOR_LOG(jitter->log_depth--);
|
|
|
|
END_JIT_DATA(12);
|
|
|
|
/* Return result */
|
|
|
|
if ((g1 == 2) && (g2 == 2))
|
|
return 2;
|
|
|
|
if (nsrs1)
|
|
jitter->need_set_rs = 1;
|
|
|
|
return 1;
|
|
}
|
|
case scheme_unclosed_procedure_type:
|
|
{
|
|
Scheme_Closure_Data *data = (Scheme_Closure_Data *)obj;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("lambda\n"));
|
|
|
|
mz_rs_sync();
|
|
|
|
/* Allocate closure */
|
|
generate_closure(data, jitter, 1);
|
|
CHECK_LIMIT();
|
|
|
|
generate_closure_fill(data, jitter);
|
|
|
|
CHECK_LIMIT();
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
|
|
END_JIT_DATA(13);
|
|
return 0;
|
|
}
|
|
case scheme_let_value_type:
|
|
{
|
|
Scheme_Let_Value *lv = (Scheme_Let_Value *)obj;
|
|
int ab = SCHEME_LET_AUTOBOX(lv), i, pos;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("let...\n"));
|
|
|
|
if (lv->count == 1) {
|
|
/* Expect one result: */
|
|
generate_non_tail(lv->value, jitter, 0, 1); /* no sync */
|
|
CHECK_LIMIT();
|
|
if (ab) {
|
|
pos = mz_remap(lv->position);
|
|
mz_rs_ldxi(JIT_R2, pos);
|
|
jit_str_p(JIT_R2, JIT_R0);
|
|
} else {
|
|
pos = mz_remap(lv->position);
|
|
mz_rs_stxi(pos, JIT_R0);
|
|
}
|
|
CHECK_LIMIT();
|
|
} else {
|
|
/* Expect multiple results: */
|
|
jit_insn *ref, *ref2, *ref3;
|
|
|
|
generate_non_tail(lv->value, jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_sync();
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
/* Did we get multiple results? If not, go to error: */
|
|
ref = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
|
|
/* Load count and result array: */
|
|
jit_ldi_p(JIT_R2, &scheme_current_thread);
|
|
jit_ldxi_l(JIT_R1, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.count);
|
|
jit_ldxi_p(JIT_R2, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.array);
|
|
CHECK_LIMIT();
|
|
/* If we got the expected count, jump to installing values: */
|
|
ref2 = jit_beqi_i(jit_forward(), JIT_R1, lv->count);
|
|
/* Otherwise, jump to error: */
|
|
ref3 = jit_jmpi(jit_forward());
|
|
CHECK_LIMIT();
|
|
|
|
/* Jump here when we didn't get multiple values. Set count to 1
|
|
and "array" to single value: */
|
|
mz_patch_branch(ref);
|
|
jit_movi_i(JIT_R1, 1);
|
|
jit_movr_p(JIT_R2, JIT_R0);
|
|
CHECK_LIMIT();
|
|
|
|
/* Error starts here: */
|
|
mz_patch_ucbranch(ref3);
|
|
JIT_UPDATE_THREAD_RSPTR_FOR_BRANCH_IF_NEEDED();
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_i(JIT_R1);
|
|
CHECK_LIMIT();
|
|
jit_movi_i(JIT_V1, lv->count);
|
|
jit_pusharg_i(JIT_V1);
|
|
(void)mz_finish(lexical_binding_wrong_return_arity);
|
|
CHECK_LIMIT();
|
|
|
|
/* Continue with expected values; R2 has value array: */
|
|
mz_patch_branch(ref2);
|
|
__END_SHORT_JUMPS__(1);
|
|
for (i = 0; i < lv->count; i++) {
|
|
jit_ldxi_p(JIT_R1, JIT_R2, WORDS_TO_BYTES(i));
|
|
if (ab) {
|
|
pos = mz_remap(lv->position + i);
|
|
jit_ldxi_p(JIT_R0, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
|
|
jit_str_p(JIT_R0, JIT_R1);
|
|
} else {
|
|
pos = mz_remap(lv->position + i);
|
|
jit_stxi_p(WORDS_TO_BYTES(pos), JIT_RUNSTACK, JIT_R1);
|
|
}
|
|
CHECK_LIMIT();
|
|
}
|
|
}
|
|
|
|
END_JIT_DATA(14);
|
|
|
|
LOG_IT(("...in\n"));
|
|
|
|
return generate(lv->body, jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_let_void_type:
|
|
{
|
|
Scheme_Let_Void *lv = (Scheme_Let_Void *)obj;
|
|
int c = lv->count;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("letv...\n"));
|
|
|
|
mz_rs_dec(c);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
stack_safety(jitter, c, 0);
|
|
mz_runstack_pushed(jitter, c);
|
|
|
|
if (SCHEME_LET_AUTOBOX(lv)) {
|
|
int i;
|
|
mz_rs_sync();
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
for (i = 0; i < c; i++) {
|
|
CHECK_LIMIT();
|
|
(void)jit_movi_p(JIT_R0, scheme_undefined);
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_make_envunbox);
|
|
jit_retval(JIT_R0);
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_R0);
|
|
}
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
END_JIT_DATA(15);
|
|
|
|
LOG_IT(("...in\n"));
|
|
|
|
return generate(lv->body, jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_letrec_type:
|
|
{
|
|
Scheme_Letrec *l = (Scheme_Letrec *)obj;
|
|
int i, nsrs;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("letrec...\n"));
|
|
|
|
mz_rs_sync();
|
|
|
|
/* Create unfinished closures */
|
|
for (i = 0; i < l->count; i++) {
|
|
((Scheme_Closure_Data *)l->procs[i])->context = (Scheme_Object *)l;
|
|
generate_closure((Scheme_Closure_Data *)l->procs[i], jitter, i + 1 == l->count);
|
|
CHECK_LIMIT();
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_R0);
|
|
}
|
|
|
|
/* Close them: */
|
|
for (i = l->count; i--; ) {
|
|
if (i != l->count - 1) {
|
|
/* Last one we created is still in JIT_R0: */
|
|
jit_ldxi_p(JIT_R0, JIT_RUNSTACK, WORDS_TO_BYTES(i));
|
|
}
|
|
generate_closure_fill((Scheme_Closure_Data *)l->procs[i], jitter);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
END_JIT_DATA(16);
|
|
|
|
LOG_IT(("...in\n"));
|
|
|
|
/* Assuming we can replace the last l->count, push closure info instead: */
|
|
nsrs = jitter->need_set_rs;
|
|
if (mz_try_runstack_pop(jitter, l->count)) {
|
|
int i;
|
|
for (i = l->count; i--; ) {
|
|
Scheme_Closure_Data *data2 = (Scheme_Closure_Data *)l->procs[i];
|
|
mz_runstack_closure_pushed(jitter,
|
|
(data2->num_params
|
|
- ((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_HAS_REST)
|
|
? 1
|
|
: 0)),
|
|
(((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_PRESERVES_MARKS)
|
|
? NATIVE_PRESERVES_MARKS
|
|
: 0)
|
|
| ((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_SINGLE_RESULT)
|
|
? NATIVE_IS_SINGLE_RESULT
|
|
: 0)));
|
|
}
|
|
jitter->need_set_rs = nsrs;
|
|
}
|
|
|
|
return generate(l->body, jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_let_one_type:
|
|
{
|
|
Scheme_Let_One *lv = (Scheme_Let_One *)obj;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("leto...\n"));
|
|
|
|
mz_runstack_skipped(jitter, 1);
|
|
|
|
PAUSE_JIT_DATA();
|
|
generate_non_tail(lv->value, jitter, 0, 1); /* no sync */
|
|
RESUME_JIT_DATA();
|
|
CHECK_LIMIT();
|
|
|
|
mz_runstack_unskipped(jitter, 1);
|
|
|
|
mz_rs_dec(1);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
mz_runstack_pushed(jitter, 1);
|
|
|
|
mz_rs_str(JIT_R0);
|
|
|
|
END_JIT_DATA(17);
|
|
|
|
LOG_IT(("...in\n"));
|
|
|
|
mz_RECORD_STATUS(mz_RS_R0_HAS_RUNSTACK0);
|
|
|
|
return generate(lv->body, jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_with_cont_mark_type:
|
|
{
|
|
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)obj;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("wcm...\n"));
|
|
|
|
/* Key: */
|
|
generate_non_tail(wcm->key, jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
if (SCHEME_TYPE(wcm->val) > _scheme_values_types_) {
|
|
/* No need to push mark onto value stack: */
|
|
jit_movr_p(JIT_V1, JIT_R0);
|
|
generate_non_tail(wcm->val, jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
} else {
|
|
mz_pushr_p(JIT_R0);
|
|
generate_non_tail(wcm->val, jitter, 0, 1); /* sync'd below */
|
|
CHECK_LIMIT();
|
|
mz_popr_p(JIT_V1); /* sync'd below */
|
|
}
|
|
|
|
mz_rs_sync();
|
|
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
|
|
|
|
mz_prepare(2);
|
|
jit_pusharg_p(JIT_R0);
|
|
jit_pusharg_p(JIT_V1);
|
|
(void)mz_finish(scheme_set_cont_mark);
|
|
CHECK_LIMIT();
|
|
|
|
END_JIT_DATA(18);
|
|
|
|
LOG_IT(("...in\n"));
|
|
|
|
return generate(wcm->body, jitter, is_tail, multi_ok, target);
|
|
}
|
|
case scheme_quote_syntax_type:
|
|
{
|
|
Scheme_Quote_Syntax *qs = (Scheme_Quote_Syntax *)obj;
|
|
int i, c, p;
|
|
START_JIT_DATA();
|
|
|
|
LOG_IT(("quote-syntax\n"));
|
|
|
|
i = qs->position;
|
|
c = mz_remap(qs->depth);
|
|
p = qs->midpoint;
|
|
|
|
mz_rs_sync();
|
|
|
|
jit_movi_i(JIT_R0, WORDS_TO_BYTES(c));
|
|
jit_movi_i(JIT_R1, WORDS_TO_BYTES(i + p + 1));
|
|
jit_movi_i(JIT_R2, WORDS_TO_BYTES(p));
|
|
(void)jit_calli(quote_syntax_code);
|
|
|
|
CHECK_LIMIT();
|
|
if (target != JIT_R0)
|
|
jit_movr_p(target, JIT_R0);
|
|
|
|
END_JIT_DATA(10);
|
|
|
|
return 1;
|
|
}
|
|
default:
|
|
{
|
|
int retptr;
|
|
Scheme_Type type = SCHEME_TYPE(obj);
|
|
START_JIT_DATA();
|
|
|
|
/* Other parts of the JIT rely on this code modifying R0, only */
|
|
|
|
LOG_IT(("const\n"));
|
|
|
|
/* Avoid compiling closures multiple times: */
|
|
if (jitter->retain_start) {
|
|
if (type == scheme_closure_type) {
|
|
/* Empty closure? If so, compile the code and get a native closure: */
|
|
Scheme_Closure *c = (Scheme_Closure *)obj;
|
|
if (ZERO_SIZED_CLOSUREP(c))
|
|
obj = scheme_jit_closure((Scheme_Object *)c->code, NULL);
|
|
} else if (type == scheme_case_closure_type) {
|
|
/* Empty case closure? Turn in into a JITted empty case closure. */
|
|
obj = scheme_unclose_case_lambda(obj, 1);
|
|
}
|
|
}
|
|
|
|
if (!SCHEME_INTP(obj)
|
|
&& !SAME_OBJ(obj, scheme_true)
|
|
&& !SAME_OBJ(obj, scheme_false)
|
|
&& !SAME_OBJ(obj, scheme_void)
|
|
&& !SAME_OBJ(obj, scheme_null)) {
|
|
retptr = mz_retain(obj);
|
|
} else
|
|
retptr = 0;
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
if (retptr)
|
|
mz_load_retained(jitter, target, retptr);
|
|
else
|
|
#endif
|
|
(void)jit_patchable_movi_p(target, obj); /* !! */
|
|
|
|
END_JIT_DATA(19);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* procedure codegen */
|
|
/*========================================================================*/
|
|
|
|
static void generate_function_prolog(mz_jit_state *jitter, void *code, int max_let_depth)
|
|
{
|
|
int in;
|
|
START_JIT_DATA();
|
|
|
|
jit_prolog(3);
|
|
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R0, in); /* closure */
|
|
in = jit_arg_i();
|
|
jit_getarg_i(JIT_R1, in); /* argc */
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R2, in); /* argv */
|
|
|
|
jit_ldi_p(JIT_RUNSTACK, &MZ_RUNSTACK);
|
|
|
|
END_JIT_DATA(1);
|
|
}
|
|
|
|
static int generate_function_getarg(mz_jit_state *jitter, int has_rest, int num_params)
|
|
{
|
|
int i, cnt;
|
|
jit_insn *ref;
|
|
int set_ref;
|
|
|
|
mz_push_locals();
|
|
|
|
/* If rands == runstack and there are no rest args, set runstack
|
|
base to runstack + rands (and don't copy rands), otherwise set
|
|
base to runstack and proceed normally. Implement this by
|
|
optimistically assuming rands == runstack, so that there's just
|
|
one jump. Skip this optimization when the procedure has
|
|
rest args, because we'll have to copy anyway. */
|
|
if (!has_rest && num_params) {
|
|
jit_lshi_l(JIT_RUNSTACK_BASE, JIT_R1, JIT_LOG_WORD_SIZE);
|
|
jit_addr_p(JIT_RUNSTACK_BASE, JIT_R2, JIT_RUNSTACK_BASE);
|
|
__START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
|
|
ref = jit_beqr_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
|
|
__END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
|
|
set_ref = 1;
|
|
} else {
|
|
ref = 0;
|
|
set_ref = 0;
|
|
}
|
|
jit_movr_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK);
|
|
|
|
/* Make stack room for arguments: */
|
|
cnt = num_params;
|
|
if (cnt) {
|
|
CHECK_LIMIT();
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(cnt));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
if (has_rest)
|
|
--cnt;
|
|
}
|
|
|
|
/* Extract arguments to runstack: */
|
|
for (i = cnt; i--; ) {
|
|
jit_ldxi_p(JIT_V1, JIT_R2, WORDS_TO_BYTES(i));
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_V1);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
if (set_ref) {
|
|
__START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
|
|
mz_patch_branch(ref);
|
|
__END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static int save_struct_temp(mz_jit_state *jitter)
|
|
{
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jit_movr_p(JIT_V(3), JIT_V1);
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
# ifdef X86_ALIGN_STACK
|
|
mz_set_local_p(JIT_V1, JIT_LOCAL3);
|
|
# else
|
|
jit_pushr_p(JIT_V1);
|
|
# endif
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static int restore_struct_temp(mz_jit_state *jitter, int reg)
|
|
{
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jit_movr_p(reg, JIT_V(3));
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
# ifdef X86_ALIGN_STACK
|
|
mz_get_local_p(reg, JIT_LOCAL3);
|
|
# else
|
|
jit_popr_p(reg);
|
|
# endif
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static int do_generate_common(mz_jit_state *jitter, void *_data)
|
|
{
|
|
int in, i, ii, iii;
|
|
GC_CAN_IGNORE jit_insn *ref, *ref2;
|
|
|
|
/* *** check_arity_code *** */
|
|
/* Called as a function: */
|
|
check_arity_code = (Native_Check_Arity_Proc)jit_get_ip().ptr;
|
|
jit_prolog(3); /* only need 1 argument, but return path overlaps with proc conventions */
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R0, in); /* closure */
|
|
in = jit_arg_p();
|
|
jit_getarg_i(JIT_R2, in); /* argc */
|
|
mz_push_locals();
|
|
jit_movi_i(JIT_R1, -1);
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
|
|
jit_jmpr(JIT_V1); /* leads to a jit_ret() that assumes 3 arguments */
|
|
CHECK_LIMIT();
|
|
|
|
/* *** get_arity_code *** */
|
|
/* Called as a function: */
|
|
get_arity_code = (Native_Get_Arity_Proc)jit_get_ip().ptr;
|
|
jit_prolog(3); /* only need 1 argument, but return path overlaps with proc conventions */
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R0, in); /* closure */
|
|
mz_push_locals();
|
|
jit_movi_i(JIT_R1, -1);
|
|
(void)jit_movi_p(JIT_R2, 0x0);
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
|
|
jit_jmpr(JIT_V1); /* leads to a jit_ret() that assumes 3 arguments */
|
|
CHECK_LIMIT();
|
|
|
|
/* *** bad_result_arity_code *** */
|
|
/* Jumped-to from non-tail contexts */
|
|
bad_result_arity_code = (Native_Get_Arity_Proc)jit_get_ip().ptr;
|
|
jit_ldi_p(JIT_R2, &scheme_current_thread);
|
|
jit_ldxi_l(JIT_R1, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.count);
|
|
jit_ldxi_p(JIT_R2, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.array);
|
|
CHECK_LIMIT();
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_i(JIT_R1);
|
|
CHECK_LIMIT();
|
|
jit_movi_i(JIT_V1, 1);
|
|
jit_pusharg_i(JIT_V1);
|
|
(void)mz_finish(call_wrong_return_arity);
|
|
CHECK_LIMIT();
|
|
|
|
/* *** unbound_global_code *** */
|
|
unbound_global_code = jit_get_ip().ptr;
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R2);
|
|
(void)mz_finish(scheme_unbound_global);
|
|
CHECK_LIMIT();
|
|
|
|
/* *** quote_syntax_code *** */
|
|
/* R0 is WORDS_TO_BYTES(c), R1 is WORDS_TO_BYTES(i+p+1), R2 is WORDS_TO_BYTES(p) */
|
|
quote_syntax_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_V1);
|
|
__START_SHORT_JUMPS__(1);
|
|
/* Load global array: */
|
|
jit_ldxr_p(JIT_V1, JIT_RUNSTACK, JIT_R0);
|
|
#ifdef JIT_PRECISE_GC
|
|
/* Save global-array index before we lose it: */
|
|
mz_set_local_p(JIT_R0, JIT_LOCAL3);
|
|
#endif
|
|
/* Load syntax object: */
|
|
jit_ldxr_p(JIT_R0, JIT_V1, JIT_R1);
|
|
/* Is it null? */
|
|
ref = jit_bnei_p(jit_forward(), JIT_R0, 0x0);
|
|
CHECK_LIMIT();
|
|
/* Syntax object is NULL, so we need to create it. */
|
|
jit_ldxr_p(JIT_R0, JIT_V1, JIT_R2); /* put element at p in R0 */
|
|
#ifndef JIT_PRECISE_GC
|
|
/* Save global array: */
|
|
mz_set_local_p(JIT_V1, JIT_LOCAL3);
|
|
#endif
|
|
/* Move R1 to V1 to save it: */
|
|
jit_movr_p(JIT_V1, JIT_R1);
|
|
/* Compute i in JIT_R1: */
|
|
jit_subr_p(JIT_R1, JIT_R1, JIT_R2);
|
|
jit_subi_p(JIT_R1, JIT_R1, WORDS_TO_BYTES(1));
|
|
jit_rshi_ul(JIT_R1, JIT_R1, JIT_LOG_WORD_SIZE);
|
|
CHECK_LIMIT();
|
|
/* Call scheme_delayed_rename: */
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
CHECK_LIMIT();
|
|
mz_prepare(2);
|
|
jit_pusharg_l(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(scheme_delayed_rename);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
/* Restore global array into JIT_R1, and put computed element at i+p+1: */
|
|
#ifdef JIT_PRECISE_GC
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL3);
|
|
jit_ldxr_p(JIT_R1, JIT_RUNSTACK, JIT_R1);
|
|
#else
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL3);
|
|
#endif
|
|
jit_stxr_p(JIT_V1, JIT_R1, JIT_R0);
|
|
mz_patch_branch(ref);
|
|
__END_SHORT_JUMPS__(1);
|
|
mz_epilog(JIT_V1);
|
|
|
|
/* *** bad_[m]{car,cdr,...}_code *** */
|
|
/* Bad argument is in R0 for car/cdr, R2 otherwise */
|
|
for (i = 0; i < 8; i++) {
|
|
void *code;
|
|
|
|
code = jit_get_ip().ptr;
|
|
switch (i) {
|
|
case 0:
|
|
bad_car_code = code;
|
|
break;
|
|
case 1:
|
|
bad_cdr_code = code;
|
|
break;
|
|
case 2:
|
|
bad_caar_code = code;
|
|
break;
|
|
case 3:
|
|
bad_cadr_code = code;
|
|
break;
|
|
case 4:
|
|
bad_cdar_code = code;
|
|
break;
|
|
case 5:
|
|
bad_cddr_code = code;
|
|
break;
|
|
case 6:
|
|
bad_mcar_code = code;
|
|
break;
|
|
case 7:
|
|
bad_mcdr_code = code;
|
|
break;
|
|
}
|
|
mz_prolog(JIT_R1);
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
if ((i < 2) || (i > 5)) {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
} else {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R2);
|
|
}
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
CHECK_LIMIT();
|
|
jit_movi_i(JIT_R1, 1);
|
|
jit_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
switch (i) {
|
|
case 0:
|
|
(void)mz_finish(scheme_checked_car);
|
|
break;
|
|
case 1:
|
|
(void)mz_finish(scheme_checked_cdr);
|
|
break;
|
|
case 2:
|
|
(void)mz_finish(scheme_checked_caar);
|
|
break;
|
|
case 3:
|
|
(void)mz_finish(scheme_checked_cadr);
|
|
break;
|
|
case 4:
|
|
(void)mz_finish(scheme_checked_cdar);
|
|
break;
|
|
case 5:
|
|
(void)mz_finish(scheme_checked_cddr);
|
|
break;
|
|
case 6:
|
|
(void)mz_finish(scheme_checked_mcar);
|
|
break;
|
|
case 7:
|
|
(void)mz_finish(scheme_checked_mcdr);
|
|
break;
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
register_sub_func(jitter, code, scheme_false);
|
|
}
|
|
|
|
/* *** bad_set_{car,cdr}_code *** */
|
|
/* Bad argument is in R0, other is in R1 */
|
|
for (i = 0; i < 2; i++) {
|
|
void *code;
|
|
code = jit_get_ip().ptr;
|
|
switch (i) {
|
|
case 0:
|
|
bad_set_mcar_code = code;
|
|
break;
|
|
case 1:
|
|
bad_set_mcdr_code = code;
|
|
break;
|
|
}
|
|
mz_prolog(JIT_R2);
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(2));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_R1);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
CHECK_LIMIT();
|
|
jit_movi_i(JIT_R1, 2);
|
|
jit_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
switch (i) {
|
|
case 0:
|
|
(void)mz_finish(scheme_checked_set_mcar);
|
|
break;
|
|
case 1:
|
|
(void)mz_finish(scheme_checked_set_mcdr);
|
|
break;
|
|
}
|
|
CHECK_LIMIT();
|
|
register_sub_func(jitter, code, scheme_false);
|
|
}
|
|
|
|
/* *** bad_unbox_code *** */
|
|
/* R0 is argument */
|
|
bad_unbox_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_R1);
|
|
jit_prepare(1);
|
|
jit_pusharg_i(JIT_R0);
|
|
(void)mz_finish(scheme_unbox);
|
|
CHECK_LIMIT();
|
|
register_sub_func(jitter, bad_unbox_code, scheme_false);
|
|
|
|
/* *** bad_vector_length_code *** */
|
|
/* R0 is argument */
|
|
bad_vector_length_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_R1);
|
|
jit_prepare(1);
|
|
jit_pusharg_i(JIT_R0);
|
|
(void)mz_finish(scheme_vector_length);
|
|
CHECK_LIMIT();
|
|
register_sub_func(jitter, bad_vector_length_code, scheme_false);
|
|
|
|
/* *** call_original_unary_arith_code *** */
|
|
/* R0 is arg, R2 is code pointer, V1 is return address */
|
|
for (i = 0; i < 3; i++) {
|
|
int argc, j;
|
|
void *code;
|
|
for (j = 0; j < 2; j++) {
|
|
code = jit_get_ip().ptr;
|
|
if (!i) {
|
|
if (!j)
|
|
call_original_unary_arith_code = code;
|
|
else
|
|
call_original_unary_arith_for_branch_code = code;
|
|
argc = 1;
|
|
} else if (i == 1) {
|
|
if (!j)
|
|
call_original_binary_arith_code = code;
|
|
else
|
|
call_original_binary_arith_for_branch_code = code;
|
|
argc = 2;
|
|
} else {
|
|
if (!j)
|
|
call_original_binary_rev_arith_code = code;
|
|
else
|
|
call_original_binary_rev_arith_for_branch_code = code;
|
|
argc = 2;
|
|
}
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(argc));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
if (i == 2) {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_R1);
|
|
} else if (i == 1) {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R1);
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_R0);
|
|
} else {
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
}
|
|
jit_movi_i(JIT_R1, argc);
|
|
if (!j) {
|
|
/* For stack-trace reporting, stuff return address into LOCAL2 */
|
|
mz_set_local_p(JIT_V1, JIT_LOCAL2);
|
|
}
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_p(JIT_R1);
|
|
(void)mz_finishr(JIT_R2);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(argc));
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
if (!j) {
|
|
jit_jmpr(JIT_V1);
|
|
} else {
|
|
/* In for_branch mode, V1 is target for false, LOCAL2 is target for true */
|
|
mz_get_local_p(JIT_R1, JIT_LOCAL2);
|
|
__START_TINY_JUMPS__(1);
|
|
ref = jit_beqi_p(jit_forward(), JIT_R0, scheme_true);
|
|
jit_jmpr(JIT_V1);
|
|
mz_patch_branch(ref);
|
|
jit_jmpr(JIT_R1);
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
register_sub_func(jitter, code, scheme_void);
|
|
}
|
|
}
|
|
|
|
/* *** on_demand_jit_[arity_]code *** */
|
|
/* Used as the code stub for a closure whose
|
|
code is not yet compiled. See generate_function_prolog
|
|
for the state of registers on entry */
|
|
on_demand_jit_code = jit_get_ip().ptr;
|
|
jit_prolog(3);
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R0, in); /* closure */
|
|
in = jit_arg_i();
|
|
jit_getarg_i(JIT_R1, in); /* argc */
|
|
in = jit_arg_p();
|
|
jit_getarg_p(JIT_R2, in); /* argv */
|
|
CHECK_LIMIT();
|
|
jit_ldi_p(JIT_RUNSTACK, &MZ_RUNSTACK);
|
|
mz_push_locals();
|
|
on_demand_jit_arity_code = jit_get_ip().ptr; /* <<<- arity variant starts here */
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(3));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_lshi_ul(JIT_R1, JIT_R1, 0x1);
|
|
jit_ori_ul(JIT_R1, JIT_R1, 0x1);
|
|
CHECK_LIMIT();
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_R1);
|
|
jit_stxi_p(WORDS_TO_BYTES(2), JIT_RUNSTACK, JIT_R2);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
(void)jit_calli(on_demand); /* DARWIN: stack needs to be 16-byte aligned */
|
|
CHECK_LIMIT();
|
|
/* Restore registers and runstack, and jump to arity checking
|
|
of newly-created code when argv == runstack (i.e., a tail call): */
|
|
jit_ldr_p(JIT_R0, JIT_RUNSTACK);
|
|
jit_ldxi_p(JIT_R1, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
jit_rshi_ul(JIT_R1, JIT_R1, 0x1);
|
|
jit_ldxi_p(JIT_R2, JIT_RUNSTACK, WORDS_TO_BYTES(2));
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(3));
|
|
CHECK_LIMIT();
|
|
ref = jit_bner_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
|
|
/* Also, check that the runstack is big enough with the revised
|
|
max_let_depth. We can use JIT_V2 here because RUNSTACK_BASE is not
|
|
yet ready: */
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_i(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->max_let_depth);
|
|
jit_ldi_p(JIT_V2, &MZ_RUNSTACK_START);
|
|
jit_subr_ul(JIT_V2, JIT_RUNSTACK, JIT_V2);
|
|
ref2 = jit_bltr_ul(jit_forward(), JIT_V2, JIT_V1);
|
|
CHECK_LIMIT();
|
|
/* This is the tail-call fast path: */
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
|
|
/* Set runstack base to end of arguments on runstack: */
|
|
jit_movr_p(JIT_RUNSTACK_BASE, JIT_R1);
|
|
jit_lshi_ul(JIT_RUNSTACK_BASE, JIT_RUNSTACK_BASE, JIT_LOG_WORD_SIZE);
|
|
jit_addr_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK_BASE, JIT_RUNSTACK);
|
|
jit_jmpr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
/* Slower path (non-tail) when argv != runstack. */
|
|
mz_patch_branch(ref);
|
|
mz_patch_branch(ref2);
|
|
CHECK_LIMIT();
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(_scheme_apply_multi_from_native);
|
|
CHECK_LIMIT();
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
CHECK_LIMIT();
|
|
register_helper_func(jitter, on_demand_jit_code);
|
|
|
|
/* *** app_values_tail_slow_code *** */
|
|
/* RELIES ON jit_prolog(3) FROM ABOVE */
|
|
/* Rator in V1, arguments are in thread's multiple-values cells. */
|
|
app_values_tail_slow_code = jit_get_ip().ptr;
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_V1);
|
|
(void)mz_finish(tail_call_with_values_from_multiple_result);
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
/* Return: */
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
CHECK_LIMIT();
|
|
|
|
/* *** finish_tail_call_[fixup_]code *** */
|
|
/* RELIES ON jit_prolog(3) FROM ABOVE */
|
|
finish_tail_call_code = jit_get_ip().ptr;
|
|
generate_finish_tail_call(jitter, 0);
|
|
CHECK_LIMIT();
|
|
register_helper_func(jitter, finish_tail_call_code);
|
|
finish_tail_call_fixup_code = jit_get_ip().ptr;
|
|
generate_finish_tail_call(jitter, 2);
|
|
CHECK_LIMIT();
|
|
register_helper_func(jitter, finish_tail_call_fixup_code);
|
|
|
|
/* *** get_stack_pointer_code *** */
|
|
get_stack_pointer_code = jit_get_ip().ptr;
|
|
jit_leaf(0);
|
|
jit_movr_p(JIT_R0, JIT_STACK_FRAME);
|
|
/* Get frame pointer of caller... */
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jit_ldr_p(JIT_R0, JIT_R0);
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
jit_ldr_p(JIT_R0, JIT_R0);
|
|
#endif
|
|
jit_movr_p(JIT_RET, JIT_R0);
|
|
jit_ret();
|
|
CHECK_LIMIT();
|
|
|
|
/* *** stack_cache_pop_code *** */
|
|
/* DANGER: this code must save and restore (or avoid)
|
|
any registers that a function call would normally save
|
|
and restore. JIT_AUX, which is used by things like jit_ldi,
|
|
is such a register for PPC. */
|
|
stack_cache_pop_code = jit_get_ip().ptr;
|
|
jit_movr_p(JIT_R0, JIT_RET);
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jit_movr_p(JIT_R(3), JIT_AUX);
|
|
#endif
|
|
/* Decrement stack_cache_stack_pos */
|
|
jit_ldi_l(JIT_R1, &stack_cache_stack_pos);
|
|
jit_subi_i(JIT_R2, JIT_R1, 1);
|
|
jit_sti_l(&stack_cache_stack_pos, JIT_R2);
|
|
CHECK_LIMIT();
|
|
/* Extract old return address and jump to it */
|
|
jit_lshi_l(JIT_R1, JIT_R1, (JIT_LOG_WORD_SIZE + 2));
|
|
jit_addi_l(JIT_R1, JIT_R1, (int)&((Stack_Cache_Elem *)0x0)->orig_return_address);
|
|
(void)jit_movi_p(JIT_R2, &stack_cache_stack);
|
|
jit_ldxr_p(JIT_R2, JIT_R2, JIT_R1);
|
|
jit_movr_p(JIT_RET, JIT_R0);
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jit_movr_p(JIT_AUX, JIT_R(3));
|
|
#endif
|
|
jit_jmpr(JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* *** bad_app_vals_target *** */
|
|
/* Non-proc is in R0 */
|
|
bad_app_vals_target = jit_get_ip().ptr;
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_finish(raise_bad_call_with_values);
|
|
/* Doesn't return */
|
|
CHECK_LIMIT();
|
|
|
|
/* *** app_values[_multi]_slow_code *** */
|
|
/* Rator in V1, arguments are in thread's multiple-values cells. */
|
|
for (i = 0; i < 2; i++) {
|
|
if (i)
|
|
app_values_multi_slow_code = jit_get_ip().ptr;
|
|
else
|
|
app_values_slow_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_R1);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_V1);
|
|
if (i) {
|
|
(void)mz_finish(call_with_values_from_multiple_result_multi);
|
|
} else {
|
|
(void)mz_finish(call_with_values_from_multiple_result);
|
|
}
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
mz_epilog(JIT_R1);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
/* *** {vector,string,bytes}_{ref,set}_[check_index_]code *** */
|
|
/* R0 is vector/string/bytes, R1 is index (Scheme number in check-index mode),
|
|
V1 is vector/string/bytes offset in non-check-index mode (and for
|
|
vector, it includes the offset to the start of the elements array.
|
|
In set mode, value is on run stack. */
|
|
for (iii = 0; iii < 2; iii++) { /* ref, set */
|
|
for (ii = 0; ii < 3; ii++) { /* vector, string, bytes */
|
|
for (i = 0; i < 2; i++) { /* check index? */
|
|
jit_insn *ref, *reffail;
|
|
Scheme_Type ty;
|
|
int offset, count_offset, log_elem_size;
|
|
|
|
switch (ii) {
|
|
case 0:
|
|
ty = scheme_vector_type;
|
|
offset = (int)&SCHEME_VEC_ELS(0x0);
|
|
count_offset = (int)&SCHEME_VEC_SIZE(0x0);
|
|
log_elem_size = JIT_LOG_WORD_SIZE;
|
|
if (!iii) {
|
|
if (!i) {
|
|
vector_ref_code = jit_get_ip().ptr;
|
|
} else {
|
|
vector_ref_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
} else {
|
|
if (!i) {
|
|
vector_set_code = jit_get_ip().ptr;
|
|
} else {
|
|
vector_set_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
ty = scheme_char_string_type;
|
|
offset = (int)&SCHEME_CHAR_STR_VAL(0x0);
|
|
count_offset = (int)&SCHEME_CHAR_STRLEN_VAL(0x0);
|
|
log_elem_size = LOG_MZCHAR_SIZE;
|
|
if (!iii) {
|
|
if (!i) {
|
|
string_ref_code = jit_get_ip().ptr;
|
|
} else {
|
|
string_ref_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
} else {
|
|
if (!i) {
|
|
string_set_code = jit_get_ip().ptr;
|
|
} else {
|
|
string_set_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
case 2:
|
|
ty = scheme_byte_string_type;
|
|
offset = (int)&SCHEME_BYTE_STR_VAL(0x0);
|
|
count_offset = (int)&SCHEME_BYTE_STRLEN_VAL(0x0);
|
|
log_elem_size = 0;
|
|
if (!iii) {
|
|
if (!i) {
|
|
bytes_ref_code = jit_get_ip().ptr;
|
|
} else {
|
|
bytes_ref_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
} else {
|
|
if (!i) {
|
|
bytes_set_code = jit_get_ip().ptr;
|
|
} else {
|
|
bytes_set_check_index_code = jit_get_ip().ptr;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
|
|
mz_prolog(JIT_R2);
|
|
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Slow path: */
|
|
reffail = _jit.x.pc;
|
|
if (!i) {
|
|
jit_lshi_ul(JIT_R1, JIT_R1, 1);
|
|
jit_ori_ul(JIT_R1, JIT_R1, 0x1);
|
|
}
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(2));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_R1);
|
|
if (!iii) {
|
|
jit_movi_i(JIT_R1, 2);
|
|
} else {
|
|
/* In set mode, value was already on run stack */
|
|
jit_movi_i(JIT_R1, 3);
|
|
}
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
switch (ii) {
|
|
case 0:
|
|
if (!iii) {
|
|
(void)mz_finish(scheme_checked_vector_ref);
|
|
} else {
|
|
(void)mz_finish(scheme_checked_vector_set);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!iii) {
|
|
(void)mz_finish(scheme_checked_string_ref);
|
|
/* might return, if char was outside Latin-1 */
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(2));
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_retval(JIT_R0);
|
|
mz_epilog(JIT_R2);
|
|
} else {
|
|
(void)mz_finish(scheme_checked_string_set);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!iii) {
|
|
(void)mz_finish(scheme_checked_byte_string_ref);
|
|
} else {
|
|
(void)mz_finish(scheme_checked_byte_string_set);
|
|
}
|
|
break;
|
|
}
|
|
/* doesn't return */
|
|
CHECK_LIMIT();
|
|
|
|
/* Continue fast path */
|
|
|
|
mz_patch_branch(ref);
|
|
if (i) {
|
|
(void)jit_bmci_ul(reffail, JIT_R1, 0x1);
|
|
(void)jit_blei_l(reffail, JIT_R1, 0x0);
|
|
}
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, ty);
|
|
if (iii) {
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &(MZ_OPT_HASH_KEY((Scheme_Inclhash_Object *)0x0)));
|
|
(void)jit_bmsi_ul(reffail, JIT_R2, 0x1);
|
|
}
|
|
jit_ldxi_i(JIT_R2, JIT_R0, count_offset);
|
|
CHECK_LIMIT();
|
|
if (i) {
|
|
/* index from expression: */
|
|
jit_rshi_ul(JIT_V1, JIT_R1, 1);
|
|
(void)jit_bler_ul(reffail, JIT_R2, JIT_V1);
|
|
if (log_elem_size)
|
|
jit_lshi_ul(JIT_V1, JIT_V1, log_elem_size);
|
|
if (!ii) /* vector */
|
|
jit_addi_p(JIT_V1, JIT_V1, offset);
|
|
} else {
|
|
/* constant index supplied: */
|
|
(void)jit_bler_ul(reffail, JIT_R2, JIT_R1);
|
|
}
|
|
if (!iii) {
|
|
/* ref mode: */
|
|
switch (ii) {
|
|
case 0: /* vector */
|
|
jit_ldxr_p(JIT_R0, JIT_R0, JIT_V1);
|
|
break;
|
|
case 1: /* string */
|
|
jit_ldxi_p(JIT_R2, JIT_R0, offset);
|
|
jit_ldxr_i(JIT_R2, JIT_R2, JIT_V1);
|
|
/* Non-Latin-1 char: use slow path: */
|
|
jit_extr_i_l(JIT_R2, JIT_R2);
|
|
(void)jit_bgti_l(reffail, JIT_R2, 255);
|
|
/* Latin-1: extract from scheme_char_constants: */
|
|
jit_lshi_l(JIT_R2, JIT_R2, JIT_LOG_WORD_SIZE);
|
|
(void)jit_movi_p(JIT_R0, scheme_char_constants);
|
|
jit_ldxr_p(JIT_R0, JIT_R0, JIT_R2);
|
|
break;
|
|
case 2: /* bytes */
|
|
jit_ldxi_p(JIT_R0, JIT_R0, offset);
|
|
jit_ldxr_c(JIT_R0, JIT_R0, JIT_V1);
|
|
jit_extr_uc_ul(JIT_R0, JIT_R0);
|
|
jit_lshi_l(JIT_R0, JIT_R0, 0x1);
|
|
jit_ori_l(JIT_R0, JIT_R0, 0x1);
|
|
break;
|
|
}
|
|
} else {
|
|
/* set mode: */
|
|
jit_ldr_p(JIT_R2, JIT_RUNSTACK);
|
|
switch (ii) {
|
|
case 0: /* vector */
|
|
jit_stxr_p(JIT_V1, JIT_R0, JIT_R2);
|
|
break;
|
|
case 1: /* string */
|
|
(void)jit_bmsi_l(reffail, JIT_R2, 0x1);
|
|
jit_ldxi_s(JIT_R2, JIT_R2, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_char_type);
|
|
jit_ldr_p(JIT_R2, JIT_RUNSTACK);
|
|
jit_ldxi_i(JIT_R2, JIT_R2, &((Scheme_Small_Object *)0x0)->u.char_val);
|
|
jit_ldxi_p(JIT_R0, JIT_R0, offset);
|
|
jit_stxr_i(JIT_V1, JIT_R0, JIT_R2);
|
|
break;
|
|
case 2: /* bytes */
|
|
(void)jit_bmci_l(reffail, JIT_R2, 0x1);
|
|
jit_rshi_ul(JIT_R2, JIT_R2, 1);
|
|
(void)jit_bmsi_l(reffail, JIT_R2, ~0xFF);
|
|
jit_ldxi_p(JIT_R0, JIT_R0, offset);
|
|
jit_stxr_c(JIT_V1, JIT_R0, JIT_R2);
|
|
break;
|
|
}
|
|
(void)jit_movi_p(JIT_R0, scheme_void);
|
|
}
|
|
mz_epilog(JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *** syntax_ecode *** */
|
|
/* R0 is (potential) syntax object */
|
|
{
|
|
jit_insn *ref, *reffail;
|
|
syntax_e_code = jit_get_ip().ptr;
|
|
__START_TINY_JUMPS__(1);
|
|
mz_prolog(JIT_R2);
|
|
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
|
|
reffail = _jit.x.pc;
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R0);
|
|
jit_movi_i(JIT_R1, 1);
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
CHECK_LIMIT();
|
|
jit_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_i(JIT_R1);
|
|
(void)mz_finish(scheme_checked_syntax_e);
|
|
jit_retval(JIT_R0);
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
|
|
mz_epilog(JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* It's not a fixnum... */
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(reffail, JIT_R2, scheme_stx_type);
|
|
|
|
/* It's a syntax object... needs to propagate? */
|
|
jit_ldxi_l(JIT_R2, JIT_R0, &((Scheme_Stx *)0x0)->u.lazy_prefix);
|
|
ref = jit_beqi_l(jit_forward(), JIT_R2, 0x0);
|
|
CHECK_LIMIT();
|
|
|
|
/* Maybe needs to propagate; check STX_SUBSTX_FLAG flag */
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &MZ_OPT_HASH_KEY(&((Scheme_Stx *)0x0)->iso));
|
|
(void)jit_bmsi_ul(reffail, JIT_R2, STX_SUBSTX_FLAG);
|
|
|
|
/* No propagations. Extract value. */
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_p(JIT_R0, JIT_R0, &((Scheme_Stx *)0x0)->val);
|
|
|
|
mz_epilog(JIT_R2);
|
|
CHECK_LIMIT();
|
|
__END_TINY_JUMPS__(1);
|
|
}
|
|
|
|
/* *** struct_{pred,get,set}[_branch]_code *** */
|
|
/* R0 is (potential) struct proc, R1 is (potential) struct. */
|
|
/* In branch mode, V1 is target address for false branch. */
|
|
/* In set mode, V1 is value to install. */
|
|
for (ii = 0; ii < 2; ii++) {
|
|
for (i = 0; i < 4; i++) {
|
|
void *code, *code_end;
|
|
int kind, for_branch;
|
|
jit_insn *ref, *ref2, *refslow, *bref1, *bref2, *bref3, *bref4, *bref5, *bref6, *bref8;
|
|
|
|
if ((ii == 1) && (i == 1)) continue; /* no multi variant of pred branch */
|
|
|
|
code = jit_get_ip().ptr;
|
|
|
|
if (!i) {
|
|
kind = 1;
|
|
for_branch = 0;
|
|
if (ii == 1)
|
|
struct_pred_multi_code = jit_get_ip().ptr;
|
|
else
|
|
struct_pred_code = jit_get_ip().ptr;
|
|
} else if (i == 1) {
|
|
kind = 1;
|
|
for_branch = 1;
|
|
struct_pred_branch_code = jit_get_ip().ptr;
|
|
/* Save target address for false branch: */
|
|
save_struct_temp(jitter);
|
|
} else if (i == 2) {
|
|
kind = 2;
|
|
for_branch = 0;
|
|
if (ii == 1)
|
|
struct_get_multi_code = jit_get_ip().ptr;
|
|
else
|
|
struct_get_code = jit_get_ip().ptr;
|
|
} else {
|
|
kind = 3;
|
|
for_branch = 0;
|
|
if (ii == 1)
|
|
struct_set_multi_code = jit_get_ip().ptr;
|
|
else
|
|
struct_set_code = jit_get_ip().ptr;
|
|
/* Save value to install: */
|
|
save_struct_temp(jitter);
|
|
}
|
|
|
|
mz_prolog(JIT_V1);
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Slow path: non-struct proc, or argument type is
|
|
bad for a getter. */
|
|
refslow = _jit.x.pc;
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES((kind == 3) ? 2 : 1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_str_p(JIT_RUNSTACK, JIT_R1);
|
|
if (kind == 3) {
|
|
restore_struct_temp(jitter, JIT_V1);
|
|
jit_stxi_p(WORDS_TO_BYTES(1), JIT_RUNSTACK, JIT_V1);
|
|
}
|
|
jit_movi_i(JIT_V1, ((kind == 3) ? 2 : 1));
|
|
jit_prepare(3);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_p(JIT_V1);
|
|
jit_pusharg_p(JIT_R0);
|
|
if (ii == 1) {
|
|
(void)mz_finish(_scheme_apply_multi_from_native);
|
|
} else {
|
|
(void)mz_finish(_scheme_apply_from_native);
|
|
}
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES((kind == 3) ? 2 : 1));
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
if (!for_branch) {
|
|
mz_epilog(JIT_V1);
|
|
bref5 = NULL;
|
|
bref6 = NULL;
|
|
} else {
|
|
/* Need to check for true or false. */
|
|
bref5 = jit_beqi_p(jit_forward(), JIT_R0, scheme_false);
|
|
bref6 = jit_jmpi(jit_forward());
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
/* Continue trying fast path: check proc */
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(refslow, JIT_R2, scheme_prim_type);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Primitive_Proc *)0x0)->pp.flags);
|
|
if (kind == 3) {
|
|
jit_andi_i(JIT_R2, JIT_R2, SCHEME_PRIM_STRUCT_OTHER_TYPE_MASK);
|
|
(void)jit_bnei_i(refslow, JIT_R2, SCHEME_PRIM_STRUCT_TYPE_INDEXED_SETTER);
|
|
} else {
|
|
(void)jit_bmci_i(refslow, JIT_R2, ((kind == 1)
|
|
? SCHEME_PRIM_IS_STRUCT_PRED
|
|
: SCHEME_PRIM_IS_STRUCT_INDEXED_GETTER));
|
|
}
|
|
CHECK_LIMIT();
|
|
/* Check argument: */
|
|
if (kind == 1) {
|
|
bref1 = jit_bmsi_ul(jit_forward(), JIT_R1, 0x1);
|
|
jit_ldxi_s(JIT_R2, JIT_R1, &((Scheme_Object *)0x0)->type);
|
|
__START_INNER_TINY__(1);
|
|
ref2 = jit_beqi_i(jit_forward(), JIT_R2, scheme_structure_type);
|
|
__END_INNER_TINY__(1);
|
|
bref2 = jit_bnei_i(jit_forward(), JIT_R2, scheme_proc_struct_type);
|
|
} else {
|
|
(void)jit_bmsi_ul(refslow, JIT_R1, 0x1);
|
|
jit_ldxi_s(JIT_R2, JIT_R1, &((Scheme_Object *)0x0)->type);
|
|
__START_INNER_TINY__(1);
|
|
ref2 = jit_beqi_i(jit_forward(), JIT_R2, scheme_structure_type);
|
|
__END_INNER_TINY__(1);
|
|
(void)jit_bnei_i(refslow, JIT_R2, scheme_proc_struct_type);
|
|
bref1 = bref2 = NULL;
|
|
}
|
|
__START_INNER_TINY__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_INNER_TINY__(1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Put argument struct type in R2, target struct type in V1 */
|
|
jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val);
|
|
if (kind >= 2) {
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Struct_Proc_Info *)0x0)->struct_type);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
/* common case: types are the same */
|
|
if (kind >= 2) {
|
|
__START_INNER_TINY__(1);
|
|
bref8 = jit_beqr_p(jit_forward(), JIT_R2, JIT_V1);
|
|
__END_INNER_TINY__(1);
|
|
} else
|
|
bref8 = NULL;
|
|
|
|
jit_ldxi_i(JIT_R2, JIT_R2, &((Scheme_Struct_Type *)0x0)->name_pos);
|
|
jit_ldxi_i(JIT_V1, JIT_V1, &((Scheme_Struct_Type *)0x0)->name_pos);
|
|
/* Now R2 is argument depth, V1 is target depth */
|
|
if (kind == 1) {
|
|
bref3 = jit_bltr_i(jit_forward(), JIT_R2, JIT_V1);
|
|
} else {
|
|
(void)jit_bltr_i(refslow, JIT_R2, JIT_V1);
|
|
bref3 = NULL;
|
|
}
|
|
CHECK_LIMIT();
|
|
/* Lookup argument type at target type depth, put it in R2: */
|
|
jit_lshi_ul(JIT_R2, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_R2, JIT_R2, &((Scheme_Struct_Type *)0x0)->parent_types);
|
|
jit_ldxi_p(JIT_V1, JIT_R1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldxr_p(JIT_R2, JIT_V1, JIT_R2);
|
|
CHECK_LIMIT();
|
|
|
|
/* Re-load target type into V1: */
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val);
|
|
if (kind >= 2) {
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Struct_Proc_Info *)0x0)->struct_type);
|
|
}
|
|
|
|
if (kind == 1) {
|
|
bref4 = jit_bner_p(jit_forward(), JIT_R2, JIT_V1);
|
|
|
|
/* True branch: */
|
|
if (!for_branch) {
|
|
(void)jit_movi_p(JIT_R0, scheme_true);
|
|
} else {
|
|
mz_patch_ucbranch(bref6);
|
|
#ifdef MZ_USE_JIT_I386
|
|
# ifndef X86_ALIGN_STACK
|
|
jit_popr_p(JIT_V1);
|
|
# endif
|
|
#endif
|
|
}
|
|
mz_epilog(JIT_V1);
|
|
|
|
/* False branch: */
|
|
mz_patch_branch(bref1);
|
|
mz_patch_branch(bref2);
|
|
mz_patch_branch(bref3);
|
|
mz_patch_branch(bref4);
|
|
if (for_branch) {
|
|
mz_patch_branch(bref5);
|
|
restore_struct_temp(jitter, JIT_V1);
|
|
mz_epilog_without_jmp();
|
|
jit_jmpr(JIT_V1);
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, scheme_false);
|
|
mz_epilog(JIT_V1);
|
|
}
|
|
} else {
|
|
(void)jit_bner_p(refslow, JIT_R2, JIT_V1);
|
|
bref4 = NULL;
|
|
__START_INNER_TINY__(1);
|
|
mz_patch_branch(bref8);
|
|
__END_INNER_TINY__(1);
|
|
/* Extract field */
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val);
|
|
jit_ldxi_i(JIT_V1, JIT_V1, &((Struct_Proc_Info *)0x0)->field);
|
|
jit_lshi_ul(JIT_V1, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_V1, JIT_V1, &((Scheme_Structure *)0x0)->slots);
|
|
if (kind == 3) {
|
|
restore_struct_temp(jitter, JIT_R0);
|
|
jit_stxr_p(JIT_V1, JIT_R1, JIT_R0);
|
|
(void)jit_movi_p(JIT_R0, scheme_void);
|
|
} else {
|
|
jit_ldxr_p(JIT_R0, JIT_R1, JIT_V1);
|
|
}
|
|
mz_epilog(JIT_V1);
|
|
}
|
|
CHECK_LIMIT();
|
|
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
if (jitter->retain_start) {
|
|
code_end = jit_get_ip().ptr;
|
|
add_symbol((unsigned long)code, (unsigned long)code_end - 1, scheme_false, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* *** retry_alloc_code[{_keep_r0_r1,_keep_fpr1}] *** */
|
|
for (i = 0; i < 3; i++) {
|
|
if (!i)
|
|
retry_alloc_code = jit_get_ip().ptr;
|
|
else if (i == 1)
|
|
retry_alloc_code_keep_r0_r1 = jit_get_ip().ptr;
|
|
else
|
|
retry_alloc_code_keep_fpr1 = jit_get_ip().ptr;
|
|
|
|
mz_prolog(JIT_V1);
|
|
generate_alloc_retry(jitter, i);
|
|
CHECK_LIMIT();
|
|
mz_epilog(JIT_V1);
|
|
CHECK_LIMIT();
|
|
}
|
|
#endif
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
/* *** make_list_code *** */
|
|
/* R2 has length, args are on runstack */
|
|
for (i = 0; i < 2; i++) {
|
|
jit_insn *ref, *refnext;
|
|
|
|
if (i == 0)
|
|
make_list_code = jit_get_ip().ptr;
|
|
else
|
|
make_list_star_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_R1);
|
|
jit_lshi_l(JIT_R2, JIT_R2, JIT_LOG_WORD_SIZE);
|
|
if (i == 0)
|
|
(void)jit_movi_p(JIT_R0, &scheme_null);
|
|
else {
|
|
jit_subi_l(JIT_R2, JIT_R2, JIT_WORD_SIZE);
|
|
jit_ldxr_p(JIT_R0, JIT_RUNSTACK, JIT_R2);
|
|
}
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
ref = jit_beqi_l(jit_forward(), JIT_R2, 0);
|
|
refnext = _jit.x.pc;
|
|
__END_SHORT_JUMPS__(1);
|
|
CHECK_LIMIT();
|
|
|
|
jit_subi_l(JIT_R2, JIT_R2, JIT_WORD_SIZE);
|
|
jit_ldxr_p(JIT_R1, JIT_RUNSTACK, JIT_R2);
|
|
mz_set_local_p(JIT_R2, JIT_LOCAL3);
|
|
|
|
generate_cons_alloc(jitter, 1, 1);
|
|
CHECK_LIMIT();
|
|
|
|
mz_get_local_p(JIT_R2, JIT_LOCAL3);
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
(void)jit_bnei_l(refnext, JIT_R2, 0);
|
|
mz_patch_branch(ref);
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
mz_epilog(JIT_R1);
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int do_generate_more_common(mz_jit_state *jitter, void *_data)
|
|
{
|
|
/* *** check_proc_extract_code *** */
|
|
/* arguments are on the Scheme stack */
|
|
{
|
|
void *code_end;
|
|
jit_insn *ref, *ref2, *ref3, *refslow;
|
|
|
|
struct_proc_extract_code = jit_get_ip().ptr;
|
|
mz_prolog(JIT_V1);
|
|
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
mz_rs_ldr(JIT_R0);
|
|
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Slow path: call C implementation */
|
|
refslow = _jit.x.pc;
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_movi_i(JIT_V1, 5);
|
|
jit_prepare(2);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
jit_pusharg_p(JIT_V1);
|
|
(void)mz_finish(scheme_extract_checked_procedure);
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
mz_epilog(JIT_V1);
|
|
|
|
/* Continue trying fast path: check proc */
|
|
mz_patch_branch(ref);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &((Scheme_Object *)0x0)->type);
|
|
(void)jit_bnei_i(refslow, JIT_R2, scheme_struct_type_type);
|
|
jit_ldxi_s(JIT_R2, JIT_R0, &MZ_OPT_HASH_KEY(&((Scheme_Struct_Type *)0x0)->iso));
|
|
(void)jit_bmci_ul(refslow, JIT_R2, STRUCT_TYPE_CHECKED_PROC);
|
|
CHECK_LIMIT();
|
|
|
|
mz_rs_ldxi(JIT_R1, 1);
|
|
(void)jit_bmsi_ul(refslow, JIT_R1, 0x1);
|
|
jit_ldxi_s(JIT_R2, JIT_R1, &((Scheme_Object *)0x0)->type);
|
|
__START_INNER_TINY__(1);
|
|
ref2 = jit_beqi_i(jit_forward(), JIT_R2, scheme_structure_type);
|
|
__END_INNER_TINY__(1);
|
|
(void)jit_bnei_i(refslow, JIT_R2, scheme_proc_struct_type);
|
|
__START_INNER_TINY__(1);
|
|
mz_patch_branch(ref2);
|
|
__END_INNER_TINY__(1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Put argument struct type in R2, target struct type is in R0 */
|
|
jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldxi_i(JIT_R2, JIT_R2, &((Scheme_Struct_Type *)0x0)->name_pos);
|
|
jit_ldxi_i(JIT_V1, JIT_R0, &((Scheme_Struct_Type *)0x0)->name_pos);
|
|
|
|
/* Now R2 is argument depth, V1 is target depth */
|
|
(void)jit_bltr_i(refslow, JIT_R2, JIT_V1);
|
|
CHECK_LIMIT();
|
|
/* Lookup argument type at target type depth, put it in R2: */
|
|
jit_lshi_ul(JIT_R2, JIT_V1, JIT_LOG_WORD_SIZE);
|
|
jit_addi_p(JIT_R2, JIT_R2, &((Scheme_Struct_Type *)0x0)->parent_types);
|
|
jit_ldxi_p(JIT_V1, JIT_R1, &((Scheme_Structure *)0x0)->stype);
|
|
jit_ldxr_p(JIT_R2, JIT_V1, JIT_R2);
|
|
CHECK_LIMIT();
|
|
(void)jit_bner_p(refslow, JIT_R2, JIT_R0);
|
|
|
|
/* Type matches. Extract checker. */
|
|
jit_ldxi_p(JIT_V1, JIT_R1, &(((Scheme_Structure *)0x0)->slots[0]));
|
|
|
|
/* Checker is in V1. Set up args on runstack, then apply it. */
|
|
mz_rs_dec(2);
|
|
mz_rs_ldxi(JIT_R2, 5);
|
|
mz_rs_str(JIT_R2);
|
|
mz_rs_ldxi(JIT_R2, 6);
|
|
mz_rs_stxi(1, JIT_R2);
|
|
CHECK_LIMIT();
|
|
mz_rs_sync();
|
|
|
|
__END_SHORT_JUMPS__(1);
|
|
generate_non_tail_call(jitter, 2, 0, 1, 0, 0, 0, 0);
|
|
CHECK_LIMIT();
|
|
__START_SHORT_JUMPS__(1);
|
|
|
|
mz_rs_inc(2);
|
|
mz_rs_sync();
|
|
ref3 = jit_bnei_p(refslow, JIT_R0, scheme_false);
|
|
CHECK_LIMIT();
|
|
|
|
/* Check failed. Apply the failure procedure. */
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_prepare(1);
|
|
jit_pusharg_p(JIT_RUNSTACK);
|
|
(void)mz_finish(apply_checked_fail);
|
|
CHECK_LIMIT();
|
|
jit_retval(JIT_R0);
|
|
VALIDATE_RESULT(JIT_R0);
|
|
mz_epilog(JIT_V1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Check passed. Extract the procedure. */
|
|
mz_patch_branch(ref3);
|
|
mz_rs_ldxi(JIT_R1, 1);
|
|
jit_ldxi_p(JIT_R0, JIT_R1, &(((Scheme_Structure *)0x0)->slots[1]));
|
|
|
|
mz_epilog(JIT_V1);
|
|
CHECK_LIMIT();
|
|
|
|
__END_SHORT_JUMPS__(1);
|
|
|
|
if (jitter->retain_start) {
|
|
code_end = jit_get_ip().ptr;
|
|
add_symbol((unsigned long)struct_proc_extract_code, (unsigned long)code_end - 1, scheme_false, 0);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef CAN_INLINE_ALLOC
|
|
static int generate_alloc_retry(mz_jit_state *jitter, int i)
|
|
{
|
|
#ifdef JIT_USE_FP_OPS
|
|
if (i == 2) {
|
|
(void)jit_sti_d_fppop(&save_fp, JIT_FPR1);
|
|
}
|
|
#endif
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
jit_prepare(2);
|
|
CHECK_LIMIT();
|
|
if (i == 1) {
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
} else {
|
|
(void)jit_movi_p(JIT_R0, NULL);
|
|
jit_pusharg_p(JIT_R0);
|
|
jit_pusharg_p(JIT_R0);
|
|
}
|
|
(void)mz_finish(prepare_retry_alloc);
|
|
jit_retval(JIT_R0);
|
|
if (i == 1) {
|
|
jit_ldi_l(JIT_R1, &retry_alloc_r1);
|
|
}
|
|
#ifdef JIT_USE_FP_OPS
|
|
if (i == 2) {
|
|
(void)jit_ldi_d_fppush(JIT_FPR1, &save_fp);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
typedef struct {
|
|
Scheme_Closure_Data *data;
|
|
void *arity_code, *code, *tail_code, *code_end, **patch_depth;
|
|
int max_extra, max_depth;
|
|
Scheme_Native_Closure *nc;
|
|
int argc;
|
|
Scheme_Object **argv;
|
|
} Generate_Closure_Data;
|
|
|
|
static int do_generate_closure(mz_jit_state *jitter, void *_data)
|
|
{
|
|
Generate_Closure_Data *gdata = (Generate_Closure_Data *)_data;
|
|
Scheme_Closure_Data *data = gdata->data;
|
|
void *code, *tail_code, *code_end, *arity_code;
|
|
int i, r, cnt, has_rest, is_method, num_params, to_args, argc;
|
|
Scheme_Object **argv;
|
|
|
|
code = jit_get_ip().ptr;
|
|
|
|
jitter->nc = gdata->nc;
|
|
|
|
argc = gdata->argc;
|
|
argv = gdata->argv;
|
|
|
|
generate_function_prolog(jitter, code,
|
|
/* max_extra_pushed may be wrong the first time around,
|
|
but it will be right the last time around */
|
|
WORDS_TO_BYTES(data->max_let_depth + jitter->max_extra_pushed));
|
|
CHECK_LIMIT();
|
|
|
|
cnt = generate_function_getarg(jitter,
|
|
(SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST),
|
|
data->num_params);
|
|
CHECK_LIMIT();
|
|
|
|
/* A tail call with arity checking can start here.
|
|
(This is a little reundant checking when `code' is the
|
|
etry point, but that's the slow path anyway.) */
|
|
|
|
has_rest = ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST) ? 1 : 0);
|
|
is_method = ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_IS_METHOD) ? 1 : 0);
|
|
num_params = data->num_params;
|
|
if (num_params && has_rest)
|
|
--num_params;
|
|
|
|
if (num_params < MAX_SHARED_ARITY_CHECK) {
|
|
void *shared_arity_code;
|
|
|
|
shared_arity_code = shared_arity_check[num_params][has_rest][is_method];
|
|
if (!shared_arity_code) {
|
|
shared_arity_code = generate_lambda_simple_arity_check(num_params, has_rest, is_method, 1);
|
|
shared_arity_check[num_params][has_rest][is_method] = shared_arity_code;
|
|
}
|
|
|
|
arity_code = jit_get_ip().ptr;
|
|
|
|
if (!has_rest)
|
|
(void)jit_bnei_i(shared_arity_code, JIT_R1, num_params);
|
|
else
|
|
(void)jit_blti_i(shared_arity_code, JIT_R1, num_params);
|
|
} else
|
|
arity_code = generate_lambda_simple_arity_check(num_params, has_rest, is_method, 0);
|
|
|
|
/* A tail call starts here. Caller must ensure that the
|
|
stack is big enough, right number of arguments, closure
|
|
is in R0. If the closure has a rest arg, also ensure
|
|
argc in R1 and argv in R2. */
|
|
tail_code = jit_get_ip().ptr;
|
|
|
|
/* 0 params and has_rest => (lambda args E) where args is not in E,
|
|
so accept any number of arguments and ignore them. */
|
|
|
|
if (has_rest && data->num_params) {
|
|
/* If runstack == argv and argc == cnt, then we didn't
|
|
copy args down, and we need to make room for scheme_null. */
|
|
jit_insn *ref, *ref2, *ref3;
|
|
|
|
CHECK_LIMIT();
|
|
|
|
__START_SHORT_JUMPS__(cnt < 100);
|
|
|
|
ref = jit_bner_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
|
|
ref3 = jit_bgti_p(jit_forward(), JIT_R1, cnt);
|
|
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(cnt+1));
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
for (i = cnt; i--; ) {
|
|
jit_ldxi_p(JIT_V1, JIT_R2, WORDS_TO_BYTES(i));
|
|
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_V1);
|
|
CHECK_LIMIT();
|
|
}
|
|
(void)jit_movi_p(JIT_V1, scheme_null);
|
|
jit_stxi_p(WORDS_TO_BYTES(cnt), JIT_RUNSTACK, JIT_V1);
|
|
ref2 = jit_jmpi(jit_forward());
|
|
CHECK_LIMIT();
|
|
|
|
/* Build a list for extra arguments: */
|
|
mz_patch_branch(ref);
|
|
mz_patch_branch(ref3);
|
|
#ifndef JIT_PRECISE_GC
|
|
if (data->closure_size)
|
|
#endif
|
|
{
|
|
mz_pushr_p(JIT_R0);
|
|
mz_rs_sync();
|
|
}
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
CHECK_LIMIT();
|
|
mz_prepare(3);
|
|
jit_movi_i(JIT_V1, cnt);
|
|
jit_pusharg_i(JIT_V1);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_i(JIT_R1);
|
|
CHECK_LIMIT();
|
|
(void)mz_finish(scheme_build_list_offset);
|
|
jit_retval(JIT_V1);
|
|
#ifndef JIT_PRECISE_GC
|
|
if (data->closure_size)
|
|
#endif
|
|
{
|
|
mz_popr_p(JIT_R0);
|
|
mz_rs_sync();
|
|
}
|
|
jit_stxi_p(WORDS_TO_BYTES(cnt), JIT_RUNSTACK, JIT_V1);
|
|
mz_patch_ucbranch(ref2); /* jump here if we copied and produced null */
|
|
CHECK_LIMIT();
|
|
|
|
__END_SHORT_JUMPS__(cnt < 100);
|
|
|
|
has_rest = 1;
|
|
if (argc < (data->num_params - 1)) {
|
|
argv = NULL;
|
|
argc = 0;
|
|
}
|
|
} else {
|
|
has_rest = 0;
|
|
if (argc != data->num_params) {
|
|
argv = NULL;
|
|
argc = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef JIT_PRECISE_GC
|
|
/* Keeping the native-closure code pointer on the runstack ensures
|
|
that the code won't be GCed while we're running it. If the
|
|
closure is empty, it's ok, faster, and useful to keep it,
|
|
otherwise keep just the code pointer for space safety. */
|
|
if (!data->closure_size) {
|
|
jitter->closure_self_on_runstack = 1;
|
|
mz_pushr_p(JIT_R0); /* no sync */
|
|
} else {
|
|
jit_ldxi_p(JIT_R1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
mz_pushr_p(JIT_R1); /* no sync */
|
|
}
|
|
to_args = 0;
|
|
#else
|
|
to_args = 0;
|
|
#endif
|
|
|
|
/* Extract closure to runstack: */
|
|
cnt = data->closure_size;
|
|
to_args += cnt;
|
|
if (cnt) {
|
|
mz_rs_dec(cnt);
|
|
CHECK_RUNSTACK_OVERFLOW();
|
|
|
|
for (i = cnt; i--; ) {
|
|
int pos;
|
|
pos = WORDS_TO_BYTES(i) + (long)&((Scheme_Native_Closure *)0x0)->vals;
|
|
jit_ldxi_p(JIT_R1, JIT_R0, pos);
|
|
mz_rs_stxi(i, JIT_R1);
|
|
CHECK_LIMIT();
|
|
}
|
|
}
|
|
|
|
mz_rs_sync();
|
|
|
|
/* If we have a letrec context, record arities */
|
|
if (data->context && SAME_TYPE(SCHEME_TYPE(data->context), scheme_letrec_type)) {
|
|
Scheme_Letrec *lr = (Scheme_Letrec *)data->context;
|
|
int pos, self_pos = -1;
|
|
for (i = data->closure_size; i--; ) {
|
|
pos = data->closure_map[i];
|
|
if (pos < lr->count) {
|
|
Scheme_Closure_Data *data2 = (Scheme_Closure_Data *)lr->procs[pos];
|
|
mz_runstack_closure_pushed(jitter,
|
|
(data2->num_params
|
|
- ((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_HAS_REST)
|
|
? 1
|
|
: 0)),
|
|
(((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_PRESERVES_MARKS)
|
|
? NATIVE_PRESERVES_MARKS
|
|
: 0)
|
|
| ((SCHEME_CLOSURE_DATA_FLAGS(data2) & CLOS_SINGLE_RESULT)
|
|
? NATIVE_IS_SINGLE_RESULT
|
|
: 0)));
|
|
if (SAME_OBJ(lr->procs[pos], (Scheme_Object *)data)) {
|
|
self_pos = i;
|
|
}
|
|
} else
|
|
mz_runstack_pushed(jitter, 1);
|
|
}
|
|
if ((self_pos >= 0) && !has_rest) {
|
|
jitter->self_pos = self_pos;
|
|
jitter->self_closure_size = data->closure_size;
|
|
}
|
|
} else {
|
|
mz_runstack_pushed(jitter, cnt);
|
|
|
|
/* A define-values context? */
|
|
if (data->context && SAME_TYPE(SCHEME_TYPE(data->context), scheme_toplevel_type)) {
|
|
jitter->self_toplevel_pos = SCHEME_TOPLEVEL_POS(data->context);
|
|
jitter->self_closure_size = data->closure_size;
|
|
}
|
|
}
|
|
|
|
LOG_IT(("PROC: %s, %d args, flags: %x\n",
|
|
(data->name ? scheme_format_utf8("~s", 2, 1, &data->name, NULL) : "???"),
|
|
data->num_params,
|
|
SCHEME_CLOSURE_DATA_FLAGS(data)));
|
|
FOR_LOG(jitter->log_depth++);
|
|
|
|
jitter->self_data = data;
|
|
|
|
jitter->self_restart_code = jit_get_ip().ptr;
|
|
if (!has_rest)
|
|
jitter->self_nontail_code = tail_code;
|
|
|
|
jitter->self_to_closure_delta = jitter->self_pos;
|
|
jitter->closure_to_args_delta = to_args;
|
|
jitter->example_argc = argc;
|
|
jitter->example_argv = argv;
|
|
|
|
/* Generate code for the body: */
|
|
jitter->need_set_rs = 1;
|
|
r = generate(data->code, jitter, 1, 1, JIT_R0); /* no need for sync */
|
|
/* Result is in JIT_R0 */
|
|
|
|
CHECK_LIMIT();
|
|
|
|
/* r == 2 => tail call performed */
|
|
if (r != 2) {
|
|
jit_movr_p(JIT_RET, JIT_R0);
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
}
|
|
|
|
code_end = jit_get_ip().ptr;
|
|
|
|
if (jitter->retain_start) {
|
|
gdata->arity_code = arity_code;
|
|
gdata->code = code;
|
|
gdata->tail_code = tail_code;
|
|
gdata->max_extra = jitter->max_extra_pushed;
|
|
gdata->max_depth = jitter->max_depth;
|
|
gdata->code_end = code_end;
|
|
gdata->patch_depth = jitter->patch_depth;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void on_demand_generate_lambda(Scheme_Native_Closure *nc, int argc, Scheme_Object **argv)
|
|
{
|
|
Scheme_Native_Closure_Data *ndata = nc->code;
|
|
Scheme_Closure_Data *data;
|
|
Generate_Closure_Data gdata;
|
|
void *code, *tail_code, *arity_code;
|
|
int max_depth;
|
|
|
|
data = ndata->u2.orig_code;
|
|
|
|
gdata.data = data;
|
|
gdata.nc = nc;
|
|
gdata.argc = argc;
|
|
gdata.argv = argv;
|
|
|
|
scheme_delay_load_closure(data);
|
|
|
|
generate_one(NULL, do_generate_closure, &gdata, 1, data->name, ndata);
|
|
|
|
if (gdata.max_depth > data->max_let_depth) {
|
|
scheme_console_printf("Bad max depth! Given %d, counted %d.\n", data->max_let_depth, gdata.max_depth);
|
|
abort();
|
|
}
|
|
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_PRESERVES_MARKS)
|
|
SCHEME_NATIVE_CLOSURE_DATA_FLAGS(ndata) |= NATIVE_PRESERVES_MARKS;
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_SINGLE_RESULT)
|
|
SCHEME_NATIVE_CLOSURE_DATA_FLAGS(ndata) |= NATIVE_IS_SINGLE_RESULT;
|
|
|
|
arity_code = gdata.arity_code;
|
|
code = gdata.code;
|
|
tail_code = gdata.tail_code;
|
|
|
|
if (data->name) {
|
|
add_symbol((unsigned long)code, (unsigned long)gdata.code_end - 1, data->name, 1);
|
|
} else {
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
add_symbol((unsigned long)code, (unsigned long)gdata.code_end - 1, scheme_null, 1);
|
|
#endif
|
|
}
|
|
|
|
/* Add a couple of extra slots to computed let-depth, in case
|
|
we haven't quite computed right for inlined uses, etc. */
|
|
max_depth = WORDS_TO_BYTES(data->max_let_depth + gdata.max_extra + 2);
|
|
|
|
/* max_let_depth is used for flags by generate_lambda: */
|
|
if (ndata->max_let_depth & 0x1) {
|
|
data->code = NULL;
|
|
}
|
|
data->context = NULL;
|
|
if (ndata->max_let_depth & 0x2) {
|
|
Scheme_Native_Closure_Data *case_lam;
|
|
case_lam = ((Scheme_Native_Closure_Data_Plus_Case *)ndata)->case_lam;
|
|
if (case_lam->max_let_depth < max_depth)
|
|
case_lam->max_let_depth = max_depth;
|
|
}
|
|
|
|
while (gdata.patch_depth) {
|
|
void **pd;
|
|
pd = (void **)gdata.patch_depth;
|
|
gdata.patch_depth = pd[1];
|
|
jit_patch_movi(((jit_insn *)(*pd)), (void *)(long)max_depth);
|
|
}
|
|
|
|
ndata->code = code;
|
|
ndata->u.tail_code = tail_code;
|
|
ndata->arity_code = arity_code;
|
|
ndata->u2.name = data->name;
|
|
/* Let-depth is in bytes instead of words: */
|
|
ndata->max_let_depth = max_depth;
|
|
}
|
|
|
|
static void on_demand()
|
|
{
|
|
/* On runstack: closure (nearest), argc, argv (deepest) */
|
|
Scheme_Object *c, *argc, **argv;
|
|
|
|
c = MZ_RUNSTACK[0];
|
|
argc = MZ_RUNSTACK[1];
|
|
argv = (Scheme_Object **)MZ_RUNSTACK[2];
|
|
|
|
on_demand_generate_lambda((Scheme_Native_Closure *)c, SCHEME_INT_VAL(argc), argv);
|
|
}
|
|
|
|
Scheme_Native_Closure_Data *scheme_generate_lambda(Scheme_Closure_Data *data, int clear_code_after_jit,
|
|
Scheme_Native_Closure_Data *case_lam)
|
|
{
|
|
Scheme_Native_Closure_Data *ndata;
|
|
|
|
if (!check_arity_code) {
|
|
/* Create shared code used for stack-overflow handling, etc.: */
|
|
generate_one(NULL, do_generate_common, NULL, 0, NULL, NULL);
|
|
generate_one(NULL, do_generate_more_common, NULL, 0, NULL, NULL);
|
|
}
|
|
|
|
if (!case_lam) {
|
|
ndata = MALLOC_ONE_RT(Scheme_Native_Closure_Data);
|
|
#ifdef MZTAG_REQUIRED
|
|
ndata->iso.so.type = scheme_rt_native_code;
|
|
#endif
|
|
} else {
|
|
Scheme_Native_Closure_Data_Plus_Case *ndatap;
|
|
ndatap = MALLOC_ONE_RT(Scheme_Native_Closure_Data_Plus_Case);
|
|
ndatap->case_lam = case_lam;
|
|
ndata = (Scheme_Native_Closure_Data *)ndatap;
|
|
#ifdef MZTAG_REQUIRED
|
|
ndata->iso.so.type = scheme_rt_native_code_plus_case;
|
|
#endif
|
|
}
|
|
ndata->code = on_demand_jit_code;
|
|
ndata->u.tail_code = on_demand_jit_arity_code;
|
|
ndata->arity_code = on_demand_jit_arity_code;
|
|
ndata->u2.orig_code = data;
|
|
ndata->closure_size = data->closure_size;
|
|
ndata->max_let_depth = 0x4 | (case_lam ? 0x2 : 0) | (clear_code_after_jit ? 0x1 : 0);
|
|
|
|
#if 0
|
|
/* Compile immediately: */
|
|
on_demand_generate_lambda(ndata);
|
|
#endif
|
|
|
|
return ndata;
|
|
}
|
|
|
|
static int generate_simple_arity_check(mz_jit_state *jitter, int num_params, int has_rest, int is_method)
|
|
{
|
|
/* JIT_R0 is closure */
|
|
/* JIT_R1 is argc */
|
|
/* JIT_R2 is argv */
|
|
/* If arity matches, JIT_RUNSTACK and JIT_RUNSTACK_BASE should be preserved */
|
|
/* That leaves just JIT_V1 to use if arity is ok. */
|
|
/* This code expects a return context with 3 arguments, so make sure that's
|
|
true dynamically for all jumps to the code. Also, at JIT time, make sure
|
|
that jitter is initialized with a size-3 prolog. */
|
|
|
|
jit_insn *ref, *ref2;
|
|
|
|
__START_TINY_JUMPS__(1);
|
|
|
|
if (!has_rest)
|
|
ref = jit_bnei_i(jit_forward(), JIT_R1, num_params);
|
|
else
|
|
ref = jit_blti_i(jit_forward(), JIT_R1, num_params);
|
|
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->u.tail_code);
|
|
jit_jmpr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
|
|
/* Failed */
|
|
mz_patch_branch(ref);
|
|
|
|
/* If argc is negative, this was really a request for arity checking or reporting */
|
|
ref = jit_blti_i(jit_forward(), JIT_R1, 0x0);
|
|
|
|
/* Not negative, so report run-time arity mismatch */
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
CHECK_LIMIT();
|
|
(void)mz_nonrs_finish(wrong_argument_count);
|
|
CHECK_LIMIT();
|
|
|
|
/* Arity check or reporting. If argv is NULL, it's a reporting request */
|
|
mz_patch_branch(ref);
|
|
ref = jit_beqi_i(jit_forward(), JIT_R2, 0x0);
|
|
|
|
/* Arity check --- try again with argv cast to argc: */
|
|
jit_subi_i(JIT_R2, JIT_R2, 1);
|
|
if (!has_rest)
|
|
ref2 = jit_bnei_i(jit_forward(), JIT_R2, num_params);
|
|
else
|
|
ref2 = jit_blti_i(jit_forward(), JIT_R2, num_params);
|
|
CHECK_LIMIT();
|
|
jit_movi_i(JIT_RET, 1);
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
mz_patch_branch(ref2);
|
|
jit_movi_i(JIT_RET, 0);
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
CHECK_LIMIT();
|
|
|
|
/* Finally, we know that it was an arity-report request */
|
|
mz_patch_branch(ref);
|
|
if (!has_rest)
|
|
(void)jit_movi_p(JIT_R0, scheme_make_integer(num_params));
|
|
else
|
|
(void)jit_movi_p(JIT_R0, scheme_make_integer(-(num_params+1)));
|
|
CHECK_LIMIT();
|
|
if (is_method) {
|
|
mz_prepare(1);
|
|
jit_pusharg_p(JIT_R0);
|
|
(void)mz_nonrs_finish(scheme_box);
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
} else {
|
|
jit_movr_p(JIT_RET, JIT_R0);
|
|
mz_pop_locals();
|
|
jit_ret();
|
|
}
|
|
|
|
__END_TINY_JUMPS__(1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
int num_params;
|
|
int has_rest;
|
|
int is_method;
|
|
} Generate_Arity_Check_Data;
|
|
|
|
static int do_generate_lambda_simple_arity_check(mz_jit_state *jitter, void *_data)
|
|
{
|
|
Generate_Arity_Check_Data *data = (Generate_Arity_Check_Data *)_data;
|
|
|
|
#ifdef MZ_USE_JIT_PPC
|
|
jitter->js.jitl.nbArgs = 2; /* matches check_arity_code prolog */
|
|
#endif
|
|
|
|
return generate_simple_arity_check(jitter, data->num_params, data->has_rest, data->is_method);
|
|
}
|
|
|
|
static void *generate_lambda_simple_arity_check(int num_params, int has_rest, int is_method, int permanent)
|
|
{
|
|
Generate_Arity_Check_Data data;
|
|
|
|
data.num_params = num_params;
|
|
data.has_rest = has_rest;
|
|
data.is_method = is_method;
|
|
|
|
return generate_one(NULL, do_generate_lambda_simple_arity_check, &data, !permanent, NULL, NULL);
|
|
}
|
|
|
|
static int generate_case_lambda_dispatch(mz_jit_state *jitter, Scheme_Case_Lambda *c, Scheme_Native_Closure_Data *ndata,
|
|
int do_getarg)
|
|
{
|
|
/* See top of generate_simple_arity_check for register and other context info. */
|
|
Scheme_Closure_Data *data;
|
|
Scheme_Object *o;
|
|
int i, cnt, has_rest, offset, num_params;
|
|
jit_insn *ref = NULL;
|
|
|
|
cnt = c->count;
|
|
for (i = 0; i < cnt; i++) {
|
|
/* Check arity for this case: */
|
|
o = c->array[i];
|
|
if (SCHEME_PROCP(o))
|
|
o = (Scheme_Object *)((Scheme_Closure *)o)->code;
|
|
data = (Scheme_Closure_Data *)o;
|
|
|
|
num_params = data->num_params;
|
|
has_rest = ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST) ? 1 : 0);
|
|
if (has_rest && num_params)
|
|
--num_params;
|
|
|
|
/* Check for arity match - not needed in getarg mode if this
|
|
is the last case, since the arity check as already done. */
|
|
if (!do_getarg || (i < cnt - 1)) {
|
|
if (!has_rest)
|
|
ref = jit_bnei_i(jit_forward(), JIT_R1, num_params);
|
|
else
|
|
ref = jit_blti_i(jit_forward(), JIT_R1, num_params);
|
|
}
|
|
|
|
/* Function-argument handling for this case: */
|
|
if (do_getarg) {
|
|
generate_function_getarg(jitter, has_rest, num_params + (has_rest ? 1 : 0));
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
/* Jump to tail-code location of the selected branch: */
|
|
offset = WORDS_TO_BYTES(i) + (unsigned long)&((Scheme_Native_Closure *)0x0)->vals;
|
|
jit_ldxi_p(JIT_R0, JIT_R0, offset);
|
|
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
|
|
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->u.tail_code);
|
|
jit_jmpr(JIT_V1);
|
|
CHECK_LIMIT();
|
|
|
|
if (!do_getarg || (i < cnt - 1)) {
|
|
mz_patch_branch(ref);
|
|
}
|
|
/* Try the next one... */
|
|
}
|
|
|
|
if (!do_getarg) {
|
|
/* Report run-time arity mismatch */
|
|
JIT_UPDATE_THREAD_RSPTR();
|
|
mz_prepare(3);
|
|
jit_pusharg_p(JIT_R2);
|
|
jit_pusharg_p(JIT_R1);
|
|
jit_pusharg_p(JIT_R0);
|
|
CHECK_LIMIT();
|
|
(void)mz_finish(wrong_argument_count);
|
|
CHECK_LIMIT();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
Scheme_Case_Lambda *c;
|
|
Scheme_Native_Closure_Data *ndata;
|
|
int is_method;
|
|
} Generate_Case_Dispatch_Data;
|
|
|
|
static int do_generate_case_lambda_dispatch(mz_jit_state *jitter, void *_data)
|
|
{
|
|
Generate_Case_Dispatch_Data *data = (Generate_Case_Dispatch_Data *)_data;
|
|
void *code, *arity_code;
|
|
|
|
code = jit_get_ip().ptr;
|
|
|
|
generate_function_prolog(jitter, code, data->ndata->max_let_depth);
|
|
CHECK_LIMIT();
|
|
|
|
if (generate_case_lambda_dispatch(jitter, data->c, data->ndata, 1)) {
|
|
arity_code = jit_get_ip().ptr;
|
|
if (generate_case_lambda_dispatch(jitter, data->c, data->ndata, 0)) {
|
|
data->ndata->code = code;
|
|
data->ndata->arity_code = arity_code;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void generate_case_lambda(Scheme_Case_Lambda *c, Scheme_Native_Closure_Data *ndata, int is_method)
|
|
{
|
|
Generate_Case_Dispatch_Data gdata;
|
|
Scheme_Closure_Data *data;
|
|
Scheme_Object *o;
|
|
int i, cnt, num_params, has_rest;
|
|
mzshort *arities;
|
|
|
|
gdata.c = c;
|
|
gdata.ndata = ndata;
|
|
gdata.is_method = is_method;
|
|
|
|
generate_one(NULL, do_generate_case_lambda_dispatch, &gdata, 1, NULL, ndata);
|
|
|
|
/* Generate arity table used by scheme_native_arity_check
|
|
and scheme_get_native_arity: */
|
|
|
|
cnt = c->count;
|
|
arities = (mzshort *)scheme_malloc_atomic(sizeof(mzshort) * (cnt + 1));
|
|
arities[cnt] = is_method;
|
|
for (i = 0; i < cnt; i++) {
|
|
o = c->array[i];
|
|
if (SCHEME_PROCP(o))
|
|
o = (Scheme_Object *)((Scheme_Closure *)o)->code;
|
|
data = (Scheme_Closure_Data *)o;
|
|
num_params = data->num_params;
|
|
has_rest = ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST) ? 1 : 0);
|
|
if (has_rest && num_params)
|
|
--num_params;
|
|
|
|
if (!has_rest)
|
|
arities[i] = num_params;
|
|
else
|
|
arities[i] = -(num_params+1);
|
|
}
|
|
ndata->u.arities = arities;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* native arity queries */
|
|
/*========================================================================*/
|
|
|
|
static int lambda_has_been_jitted(Scheme_Native_Closure_Data *ndata)
|
|
{
|
|
return (ndata->code != on_demand_jit_code);
|
|
}
|
|
|
|
int scheme_native_arity_check(Scheme_Object *closure, int argc)
|
|
{
|
|
int cnt;
|
|
|
|
cnt = ((Scheme_Native_Closure *)closure)->code->closure_size;
|
|
if (cnt < 0) {
|
|
/* Case-lambda */
|
|
int i;
|
|
mzshort *arities, v;
|
|
|
|
arities = ((Scheme_Native_Closure *)closure)->code->u.arities;
|
|
cnt = -(cnt + 1);
|
|
for (i = 0; i < cnt; i++) {
|
|
v = arities[i];
|
|
if (v < 0) {
|
|
v = -(v + 1);
|
|
if (argc >= v)
|
|
return 1;
|
|
} else if (argc == v)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (!lambda_has_been_jitted(((Scheme_Native_Closure *)closure)->code)) {
|
|
Scheme_Closure c;
|
|
c.so.type = scheme_closure_type;
|
|
c.code = ((Scheme_Native_Closure *)closure)->code->u2.orig_code;
|
|
return SCHEME_TRUEP(scheme_get_or_check_arity((Scheme_Object *)&c, argc));
|
|
}
|
|
|
|
return check_arity_code(closure, argc + 1, 0);
|
|
}
|
|
|
|
Scheme_Object *scheme_get_native_arity(Scheme_Object *closure)
|
|
{
|
|
int cnt;
|
|
|
|
cnt = ((Scheme_Native_Closure *)closure)->code->closure_size;
|
|
if (cnt < 0) {
|
|
/* Case-lambda */
|
|
Scheme_Object *l = scheme_null, *a;
|
|
int i, has_rest, is_method;
|
|
mzshort *arities, v;
|
|
|
|
arities = ((Scheme_Native_Closure *)closure)->code->u.arities;
|
|
cnt = -(cnt + 1);
|
|
is_method = arities[cnt];
|
|
for (i = cnt; i--; ) {
|
|
v = arities[i];
|
|
if (v < 0) {
|
|
v = -(v + 1);
|
|
has_rest = 1;
|
|
} else
|
|
has_rest = 0;
|
|
a = scheme_make_arity(v, has_rest ? -1 : v);
|
|
l = scheme_make_pair(a, l);
|
|
}
|
|
if (is_method)
|
|
l = scheme_box(l);
|
|
return l;
|
|
}
|
|
|
|
if (!lambda_has_been_jitted(((Scheme_Native_Closure *)closure)->code)) {
|
|
Scheme_Closure c;
|
|
Scheme_Object *a;
|
|
c.so.type = scheme_closure_type;
|
|
c.code = ((Scheme_Native_Closure *)closure)->code->u2.orig_code;
|
|
a = scheme_get_or_check_arity((Scheme_Object *)&c, -1);
|
|
if (SCHEME_CLOSURE_DATA_FLAGS(c.code) & CLOS_IS_METHOD)
|
|
a = scheme_box(a);
|
|
return a;
|
|
}
|
|
|
|
return get_arity_code(closure, 0, 0);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* stack trace */
|
|
/*========================================================================*/
|
|
|
|
typedef void *(*Get_Stack_Proc)();
|
|
|
|
#ifdef MZ_USE_JIT_PPC
|
|
# ifdef _CALL_DARWIN
|
|
# define RETURN_ADDRESS_OFFSET 2
|
|
# else
|
|
# define RETURN_ADDRESS_OFFSET 1
|
|
# endif
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
# define RETURN_ADDRESS_OFFSET 1
|
|
#endif
|
|
|
|
#define CACHE_STACK_MIN_TRIGGER 1024
|
|
|
|
#define USE_STACK_CHECK 0
|
|
|
|
#if USE_STACK_CHECK
|
|
static void check_stack(void)
|
|
{
|
|
void *p, *q;
|
|
unsigned long stack_end;
|
|
int pos = stack_cache_stack_pos;
|
|
Get_Stack_Proc gs;
|
|
|
|
gs = (Get_Stack_Proc)get_stack_pointer_code;
|
|
p = gs();
|
|
|
|
stack_end = (unsigned long)(scheme_current_thread->next
|
|
? scheme_current_thread->stack_start
|
|
: scheme_current_thread->o_start);
|
|
|
|
while (STK_COMP((unsigned long)p, stack_end)) {
|
|
q = ((void **)p)[RETURN_ADDRESS_OFFSET];
|
|
|
|
if (q == stack_cache_pop_code) {
|
|
if (!pos)
|
|
*(long *)0x0 = 1;
|
|
else {
|
|
if (stack_cache_stack[pos].stack_frame != (void *)(((void **)p) + RETURN_ADDRESS_OFFSET)) {
|
|
*(long *)0X0 = 1;
|
|
}
|
|
--pos;
|
|
}
|
|
}
|
|
|
|
q = *(void **)p;
|
|
if (STK_COMP((unsigned long)q, (unsigned long)p))
|
|
break;
|
|
p = q;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MZ_DO_NOT_INLINE(unsigned long scheme_approx_sp());
|
|
unsigned long scheme_approx_sp()
|
|
{
|
|
unsigned long p;
|
|
p = (unsigned long)&p;
|
|
return p;
|
|
}
|
|
|
|
Scheme_Object *scheme_native_stack_trace(void)
|
|
{
|
|
void *p, *q;
|
|
unsigned long stack_end, stack_start, halfway;
|
|
Scheme_Object *name, *last = NULL, *first = NULL, *tail;
|
|
int set_next_push = 0, prev_had_name = 0;
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
unw_context_t cx;
|
|
unw_cursor_t c;
|
|
int manual_unw;
|
|
unw_word_t stack_addr;
|
|
#else
|
|
Get_Stack_Proc gs;
|
|
#endif
|
|
int use_unw = 0;
|
|
|
|
if (!get_stack_pointer_code)
|
|
return NULL;
|
|
|
|
#if USE_STACK_CHECK
|
|
check_stack();
|
|
#endif
|
|
|
|
stack_start = scheme_approx_sp();
|
|
|
|
if (stack_cache_stack_pos) {
|
|
stack_end = (unsigned long)stack_cache_stack[stack_cache_stack_pos].stack_frame;
|
|
stack_end -= (RETURN_ADDRESS_OFFSET << JIT_LOG_WORD_SIZE);
|
|
tail = stack_cache_stack[stack_cache_stack_pos].cache;
|
|
} else {
|
|
stack_end = (unsigned long)scheme_current_thread->stack_start;
|
|
tail = scheme_null;
|
|
}
|
|
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
unw_set_safe_pointer_range(stack_start, stack_end);
|
|
unw_reset_bad_ptr_flag();
|
|
#endif
|
|
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
unw_getcontext(&cx);
|
|
unw_init_local(&c, &cx);
|
|
use_unw = 1;
|
|
p = NULL;
|
|
#else
|
|
gs = (Get_Stack_Proc)get_stack_pointer_code;
|
|
p = gs();
|
|
#endif
|
|
|
|
halfway = STK_DIFF(stack_end, (unsigned long)p) / 2;
|
|
if (halfway < CACHE_STACK_MIN_TRIGGER)
|
|
halfway = stack_end;
|
|
else {
|
|
#ifdef STACK_GROWS_DOWN
|
|
halfway += (unsigned long)p;
|
|
#else
|
|
halfway += stack_end;
|
|
#endif
|
|
}
|
|
|
|
while (1) {
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
if (use_unw) {
|
|
q = (void *)unw_get_ip(&c);
|
|
} else {
|
|
q = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (!use_unw) {
|
|
if (!(STK_COMP((unsigned long)p, stack_end)
|
|
&& STK_COMP(stack_start, (unsigned long)p)))
|
|
break;
|
|
q = ((void **)p)[RETURN_ADDRESS_OFFSET];
|
|
/* p is the frame pointer for the function called by q,
|
|
not for q. */
|
|
}
|
|
|
|
name = find_symbol((unsigned long)q);
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
if (name) manual_unw = 1;
|
|
#endif
|
|
|
|
if (SCHEME_FALSEP(name) || SCHEME_VOIDP(name)) {
|
|
/* Code uses special calling convention */
|
|
#ifdef MZ_USE_JIT_PPC
|
|
/* JIT_LOCAL2 has the next return address */
|
|
q = ((void **)p)[JIT_LOCAL2 >> JIT_LOG_WORD_SIZE];
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
|
|
# ifdef MZ_USE_DWARF_LIBUNWIND
|
|
if (use_unw) {
|
|
q = (void *)unw_get_frame_pointer(&c);
|
|
} else
|
|
# endif
|
|
q = *(void **)p;
|
|
|
|
/* q is now the frame pointer for the former q,
|
|
so we can find the actual q */
|
|
if (STK_COMP((unsigned long)q, stack_end)
|
|
&& STK_COMP(stack_start, (unsigned long)q)) {
|
|
if (SCHEME_VOIDP(name)) {
|
|
/* JIT_LOCAL2 has the next return address */
|
|
q = ((void **)q)[JIT_LOCAL2 >> JIT_LOG_WORD_SIZE];
|
|
} else {
|
|
/* Push after local stack of return-address proc
|
|
has the next return address */
|
|
q = ((void **)q)[-(3 + LOCAL_FRAME_SIZE + 1)];
|
|
}
|
|
} else {
|
|
q = NULL;
|
|
}
|
|
#endif
|
|
name = find_symbol((unsigned long)q);
|
|
}
|
|
|
|
if (name && !SCHEME_NULLP(name)) { /* null is used to help unwind without a true name */
|
|
name = scheme_make_pair(name, scheme_null);
|
|
if (last)
|
|
SCHEME_CDR(last) = name;
|
|
else
|
|
first = name;
|
|
last = name;
|
|
if (set_next_push) {
|
|
stack_cache_stack[stack_cache_stack_pos].cache = name;
|
|
set_next_push = 0;
|
|
}
|
|
}
|
|
|
|
/* Cache the result halfway up the stack, if possible. Only cache
|
|
on frames where the previous frame had a return address with a
|
|
name, because an arbitrary frame's return address on the stack
|
|
might not be used (depending on how the C compiler optimized the
|
|
cdode); any frame whose procedure has a name is JITted code, so
|
|
it will use the return address from the stack. */
|
|
if (STK_COMP((unsigned long)halfway, (unsigned long)p)
|
|
&& prev_had_name) {
|
|
int pos;
|
|
|
|
if (stack_cache_stack_pos >= (STACK_CACHE_SIZE - 1)) {
|
|
/* Make room on the stack */
|
|
void **z;
|
|
z = (void **)stack_cache_stack[stack_cache_stack_pos].stack_frame;
|
|
*z = stack_cache_stack[stack_cache_stack_pos].orig_return_address;
|
|
--stack_cache_stack_pos;
|
|
}
|
|
|
|
pos = ++stack_cache_stack_pos;
|
|
stack_cache_stack[pos].orig_return_address = ((void **)p)[RETURN_ADDRESS_OFFSET];
|
|
stack_cache_stack[pos].stack_frame = (void *)(((void **)p) + RETURN_ADDRESS_OFFSET);
|
|
stack_cache_stack[pos].cache = tail;
|
|
set_next_push = 1;
|
|
((void **)p)[RETURN_ADDRESS_OFFSET] = stack_cache_pop_code;
|
|
|
|
halfway = stack_end;
|
|
}
|
|
|
|
prev_had_name = !!name;
|
|
|
|
#ifdef MZ_USE_DWARF_LIBUNWIND
|
|
if (use_unw) {
|
|
if (manual_unw) {
|
|
/* A JIT-generated function, so we unwind ourselves... */
|
|
void **pp;
|
|
pp = (void **)unw_get_frame_pointer(&c);
|
|
if (!(STK_COMP((unsigned long)pp, stack_end)
|
|
&& STK_COMP(stack_start, (unsigned long)pp)))
|
|
break;
|
|
stack_addr = (unw_word_t)&(pp[RETURN_ADDRESS_OFFSET+1]);
|
|
unw_manual_step(&c, &pp[RETURN_ADDRESS_OFFSET], &pp[0],
|
|
&stack_addr, &pp[-1], &pp[-2], &pp[-3]);
|
|
manual_unw = 0;
|
|
} else {
|
|
void *prev_q = q;
|
|
unw_step(&c);
|
|
q = (void *)unw_get_ip(&c);
|
|
if ((q == prev_q)
|
|
|| unw_reset_bad_ptr_flag())
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!use_unw) {
|
|
q = *(void **)p;
|
|
if (STK_COMP((unsigned long)q, (unsigned long)p))
|
|
break;
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
if (last)
|
|
SCHEME_CDR(last) = tail;
|
|
else
|
|
first = tail;
|
|
|
|
if (SCHEME_NULLP(first))
|
|
return NULL;
|
|
|
|
return first;
|
|
}
|
|
|
|
#if 0
|
|
/* Sometimes useful for debugging MzScheme: */
|
|
void scheme_dump_stack_trace(void)
|
|
{
|
|
void *p, *q;
|
|
unsigned long stack_end, stack_start;
|
|
Get_Stack_Proc gs;
|
|
Scheme_Object *name;
|
|
|
|
gs = (Get_Stack_Proc)get_stack_pointer_code;
|
|
p = gs();
|
|
stack_start = scheme_approx_sp();
|
|
|
|
stack_end = (unsigned long)scheme_current_thread->stack_start;
|
|
|
|
while (STK_COMP((unsigned long)p, stack_end)
|
|
&& STK_COMP(stack_start, (unsigned long)p)) {
|
|
name = find_symbol((unsigned long)q);
|
|
if (SCHEME_FALSEP(name)) {
|
|
/* Code uses special calling convention */
|
|
#ifdef MZ_USE_JIT_PPC
|
|
/* JIT_LOCAL2 has the next return address */
|
|
q = ((void **)p)[JIT_LOCAL2 >> JIT_LOG_WORD_SIZE];
|
|
#endif
|
|
#ifdef MZ_USE_JIT_I386
|
|
/* Push after local stack of return-address proc
|
|
has the next return address */
|
|
q = *(void **)p;
|
|
q = ((void **)q)[-(3 + LOCAL_FRAME_SIZE + 1)];
|
|
#endif
|
|
name = find_symbol((unsigned long)q);
|
|
}
|
|
|
|
if (name) {
|
|
printf(" scheme\n");
|
|
} else {
|
|
printf(" %p\n", q);
|
|
}
|
|
|
|
q = *(void **)p;
|
|
if (STK_COMP((unsigned long)q, (unsigned long)p))
|
|
break;
|
|
p = q;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MZ_XFORM
|
|
START_XFORM_SKIP;
|
|
#endif
|
|
|
|
void scheme_flush_stack_cache()
|
|
{
|
|
void **p;
|
|
|
|
while (stack_cache_stack_pos) {
|
|
p = (void **)stack_cache_stack[stack_cache_stack_pos].stack_frame;
|
|
*p = stack_cache_stack[stack_cache_stack_pos].orig_return_address;
|
|
--stack_cache_stack_pos;
|
|
}
|
|
}
|
|
|
|
void scheme_jit_longjmp(mz_jit_jmp_buf b, int v)
|
|
{
|
|
unsigned long limit;
|
|
void **p;
|
|
|
|
limit = b->stack_frame;
|
|
|
|
while (stack_cache_stack_pos
|
|
&& STK_COMP((unsigned long)stack_cache_stack[stack_cache_stack_pos].stack_frame,
|
|
limit)) {
|
|
p = (void **)stack_cache_stack[stack_cache_stack_pos].stack_frame;
|
|
*p = stack_cache_stack[stack_cache_stack_pos].orig_return_address;
|
|
--stack_cache_stack_pos;
|
|
}
|
|
|
|
scheme_mz_longjmp(b->jb, v);
|
|
}
|
|
|
|
void scheme_jit_setjmp_prepare(mz_jit_jmp_buf b)
|
|
{
|
|
void *p;
|
|
p = &p;
|
|
b->stack_frame = (unsigned long)p;
|
|
}
|
|
|
|
#ifdef MZ_XFORM
|
|
END_XFORM_SKIP;
|
|
#endif
|
|
|
|
void scheme_clean_native_symtab(void)
|
|
{
|
|
#ifndef MZ_PRECISE_GC
|
|
clear_symbols_for_collected();
|
|
jit_notify_freed_code();
|
|
#endif
|
|
}
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
static void release_native_code(void *fnlized, void *p)
|
|
{
|
|
Scheme_Object *len;
|
|
|
|
len = SCHEME_BOX_VAL(fnlized);
|
|
|
|
scheme_jit_malloced -= SCHEME_INT_VAL(len);
|
|
|
|
/* Remove name mapping: */
|
|
add_symbol((unsigned long)p, (unsigned long)p + SCHEME_INT_VAL(len), NULL, 0);
|
|
/* Free memory: */
|
|
scheme_free_code(p);
|
|
jit_notify_freed_code();
|
|
}
|
|
#endif
|
|
|
|
/**********************************************************************/
|
|
/* Precise GC */
|
|
/**********************************************************************/
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
|
|
START_XFORM_SKIP;
|
|
|
|
#define MARKS_FOR_JIT_C
|
|
#include "mzmark.c"
|
|
|
|
static void register_traversers(void)
|
|
{
|
|
GC_REG_TRAV(scheme_native_closure_type, native_closure);
|
|
GC_REG_TRAV(scheme_rt_jitter_data, mark_jit_state);
|
|
GC_REG_TRAV(scheme_rt_native_code, native_unclosed_proc);
|
|
GC_REG_TRAV(scheme_rt_native_code_plus_case, native_unclosed_proc_plus_case);
|
|
}
|
|
|
|
END_XFORM_SKIP;
|
|
|
|
#endif /* MZ_PRECISE_GC */
|
|
|
|
#endif /* MZ_USE_JIT */
|