mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 12:25:53 +00:00
Backed out 3 changesets (bug 1019304) for spidermonkey test failures on a CLOSED TREE
Backed out changeset adc7e2d717a9 (bug 1019304) Backed out changeset 5322e6721141 (bug 1019304) Backed out changeset 45f24290b96e (bug 1019304)
This commit is contained in:
parent
7d39d62d88
commit
5b2491dc29
@ -15,7 +15,6 @@
|
||||
|
||||
#include "gc/Heap.h"
|
||||
#include "jit/IonFrames.h"
|
||||
#include "jit/RematerializedFrame.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
@ -391,7 +390,6 @@ ForkJoinNursery::forwardFromRoots(ForkJoinNurseryCollectionTracer *trc)
|
||||
forwardFromUpdatable(trc);
|
||||
forwardFromStack(trc);
|
||||
forwardFromTenured(trc);
|
||||
forwardFromRematerializedFrames(trc);
|
||||
}
|
||||
|
||||
void
|
||||
@ -446,13 +444,6 @@ ForkJoinNursery::forwardFromTenured(ForkJoinNurseryCollectionTracer *trc)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ForkJoinNursery::forwardFromRematerializedFrames(ForkJoinNurseryCollectionTracer *trc)
|
||||
{
|
||||
if (cx_->bailoutRecord->hasFrames())
|
||||
jit::RematerializedFrame::MarkInVector(trc, cx_->bailoutRecord->frames());
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ForkJoinNursery::forwardBufferPointer(JSTracer *trc, HeapSlot **pSlotsElems)
|
||||
{
|
||||
|
@ -236,7 +236,6 @@ class ForkJoinNursery
|
||||
void forwardFromUpdatable(ForkJoinNurseryCollectionTracer *trc);
|
||||
void forwardFromStack(ForkJoinNurseryCollectionTracer *trc);
|
||||
void forwardFromTenured(ForkJoinNurseryCollectionTracer *trc);
|
||||
void forwardFromRematerializedFrames(ForkJoinNurseryCollectionTracer *trc);
|
||||
void collectToFixedPoint(ForkJoinNurseryCollectionTracer *trc);
|
||||
void freeFromspace();
|
||||
void computeNurserySizeAfterGC(size_t live, const char **msg);
|
||||
|
@ -69,6 +69,10 @@ 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,10 +100,6 @@ 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,10 +1229,7 @@ class OutOfLineInterruptCheckImplicit : public OutOfLineCodeBase<CodeGenerator>
|
||||
};
|
||||
|
||||
typedef bool (*InterruptCheckFn)(JSContext *);
|
||||
typedef bool (*InterruptCheckParFn)(ForkJoinContext *);
|
||||
static const VMFunctionsModal InterruptCheckInfo = VMFunctionsModal(
|
||||
FunctionInfo<InterruptCheckFn>(InterruptCheck),
|
||||
FunctionInfo<InterruptCheckParFn>(InterruptCheckPar));
|
||||
static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
|
||||
@ -1852,7 +1849,13 @@ CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
|
||||
masm.passABIArg(ToRegister(lir->object()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
|
||||
|
||||
return bailoutIfFalseBool(ReturnReg, lir->snapshot());
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2380,23 +2383,27 @@ 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)
|
||||
{
|
||||
pushArg(calleeReg);
|
||||
if (!callVM(CallToUncompiledScriptParInfo, lir))
|
||||
OutOfLineCode *bail = oolAbortPar(ParallelBailoutCalledToUncompiledScript, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.assumeUnreachable("CallToUncompiledScriptParInfo always returns 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());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2471,6 +2478,9 @@ 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()) {
|
||||
@ -2484,6 +2494,22 @@ 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)
|
||||
{
|
||||
@ -2896,10 +2922,10 @@ CodeGenerator::generateArgumentsChecks(bool bailout)
|
||||
// Out-of-line path to report over-recursed error and fail.
|
||||
class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LInstruction *lir_;
|
||||
LCheckOverRecursed *lir_;
|
||||
|
||||
public:
|
||||
explicit CheckOverRecursedFailure(LInstruction *lir)
|
||||
explicit CheckOverRecursedFailure(LCheckOverRecursed *lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
@ -2907,7 +2933,7 @@ class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
|
||||
return codegen->visitCheckOverRecursedFailure(this);
|
||||
}
|
||||
|
||||
LInstruction *lir() const {
|
||||
LCheckOverRecursed *lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
@ -2977,11 +3003,9 @@ CodeGenerator::visitDefFun(LDefFun *lir)
|
||||
return callVM(DefFunOperationInfo, lir);
|
||||
}
|
||||
|
||||
typedef bool (*CheckOverRecursedFn)(JSContext *);
|
||||
typedef bool (*CheckOverRecursedParFn)(ForkJoinContext *);
|
||||
static const VMFunctionsModal CheckOverRecursedInfo = VMFunctionsModal(
|
||||
FunctionInfo<CheckOverRecursedFn>(CheckOverRecursed),
|
||||
FunctionInfo<CheckOverRecursedParFn>(CheckOverRecursedPar));
|
||||
typedef bool (*ReportOverRecursedFn)(JSContext *);
|
||||
static const VMFunction CheckOverRecursedInfo =
|
||||
FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
|
||||
@ -3002,6 +3026,25 @@ 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)
|
||||
{
|
||||
@ -3019,10 +3062,9 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
|
||||
masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
|
||||
|
||||
// Conditional forward (unlikely) branch to failure.
|
||||
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
|
||||
CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
|
||||
masm.checkInterruptFlagPar(tempReg, ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
@ -3030,12 +3072,55 @@ 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_.
|
||||
OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
|
||||
if (!ool)
|
||||
OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
|
||||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
Register tempReg = ToRegister(lir->getTempReg());
|
||||
@ -3044,6 +3129,34 @@ 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()
|
||||
{
|
||||
@ -3346,6 +3459,9 @@ CodeGenerator::generateBody()
|
||||
resetOsiPointRegs(iter->safepoint());
|
||||
#endif
|
||||
|
||||
if (!callTraceLIR(i, *iter))
|
||||
return false;
|
||||
|
||||
if (!iter->accept(this))
|
||||
return false;
|
||||
|
||||
@ -3794,6 +3910,12 @@ 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;
|
||||
}
|
||||
|
||||
@ -3901,10 +4023,25 @@ CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
|
||||
masm.storeCallResult(out);
|
||||
restoreVolatile(out);
|
||||
|
||||
return bailoutTestPtr(Assembler::Zero, out, out, ool->lir->snapshot());
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
#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 =
|
||||
@ -8415,6 +8552,29 @@ 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)
|
||||
{
|
||||
@ -8458,6 +8618,22 @@ 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,6 +152,7 @@ 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);
|
||||
@ -300,6 +301,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
|
||||
|
||||
bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
|
||||
bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool);
|
||||
|
||||
bool visitInterruptCheckPar(LInterruptCheckPar *lir);
|
||||
bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
|
||||
@ -312,6 +314,8 @@ 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);
|
||||
|
||||
@ -370,6 +374,7 @@ 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,17 +226,10 @@ JitRuntime::initialize(JSContext *cx)
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_Codegen, "# Emitting bailout handler");
|
||||
bailoutHandler_ = generateBailoutHandler(cx, SequentialExecution);
|
||||
bailoutHandler_ = generateBailoutHandler(cx);
|
||||
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,18 +755,28 @@ 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");
|
||||
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
JitFrameIterator frameIter(cx);
|
||||
while (!iter.isEntry()) {
|
||||
if (iter.isScripted()) {
|
||||
cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
|
||||
iter.script(), iter.script(), nullptr);
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutUnsupportedVM);
|
||||
cx->bailoutRecord->rematerializeFrames(cx, frameIter);
|
||||
while (!iter.isEntry()) {
|
||||
if (iter.isScripted())
|
||||
PropagateAbortPar(iter.script(), iter.script());
|
||||
++iter;
|
||||
}
|
||||
|
||||
rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
|
||||
|
||||
MOZ_ASSERT(frameIter.done());
|
||||
rfe->stackPointer = frameIter.fp();
|
||||
rfe->stackPointer = iter.fp();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -249,6 +249,7 @@ 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"
|
||||
@ -301,6 +302,8 @@ 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,6 +54,8 @@ namespace jit {
|
||||
_(Safepoints) \
|
||||
/* Debug info about Pools*/ \
|
||||
_(Pools) \
|
||||
/* Calls to js::jit::Trace() */ \
|
||||
_(Trace) \
|
||||
/* Debug info about the I$ */ \
|
||||
_(CacheFlush) \
|
||||
\
|
||||
|
@ -171,9 +171,6 @@ 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_;
|
||||
@ -240,7 +237,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, ExecutionMode mode);
|
||||
JitCode *generateBailoutHandler(JSContext *cx);
|
||||
JitCode *generateInvalidator(JSContext *cx);
|
||||
JitCode *generatePreBarrier(JSContext *cx, MIRType type);
|
||||
JitCode *generateMallocStub(JSContext *cx);
|
||||
@ -307,12 +304,8 @@ class JitRuntime
|
||||
JitCode *getBaselineDebugModeOSRHandler(JSContext *cx);
|
||||
void *getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg);
|
||||
|
||||
JitCode *getGenericBailoutHandler(ExecutionMode mode) const {
|
||||
switch (mode) {
|
||||
case SequentialExecution: return bailoutHandler_;
|
||||
case ParallelExecution: return parallelBailoutHandler_;
|
||||
default: MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
}
|
||||
JitCode *getGenericBailoutHandler() const {
|
||||
return bailoutHandler_;
|
||||
}
|
||||
|
||||
JitCode *getExceptionTail() const {
|
||||
|
@ -571,6 +571,12 @@ 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,6 +34,7 @@
|
||||
_(NewDenseArrayPar) \
|
||||
_(NewCallObjectPar) \
|
||||
_(NewDerivedTypedObject) \
|
||||
_(AbortPar) \
|
||||
_(InitElem) \
|
||||
_(InitElemGetterSetter) \
|
||||
_(MutateProto) \
|
||||
|
@ -118,7 +118,13 @@ bool
|
||||
LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins)
|
||||
{
|
||||
LCheckOverRecursed *lir = new(alloc()) LCheckOverRecursed();
|
||||
return add(lir, ins) && assignSafepoint(lir, ins);
|
||||
|
||||
if (!add(lir, ins))
|
||||
return false;
|
||||
if (!assignSafepoint(lir, ins))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -231,6 +237,13 @@ 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)
|
||||
{
|
||||
@ -2166,7 +2179,7 @@ LIRGenerator::visitGuardThreadExclusive(MGuardThreadExclusive *ins)
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
lir->setMir(ins);
|
||||
return assignSnapshot(lir, Bailout_GuardThreadExclusive) && add(lir, ins);
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -77,6 +77,7 @@ 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,6 +1821,24 @@ 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>,
|
||||
@ -2274,11 +2292,10 @@ class MApplyArgs
|
||||
}
|
||||
};
|
||||
|
||||
class MBail : public MAryControlInstruction<0, 0>
|
||||
class MBail : public MNullaryInstruction
|
||||
{
|
||||
protected:
|
||||
MBail(BailoutKind kind)
|
||||
: MAryControlInstruction<0, 0>()
|
||||
{
|
||||
bailoutKind_ = kind;
|
||||
setGuard();
|
||||
@ -5236,6 +5253,7 @@ class MInterruptCheckPar : public MUnaryInstruction
|
||||
{
|
||||
setResultType(MIRType_None);
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
@ -5248,9 +5266,6 @@ 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,15 +748,7 @@ MBasicBlock::discardDefAt(MDefinitionIterator &old)
|
||||
void
|
||||
MBasicBlock::discardAllInstructions()
|
||||
{
|
||||
MInstructionIterator iter = begin();
|
||||
discardAllInstructionsStartingAt(iter);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator &iter)
|
||||
{
|
||||
while (iter != end()) {
|
||||
for (MInstructionIterator iter = begin(); iter != end(); ) {
|
||||
for (size_t i = 0, e = iter->numOperands(); i < e; i++)
|
||||
iter->discardOperand(i);
|
||||
iter = instructions_.removeAt(iter);
|
||||
|
@ -232,7 +232,6 @@ 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,6 +217,7 @@ namespace jit {
|
||||
_(NewPar) \
|
||||
_(NewDenseArrayPar) \
|
||||
_(NewDerivedTypedObject) \
|
||||
_(AbortPar) \
|
||||
_(LambdaPar) \
|
||||
_(RestPar) \
|
||||
_(ForkJoinContext) \
|
||||
|
@ -9,7 +9,6 @@
|
||||
#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"
|
||||
@ -23,6 +22,7 @@ using JS::AutoCheckCannotGC;
|
||||
using parallel::Spew;
|
||||
using parallel::SpewOps;
|
||||
using parallel::SpewBailouts;
|
||||
using parallel::SpewBailoutIR;
|
||||
|
||||
// Load the current thread context.
|
||||
ForkJoinContext *
|
||||
@ -125,6 +125,63 @@ 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)
|
||||
{
|
||||
@ -142,7 +199,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
||||
|
||||
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
|
||||
if (Simulator::Current()->overRecursed()) {
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
||||
cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@ -154,7 +211,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
|
||||
realStackLimit = cx->perThreadData->jitStackLimit;
|
||||
|
||||
if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
|
||||
cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -167,7 +224,11 @@ jit::InterruptCheckPar(ForkJoinContext *cx)
|
||||
JS_ASSERT(ForkJoinContext::current() == cx);
|
||||
bool result = cx->check();
|
||||
if (!result) {
|
||||
cx->bailoutRecord->joinCause(ParallelBailoutInterrupt);
|
||||
// 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.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -178,10 +239,8 @@ jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
|
||||
{
|
||||
JSObject::EnsureDenseResult res =
|
||||
array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
|
||||
if (res != JSObject::ED_OK) {
|
||||
fprintf(stderr, "==== NGNG\n");
|
||||
if (res != JSObject::ED_OK)
|
||||
return nullptr;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -514,36 +573,58 @@ jit::UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs,
|
||||
}
|
||||
|
||||
void
|
||||
jit::BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer)
|
||||
jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
|
||||
jsbytecode *bytecode)
|
||||
{
|
||||
parallel::Spew(parallel::SpewBailouts, "Bailing");
|
||||
// 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());
|
||||
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
|
||||
// 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();
|
||||
JS_ASSERT(cx->bailoutRecord->depth == 0);
|
||||
cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
|
||||
}
|
||||
|
||||
bool
|
||||
jit::CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj)
|
||||
void
|
||||
jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
|
||||
{
|
||||
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 false;
|
||||
return;
|
||||
}
|
||||
|
||||
JSFunction *func = &obj->as<JSFunction>();
|
||||
@ -575,8 +656,6 @@ jit::CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj)
|
||||
Spew(SpewBailouts, "Call to native function");
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
|
@ -72,8 +72,13 @@ JSObject *InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest
|
||||
HandleObject templateObj, HandleObject res);
|
||||
|
||||
// Abort and debug tracing functions.
|
||||
void BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer);
|
||||
bool CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj);
|
||||
void AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
|
||||
jsbytecode *bytecode);
|
||||
void PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript);
|
||||
|
||||
void TraceLIR(IonLIRTraceData *current);
|
||||
|
||||
void CallToUncompiledScriptPar(JSObject *obj);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -75,7 +75,6 @@ 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);
|
||||
|
||||
@ -107,7 +106,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
return cx_;
|
||||
}
|
||||
|
||||
bool convertToBailout(MInstructionIterator &iter);
|
||||
bool convertToBailout(MBasicBlock *block, MInstruction *ins);
|
||||
|
||||
// I am taking the policy of blacklisting everything that's not
|
||||
// obviously safe for now. We can loosen as we need.
|
||||
@ -282,6 +281,7 @@ 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,16 +329,6 @@ 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()
|
||||
{
|
||||
@ -364,20 +354,21 @@ ParallelSafetyAnalysis::analyze()
|
||||
// if we encounter an inherently unsafe operation, in
|
||||
// which case we will transform this block into a bailout
|
||||
// block.
|
||||
MInstruction *ins = nullptr;
|
||||
MInstructionIterator iter(block->begin());
|
||||
while (iter != block->end() && !visitor.unsafe()) {
|
||||
MInstruction *instr = nullptr;
|
||||
for (MInstructionIterator ins(block->begin());
|
||||
ins != block->end() && !visitor.unsafe();)
|
||||
{
|
||||
if (mir_->shouldCancel("ParallelSafetyAnalysis"))
|
||||
return false;
|
||||
|
||||
// We may be removing or replacing the current
|
||||
// instruction, so advance `iter` now. Remember the
|
||||
// instruction, so advance `ins` now. Remember the
|
||||
// last instr. we looked at for use later if it should
|
||||
// prove unsafe.
|
||||
ins = *iter++;
|
||||
instr = *ins++;
|
||||
|
||||
if (!ins->accept(&visitor)) {
|
||||
SpewMIR(ins, "Unaccepted");
|
||||
if (!instr->accept(&visitor)) {
|
||||
SpewMIR(instr, "Unaccepted");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -399,11 +390,8 @@ ParallelSafetyAnalysis::analyze()
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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))
|
||||
// Otherwise, create a replacement that will.
|
||||
if (!visitor.convertToBailout(*block, instr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -418,30 +406,79 @@ ParallelSafetyAnalysis::analyze()
|
||||
IonSpewPass("UCEAfterParallelSafetyAnalysis");
|
||||
AssertExtendedGraphCoherency(graph_);
|
||||
|
||||
if (!removeResumePointOperands())
|
||||
return false;
|
||||
IonSpewPass("RemoveResumePointOperands");
|
||||
AssertExtendedGraphCoherency(graph_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelSafetyVisitor::convertToBailout(MInstructionIterator &iter)
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
|
||||
clearUnsafe();
|
||||
|
||||
// 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++)
|
||||
// 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);
|
||||
block->discardAllInstructionsStartingAt(iter);
|
||||
|
||||
// End the block in the bail.
|
||||
MBail *bailout = MBail::New(graph_.alloc());
|
||||
TransplantResumePoint(ins, bailout);
|
||||
block->end(bailout);
|
||||
clearUnsafe();
|
||||
block->discardAllPhis();
|
||||
block->discardAllInstructions();
|
||||
block->end(MAbortPar::New(graph_.alloc()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -466,8 +503,8 @@ ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
|
||||
SpewMIR(ins, "call with dynamic slots");
|
||||
return markUnsafe();
|
||||
}
|
||||
|
||||
return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -477,8 +514,8 @@ ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
|
||||
SpewMIR(ins, "call with dynamic slots");
|
||||
return markUnsafe();
|
||||
}
|
||||
|
||||
return replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -490,7 +527,8 @@ ParallelSafetyVisitor::visitLambda(MLambda *ins)
|
||||
}
|
||||
|
||||
// fast path: replace with LambdaPar op
|
||||
return replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
|
||||
replace(ins, MLambdaPar::New(alloc(), ForkJoinContext(), ins));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -560,18 +598,22 @@ bool
|
||||
ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
|
||||
JSObject *templateObject)
|
||||
{
|
||||
return replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
|
||||
replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
@ -589,17 +631,6 @@ 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
|
||||
//
|
||||
@ -763,8 +794,13 @@ ParallelSafetyVisitor::visitSpecializedInstruction(MInstruction *ins, MIRType sp
|
||||
bool
|
||||
ParallelSafetyVisitor::visitThrow(MThrow *thr)
|
||||
{
|
||||
JS_ASSERT(thr->block()->lastIns() == thr);
|
||||
replaceLastIns(thr, MBail::New(alloc()));
|
||||
MBasicBlock *block = thr->block();
|
||||
JS_ASSERT(block->lastIns() == thr);
|
||||
block->discardLastIns();
|
||||
MAbortPar *bailout = MAbortPar::New(alloc());
|
||||
if (!bailout)
|
||||
return false;
|
||||
block->end(bailout);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,15 @@ 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 MBail blocks.
|
||||
// graph and replaces them with MAbortPar blocks.
|
||||
class ParallelSafetyAnalysis
|
||||
{
|
||||
MIRGenerator *mir_;
|
||||
MIRGraph &graph_;
|
||||
|
||||
bool removeResumePointOperands();
|
||||
void replaceOperandsOnResumePoint(MResumePoint *resumePoint, MDefinition *withDef);
|
||||
|
||||
public:
|
||||
ParallelSafetyAnalysis(MIRGenerator *mir,
|
||||
MIRGraph &graph)
|
||||
|
@ -29,12 +29,11 @@ struct CopyValueToRematerializedFrame
|
||||
};
|
||||
|
||||
RematerializedFrame::RematerializedFrame(ThreadSafeContext *cx, uint8_t *top,
|
||||
unsigned numActualArgs, InlineFrameIterator &iter)
|
||||
InlineFrameIterator &iter)
|
||||
: prevUpToDate_(false),
|
||||
top_(top),
|
||||
pc_(iter.pc()),
|
||||
frameNo_(iter.frameNo()),
|
||||
numActualArgs_(numActualArgs),
|
||||
numActualArgs_(iter.numActualArgs()),
|
||||
script_(iter.script())
|
||||
{
|
||||
CopyValueToRematerializedFrame op(slots_);
|
||||
@ -46,56 +45,16 @@ RematerializedFrame::RematerializedFrame(ThreadSafeContext *cx, uint8_t *top,
|
||||
RematerializedFrame::New(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter)
|
||||
{
|
||||
unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
|
||||
unsigned numActualArgs = Max(numFormals, iter.numActualArgs());
|
||||
size_t numBytes = sizeof(RematerializedFrame) +
|
||||
(numActualArgs + iter.script()->nfixed()) * sizeof(Value) -
|
||||
(Max(numFormals, iter.numActualArgs()) +
|
||||
iter.script()->nfixed()) * sizeof(Value) -
|
||||
sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
|
||||
|
||||
void *buf = cx->calloc_(numBytes);
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
return new (buf) RematerializedFrame(cx, top, numActualArgs, iter);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
RematerializedFrame::RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
|
||||
InlineFrameIterator &iter,
|
||||
Vector<RematerializedFrame *> &frames)
|
||||
{
|
||||
if (!frames.resize(iter.frameCount()))
|
||||
return false;
|
||||
|
||||
while (true) {
|
||||
size_t frameNo = iter.frameNo();
|
||||
frames[frameNo] = RematerializedFrame::New(cx, top, iter);
|
||||
if (!frames[frameNo])
|
||||
return false;
|
||||
|
||||
if (!iter.more())
|
||||
break;
|
||||
++iter;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
RematerializedFrame::FreeInVector(Vector<RematerializedFrame *> &frames)
|
||||
{
|
||||
for (size_t i = 0; i < frames.length(); i++) {
|
||||
RematerializedFrame *f = frames[i];
|
||||
f->RematerializedFrame::~RematerializedFrame();
|
||||
js_free(f);
|
||||
}
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
RematerializedFrame::MarkInVector(JSTracer *trc, Vector<RematerializedFrame *> &frames)
|
||||
{
|
||||
for (size_t i = 0; i < frames.length(); i++)
|
||||
frames[i]->mark(trc);
|
||||
return new (buf) RematerializedFrame(cx, top, iter);
|
||||
}
|
||||
|
||||
CallObject &
|
||||
@ -123,11 +82,11 @@ RematerializedFrame::mark(JSTracer *trc)
|
||||
void
|
||||
RematerializedFrame::dump()
|
||||
{
|
||||
fprintf(stderr, " Rematerialized Ion Frame%s\n", inlined() ? " (inlined)" : "");
|
||||
fprintf(stderr, " Rematerialized Optimized Frame%s\n", inlined() ? " (inlined)" : "");
|
||||
if (isFunctionFrame()) {
|
||||
fprintf(stderr, " callee fun: ");
|
||||
#ifdef DEBUG
|
||||
js_DumpValue(ObjectValue(*callee()));
|
||||
js_DumpObject(callee());
|
||||
#else
|
||||
fprintf(stderr, "?\n");
|
||||
#endif
|
||||
@ -135,16 +94,15 @@ RematerializedFrame::dump()
|
||||
fprintf(stderr, " global frame, no callee\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, " file %s line %u offset %zu\n",
|
||||
script()->filename(), (unsigned) script()->lineno(),
|
||||
script()->pcToOffset(pc()));
|
||||
fprintf(stderr, " file %s line %u\n",
|
||||
script()->filename(), (unsigned) script()->lineno());
|
||||
|
||||
fprintf(stderr, " script = %p\n", (void*) script());
|
||||
|
||||
if (isFunctionFrame()) {
|
||||
fprintf(stderr, " scope chain: ");
|
||||
#ifdef DEBUG
|
||||
js_DumpValue(ObjectValue(*scopeChain()));
|
||||
js_DumpObject(scopeChain());
|
||||
#else
|
||||
fprintf(stderr, "?\n");
|
||||
#endif
|
||||
@ -152,7 +110,7 @@ RematerializedFrame::dump()
|
||||
if (hasArgsObj()) {
|
||||
fprintf(stderr, " args obj: ");
|
||||
#ifdef DEBUG
|
||||
js_DumpValue(ObjectValue(argsObj()));
|
||||
js_DumpObject(&argsObj());
|
||||
#else
|
||||
fprintf(stderr, "?\n");
|
||||
#endif
|
||||
|
@ -30,9 +30,6 @@ class RematerializedFrame
|
||||
// The fp of the top frame associated with this possibly inlined frame.
|
||||
uint8_t *top_;
|
||||
|
||||
// The bytecode at the time of rematerialization.
|
||||
jsbytecode *pc_;
|
||||
|
||||
size_t frameNo_;
|
||||
unsigned numActualArgs_;
|
||||
|
||||
@ -44,26 +41,12 @@ class RematerializedFrame
|
||||
Value thisValue_;
|
||||
Value slots_[1];
|
||||
|
||||
RematerializedFrame(ThreadSafeContext *cx, uint8_t *top, unsigned numActualArgs,
|
||||
InlineFrameIterator &iter);
|
||||
RematerializedFrame(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter);
|
||||
|
||||
public:
|
||||
static RematerializedFrame *New(ThreadSafeContext *cx, uint8_t *top,
|
||||
InlineFrameIterator &iter);
|
||||
|
||||
// Rematerialize all remaining frames pointed to by |iter| into |frames|
|
||||
// in older-to-younger order, e.g., frames[0] is the oldest frame.
|
||||
static bool RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
|
||||
InlineFrameIterator &iter,
|
||||
Vector<RematerializedFrame *> &frames);
|
||||
|
||||
// Free a vector of RematerializedFrames; takes care to call the
|
||||
// destructor. Also clears the vector.
|
||||
static void FreeInVector(Vector<RematerializedFrame *> &frames);
|
||||
|
||||
// Mark a vector of RematerializedFrames.
|
||||
static void MarkInVector(JSTracer *trc, Vector<RematerializedFrame *> &frames);
|
||||
|
||||
bool prevUpToDate() const {
|
||||
return prevUpToDate_;
|
||||
}
|
||||
@ -74,9 +57,6 @@ class RematerializedFrame
|
||||
uint8_t *top() const {
|
||||
return top_;
|
||||
}
|
||||
jsbytecode *pc() const {
|
||||
return pc_;
|
||||
}
|
||||
size_t frameNo() const {
|
||||
return frameNo_;
|
||||
}
|
||||
|
@ -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(gen->info().executionMode());
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
|
||||
masm.branch(handler);
|
||||
}
|
||||
|
||||
@ -188,6 +188,22 @@ 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;
|
||||
|
||||
@ -222,6 +238,23 @@ 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,10 +68,6 @@ 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,7 +16,6 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
|
||||
#include "jit/ExecutionMode-inl.h"
|
||||
@ -515,7 +514,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
// the stack should look like:
|
||||
// [IonFrame]
|
||||
@ -560,17 +559,10 @@ PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
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);
|
||||
@ -618,32 +610,6 @@ 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)
|
||||
{
|
||||
@ -668,20 +634,10 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
|
||||
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");
|
||||
}
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
|
@ -213,6 +213,22 @@ 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,11 +93,6 @@ 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,20 +642,10 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
{
|
||||
MacroAssembler masm(cx);
|
||||
|
||||
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");
|
||||
}
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("BailoutHandler");
|
||||
|
@ -340,13 +340,6 @@ 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)
|
||||
@ -829,6 +822,123 @@ 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,6 +26,8 @@ class OutOfLineCode;
|
||||
class CodeGenerator;
|
||||
class MacroAssembler;
|
||||
class IonCache;
|
||||
class OutOfLineAbortPar;
|
||||
class OutOfLinePropagateAbortPar;
|
||||
|
||||
template <class ArgSeq, class StoreOutputTo>
|
||||
class OutOfLineCallVM;
|
||||
@ -466,6 +468,28 @@ 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);
|
||||
@ -746,6 +770,53 @@ 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(gen->info().executionMode());
|
||||
JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
|
||||
masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE);
|
||||
}
|
||||
|
||||
@ -398,6 +398,22 @@ 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,10 +75,6 @@ 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,7 +11,6 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
#include "jit/x64/BaselineHelpers-x64.h"
|
||||
|
||||
@ -447,19 +446,13 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
PushBailoutFrame(MacroAssembler &masm, Register spArg)
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
// 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, spArg);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
PushBailoutFrame(masm, r8);
|
||||
masm.movq(rsp, r8);
|
||||
|
||||
// Make space for Bailout's bailoutInfo outparam.
|
||||
masm.reserveStack(sizeof(void *));
|
||||
@ -481,7 +474,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);
|
||||
@ -491,31 +484,6 @@ 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)
|
||||
{
|
||||
@ -523,20 +491,11 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
{
|
||||
MacroAssembler masm;
|
||||
|
||||
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");
|
||||
}
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
|
@ -16,7 +16,6 @@
|
||||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "jit/ParallelFunctions.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
#include "jit/x86/BaselineHelpers-x86.h"
|
||||
|
||||
@ -456,7 +455,7 @@ JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
|
||||
}
|
||||
|
||||
static void
|
||||
PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
// Push registers such that we can access them from [base + code].
|
||||
masm.PushRegsInMask(AllRegs);
|
||||
@ -465,13 +464,7 @@ PushBailoutFrame(MacroAssembler &masm, uint32_t frameClass, Register spArg)
|
||||
masm.push(Imm32(frameClass));
|
||||
|
||||
// The current stack pointer is the first argument to jit::Bailout.
|
||||
masm.movl(esp, spArg);
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
|
||||
{
|
||||
PushBailoutFrame(masm, frameClass, eax);
|
||||
masm.movl(esp, eax);
|
||||
|
||||
// Make space for Bailout's baioutInfo outparam.
|
||||
masm.reserveStack(sizeof(void *));
|
||||
@ -515,31 +508,6 @@ 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)
|
||||
{
|
||||
@ -563,20 +531,11 @@ JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
|
||||
}
|
||||
|
||||
JitCode *
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx, ExecutionMode mode)
|
||||
JitRuntime::generateBailoutHandler(JSContext *cx)
|
||||
{
|
||||
MacroAssembler masm;
|
||||
|
||||
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");
|
||||
}
|
||||
GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
|
||||
|
||||
Linker linker(masm);
|
||||
JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
#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"
|
||||
@ -118,13 +117,26 @@ ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
|
||||
ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
|
||||
js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::addTrace(JSScript *script,
|
||||
jsbytecode *pc)
|
||||
{
|
||||
MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
|
||||
}
|
||||
@ -262,6 +274,11 @@ 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();
|
||||
@ -305,7 +322,7 @@ class ForkJoinOperation
|
||||
HandleObject updatable_;
|
||||
uint16_t sliceStart_;
|
||||
uint16_t sliceEnd_;
|
||||
Vector<ParallelBailoutRecord> bailoutRecords_;
|
||||
Vector<ParallelBailoutRecord, 16> bailoutRecords_;
|
||||
AutoScriptVector worklist_;
|
||||
Vector<WorklistData, 16> worklistData_;
|
||||
ForkJoinMode mode_;
|
||||
@ -318,9 +335,7 @@ class ForkJoinOperation
|
||||
TrafficLight recoverFromBailout(ExecutionStatus *status);
|
||||
TrafficLight fatalError(ExecutionStatus *status);
|
||||
bool isInitialScript(HandleScript script);
|
||||
void getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
|
||||
ParallelBailoutCause *bailoutCause);
|
||||
bool reportBailoutWarnings();
|
||||
void determineBailoutCause();
|
||||
bool invalidateBailedOutScripts();
|
||||
ExecutionStatus sequentialExecution(bool disqualified);
|
||||
|
||||
@ -339,14 +354,14 @@ class ForkJoinShared : public ParallelJob, public Monitor
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Constant fields
|
||||
|
||||
JSContext *const cx_; // Current context
|
||||
ThreadPool *const threadPool_; // The thread pool
|
||||
HandleFunction fun_; // The JavaScript function to execute
|
||||
HandleObject updatable_; // Pre-existing object that might be updated
|
||||
uint16_t sliceStart_; // The starting slice id.
|
||||
uint16_t sliceEnd_; // The ending slice id + 1.
|
||||
PRLock *cxLock_; // Locks cx_ for parallel VM calls
|
||||
Vector<ParallelBailoutRecord> &records_; // Bailout records for each worker
|
||||
JSContext *const cx_; // Current context
|
||||
ThreadPool *const threadPool_; // The thread pool
|
||||
HandleFunction fun_; // The JavaScript function to execute
|
||||
HandleObject updatable_; // Pre-existing object that might be updated
|
||||
uint16_t sliceStart_; // The starting slice id.
|
||||
uint16_t sliceEnd_; // The ending slice id + 1.
|
||||
PRLock *cxLock_; // Locks cx_ for parallel VM calls
|
||||
ParallelBailoutRecord *const records_; // Bailout records for each worker
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Per-thread arenas
|
||||
@ -382,7 +397,7 @@ class ForkJoinShared : public ParallelJob, public Monitor
|
||||
HandleObject updatable,
|
||||
uint16_t sliceStart,
|
||||
uint16_t sliceEnd,
|
||||
Vector<ParallelBailoutRecord> &records);
|
||||
ParallelBailoutRecord *records);
|
||||
~ForkJoinShared();
|
||||
|
||||
bool init();
|
||||
@ -577,6 +592,9 @@ 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),
|
||||
@ -627,10 +645,8 @@ ForkJoinOperation::apply()
|
||||
if (!bailoutRecords_.resize(numWorkers))
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
|
||||
for (uint32_t i = 0; i < numWorkers; i++) {
|
||||
if (!bailoutRecords_[i].init(cx_))
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
}
|
||||
for (uint32_t i = 0; i < numWorkers; i++)
|
||||
bailoutRecords_[i].init(cx_);
|
||||
|
||||
if (enqueueInitialScript(&status) == RedLight)
|
||||
return SpewEndOp(status);
|
||||
@ -660,7 +676,7 @@ ForkJoinOperation::apply()
|
||||
|
||||
while (bailouts < MAX_BAILOUTS) {
|
||||
for (uint32_t i = 0; i < numWorkers; i++)
|
||||
bailoutRecords_[i].reset();
|
||||
bailoutRecords_[i].reset(cx_);
|
||||
|
||||
if (compileForParallelExecution(&status) == RedLight)
|
||||
return SpewEndOp(status);
|
||||
@ -1087,139 +1103,41 @@ ForkJoinOperation::isInitialScript(HandleScript script)
|
||||
}
|
||||
|
||||
void
|
||||
ForkJoinOperation::getBailoutScriptAndPc(MutableHandleScript bailoutScript, jsbytecode **bailoutPc,
|
||||
ParallelBailoutCause *bailoutCause)
|
||||
ForkJoinOperation::determineBailoutCause()
|
||||
{
|
||||
bailoutCause = ParallelBailoutNone;
|
||||
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
case ParallelBailoutNone:
|
||||
case ParallelBailoutInterrupt:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bailoutRecords_[i].hasFrames()) {
|
||||
RematerializedFrame *frame = bailoutRecords_[i].frames()[0];
|
||||
bailoutScript.set(frame->script());
|
||||
*bailoutPc = frame->pc();
|
||||
*bailoutCause = bailoutRecords_[i].cause;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
ValueToChar(JSContext *cx, HandleValue val, JSAutoByteString &bytes)
|
||||
{
|
||||
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)
|
||||
if (bailoutRecords_[i].cause == ParallelBailoutNone)
|
||||
continue;
|
||||
|
||||
if (bailoutRecords_[threadId].hasFrames()) {
|
||||
Vector<RematerializedFrame *> &frames = bailoutRecords_[threadId].frames();
|
||||
if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
sp.printf("\n in thread %d due to %s", threadId, BailoutExplanation(cause));
|
||||
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);
|
||||
|
||||
for (uint32_t frameIndex = 0; frameIndex < frames.length(); frameIndex++) {
|
||||
RematerializedFrame *frame = frames[frameIndex];
|
||||
RootedScript script(cx_, frame->script());
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Spew(SpewBailouts, "Bailout from thread %d: cause %d, unknown loc",
|
||||
i,
|
||||
bailoutCause);
|
||||
}
|
||||
}
|
||||
|
||||
if (SpewEnabled(SpewBailouts))
|
||||
JS_ReportWarning(cx_, "%s", sp.string());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1227,33 +1145,32 @@ ForkJoinOperation::invalidateBailedOutScripts()
|
||||
{
|
||||
Vector<types::RecompileInfo> invalid(cx_);
|
||||
for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
|
||||
switch (bailoutRecords_[i].cause) {
|
||||
// No bailout.
|
||||
case ParallelBailoutNone:
|
||||
RootedScript script(cx_, bailoutRecords_[i].topScript);
|
||||
|
||||
// No script to invalidate.
|
||||
if (!script || !script->hasParallelIonScript())
|
||||
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 (!script->hasParallelIonScript() || hasScript(invalid, script))
|
||||
if (hasScript(invalid, script))
|
||||
continue;
|
||||
|
||||
Spew(SpewBailouts, "Invalidating script %p:%s:%d due to cause %d",
|
||||
@ -1265,7 +1182,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;
|
||||
@ -1334,7 +1251,7 @@ ForkJoinOperation::parallelExecution(ExecutionStatus *status)
|
||||
ForkJoinActivation activation(cx_);
|
||||
ThreadPool *threadPool = &cx_->runtime()->threadPool;
|
||||
ForkJoinShared shared(cx_, threadPool, fun_, updatable_, sliceStart_, sliceEnd_,
|
||||
bailoutRecords_);
|
||||
&bailoutRecords_[0]);
|
||||
if (!shared.init()) {
|
||||
*status = ExecutionFatal;
|
||||
return RedLight;
|
||||
@ -1364,9 +1281,9 @@ ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
|
||||
// RedLight: fatal error
|
||||
|
||||
bailouts += 1;
|
||||
determineBailoutCause();
|
||||
|
||||
if (!reportBailoutWarnings())
|
||||
return fatalError(status);
|
||||
SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
|
||||
|
||||
// After any bailout, we always scan over callee list of main
|
||||
// function, if nothing else
|
||||
@ -1430,7 +1347,8 @@ class ParallelIonInvoke
|
||||
|
||||
bool invoke(ForkJoinContext *cx) {
|
||||
JitActivation activation(cx);
|
||||
Value result = Int32Value(argc_);
|
||||
// In-out parameter: on input it denotes the number of values to preserve after the call.
|
||||
Value result = Int32Value(0);
|
||||
CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
|
||||
nullptr, 0, &result);
|
||||
return !result.isMagic();
|
||||
@ -1447,7 +1365,7 @@ ForkJoinShared::ForkJoinShared(JSContext *cx,
|
||||
HandleObject updatable,
|
||||
uint16_t sliceStart,
|
||||
uint16_t sliceEnd,
|
||||
Vector<ParallelBailoutRecord> &records)
|
||||
ParallelBailoutRecord *records)
|
||||
: cx_(cx),
|
||||
threadPool_(threadPool),
|
||||
fun_(fun),
|
||||
@ -1648,7 +1566,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
||||
CompileCompartment::get(cx_->compartment()),
|
||||
nullptr);
|
||||
|
||||
JS_ASSERT(!cx.bailoutRecord->bailedOut());
|
||||
JS_ASSERT(cx.bailoutRecord->topScript == nullptr);
|
||||
|
||||
if (!fun_->nonLazyScript()->hasParallelIonScript()) {
|
||||
// Sometimes, particularly with GCZeal, the parallel ion
|
||||
@ -1656,7 +1574,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->joinCause(ParallelBailoutMainScriptNotPresent);
|
||||
cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
} else {
|
||||
ParallelIonInvoke<3> fii(runtime(), fun_, 3);
|
||||
@ -1666,7 +1584,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
|
||||
fii.args[2] = Int32Value(sliceEnd_);
|
||||
|
||||
bool ok = fii.invoke(&cx);
|
||||
JS_ASSERT(ok == !cx.bailoutRecord->bailedOut());
|
||||
JS_ASSERT(ok == !cx.bailoutRecord->topScript);
|
||||
if (!ok) {
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
#ifdef JSGC_FJGENERATIONAL
|
||||
@ -1712,7 +1630,7 @@ ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
|
||||
JS_ASSERT(!cx_->runtime()->gc.isNeeded);
|
||||
|
||||
if (!abort_) {
|
||||
cx.bailoutRecord->joinCause(ParallelBailoutInterrupt);
|
||||
cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
|
||||
setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
}
|
||||
@ -1886,7 +1804,7 @@ void
|
||||
ForkJoinContext::requestGC(JS::gcreason::Reason reason)
|
||||
{
|
||||
shared_->requestGC(reason);
|
||||
bailoutRecord->joinCause(ParallelBailoutRequestedGC);
|
||||
bailoutRecord->setCause(ParallelBailoutRequestedGC);
|
||||
shared_->setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
|
||||
@ -1894,7 +1812,7 @@ void
|
||||
ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
|
||||
{
|
||||
shared_->requestZoneGC(zone, reason);
|
||||
bailoutRecord->joinCause(ParallelBailoutRequestedZoneGC);
|
||||
bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
|
||||
shared_->setAbortFlagAndRequestInterrupt(false);
|
||||
}
|
||||
|
||||
@ -1902,84 +1820,75 @@ bool
|
||||
ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
|
||||
{
|
||||
shared_->setPendingAbortFatal();
|
||||
bailoutRecord->joinCause(cause);
|
||||
bailoutRecord->setCause(cause);
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ParallelBailoutRecord
|
||||
|
||||
ParallelBailoutRecord::~ParallelBailoutRecord()
|
||||
void
|
||||
js::ParallelBailoutRecord::init(JSContext *cx)
|
||||
{
|
||||
reset();
|
||||
js_delete(frames_);
|
||||
}
|
||||
|
||||
bool
|
||||
ParallelBailoutRecord::init(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(!frames_);
|
||||
frames_ = cx->new_<Vector<RematerializedFrame *> >(cx);
|
||||
return !!frames_;
|
||||
reset(cx);
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::reset()
|
||||
js::ParallelBailoutRecord::reset(JSContext *cx)
|
||||
{
|
||||
RematerializedFrame::FreeInVector(frames());
|
||||
topScript = nullptr;
|
||||
cause = ParallelBailoutNone;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void
|
||||
RematerializeFramesWithIter(ForkJoinContext *cx, T &frameIter,
|
||||
Vector<RematerializedFrame *> &frames)
|
||||
void
|
||||
js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *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.
|
||||
this->cause = cause;
|
||||
updateCause(cause, outermostScript, currentScript, currentPc);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(frames.empty());
|
||||
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);
|
||||
|
||||
for (; !frameIter.done(); ++frameIter) {
|
||||
if (!frameIter.isIonJS())
|
||||
continue;
|
||||
if (this->cause == ParallelBailoutNone)
|
||||
this->cause = cause;
|
||||
|
||||
InlineFrameIterator inlineIter(cx, &frameIter);
|
||||
Vector<RematerializedFrame *> inlineFrames(cx);
|
||||
if (outermostScript)
|
||||
this->topScript = outermostScript;
|
||||
|
||||
if (!RematerializedFrame::RematerializeInlineFrames(cx, frameIter.fp(),
|
||||
inlineIter, inlineFrames))
|
||||
{
|
||||
RematerializedFrame::FreeInVector(inlineFrames);
|
||||
RematerializedFrame::FreeInVector(frames);
|
||||
return;
|
||||
}
|
||||
if (currentScript)
|
||||
addTrace(currentScript, currentPc);
|
||||
}
|
||||
|
||||
// Reverse the inline frames into the main vector.
|
||||
while (!inlineFrames.empty()) {
|
||||
if (!frames.append(inlineFrames.popCopy())) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, JitFrameIterator &frameIter)
|
||||
{
|
||||
RematerializeFramesWithIter(cx, frameIter, frames());
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::rematerializeFrames(ForkJoinContext *cx, IonBailoutIterator &frameIter)
|
||||
{
|
||||
RematerializeFramesWithIter(cx, frameIter, frames());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
@ -2251,6 +2160,21 @@ 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.
|
||||
@ -2319,6 +2243,12 @@ parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
parallel::SpewBailoutIR(IonLIRTraceData *data)
|
||||
{
|
||||
spewer.spewBailoutIR(data);
|
||||
}
|
||||
|
||||
#endif // FORKJOIN_SPEW
|
||||
|
||||
bool
|
||||
|
@ -272,29 +272,21 @@ 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 = 0,
|
||||
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedVM,
|
||||
|
||||
// The periodic interrupt failed, which can mean that either
|
||||
// another thread canceled, the user interrupted us, etc
|
||||
ParallelBailoutInterrupt,
|
||||
ParallelBailoutNone,
|
||||
|
||||
// Compiler returned Method_Skipped
|
||||
ParallelBailoutCompilationSkipped,
|
||||
@ -302,9 +294,9 @@ enum ParallelBailoutCause {
|
||||
// Compiler returned Method_CantCompile
|
||||
ParallelBailoutCompilationFailure,
|
||||
|
||||
// Propagating a failure, i.e., another thread requested the computation
|
||||
// be aborted.
|
||||
ParallelBailoutPropagate,
|
||||
// The periodic interrupt failed, which can mean that either
|
||||
// another thread canceled, the user interrupted us, etc
|
||||
ParallelBailoutInterrupt,
|
||||
|
||||
// An IC update failed
|
||||
ParallelBailoutFailedIC,
|
||||
@ -318,49 +310,41 @@ enum ParallelBailoutCause {
|
||||
ParallelBailoutAccessToIntrinsic,
|
||||
ParallelBailoutOverRecursed,
|
||||
ParallelBailoutOutOfMemory,
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedVM,
|
||||
ParallelBailoutUnsupportedStringComparison,
|
||||
ParallelBailoutRequestedGC,
|
||||
ParallelBailoutRequestedZoneGC
|
||||
ParallelBailoutRequestedZoneGC,
|
||||
};
|
||||
|
||||
namespace jit {
|
||||
class BailoutStack;
|
||||
class JitFrameIterator;
|
||||
class RematerializedFrame;
|
||||
}
|
||||
struct ParallelBailoutTrace {
|
||||
JSScript *script;
|
||||
jsbytecode *bytecode;
|
||||
};
|
||||
|
||||
// See "Bailouts" section in comment above.
|
||||
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_;
|
||||
struct ParallelBailoutRecord {
|
||||
JSScript *topScript;
|
||||
ParallelBailoutCause cause;
|
||||
|
||||
ParallelBailoutRecord()
|
||||
: frames_(nullptr),
|
||||
cause(ParallelBailoutNone)
|
||||
{ }
|
||||
// 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();
|
||||
|
||||
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);
|
||||
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);
|
||||
};
|
||||
|
||||
class ForkJoinShared;
|
||||
@ -372,6 +356,9 @@ 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
|
||||
@ -592,6 +579,7 @@ 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
|
||||
|
||||
@ -607,6 +595,7 @@ 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
|
||||
|
||||
|
@ -1586,6 +1586,17 @@ jit::JitActivation::setActive(JSContext *cx, bool active)
|
||||
|
||||
#ifdef JS_ION
|
||||
|
||||
void
|
||||
jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames)
|
||||
{
|
||||
for (size_t i = 0; i < frames.length(); i++) {
|
||||
RematerializedFrame *f = frames[i];
|
||||
f->RematerializedFrame::~RematerializedFrame();
|
||||
js_free(f);
|
||||
}
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
void
|
||||
jit::JitActivation::removeRematerializedFrame(uint8_t *top)
|
||||
{
|
||||
@ -1593,7 +1604,7 @@ jit::JitActivation::removeRematerializedFrame(uint8_t *top)
|
||||
return;
|
||||
|
||||
if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
|
||||
RematerializedFrame::FreeInVector(p->value());
|
||||
freeRematerializedFramesInVector(p->value());
|
||||
rematerializedFrames_->remove(p);
|
||||
}
|
||||
}
|
||||
@ -1605,14 +1616,14 @@ jit::JitActivation::clearRematerializedFrames()
|
||||
return;
|
||||
|
||||
for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
|
||||
RematerializedFrame::FreeInVector(e.front().value());
|
||||
freeRematerializedFramesInVector(e.front().value());
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
jit::RematerializedFrame *
|
||||
jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter, size_t inlineDepth)
|
||||
jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, JitFrameIterator &iter,
|
||||
size_t inlineDepth)
|
||||
{
|
||||
// Only allow rematerializing from the same thread.
|
||||
MOZ_ASSERT(cx->perThreadData == cx_->perThreadData);
|
||||
@ -1627,6 +1638,12 @@ jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter,
|
||||
}
|
||||
}
|
||||
|
||||
// The unit of rematerialization is an uninlined frame and its inlined
|
||||
// frames. Since inlined frames do not exist outside of snapshots, it is
|
||||
// impossible to synchronize their rematerialized copies to preserve
|
||||
// identity. Therefore, we always rematerialize an uninlined frame and all
|
||||
// its inlined frames at once.
|
||||
|
||||
uint8_t *top = iter.fp();
|
||||
RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
|
||||
if (!p) {
|
||||
@ -1634,28 +1651,25 @@ jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter,
|
||||
if (!rematerializedFrames_->add(p, top, Move(empty)))
|
||||
return nullptr;
|
||||
|
||||
// The unit of rematerialization is an uninlined frame and its inlined
|
||||
// frames. Since inlined frames do not exist outside of snapshots, it
|
||||
// is impossible to synchronize their rematerialized copies to
|
||||
// preserve identity. Therefore, we always rematerialize an uninlined
|
||||
// frame and all its inlined frames at once.
|
||||
InlineFrameIterator inlineIter(cx, &iter);
|
||||
if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, p->value()))
|
||||
if (!p->value().resize(inlineIter.frameCount()))
|
||||
return nullptr;
|
||||
|
||||
while (true) {
|
||||
size_t frameNo = inlineIter.frameNo();
|
||||
p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter);
|
||||
if (!p->value()[frameNo])
|
||||
return nullptr;
|
||||
|
||||
if (!inlineIter.more())
|
||||
break;
|
||||
++inlineIter;
|
||||
}
|
||||
}
|
||||
|
||||
return p->value()[inlineDepth];
|
||||
}
|
||||
|
||||
template jit::RematerializedFrame *
|
||||
jit::JitActivation::getRematerializedFrame<jit::JitFrameIterator>(ThreadSafeContext *cx,
|
||||
const jit::JitFrameIterator &iter,
|
||||
size_t inlineDepth);
|
||||
template jit::RematerializedFrame *
|
||||
jit::JitActivation::getRematerializedFrame<jit::IonBailoutIterator>(ThreadSafeContext *cx,
|
||||
const jit::IonBailoutIterator &iter,
|
||||
size_t inlineDepth);
|
||||
|
||||
jit::RematerializedFrame *
|
||||
jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth)
|
||||
{
|
||||
@ -1671,8 +1685,11 @@ jit::JitActivation::markRematerializedFrames(JSTracer *trc)
|
||||
{
|
||||
if (!rematerializedFrames_)
|
||||
return;
|
||||
for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
|
||||
RematerializedFrame::MarkInVector(trc, e.front().value());
|
||||
for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
|
||||
RematerializedFrameVector &frames = e.front().value();
|
||||
for (size_t i = 0; i < frames.length(); i++)
|
||||
frames[i]->mark(trc);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // JS_ION
|
||||
|
@ -1338,10 +1338,11 @@ class JitActivation : public Activation
|
||||
// inline frames associated with that frame.
|
||||
//
|
||||
// This table is lazily initialized by calling getRematerializedFrame.
|
||||
typedef Vector<RematerializedFrame *> RematerializedFrameVector;
|
||||
typedef Vector<RematerializedFrame *, 1> RematerializedFrameVector;
|
||||
typedef HashMap<uint8_t *, RematerializedFrameVector> RematerializedFrameTable;
|
||||
RematerializedFrameTable *rematerializedFrames_;
|
||||
|
||||
void freeRematerializedFramesInVector(RematerializedFrameVector &frames);
|
||||
void clearRematerializedFrames();
|
||||
#endif
|
||||
|
||||
@ -1398,11 +1399,8 @@ class JitActivation : public Activation
|
||||
// if an IonFrameIterator pointing to the nearest uninlined frame can be
|
||||
// provided, as values need to be read out of snapshots.
|
||||
//
|
||||
// T is either JitFrameIterator or IonBailoutIterator.
|
||||
//
|
||||
// The inlineDepth must be within bounds of the frame pointed to by iter.
|
||||
template <class T>
|
||||
RematerializedFrame *getRematerializedFrame(ThreadSafeContext *cx, const T &iter,
|
||||
RematerializedFrame *getRematerializedFrame(ThreadSafeContext *cx, JitFrameIterator &iter,
|
||||
size_t inlineDepth = 0);
|
||||
|
||||
// Look up a rematerialized frame by the fp. If inlineDepth is out of
|
||||
|
Loading…
Reference in New Issue
Block a user