racket/src/mzscheme/src/jit.c
2006-01-31 21:23:35 +00:00

3614 lines
99 KiB
C

/*
MzScheme
Copyright (c) 2006 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "schpriv.h"
#include "schmach.h"
#ifdef MZ_USE_JIT
#ifdef __APPLE__
# define _CALL_DARWIN
#endif
#include "lightning/lightning.h"
#define JIT_LOG_WORD_SIZE 2
#define WORDS_TO_BYTES(x) ((x) << JIT_LOG_WORD_SIZE)
#define JIT_WORD_SIZE (1 << JIT_LOG_WORD_SIZE)
#define JIT_NOT_RET JIT_R1
#if JIT_NOT_RET == JIT_RET
Fix me! See use.
#endif
#define MAX_SHARED_CALL_RANDS 25
static void *shared_tail_code[2][MAX_SHARED_CALL_RANDS];
static void *shared_non_tail_code[2][MAX_SHARED_CALL_RANDS][2];
#define MAX_SHARED_ARITY_CHECK 25
static void *shared_arity_check[MAX_SHARED_ARITY_CHECK][2][2];
static void *jump_to_native_code;
static void *bad_result_arity_code;
static void *unbound_global_code;
static void *quote_syntax_code;
static void *call_original_code;
static void *call_original_reversed_code;
static void *call_original_single_code;
static void *bad_car_code, *bad_cdr_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;
typedef struct {
jit_state js;
char *limit;
int extra_pushed, max_extra_pushed;
int depth, max_depth;
int *mappings; /* low bit indicates mode: orig pushed (0) or new pushed (1);
new pushed can be native; zero marks a save point */
int num_mappings, mappings_size;
int retained;
int need_set_rs;
void **retain_start;
} mz_jit_state;
typedef int (*Native_Check_Arity_Proc)(Scheme_Object *o, int argc);
typedef Scheme_Object *(*Native_Get_Arity_Proc)(Scheme_Object *o);
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);
static void *generate_top_simple_arity_check(int num_params, int has_rest, int is_method, int permanent);
static void generate_top_case_lambda_dispatch(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);
/* 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];
int stack_cache_stack_pos = 0;
#include "codetab.inc"
/*========================================================================*/
/* JIT buffer */
/*========================================================================*/
#define _jit (jitter->js)
#define PAST_LIMIT() (jit_get_ip().ptr > jitter->limit)
#define CHECK_LIMIT() if (PAST_LIMIT()) return 0;
#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 void mz_retain_it(mz_jit_state *jitter, void *v)
{
if (jitter->retain_start) {
jitter->retain_start[jitter->retained] = v;
}
jitter->retained++;
}
static void *generate_one(mz_jit_state *old_jitter,
Generate_Proc generate,
void *data,
int gcable,
void *save_ptr)
{
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;
if (!jit_buffer_cache_registered) {
jit_buffer_cache_registered = 1;
REGISTER_SO(jit_buffer_cache);
REGISTER_SO(stack_cache_stack);
/* printf("zap!\n"); */
}
while (1) {
memset(jitter, 0, sizeof(_jitter));
#ifdef MZ_USE_JIT_PPC
_jitl.long_jumps = 1;
#endif
padding = 100;
if (known_size) {
size_pre_retained = known_size;
size = size_pre_retained + WORDS_TO_BYTES(num_retained);
padding = 0;
if (gcable) {
buffer = scheme_malloc(size);
} else {
buffer = malloc(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 {
buffer = scheme_malloc(size);
}
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;
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;
ok = generate(jitter, data);
if (save_ptr) {
mz_retain_it(jitter, save_ptr);
}
jitter->limit = (char *)jitter->limit + padding;
if (PAST_LIMIT() || (jitter->retain_start
&& (jitter->retained > num_retained))) {
scheme_console_printf("JIT buffer overflow!!\n");
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 & 0x3) {
known_size += (4 - (known_size & 0x3));
}
num_retained = jitter->retained;
/* 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;
}
}
}
/*========================================================================*/
/* run time */
/*========================================================================*/
static
#ifndef NO_INLINE_KEYWORD
MSC_IZE(inline)
#endif
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 box_multiple_array_element(int pos)
{
Scheme_Thread *p = scheme_current_thread;
Scheme_Object **naya, **a;
int i;
naya = MALLOC_N(Scheme_Object *, p->ku.multiple.count);
a = p->ku.multiple.array;
for (i = p->ku.multiple.count; i--; ) {
naya[i] = a[i];
}
{
Scheme_Object *eb;
eb = scheme_make_envunbox(naya[pos]);
naya[pos] = eb;
}
p->ku.multiple.array = naya;
}
static void thread_block()
{
scheme_thread_block(0);
scheme_current_thread->ran_some = 1;
}
/*========================================================================*/
/* 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(); \
}
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)
{
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);
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
jit_str_p(JIT_RUNSTACK, reg);
jitter->need_set_rs = 1;
}
static void mz_popr_p_it(mz_jit_state *jitter, int reg)
{
int v;
jitter->extra_pushed--;
v = jitter->mappings[jitter->num_mappings] >> 1;
v--;
if (!v)
--jitter->num_mappings;
else
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
jit_ldr_p(reg, JIT_RUNSTACK);
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(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;
v -= n;
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
}
static void mz_runstack_unskipped(mz_jit_state *jitter, int n)
{
int v;
v = (jitter->mappings[jitter->num_mappings]) >> 1;
v += n;
if (!v)
--jitter->num_mappings;
else
jitter->mappings[jitter->num_mappings] = ((v << 1) | 0x1);
}
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;
if (!jitter->mappings[jitter->num_mappings]
|| (jitter->mappings[jitter->num_mappings] & 0x1)) {
new_mapping(jitter);
}
jitter->mappings[jitter->num_mappings] += (n << 1);
jitter->need_set_rs = 1;
}
static void mz_runstack_popped(mz_jit_state *jitter, int n)
{
int v;
jitter->depth -= n;
v = (jitter->mappings[jitter->num_mappings]) >> 1;
v -= n;
if (!v)
--jitter->num_mappings;
else
jitter->mappings[jitter->num_mappings] = (v << 1);
jitter->need_set_rs = 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) {
c >>= 1;
if (c > 0)
amt += c;
} else
amt += (c >> 1);
--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) {
c >>= 1;
i += c;
if (c < 0)
j += c;
} else {
j -= (c >> 1);
}
--p;
}
return i;
}
#define mz_pushr_p(x) mz_pushr_p_it(jitter, x)
#define mz_popr_p(x) mz_popr_p_it(jitter, x)
#define mz_prepare(x) jit_prepare(x)
#define mz_finish(x) jit_finish(x)
#define mz_finishr(x) jit_finishr(x)
#define mz_retain(x) mz_retain_it(jitter, x)
#define mz_remap(x) mz_remap_it(jitter, x)
#ifdef MZ_USE_JIT_PPC
/* JIT_LOCAL1 and JIT_LOCAL2 are offsets in the stack frame.
We use the last two slots reserved for parameters to calless,
because we never call with more than 6 arguments. */
# define JIT_LOCAL1 56
# define JIT_LOCAL2 60
# define JIT_LOCAL3 64
# define mz_push_local_p(x, l) jit_stxi_p(l, 1, x)
# define mz_pop_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_patch_branch(a) mz_patch_branch_at(a, (_jit.x.pc))
# define mz_patch_ucbranch(a) mz_patch_ucbranch_at(a, (_jit.x.pc))
# define mz_prolog(x) (MFLRr(x), mz_push_local_p(x, JIT_LOCAL2))
# define mz_epilog(x) (mz_pop_local_p(x, JIT_LOCAL2), jit_jmpr(x))
#else
# define mz_push_local_p(x, l) jit_pushr_p(x)
# define mz_pop_local_p(x, l) jit_popr_p(x)
# define mz_patch_branch_at(a, v) jit_patch_at(a, v)
# define mz_patch_ucbranch_at(a, v) jit_patch_at(a, v)
# define mz_patch_branch(a) jit_patch(a)
# define mz_patch_ucbranch(a) jit_patch(a)
# define mz_prolog(x) /* empty */
# define mz_epilog(x) RET_()
#endif
#ifdef MZ_USE_JIT_PPC
# 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
/* Note: Things like
refm = jit_jmpi(jit_forward());
jit_patch_at(refm, jump_to_native_code);
appear in the code because the generated instructions can depend on
the actual value supplied to jit_jmpi, and it can depend on the
relative location between the instruction address and the actual
value. Using jit_patch ensures that the generated instructions
always have the same size. */
/*========================================================================*/
/* bytecode properties */
/*========================================================================*/
#ifdef MZ_USE_JIT_PPC
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)
|| (t == QUOTE_SYNTAX_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_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 inlined_unary_prim(Scheme_Object *o, Scheme_Object *_app)
{
return (SAME_OBJ(o, scheme_not_prim)
|| SAME_OBJ(o, scheme_null_p_prim)
|| SAME_OBJ(o, scheme_pair_p_prim)
|| SAME_OBJ(o, scheme_car_prim)
|| SAME_OBJ(o, scheme_cdr_prim)
|| SAME_OBJ(o, scheme_add1_prim)
|| SAME_OBJ(o, scheme_sub1_prim));
}
static int inlined_binary_prim(Scheme_Object *o, Scheme_Object *_app)
{
if (SAME_OBJ(o, scheme_plus_prim)) {
Scheme_App3_Rec *app = (Scheme_App3_Rec *)_app;
if (SCHEME_INTP(app->rand1)
|| SCHEME_INTP(app->rand2))
return 1;
}
if (SAME_OBJ(o, scheme_minus_prim)) {
Scheme_App3_Rec *app = (Scheme_App3_Rec *)_app;
if (SCHEME_INTP(app->rand2))
return 1;
}
return (SAME_OBJ(o, scheme_eq_prim));
}
static int is_noncm(Scheme_Object *a)
{
if (SCHEME_PRIMP(a)) {
if (((Scheme_Prim_Proc_Header *)a)->flags & SCHEME_PRIM_IS_NONCM)
return 1;
}
return 0;
}
#define INIT_SIMPLE_DEPTH 10
static int is_simple(Scheme_Object *obj, int depth, int just_markless)
{
/* 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)
|| (t == QUOTE_SYNTAX_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)
&& is_simple(b->fbranch, depth - 1, just_markless));
}
break;
case scheme_let_value_type:
if (depth) {
return is_simple(((Scheme_Let_Value *)obj)->body, depth - 1, just_markless);
}
break;
case scheme_let_one_type:
if (just_markless && depth) {
return is_simple(((Scheme_Let_One *)obj)->body, depth - 1, just_markless);
}
break;
case scheme_let_void_type:
if (just_markless && depth) {
return is_simple(((Scheme_Let_Void *)obj)->body, depth - 1, just_markless);
}
break;
case scheme_letrec_type:
if (just_markless && depth) {
return is_simple(((Scheme_Letrec *)obj)->body, depth - 1, just_markless);
}
break;
case scheme_application_type:
if (just_markless) {
return is_noncm(((Scheme_App_Rec *)obj)->args[0]);
}
break;
case scheme_application2_type:
if (inlined_unary_prim(((Scheme_App2_Rec *)obj)->rator, obj))
return 1;
else if (just_markless) {
return is_noncm(((Scheme_App2_Rec *)obj)->rator);
}
break;
case scheme_application3_type:
if (inlined_binary_prim(((Scheme_App2_Rec *)obj)->rator, obj))
return 1;
else if (just_markless) {
return is_noncm(((Scheme_App3_Rec *)obj)->rator);
}
break;
case scheme_toplevel_type:
case scheme_local_type:
case scheme_local_unbox_type:
case scheme_unclosed_procedure_type:
return 1;
break;
}
return (type > _scheme_values_types_);
}
/*========================================================================*/
/* application codegen */
/*========================================================================*/
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));
jit_str_p(JIT_RUNSTACK, JIT_R0);
JIT_UPDATE_THREAD_RSPTR();
}
jit_movi_i(JIT_R1, num_rands);
mz_prepare(2);
CHECK_LIMIT();
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_i(JIT_R1);
mz_finishr(JIT_V1);
CHECK_LIMIT();
/* Pop saved runstack val and return: */
mz_pop_local_p(JIT_NOT_RET, JIT_LOCAL1);
jit_sti_p(&scheme_current_runstack, JIT_NOT_RET);
jit_ret();
return 1;
}
static int generate_tail_call(mz_jit_state *jitter, int num_rands, int need_set_rs)
{
int i;
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref4, *ref5;
__START_SHORT_JUMPS__(num_rands < 100);
/* First, try fast direct jump to native code: */
ref = jit_bmsi_ul(jit_forward(), JIT_V1, 0x1);
jit_ldr_s(JIT_R1, JIT_V1);
ref2 = jit_bnei_p(jit_forward(), JIT_R1, scheme_native_closure_type);
CHECK_LIMIT();
/* 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, JIT_V1 and JIT_R0 are set up. */
/* Check for thread swap: */
(void)jit_movi_p(JIT_R1, &scheme_fuel_counter);
jit_ldr_i(JIT_R1, JIT_R1);
ref5 = jit_bgti_i(jit_forward(), JIT_R1, 0);
if (need_set_rs) {
JIT_UPDATE_THREAD_RSPTR();
}
/* FIXME 3m: need to move JIT_V1 and JIT_R0 to GC-visible place */
mz_push_local_p(JIT_R0, JIT_LOCAL2);
(void)jit_calli(thread_block);
mz_pop_local_p(JIT_R0, JIT_LOCAL2);
mz_patch_branch(ref5);
/* Copy args to runstack base: */
jit_subi_p(JIT_R2, JIT_RUNSTACK_BASE, WORDS_TO_BYTES(num_rands));
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);
/* Extract function and data: */
jit_movr_p(JIT_R2, JIT_V1);
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);
jit_movi_i(JIT_R1, num_rands);
jit_movr_p(JIT_R2, JIT_RUNSTACK);
/* Now jump: */
jit_jmpr(JIT_V1);
CHECK_LIMIT();
/* The slow way: */
/* JIT_R0, JIT_V1, and JIT_RUNSTACK must be intact! */
mz_patch_branch(ref);
mz_patch_branch(ref2);
mz_patch_branch(ref4);
CHECK_LIMIT();
if (need_set_rs) {
JIT_UPDATE_THREAD_RSPTR();
}
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);
(void)mz_finish(_scheme_tail_apply_from_native);
/* Pop saved runstack val and return: */
mz_pop_local_p(JIT_NOT_RET, JIT_LOCAL1);
jit_sti_p(&scheme_current_runstack, JIT_NOT_RET);
jit_ret();
__END_SHORT_JUMPS__(num_rands < 100);
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));
jit_str_p(JIT_RUNSTACK, JIT_R0);
JIT_UPDATE_THREAD_RSPTR();
}
jit_movi_i(JIT_R1, num_rands);
mz_prepare(2);
CHECK_LIMIT();
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_i(JIT_R1);
mz_finishr(JIT_V1);
CHECK_LIMIT();
jit_retval(JIT_R0);
#if 0
/* No need to check for multi values or tail-call, because
we only use this for noncm primitives. */
jit_insn *ref;
if (!multi_ok) {
jit_insn *refm;
refm = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
mz_patch_branch_at(refm, bad_result_arity_code);
}
ref = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_TAIL_CALL_WAITING);
CHECK_LIMIT();
mz_prepare(1);
jit_pusharg_p(JIT_R0);
if (multi_ok) {
(void)mz_finish(scheme_force_value);
} else {
(void)mz_finish(scheme_force_one_value);
}
jit_retval(JIT_R0);
mz_patch_branch(ref);
#endif
if (num_rands == 1) {
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
}
if (pop_and_jump) {
mz_epilog(JIT_V1);
}
return 1;
}
static int generate_non_tail_call(mz_jit_state *jitter, int num_rands, int need_set_rs, int multi_ok, int pop_and_jump)
{
/* Non-tail call: */
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref4, *ref5, *ref6, *ref7, *ref8, *ref9;
GC_CAN_IGNORE jit_insn *ref10;
__START_SHORT_JUMPS__(num_rands < 100);
if (pop_and_jump) {
mz_prolog(JIT_R1);
}
/* Check for inlined prim types */
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();
/* Before inlined native, check max let 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();
/* Before inlined native, check stack depth: */
(void)jit_movi_p(JIT_R1, &scheme_stack_boundary);
jit_ldr_i(JIT_R1, JIT_R1);
ref9 = jit_bltr_ul(jit_forward(), JIT_STACK, JIT_R1);
CHECK_LIMIT();
/* Fast inlined-native jump ok (proc will check argc); */
/* extract function and data: */
mz_prepare(3);
jit_pusharg_p(JIT_RUNSTACK);
jit_movi_i(JIT_R1, num_rands);
jit_pusharg_i(JIT_R1);
jit_pusharg_p(JIT_V1);
(void)mz_finish(jump_to_native_code);
CHECK_LIMIT();
jit_retval(JIT_R0);
if (!multi_ok) {
jit_insn *refm;
__END_SHORT_JUMPS__(num_rands < 100);
refm = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
mz_patch_branch_at(refm, bad_result_arity_code);
__START_SHORT_JUMPS__(num_rands < 100);
}
ref6 = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_TAIL_CALL_WAITING);
if (need_set_rs) {
JIT_UPDATE_THREAD_RSPTR();
}
mz_prepare(1);
jit_pusharg_p(JIT_R0);
if (multi_ok) {
(void)mz_finish(scheme_force_value);
} else {
(void)mz_finish(scheme_force_one_value);
}
ref5 = jit_jmpi(jit_forward());
CHECK_LIMIT();
/* Maybe it's a prim? */
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_R0, JIT_V1, &((Scheme_Primitive_Proc *)0x0)->mina);
ref7 = jit_bnei_i(jit_forward(), JIT_R0, num_rands);
/* Fast prim application */
jit_ldxi_p(JIT_V1, JIT_V1, &((Scheme_Primitive_Proc *)0x0)->prim_val);
if (need_set_rs) {
JIT_UPDATE_THREAD_RSPTR();
}
mz_prepare(2);
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_i(JIT_R0);
(void)mz_finishr(JIT_V1);
CHECK_LIMIT();
jit_retval(JIT_R0);
if (!multi_ok) {
jit_insn *refm;
__END_SHORT_JUMPS__(num_rands < 100);
refm = jit_beqi_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
mz_patch_branch_at(refm, bad_result_arity_code);
__START_SHORT_JUMPS__(num_rands < 100);
}
ref10 = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_TAIL_CALL_WAITING);
mz_prepare(1);
jit_pusharg_p(JIT_R0);
if (multi_ok) {
(void)mz_finish(scheme_force_value);
} else {
(void)mz_finish(scheme_force_one_value);
}
CHECK_LIMIT();
ref8 = jit_jmpi(jit_forward());
/* The slow way: */
mz_patch_branch(ref);
mz_patch_branch(ref2);
mz_patch_branch(ref4);
mz_patch_branch(ref7);
mz_patch_branch(ref9);
if (need_set_rs) {
JIT_UPDATE_THREAD_RSPTR();
}
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 (multi_ok) {
(void)mz_finish(_scheme_apply_multi_from_native);
} else {
(void)mz_finish(_scheme_apply_from_native);
}
mz_patch_ucbranch(ref5);
mz_patch_ucbranch(ref8);
jit_retval(JIT_R0);
mz_patch_branch(ref6);
mz_patch_branch(ref10);
if (pop_and_jump) {
mz_epilog(JIT_V1);
}
__END_SHORT_JUMPS__(num_rands < 100);
return 1;
}
typedef struct {
int num_rands;
mz_jit_state *old_jitter;
int multi_ok;
int is_tail;
int direct_prim;
} Generate_Call_Data;
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) {
if (data->direct_prim)
return generate_direct_prim_tail_call(jitter, data->num_rands);
else
return generate_tail_call(jitter, data->num_rands, 1);
} else {
int ok;
void *code, *code_end;
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, 1, data->multi_ok, 1);
code_end = jit_get_ip().ptr;
if (jitter->retain_start)
add_symbol((unsigned long)code, (unsigned long)code_end - 1, scheme_false, 0);
return ok;
}
}
static void *generate_shared_call(int num_rands, mz_jit_state *old_jitter, int multi_ok, int is_tail,
int direct_prim)
{
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;
return generate_one(old_jitter, do_generate_shared_call, &data, 0, NULL);
}
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 i, direct_prim = 0, need_non_tail = 0, offset;
Scheme_Object *rator, *v;
int reorder_ok = 0;
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)->maxa)
|| (((Scheme_Primitive_Proc *)rator)->maxa < 0))
&& is_noncm(rator))
direct_prim = 1;
} else {
Scheme_Type t;
t = SCHEME_TYPE(rator);
if ((t == scheme_local_type) || (t > _scheme_values_types_)) {
/* We can re-order evaluation. */
reorder_ok = 1;
}
}
if (num_rands) {
if (!direct_prim || (num_rands > 1)) {
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(num_rands));
mz_runstack_pushed(jitter, num_rands);
} else {
mz_runstack_skipped(jitter, 1);
}
}
for (i = 0; i <= num_rands; i++) {
v = (alt_rands ? alt_rands[i] : app->args[i]);
if (!is_simple(v, INIT_SIMPLE_DEPTH, 1)) {
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) {
generate_non_tail(rator, jitter, 0, !need_non_tail);
CHECK_LIMIT();
if (num_rands) {
/* Save rator where GC can see it */
jit_stxi_p(WORDS_TO_BYTES(num_rands - 1 + offset), JIT_RUNSTACK, JIT_R0);
} else {
jit_movr_p(JIT_V1, JIT_R0);
}
}
for (i = 0; i < num_rands; i++) {
PAUSE_JIT_DATA();
generate_non_tail(alt_rands ? alt_rands[i+1] : app->args[i+1], jitter, 0, !need_non_tail);
RESUME_JIT_DATA();
CHECK_LIMIT();
if ((i == num_rands - 1) && !direct_prim && !reorder_ok) {
/* Move rator back to register: */
jit_ldxi_p(JIT_V1, JIT_RUNSTACK, WORDS_TO_BYTES(i + offset));
}
if (!direct_prim || (num_rands > 1)) {
jit_stxi_p(WORDS_TO_BYTES(i + offset), JIT_RUNSTACK, JIT_R0);
}
}
if (need_non_tail) {
/* Uses JIT_R2: */
generate_non_tail_mark_pos_suffix(jitter);
CHECK_LIMIT();
}
if (direct_prim) {
(void)jit_movi_p(JIT_V1, ((Scheme_Primitive_Proc *)rator)->prim_val);
if (num_rands == 1) {
mz_runstack_unskipped(jitter, 1);
} else {
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
}
}
if (reorder_ok) {
generate(rator, jitter, 0, 0);
CHECK_LIMIT();
jit_movr_p(JIT_V1, JIT_R0);
}
END_JIT_DATA(20);
if (num_rands >= MAX_SHARED_CALL_RANDS) {
if (is_tail) {
if (direct_prim)
generate_direct_prim_tail_call(jitter, num_rands);
else
generate_tail_call(jitter, num_rands, jitter->need_set_rs);
} else {
if (direct_prim)
generate_direct_prim_non_tail_call(jitter, num_rands, multi_ok, 0);
else
generate_non_tail_call(jitter, num_rands, jitter->need_set_rs, multi_ok, 0);
}
} else {
/* Jump to code to implement a tail call for num_rands arguments */
void *code;
int dp = (direct_prim ? 1 : 0);
if (is_tail) {
jit_insn *refm;
if (!shared_tail_code[dp][num_rands]) {
code = generate_shared_call(num_rands, jitter, multi_ok, is_tail, direct_prim);
shared_tail_code[dp][num_rands] = code;
}
code = shared_tail_code[dp][num_rands];
refm = jit_jmpi(jit_forward());
mz_patch_ucbranch_at(refm, code);
} else {
int mo = (multi_ok ? 1 : 0);
if (!shared_non_tail_code[dp][num_rands][mo]) {
code = generate_shared_call(num_rands, jitter, multi_ok, is_tail, direct_prim);
shared_non_tail_code[dp][num_rands][mo] = code;
}
code = shared_non_tail_code[dp][num_rands][mo];
(void)jit_calli(code);
/* Whether we call a prim, a native, or something else,
scheme_current_runstack is up-to-date. */
jitter->need_set_rs = 0;
}
}
END_JIT_DATA(need_non_tail ? 22 : 4);
return is_tail ? 2 : 1;
}
static int generate_add(mz_jit_state *jitter, Scheme_Object *v, long delta, int orig_args,
Scheme_Object *orig_prim, int single, int negate, int reversed)
{
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3;
mz_runstack_skipped(jitter, orig_args);
generate_non_tail(v, jitter, 0, 1);
CHECK_LIMIT();
mz_runstack_unskipped(jitter, orig_args);
__START_SHORT_JUMPS__(1);
jit_movr_p(JIT_R1, JIT_R0); /* save it in case of overflow... */
ref = jit_bmci_ul(jit_forward(), JIT_R0, 0x1);
ref3 = jit_boaddi_i(jit_forward(), JIT_R0, delta << 1);
ref2 = jit_jmpi(jit_forward());
CHECK_LIMIT();
/* Fixnum fast path failed; call original primitive */
mz_patch_branch(ref);
mz_patch_branch(ref3);
__END_SHORT_JUMPS__(1);
if (negate)
delta = -delta;
if (!single) {
(void)jit_movi_p(JIT_R0, scheme_make_integer(delta));
}
(void)jit_movi_p(JIT_R2, ((Scheme_Primitive_Proc *)orig_prim)->prim_val);
if (single) {
(void)jit_calli(call_original_single_code);
} else if (reversed) {
(void)jit_calli(call_original_reversed_code);
} else {
(void)jit_calli(call_original_code);
}
__START_SHORT_JUMPS__(1);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
return 1;
}
static int generate_inlined_unary(mz_jit_state *jitter, Scheme_App2_Rec *app, int is_tail, int multi_ok)
{
Scheme_Object *rator = app->rator;
if (SAME_OBJ(rator, scheme_not_prim)
|| SAME_OBJ(rator, scheme_null_p_prim)) {
GC_CAN_IGNORE jit_insn *ref, *ref2;
Scheme_Object *cnst;
if (SAME_OBJ(rator, scheme_not_prim)) {
cnst = scheme_false;
} else {
cnst = scheme_null;
}
mz_runstack_skipped(jitter, 1);
generate_non_tail(app->rand, jitter, 0, 1);
CHECK_LIMIT();
mz_runstack_unskipped(jitter, 1);
__START_SHORT_JUMPS__(1);
ref = jit_beqi_p(jit_forward(), JIT_R0, cnst);
(void)jit_movi_p(JIT_R0, scheme_false);
ref2 = jit_jmpi(jit_forward());
mz_patch_branch(ref);
(void)jit_movi_p(JIT_R0, scheme_true);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
return 1;
} else if (SAME_OBJ(rator, scheme_pair_p_prim)) {
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3;
Scheme_Type ty;
ty = scheme_pair_type;
mz_runstack_skipped(jitter, 1);
generate_non_tail(app->rand, jitter, 0, 1);
CHECK_LIMIT();
mz_runstack_unskipped(jitter, 1);
__START_SHORT_JUMPS__(1);
ref = jit_bmsi_ul(jit_forward(), JIT_R0, 0x1);
jit_ldxi_s(JIT_R0, JIT_R0, &((Scheme_Object *)0x0)->type);
ref3 = jit_bnei_p(jit_forward(), JIT_R0, ty);
(void)jit_movi_p(JIT_R0, scheme_true);
ref2 = jit_jmpi(jit_forward());
mz_patch_branch(ref);
mz_patch_branch(ref3);
(void)jit_movi_p(JIT_R0, scheme_false);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
return 1;
} else if (SAME_OBJ(rator, scheme_car_prim)
|| SAME_OBJ(rator, scheme_cdr_prim)) {
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3;
int is_car;
is_car = SAME_OBJ(rator, scheme_car_prim);
mz_runstack_skipped(jitter, 1);
generate_non_tail(app->rand, jitter, 0, 1);
CHECK_LIMIT();
mz_runstack_unskipped(jitter, 1);
__START_SHORT_JUMPS__(1);
ref = jit_bmsi_ul(jit_forward(), JIT_R0, 0x1);
jit_ldxi_s(JIT_R1, JIT_R0, &((Scheme_Object *)0x0)->type);
ref3 = jit_bnei_p(jit_forward(), JIT_R1, scheme_pair_type);
if (is_car) {
(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);
}
ref2 = jit_jmpi(jit_forward());
mz_patch_branch(ref);
mz_patch_branch(ref3);
__END_SHORT_JUMPS__(1);
if (is_car) {
(void)jit_jmpi(bad_car_code);
} else {
(void)jit_jmpi(bad_cdr_code);
}
__START_SHORT_JUMPS__(1);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
return 1;
} else if (SAME_OBJ(rator, scheme_add1_prim)) {
generate_add(jitter, app->rand, 1, 1, rator, 1, 0, 0);
return 1;
} else if (SAME_OBJ(rator, scheme_sub1_prim)) {
generate_add(jitter, app->rand, -1, 1, rator, 1, 0, 0);
return 1;
}
return 0;
}
static int generate_inlined_binary(mz_jit_state *jitter, Scheme_App3_Rec *app, int is_tail, int multi_ok)
{
Scheme_Object *rator = app->rator;
if (SAME_OBJ(rator, scheme_eq_prim)) {
Scheme_Object *a1, *a2;
GC_CAN_IGNORE jit_insn *ref, *ref2;
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: */
mz_runstack_skipped(jitter, 2);
generate_non_tail(a2, jitter, 0, 1);
CHECK_LIMIT();
mz_runstack_unskipped(jitter, 2);
__START_SHORT_JUMPS__(1);
mz_retain(a1);
ref = jit_beqi_p(jit_forward(), JIT_R0, a1);
(void)jit_movi_p(JIT_R0, scheme_false);
ref2 = jit_jmpi(jit_forward());
mz_patch_branch(ref);
(void)jit_movi_p(JIT_R0, scheme_true);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
} else {
/* Two complex expressions: */
mz_runstack_skipped(jitter, 1);
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
mz_runstack_pushed(jitter, 1);
generate_non_tail(a2, jitter, 0, 1);
CHECK_LIMIT();
jit_str_p(JIT_RUNSTACK, JIT_R0);
generate_non_tail(a1, jitter, 0, 1);
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);
mz_runstack_unskipped(jitter, 1);
__START_SHORT_JUMPS__(1);
ref = jit_beqr_p(jit_forward(), JIT_R0, JIT_R1);
(void)jit_movi_p(JIT_R0, scheme_false);
ref2 = jit_jmpi(jit_forward());
mz_patch_branch(ref);
(void)jit_movi_p(JIT_R0, scheme_true);
mz_patch_ucbranch(ref2);
__END_SHORT_JUMPS__(1);
}
return 1;
} else if (SAME_OBJ(rator, scheme_plus_prim)
|| SAME_OBJ(rator, scheme_minus_prim)) {
Scheme_Object *c, *v;
int reversed = 0, negative = 0;
c = app->rand1;
if (SAME_OBJ(rator, scheme_plus_prim) && SCHEME_INTP(c)) {
v = app->rand2;
} else {
c = app->rand2;
v = app->rand1;
reversed = 1;
if (SAME_OBJ(rator, scheme_minus_prim))
negative = 1;
}
if (SCHEME_INTP(c)) {
long delta = SCHEME_INT_VAL(c);
if (negative) {
long d2;
d2 = -delta;
delta = d2;
}
generate_add(jitter, v, delta, 2, rator, 0, negative, reversed);
return 1;
}
}
return 0;
}
/*========================================================================*/
/* lambda codegen */
/*========================================================================*/
static void ensure_closure_native(Scheme_Closure_Data *data,
Scheme_Native_Closure_Data *case_lam)
{
if (!data->native_code || SCHEME_FALSEP((Scheme_Object *)data->native_code)) {
Scheme_Native_Closure_Data *code;
code = scheme_generate_lambda(data, 0, case_lam);
data->native_code = code;
}
}
static int generate_closure(Scheme_Closure_Data *data,
mz_jit_state *jitter)
{
Scheme_Native_Closure_Data *code;
ensure_closure_native(data, NULL);
code = data->native_code;
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
mz_prepare(1);
mz_retain(code);
(void)jit_movi_p(JIT_R0, code); /* !! */
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);
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->native_code->max_let_depth > max_let_depth)
max_let_depth = data->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_top_case_lambda_dispatch(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_lambda(Scheme_Object *obj, mz_jit_state *jitter)
{
Scheme_Case_Lambda *c = (Scheme_Case_Lambda *)obj;
Scheme_Native_Closure_Data *ndata;
Scheme_Closure_Data *data;
Scheme_Object *o;
int i, offset, count;
ensure_case_closure_native(c);
ndata = c->native_code;
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
mz_prepare(1);
mz_retain(ndata);
(void)jit_movi_p(JIT_R0, ndata); /* !! */
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); /* !!!!!!! */
generate_closure(data, jitter);
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(JIT_R0, JIT_R1);
return 1;
}
/*========================================================================*/
/* non-tail codegen */
/*========================================================================*/
static int generate_non_tail_mark_pos_prefix(mz_jit_state *jitter)
{
/* 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_i(JIT_R2, &scheme_current_cont_mark_pos);
jit_addi_i(JIT_R2, JIT_R2, 2);
jit_sti_i(&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)
{
jit_ldi_i(JIT_R2, &scheme_current_cont_mark_pos);
jit_subi_i(JIT_R2, JIT_R2, 2);
jit_sti_i(&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)
{
if (is_simple(obj, INIT_SIMPLE_DEPTH, 0)) {
/* Simple; doesn't change the stack or set marks: */
return generate(obj, jitter, 0, multi_ok);
}
{
int amt, need_ends = 1;
START_JIT_DATA();
/* Might change the stack or marks: */
if (is_simple(obj, INIT_SIMPLE_DEPTH, 1)) {
need_ends = 0;
} else {
if (mark_pos_ends)
generate_non_tail_mark_pos_prefix(jitter);
jit_ldi_p(JIT_R2, &scheme_current_cont_mark_stack);
mz_pushr_p(JIT_R2);
CHECK_LIMIT();
}
mz_runstack_saved(jitter);
CHECK_LIMIT();
PAUSE_JIT_DATA();
generate(obj, jitter, 0, multi_ok);
RESUME_JIT_DATA();
CHECK_LIMIT();
amt = mz_runstack_restored(jitter);
if (amt) {
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(amt));
}
if (need_ends) {
mz_popr_p(JIT_R2);
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 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);
return scheme_make_integer(v);
}
static int generate(Scheme_Object *obj, mz_jit_state *jitter, int is_tail, int multi_ok)
/* result goes to JIT_R0 */
{
Scheme_Type type;
#ifdef DO_STACK_CHECK
# include "mzstkchk.h"
{
Scheme_Object *ok;
Scheme_Thread *p = scheme_current_thread;
mz_jit_state *jitter_copy;
/* 3m FIXME: need precise handling of this copy: */
jitter_copy = (mz_jit_state *)scheme_malloc(sizeof(jitter_copy));
memcpy(jitter_copy, jitter, sizeof(mz_jit_state));
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;
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;
START_JIT_DATA();
/* 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));
/* Extract bucket value */
jit_ldxi_p(JIT_R0, JIT_R2, &(SCHEME_VAR_BUCKET(0x0)->val));
CHECK_LIMIT();
/* Is it NULL? */
(void)jit_beqi_p(unbound_global_code, JIT_R0, 0);
END_JIT_DATA(0);
return 1;
}
case scheme_local_type:
{
int pos;
START_JIT_DATA();
pos = mz_remap(SCHEME_LOCAL_POS(obj));
jit_ldxi_p(JIT_R0, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
END_JIT_DATA(2);
return 1;
}
case scheme_local_unbox_type:
{
int pos;
START_JIT_DATA();
pos = mz_remap(SCHEME_LOCAL_POS(obj));
jit_ldxi_p(JIT_R0, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
jit_ldr_p(JIT_R0, JIT_R0);
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();
/* case-lambda */
generate_case_lambda(SCHEME_IPTR_VAL(obj), jitter);
END_JIT_DATA(5);
}
break;
case BEGIN0_EXPD:
{
Scheme_Sequence *seq;
jit_insn *ref, *ref2;
int i;
START_JIT_DATA();
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); /* !!!!!!!! */
__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);
mz_popr_p(JIT_V1);
mz_popr_p(JIT_V1);
jit_ldi_p(JIT_R0, &scheme_current_thread);
CHECK_LIMIT();
jit_ldxi_i(JIT_V1, JIT_R0, &((Scheme_Thread *)0x0)->ku.multiple.count);
jit_lshi_i(JIT_V1, JIT_V1, 0x1);
jit_ori_i(JIT_V1, JIT_V1, 0x1);
mz_pushr_p(JIT_V1);
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Thread *)0x0)->ku.multiple.array);
mz_pushr_p(JIT_V1); /* !!!!!!!! */
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_i(JIT_R2, JIT_R0, &((Scheme_Thread *)0x0)->values_buffer);
ref2 = jit_bner_p(jit_forward(), JIT_V1, JIT_R2);
jit_stxi_i(&((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_non_tail(seq->array[i], jitter, 1, 1);
CHECK_LIMIT();
}
/* Restore values, if necessary */
mz_popr_p(JIT_R0);
mz_popr_p(JIT_R1);
mz_popr_p(JIT_R2);
CHECK_LIMIT();
__START_SHORT_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_p(&((Scheme_Thread *)0x0)->ku.multiple.count, JIT_R0, JIT_R2);
(void)jit_movi_p(JIT_R0, SCHEME_MULTIPLE_VALUES);
mz_patch_branch(ref);
__END_SHORT_JUMPS__(1);
END_JIT_DATA(6);
}
break;
case SET_EXPD:
{
Scheme_Object *p, *v;
int pos, set_undef;
START_JIT_DATA();
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();
/* 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(4);
(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)jit_movi_i(JIT_R1, "set!");
jit_pusharg_p(JIT_R1);
(void)mz_finish(scheme_set_global_bucket);
CHECK_LIMIT();
(void)jit_movi_p(JIT_R0, scheme_void);
END_JIT_DATA(7);
}
break;
case BOXENV_EXPD:
{
Scheme_Object *p, *v;
int pos;
START_JIT_DATA();
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);
END_JIT_DATA(8);
}
break;
case BOXVAL_EXPD:
{
Scheme_Object *p, *v;
int pos, cnt;
START_JIT_DATA();
p = (Scheme_Object *)SCHEME_IPTR_VAL(obj);
v = SCHEME_CAR(p);
pos = SCHEME_INT_VAL(v);
p = SCHEME_CDR(p);
v = SCHEME_CAR(p);
cnt = SCHEME_INT_VAL(v);
p = SCHEME_CDR(p);
/* cnt is expected number of returns, and it will be
consistent with multi_ok; do something only if the actual
count is the same as cnt */
generate_non_tail(p, jitter, cnt != 1, 1);
CHECK_LIMIT();
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
if (cnt != 1) {
jit_insn *ref, *ref2, *ref3;
__START_SHORT_JUMPS__(1);
ref = jit_bnei_p(jit_forward(), JIT_R0, SCHEME_MULTIPLE_VALUES);
/* Handle multiple values: */
jit_ldi_p(JIT_R2, &scheme_current_thread);
jit_ldxi_i(JIT_R1, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.count);
ref3 = jit_bnei_p(jit_forward(), JIT_R1, cnt);
CHECK_LIMIT();
/* Received results match expected results */
(void)jit_movi_i(JIT_R0, pos);
mz_prepare(1);
jit_pusharg_p(JIT_R0);
(void)mz_finish(box_multiple_array_element);
CHECK_LIMIT();
(void)jit_movi_p(JIT_R0, SCHEME_MULTIPLE_VALUES);
/* Jump over single-value handling: */
ref2 = jit_jmpi(jit_forward());
CHECK_LIMIT();
/* Handle single value: */
mz_patch_branch(ref);
mz_prepare(1);
jit_pusharg_p(JIT_R0);
(void)mz_finish(scheme_make_envunbox);
CHECK_LIMIT();
jit_retval(JIT_R0);
mz_patch_ucbranch(ref2);
mz_patch_branch(ref3);
CHECK_LIMIT();
__END_SHORT_JUMPS__(1);
} else {
mz_prepare(1);
jit_pusharg_p(JIT_R0);
(void)mz_finish(scheme_make_envunbox);
jit_retval(JIT_R0);
}
END_JIT_DATA(9);
}
break;
case QUOTE_SYNTAX_EXPD:
{
int i, c, p;
START_JIT_DATA();
obj = SCHEME_IPTR_VAL(obj);
i = SCHEME_INT_VAL(SCHEME_CAR(obj));
c = mz_remap(SCHEME_INT_VAL(SCHEME_CADR(obj)));
p = SCHEME_INT_VAL(SCHEME_CDDR(obj));
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);
END_JIT_DATA(10);
}
break;
default:
{
JIT_UPDATE_THREAD_RSPTR_IF_NEEDED();
obj = SCHEME_IPTR_VAL(obj);
(void)jit_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(JIT_R0);
}
}
return 1;
}
case scheme_application_type:
{
Scheme_App_Rec *app = (Scheme_App_Rec *)obj;
return generate_app(app, NULL, app->num_args, jitter, is_tail, multi_ok);
}
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);
if (r)
return r;
CHECK_LIMIT();
args[0] = app->rator;
args[1] = app->rand;
return generate_app(NULL, args, 1, jitter, is_tail, multi_ok);
}
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);
if (r)
return r;
CHECK_LIMIT();
args[0] = app->rator;
args[1] = app->rand1;
args[2] = app->rand2;
return generate_app(NULL, args, 2, jitter, is_tail, multi_ok);
}
case scheme_sequence_type:
{
Scheme_Sequence *seq = (Scheme_Sequence *)obj;
int cnt = seq->count, i;
START_JIT_DATA();
for (i = 0; i < cnt - 1; i++) {
generate_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);
}
case scheme_branch_type:
{
Scheme_Branch_Rec *branch = (Scheme_Branch_Rec *)obj;
jit_insn *ref, *ref2;
int nsrs, nsrs1, g1, g2, amt;
START_JIT_DATA();
#ifdef MZ_USE_JIT_PPC
int then_short_ok, else_short_ok;
/* 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
generate_non_tail(branch->test, jitter, 0, 1);
CHECK_LIMIT();
__START_SHORT_JUMPS__(then_short_ok);
ref = jit_beqi_p(jit_forward(), JIT_R0, scheme_false);
__END_SHORT_JUMPS__(then_short_ok);
/* True branch */
mz_runstack_saved(jitter);
nsrs = jitter->need_set_rs;
PAUSE_JIT_DATA();
g1 = generate(branch->tbranch, jitter, is_tail, multi_ok);
RESUME_JIT_DATA();
CHECK_LIMIT();
amt = mz_runstack_restored(jitter);
if (g1 != 2) {
if (amt && !is_tail) {
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(amt));
}
__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;
/* False branch */
mz_runstack_saved(jitter);
__START_SHORT_JUMPS__(then_short_ok);
mz_patch_branch(ref);
__END_SHORT_JUMPS__(then_short_ok);
PAUSE_JIT_DATA();
g2 = generate(branch->fbranch, jitter, is_tail, multi_ok);
RESUME_JIT_DATA();
CHECK_LIMIT();
amt = mz_runstack_restored(jitter);
if (g2 != 2) {
if (amt && !is_tail) {
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(amt));
}
} 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);
}
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();
/* Allocate closure */
generate_closure(data, jitter);
CHECK_LIMIT();
generate_closure_fill(data, jitter);
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();
if (lv->count == 1) {
/* Expect one result: */
generate_non_tail(lv->value, jitter, 0, 1);
if (ab) {
pos = mz_remap(lv->position);
jit_ldxi_p(JIT_R2, JIT_RUNSTACK, WORDS_TO_BYTES(pos));
jit_str_p(JIT_R2, JIT_R0);
} else {
pos = mz_remap(lv->position);
jit_stxi_p(WORDS_TO_BYTES(pos), JIT_RUNSTACK, JIT_R0);
}
CHECK_LIMIT();
} else {
/* Expect multiple results: */
jit_insn *ref, *ref2, *ref3;
generate_non_tail(lv->value, jitter, 1, 1);
CHECK_LIMIT();
__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_i(JIT_R1, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.count);
jit_ldxi_i(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_i(JIT_R2, JIT_R0);
CHECK_LIMIT();
/* Error starts here: */
mz_patch_ucbranch(ref3);
JIT_UPDATE_THREAD_RSPTR_FOR_BRANCH_IF_NEEDED();
mz_prepare(5);
(void)jit_movi_p(JIT_V1, "lexical binding");
jit_pusharg_p(JIT_V1);
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)jit_movi_p(JIT_V1, NULL);
jit_pusharg_i(JIT_V1);
(void)mz_finish(scheme_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);
return generate(lv->body, jitter, is_tail, multi_ok);
}
case scheme_let_void_type:
{
Scheme_Let_Void *lv = (Scheme_Let_Void *)obj;
int c = lv->count;
START_JIT_DATA();
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(c));
mz_runstack_pushed(jitter, c);
if (SCHEME_LET_AUTOBOX(lv)) {
int i;
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);
return generate(lv->body, jitter, is_tail, multi_ok);
}
case scheme_letrec_type:
{
Scheme_Letrec *l = (Scheme_Letrec *)obj;
int i;
START_JIT_DATA();
/* Create unfinished closures */
for (i = 0; i < l->count; i++) {
generate_closure((Scheme_Closure_Data *)l->procs[i], jitter);
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);
return generate(l->body, jitter, is_tail, multi_ok);
}
case scheme_let_one_type:
{
Scheme_Let_One *lv = (Scheme_Let_One *)obj;
START_JIT_DATA();
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
mz_runstack_pushed(jitter, 1);
PAUSE_JIT_DATA();
generate_non_tail(lv->value, jitter, 0, 1);
RESUME_JIT_DATA();
CHECK_LIMIT();
jit_str_p(JIT_RUNSTACK, JIT_R0);
END_JIT_DATA(17);
return generate(lv->body, jitter, is_tail, multi_ok);
}
case scheme_with_cont_mark_type:
{
Scheme_With_Continuation_Mark *wcm = (Scheme_With_Continuation_Mark *)obj;
START_JIT_DATA();
/* Key: */
generate_non_tail(wcm->key, jitter, 0, 1);
CHECK_LIMIT();
if (SCHEME_TYPE(obj) > _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);
} else {
mz_pushr_p(JIT_R0); /* !!!!!!! */
generate_non_tail(wcm->val, jitter, 0, 1);
CHECK_LIMIT();
mz_popr_p(JIT_V1);
}
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);
return generate(wcm->body, jitter, is_tail, multi_ok);
}
default:
{
Scheme_Type type = SCHEME_TYPE(obj);
START_JIT_DATA();
/* 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);
} 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)) {
mz_retain(obj);
}
(void)jit_movi_p(JIT_R0, 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_local_p(JIT_RUNSTACK, JIT_LOCAL1);
/* 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
optimisitcally 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_i(JIT_RUNSTACK_BASE, JIT_R1, JIT_LOG_WORD_SIZE);
jit_addr_p(JIT_RUNSTACK_BASE, JIT_R2, JIT_RUNSTACK_BASE);
__START_SHORT_JUMPS__(num_params < 100);
ref = jit_beqr_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
__END_SHORT_JUMPS__(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));
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_SHORT_JUMPS__(num_params < 100);
mz_patch_branch(ref);
__END_SHORT_JUMPS__(num_params < 100);
}
return cnt;
}
static int do_generate_common(mz_jit_state *jitter, void *_data)
{
int in, i;
GC_CAN_IGNORE jit_insn *ref, *ref2;
/* *** jump_to_native_code *** */
/* Called as a function: */
jump_to_native_code = jit_get_ip().ptr;
jit_prolog(3);
in = jit_arg_p();
jit_getarg_p(JIT_R0, in); /* closure */
in = jit_arg_p();
jit_getarg_i(JIT_R1, in); /* argc */
in = jit_arg_p();
jit_getarg_i(JIT_R2, in); /* argv */
CHECK_LIMIT();
jit_movr_p(JIT_RUNSTACK, JIT_R2);
jit_movr_p(JIT_RUNSTACK_BASE, JIT_R2);
mz_push_local_p(JIT_RUNSTACK, JIT_LOCAL1);
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);
CHECK_LIMIT();
/* *** check_arity_code *** */
/* Called as a function: */
check_arity_code = (Native_Check_Arity_Proc)jit_get_ip().ptr;
jit_prolog(2);
in = jit_arg_p();
jit_getarg_p(JIT_R0, in); /* closure */
in = jit_arg_p();
jit_getarg_i(JIT_R2, in); /* argc */
jit_movi_i(JIT_R1, -1);
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
jit_ldxi_i(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
jit_jmpr(JIT_V1);
CHECK_LIMIT();
/* *** check_arity_code *** */
/* Called as a function: */
get_arity_code = (Native_Get_Arity_Proc)jit_get_ip().ptr;
jit_prolog(1);
in = jit_arg_p();
jit_getarg_p(JIT_R0, in); /* closure */
jit_movi_i(JIT_R1, -1);
jit_movi_i(JIT_R2, 0x0);
jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Native_Closure *)0x0)->code);
jit_ldxi_i(JIT_V1, JIT_V1, &((Scheme_Native_Closure_Data *)0x0)->arity_code);
jit_jmpr(JIT_V1);
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_i(JIT_R1, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.count);
jit_ldxi_i(JIT_R2, JIT_R2, &((Scheme_Thread *)0x0)->ku.multiple.array);
CHECK_LIMIT();
mz_prepare(5);
(void)jit_movi_p(JIT_V1, NULL);
jit_pusharg_p(JIT_V1);
jit_pusharg_p(JIT_R2);
jit_pusharg_i(JIT_R1);
CHECK_LIMIT();
jit_movi_i(JIT_V1, 1);
jit_pusharg_i(JIT_V1);
(void)jit_movi_p(JIT_V1, NULL);
jit_pusharg_i(JIT_V1);
(void)mz_finish(scheme_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);
/* 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 */
/* Save global array: */
mz_push_local_p(JIT_V1, JIT_LOCAL3);
/* Compute i in JIT_V1: */
jit_subr_p(JIT_V1, JIT_R1, JIT_R2);
jit_subi_p(JIT_V1, JIT_V1, WORDS_TO_BYTES(1));
CHECK_LIMIT();
/* Load car & cdr of elements at p: */
jit_ldxi_p(JIT_R2, JIT_R0, &SCHEME_CAR((Scheme_Object *)0x0));
jit_ldxi_p(JIT_R0, JIT_R0, &SCHEME_CDR((Scheme_Object *)0x0));
jit_ldxr_p(JIT_R0, JIT_R0, JIT_V1);
/* Move R1 to V1 to save it: */
jit_movr_p(JIT_V1, JIT_R1);
/* Call scheme_add_rename: */
JIT_UPDATE_THREAD_RSPTR();
CHECK_LIMIT();
mz_prepare(2);
jit_pusharg_p(JIT_R2);
jit_pusharg_p(JIT_R0);
(void)mz_finish(scheme_add_rename);
CHECK_LIMIT();
jit_retval(JIT_R0);
/* Restore global array into JIT_R1, and put computed element at i+p+1: */
mz_pop_local_p(JIT_R1, JIT_LOCAL3);
jit_stxr_p(JIT_V1, JIT_R1, JIT_R0);
mz_patch_branch(ref);
__END_SHORT_JUMPS__(1);
mz_epilog(JIT_V1);
/* *** bad_{car,cdr}_code *** */
/* Non-pair is in R0 */
for (i = 0; i < 2; i++) {
if (!i) {
bad_car_code = jit_get_ip().ptr;
} else {
bad_cdr_code = jit_get_ip().ptr;
}
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1));
jit_str_p(JIT_RUNSTACK, JIT_R0);
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);
if (!i) {
(void)jit_finish(((Scheme_Primitive_Proc *)scheme_car_prim)->prim_val);
} else {
(void)jit_finish(((Scheme_Primitive_Proc *)scheme_cdr_prim)->prim_val);
}
CHECK_LIMIT();
}
/* *** call_original_[{reversed,single}_]code *** */
/* R0 is constant arg, R1 is arg, R2 is code pointer */
{
int cnt;
for (i = 0; i < 3; i++) {
if (!i) {
call_original_code = jit_get_ip().ptr;
cnt = 2;
} else if (i == 1) {
call_original_reversed_code = jit_get_ip().ptr;
cnt = 2;
} else {
call_original_single_code = jit_get_ip().ptr;
cnt = 1;
}
mz_prolog(JIT_V1);
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(cnt));
if (!i) {
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_R1);
}
CHECK_LIMIT();
JIT_UPDATE_THREAD_RSPTR();
jit_movi_i(JIT_R1, cnt);
CHECK_LIMIT();
mz_prepare(2);
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_p(JIT_R1);
(void)mz_finishr(JIT_R2);
CHECK_LIMIT();
jit_retval(JIT_R0);
jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(cnt));
JIT_UPDATE_THREAD_RSPTR();
mz_epilog(JIT_V1);
CHECK_LIMIT();
}
}
/* *** 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_local_p(JIT_RUNSTACK, JIT_LOCAL1);
on_demand_jit_arity_code = jit_get_ip().ptr; /* <<<- arity variant starts here */
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(3));
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);
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 isnot
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. To simulate
a tail call, we must decrement the cont-mark pos. */
mz_patch_branch(ref);
mz_patch_branch(ref2);
jit_ldi_i(JIT_V1, &scheme_current_cont_mark_pos);
jit_subi_i(JIT_V1, JIT_V1, 2);
jit_sti_i(&scheme_current_cont_mark_pos, JIT_V1);
CHECK_LIMIT();
mz_prepare(3);
jit_pusharg_p(JIT_R2);
jit_pusharg_p(JIT_R1);
jit_pusharg_p(JIT_R0);
(void)jit_finish(_scheme_apply_multi_from_native);
CHECK_LIMIT();
jit_ldi_i(JIT_NOT_RET, &scheme_current_cont_mark_pos);
jit_addi_i(JIT_NOT_RET, JIT_NOT_RET, 2);
jit_sti_i(&scheme_current_cont_mark_pos, JIT_NOT_RET);
mz_pop_local_p(JIT_NOT_RET, JIT_LOCAL1);
jit_ret();
CHECK_LIMIT();
/* *** 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 *** */
stack_cache_pop_code = jit_get_ip().ptr;
jit_movr_p(JIT_R0, JIT_RET);
/* Decrement stack_cache_stack_pos */
jit_ldi_i(JIT_R1, &stack_cache_stack_pos);
jit_subi_i(JIT_R2, JIT_R1, 1);
jit_sti_p(&stack_cache_stack_pos, JIT_R2);
CHECK_LIMIT();
/* Extract old return address and jump to it */
jit_lshi_i(JIT_R1, JIT_R1, (JIT_LOG_WORD_SIZE + 2));
jit_addi_i(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);
jit_jmpr(JIT_R2);
CHECK_LIMIT();
return 1;
}
typedef struct {
Scheme_Closure_Data *data;
void *code, *tail_code, *code_end;
int max_extra, max_depth;
} 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;
int i, r, cnt;
code = jit_get_ip().ptr;
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 starts here. Caller must ensure that the
stack is big enough, right number of arguments, closure
is in R0. */
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 ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_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));
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);
if (data->closure_size)
mz_pushr_p(JIT_R0);
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);
if (data->closure_size)
mz_popr_p(JIT_R0);
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);
}
/* Extract closure to runstack: */
cnt = data->closure_size;
if (cnt) {
jit_subi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(cnt));
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);
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_R1);
CHECK_LIMIT();
}
}
/* Generate code for the body: */
jitter->need_set_rs = 1;
r = generate(data->code, jitter, 1, 1);
/* Result is in JIT_R0 */
CHECK_LIMIT();
/* r == 2 => tail call performed */
if (r != 2) {
mz_pop_local_p(JIT_RUNSTACK, JIT_LOCAL1);
jit_sti_p(&MZ_RUNSTACK, JIT_RUNSTACK);
jit_movr_p(JIT_RET, JIT_R0);
jit_ret();
}
code_end = jit_get_ip().ptr;
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;
return 1;
}
static void on_demand_generate_top(Scheme_Native_Closure_Data *ndata)
{
Scheme_Closure_Data *data;
Generate_Closure_Data gdata;
void *code, *tail_code, *arity_code;
int has_rest, is_method, num_params, max_depth;
data = ndata->u2.orig_code;
gdata.data = data;
generate_one(NULL, do_generate_closure, &gdata, 1, data->name);
if (gdata.max_depth > data->max_let_depth) {
scheme_console_printf("Bad max depth!\n");
abort();
}
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);
}
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) {
arity_code = shared_arity_check[num_params][has_rest][is_method];
if (!arity_code) {
arity_code = generate_top_simple_arity_check(num_params, has_rest, is_method, 1);
shared_arity_check[num_params][has_rest][is_method] = arity_code;
}
} else
arity_code = generate_top_simple_arity_check(num_params, has_rest, is_method, 0);
max_depth = WORDS_TO_BYTES(data->max_let_depth + gdata.max_extra);
/* max_let_depth is used for flags by generate_top: */
if (ndata->max_let_depth & 0x1) {
data->code = 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;
}
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;
Scheme_Native_Closure_Data *ndata;
c = MZ_RUNSTACK[0];
argc = MZ_RUNSTACK[1];
argv = (Scheme_Object **)MZ_RUNSTACK[2];
ndata = ((Scheme_Native_Closure *)c)->code;
on_demand_generate_top(ndata);
}
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 (!jump_to_native_code) {
/* Create shared code used for stack-overflow handling, etc.: */
generate_one(NULL, do_generate_common, NULL, 0, NULL);
}
if (!case_lam) {
ndata = MALLOC_ONE_RT(Scheme_Native_Closure_Data);
} 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;
}
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_top(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 airty is ok. */
jit_insn *ref, *ref2;
__START_SHORT_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_i(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(5);
jit_pusharg_p(JIT_R2);
jit_pusharg_p(JIT_R1);
jit_movi_i(JIT_V1, -1);
CHECK_LIMIT();
jit_pusharg_i(JIT_V1);
jit_pusharg_i(JIT_V1);
jit_pusharg_p(JIT_R0);
(void)mz_finish(scheme_wrong_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);
jit_ret();
mz_patch_branch(ref2);
jit_movi_i(JIT_RET, 0);
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_finish(scheme_box);
jit_ret();
} else {
jit_movr_p(JIT_RET, JIT_R0);
jit_ret();
}
__END_SHORT_JUMPS__(1);
return 1;
}
typedef struct {
int num_params;
int has_rest;
int is_method;
} Generate_Arity_Check_Data;
static int do_generate_top_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_top_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_top_simple_arity_check, &data, !permanent, NULL);
}
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 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_i(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(5);
jit_pusharg_p(JIT_R2);
jit_pusharg_p(JIT_R1);
jit_movi_i(JIT_V1, -1);
CHECK_LIMIT();
jit_pusharg_i(JIT_V1);
jit_pusharg_i(JIT_V1);
jit_pusharg_p(JIT_R0);
(void)mz_finish(scheme_wrong_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_top_case_lambda_dispatch(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);
/* 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 */
/*========================================================================*/
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 (((Scheme_Native_Closure *)closure)->code->code == on_demand_jit_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);
}
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;
if (is_method && v)
--v;
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 (((Scheme_Native_Closure *)closure)->code->code == on_demand_jit_code) {
Scheme_Closure c;
c.so.type = scheme_closure_type;
c.code = ((Scheme_Native_Closure *)closure)->code->u2.orig_code;
return scheme_get_or_check_arity((Scheme_Object *)&c, -1);
}
return get_arity_code(closure);
}
/*========================================================================*/
/* 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
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;
Get_Stack_Proc gs;
Scheme_Object *name, *last = NULL, *first = NULL, *tail;
int set_next_push = 0, prev_had_name = 0;
if (!get_stack_pointer_code)
return NULL;
#if USE_STACK_CHECK
check_stack();
#endif
gs = (Get_Stack_Proc)get_stack_pointer_code;
p = gs();
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->next
? scheme_current_thread->stack_start
: scheme_current_thread->o_start);
tail = scheme_null;
}
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 (STK_COMP((unsigned long)p, stack_end)
&& STK_COMP(stack_start, (unsigned long)p)) {
q = ((void **)p)[RETURN_ADDRESS_OFFSET];
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
/* 2nd push onto local stack of return-address proc
has the next return address */
q = *(void **)p;
q = ((void **)q)[-5];
#endif
name = find_symbol((unsigned long)q);
}
if (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;
}
if (((void **)p)[RETURN_ADDRESS_OFFSET] == stack_cache_pop_code) {
*(long *)0x0 = 1;
}
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;
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;
}
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 = (unsigned long)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 = p;
}
void scheme_clean_native_symtab(void)
{
clear_symbols_for_collected();
}
#endif /* MZ_USE_JIT */