optimizer: refine tracking of when space safety is a constraint

Some expression movements are limited by the possibility of retaining
a value in a way that interacts with space safety, but primitives that
return immediately shouldn't get in the way of those movements.
This commit is contained in:
Matthew Flatt 2014-11-02 19:33:11 -07:00
parent 9a94366c2c
commit 89106b6708
2 changed files with 73 additions and 23 deletions

View File

@ -1754,7 +1754,6 @@
'(lambda (z)
(list (list (z 2)) (list z))))
#;
(test-comp '(lambda (z)
;; It's ok to reorder unsafe operations relative
;; to each other:

View File

@ -63,6 +63,7 @@ struct Optimize_Info
increment that models a branch (if the branch is not
taken or doesn't increment the clock) */
int kclock; /* virtual clock that ticks for a potential continuation capture */
int sclock; /* virtual clock that ticks when space consumption is potentially observed */
int psize;
short inline_fuel, shift_fuel;
char letrec_not_twice, enforce_const, use_psize, has_nonleaf;
@ -148,7 +149,7 @@ static Scheme_Object *optimize_ignored(Scheme_Object *e, Optimize_Info *info, in
int expected_vals, int maybe_omittable,
int fuel);
static int movable_expression(Scheme_Object *expr, Optimize_Info *info, int delta,
int cross_lambda, int cross_k,
int cross_lambda, int cross_k, int cross_s,
int check_space, int fuel);
#define ID_OMIT 0
@ -166,6 +167,7 @@ typedef struct Scheme_Once_Used {
int pos;
int vclock;
int kclock;
int sclock;
int used;
int delta;
@ -175,7 +177,9 @@ typedef struct Scheme_Once_Used {
struct Scheme_Once_Used *next;
} Scheme_Once_Used;
static Scheme_Once_Used *make_once_used(Scheme_Object *val, int pos, int vclock, int kclock, Scheme_Once_Used *prev);
static Scheme_Once_Used *make_once_used(Scheme_Object *val, int pos,
int vclock, int kclock, int sclock,
Scheme_Once_Used *prev);
#ifdef MZ_PRECISE_GC
static void register_traversers(void);
@ -545,7 +549,7 @@ static Scheme_Object *do_make_discarding_sequence(Scheme_Object *e1, Scheme_Obje
return e1;
/* use `begin` instead of `begin0` if we can swap the order: */
if (rev && movable_expression(e2, info, -id_offset, 0, 0, 0, 50))
if (rev && movable_expression(e2, info, -id_offset, 0, 0, 0, 0, 50))
rev = 0;
return scheme_make_sequence_compilation(scheme_make_pair((rev ? e2 : e1),
@ -1277,7 +1281,7 @@ static int is_movable_prim(Scheme_Object *rator, int n, int cross_lambda, int cr
}
static int movable_expression(Scheme_Object *expr, Optimize_Info *info, int delta,
int cross_lambda, int cross_k,
int cross_lambda, int cross_k, int cross_s,
int check_space, int fuel)
/* An expression that can't necessarily be constant-folded,
but can be delayed because it has no side-effects (or is unsafe),
@ -1317,8 +1321,9 @@ static int movable_expression(Scheme_Object *expr, Optimize_Info *info, int delt
if (can_move) {
int i;
for (i = ((Scheme_App_Rec *)expr)->num_args; i--; ) {
if (!movable_expression(((Scheme_App_Rec *)expr)->args[i+1], info, delta, cross_lambda, cross_k,
check_space || (can_move < 0), fuel - 1))
if (!movable_expression(((Scheme_App_Rec *)expr)->args[i+1], info, delta,
cross_lambda, cross_k, cross_s,
check_space || (cross_s && (can_move < 0)), fuel - 1))
return 0;
}
return 1;
@ -1327,27 +1332,30 @@ static int movable_expression(Scheme_Object *expr, Optimize_Info *info, int delt
case scheme_application2_type:
can_move = is_movable_prim(((Scheme_App2_Rec *)expr)->rator, 1, cross_lambda, cross_k, info);
if (can_move) {
if (movable_expression(((Scheme_App2_Rec *)expr)->rand, info, delta, cross_lambda, cross_k,
check_space || (can_move < 0), fuel - 1))
if (movable_expression(((Scheme_App2_Rec *)expr)->rand, info, delta,
cross_lambda, cross_k, cross_s,
check_space || (cross_s && (can_move < 0)), fuel - 1))
return 1;
}
break;
case scheme_application3_type:
can_move = is_movable_prim(((Scheme_App3_Rec *)expr)->rator, 2, cross_lambda, cross_k, info);
if (can_move) {
if (movable_expression(((Scheme_App3_Rec *)expr)->rand1, info, delta, cross_lambda, cross_k,
check_space || (can_move < 0), fuel - 1)
&& movable_expression(((Scheme_App3_Rec *)expr)->rand2, info, delta, cross_lambda, cross_k,
check_space || (can_move < 0), fuel - 1))
if (movable_expression(((Scheme_App3_Rec *)expr)->rand1, info, delta,
cross_lambda, cross_k, cross_s,
check_space || (cross_s && (can_move < 0)), fuel - 1)
&& movable_expression(((Scheme_App3_Rec *)expr)->rand2, info, delta,
cross_lambda, cross_k, cross_s,
check_space || (cross_s && (can_move < 0)), fuel - 1))
return 1;
}
break;
case scheme_branch_type:
{
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
if (movable_expression(b->test, info, delta, cross_lambda, cross_k, check_space, fuel-1)
&& movable_expression(b->tbranch, info, delta, cross_lambda, cross_k, check_space, fuel-1)
&& movable_expression(b->fbranch, info, delta, cross_lambda, cross_k, check_space, fuel-1))
if (movable_expression(b->test, info, delta, cross_lambda, cross_k, cross_s, check_space, fuel-1)
&& movable_expression(b->tbranch, info, delta, cross_lambda, cross_k, cross_s, check_space, fuel-1)
&& movable_expression(b->fbranch, info, delta, cross_lambda, cross_k, cross_s, check_space, fuel-1))
return 1;
}
break;
@ -2186,6 +2194,18 @@ static int is_noncapturing_primitive(Scheme_Object *rator, int n)
return 0;
}
static int is_nonsaving_primitive(Scheme_Object *rator, int n)
{
if (SCHEME_PRIMP(rator)) {
int opt;
opt = ((Scheme_Prim_Proc_Header *)rator)->flags & SCHEME_PRIM_OPT_MASK;
if (opt >= SCHEME_PRIM_OPT_IMMEDIATE)
return 1;
}
return 0;
}
#define IS_NAMED_PRIM(p, nm) (!strcmp(((Scheme_Primitive_Proc *)p)->name, nm))
static int wants_local_type_arguments(Scheme_Object *rator, int argpos)
@ -2668,6 +2688,8 @@ static Scheme_Object *finish_optimize_application(Scheme_App_Rec *app, Optimize_
info->vclock += 1;
if (!is_noncapturing_primitive(app->args[0], app->num_args))
info->kclock += 1;
if (!is_nonsaving_primitive(app->args[0], app->num_args))
info->sclock += 1;
if (all_vals) {
le = try_optimize_fold(app->args[0], NULL, (Scheme_Object *)app, info);
@ -2941,6 +2963,8 @@ static Scheme_Object *finish_optimize_application2(Scheme_App2_Rec *app, Optimiz
info->vclock += 1;
if (!is_noncapturing_primitive(app->rator, 1))
info->kclock += 1;
if (!is_nonsaving_primitive(app->rator, 1))
info->sclock += 1;
info->preserves_marks = !!(rator_flags & CLOS_PRESERVES_MARKS);
info->single_result = !!(rator_flags & CLOS_SINGLE_RESULT);
@ -3259,6 +3283,8 @@ static Scheme_Object *finish_optimize_application3(Scheme_App3_Rec *app, Optimiz
info->vclock += 1;
if (!is_noncapturing_primitive(app->rator, 2))
info->kclock += 1;
if (!is_nonsaving_primitive(app->rator, 2))
info->sclock += 1;
/* Check for (call-with-values (lambda () M) N): */
if (SAME_OBJ(app->rator, scheme_call_with_values_proc)) {
@ -3743,7 +3769,8 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
Scheme_Branch_Rec *b;
Scheme_Object *t, *tb, *fb;
Scheme_Hash_Tree *old_types;
int preserves_marks = 1, single_result = 1, same_then_vclock, init_vclock, init_kclock, then_kclock;
int preserves_marks = 1, single_result = 1, init_vclock, init_kclock, init_sclock;
int same_then_vclock, then_kclock, then_sclock;
Optimize_Info_Sequence info_seq;
b = (Scheme_Branch_Rec *)o;
@ -3848,6 +3875,7 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
info->vclock += 1; /* model branch as clock increment */
init_vclock = info->vclock;
init_kclock = info->kclock;
init_sclock = info->sclock;
old_types = info->types;
add_types(t, info, 5);
@ -3867,8 +3895,10 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
info->types = old_types;
then_kclock = info->kclock;
then_sclock = info->sclock;
info->vclock = init_vclock;
info->kclock = init_kclock;
info->sclock = init_sclock;
optimize_info_seq_step(info, &info_seq);
@ -3885,6 +3915,8 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
if (then_kclock > info->kclock)
info->kclock = then_kclock;
if (then_sclock > info->sclock)
info->sclock = then_sclock;
info->types = old_types; /* could try to take an intersection here ... */
@ -4183,6 +4215,7 @@ apply_values_optimize(Scheme_Object *data, Optimize_Info *info, int context)
info->size += 1;
info->vclock += 1;
info->kclock += 1;
info->sclock += 1;
return scheme_optimize_apply_values(f, e, info, info->single_result, context);
}
@ -5440,7 +5473,9 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
if (cnt == 1) {
/* used only once; we may be able to shift the expression to the use
site, instead of binding to a temporary */
once_used = make_once_used(value, pos, rhs_info->vclock, rhs_info->kclock, NULL);
once_used = make_once_used(value, pos,
rhs_info->vclock, rhs_info->kclock, rhs_info->sclock,
NULL);
if (!last_once_used)
first_once_used = once_used;
else
@ -5460,7 +5495,9 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
cnt = ((pre_body->flags[i] & SCHEME_USE_COUNT_MASK) >> SCHEME_USE_COUNT_SHIFT);
if (cnt == 1) {
/* Need to register as once-used, in case of copy propagation */
once_used = make_once_used(NULL, pos+i, rhs_info->vclock, rhs_info->kclock, NULL);
once_used = make_once_used(NULL, pos+i,
rhs_info->vclock, rhs_info->kclock, rhs_info->sclock,
NULL);
if (!last_once_used)
first_once_used = once_used;
else
@ -5660,6 +5697,7 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
info->preserves_marks = body_info->preserves_marks;
info->vclock = body_info->vclock;
info->kclock = body_info->kclock;
info->sclock = body_info->sclock;
/* Clear used flags where possible */
body = head->body;
@ -5823,7 +5861,7 @@ optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, int cont
Scheme_Object *code, *ctx;
Closure_Info *cl;
mzshort dcs, *dcm;
int i, cnt, init_vclock, init_kclock;
int i, cnt, init_vclock, init_kclock, init_sclock;
Scheme_Once_Used *first_once_used = NULL, *last_once_used = NULL;
data = (Scheme_Closure_Data *)_data;
@ -5836,9 +5874,11 @@ optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, int cont
init_vclock = info->vclock;
init_kclock = info->kclock;
init_sclock = info->sclock;
info->vclock += 1; /* model delayed evaluation as vclock increment */
info->kclock += 1;
info->sclock += 1;
/* For reporting warnings: */
if (info->context && SCHEME_PAIRP(info->context))
@ -5857,7 +5897,9 @@ optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, int cont
cnt = ((cl->local_flags[i] & SCHEME_USE_COUNT_MASK) >> SCHEME_USE_COUNT_SHIFT);
if (cnt == 1) {
last_once_used = make_once_used(NULL, i, info->vclock, info->kclock, last_once_used);
last_once_used = make_once_used(NULL, i,
info->vclock, info->kclock, info->sclock,
last_once_used);
if (!first_once_used) first_once_used = last_once_used;
optimize_propagate(info, i, (Scheme_Object *)last_once_used, 1);
}
@ -5911,6 +5953,7 @@ optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, int cont
/* closure itself is not an effect */
info->vclock = init_vclock;
info->kclock = init_kclock;
info->sclock = init_sclock;
info->size++;
@ -6842,17 +6885,19 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
|| single_valued_noncm_expression(o->expr, 5)))
|| movable_expression(o->expr, info, o->delta, o->cross_lambda,
o->kclock != info->kclock,
o->sclock != info->sclock,
0, 5)) {
val = optimize_clone(1, o->expr, info, o->delta, 0);
if (val) {
int save_fuel = info->inline_fuel, save_no_types = info->no_types;
int save_vclock, save_kclock;
int save_vclock, save_kclock, save_sclock;
info->size -= 1;
o->used = 1;
info->inline_fuel = 0; /* no more inlining; o->expr was already optimized */
info->no_types = 1; /* cannot used inferred types, in case `val' inferred them */
save_vclock = info->vclock; /* allowed to move => no change to clocks */
save_kclock = info->kclock;
save_sclock = info->sclock;
val = scheme_optimize_expr(val, info, context);
@ -6860,6 +6905,7 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
info->no_types = save_no_types;
info->vclock = save_vclock;
info->kclock = save_kclock;
info->sclock = save_sclock;
return val;
}
}
@ -7665,7 +7711,9 @@ static void optimize_propagate(Optimize_Info *info, int pos, Scheme_Object *valu
info->consts = p;
}
static Scheme_Once_Used *make_once_used(Scheme_Object *val, int pos, int vclock, int kclock, Scheme_Once_Used *prev)
static Scheme_Once_Used *make_once_used(Scheme_Object *val, int pos,
int vclock, int kclock, int sclock,
Scheme_Once_Used *prev)
{
Scheme_Once_Used *o;
@ -7676,6 +7724,7 @@ static Scheme_Once_Used *make_once_used(Scheme_Object *val, int pos, int vclock,
o->pos = pos;
o->vclock = vclock;
o->kclock = kclock;
o->sclock = sclock;
if (prev)
prev->next = o;
@ -8023,6 +8072,7 @@ static Optimize_Info *optimize_info_add_frame(Optimize_Info *info, int orig, int
naya->context = info->context;
naya->vclock = info->vclock;
naya->kclock = info->kclock;
naya->sclock = info->sclock;
naya->init_kclock = info->kclock;
naya->use_psize = info->use_psize;
naya->logger = info->logger;
@ -8056,6 +8106,7 @@ static void optimize_info_done(Optimize_Info *info, Optimize_Info *parent)
parent->size += info->size;
parent->vclock = info->vclock;
parent->kclock = info->kclock;
parent->sclock = info->sclock;
parent->psize += info->psize;
parent->shift_fuel = info->shift_fuel;
if (info->has_nonleaf)