Bug 1178834: IonMonkey - Always lazy link code, r=jandem

This commit is contained in:
Hannes Verschore 2015-08-14 17:57:57 +02:00
parent b8397463f1
commit 3f53d0fb48
8 changed files with 119 additions and 141 deletions

View File

@ -67,7 +67,8 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
postDebugPrologueOffset_(postDebugPrologueOffset),
flags_(0),
inlinedBytecodeLength_(0),
maxInliningDepth_(UINT8_MAX)
maxInliningDepth_(UINT8_MAX),
pendingBuilder_(nullptr)
{ }
static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
@ -473,6 +474,8 @@ BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
*/
MOZ_ASSERT(fop->runtime()->gc.nursery.isEmpty());
MOZ_ASSERT(!script->hasPendingIonBuilder());
script->unlinkDependentAsmJSModules(fop);
fop->delete_(script);

View File

@ -224,6 +224,9 @@ struct BaselineScript
// more info.
uint8_t maxInliningDepth_;
// An ion compilation that is ready, but isn't linked yet.
IonBuilder *pendingBuilder_;
public:
// Do not call directly, use BaselineScript::New. This is public for cx->new_.
BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
@ -456,6 +459,32 @@ struct BaselineScript
len = UINT16_MAX;
inlinedBytecodeLength_ = len;
}
bool hasPendingIonBuilder() const {
return !!pendingBuilder_;
}
js::jit::IonBuilder* pendingIonBuilder() {
MOZ_ASSERT(hasPendingIonBuilder());
return pendingBuilder_;
}
void setPendingIonBuilder(JSContext* maybecx, JSScript* script, js::jit::IonBuilder* builder) {
MOZ_ASSERT(script->baselineScript() == this);
MOZ_ASSERT(!builder || !hasPendingIonBuilder());
if (script->isIonCompilingOffThread())
script->setIonScript(maybecx, ION_PENDING_SCRIPT);
pendingBuilder_ = builder;
script->updateBaselineOrIonRaw(maybecx);
}
void removePendingIonBuilder(JSScript* script) {
setPendingIonBuilder(nullptr, script, nullptr);
if (script->maybeIonScript() == ION_PENDING_SCRIPT)
script->setIonScript(nullptr, nullptr);
}
};
static_assert(sizeof(BaselineScript) % sizeof(uintptr_t) == 0,
"The data attached to the script must be aligned for fast JIT access.");

View File

@ -468,8 +468,11 @@ void
jit::FinishOffThreadBuilder(JSContext* cx, IonBuilder* builder)
{
// Clean the references to the pending IonBuilder, if we just finished it.
if (builder->script()->hasIonScript() && builder->script()->pendingIonBuilder() == builder)
builder->script()->setPendingIonBuilder(cx, nullptr);
if (builder->script()->baselineScript()->hasPendingIonBuilder() &&
builder->script()->baselineScript()->pendingIonBuilder() == builder)
{
builder->script()->baselineScript()->removePendingIonBuilder(builder->script());
}
// If the builder is still in one of the helper thread list, then remove it.
if (builder->isInList())
@ -566,21 +569,13 @@ LinkBackgroundCodeGen(JSContext* cx, IonBuilder* builder,
return LinkCodeGen(cx, builder, codegen, scripts, info);
}
uint8_t*
jit::LazyLinkTopActivation(JSContext* cx)
void
jit::LazyLink(JSContext* cx, HandleScript calleeScript)
{
JitActivationIterator iter(cx->runtime());
AutoLazyLinkExitFrame lazyLinkExitFrame(iter->asJit());
// First frame should be an exit frame.
JitFrameIterator it(iter);
LazyLinkExitFrameLayout* ll = it.exitFrame()->as<LazyLinkExitFrameLayout>();
RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken()));
// Get the pending builder from the Ion frame.
IonBuilder* builder = calleeScript->ionScript()->pendingBuilder();
calleeScript->setPendingIonBuilder(cx, nullptr);
MOZ_ASSERT(calleeScript->hasBaselineScript());
IonBuilder* builder = calleeScript->baselineScript()->pendingIonBuilder();
calleeScript->baselineScript()->removePendingIonBuilder(calleeScript);
// See PrepareForDebuggerOnIonCompilationHook
AutoScriptVector debugScripts(cx);
@ -606,6 +601,20 @@ jit::LazyLinkTopActivation(JSContext* cx)
MOZ_ASSERT(calleeScript->hasBaselineScript());
MOZ_ASSERT(calleeScript->baselineOrIonRawPointer());
}
uint8_t*
jit::LazyLinkTopActivation(JSContext* cx)
{
JitActivationIterator iter(cx->runtime());
AutoLazyLinkExitFrame lazyLinkExitFrame(iter->asJit());
// First frame should be an exit frame.
JitFrameIterator it(iter);
LazyLinkExitFrameLayout* ll = it.exitFrame()->as<LazyLinkExitFrameLayout>();
RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken()));
LazyLink(cx, calleeScript);
return calleeScript->baselineOrIonRawPointer();
}
@ -889,8 +898,7 @@ IonScript::IonScript()
backedgeEntries_(0),
invalidationCount_(0),
recompileInfo_(),
osrPcMismatchCounter_(0),
pendingBuilder_(nullptr)
osrPcMismatchCounter_(0)
{
}
@ -1210,9 +1218,6 @@ IonScript::Trace(JSTracer* trc, IonScript* script)
void
IonScript::Destroy(FreeOp* fop, IonScript* script)
{
if (script->pendingBuilder())
jit::FinishOffThreadBuilder(nullptr, script->pendingBuilder());
script->unlinkFromRuntime(fop);
fop->free_(script);
}
@ -1763,25 +1768,6 @@ GetFinishedBuilder(JSContext* cx, GlobalHelperThreadState::IonBuilderVector& fin
return nullptr;
}
static bool
IsBuilderScriptOnStack(JSContext* cx, IonBuilder* builder)
{
for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
for (JitFrameIterator it(iter); !it.done(); ++it) {
if (!it.isIonJS())
continue;
if (it.checkInvalidation())
continue;
JSScript* script = it.script();
if (builder->script() == script)
return true;
}
}
return false;
}
void
AttachFinishedCompilations(JSContext* cx)
{
@ -1789,19 +1775,6 @@ AttachFinishedCompilations(JSContext* cx)
if (!ion)
return;
LifoAlloc* debuggerAlloc = cx->new_<LifoAlloc>(TempAllocator::PreferredLifoChunkSize);
if (!debuggerAlloc) {
// Silently ignore OOM during code generation. The caller is
// InvokeInterruptCallback, which always runs at a nondeterministic
// time. It's not OK to throw a catchable exception from there.
cx->clearPendingException();
return;
}
// See PrepareForDebuggerOnIonCompilationHook
AutoScriptVector debugScripts(cx);
OnIonCompilationVector onIonCompilationVector(cx);
{
AutoEnterAnalysis enterTypes(cx);
AutoLockHelperThreadState lock;
@ -1816,52 +1789,13 @@ AttachFinishedCompilations(JSContext* cx)
if (!builder)
break;
// Try to defer linking if the script is on the stack, to postpone
// invalidating them.
if (builder->script()->hasIonScript() && IsBuilderScriptOnStack(cx, builder)) {
builder->script()->setPendingIonBuilder(cx, builder);
HelperThreadState().ionLazyLinkList().insertFront(builder);
continue;
}
AutoUnlockHelperThreadState unlock;
OnIonCompilationInfo info(debuggerAlloc);
if (!LinkBackgroundCodeGen(cx, builder, &debugScripts, &info)) {
// Silently ignore OOM during code generation. The caller is
// InvokeInterruptCallback, which always runs at a
// nondeterministic time. It's not OK to throw a catchable
// exception from there.
cx->clearPendingException();
}
if (info.filled()) {
if (!onIonCompilationVector.append(info))
cx->clearPendingException();
}
FinishOffThreadBuilder(cx, builder);
}
}
for (size_t i = 0; i < onIonCompilationVector.length(); i++) {
OnIonCompilationInfo& info = onIonCompilationVector[i];
// As it is easier to root a vector, instead of a vector of vector, we
// slice for each compilation.
AutoScriptVector sliceScripts(cx);
if (!sliceScripts.reserve(info.numBlocks)) {
cx->clearPendingException();
JSScript* script = builder->script();
MOZ_ASSERT(script->hasBaselineScript());
script->baselineScript()->setPendingIonBuilder(cx, script, builder);
HelperThreadState().ionLazyLinkList().insertFront(builder);
continue;
}
for (size_t b = 0; b < info.numBlocks; b++)
sliceScripts.infallibleAppend(debugScripts[info.scriptIndex + b]);
Debugger::onIonCompilation(cx, sliceScripts, info.graph);
}
js_delete(debuggerAlloc);
}
static void
@ -2215,8 +2149,8 @@ Compile(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode*
if (optimizationLevel == Optimization_DontCompile)
return Method_Skipped;
IonScript* scriptIon = script->maybeIonScript();
if (scriptIon) {
if (script->hasIonScript()) {
IonScript* scriptIon = script->ionScript();
if (!scriptIon->method())
return Method_CantCompile;
@ -2235,6 +2169,14 @@ Compile(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode*
recompile = true;
}
if (script->baselineScript()->hasPendingIonBuilder()) {
IonBuilder* buildIon = script->baselineScript()->pendingIonBuilder();
if (optimizationLevel <= buildIon->optimizationInfo().level() && !forceRecompile)
return Method_Compiled;
recompile = true;
}
AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing,
recompile, optimizationLevel);
if (reason == AbortReason_Error)
@ -2273,7 +2215,7 @@ jit::OffThreadCompilationAvailable(JSContext* cx)
// Decide if a transition from interpreter execution to Ion code should occur.
// May compile or recompile the target JSScript.
MethodStatus
jit::CanEnterAtBranch(JSContext* cx, JSScript* script, BaselineFrame* osrFrame, jsbytecode* pc)
jit::CanEnterAtBranch(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode* pc)
{
MOZ_ASSERT(jit::IsIonEnabled(cx));
MOZ_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
@ -2301,6 +2243,11 @@ jit::CanEnterAtBranch(JSContext* cx, JSScript* script, BaselineFrame* osrFrame,
return Method_CantCompile;
}
// Check if the jitcode still needs to get linked and do this
// to have a valid IonScript.
if (script->baselineScript()->hasPendingIonBuilder())
LazyLink(cx, script);
// By default a recompilation doesn't happen on osr mismatch.
// Decide if we want to force a recompilation if this happens too much.
bool force = false;
@ -2329,7 +2276,7 @@ jit::CanEnterAtBranch(JSContext* cx, JSScript* script, BaselineFrame* osrFrame,
// This can happen when there was still an IonScript available and a
// background compilation started, but hasn't finished yet.
// Or when we didn't force a recompile.
if (pc != script->ionScript()->osrPc())
if (script->hasIonScript() && pc != script->ionScript()->osrPc())
return Method_Skipped;
return Method_Compiled;
@ -2397,6 +2344,12 @@ jit::CanEnter(JSContext* cx, RunState& state)
return status;
}
if (state.script()->baselineScript()->hasPendingIonBuilder()) {
LazyLink(cx, state.script());
if (!state.script()->hasIonScript())
return jit::Method_Skipped;
}
return Method_Compiled;
}

View File

@ -82,7 +82,7 @@ void SetJitContext(JitContext* ctx);
bool CanIonCompileScript(JSContext* cx, JSScript* script, bool osr);
MethodStatus CanEnterAtBranch(JSContext* cx, JSScript* script,
MethodStatus CanEnterAtBranch(JSContext* cx, HandleScript script,
BaselineFrame* frame, jsbytecode* pc);
MethodStatus CanEnter(JSContext* cx, RunState& state);
MethodStatus CompileFunctionForBaseline(JSContext* cx, HandleScript script, BaselineFrame* frame);
@ -147,6 +147,7 @@ void FinishOffThreadBuilder(JSContext* cx, IonBuilder* builder);
void StopAllOffThreadCompilations(Zone* zone);
void StopAllOffThreadCompilations(JSCompartment* comp);
void LazyLink(JSContext* cx, HandleScript calleescript);
uint8_t* LazyLinkTopActivation(JSContext* cx);
static inline bool

View File

@ -275,8 +275,6 @@ struct IonScript
// The tracelogger event used to log the start/stop of this IonScript.
TraceLoggerEvent traceLoggerScriptEvent_;
IonBuilder* pendingBuilder_;
private:
inline uint8_t* bottomBuffer() {
return reinterpret_cast<uint8_t*>(this);
@ -287,14 +285,6 @@ struct IonScript
public:
// SHOULD ONLY BE CALLED FROM JSScript
void setPendingBuilderPrivate(IonBuilder* builder) {
pendingBuilder_ = builder;
}
IonBuilder* pendingBuilder() const {
return pendingBuilder_;
}
SnapshotOffset* bailoutTable() {
return (SnapshotOffset*) &bottomBuffer()[bailoutTable_];
}

View File

@ -1327,6 +1327,17 @@ JSScript::getPCCounts(jsbytecode* pc) {
return p->value().pcCountsVector[pcToOffset(pc)];
}
void
JSScript::setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript)
{
MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
if (hasIonScript())
js::jit::IonScript::writeBarrierPre(zone(), ion);
ion = ionScript;
MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
updateBaselineOrIonRaw(maybecx);
}
void
JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
{
@ -4045,15 +4056,14 @@ LazyScript::hasUncompiledEnclosingScript() const
void
JSScript::updateBaselineOrIonRaw(JSContext* maybecx)
{
if (hasIonScript()) {
if (ion->pendingBuilder()) {
MOZ_ASSERT(maybecx);
baselineOrIonRaw = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
baselineOrIonSkipArgCheck = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
} else {
baselineOrIonRaw = ion->method()->raw();
baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
}
if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
MOZ_ASSERT(maybecx);
MOZ_ASSERT(!isIonCompilingOffThread());
baselineOrIonRaw = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
baselineOrIonSkipArgCheck = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
} else if (hasIonScript()) {
baselineOrIonRaw = ion->method()->raw();
baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
} else if (hasBaselineScript()) {
baselineOrIonRaw = baseline->method()->raw();
baselineOrIonSkipArgCheck = baseline->method()->raw();

View File

@ -38,6 +38,7 @@ namespace jit {
# define ION_DISABLED_SCRIPT ((js::jit::IonScript*)0x1)
# define ION_COMPILING_SCRIPT ((js::jit::IonScript*)0x2)
# define ION_PENDING_SCRIPT ((js::jit::IonScript*)0x3)
# define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
@ -965,8 +966,15 @@ class JSScript : public js::gc::TenuredCell
js::HeapPtrFunction function_;
js::HeapPtrObject enclosingStaticScope_;
/* Information attached by Baseline/Ion for sequential mode execution. */
/*
* Information attached by Ion. Nexto a valid IonScript this could be
* ION_DISABLED_SCRIPT, ION_COMPILING_SCRIPT or ION_PENDING_SCRIPT.
* The later is a ion compilation that is ready, but hasn't been linked
* yet.
*/
js::jit::IonScript* ion;
/* Information attached by Baseline. */
js::jit::BaselineScript* baseline;
/* Information used to re-lazify a lazily-parsed interpreted function. */
@ -1472,14 +1480,14 @@ class JSScript : public js::gc::TenuredCell
}
bool hasIonScript() const {
bool res = ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
bool res = ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT &&
ion != ION_PENDING_SCRIPT;
MOZ_ASSERT_IF(res, baseline);
return res;
}
bool canIonCompile() const {
return ion != ION_DISABLED_SCRIPT;
}
bool isIonCompilingOffThread() const {
return ion == ION_COMPILING_SCRIPT;
}
@ -1494,13 +1502,7 @@ class JSScript : public js::gc::TenuredCell
js::jit::IonScript* const* addressOfIonScript() const {
return &ion;
}
void setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript) {
if (hasIonScript())
js::jit::IonScript::writeBarrierPre(zone(), ion);
ion = ionScript;
MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
updateBaselineOrIonRaw(maybecx);
}
void setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript);
bool hasBaselineScript() const {
bool res = baseline && baseline != BASELINE_DISABLED_SCRIPT;
@ -1518,16 +1520,6 @@ class JSScript : public js::gc::TenuredCell
void updateBaselineOrIonRaw(JSContext* maybecx);
void setPendingIonBuilder(JSContext* maybecx, js::jit::IonBuilder* builder) {
MOZ_ASSERT(!builder || !ion->pendingBuilder());
ion->setPendingBuilderPrivate(builder);
updateBaselineOrIonRaw(maybecx);
}
js::jit::IonBuilder* pendingIonBuilder() {
MOZ_ASSERT(hasIonScript());
return ion->pendingBuilder();
}
static size_t offsetOfBaselineScript() {
return offsetof(JSScript, baseline);
}

View File

@ -181,7 +181,7 @@ js::CancelOffThreadIonCompile(JSCompartment* compartment, JSScript* script)
while (builder) {
jit::IonBuilder* next = builder->getNext();
if (CompiledScriptMatches(compartment, script, builder->script())) {
builder->script()->setPendingIonBuilder(nullptr, nullptr);
builder->script()->baselineScript()->removePendingIonBuilder(builder->script());
jit::FinishOffThreadBuilder(nullptr, builder);
}
builder = next;