Try to collapse references in a branch using the type information of the other branch
A reference to a local may be reduced in a branch to a constant, while it's unchanged in the other because the optimizer has different type information for each branch. Try to use the type information of the other branch to see if both branches are actually equivalent. For example, (if (null? x) x x) is first reduced to (if (null? x) null x) using the type information of the #t branch. But both branches are equivalent so they can be reduced to (begin (null? x) x) and then to just x.
This commit is contained in:
parent
3f246dd857
commit
65838bd3c8
|
@ -1762,6 +1762,12 @@
|
||||||
(test-comp '(lambda (x) (if (values 1 2) 78 78))
|
(test-comp '(lambda (x) (if (values 1 2) 78 78))
|
||||||
'(lambda (x) (values 1 2) 78)
|
'(lambda (x) (values 1 2) 78)
|
||||||
#f)
|
#f)
|
||||||
|
(test-comp '(lambda (x) (if (null? x) x x))
|
||||||
|
'(lambda (x) x))
|
||||||
|
(test-comp '(lambda (x) (if (null? x) null x))
|
||||||
|
'(lambda (x) x))
|
||||||
|
(test-comp '(lambda (x) (not (if (null? x) #t x)))
|
||||||
|
'(lambda (x) (not x)))
|
||||||
|
|
||||||
(test-comp '(lambda (x) (if (let ([r (something)])
|
(test-comp '(lambda (x) (if (let ([r (something)])
|
||||||
(if r r (something-else)))
|
(if r r (something-else)))
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* This file implements bytecode optimzation.
|
/* This file implements bytecode optimization.
|
||||||
|
|
||||||
See "eval.c" for an overview of compilation passes. */
|
See "eval.c" for an overview of compilation passes. */
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ struct Optimize_Info
|
||||||
|
|
||||||
/* Set by expression optimization: */
|
/* Set by expression optimization: */
|
||||||
int single_result, preserves_marks; /* negative means "tentative", due to fixpoint in progress */
|
int single_result, preserves_marks; /* negative means "tentative", due to fixpoint in progress */
|
||||||
int escapes; /* flag to signal that the expression allways escapes. When escapes is 1, it's assumed
|
int escapes; /* flag to signal that the expression always escapes. When escapes is 1, it's assumed
|
||||||
that single_result and preserves_marks are also 1, and that it's not necesary to
|
that single_result and preserves_marks are also 1, and that it's not necessary to
|
||||||
use optimize_ignored before including the expression. */
|
use optimize_ignored before including the expression. */
|
||||||
|
|
||||||
char **stat_dists; /* (pos, depth) => used? */
|
char **stat_dists; /* (pos, depth) => used? */
|
||||||
|
@ -4176,16 +4176,63 @@ int scheme_compiled_duplicate_ok(Scheme_Object *fb, int cross_module)
|
||||||
|| SAME_TYPE(SCHEME_TYPE(fb), scheme_ctype_type));
|
|| SAME_TYPE(SCHEME_TYPE(fb), scheme_ctype_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int equivalent_exprs(Scheme_Object *a, Scheme_Object *b)
|
static Scheme_Object *collapse_local(int pos, Optimize_Info *info, int context)
|
||||||
|
/* pos is in new-frame counts */
|
||||||
|
{
|
||||||
|
if (!optimize_is_mutated(info, pos)) {
|
||||||
|
Scheme_Object *pred;
|
||||||
|
|
||||||
|
pred = optimize_get_predicate(pos, info);
|
||||||
|
if (pred) {
|
||||||
|
if (SAME_OBJ(pred, scheme_not_prim))
|
||||||
|
return scheme_false;
|
||||||
|
|
||||||
|
if (context & OPT_CONTEXT_BOOLEAN) {
|
||||||
|
/* all other predicates recognize non-#f things */
|
||||||
|
return scheme_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SAME_OBJ(pred, scheme_null_p_proc))
|
||||||
|
return scheme_null;
|
||||||
|
if (SAME_OBJ(pred, scheme_void_p_proc))
|
||||||
|
return scheme_void;
|
||||||
|
if (SAME_OBJ(pred, scheme_eof_object_p_proc))
|
||||||
|
return scheme_eof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Scheme_Object *equivalent_exprs(Scheme_Object *a, Scheme_Object *b,
|
||||||
|
Optimize_Info *a_info, Optimize_Info *b_info, int context)
|
||||||
{
|
{
|
||||||
if (SAME_OBJ(a, b))
|
if (SAME_OBJ(a, b))
|
||||||
return 1;
|
return a;
|
||||||
|
|
||||||
if (SAME_TYPE(SCHEME_TYPE(a), scheme_local_type)
|
if (SAME_TYPE(SCHEME_TYPE(a), scheme_local_type)
|
||||||
&& SAME_TYPE(SCHEME_TYPE(b), scheme_local_type)
|
&& SAME_TYPE(SCHEME_TYPE(b), scheme_local_type)
|
||||||
&& (SCHEME_LOCAL_POS(a) == SCHEME_LOCAL_POS(b)))
|
&& (SCHEME_LOCAL_POS(a) == SCHEME_LOCAL_POS(b)))
|
||||||
return 1;
|
return a;
|
||||||
|
|
||||||
return 0;
|
if (b_info
|
||||||
|
&& SAME_TYPE(SCHEME_TYPE(a), scheme_local_type)
|
||||||
|
&& (SCHEME_TYPE(b) > _scheme_compiled_values_types_)) {
|
||||||
|
Scheme_Object *n;
|
||||||
|
n = collapse_local(SCHEME_LOCAL_POS(a), b_info, context);
|
||||||
|
if (n && SAME_OBJ(n, b))
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a_info
|
||||||
|
&& SAME_TYPE(SCHEME_TYPE(b), scheme_local_type)
|
||||||
|
&& (SCHEME_TYPE(a) > _scheme_compiled_values_types_)) {
|
||||||
|
Scheme_Object *n;
|
||||||
|
n = collapse_local(SCHEME_LOCAL_POS(b), a_info, context);
|
||||||
|
if (n && SAME_OBJ(n, a))
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_type(Optimize_Info *info, int pos, Scheme_Object *pred)
|
static void add_type(Optimize_Info *info, int pos, Scheme_Object *pred)
|
||||||
|
@ -4220,8 +4267,9 @@ static void merge_types(Optimize_Info *src_info, Optimize_Info *info, int delta)
|
||||||
|
|
||||||
static void intersect_and_merge_types(Optimize_Info *t_info, Optimize_Info *f_info,
|
static void intersect_and_merge_types(Optimize_Info *t_info, Optimize_Info *f_info,
|
||||||
Optimize_Info *base_info)
|
Optimize_Info *base_info)
|
||||||
/* return (union (intersetion t_type f_types) base_types)
|
/* Save in base_info->types the result of
|
||||||
in case a key is already in base_type, the value is not modified*/
|
(union (intersection t_info->type f_info->types) base_info->types)
|
||||||
|
in case a key is already in base_info->types, the value is not modified */
|
||||||
{
|
{
|
||||||
Scheme_Hash_Tree *t_types = t_info->types, *f_types = f_info->types,
|
Scheme_Hash_Tree *t_types = t_info->types, *f_types = f_info->types,
|
||||||
*base_types = base_info->types;
|
*base_types = base_info->types;
|
||||||
|
@ -4377,6 +4425,7 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
|
||||||
Scheme_Object *t, *tb, *fb;
|
Scheme_Object *t, *tb, *fb;
|
||||||
int init_vclock, init_aclock, init_kclock, init_sclock;
|
int init_vclock, init_aclock, init_kclock, init_sclock;
|
||||||
Optimize_Info *then_info, *else_info;
|
Optimize_Info *then_info, *else_info;
|
||||||
|
Optimize_Info *then_info_init, *else_info_init;
|
||||||
Optimize_Info_Sequence info_seq;
|
Optimize_Info_Sequence info_seq;
|
||||||
Scheme_Object *pred;
|
Scheme_Object *pred;
|
||||||
|
|
||||||
|
@ -4495,6 +4544,7 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
|
||||||
|
|
||||||
then_info = optimize_info_add_frame(info, 0, 0, 0);
|
then_info = optimize_info_add_frame(info, 0, 0, 0);
|
||||||
add_types_for_t_branch(t, then_info, 5);
|
add_types_for_t_branch(t, then_info, 5);
|
||||||
|
then_info_init = optimize_info_add_frame(then_info, 0, 0, 0);
|
||||||
tb = scheme_optimize_expr(tb, then_info, scheme_optimize_tail_context(context));
|
tb = scheme_optimize_expr(tb, then_info, scheme_optimize_tail_context(context));
|
||||||
optimize_info_done(then_info, NULL);
|
optimize_info_done(then_info, NULL);
|
||||||
|
|
||||||
|
@ -4508,6 +4558,7 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
|
||||||
|
|
||||||
else_info = optimize_info_add_frame(info, 0, 0, 0);
|
else_info = optimize_info_add_frame(info, 0, 0, 0);
|
||||||
add_types_for_f_branch(t, else_info, 5);
|
add_types_for_f_branch(t, else_info, 5);
|
||||||
|
else_info_init = optimize_info_add_frame(else_info, 0, 0, 0);
|
||||||
fb = scheme_optimize_expr(fb, else_info, scheme_optimize_tail_context(context));
|
fb = scheme_optimize_expr(fb, else_info, scheme_optimize_tail_context(context));
|
||||||
optimize_info_done(else_info, NULL);
|
optimize_info_done(else_info, NULL);
|
||||||
|
|
||||||
|
@ -4555,15 +4606,6 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
|
||||||
|
|
||||||
optimize_info_seq_done(info, &info_seq);
|
optimize_info_seq_done(info, &info_seq);
|
||||||
|
|
||||||
/* Try optimize: (if x x #f) => x */
|
|
||||||
if (SAME_TYPE(SCHEME_TYPE(t), scheme_local_type)
|
|
||||||
&& SAME_TYPE(SCHEME_TYPE(tb), scheme_local_type)
|
|
||||||
&& (SCHEME_LOCAL_POS(t) == SCHEME_LOCAL_POS(tb))
|
|
||||||
&& SCHEME_FALSEP(fb)) {
|
|
||||||
info->size -= 2;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try optimize: (if x #f #t) => (not x) */
|
/* Try optimize: (if x #f #t) => (not x) */
|
||||||
if (SCHEME_FALSEP(tb)
|
if (SCHEME_FALSEP(tb)
|
||||||
&& SAME_OBJ(fb, scheme_true)) {
|
&& SAME_OBJ(fb, scheme_true)) {
|
||||||
|
@ -4572,9 +4614,25 @@ static Scheme_Object *optimize_branch(Scheme_Object *o, Optimize_Info *info, int
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try optimize: (if <omitable-expr> v v) => v */
|
/* Try optimize: (if <omitable-expr> v v) => v */
|
||||||
if (equivalent_exprs(tb, fb)) {
|
{
|
||||||
info->size -= 1; /* could be more precise */
|
Scheme_Object *nb;
|
||||||
return make_discarding_first_sequence(t, tb, info, 0);
|
|
||||||
|
nb = equivalent_exprs(tb, fb, then_info_init, else_info_init, context);
|
||||||
|
if (nb) {
|
||||||
|
info->size -= 1; /* could be more precise */
|
||||||
|
return make_discarding_first_sequence(t, nb, info, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try optimize: (if x x #f) => x
|
||||||
|
This pattern is included in the previous reduction,
|
||||||
|
but this is still useful if x is mutable */
|
||||||
|
if (SAME_TYPE(SCHEME_TYPE(t), scheme_local_type)
|
||||||
|
&& SAME_TYPE(SCHEME_TYPE(tb), scheme_local_type)
|
||||||
|
&& (SCHEME_LOCAL_POS(t) == SCHEME_LOCAL_POS(tb))
|
||||||
|
&& SCHEME_FALSEP(fb)) {
|
||||||
|
info->size -= 2;
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert: (if (if M N #f) M2 K) => (if M (if N M2 K) K)
|
/* Convert: (if (if M N #f) M2 K) => (if M (if N M2 K) K)
|
||||||
|
@ -5600,7 +5658,10 @@ static void update_rhs_value(Scheme_Compiled_Let_Value *naya, Scheme_Object *e,
|
||||||
Optimize_Info *info, Scheme_Object *tst)
|
Optimize_Info *info, Scheme_Object *tst)
|
||||||
{
|
{
|
||||||
if (tst) {
|
if (tst) {
|
||||||
if (!equivalent_exprs(naya->value, e)) {
|
Scheme_Object *n;
|
||||||
|
|
||||||
|
n = equivalent_exprs(naya->value, e, info, info, 0);
|
||||||
|
if (!n) {
|
||||||
Scheme_Branch_Rec *b;
|
Scheme_Branch_Rec *b;
|
||||||
|
|
||||||
/* In case `tst` was formerly a single-use variable, mark it as multi-use: */
|
/* In case `tst` was formerly a single-use variable, mark it as multi-use: */
|
||||||
|
@ -5613,7 +5674,8 @@ static void update_rhs_value(Scheme_Compiled_Let_Value *naya, Scheme_Object *e,
|
||||||
b->fbranch = e;
|
b->fbranch = e;
|
||||||
|
|
||||||
naya->value = (Scheme_Object *)b;
|
naya->value = (Scheme_Object *)b;
|
||||||
}
|
} else
|
||||||
|
naya->value = n;
|
||||||
} else
|
} else
|
||||||
naya->value = e;
|
naya->value = e;
|
||||||
}
|
}
|
||||||
|
@ -7955,27 +8017,9 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
|
||||||
|
|
||||||
delta = optimize_info_get_shift(info, pos);
|
delta = optimize_info_get_shift(info, pos);
|
||||||
|
|
||||||
if (!is_mutated) {
|
val = collapse_local(pos + delta, info, context);
|
||||||
Scheme_Object *pred;
|
if (val)
|
||||||
|
return val;
|
||||||
pred = optimize_get_predicate(pos + delta, info);
|
|
||||||
if (pred) {
|
|
||||||
if (SAME_OBJ(pred, scheme_not_prim))
|
|
||||||
return scheme_false;
|
|
||||||
|
|
||||||
if (context & OPT_CONTEXT_BOOLEAN) {
|
|
||||||
/* all other predicates recognize non-#f things */
|
|
||||||
return scheme_true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SAME_OBJ(pred, scheme_null_p_proc))
|
|
||||||
return scheme_null;
|
|
||||||
if (SAME_OBJ(pred, scheme_void_p_proc))
|
|
||||||
return scheme_void;
|
|
||||||
if (SAME_OBJ(pred, scheme_eof_object_p_proc))
|
|
||||||
return scheme_eof;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta)
|
if (delta)
|
||||||
expr = scheme_make_local(scheme_local_type, pos + delta, 0);
|
expr = scheme_make_local(scheme_local_type, pos + delta, 0);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user