fix interaction between copy prop and once-used binding elimination
in the bytecode compiler, which could cause an expression to be duplicated
This commit is contained in:
parent
f04a92d009
commit
962ceb6b63
|
@ -831,6 +831,57 @@
|
||||||
(- x 8))
|
(- x 8))
|
||||||
'(- (+ (cons 1 2) 0) 8))
|
'(- (+ (cons 1 2) 0) 8))
|
||||||
|
|
||||||
|
(test-comp '(let ([x (peek-char)])
|
||||||
|
(cons x 10))
|
||||||
|
'(cons (peek-char) 10))
|
||||||
|
|
||||||
|
(test-comp '(let ([x (peek-char)])
|
||||||
|
(let ([y x])
|
||||||
|
(cons y 10)))
|
||||||
|
'(cons (peek-char) 10))
|
||||||
|
|
||||||
|
(test-comp '(lambda (x)
|
||||||
|
(let ([y x])
|
||||||
|
(cons y 10)))
|
||||||
|
'(lambda (x) (cons x 10)))
|
||||||
|
|
||||||
|
(test-comp '(lambda (x)
|
||||||
|
(let ([y x])
|
||||||
|
(cons y y)))
|
||||||
|
'(lambda (x) (cons x x)))
|
||||||
|
|
||||||
|
(test-comp '(let ([f (lambda (x)
|
||||||
|
(let ([y x])
|
||||||
|
(cons y y)))])
|
||||||
|
(f (peek-char)))
|
||||||
|
'(let ([y (peek-char)])
|
||||||
|
(cons y y)))
|
||||||
|
|
||||||
|
(test-comp '(let ([g (lambda (f)
|
||||||
|
;; Try to get uses of `z' replaced by `x',
|
||||||
|
;; but before `x' and `y' are split apart.
|
||||||
|
;; Single-use tracking of `x' can go wrong.
|
||||||
|
(let-values ([(x y) (f (cons 1 2)
|
||||||
|
(cons 3 4))])
|
||||||
|
(let ([z x])
|
||||||
|
(list z z y))))])
|
||||||
|
(g values))
|
||||||
|
'(let ([x (cons 1 2)]
|
||||||
|
[y (cons 3 4)])
|
||||||
|
(list x x y)))
|
||||||
|
|
||||||
|
(test-comp '(let ([g (lambda (f)
|
||||||
|
(letrec-values ([(x y) (f (cons 1 2)
|
||||||
|
(cons 3 4))])
|
||||||
|
(let ([z x])
|
||||||
|
(list z z y))))])
|
||||||
|
(g values))
|
||||||
|
'(let ([g (lambda (f)
|
||||||
|
(letrec-values ([(x y) (f (cons 1 2)
|
||||||
|
(cons 3 4))])
|
||||||
|
(list x x y)))])
|
||||||
|
(g values)))
|
||||||
|
|
||||||
(test-comp '(let-values ([(x y) (values 1 2)])
|
(test-comp '(let-values ([(x y) (values 1 2)])
|
||||||
(+ x y))
|
(+ x y))
|
||||||
3)
|
3)
|
||||||
|
|
|
@ -3648,7 +3648,8 @@ int scheme_optimize_any_uses(Optimize_Info *info, int start_pos, int end_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Scheme_Object *do_optimize_info_lookup(Optimize_Info *info, int pos, int j, int *closure_offset, int *single_use,
|
static Scheme_Object *do_optimize_info_lookup(Optimize_Info *info, int pos, int j, int *closure_offset, int *single_use,
|
||||||
int *not_ready, int once_used_ok, int context, int *potential_size)
|
int *not_ready, int once_used_ok, int context, int *potential_size,
|
||||||
|
int disrupt_single_use)
|
||||||
{
|
{
|
||||||
Scheme_Object *p, *n;
|
Scheme_Object *p, *n;
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
|
@ -3701,10 +3702,17 @@ static Scheme_Object *do_optimize_info_lookup(Optimize_Info *info, int pos, int
|
||||||
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_once_used_type)) {
|
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_once_used_type)) {
|
||||||
Scheme_Once_Used *o;
|
Scheme_Once_Used *o;
|
||||||
|
|
||||||
|
if (disrupt_single_use) {
|
||||||
|
((Scheme_Once_Used *)n)->expr = NULL;
|
||||||
|
((Scheme_Once_Used *)n)->vclock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!once_used_ok)
|
if (!once_used_ok)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
o = (Scheme_Once_Used *)n;
|
o = (Scheme_Once_Used *)n;
|
||||||
|
if (!o->expr) break; /* disrupted or not available */
|
||||||
|
|
||||||
o->delta = delta;
|
o->delta = delta;
|
||||||
o->info = info;
|
o->info = info;
|
||||||
return (Scheme_Object *)o;
|
return (Scheme_Object *)o;
|
||||||
|
@ -3726,13 +3734,23 @@ static Scheme_Object *do_optimize_info_lookup(Optimize_Info *info, int pos, int
|
||||||
single_use = NULL;
|
single_use = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = do_optimize_info_lookup(info, pos, j, NULL, single_use, NULL, 0, context, potential_size);
|
/* If the referenced variable is not single-use, then
|
||||||
|
the variable it is replaced by is no longer single-use */
|
||||||
|
disrupt_single_use = !SCHEME_TRUEP(SCHEME_VEC_ELS(p)[3]);
|
||||||
|
|
||||||
|
n = do_optimize_info_lookup(info, pos, j, NULL, single_use, NULL,
|
||||||
|
once_used_ok && !disrupt_single_use, context,
|
||||||
|
potential_size, disrupt_single_use);
|
||||||
|
|
||||||
if (!n) {
|
if (!n) {
|
||||||
/* Return shifted reference to other local: */
|
/* Return shifted reference to other local: */
|
||||||
delta += scheme_optimize_info_get_shift(info, pos);
|
delta += scheme_optimize_info_get_shift(info, pos);
|
||||||
n = scheme_make_local(scheme_local_type, pos + delta, 0);
|
n = scheme_make_local(scheme_local_type, pos + delta, 0);
|
||||||
}
|
} else if (SAME_TYPE(SCHEME_TYPE(n), scheme_once_used_type)) {
|
||||||
|
/* Need to adjust delta: */
|
||||||
|
delta = scheme_optimize_info_get_shift(info, pos);
|
||||||
|
((Scheme_Once_Used *)n)->delta += delta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -3748,14 +3766,14 @@ static Scheme_Object *do_optimize_info_lookup(Optimize_Info *info, int pos, int
|
||||||
Scheme_Object *scheme_optimize_info_lookup(Optimize_Info *info, int pos, int *closure_offset, int *single_use,
|
Scheme_Object *scheme_optimize_info_lookup(Optimize_Info *info, int pos, int *closure_offset, int *single_use,
|
||||||
int once_used_ok, int context, int *potential_size)
|
int once_used_ok, int context, int *potential_size)
|
||||||
{
|
{
|
||||||
return do_optimize_info_lookup(info, pos, 0, closure_offset, single_use, NULL, once_used_ok, context, potential_size);
|
return do_optimize_info_lookup(info, pos, 0, closure_offset, single_use, NULL, once_used_ok, context, potential_size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int scheme_optimize_info_is_ready(Optimize_Info *info, int pos)
|
int scheme_optimize_info_is_ready(Optimize_Info *info, int pos)
|
||||||
{
|
{
|
||||||
int closure_offset, single_use, ready = 1;
|
int closure_offset, single_use, ready = 1;
|
||||||
|
|
||||||
do_optimize_info_lookup(info, pos, 0, &closure_offset, &single_use, &ready, 0, 0, NULL);
|
do_optimize_info_lookup(info, pos, 0, &closure_offset, &single_use, &ready, 0, 0, NULL, 0);
|
||||||
|
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4359,8 +4359,11 @@ Scheme_Object *scheme_optimize_expr(Scheme_Object *expr, Optimize_Info *info, in
|
||||||
return scheme_optimize_expr(val, info, context);
|
return scheme_optimize_expr(val, info, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Can't move expression, so lookup again to mark as used. */
|
/* Can't move expression, so lookup again to mark as used
|
||||||
(void)scheme_optimize_info_lookup(info, pos, NULL, NULL, 0, context, NULL);
|
and to perform any copy propagation that might apply. */
|
||||||
|
val = scheme_optimize_info_lookup(info, pos, NULL, NULL, 0, context, NULL);
|
||||||
|
if (val)
|
||||||
|
return val;
|
||||||
} else {
|
} else {
|
||||||
if (SAME_TYPE(SCHEME_TYPE(val), scheme_compiled_toplevel_type)) {
|
if (SAME_TYPE(SCHEME_TYPE(val), scheme_compiled_toplevel_type)) {
|
||||||
info->size -= 1;
|
info->size -= 1;
|
||||||
|
|
|
@ -1025,7 +1025,8 @@ scheme_optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, i
|
||||||
Scheme_Object *code, *ctx;
|
Scheme_Object *code, *ctx;
|
||||||
Closure_Info *cl;
|
Closure_Info *cl;
|
||||||
mzshort dcs, *dcm;
|
mzshort dcs, *dcm;
|
||||||
int i;
|
int i, cnt;
|
||||||
|
Scheme_Once_Used *first_once_used = NULL, *last_once_used = NULL;
|
||||||
|
|
||||||
data = (Scheme_Closure_Data *)_data;
|
data = (Scheme_Closure_Data *)_data;
|
||||||
|
|
||||||
|
@ -1051,6 +1052,13 @@ scheme_optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, i
|
||||||
for (i = 0; i < data->num_params; i++) {
|
for (i = 0; i < data->num_params; i++) {
|
||||||
if (cl->local_flags[i] & SCHEME_WAS_SET_BANGED)
|
if (cl->local_flags[i] & SCHEME_WAS_SET_BANGED)
|
||||||
scheme_optimize_mutated(info, i);
|
scheme_optimize_mutated(info, i);
|
||||||
|
|
||||||
|
cnt = ((cl->local_flags[i] & SCHEME_USE_COUNT_MASK) >> SCHEME_USE_COUNT_SHIFT);
|
||||||
|
if (cnt == 1) {
|
||||||
|
last_once_used = scheme_make_once_used(NULL, i, info->vclock, last_once_used);
|
||||||
|
if (!first_once_used) first_once_used = last_once_used;
|
||||||
|
scheme_optimize_propagate(info, i, (Scheme_Object *)last_once_used, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code = scheme_optimize_expr(data->code, info, 0);
|
code = scheme_optimize_expr(data->code, info, 0);
|
||||||
|
@ -1060,6 +1068,14 @@ scheme_optimize_closure_compilation(Scheme_Object *_data, Optimize_Info *info, i
|
||||||
cl->local_flags[i] |= SCHEME_WAS_FLONUM_ARGUMENT;
|
cl->local_flags[i] |= SCHEME_WAS_FLONUM_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (first_once_used) {
|
||||||
|
if (first_once_used->vclock < 0) {
|
||||||
|
/* no longer used once, due to binding propagation */
|
||||||
|
cl->local_flags[first_once_used->pos] |= SCHEME_USE_COUNT_MASK;
|
||||||
|
}
|
||||||
|
first_once_used = first_once_used->next;
|
||||||
|
}
|
||||||
|
|
||||||
if (info->single_result)
|
if (info->single_result)
|
||||||
SCHEME_CLOSURE_DATA_FLAGS(data) |= CLOS_SINGLE_RESULT;
|
SCHEME_CLOSURE_DATA_FLAGS(data) |= CLOS_SINGLE_RESULT;
|
||||||
else if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_SINGLE_RESULT)
|
else if (SCHEME_CLOSURE_DATA_FLAGS(data) & CLOS_SINGLE_RESULT)
|
||||||
|
|
|
@ -3091,9 +3091,9 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
Scheme_Let_Header *head = (Scheme_Let_Header *)form;
|
Scheme_Let_Header *head = (Scheme_Let_Header *)form;
|
||||||
Scheme_Compiled_Let_Value *clv, *pre_body, *retry_start, *prev_body;
|
Scheme_Compiled_Let_Value *clv, *pre_body, *retry_start, *prev_body;
|
||||||
Scheme_Object *body, *value, *ready_pairs = NULL, *rp_last = NULL, *ready_pairs_start;
|
Scheme_Object *body, *value, *ready_pairs = NULL, *rp_last = NULL, *ready_pairs_start;
|
||||||
Scheme_Once_Used *first_once_used = NULL, *last_once_used = NULL;
|
Scheme_Once_Used *first_once_used = NULL, *last_once_used = NULL, *once_used;
|
||||||
int i, j, pos, is_rec, not_simply_let_star = 0, undiscourage, split_shift, skip_opts = 0;
|
int i, j, pos, is_rec, not_simply_let_star = 0, undiscourage, split_shift, skip_opts = 0;
|
||||||
int size_before_opt, did_set_value;
|
int size_before_opt, did_set_value, checked_once;
|
||||||
int remove_last_one = 0, inline_fuel, rev_bind_order;
|
int remove_last_one = 0, inline_fuel, rev_bind_order;
|
||||||
int post_bind = !(SCHEME_LET_FLAGS(head) & (SCHEME_LET_RECURSIVE | SCHEME_LET_STAR));
|
int post_bind = !(SCHEME_LET_FLAGS(head) & (SCHEME_LET_RECURSIVE | SCHEME_LET_STAR));
|
||||||
|
|
||||||
|
@ -3460,6 +3460,7 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
pre_body = naya;
|
pre_body = naya;
|
||||||
body = (Scheme_Object *)naya;
|
body = (Scheme_Object *)naya;
|
||||||
value = pre_body->value;
|
value = pre_body->value;
|
||||||
|
pos = pre_body->position;
|
||||||
} else {
|
} else {
|
||||||
/* We've dropped this clause entirely. */
|
/* We've dropped this clause entirely. */
|
||||||
i++;
|
i++;
|
||||||
|
@ -3472,6 +3473,8 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checked_once = 0;
|
||||||
|
|
||||||
if ((pre_body->count == 1)
|
if ((pre_body->count == 1)
|
||||||
&& !(pre_body->flags[0] & SCHEME_WAS_SET_BANGED)) {
|
&& !(pre_body->flags[0] & SCHEME_WAS_SET_BANGED)) {
|
||||||
int indirect = 0, indirect_binding = 0;
|
int indirect = 0, indirect_binding = 0;
|
||||||
|
@ -3536,6 +3539,7 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
|
|
||||||
scheme_optimize_propagate(body_info, pos, value, cnt == 1);
|
scheme_optimize_propagate(body_info, pos, value, cnt == 1);
|
||||||
did_set_value = 1;
|
did_set_value = 1;
|
||||||
|
checked_once = 1;
|
||||||
} else if (value && !is_rec) {
|
} else if (value && !is_rec) {
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
|
@ -3543,13 +3547,38 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
scheme_optimize_produces_flonum(body_info, pos);
|
scheme_optimize_produces_flonum(body_info, pos);
|
||||||
|
|
||||||
if (!indirect) {
|
if (!indirect) {
|
||||||
|
checked_once = 1;
|
||||||
cnt = ((pre_body->flags[0] & SCHEME_USE_COUNT_MASK) >> SCHEME_USE_COUNT_SHIFT);
|
cnt = ((pre_body->flags[0] & SCHEME_USE_COUNT_MASK) >> SCHEME_USE_COUNT_SHIFT);
|
||||||
if (cnt == 1) {
|
if (cnt == 1) {
|
||||||
/* used only once; we may be able to shift the expression to the use
|
/* used only once; we may be able to shift the expression to the use
|
||||||
site, instead of binding to a temporary */
|
site, instead of binding to a temporary */
|
||||||
last_once_used = scheme_make_once_used(value, pos, rhs_info->vclock, last_once_used);
|
once_used = scheme_make_once_used(value, pos, rhs_info->vclock, NULL);
|
||||||
if (!first_once_used) first_once_used = last_once_used;
|
if (!last_once_used)
|
||||||
scheme_optimize_propagate(body_info, pos, (Scheme_Object *)last_once_used, 1);
|
first_once_used = once_used;
|
||||||
|
else
|
||||||
|
last_once_used->next = once_used;
|
||||||
|
last_once_used = once_used;
|
||||||
|
scheme_optimize_propagate(body_info, pos, (Scheme_Object *)once_used, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checked_once) {
|
||||||
|
/* Didn't handle once-used check in case of copy propagation, so check here. */
|
||||||
|
int i, cnt;
|
||||||
|
for (i = pre_body->count; i--; ) {
|
||||||
|
if (!(pre_body->flags[i] & SCHEME_WAS_SET_BANGED)) {
|
||||||
|
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 = scheme_make_once_used(NULL, pos+i, rhs_info->vclock, NULL);
|
||||||
|
if (!last_once_used)
|
||||||
|
first_once_used = once_used;
|
||||||
|
else
|
||||||
|
last_once_used->next = once_used;
|
||||||
|
last_once_used = once_used;
|
||||||
|
scheme_optimize_propagate(body_info, pos+i, (Scheme_Object *)once_used, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3729,25 +3758,26 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
pre_body = (Scheme_Compiled_Let_Value *)body;
|
pre_body = (Scheme_Compiled_Let_Value *)body;
|
||||||
pos = pre_body->position;
|
pos = pre_body->position;
|
||||||
|
|
||||||
while (first_once_used && pos_EARLIER(first_once_used->pos, pos)) {
|
|
||||||
first_once_used = first_once_used->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = pre_body->count; j--; ) {
|
for (j = pre_body->count; j--; ) {
|
||||||
if (scheme_optimize_is_used(body_info, pos+j)) {
|
if (scheme_optimize_is_used(body_info, pos+j)) {
|
||||||
used = 1;
|
used = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!used
|
if (!used
|
||||||
&& (scheme_omittable_expr(pre_body->value, pre_body->count, -1, 0, info, -1)
|
&& (scheme_omittable_expr(pre_body->value, pre_body->count, -1, 0, info, -1)
|
||||||
|| (first_once_used
|
|| ((pre_body->count == 1)
|
||||||
|
&& first_once_used
|
||||||
&& (first_once_used->pos == pos)
|
&& (first_once_used->pos == pos)
|
||||||
&& first_once_used->used))) {
|
&& first_once_used->used))) {
|
||||||
for (j = pre_body->count; j--; ) {
|
for (j = pre_body->count; j--; ) {
|
||||||
if (pre_body->flags[j] & SCHEME_WAS_USED) {
|
if (pre_body->flags[j] & SCHEME_WAS_USED) {
|
||||||
pre_body->flags[j] -= SCHEME_WAS_USED;
|
pre_body->flags[j] -= SCHEME_WAS_USED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (first_once_used && (first_once_used->pos == (pos + j)))
|
||||||
|
first_once_used = first_once_used->next;
|
||||||
}
|
}
|
||||||
if (pre_body->count == 1) {
|
if (pre_body->count == 1) {
|
||||||
/* Drop expr and deduct from size to aid further inlining. */
|
/* Drop expr and deduct from size to aid further inlining. */
|
||||||
|
@ -3761,6 +3791,14 @@ scheme_optimize_lets(Scheme_Object *form, Optimize_Info *info, int for_inline, i
|
||||||
pre_body->flags[j] |= SCHEME_WAS_USED;
|
pre_body->flags[j] |= SCHEME_WAS_USED;
|
||||||
if (scheme_optimize_is_flonum_arg(body_info, pos+j, 0))
|
if (scheme_optimize_is_flonum_arg(body_info, pos+j, 0))
|
||||||
pre_body->flags[j] |= SCHEME_WAS_FLONUM_ARGUMENT;
|
pre_body->flags[j] |= SCHEME_WAS_FLONUM_ARGUMENT;
|
||||||
|
|
||||||
|
if (first_once_used && (first_once_used->pos == (pos+j))) {
|
||||||
|
if (first_once_used->vclock < 0) {
|
||||||
|
/* single-use no longer true, due to copy propagation */
|
||||||
|
pre_body->flags[j] |= SCHEME_USE_COUNT_MASK;
|
||||||
|
}
|
||||||
|
first_once_used = first_once_used->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
info->size += 1;
|
info->size += 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user