Bug 1062869 part 6 - Handle early execution of recover instructions. r=h4writer

This commit is contained in:
Nicolas B. Pierron 2014-09-18 18:28:56 +02:00
parent 153eeae0e1
commit 69ad0ca3a2
6 changed files with 96 additions and 7 deletions

View File

@ -444,6 +444,16 @@ class CompileInfo
// would have to be executed and that they cannot be removed even if they // would have to be executed and that they cannot be removed even if they
// are unused. // are unused.
bool isObservableSlot(uint32_t slot) const { bool isObservableSlot(uint32_t slot) const {
if (isObservableFrameSlot(slot))
return true;
if (isObservableArgumentSlot(slot))
return true;
return false;
}
bool isObservableFrameSlot(uint32_t slot) const {
if (!funMaybeLazy()) if (!funMaybeLazy())
return false; return false;
@ -458,6 +468,13 @@ class CompileInfo
if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot())) if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot()))
return true; return true;
return false;
}
bool isObservableArgumentSlot(uint32_t slot) const {
if (!funMaybeLazy())
return false;
// Function.arguments can be used to access all arguments in non-strict // Function.arguments can be used to access all arguments in non-strict
// scripts, so we can't optimize out any arguments. // scripts, so we can't optimize out any arguments.
if ((hasArguments() || !script()->strict()) && if ((hasArguments() || !script()->strict()) &&
@ -469,6 +486,19 @@ class CompileInfo
return false; return false;
} }
// Returns true if a slot can be recovered before or during a bailout. A
// definition which can be observed and recovered, implies that this
// definition can be optimized away as long as we can compute its values.
bool isRecoverableOperand(uint32_t slot) const {
if (isObservableFrameSlot(slot))
return false;
if (needsArgsObj() && isObservableArgumentSlot(slot))
return false;
return true;
}
private: private:
unsigned nimplicit_; unsigned nimplicit_;
unsigned nargs_; unsigned nargs_;

View File

@ -1773,6 +1773,49 @@ SnapshotIterator::skipInstruction()
nextInstruction(); nextInstruction();
} }
bool
SnapshotIterator::initInstructionResults(MaybeReadFallback &fallback)
{
MOZ_ASSERT(fallback.canRecoverResults());
JSContext *cx = fallback.maybeCx;
// If there is only one resume point in the list of instructions, then there
// is no instruction to recover, and thus no need to register any results.
if (recover_.numInstructions() == 1)
return true;
IonJSFrameLayout *fp = fallback.frame->jsFrame();
RInstructionResults *results = fallback.activation->maybeIonFrameRecovery(fp);
if (!results) {
// We do not have the result yet, which means that an observable stack
// slot is requested. As we do not want to bailout every time for the
// same reason, we need to recompile without optimizing away the
// observable stack slots. The script would later be recompiled to have
// support for Argument objects.
if (!ionScript_->invalidate(cx, /* resetUses = */ false, "Observe recovered instruction."))
return false;
// Start a new snapshot at the beginning of the JitFrameIterator. This
// SnapshotIterator is used for evaluating the content of all recover
// instructions. The result is then saved on the JitActivation.
SnapshotIterator s(*fallback.frame);
RInstructionResults tmp;
if (!s.initInstructionResults(cx, &tmp))
return false;
// Register the list of result on the activation.
if (!fallback.activation->registerIonFrameRecovery(fallback.frame->jsFrame(),
mozilla::Move(tmp)))
return false;
results = fallback.activation->maybeIonFrameRecovery(fp);
}
MOZ_ASSERT(results->isInitialized());
instructionResults_ = results;
return true;
}
bool bool
SnapshotIterator::initInstructionResults(JSContext *cx, RInstructionResults *results) SnapshotIterator::initInstructionResults(JSContext *cx, RInstructionResults *results)
{ {

View File

@ -426,6 +426,7 @@ class SnapshotIterator
// recover instructions. This vector should be registered before the // recover instructions. This vector should be registered before the
// beginning of the iteration. This function is in charge of allocating // beginning of the iteration. This function is in charge of allocating
// enough space for all instructions results, and return false iff it fails. // enough space for all instructions results, and return false iff it fails.
bool initInstructionResults(MaybeReadFallback &fallback);
bool initInstructionResults(JSContext *cx, RInstructionResults *results); bool initInstructionResults(JSContext *cx, RInstructionResults *results);
void storeInstructionResult(Value v); void storeInstructionResult(Value v);
@ -460,8 +461,16 @@ class SnapshotIterator
if (allocationReadable(a)) if (allocationReadable(a))
return allocationValue(a); return allocationValue(a);
if (fallback.canRecoverResults()) if (fallback.canRecoverResults()) {
warnUnreadableAllocation(); if (!initInstructionResults(fallback))
return fallback.unreadablePlaceholder;
if (allocationReadable(a))
return allocationValue(a);
MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
}
return fallback.unreadablePlaceholder; return fallback.unreadablePlaceholder;
} }

View File

@ -499,7 +499,7 @@ MDefinition::hasLiveDefUses() const
return true; return true;
} else { } else {
MOZ_ASSERT(ins->isResumePoint()); MOZ_ASSERT(ins->isResumePoint());
if (ins->toResumePoint()->isObservableOperand(*i)) if (!ins->toResumePoint()->isRecoverableOperand(*i))
return true; return true;
} }
} }
@ -2620,6 +2620,12 @@ MResumePoint::isObservableOperand(size_t index) const
return block()->info().isObservableSlot(index); return block()->info().isObservableSlot(index);
} }
bool
MResumePoint::isRecoverableOperand(MUse *u) const
{
return block()->info().isRecoverableOperand(indexOf(u));
}
MDefinition * MDefinition *
MToInt32::foldsTo(TempAllocator &alloc) MToInt32::foldsTo(TempAllocator &alloc)
{ {

View File

@ -10746,6 +10746,7 @@ class MResumePoint MOZ_FINAL :
bool isObservableOperand(MUse *u) const; bool isObservableOperand(MUse *u) const;
bool isObservableOperand(size_t index) const; bool isObservableOperand(size_t index) const;
bool isRecoverableOperand(MUse *u) const;
MDefinition *getOperand(size_t index) const { MDefinition *getOperand(size_t index) const {
return operands_[index].producer(); return operands_[index].producer();

View File

@ -146,8 +146,8 @@ IsObjectEscaped(MInstruction *ins)
MNode *consumer = (*i)->consumer(); MNode *consumer = (*i)->consumer();
if (!consumer->isDefinition()) { if (!consumer->isDefinition()) {
// Cannot optimize if it is observable from fun.arguments or others. // Cannot optimize if it is observable from fun.arguments or others.
if (consumer->toResumePoint()->isObservableOperand(*i)) { if (!consumer->toResumePoint()->isRecoverableOperand(*i)) {
JitSpewDef(JitSpew_Escape, "Object is observable\n", ins); JitSpewDef(JitSpew_Escape, "Observable object cannot be recovered\n", ins);
return true; return true;
} }
continue; continue;
@ -525,8 +525,8 @@ IsArrayEscaped(MInstruction *ins)
MNode *consumer = (*i)->consumer(); MNode *consumer = (*i)->consumer();
if (!consumer->isDefinition()) { if (!consumer->isDefinition()) {
// Cannot optimize if it is observable from fun.arguments or others. // Cannot optimize if it is observable from fun.arguments or others.
if (consumer->toResumePoint()->isObservableOperand(*i)) { if (!consumer->toResumePoint()->isRecoverableOperand(*i)) {
JitSpewDef(JitSpew_Escape, "Array is observable\n", ins); JitSpewDef(JitSpew_Escape, "Observable array cannot be recovered\n", ins);
return true; return true;
} }
continue; continue;