mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
Allow compiling scripts off thread with Ion, bug 774253. r=dvander
This commit is contained in:
parent
2af6b6788a
commit
fad2d4be88
@ -106,6 +106,7 @@ CPPSRCS = \
|
|||||||
jsutil.cpp \
|
jsutil.cpp \
|
||||||
jswatchpoint.cpp \
|
jswatchpoint.cpp \
|
||||||
jsweakmap.cpp \
|
jsweakmap.cpp \
|
||||||
|
jsworkers.cpp \
|
||||||
jswrapper.cpp \
|
jswrapper.cpp \
|
||||||
jsxml.cpp \
|
jsxml.cpp \
|
||||||
prmjtime.cpp \
|
prmjtime.cpp \
|
||||||
|
@ -2803,7 +2803,7 @@ CodeGenerator::generate()
|
|||||||
encodeSafepoints();
|
encodeSafepoints();
|
||||||
|
|
||||||
JSScript *script = gen->info().script();
|
JSScript *script = gen->info().script();
|
||||||
JS_ASSERT(!script->ion);
|
JS_ASSERT(!script->hasIonScript());
|
||||||
|
|
||||||
uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None()
|
uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None()
|
||||||
? frameDepth_
|
? frameDepth_
|
||||||
|
@ -35,12 +35,12 @@ class CompilerRoot : public CompilerRootNode
|
|||||||
public:
|
public:
|
||||||
// Sets the pointer and inserts into root list. The pointer becomes read-only.
|
// Sets the pointer and inserts into root list. The pointer becomes read-only.
|
||||||
void setRoot(T root) {
|
void setRoot(T root) {
|
||||||
JSRuntime *rt = root->compartment()->rt;
|
JS::CompilerRootNode *&rootList = GetIonContext()->temp->rootList();
|
||||||
|
|
||||||
JS_ASSERT(!ptr);
|
JS_ASSERT(!ptr);
|
||||||
ptr = root;
|
ptr = root;
|
||||||
next = rt->ionCompilerRootList;
|
next = rootList;
|
||||||
rt->ionCompilerRootList = this;
|
rootList = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -53,24 +53,6 @@ typedef CompilerRoot<JSFunction*> CompilerRootFunction;
|
|||||||
typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
|
typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
|
||||||
typedef CompilerRoot<Value> CompilerRootValue;
|
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 ion
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "RangeAnalysis.h"
|
#include "RangeAnalysis.h"
|
||||||
#include "LinearScan.h"
|
#include "LinearScan.h"
|
||||||
#include "jscompartment.h"
|
#include "jscompartment.h"
|
||||||
|
#include "jsworkers.h"
|
||||||
#include "IonCompartment.h"
|
#include "IonCompartment.h"
|
||||||
#include "CodeGenerator.h"
|
#include "CodeGenerator.h"
|
||||||
|
|
||||||
@ -146,6 +147,29 @@ IonCompartment::initialize(JSContext *cx)
|
|||||||
return true;
|
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
|
void
|
||||||
IonCompartment::mark(JSTracer *trc, JSCompartment *compartment)
|
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
|
// functionWrappers_ are not marked because this is a WeakCache of VM
|
||||||
// function implementations.
|
// function implementations.
|
||||||
|
|
||||||
|
// Cancel any active or pending off thread compilations.
|
||||||
|
CancelOffThreadIonCompile(compartment, NULL);
|
||||||
|
FinishAllOffThreadCompilations(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -700,16 +728,16 @@ ion::ToggleBarriers(JSCompartment *comp, bool needs)
|
|||||||
namespace js {
|
namespace js {
|
||||||
namespace ion {
|
namespace ion {
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
BuildMIR(IonBuilder &builder, MIRGraph &graph)
|
CompileBackEnd(IonBuilder *builder)
|
||||||
{
|
{
|
||||||
if (!builder.build())
|
|
||||||
return false;
|
|
||||||
IonSpewPass("BuildSSA");
|
IonSpewPass("BuildSSA");
|
||||||
// Note: don't call AssertGraphCoherency before SplitCriticalEdges,
|
// Note: don't call AssertGraphCoherency before SplitCriticalEdges,
|
||||||
// the graph is not in RPO at this point.
|
// the graph is not in RPO at this point.
|
||||||
|
|
||||||
if (!SplitCriticalEdges(&builder, graph))
|
MIRGraph &graph = builder->graph();
|
||||||
|
|
||||||
|
if (!SplitCriticalEdges(builder, graph))
|
||||||
return false;
|
return false;
|
||||||
IonSpewPass("Split Critical Edges");
|
IonSpewPass("Split Critical Edges");
|
||||||
AssertGraphCoherency(graph);
|
AssertGraphCoherency(graph);
|
||||||
@ -813,50 +841,141 @@ BuildMIR(IonBuilder &builder, MIRGraph &graph)
|
|||||||
IonSpewPass("Bounds Check Elimination");
|
IonSpewPass("Bounds Check Elimination");
|
||||||
AssertGraphCoherency(graph);
|
AssertGraphCoherency(graph);
|
||||||
|
|
||||||
return true;
|
LIRGraph *lir = builder->temp().lifoAlloc()->new_<LIRGraph>(graph);
|
||||||
}
|
if (!lir)
|
||||||
|
return false;
|
||||||
|
|
||||||
static bool
|
LIRGenerator lirgen(builder, graph, *lir);
|
||||||
GenerateCode(IonBuilder &builder, MIRGraph &graph)
|
|
||||||
{
|
|
||||||
LIRGraph lir(graph);
|
|
||||||
LIRGenerator lirgen(&builder, graph, lir);
|
|
||||||
if (!lirgen.generate())
|
if (!lirgen.generate())
|
||||||
return false;
|
return false;
|
||||||
IonSpewPass("Generate LIR");
|
IonSpewPass("Generate LIR");
|
||||||
|
|
||||||
if (js_IonOptions.lsra) {
|
if (js_IonOptions.lsra) {
|
||||||
LinearScanAllocator regalloc(&lirgen, lir);
|
LinearScanAllocator regalloc(&lirgen, *lir);
|
||||||
if (!regalloc.go())
|
if (!regalloc.go())
|
||||||
return false;
|
return false;
|
||||||
IonSpewPass("Allocate Registers", ®alloc);
|
IonSpewPass("Allocate Registers", ®alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeGenerator codegen(&builder, lir);
|
builder->lir = lir;
|
||||||
if (!codegen.generate())
|
|
||||||
return false;
|
|
||||||
// No spew: graph not changed.
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
class AutoDestroyAllocator
|
||||||
TestCompiler(IonBuilder &builder, MIRGraph &graph)
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
if (!GenerateCode(builder, graph))
|
CodeGenerator codegen(builder, *builder->lir);
|
||||||
|
if (!codegen.generate())
|
||||||
return false;
|
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();
|
IonSpewEndFunction();
|
||||||
|
|
||||||
return true;
|
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
|
static bool
|
||||||
IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
|
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);
|
script);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TempAllocator temp(&cx->tempLifoAlloc());
|
LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||||
IonContext ictx(cx, cx->compartment, &temp);
|
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))
|
if (!cx->compartment->ensureIonCompartmentExists(cx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MIRGraph graph(&temp);
|
MIRGraph *graph = alloc->new_<MIRGraph>(temp);
|
||||||
CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(script, fun, osrPc, constructing);
|
CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing);
|
||||||
if (!info)
|
if (!info)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -888,10 +1016,11 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
|
|||||||
|
|
||||||
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
|
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
|
||||||
enterCompiler.init(script, false, 0);
|
enterCompiler.init(script, false, 0);
|
||||||
AutoCompilerRoots roots(script->compartment()->rt);
|
|
||||||
|
|
||||||
IonBuilder builder(cx, &temp, &graph, &oracle, info);
|
AutoTempAllocatorRooter root(cx, temp);
|
||||||
if (!Compiler(builder, graph)) {
|
|
||||||
|
IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, info);
|
||||||
|
if (!Compiler(builder, graph, autoDestroy)) {
|
||||||
IonSpew(IonSpew_Abort, "IM Compilation failed.");
|
IonSpew(IonSpew_Abort, "IM Compilation failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -899,6 +1028,17 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
|
|||||||
return true;
|
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
|
static bool
|
||||||
CheckFrame(StackFrame *fp)
|
CheckFrame(StackFrame *fp)
|
||||||
{
|
{
|
||||||
@ -979,7 +1119,7 @@ CheckScriptSize(JSScript *script)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Compiler(IonBuilder &, MIRGraph &)>
|
template <bool Compiler(IonBuilder *, MIRGraph *, AutoDestroyAllocator &)>
|
||||||
static MethodStatus
|
static MethodStatus
|
||||||
Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
|
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)
|
if (script->ion == ION_DISABLED_SCRIPT)
|
||||||
return Method_Skipped;
|
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.
|
// Skip if the code is expected to result in a bailout.
|
||||||
if (script->ion && script->ion->bailoutExpected())
|
if (script->ion && script->ion->bailoutExpected())
|
||||||
return Method_Skipped;
|
return Method_Skipped;
|
||||||
@ -1079,6 +1223,10 @@ ion::CanEnter(JSContext *cx, JSScript *script, StackFrame *fp, bool newType)
|
|||||||
if (script->ion == ION_DISABLED_SCRIPT)
|
if (script->ion == ION_DISABLED_SCRIPT)
|
||||||
return Method_Skipped;
|
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.
|
// Skip if the code is expected to result in a bailout.
|
||||||
if (script->ion && script->ion->bailoutExpected())
|
if (script->ion && script->ion->bailoutExpected())
|
||||||
return Method_Skipped;
|
return Method_Skipped;
|
||||||
@ -1379,6 +1527,13 @@ InvalidateActivation(FreeOp *fop, uint8 *ionTop, bool invalidateAll)
|
|||||||
void
|
void
|
||||||
ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
|
ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
|
||||||
{
|
{
|
||||||
|
if (!c->ionCompartment())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CancelOffThreadIonCompile(c, NULL);
|
||||||
|
|
||||||
|
FinishAllOffThreadCompilations(c->ionCompartment());
|
||||||
|
|
||||||
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
|
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
|
||||||
if (iter.activation()->compartment() == c) {
|
if (iter.activation()->compartment() == c) {
|
||||||
AutoFlushCache afc ("InvalidateAll", c->ionCompartment());
|
AutoFlushCache afc ("InvalidateAll", c->ionCompartment());
|
||||||
|
@ -67,6 +67,11 @@ struct IonOptions
|
|||||||
// Default: false
|
// Default: false
|
||||||
bool rangeAnalysis;
|
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
|
// How many invocations or loop iterations are needed before functions
|
||||||
// are compiled.
|
// are compiled.
|
||||||
//
|
//
|
||||||
@ -140,6 +145,8 @@ struct IonOptions
|
|||||||
// Eagerly inline calls to improve test coverage.
|
// Eagerly inline calls to improve test coverage.
|
||||||
usesBeforeInlining = 0;
|
usesBeforeInlining = 0;
|
||||||
smallFunctionUsesBeforeInlining = 0;
|
smallFunctionUsesBeforeInlining = 0;
|
||||||
|
|
||||||
|
parallelCompilation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IonOptions()
|
IonOptions()
|
||||||
@ -152,6 +159,7 @@ struct IonOptions
|
|||||||
inlining(true),
|
inlining(true),
|
||||||
edgeCaseAnalysis(true),
|
edgeCaseAnalysis(true),
|
||||||
rangeAnalysis(false),
|
rangeAnalysis(false),
|
||||||
|
parallelCompilation(false),
|
||||||
usesBeforeCompile(10240),
|
usesBeforeCompile(10240),
|
||||||
usesBeforeCompileNoJaeger(40),
|
usesBeforeCompileNoJaeger(40),
|
||||||
usesBeforeInlining(usesBeforeCompile),
|
usesBeforeInlining(usesBeforeCompile),
|
||||||
@ -163,7 +171,8 @@ struct IonOptions
|
|||||||
inlineMaxTotalBytecodeLength(800),
|
inlineMaxTotalBytecodeLength(800),
|
||||||
eagerCompilation(false),
|
eagerCompilation(false),
|
||||||
slowCallLimit(512)
|
slowCallLimit(512)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MethodStatus
|
enum MethodStatus
|
||||||
@ -230,6 +239,13 @@ void MarkFromIon(JSCompartment *comp, Value *vp);
|
|||||||
|
|
||||||
void ToggleBarriers(JSCompartment *comp, bool needs);
|
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)
|
static inline bool IsEnabled(JSContext *cx)
|
||||||
{
|
{
|
||||||
return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
|
return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
|
||||||
|
@ -22,10 +22,14 @@ class TempAllocator
|
|||||||
LifoAlloc *lifoAlloc_;
|
LifoAlloc *lifoAlloc_;
|
||||||
void *mark_;
|
void *mark_;
|
||||||
|
|
||||||
|
// Linked list of GCThings rooted by this allocator.
|
||||||
|
JS::CompilerRootNode *rootList_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TempAllocator(LifoAlloc *lifoAlloc)
|
TempAllocator(LifoAlloc *lifoAlloc)
|
||||||
: lifoAlloc_(lifoAlloc),
|
: lifoAlloc_(lifoAlloc),
|
||||||
mark_(lifoAlloc->mark())
|
mark_(lifoAlloc->mark()),
|
||||||
|
rootList_(NULL)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
~TempAllocator()
|
~TempAllocator()
|
||||||
@ -53,6 +57,11 @@ class TempAllocator
|
|||||||
return lifoAlloc_;
|
return lifoAlloc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS::CompilerRootNode *&rootList()
|
||||||
|
{
|
||||||
|
return rootList_;
|
||||||
|
}
|
||||||
|
|
||||||
bool ensureBallast() {
|
bool ensureBallast() {
|
||||||
// Most infallible Ion allocations are small, so we use a ballast of
|
// Most infallible Ion allocations are small, so we use a ballast of
|
||||||
// ~16K for now.
|
// ~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
|
class IonAllocPolicy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -27,6 +27,8 @@ IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
|
|||||||
TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
|
TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
|
||||||
: MIRGenerator(cx->compartment, temp, graph, info),
|
: MIRGenerator(cx->compartment, temp, graph, info),
|
||||||
script(info->script()),
|
script(info->script()),
|
||||||
|
recompileInfo(cx->compartment->types.compiledInfo),
|
||||||
|
lir(NULL),
|
||||||
cx(cx),
|
cx(cx),
|
||||||
loopDepth_(loopDepth),
|
loopDepth_(loopDepth),
|
||||||
callerResumePoint_(NULL),
|
callerResumePoint_(NULL),
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
namespace js {
|
namespace js {
|
||||||
namespace ion {
|
namespace ion {
|
||||||
|
|
||||||
|
class LIRGraph;
|
||||||
|
|
||||||
class IonBuilder : public MIRGenerator
|
class IonBuilder : public MIRGenerator
|
||||||
{
|
{
|
||||||
enum ControlStatus {
|
enum ControlStatus {
|
||||||
@ -425,6 +427,12 @@ class IonBuilder : public MIRGenerator
|
|||||||
// A builder is inextricably tied to a particular script.
|
// A builder is inextricably tied to a particular script.
|
||||||
JSScript * const 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();
|
void clearForBackEnd();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -23,6 +23,9 @@ typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
|
|||||||
CalleeToken calleeToken, Value *vp);
|
CalleeToken calleeToken, Value *vp);
|
||||||
|
|
||||||
class IonActivation;
|
class IonActivation;
|
||||||
|
class IonBuilder;
|
||||||
|
|
||||||
|
typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
|
||||||
|
|
||||||
class IonCompartment
|
class IonCompartment
|
||||||
{
|
{
|
||||||
@ -37,7 +40,7 @@ class IonCompartment
|
|||||||
ReadBarriered<IonCode> enterJIT_;
|
ReadBarriered<IonCode> enterJIT_;
|
||||||
|
|
||||||
// Vector mapping frame class sizes to bailout tables.
|
// 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.
|
// Generic bailout table; used if the bailout table overflows.
|
||||||
ReadBarriered<IonCode> bailoutHandler_;
|
ReadBarriered<IonCode> bailoutHandler_;
|
||||||
@ -55,8 +58,14 @@ class IonCompartment
|
|||||||
// Map VMFunction addresses to the IonCode of the wrapper.
|
// Map VMFunction addresses to the IonCode of the wrapper.
|
||||||
VMWrapperMap *functionWrappers_;
|
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.
|
// Keep track of memoryregions that are going to be flushed.
|
||||||
js::ion::AutoFlushCache *flusher_;
|
AutoFlushCache *flusher_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IonCode *generateEnterJIT(JSContext *cx);
|
IonCode *generateEnterJIT(JSContext *cx);
|
||||||
@ -70,6 +79,10 @@ class IonCompartment
|
|||||||
public:
|
public:
|
||||||
IonCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
|
IonCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
|
||||||
|
|
||||||
|
OffThreadCompilationVector &finishedOffThreadCompilations() {
|
||||||
|
return finishedOffThreadCompilations_;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool initialize(JSContext *cx);
|
bool initialize(JSContext *cx);
|
||||||
IonCompartment();
|
IonCompartment();
|
||||||
|
@ -621,10 +621,9 @@ ion::MarkIonActivations(JSRuntime *rt, JSTracer *trc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ion::MarkIonCompilerRoots(JSTracer *trc)
|
ion::AutoTempAllocatorRooter::trace(JSTracer *trc)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = trc->runtime;
|
for (CompilerRootNode *root = temp->rootList(); root != NULL; root = root->next)
|
||||||
for (CompilerRootNode *root = rt->ionCompilerRootList; root != NULL; root = root->next)
|
|
||||||
gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
|
gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
#include "Ion.h"
|
||||||
#include "IonSpewer.h"
|
#include "IonSpewer.h"
|
||||||
|
|
||||||
#ifndef ION_SPEW_DIR
|
#ifndef ION_SPEW_DIR
|
||||||
@ -42,25 +43,29 @@ ion::EnableIonDebugLogging()
|
|||||||
void
|
void
|
||||||
ion::IonSpewNewFunction(MIRGraph *graph, JSScript *function)
|
ion::IonSpewNewFunction(MIRGraph *graph, JSScript *function)
|
||||||
{
|
{
|
||||||
ionspewer.beginFunction(graph, function);
|
if (!js_IonOptions.parallelCompilation)
|
||||||
|
ionspewer.beginFunction(graph, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ion::IonSpewPass(const char *pass)
|
ion::IonSpewPass(const char *pass)
|
||||||
{
|
{
|
||||||
ionspewer.spewPass(pass);
|
if (!js_IonOptions.parallelCompilation)
|
||||||
|
ionspewer.spewPass(pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ion::IonSpewPass(const char *pass, LinearScanAllocator *ra)
|
ion::IonSpewPass(const char *pass, LinearScanAllocator *ra)
|
||||||
{
|
{
|
||||||
ionspewer.spewPass(pass, ra);
|
if (!js_IonOptions.parallelCompilation)
|
||||||
|
ionspewer.spewPass(pass, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ion::IonSpewEndFunction()
|
ion::IonSpewEndFunction()
|
||||||
{
|
{
|
||||||
ionspewer.endFunction();
|
if (!js_IonOptions.parallelCompilation)
|
||||||
|
ionspewer.endFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
#include "prmjtime.h"
|
#include "prmjtime.h"
|
||||||
#include "jsweakmap.h"
|
#include "jsweakmap.h"
|
||||||
|
#include "jsworkers.h"
|
||||||
#include "jswrapper.h"
|
#include "jswrapper.h"
|
||||||
#include "jstypedarray.h"
|
#include "jstypedarray.h"
|
||||||
#include "jsxml.h"
|
#include "jsxml.h"
|
||||||
@ -864,7 +865,6 @@ JSRuntime::JSRuntime()
|
|||||||
ionJSContext(NULL),
|
ionJSContext(NULL),
|
||||||
ionStackLimit(0),
|
ionStackLimit(0),
|
||||||
ionActivation(NULL),
|
ionActivation(NULL),
|
||||||
ionCompilerRootList(NULL),
|
|
||||||
ionReturnOverride_(MagicValue(JS_ARG_POISON))
|
ionReturnOverride_(MagicValue(JS_ARG_POISON))
|
||||||
{
|
{
|
||||||
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
|
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
|
||||||
@ -928,6 +928,10 @@ JSRuntime::init(uint32_t maxbytes)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
workerThreadState = this->new_<WorkerThreadState>();
|
||||||
|
if (!workerThreadState || !workerThreadState->init(this))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!sourceCompressorThread.init())
|
if (!sourceCompressorThread.init())
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@ -958,6 +962,7 @@ JSRuntime::~JSRuntime()
|
|||||||
FreeScriptFilenames(this);
|
FreeScriptFilenames(this);
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
delete_(workerThreadState);
|
||||||
sourceCompressorThread.finish();
|
sourceCompressorThread.finish();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1064,7 +1064,8 @@ class JS_PUBLIC_API(AutoGCRooter) {
|
|||||||
REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
|
REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
|
||||||
NAMEVECTOR = -26, /* js::AutoNameVector */
|
NAMEVECTOR = -26, /* js::AutoNameVector */
|
||||||
HASHABLEVALUE=-27,
|
HASHABLEVALUE=-27,
|
||||||
IONMASM = -28 /* js::ion::MacroAssembler */
|
IONMASM = -28, /* js::ion::MacroAssembler */
|
||||||
|
IONALLOC = -29 /* js::ion::AutoTempAllocatorRooter */
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#include "jsscope.h"
|
#include "jsscope.h"
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
|
#include "jsworkers.h"
|
||||||
|
#include "ion/Ion.h"
|
||||||
#include "ion/IonFrames.h"
|
#include "ion/IonFrames.h"
|
||||||
|
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
@ -389,6 +391,10 @@ js::DestroyContext(JSContext *cx, DestroyContextMode mode)
|
|||||||
for (CompartmentsIter c(rt); !c.done(); c.next())
|
for (CompartmentsIter c(rt); !c.done(); c.next())
|
||||||
c->types.print(cx, false);
|
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. */
|
/* Unpin all common atoms before final GC. */
|
||||||
FinishCommonAtoms(rt);
|
FinishCommonAtoms(rt);
|
||||||
|
|
||||||
@ -1010,6 +1016,12 @@ js_InvokeOperationCallback(JSContext *cx)
|
|||||||
if (rt->gcIsNeeded)
|
if (rt->gcIsNeeded)
|
||||||
GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
|
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
|
* Important: Additional callbacks can occur inside the callback handler
|
||||||
* if it re-enters the JS engine. The embedding must ensure that the
|
* if it re-enters the JS engine. The embedding must ensure that the
|
||||||
|
@ -77,6 +77,7 @@ class IonActivation;
|
|||||||
class WeakMapBase;
|
class WeakMapBase;
|
||||||
class InterpreterFrames;
|
class InterpreterFrames;
|
||||||
class DebugScopes;
|
class DebugScopes;
|
||||||
|
class WorkerThreadState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
|
* 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;
|
js::GCHelperThread gcHelperThread;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
js::WorkerThreadState *workerThreadState;
|
||||||
|
|
||||||
js::SourceCompressorThread sourceCompressorThread;
|
js::SourceCompressorThread sourceCompressorThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -894,9 +897,6 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||||||
// This points to the most recent Ion activation running on the thread.
|
// This points to the most recent Ion activation running on the thread.
|
||||||
js::ion::IonActivation *ionActivation;
|
js::ion::IonActivation *ionActivation;
|
||||||
|
|
||||||
// Linked list of GCThings rooted for the current compilation.
|
|
||||||
JS::CompilerRootNode *ionCompilerRootList;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// In certain cases, we want to optimize certain opcodes to typed instructions,
|
// In certain cases, we want to optimize certain opcodes to typed instructions,
|
||||||
// to avoid carrying an extra register to feed into an unbox. Unfortunately,
|
// to avoid carrying an extra register to feed into an unbox. Unfortunately,
|
||||||
|
@ -2491,6 +2491,13 @@ AutoGCRooter::trace(JSTracer *trc)
|
|||||||
case IONMASM: {
|
case IONMASM: {
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
static_cast<js::ion::MacroAssembler::AutoRooter *>(this)->masm()->trace(trc);
|
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
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2623,7 +2630,6 @@ MarkRuntime(JSTracer *trc, bool useSavedRoots = false)
|
|||||||
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
ion::MarkIonActivations(rt, trc);
|
ion::MarkIonActivations(rt, trc);
|
||||||
ion::MarkIonCompilerRoots(trc);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (CompartmentsIter c(rt); !c.done(); c.next())
|
for (CompartmentsIter c(rt); !c.done(); c.next())
|
||||||
@ -2918,7 +2924,7 @@ AssertBackgroundSweepingFinshed(JSRuntime *rt)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
static unsigned
|
unsigned
|
||||||
GetCPUCount()
|
GetCPUCount()
|
||||||
{
|
{
|
||||||
static unsigned ncpus = 0;
|
static unsigned ncpus = 0;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "jsscope.h"
|
#include "jsscope.h"
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
#include "jsiter.h"
|
#include "jsiter.h"
|
||||||
|
#include "jsworkers.h"
|
||||||
|
|
||||||
#include "ion/Ion.h"
|
#include "ion/Ion.h"
|
||||||
#include "ion/IonCompartment.h"
|
#include "ion/IonCompartment.h"
|
||||||
@ -2253,6 +2254,9 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
|||||||
if (co->pendingRecompilation)
|
if (co->pendingRecompilation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (co->isValid())
|
||||||
|
CancelOffThreadIonCompile(cx->compartment, co->script);
|
||||||
|
|
||||||
if (!co->isValid()) {
|
if (!co->isValid()) {
|
||||||
JS_ASSERT(co->script == NULL);
|
JS_ASSERT(co->script == NULL);
|
||||||
return;
|
return;
|
||||||
@ -2319,6 +2323,8 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ifdef JS_ION
|
# ifdef JS_ION
|
||||||
|
CancelOffThreadIonCompile(cx->compartment, script);
|
||||||
|
|
||||||
if (script->hasIonScript())
|
if (script->hasIonScript())
|
||||||
addPendingRecompile(cx, script->ionScript()->recompileInfo());
|
addPendingRecompile(cx, script->ionScript()->recompileInfo());
|
||||||
# endif
|
# endif
|
||||||
@ -5849,7 +5855,6 @@ TypeCompartment::sweep(FreeOp *fop)
|
|||||||
void
|
void
|
||||||
TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
|
TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (constrainedOutputs) {
|
if (constrainedOutputs) {
|
||||||
bool isCompiling = compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning;
|
bool isCompiling = compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning;
|
||||||
if (isCompiling && !compartment()->activeAnalysis)
|
if (isCompiling && !compartment()->activeAnalysis)
|
||||||
@ -5860,6 +5865,7 @@ TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
|
|||||||
JS_ASSERT(!co.isValid());
|
JS_ASSERT(!co.isValid());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fop->delete_(constrainedOutputs);
|
fop->delete_(constrainedOutputs);
|
||||||
constrainedOutputs = NULL;
|
constrainedOutputs = NULL;
|
||||||
} else {
|
} else {
|
||||||
@ -5868,14 +5874,12 @@ TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
|
|||||||
// potentially created multiple types with this index. Instead, we
|
// potentially created multiple types with this index. Instead, we
|
||||||
// invalidate all compilations except the one running now.
|
// invalidate all compilations except the one running now.
|
||||||
size_t len = constrainedOutputs->length();
|
size_t len = constrainedOutputs->length();
|
||||||
if (isCompiling) {
|
|
||||||
len--;
|
|
||||||
JS_ASSERT(compiledInfo.outputIndex == len);
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < len; i++) {
|
for (unsigned i = 0; i < len; i++) {
|
||||||
CompilerOutput &co = (*constrainedOutputs)[i];
|
if (i != compiledInfo.outputIndex) {
|
||||||
JS_ASSERT(!co.isValid());
|
CompilerOutput &co = (*constrainedOutputs)[i];
|
||||||
co.invalidate();
|
JS_ASSERT(!co.isValid());
|
||||||
|
co.invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,10 +80,13 @@ CompilerOutput::isValid() const
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (isIon()) {
|
if (isIon()) {
|
||||||
if (!script->hasIonScript())
|
if (script->hasIonScript()) {
|
||||||
return false;
|
JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
|
||||||
JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types));
|
return true;
|
||||||
return true;
|
}
|
||||||
|
if (script->isIonCompilingOffThread())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -372,6 +375,13 @@ struct AutoEnterCompilation
|
|||||||
return true;
|
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()
|
~AutoEnterCompilation()
|
||||||
{
|
{
|
||||||
CompilerOutput *co = info.compilerOutput(cx);
|
CompilerOutput *co = info.compilerOutput(cx);
|
||||||
|
@ -21,6 +21,10 @@
|
|||||||
# define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((int32_t *)(p), (int32_t)(v))
|
# 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))
|
# define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((int32_t *)(p), (int32_t)(v))
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
unsigned GetCPUCount();
|
||||||
|
}
|
||||||
|
|
||||||
#else /* JS_THREADSAFE */
|
#else /* JS_THREADSAFE */
|
||||||
|
|
||||||
typedef struct PRThread PRThread;
|
typedef struct PRThread PRThread;
|
||||||
|
@ -551,11 +551,14 @@ struct JSScript : public js::gc::Cell
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool hasIonScript() const {
|
bool hasIonScript() const {
|
||||||
return ion && ion != ION_DISABLED_SCRIPT;
|
return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
|
||||||
}
|
}
|
||||||
bool canIonCompile() const {
|
bool canIonCompile() const {
|
||||||
return ion != ION_DISABLED_SCRIPT;
|
return ion != ION_DISABLED_SCRIPT;
|
||||||
}
|
}
|
||||||
|
bool isIonCompilingOffThread() const {
|
||||||
|
return ion == ION_COMPILING_SCRIPT;
|
||||||
|
}
|
||||||
js::ion::IonScript *ionScript() const {
|
js::ion::IonScript *ionScript() const {
|
||||||
JS_ASSERT(hasIonScript());
|
JS_ASSERT(hasIonScript());
|
||||||
return ion;
|
return ion;
|
||||||
|
295
js/src/jsworkers.cpp
Normal file
295
js/src/jsworkers.cpp
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/* -*- 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;
|
||||||
|
numThreads = 0;
|
||||||
|
|
||||||
|
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
162
js/src/jsworkers.h
Normal 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___
|
@ -51,12 +51,6 @@ using namespace js::analyze;
|
|||||||
return retval; \
|
return retval; \
|
||||||
JS_END_MACRO
|
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,
|
mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
|
||||||
unsigned chunkIndex, bool isConstructing)
|
unsigned chunkIndex, bool isConstructing)
|
||||||
: BaseCompiler(cx),
|
: BaseCompiler(cx),
|
||||||
@ -104,12 +98,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
|
|||||||
gcNumber(cx->runtime->gcNumber),
|
gcNumber(cx->runtime->gcNumber),
|
||||||
pcLengths(NULL)
|
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
|
CompileStatus
|
||||||
@ -961,6 +949,11 @@ IonGetsFirstChance(JSContext *cx, JSScript *script, CompileRequest request)
|
|||||||
if (script->hasIonScript() && script->ion->bailoutExpected())
|
if (script->hasIonScript() && script->ion->bailoutExpected())
|
||||||
return false;
|
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;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
@ -1036,19 +1029,6 @@ mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|||||||
unsigned chunkIndex = jit->chunkIndex(pc);
|
unsigned chunkIndex = jit->chunkIndex(pc);
|
||||||
ChunkDescriptor &desc = jit->chunkDescriptor(chunkIndex);
|
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)
|
if (desc.chunk)
|
||||||
return Compile_Okay;
|
return Compile_Okay;
|
||||||
|
|
||||||
@ -1230,7 +1210,7 @@ mjit::Compiler::generatePrologue()
|
|||||||
|
|
||||||
CompileStatus status = methodEntryHelper();
|
CompileStatus status = methodEntryHelper();
|
||||||
if (status == Compile_Okay)
|
if (status == Compile_Okay)
|
||||||
recompileCheckHelper();
|
ionCompileHelper();
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -3269,7 +3249,7 @@ mjit::Compiler::generateMethod()
|
|||||||
// Insert the recompile check here so that we can immediately
|
// Insert the recompile check here so that we can immediately
|
||||||
// enter Ion.
|
// enter Ion.
|
||||||
if (loop)
|
if (loop)
|
||||||
recompileCheckHelper();
|
ionCompileHelper();
|
||||||
END_CASE(JSOP_LOOPENTRY)
|
END_CASE(JSOP_LOOPENTRY)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_DEBUGGER)
|
BEGIN_CASE(JSOP_DEBUGGER)
|
||||||
@ -3971,56 +3951,61 @@ MaybeIonCompileable(JSContext *cx, JSScript *script, bool *recompileCheckForIon)
|
|||||||
return false;
|
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
|
#ifdef JS_ION
|
||||||
if (recompileCheckForIon)
|
void
|
||||||
minUses = ion::UsesBeforeIonRecompile(outerScript, PC);
|
mjit::Compiler::ionCompileHelper()
|
||||||
#endif
|
{
|
||||||
|
if (debugMode() || !globalObj || !cx->typeInferenceEnabled())
|
||||||
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)
|
|
||||||
return;
|
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)
|
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
|
||||||
Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr),
|
Jump first = masm.branch32(Assembler::LessThan, AbsoluteAddress(useCountAddress),
|
||||||
Imm32(minUses));
|
Imm32(minUses));
|
||||||
|
Jump second = masm.branch32(Assembler::Equal, AbsoluteAddress(ionScriptAddress),
|
||||||
|
Imm32(0));
|
||||||
#else
|
#else
|
||||||
/* Handle processors that can't load from absolute addresses. */
|
/* Handle processors that can't load from absolute addresses. */
|
||||||
RegisterID reg = frame.allocReg();
|
RegisterID reg = frame.allocReg();
|
||||||
masm.move(ImmPtr(addr), reg);
|
masm.move(ImmPtr(useCountAddress), reg);
|
||||||
Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, Address(reg, 0),
|
Jump first = masm.branch32(Assembler::LessThan, Address(reg), Imm32(minUses));
|
||||||
Imm32(minUses));
|
masm.move(ImmPtr(ionScriptAddress), reg);
|
||||||
|
Jump second = masm.branchPtr(Assembler::Equal, Address(reg), ImmPtr(NULL));
|
||||||
frame.freeReg(reg);
|
frame.freeReg(reg);
|
||||||
#endif
|
#endif
|
||||||
stubcc.linkExit(jump, Uses(0));
|
first.linkTo(masm.label(), &masm);
|
||||||
|
|
||||||
|
stubcc.linkExit(second, Uses(0));
|
||||||
stubcc.leave();
|
stubcc.leave();
|
||||||
|
|
||||||
OOL_STUBCALL(stubs::RecompileForInline, REJOIN_RESUME);
|
OOL_STUBCALL(stubs::TriggerIonCompile, REJOIN_RESUME);
|
||||||
stubcc.rejoin(Changes(0));
|
stubcc.rejoin(Changes(0));
|
||||||
}
|
}
|
||||||
|
#else /* JS_ION */
|
||||||
|
void
|
||||||
|
mjit::Compiler::ionCompileHelper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* JS_ION */
|
||||||
|
|
||||||
CompileStatus
|
CompileStatus
|
||||||
mjit::Compiler::methodEntryHelper()
|
mjit::Compiler::methodEntryHelper()
|
||||||
|
@ -631,7 +631,7 @@ private:
|
|||||||
void emitInlineReturnValue(FrameEntry *fe);
|
void emitInlineReturnValue(FrameEntry *fe);
|
||||||
void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc);
|
void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc);
|
||||||
void interruptCheckHelper();
|
void interruptCheckHelper();
|
||||||
void recompileCheckHelper();
|
void ionCompileHelper();
|
||||||
CompileStatus methodEntryHelper();
|
CompileStatus methodEntryHelper();
|
||||||
CompileStatus profilingPushHelper();
|
CompileStatus profilingPushHelper();
|
||||||
void profilingPopHelper();
|
void profilingPopHelper();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "jsscope.h"
|
#include "jsscope.h"
|
||||||
#include "ion/Ion.h"
|
#include "ion/Ion.h"
|
||||||
#include "ion/IonCompartment.h"
|
#include "ion/IonCompartment.h"
|
||||||
|
#include "methodjit/Retcon.h"
|
||||||
|
|
||||||
#include "jsgcinlines.h"
|
#include "jsgcinlines.h"
|
||||||
#include "jsinterpinlines.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
|
#ifdef JS_METHODJIT_PROFILE_STUBS
|
||||||
void JS_FASTCALL
|
void JS_FASTCALL
|
||||||
mjit::ProfileStubCall(VMFrame &f)
|
mjit::ProfileStubCall(VMFrame &f)
|
||||||
|
@ -795,13 +795,6 @@ struct JITScript
|
|||||||
*/
|
*/
|
||||||
uint32_t ionCalls;
|
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
|
#ifdef JS_MONOIC
|
||||||
/* Inline cache at function entry for checking this/argument types. */
|
/* Inline cache at function entry for checking this/argument types. */
|
||||||
JSC::CodeLocationLabel argsCheckStub;
|
JSC::CodeLocationLabel argsCheckStub;
|
||||||
@ -909,6 +902,10 @@ ReleaseScriptCode(FreeOp *fop, JSScript *script)
|
|||||||
script->destroyMJITInfo(fop);
|
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.
|
// Expand all stack frames inlined by the JIT within a compartment.
|
||||||
void
|
void
|
||||||
ExpandInlineFrames(JSCompartment *compartment);
|
ExpandInlineFrames(JSCompartment *compartment);
|
||||||
|
@ -591,7 +591,7 @@ class CallCompiler : public BaseCompiler
|
|||||||
|
|
||||||
/* Guard that the ion pointer is valid. */
|
/* Guard that the ion pointer is valid. */
|
||||||
Jump noIonCode = masm.branchPtr(Assembler::BelowOrEqual, ionScript,
|
Jump noIonCode = masm.branchPtr(Assembler::BelowOrEqual, ionScript,
|
||||||
ImmPtr(ION_DISABLED_SCRIPT));
|
ImmPtr(ION_COMPILING_SCRIPT));
|
||||||
|
|
||||||
RegisterID t0 = regs.takeAnyReg().reg();
|
RegisterID t0 = regs.takeAnyReg().reg();
|
||||||
RegisterID t1 = Registers::ClobberInCall;
|
RegisterID t1 = Registers::ClobberInCall;
|
||||||
|
@ -771,28 +771,19 @@ stubs::Interrupt(VMFrame &f, jsbytecode *pc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JS_FASTCALL
|
void JS_FASTCALL
|
||||||
stubs::RecompileForInline(VMFrame &f)
|
stubs::TriggerIonCompile(VMFrame &f)
|
||||||
{
|
{
|
||||||
JSScript *script = f.script();
|
JSScript *script = f.script();
|
||||||
|
JS_ASSERT(!script->ion);
|
||||||
|
|
||||||
ExpandInlineFrames(f.cx->compartment);
|
jsbytecode *osrPC = f.regs.pc;
|
||||||
Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
|
if (*osrPC != JSOP_LOOPENTRY)
|
||||||
|
osrPC = NULL;
|
||||||
|
|
||||||
#ifdef JS_ION
|
if (!ion::TestIonCompile(f.cx, script, script->function(), osrPC, f.fp()->isConstructing())) {
|
||||||
if (ion::IsEnabled(f.cx) && f.jit()->nchunks == 1 &&
|
if (f.cx->isExceptionPending())
|
||||||
script->canIonCompile() && !script->hasIonScript())
|
THROW();
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JS_FASTCALL
|
void JS_FASTCALL
|
||||||
|
@ -26,7 +26,7 @@ void JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
|
|||||||
void JS_FASTCALL Trap(VMFrame &f, uint32_t trapTypes);
|
void JS_FASTCALL Trap(VMFrame &f, uint32_t trapTypes);
|
||||||
void JS_FASTCALL DebuggerStatement(VMFrame &f, jsbytecode *pc);
|
void JS_FASTCALL DebuggerStatement(VMFrame &f, jsbytecode *pc);
|
||||||
void JS_FASTCALL Interrupt(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 InitElem(VMFrame &f, uint32_t last);
|
||||||
void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name);
|
void JS_FASTCALL InitProp(VMFrame &f, PropertyName *name);
|
||||||
|
|
||||||
|
@ -4878,7 +4878,24 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
|||||||
|
|
||||||
if (op->getBoolOption("ion-eager"))
|
if (op->getBoolOption("ion-eager"))
|
||||||
ion::js_IonOptions.setEagerCompilation();
|
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. */
|
/* |scriptArgs| gets bound on the global before any code is run. */
|
||||||
if (!BindScriptArgs(cx, obj, op))
|
if (!BindScriptArgs(cx, obj, op))
|
||||||
@ -5084,6 +5101,10 @@ main(int argc, char **argv, char **envp)
|
|||||||
"Specify Ion register allocation:\n"
|
"Specify Ion register allocation:\n"
|
||||||
" lsra: Linear Scan register allocation (default)")
|
" lsra: Linear Scan register allocation (default)")
|
||||||
|| !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods")
|
|| !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;
|
return EXIT_FAILURE;
|
||||||
|
Loading…
Reference in New Issue
Block a user