Add analysis to eliminate dead resume point operands, bug 814997. r=dvander

This commit is contained in:
Brian Hackett 2012-11-30 15:59:52 -07:00
parent 93bf5aa58a
commit d7a47a58b2
8 changed files with 118 additions and 1 deletions

View File

@ -849,6 +849,15 @@ CompileBackEnd(MIRGenerator *mir)
if (mir->shouldCancel("Alias analysis"))
return NULL;
// Eliminating dead resume point operands requires basic block
// instructions to be numbered. Reuse the numbering computed during
// alias analysis.
if (!EliminateDeadResumePointOperands(mir, graph))
return NULL;
if (mir->shouldCancel("Eliminate dead resume point operands"))
return NULL;
}
if (js_IonOptions.edgeCaseAnalysis) {

View File

@ -40,6 +40,95 @@ ion::SplitCriticalEdges(MIRGraph &graph)
return true;
}
// Operands to a resume point which are dead at the point of the resume can be
// replaced with undefined values. This analysis supports limited detection of
// dead operands, pruning those which are defined in the resume point's basic
// block and have no uses outside the block or at points later than the resume
// point.
//
// This is intended to ensure that extra resume points within a basic block
// will not artificially extend the lifetimes of any SSA values. This could
// otherwise occur if the new resume point captured a value which is created
// between the old and new resume point and is dead at the new resume point.
bool
ion::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
{
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
if (mir->shouldCancel("Eliminate Dead Resume Point Operands (main loop)"))
return false;
// The logic below can get confused on infinite loops.
if (block->isLoopHeader() && block->backedge() == *block)
continue;
for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
// No benefit to replacing constant operands with other constants.
if (ins->isConstant())
continue;
// Scanning uses does not give us sufficient information to tell
// where instructions that are involved in box/unbox operations or
// parameter passing might be live. Rewriting uses of these terms
// in resume points may affect the interpreter's behavior. Rather
// than doing a more sophisticated analysis, just ignore these.
if (ins->isUnbox() || ins->isParameter())
continue;
// Check if this instruction's result is only used within the
// current block, and keep track of its last use in a definition
// (not resume point). This requires the instructions in the block
// to be numbered, ensured by running this immediately after alias
// analysis.
uint32 maxDefinition = 0;
for (MUseDefIterator uses(*ins); uses; uses++) {
if (uses.def()->block() != *block || uses.def()->isBox() || uses.def()->isPassArg()) {
maxDefinition = UINT32_MAX;
break;
}
maxDefinition = Max(maxDefinition, uses.def()->id());
}
if (maxDefinition == UINT32_MAX)
continue;
// Walk the uses a second time, removing any in resume points after
// the last use in a definition.
for (MUseIterator uses(ins->usesBegin()); uses != ins->usesEnd(); ) {
if (uses->node()->isDefinition()) {
uses++;
continue;
}
MResumePoint *mrp = uses->node()->toResumePoint();
if (mrp->block() != *block ||
!mrp->instruction() ||
mrp->instruction() == *ins ||
mrp->instruction()->id() <= maxDefinition)
{
uses++;
continue;
}
// Store an undefined value in place of all dead resume point
// operands. Making any such substitution can in general alter
// the interpreter's behavior, even though the code is dead, as
// the interpreter will still execute opcodes whose effects
// cannot be observed. If the undefined value were to flow to,
// say, a dead property access the interpreter could throw an
// exception; we avoid this problem by removing dead operands
// before removing dead code.
MConstant *constant = MConstant::New(UndefinedValue());
block->insertBefore(*(block->begin()), constant);
uses = mrp->replaceOperand(uses, constant);
}
MResumePoint *mrp = ins->resumePoint();
if (!mrp)
continue;
}
}
return true;
}
// Instructions are useless if they are unused and have no side effects.
// This pass eliminates useless instructions.
// The graph itself is unchanged.

View File

@ -25,6 +25,9 @@ SplitCriticalEdges(MIRGraph &graph);
bool
EliminatePhis(MIRGenerator *mir, MIRGraph &graph);
bool
EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);
bool
EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph);

View File

@ -4292,6 +4292,7 @@ IonBuilder::resume(MInstruction *ins, jsbytecode *pc, MResumePoint::Mode mode)
if (!resumePoint)
return false;
ins->setResumePoint(resumePoint);
resumePoint->setInstruction(ins);
return true;
}

View File

@ -1245,6 +1245,7 @@ MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *cal
stackDepth_(block->stackDepth()),
pc_(pc),
caller_(caller),
instruction_(NULL),
mode_(mode)
{
}

View File

@ -5639,6 +5639,7 @@ class MResumePoint : public MNode
uint32 stackDepth_;
jsbytecode *pc_;
MResumePoint *caller_;
MInstruction *instruction_;
Mode mode_;
MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode);
@ -5683,6 +5684,12 @@ class MResumePoint : public MNode
count++;
return count;
}
MInstruction *instruction() {
return instruction_;
}
void setInstruction(MInstruction *ins) {
instruction_ = ins;
}
Mode mode() const {
return mode_;
}

View File

@ -5608,6 +5608,13 @@ TypeScript::CheckBytecode(JSContext *cx, HandleScript script, jsbytecode *pc, co
if (IgnorePushed(pc, i))
continue;
/*
* Ignore undefined values, these may have been inserted by Ion to
* substitute for dead values.
*/
if (val.isUndefined())
continue;
Type type = GetValueType(cx, val);
if (!types->hasType(type)) {

View File

@ -3023,7 +3023,7 @@ BEGIN_CASE(JSOP_ENDINIT)
{
/* FIXME remove JSOP_ENDINIT bug 588522 */
JS_ASSERT(regs.stackDepth() >= 1);
JS_ASSERT(regs.sp[-1].isObject());
JS_ASSERT(regs.sp[-1].isObject() || regs.sp[-1].isUndefined());
}
END_CASE(JSOP_ENDINIT)