Allow compiling scripts off thread with Ion, bug 774253. r=dvander

This commit is contained in:
Brian Hackett 2012-08-22 19:00:33 -06:00
parent c096ec1828
commit 6b847aacbd
30 changed files with 892 additions and 177 deletions

View File

@ -106,6 +106,7 @@ CPPSRCS = \
jsutil.cpp \
jswatchpoint.cpp \
jsweakmap.cpp \
jsworkers.cpp \
jswrapper.cpp \
jsxml.cpp \
prmjtime.cpp \

View File

@ -2803,7 +2803,7 @@ CodeGenerator::generate()
encodeSafepoints();
JSScript *script = gen->info().script();
JS_ASSERT(!script->ion);
JS_ASSERT(!script->hasIonScript());
uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None()
? frameDepth_

View File

@ -35,12 +35,12 @@ class CompilerRoot : public CompilerRootNode
public:
// Sets the pointer and inserts into root list. The pointer becomes read-only.
void setRoot(T root) {
JSRuntime *rt = root->compartment()->rt;
JS::CompilerRootNode *&rootList = GetIonContext()->temp->rootList();
JS_ASSERT(!ptr);
ptr = root;
next = rt->ionCompilerRootList;
rt->ionCompilerRootList = this;
next = rootList;
rootList = this;
}
public:
@ -53,24 +53,6 @@ typedef CompilerRoot<JSFunction*> CompilerRootFunction;
typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
typedef CompilerRoot<Value> CompilerRootValue;
// Automatically clears the compiler root list when compilation finishes.
class AutoCompilerRoots
{
JSRuntime *rt_;
public:
AutoCompilerRoots(JSRuntime *rt)
: rt_(rt)
{
JS_ASSERT(rt_->ionCompilerRootList == NULL);
}
~AutoCompilerRoots()
{
rt_->ionCompilerRootList = NULL;
}
};
} // namespace ion
} // namespace js

View File

@ -18,6 +18,7 @@
#include "RangeAnalysis.h"
#include "LinearScan.h"
#include "jscompartment.h"
#include "jsworkers.h"
#include "IonCompartment.h"
#include "CodeGenerator.h"
@ -146,6 +147,29 @@ IonCompartment::initialize(JSContext *cx)
return true;
}
void
ion::FinishOffThreadBuilder(IonBuilder *builder)
{
if (builder->script->isIonCompilingOffThread()) {
types::TypeCompartment &types = builder->script->compartment()->types;
builder->recompileInfo.compilerOutput(types)->invalidate();
builder->script->ion = NULL;
}
Foreground::delete_(builder->temp().lifoAlloc());
}
static inline void
FinishAllOffThreadCompilations(IonCompartment *ion)
{
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
for (size_t i = 0; i < compilations.length(); i++) {
IonBuilder *builder = compilations[i];
FinishOffThreadBuilder(builder);
}
compilations.clear();
}
void
IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
{
@ -179,6 +203,10 @@ IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
// functionWrappers_ are not marked because this is a WeakCache of VM
// function implementations.
// Cancel any active or pending off thread compilations.
CancelOffThreadIonCompile(compartment, NULL);
FinishAllOffThreadCompilations(this);
}
void
@ -700,16 +728,16 @@ ion::ToggleBarriers(JSCompartment *comp, bool needs)
namespace js {
namespace ion {
static bool
BuildMIR(IonBuilder &builder, MIRGraph &graph)
bool
CompileBackEnd(IonBuilder *builder)
{
if (!builder.build())
return false;
IonSpewPass("BuildSSA");
// Note: don't call AssertGraphCoherency before SplitCriticalEdges,
// the graph is not in RPO at this point.
if (!SplitCriticalEdges(&builder, graph))
MIRGraph &graph = builder->graph();
if (!SplitCriticalEdges(builder, graph))
return false;
IonSpewPass("Split Critical Edges");
AssertGraphCoherency(graph);
@ -813,50 +841,141 @@ BuildMIR(IonBuilder &builder, MIRGraph &graph)
IonSpewPass("Bounds Check Elimination");
AssertGraphCoherency(graph);
return true;
}
LIRGraph *lir = builder->temp().lifoAlloc()->new_<LIRGraph>(graph);
if (!lir)
return false;
static bool
GenerateCode(IonBuilder &builder, MIRGraph &graph)
{
LIRGraph lir(graph);
LIRGenerator lirgen(&builder, graph, lir);
LIRGenerator lirgen(builder, graph, *lir);
if (!lirgen.generate())
return false;
IonSpewPass("Generate LIR");
if (js_IonOptions.lsra) {
LinearScanAllocator regalloc(&lirgen, lir);
LinearScanAllocator regalloc(&lirgen, *lir);
if (!regalloc.go())
return false;
IonSpewPass("Allocate Registers", &regalloc);
}
CodeGenerator codegen(&builder, lir);
if (!codegen.generate())
return false;
// No spew: graph not changed.
builder->lir = lir;
return true;
}
/* static */ bool
TestCompiler(IonBuilder &builder, MIRGraph &graph)
class AutoDestroyAllocator
{
IonSpewNewFunction(&graph, builder.script);
LifoAlloc *alloc;
if (!BuildMIR(builder, graph))
public:
AutoDestroyAllocator(LifoAlloc *alloc) : alloc(alloc) {}
void cancel()
{
alloc = NULL;
}
~AutoDestroyAllocator()
{
if (alloc)
Foreground::delete_(alloc);
}
};
/* static */ bool
TestCompiler(IonBuilder *builder, MIRGraph *graph, AutoDestroyAllocator &autoDestroy)
{
JS_ASSERT(!builder->script->ion);
JSContext *cx = GetIonContext()->cx;
IonSpewNewFunction(graph, builder->script);
if (!builder->build())
return false;
builder->clearForBackEnd();
if (js_IonOptions.parallelCompilation) {
builder->script->ion = ION_COMPILING_SCRIPT;
if (!StartOffThreadIonCompile(cx, builder))
return false;
// The allocator and associated data will be destroyed after being
// processed in the finishedOffThreadCompilations list.
autoDestroy.cancel();
return true;
}
if (!CompileBackEnd(builder))
return false;
if (!GenerateCode(builder, graph))
CodeGenerator codegen(builder, *builder->lir);
if (!codegen.generate())
return false;
if (builder->script->hasIonScript()) {
// After Ion has finished compiling a script, remove any JITScripts it
// has to force continued execution in Ion code.
mjit::ReleaseScriptCodeFromVM(cx, builder->script);
}
IonSpewEndFunction();
return true;
}
template <bool Compiler(IonBuilder &, MIRGraph &)>
void
AttachFinishedCompilations(JSContext *cx)
{
IonCompartment *ion = cx->compartment->ionCompartment();
if (!ion)
return;
AutoLockWorkerThreadState lock(cx->runtime);
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
// Incorporate any off thread compilations which have finished, failed or
// have been cancelled, and destroy JM jitcode for any compilations which
// succeeded, to allow entering the Ion code from the interpreter.
while (!compilations.empty()) {
IonBuilder *builder = compilations.popCopy();
if (builder->lir) {
JSScript *script = builder->script;
IonContext ictx(cx, cx->compartment, &builder->temp());
CodeGenerator codegen(builder, *builder->lir);
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
enterCompiler.initExisting(builder->recompileInfo);
bool success;
{
// Release the worker thread lock and root the compiler for GC.
AutoTempAllocatorRooter root(cx, &builder->temp());
AutoUnlockWorkerThreadState unlock(cx->runtime);
success = codegen.generate();
}
if (success) {
if (script->hasIonScript())
mjit::ReleaseScriptCodeFromVM(cx, script);
} else {
// Silently ignore OOM during code generation, we're at an
// operation callback and can't propagate failures.
cx->clearPendingException();
}
}
FinishOffThreadBuilder(builder);
}
compilations.clear();
}
static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
template <bool Compiler(IonBuilder *, MIRGraph *, AutoDestroyAllocator &)>
static bool
IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
{
@ -867,14 +986,23 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
script);
#endif
TempAllocator temp(&cx->tempLifoAlloc());
IonContext ictx(cx, cx->compartment, &temp);
LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
if (!alloc)
return false;
AutoDestroyAllocator autoDestroy(alloc);
TempAllocator *temp = alloc->new_<TempAllocator>(alloc);
if (!temp)
return false;
IonContext ictx(cx, cx->compartment, temp);
if (!cx->compartment->ensureIonCompartmentExists(cx))
return false;
MIRGraph graph(&temp);
CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(script, fun, osrPc, constructing);
MIRGraph *graph = alloc->new_<MIRGraph>(temp);
CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing);
if (!info)
return false;
@ -888,10 +1016,11 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
enterCompiler.init(script, false, 0);
AutoCompilerRoots roots(script->compartment()->rt);
IonBuilder builder(cx, &temp, &graph, &oracle, info);
if (!Compiler(builder, graph)) {
AutoTempAllocatorRooter root(cx, temp);
IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, info);
if (!Compiler(builder, graph, autoDestroy)) {
IonSpew(IonSpew_Abort, "IM Compilation failed.");
return false;
}
@ -899,6 +1028,17 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
return true;
}
bool
TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
{
if (!IonCompile<TestCompiler>(cx, script, fun, osrPc, constructing)) {
if (!cx->isExceptionPending())
ForbidCompilation(script);
return false;
}
return true;
}
static bool
CheckFrame(StackFrame *fp)
{
@ -979,7 +1119,7 @@ CheckScriptSize(JSScript *script)
return true;
}
template <bool Compiler(IonBuilder &, MIRGraph &)>
template <bool Compiler(IonBuilder *, MIRGraph *, AutoDestroyAllocator &)>
static MethodStatus
Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
{
@ -1034,6 +1174,10 @@ ion::CanEnterAtBranch(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecod
if (script->ion == ION_DISABLED_SCRIPT)
return Method_Skipped;
// Skip if the script is being compiled off thread.
if (script->ion == ION_COMPILING_SCRIPT)
return Method_Skipped;
// Skip if the code is expected to result in a bailout.
if (script->ion && script->ion->bailoutExpected())
return Method_Skipped;
@ -1079,6 +1223,10 @@ ion::CanEnter(JSContext *cx, JSScript *script, StackFrame *fp, bool newType)
if (script->ion == ION_DISABLED_SCRIPT)
return Method_Skipped;
// Skip if the script is being compiled off thread.
if (script->ion == ION_COMPILING_SCRIPT)
return Method_Skipped;
// Skip if the code is expected to result in a bailout.
if (script->ion && script->ion->bailoutExpected())
return Method_Skipped;
@ -1379,6 +1527,13 @@ InvalidateActivation(FreeOp *fop, uint8 *ionTop, bool invalidateAll)
void
ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
{
if (!c->ionCompartment())
return;
CancelOffThreadIonCompile(c, NULL);
FinishAllOffThreadCompilations(c->ionCompartment());
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
if (iter.activation()->compartment() == c) {
AutoFlushCache afc ("InvalidateAll", c->ionCompartment());

View File

@ -67,6 +67,11 @@ struct IonOptions
// Default: false
bool rangeAnalysis;
// Toggles whether compilation occurs off the main thread.
//
// Default: true iff there are at least two CPUs available
bool parallelCompilation;
// How many invocations or loop iterations are needed before functions
// are compiled.
//
@ -140,6 +145,8 @@ struct IonOptions
// Eagerly inline calls to improve test coverage.
usesBeforeInlining = 0;
smallFunctionUsesBeforeInlining = 0;
parallelCompilation = false;
}
IonOptions()
@ -152,6 +159,7 @@ struct IonOptions
inlining(true),
edgeCaseAnalysis(true),
rangeAnalysis(false),
parallelCompilation(false),
usesBeforeCompile(10240),
usesBeforeCompileNoJaeger(40),
usesBeforeInlining(usesBeforeCompile),
@ -163,7 +171,8 @@ struct IonOptions
inlineMaxTotalBytecodeLength(800),
eagerCompilation(false),
slowCallLimit(512)
{ }
{
}
};
enum MethodStatus
@ -230,6 +239,13 @@ void MarkFromIon(JSCompartment *comp, Value *vp);
void ToggleBarriers(JSCompartment *comp, bool needs);
class IonBuilder;
bool CompileBackEnd(IonBuilder *builder);
void AttachFinishedCompilations(JSContext *cx);
void FinishOffThreadBuilder(IonBuilder *builder);
bool TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing);
static inline bool IsEnabled(JSContext *cx)
{
return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();

View File

@ -22,10 +22,14 @@ class TempAllocator
LifoAlloc *lifoAlloc_;
void *mark_;
// Linked list of GCThings rooted by this allocator.
JS::CompilerRootNode *rootList_;
public:
TempAllocator(LifoAlloc *lifoAlloc)
: lifoAlloc_(lifoAlloc),
mark_(lifoAlloc->mark())
mark_(lifoAlloc->mark()),
rootList_(NULL)
{ }
~TempAllocator()
@ -53,6 +57,11 @@ class TempAllocator
return lifoAlloc_;
}
JS::CompilerRootNode *&rootList()
{
return rootList_;
}
bool ensureBallast() {
// Most infallible Ion allocations are small, so we use a ballast of
// ~16K for now.
@ -60,6 +69,25 @@ class TempAllocator
}
};
// Stack allocated rooter for all roots associated with a TempAllocator
class AutoTempAllocatorRooter : private AutoGCRooter
{
public:
explicit AutoTempAllocatorRooter(JSContext *cx, TempAllocator *temp
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoGCRooter(cx, IONALLOC), temp(temp)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
friend void AutoGCRooter::trace(JSTracer *trc);
void trace(JSTracer *trc);
private:
TempAllocator *temp;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class IonAllocPolicy
{
public:

View File

@ -27,6 +27,8 @@ IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
: MIRGenerator(cx->compartment, temp, graph, info),
script(info->script()),
recompileInfo(cx->compartment->types.compiledInfo),
lir(NULL),
cx(cx),
loopDepth_(loopDepth),
callerResumePoint_(NULL),

View File

@ -17,6 +17,8 @@
namespace js {
namespace ion {
class LIRGraph;
class IonBuilder : public MIRGenerator
{
enum ControlStatus {
@ -425,6 +427,12 @@ class IonBuilder : public MIRGenerator
// A builder is inextricably tied to a particular script.
JSScript * const script;
// Compilation index for this attempt.
types::RecompileInfo const recompileInfo;
// If off thread compilation is successful, final LIR is attached here.
LIRGraph *lir;
void clearForBackEnd();
private:

View File

@ -23,6 +23,9 @@ typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
CalleeToken calleeToken, Value *vp);
class IonActivation;
class IonBuilder;
typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
class IonCompartment
{
@ -37,7 +40,7 @@ class IonCompartment
ReadBarriered<IonCode> enterJIT_;
// Vector mapping frame class sizes to bailout tables.
js::Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
Vector<ReadBarriered<IonCode>, 4, SystemAllocPolicy> bailoutTables_;
// Generic bailout table; used if the bailout table overflows.
ReadBarriered<IonCode> bailoutHandler_;
@ -55,8 +58,14 @@ class IonCompartment
// Map VMFunction addresses to the IonCode of the wrapper.
VMWrapperMap *functionWrappers_;
// Any scripts for which off thread compilation has successfully finished,
// failed, or been cancelled. All off thread compilations which are started
// will eventually appear in this list asynchronously. Protected by the
// runtime's analysis lock.
OffThreadCompilationVector finishedOffThreadCompilations_;
// Keep track of memoryregions that are going to be flushed.
js::ion::AutoFlushCache *flusher_;
AutoFlushCache *flusher_;
private:
IonCode *generateEnterJIT(JSContext *cx);
@ -70,6 +79,10 @@ class IonCompartment
public:
IonCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
OffThreadCompilationVector &finishedOffThreadCompilations() {
return finishedOffThreadCompilations_;
}
public:
bool initialize(JSContext *cx);
IonCompartment();

View File

@ -621,10 +621,9 @@ ion::MarkIonActivations(JSRuntime *rt, JSTracer *trc)
}
void
ion::MarkIonCompilerRoots(JSTracer *trc)
ion::AutoTempAllocatorRooter::trace(JSTracer *trc)
{
JSRuntime *rt = trc->runtime;
for (CompilerRootNode *root = rt->ionCompilerRootList; root != NULL; root = root->next)
for (CompilerRootNode *root = temp->rootList(); root != NULL; root = root->next)
gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
}

View File

@ -7,6 +7,7 @@
#ifdef DEBUG
#include "Ion.h"
#include "IonSpewer.h"
#ifndef ION_SPEW_DIR
@ -42,25 +43,29 @@ ion::EnableIonDebugLogging()
void
ion::IonSpewNewFunction(MIRGraph *graph, JSScript *function)
{
ionspewer.beginFunction(graph, function);
if (!js_IonOptions.parallelCompilation)
ionspewer.beginFunction(graph, function);
}
void
ion::IonSpewPass(const char *pass)
{
ionspewer.spewPass(pass);
if (!js_IonOptions.parallelCompilation)
ionspewer.spewPass(pass);
}
void
ion::IonSpewPass(const char *pass, LinearScanAllocator *ra)
{
ionspewer.spewPass(pass, ra);
if (!js_IonOptions.parallelCompilation)
ionspewer.spewPass(pass, ra);
}
void
ion::IonSpewEndFunction()
{
ionspewer.endFunction();
if (!js_IonOptions.parallelCompilation)
ionspewer.endFunction();
}

View File

@ -48,6 +48,7 @@
#include "jsstr.h"
#include "prmjtime.h"
#include "jsweakmap.h"
#include "jsworkers.h"
#include "jswrapper.h"
#include "jstypedarray.h"
#include "jsxml.h"
@ -864,7 +865,6 @@ JSRuntime::JSRuntime()
ionJSContext(NULL),
ionStackLimit(0),
ionActivation(NULL),
ionCompilerRootList(NULL),
ionReturnOverride_(MagicValue(JS_ARG_POISON))
{
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
@ -928,6 +928,10 @@ JSRuntime::init(uint32_t maxbytes)
return false;
#ifdef JS_THREADSAFE
workerThreadState = this->new_<WorkerThreadState>();
if (!workerThreadState || !workerThreadState->init(this))
return false;
if (!sourceCompressorThread.init())
return false;
#endif
@ -958,6 +962,7 @@ JSRuntime::~JSRuntime()
FreeScriptFilenames(this);
#ifdef JS_THREADSAFE
delete_(workerThreadState);
sourceCompressorThread.finish();
#endif

View File

@ -1064,7 +1064,8 @@ class JS_PUBLIC_API(AutoGCRooter) {
REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
NAMEVECTOR = -26, /* js::AutoNameVector */
HASHABLEVALUE=-27,
IONMASM = -28 /* js::ion::MacroAssembler */
IONMASM = -28, /* js::ion::MacroAssembler */
IONALLOC = -29 /* js::ion::AutoTempAllocatorRooter */
};
private:

View File

@ -42,6 +42,8 @@
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsworkers.h"
#include "ion/Ion.h"
#include "ion/IonFrames.h"
#ifdef JS_METHODJIT
@ -389,6 +391,10 @@ js::DestroyContext(JSContext *cx, DestroyContextMode mode)
for (CompartmentsIter c(rt); !c.done(); c.next())
c->types.print(cx, false);
/* Off thread ion compilations depend on atoms still existing. */
for (CompartmentsIter c(rt); !c.done(); c.next())
CancelOffThreadIonCompile(c, NULL);
/* Unpin all common atoms before final GC. */
FinishCommonAtoms(rt);
@ -1010,6 +1016,12 @@ js_InvokeOperationCallback(JSContext *cx)
if (rt->gcIsNeeded)
GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
/*
* A worker thread may have set the callback after finishing an Ion
* compilation.
*/
ion::AttachFinishedCompilations(cx);
/*
* Important: Additional callbacks can occur inside the callback handler
* if it re-enters the JS engine. The embedding must ensure that the

View File

@ -77,6 +77,7 @@ class IonActivation;
class WeakMapBase;
class InterpreterFrames;
class DebugScopes;
class WorkerThreadState;
/*
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
@ -789,6 +790,8 @@ struct JSRuntime : js::RuntimeFriendFields
js::GCHelperThread gcHelperThread;
#ifdef JS_THREADSAFE
js::WorkerThreadState *workerThreadState;
js::SourceCompressorThread sourceCompressorThread;
#endif
@ -894,9 +897,6 @@ struct JSRuntime : js::RuntimeFriendFields
// This points to the most recent Ion activation running on the thread.
js::ion::IonActivation *ionActivation;
// Linked list of GCThings rooted for the current compilation.
JS::CompilerRootNode *ionCompilerRootList;
private:
// In certain cases, we want to optimize certain opcodes to typed instructions,
// to avoid carrying an extra register to feed into an unbox. Unfortunately,

View File

@ -2491,6 +2491,13 @@ AutoGCRooter::trace(JSTracer *trc)
case IONMASM: {
#ifdef JS_ION
static_cast<js::ion::MacroAssembler::AutoRooter *>(this)->masm()->trace(trc);
#endif
return;
}
case IONALLOC: {
#ifdef JS_ION
static_cast<js::ion::AutoTempAllocatorRooter *>(this)->trace(trc);
#endif
return;
}
@ -2623,7 +2630,6 @@ MarkRuntime(JSTracer *trc, bool useSavedRoots = false)
#ifdef JS_ION
ion::MarkIonActivations(rt, trc);
ion::MarkIonCompilerRoots(trc);
#endif
for (CompartmentsIter c(rt); !c.done(); c.next())
@ -2918,7 +2924,7 @@ AssertBackgroundSweepingFinshed(JSRuntime *rt)
#endif
#ifdef JS_THREADSAFE
static unsigned
unsigned
GetCPUCount()
{
static unsigned ncpus = 0;

View File

@ -20,6 +20,7 @@
#include "jsscope.h"
#include "jsstr.h"
#include "jsiter.h"
#include "jsworkers.h"
#include "ion/Ion.h"
#include "ion/IonCompartment.h"
@ -2253,6 +2254,9 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
if (co->pendingRecompilation)
return;
if (co->isValid())
CancelOffThreadIonCompile(cx->compartment, co->script);
if (!co->isValid()) {
JS_ASSERT(co->script == NULL);
return;
@ -2319,6 +2323,8 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode
}
# ifdef JS_ION
CancelOffThreadIonCompile(cx->compartment, script);
if (script->hasIonScript())
addPendingRecompile(cx, script->ionScript()->recompileInfo());
# endif
@ -5849,7 +5855,6 @@ TypeCompartment::sweep(FreeOp *fop)
void
TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
{
if (constrainedOutputs) {
bool isCompiling = compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning;
if (isCompiling && !compartment()->activeAnalysis)
@ -5860,6 +5865,7 @@ TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
JS_ASSERT(!co.isValid());
}
#endif
fop->delete_(constrainedOutputs);
constrainedOutputs = NULL;
} else {
@ -5868,14 +5874,12 @@ TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
// potentially created multiple types with this index. Instead, we
// invalidate all compilations except the one running now.
size_t len = constrainedOutputs->length();
if (isCompiling) {
len--;
JS_ASSERT(compiledInfo.outputIndex == len);
}
for (unsigned i = 0; i < len; i++) {
CompilerOutput &co = (*constrainedOutputs)[i];
JS_ASSERT(!co.isValid());
co.invalidate();
if (i != compiledInfo.outputIndex) {
CompilerOutput &co = (*constrainedOutputs)[i];
JS_ASSERT(!co.isValid());
co.invalidate();
}
}
}
}

View File

@ -80,10 +80,13 @@ CompilerOutput::isValid() const
#endif
if (isIon()) {
if (!script->hasIonScript())
return false;
JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
return true;
if (script->hasIonScript()) {
JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
return true;
}
if (script->isIonCompilingOffThread())
return true;
return false;
}
return false;
}
@ -372,6 +375,13 @@ struct AutoEnterCompilation
return true;
}
void initExisting(RecompileInfo oldInfo)
{
// Initialize the active compilation index from that produced during a
// previous compilation, for finishing an off thread compilation.
info = oldInfo;
}
~AutoEnterCompilation()
{
CompilerOutput *co = info.compilerOutput(cx);

View File

@ -21,6 +21,10 @@
# define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((int32_t *)(p), (int32_t)(v))
# define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((int32_t *)(p), (int32_t)(v))
namespace js {
unsigned GetCPUCount();
}
#else /* JS_THREADSAFE */
typedef struct PRThread PRThread;

View File

@ -551,11 +551,14 @@ struct JSScript : public js::gc::Cell
#endif
bool hasIonScript() const {
return ion && ion != ION_DISABLED_SCRIPT;
return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
}
bool canIonCompile() const {
return ion != ION_DISABLED_SCRIPT;
}
bool isIonCompilingOffThread() const {
return ion == ION_COMPILING_SCRIPT;
}
js::ion::IonScript *ionScript() const {
JS_ASSERT(hasIonScript());
return ion;

294
js/src/jsworkers.cpp Normal file
View File

@ -0,0 +1,294 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
/* vim: set ts=40 sw=4 et tw=99: */
/* 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 "jsworkers.h"
#include "ion/IonBuilder.h"
using namespace js;
#ifdef JS_THREADSAFE
bool
js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
{
WorkerThreadState &state = *cx->runtime->workerThreadState;
JS_ASSERT(state.numThreads);
AutoLockWorkerThreadState lock(cx->runtime);
if (!state.ionWorklist.append(builder))
return false;
state.notify(WorkerThreadState::WORKER);
return true;
}
/*
* Move an IonBuilder for which compilation has either finished, failed, or
* been cancelled into the Ion compartment's finished compilations list.
* All off thread compilations which are started must eventually be finished.
*/
static void
FinishOffThreadIonCompile(ion::IonBuilder *builder)
{
JSCompartment *compartment = builder->script->compartment();
JS_ASSERT(compartment->rt->workerThreadState->isLocked());
compartment->ionCompartment()->finishedOffThreadCompilations().append(builder);
}
static inline bool
CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
{
if (script)
return target == script;
return target->compartment() == compartment;
}
void
js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
{
WorkerThreadState &state = *compartment->rt->workerThreadState;
ion::IonCompartment *ion = compartment->ionCompartment();
if (!ion)
return;
AutoLockWorkerThreadState lock(compartment->rt);
/* Cancel any pending entries for which processing hasn't started. */
for (size_t i = 0; i < state.ionWorklist.length(); i++) {
ion::IonBuilder *builder = state.ionWorklist[i];
if (CompiledScriptMatches(compartment, script, builder->script)) {
FinishOffThreadIonCompile(builder);
state.ionWorklist[i--] = state.ionWorklist.back();
state.ionWorklist.popBack();
}
}
/* Wait for in progress entries to finish up. */
for (size_t i = 0; i < state.numThreads; i++) {
const WorkerThread &helper = state.threads[i];
while (helper.ionScript && CompiledScriptMatches(compartment, script, helper.ionScript))
state.wait(WorkerThreadState::MAIN);
}
ion::OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
/* Cancel code generation for any completed entries. */
for (size_t i = 0; i < compilations.length(); i++) {
ion::IonBuilder *builder = compilations[i];
if (CompiledScriptMatches(compartment, script, builder->script)) {
ion::FinishOffThreadBuilder(builder);
compilations[i--] = compilations.back();
compilations.popBack();
}
}
}
bool
WorkerThreadState::init(JSRuntime *rt)
{
workerLock = PR_NewLock();
if (!workerLock)
return false;
mainWakeup = PR_NewCondVar(workerLock);
if (!mainWakeup)
return false;
helperWakeup = PR_NewCondVar(workerLock);
if (!helperWakeup)
return false;
numThreads = GetCPUCount() - 1;
threads = (WorkerThread*) rt->calloc_(sizeof(WorkerThread) * numThreads);
if (!threads) {
numThreads = 0;
return false;
}
for (size_t i = 0; i < numThreads; i++) {
WorkerThread &helper = threads[i];
helper.runtime = rt;
helper.thread = PR_CreateThread(PR_USER_THREAD,
WorkerThread::ThreadMain, &helper,
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
if (!helper.thread) {
for (size_t j = 0; j < numThreads; j++)
threads[j].destroy();
Foreground::free_(threads);
threads = NULL;
numThreads = 0;
return false;
}
}
return true;
}
WorkerThreadState::~WorkerThreadState()
{
/*
* Join created threads first, which needs locks and condition variables
* to be intact.
*/
if (threads) {
for (size_t i = 0; i < numThreads; i++)
threads[i].destroy();
Foreground::free_(threads);
}
if (workerLock)
PR_DestroyLock(workerLock);
if (mainWakeup)
PR_DestroyCondVar(mainWakeup);
if (helperWakeup)
PR_DestroyCondVar(helperWakeup);
}
void
WorkerThreadState::lock()
{
JS_ASSERT(!isLocked());
PR_Lock(workerLock);
#ifdef DEBUG
lockOwner = PR_GetCurrentThread();
#endif
}
void
WorkerThreadState::unlock()
{
JS_ASSERT(isLocked());
#ifdef DEBUG
lockOwner = NULL;
#endif
PR_Unlock(workerLock);
}
#ifdef DEBUG
bool
WorkerThreadState::isLocked()
{
return lockOwner == PR_GetCurrentThread();
}
#endif
void
WorkerThreadState::wait(CondVar which, uint32_t millis)
{
JS_ASSERT(isLocked());
#ifdef DEBUG
lockOwner = NULL;
#endif
PR_WaitCondVar((which == MAIN) ? mainWakeup : helperWakeup,
millis ? PR_MillisecondsToInterval(millis) : PR_INTERVAL_NO_TIMEOUT);
#ifdef DEBUG
lockOwner = PR_GetCurrentThread();
#endif
}
void
WorkerThreadState::notify(CondVar which)
{
JS_ASSERT(isLocked());
PR_NotifyAllCondVar((which == MAIN) ? mainWakeup : helperWakeup);
}
void
WorkerThread::destroy()
{
WorkerThreadState &state = *runtime->workerThreadState;
if (!thread)
return;
terminate = true;
{
AutoLockWorkerThreadState lock(runtime);
state.notify(WorkerThreadState::WORKER);
}
PR_JoinThread(thread);
}
/* static */
void
WorkerThread::ThreadMain(void *arg)
{
PR_SetCurrentThreadName("Analysis Helper");
static_cast<WorkerThread *>(arg)->threadLoop();
}
void
WorkerThread::threadLoop()
{
WorkerThreadState &state = *runtime->workerThreadState;
state.lock();
while (true) {
JS_ASSERT(!ionScript);
while (state.ionWorklist.empty()) {
state.wait(WorkerThreadState::WORKER);
if (terminate) {
state.unlock();
return;
}
}
ion::IonBuilder *builder = state.ionWorklist.popCopy();
ionScript = builder->script;
JS_ASSERT(ionScript->ion == ION_COMPILING_SCRIPT);
state.unlock();
{
ion::IonContext ictx(NULL, ionScript->compartment(), &builder->temp());
ion::CompileBackEnd(builder);
}
state.lock();
ionScript = NULL;
FinishOffThreadIonCompile(builder);
/*
* Notify the main thread in case it is waiting for the compilation to
* finish.
*/
state.notify(WorkerThreadState::MAIN);
/*
* Ping the main thread so that the compiled code can be incorporated
* at the next operation callback.
*/
runtime->triggerOperationCallback();
}
}
#else /* JS_THREADSAFE */
bool
js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
{
JS_NOT_REACHED("Off thread compilation not available in non-THREADSAFE builds");
return false;
}
void
js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
{
}
#endif /* JS_THREADSAFE */

162
js/src/jsworkers.h Normal file
View File

@ -0,0 +1,162 @@
//* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
/* vim: set ts=40 sw=4 et tw=99: */
/* 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/. */
/*
* Definitions for managing off-main-thread work using a shared, per runtime
* worklist. Worklist items are engine internal, and are distinct from e.g.
* web workers.
*/
#ifndef jsworkers_h___
#define jsworkers_h___
#include "jscntxt.h"
#include "jslock.h"
namespace js {
namespace ion {
class IonBuilder;
}
#ifdef JS_THREADSAFE
struct WorkerThread;
/* Per-runtime state for off thread work items. */
struct WorkerThreadState
{
/* Available threads. */
WorkerThread *threads;
size_t numThreads;
enum CondVar {
MAIN,
WORKER
};
/* Shared worklist for helper threads. */
js::Vector<ion::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
WorkerThreadState() { PodZero(this); }
~WorkerThreadState();
bool init(JSRuntime *rt);
void lock();
void unlock();
#ifdef DEBUG
bool isLocked();
#endif
void wait(CondVar which, uint32_t timeoutMillis = 0);
void notify(CondVar which);
private:
/*
* Lock protecting all mutable shared state accessed by helper threads, and
* used by all condition variables.
*/
PRLock *workerLock;
#ifdef DEBUG
PRThread *lockOwner;
#endif
/* Condvar to notify the main thread that work has been completed. */
PRCondVar *mainWakeup;
/* Condvar to notify helper threads that they may be able to make progress. */
PRCondVar *helperWakeup;
};
/* Individual helper thread, one allocated per core. */
struct WorkerThread
{
JSRuntime *runtime;
PRThread *thread;
/* Indicate to an idle thread that it should finish executing. */
bool terminate;
/* Any script currently being compiled for Ion on this thread. */
JSScript *ionScript;
void destroy();
static void ThreadMain(void *arg);
void threadLoop();
};
#endif /* JS_THREADSAFE */
/* Methods for interacting with worker threads. */
/*
* Schedule an Ion compilation for a script, given a builder which has been
* generated and read everything needed from the VM state.
*/
bool StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder);
/*
* Cancel a scheduled or in progress Ion compilation for script. If script is
* NULL, all compilations for the compartment are cancelled.
*/
void CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
class AutoLockWorkerThreadState
{
JSRuntime *rt;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoLockWorkerThreadState(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM)
: rt(rt)
{
#ifdef JS_THREADSAFE
rt->workerThreadState->lock();
#endif
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoLockWorkerThreadState()
{
#ifdef JS_THREADSAFE
rt->workerThreadState->unlock();
#endif
}
};
class AutoUnlockWorkerThreadState
{
JSRuntime *rt;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoUnlockWorkerThreadState(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM)
: rt(rt)
{
#ifdef JS_THREADSAFE
rt->workerThreadState->unlock();
#endif
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoUnlockWorkerThreadState()
{
#ifdef JS_THREADSAFE
rt->workerThreadState->lock();
#endif
}
};
} /* namespace js */
#endif // jsworkers_h___

View File

@ -51,12 +51,6 @@ using namespace js::analyze;
return retval; \
JS_END_MACRO
/*
* Number of times a script must be called or had a backedge before we try to
* inline its calls. This number should match IonMonkey's usesBeforeCompile.
*/
static const size_t USES_BEFORE_INLINING = 10240;
mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
unsigned chunkIndex, bool isConstructing)
: BaseCompiler(cx),
@ -104,12 +98,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
gcNumber(cx->runtime->gcNumber),
pcLengths(NULL)
{
/* Once a script starts getting really hot we will inline calls in it. */
if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
(outerScript->getUseCount() >= USES_BEFORE_INLINING ||
cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
inlining_ = true;
}
}
CompileStatus
@ -961,6 +949,11 @@ IonGetsFirstChance(JSContext *cx, JSScript *script, CompileRequest request)
if (script->hasIonScript() && script->ion->bailoutExpected())
return false;
// If ion compilation is pending or in progress on another thread, continue
// using JM until that compilation finishes.
if (script->ion == ION_COMPILING_SCRIPT)
return false;
return true;
#endif
return false;
@ -1036,19 +1029,6 @@ mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
unsigned chunkIndex = jit->chunkIndex(pc);
ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
if (jit->mustDestroyEntryChunk) {
// We kept this chunk around so that Ion can get info from its caches.
// If we end up here, we decided not to use Ion so we can destroy the
// chunk now.
JS_ASSERT(jit->nchunks == 1);
jit->mustDestroyEntryChunk = false;
if (desc.chunk) {
jit->destroyChunk(cx->runtime->defaultFreeOp(), chunkIndex, /* resetUses = */ false);
return Compile_Skipped;
}
}
if (desc.chunk)
return Compile_Okay;
@ -1230,7 +1210,7 @@ mjit::Compiler::generatePrologue()
CompileStatus status = methodEntryHelper();
if (status == Compile_Okay)
recompileCheckHelper();
ionCompileHelper();
return status;
}
@ -3269,7 +3249,7 @@ mjit::Compiler::generateMethod()
// Insert the recompile check here so that we can immediately
// enter Ion.
if (loop)
recompileCheckHelper();
ionCompileHelper();
END_CASE(JSOP_LOOPENTRY)
BEGIN_CASE(JSOP_DEBUGGER)
@ -3971,56 +3951,61 @@ MaybeIonCompileable(JSContext *cx, JSScript *script, bool *recompileCheckForIon)
return false;
}
void
mjit::Compiler::recompileCheckHelper()
{
if (inlining() || debugMode() || !globalObj || !cx->typeInferenceEnabled())
return;
bool recompileCheckForIon = true;
bool maybeIonCompileable = MaybeIonCompileable(cx, outerScript, &recompileCheckForIon);
bool hasFunctionCalls = analysis->hasFunctionCalls();
// Insert a recompile check if either:
// 1) IonMonkey is enabled, to optimize the function when it becomes hot.
// 2) The script contains function calls JM can inline.
if (!maybeIonCompileable && !hasFunctionCalls)
return;
uint32_t minUses = USES_BEFORE_INLINING;
#ifdef JS_ION
if (recompileCheckForIon)
minUses = ion::UsesBeforeIonRecompile(outerScript, PC);
#endif
uint32_t *addr = script->addressOfUseCount();
masm.add32(Imm32(1), AbsoluteAddress(addr));
// If there are no function calls, and we don't want to do a recompileCheck for
// Ion, then this just needs to increment the useCount so that we know when to
// recompile this function from an Ion call. No need to call out to recompiler
// stub.
if (!hasFunctionCalls && !recompileCheckForIon)
void
mjit::Compiler::ionCompileHelper()
{
if (debugMode() || !globalObj || !cx->typeInferenceEnabled())
return;
bool recompileCheckForIon = false;
if (!MaybeIonCompileable(cx, outerScript, &recompileCheckForIon))
return;
uint32_t minUses = ion::UsesBeforeIonRecompile(outerScript, PC);
uint32_t *useCountAddress = script->addressOfUseCount();
masm.add32(Imm32(1), AbsoluteAddress(useCountAddress));
// If we don't want to do a recompileCheck for Ion, then this just needs to
// increment the useCount so that we know when to recompile this function
// from an Ion call. No need to call out to recompiler stub.
if (!recompileCheckForIon)
return;
void *ionScriptAddress = &script->ion;
// Trigger ion compilation if (a) the script has been used enough times for
// this opcode, and (b) the script does not already have ion information
// (whether successful, failed, or in progress off thread compilation).
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr),
Imm32(minUses));
Jump first = masm.branch32(Assembler::LessThan, AbsoluteAddress(useCountAddress),
Imm32(minUses));
Jump second = masm.branch32(Assembler::Equal, AbsoluteAddress(ionScriptAddress),
Imm32(0));
#else
/* Handle processors that can't load from absolute addresses. */
RegisterID reg = frame.allocReg();
masm.move(ImmPtr(addr), reg);
Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, Address(reg, 0),
Imm32(minUses));
masm.move(ImmPtr(useCountAddress), reg);
Jump first = masm.branch32(Assembler::LessThan, Address(reg), Imm32(minUses));
masm.move(ImmPtr(ionScriptAddress), reg);
Jump second = masm.branchPtr(Assembler::Equal, Address(reg), ImmPtr(NULL));
frame.freeReg(reg);
#endif
stubcc.linkExit(jump, Uses(0));
first.linkTo(masm.label(), &masm);
stubcc.linkExit(second, Uses(0));
stubcc.leave();
OOL_STUBCALL(stubs::RecompileForInline, REJOIN_RESUME);
OOL_STUBCALL(stubs::TriggerIonCompile, REJOIN_RESUME);
stubcc.rejoin(Changes(0));
}
#else /* JS_ION */
void
mjit::Compiler::ionCompileHelper()
{
}
#endif /* JS_ION */
CompileStatus
mjit::Compiler::methodEntryHelper()

View File

@ -631,7 +631,7 @@ private:
void emitInlineReturnValue(FrameEntry *fe);
void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc);
void interruptCheckHelper();
void recompileCheckHelper();
void ionCompileHelper();
CompileStatus methodEntryHelper();
CompileStatus profilingPushHelper();
void profilingPopHelper();

View File

@ -21,6 +21,7 @@
#include "jsscope.h"
#include "ion/Ion.h"
#include "ion/IonCompartment.h"
#include "methodjit/Retcon.h"
#include "jsgcinlines.h"
#include "jsinterpinlines.h"
@ -1510,6 +1511,16 @@ JSScript::ReleaseCode(FreeOp *fop, JITScriptHandle *jith)
}
}
void
mjit::ReleaseScriptCodeFromVM(JSContext *cx, JSScript *script)
{
if (script->hasMJITInfo()) {
ExpandInlineFrames(cx->compartment);
Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
}
}
#ifdef JS_METHODJIT_PROFILE_STUBS
void JS_FASTCALL
mjit::ProfileStubCall(VMFrame &f)

View File

@ -795,13 +795,6 @@ struct JITScript
*/
uint32_t ionCalls;
/*
* If set, we decided to keep the JITChunk so that Ion can access its caches.
* The chunk has to be destroyed the next time the script runs in JM.
* Note that this flag implies nchunks == 1.
*/
bool mustDestroyEntryChunk;
#ifdef JS_MONOIC
/* Inline cache at function entry for checking this/argument types. */
JSC::CodeLocationLabel argsCheckStub;
@ -909,6 +902,10 @@ ReleaseScriptCode(FreeOp *fop, JSScript *script)
script->destroyMJITInfo(fop);
}
/* Can be called at any time. */
void
ReleaseScriptCodeFromVM(JSContext *cx, JSScript *script);
// Expand all stack frames inlined by the JIT within a compartment.
void
ExpandInlineFrames(JSCompartment *compartment);

View File

@ -591,7 +591,7 @@ class CallCompiler : public BaseCompiler
/* Guard that the ion pointer is valid. */
Jump noIonCode = masm.branchPtr(Assembler::BelowOrEqual, ionScript,
ImmPtr(ION_DISABLED_SCRIPT));
ImmPtr(ION_COMPILING_SCRIPT));
RegisterID t0 = regs.takeAnyReg().reg();
RegisterID t1 = Registers::ClobberInCall;

View File

@ -771,28 +771,19 @@ stubs::Interrupt(VMFrame &f, jsbytecode *pc)
}
void JS_FASTCALL
stubs::RecompileForInline(VMFrame &f)
stubs::TriggerIonCompile(VMFrame &f)
{
JSScript *script = f.script();
JS_ASSERT(!script->ion);
ExpandInlineFrames(f.cx->compartment);
Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
jsbytecode *osrPC = f.regs.pc;
if (*osrPC != JSOP_LOOPENTRY)
osrPC = NULL;
#ifdef JS_ION
if (ion::IsEnabled(f.cx) && f.jit()->nchunks == 1 &&
script->canIonCompile() && !script->hasIonScript())
{
// After returning to the interpreter, IonMonkey will try to compile
// this script. Don't destroy the JITChunk immediately so that Ion
// still has access to its ICs.
JS_ASSERT(!f.jit()->mustDestroyEntryChunk);
f.jit()->mustDestroyEntryChunk = true;
f.jit()->disableScriptEntry();
return;
if (!ion::TestIonCompile(f.cx, script, script->function(), osrPC, f.fp()->isConstructing())) {
if (f.cx->isExceptionPending())
THROW();
}
#endif
f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
}
void JS_FASTCALL

View File

@ -26,7 +26,7 @@ void JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
void JS_FASTCALL Trap(VMFrame &f, uint32_t trapTypes);
void JS_FASTCALL DebuggerStatement(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL RecompileForInline(VMFrame &f);
void JS_FASTCALL TriggerIonCompile(VMFrame &f);
void JS_FASTCALL InitElem(VMFrame &f, uint32_t last);
void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name);

View File

@ -4878,7 +4878,24 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
if (op->getBoolOption("ion-eager"))
ion::js_IonOptions.setEagerCompilation();
#endif
#ifdef JS_THREADSAFE
if (const char *str = op->getStringOption("ion-parallel-compile")) {
if (strcmp(str, "on") == 0) {
if (GetCPUCount() <= 1) {
fprintf(stderr, "Parallel compilation not available on single core machines");
return EXIT_FAILURE;
}
ion::js_IonOptions.parallelCompilation = true;
} else if (strcmp(str, "off") == 0) {
ion::js_IonOptions.parallelCompilation = false;
} else {
return OptionFailure("ion-parallel-compile", str);
}
}
#endif /* JS_THREADSAFE */
#endif /* JS_ION */
/* |scriptArgs| gets bound on the global before any code is run. */
if (!BindScriptArgs(cx, obj, op))
@ -5084,6 +5101,10 @@ main(int argc, char **argv, char **envp)
"Specify Ion register allocation:\n"
" lsra: Linear Scan register allocation (default)")
|| !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods")
#ifdef JS_THREADSAFE
|| !op.addStringOption('\0', "ion-parallel-compile", "on/off",
"Compile scripts off thread (default: off)")
#endif
)
{
return EXIT_FAILURE;