safe-for-space repairs for functions with rest args

This commit is contained in:
Matthew Flatt 2011-04-22 14:42:57 -06:00
parent f4307c65a7
commit 0754ad0114
11 changed files with 111 additions and 36 deletions

View File

@ -307,6 +307,7 @@
,@(if (and name (not (null? name))) ,@(if (and name (not (null? name)))
`(',name) `(',name)
null) null)
,@(if (null? flags) null `('(flags: ,@flags)))
,@(if (null? captures) ,@(if (null? captures)
null null
`('(captures: ,@(map (lambda (c t) `('(captures: ,@(map (lambda (c t)

View File

@ -257,6 +257,7 @@
(define CLOS_HAS_REST 1) (define CLOS_HAS_REST 1)
(define CLOS_HAS_REF_ARGS 2) (define CLOS_HAS_REF_ARGS 2)
(define CLOS_PRESERVES_MARKS 4) (define CLOS_PRESERVES_MARKS 4)
(define CLOS_NEED_REST_CLEAR 8)
(define CLOS_IS_METHOD 16) (define CLOS_IS_METHOD 16)
(define CLOS_SINGLE_RESULT 32) (define CLOS_SINGLE_RESULT 32)
@ -1006,6 +1007,7 @@
(+ (if rest? CLOS_HAS_REST 0) (+ (if rest? CLOS_HAS_REST 0)
(if any-refs? CLOS_HAS_REF_ARGS 0) (if any-refs? CLOS_HAS_REF_ARGS 0)
(if (memq 'preserves-marks flags) CLOS_PRESERVES_MARKS 0) (if (memq 'preserves-marks flags) CLOS_PRESERVES_MARKS 0)
(if (memq 'sfs-clear-rest-args flags) CLOS_NEED_REST_CLEAR 0)
(if (memq 'is-method flags) CLOS_IS_METHOD 0) (if (memq 'is-method flags) CLOS_IS_METHOD 0)
(if (memq 'single-result flags) CLOS_SINGLE_RESULT 0)) (if (memq 'single-result flags) CLOS_SINGLE_RESULT 0))
num-all-params num-all-params

View File

@ -81,6 +81,7 @@
(define CLOS_HAS_REST 1) (define CLOS_HAS_REST 1)
(define CLOS_HAS_REF_ARGS 2) (define CLOS_HAS_REF_ARGS 2)
(define CLOS_PRESERVES_MARKS 4) (define CLOS_PRESERVES_MARKS 4)
(define CLOS_NEED_REST_CLEAR 8)
(define CLOS_IS_METHOD 16) (define CLOS_IS_METHOD 16)
(define CLOS_SINGLE_RESULT 32) (define CLOS_SINGLE_RESULT 32)
(define BITS_PER_MZSHORT 32) (define BITS_PER_MZSHORT 32)
@ -118,6 +119,7 @@
(if (zero? (bitwise-and flags flags CLOS_PRESERVES_MARKS)) null '(preserves-marks)) (if (zero? (bitwise-and flags flags CLOS_PRESERVES_MARKS)) null '(preserves-marks))
(if (zero? (bitwise-and flags flags CLOS_IS_METHOD)) null '(is-method)) (if (zero? (bitwise-and flags flags CLOS_IS_METHOD)) null '(is-method))
(if (zero? (bitwise-and flags flags CLOS_SINGLE_RESULT)) null '(single-result)) (if (zero? (bitwise-and flags flags CLOS_SINGLE_RESULT)) null '(single-result))
(if (zero? (bitwise-and flags flags CLOS_NEED_REST_CLEAR)) null '(sfs-clear-rest-args))
(if (and rest? (zero? num-params)) '(only-rest-arg-not-used) null)) (if (and rest? (zero? num-params)) '(only-rest-arg-not-used) null))
(if (and rest? (num-params . > . 0)) (if (and rest? (num-params . > . 0))
(sub1 num-params) (sub1 num-params)

View File

@ -131,7 +131,8 @@
[internal-context (or/c #f #t stx?)])) [internal-context (or/c #f #t stx?)]))
(define-form-struct (lam expr) ([name (or/c symbol? vector? empty?)] (define-form-struct (lam expr) ([name (or/c symbol? vector? empty?)]
[flags (listof (or/c 'preserves-marks 'is-method 'single-result 'only-rest-arg-not-used))] [flags (listof (or/c 'preserves-marks 'is-method 'single-result
'only-rest-arg-not-used 'sfs-clear-rest-args))]
[num-params exact-nonnegative-integer?] [num-params exact-nonnegative-integer?]
[param-types (listof (or/c 'val 'ref 'flonum))] [param-types (listof (or/c 'val 'ref 'flonum))]
[rest? boolean?] [rest? boolean?]

View File

@ -271,7 +271,8 @@
@defstruct+[(lam expr) @defstruct+[(lam expr)
([name (or/c symbol? vector?)] ([name (or/c symbol? vector?)]
[flags (listof (or/c 'preserves-marks 'is-method 'single-result 'only-rest-arg-not-used))] [flags (listof (or/c 'preserves-marks 'is-method 'single-result
'only-rest-arg-not-used 'sfs-clear-rest-args))]
[num-params exact-nonnegative-integer?] [num-params exact-nonnegative-integer?]
[param-types (listof (or/c 'val 'ref 'flonum))] [param-types (listof (or/c 'val 'ref 'flonum))]
[rest? boolean?] [rest? boolean?]

View File

@ -8626,6 +8626,27 @@ static Scheme_Object *do_eval_k(void)
p->ku.k.i2); p->ku.k.i2);
} }
#ifdef MZ_USE_JIT
static Scheme_Object *do_eval_native_k(void)
{
/* If argv corresponds to old runstack, copy to new runstack
and clear old argv for space safety. */
Scheme_Thread *p = scheme_current_thread;
Scheme_Object **argv = (Scheme_Object **)p->ku.k.p2;
if (argv == (p->runstack_saved->runstack_start
+ p->runstack_saved->runstack_offset)) {
int argc = p->ku.k.i1;
MZ_RUNSTACK -= argc;
memcpy(MZ_RUNSTACK, argv, argc * sizeof(Scheme_Object*));
memset(argv, 0, argc * sizeof(Scheme_Object*));
p->ku.k.p2 = MZ_RUNSTACK;
}
return do_eval_k();
}
#endif
static void unbound_global(Scheme_Object *obj) static void unbound_global(Scheme_Object *obj)
{ {
Scheme_Object *tmp; Scheme_Object *tmp;
@ -9519,7 +9540,7 @@ scheme_do_eval(Scheme_Object *obj, int num_rands, Scheme_Object **rands,
MZ_CONT_MARK_POS -= 2; MZ_CONT_MARK_POS -= 2;
v = (Scheme_Object *)scheme_enlarge_runstack(data->max_let_depth / sizeof(void *), v = (Scheme_Object *)scheme_enlarge_runstack(data->max_let_depth / sizeof(void *),
(void *(*)(void))do_eval_k); (void *(*)(void))do_eval_native_k);
MZ_CONT_MARK_POS += 2; MZ_CONT_MARK_POS += 2;
goto returnv; goto returnv;
} }

View File

@ -1325,6 +1325,9 @@ Scheme_Object *scheme_sfs_closure(Scheme_Object *expr, SFS_Info *info, int self_
if (SCHEME_PAIRP(clears)) if (SCHEME_PAIRP(clears))
code = scheme_sfs_add_clears(code, clears, 1); code = scheme_sfs_add_clears(code, clears, 1);
if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST)
SCHEME_CLOSURE_DATA_FLAGS(data) |= CLOS_NEED_REST_CLEAR;
} }
data->code = code; data->code = code;

View File

@ -99,6 +99,16 @@ static void wrong_argument_count(Scheme_Object *proc, int argc, Scheme_Object **
scheme_wrong_count((char *)proc, -1, -1, argc, argv); scheme_wrong_count((char *)proc, -1, -1, argc, argv);
} }
static Scheme_Object *clear_rs_arguments(Scheme_Object *v, int size, int delta) XFORM_SKIP_PROC
{
int i;
Scheme_Object **argv = MZ_RUNSTACK;
for (i = size; i-- > delta; ) {
argv[i] = NULL;
}
return v;
}
#ifdef JIT_THREAD_LOCAL #ifdef JIT_THREAD_LOCAL
void *scheme_jit_get_threadlocal_table() XFORM_SKIP_PROC { return &BOTTOM_VARIABLE; } void *scheme_jit_get_threadlocal_table() XFORM_SKIP_PROC { return &BOTTOM_VARIABLE; }
#endif #endif
@ -2856,28 +2866,20 @@ static int generate_function_getarg(mz_jit_state *jitter, int has_rest, int num_
{ {
int i, cnt; int i, cnt;
GC_CAN_IGNORE jit_insn *ref; GC_CAN_IGNORE jit_insn *ref;
int set_ref;
/* If rands == runstack and there are no rest args, set runstack /* If rands == runstack, set runstack base to runstack + rands (and
base to runstack + rands (and don't copy rands), otherwise set don't copy rands), otherwise set base to runstack and copy
base to runstack and proceed normally. Implement this by arguments at runstack. Implement the test by optimistically
optimistically assuming rands == runstack, so that there's just assuming rands == runstack, so that there's just one jump. */
one jump. Skip this optimization when the procedure has jit_lshi_l(JIT_RUNSTACK_BASE_OR_ALT(JIT_V1), JIT_R1, JIT_LOG_WORD_SIZE);
rest args, because we'll have to copy anyway. */ jit_addr_p(JIT_RUNSTACK_BASE_OR_ALT(JIT_V1), JIT_R2, JIT_RUNSTACK_BASE_OR_ALT(JIT_V1));
if (!has_rest && num_params) {
jit_lshi_l(JIT_RUNSTACK_BASE_OR_ALT(JIT_V1), JIT_R1, JIT_LOG_WORD_SIZE);
jit_addr_p(JIT_RUNSTACK_BASE_OR_ALT(JIT_V1), JIT_R2, JIT_RUNSTACK_BASE_OR_ALT(JIT_V1));
#ifndef JIT_RUNSTACK_BASE #ifndef JIT_RUNSTACK_BASE
mz_set_local_p(JIT_V1, JIT_RUNSTACK_BASE_LOCAL); mz_set_local_p(JIT_V1, JIT_RUNSTACK_BASE_LOCAL);
#endif #endif
__START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100); __START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
ref = jit_beqr_p(jit_forward(), JIT_RUNSTACK, JIT_R2); ref = jit_beqr_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
__END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100); __END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
set_ref = 1;
} else {
ref = 0;
set_ref = 0;
}
#ifdef JIT_RUNSTACK_BASE #ifdef JIT_RUNSTACK_BASE
jit_movr_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK); jit_movr_p(JIT_RUNSTACK_BASE, JIT_RUNSTACK);
#else #else
@ -2901,11 +2903,9 @@ static int generate_function_getarg(mz_jit_state *jitter, int has_rest, int num_
CHECK_LIMIT(); CHECK_LIMIT();
} }
if (set_ref) { __START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
__START_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100); mz_patch_branch(ref);
mz_patch_branch(ref); __END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
__END_TINY_OR_SHORT_JUMPS__(num_params < 10, num_params < 100);
}
return cnt; return cnt;
} }
@ -2943,6 +2943,7 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
cnt = generate_function_getarg(jitter, cnt = generate_function_getarg(jitter,
(SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST), (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_HAS_REST),
data->num_params); data->num_params);
/* At this point, all non-rest arguments are now at the runstack */
CHECK_LIMIT(); CHECK_LIMIT();
/* A tail call with arity checking can start here. /* A tail call with arity checking can start here.
@ -2980,7 +2981,8 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
tail_code = jit_get_ip().ptr; tail_code = jit_get_ip().ptr;
/* 0 params and has_rest => (lambda args E) where args is not in E, /* 0 params and has_rest => (lambda args E) where args is not in E,
so accept any number of arguments and ignore them. */ so accept any number of arguments and just clear them (for space
safety). */
if (has_rest && data->num_params) { if (has_rest && data->num_params) {
/* If runstack == argv and argc == cnt, then we didn't /* If runstack == argv and argc == cnt, then we didn't
@ -2998,6 +3000,8 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
for (i = cnt; i--; ) { for (i = cnt; i--; ) {
jit_ldxi_p(JIT_V1, JIT_R2, WORDS_TO_BYTES(i)); jit_ldxi_p(JIT_V1, JIT_R2, WORDS_TO_BYTES(i));
jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_V1); jit_stxi_p(WORDS_TO_BYTES(i), JIT_RUNSTACK, JIT_V1);
/* space safety: */
jit_stxi_p(WORDS_TO_BYTES(i), JIT_R2, JIT_RUNSTACK);
CHECK_LIMIT(); CHECK_LIMIT();
} }
(void)jit_movi_p(JIT_V1, scheme_null); (void)jit_movi_p(JIT_V1, scheme_null);
@ -3008,6 +3012,7 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
/* Build a list for extra arguments: */ /* Build a list for extra arguments: */
mz_patch_branch(ref); mz_patch_branch(ref);
mz_patch_branch(ref3); mz_patch_branch(ref3);
CHECK_LIMIT();
#ifndef JIT_PRECISE_GC #ifndef JIT_PRECISE_GC
if (data->closure_size) if (data->closure_size)
#endif #endif
@ -3017,8 +3022,17 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
} }
JIT_UPDATE_THREAD_RSPTR(); JIT_UPDATE_THREAD_RSPTR();
CHECK_LIMIT(); CHECK_LIMIT();
mz_prepare(3);
jit_movi_i(JIT_V1, cnt); jit_movi_i(JIT_V1, cnt);
if ((SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_NEED_REST_CLEAR)) {
/* negative count => clear argv */
GC_CAN_IGNORE jit_insn *ref;
__START_INNER_TINY__(cnt < 100);
ref = jit_bner_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
jit_negr_i(JIT_R1, JIT_R1);
mz_patch_branch(ref);
__END_INNER_TINY__(cnt < 100);
}
mz_prepare(3);
jit_pusharg_i(JIT_V1); jit_pusharg_i(JIT_V1);
jit_pusharg_p(JIT_R2); jit_pusharg_p(JIT_R2);
jit_pusharg_i(JIT_R1); jit_pusharg_i(JIT_R1);
@ -3034,7 +3048,6 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
} }
jit_stxi_p(WORDS_TO_BYTES(cnt), JIT_RUNSTACK, JIT_V1); jit_stxi_p(WORDS_TO_BYTES(cnt), JIT_RUNSTACK, JIT_V1);
mz_patch_ucbranch(ref2); /* jump here if we copied and produced null */ mz_patch_ucbranch(ref2); /* jump here if we copied and produced null */
CHECK_LIMIT();
__END_SHORT_JUMPS__(cnt < 100); __END_SHORT_JUMPS__(cnt < 100);
@ -3044,6 +3057,27 @@ static int do_generate_closure(mz_jit_state *jitter, void *_data)
argc = 0; argc = 0;
} }
} else { } else {
if (has_rest && (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_NEED_REST_CLEAR)) {
/* if we get here, the rest argument isn't used */
GC_CAN_IGNORE jit_insn *ref;
__START_TINY_JUMPS__(1);
ref = jit_bner_p(jit_forward(), JIT_RUNSTACK, JIT_R2);
__END_TINY_JUMPS__(1);
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_i(JIT_R1);
jit_pusharg_p(JIT_R0);
CHECK_LIMIT();
(void)mz_finish(clear_rs_arguments);
jit_retval(JIT_R0);
__START_TINY_JUMPS__(1);
mz_patch_branch(ref);
__END_TINY_JUMPS__(1);
}
has_rest = 0; has_rest = 0;
if (argc != data->num_params) { if (argc != data->num_params) {
argv = NULL; argv = NULL;

View File

@ -2976,9 +2976,8 @@ int scheme_generate_inlined_nary(mz_jit_state *jitter, Scheme_App_Rec *app, int
} }
int scheme_generate_cons_alloc(mz_jit_state *jitter, int rev, int inline_retry) int scheme_generate_cons_alloc(mz_jit_state *jitter, int rev, int inline_retry)
/* Args must be in R0 (car) and R1 (cdr); uses R2 and V1 as temporaries */
{ {
/* Args should be in R0 (car) and R1 (cdr) */
#ifdef CAN_INLINE_ALLOC #ifdef CAN_INLINE_ALLOC
/* Inlined alloc */ /* Inlined alloc */
scheme_inline_alloc(jitter, sizeof(Scheme_Simple_Object), scheme_pair_type, 0, 1, 0, inline_retry); scheme_inline_alloc(jitter, sizeof(Scheme_Simple_Object), scheme_pair_type, 0, 1, 0, inline_retry);

View File

@ -848,12 +848,22 @@ Scheme_Object *scheme_build_list(int size, Scheme_Object **argv)
} }
Scheme_Object *scheme_build_list_offset(int size, Scheme_Object **argv, int delta) Scheme_Object *scheme_build_list_offset(int size, Scheme_Object **argv, int delta)
/* clears originals in argv for space safety! */
{ {
Scheme_Object *pair = scheme_null; Scheme_Object *pair = scheme_null;
int i; int i;
for (i = size; i-- > delta; ) { if (size < 0) {
pair = cons(argv[i], pair); /* clearing mode: */
size = -size;
for (i = size; i-- > delta; ) {
pair = cons(argv[i], pair);
argv[i] = NULL;
}
} else {
for (i = size; i-- > delta; ) {
pair = cons(argv[i], pair);
}
} }
return pair; return pair;

View File

@ -2088,10 +2088,11 @@ typedef struct Scheme_Comp_Env
#define CLOS_HAS_REST 1 #define CLOS_HAS_REST 1
#define CLOS_HAS_TYPED_ARGS 2 #define CLOS_HAS_TYPED_ARGS 2
#define CLOS_PRESERVES_MARKS 4 #define CLOS_PRESERVES_MARKS 4
#define CLOS_SFS 8 #define CLOS_NEED_REST_CLEAR 8
#define CLOS_IS_METHOD 16 #define CLOS_IS_METHOD 16
#define CLOS_SINGLE_RESULT 32 #define CLOS_SINGLE_RESULT 32
#define CLOS_RESULT_TENTATIVE 64 #define CLOS_RESULT_TENTATIVE 64
#define CLOS_SFS 128
#define CLOS_VALIDATED 128 #define CLOS_VALIDATED 128
/* BITS 8-15 used by write_compiled_closure() */ /* BITS 8-15 used by write_compiled_closure() */