fix two safe-for-space compiler bugs

svn: r8664
This commit is contained in:
Matthew Flatt 2008-02-14 21:04:37 +00:00
parent b87d9a986e
commit d3ce4799e5
3 changed files with 56 additions and 18 deletions

View File

@ -3044,9 +3044,12 @@ static void garbage_collect(int force_full)
/* determine if this should be a full collection or not */
gc_full = force_full || !generations_available
|| (since_last_full > 100) || (memory_in_use > (2 * last_full_mem_use));
/* printf("Collection #li (full = %i): %i / %i / %i / %i\n", number, */
/* gc_full, force_full, !generations_available, */
/* (since_last_full > 100), (memory_in_use > (2 * last_full_mem_use))); */
#if 0
printf("Collection %li (full = %i): %i / %i / %i / %i %ld\n", number,
gc_full, force_full, !generations_available,
(since_last_full > 100), (memory_in_use > (2 * last_full_mem_use)),
last_full_mem_use);
#endif
next_gc_full = gc_full;

View File

@ -80,9 +80,16 @@
Bytecodes are not linear. They're actually trees of expression
nodes.
Top-level variables (global or module) are referenced through the
Scheme stack, so that the variables can be "re-linked" each time a
module is instantiated. Syntax constants are similarly accessed
through the Scheme stack. The global variables and syntax objects
are sometimes called the "prefix", and scheme_push_prefix()
initializes the prefix portion of the stack.
Compilation:
Compilation works in three passes.
Compilation works in four passes.
The first pass, called "compile", performs most of the work and
tracks variable usage (including whether a variable is mutated or
@ -101,12 +108,10 @@
due to sharing (potentially cyclic) of closures that are "empty"
but actually refer to other "empty" closures.
Top-level variables (global or module) are referenced through the
Scheme stack, so that the variables can be "re-linked" each time a
module is instantiated. Syntax constants are similarly accessed
through the Scheme stack. The global variables and syntax objects
are sometimes called the "prefix", and scheme_push_prefix()
initializes the prefix portion of the stack.
The fourth pass, "sfs", performs another liveness analysis on stack
slows and inserts operations to clear stack slots as necessary to
make execution safe for space. In particular, dead slots need to be
cleared before a non-tail call into arbitrary Scheme code.
Just-in-time compilation:
@ -3574,7 +3579,7 @@ static Scheme_Object *sfs_one_branch(SFS_Info *info, int ip,
n = SCHEME_INT_VAL(o);
SFS_LOG(printf(" @%d %d\n", i + t_min_t, n));
if (info->max_used[i + t_min_t] < n) {
SFS_LOG(printf(" |%d %d\n", i + t_min_t, n));
SFS_LOG(printf(" |%d %d %d\n", i + t_min_t, n, info->max_nontail));
info->max_used[i + t_min_t] = n;
info->max_calls[i + t_min_t] = info->max_nontail;
}
@ -3587,10 +3592,12 @@ static Scheme_Object *sfs_one_branch(SFS_Info *info, int ip,
Note that it doesn't matter whether the other branch actually
clears them (i.e., the relevant non-tail call might be only
in this branch). */
o = SCHEME_VEC_ELS(vec)[((1 - delta) * SFS_BRANCH_W) + 3];
o = SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 3];
b_end = SCHEME_INT_VAL(o);
SFS_LOG(printf(" %d %d %d %d\n", nt, ip, b_end, save_nt));
if (((nt > (ip + 1)) && (nt < b_end)) /* => non-tail call in branch */
|| ((ip + 1) < save_nt)) { /* => non-tail call after branches */
SFS_LOG(printf(" other\n"));
o = SCHEME_VEC_ELS(vec)[(1 - delta) * SFS_BRANCH_W];
t_min_t = SCHEME_INT_VAL(o);
if (t_min_t > -1) {
@ -3608,10 +3615,10 @@ static Scheme_Object *sfs_one_branch(SFS_Info *info, int ip,
pos = i + t_min_t;
at_ip = info->max_used[pos];
SFS_LOG(printf(" ?%d %d %d\n", pos, n, at_ip));
/* is last use in other branch? */
if (((!delta && (at_ip == ip))
|| (delta && (at_ip == n)))
&& (at_ip < info->max_calls[pos])) {
/* Add clear */
|| (delta && (at_ip == n)))) {
/* Yes, so add clear */
SFS_LOG(printf(" !%d %d %d\n", pos, n, at_ip));
pos -= info->stackpos;
clears = scheme_make_pair(scheme_make_integer(pos),
@ -3669,6 +3676,9 @@ static Scheme_Object *sfs_one_branch(SFS_Info *info, int ip,
SCHEME_VEC_ELS(vec)[(delta * SFS_BRANCH_W) + 3] = scheme_make_integer(info->ip);
}
memset(info->max_used + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
memset(info->max_calls + info->stackpos, 0, (stackpos - info->stackpos) * sizeof(int));
info->stackpos = stackpos;
return tbranch;
@ -3784,11 +3794,12 @@ static Scheme_Object *sfs_let_one(Scheme_Object *o, SFS_Info *info)
{
Scheme_Let_One *lo = (Scheme_Let_One *)o;
Scheme_Object *body, *rhs, *vec;
int pos, save_mnt;
int pos, save_mnt, ip, et;
scheme_sfs_start_sequence(info, 2, 1);
scheme_sfs_push(info, 1, 1);
ip = info->ip;
pos = info->stackpos;
save_mnt = info->max_nontail;
@ -3822,11 +3833,35 @@ static Scheme_Object *sfs_let_one(Scheme_Object *o, SFS_Info *info)
SCHEME_VEC_ELS(vec)[2] = scheme_make_integer(info->max_nontail);
} else {
info->max_nontail = save_mnt;
if (info->max_used[pos] <= ip) {
/* No one is using it, so either don't push the real value, or clear it.
The optimizer normally would have converted away the binding, but
it might not because (1) it was introduced late by inlining,
or (2) the rhs expression doesn't always produce a single
value. */
if (scheme_omittable_expr(rhs, 1, -1, 1)) {
rhs = scheme_false;
} else {
Scheme_Object *clr;
Scheme_Sequence *s;
s = malloc_sequence(2);
s->so.type = scheme_sequence_type;
s->count = 2;
clr = scheme_make_local(scheme_local_type, 0, SCHEME_LOCAL_CLEAR_ON_READ);
s->array[0] = clr;
s->array[1] = body;
body = (Scheme_Object *)s;
}
}
}
lo->value = rhs;
lo->body = body;
et = scheme_get_eval_type(lo->value);
SCHEME_LET_EVAL_TYPE(lo) = et;
return o;
}

View File

@ -1097,9 +1097,9 @@ Scheme_Object *scheme_sfs_closure(Scheme_Object *expr, SFS_Info *info, int self_
}
}
}
code = scheme_sfs(data->code, info, data->max_let_depth);
/* If any arguments go unused, and if there's a non-tail,
non-immediate call in the body, then we flush the
unused arguments at the start of the body. We assume that