зеркало из https://github.com/mozilla/gecko-dev.git
Add analysis to eliminate dead resume point operands, bug 814997. r=dvander
This commit is contained in:
Родитель
cc51633aa3
Коммит
1d8b30b54d
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1245,6 +1245,7 @@ MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *cal
|
|||
stackDepth_(block->stackDepth()),
|
||||
pc_(pc),
|
||||
caller_(caller),
|
||||
instruction_(NULL),
|
||||
mode_(mode)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче