From 488bdb76a7c9ba7240d496ab7da6c02ece834894 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 7 Feb 2014 14:40:31 -0800 Subject: [PATCH] Bug 949296 - Ignore DontStopIon interrupt triggers during ForkJoin. (r=nmatsakis) --- .../tests/parallel/Array-mapPar-nested.js | 5 +- js/src/jit/BaselineJIT.cpp | 2 +- js/src/jit/CodeGenerator.cpp | 27 ++++------ js/src/jit/CodeGenerator.h | 6 +-- js/src/jit/CompileWrappers.cpp | 8 +++ js/src/jit/CompileWrappers.h | 4 ++ js/src/jit/Ion.cpp | 1 + js/src/jit/IonMacroAssembler.cpp | 9 ++-- js/src/jit/IonMacroAssembler.h | 2 +- js/src/jit/LIR-Common.h | 6 +-- js/src/jit/LOpcodes.h | 2 +- js/src/jit/Lowering.cpp | 6 +-- js/src/jit/Lowering.h | 2 +- js/src/jit/MIR.h | 10 ++-- js/src/jit/MOpcodes.h | 2 +- js/src/jit/ParallelFunctions.cpp | 4 +- js/src/jit/ParallelFunctions.h | 2 +- js/src/jit/ParallelSafetyAnalysis.cpp | 4 +- js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jscntxt.cpp | 4 ++ js/src/vm/ForkJoin.cpp | 54 +++++++++---------- js/src/vm/ForkJoin.h | 3 ++ js/src/vm/Runtime.cpp | 9 +++- js/src/vm/Runtime.h | 18 +++++-- js/src/vm/SelfHosting.cpp | 2 +- 25 files changed, 113 insertions(+), 81 deletions(-) diff --git a/js/src/jit-test/tests/parallel/Array-mapPar-nested.js b/js/src/jit-test/tests/parallel/Array-mapPar-nested.js index 4b6bcafcb65..d08b89c0cc5 100644 --- a/js/src/jit-test/tests/parallel/Array-mapPar-nested.js +++ b/js/src/jit-test/tests/parallel/Array-mapPar-nested.js @@ -17,6 +17,5 @@ function test() { } } -// FIXME: Bug 949296. Broken due to all interrupt triggers aborting PJS. -//if (getBuildConfiguration().parallelJS) -// test(); +if (getBuildConfiguration().parallelJS) + test(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index a59481647d6..825ce701b1f 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -275,7 +275,7 @@ CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr) // parallel execution. We want to avoid the situation of OSRing during // warmup and only gathering type information for the loop, and not the // rest of the function. - if (IsJSDEnabled(cx) || cx->runtime()->parallelWarmup > 0) { + if (IsJSDEnabled(cx) || cx->runtime()->forkJoinWarmup > 0) { if (osr) return Method_Skipped; } else if (script->incUseCount() <= js_JitOptions.baselineUsesBeforeCompile) { diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index e1d8c0b99c1..1c06b107fec 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2851,7 +2851,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir) if (!addOutOfLineCode(ool)) return false; masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry()); - masm.checkInterruptFlagsPar(tempReg, ool->entry()); + masm.checkInterruptFlagPar(tempReg, ool->entry()); masm.bind(ool->rejoin()); return true; @@ -2886,41 +2886,36 @@ CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool } // Out-of-line path to report over-recursed error and fail. -class OutOfLineCheckInterruptPar : public OutOfLineCodeBase +class OutOfLineInterruptCheckPar : public OutOfLineCodeBase { public: - LCheckInterruptPar *const lir; + LInterruptCheckPar *const lir; - OutOfLineCheckInterruptPar(LCheckInterruptPar *lir) + OutOfLineInterruptCheckPar(LInterruptCheckPar *lir) : lir(lir) { } bool accept(CodeGenerator *codegen) { - return codegen->visitOutOfLineCheckInterruptPar(this); + return codegen->visitOutOfLineInterruptCheckPar(this); } }; bool -CodeGenerator::visitCheckInterruptPar(LCheckInterruptPar *lir) +CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir) { // First check for cx->shared->interrupt_. - OutOfLineCheckInterruptPar *ool = new(alloc()) OutOfLineCheckInterruptPar(lir); + OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir); if (!addOutOfLineCode(ool)) return false; - // We must check two flags: - // - runtime->interrupt - // - runtime->parallelAbort - // See vm/ForkJoin.h for discussion on why we use this design. - Register tempReg = ToRegister(lir->getTempReg()); - masm.checkInterruptFlagsPar(tempReg, ool->entry()); + masm.checkInterruptFlagPar(tempReg, ool->entry()); masm.bind(ool->rejoin()); return true; } bool -CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool) +CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool) { OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir); if (!bail) @@ -2929,7 +2924,7 @@ CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool) // 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(): - LCheckInterruptPar *lir = ool->lir; + LInterruptCheckPar *lir = ool->lir; Register tempReg = ToRegister(lir->getTempReg()); RegisterSet saveSet(lir->safepoint()->liveRegs()); saveSet.takeUnchecked(tempReg); @@ -2938,7 +2933,7 @@ CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool) masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0); masm.setupUnalignedABICall(1, CallTempReg1); masm.passABIArg(CallTempReg0); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckInterruptPar)); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar)); masm.movePtr(ReturnReg, tempReg); masm.PopRegsInMask(saveSet); masm.branchIfFalseBool(tempReg, bail->entry()); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 22ba680de81..4e0111aee69 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -30,7 +30,7 @@ class OutOfLineNewArray; class OutOfLineNewObject; class CheckOverRecursedFailure; class CheckOverRecursedFailurePar; -class OutOfLineCheckInterruptPar; +class OutOfLineInterruptCheckPar; class OutOfLineInterruptCheckImplicit; class OutOfLineUnboxFloatingPoint; class OutOfLineStoreElementHole; @@ -291,8 +291,8 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir); bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool); - bool visitCheckInterruptPar(LCheckInterruptPar *lir); - bool visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool); + bool visitInterruptCheckPar(LInterruptCheckPar *lir); + bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool); bool visitInterruptCheckImplicit(LInterruptCheckImplicit *ins); bool visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ins); diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 37cb3efea5c..671589a00a6 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -71,6 +71,14 @@ CompileRuntime::addressOfInterrupt() return &runtime()->interrupt; } +#ifdef JS_THREADSAFE +const void * +CompileRuntime::addressOfInterruptPar() +{ + return &runtime()->interruptPar; +} +#endif + const JitRuntime * CompileRuntime::jitRuntime() { diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index 2a8bc4f8d30..208080a0ba2 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -52,6 +52,10 @@ class CompileRuntime const void *addressOfInterrupt(); +#ifdef JS_THREADSAFE + const void *addressOfInterruptPar(); +#endif + const JitRuntime *jitRuntime(); // Compilation does not occur off thread when the SPS profiler is enabled. diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 3ba423383fb..d6501a06c40 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -433,6 +433,7 @@ jit::TriggerOperationCallbackForIonCode(JSRuntime *rt, break; case JSRuntime::TriggerCallbackAnyThreadDontStopIon: + case JSRuntime::TriggerCallbackAnyThreadForkJoin: // When the trigger does not require Ion code to be interrupted, // nothing more needs to be done. break; diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index 6202a0f3103..3f10669e4f2 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -875,11 +875,14 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register } void -MacroAssembler::checkInterruptFlagsPar(const Register &tempReg, - Label *fail) +MacroAssembler::checkInterruptFlagPar(const Register &tempReg, Label *fail) { - movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterrupt()), tempReg); +#ifdef JS_THREADSAFE + movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg); branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail); +#else + MOZ_ASSUME_UNREACHABLE("JSRuntime::interruptPar doesn't exist on non-threadsafe builds."); +#endif } static void diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index b3b3286d3b7..a61d9b649f2 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -797,7 +797,7 @@ class MacroAssembler : public MacroAssemblerSpecific // Checks the flags that signal that parallel code may need to interrupt or // abort. Branches to fail in that case. - void checkInterruptFlagsPar(const Register &tempReg, Label *fail); + void checkInterruptFlagPar(const Register &tempReg, Label *fail); // If the JitCode that created this assembler needs to transition into the VM, // we want to store the JitCode on the stack in order to mark it during a GC. diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 3e0a900a69a..1cbfda332e5 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -737,12 +737,12 @@ class LInterruptCheckImplicit : public LInstructionHelper<0, 0, 0> } }; -class LCheckInterruptPar : public LInstructionHelper<0, 1, 1> +class LInterruptCheckPar : public LInstructionHelper<0, 1, 1> { public: - LIR_HEADER(CheckInterruptPar); + LIR_HEADER(InterruptCheckPar); - LCheckInterruptPar(const LAllocation &cx, const LDefinition &tempReg) { + LInterruptCheckPar(const LAllocation &cx, const LDefinition &tempReg) { setOperand(0, cx); setTemp(0, tempReg); } diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 05981d2079b..03c46126a8a 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -285,7 +285,7 @@ _(AsmJSPassStackArg) \ _(AsmJSCall) \ _(AsmJSCheckOverRecursed) \ - _(CheckInterruptPar) \ + _(InterruptCheckPar) \ _(RecompileCheck) \ _(AssertRangeI) \ _(AssertRangeD) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 56c19c35e31..c21cfcf6d61 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2140,10 +2140,10 @@ LIRGenerator::visitInterruptCheck(MInterruptCheck *ins) } bool -LIRGenerator::visitCheckInterruptPar(MCheckInterruptPar *ins) +LIRGenerator::visitInterruptCheckPar(MInterruptCheckPar *ins) { - LCheckInterruptPar *lir = - new(alloc()) LCheckInterruptPar(useRegister(ins->forkJoinContext()), temp()); + LInterruptCheckPar *lir = + new(alloc()) LInterruptCheckPar(useRegister(ins->forkJoinContext()), temp()); if (!add(lir, ins)) return false; if (!assignSafepoint(lir, ins)) diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index cec0a296bdb..8d18ff23f18 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -164,7 +164,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitForkJoinContext(MForkJoinContext *ins); bool visitGuardThreadExclusive(MGuardThreadExclusive *ins); bool visitInterruptCheck(MInterruptCheck *ins); - bool visitCheckInterruptPar(MCheckInterruptPar *ins); + bool visitInterruptCheckPar(MInterruptCheckPar *ins); bool visitStoreSlot(MStoreSlot *ins); bool visitTypeBarrier(MTypeBarrier *ins); bool visitMonitorTypes(MMonitorTypes *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index a42d19a8b2f..023268db8b0 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4747,9 +4747,9 @@ class MCheckOverRecursedPar : public MUnaryInstruction }; // Check for an interrupt (or rendezvous) in parallel mode. -class MCheckInterruptPar : public MUnaryInstruction +class MInterruptCheckPar : public MUnaryInstruction { - MCheckInterruptPar(MDefinition *cx) + MInterruptCheckPar(MDefinition *cx) : MUnaryInstruction(cx) { setResultType(MIRType_None); @@ -4758,10 +4758,10 @@ class MCheckInterruptPar : public MUnaryInstruction } public: - INSTRUCTION_HEADER(CheckInterruptPar); + INSTRUCTION_HEADER(InterruptCheckPar); - static MCheckInterruptPar *New(TempAllocator &alloc, MDefinition *cx) { - return new(alloc) MCheckInterruptPar(cx); + static MInterruptCheckPar *New(TempAllocator &alloc, MDefinition *cx) { + return new(alloc) MInterruptCheckPar(cx); } MDefinition *forkJoinContext() const { diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 0958fddce6f..85e49fece67 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -213,7 +213,7 @@ namespace jit { _(RestPar) \ _(ForkJoinContext) \ _(GuardThreadExclusive) \ - _(CheckInterruptPar) \ + _(InterruptCheckPar) \ _(RecompileCheck) // Forward declarations of MIR types. diff --git a/js/src/jit/ParallelFunctions.cpp b/js/src/jit/ParallelFunctions.cpp index 83c91b2884c..f4c99828326 100644 --- a/js/src/jit/ParallelFunctions.cpp +++ b/js/src/jit/ParallelFunctions.cpp @@ -208,11 +208,11 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx) return false; } - return CheckInterruptPar(cx); + return InterruptCheckPar(cx); } bool -jit::CheckInterruptPar(ForkJoinContext *cx) +jit::InterruptCheckPar(ForkJoinContext *cx) { JS_ASSERT(ForkJoinContext::current() == cx); bool result = cx->check(); diff --git a/js/src/jit/ParallelFunctions.h b/js/src/jit/ParallelFunctions.h index a2d68fc37d7..e903e65c30b 100644 --- a/js/src/jit/ParallelFunctions.h +++ b/js/src/jit/ParallelFunctions.h @@ -21,7 +21,7 @@ JSObject *NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind); bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object); bool IsInTargetRegion(ForkJoinContext *cx, TypedDatum *object); bool CheckOverRecursedPar(ForkJoinContext *cx); -bool CheckInterruptPar(ForkJoinContext *cx); +bool InterruptCheckPar(ForkJoinContext *cx); // Extends the given array with `length` new holes. Returns nullptr on // failure or else `array`, which is convenient during code diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index e4fa1a18939..f69d9d9b938 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -291,7 +291,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor UNSAFE_OP(In) UNSAFE_OP(InArray) SAFE_OP(GuardThreadExclusive) - SAFE_OP(CheckInterruptPar) + SAFE_OP(InterruptCheckPar) SAFE_OP(CheckOverRecursedPar) SAFE_OP(FunctionDispatch) SAFE_OP(TypeObjectDispatch) @@ -746,7 +746,7 @@ ParallelSafetyVisitor::visitCheckOverRecursed(MCheckOverRecursed *ins) bool ParallelSafetyVisitor::visitInterruptCheck(MInterruptCheck *ins) { - return replace(ins, MCheckInterruptPar::New(alloc(), ForkJoinContext())); + return replace(ins, MInterruptCheckPar::New(alloc(), ForkJoinContext())); } ///////////////////////////////////////////////////////////////////////////// diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 96386254887..c7002a474fc 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -951,7 +951,7 @@ CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir) } else { // The interrupt check should be the first instruction in the // loop header other than the initial label and move groups. - JS_ASSERT(iter->isInterruptCheck() || iter->isCheckInterruptPar()); + JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar()); return nullptr; } } diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index fc04d986ef2..004edf8368e 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1019,6 +1019,10 @@ js_InvokeOperationCallback(JSContext *cx) js::gc::GCIfNeeded(cx); #ifdef JS_ION +#ifdef JS_THREADSAFE + rt->interruptPar = false; +#endif + /* * A worker thread may have set the callback after finishing an Ion * compilation. diff --git a/js/src/vm/ForkJoin.cpp b/js/src/vm/ForkJoin.cpp index d7aa21e6cc6..a67b30c6f89 100644 --- a/js/src/vm/ForkJoin.cpp +++ b/js/src/vm/ForkJoin.cpp @@ -174,7 +174,7 @@ ExecuteSequentially(JSContext *cx, HandleValue funVal) return false; args.setCallee(funVal); args.setThis(UndefinedValue()); - args[0].setBoolean(!!cx->runtime()->parallelWarmup); + args[0].setBoolean(!!cx->runtime()->forkJoinWarmup); return fig.invoke(cx); } @@ -389,7 +389,7 @@ class ForkJoinShared : public ParallelJob, public Monitor void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason); // Requests that computation abort. - void setAbortFlag(bool fatal); + void setAbortFlagAndTriggerOperationCallback(bool fatal); // Set the fatal flag for the next abort. void setPendingAbortFatal() { fatal_ = true; } @@ -407,8 +407,8 @@ class AutoEnterWarmup JSRuntime *runtime_; public: - AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; } - ~AutoEnterWarmup() { runtime_->parallelWarmup--; } + AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->forkJoinWarmup++; } + ~AutoEnterWarmup() { runtime_->forkJoinWarmup--; } }; class AutoSetForkJoinContext @@ -841,18 +841,8 @@ ForkJoinOperation::compileForParallelExecution(ExecutionStatus *status) } } - if (allScriptsPresent) { - // For testing modes, we want to make sure that all off thread - // compilation tasks are finished, so we don't race with - // off-main-thread-compilation setting an interrupt flag while we - // are in the middle of a test, causing unexpected bailouts. - if (mode_ != ForkJoinModeNormal) { - StopAllOffThreadCompilations(cx_->compartment()); - if (!js_HandleExecutionInterrupt(cx_)) - return fatalError(status); - } + if (allScriptsPresent) break; - } } Spew(SpewCompile, "Compilation complete (final worklist length %d)", @@ -1406,7 +1396,7 @@ ForkJoinShared::execute() // Sometimes a GC request occurs *just before* we enter into the // parallel section. Rather than enter into the parallel section // and then abort, we just check here and abort early. - if (cx_->runtime()->interrupt) + if (cx_->runtime()->interruptPar) return TP_RETRY_SEQUENTIALLY; AutoLockMonitor lock(*this); @@ -1464,7 +1454,7 @@ ForkJoinShared::executeFromWorker(uint32_t workerId, uintptr_t stackLimit) { PerThreadData thisThread(cx_->runtime()); if (!thisThread.init()) { - setAbortFlag(true); + setAbortFlagAndTriggerOperationCallback(true); return false; } TlsPerThreadData.set(&thisThread); @@ -1526,7 +1516,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId) // and fallback. Spew(SpewOps, "Down (Script no longer present)"); cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent); - setAbortFlag(false); + setAbortFlagAndTriggerOperationCallback(false); } else { ParallelIonInvoke<2> fii(cx_->runtime(), fun_, 1); @@ -1535,7 +1525,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId) bool ok = fii.invoke(perThread); JS_ASSERT(ok == !cx.bailoutRecord->topScript); if (!ok) - setAbortFlag(false); + setAbortFlagAndTriggerOperationCallback(false); } Spew(SpewOps, "Down"); @@ -1544,7 +1534,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId) bool ForkJoinShared::check(ForkJoinContext &cx) { - JS_ASSERT(cx_->runtime()->interrupt); + JS_ASSERT(cx_->runtime()->interruptPar); if (abort_) return false; @@ -1555,14 +1545,14 @@ ForkJoinShared::check(ForkJoinContext &cx) if (cx.isMainThread() || !threadPool_->isMainThreadActive()) { JS_ASSERT(!cx_->runtime()->gcIsNeeded); - if (cx_->runtime()->interrupt) { + if (cx_->runtime()->interruptPar) { // The GC Needed flag should not be set during parallel // execution. Instead, one of the requestGC() or // requestZoneGC() methods should be invoked. JS_ASSERT(!cx_->runtime()->gcIsNeeded); cx.bailoutRecord->setCause(ParallelBailoutInterrupt); - setAbortFlag(false); + setAbortFlagAndTriggerOperationCallback(false); return false; } } @@ -1571,16 +1561,16 @@ ForkJoinShared::check(ForkJoinContext &cx) } void -ForkJoinShared::setAbortFlag(bool fatal) +ForkJoinShared::setAbortFlagAndTriggerOperationCallback(bool fatal) { AutoLockMonitor lock(*this); abort_ = true; fatal_ = fatal_ || fatal; - // Note: DontStopIon here avoids the expensive memory protection needed to + // Note: The ForkJoin trigger here avoids the expensive memory protection needed to // interrupt Ion code compiled for sequential execution. - cx_->runtime()->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon); + cx_->runtime()->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadForkJoin); } void @@ -1681,7 +1671,7 @@ ForkJoinContext::hasAcquiredJSContext() const bool ForkJoinContext::check() { - if (runtime()->interrupt) + if (runtime()->interruptPar) return shared->check(*this); else return true; @@ -1692,7 +1682,7 @@ ForkJoinContext::requestGC(JS::gcreason::Reason reason) { shared->requestGC(reason); bailoutRecord->setCause(ParallelBailoutRequestedGC); - shared->setAbortFlag(false); + shared->setAbortFlagAndTriggerOperationCallback(false); } void @@ -1700,7 +1690,7 @@ ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason) { shared->requestZoneGC(zone, reason); bailoutRecord->setCause(ParallelBailoutRequestedZoneGC); - shared->setAbortFlag(false); + shared->setAbortFlagAndTriggerOperationCallback(false); } bool @@ -2145,6 +2135,14 @@ js::ParallelTestsShouldPass(JSContext *cx) cx->runtime()->gcZeal() == 0; } +void +js::TriggerOperationCallbackForForkJoin(JSRuntime *rt, + JSRuntime::OperationCallbackTrigger trigger) +{ + if (trigger != JSRuntime::TriggerCallbackAnyThreadDontStopIon) + rt->interruptPar = true; +} + bool js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp) { diff --git a/js/src/vm/ForkJoin.h b/js/src/vm/ForkJoin.h index 8b88fb2f312..cc0040aa136 100644 --- a/js/src/vm/ForkJoin.h +++ b/js/src/vm/ForkJoin.h @@ -465,6 +465,9 @@ bool InExclusiveParallelSection(); bool ParallelTestsShouldPass(JSContext *cx); +void TriggerOperationCallbackForForkJoin(JSRuntime *rt, + JSRuntime::OperationCallbackTrigger trigger); + bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp); extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 8efeb675772..968aa30621f 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -120,6 +120,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) ), mainThread(this), interrupt(0), +#if defined(JS_THREADSAFE) && defined(JS_ION) + interruptPar(false), +#endif handlingSignal(false), operationCallback(nullptr), #ifdef JS_THREADSAFE @@ -293,7 +296,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) threadPool(this), defaultJSContextCallback(nullptr), ctypesActivityCallback(nullptr), - parallelWarmup(0), + forkJoinWarmup(0), ionReturnOverride_(MagicValue(JS_ARG_POISON)), useHelperThreads_(useHelperThreads), parallelIonCompilationEnabled_(true), @@ -654,6 +657,10 @@ JSRuntime::triggerOperationCallback(OperationCallbackTrigger trigger) interrupt = 1; #ifdef JS_ION +#ifdef JS_THREADSAFE + TriggerOperationCallbackForForkJoin(this, trigger); +#endif + /* * asm.js and, optionally, normal Ion code use memory protection and signal * handlers to halt running code. diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 7681b536781..d29f4705156 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -689,6 +689,15 @@ struct JSRuntime : public JS::shadow::Runtime, int32_t interrupt; #endif +#if defined(JS_THREADSAFE) && defined(JS_ION) + /* + * If non-zero, ForkJoin should service an interrupt. This is a separate + * flag from |interrupt| because we cannot use the mprotect trick with PJS + * code and ignore the TriggerCallbackAnyThreadDontStopIon trigger. + */ + mozilla::Atomic interruptPar; +#endif + /* Set when handling a signal for a thread associated with this runtime. */ bool handlingSignal; @@ -1627,9 +1636,9 @@ struct JSRuntime : public JS::shadow::Runtime, js::CTypesActivityCallback ctypesActivityCallback; - // Non-zero if this is a parallel warmup execution. See - // js::parallel::Do() for more information. - uint32_t parallelWarmup; + // Non-zero if this is a ForkJoin warmup execution. See + // js::ForkJoin() for more information. + uint32_t forkJoinWarmup; private: // In certain cases, we want to optimize certain opcodes to typed instructions, @@ -1722,7 +1731,8 @@ struct JSRuntime : public JS::shadow::Runtime, enum OperationCallbackTrigger { TriggerCallbackMainThread, TriggerCallbackAnyThread, - TriggerCallbackAnyThreadDontStopIon + TriggerCallbackAnyThreadDontStopIon, + TriggerCallbackAnyThreadForkJoin }; void triggerOperationCallback(OperationCallbackTrigger trigger); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 20cf4ff52ab..414fa86eef0 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -605,7 +605,7 @@ js::intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); #ifdef JS_THREADSAFE - args.rval().setBoolean(cx->runtime()->parallelWarmup || + args.rval().setBoolean(cx->runtime()->forkJoinWarmup || InParallelSection()); #else args.rval().setBoolean(true);