Skip to content

Instantly share code, notes, and snippets.

@syg
Created February 27, 2013 23:57
Show Gist options
  • Save syg/5053020 to your computer and use it in GitHub Desktop.
Save syg/5053020 to your computer and use it in GitHub Desktop.
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/ParallelDo.h"
#include "jsapi.h"
#include "jsobj.h"
#include "jsarray.h"
#include "ion/ParallelArrayAnalysis.h"
#include "vm/String.h"
#include "vm/GlobalObject.h"
#include "vm/ThreadPool.h"
#include "vm/ForkJoin.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#ifdef JS_ION
#include "ion/ParallelArrayAnalysis.h"
#endif // ION
#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
#include "ion/Ion.h"
#include "ion/MIR.h"
#include "ion/MIRGraph.h"
#include "ion/IonCompartment.h"
#include "prprf.h"
#endif // DEBUG && THREADSAFE && ION
using namespace js;
using namespace js::parallel;
using namespace js::ion;
//
// Debug spew
//
#if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION)
static const char *
ExecutionStatusToString(ExecutionStatus status)
{
switch (status) {
case ExecutionFatal:
return "fatal";
case ExecutionSequential:
return "sequential";
case ExecutionParallel:
return "parallel";
}
return "(unknown status)";
}
static const char *
MethodStatusToString(MethodStatus status)
{
switch (status) {
case Method_Error:
return "error";
case Method_CantCompile:
return "can't compile";
case Method_Skipped:
return "skipped";
case Method_Compiled:
return "compiled";
}
return "(unknown status)";
}
static const size_t BufferSize = 4096;
class ParallelSpewer
{
uint32_t depth;
bool colorable;
bool active[NumSpewChannels];
const char *color(const char *colorCode) {
if (!colorable)
return "";
return colorCode;
}
const char *reset() { return color("\x1b[0m"); }
const char *bold() { return color("\x1b[1m"); }
const char *red() { return color("\x1b[31m"); }
const char *green() { return color("\x1b[32m"); }
const char *yellow() { return color("\x1b[33m"); }
const char *cyan() { return color("\x1b[36m"); }
const char *sliceColor(uint32_t id) {
static const char *colors[] = {
"\x1b[7m\x1b[31m", "\x1b[7m\x1b[32m", "\x1b[7m\x1b[33m",
"\x1b[7m\x1b[34m", "\x1b[7m\x1b[35m", "\x1b[7m\x1b[36m",
"\x1b[7m\x1b[37m",
"\x1b[31m", "\x1b[32m", "\x1b[33m",
"\x1b[34m", "\x1b[35m", "\x1b[36m",
"\x1b[37m"
};
return color(colors[id % 14]);
}
public:
ParallelSpewer()
: depth(0)
{
const char *env;
PodArrayZero(active);
env = getenv("PAFLAGS");
if (env) {
if (strstr(env, "ops"))
active[SpewOps] = true;
if (strstr(env, "compile"))
active[SpewCompile] = true;
if (strstr(env, "bailouts"))
active[SpewBailouts] = true;
if (strstr(env, "full")) {
for (uint32_t i = 0; i < NumSpewChannels; i++)
active[i] = true;
}
}
env = getenv("TERM");
if (env) {
if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
colorable = true;
}
}
bool isActive(SpewChannel channel) {
return active[channel];
}
void spewVA(SpewChannel channel, const char *fmt, va_list ap) {
if (!active[channel])
return;
// Print into a buffer first so we use one fprintf, which usually
// doesn't get interrupted when running with multiple threads.
char buf[BufferSize];
if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
PR_snprintf(buf, BufferSize, "[%sParallel:%u%s] ",
sliceColor(slice->sliceId), slice->sliceId, reset());
} else {
PR_snprintf(buf, BufferSize, "[Parallel:M] ");
}
for (uint32_t i = 0; i < depth; i++)
PR_snprintf(buf + strlen(buf), BufferSize, " ");
PR_vsnprintf(buf + strlen(buf), BufferSize, fmt, ap);
PR_snprintf(buf + strlen(buf), BufferSize, "\n");
fprintf(stderr, "%s", buf);
}
void spew(SpewChannel channel, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
spewVA(channel, fmt, ap);
va_end(ap);
}
void beginOp(JSContext *cx, const char *name) {
if (!active[SpewOps])
return;
if (cx) {
jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc);
if (script && pc) {
NonBuiltinScriptFrameIter iter(cx);
if (iter.done()) {
spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
script->filename, PCToLineNumber(script, pc));
} else {
spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
iter.script()->filename, PCToLineNumber(iter.script(), iter.pc()),
script->filename, PCToLineNumber(script, pc));
}
} else {
spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
}
} else {
spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
}
depth++;
}
void endOp(ExecutionStatus status) {
if (!active[SpewOps])
return;
JS_ASSERT(depth > 0);
depth--;
const char *statusColor;
switch (status) {
case ExecutionFatal:
statusColor = red();
break;
case ExecutionSequential:
statusColor = yellow();
break;
case ExecutionParallel:
statusColor = green();
break;
default:
statusColor = reset();
break;
}
spew(SpewOps, "%sEND %s%s%s", bold(),
statusColor, ExecutionStatusToString(status), reset());
}
void bailout(uint32_t count) {
if (!active[SpewOps])
return;
spew(SpewOps, "%s%sBAILOUT %d%s", bold(), yellow(), count, reset());
}
void beginCompile(HandleScript script) {
if (!active[SpewCompile])
return;
spew(SpewCompile, "COMPILE %p:%s:%u", script.get(), script->filename, script->lineno);
depth++;
}
void endCompile(MethodStatus status) {
if (!active[SpewCompile])
return;
JS_ASSERT(depth > 0);
depth--;
const char *statusColor;
switch (status) {
case Method_Error:
case Method_CantCompile:
statusColor = red();
break;
case Method_Skipped:
statusColor = yellow();
break;
case Method_Compiled:
statusColor = green();
break;
default:
statusColor = reset();
break;
}
spew(SpewCompile, "END %s%s%s", statusColor, MethodStatusToString(status), reset());
}
void spewMIR(MDefinition *mir, const char *fmt, va_list ap) {
if (!active[SpewCompile])
return;
char buf[BufferSize];
PR_vsnprintf(buf, BufferSize, fmt, ap);
JSScript *script = mir->block()->info().script();
spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
script->filename, PCToLineNumber(script, mir->trackedPc()));
}
void spewBailoutIR(uint32_t bblockId, uint32_t lirId,
const char *lir, const char *mir, JSScript *script, jsbytecode *pc) {
if (!active[SpewBailouts])
return;
// If we didn't bail from a LIR/MIR but from a propagated parallel
// bailout, don't bother printing anything since we've printed it
// elsewhere.
if (mir && script) {
spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
lir, cyan(), mir, reset(),
bblockId, lirId,
script->filename, PCToLineNumber(script, pc));
}
}
};
// Singleton instance of the spewer.
static ParallelSpewer spewer;
bool
parallel::SpewEnabled(SpewChannel channel)
{
return spewer.isActive(channel);
}
void
parallel::Spew(SpewChannel channel, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
spewer.spewVA(channel, fmt, ap);
va_end(ap);
}
void
parallel::SpewBeginOp(JSContext *cx, const char *name)
{
spewer.beginOp(cx, name);
}
ExecutionStatus
parallel::SpewEndOp(ExecutionStatus status)
{
spewer.endOp(status);
return status;
}
void
parallel::SpewBailout(uint32_t count)
{
spewer.bailout(count);
}
void
parallel::SpewBeginCompile(HandleScript script)
{
spewer.beginCompile(script);
}
MethodStatus
parallel::SpewEndCompile(MethodStatus status)
{
spewer.endCompile(status);
return status;
}
void
parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
spewer.spewMIR(mir, fmt, ap);
va_end(ap);
}
void
parallel::SpewBailoutIR(uint32_t bblockId, uint32_t lirId,
const char *lir, const char *mir,
JSScript *script, jsbytecode *pc)
{
spewer.spewBailoutIR(bblockId, lirId, lir, mir, script, pc);
}
#endif // DEBUG && JS_THREADSAFE && JS_ION
<<<<<<< HEAD
class AutoEnterWarmup
{
JSRuntime *runtime_;
public:
AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; }
~AutoEnterWarmup() { runtime_->parallelWarmup--; }
};
// Can only enter callees with a valid IonScript.
template <uint32_t maxArgc>
class ParallelIonInvoke
{
EnterIonCode enter_;
void *jitcode_;
void *calleeToken_;
Value argv_[maxArgc + 2];
uint32_t argc_;
public:
Value *args;
ParallelIonInvoke(JSContext *cx, HandleFunction callee, uint32_t argc)
: argc_(argc),
args(argv_ + 2)
{
JS_ASSERT(argc <= maxArgc + 2);
// Set 'callee' and 'this'.
argv_[0] = ObjectValue(*callee);
argv_[1] = UndefinedValue();
// Find JIT code pointer.
IonScript *ion = callee->nonLazyScript()->parallelIonScript();
IonCode *code = ion->method();
jitcode_ = code->raw();
enter_ = cx->compartment->ionCompartment()->enterJIT();
calleeToken_ = ParCalleeToToken(callee);
}
bool invoke() {
Value result;
enter_(jitcode_, argc_ + 1, argv_ + 1, NULL, calleeToken_, &result);
return !result.isMagic();
}
};
class ParallelDo : public ForkJoinOp
{
JSContext *cx_;
HeapPtrObject fun_;
public:
// For tests, make sure to keep this in sync with minItemsTestingThreshold.
const static uint32_t MAX_BAILOUTS = 3;
uint32_t bailouts;
Vector<RawScript, 4> pendingInvalidations;
ParallelDo(JSContext *cx, HandleObject fun)
: cx_(cx),
fun_(fun),
bailouts(0),
pendingInvalidations(cx)
{ }
ExecutionStatus apply() {
SpewBeginOp(cx_, "ParallelDo");
if (!ion::IsEnabled(cx_))
return SpewEndOp(disqualifyFromParallelExecution());
if (!pendingInvalidations.resize(ForkJoinSlices(cx_)))
return SpewEndOp(ExecutionFatal);
// Try to execute in parallel. If a bailout occurs, re-warmup
// and then try again. Repeat this a few times.
while (bailouts < MAX_BAILOUTS) {
MethodStatus status = compileForParallelExecution();
if (status == Method_Error)
return SpewEndOp(ExecutionFatal);
if (status != Method_Compiled)
return SpewEndOp(disqualifyFromParallelExecution());
ParallelResult result = js::ExecuteForkJoinOp(cx_, *this);
switch (result) {
case TP_RETRY_AFTER_GC:
Spew(SpewBailouts, "Bailout due to GC request");
break;
case TP_RETRY_SEQUENTIALLY:
Spew(SpewBailouts, "Bailout not categorized");
break;
case TP_SUCCESS:
return SpewEndOp(ExecutionParallel);
case TP_FATAL:
return SpewEndOp(ExecutionFatal);
}
bailouts += 1;
SpewBailout(bailouts);
if (!invalidateBailedOutScripts())
return SpewEndOp(ExecutionFatal);
if (!warmupForParallelExecution())
return SpewEndOp(ExecutionFatal);
}
// After enough tries, just execute sequentially.
return SpewEndOp(disqualifyFromParallelExecution());
}
MethodStatus compileForParallelExecution() {
// The kernel should be a self-hosted function.
if (!fun_->isFunction())
return Method_Skipped;
RootedFunction callee(cx_, fun_->toFunction());
if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
return Method_Skipped;
if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
return Method_Error;
// If this function has not been run enough to enable parallel
// execution, perform a warmup.
RootedScript script(cx_, callee->nonLazyScript());
if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
if (!warmupForParallelExecution())
return Method_Error;
}
if (script->hasParallelIonScript() &&
!script->parallelIonScript()->hasInvalidatedCallTarget())
{
Spew(SpewOps, "Already compiled");
return Method_Compiled;
}
Spew(SpewOps, "Compiling all reachable functions");
ParallelCompileContext compileContext(cx_);
if (!compileContext.appendToWorklist(script))
return Method_Error;
MethodStatus status = compileContext.compileTransitively();
if (status != Method_Compiled)
return status;
// it can happen that during transitive compilation, our
// callee's parallel ion script is invalidated or GC'd. So
// before we declare success, double check that it's still
// compiled!
if (!script->hasParallelIonScript())
return Method_Skipped;
return Method_Compiled;
}
ExecutionStatus disqualifyFromParallelExecution() {
if (!executeSequentially())
return ExecutionFatal;
return ExecutionSequential;
}
bool invalidateBailedOutScripts() {
RootedScript script(cx_, fun_->toFunction()->nonLazyScript());
// 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;
}
pendingInvalidations[i] = NULL;
}
Invalidate(cx_, invalid);
return true;
}
bool warmupForParallelExecution() {
AutoEnterWarmup warmup(cx_->runtime);
return executeSequentially();
}
bool executeSequentially() {
uint32_t numSlices = ForkJoinSlices(cx_);
RootedValue funVal(cx_, ObjectValue(*fun_));
FastInvokeGuard fig(cx_, funVal);
for (uint32_t i = 0; i < numSlices; i++) {
InvokeArgsGuard &args = fig.args();
if (!args.pushed() && !cx_->stack.pushInvokeArgs(cx_, 3, &args))
return false;
args.setCallee(funVal);
args.setThis(UndefinedValue());
args[0].setInt32(i);
args[1].setInt32(numSlices);
args[2].setBoolean(!!cx_->runtime->parallelWarmup);
if (!fig.invoke(cx_))
return false;
}
return true;
}
virtual bool parallel(ForkJoinSlice &slice) {
Spew(SpewOps, "Up");
// Make a new IonContext for the slice, which is needed if we need to
// re-enter the VM.
IonContext icx(cx_, cx_->compartment, NULL);
uintptr_t *myStackTop = (uintptr_t*)&icx;
JS_ASSERT(pendingInvalidations[slice.sliceId] == NULL);
// This works in concert with ForkJoinSlice::recordStackExtent
// to establish the stack extent for this slice.
slice.recordStackBase(myStackTop);
js::PerThreadData *pt = slice.perThreadData;
RootedObject fun(pt, fun_);
JS_ASSERT(fun->isFunction());
RootedFunction callee(cx_, fun->toFunction());
if (!callee->nonLazyScript()->hasParallelIonScript()) {
// Sometimes, particularly with GCZeal, the parallel ion
// script can be collected between starting the parallel
// op and reaching this point. In that case, we just fail
// and fallback.
Spew(SpewOps, "Down (Script no longer present)");
return false;
}
ParallelIonInvoke<3> fii(cx_, callee, 3);
fii.args[0] = Int32Value(slice.sliceId);
fii.args[1] = Int32Value(slice.numSlices);
fii.args[2] = BooleanValue(false);
bool ok = fii.invoke();
JS_ASSERT(ok == !slice.abortedScript);
if (!ok) {
JSScript *script = slice.abortedScript;
// Again, parallel ion script can be collected before we
// get here; ensure pendingInvalidations is initialized.
pendingInvalidations[slice.sliceId] =
script->hasParallelIonScript() ? script : NULL;
}
Spew(SpewOps, "Down");
return ok;
}
inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script) {
for (uint32_t i = 0; i < scripts.length(); i++) {
if (scripts[i] == script->parallelIonScript()->recompileInfo())
return true;
}
return false;
}
inline bool hasNoPendingInvalidations() {
for (uint32_t i = 0; i < pendingInvalidations.length(); i++) {
if (pendingInvalidations[i] != NULL)
return false;
}
return true;
}
};
bool
js::parallel::Do(JSContext *cx, CallArgs &args)
{
JS_ASSERT(args[0].isObject());
JS_ASSERT(args[0].toObject().isFunction());
RootedObject fun(cx, &args[0].toObject());
ParallelDo op(cx, fun);
ExecutionStatus status = op.apply();
if (status == ExecutionFatal)
return false;
if (args[1].isObject()) {
RootedObject feedback(cx, &args[1].toObject());
if (feedback && feedback->isFunction()) {
InvokeArgsGuard feedbackArgs;
if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
return false;
feedbackArgs.setCallee(ObjectValue(*feedback));
feedbackArgs.setThis(UndefinedValue());
if (status == ExecutionParallel)
feedbackArgs[0].setInt32(op.bailouts);
else
feedbackArgs[0] = cx->runtime->positiveInfinityValue;
if (!Invoke(cx, feedbackArgs))
return false;
}
}
return true;
}
||||||| merged common ancestors
=======
#ifdef JS_ION
class AutoEnterWarmup
{
JSRuntime *runtime_;
public:
AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; }
~AutoEnterWarmup() { runtime_->parallelWarmup--; }
};
// Can only enter callees with a valid IonScript.
template <uint32_t maxArgc>
class ParallelIonInvoke
{
EnterIonCode enter_;
void *jitcode_;
void *calleeToken_;
Value argv_[maxArgc + 2];
uint32_t argc_;
public:
Value *args;
ParallelIonInvoke(JSContext *cx, HandleFunction callee, uint32_t argc)
: argc_(argc),
args(argv_ + 2)
{
JS_ASSERT(argc <= maxArgc + 2);
// Set 'callee' and 'this'.
argv_[0] = ObjectValue(*callee);
argv_[1] = UndefinedValue();
// Find JIT code pointer.
IonScript *ion = callee->nonLazyScript()->parallelIonScript();
IonCode *code = ion->method();
jitcode_ = code->raw();
enter_ = cx->compartment->ionCompartment()->enterJIT();
calleeToken_ = CalleeToToken(callee);
}
bool invoke() {
Value result;
enter_(jitcode_, argc_ + 1, argv_ + 1, NULL, calleeToken_, &result);
return !result.isMagic();
}
};
#endif // JS_ION
class ParallelDo : public ForkJoinOp
{
JSContext *cx_;
HeapPtrObject fun_;
public:
// For tests, make sure to keep this in sync with minItemsTestingThreshold.
const static uint32_t MAX_BAILOUTS = 3;
uint32_t bailouts;
Vector<JSScript *> pendingInvalidations;
ParallelDo(JSContext *cx, HandleObject fun)
: cx_(cx),
fun_(fun),
bailouts(0),
pendingInvalidations(cx)
{ }
#ifndef JS_ION
ExecutionStatus apply() {
if (!executeSequentially())
return ExecutionFatal;
return ExecutionSequential;
}
#else
ExecutionStatus apply() {
SpewBeginOp(cx_, "ParallelDo");
if (!ion::IsEnabled(cx_))
return SpewEndOp(disqualifyFromParallelExecution());
if (!pendingInvalidations.resize(ForkJoinSlices(cx_)))
return SpewEndOp(ExecutionFatal);
// Try to execute in parallel. If a bailout occurs, re-warmup
// and then try again. Repeat this a few times.
while (bailouts < MAX_BAILOUTS) {
MethodStatus status = compileForParallelExecution();
if (status == Method_Error)
return SpewEndOp(ExecutionFatal);
if (status != Method_Compiled)
return SpewEndOp(disqualifyFromParallelExecution());
ParallelResult result = js::ExecuteForkJoinOp(cx_, *this);
switch (result) {
case TP_RETRY_SEQUENTIALLY:
Spew(SpewBailouts, "Bailout not categorized");
break;
case TP_SUCCESS:
return SpewEndOp(ExecutionParallel);
case TP_FATAL:
return SpewEndOp(ExecutionFatal);
}
bailouts += 1;
SpewBailout(bailouts);
if (!invalidateBailedOutScripts())
return SpewEndOp(ExecutionFatal);
if (!warmupForParallelExecution())
return SpewEndOp(ExecutionFatal);
}
// After enough tries, just execute sequentially.
return SpewEndOp(disqualifyFromParallelExecution());
}
MethodStatus compileForParallelExecution() {
// The kernel should be a self-hosted function.
if (!fun_->isFunction())
return Method_Skipped;
RootedFunction callee(cx_, fun_->toFunction());
if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
return Method_Skipped;
if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
return Method_Error;
// If this function has not been run enough to enable parallel
// execution, perform a warmup.
RootedScript script(cx_, callee->nonLazyScript());
if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
if (!warmupForParallelExecution())
return Method_Error;
}
Spew(SpewOps, "Compiling all reachable functions");
ParallelCompileContext compileContext(cx_);
if (!compileContext.appendToWorklist(callee))
return Method_Error;
MethodStatus status = compileContext.compileTransitively();
if (status != Method_Compiled)
return status;
// it can happen that during transitive compilation, our
// callee's parallel ion script is invalidated or GC'd. So
// before we declare success, double check that it's still
// compiled!
if (!callee->nonLazyScript()->hasParallelIonScript())
return Method_Skipped;
return Method_Compiled;
}
ExecutionStatus disqualifyFromParallelExecution() {
if (!executeSequentially())
return ExecutionFatal;
return ExecutionSequential;
}
bool invalidateBailedOutScripts() {
RootedScript script(cx_, fun_->toFunction()->nonLazyScript());
// 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;
}
IonScript *ion = script->parallelIonScript();
JS_ASSERT(pendingInvalidations.length() == ion->parallelInvalidatedScriptEntries());
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;
ion->parallelInvalidatedScriptList()[i] = script;
}
pendingInvalidations[i] = NULL;
}
Invalidate(cx_, invalid);
return true;
}
bool warmupForParallelExecution() {
AutoEnterWarmup warmup(cx_->runtime);
return executeSequentially();
}
#endif // JS_ION
bool executeSequentially() {
uint32_t numSlices = ForkJoinSlices(cx_);
RootedValue funVal(cx_, ObjectValue(*fun_));
FastInvokeGuard fig(cx_, funVal);
for (uint32_t i = 0; i < numSlices; i++) {
InvokeArgsGuard &args = fig.args();
if (!args.pushed() && !cx_->stack.pushInvokeArgs(cx_, 3, &args))
return false;
args.setCallee(funVal);
args.setThis(UndefinedValue());
args[0].setInt32(i);
args[1].setInt32(numSlices);
args[2].setBoolean(!!cx_->runtime->parallelWarmup);
if (!fig.invoke(cx_))
return false;
}
return true;
}
virtual bool parallel(ForkJoinSlice &slice) {
#ifndef JS_ION
JS_NOT_REACHED("Parallel execution without ion");
return false;
#else
Spew(SpewOps, "Up");
// Make a new IonContext for the slice, which is needed if we need to
// re-enter the VM.
IonContext icx(cx_, cx_->compartment, NULL);
JS_ASSERT(pendingInvalidations[slice.sliceId] == NULL);
js::PerThreadData *pt = slice.perThreadData;
RootedObject fun(pt, fun_);
JS_ASSERT(fun->isFunction());
RootedFunction callee(cx_, fun->toFunction());
if (!callee->nonLazyScript()->hasParallelIonScript()) {
// Sometimes, particularly with GCZeal, the parallel ion
// script can be collected between starting the parallel
// op and reaching this point. In that case, we just fail
// and fallback.
Spew(SpewOps, "Down (Script no longer present)");
return false;
}
ParallelIonInvoke<3> fii(cx_, callee, 3);
fii.args[0] = Int32Value(slice.sliceId);
fii.args[1] = Int32Value(slice.numSlices);
fii.args[2] = BooleanValue(false);
bool ok = fii.invoke();
JS_ASSERT(ok == !slice.abortedScript);
if (!ok) {
JSScript *script = slice.abortedScript;
JS_ASSERT(script->hasParallelIonScript());
pendingInvalidations[slice.sliceId] = script;
}
Spew(SpewOps, "Down");
return ok;
#endif // JS_ION
}
inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script) {
for (uint32_t i = 0; i < scripts.length(); i++) {
if (scripts[i] == script->parallelIonScript()->recompileInfo())
return true;
}
return false;
}
inline bool hasNoPendingInvalidations() {
for (uint32_t i = 0; i < pendingInvalidations.length(); i++) {
if (pendingInvalidations[i] != NULL)
return false;
}
return true;
}
};
bool
js::parallel::Do(JSContext *cx, CallArgs &args)
{
JS_ASSERT(args[0].isObject());
JS_ASSERT(args[0].toObject().isFunction());
RootedObject fun(cx, &args[0].toObject());
ParallelDo op(cx, fun);
ExecutionStatus status = op.apply();
if (status == ExecutionFatal)
return false;
if (args[1].isObject()) {
RootedObject feedback(cx, &args[1].toObject());
if (feedback && feedback->isFunction()) {
InvokeArgsGuard feedbackArgs;
if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
return false;
feedbackArgs.setCallee(ObjectValue(*feedback));
feedbackArgs.setThis(UndefinedValue());
if (status == ExecutionParallel)
feedbackArgs[0].setInt32(op.bailouts);
else
feedbackArgs[0] = cx->runtime->positiveInfinityValue;
if (!Invoke(cx, feedbackArgs))
return false;
}
}
return true;
}
>>>>>>> mozilla/master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment