Bug 949296 - Ignore DontStopIon interrupt triggers during ForkJoin. (r=nmatsakis)

This commit is contained in:
Shu-yu Guo 2014-02-07 14:40:31 -08:00
parent c6165cfa30
commit 488bdb76a7
25 changed files with 113 additions and 81 deletions

View File

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

View File

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

View File

@ -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<CodeGenerator>
class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
{
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());

View File

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

View File

@ -71,6 +71,14 @@ CompileRuntime::addressOfInterrupt()
return &runtime()->interrupt;
}
#ifdef JS_THREADSAFE
const void *
CompileRuntime::addressOfInterruptPar()
{
return &runtime()->interruptPar;
}
#endif
const JitRuntime *
CompileRuntime::jitRuntime()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -285,7 +285,7 @@
_(AsmJSPassStackArg) \
_(AsmJSCall) \
_(AsmJSCheckOverRecursed) \
_(CheckInterruptPar) \
_(InterruptCheckPar) \
_(RecompileCheck) \
_(AssertRangeI) \
_(AssertRangeD) \

View File

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

View File

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

View File

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

View File

@ -213,7 +213,7 @@ namespace jit {
_(RestPar) \
_(ForkJoinContext) \
_(GuardThreadExclusive) \
_(CheckInterruptPar) \
_(InterruptCheckPar) \
_(RecompileCheck)
// Forward declarations of MIR types.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<bool, mozilla::Relaxed> 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);

View File

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