optimizer: split optimize_info_lookup

With the old representation of local variables, optimize_info_lookup
had to search the stack for the frame with the information about the
variable. This was complicated so it has many flags to be used in
different situations and extract different kind of information.

With the new representation this process is easier, so it's possible
to split the function into a few smaller functions with an easier
control flow.

In particular, this is useful to avoid marking a variable as used
inside a lambda when the reference in immediately reduced to a
constant using the type information.
This commit is contained in:
Gustavo Massaccesi 2016-02-24 00:36:47 -03:00
parent 5ef3a53002
commit 828335a879
2 changed files with 145 additions and 117 deletions

View File

@ -1630,10 +1630,29 @@
'(lambda (a b) '(lambda (a b)
(not (if a b #t)))) (not (if a b #t))))
;ensure that variable p is not marked as used in the lambda
(test-comp '(let ([p (if (zero? (random 2)) 1 2)])
(list p p (lambda () (not p))))
'(let ([p (if (zero? (random 2)) 1 2)])
(list p p (lambda () #f))))
(test-comp '(let ([p (lambda () 0)])
(list p p (lambda () (not p))))
'(let ([p (lambda () 0)])
(list p p (lambda () #f))))
;this still doesn't work without the additional p
#;(test-comp '(let ([p (lambda () 0)])
(list p p (lambda () (procedure? p))))
'(let ([p (lambda () 0)])
(list p p (lambda () #t))))
(test-comp '(let ([p (lambda () 0)])
(list p p (lambda () (procedure? p))))
'(let ([p (lambda () 0)])
(list p p (lambda () p #t))))
(test-comp '(lambda (w) (if (void (list w)) 1 2)) (test-comp '(lambda (w) (if (void (list w)) 1 2))
'(lambda (w) 1)) '(lambda (w) 1))
; Diferent number of argumets use different codepaths ; Different number of arguments use different code paths
(test-comp '(lambda (f x) (void)) (test-comp '(lambda (f x) (void))
'(lambda (f x) (void (list)))) '(lambda (f x) (void (list))))
(test-comp '(lambda (f x) (begin (values (f x)) (void))) (test-comp '(lambda (f x) (begin (values (f x)) (void)))
@ -1811,6 +1830,19 @@
(test-comp '(lambda (x) (not (if (null? x) #t x))) (test-comp '(lambda (x) (not (if (null? x) #t x)))
'(lambda (x) (not x))) '(lambda (x) (not x)))
(test-comp '(lambda (x) (let ([n (list 1)])
(list n n (not (if x #t n)))))
'(lambda (x) (let ([n (list 1)])
(list n n #f))))
(test-comp '(lambda (x) (let ([n (if (zero? (random 2)) 1 -1)])
(list n n (not (if x #t n)))))
'(lambda (x) (let ([n (if (zero? (random 2)) 1 -1)])
(list n n #f))))
(test-comp '(lambda (x) (let ([n (if (zero? (random 2)) 1 -1)])
(list n n (not (if x #t n)))))
'(lambda (x) (let ([n (if (zero? (random 2)) 1 -1)])
(list n n #f))))
(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)))
(a1) (a1)

View File

@ -114,11 +114,11 @@ static int wants_local_type_arguments(Scheme_Object *rator, int argpos);
static void add_types_for_f_branch(Scheme_Object *t, Optimize_Info *info, int fuel); static void add_types_for_f_branch(Scheme_Object *t, Optimize_Info *info, int fuel);
static Scheme_Object *optimize_info_lookup(Optimize_Info *info, Scheme_Object *var, int closure_ok, int *single_use, static void register_use(Scheme_IR_Local *var, Optimize_Info *info);
int once_used_ok, int context, int *potential_size, int *_mutated); static Scheme_Object *optimize_info_lookup_lambda(Scheme_Object *var);
static Scheme_Object *optimize_info_propagate_local(Scheme_Object *var);
static void optimize_info_used_top(Optimize_Info *info); static void optimize_info_used_top(Optimize_Info *info);
static Scheme_Object *do_optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types); static Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types);
static Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var);
static void add_type(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pred); static void add_type(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pred);
static void merge_types(Optimize_Info *src_info, Optimize_Info *info, Scheme_Hash_Tree *skip_vars); static void merge_types(Optimize_Info *src_info, Optimize_Info *info, Scheme_Hash_Tree *skip_vars);
static Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *rand); static Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *rand);
@ -1923,6 +1923,31 @@ int scheme_check_leaf_rator(Scheme_Object *le, int *_flags)
return 0; return 0;
} }
int check_single_use(Scheme_Object *var)
{
Scheme_IR_Local *v = SCHEME_VAR(var);
return ((v->use_count == 1)
/* If we're outside the binding, then the binding
itself will remain as a used: */
&& !v->optimize_outside_binding
/* To help avoid infinite unrolling,
don't count a self use as "single" use. */
&& !v->optimize_unready);
}
int check_potential_size(Scheme_Object *var)
{
Scheme_Object* n;
n = SCHEME_VAR(var)->optimize.known_val;
if (n && SCHEME_BOXP(n)) {
return (int)SCHEME_INT_VAL(SCHEME_BOX_VAL(n));
}
return 0;
}
#if 0 #if 0
# define LOG_INLINE(x) x # define LOG_INLINE(x) x
#else #else
@ -1961,7 +1986,9 @@ Scheme_Object *optimize_for_inline(Optimize_Info *info, Scheme_Object *le, int a
if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_local_type)) { if (SAME_TYPE(SCHEME_TYPE(le), scheme_ir_local_type)) {
/* Check for inlining: */ /* Check for inlining: */
le = optimize_info_lookup(info, le, 1, &single_use, 0, 0, &psize, NULL); single_use = check_single_use(le);
psize = check_potential_size(le);
le = optimize_info_lookup_lambda(le);
already_opt = 1; already_opt = 1;
} }
@ -2198,7 +2225,7 @@ static void register_local_argument_types(Scheme_App_Rec *app, Scheme_App2_Rec *
if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_local_type)) { if (SAME_TYPE(SCHEME_TYPE(rator), scheme_ir_local_type)) {
{ {
le = optimize_info_lookup(info, rator, 1, NULL, 0, 0, NULL, NULL); le = optimize_info_lookup_lambda(rator);
if (le && SAME_TYPE(SCHEME_TYPE(le), scheme_ir_lambda_type)) { if (le && SAME_TYPE(SCHEME_TYPE(le), scheme_ir_lambda_type)) {
Scheme_Lambda *lam = (Scheme_Lambda *)le; Scheme_Lambda *lam = (Scheme_Lambda *)le;
char *map; char *map;
@ -2478,7 +2505,7 @@ static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info
Scheme_Object *p; Scheme_Object *p;
if (info) { if (info) {
p = optimize_get_predicate(info, expr); p = optimize_get_predicate(info, expr, 0);
if (p) if (p)
return p; return p;
} }
@ -2871,8 +2898,7 @@ static int appn_flags(Scheme_Object *rator, Optimize_Info *info)
return 0; return 0;
} }
if (SAME_TYPE(scheme_ir_lambda_type, SCHEME_TYPE(rator)) if (SCHEME_LAMBDAP(rator)
|| SAME_TYPE(scheme_case_lambda_sequence_type, SCHEME_TYPE(rator))
|| SAME_TYPE(scheme_noninline_proc_type, SCHEME_TYPE(rator))) || SAME_TYPE(scheme_noninline_proc_type, SCHEME_TYPE(rator)))
return APPN_FLAG_SFS_TAIL; return APPN_FLAG_SFS_TAIL;
@ -3076,10 +3102,10 @@ static Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *r
{ {
Scheme_Object *c = NULL; Scheme_Object *c = NULL;
if (SAME_TYPE(scheme_ir_lambda_type, SCHEME_TYPE(rand))) if (SCHEME_LAMBDAP(rand))
c = rand; c = rand;
else if (SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_local_type)) else if (SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_local_type))
c = optimize_info_lookup(info, rand, 1, NULL, 0, 0, NULL, NULL); c = optimize_info_lookup_lambda(rand);
else if (SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_toplevel_type)) { else if (SAME_TYPE(SCHEME_TYPE(rand), scheme_ir_toplevel_type)) {
if (info->top_level_consts) { if (info->top_level_consts) {
int pos; int pos;
@ -3100,8 +3126,7 @@ static Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *r
c = SCHEME_BOX_VAL(c); c = SCHEME_BOX_VAL(c);
} }
if (c && (SAME_TYPE(scheme_ir_lambda_type, SCHEME_TYPE(c)) if (c && (SCHEME_LAMBDAP(c)))
|| SAME_TYPE(scheme_case_lambda_sequence_type, SCHEME_TYPE(c))))
return c; return c;
return NULL; return NULL;
@ -4171,7 +4196,7 @@ static Scheme_Object *collapse_local(Scheme_Object *var, Optimize_Info *info, in
if (!SCHEME_VAR(var)->mutated) { if (!SCHEME_VAR(var)->mutated) {
Scheme_Object *pred; Scheme_Object *pred;
pred = optimize_get_predicate(info, var); pred = expr_implies_predicate(var, info, NULL, 5);
if (pred) { if (pred) {
if (SAME_OBJ(pred, scheme_not_proc)) if (SAME_OBJ(pred, scheme_not_proc))
return scheme_false; return scheme_false;
@ -4238,8 +4263,8 @@ static void add_type(Optimize_Info *info, Scheme_Object *var, Scheme_Object *pre
return; return;
/* Don't add the type if something is already there, this may happen when no_types. */ /* Don't add the type if something is already there, this may happen when no_types. */
if (do_optimize_get_predicate(info, var, 1) if (SCHEME_VAR(var)->val_type
|| SCHEME_VAR(var)->val_type) { || optimize_get_predicate(info, var, 1)) {
return; return;
} }
@ -4340,7 +4365,7 @@ static void add_types_for_t_branch(Scheme_Object *t, Optimize_Info *info, int fu
if (SAME_OBJ(app->rator, scheme_eq_proc)) { if (SAME_OBJ(app->rator, scheme_eq_proc)) {
if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_ir_local_type) if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_ir_local_type)
&& !SCHEME_VAR(app->rand1)->mutated) { && !SCHEME_VAR(app->rand1)->mutated) {
pred1 = optimize_get_predicate(info, app->rand1); pred1 = expr_implies_predicate(app->rand1, info, NULL, 5);
if (!pred1) { if (!pred1) {
pred2 = expr_implies_predicate(app->rand2, info, NULL, 5); pred2 = expr_implies_predicate(app->rand2, info, NULL, 5);
if (pred2) if (pred2)
@ -4349,7 +4374,7 @@ static void add_types_for_t_branch(Scheme_Object *t, Optimize_Info *info, int fu
} }
if (SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_ir_local_type) if (SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_ir_local_type)
&& !SCHEME_VAR(app->rand2)->mutated) { && !SCHEME_VAR(app->rand2)->mutated) {
pred2 = optimize_get_predicate(info, app->rand2); pred2 = expr_implies_predicate(app->rand2, info, NULL, 5);
if (!pred2) { if (!pred2) {
pred1 = expr_implies_predicate(app->rand1, info, NULL, 5); pred1 = expr_implies_predicate(app->rand1, info, NULL, 5);
if (pred1) if (pred1)
@ -4749,8 +4774,7 @@ set_optimize(Scheme_Object *data, Optimize_Info *info, int context)
info->single_result = 1; info->single_result = 1;
if (SAME_TYPE(SCHEME_TYPE(var), scheme_ir_local_type)) { if (SAME_TYPE(SCHEME_TYPE(var), scheme_ir_local_type)) {
/* Register that we use this variable: */ register_use(SCHEME_VAR(var), info);
optimize_info_lookup(info, var, 0, NULL, 0, 0, NULL, NULL);
} else { } else {
optimize_info_used_top(info); optimize_info_used_top(info);
} }
@ -5407,9 +5431,7 @@ int scheme_is_statically_proc(Scheme_Object *value, Optimize_Info *info)
/* Does `value` definitely produce a procedure of a specific shape? */ /* Does `value` definitely produce a procedure of a specific shape? */
{ {
while (1) { while (1) {
if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_lambda_type)) if (SCHEME_LAMBDAP(value)) {
return 1;
else if (SAME_TYPE(SCHEME_TYPE(value), scheme_case_lambda_sequence_type)) {
return 1; return 1;
} else if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_let_header_type)) { } else if (SAME_TYPE(SCHEME_TYPE(value), scheme_ir_let_header_type)) {
/* Look for (let ([x <omittable>]) <proc>), which is generated for optional arguments. */ /* Look for (let ([x <omittable>]) <proc>), which is generated for optional arguments. */
@ -7639,17 +7661,37 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
case scheme_ir_local_type: case scheme_ir_local_type:
{ {
Scheme_Object *val; Scheme_Object *val;
int is_mutated = 0;
info->size += 1; info->size += 1;
val = optimize_info_lookup(info, expr, 0, NULL, if (SCHEME_VAR(expr)->mutated) {
(context & OPT_CONTEXT_NO_SINGLE) ? 0 : 1, info->vclock += 1;
context, NULL, &is_mutated); register_use(SCHEME_VAR(expr), info);
return expr;
}
val = optimize_info_propagate_local(expr);
if (val) { if (val) {
if (SAME_TYPE(SCHEME_TYPE(val), scheme_once_used_type)) { info->size -= 1;
return scheme_optimize_expr(val, info, context);
}
val = collapse_local(expr, info, context);
if (val)
return val;
if (!(context & OPT_CONTEXT_NO_SINGLE)) {
val = SCHEME_VAR(expr)->optimize.known_val;
if (val && SAME_TYPE(SCHEME_TYPE(val), scheme_once_used_type)) {
Scheme_Once_Used *o = (Scheme_Once_Used *)val; Scheme_Once_Used *o = (Scheme_Once_Used *)val;
MZ_ASSERT(!o->moved);
MZ_ASSERT(!SCHEME_VAR(expr)->optimize_outside_binding);
/* In case this variable was tentatively used before: */
SCHEME_VAR(expr)->optimize_used = 0;
if (((o->vclock == info->vclock) if (((o->vclock == info->vclock)
&& ((o->aclock == info->aclock) && ((o->aclock == info->aclock)
|| !o->spans_k) || !o->spans_k)
@ -7692,27 +7734,15 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
info->sclock = save_sclock; info->sclock = save_sclock;
return val; return val;
} }
/* Can't move expression, so lookup again to mark as used
and to perform any copy propagation that might apply. */
val = optimize_info_lookup(info, expr, 0, NULL, 0, context, NULL, NULL);
if (val)
return val;
} else {
if (SAME_TYPE(SCHEME_TYPE(val), scheme_ir_toplevel_type)
|| (SCHEME_TYPE(val) > _scheme_ir_values_types_)) {
info->size -= 1;
return scheme_optimize_expr(val, info, context);
} }
return val;
}
} else if (is_mutated) {
info->vclock += 1;
} }
val = collapse_local(expr, info, context); /* If everything fails, mark it as used. */
if (val) if (OPT_CONTEXT_TYPE(context))
return val; SCHEME_VAR(expr)->arg_type = OPT_CONTEXT_TYPE(context);
if (info->kclock > SCHEME_VAR(expr)->optimize.init_kclock)
SCHEME_VAR(expr)->escapes_after_k_tick = 1;
register_use(SCHEME_VAR(expr), info);
return expr; return expr;
} }
case scheme_application_type: case scheme_application_type:
@ -8230,6 +8260,9 @@ static int optimize_any_uses(Optimize_Info *info, Scheme_IR_Let_Value *at_irlv,
static void register_use(Scheme_IR_Local *var, Optimize_Info *info) static void register_use(Scheme_IR_Local *var, Optimize_Info *info)
{ {
MZ_ASSERT(SCHEME_VAR(var)->mode == SCHEME_VAR_MODE_OPTIMIZE);
MZ_ASSERT(SCHEME_VAR(var)->use_count);
if (var->optimize.lambda_depth < info->lambda_depth) if (var->optimize.lambda_depth < info->lambda_depth)
scheme_hash_set(info->uses, (Scheme_Object *)var, scheme_true); scheme_hash_set(info->uses, (Scheme_Object *)var, scheme_true);
@ -8266,83 +8299,51 @@ static void register_transitive_uses(Scheme_IR_Local *var, Optimize_Info *info)
} }
} }
static Scheme_Object *optimize_info_lookup(Optimize_Info *info, Scheme_Object *var, int closure_ok, int *single_use, static Scheme_Object *optimize_info_lookup_lambda(Scheme_Object *var)
int once_used_ok, int context, int *potential_size, int *is_mutated)
{ {
Scheme_Object *n; Scheme_Object *n;
int kclock = info->kclock;
MZ_ASSERT(SCHEME_VAR(var)->mode == SCHEME_VAR_MODE_OPTIMIZE); MZ_ASSERT(SCHEME_VAR(var)->mode == SCHEME_VAR_MODE_OPTIMIZE);
MZ_ASSERT(SCHEME_VAR(var)->use_count); MZ_ASSERT(SCHEME_VAR(var)->use_count);
if (OPT_CONTEXT_TYPE(context))
SCHEME_VAR(var)->arg_type = OPT_CONTEXT_TYPE(context);
if (kclock > SCHEME_VAR(var)->optimize.init_kclock)
SCHEME_VAR(var)->escapes_after_k_tick = 1;
if (is_mutated && SCHEME_VAR(var)->mutated)
*is_mutated = 1;
if (single_use)
*single_use = ((SCHEME_VAR(var)->use_count == 1)
/* If we're outside the binding, then the binding
itself will remain as a used: */
&& !SCHEME_VAR(var)->optimize_outside_binding
/* To help avoid infinite unrolling,
don't count a self use as "single" use. */
&& !SCHEME_VAR(var)->optimize_unready);
n = SCHEME_VAR(var)->optimize.known_val; n = SCHEME_VAR(var)->optimize.known_val;
if (n) { if (n
if (SCHEME_BOXP(n)) { && (SCHEME_LAMBDAP(n)
/* A potential-size record: */ || SAME_TYPE(SCHEME_TYPE(n), scheme_ir_toplevel_type))) {
if (potential_size)
*potential_size = (int)SCHEME_INT_VAL(SCHEME_BOX_VAL(n));
} else {
if (SAME_TYPE(SCHEME_TYPE(n), scheme_ir_lambda_type)) {
if (context & OPT_CONTEXT_BOOLEAN) return scheme_true;
if (closure_ok) return n;
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_case_lambda_sequence_type)) {
if (context & OPT_CONTEXT_BOOLEAN) return scheme_true;
if (closure_ok) return n;
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_ir_toplevel_type)) {
return n;
} else if (closure_ok) {
/* Inlining can deal with procedures and top-levels, but not other things. */
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_once_used_type)) {
MZ_ASSERT(!((Scheme_Once_Used *)n)->moved);
MZ_ASSERT(!SCHEME_VAR(var)->optimize_outside_binding);
if (once_used_ok) {
/* In case this variable was tenatively used before: */
SCHEME_VAR(var)->optimize_used = 0;
return n; return n;
} }
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_ir_local_type)) {
Scheme_Object *v2;
int cnt = SCHEME_VAR(var)->use_count;
v2 = optimize_info_lookup(info, n, 0, single_use,
once_used_ok && (cnt == 1), context,
potential_size, NULL);
if (v2)
return v2;
else {
if (cnt != 1)
increment_use_count(SCHEME_VAR(n), 0);
return n;
}
} else
return n;
}
}
if (!closure_ok)
register_use(SCHEME_VAR(var), info);
return NULL; return NULL;
} }
Scheme_Object *do_optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types) static Scheme_Object *optimize_info_propagate_local(Scheme_Object *var)
{
Scheme_Object *last, *val = var;
while (val && SAME_TYPE(SCHEME_TYPE(val), scheme_ir_local_type)) {
MZ_ASSERT(SCHEME_VAR(val)->mode == SCHEME_VAR_MODE_OPTIMIZE);
MZ_ASSERT(SCHEME_VAR(val)->use_count);
last = val;
val = SCHEME_VAR(val)->optimize.known_val;
}
if (!val
|| SCHEME_BOXP(val) /* A potential-size record */
|| SCHEME_LAMBDAP(val)
|| SAME_TYPE(SCHEME_TYPE(val), scheme_once_used_type)) {
if (SAME_OBJ(last, var))
return NULL;
if (SCHEME_VAR(var)->use_count != 1)
increment_use_count(SCHEME_VAR(last), 0);
return last;
}
return val;
}
Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var, int ignore_no_types)
{ {
Scheme_Object *pred; Scheme_Object *pred;
@ -8360,11 +8361,6 @@ Scheme_Object *do_optimize_get_predicate(Optimize_Info *info, Scheme_Object *var
return NULL; return NULL;
} }
Scheme_Object *optimize_get_predicate(Optimize_Info *info, Scheme_Object *var)
{
return do_optimize_get_predicate(info, var, 0);
}
static Optimize_Info *optimize_info_add_frame(Optimize_Info *info, int orig, int current, int flags) static Optimize_Info *optimize_info_add_frame(Optimize_Info *info, int orig, int current, int flags)
{ {
Optimize_Info *naya; Optimize_Info *naya;