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:
Wes Kocher 2014-06-20 13:33:03 -07:00
parent 44ecb76c25
commit 40fead1259
41 changed files with 1019 additions and 727 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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)
{

View File

@ -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

View File

@ -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(&notPrimitive);
}
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)
{

View File

@ -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);

View File

@ -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_)

View File

@ -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

View File

@ -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);

View File

@ -54,6 +54,8 @@ namespace jit {
_(Safepoints) \
/* Debug info about Pools*/ \
_(Pools) \
/* Calls to js::jit::Trace() */ \
_(Trace) \
/* Debug info about the I$ */ \
_(CacheFlush) \
\

View File

@ -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 {

View File

@ -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:

View File

@ -34,6 +34,7 @@
_(NewDenseArrayPar) \
_(NewCallObjectPar) \
_(NewDerivedTypedObject) \
_(AbortPar) \
_(InitElem) \
_(InitElemGetterSetter) \
_(MutateProto) \

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -217,6 +217,7 @@ namespace jit {
_(NewPar) \
_(NewDenseArrayPar) \
_(NewDerivedTypedObject) \
_(AbortPar) \
_(LambdaPar) \
_(RestPar) \
_(ForkJoinContext) \

View File

@ -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 *

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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_;
}

View File

@ -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;

View File

@ -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();

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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)
{

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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