[INFER] Add loop tail lifetimes when variables are killed in the middle of loops, bug 643805.

This commit is contained in:
Brian Hackett 2011-04-19 06:39:49 -07:00
parent 8f7dcba656
commit a6c4770037
3 changed files with 142 additions and 34 deletions

View File

@ -0,0 +1,50 @@
function _tt_face_get_name() {
var __label__ = -1;
var $rec;
var $n;
while(true) {
switch(__label__) {
case -1:
$rec=0;
$n=0;
case 0:
if ($rec == 20) {
__label__ = 2;
break;
}
var $63 = $n;
var $64 = $63 + 1;
$n = $64;
var $65 = $rec;
$rec = $rec + 1;
assertEq($64 < 30, true);
__label__ = 0;
break;
case 1:
$rec = 0;
case 2:
return;
}
}
}
_tt_face_get_name();
/* Test tracking of lifetimes around backedges in nested loops. */
function nested() {
var x = 100;
var i = 0;
while (i < 10) {
while (i < 10) {
i++;
if (x < 20)
break;
if (i > 10) {
x = 200;
i++;
}
}
if (i > 10)
x = 100;
}
}
nested();

View File

@ -1022,7 +1022,7 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
/*
* Find the entry jump, which will be a GOTO for 'for' or
* 'while'loops or a fallthrough for 'do while' loops.
* 'while' loops or a fallthrough for 'do while' loops.
*/
uint32 entry = targetOffset;
if (entry) {
@ -1046,7 +1046,17 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
for (unsigned i = 0; i < savedCount; i++) {
LifetimeVariable &var = *saved[i];
JS_ASSERT(!var.lifetime && var.saved);
if (!var.savedEnd) {
if (var.live(targetOffset)) {
/*
* Jumping to a place where this variable is live. Make a new
* lifetime segment for the variable.
*/
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
if (!var.lifetime)
return false;
var.saved = NULL;
saved[i--] = saved[--savedCount];
} else if (loop && !var.savedEnd) {
/*
* This jump precedes the basic block which killed the variable,
* remember it and use it for the end of the next lifetime
@ -1056,17 +1066,6 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
*/
var.savedEnd = offset;
}
if (var.live(targetOffset)) {
/*
* Jumping to a place where this variable is live. Make a new
* lifetime segment for the variable.
*/
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.saved);
if (!var.lifetime)
return false;
var.saved = NULL;
saved[i--] = saved[--savedCount];
}
}
break;
}
@ -1078,7 +1077,7 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
/* Restore all saved variables. :FIXME: maybe do this precisely. */
for (unsigned i = 0; i < savedCount; i++) {
LifetimeVariable &var = *saved[i];
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.saved);
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
if (!var.lifetime)
return false;
var.saved = NULL;
@ -1135,7 +1134,7 @@ LifetimeScript::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offse
}
}
}
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.saved);
var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
if (!var.lifetime)
return false;
var.saved = NULL;
@ -1148,10 +1147,13 @@ LifetimeScript::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs
{
if (!var.lifetime) {
/* Make a point lifetime indicating the write. */
var.saved = ArenaNew<Lifetime>(pool, offset, var.saved);
if (!var.saved)
saved[savedCount++] = &var;
var.saved = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
if (!var.saved)
return false;
var.saved->write = true;
var.savedEnd = 0;
return true;
}
JS_ASSERT(offset < var.lifetime->start);
@ -1179,20 +1181,69 @@ LifetimeScript::extendVariable(JSContext *cx, LifetimeVariable &var, unsigned st
JS_ASSERT(var.lifetime);
var.lifetime->start = start;
Lifetime *segment = var.lifetime;
if (segment->start >= end)
return true;
while (segment->next && segment->next->start < end)
segment = segment->next;
if (segment->end >= end)
return true;
/*
* When walking backwards through loop bodies, we don't know which vars
* are live at the loop's backedge. We save the endpoints for lifetime
* segments which we *would* use if the variables were live at the backedge
* and extend the variable with new lifetimes if we find the variable is
* indeed live at the head of the loop.
*
* while (...) {
* if (x #1) { ... }
* ...
* if (... #2) { x = 0; #3}
* }
*
* If x is not live after the loop, we treat it as dead in the walk and
* make a point lifetime for the write at #3. At the beginning of that
* basic block (#2), we save the loop endpoint; if we knew x was live in
* the next iteration then a new lifetime would be made here. At #1 we
* mark x live again, make a segment between the head of the loop and #1,
* and then extend x with loop tail lifetimes from #1 to #2, and from #3
* to the back edge.
*/
Lifetime *tail = ArenaNew<Lifetime>(pool, end, segment->next);
Lifetime *segment = var.lifetime;
while (segment && segment->start < end) {
uint32 savedEnd = segment->savedEnd;
if (!segment->next || segment->next->start >= end) {
/*
* savedEnd is only set for variables killed in the middle of the
* loop. Make a tail segment connecting the last use with the
* back edge.
*/
if (segment->end >= end) {
/* Variable known to be live after the loop finishes. */
break;
}
savedEnd = end;
}
JS_ASSERT(savedEnd <= end);
if (savedEnd > segment->end) {
Lifetime *tail = ArenaNew<Lifetime>(pool, savedEnd, 0, segment->next);
if (!tail)
return false;
tail->start = segment->end;
tail->loopTail = true;
/*
* Clear the segment's saved end, but preserve in the tail if this
* is the last segment in the loop and the variable is killed in an
* outer loop before the backedge.
*/
if (segment->savedEnd > end) {
JS_ASSERT(savedEnd == end);
tail->savedEnd = segment->savedEnd;
}
segment->savedEnd = 0;
segment->next = tail;
segment = tail->next;
} else {
JS_ASSERT(segment->savedEnd == 0);
segment = segment->next;
}
}
return true;
}

View File

@ -386,9 +386,15 @@ struct Lifetime
uint32 end;
/*
* This is an artificial segment extending the lifetime of a variable to
* the end of a loop, when it is live at the head of the loop. It will not
* be used anymore in the loop body until the next iteration.
* In a loop body, endpoint to extend this lifetime with if the variable is
* live in the next iteration.
*/
uint32 savedEnd;
/*
* This is an artificial segment extending the lifetime of this variable
* when it is live at the head of the loop. It will not be used until the
* next iteration.
*/
bool loopTail;
@ -401,8 +407,9 @@ struct Lifetime
/* Next lifetime. The variable is dead from this->end to next->start. */
Lifetime *next;
Lifetime(uint32 offset, Lifetime *next)
: start(offset), end(offset), loopTail(false), write(false), next(next)
Lifetime(uint32 offset, uint32 savedEnd, Lifetime *next)
: start(offset), end(offset), savedEnd(savedEnd),
loopTail(false), write(false), next(next)
{}
};