mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 862922 - Track causes and locations of parallel bailouts and issue a somewhat obscure warning r=jandem
This commit is contained in:
parent
7a055e0974
commit
e61ede7e17
@ -1262,25 +1262,17 @@ function CheckParallel(mode) {
|
||||
if (!mode || !ParallelTestsShouldPass())
|
||||
return null;
|
||||
|
||||
return function(bailouts) {
|
||||
return function(result, bailouts, causes) {
|
||||
if (!("expect" in mode) || mode.expect === "any") {
|
||||
return; // Ignore result when unspecified or unimportant.
|
||||
} else if (mode.expect === "mixed" && result !== "disqualified") {
|
||||
return; // "mixed" means that it may bailout, may succeed
|
||||
} else if (result === mode.expect) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result;
|
||||
if (bailouts === 0)
|
||||
result = "success";
|
||||
else if (bailouts === global.Infinity)
|
||||
result = "disqualified";
|
||||
else
|
||||
result = "bailout";
|
||||
|
||||
if (mode.expect === "mixed") {
|
||||
if (result === "disqualified")
|
||||
ThrowError(JSMSG_WRONG_VALUE, mode.expect, result);
|
||||
} else if (result !== mode.expect) {
|
||||
ThrowError(JSMSG_WRONG_VALUE, mode.expect, result);
|
||||
}
|
||||
ThrowError(JSMSG_WRONG_VALUE, mode.expect,
|
||||
result+":"+bailouts+":"+causes);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -691,7 +691,7 @@ CodeGenerator::visitParLambda(LParLambda *lir)
|
||||
|
||||
JS_ASSERT(scopeChainReg != resultReg);
|
||||
|
||||
emitParAllocateGCThing(resultReg, parSliceReg, tempReg1, tempReg2, fun);
|
||||
emitParAllocateGCThing(lir, resultReg, parSliceReg, tempReg1, tempReg2, fun);
|
||||
emitLambdaInit(resultReg, scopeChainReg, fun);
|
||||
return true;
|
||||
}
|
||||
@ -1107,12 +1107,12 @@ CodeGenerator::visitParWriteGuard(LParWriteGuard *lir)
|
||||
masm.passABIArg(ToRegister(lir->object()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParWriteGuard));
|
||||
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(ParallelBailoutIllegalWrite, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
masm.branchIfFalseBool(ReturnReg, bail);
|
||||
|
||||
// branch to the OOL failure code if false is returned
|
||||
masm.branchIfFalseBool(ReturnReg, bail->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1365,11 +1365,11 @@ CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
|
||||
}
|
||||
|
||||
case ParallelExecution: {
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(ParallelBailoutAccessToIntrinsic, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
masm.jump(bail);
|
||||
masm.jump(bail->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1485,7 +1485,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
|
||||
break;
|
||||
|
||||
case ParallelExecution:
|
||||
if (!emitParCallToUncompiledScript(calleereg))
|
||||
if (!emitParCallToUncompiledScript(call, calleereg))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -1501,7 +1501,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
|
||||
masm.bind(¬Primitive);
|
||||
}
|
||||
|
||||
if (!checkForParallelBailout())
|
||||
if (!checkForParallelBailout(call))
|
||||
return false;
|
||||
|
||||
dropArguments(call->numStackArgs() + 1);
|
||||
@ -1511,17 +1511,18 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
|
||||
// Generates a call to ParCallToUncompiledScript() and then bails out.
|
||||
// |calleeReg| should contain the JSFunction*.
|
||||
bool
|
||||
CodeGenerator::emitParCallToUncompiledScript(Register calleeReg)
|
||||
CodeGenerator::emitParCallToUncompiledScript(LInstruction *lir,
|
||||
Register calleeReg)
|
||||
{
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineCode *bail = oolParallelAbort(ParallelBailoutCalledToUncompiledScript, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
masm.movePtr(calleeReg, CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCallToUncompiledScript));
|
||||
masm.jump(bail);
|
||||
masm.jump(bail->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1605,14 +1606,14 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
||||
break;
|
||||
|
||||
case ParallelExecution:
|
||||
if (!emitParCallToUncompiledScript(calleereg))
|
||||
if (!emitParCallToUncompiledScript(call, calleereg))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
masm.bind(&end);
|
||||
|
||||
if (!checkForParallelBailout())
|
||||
if (!checkForParallelBailout(call))
|
||||
return false;
|
||||
|
||||
// If the return value of the constructing function is Primitive,
|
||||
@ -1629,17 +1630,17 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::checkForParallelBailout()
|
||||
CodeGenerator::checkForParallelBailout(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) {
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLinePropagateParallelAbort *bail = oolPropagateParallelAbort(lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail);
|
||||
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail->entry());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2124,8 +2125,8 @@ CodeGenerator::visitParCheckOverRecursed(LParCheckOverRecursed *lir)
|
||||
bool
|
||||
CodeGenerator::visitParCheckOverRecursedFailure(ParCheckOverRecursedFailure *ool)
|
||||
{
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLinePropagateParallelAbort *bail = oolPropagateParallelAbort(ool->lir());
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// Avoid saving/restoring the temp register since we will put the
|
||||
@ -2143,7 +2144,7 @@ CodeGenerator::visitParCheckOverRecursedFailure(ParCheckOverRecursedFailure *ool
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCheckOverRecursed));
|
||||
masm.movePtr(ReturnReg, tempReg);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
masm.branchIfFalseBool(tempReg, bail);
|
||||
masm.branchIfFalseBool(tempReg, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
return true;
|
||||
@ -2186,8 +2187,8 @@ CodeGenerator::visitParCheckInterrupt(LParCheckInterrupt *lir)
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineParCheckInterrupt(OutOfLineParCheckInterrupt *ool)
|
||||
{
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLinePropagateParallelAbort *bail = oolPropagateParallelAbort(ool->lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// Avoid saving/restoring the temp register since we will put the
|
||||
@ -2205,7 +2206,7 @@ CodeGenerator::visitOutOfLineParCheckInterrupt(OutOfLineParCheckInterrupt *ool)
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCheckInterrupt));
|
||||
masm.movePtr(ReturnReg, tempReg);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
masm.branchIfFalseBool(tempReg, bail);
|
||||
masm.branchIfFalseBool(tempReg, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
return true;
|
||||
@ -2711,7 +2712,7 @@ CodeGenerator::visitParNewCallObject(LParNewCallObject *lir)
|
||||
Register tempReg2 = ToRegister(lir->getTemp1());
|
||||
JSObject *templateObj = lir->mir()->templateObj();
|
||||
|
||||
emitParAllocateGCThing(resultReg, parSliceReg, tempReg1, tempReg2, templateObj);
|
||||
emitParAllocateGCThing(lir, resultReg, parSliceReg, tempReg1, tempReg2, templateObj);
|
||||
|
||||
// NB: !lir->slots()->isRegister() implies that there is no slots
|
||||
// array at all, and the memory is already zeroed when copying
|
||||
@ -2738,7 +2739,7 @@ CodeGenerator::visitParNewDenseArray(LParNewDenseArray *lir)
|
||||
|
||||
// Allocate the array into tempReg2. Don't use resultReg because it
|
||||
// may alias parSliceReg etc.
|
||||
emitParAllocateGCThing(tempReg2, parSliceReg, tempReg0, tempReg1, templateObj);
|
||||
emitParAllocateGCThing(lir, tempReg2, parSliceReg, tempReg0, tempReg1, templateObj);
|
||||
|
||||
// Invoke a C helper to allocate the elements. For convenience,
|
||||
// this helper also returns the array back to us, or NULL, which
|
||||
@ -2756,10 +2757,10 @@ CodeGenerator::visitParNewDenseArray(LParNewDenseArray *lir)
|
||||
|
||||
Register resultReg = ToRegister(lir->output());
|
||||
JS_ASSERT(resultReg == ReturnReg);
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(ParallelBailoutOutOfMemory, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail);
|
||||
masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail->entry());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2801,19 +2802,19 @@ CodeGenerator::visitParNew(LParNew *lir)
|
||||
Register tempReg1 = ToRegister(lir->getTemp0());
|
||||
Register tempReg2 = ToRegister(lir->getTemp1());
|
||||
JSObject *templateObject = lir->mir()->templateObject();
|
||||
emitParAllocateGCThing(objReg, parSliceReg, tempReg1, tempReg2,
|
||||
templateObject);
|
||||
emitParAllocateGCThing(lir, objReg, parSliceReg, tempReg1, tempReg2, templateObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
class OutOfLineParNewGCThing : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
public:
|
||||
LInstruction *lir;
|
||||
gc::AllocKind allocKind;
|
||||
Register objReg;
|
||||
|
||||
OutOfLineParNewGCThing(gc::AllocKind allocKind, Register objReg)
|
||||
: allocKind(allocKind), objReg(objReg)
|
||||
OutOfLineParNewGCThing(LInstruction *lir, gc::AllocKind allocKind, Register objReg)
|
||||
: lir(lir), allocKind(allocKind), objReg(objReg)
|
||||
{}
|
||||
|
||||
bool accept(CodeGenerator *codegen) {
|
||||
@ -2822,14 +2823,15 @@ public:
|
||||
};
|
||||
|
||||
bool
|
||||
CodeGenerator::emitParAllocateGCThing(const Register &objReg,
|
||||
CodeGenerator::emitParAllocateGCThing(LInstruction *lir,
|
||||
const Register &objReg,
|
||||
const Register &parSliceReg,
|
||||
const Register &tempReg1,
|
||||
const Register &tempReg2,
|
||||
JSObject *templateObj)
|
||||
{
|
||||
gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
|
||||
OutOfLineParNewGCThing *ool = new OutOfLineParNewGCThing(allocKind, objReg);
|
||||
OutOfLineParNewGCThing *ool = new OutOfLineParNewGCThing(lir, allocKind, objReg);
|
||||
if (!ool || !addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
@ -2869,10 +2871,10 @@ CodeGenerator::visitOutOfLineParNewGCThing(OutOfLineParNewGCThing *ool)
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParNewGCThing));
|
||||
masm.movePtr(ReturnReg, ool->objReg);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(ParallelBailoutOutOfMemory, ool->lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.branchTestPtr(Assembler::Zero, ool->objReg, ool->objReg, bail);
|
||||
masm.branchTestPtr(Assembler::Zero, ool->objReg, ool->objReg, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
@ -2880,10 +2882,10 @@ CodeGenerator::visitOutOfLineParNewGCThing(OutOfLineParNewGCThing *ool)
|
||||
bool
|
||||
CodeGenerator::visitParBailout(LParBailout *lir)
|
||||
{
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(ParallelBailoutUnsupported, lir);
|
||||
if (!bail)
|
||||
return false;
|
||||
masm.jump(bail);
|
||||
masm.jump(bail->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4335,10 +4337,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
|
||||
return true;
|
||||
|
||||
case ParallelExecution:
|
||||
Label *bail;
|
||||
if (!ensureOutOfLineParallelAbort(&bail))
|
||||
return false;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// If the problem is that we do not have sufficient capacity,
|
||||
// try to reallocate the elements array and then branch back
|
||||
@ -4349,6 +4347,11 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
|
||||
// pass in a Value to a C function!).
|
||||
masm.bind(&indexWouldExceedCapacity);
|
||||
|
||||
OutOfLineParallelAbort *bail = oolParallelAbort(
|
||||
ParallelBailoutOutOfMemory, ins);
|
||||
if (!bail)
|
||||
return false;
|
||||
|
||||
// The use of registers here is somewhat subtle. We need to
|
||||
// save and restore the volatile registers but we also need to
|
||||
// preserve the ReturnReg. Normally we'd just add a constraint
|
||||
@ -4375,7 +4378,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
|
||||
masm.freeStack(sizeof(ParPushArgs));
|
||||
masm.movePtr(ReturnReg, object);
|
||||
masm.PopRegsInMask(saveSet);
|
||||
masm.branchTestPtr(Assembler::Zero, object, object, bail);
|
||||
masm.branchTestPtr(Assembler::Zero, object, object, bail->entry());
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@ -4384,7 +4387,11 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
|
||||
// sparse array, and since we don't want to think about that
|
||||
// case right now, we just bail out.
|
||||
masm.bind(&indexNotInitLen);
|
||||
masm.jump(bail);
|
||||
OutOfLineParallelAbort *bail1 =
|
||||
oolParallelAbort(ParallelBailoutUnsupportedSparseArray, ins);
|
||||
if (!bail1)
|
||||
return false;
|
||||
masm.jump(bail1->entry());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6423,16 +6430,19 @@ CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
|
||||
bool
|
||||
CodeGenerator::visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool)
|
||||
{
|
||||
// Subtle: Do not pass the script associated with `current`, which
|
||||
// is often an inlined script or something like that, but rather the
|
||||
// "outermost" JSScript.
|
||||
ParallelBailoutCause cause = ool->cause();
|
||||
jsbytecode *bytecode = ool->bytecode();
|
||||
|
||||
MIRGraph &graph = current->mir()->graph();
|
||||
MBasicBlock *entryBlock = graph.entryBlock();
|
||||
masm.move32(Imm32(cause), CallTempReg0);
|
||||
loadOutermostJSScript(CallTempReg1);
|
||||
loadJSScriptForBlock(ool->basicBlock(), CallTempReg2);
|
||||
masm.movePtr(ImmWord((void *) bytecode), CallTempReg3);
|
||||
|
||||
masm.movePtr(ImmWord((void *) entryBlock->info().script()), CallTempReg0);
|
||||
masm.setupUnalignedABICall(1, CallTempReg1);
|
||||
masm.setupUnalignedABICall(4, CallTempReg4);
|
||||
masm.passABIArg(CallTempReg0);
|
||||
masm.passABIArg(CallTempReg1);
|
||||
masm.passABIArg(CallTempReg2);
|
||||
masm.passABIArg(CallTempReg3);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelAbort));
|
||||
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
@ -6462,6 +6472,44 @@ CodeGenerator::visitIsCallable(LIsCallable *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::loadOutermostJSScript(Register reg)
|
||||
{
|
||||
// The "outermost" JSScript means the script that we are compiling
|
||||
// basically; this is not always the script associated with the
|
||||
// current basic block, which might be an inlined script.
|
||||
|
||||
MIRGraph &graph = current->mir()->graph();
|
||||
MBasicBlock *entryBlock = graph.entryBlock();
|
||||
masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::loadJSScriptForBlock(MBasicBlock *block, Register reg)
|
||||
{
|
||||
// The current JSScript means the script for the current
|
||||
// basic block. This may be an inlined script.
|
||||
|
||||
JSScript *script = block->info().script();
|
||||
masm.movePtr(ImmGCPtr(script), reg);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitOutOfLinePropagateParallelAbort(OutOfLinePropagateParallelAbort *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 *, PropagateParallelAbort));
|
||||
|
||||
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
masm.jump(returnLabel_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
|
||||
{
|
||||
|
@ -248,6 +248,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
|
||||
bool visitOutOfLineParNewGCThing(OutOfLineParNewGCThing *ool);
|
||||
bool visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool);
|
||||
bool visitOutOfLinePropagateParallelAbort(OutOfLinePropagateParallelAbort *ool);
|
||||
void loadJSScriptForBlock(MBasicBlock *block, Register reg);
|
||||
void loadOutermostJSScript(Register reg);
|
||||
|
||||
// Inline caches visitors.
|
||||
bool visitOutOfLineCache(OutOfLineUpdateCache *ool);
|
||||
@ -281,17 +284,19 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
|
||||
PropertyName *name, TypedOrValueRegister output,
|
||||
bool allowGetters);
|
||||
bool checkForParallelBailout(LInstruction *lir);
|
||||
|
||||
bool checkForParallelBailout();
|
||||
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
|
||||
|
||||
bool emitParAllocateGCThing(const Register &objReg,
|
||||
bool emitParAllocateGCThing(LInstruction *lir,
|
||||
const Register &objReg,
|
||||
const Register &threadContextReg,
|
||||
const Register &tempReg1,
|
||||
const Register &tempReg2,
|
||||
JSObject *templateObj);
|
||||
|
||||
bool emitParCallToUncompiledScript(Register calleeReg);
|
||||
bool emitParCallToUncompiledScript(LInstruction *lir,
|
||||
Register calleeReg);
|
||||
|
||||
void emitLambdaInit(const Register &resultReg,
|
||||
const Register &scopeChainReg,
|
||||
|
@ -538,8 +538,17 @@ HandleParallelFailure(ResumeFromException *rfe)
|
||||
|
||||
while (!iter.isEntry()) {
|
||||
parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
|
||||
if (!slice->abortedScript && iter.isScripted())
|
||||
slice->abortedScript = iter.script();
|
||||
if (iter.isScripted()) {
|
||||
slice->bailoutRecord->setCause(ParallelBailoutFailedIC,
|
||||
iter.script(), iter.script(), NULL);
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
while (!iter.isEntry()) {
|
||||
if (iter.isScripted())
|
||||
PropagateParallelAbort(iter.script(), iter.script());
|
||||
++iter;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ LIRGenerator::visitParCheckOverRecursed(MParCheckOverRecursed *ins)
|
||||
LParCheckOverRecursed *lir = new LParCheckOverRecursed(
|
||||
useRegister(ins->parSlice()),
|
||||
temp());
|
||||
if (!add(lir))
|
||||
if (!add(lir, ins))
|
||||
return false;
|
||||
if (!assignSafepoint(lir, ins))
|
||||
return false;
|
||||
@ -293,8 +293,7 @@ LIRGenerator::visitPassArg(MPassArg *arg)
|
||||
|
||||
// Known types can move constant types and/or payloads.
|
||||
LStackArgT *stack = new LStackArgT(argslot, useRegisterOrConstant(opd));
|
||||
stack->setMir(arg);
|
||||
return add(stack);
|
||||
return add(stack, arg);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1640,9 +1639,11 @@ LIRGenerator::visitParSlice(MParSlice *ins)
|
||||
bool
|
||||
LIRGenerator::visitParWriteGuard(MParWriteGuard *ins)
|
||||
{
|
||||
return add(new LParWriteGuard(useFixed(ins->parSlice(), CallTempReg0),
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2)));
|
||||
LParWriteGuard *lir = new LParWriteGuard(useFixed(ins->parSlice(), CallTempReg0),
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
lir->setMir(ins);
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1651,7 +1652,7 @@ LIRGenerator::visitParCheckInterrupt(MParCheckInterrupt *ins)
|
||||
LParCheckInterrupt *lir = new LParCheckInterrupt(
|
||||
useRegister(ins->parSlice()),
|
||||
temp());
|
||||
if (!add(lir))
|
||||
if (!add(lir, ins))
|
||||
return false;
|
||||
if (!assignSafepoint(lir, ins))
|
||||
return false;
|
||||
@ -2610,8 +2611,7 @@ LIRGenerator::visitAsmJSCall(MAsmJSCall *ins)
|
||||
|
||||
LInstruction *lir = new LAsmJSCall(args, ins->numOperands());
|
||||
if (ins->type() == MIRType_None) {
|
||||
lir->setMir(ins);
|
||||
return add(lir);
|
||||
return add(lir, ins);
|
||||
}
|
||||
return defineReturn(lir, ins);
|
||||
}
|
||||
|
@ -119,24 +119,30 @@ bool
|
||||
ion::ParCheckOverRecursed(ForkJoinSlice *slice)
|
||||
{
|
||||
JS_ASSERT(ForkJoinSlice::Current() == slice);
|
||||
int stackDummy_;
|
||||
|
||||
// When an interrupt is triggered, we currently overwrite the
|
||||
// stack limit with a sentinel value that brings us here.
|
||||
// When an interrupt is triggered, the main thread stack limit is
|
||||
// overwritten with a sentinel value that brings us here.
|
||||
// Therefore, we must check whether this is really a stack overrun
|
||||
// and, if not, check whether an interrupt is needed.
|
||||
if (slice->isMainThread()) {
|
||||
int stackDummy_;
|
||||
if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(slice->runtime()), &stackDummy_))
|
||||
return false;
|
||||
return ParCheckInterrupt(slice);
|
||||
} else {
|
||||
// FIXME---we don't ovewrite the stack limit for worker
|
||||
// threads, which means that technically they can recurse
|
||||
// forever---or at least a long time---without ever checking
|
||||
// the interrupt. it also means that if we get here on a
|
||||
// worker thread, this is a real stack overrun!
|
||||
//
|
||||
// When not on the main thread, we don't overwrite the stack
|
||||
// limit, but we do still call into this routine if the interrupt
|
||||
// flag is set, so we still need to double check.
|
||||
|
||||
uintptr_t realStackLimit;
|
||||
if (slice->isMainThread())
|
||||
realStackLimit = js::GetNativeStackLimit(slice->runtime());
|
||||
else
|
||||
realStackLimit = slice->perThreadData->ionStackLimit;
|
||||
|
||||
if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
|
||||
slice->bailoutRecord->setCause(ParallelBailoutOverRecursed,
|
||||
NULL, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ParCheckInterrupt(slice);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -144,8 +150,14 @@ ion::ParCheckInterrupt(ForkJoinSlice *slice)
|
||||
{
|
||||
JS_ASSERT(ForkJoinSlice::Current() == slice);
|
||||
bool result = slice->check();
|
||||
if (!result)
|
||||
if (!result) {
|
||||
// Do not set the cause here. Either it was set by this
|
||||
// thread already by some code that then triggered an abort,
|
||||
// or else we are just picking up an abort from some other
|
||||
// thread. Either way we have nothing useful to contribute so
|
||||
// we might as well leave our bailout case unset.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -362,23 +374,48 @@ js::ion::ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v
|
||||
}
|
||||
|
||||
void
|
||||
ion::ParallelAbort(JSScript *script)
|
||||
ion::ParallelAbort(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *bytecode)
|
||||
{
|
||||
// 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);
|
||||
|
||||
JS_ASSERT(InParallelSection());
|
||||
JS_ASSERT(outermostScript != NULL);
|
||||
JS_ASSERT(currentScript != NULL);
|
||||
JS_ASSERT(outermostScript->hasParallelIonScript());
|
||||
|
||||
ForkJoinSlice *slice = ForkJoinSlice::Current();
|
||||
|
||||
Spew(SpewBailouts, "Parallel abort in %p:%s:%d (hasParallelIonScript:%d)",
|
||||
script, script->filename(), script->lineno,
|
||||
script->hasParallelIonScript());
|
||||
JS_ASSERT(slice->bailoutRecord->depth == 0);
|
||||
slice->bailoutRecord->setCause(cause, outermostScript,
|
||||
currentScript, bytecode);
|
||||
}
|
||||
|
||||
// Otherwise what the heck are we executing?
|
||||
JS_ASSERT(script->hasParallelIonScript());
|
||||
void
|
||||
ion::PropagateParallelAbort(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);
|
||||
|
||||
if (!slice->abortedScript)
|
||||
slice->abortedScript = script;
|
||||
else
|
||||
script->parallelIonScript()->setHasInvalidatedCallTarget();
|
||||
JS_ASSERT(InParallelSection());
|
||||
JS_ASSERT(outermostScript->hasParallelIonScript());
|
||||
|
||||
outermostScript->parallelIonScript()->setHasInvalidatedCallTarget();
|
||||
|
||||
ForkJoinSlice *slice = ForkJoinSlice::Current();
|
||||
if (currentScript)
|
||||
slice->bailoutRecord->addTrace(currentScript, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -55,7 +55,13 @@ ParallelResult ParGreaterThanOrEqual(ForkJoinSlice *slice, MutableHandleValue v1
|
||||
ParallelResult ParStringsEqual(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
|
||||
ParallelResult ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
|
||||
|
||||
void ParallelAbort(JSScript *script);
|
||||
void ParallelAbort(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *bytecode);
|
||||
|
||||
void PropagateParallelAbort(JSScript *outermostScript,
|
||||
JSScript *currentScript);
|
||||
|
||||
void TraceLIR(uint32_t bblock, uint32_t lir, uint32_t execModeInt,
|
||||
const char *lirOpName, const char *mirOpName,
|
||||
|
@ -205,11 +205,11 @@ CodeGeneratorARM::bailoutFrom(Label *label, 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.
|
||||
Label *ool;
|
||||
if (!ensureOutOfLineParallelAbort(&ool))
|
||||
return false;
|
||||
masm.retarget(label, ool);
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineParallelAbort *ool = oolParallelAbort(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
masm.retarget(label, ool->entry());
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
|
@ -35,7 +35,6 @@ CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg)
|
||||
|
||||
CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg)
|
||||
: oolIns(NULL),
|
||||
oolParallelAbort_(NULL),
|
||||
maybeMasm_(),
|
||||
masm(ensureMasm(masmArg)),
|
||||
gen(gen),
|
||||
@ -541,17 +540,40 @@ CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorShared::ensureOutOfLineParallelAbort(Label **result)
|
||||
OutOfLineParallelAbort *
|
||||
CodeGeneratorShared::oolParallelAbort(ParallelBailoutCause cause,
|
||||
MBasicBlock *basicBlock,
|
||||
jsbytecode *bytecode)
|
||||
{
|
||||
if (!oolParallelAbort_) {
|
||||
oolParallelAbort_ = new OutOfLineParallelAbort();
|
||||
if (!addOutOfLineCode(oolParallelAbort_))
|
||||
return false;
|
||||
}
|
||||
OutOfLineParallelAbort *ool = new OutOfLineParallelAbort(cause, basicBlock, bytecode);
|
||||
if (!ool || !addOutOfLineCode(ool))
|
||||
return NULL;
|
||||
return ool;
|
||||
}
|
||||
|
||||
*result = oolParallelAbort_->entry();
|
||||
return true;
|
||||
OutOfLineParallelAbort *
|
||||
CodeGeneratorShared::oolParallelAbort(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 oolParallelAbort(cause, block, pc);
|
||||
}
|
||||
|
||||
OutOfLinePropagateParallelAbort *
|
||||
CodeGeneratorShared::oolPropagateParallelAbort(LInstruction *lir)
|
||||
{
|
||||
OutOfLinePropagateParallelAbort *ool = new OutOfLinePropagateParallelAbort(lir);
|
||||
if (!ool || !addOutOfLineCode(ool))
|
||||
return NULL;
|
||||
return ool;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -561,6 +583,13 @@ OutOfLineParallelAbort::generate(CodeGeneratorShared *codegen)
|
||||
return codegen->visitOutOfLineParallelAbort(this);
|
||||
}
|
||||
|
||||
bool
|
||||
OutOfLinePropagateParallelAbort::generate(CodeGeneratorShared *codegen)
|
||||
{
|
||||
codegen->callTraceLIR(0xDEADBEEF, NULL, "ParallelBailout");
|
||||
return codegen->visitOutOfLinePropagateParallelAbort(this);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
|
||||
const char *bailoutName)
|
||||
|
@ -26,6 +26,7 @@ class CodeGenerator;
|
||||
class MacroAssembler;
|
||||
class IonCache;
|
||||
class OutOfLineParallelAbort;
|
||||
class OutOfLinePropagateParallelAbort;
|
||||
|
||||
template <class ArgSeq, class StoreOutputTo>
|
||||
class OutOfLineCallVM;
|
||||
@ -36,7 +37,6 @@ class CodeGeneratorShared : public LInstructionVisitor
|
||||
{
|
||||
js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
|
||||
OutOfLineCode *oolIns;
|
||||
OutOfLineParallelAbort *oolParallelAbort_;
|
||||
|
||||
MacroAssembler &ensureMasm(MacroAssembler *masm);
|
||||
mozilla::Maybe<MacroAssembler> maybeMasm_;
|
||||
@ -344,13 +344,28 @@ class CodeGeneratorShared : public LInstructionVisitor
|
||||
bool visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool);
|
||||
|
||||
public:
|
||||
// When compiling parallel code, all bailouts just abort funnel to
|
||||
// this same point and hence abort execution altogether:
|
||||
virtual bool visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool) = 0;
|
||||
bool callTraceLIR(uint32_t blockIndex, LInstruction *lir, const char *bailoutName = NULL);
|
||||
|
||||
protected:
|
||||
bool ensureOutOfLineParallelAbort(Label **result);
|
||||
// Parallel aborts:
|
||||
//
|
||||
// Parallel aborts work somewhat differently from sequential
|
||||
// bailouts. When an abort occurs, we first invoke
|
||||
// ParReportBailout() 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 `oolParallelAbort` is currently
|
||||
// only used for error reporting, so that we can provide feedback
|
||||
// to the user about which instruction aborted and (perhaps) why.
|
||||
OutOfLineParallelAbort *oolParallelAbort(ParallelBailoutCause cause,
|
||||
MBasicBlock *basicBlock,
|
||||
jsbytecode *bytecode);
|
||||
OutOfLineParallelAbort *oolParallelAbort(ParallelBailoutCause cause,
|
||||
LInstruction *lir);
|
||||
OutOfLinePropagateParallelAbort *oolPropagateParallelAbort(LInstruction *lir);
|
||||
virtual bool visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool) = 0;
|
||||
virtual bool visitOutOfLinePropagateParallelAbort(OutOfLinePropagateParallelAbort *ool) = 0;
|
||||
};
|
||||
|
||||
// An out-of-line path is generated at the end of the function.
|
||||
@ -592,14 +607,52 @@ CodeGeneratorShared::visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo>
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// An out-of-line parallel abort thunk.
|
||||
// Initiate a parallel abort. The snapshot is used to record the
|
||||
// cause.
|
||||
class OutOfLineParallelAbort : public OutOfLineCode
|
||||
{
|
||||
private:
|
||||
ParallelBailoutCause cause_;
|
||||
MBasicBlock *basicBlock_;
|
||||
jsbytecode *bytecode_;
|
||||
|
||||
public:
|
||||
OutOfLineParallelAbort()
|
||||
OutOfLineParallelAbort(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 OutOfLinePropagateParallelAbort : public OutOfLineCode
|
||||
{
|
||||
private:
|
||||
LInstruction *lir_;
|
||||
|
||||
public:
|
||||
OutOfLinePropagateParallelAbort(LInstruction *lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
LInstruction *lir() { return lir_; }
|
||||
|
||||
bool generate(CodeGeneratorShared *codegen);
|
||||
};
|
||||
|
||||
|
@ -283,11 +283,11 @@ 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.
|
||||
Label *ool;
|
||||
if (!ensureOutOfLineParallelAbort(&ool))
|
||||
return false;
|
||||
binder(masm, ool);
|
||||
// in parallel mode, make no attempt to recover, just signal an error.
|
||||
OutOfLineParallelAbort *ool = oolParallelAbort(ParallelBailoutUnsupported,
|
||||
snapshot->mir()->block(),
|
||||
snapshot->mir()->pc());
|
||||
binder(masm, ool->entry());
|
||||
return true;
|
||||
}
|
||||
case SequentialExecution:
|
||||
|
@ -109,6 +109,22 @@ ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
|
||||
JS_NOT_REACHED("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript,
|
||||
JSScript *currentScript,
|
||||
jsbytecode *currentPc)
|
||||
{
|
||||
JS_NOT_REACHED("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
void
|
||||
ParallelBailoutRecord::addTrace(JSScript *script,
|
||||
jsbytecode *pc)
|
||||
{
|
||||
JS_NOT_REACHED("Not THREADSAFE build");
|
||||
}
|
||||
|
||||
#endif // !JS_THREADSAFE || !JS_ION
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -158,7 +174,11 @@ class ParallelDo
|
||||
// For tests, make sure to keep this in sync with minItemsTestingThreshold.
|
||||
const static uint32_t MAX_BAILOUTS = 3;
|
||||
uint32_t bailouts;
|
||||
AutoScriptVector pendingInvalidations_;
|
||||
|
||||
// Information about the bailout:
|
||||
ParallelBailoutCause bailoutCause;
|
||||
RootedScript bailoutScript;
|
||||
jsbytecode *bailoutBytecode;
|
||||
|
||||
ParallelDo(JSContext *cx, HandleObject fun);
|
||||
ExecutionStatus apply();
|
||||
@ -166,11 +186,13 @@ class ParallelDo
|
||||
private:
|
||||
JSContext *cx_;
|
||||
HeapPtrObject fun_;
|
||||
Vector<ParallelBailoutRecord, 16> bailoutRecords;
|
||||
|
||||
inline bool executeSequentially();
|
||||
|
||||
MethodStatus compileForParallelExecution();
|
||||
ExecutionStatus disqualifyFromParallelExecution();
|
||||
void determineBailoutCause();
|
||||
bool invalidateBailedOutScripts();
|
||||
bool warmupForParallelExecution();
|
||||
ParallelResult executeInParallel();
|
||||
@ -190,7 +212,7 @@ class ForkJoinShared : public TaskExecutor, public Monitor
|
||||
const uint32_t numSlices_; // Total number of threads.
|
||||
PRCondVar *rendezvousEnd_; // Cond. var used to signal end of rendezvous.
|
||||
PRLock *cxLock_; // Locks cx_ for parallel VM calls.
|
||||
AutoScriptVector &pendingInvalidations_; // From ParallelDo
|
||||
ParallelBailoutRecord *const records_; // Bailout records for each slice
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// Per-thread arenas
|
||||
@ -258,7 +280,7 @@ class ForkJoinShared : public TaskExecutor, public Monitor
|
||||
HandleObject fun,
|
||||
uint32_t numSlices,
|
||||
uint32_t uncompleted,
|
||||
AutoScriptVector &pendingInvalidations);
|
||||
ParallelBailoutRecord *records);
|
||||
~ForkJoinShared();
|
||||
|
||||
bool init();
|
||||
@ -353,14 +375,25 @@ js::ForkJoin(JSContext *cx, CallArgs &args)
|
||||
RootedObject feedback(cx, &args[1].toObject());
|
||||
if (feedback && feedback->isFunction()) {
|
||||
InvokeArgsGuard feedbackArgs;
|
||||
if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
|
||||
if (!cx->stack.pushInvokeArgs(cx, 3, &feedbackArgs))
|
||||
return false;
|
||||
|
||||
const char *resultString;
|
||||
switch (status) {
|
||||
case ExecutionParallel:
|
||||
resultString = (op.bailouts == 0 ? "success" : "bailout");
|
||||
break;
|
||||
|
||||
case ExecutionFatal:
|
||||
case ExecutionSequential:
|
||||
resultString = "disqualified";
|
||||
break;
|
||||
}
|
||||
feedbackArgs.setCallee(ObjectValue(*feedback));
|
||||
feedbackArgs.setThis(UndefinedValue());
|
||||
if (status == ExecutionParallel)
|
||||
feedbackArgs[0].setInt32(op.bailouts);
|
||||
else
|
||||
feedbackArgs[0] = cx->runtime->positiveInfinityValue;
|
||||
feedbackArgs[0].setString(JS_NewStringCopyZ(cx, resultString));
|
||||
feedbackArgs[1].setInt32(op.bailouts);
|
||||
feedbackArgs[2].setInt32(op.bailoutCause);
|
||||
if (!Invoke(cx, feedbackArgs))
|
||||
return false;
|
||||
}
|
||||
@ -371,9 +404,12 @@ js::ForkJoin(JSContext *cx, CallArgs &args)
|
||||
|
||||
js::ParallelDo::ParallelDo(JSContext *cx, HandleObject fun)
|
||||
: bailouts(0),
|
||||
pendingInvalidations_(cx),
|
||||
bailoutCause(ParallelBailoutNone),
|
||||
bailoutScript(cx),
|
||||
bailoutBytecode(NULL),
|
||||
cx_(cx),
|
||||
fun_(fun)
|
||||
fun_(fun),
|
||||
bailoutRecords(cx)
|
||||
{ }
|
||||
|
||||
ExecutionStatus
|
||||
@ -381,15 +417,23 @@ js::ParallelDo::apply()
|
||||
{
|
||||
SpewBeginOp(cx_, "ParallelDo");
|
||||
|
||||
uint32_t slices = ForkJoinSlices(cx_);
|
||||
|
||||
if (!ion::IsEnabled(cx_))
|
||||
return SpewEndOp(disqualifyFromParallelExecution());
|
||||
|
||||
if (!pendingInvalidations_.resize(ForkJoinSlices(cx_)))
|
||||
if (!bailoutRecords.resize(slices))
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
|
||||
for (uint32_t i = 0; i < slices; i++)
|
||||
bailoutRecords[i].init(cx_);
|
||||
|
||||
// Try to execute in parallel. If a bailout occurs, re-warmup
|
||||
// and then try again. Repeat this a few times.
|
||||
while (bailouts < MAX_BAILOUTS) {
|
||||
for (uint32_t i = 0; i < slices; i++)
|
||||
bailoutRecords[i].reset(cx_);
|
||||
|
||||
MethodStatus status = compileForParallelExecution();
|
||||
if (status == Method_Error)
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
@ -414,8 +458,9 @@ js::ParallelDo::apply()
|
||||
}
|
||||
|
||||
bailouts += 1;
|
||||
determineBailoutCause();
|
||||
|
||||
SpewBailout(bailouts);
|
||||
SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
|
||||
|
||||
if (!invalidateBailedOutScripts())
|
||||
return SpewEndOp(ExecutionFatal);
|
||||
@ -493,6 +538,73 @@ js::ParallelDo::disqualifyFromParallelExecution()
|
||||
return ExecutionSequential;
|
||||
}
|
||||
|
||||
static const char *
|
||||
BailoutExplanation(ParallelBailoutCause cause)
|
||||
{
|
||||
switch (cause) {
|
||||
case ParallelBailoutNone:
|
||||
return "no particular reason";
|
||||
case ParallelBailoutCompilationSkipped:
|
||||
return "compilation failed (method skipped)";
|
||||
case ParallelBailoutCompilationFailure:
|
||||
return "compilation failed";
|
||||
case ParallelBailoutInterrupt:
|
||||
return "interrupted";
|
||||
case ParallelBailoutFailedIC:
|
||||
return "at runtime, the behavior changed, invalidating compiled code (IC update)";
|
||||
case ParallelBailoutHeapBusy:
|
||||
return "heap busy flag set during interrupt";
|
||||
case ParallelBailoutMainScriptNotPresent:
|
||||
return "main script not present";
|
||||
case ParallelBailoutCalledToUncompiledScript:
|
||||
return "called to uncompiled script";
|
||||
case ParallelBailoutIllegalWrite:
|
||||
return "illegal write";
|
||||
case ParallelBailoutAccessToIntrinsic:
|
||||
return "access to intrinsic";
|
||||
case ParallelBailoutOverRecursed:
|
||||
return "over recursed";
|
||||
case ParallelBailoutOutOfMemory:
|
||||
return "out of memory";
|
||||
case ParallelBailoutUnsupported:
|
||||
return "unsupported";
|
||||
case ParallelBailoutUnsupportedStringComparison:
|
||||
return "unsupported string comparison";
|
||||
case ParallelBailoutUnsupportedSparseArray:
|
||||
return "unsupported sparse array";
|
||||
default:
|
||||
return "no known reason";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelDo::determineBailoutCause()
|
||||
{
|
||||
bailoutCause = ParallelBailoutNone;
|
||||
for (uint32_t i = 0; i < bailoutRecords.length(); i++) {
|
||||
if (bailoutRecords[i].cause == ParallelBailoutNone)
|
||||
continue;
|
||||
|
||||
if (bailoutRecords[i].cause == ParallelBailoutInterrupt)
|
||||
continue;
|
||||
|
||||
bailoutCause = bailoutRecords[i].cause;
|
||||
const char *causeStr = BailoutExplanation(bailoutCause);
|
||||
if (bailoutRecords[i].depth) {
|
||||
bailoutScript = bailoutRecords[i].trace[0].script;
|
||||
bailoutBytecode = bailoutRecords[i].trace[0].bytecode;
|
||||
|
||||
const char *filename = bailoutScript->filename();
|
||||
int line = JS_PCToLineNumber(cx_, bailoutScript, bailoutBytecode);
|
||||
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%d",
|
||||
causeStr, filename, line);
|
||||
} else {
|
||||
JS_ReportWarning(cx_, "Bailed out of parallel operation: %s",
|
||||
causeStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
js::ParallelDo::invalidateBailedOutScripts()
|
||||
{
|
||||
@ -501,19 +613,35 @@ js::ParallelDo::invalidateBailedOutScripts()
|
||||
// Sometimes the script is collected or invalidated already,
|
||||
// for example when a full GC runs at an inconvenient time.
|
||||
if (!script->hasParallelIonScript()) {
|
||||
JS_ASSERT(hasNoPendingInvalidations());
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector<types::RecompileInfo> invalid(cx_);
|
||||
for (uint32_t i = 0; i < pendingInvalidations_.length(); i++) {
|
||||
JSScript *script = pendingInvalidations_[i];
|
||||
if (script && !hasScript(invalid, script)) {
|
||||
JS_ASSERT(script->hasParallelIonScript());
|
||||
if (!invalid.append(script->parallelIonScript()->recompileInfo()))
|
||||
return false;
|
||||
for (uint32_t i = 0; i < bailoutRecords.length(); i++) {
|
||||
JSScript *script = bailoutRecords[i].topScript;
|
||||
|
||||
// No script to invalidate.
|
||||
if (!script || !script->hasParallelIonScript())
|
||||
continue;
|
||||
|
||||
switch (bailoutRecords[i].cause) {
|
||||
// An interrupt is not the fault of the script, so don't
|
||||
// invalidate it.
|
||||
case ParallelBailoutInterrupt: continue;
|
||||
|
||||
// An illegal write will not be made legal by invalidation.
|
||||
case ParallelBailoutIllegalWrite: continue;
|
||||
|
||||
// For other cases, consider invalidation.
|
||||
default: break;
|
||||
}
|
||||
pendingInvalidations_[i] = NULL;
|
||||
|
||||
// Already invalidated.
|
||||
if (hasScript(invalid, script))
|
||||
continue;
|
||||
|
||||
if (!invalid.append(script->parallelIonScript()->recompileInfo()))
|
||||
return false;
|
||||
}
|
||||
Invalidate(cx_, invalid);
|
||||
return true;
|
||||
@ -568,8 +696,7 @@ js::ParallelDo::executeInParallel()
|
||||
uint32_t numSlices = ForkJoinSlices(cx_);
|
||||
|
||||
RootedObject rootedFun(cx_, fun_);
|
||||
ForkJoinShared shared(cx_, threadPool, rootedFun, numSlices, numSlices - 1,
|
||||
pendingInvalidations_);
|
||||
ForkJoinShared shared(cx_, threadPool, rootedFun, numSlices, numSlices - 1, &bailoutRecords[0]);
|
||||
if (!shared.init())
|
||||
return TP_RETRY_SEQUENTIALLY;
|
||||
|
||||
@ -586,15 +713,6 @@ js::ParallelDo::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *scrip
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
js::ParallelDo::hasNoPendingInvalidations() {
|
||||
for (uint32_t i = 0; i < pendingInvalidations_.length(); i++) {
|
||||
if (pendingInvalidations_[i] != NULL)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can only enter callees with a valid IonScript.
|
||||
template <uint32_t maxArgc>
|
||||
class ParallelIonInvoke
|
||||
@ -644,14 +762,14 @@ ForkJoinShared::ForkJoinShared(JSContext *cx,
|
||||
HandleObject fun,
|
||||
uint32_t numSlices,
|
||||
uint32_t uncompleted,
|
||||
AutoScriptVector &pendingInvalidations)
|
||||
ParallelBailoutRecord *records)
|
||||
: cx_(cx),
|
||||
threadPool_(threadPool),
|
||||
fun_(fun),
|
||||
numSlices_(numSlices),
|
||||
rendezvousEnd_(NULL),
|
||||
cxLock_(NULL),
|
||||
pendingInvalidations_(pendingInvalidations),
|
||||
records_(records),
|
||||
allocators_(cx),
|
||||
uncompleted_(uncompleted),
|
||||
blocked_(0),
|
||||
@ -805,7 +923,8 @@ ForkJoinShared::executePortion(PerThreadData *perThread,
|
||||
// Therefore, it should NOT access `cx_` in any way!
|
||||
|
||||
Allocator *allocator = allocators_[threadId];
|
||||
ForkJoinSlice slice(perThread, threadId, numSlices_, allocator, this);
|
||||
ForkJoinSlice slice(perThread, threadId, numSlices_, allocator,
|
||||
this, &records_[threadId]);
|
||||
AutoSetForkJoinSlice autoContext(&slice);
|
||||
|
||||
Spew(SpewOps, "Up");
|
||||
@ -814,7 +933,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread,
|
||||
// re-enter the VM.
|
||||
IonContext icx(cx_->compartment, NULL);
|
||||
|
||||
JS_ASSERT(pendingInvalidations_[slice.sliceId] == NULL);
|
||||
JS_ASSERT(slice.bailoutRecord->topScript == NULL);
|
||||
|
||||
RootedObject fun(perThread, fun_);
|
||||
JS_ASSERT(fun->isFunction());
|
||||
@ -825,6 +944,8 @@ ForkJoinShared::executePortion(PerThreadData *perThread,
|
||||
// op and reaching this point. In that case, we just fail
|
||||
// and fallback.
|
||||
Spew(SpewOps, "Down (Script no longer present)");
|
||||
slice.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent,
|
||||
NULL, NULL, NULL);
|
||||
setAbortFlag(false);
|
||||
} else {
|
||||
ParallelIonInvoke<3> fii(cx_->compartment, callee, 3);
|
||||
@ -834,15 +955,9 @@ ForkJoinShared::executePortion(PerThreadData *perThread,
|
||||
fii.args[2] = BooleanValue(false);
|
||||
|
||||
bool ok = fii.invoke(perThread);
|
||||
JS_ASSERT(ok == !slice.abortedScript);
|
||||
if (!ok) {
|
||||
JSScript *script = slice.abortedScript;
|
||||
Spew(SpewBailouts, "Aborted script: %p (hasParallelIonScript? %d)",
|
||||
script, script->hasParallelIonScript());
|
||||
JS_ASSERT(script->hasParallelIonScript());
|
||||
pendingInvalidations_[slice.sliceId] = script;
|
||||
JS_ASSERT(ok == !slice.bailoutRecord->topScript);
|
||||
if (!ok)
|
||||
setAbortFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
Spew(SpewOps, "Down");
|
||||
@ -1008,12 +1123,13 @@ ForkJoinShared::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
|
||||
|
||||
ForkJoinSlice::ForkJoinSlice(PerThreadData *perThreadData,
|
||||
uint32_t sliceId, uint32_t numSlices,
|
||||
Allocator *allocator, ForkJoinShared *shared)
|
||||
Allocator *allocator, ForkJoinShared *shared,
|
||||
ParallelBailoutRecord *bailoutRecord)
|
||||
: perThreadData(perThreadData),
|
||||
sliceId(sliceId),
|
||||
numSlices(numSlices),
|
||||
allocator(allocator),
|
||||
abortedScript(NULL),
|
||||
bailoutRecord(bailoutRecord),
|
||||
shared(shared)
|
||||
{ }
|
||||
|
||||
@ -1084,7 +1200,61 @@ js::ForkJoinSlices(JSContext *cx)
|
||||
return cx->runtime->threadPool.numWorkers() + 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ParallelBailoutRecord
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::init(JSContext *cx)
|
||||
{
|
||||
reset(cx);
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::reset(JSContext *cx)
|
||||
{
|
||||
topScript = NULL;
|
||||
cause = ParallelBailoutNone;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
void
|
||||
js::ParallelBailoutRecord::setCause(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);
|
||||
|
||||
this->cause = cause;
|
||||
|
||||
if (outermostScript) {
|
||||
this->topScript = outermostScript;
|
||||
}
|
||||
|
||||
if (currentScript) {
|
||||
addTrace(currentScript, currentPc);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL && script != NULL)
|
||||
topScript = script;
|
||||
|
||||
if (depth < MaxDepth) {
|
||||
trace[depth].script = script;
|
||||
trace[depth].bytecode = pc;
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -1273,11 +1443,19 @@ class ParallelSpewer
|
||||
statusColor, ExecutionStatusToString(status), reset());
|
||||
}
|
||||
|
||||
void bailout(uint32_t count) {
|
||||
void bailout(uint32_t count, HandleScript script,
|
||||
jsbytecode *pc, ParallelBailoutCause cause) {
|
||||
if (!active[SpewOps])
|
||||
return;
|
||||
|
||||
spew(SpewOps, "%s%sBAILOUT %d%s", bold(), yellow(), count, reset());
|
||||
const char *filename = "";
|
||||
unsigned line=0, column=0;
|
||||
if (script) {
|
||||
line = PCToLineNumber(script, pc, &column);
|
||||
filename = script->filename();
|
||||
}
|
||||
|
||||
spew(SpewOps, "%s%sBAILOUT %d%s: %d at %s:%d:%d", bold(), yellow(), count, reset(), cause, filename, line, column);
|
||||
}
|
||||
|
||||
void beginCompile(HandleScript script) {
|
||||
@ -1376,9 +1554,10 @@ parallel::SpewEndOp(ExecutionStatus status)
|
||||
}
|
||||
|
||||
void
|
||||
parallel::SpewBailout(uint32_t count)
|
||||
parallel::SpewBailout(uint32_t count, HandleScript script,
|
||||
jsbytecode *pc, ParallelBailoutCause cause)
|
||||
{
|
||||
spewer.bailout(count);
|
||||
spewer.bailout(count, script, pc, cause);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -35,10 +35,10 @@
|
||||
// ThreadPool.h---by default, N is the number of cores on the
|
||||
// computer).
|
||||
//
|
||||
// Typically there will be one call to |parallel()| from each worker thread,
|
||||
// but that is not something you should rely upon---if we implement
|
||||
// work-stealing, for example, then it could be that a single worker thread
|
||||
// winds up handling multiple slices.
|
||||
// Typically, each of the N slices will execute from a different
|
||||
// worker thread, but that is not something you should rely upon---if
|
||||
// we implement work-stealing, for example, then it could be that a
|
||||
// single worker thread winds up handling multiple slices.
|
||||
//
|
||||
// The second argument, |feedback|, is an optional callback that will
|
||||
// receiver information about how execution proceeded. This is
|
||||
@ -112,36 +112,38 @@
|
||||
// parallelization and just invoke |func()| N times in a row (once
|
||||
// for each worker) but with |warmup| set to false.
|
||||
//
|
||||
// Operation callback
|
||||
// ------------------
|
||||
// Operation callback:
|
||||
//
|
||||
// During parallel execution, you should periodically invoke |slice.check()|,
|
||||
// which will handle the operation callback. If the operation callback is
|
||||
// necessary, |slice.check()| will arrange a rendezvous---that is, as each
|
||||
// active worker invokes |check()|, it will come to a halt until everyone is
|
||||
// blocked (Stop The World). At this point, we perform the callback on the
|
||||
// main thread, and then resume execution. If a worker thread terminates
|
||||
// before calling |check()|, that's fine too. We assume that you do not do
|
||||
// unbounded work without invoking |check()|.
|
||||
// During parallel execution, |slice.check()| must be periodically
|
||||
// invoked to check for the operation callback. This is automatically
|
||||
// done by the ion-generated code. If the operation callback is
|
||||
// necessary, |slice.check()| will arrange a rendezvous---that is, as
|
||||
// each active worker invokes |check()|, it will come to a halt until
|
||||
// everyone is blocked (Stop The World). At this point, we perform
|
||||
// the callback on the main thread, and then resume execution. If a
|
||||
// worker thread terminates before calling |check()|, that's fine too.
|
||||
// We assume that you do not do unbounded work without invoking
|
||||
// |check()|.
|
||||
//
|
||||
// Sequential Fallback:
|
||||
// Bailout tracing and recording:
|
||||
//
|
||||
// It is assumed that anyone using this API must be prepared for a sequential
|
||||
// fallback. Therefore, the |ExecuteForkJoinOp()| returns a status code
|
||||
// indicating whether a fatal error occurred (in which case you should just
|
||||
// stop) or whether you should retry the operation, but executing
|
||||
// sequentially. An example of where the fallback would be useful is if the
|
||||
// parallel code encountered an unexpected path that cannot safely be executed
|
||||
// in parallel (writes to shared state, say).
|
||||
// When a bailout occurs, we record a bit of state so that we can
|
||||
// recover with grace. Each |ForkJoinSlice| has a pointer to a
|
||||
// |ParallelBailoutRecord| pre-allocated for this purpose. This
|
||||
// structure is used to record the cause of the bailout, the JSScript
|
||||
// which was executing, as well as the location in the source where
|
||||
// the bailout occurred (in principle, we can record a full stack
|
||||
// trace, but right now we only record the top-most frame). Note that
|
||||
// the error location might not be in the same JSScript as the one
|
||||
// which was executing due to inlining.
|
||||
//
|
||||
// Garbage collection and allocation:
|
||||
//
|
||||
// Code which executes on these parallel threads must be very careful
|
||||
// with respect to garbage collection and allocation. Currently, we
|
||||
// do not permit GC to occur when executing in parallel. Furthermore,
|
||||
// the typical allocation paths are UNSAFE in parallel code because
|
||||
// they access shared state (the compartment's arena lists and so
|
||||
// forth) without any synchronization.
|
||||
// with respect to garbage collection and allocation. The typical
|
||||
// allocation paths are UNSAFE in parallel code because they access
|
||||
// shared state (the compartment's arena lists and so forth) without
|
||||
// any synchronization. They can also trigger GC in an ad-hoc way.
|
||||
//
|
||||
// To deal with this, the forkjoin code creates a distinct |Allocator|
|
||||
// object for each slice. You can access the appropriate object via
|
||||
@ -166,17 +168,18 @@
|
||||
// Current Limitations:
|
||||
//
|
||||
// - The API does not support recursive or nested use. That is, the
|
||||
// |parallel()| callback of a |ForkJoinOp| may not itself invoke
|
||||
// |ExecuteForkJoinOp()|. We may lift this limitation in the future.
|
||||
// JavaScript function given to |ForkJoin| should not itself invoke
|
||||
// |ForkJoin()|. Instead, use the intrinsic |InParallelSection()| to
|
||||
// check for recursive use and execute a sequential fallback.
|
||||
//
|
||||
// - No load balancing is performed between worker threads. That means that
|
||||
// the fork-join system is best suited for problems that can be slice into
|
||||
// uniform bits.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace js {
|
||||
|
||||
struct ForkJoinShared;
|
||||
struct ForkJoinSlice;
|
||||
|
||||
bool ForkJoin(JSContext *cx, CallArgs &args);
|
||||
@ -203,6 +206,68 @@ struct IonLIRTraceData {
|
||||
// Different subcategories of the "bail" state are encoded as variants of
|
||||
// TP_RETRY_*.
|
||||
enum ParallelResult { TP_SUCCESS, TP_RETRY_SEQUENTIALLY, TP_RETRY_AFTER_GC, TP_FATAL };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Bailout tracking
|
||||
|
||||
enum ParallelBailoutCause {
|
||||
ParallelBailoutNone,
|
||||
|
||||
// compiler returned Method_Skipped
|
||||
ParallelBailoutCompilationSkipped,
|
||||
|
||||
// compiler returned Method_CantCompile
|
||||
ParallelBailoutCompilationFailure,
|
||||
|
||||
// the periodic interrupt failed, which can mean that either
|
||||
// another thread canceled, the user interrupted us, etc
|
||||
ParallelBailoutInterrupt,
|
||||
|
||||
// an IC update failed
|
||||
ParallelBailoutFailedIC,
|
||||
|
||||
// Heap busy flag was set during interrupt
|
||||
ParallelBailoutHeapBusy,
|
||||
|
||||
ParallelBailoutMainScriptNotPresent,
|
||||
ParallelBailoutCalledToUncompiledScript,
|
||||
ParallelBailoutIllegalWrite,
|
||||
ParallelBailoutAccessToIntrinsic,
|
||||
ParallelBailoutOverRecursed,
|
||||
ParallelBailoutOutOfMemory,
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedStringComparison,
|
||||
ParallelBailoutUnsupportedSparseArray,
|
||||
};
|
||||
|
||||
struct ParallelBailoutTrace {
|
||||
JSScript *script;
|
||||
jsbytecode *bytecode;
|
||||
};
|
||||
|
||||
// See "Bailouts" section in comment above.
|
||||
struct ParallelBailoutRecord {
|
||||
JSScript *topScript;
|
||||
ParallelBailoutCause cause;
|
||||
|
||||
// Eventually we will support deeper traces,
|
||||
// but for now we gather at most a single frame.
|
||||
static const uint32_t MaxDepth = 1;
|
||||
uint32_t depth;
|
||||
ParallelBailoutTrace trace[MaxDepth];
|
||||
|
||||
void init(JSContext *cx);
|
||||
void reset(JSContext *cx);
|
||||
void setCause(ParallelBailoutCause cause,
|
||||
JSScript *outermostScript, // inliner (if applicable)
|
||||
JSScript *currentScript, // inlinee (if applicable)
|
||||
jsbytecode *currentPc);
|
||||
void addTrace(JSScript *script,
|
||||
jsbytecode *pc);
|
||||
};
|
||||
|
||||
struct ForkJoinShared;
|
||||
|
||||
struct ForkJoinSlice
|
||||
{
|
||||
public:
|
||||
@ -220,8 +285,8 @@ struct ForkJoinSlice
|
||||
// |perThreadData|.
|
||||
Allocator *const allocator;
|
||||
|
||||
// If we took a parallel bailout, the script that bailed out is stored here.
|
||||
JSScript *abortedScript;
|
||||
// Bailout record used to record the reason this thread stopped executing
|
||||
ParallelBailoutRecord *const bailoutRecord;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Records the last instr. to execute on this thread.
|
||||
@ -229,7 +294,8 @@ struct ForkJoinSlice
|
||||
#endif
|
||||
|
||||
ForkJoinSlice(PerThreadData *perThreadData, uint32_t sliceId, uint32_t numSlices,
|
||||
Allocator *arenaLists, ForkJoinShared *shared);
|
||||
Allocator *arenaLists, ForkJoinShared *shared,
|
||||
ParallelBailoutRecord *bailoutRecord);
|
||||
|
||||
// True if this is the main thread, false if it is one of the parallel workers.
|
||||
bool isMainThread();
|
||||
@ -286,7 +352,15 @@ struct ForkJoinSlice
|
||||
ForkJoinShared *const shared;
|
||||
};
|
||||
|
||||
// Locks a JSContext for its scope.
|
||||
// Locks a JSContext for its scope. Be very careful, because locking a
|
||||
// JSContext does *not* allow you to safely mutate the data in the
|
||||
// JSContext unless you can guarantee that any of the other threads
|
||||
// that want to access that data will also acquire the lock, which is
|
||||
// generally not the case. For example, the lock is used in the IC
|
||||
// code to allow us to atomically patch up the dispatch table, but we
|
||||
// must be aware that other threads may be reading from the table even
|
||||
// as we write to it (though they cannot be writing, since they must
|
||||
// hold the lock to write).
|
||||
class LockedJSContext
|
||||
{
|
||||
#if defined(JS_THREADSAFE) && defined(JS_ION)
|
||||
@ -353,7 +427,8 @@ enum SpewChannel {
|
||||
bool SpewEnabled(SpewChannel channel);
|
||||
void Spew(SpewChannel channel, const char *fmt, ...);
|
||||
void SpewBeginOp(JSContext *cx, const char *name);
|
||||
void SpewBailout(uint32_t count);
|
||||
void SpewBailout(uint32_t count, HandleScript script, jsbytecode *pc,
|
||||
ParallelBailoutCause cause);
|
||||
ExecutionStatus SpewEndOp(ExecutionStatus status);
|
||||
void SpewBeginCompile(HandleScript script);
|
||||
ion::MethodStatus SpewEndCompile(ion::MethodStatus status);
|
||||
@ -366,7 +441,8 @@ void SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
|
||||
static inline bool SpewEnabled(SpewChannel channel) { return false; }
|
||||
static inline void Spew(SpewChannel channel, const char *fmt, ...) { }
|
||||
static inline void SpewBeginOp(JSContext *cx, const char *name) { }
|
||||
static inline void SpewBailout(uint32_t count) {}
|
||||
static inline void SpewBailout(uint32_t count, HandleScript script,
|
||||
jsbytecode *pc, ParallelBailoutCause cause) {}
|
||||
static inline ExecutionStatus SpewEndOp(ExecutionStatus status) { return status; }
|
||||
static inline void SpewBeginCompile(HandleScript script) { }
|
||||
#ifdef JS_ION
|
||||
|
Loading…
Reference in New Issue
Block a user