Bug 1028331 - Use BailoutKind to report more detailed PJS bailout warnings. (r=lth)

This commit is contained in:
Shu-yu Guo 2014-06-27 00:41:20 -07:00
parent a1d6f81702
commit 0b680d2b4b
9 changed files with 168 additions and 113 deletions

View File

@ -1870,7 +1870,7 @@ GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
if (cache.canAttachStub()) {
bool alreadyStubbed;
if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
if (alreadyStubbed)
return true;
@ -1887,13 +1887,13 @@ GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
if (canCache == GetPropertyIC::CanAttachReadSlot) {
if (!cache.attachReadSlot(ncx, ion, obj, holder, shape))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
if (!cache.attachArrayLength(ncx, ion, obj))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
}
@ -1903,7 +1903,7 @@ GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
(cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
{
if (!cache.attachTypedArrayLength(ncx, ion, obj))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
}
@ -2869,7 +2869,7 @@ SetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject ob
if (cache.canAttachStub()) {
bool alreadyStubbed;
if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
if (alreadyStubbed) {
return baseops::SetPropertyHelper<ParallelExecution>(
cx, obj, obj, id, baseops::Qualified, &v, cache.strict());
@ -2889,7 +2889,7 @@ SetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject ob
if (canCache == SetPropertyIC::CanAttachSetSlot) {
if (!cache.attachSetSlot(ncx, ion, obj, shape, checkTypeset))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
}
@ -2912,7 +2912,7 @@ SetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject ob
{
LockedJSContext ncx(cx);
if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, obj, oldShape, checkTypeset))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
}
return true;
@ -3890,20 +3890,20 @@ SetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
if (cache.canAttachStub()) {
bool alreadyStubbed;
if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
if (alreadyStubbed)
return SetElementPar(cx, obj, idval, value, cache.strict());
bool attachedStub = false;
if (IsDenseElementSetInlineable(obj, idval)) {
if (!cache.attachDenseElement(ncx, ion, obj, idval))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(ncx, ion, tarr))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
}
}
}
@ -3976,7 +3976,7 @@ GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
if (cache.canAttachStub()) {
bool alreadyStubbed;
if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
if (alreadyStubbed)
return true;
@ -3998,7 +3998,7 @@ GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
if (canCache == GetPropertyIC::CanAttachReadSlot)
{
if (!cache.attachReadSlot(ncx, ion, obj, idval, name, holder, shape))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
}
@ -4006,7 +4006,7 @@ GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
GetElementIC::canAttachDenseElement(obj, idval))
{
if (!cache.attachDenseElement(ncx, ion, obj, idval))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
if (!attachedStub &&
@ -4014,7 +4014,7 @@ GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
{
RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(ncx, ion, tarr, idval))
return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}
}

View File

@ -761,7 +761,12 @@ HandleParallelFailure(ResumeFromException *rfe)
ForkJoinContext *cx = ForkJoinContext::current();
JitFrameIterator frameIter(cx);
cx->bailoutRecord->joinCause(ParallelBailoutUnsupportedVM);
// Advance to the first Ion frame so we can pull out the BailoutKind.
while (!frameIter.isIonJS())
++frameIter;
SnapshotIterator snapIter(frameIter);
cx->bailoutRecord->setIonBailoutKind(snapIter.bailoutKind());
cx->bailoutRecord->rematerializeFrames(cx, frameIter);
rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;

View File

@ -100,8 +100,12 @@ enum BailoutKind
Bailout_NonStringInput,
Bailout_NonSymbolInput,
// PJS bailout when writing to a non-thread local object.
Bailout_GuardThreadExclusive,
// PJS bailout when encountering MIR unsafe for parallel execution.
Bailout_ParallelUnsafe,
// For the initial snapshot when entering a function.
Bailout_InitialState,

View File

@ -531,7 +531,9 @@ jit::BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer)
JitActivationIterator jitActivations(cx->perThreadData);
IonBailoutIterator frameIter(jitActivations, sp);
cx->bailoutRecord->joinCause(ParallelBailoutUnsupported);
SnapshotIterator snapIter(frameIter);
cx->bailoutRecord->setIonBailoutKind(snapIter.bailoutKind());
cx->bailoutRecord->rematerializeFrames(cx, frameIter);
MOZ_ASSERT(frameIter.done());

View File

@ -439,7 +439,7 @@ ParallelSafetyVisitor::convertToBailout(MInstructionIterator &iter)
block->discardAllInstructionsStartingAt(iter);
// End the block in a bail.
MBail *bail = MBail::New(graph_.alloc());
MBail *bail = MBail::New(graph_.alloc(), Bailout_ParallelUnsafe);
TransplantResumePoint(ins, bail);
block->add(bail);
block->end(MUnreachable::New(alloc()));
@ -755,7 +755,7 @@ ParallelSafetyVisitor::visitThrow(MThrow *thr)
{
MBasicBlock *block = thr->block();
JS_ASSERT(block->lastIns() == thr);
MBail *bail = MBail::New(alloc());
MBail *bail = MBail::New(alloc(), Bailout_ParallelUnsafe);
TransplantResumePoint(thr, bail);
block->discardLastIns();
block->end(MUnreachable::New(alloc()));

View File

@ -770,7 +770,7 @@ js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
// Error in strict mode code or warn with strict option.
unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
if (cx->isForkJoinContext())
return cx->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, flags);
return cx->asForkJoinContext()->reportError(flags);
if (!cx->isJSContext())
return true;

View File

@ -4905,7 +4905,7 @@ bool
JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
{
if (cxArg->isForkJoinContext())
return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
return cxArg->asForkJoinContext()->reportError(report);
if (!cxArg->isJSContext())
return true;
@ -4921,7 +4921,7 @@ bool
JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
{
if (cxArg->isForkJoinContext())
return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
return cxArg->asForkJoinContext()->reportError(report);
if (!cxArg->isJSContext())
return true;
@ -4937,7 +4937,7 @@ bool
JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
{
if (cxArg->isForkJoinContext())
return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
return cxArg->asForkJoinContext()->reportError(report);
if (!cxArg->isJSContext())
return true;

View File

@ -318,8 +318,6 @@ class ForkJoinOperation
TrafficLight recoverFromBailout(ExecutionStatus *status);
TrafficLight fatalError(ExecutionStatus *status);
bool isInitialScript(HandleScript script);
void getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
ParallelBailoutCause *bailoutCause);
bool reportBailoutWarnings();
bool invalidateBailedOutScripts();
ExecutionStatus sequentialExecution(bool disqualified);
@ -615,8 +613,8 @@ ForkJoinOperation::apply()
// - Re-enqueue main script and any uncompiled scripts that were called
// - Too many bailouts: Fallback to sequential
JS_ASSERT_IF(!jit::IsBaselineEnabled(cx_), !jit::IsIonEnabled(cx_));
if (!jit::IsBaselineEnabled(cx_) || !jit::IsIonEnabled(cx_))
JS_ASSERT_IF(!IsBaselineEnabled(cx_), !IsIonEnabled(cx_));
if (!IsBaselineEnabled(cx_) || !IsIonEnabled(cx_))
return sequentialExecution(true);
SpewBeginOp(cx_, "ForkJoinOperation");
@ -796,7 +794,7 @@ ForkJoinOperation::compileForParallelExecution(ExecutionStatus *status)
if (!script->hasParallelIonScript()) {
// Script has not yet been compiled. Attempt to compile it.
SpewBeginCompile(script);
MethodStatus mstatus = jit::CanEnterInParallel(cx_, script);
MethodStatus mstatus = CanEnterInParallel(cx_, script);
SpewEndCompile(mstatus);
switch (mstatus) {
@ -1042,41 +1040,100 @@ BailoutExplanation(ParallelBailoutCause cause)
{
switch (cause) {
case ParallelBailoutNone:
return "no particular reason";
return "no bailout";
case ParallelBailoutInterrupt:
return "interrupted";
case ParallelBailoutExecution:
return "";
case ParallelBailoutCompilationSkipped:
return "compilation failed (method skipped)";
case ParallelBailoutCompilationFailure:
return "compilation failed";
case ParallelBailoutInterrupt:
return "interrupted";
case ParallelBailoutFailedIC:
return "failed to attach stub to IC";
case ParallelBailoutHeapBusy:
return "heap busy flag set during interrupt";
case ParallelBailoutMainScriptNotPresent:
return "main script not present";
case ParallelBailoutCalledToUncompiledScript:
return "called to uncompiled script";
case ParallelBailoutIllegalWrite:
return "illegal write";
case ParallelBailoutAccessToIntrinsic:
return "access to intrinsic";
return "main script JIT code was collected";
case ParallelBailoutOverRecursed:
return "over recursed";
return "stack limit exceeded";
case ParallelBailoutOutOfMemory:
return "out of memory";
case ParallelBailoutUnsupported:
return "unsupported";
case ParallelBailoutUnsupportedVM:
return "unsupported operation in VM call";
case ParallelBailoutUnsupportedStringComparison:
return "unsupported string comparison";
case ParallelBailoutRequestedGC:
return "requested GC";
return "requested GC of common heap";
case ParallelBailoutRequestedZoneGC:
return "requested zone GC";
return "requested zone GC of common heap";
default:
return "no known reason";
MOZ_ASSUME_UNREACHABLE("Invalid ParallelBailoutCause");
}
}
static const char *
IonBailoutKindExplanation(ParallelBailoutCause cause, BailoutKind kind)
{
if (cause != ParallelBailoutExecution)
return "";
switch (kind) {
// Normal bailouts.
case Bailout_Inevitable:
return "inevitable";
case Bailout_DuringVMCall:
return "on vm reentry";
case Bailout_NonJSFunctionCallee:
return "non-scripted callee";
case Bailout_DynamicNameNotFound:
return "dynamic name not found";
case Bailout_StringArgumentsEval:
return "string contains 'arguments' or 'eval'";
case Bailout_Overflow:
case Bailout_OverflowInvalidate:
return "integer overflow";
case Bailout_Round:
return "unhandled input to rounding function";
case Bailout_NonPrimitiveInput:
return "trying to convert non-primitive input to number or string";
case Bailout_PrecisionLoss:
return "precision loss when converting to int32";
case Bailout_TypeBarrierO:
return "tripped type barrier: unexpected object";
case Bailout_TypeBarrierV:
return "tripped type barrier: unexpected value";
case Bailout_MonitorTypes:
return "wrote value of unexpected type to property";
case Bailout_Hole:
return "saw unexpected array hole";
case Bailout_NegativeIndex:
return "negative array index";
case Bailout_ObjectIdentityOrTypeGuard:
return "saw unexpected object type barrier";
case Bailout_NonInt32Input:
return "can't unbox: expected int32";
case Bailout_NonNumericInput:
return "can't unbox: expected number";
case Bailout_NonBooleanInput:
return "can't unbox: expected boolean";
case Bailout_NonObjectInput:
return "can't unbox: expected object";
case Bailout_NonStringInput:
case Bailout_NonStringInputInvalidate:
return "can't unbox: expected string";
case Bailout_GuardThreadExclusive:
return "tried to write to non-thread local value";
case Bailout_ParallelUnsafe:
return "unsafe";
case Bailout_InitialState:
return "during function prologue";
case Bailout_DoubleOutput:
return "integer arithmetic overflowed to double";
case Bailout_ArgumentCheck:
return "unexpected argument type";
case Bailout_BoundsCheck:
return "out of bounds element access";
case Bailout_Neutered:
return "neutered typed object access";
case Bailout_ShapeGuard:
return "saw unexpected shape";
case Bailout_IonExceptionDebugMode:
// Fallthrough. This case cannot occur in parallel execution.
default:
MOZ_ASSUME_UNREACHABLE("Invalid BailoutKind");
}
}
@ -1086,29 +1143,6 @@ ForkJoinOperation::isInitialScript(HandleScript script)
return fun_->is<JSFunction>() && (fun_->as<JSFunction>().nonLazyScript() == script);
}
void
ForkJoinOperation::getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
ParallelBailoutCause *bailoutCause)
{
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
switch (bailoutRecords_[i].cause) {
case ParallelBailoutNone:
case ParallelBailoutInterrupt:
continue;
default:
break;
}
if (bailoutRecords_[i].hasFrames()) {
RematerializedFrame *frame = bailoutRecords_[i].frames()[0];
bailoutScript.set(frame->script());
*bailoutPc = frame->pc();
*bailoutCause = bailoutRecords_[i].cause;
return;
}
}
}
static const char *
ValueToChar(JSContext *cx, HandleValue val, JSAutoByteString &bytes)
{
@ -1135,24 +1169,30 @@ ForkJoinOperation::reportBailoutWarnings()
}
for (uint32_t threadId = 0; threadId < bailoutRecords_.length(); threadId++) {
ParallelBailoutCause cause = bailoutRecords_[threadId].cause;
ParallelBailoutRecord &record = bailoutRecords_[threadId];
ParallelBailoutCause cause = record.cause;
BailoutKind ionBailoutKind = record.ionBailoutKind;
if (cause == ParallelBailoutNone)
continue;
if (bailoutRecords_[threadId].hasFrames()) {
Vector<RematerializedFrame *> &frames = bailoutRecords_[threadId].frames();
if (record.hasFrames()) {
Vector<RematerializedFrame *> &frames = record.frames();
if (!SpewEnabled(SpewBailouts)) {
RematerializedFrame *frame = frames[0];
RootedScript bailoutScript(cx_, frame->script());
SpewBailout(bailouts, bailoutScript, frame->pc(), cause);
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%u",
BailoutExplanation(cause), bailoutScript->filename(),
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s%s at %s:%u",
BailoutExplanation(cause),
IonBailoutKindExplanation(cause, ionBailoutKind),
bailoutScript->filename(),
PCToLineNumber(bailoutScript, frame->pc()));
return true;
}
sp.printf("\n in thread %d due to %s", threadId, BailoutExplanation(cause));
sp.printf("\n in thread %u: %s%s",
threadId, BailoutExplanation(cause),
IonBailoutKindExplanation(cause, ionBailoutKind));
for (uint32_t frameIndex = 0; frameIndex < frames.length(); frameIndex++) {
RematerializedFrame *frame = frames[frameIndex];
@ -1237,10 +1277,6 @@ ForkJoinOperation::invalidateBailedOutScripts()
case ParallelBailoutInterrupt:
continue;
// An illegal write will not be made legal by invalidation.
case ParallelBailoutIllegalWrite:
continue;
// For other cases, consider invalidation.
default:
break;
@ -2354,10 +2390,10 @@ js::InExclusiveParallelSection()
bool
js::ParallelTestsShouldPass(JSContext *cx)
{
return jit::IsIonEnabled(cx) &&
jit::IsBaselineEnabled(cx) &&
!jit::js_JitOptions.eagerCompilation &&
jit::js_JitOptions.baselineUsesBeforeCompile != 0 &&
return IsIonEnabled(cx) &&
IsBaselineEnabled(cx) &&
!js_JitOptions.eagerCompilation &&
js_JitOptions.baselineUsesBeforeCompile != 0 &&
cx->runtime()->gcZeal() == 0;
}

View File

@ -17,6 +17,7 @@
#include "gc/GCInternals.h"
#include "jit/Ion.h"
#include "jit/IonTypes.h"
#ifdef DEBUG
#define FORKJOIN_SPEW
@ -281,44 +282,39 @@ bool ForkJoin(JSContext *cx, CallArgs &args);
// { everything else }
// |
// Interrupt
// / |
// Unsupported UnsupportedVM
// \ /
// |
// Execution
// |
// None
//
enum ParallelBailoutCause {
ParallelBailoutNone = 0,
ParallelBailoutUnsupported,
ParallelBailoutUnsupportedVM,
// Bailed out of JIT code during execution. The specific reason is found
// in the ionBailoutKind field in ParallelBailoutRecord below.
ParallelBailoutExecution,
// The periodic interrupt failed, which can mean that either
// another thread canceled, the user interrupted us, etc
// another thread canceled, the user interrupted us, etc.
ParallelBailoutInterrupt,
// Compiler returned Method_Skipped
// Compiler returned Method_Skipped.
ParallelBailoutCompilationSkipped,
// Compiler returned Method_CantCompile
// Compiler returned Method_CantCompile.
ParallelBailoutCompilationFailure,
// Propagating a failure, i.e., another thread requested the computation
// be aborted.
ParallelBailoutPropagate,
// An IC update failed
ParallelBailoutFailedIC,
// Heap busy flag was set during interrupt
ParallelBailoutHeapBusy,
// The main script was GCed before we could start executing.
ParallelBailoutMainScriptNotPresent,
ParallelBailoutCalledToUncompiledScript,
ParallelBailoutIllegalWrite,
ParallelBailoutAccessToIntrinsic,
// Went over the stack limit.
ParallelBailoutOverRecursed,
// True memory exhaustion. See js_ReportOutOfMemory.
ParallelBailoutOutOfMemory,
ParallelBailoutUnsupportedStringComparison,
// GC was requested on the tenured heap, which we cannot comply with in
// parallel.
ParallelBailoutRequestedGC,
ParallelBailoutRequestedZoneGC
};
@ -336,11 +332,18 @@ struct ParallelBailoutRecord
// Captured Ion frames at the point of bailout. Stored younger-to-older,
// i.e., the 0th frame is the youngest frame.
Vector<jit::RematerializedFrame *> *frames_;
// The reason for unsuccessful parallel execution.
ParallelBailoutCause cause;
// The more specific bailout reason if cause above is
// ParallelBailoutExecution.
jit::BailoutKind ionBailoutKind;
ParallelBailoutRecord()
: frames_(nullptr),
cause(ParallelBailoutNone)
cause(ParallelBailoutNone),
ionBailoutKind(jit::Bailout_Inevitable)
{ }
~ParallelBailoutRecord();
@ -360,6 +363,11 @@ struct ParallelBailoutRecord
}
}
void setIonBailoutKind(jit::BailoutKind kind) {
joinCause(ParallelBailoutExecution);
ionBailoutKind = kind;
}
void rematerializeFrames(ForkJoinContext *cx, jit::JitFrameIterator &frameIter);
void rematerializeFrames(ForkJoinContext *cx, jit::IonBailoutIterator &frameIter);
};
@ -427,9 +435,9 @@ class ForkJoinContext : public ThreadSafeContext
// Reports an unsupported operation, returning false if we are reporting
// an error. Otherwise drop the warning on the floor.
bool reportError(ParallelBailoutCause cause, unsigned report) {
bool reportError(unsigned report) {
if (report & JSREPORT_ERROR)
return setPendingAbortFatal(cause);
return setPendingAbortFatal(ParallelBailoutExecution);
return true;
}