mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Add loop tail lifetimes when variables are killed in the middle of loops, bug 643805.
This commit is contained in:
parent
8f7dcba656
commit
a6c4770037
50
js/src/jit-test/tests/jaeger/bug643805.js
Normal file
50
js/src/jit-test/tests/jaeger/bug643805.js
Normal 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();
|
@ -1046,7 +1046,17 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
|
|||||||
for (unsigned i = 0; i < savedCount; i++) {
|
for (unsigned i = 0; i < savedCount; i++) {
|
||||||
LifetimeVariable &var = *saved[i];
|
LifetimeVariable &var = *saved[i];
|
||||||
JS_ASSERT(!var.lifetime && var.saved);
|
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,
|
* This jump precedes the basic block which killed the variable,
|
||||||
* remember it and use it for the end of the next lifetime
|
* 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;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@ -1078,7 +1077,7 @@ LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *scri
|
|||||||
/* Restore all saved variables. :FIXME: maybe do this precisely. */
|
/* Restore all saved variables. :FIXME: maybe do this precisely. */
|
||||||
for (unsigned i = 0; i < savedCount; i++) {
|
for (unsigned i = 0; i < savedCount; i++) {
|
||||||
LifetimeVariable &var = *saved[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)
|
if (!var.lifetime)
|
||||||
return false;
|
return false;
|
||||||
var.saved = NULL;
|
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)
|
if (!var.lifetime)
|
||||||
return false;
|
return false;
|
||||||
var.saved = NULL;
|
var.saved = NULL;
|
||||||
@ -1148,10 +1147,13 @@ LifetimeScript::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs
|
|||||||
{
|
{
|
||||||
if (!var.lifetime) {
|
if (!var.lifetime) {
|
||||||
/* Make a point lifetime indicating the write. */
|
/* 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)
|
if (!var.saved)
|
||||||
return false;
|
return false;
|
||||||
var.saved->write = true;
|
var.saved->write = true;
|
||||||
|
var.savedEnd = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
JS_ASSERT(offset < var.lifetime->start);
|
JS_ASSERT(offset < var.lifetime->start);
|
||||||
@ -1179,20 +1181,69 @@ LifetimeScript::extendVariable(JSContext *cx, LifetimeVariable &var, unsigned st
|
|||||||
JS_ASSERT(var.lifetime);
|
JS_ASSERT(var.lifetime);
|
||||||
var.lifetime->start = start;
|
var.lifetime->start = start;
|
||||||
|
|
||||||
Lifetime *segment = var.lifetime;
|
/*
|
||||||
if (segment->start >= end)
|
* When walking backwards through loop bodies, we don't know which vars
|
||||||
return true;
|
* are live at the loop's backedge. We save the endpoints for lifetime
|
||||||
while (segment->next && segment->next->start < end)
|
* segments which we *would* use if the variables were live at the backedge
|
||||||
segment = segment->next;
|
* and extend the variable with new lifetimes if we find the variable is
|
||||||
if (segment->end >= end)
|
* indeed live at the head of the loop.
|
||||||
return true;
|
*
|
||||||
|
* 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)
|
if (!tail)
|
||||||
return false;
|
return false;
|
||||||
tail->start = segment->end;
|
tail->start = segment->end;
|
||||||
tail->loopTail = true;
|
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->next = tail;
|
||||||
|
segment = tail->next;
|
||||||
|
} else {
|
||||||
|
JS_ASSERT(segment->savedEnd == 0);
|
||||||
|
segment = segment->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -386,9 +386,15 @@ struct Lifetime
|
|||||||
uint32 end;
|
uint32 end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an artificial segment extending the lifetime of a variable to
|
* In a loop body, endpoint to extend this lifetime with if the variable is
|
||||||
* the end of a loop, when it is live at the head of the loop. It will not
|
* live in the next iteration.
|
||||||
* be used anymore in the loop body until 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;
|
bool loopTail;
|
||||||
|
|
||||||
@ -401,8 +407,9 @@ struct Lifetime
|
|||||||
/* Next lifetime. The variable is dead from this->end to next->start. */
|
/* Next lifetime. The variable is dead from this->end to next->start. */
|
||||||
Lifetime *next;
|
Lifetime *next;
|
||||||
|
|
||||||
Lifetime(uint32 offset, Lifetime *next)
|
Lifetime(uint32 offset, uint32 savedEnd, Lifetime *next)
|
||||||
: start(offset), end(offset), loopTail(false), write(false), next(next)
|
: start(offset), end(offset), savedEnd(savedEnd),
|
||||||
|
loopTail(false), write(false), next(next)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user