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();
|
@ -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);
|
||||
if (!tail)
|
||||
return false;
|
||||
tail->start = segment->end;
|
||||
tail->loopTail = true;
|
||||
segment->next = tail;
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
{}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user