diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 662f8bfa4eb..53042cf26ef 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -863,6 +863,29 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock) loop->lastBlock = offset; + if (code->exceptionEntry) { + unsigned tryOffset; + JSTryNote *tn = script->trynotes()->vector; + JSTryNote *tnlimit = tn + script->trynotes()->length; + for (; tn < tnlimit; tn++) { + unsigned startOffset = script->main - script->code + tn->start; + if (startOffset + tn->length == offset) { + tryOffset = startOffset - 1; + break; + } + JS_NOT_REACHED("Start of try block not found"); + } + + /* + * Extend all live variables at exception entry to the start of + * the try block. + */ + for (unsigned i = 0; i < numSlots; i++) { + if (lifetimes[i].lifetime) + ensureVariable(lifetimes[i], tryOffset); + } + } + switch (op) { case JSOP_GETARG: case JSOP_CALLARG: @@ -906,7 +929,6 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) case JSOP_LOOKUPSWITCHX: case JSOP_TABLESWITCH: case JSOP_TABLESWITCHX: - case JSOP_TRY: /* Restore all saved variables. :FIXME: maybe do this precisely. */ for (unsigned i = 0; i < savedCount; i++) { LifetimeVariable &var = *saved[i]; @@ -922,6 +944,17 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) savedCount = 0; break; + case JSOP_TRY: + for (unsigned i = 0; i < numSlots; i++) { + LifetimeVariable &var = lifetimes[i]; + if (var.ensured) { + JS_ASSERT(var.lifetime); + if (var.lifetime->start == offset) + var.ensured = false; + } + } + break; + case JSOP_NEW: case JSOP_CALL: case JSOP_EVAL: @@ -1063,6 +1096,9 @@ ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offse LifetimeVariable **&saved, unsigned &savedCount) { if (var.lifetime) { + if (var.ensured) + return; + JS_ASSERT(offset < var.lifetime->start); var.lifetime->start = offset; } else { @@ -1102,6 +1138,10 @@ ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs var.savedEnd = 0; return; } + + if (var.ensured) + return; + JS_ASSERT(offset < var.lifetime->start); /* @@ -1124,6 +1164,16 @@ ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end) { JS_ASSERT(var.lifetime); + if (var.ensured) { + /* + * If we are still ensured to be live, the try block must scope over + * the loop, in which case the variable is already guaranteed to be + * live for the entire loop. + */ + JS_ASSERT(var.lifetime->start < start); + return; + } + var.lifetime->start = start; /* @@ -1193,6 +1243,15 @@ ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var, } } +inline void +ScriptAnalysis::ensureVariable(LifetimeVariable &var, unsigned until) +{ + JS_ASSERT(var.lifetime); + JS_ASSERT(until < var.lifetime->start); + var.lifetime->start = until; + var.ensured = true; +} + void ScriptAnalysis::clearAllocations() { diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 3bbf6234744..657859e4564 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -559,7 +559,10 @@ struct LifetimeVariable Lifetime *saved; /* Jump preceding the basic block which killed this variable. */ - uint32 savedEnd; + uint32 savedEnd : 31; + + /* If the variable needs to be kept alive until lifetime->start. */ + bool ensured : 1; /* Whether this variable is live at offset. */ Lifetime * live(uint32 offset) const { @@ -1168,6 +1171,7 @@ class ScriptAnalysis inline void killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset, LifetimeVariable **&saved, unsigned &savedCount); inline void extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end); + inline void ensureVariable(LifetimeVariable &var, unsigned until); /* SSA helpers */ bool makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv);