fix JIT bug in struct pred/get/set corner case

When thie JIT guesses that an identifier is bound to a
structure predicate, getter, setter, etc., but that guess
turns out to be wrong, and the call is in a tail position,
then preserve tail-call behavior.

(Changes include some setup to inline structure constructors.)
This commit is contained in:
Matthew Flatt 2012-10-14 12:43:14 -04:00
parent a59df8c7ee
commit 79ada3b16e
8 changed files with 189 additions and 59 deletions

View File

@ -2196,4 +2196,28 @@
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module check-tail-call-by-jit-for-struct-predicate racket/base
(provide go)
(struct s (x))
(define f #f)
(set! f (lambda (v)
(if (zero? v)
(let ([vec (make-vector 6)])
(vector-set-performance-stats! vec (current-thread))
(vector-ref vec 3))
(s? (sub1 v)))))
(void (f 5)) ; JIT decides that `s?' is a struct predicate
(set! s? f) ; break the JIT's optimistic assumption
(define (go)
(define size (f 500000)) ; make sure that this still leads to a tail loop
(size . < . 80000)))
(test #t (dynamic-require ''check-tail-call-by-jit-for-struct-predicate 'go))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(report-errs)

View File

@ -621,7 +621,7 @@ int scheme_is_simple(Scheme_Object *obj, int depth, int just_markless, mz_jit_st
break;
case scheme_application_type:
if (scheme_inlined_nary_prim(((Scheme_App_Rec *)obj)->args[0], obj)
if (scheme_inlined_nary_prim(((Scheme_App_Rec *)obj)->args[0], obj, jitter)
&& !SAME_OBJ(((Scheme_App_Rec *)obj)->args[0], scheme_values_func))
return 1;
if (just_markless) {

View File

@ -250,13 +250,13 @@ struct scheme_jit_common_record {
void *on_demand_jit_arity_code, *in_progress_on_demand_jit_arity_code;
void *get_stack_pointer_code;
void *stack_cache_pop_code;
void *struct_pred_code, *struct_pred_multi_code;
void *struct_pred_code, *struct_pred_tail_code, *struct_pred_multi_code;
void *struct_pred_branch_code;
void *struct_get_code, *struct_get_multi_code;
void *struct_set_code, *struct_set_multi_code;
void *struct_prop_get_code, *struct_prop_get_multi_code;
void *struct_prop_get_defl_code, *struct_prop_get_defl_multi_code;
void *struct_prop_pred_code, *struct_prop_pred_multi_code;
void *struct_get_code, *struct_get_tail_code, *struct_get_multi_code;
void *struct_set_code, *struct_set_tail_code, *struct_set_multi_code;
void *struct_prop_get_code, *struct_prop_get_tail_code, *struct_prop_get_multi_code;
void *struct_prop_get_defl_code, *struct_prop_get_defl_tail_code, *struct_prop_get_defl_multi_code;
void *struct_prop_pred_code, *struct_prop_pred_tail_code, *struct_prop_pred_multi_code;
void *struct_proc_extract_code;
void *bad_app_vals_target;
void *app_values_slow_code, *app_values_multi_slow_code, *app_values_tail_slow_code;
@ -1170,7 +1170,7 @@ int scheme_mz_try_runstack_pop(mz_jit_state *jitter, int n);
int scheme_inlined_unary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter);
int scheme_inlined_binary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter);
int scheme_inlined_nary_prim(Scheme_Object *o, Scheme_Object *_app);
int scheme_inlined_nary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter);
int scheme_generate_inlined_unary(mz_jit_state *jitter, Scheme_App2_Rec *app, int is_tail, int multi_ok,
Branch_Info *for_branch, int branch_short, int need_sync, int result_ignored);
int scheme_generate_inlined_binary(mz_jit_state *jitter, Scheme_App3_Rec *app, int is_tail, int multi_ok,
@ -1232,6 +1232,7 @@ int scheme_generate_tail_call(mz_jit_state *jitter, int num_rands, int direct_na
int scheme_generate_non_tail_call(mz_jit_state *jitter, int num_rands, int direct_native, int need_set_rs,
int multi_ok, int nontail_self, int pop_and_jump, int is_inlined, int unboxed_args);
int scheme_generate_finish_tail_call(mz_jit_state *jitter, int direct_native);
int scheme_generate_finish_tail_apply(mz_jit_state *jitter);
void scheme_jit_register_sub_func(mz_jit_state *jitter, void *code, Scheme_Object *protocol);
void scheme_jit_register_helper_func(mz_jit_state *jitter, void *code);
#ifdef MZ_USE_FUTURES
@ -1383,3 +1384,16 @@ Scheme_Object *scheme_jit_continuation_apply_install(Apply_LWC_Args *args);
#define CMP_EVENP 4
/* odd? */
#define CMP_ODDP -4
/**********************************************************************/
#define INLINE_STRUCT_PROC_PRED 1
#define INLINE_STRUCT_PROC_GET 2
#define INLINE_STRUCT_PROC_SET 3
#define INLINE_STRUCT_PROC_PROP_GET 4
#define INLINE_STRUCT_PROC_PROP_GET_W_DEFAULT 5
#define INLINE_STRUCT_PROC_PROP_PRED 6
#define INLINE_STRUCT_PROC_CONSTR 7

View File

@ -515,6 +515,13 @@ int scheme_generate_tail_call(mz_jit_state *jitter, int num_rands, int direct_na
return 1;
}
int scheme_generate_finish_tail_apply(mz_jit_state *jitter)
{
GC_CAN_IGNORE jit_insn *refr;
(void)mz_finish_lwe(_scheme_tail_apply_from_native, refr);
return 1;
}
int scheme_generate_finish_tail_call(mz_jit_state *jitter, int direct_native)
{
mz_prepare(3);

View File

@ -1356,7 +1356,7 @@ static int common3(mz_jit_state *jitter, void *_data)
}
static int gen_struct_slow(mz_jit_state *jitter, int kind, int ok_proc,
int for_branch, int multi_ok,
int for_branch, int is_tail, int multi_ok,
GC_CAN_IGNORE jit_insn **_bref5,
GC_CAN_IGNORE jit_insn **_bref6)
{
@ -1376,7 +1376,10 @@ static int gen_struct_slow(mz_jit_state *jitter, int kind, int ok_proc,
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_i(JIT_V1);
jit_pusharg_p(JIT_R0);
if (multi_ok) {
if (is_tail) {
scheme_generate_finish_tail_apply(jitter);
CHECK_LIMIT();
} else if (multi_ok) {
(void)mz_finish_lwe(ts__scheme_apply_multi_from_native, refrts);
} else {
(void)mz_finish_lwe(ts__scheme_apply_from_native, refrts);
@ -1560,25 +1563,28 @@ static int common4(mz_jit_state *jitter, void *_data)
__END_TINY_JUMPS__(1);
}
/* *** struct_{pred,get,set}[_branch]_code *** */
/* *** struct_{pred,get,set}[_branch,_multi,_tail]_code *** */
/* R0 is (potential) struct proc, R1 is (potential) struct. */
/* In branch mode, V1 is target address for false branch. */
/* In set mode, V1 is value to install. */
for (ii = 0; ii < 2; ii++) {
for (i = 0; i < 4; i++) {
for (ii = 0; ii < 3; ii++) { /* single, multi, or tail */
for (i = 0; i < 4; i++) { /* pred, pred_branch, get, or set */
void *code;
int kind, for_branch;
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *refslow, *refslow2, *bref1, *bref2, *refretry;
GC_CAN_IGNORE jit_insn *bref3, *bref4, *bref5, *bref6, *bref8, *ref9;
if ((ii == 1) && (i == 1)) continue; /* no multi variant of pred branch */
if ((ii == 2) && (i == 1)) continue; /* no tail variant of pred branch */
code = jit_get_ip().ptr;
if (!i) {
kind = 1;
for_branch = 0;
if (ii == 1)
if (ii == 2)
sjc.struct_pred_tail_code = jit_get_ip().ptr;
else if (ii == 1)
sjc.struct_pred_multi_code = jit_get_ip().ptr;
else
sjc.struct_pred_code = jit_get_ip().ptr;
@ -1591,14 +1597,18 @@ static int common4(mz_jit_state *jitter, void *_data)
} else if (i == 2) {
kind = 2;
for_branch = 0;
if (ii == 1)
if (ii == 2)
sjc.struct_get_tail_code = jit_get_ip().ptr;
else if (ii == 1)
sjc.struct_get_multi_code = jit_get_ip().ptr;
else
sjc.struct_get_code = jit_get_ip().ptr;
} else {
kind = 3;
for_branch = 0;
if (ii == 1)
if (ii == 2)
sjc.struct_set_tail_code = jit_get_ip().ptr;
else if (ii == 1)
sjc.struct_set_multi_code = jit_get_ip().ptr;
else
sjc.struct_set_code = jit_get_ip().ptr;
@ -1615,13 +1625,13 @@ static int common4(mz_jit_state *jitter, void *_data)
/* Slow path: non-struct proc. */
refslow = _jit.x.pc;
gen_struct_slow(jitter, kind, 0, for_branch, ii == 1, &bref5, &bref6);
gen_struct_slow(jitter, kind, 0, for_branch, ii == 2, ii == 1, &bref5, &bref6);
CHECK_LIMIT();
if ((kind == 2) || (kind == 3)) {
/* Slow path: argument type is bad for a getter/setter. */
refslow2 = _jit.x.pc;
gen_struct_slow(jitter, kind, 1, 0, 0, NULL, NULL);
gen_struct_slow(jitter, kind, 1, 0, 0, 0, NULL, NULL);
CHECK_LIMIT();
} else
refslow2 = refslow;
@ -1773,28 +1783,34 @@ static int common4b(mz_jit_state *jitter, void *_data)
{
int i, ii;
/* *** struct_prop_{pred,get[_defl]}_[multi_]code *** */
/* *** struct_prop_{pred,get[_defl]}_[multi_,tail_]code *** */
/* R0 is (potential) struct-prop proc, R1 is (potential) struct.
If defl_, V1 is second argument for default value. */
for (i = 0; i < 3; i++) {
for (ii = 0; ii < 2; ii++) {
for (ii = 0; ii < 3; ii++) { /* single, multi, or tail */
void *code;
GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *refno, *refslow, *refloop, *refrts;
code = jit_get_ip().ptr;
if (i == 0) {
if (ii == 1)
if (ii == 2)
sjc.struct_prop_get_tail_code = code;
else if (ii == 1)
sjc.struct_prop_get_multi_code = code;
else
sjc.struct_prop_get_code = code;
} else if (i == 1) {
if (ii == 1)
if (ii == 2)
sjc.struct_prop_get_defl_tail_code = code;
else if (ii == 1)
sjc.struct_prop_get_defl_multi_code = code;
else
sjc.struct_prop_get_defl_code = code;
} else if (i == 2) {
if (ii == 1)
if (ii == 2)
sjc.struct_prop_pred_tail_code = code;
else if (ii == 1)
sjc.struct_prop_pred_multi_code = code;
else
sjc.struct_prop_pred_code = code;
@ -1826,7 +1842,10 @@ static int common4b(mz_jit_state *jitter, void *_data)
jit_pusharg_p(JIT_RUNSTACK);
jit_pusharg_i(JIT_V1);
jit_pusharg_p(JIT_R0);
if (ii == 1) {
if (ii == 2) {
scheme_generate_finish_tail_apply(jitter);
CHECK_LIMIT();
} else if (ii == 1) {
(void)mz_finish_lwe(ts__scheme_apply_multi_from_native, refrts);
} else {
(void)mz_finish_lwe(ts__scheme_apply_from_native, refrts);

View File

@ -121,22 +121,32 @@ static int generate_two_args(Scheme_Object *rand1, Scheme_Object *rand2, mz_jit_
static int check_val_struct_prim(Scheme_Object *p, int arity)
{
if (p && SCHEME_PRIMP(p)) {
if (arity == 1) {
int t = (((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_OTHER_TYPE_MASK);
int t = (((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_OTHER_TYPE_MASK);
if (t == SCHEME_PRIM_STRUCT_TYPE_CONSTR) {
#if 0
/* not yet ready... */
Scheme_Struct_Type *t;
t = (Scheme_Struct_Type *)SCHEME_PRIM_CLOSURE_ELS(p)[0];
if ((arity == t->num_islots)
&& scheme_is_simple_struct_type(t)) {
return INLINE_STRUCT_PROC_CONSTR;
}
#endif
return 0;
} else if (arity == 1) {
if (t == SCHEME_PRIM_STRUCT_TYPE_PRED)
return 1;
return INLINE_STRUCT_PROC_PRED;
if (t == SCHEME_PRIM_STRUCT_TYPE_INDEXED_GETTER)
return 2;
return INLINE_STRUCT_PROC_GET;
else if (t == SCHEME_PRIM_TYPE_STRUCT_PROP_GETTER)
return 4;
return INLINE_STRUCT_PROC_PROP_GET;
else if (t == SCHEME_PRIM_STRUCT_TYPE_STRUCT_PROP_PRED)
return 6;
return INLINE_STRUCT_PROC_PROP_PRED;
} else if (arity == 2) {
int t = (((Scheme_Primitive_Proc *)p)->pp.flags & SCHEME_PRIM_OTHER_TYPE_MASK);
if (t == SCHEME_PRIM_STRUCT_TYPE_INDEXED_SETTER)
return 3;
return INLINE_STRUCT_PROC_SET;
else if (t == SCHEME_PRIM_TYPE_STRUCT_PROP_GETTER)
return 5;
return INLINE_STRUCT_PROC_PROP_GET_W_DEFAULT;
}
}
return 0;
@ -178,12 +188,15 @@ int scheme_inlined_binary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_sta
|| inlineable_struct_prim(o, jitter, 2, 2));
}
int scheme_inlined_nary_prim(Scheme_Object *o, Scheme_Object *_app)
int scheme_inlined_nary_prim(Scheme_Object *o, Scheme_Object *_app, mz_jit_state *jitter)
{
return (SCHEME_PRIMP(o)
&& (SCHEME_PRIM_PROC_FLAGS(o) & SCHEME_PRIM_IS_NARY_INLINED)
&& (((Scheme_App_Rec *)_app)->num_args >= ((Scheme_Primitive_Proc *)o)->mina)
&& (((Scheme_App_Rec *)_app)->num_args <= ((Scheme_Primitive_Proc *)o)->mu.maxa));
int n = ((Scheme_App_Rec *)_app)->num_args;
return ((SCHEME_PRIMP(o)
&& (SCHEME_PRIM_PROC_FLAGS(o) & SCHEME_PRIM_IS_NARY_INLINED)
&& (n >= ((Scheme_Primitive_Proc *)o)->mina)
&& (n <= ((Scheme_Primitive_Proc *)o)->mu.maxa))
|| inlineable_struct_prim(o, jitter, n, n));
}
static int generate_inlined_constant_test(mz_jit_state *jitter, Scheme_App2_Rec *app,
@ -345,7 +358,7 @@ static int generate_inlined_type_test(mz_jit_state *jitter, Scheme_App2_Rec *app
static int generate_inlined_struct_op(int kind, mz_jit_state *jitter,
Scheme_Object *rator, Scheme_Object *rand, Scheme_Object *rand2,
Branch_Info *for_branch, int branch_short,
int multi_ok)
int is_tail, int multi_ok)
/* de-sync'd ok; for branch, sync'd before */
{
LOG_IT(("inlined struct op\n"));
@ -382,42 +395,62 @@ static int generate_inlined_struct_op(int kind, mz_jit_state *jitter,
scheme_branch_for_true(jitter, for_branch);
__END_SHORT_JUMPS__(for_branch->branch_short);
CHECK_LIMIT();
} else if (kind == 1) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_PRED) {
if (is_tail) {
(void)jit_calli(sjc.struct_pred_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_pred_multi_code);
} else {
(void)jit_calli(sjc.struct_pred_code);
}
} else if (kind == 2) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_GET) {
if (is_tail) {
(void)jit_calli(sjc.struct_get_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_get_multi_code);
} else {
(void)jit_calli(sjc.struct_get_code);
}
} else if (kind == 3) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_SET) {
if (is_tail) {
(void)jit_calli(sjc.struct_set_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_set_multi_code);
} else {
(void)jit_calli(sjc.struct_set_code);
}
} else if (kind == 4) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_PROP_GET) {
if (is_tail) {
(void)jit_calli(sjc.struct_prop_get_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_prop_get_multi_code);
} else {
(void)jit_calli(sjc.struct_prop_get_code);
}
} else if (kind == 5) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_PROP_GET_W_DEFAULT) {
if (is_tail) {
(void)jit_calli(sjc.struct_prop_get_defl_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_prop_get_defl_multi_code);
} else {
(void)jit_calli(sjc.struct_prop_get_defl_code);
}
} else if (kind == 6) {
if (multi_ok) {
} else if (kind == INLINE_STRUCT_PROC_PROP_PRED) {
if (is_tail) {
(void)jit_calli(sjc.struct_prop_pred_tail_code);
} else if (multi_ok) {
(void)jit_calli(sjc.struct_prop_pred_multi_code);
} else {
(void)jit_calli(sjc.struct_prop_pred_code);
}
} else if (kind == INLINE_STRUCT_PROC_CONSTR) {
return 0;
#if 0
if (!rand2)
(void)jit_calli(sjc.struct_constr_unary_code);
else
(void)jit_calli(sjc.struct_constr_binary_code);
#endif
} else {
scheme_signal_error("internal error: unknown struct-op mode");
}
@ -425,6 +458,23 @@ static int generate_inlined_struct_op(int kind, mz_jit_state *jitter,
return 1;
}
static int generate_inlined_nary_struct_op(int kind, mz_jit_state *jitter,
Scheme_Object *rator, Scheme_App_Rec *app,
Branch_Info *for_branch, int branch_short,
int multi_ok)
/* de-sync'd ok; for branch, sync'd before */
{
#if 1
scheme_signal_error("shouldn't get here, yet"); /* REMOVEME */
#else
/* generate code to evaluate the arguments */
scheme_generate_app(app, NULL, app->num_args, jitter, 0, 0, 2);
CHECK_LIMIT();
mz_rs_sync();
#endif
return 0;
}
static int is_cXr_prim(const char *name)
{
int i;
@ -503,12 +553,16 @@ int scheme_generate_inlined_unary(mz_jit_state *jitter, Scheme_App2_Rec *app, in
{
int k;
k = inlineable_struct_prim(rator, jitter, 1, 1);
if (k == 1) {
generate_inlined_struct_op(1, jitter, rator, app->rand, NULL, for_branch, branch_short, multi_ok);
if (k == INLINE_STRUCT_PROC_PRED) {
generate_inlined_struct_op(1, jitter, rator, app->rand, NULL, for_branch, branch_short, is_tail, multi_ok);
scheme_direct_call_count++;
return 1;
} else if (((k == 2) || (k == 4) || (k == 6)) && !for_branch) {
generate_inlined_struct_op(k, jitter, rator, app->rand, NULL, for_branch, branch_short, multi_ok);
} else if (((k == INLINE_STRUCT_PROC_GET)
|| (k == INLINE_STRUCT_PROC_PROP_GET)
|| (k == INLINE_STRUCT_PROC_PROP_PRED)
|| (k == INLINE_STRUCT_PROC_CONSTR))
&& !for_branch) {
generate_inlined_struct_op(k, jitter, rator, app->rand, NULL, for_branch, branch_short, is_tail, multi_ok);
scheme_direct_call_count++;
return 1;
}
@ -1730,7 +1784,7 @@ int scheme_generate_inlined_binary(mz_jit_state *jitter, Scheme_App3_Rec *app, i
int k;
k = inlineable_struct_prim(rator, jitter, 2, 2);
if (k) {
generate_inlined_struct_op(k, jitter, rator, app->rand1, app->rand2, for_branch, branch_short, multi_ok);
generate_inlined_struct_op(k, jitter, rator, app->rand1, app->rand2, for_branch, branch_short, is_tail, multi_ok);
scheme_direct_call_count++;
return 1;
}
@ -2790,7 +2844,17 @@ int scheme_generate_inlined_nary(mz_jit_state *jitter, Scheme_App_Rec *app, int
/* de-sync's; for branch, sync'd before */
{
Scheme_Object *rator = app->args[0];
if (!for_branch) {
int k;
k = inlineable_struct_prim(rator, jitter, app->num_args, app->num_args);
if (k) {
generate_inlined_nary_struct_op(k, jitter, rator, app, for_branch, branch_short, multi_ok);
scheme_direct_call_count++;
return 1;
}
}
if (!SCHEME_PRIMP(rator))
return 0;

View File

@ -819,6 +819,8 @@ Scheme_Object *scheme_extract_struct_procedure(Scheme_Object *obj, int num_rands
Scheme_Object *scheme_proc_struct_name_source(Scheme_Object *a);
Scheme_Object *scheme_object_name(Scheme_Object *a);
int scheme_is_simple_struct_type(Scheme_Struct_Type *stype);
Scheme_Object *scheme_is_writable_struct(Scheme_Object *s);
Scheme_Object *scheme_print_attribute_ref(Scheme_Object *s);

View File

@ -2265,7 +2265,7 @@ make_simple_struct_instance(int argc, Scheme_Object **args, Scheme_Object *prim)
return (Scheme_Object *)inst;
}
static int is_simple_struct_type(Scheme_Struct_Type *stype)
int scheme_is_simple_struct_type(Scheme_Struct_Type *stype)
{
int p;
@ -3771,7 +3771,7 @@ make_struct_proc(Scheme_Struct_Type *struct_type,
if (proc_type == SCHEME_CONSTR) {
int simple;
simple = is_simple_struct_type(struct_type);
simple = scheme_is_simple_struct_type(struct_type);
a[0] = (Scheme_Object *)struct_type;
p = scheme_make_folding_prim_closure((simple
? make_simple_struct_instance