compiler letrec_check pass: recognize effect-free primitives

As in "Fixing Letrec". This improvement corrects a performance
regression with the revised expansion of R5RS `letrec`, which
wraps right-hand sides with `values`.

Besides detecting effect-free primitives, we have to fix the
treatment of the right-hand side for a multi-binding `letrec-values`
clause. For now, we conflate all of the bindings in a single
clause.
This commit is contained in:
Matthew Flatt 2014-05-30 11:16:46 +01:00
parent 71591a62a4
commit d1be74fc3b
2 changed files with 105 additions and 32 deletions

View File

@ -1427,6 +1427,22 @@
(list x x y)))])
(g values)))
(test-comp '(letrec-values ([(g f) (values
;; The `let`s provide names:
(let ([g (lambda () (f))]) g)
(let ([f (lambda () (g))]) f))])
(g))
'(letrec ([g (lambda () (f))]
[f (lambda () (g))])
(g)))
;; Since `list` is effect-free, `f` should not be checked for
;; undefined. I don't see a way to test that, though.
#;
(letrec ([f (list
(let ([f (lambda () f)]) f))])
(car f))
(test-comp '(let-values ([(x y) (values 1 2)])
(+ x y))
3)

View File

@ -599,6 +599,15 @@ static Scheme_Object *letrec_check_local(Scheme_Object *o, Letrec_Check_Frame *f
return o;
}
static int is_effect_free_prim(Scheme_Object *rator)
{
if (SCHEME_PRIMP(rator)
&& (SCHEME_PRIM_PROC_OPT_FLAGS(rator) & SCHEME_PRIM_IS_OMITABLE))
return 1;
return 0;
}
static Scheme_Object *letrec_check_application(Scheme_Object *o, Letrec_Check_Frame *frame,
Scheme_Object *uvars, Scheme_Object *pvars, Scheme_Object *pos)
{
@ -611,11 +620,17 @@ static Scheme_Object *letrec_check_application(Scheme_Object *o, Letrec_Check_Fr
/* we'll have to check the rator and all the arguments */
n = 1 + app->num_args;
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
if (is_effect_free_prim(app->args[0])) {
/* an immediate prim cannot call anything among its arguments */
new_uvars = uvars;
new_pvars = pvars;
} else {
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
}
for (i = 0; i < n; i++) {
val = letrec_check_expr(app->args[i], frame, new_uvars, new_pvars, pos);
@ -633,11 +648,17 @@ static Scheme_Object *letrec_check_application2(Scheme_Object *o, Letrec_Check_F
app = (Scheme_App2_Rec *)o;
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
if (is_effect_free_prim(app->rator)) {
/* an immediate prim cannot call anything among its arguments */
new_uvars = uvars;
new_pvars = pvars;
} else {
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
}
val = letrec_check_expr(app->rator, frame, new_uvars, new_pvars, pos);
app->rator = val;
@ -655,11 +676,17 @@ static Scheme_Object *letrec_check_application3(Scheme_Object *o, Letrec_Check_F
app = (Scheme_App3_Rec *)o;
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
if (is_effect_free_prim(app->rator)) {
/* an immediate prim cannot call anything among its arguments */
new_uvars = uvars;
new_pvars = pvars;
} else {
/* by entering the sub-expressions of an application, all
protectable variables are moved to the unprotected state. */
new_uvars = merge_vars(uvars, pvars);
new_pvars = rem_vars(pvars);
pos = scheme_false;
}
val = letrec_check_expr(app->rator, frame, new_uvars, new_pvars, pos);
app->rator = val;
@ -760,18 +787,32 @@ static Scheme_Object *letrec_check_closure_compilation(Scheme_Object *o, Letrec_
application! hurray! */
Scheme_Deferred_Expr *clos;
Letrec_Check_Frame *outer_frame;
/* create a deferred expression that closes over the frame it
appeared in, the variable to which it is being deferred,
and the current uvars and pvars */
int position = SCHEME_INT_VAL(pos);
clos = make_deferred_expr_closure(data, frame, position, uvars, pvars);
Letrec_Check_Frame *outer_frame = NULL;
/* get the correct frame and stick the deferred_expr_closure up there */
outer_frame = get_nearest_rhs(frame);
update_frame(outer_frame, frame, position, clos);
/* pos is either a single integer or a list of integers */
while (SCHEME_INTP(pos) || SCHEME_PAIRP(pos)) {
/* create a deferred expression that closes over the frame it
appeared in, the variable to which it is being deferred,
and the current uvars and pvars */
int position;
if (SCHEME_INTP(pos)) {
position = SCHEME_INT_VAL(pos);
pos = scheme_null;
} else {
position = SCHEME_INT_VAL(SCHEME_CAR(pos));
pos = SCHEME_CDR(pos);
}
clos = make_deferred_expr_closure(data, frame, position, uvars, pvars);
/* get the correct frame: */
if (!outer_frame)
outer_frame = get_nearest_rhs(frame);
/* attach the deferred_expr_closure to the right position in the correct frame */
update_frame(outer_frame, frame, position, clos);
}
}
return o;
@ -1174,18 +1215,34 @@ static Scheme_Object *letrec_check_lets(Scheme_Object *o, Letrec_Check_Frame *ol
k -= clv->count;
/* TODO: is there something more sane for the treatment of
bindings with no variables? every lambda in the rhs will
be treated as if there is an unsafe application where in
fact it is just the opposite, no unsafe application can
possibly happen */
if (clv->count == 0) {
val = letrec_check_expr(clv->value, frame, new_uvars, new_pvars,
scheme_false);
/* deferred closures get attached to no variables,
which is sensible because the closure will not
be reachable: */
scheme_null);
}
else if (frame_type == FRAME_TYPE_LETREC) {
Scheme_Object *new_pos;
if (clv->count == 1) {
/* any deferred closure on the right-hand side gets attached to the
variable on the left-hand side: */
new_pos = scheme_make_integer(k);
} else {
/* attach any deferred closures on the right-hand side to all
variables on the left-hand side; we could do better by
recognizing an immediate `values` to avoid conflating all
variables in that case */
int sub;
new_pos = scheme_null;
for (sub = clv->count; sub--; ) {
new_pos = scheme_make_pair(scheme_make_integer(k+sub), new_pos);
}
}
val = letrec_check_expr(clv->value, frame, new_uvars, new_pvars,
scheme_make_integer(k));
new_pos);
}
else {
val = letrec_check_expr(clv->value, frame, new_uvars, new_pvars,