mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 14:55:47 +00:00
Bug 1019304 - Part 2: Overhaul PJS bailout mechanism to be like the normal bailout mechanism. (r=nmatsakis)
- Remove ad-hoc IR tracing and AbortPar machinery. - PJS bailouts use a different handler that unwinds the entire ForkJoin stack, but otherwise shares the same code as sequential bailouts. - Each thread's stack is snapshotted as a RematerializedFrame vector.
This commit is contained in:
parent
7dcb64a3c7
commit
0999921fcf
@ -69,10 +69,6 @@ IonBailoutIterator::dump() const
|
||||
}
|
||||
}
|
||||
|
||||
// This address is a magic number made to cause crashes while indicating that we
|
||||
// are making an attempt to mark the stack during a bailout.
|
||||
static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t *>(0xba1);
|
||||
|
||||
uint32_t
|
||||
jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
|
||||
{
|
||||
|
@ -100,6 +100,10 @@ static const uint32_t BAILOUT_RETURN_OK = 0;
|
||||
static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1;
|
||||
static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2;
|
||||
|
||||
// This address is a magic number made to cause crashes while indicating that we
|
||||
// are making an attempt to mark the stack during a bailout.
|
||||
static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t *>(0xba1);
|
||||
|
||||
class JitCompartment;
|
||||
|
||||
// BailoutStack is an architecture specific pointer to the stack, given by the
|
||||
|
@ -1229,7 +1229,10 @@ class OutOfLineInterruptCheckImplicit : public OutOfLineCodeBase<CodeGenerator>
|
||||
};
|
||||
|
||||
typedef bool (*InterruptCheckFn)(JSContext *);
|
||||
static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
|
||||
typedef bool (*InterruptCheckParFn)(ForkJoinContext *);
|
||||
static const VMFunctionsModal InterruptCheckInfo = VMFunctionsModal(
|
||||
FunctionInfo<InterruptCheckFn>(InterruptCheck),
|
||||
FunctionInfo<InterruptCheckParFn>(InterruptCheckPar));
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
|
||||
@ -1849,13 +1852,7 @@ CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
|
||||
masm.passABIArg(ToRegister(lir->object()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
|
||||
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// branch to the OOL failure code if false is returned
|
||||
masm.branchIfFalseBool(ReturnReg, bail->entry());
|
||||
return true;
|
||||
return bailoutIfFalseBool(ReturnReg, lir->snapshot());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2383,27 +2380,23 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
|
||||
masm.bind(¬Primitive);
|
||||
}
|
||||
|
||||
if (!checkForAbortPar(call))
|
||||
return false;
|
||||
|
||||
dropArguments(call->numStackArgs() + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CallToUncompiledScriptParFn)(ForkJoinContext *, JSObject *);
|
||||
static const VMFunction CallToUncompiledScriptParInfo =
|
||||
FunctionInfo<CallToUncompiledScriptParFn>(CallToUncompiledScriptPar);
|
||||
|
||||
// Generates a call to CallToUncompiledScriptPar() and then bails out.
|
||||
// |calleeReg| should contain the JSFunction*.
|
||||
bool
|
||||
CodeGenerator::emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg)
|
||||
{
|
||||
OutOfLineCode *bail = oolAbortPar(ParallelBailoutCalledToUncompiledScript, lir);
|
||||
if (!bail)
|
||||
pushArg(calleeReg);
|
||||
if (!callVM(CallToUncompiledScriptParInfo, lir))
|
||||
return false;
|
||||
|
||||
masm.movePtr(calleeReg, CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CallToUncompiledScriptPar));
|
||||
masm.jump(bail->entry());
|
||||
masm.assumeUnreachable("CallToUncompiledScriptParInfo always returns false.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2478,9 +2471,6 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
||||
|
||||
masm.bind(&end);
|
||||
|
||||
if (!checkForAbortPar(call))
|
||||
return false;
|
||||
|
||||
// If the return value of the constructing function is Primitive,
|
||||
// replace the return value with the Object from CreateThis.
|
||||
if (call->mir()->isConstructing()) {
|
||||
@ -2494,22 +2484,6 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::checkForAbortPar(LInstruction *lir)
|
||||
{
|
||||
// In parallel mode, if we call another ion-compiled function and
|
||||
// it returns JS_ION_ERROR, that indicates a bailout that we have
|
||||
// to propagate up the stack.
|
||||
ExecutionMode executionMode = gen->info().executionMode();
|
||||
if (executionMode == ParallelExecution) {
|
||||
OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail->entry());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
|
||||
{
|
||||
@ -2922,10 +2896,10 @@ CodeGenerator::generateArgumentsChecks(bool bailout)
|
||||
// Out-of-line path to report over-recursed error and fail.
|
||||
class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LCheckOverRecursed *lir_;
|
||||
LInstruction *lir_;
|
||||
|
||||
public:
|
||||
explicit CheckOverRecursedFailure(LCheckOverRecursed *lir)
|
||||
explicit CheckOverRecursedFailure(LInstruction *lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
@ -2933,7 +2907,7 @@ class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
|
||||
return codegen->visitCheckOverRecursedFailure(this);
|
||||
}
|
||||
|
||||
LCheckOverRecursed *lir() const {
|
||||
LInstruction *lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
@ -3003,9 +2977,11 @@ CodeGenerator::visitDefFun(LDefFun *lir)
|
||||
return callVM(DefFunOperationInfo, lir);
|
||||
}
|
||||
|
||||
typedef bool (*ReportOverRecursedFn)(JSContext *);
|
||||
static const VMFunction CheckOverRecursedInfo =
|
||||
FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
|
||||
typedef bool (*CheckOverRecursedFn)(JSContext *);
|
||||
typedef bool (*CheckOverRecursedParFn)(ForkJoinContext *);
|
||||
static const VMFunctionsModal CheckOverRecursedInfo = VMFunctionsModal(
|
||||
FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed),
|
||||
FunctionInfo<CheckOverRecursedParFn>(CheckOverRecursedPar));
|
||||
|
||||
bool
|
||||
CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
|
||||
@ -3026,25 +3002,6 @@ CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Out-of-line path to report over-recursed error and fail.
|
||||
class CheckOverRecursedFailurePar : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LCheckOverRecursedPar *lir_;
|
||||
|
||||
public:
|
||||
explicit CheckOverRecursedFailurePar(LCheckOverRecursedPar *lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
bool accept(CodeGenerator *codegen) {
|
||||
return codegen->visitCheckOverRecursedFailurePar(this);
|
||||
}
|
||||
|
||||
LCheckOverRecursedPar *lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
|
||||
{
|
||||
@ -3062,9 +3019,10 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
|
||||
masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
|
||||
|
||||
// Conditional forward (unlikely) branch to failure.
|
||||
CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
|
||||
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
|
||||
masm.checkInterruptFlagPar(tempReg, ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
@ -3072,55 +3030,12 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool)
|
||||
{
|
||||
OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir());
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// Avoid saving/restoring the temp register since we will put the
|
||||
// ReturnReg into it below and we don't want to clobber that
|
||||
// during PopRegsInMask():
|
||||
LCheckOverRecursedPar *lir = ool->lir();
|
||||
Register tempReg = ToRegister(lir->getTempReg());
|
||||
RegisterSet saveSet(lir->safepoint()->liveRegs());
|
||||
saveSet.takeUnchecked(tempReg);
|
||||
|
||||
masm.PushRegsInMask(saveSet);
|
||||
masm.movePtr(ToRegister(lir->forkJoinContext()), CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckOverRecursedPar));
|
||||
masm.movePtr(ReturnReg, tempReg);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
masm.branchIfFalseBool(tempReg, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Out-of-line path to report over-recursed error and fail.
|
||||
class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
public:
|
||||
LInterruptCheckPar *const lir;
|
||||
|
||||
explicit OutOfLineInterruptCheckPar(LInterruptCheckPar *lir)
|
||||
: lir(lir)
|
||||
{ }
|
||||
|
||||
bool accept(CodeGenerator *codegen) {
|
||||
return codegen->visitOutOfLineInterruptCheckPar(this);
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
|
||||
{
|
||||
// First check for cx->shared->interrupt_.
|
||||
OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
|
||||
if (!addOutOfLineCode(ool))
|
||||
OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
|
||||
if (!ool)
|
||||
return false;
|
||||
|
||||
Register tempReg = ToRegister(lir->getTempReg());
|
||||
@ -3129,34 +3044,6 @@ CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool)
|
||||
{
|
||||
OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// Avoid saving/restoring the temp register since we will put the
|
||||
// ReturnReg into it below and we don't want to clobber that
|
||||
// during PopRegsInMask():
|
||||
LInterruptCheckPar *lir = ool->lir;
|
||||
Register tempReg = ToRegister(lir->getTempReg());
|
||||
RegisterSet saveSet(lir->safepoint()->liveRegs());
|
||||
saveSet.takeUnchecked(tempReg);
|
||||
|
||||
masm.PushRegsInMask(saveSet);
|
||||
masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar));
|
||||
masm.movePtr(ReturnReg, tempReg);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
masm.branchIfFalseBool(tempReg, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IonScriptCounts *
|
||||
CodeGenerator::maybeCreateScriptCounts()
|
||||
{
|
||||
@ -3459,9 +3346,6 @@ CodeGenerator::generateBody()
|
||||
resetOsiPointRegs(iter->safepoint());
|
||||
#endif
|
||||
|
||||
if (!callTraceLIR(i, *iter))
|
||||
return false;
|
||||
|
||||
if (!iter->accept(this))
|
||||
return false;
|
||||
|
||||
@ -3910,12 +3794,6 @@ CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
|
||||
storeResultTo(ToRegister(lir->output()));
|
||||
restoreLive(lir);
|
||||
|
||||
Register resultReg = ToRegister(lir->output());
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail->entry());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4023,25 +3901,10 @@ CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
|
||||
masm.storeCallResult(out);
|
||||
restoreVolatile(out);
|
||||
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
return true;
|
||||
return bailoutTestPtr(Assembler::Zero, out, out, ool->lir->snapshot());
|
||||
}
|
||||
#endif // JSGC_FJGENERATIONAL
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAbortPar(LAbortPar *lir)
|
||||
{
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutUnsupported, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.jump(bail->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj,
|
||||
HandleValue id, HandleValue value);
|
||||
static const VMFunction InitElemInfo =
|
||||
@ -8552,29 +8415,6 @@ CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool)
|
||||
{
|
||||
ParallelBailoutCause cause = ool->cause();
|
||||
jsbytecode *bytecode = ool->bytecode();
|
||||
|
||||
masm.move32(Imm32(cause), CallTempReg0);
|
||||
loadOutermostJSScript(CallTempReg1);
|
||||
loadJSScriptForBlock(ool->basicBlock(), CallTempReg2);
|
||||
masm.movePtr(ImmPtr(bytecode), CallTempReg3);
|
||||
|
||||
masm.setupUnalignedABICall(4, CallTempReg4);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.passABIArg(CallTempReg1);
|
||||
masm.passABIArg(CallTempReg2);
|
||||
masm.passABIArg(CallTempReg3);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AbortPar));
|
||||
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.jump(&returnLabel_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitIsCallable(LIsCallable *ins)
|
||||
{
|
||||
@ -8618,22 +8458,6 @@ CodeGenerator::loadJSScriptForBlock(MBasicBlock *block, Register reg)
|
||||
masm.movePtr(ImmGCPtr(script), reg);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool)
|
||||
{
|
||||
loadOutermostJSScript(CallTempReg0);
|
||||
loadJSScriptForBlock(ool->lir()->mirRaw()->block(), CallTempReg1);
|
||||
|
||||
masm.setupUnalignedABICall(2, CallTempReg2);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.passABIArg(CallTempReg1);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PropagateAbortPar));
|
||||
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.jump(&returnLabel_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitHaveSameClass(LHaveSameClass *ins)
|
||||
{
|
||||
|
@ -152,7 +152,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitNewPar(LNewPar *lir);
|
||||
bool visitNewDenseArrayPar(LNewDenseArrayPar *lir);
|
||||
bool visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
|
||||
bool visitAbortPar(LAbortPar *lir);
|
||||
bool visitInitElem(LInitElem *lir);
|
||||
bool visitInitElemGetterSetter(LInitElemGetterSetter *lir);
|
||||
bool visitMutateProto(LMutateProto *lir);
|
||||
@ -301,7 +300,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
|
||||
|
||||
bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
|
||||
bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool);
|
||||
|
||||
bool visitInterruptCheckPar(LInterruptCheckPar *lir);
|
||||
bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
|
||||
@ -314,8 +312,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool);
|
||||
|
||||
bool visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool);
|
||||
bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool);
|
||||
bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool);
|
||||
void loadJSScriptForBlock(MBasicBlock *block, Register reg);
|
||||
void loadOutermostJSScript(Register reg);
|
||||
|
||||
@ -374,7 +370,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
|
||||
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
|
||||
bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
|
||||
bool checkForAbortPar(LInstruction *lir);
|
||||
|
||||
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
|
||||
|
||||
|
@ -226,10 +226,17 @@ JitRuntime::initialize(JSContext *cx)
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_Codegen, "# Emitting bailout handler");
|
||||
bailoutHandler_ = generateBailoutHandler(cx);
|
||||
bailoutHandler_ = generateBailoutHandler(cx, SequentialExecution);
|
||||
if (!bailoutHandler_)
|
||||
return false;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
IonSpew(IonSpew_Codegen, "# Emitting parallel bailout handler");
|
||||
parallelBailoutHandler_ = generateBailoutHandler(cx, ParallelExecution);
|
||||
if (!parallelBailoutHandler_)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
IonSpew(IonSpew_Codegen, "# Emitting invalidator");
|
||||
invalidator_ = generateInvalidator(cx);
|
||||
if (!invalidator_)
|
||||
|
@ -755,28 +755,18 @@ HandleException(ResumeFromException *rfe)
|
||||
void
|
||||
HandleParallelFailure(ResumeFromException *rfe)
|
||||
{
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
JitFrameIterator iter(cx->perThreadData->jitTop, ParallelExecution);
|
||||
|
||||
parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
|
||||
|
||||
while (!iter.isEntry()) {
|
||||
if (iter.isScripted()) {
|
||||
cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
|
||||
iter.script(), iter.script(), nullptr);
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
JitFrameIterator frameIter(cx);
|
||||
|
||||
while (!iter.isEntry()) {
|
||||
if (iter.isScripted())
|
||||
PropagateAbortPar(iter.script(), iter.script());
|
||||
++iter;
|
||||
}
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutUnsupportedVM);
|
||||
cx->bailoutRecord->rematerializeFrames(cx, frameIter);
|
||||
|
||||
rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
|
||||
rfe->stackPointer = iter.fp();
|
||||
|
||||
MOZ_ASSERT(frameIter.done());
|
||||
rfe->stackPointer = frameIter.fp();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -249,7 +249,6 @@ jit::CheckLogging()
|
||||
" cacheflush Instruction Cache flushes (ARM only for now)\n"
|
||||
" range Range Analysis\n"
|
||||
" logs C1 and JSON visualization logging\n"
|
||||
" trace Generate calls to js::jit::Trace() for effectful instructions\n"
|
||||
" all Everything\n"
|
||||
"\n"
|
||||
" bl-aborts Baseline compiler abort messages\n"
|
||||
@ -302,8 +301,6 @@ jit::CheckLogging()
|
||||
EnableChannel(IonSpew_CacheFlush);
|
||||
if (ContainsFlag(env, "logs"))
|
||||
EnableIonDebugLogging();
|
||||
if (ContainsFlag(env, "trace"))
|
||||
EnableChannel(IonSpew_Trace);
|
||||
if (ContainsFlag(env, "all"))
|
||||
LoggingBits = uint32_t(-1);
|
||||
|
||||
|
@ -54,8 +54,6 @@ namespace jit {
|
||||
_(Safepoints) \
|
||||
/* Debug info about Pools*/ \
|
||||
_(Pools) \
|
||||
/* Calls to js::jit::Trace() */ \
|
||||
_(Trace) \
|
||||
/* Debug info about the I$ */ \
|
||||
_(CacheFlush) \
|
||||
\
|
||||
|
@ -171,6 +171,9 @@ class JitRuntime
|
||||
// Generic bailout table; used if the bailout table overflows.
|
||||
JitCode *bailoutHandler_;
|
||||
|
||||
// Bailout handler for parallel execution.
|
||||
JitCode *parallelBailoutHandler_;
|
||||
|
||||
// Argument-rectifying thunk, in the case of insufficient arguments passed
|
||||
// to a function call site.
|
||||
JitCode *argumentsRectifier_;
|
||||
@ -237,7 +240,7 @@ class JitRuntime
|
||||
JitCode *generateEnterJIT(JSContext *cx, EnterJitType type);
|
||||
JitCode *generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut);
|
||||
JitCode *generateBailoutTable(JSContext *cx, uint32_t frameClass);
|
||||
JitCode *generateBailoutHandler(JSContext *cx);
|
||||
JitCode *generateBailoutHandler(JSContext *cx, ExecutionMode mode);
|
||||
JitCode *generateInvalidator(JSContext *cx);
|
||||
JitCode *generatePreBarrier(JSContext *cx, MIRType type);
|
||||
JitCode *generateMallocStub(JSContext *cx);
|
||||
@ -304,8 +307,12 @@ class JitRuntime
|
||||
JitCode *getBaselineDebugModeOSRHandler(JSContext *cx);
|
||||
void *getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg);
|
||||
|
||||
JitCode *getGenericBailoutHandler() const {
|
||||
return bailoutHandler_;
|
||||
JitCode *getGenericBailoutHandler(ExecutionMode mode) const {
|
||||
switch (mode) {
|
||||
case SequentialExecution: return bailoutHandler_;
|
||||
case ParallelExecution: return parallelBailoutHandler_;
|
||||
default: MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
}
|
||||
|
||||
JitCode *getExceptionTail() const {
|
||||
|
@ -571,12 +571,6 @@ class LNewStringObject : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
class LAbortPar : public LInstructionHelper<0, 0, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(AbortPar);
|
||||
};
|
||||
|
||||
class LInitElem : public LCallInstructionHelper<0, 1 + 2*BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
|
@ -34,7 +34,6 @@
|
||||
_(NewDenseArrayPar) \
|
||||
_(NewCallObjectPar) \
|
||||
_(NewDerivedTypedObject) \
|
||||
_(AbortPar) \
|
||||
_(InitElem) \
|
||||
_(InitElemGetterSetter) \
|
||||
_(MutateProto) \
|
||||
|
@ -118,13 +118,7 @@ bool
|
||||
LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins)
|
||||
{
|
||||
LCheckOverRecursed *lir = new(alloc()) LCheckOverRecursed();
|
||||
|
||||
if (!add(lir, ins))
|
||||
return false;
|
||||
if (!assignSafepoint(lir, ins))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return add(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -237,13 +231,6 @@ LIRGenerator::visitNewStringObject(MNewStringObject *ins)
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitAbortPar(MAbortPar *ins)
|
||||
{
|
||||
LAbortPar *lir = new(alloc()) LAbortPar();
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitInitElem(MInitElem *ins)
|
||||
{
|
||||
@ -2179,7 +2166,7 @@ LIRGenerator::visitGuardThreadExclusive(MGuardThreadExclusive *ins)
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
lir->setMir(ins);
|
||||
return add(lir, ins);
|
||||
return assignSnapshot(lir, Bailout_GuardThreadExclusive) && add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -77,7 +77,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitNewPar(MNewPar *ins);
|
||||
bool visitNewCallObjectPar(MNewCallObjectPar *ins);
|
||||
bool visitNewDenseArrayPar(MNewDenseArrayPar *ins);
|
||||
bool visitAbortPar(MAbortPar *ins);
|
||||
bool visitInitElem(MInitElem *ins);
|
||||
bool visitInitElemGetterSetter(MInitElemGetterSetter *ins);
|
||||
bool visitMutateProto(MMutateProto *ins);
|
||||
|
@ -1821,24 +1821,6 @@ class MNewDerivedTypedObject
|
||||
}
|
||||
};
|
||||
|
||||
// Abort parallel execution.
|
||||
class MAbortPar : public MAryControlInstruction<0, 0>
|
||||
{
|
||||
MAbortPar()
|
||||
: MAryControlInstruction<0, 0>()
|
||||
{
|
||||
setResultType(MIRType_None);
|
||||
setGuard();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(AbortPar);
|
||||
|
||||
static MAbortPar *New(TempAllocator &alloc) {
|
||||
return new(alloc) MAbortPar();
|
||||
}
|
||||
};
|
||||
|
||||
// Setting __proto__ in an object literal.
|
||||
class MMutateProto
|
||||
: public MAryInstruction<2>,
|
||||
@ -2292,10 +2274,11 @@ class MApplyArgs
|
||||
}
|
||||
};
|
||||
|
||||
class MBail : public MNullaryInstruction
|
||||
class MBail : public MAryControlInstruction<0, 0>
|
||||
{
|
||||
protected:
|
||||
MBail(BailoutKind kind)
|
||||
: MAryControlInstruction<0, 0>()
|
||||
{
|
||||
bailoutKind_ = kind;
|
||||
setGuard();
|
||||
@ -5253,7 +5236,6 @@ class MInterruptCheckPar : public MUnaryInstruction
|
||||
{
|
||||
setResultType(MIRType_None);
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
@ -5266,6 +5248,9 @@ class MInterruptCheckPar : public MUnaryInstruction
|
||||
MDefinition *forkJoinContext() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Check whether we need to fire the interrupt handler.
|
||||
|
@ -748,7 +748,15 @@ MBasicBlock::discardDefAt(MDefinitionIterator &old)
|
||||
void
|
||||
MBasicBlock::discardAllInstructions()
|
||||
{
|
||||
for (MInstructionIterator iter = begin(); iter != end(); ) {
|
||||
MInstructionIterator iter = begin();
|
||||
discardAllInstructionsStartingAt(iter);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator &iter)
|
||||
{
|
||||
while (iter != end()) {
|
||||
for (size_t i = 0, e = iter->numOperands(); i < e; i++)
|
||||
iter->discardOperand(i);
|
||||
iter = instructions_.removeAt(iter);
|
||||
|
@ -232,6 +232,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
|
||||
MInstructionReverseIterator discardAt(MInstructionReverseIterator &iter);
|
||||
MDefinitionIterator discardDefAt(MDefinitionIterator &iter);
|
||||
void discardAllInstructions();
|
||||
void discardAllInstructionsStartingAt(MInstructionIterator &iter);
|
||||
void discardAllPhiOperands();
|
||||
void discardAllPhis();
|
||||
void discardAllResumePoints(bool discardEntry = true);
|
||||
|
@ -217,7 +217,6 @@ namespace jit {
|
||||
_(NewPar) \
|
||||
_(NewDenseArrayPar) \
|
||||
_(NewDerivedTypedObject) \
|
||||
_(AbortPar) \
|
||||
_(LambdaPar) \
|
||||
_(RestPar) \
|
||||
_(ForkJoinContext) \
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/mips/Simulator-mips.h"
|
||||
#include "jit/RematerializedFrame.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
@ -22,7 +23,6 @@ using JS::AutoCheckCannotGC;
|
||||
using parallel::Spew;
|
||||
using parallel::SpewOps;
|
||||
using parallel::SpewBailouts;
|
||||
using parallel::SpewBailoutIR;
|
||||
|
||||
// Load the current thread context.
|
||||
ForkJoinContext *
|
||||
@ -125,63 +125,6 @@ jit::IsInTargetRegion(ForkJoinContext *cx, TypedObject *typedObj)
|
||||
typedMem < cx->targetRegionEnd);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
printTrace(const char *prefix, struct IonLIRTraceData *cached)
|
||||
{
|
||||
fprintf(stderr, "%s / Block %3u / LIR %3u / Mode %u / LIR %s\n",
|
||||
prefix,
|
||||
cached->blockIndex, cached->lirIndex, cached->execModeInt, cached->lirOpName);
|
||||
}
|
||||
|
||||
static struct IonLIRTraceData seqTraceData;
|
||||
#endif
|
||||
|
||||
void
|
||||
jit::TraceLIR(IonLIRTraceData *current)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
static enum { NotSet, All, Bailouts } traceMode;
|
||||
|
||||
// If you set IONFLAGS=trace, this function will be invoked before every LIR.
|
||||
//
|
||||
// You can either modify it to do whatever you like, or use gdb scripting.
|
||||
// For example:
|
||||
//
|
||||
// break TraceLIR
|
||||
// commands
|
||||
// continue
|
||||
// exit
|
||||
|
||||
if (traceMode == NotSet) {
|
||||
// Racy, but that's ok.
|
||||
const char *env = getenv("IONFLAGS");
|
||||
if (strstr(env, "trace-all"))
|
||||
traceMode = All;
|
||||
else
|
||||
traceMode = Bailouts;
|
||||
}
|
||||
|
||||
IonLIRTraceData *cached;
|
||||
if (current->execModeInt == 0)
|
||||
cached = &seqTraceData;
|
||||
else
|
||||
cached = &ForkJoinContext::current()->traceData;
|
||||
|
||||
if (current->blockIndex == 0xDEADBEEF) {
|
||||
if (current->execModeInt == 0)
|
||||
printTrace("BAILOUT", cached);
|
||||
else
|
||||
SpewBailoutIR(cached);
|
||||
}
|
||||
|
||||
memcpy(cached, current, sizeof(IonLIRTraceData));
|
||||
|
||||
if (traceMode == All)
|
||||
printTrace("Exec", cached);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
||||
{
|
||||
@ -199,7 +142,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
if (Simulator::Current()->overRecursed()) {
|
||||
cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@ -211,7 +154,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
||||
realStackLimit = cx->perThreadData->jitStackLimit;
|
||||
|
||||
if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
|
||||
cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -224,11 +167,7 @@ jit::InterruptCheckPar(ForkJoinContext *cx)
|
||||
JS_ASSERT(ForkJoinContext::current() == cx);
|
||||
bool result = cx->check();
|
||||
if (!result) {
|
||||
// Do not set the cause here. Either it was set by this
|
||||
// thread already by some code that then triggered an abort,
|
||||
// or else we are just picking up an abort from some other
|
||||
// thread. Either way we have nothing useful to contribute so
|
||||
// we might as well leave our bailout case unset.
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutInterrupt);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -239,8 +178,10 @@ jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
|
||||
{
|
||||
JSObject::EnsureDenseResult res =
|
||||
array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
|
||||
if (res != JSObject::ED_OK)
|
||||
if (res != JSObject::ED_OK) {
|
||||
fprintf(stderr, "==== NGNG\n");
|
||||
return nullptr;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -573,58 +514,36 @@ jit::UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs,
|
||||
}
|
||||
|
||||
void
|
||||
jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
|
||||
jsbytecode *bytecode)
|
||||
jit::BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer)
|
||||
{
|
||||
// Spew before asserts to help with diagnosing failures.
|
||||
Spew(SpewBailouts,
|
||||
"Parallel abort with cause %d in %p:%s:%d "
|
||||
"(%p:%s:%d at line %d)",
|
||||
cause,
|
||||
outermostScript, outermostScript->filename(), outermostScript->lineno(),
|
||||
currentScript, currentScript->filename(), currentScript->lineno(),
|
||||
(currentScript ? PCToLineNumber(currentScript, bytecode) : 0));
|
||||
|
||||
JS_ASSERT(InParallelSection());
|
||||
JS_ASSERT(outermostScript != nullptr);
|
||||
JS_ASSERT(currentScript != nullptr);
|
||||
JS_ASSERT(outermostScript->hasParallelIonScript());
|
||||
parallel::Spew(parallel::SpewBailouts, "Bailing");
|
||||
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
|
||||
JS_ASSERT(cx->bailoutRecord->depth == 0);
|
||||
cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
|
||||
// We don't have an exit frame.
|
||||
MOZ_ASSERT(size_t(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(IonCommonFrameLayout)) < 0x1000 &&
|
||||
size_t(FAKE_JIT_TOP_FOR_BAILOUT) >= 0,
|
||||
"Fake jitTop pointer should be within the first page.");
|
||||
cx->perThreadData->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
|
||||
|
||||
JitActivationIterator jitActivations(cx->perThreadData);
|
||||
IonBailoutIterator frameIter(jitActivations, sp);
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutUnsupported);
|
||||
cx->bailoutRecord->rematerializeFrames(cx, frameIter);
|
||||
|
||||
MOZ_ASSERT(frameIter.done());
|
||||
*entryFramePointer = frameIter.fp();
|
||||
}
|
||||
|
||||
void
|
||||
jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
|
||||
bool
|
||||
jit::CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj)
|
||||
{
|
||||
Spew(SpewBailouts,
|
||||
"Propagate parallel abort via %p:%s:%d (%p:%s:%d)",
|
||||
outermostScript, outermostScript->filename(), outermostScript->lineno(),
|
||||
currentScript, currentScript->filename(), currentScript->lineno());
|
||||
|
||||
JS_ASSERT(InParallelSection());
|
||||
JS_ASSERT(outermostScript->hasParallelIonScript());
|
||||
|
||||
outermostScript->parallelIonScript()->setHasUncompiledCallTarget();
|
||||
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
if (currentScript)
|
||||
cx->bailoutRecord->addTrace(currentScript, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
jit::CallToUncompiledScriptPar(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(InParallelSection());
|
||||
|
||||
#ifdef DEBUG
|
||||
static const int max_bound_function_unrolling = 5;
|
||||
|
||||
if (!obj->is<JSFunction>()) {
|
||||
Spew(SpewBailouts, "Call to non-function");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSFunction *func = &obj->as<JSFunction>();
|
||||
@ -656,6 +575,8 @@ jit::CallToUncompiledScriptPar(JSObject *obj)
|
||||
Spew(SpewBailouts, "Call to native function");
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
@ -72,13 +72,8 @@ JSObject *InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest
|
||||
HandleObject templateObj, HandleObject res);
|
||||
|
||||
// Abort and debug tracing functions.
|
||||
void AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
|
||||
jsbytecode *bytecode);
|
||||
void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript);
|
||||
|
||||
void TraceLIR(IonLIRTraceData *current);
|
||||
|
||||
void CallToUncompiledScriptPar(JSObject *obj);
|
||||
void BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer);
|
||||
bool CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -75,6 +75,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
|
||||
bool replaceWithNewPar(MInstruction *newInstruction, JSObject *templateObject);
|
||||
bool replace(MInstruction *oldInstruction, MInstruction *replacementInstruction);
|
||||
bool replaceLastIns(MInstruction *oldInstruction, MControlInstruction *replacementInstruction);
|
||||
|
||||
bool visitSpecializedInstruction(MInstruction *ins, MIRType spec, uint32_t flags);
|
||||
|
||||
@ -106,7 +107,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
return cx_;
|
||||
}
|
||||
|
||||
bool convertToBailout(MBasicBlock *block, MInstruction *ins);
|
||||
bool convertToBailout(MInstructionIterator &iter);
|
||||
|
||||
// I am taking the policy of blacklisting everything that's not
|
||||
// obviously safe for now. We can loosen as we need.
|
||||
@ -281,7 +282,6 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
SAFE_OP(NewDenseArrayPar)
|
||||
SAFE_OP(NewCallObjectPar)
|
||||
SAFE_OP(LambdaPar)
|
||||
SAFE_OP(AbortPar)
|
||||
UNSAFE_OP(ArrayConcat)
|
||||
UNSAFE_OP(GetDOMProperty)
|
||||
UNSAFE_OP(GetDOMMember)
|
||||
@ -329,6 +329,16 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
UNSAFE_OP(ConvertElementsToDoubles)
|
||||
};
|
||||
|
||||
static void
|
||||
TransplantResumePoint(MInstruction *oldInstruction, MInstruction *replacementInstruction)
|
||||
{
|
||||
if (MResumePoint *rp = oldInstruction->resumePoint()) {
|
||||
replacementInstruction->setResumePoint(rp);
|
||||
if (rp->instruction() == oldInstruction)
|
||||
rp->setInstruction(replacementInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyAnalysis::analyze()
|
||||
{
|
||||
@ -354,21 +364,20 @@ ParallelSafetyAnalysis::analyze()
|
||||
// if we encounter an inherently unsafe operation, in
|
||||
// which case we will transform this block into a bailout
|
||||
// block.
|
||||
MInstruction *instr = nullptr;
|
||||
for (MInstructionIterator ins(block->begin());
|
||||
ins != block->end() && !visitor.unsafe();)
|
||||
{
|
||||
MInstruction *ins = nullptr;
|
||||
MInstructionIterator iter(block->begin());
|
||||
while (iter != block->end() && !visitor.unsafe()) {
|
||||
if (mir_->shouldCancel("ParallelSafetyAnalysis"))
|
||||
return false;
|
||||
|
||||
// We may be removing or replacing the current
|
||||
// instruction, so advance `ins` now. Remember the
|
||||
// instruction, so advance `iter` now. Remember the
|
||||
// last instr. we looked at for use later if it should
|
||||
// prove unsafe.
|
||||
instr = *ins++;
|
||||
ins = *iter++;
|
||||
|
||||
if (!instr->accept(&visitor)) {
|
||||
SpewMIR(instr, "Unaccepted");
|
||||
if (!ins->accept(&visitor)) {
|
||||
SpewMIR(ins, "Unaccepted");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -390,8 +399,11 @@ ParallelSafetyAnalysis::analyze()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, create a replacement that will.
|
||||
if (!visitor.convertToBailout(*block, instr))
|
||||
// Otherwise, create a replacement that will. We seek back one
|
||||
// position on the instruction iterator, as we will be
|
||||
// discarding all instructions starting at the unsafe
|
||||
// instruction.
|
||||
if (!visitor.convertToBailout(--iter))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -406,79 +418,30 @@ ParallelSafetyAnalysis::analyze()
|
||||
IonSpewPass("UCEAfterParallelSafetyAnalysis");
|
||||
AssertExtendedGraphCoherency(graph_);
|
||||
|
||||
if (!removeResumePointOperands())
|
||||
return false;
|
||||
IonSpewPass("RemoveResumePointOperands");
|
||||
AssertExtendedGraphCoherency(graph_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyAnalysis::removeResumePointOperands()
|
||||
{
|
||||
// In parallel exec mode, nothing is effectful, therefore we do
|
||||
// not need to reconstruct interpreter state and can simply
|
||||
// bailout by returning a special code. Ideally we'd either
|
||||
// remove the unused resume points or else never generate them in
|
||||
// the first place, but I encountered various assertions and
|
||||
// crashes attempting to do that, so for the time being I simply
|
||||
// replace their operands with undefined. This prevents them from
|
||||
// interfering with DCE and other optimizations. It is also *necessary*
|
||||
// to handle cases like this:
|
||||
//
|
||||
// foo(a, b, c.bar())
|
||||
//
|
||||
// where `foo` was deemed to be an unsafe function to call. This
|
||||
// is because without neutering the ResumePoints, they would still
|
||||
// refer to the MPassArg nodes generated for the call to foo().
|
||||
// But the call to foo() is dead and has been removed, leading to
|
||||
// an inconsistent IR and assertions at codegen time.
|
||||
|
||||
MConstant *udef = nullptr;
|
||||
for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
|
||||
if (udef)
|
||||
replaceOperandsOnResumePoint(block->entryResumePoint(), udef);
|
||||
|
||||
for (MInstructionIterator ins(block->begin()); ins != block->end(); ins++) {
|
||||
if (ins->isStart()) {
|
||||
JS_ASSERT(udef == nullptr);
|
||||
udef = MConstant::New(graph_.alloc(), UndefinedValue());
|
||||
block->insertAfter(*ins, udef);
|
||||
} else if (udef) {
|
||||
if (MResumePoint *resumePoint = ins->resumePoint())
|
||||
replaceOperandsOnResumePoint(resumePoint, udef);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ParallelSafetyAnalysis::replaceOperandsOnResumePoint(MResumePoint *resumePoint,
|
||||
MDefinition *withDef)
|
||||
{
|
||||
for (size_t i = 0, e = resumePoint->numOperands(); i < e; i++)
|
||||
resumePoint->replaceOperand(i, withDef);
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins)
|
||||
ParallelSafetyVisitor::convertToBailout(MInstructionIterator &iter)
|
||||
{
|
||||
// We expect iter to be settled on the unsafe instruction.
|
||||
MInstruction *ins = *iter;
|
||||
MBasicBlock *block = ins->block();
|
||||
JS_ASSERT(unsafe()); // `block` must have contained unsafe items
|
||||
JS_ASSERT(block->isMarked()); // `block` must have been reachable to get here
|
||||
|
||||
// Convert the block to a bailout block. In principle, we
|
||||
// only need one bailout block per graph! But I
|
||||
// found this approach easier to implement given the design of the
|
||||
// MIR Graph construction routines. Using multiple blocks helps to
|
||||
// keep the PC information more accurate.
|
||||
for (size_t i = 0, e = block->numSuccessors(); i < e; i++)
|
||||
block->getSuccessor(i)->removePredecessor(block);
|
||||
clearUnsafe();
|
||||
block->discardAllPhis();
|
||||
block->discardAllInstructions();
|
||||
block->end(MAbortPar::New(graph_.alloc()));
|
||||
|
||||
// Discard the rest of the block and sever its link to its successors in
|
||||
// the CFG.
|
||||
for (size_t i = 0; i < block->numSuccessors(); i++)
|
||||
block->getSuccessor(i)->removePredecessor(block);
|
||||
block->discardAllInstructionsStartingAt(iter);
|
||||
|
||||
// End the block in the bail.
|
||||
MBail *bailout = MBail::New(graph_.alloc());
|
||||
TransplantResumePoint(ins, bailout);
|
||||
block->end(bailout);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -503,8 +466,8 @@ ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
|
||||
SpewMIR(ins, "call with dynamic slots");
|
||||
return markUnsafe();
|
||||
}
|
||||
replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
|
||||
return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -514,8 +477,8 @@ ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
|
||||
SpewMIR(ins, "call with dynamic slots");
|
||||
return markUnsafe();
|
||||
}
|
||||
replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
|
||||
return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -527,8 +490,7 @@ ParallelSafetyVisitor::visitLambda(MLambda *ins)
|
||||
}
|
||||
|
||||
// fast path: replace with LambdaPar op
|
||||
replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
return replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -598,22 +560,18 @@ bool
|
||||
ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
|
||||
JSObject *templateObject)
|
||||
{
|
||||
replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
|
||||
return true;
|
||||
return replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyVisitor::replace(MInstruction *oldInstruction,
|
||||
MInstruction *replacementInstruction)
|
||||
{
|
||||
TransplantResumePoint(oldInstruction, replacementInstruction);
|
||||
|
||||
MBasicBlock *block = oldInstruction->block();
|
||||
block->insertBefore(oldInstruction, replacementInstruction);
|
||||
oldInstruction->replaceAllUsesWith(replacementInstruction);
|
||||
MResumePoint *rp = oldInstruction->resumePoint();
|
||||
if (rp && rp->instruction() == oldInstruction) {
|
||||
rp->setInstruction(replacementInstruction);
|
||||
replacementInstruction->setResumePoint(rp);
|
||||
}
|
||||
block->discard(oldInstruction);
|
||||
|
||||
// We may have replaced a specialized Float32 instruction by its
|
||||
@ -631,6 +589,17 @@ ParallelSafetyVisitor::replace(MInstruction *oldInstruction,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyVisitor::replaceLastIns(MInstruction *oldInstruction,
|
||||
MControlInstruction *replacementInstruction)
|
||||
{
|
||||
TransplantResumePoint(oldInstruction, replacementInstruction);
|
||||
MBasicBlock *block = oldInstruction->block();
|
||||
block->discardLastIns();
|
||||
block->end(replacementInstruction);
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Write Guards
|
||||
//
|
||||
@ -794,13 +763,8 @@ ParallelSafetyVisitor::visitSpecializedInstruction(MInstruction *ins, MIRType sp
|
||||
bool
|
||||
ParallelSafetyVisitor::visitThrow(MThrow *thr)
|
||||
{
|
||||
MBasicBlock *block = thr->block();
|
||||
JS_ASSERT(block->lastIns() == thr);
|
||||
block->discardLastIns();
|
||||
MAbortPar *bailout = MAbortPar::New(alloc());
|
||||
if (!bailout)
|
||||
return false;
|
||||
block->end(bailout);
|
||||
JS_ASSERT(thr->block()->lastIns() == thr);
|
||||
replaceLastIns(thr, MBail::New(alloc()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,15 +20,12 @@ class AutoDestroyAllocator;
|
||||
|
||||
// Determines whether a function is compatible for parallel execution.
|
||||
// Removes basic blocks containing unsafe MIR operations from the
|
||||
// graph and replaces them with MAbortPar blocks.
|
||||
// graph and replaces them with MBail blocks.
|
||||
class ParallelSafetyAnalysis
|
||||
{
|
||||
MIRGenerator *mir_;
|
||||
MIRGraph &graph_;
|
||||
|
||||
bool removeResumePointOperands();
|
||||
void replaceOperandsOnResumePoint(MResumePoint *resumePoint, MDefinition *withDef);
|
||||
|
||||
public:
|
||||
ParallelSafetyAnalysis(MIRGenerator *mir,
|
||||
MIRGraph &graph)
|
||||
|
@ -178,7 +178,7 @@ CodeGeneratorARM::generateOutOfLineCode()
|
||||
// Push the frame size, so the handler can recover the IonScript.
|
||||
masm.ma_mov(Imm32(frameSize()), lr);
|
||||
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
|
||||
masm.branch(handler);
|
||||
}
|
||||
|
||||
@ -188,22 +188,6 @@ CodeGeneratorARM::generateOutOfLineCode()
|
||||
bool
|
||||
CodeGeneratorARM::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot)
|
||||
{
|
||||
CompileInfo &info = snapshot->mir()->block()->info();
|
||||
switch (info.executionMode()) {
|
||||
|
||||
case ParallelExecution: {
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
masm.ma_b(ool->entry(), condition);
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
if (!encode(snapshot))
|
||||
return false;
|
||||
|
||||
@ -238,23 +222,6 @@ CodeGeneratorARM::bailoutFrom(Label *label, LSnapshot *snapshot)
|
||||
JS_ASSERT(label->used());
|
||||
JS_ASSERT(!label->bound());
|
||||
|
||||
CompileInfo &info = snapshot->mir()->block()->info();
|
||||
switch (info.executionMode()) {
|
||||
|
||||
case ParallelExecution: {
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
masm.retarget(label, ool->entry());
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
if (!encode(snapshot))
|
||||
return false;
|
||||
|
||||
|
@ -68,6 +68,10 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
masm.test32(lhs, rhs);
|
||||
return bailoutIf(c, snapshot);
|
||||
}
|
||||
bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
|
||||
masm.test32(reg, Imm32(0xFF));
|
||||
return bailoutIf(Assembler::Zero, snapshot);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool generatePrologue();
|
||||
|
@ -16,6 +16,7 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
|
||||
#include "jit/ExecutionMode-inl.h"
|
||||
@ -514,7 +515,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
{
|
||||
// the stack should look like:
|
||||
// [IonFrame]
|
||||
@ -559,10 +560,17 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
masm.transferReg(lr);
|
||||
masm.finishDataTransfer();
|
||||
|
||||
masm.ma_mov(sp, spArg);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
PushBailoutFrame(masm, frameClass, r0);
|
||||
|
||||
// SP % 8 == 4
|
||||
// STEP 1c: Call the bailout function, giving a pointer to the
|
||||
// structure we just blitted onto the stack
|
||||
masm.ma_mov(sp, r0);
|
||||
const int sizeOfBailoutInfo = sizeof(void *)*2;
|
||||
masm.reserveStack(sizeOfBailoutInfo);
|
||||
masm.mov(sp, r1);
|
||||
@ -610,6 +618,32 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
masm.branch(bailoutTail);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateParallelBailoutThunk(MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
// As GenerateBailoutThunk, except we return an error immediately. We do
|
||||
// the bailout dance so that we can walk the stack and have accurate
|
||||
// reporting of frame information.
|
||||
|
||||
PushBailoutFrame(masm, frameClass, r0);
|
||||
|
||||
// Parallel bailout is like parallel failure in that we unwind all the way
|
||||
// to the entry frame. Reserve space for the frame pointer of the entry frame.
|
||||
const int sizeOfEntryFramePointer = sizeof(uint8_t *) * 2;
|
||||
masm.reserveStack(sizeOfEntryFramePointer);
|
||||
masm.mov(sp, r1);
|
||||
|
||||
masm.setupAlignedABICall(2);
|
||||
masm.passABIArg(r0);
|
||||
masm.passABIArg(r1);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
|
||||
|
||||
// Get the frame pointer of the entry fram and return.
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.ma_ldr(Address(sp, 0), sp);
|
||||
masm.as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
{
|
||||
@ -634,10 +668,20 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
|
@ -213,22 +213,6 @@ CodeGeneratorMIPS::bailoutFrom(Label *label, LSnapshot *snapshot)
|
||||
MOZ_ASSERT(label->used());
|
||||
MOZ_ASSERT(!label->bound());
|
||||
|
||||
CompileInfo &info = snapshot->mir()->block()->info();
|
||||
switch (info.executionMode()) {
|
||||
case ParallelExecution: {
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
masm.retarget(label, ool->entry());
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
if (!encode(snapshot))
|
||||
return false;
|
||||
|
||||
|
@ -93,6 +93,11 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
|
||||
masm.branchTestPtr(c, lhs, rhs, &bail);
|
||||
return bailoutFrom(&bail, snapshot);
|
||||
}
|
||||
bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
|
||||
Label bail;
|
||||
masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail);
|
||||
return bailoutFrom(&bail, snapshot);
|
||||
}
|
||||
|
||||
bool bailoutFrom(Label *label, LSnapshot *snapshot);
|
||||
bool bailout(LSnapshot *snapshot);
|
||||
|
@ -642,10 +642,20 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
|
@ -340,6 +340,13 @@ CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
|
||||
if (!deoptTable_)
|
||||
return false;
|
||||
|
||||
// We do not generate a bailout table for parallel code.
|
||||
switch (gen->info().executionMode()) {
|
||||
case SequentialExecution: break;
|
||||
case ParallelExecution: return false;
|
||||
default: MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
JS_ASSERT(frameClass_ != FrameSizeClass::None());
|
||||
|
||||
if (snapshot->bailoutId() != INVALID_BAILOUT_ID)
|
||||
@ -822,123 +829,6 @@ CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
OutOfLineAbortPar *
|
||||
CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
|
||||
jsbytecode *bytecode)
|
||||
{
|
||||
OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode);
|
||||
if (!ool || !addOutOfLineCode(ool))
|
||||
return nullptr;
|
||||
return ool;
|
||||
}
|
||||
|
||||
OutOfLineAbortPar *
|
||||
CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir)
|
||||
{
|
||||
MDefinition *mir = lir->mirRaw();
|
||||
MBasicBlock *block = mir->block();
|
||||
jsbytecode *pc = mir->trackedPc();
|
||||
if (!pc) {
|
||||
if (lir->snapshot())
|
||||
pc = lir->snapshot()->mir()->pc();
|
||||
else
|
||||
pc = block->pc();
|
||||
}
|
||||
return oolAbortPar(cause, block, pc);
|
||||
}
|
||||
|
||||
OutOfLinePropagateAbortPar *
|
||||
CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir)
|
||||
{
|
||||
OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir);
|
||||
if (!ool || !addOutOfLineCode(ool))
|
||||
return nullptr;
|
||||
return ool;
|
||||
}
|
||||
|
||||
bool
|
||||
OutOfLineAbortPar::generate(CodeGeneratorShared *codegen)
|
||||
{
|
||||
codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
|
||||
return codegen->visitOutOfLineAbortPar(this);
|
||||
}
|
||||
|
||||
bool
|
||||
OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen)
|
||||
{
|
||||
codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
|
||||
return codegen->visitOutOfLinePropagateAbortPar(this);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
|
||||
const char *bailoutName)
|
||||
{
|
||||
JS_ASSERT_IF(!lir, bailoutName);
|
||||
|
||||
if (!IonSpewEnabled(IonSpew_Trace))
|
||||
return true;
|
||||
|
||||
uint32_t execMode = (uint32_t) gen->info().executionMode();
|
||||
uint32_t lirIndex;
|
||||
const char *lirOpName;
|
||||
const char *mirOpName;
|
||||
JSScript *script;
|
||||
jsbytecode *pc;
|
||||
|
||||
masm.PushRegsInMask(RegisterSet::Volatile());
|
||||
masm.reserveStack(sizeof(IonLIRTraceData));
|
||||
|
||||
// This first move is here so that when you scan the disassembly,
|
||||
// you can easily pick out where each instruction begins. The
|
||||
// next few items indicate to you the Basic Block / LIR.
|
||||
masm.move32(Imm32(0xDEADBEEF), CallTempReg0);
|
||||
|
||||
if (lir) {
|
||||
lirIndex = lir->id();
|
||||
lirOpName = lir->opName();
|
||||
if (MDefinition *mir = lir->mirRaw()) {
|
||||
mirOpName = mir->opName();
|
||||
script = mir->block()->info().script();
|
||||
pc = mir->trackedPc();
|
||||
} else {
|
||||
mirOpName = nullptr;
|
||||
script = nullptr;
|
||||
pc = nullptr;
|
||||
}
|
||||
} else {
|
||||
blockIndex = lirIndex = 0xDEADBEEF;
|
||||
lirOpName = mirOpName = bailoutName;
|
||||
script = nullptr;
|
||||
pc = nullptr;
|
||||
}
|
||||
|
||||
masm.store32(Imm32(blockIndex),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, blockIndex)));
|
||||
masm.store32(Imm32(lirIndex),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, lirIndex)));
|
||||
masm.store32(Imm32(execMode),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, execModeInt)));
|
||||
masm.storePtr(ImmPtr(lirOpName),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, lirOpName)));
|
||||
masm.storePtr(ImmPtr(mirOpName),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, mirOpName)));
|
||||
masm.storePtr(ImmGCPtr(script),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, script)));
|
||||
masm.storePtr(ImmPtr(pc),
|
||||
Address(StackPointer, offsetof(IonLIRTraceData, pc)));
|
||||
|
||||
masm.movePtr(StackPointer, CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR));
|
||||
|
||||
masm.freeStack(sizeof(IonLIRTraceData));
|
||||
masm.PopRegsInMask(RegisterSet::Volatile());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Label *
|
||||
CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
|
||||
{
|
||||
|
@ -26,8 +26,6 @@ class OutOfLineCode;
|
||||
class CodeGenerator;
|
||||
class MacroAssembler;
|
||||
class IonCache;
|
||||
class OutOfLineAbortPar;
|
||||
class OutOfLinePropagateAbortPar;
|
||||
|
||||
template <class ArgSeq, class StoreOutputTo>
|
||||
class OutOfLineCallVM;
|
||||
@ -468,28 +466,6 @@ class CodeGeneratorShared : public LInstructionVisitor
|
||||
|
||||
bool omitOverRecursedCheck() const;
|
||||
|
||||
public:
|
||||
bool callTraceLIR(uint32_t blockIndex, LInstruction *lir, const char *bailoutName = nullptr);
|
||||
|
||||
// Parallel aborts:
|
||||
//
|
||||
// Parallel aborts work somewhat differently from sequential
|
||||
// bailouts. When an abort occurs, we first invoke
|
||||
// ReportAbortPar() and then we return JS_ION_ERROR. Each
|
||||
// call on the stack will check for this error return and
|
||||
// propagate it upwards until the C++ code that invoked the ion
|
||||
// code is reached.
|
||||
//
|
||||
// The snapshot that is provided to `oolAbortPar` is currently
|
||||
// only used for error reporting, so that we can provide feedback
|
||||
// to the user about which instruction aborted and (perhaps) why.
|
||||
OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
|
||||
jsbytecode *bytecode);
|
||||
OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, LInstruction *lir);
|
||||
OutOfLinePropagateAbortPar *oolPropagateAbortPar(LInstruction *lir);
|
||||
virtual bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool) = 0;
|
||||
virtual bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool) = 0;
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
protected:
|
||||
bool emitTracelogScript(bool isStart);
|
||||
@ -770,53 +746,6 @@ CodeGeneratorShared::visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo>
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initiate a parallel abort. The snapshot is used to record the
|
||||
// cause.
|
||||
class OutOfLineAbortPar : public OutOfLineCode
|
||||
{
|
||||
private:
|
||||
ParallelBailoutCause cause_;
|
||||
MBasicBlock *basicBlock_;
|
||||
jsbytecode *bytecode_;
|
||||
|
||||
public:
|
||||
OutOfLineAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, jsbytecode *bytecode)
|
||||
: cause_(cause),
|
||||
basicBlock_(basicBlock),
|
||||
bytecode_(bytecode)
|
||||
{ }
|
||||
|
||||
ParallelBailoutCause cause() {
|
||||
return cause_;
|
||||
}
|
||||
|
||||
MBasicBlock *basicBlock() {
|
||||
return basicBlock_;
|
||||
}
|
||||
|
||||
jsbytecode *bytecode() {
|
||||
return bytecode_;
|
||||
}
|
||||
|
||||
bool generate(CodeGeneratorShared *codegen);
|
||||
};
|
||||
|
||||
// Used when some callee has aborted.
|
||||
class OutOfLinePropagateAbortPar : public OutOfLineCode
|
||||
{
|
||||
private:
|
||||
LInstruction *lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLinePropagateAbortPar(LInstruction *lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
LInstruction *lir() { return lir_; }
|
||||
|
||||
bool generate(CodeGeneratorShared *codegen);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -356,7 +356,7 @@ CodeGeneratorX86Shared::generateOutOfLineCode()
|
||||
// Push the frame size, so the handler can recover the IonScript.
|
||||
masm.push(Imm32(frameSize()));
|
||||
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler(gen->info().executionMode());
|
||||
masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE);
|
||||
}
|
||||
|
||||
@ -398,22 +398,6 @@ class BailoutLabel {
|
||||
template <typename T> bool
|
||||
CodeGeneratorX86Shared::bailout(const T &binder, LSnapshot *snapshot)
|
||||
{
|
||||
CompileInfo &info = snapshot->mir()->block()->info();
|
||||
switch (info.executionMode()) {
|
||||
case ParallelExecution: {
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
binder(masm, ool->entry());
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
if (!encode(snapshot))
|
||||
return false;
|
||||
|
||||
|
@ -75,6 +75,10 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
masm.test32(lhs, rhs);
|
||||
return bailoutIf(c, snapshot);
|
||||
}
|
||||
bool bailoutIfFalseBool(Register reg, LSnapshot *snapshot) {
|
||||
masm.test32(reg, Imm32(0xFF));
|
||||
return bailoutIf(Assembler::Zero, snapshot);
|
||||
}
|
||||
bool bailoutCvttsd2si(FloatRegister src, Register dest, LSnapshot *snapshot) {
|
||||
// cvttsd2si returns 0x80000000 on failure. Test for it by
|
||||
// subtracting 1 and testing overflow. The other possibility is to test
|
||||
|
@ -11,6 +11,7 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
#include "jit/x64/BaselineHelpers-x64.h"
|
||||
|
||||
@ -446,13 +447,19 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
PushBailoutFrame(MacroAssembler &masm, Register spArg)
|
||||
{
|
||||
// Push registers such that we can access them from [base + code].
|
||||
masm.PushRegsInMask(AllRegs);
|
||||
|
||||
// Get the stack pointer into a register, pre-alignment.
|
||||
masm.movq(rsp, r8);
|
||||
masm.movq(rsp, spArg);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
PushBailoutFrame(masm, r8);
|
||||
|
||||
// Make space for Bailout's bailoutInfo outparam.
|
||||
masm.reserveStack(sizeof(void *));
|
||||
@ -474,7 +481,7 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
//
|
||||
// Remove both the bailout frame and the topmost Ion frame's stack.
|
||||
static const uint32_t BailoutDataSize = sizeof(void *) * Registers::Total +
|
||||
sizeof(double) * FloatRegisters::Total;
|
||||
sizeof(double) * FloatRegisters::Total;
|
||||
masm.addq(Imm32(BailoutDataSize), rsp);
|
||||
masm.pop(rcx);
|
||||
masm.lea(Operand(rsp, rcx, TimesOne, sizeof(void *)), rsp);
|
||||
@ -484,6 +491,31 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
masm.jmp(bailoutTail);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateParallelBailoutThunk(MacroAssembler &masm)
|
||||
{
|
||||
// As GenerateBailoutThunk, except we return an error immediately. We do
|
||||
// the bailout dance so that we can walk the stack and have accurate
|
||||
// reporting of frame information.
|
||||
|
||||
PushBailoutFrame(masm, r8);
|
||||
|
||||
// Parallel bailout is like parallel failure in that we unwind all the way
|
||||
// to the entry frame. Reserve space for the frame pointer of the entry frame.
|
||||
masm.reserveStack(sizeof(uint8_t *));
|
||||
masm.movePtr(rsp, r9);
|
||||
|
||||
masm.setupUnalignedABICall(2, rax);
|
||||
masm.passABIArg(r8);
|
||||
masm.passABIArg(r9);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
|
||||
|
||||
// Get the frame pointer of the entry frame and return.
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.loadPtr(Address(rsp, 0), rsp);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
{
|
||||
@ -491,11 +523,20 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
MacroAssembler masm;
|
||||
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
GenerateParallelBailoutThunk(masm);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
#include "jit/x86/BaselineHelpers-x86.h"
|
||||
|
||||
@ -455,7 +456,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
{
|
||||
// Push registers such that we can access them from [base + code].
|
||||
masm.PushRegsInMask(AllRegs);
|
||||
@ -464,7 +465,13 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
masm.push(Imm32(frameClass));
|
||||
|
||||
// The current stack pointer is the first argument to jit::Bailout.
|
||||
masm.movl(esp, eax);
|
||||
masm.movl(esp, spArg);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
PushBailoutFrame(masm, frameClass, eax);
|
||||
|
||||
// Make space for Bailout's baioutInfo outparam.
|
||||
masm.reserveStack(sizeof(void *));
|
||||
@ -508,6 +515,31 @@ GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
masm.jmp(bailoutTail);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateParallelBailoutThunk(MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
// As GenerateBailoutThunk, except we return an error immediately. We do
|
||||
// the bailout dance so that we can walk the stack and have accurate
|
||||
// reporting of frame information.
|
||||
|
||||
PushBailoutFrame(masm, frameClass, eax);
|
||||
|
||||
// Parallel bailout is like parallel failure in that we unwind all the way
|
||||
// to the entry frame. Reserve space for the frame pointer of the entry frame.
|
||||
masm.reserveStack(sizeof(uint8_t *));
|
||||
masm.movePtr(esp, ebx);
|
||||
|
||||
masm.setupUnalignedABICall(2, ecx);
|
||||
masm.passABIArg(eax);
|
||||
masm.passABIArg(ebx);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, BailoutPar));
|
||||
|
||||
// Get the frame pointer of the entry frame and return.
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.loadPtr(Address(esp, 0), esp);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
{
|
||||
@ -531,11 +563,20 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
{
|
||||
MacroAssembler masm;
|
||||
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
switch (mode) {
|
||||
case SequentialExecution:
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
case ParallelExecution:
|
||||
GenerateParallelBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#if defined(JS_THREADSAFE) && defined(JS_ION)
|
||||
# include "jit/JitCommon.h"
|
||||
# include "jit/RematerializedFrame.h"
|
||||
# ifdef FORKJOIN_SPEW
|
||||
# include "jit/Ion.h"
|
||||
# include "jit/JitCompartment.h"
|
||||
@ -117,26 +118,13 @@ ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::addTrace(JSScript *script,
|
||||
jsbytecode *pc)
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
@ -274,11 +262,6 @@ class ForkJoinOperation
|
||||
static const uint32_t MAX_BAILOUTS = 3;
|
||||
uint32_t bailouts;
|
||||
|
||||
// Information about the bailout:
|
||||
ParallelBailoutCause bailoutCause;
|
||||
RootedScript bailoutScript;
|
||||
jsbytecode *bailoutBytecode;
|
||||
|
||||
ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
|
||||
uint16_t sliceEnd, ForkJoinMode mode, HandleObject updatable);
|
||||
ExecutionStatus apply();
|
||||
@ -335,7 +318,9 @@ class ForkJoinOperation
|
||||
TrafficLight recoverFromBailout(ExecutionStatus *status);
|
||||
TrafficLight fatalError(ExecutionStatus *status);
|
||||
bool isInitialScript(HandleScript script);
|
||||
void determineBailoutCause();
|
||||
void getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
|
||||
ParallelBailoutCause *bailoutCause);
|
||||
bool reportBailoutWarnings();
|
||||
bool invalidateBailedOutScripts();
|
||||
ExecutionStatus sequentialExecution(bool disqualified);
|
||||
|
||||
@ -592,9 +577,6 @@ ForkJoinModeString(ForkJoinMode mode) {
|
||||
ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
|
||||
uint16_t sliceEnd, ForkJoinMode mode, HandleObject updatable)
|
||||
: bailouts(0),
|
||||
bailoutCause(ParallelBailoutNone),
|
||||
bailoutScript(cx),
|
||||
bailoutBytecode(nullptr),
|
||||
cx_(cx),
|
||||
fun_(fun),
|
||||
updatable_(updatable),
|
||||
@ -645,8 +627,10 @@ ForkJoinOperation::apply()
|
||||
if (!bailoutRecords_.resize(numWorkers))
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
|
||||
for (uint32_t i = 0; i < numWorkers; i++)
|
||||
bailoutRecords_[i].init(cx_);
|
||||
for (uint32_t i = 0; i < numWorkers; i++) {
|
||||
if (!bailoutRecords_[i].init(cx_))
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
}
|
||||
|
||||
if (enqueueInitialScript(&status) == RedLight)
|
||||
return SpewEndOp(status);
|
||||
@ -676,7 +660,7 @@ ForkJoinOperation::apply()
|
||||
|
||||
while (bailouts < MAX_BAILOUTS) {
|
||||
for (uint32_t i = 0; i < numWorkers; i++)
|
||||
bailoutRecords_[i].reset(cx_);
|
||||
bailoutRecords_[i].reset();
|
||||
|
||||
if (compileForParallelExecution(&status) == RedLight)
|
||||
return SpewEndOp(status);
|
||||
@ -1103,74 +1087,173 @@ ForkJoinOperation::isInitialScript(HandleScript script)
|
||||
}
|
||||
|
||||
void
|
||||
ForkJoinOperation::determineBailoutCause()
|
||||
ForkJoinOperation::getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
|
||||
ParallelBailoutCause *bailoutCause)
|
||||
{
|
||||
bailoutCause = ParallelBailoutNone;
|
||||
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
|
||||
if (bailoutRecords_[i].cause == ParallelBailoutNone)
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
case ParallelBailoutNone:
|
||||
case ParallelBailoutInterrupt:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
|
||||
continue;
|
||||
|
||||
bailoutCause = bailoutRecords_[i].cause;
|
||||
const char *causeStr = BailoutExplanation(bailoutCause);
|
||||
if (bailoutRecords_[i].depth) {
|
||||
bailoutScript = bailoutRecords_[i].trace[0].script;
|
||||
bailoutBytecode = bailoutRecords_[i].trace[0].bytecode;
|
||||
|
||||
const char *filename = bailoutScript->filename();
|
||||
int line = JS_PCToLineNumber(cx_, bailoutScript, bailoutBytecode);
|
||||
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%d",
|
||||
causeStr, filename, line);
|
||||
|
||||
Spew(SpewBailouts, "Bailout from thread %d: cause %d at loc %s:%d",
|
||||
i,
|
||||
bailoutCause,
|
||||
bailoutScript->filename(),
|
||||
PCToLineNumber(bailoutScript, bailoutBytecode));
|
||||
} else {
|
||||
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s",
|
||||
causeStr);
|
||||
|
||||
Spew(SpewBailouts, "Bailout from thread %d: cause %d, unknown loc",
|
||||
i,
|
||||
bailoutCause);
|
||||
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)
|
||||
{
|
||||
if (val.isMagic()) {
|
||||
switch (val.whyMagic()) {
|
||||
case JS_OPTIMIZED_OUT: return "<optimized out>";
|
||||
default: return "<unknown magic?>";
|
||||
}
|
||||
}
|
||||
|
||||
RootedString str(cx, ToString<CanGC>(cx, val));
|
||||
if (!str)
|
||||
return nullptr;
|
||||
return bytes.encodeUtf8(cx, str);
|
||||
}
|
||||
|
||||
bool
|
||||
ForkJoinOperation::reportBailoutWarnings()
|
||||
{
|
||||
Sprinter sp(cx_);
|
||||
if (SpewEnabled(SpewBailouts)) {
|
||||
sp.init();
|
||||
sp.printf("Bailed out of parallel operation");
|
||||
}
|
||||
|
||||
for (uint32_t threadId = 0; threadId < bailoutRecords_.length(); threadId++) {
|
||||
ParallelBailoutCause cause = bailoutRecords_[threadId].cause;
|
||||
if (cause == ParallelBailoutNone)
|
||||
continue;
|
||||
|
||||
if (bailoutRecords_[threadId].hasFrames()) {
|
||||
Vector<RematerializedFrame *> &frames = bailoutRecords_[threadId].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(),
|
||||
PCToLineNumber(bailoutScript, frame->pc()));
|
||||
return true;
|
||||
}
|
||||
|
||||
sp.printf("\n in thread %d due to %s", threadId, BailoutExplanation(cause));
|
||||
|
||||
for (uint32_t frameIndex = 0; frameIndex < frames.length(); frameIndex++) {
|
||||
RematerializedFrame *frame = frames[frameIndex];
|
||||
RootedScript script(cx_, frame->script());
|
||||
|
||||
// Format the frame's location.
|
||||
sp.printf("\n at ");
|
||||
if (JSFunction *fun = frame->maybeFun()) {
|
||||
if (fun->displayAtom()) {
|
||||
JSAutoByteString displayBytes;
|
||||
RootedString displayAtom(cx_, fun->displayAtom());
|
||||
const char *displayChars = displayBytes.encodeUtf8(cx_, displayAtom);
|
||||
if (!displayChars)
|
||||
return false;
|
||||
sp.printf("%s", displayChars);
|
||||
} else {
|
||||
sp.printf("<anonymous>");
|
||||
}
|
||||
} else {
|
||||
sp.printf("<global>");
|
||||
}
|
||||
sp.printf(" (%s:%u)", script->filename(), PCToLineNumber(script, frame->pc()));
|
||||
|
||||
// Format bindings.
|
||||
BindingVector bindings(cx_);
|
||||
if (!FillBindingVector(script, &bindings))
|
||||
return false;
|
||||
|
||||
unsigned scopeSlot = 0;
|
||||
for (unsigned i = 0; i < bindings.length(); i++) {
|
||||
JSAutoByteString nameBytes;
|
||||
const char *nameChars = nullptr;
|
||||
RootedPropertyName bindingName(cx_, bindings[i].name());
|
||||
nameChars = nameBytes.encodeUtf8(cx_, bindingName);
|
||||
if (!nameChars)
|
||||
return false;
|
||||
|
||||
RootedValue arg(cx_);
|
||||
if (bindings[i].aliased()) {
|
||||
arg = frame->callObj().getSlot(scopeSlot);
|
||||
scopeSlot++;
|
||||
} else if (i < frame->numFormalArgs()) {
|
||||
if (script->argsObjAliasesFormals() && frame->hasArgsObj())
|
||||
arg = frame->argsObj().arg(i);
|
||||
else
|
||||
arg = frame->unaliasedActual(i, DONT_CHECK_ALIASING);
|
||||
} else {
|
||||
arg = frame->unaliasedLocal(i - frame->numFormalArgs(), DONT_CHECK_ALIASING);
|
||||
}
|
||||
|
||||
JSAutoByteString valueBytes;
|
||||
const char *valueChars = ValueToChar(cx_, arg, valueBytes);
|
||||
if (!valueChars)
|
||||
return false;
|
||||
|
||||
sp.printf("\n %s %s = %s",
|
||||
bindings[i].kind() == Binding::ARGUMENT ? "arg" : "var",
|
||||
nameChars, valueChars);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SpewEnabled(SpewBailouts))
|
||||
JS_ReportWarning(cx_, "%s", sp.string());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ForkJoinOperation::invalidateBailedOutScripts()
|
||||
{
|
||||
Vector<types::RecompileInfo> invalid(cx_);
|
||||
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
|
||||
RootedScript script(cx_, bailoutRecords_[i].topScript);
|
||||
|
||||
// No script to invalidate.
|
||||
if (!script || !script->hasParallelIonScript())
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
// No bailout.
|
||||
case ParallelBailoutNone:
|
||||
continue;
|
||||
|
||||
Spew(SpewBailouts,
|
||||
"Bailout from thread %d: cause %d, topScript %p:%s:%d",
|
||||
i,
|
||||
bailoutRecords_[i].cause,
|
||||
script.get(), script->filename(), script->lineno());
|
||||
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
// An interrupt is not the fault of the script, so don't
|
||||
// invalidate it.
|
||||
case ParallelBailoutInterrupt: continue;
|
||||
case ParallelBailoutInterrupt:
|
||||
continue;
|
||||
|
||||
// An illegal write will not be made legal by invalidation.
|
||||
case ParallelBailoutIllegalWrite: continue;
|
||||
case ParallelBailoutIllegalWrite:
|
||||
continue;
|
||||
|
||||
// For other cases, consider invalidation.
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bailoutRecords_[i].hasFrames())
|
||||
continue;
|
||||
|
||||
// Get the script of the youngest frame.
|
||||
RootedScript script(cx_, bailoutRecords_[i].frames()[0]->script());
|
||||
|
||||
// Already invalidated.
|
||||
if (hasScript(invalid, script))
|
||||
if (!script->hasParallelIonScript() || hasScript(invalid, script))
|
||||
continue;
|
||||
|
||||
Spew(SpewBailouts, "Invalidating script %p:%s:%d due to cause %d",
|
||||
@ -1182,7 +1265,7 @@ ForkJoinOperation::invalidateBailedOutScripts()
|
||||
if (!invalid.append(co))
|
||||
return false;
|
||||
|
||||
// any script that we have marked for invalidation will need
|
||||
// Any script that we have marked for invalidation will need
|
||||
// to be recompiled
|
||||
if (!addToWorklist(script))
|
||||
return false;
|
||||
@ -1281,9 +1364,9 @@ ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
|
||||
// RedLight: fatal error
|
||||
|
||||
bailouts += 1;
|
||||
determineBailoutCause();
|
||||
|
||||
SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
|
||||
if (!reportBailoutWarnings())
|
||||
return fatalError(status);
|
||||
|
||||
// After any bailout, we always scan over callee list of main
|
||||
// function, if nothing else
|
||||
@ -1347,8 +1430,7 @@ class ParallelIonInvoke
|
||||
|
||||
bool invoke(ForkJoinContext *cx) {
|
||||
JitActivation activation(cx);
|
||||
// In-out parameter: on input it denotes the number of values to preserve after the call.
|
||||
Value result = Int32Value(0);
|
||||
Value result = Int32Value(argc_);
|
||||
CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
|
||||
nullptr, 0, &result);
|
||||
return !result.isMagic();
|
||||
@ -1566,7 +1648,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
||||
CompileCompartment::get(cx_->compartment()),
|
||||
nullptr);
|
||||
|
||||
JS_ASSERT(cx.bailoutRecord->topScript == nullptr);
|
||||
JS_ASSERT(!cx.bailoutRecord->bailedOut());
|
||||
|
||||
if (!fun_->nonLazyScript()->hasParallelIonScript()) {
|
||||
// Sometimes, particularly with GCZeal, the parallel ion
|
||||
@ -1574,7 +1656,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
||||
// op and reaching this point. In that case, we just fail
|
||||
// and fallback.
|
||||
Spew(SpewOps, "Down (Script no longer present)");
|
||||
cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
|
||||
cx.bailoutRecord->joinCause(ParallelBailoutMainScriptNotPresent);
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
} else {
|
||||
ParallelIonInvoke<3> fii(runtime(), fun_, 3);
|
||||
@ -1584,7 +1666,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
||||
fii.args[2] = Int32Value(sliceEnd_);
|
||||
|
||||
bool ok = fii.invoke(&cx);
|
||||
JS_ASSERT(ok == !cx.bailoutRecord->topScript);
|
||||
JS_ASSERT(ok == !cx.bailoutRecord->bailedOut());
|
||||
if (!ok) {
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
#ifdef JSGC_FJGENERATIONAL
|
||||
@ -1630,7 +1712,7 @@ ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
|
||||
JS_ASSERT(!cx_->runtime()->gc.isNeeded);
|
||||
|
||||
if (!abort_) {
|
||||
cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
|
||||
cx.bailoutRecord->joinCause(ParallelBailoutInterrupt);
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
}
|
||||
@ -1804,7 +1886,7 @@ void
|
||||
ForkJoinContext::requestGC(JS::gcreason::Reason reason)
|
||||
{
|
||||
shared_->requestGC(reason);
|
||||
bailoutRecord->setCause(ParallelBailoutRequestedGC);
|
||||
bailoutRecord->joinCause(ParallelBailoutRequestedGC);
|
||||
shared_->setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
|
||||
@ -1812,7 +1894,7 @@ void
|
||||
ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
|
||||
{
|
||||
shared_->requestZoneGC(zone, reason);
|
||||
bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
|
||||
bailoutRecord->joinCause(ParallelBailoutRequestedZoneGC);
|
||||
shared_->setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
|
||||
@ -1820,75 +1902,84 @@ bool
|
||||
ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
|
||||
{
|
||||
shared_->setPendingAbortFatal();
|
||||
bailoutRecord->setCause(cause);
|
||||
bailoutRecord->joinCause(cause);
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ParallelBailoutRecord
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::init(JSContext *cx)
|
||||
ParallelBailoutRecord::~ParallelBailoutRecord()
|
||||
{
|
||||
reset(cx);
|
||||
reset();
|
||||
js_delete(frames_);
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelBailoutRecord::init(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(!frames_);
|
||||
frames_ = cx->new_<Vector<RematerializedFrame *> >(cx);
|
||||
return !!frames_;
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::reset(JSContext *cx)
|
||||
ParallelBailoutRecord::reset()
|
||||
{
|
||||
topScript = nullptr;
|
||||
RematerializedFrame::FreeInVector(frames());
|
||||
cause = ParallelBailoutNone;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
template <class T>
|
||||
static void
|
||||
RematerializeFramesWithIter(ForkJoinContext *cx, T &frameIter,
|
||||
Vector<RematerializedFrame *> &frames)
|
||||
{
|
||||
this->cause = cause;
|
||||
updateCause(cause, outermostScript, currentScript, currentPc);
|
||||
}
|
||||
// This function as well as |rematerializeFrames| methods below are
|
||||
// infallible. These are only called when we are already erroring out. If
|
||||
// we OOM here, free what we've allocated and return. Error reporting is
|
||||
// then unable to give the user detailed stack information.
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
{
|
||||
JS_ASSERT_IF(outermostScript, currentScript);
|
||||
JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
|
||||
JS_ASSERT_IF(currentScript, outermostScript);
|
||||
JS_ASSERT_IF(!currentScript, !currentPc);
|
||||
MOZ_ASSERT(frames.empty());
|
||||
|
||||
if (this->cause == ParallelBailoutNone)
|
||||
this->cause = cause;
|
||||
for (; !frameIter.done(); ++frameIter) {
|
||||
if (!frameIter.isIonJS())
|
||||
continue;
|
||||
|
||||
if (outermostScript)
|
||||
this->topScript = outermostScript;
|
||||
InlineFrameIterator inlineIter(cx, &frameIter);
|
||||
Vector<RematerializedFrame *> inlineFrames(cx);
|
||||
|
||||
if (currentScript)
|
||||
addTrace(currentScript, currentPc);
|
||||
}
|
||||
if (!RematerializedFrame::RematerializeInlineFrames(cx, frameIter.fp(),
|
||||
inlineIter, inlineFrames))
|
||||
{
|
||||
RematerializedFrame::FreeInVector(inlineFrames);
|
||||
RematerializedFrame::FreeInVector(frames);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::addTrace(JSScript *script,
|
||||
jsbytecode *pc)
|
||||
{
|
||||
// Ideally, this should never occur, because we should always have
|
||||
// a script when we invoke setCause, but I havent' fully
|
||||
// refactored things to that point yet:
|
||||
if (topScript == nullptr && script != nullptr)
|
||||
topScript = script;
|
||||
|
||||
if (depth < MaxDepth) {
|
||||
trace[depth].script = script;
|
||||
trace[depth].bytecode = pc;
|
||||
depth += 1;
|
||||
// Reverse the inline frames into the main vector.
|
||||
while (!inlineFrames.empty()) {
|
||||
if (!frames.append(inlineFrames.popCopy())) {
|
||||
RematerializedFrame::FreeInVector(inlineFrames);
|
||||
RematerializedFrame::FreeInVector(frames);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
|
||||
{
|
||||
RematerializeFramesWithIter(cx, frameIter, frames());
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
|
||||
{
|
||||
RematerializeFramesWithIter(cx, frameIter, frames());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
@ -2160,21 +2251,6 @@ class ParallelSpewer
|
||||
spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
|
||||
script->filename(), PCToLineNumber(script, mir->trackedPc()));
|
||||
}
|
||||
|
||||
void spewBailoutIR(IonLIRTraceData *data) {
|
||||
if (!active[SpewBailouts])
|
||||
return;
|
||||
|
||||
// If we didn't bail from a LIR/MIR but from a propagated parallel
|
||||
// bailout, don't bother printing anything since we've printed it
|
||||
// elsewhere.
|
||||
if (data->mirOpName && data->script) {
|
||||
spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
|
||||
data->lirOpName, cyan(), data->mirOpName, reset(),
|
||||
data->blockIndex, data->lirIndex, data->script->filename(),
|
||||
PCToLineNumber(data->script, data->pc));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Singleton instance of the spewer.
|
||||
@ -2243,12 +2319,6 @@ parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
parallel::SpewBailoutIR(IonLIRTraceData *data)
|
||||
{
|
||||
spewer.spewBailoutIR(data);
|
||||
}
|
||||
|
||||
#endif // FORKJOIN_SPEW
|
||||
|
||||
bool
|
||||
|
@ -272,21 +272,29 @@ class ForkJoinContext;
|
||||
|
||||
bool ForkJoin(JSContext *cx, CallArgs &args);
|
||||
|
||||
struct IonLIRTraceData {
|
||||
uint32_t blockIndex;
|
||||
uint32_t lirIndex;
|
||||
uint32_t execModeInt;
|
||||
const char *lirOpName;
|
||||
const char *mirOpName;
|
||||
JSScript *script;
|
||||
jsbytecode *pc;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Bailout tracking
|
||||
|
||||
//
|
||||
// The lattice of causes goes:
|
||||
//
|
||||
// { everything else }
|
||||
// |
|
||||
// Interrupt
|
||||
// / \
|
||||
// Unsupported UnsupportedVM
|
||||
// \ /
|
||||
// None
|
||||
//
|
||||
enum ParallelBailoutCause {
|
||||
ParallelBailoutNone,
|
||||
ParallelBailoutNone = 0,
|
||||
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedVM,
|
||||
|
||||
// The periodic interrupt failed, which can mean that either
|
||||
// another thread canceled, the user interrupted us, etc
|
||||
ParallelBailoutInterrupt,
|
||||
|
||||
// Compiler returned Method_Skipped
|
||||
ParallelBailoutCompilationSkipped,
|
||||
@ -294,9 +302,9 @@ enum ParallelBailoutCause {
|
||||
// Compiler returned Method_CantCompile
|
||||
ParallelBailoutCompilationFailure,
|
||||
|
||||
// The periodic interrupt failed, which can mean that either
|
||||
// another thread canceled, the user interrupted us, etc
|
||||
ParallelBailoutInterrupt,
|
||||
// Propagating a failure, i.e., another thread requested the computation
|
||||
// be aborted.
|
||||
ParallelBailoutPropagate,
|
||||
|
||||
// An IC update failed
|
||||
ParallelBailoutFailedIC,
|
||||
@ -310,41 +318,49 @@ enum ParallelBailoutCause {
|
||||
ParallelBailoutAccessToIntrinsic,
|
||||
ParallelBailoutOverRecursed,
|
||||
ParallelBailoutOutOfMemory,
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedVM,
|
||||
ParallelBailoutUnsupportedStringComparison,
|
||||
ParallelBailoutRequestedGC,
|
||||
ParallelBailoutRequestedZoneGC,
|
||||
ParallelBailoutRequestedZoneGC
|
||||
};
|
||||
|
||||
struct ParallelBailoutTrace {
|
||||
JSScript *script;
|
||||
jsbytecode *bytecode;
|
||||
};
|
||||
namespace jit {
|
||||
class BailoutStack;
|
||||
class JitFrameIterator;
|
||||
class RematerializedFrame;
|
||||
}
|
||||
|
||||
// See "Bailouts" section in comment above.
|
||||
struct ParallelBailoutRecord {
|
||||
JSScript *topScript;
|
||||
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_;
|
||||
ParallelBailoutCause cause;
|
||||
|
||||
// Eventually we will support deeper traces,
|
||||
// but for now we gather at most a single frame.
|
||||
static const uint32_t MaxDepth = 1;
|
||||
uint32_t depth;
|
||||
ParallelBailoutTrace trace[MaxDepth];
|
||||
ParallelBailoutRecord()
|
||||
: frames_(nullptr),
|
||||
cause(ParallelBailoutNone)
|
||||
{ }
|
||||
|
||||
void init(JSContext *cx);
|
||||
void reset(JSContext *cx);
|
||||
void setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript = nullptr, // inliner (if applicable)
|
||||
JSScript *currentScript = nullptr, // inlinee (if applicable)
|
||||
jsbytecode *currentPc = nullptr);
|
||||
void updateCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc);
|
||||
void addTrace(JSScript *script,
|
||||
jsbytecode *pc);
|
||||
~ParallelBailoutRecord();
|
||||
|
||||
bool init(JSContext *cx);
|
||||
void reset();
|
||||
|
||||
Vector<jit::RematerializedFrame *> &frames() { MOZ_ASSERT(frames_); return *frames_; }
|
||||
bool hasFrames() const { return frames_ && !frames_->empty(); }
|
||||
bool bailedOut() const { return cause != ParallelBailoutNone; }
|
||||
|
||||
void joinCause(ParallelBailoutCause cause) {
|
||||
if (this->cause <= ParallelBailoutInterrupt &&
|
||||
(cause > ParallelBailoutInterrupt || cause > this->cause))
|
||||
{
|
||||
this->cause = cause;
|
||||
}
|
||||
}
|
||||
|
||||
void rematerializeFrames(ForkJoinContext *cx, jit::JitFrameIterator &frameIter);
|
||||
void rematerializeFrames(ForkJoinContext *cx, jit::IonBailoutIterator &frameIter);
|
||||
};
|
||||
|
||||
class ForkJoinShared;
|
||||
@ -356,9 +372,6 @@ class ForkJoinContext : public ThreadSafeContext
|
||||
ParallelBailoutRecord *const bailoutRecord;
|
||||
|
||||
#ifdef FORKJOIN_SPEW
|
||||
// Records the last instr. to execute on this thread.
|
||||
IonLIRTraceData traceData;
|
||||
|
||||
// The maximum worker id.
|
||||
uint32_t maxWorkerId;
|
||||
#endif
|
||||
@ -579,7 +592,6 @@ ExecutionStatus SpewEndOp(ExecutionStatus status);
|
||||
void SpewBeginCompile(HandleScript script);
|
||||
jit::MethodStatus SpewEndCompile(jit::MethodStatus status);
|
||||
void SpewMIR(jit::MDefinition *mir, const char *fmt, ...);
|
||||
void SpewBailoutIR(IonLIRTraceData *data);
|
||||
|
||||
#else
|
||||
|
||||
@ -595,7 +607,6 @@ static inline void SpewBeginCompile(HandleScript script) { }
|
||||
static inline jit::MethodStatus SpewEndCompile(jit::MethodStatus status) { return status; }
|
||||
static inline void SpewMIR(jit::MDefinition *mir, const char *fmt, ...) { }
|
||||
#endif
|
||||
static inline void SpewBailoutIR(IonLIRTraceData *data) { }
|
||||
|
||||
#endif // FORKJOIN_SPEW && JS_THREADSAFE && JS_ION
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user