From cdee8bbc50cda8b071c9f88b009764661986cdf0 Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Tue, 13 Nov 2012 18:58:48 -0500 Subject: [PATCH] Bug 781602 - Permit multiple ion compilation modes. r=dvander This implies the possibility of multiple ion scripts per JSScript. * * * Add abstractions for cases that handle all comp. modes at once. * * * split out separate fields for seq, par * * * Convert Invalidate() to assert that it is being run in sequential mode. --- js/src/ion/CodeGenerator.cpp | 97 ++++++++++++-------- js/src/ion/CompileInfo.h | 24 ++++- js/src/ion/ExecutionModeInlines.h | 103 ++++++++++++++++++++++ js/src/ion/Ion.cpp | 141 ++++++++++++++++++++++++------ js/src/ion/Ion.h | 5 ++ js/src/ion/IonBuilder.cpp | 8 +- js/src/jsdbgapi.cpp | 3 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 3 +- js/src/jsinfer.cpp | 24 +++-- js/src/jsinfer.h | 18 +++- js/src/jsinferinlines.h | 49 +++++++---- js/src/jsinterpinlines.h | 8 +- js/src/jsmemorymetrics.cpp | 4 +- js/src/jsscript.cpp | 6 +- js/src/jsscript.h | 32 ++++++- js/src/jsworkers.cpp | 4 +- js/src/methodjit/Compiler.cpp | 2 +- 18 files changed, 414 insertions(+), 119 deletions(-) create mode 100644 js/src/ion/ExecutionModeInlines.h diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index 3f843bcf6104..b7addc635beb 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -13,6 +13,7 @@ #include "jsnum.h" #include "jsmath.h" #include "jsinterpinlines.h" +#include "ExecutionModeInlines.h" #include "vm/StringObject-inl.h" @@ -798,6 +799,17 @@ CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg, return true; } +static inline int32_t ionOffset(ExecutionMode executionMode) +{ + switch (executionMode) { + case SequentialExecution: return offsetof(JSScript, ion); + case ParallelExecution: return offsetof(JSScript, parallelIon); + } + + JS_ASSERT(false); + return offsetof(JSScript, ion); +} + bool CodeGenerator::visitCallGeneric(LCallGeneric *call) { @@ -831,7 +843,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call) // Knowing that calleereg is a non-native function, load the JSScript. masm.movePtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg); - masm.movePtr(Address(objreg, offsetof(JSScript, ion)), objreg); + ExecutionMode executionMode = gen->info().executionMode(); + masm.movePtr(Address(objreg, ionOffset(executionMode)), objreg); // Guard that the IonScript has been compiled. masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke); @@ -889,6 +902,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call) bool CodeGenerator::visitCallKnown(LCallKnown *call) { + JSContext *cx = GetIonContext()->cx; Register calleereg = ToRegister(call->getFunction()); Register objreg = ToRegister(call->getTempObject()); uint32 unusedStack = StackOffsetOfPassedArg(call->argslot()); @@ -903,7 +917,9 @@ CodeGenerator::visitCallKnown(LCallKnown *call) masm.checkStackAlignment(); // If the function is known to be uncompilable, only emit the call to InvokeFunction. - if (target->script()->ion == ION_DISABLED_SCRIPT) { + ExecutionMode executionMode = gen->info().executionMode(); + RootedScript targetScript(cx, target->script()); + if (GetIonScript(targetScript, executionMode) == ION_DISABLED_SCRIPT) { if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack)) return false; @@ -920,7 +936,7 @@ CodeGenerator::visitCallKnown(LCallKnown *call) // Knowing that calleereg is a non-native function, load the JSScript. masm.movePtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg); - masm.movePtr(Address(objreg, offsetof(JSScript, ion)), objreg); + masm.movePtr(Address(objreg, ionOffset(executionMode)), objreg); // Guard that the IonScript has been compiled. masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke); @@ -1088,6 +1104,8 @@ CodeGenerator::emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSpa bool CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) { + JSContext *cx = GetIonContext()->cx; + // Holds the function object. Register calleereg = ToRegister(apply->getFunction()); @@ -1112,14 +1130,15 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) masm.checkStackAlignment(); // If the function is known to be uncompilable, only emit the call to InvokeFunction. - if (apply->hasSingleTarget() && - (!apply->getSingleTarget()->isInterpreted() || - apply->getSingleTarget()->script()->ion == ION_DISABLED_SCRIPT)) - { - if (!emitCallInvokeFunction(apply, copyreg)) - return false; - emitPopArguments(apply, copyreg); - return true; + ExecutionMode executionMode = gen->info().executionMode(); + if (apply->hasSingleTarget()) { + RootedFunction target(cx, apply->getSingleTarget()); + if (!CanIonCompile(cx, target, executionMode)) { + if (!emitCallInvokeFunction(apply, copyreg)) + return false; + emitPopArguments(apply, copyreg); + return true; + } } Label end, invoke; @@ -1134,7 +1153,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) // Knowing that calleereg is a non-native function, load the JSScript. masm.movePtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg); - masm.movePtr(Address(objreg, offsetof(JSScript, ion)), objreg); + masm.movePtr(Address(objreg, ionOffset(executionMode)), objreg); // Guard that the IonScript has been compiled. masm.branchPtr(Assembler::BelowOrEqual, objreg, ImmWord(ION_COMPILING_SCRIPT), &invoke); @@ -2985,7 +3004,8 @@ CodeGenerator::generate() encodeSafepoints(); RootedScript script(cx, gen->info().script()); - JS_ASSERT(!script->hasIonScript()); + ExecutionMode executionMode = gen->info().executionMode(); + JS_ASSERT(!HasIonScript(script, executionMode)); uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None() ? frameDepth_ @@ -2996,48 +3016,51 @@ CodeGenerator::generate() if (cx->compartment->types.compiledInfo.compilerOutput(cx)->isInvalidated()) return true; - script->ion = IonScript::New(cx, slots, scriptFrameSize, snapshots_.size(), - bailouts_.length(), graph.numConstants(), - safepointIndices_.length(), osiIndices_.length(), - cacheList_.length(), barrierOffsets_.length(), - safepoints_.size(), graph.mir().numScripts()); - if (!script->ion) + IonScript *ionScript = + IonScript::New(cx, slots, scriptFrameSize, snapshots_.size(), + bailouts_.length(), graph.numConstants(), + safepointIndices_.length(), osiIndices_.length(), + cacheList_.length(), barrierOffsets_.length(), + safepoints_.size(), graph.mir().numScripts()); + SetIonScript(script, executionMode, ionScript); + + if (!ionScript) return false; invalidateEpilogueData_.fixup(&masm); Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_), - ImmWord(uintptr_t(script->ion)), + ImmWord(uintptr_t(ionScript)), ImmWord(uintptr_t(-1))); IonSpew(IonSpew_Codegen, "Created IonScript %p (raw %p)", - (void *) script->ion, (void *) code->raw()); + (void *) ionScript, (void *) code->raw()); - script->ion->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset()); - script->ion->setOsrPc(gen->info().osrPc()); - script->ion->setOsrEntryOffset(getOsrEntryOffset()); + ionScript->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset()); + ionScript->setOsrPc(gen->info().osrPc()); + ionScript->setOsrEntryOffset(getOsrEntryOffset()); ptrdiff_t real_invalidate = masm.actualOffset(invalidate_.offset()); - script->ion->setInvalidationEpilogueOffset(real_invalidate); + ionScript->setInvalidationEpilogueOffset(real_invalidate); - script->ion->setMethod(code); - script->ion->setDeoptTable(deoptTable_); + ionScript->setMethod(code); + ionScript->setDeoptTable(deoptTable_); if (snapshots_.size()) - script->ion->copySnapshots(&snapshots_); + ionScript->copySnapshots(&snapshots_); if (bailouts_.length()) - script->ion->copyBailoutTable(&bailouts_[0]); + ionScript->copyBailoutTable(&bailouts_[0]); if (graph.numConstants()) - script->ion->copyConstants(graph.constantPool()); + ionScript->copyConstants(graph.constantPool()); if (safepointIndices_.length()) - script->ion->copySafepointIndices(&safepointIndices_[0], masm); + ionScript->copySafepointIndices(&safepointIndices_[0], masm); if (osiIndices_.length()) - script->ion->copyOsiIndices(&osiIndices_[0], masm); + ionScript->copyOsiIndices(&osiIndices_[0], masm); if (cacheList_.length()) - script->ion->copyCacheEntries(&cacheList_[0], masm); + ionScript->copyCacheEntries(&cacheList_[0], masm); if (barrierOffsets_.length()) - script->ion->copyPrebarrierEntries(&barrierOffsets_[0], masm); + ionScript->copyPrebarrierEntries(&barrierOffsets_[0], masm); if (safepoints_.size()) - script->ion->copySafepoints(&safepoints_); + ionScript->copySafepoints(&safepoints_); JS_ASSERT(graph.mir().numScripts() > 0); - script->ion->copyScriptEntries(graph.mir().scripts()); + ionScript->copyScriptEntries(graph.mir().scripts()); linkAbsoluteLabels(); @@ -3045,7 +3068,7 @@ CodeGenerator::generate() // since a GC can occur during code generation. All barriers are emitted // off-by-default, and are toggled on here if necessary. if (cx->compartment->needsBarrier()) - script->ion->toggleBarriers(true); + ionScript->toggleBarriers(true); return true; } diff --git a/js/src/ion/CompileInfo.h b/js/src/ion/CompileInfo.h index 370635913c2f..c6ed074dbe99 100644 --- a/js/src/ion/CompileInfo.h +++ b/js/src/ion/CompileInfo.h @@ -17,12 +17,23 @@ CountArgSlots(JSFunction *fun) return fun ? fun->nargs + 2 : 1; // +2 for |scopeChain| and |this|, or +1 for |scopeChain| } +enum ExecutionMode { + // Normal JavaScript execution + SequentialExecution, + + // JavaScript code to be executed in parallel worker threads, + // e.g. by ParallelArray + ParallelExecution +}; + // Contains information about the compilation source for IR being generated. class CompileInfo { public: - CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing) - : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing) + CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing, + ExecutionMode executionMode) + : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing), + executionMode_(executionMode) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); nslots_ = script->nslots + CountArgSlots(fun); @@ -133,12 +144,21 @@ class CompileInfo return script()->argumentsHasVarBinding(); } + ExecutionMode executionMode() const { + return executionMode_; + } + + bool isParallelExecution() const { + return executionMode_ == ParallelExecution; + } + private: JSScript *script_; JSFunction *fun_; unsigned nslots_; jsbytecode *osrPc_; bool constructing_; + ExecutionMode executionMode_; }; } // namespace ion diff --git a/js/src/ion/ExecutionModeInlines.h b/js/src/ion/ExecutionModeInlines.h new file mode 100644 index 000000000000..6921dd9b7f30 --- /dev/null +++ b/js/src/ion/ExecutionModeInlines.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 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/. */ + +#ifndef jsion_compilemode_h__ +#define jsion_compilemode_h__ + +namespace js { +namespace ion { + +static inline bool HasIonScript(JSScript *script, ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return script->hasIonScript(); + case ParallelExecution: return script->hasParallelIonScript(); + } + JS_NOT_REACHED("No such execution mode"); + return false; +} + +static inline IonScript *GetIonScript(JSScript *script, ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return script->ion; + case ParallelExecution: return script->parallelIon; + } + JS_NOT_REACHED("No such execution mode"); + return NULL; +} + +static inline void SetIonScript(JSScript *script, ExecutionMode cmode, IonScript *ionScript) +{ + switch (cmode) { + case SequentialExecution: script->ion = ionScript; return; + case ParallelExecution: script->parallelIon = ionScript; return; + } + JS_NOT_REACHED("No such execution mode"); +} + +static inline bool CanIonCompile(HandleScript script, ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return script->canIonCompile(); + case ParallelExecution: return script->canParallelIonCompile(); + } + JS_NOT_REACHED("No such execution mode"); + return false; +} + +static inline bool CanIonCompile(JSContext *cx, HandleFunction fun, ExecutionMode cmode) +{ + if (!fun->isInterpreted()) + return false; + RootedScript script(cx, fun->script()); + return CanIonCompile(script, cmode); +} + +static inline bool CompilingOffThread(JSScript *script, ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return script->isIonCompilingOffThread(); + case ParallelExecution: return script->isParallelIonCompilingOffThread(); + } + JS_NOT_REACHED("No such execution mode"); + return false; +} + +static inline bool CompilingOffThread(HandleScript script, ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return script->isIonCompilingOffThread(); + case ParallelExecution: return script->isParallelIonCompilingOffThread(); + } + JS_NOT_REACHED("No such execution mode"); + return false; +} + +static inline bool Disabled(JSScript *script, ExecutionMode cmode) { + switch (cmode) { + case SequentialExecution: return script->isIonCompilingOffThread(); + case ParallelExecution: return script->isParallelIonCompilingOffThread(); + } + JS_NOT_REACHED("No such execution mode"); + return false; +} + +static inline types::CompilerOutput::Kind CompilerOutputKind(ExecutionMode cmode) +{ + switch (cmode) { + case SequentialExecution: return types::CompilerOutput::Ion; + case ParallelExecution: return types::CompilerOutput::ParallelIon; + } + JS_NOT_REACHED("No such execution mode"); + return types::CompilerOutput::Ion; +} + +} +} + +#endif diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index e846c25c810a..02a413b11b9b 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -37,6 +37,7 @@ #include "ion/IonFrames-inl.h" #include "ion/CompilerRoot.h" #include "methodjit/Retcon.h" +#include "ExecutionModeInlines.h" #if JS_TRACE_LOGGING #include "TraceLogging.h" @@ -963,6 +964,16 @@ class AutoDestroyAllocator } }; +class SequentialCompileContext { +public: + ExecutionMode executionMode() { + return SequentialExecution; + } + + bool compile(IonBuilder *builder, MIRGraph *graph, + AutoDestroyAllocator &autoDestroy); +}; + void AttachFinishedCompilations(JSContext *cx) { @@ -988,7 +999,8 @@ AttachFinishedCompilations(JSContext *cx) CodeGenerator codegen(builder, *builder->backgroundCompiledLir); - types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion); + ExecutionMode executionMode = builder->info().executionMode(); + types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode)); enterCompiler.initExisting(builder->recompileInfo); bool success; @@ -1018,8 +1030,10 @@ AttachFinishedCompilations(JSContext *cx) static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; +template static bool -IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing) +IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing, + CompileContext &compileContext) { AssertCanGC(); #if JS_TRACE_LOGGING @@ -1045,7 +1059,9 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, return false; MIRGraph *graph = alloc->new_(temp); - CompileInfo *info = alloc->new_(script, fun, osrPc, constructing); + ExecutionMode executionMode = compileContext.executionMode(); + CompileInfo *info = alloc->new_(script, fun, osrPc, constructing, + executionMode); if (!info) return false; @@ -1057,13 +1073,26 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, AutoFlushCache afc("IonCompile"); - types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion); + types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode)); enterCompiler.init(script, false, 0); AutoTempAllocatorRooter root(cx, temp); IonBuilder *builder = alloc->new_(cx, temp, graph, &oracle, info); + if (!compileContext.compile(builder, graph, autoDestroy)) { + IonSpew(IonSpew_Abort, "IM Compilation failed."); + return false; + } + + return true; +} + +bool +SequentialCompileContext::compile(IonBuilder *builder, MIRGraph *graph, + AutoDestroyAllocator &autoDestroy) +{ JS_ASSERT(!builder->script()->ion); + JSContext *cx = GetIonContext()->cx; IonSpewNewFunction(graph, builder->script().unsafeGet()); @@ -1108,7 +1137,8 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing) { - if (!IonCompile(cx, script, fun, osrPc, constructing)) { + SequentialCompileContext compileContext; + if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext)) { if (!cx->isExceptionPending()) ForbidCompilation(cx, script); return false; @@ -1228,7 +1258,8 @@ Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, boo return Method_Skipped; } - if (!IonCompile(cx, script, fun, osrPc, constructing)) + SequentialCompileContext compileContext; + if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext)) return Method_CantCompile; // Compilation succeeded, but we invalidated right away. @@ -1724,7 +1755,11 @@ ion::Invalidate(types::TypeCompartment &types, FreeOp *fop, bool anyInvalidation = false; for (size_t i = 0; i < invalid.length(); i++) { const types::CompilerOutput &co = *invalid[i].compilerOutput(types); - if (co.isIon()) { + switch (co.kind()) { + case types::CompilerOutput::MethodJIT: + break; + case types::CompilerOutput::Ion: + case types::CompilerOutput::ParallelIon: JS_ASSERT(co.isValid()); IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p", co.script->filename, co.script->lineno, co.ion()); @@ -1750,29 +1785,38 @@ ion::Invalidate(types::TypeCompartment &types, FreeOp *fop, // until its last invalidated frame is destroyed. for (size_t i = 0; i < invalid.length(); i++) { types::CompilerOutput &co = *invalid[i].compilerOutput(types); - if (co.isIon()) { - JS_ASSERT(co.isValid()); - JSScript *script = co.script; - IonScript *ionScript = script->ionScript(); - - JSCompartment *compartment = script->compartment(); - if (compartment->needsBarrier()) { - // We're about to remove edges from the JSScript to gcthings - // embedded in the IonScript. Perform one final trace of the - // IonScript for the incremental GC, as it must know about - // those edges. - IonScript::Trace(compartment->barrierTracer(), ionScript); - } - - ionScript->decref(fop); - script->ion = NULL; - co.invalidate(); - - // Wait for the scripts to get warm again before doing another - // compile, unless we are recompiling *because* a script got hot. - if (resetUses) - script->resetUseCount(); + ExecutionMode executionMode; + switch (co.kind()) { + case types::CompilerOutput::MethodJIT: + continue; + case types::CompilerOutput::Ion: + executionMode = SequentialExecution; + break; + case types::CompilerOutput::ParallelIon: + executionMode = ParallelExecution; + break; } + JS_ASSERT(co.isValid()); + JSScript *script = co.script; + IonScript *ionScript = GetIonScript(script, executionMode); + + JSCompartment *compartment = script->compartment(); + if (compartment->needsBarrier()) { + // We're about to remove edges from the JSScript to gcthings + // embedded in the IonScript. Perform one final trace of the + // IonScript for the incremental GC, as it must know about + // those edges. + IonScript::Trace(compartment->barrierTracer(), ionScript); + } + + ionScript->decref(fop); + SetIonScript(script, executionMode, NULL); + co.invalidate(); + + // Wait for the scripts to get warm again before doing another + // compile, unless we are recompiling *because* a script got hot. + if (resetUses) + script->resetUseCount(); } } @@ -1891,3 +1935,42 @@ AutoFlushCache::AutoFlushCache(const char *nonce, IonCompartment *comp) } int js::ion::LabelBase::id_count = 0; +void +ion::PurgeCaches(JSScript *script, JSCompartment *c) { + if (script->hasIonScript()) + script->ion->purgeCaches(c); + + if (script->hasParallelIonScript()) + script->ion->purgeCaches(c); +} + +size_t +ion::MemoryUsed(JSScript *script, JSMallocSizeOfFun mallocSizeOf) { + size_t result = 0; + + if (script->hasIonScript()) + result += script->ion->sizeOfIncludingThis(mallocSizeOf); + + if (script->hasParallelIonScript()) + result += script->parallelIon->sizeOfIncludingThis(mallocSizeOf); + + return result; +} + +void +ion::DestroyIonScripts(FreeOp *fop, JSScript *script) { + if (script->hasIonScript()) + ion::IonScript::Destroy(fop, script->ion); + + if (script->hasParallelIonScript()) + ion::IonScript::Destroy(fop, script->parallelIon); +} + +void +ion::TraceIonScripts(JSTracer* trc, JSScript *script) { + if (script->hasIonScript()) + ion::IonScript::Trace(trc, script->ion); + + if (script->hasParallelIonScript()) + ion::IonScript::Trace(trc, script->parallelIon); +} diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index 06b8bd881cd4..9963d9d82097 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -268,6 +268,11 @@ static inline bool IsEnabled(JSContext *cx) void ForbidCompilation(JSContext *cx, JSScript *script); uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc); +void PurgeCaches(JSScript *script, JSCompartment *c); +size_t MemoryUsed(JSScript *script, JSMallocSizeOfFun mallocSizeOf); +void DestroyIonScripts(FreeOp *fop, JSScript *script); +void TraceIonScripts(JSTracer* trc, JSScript *script); + } // namespace ion } // namespace js diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 3c87e89aded9..2abbaa6d729e 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -15,6 +15,7 @@ #include "jsscriptinlines.h" #include "jstypedarrayinlines.h" +#include "ExecutionModeInlines.h" #ifdef JS_THREADSAFE # include "prthread.h" @@ -204,8 +205,8 @@ IonBuilder::canInlineTarget(JSFunction *target) } RootedScript inlineScript(cx, target->script()); - - if (!inlineScript->canIonCompile()) { + ExecutionMode executionMode = info().executionMode(); + if (!CanIonCompile(inlineScript, executionMode)) { IonSpew(IonSpew_Inlining, "Cannot inline due to disable Ion compilation"); return false; } @@ -2829,7 +2830,8 @@ IonBuilder::jsop_call_inline(HandleFunction callee, uint32 argc, bool constructi // lifetime. RootedScript calleeScript(cx, callee->script()); CompileInfo *info = cx->tempLifoAlloc().new_(calleeScript.get(), callee, - (jsbytecode *)NULL, constructing); + (jsbytecode *)NULL, constructing, + SequentialExecution); if (!info) return false; diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index c5153eec438e..c8e5c638dde7 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -552,8 +552,9 @@ JS_SetTopFrameAnnotation(JSContext *cx, void *annotation) ReleaseAllJITCode(cx->runtime->defaultFreeOp()); // Ensure that we'll never try to compile this again. - JS_ASSERT(!script->hasIonScript()); + JS_ASSERT(!script->hasAnyIonScript()); script->ion = ION_DISABLED_SCRIPT; + script->parallelIon = ION_DISABLED_SCRIPT; } JS_PUBLIC_API(JSObject *) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9733bd4f0c7b..f2986b92fe67 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -132,7 +132,7 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValu // fully recovered, so we try to mitigate observing this behavior by // detecting its use early. RawScript script = iter.script().get(nogc); - if (!script->hasIonScript()) + if (!script->hasAnyIonScript()) ion::ForbidCompilation(cx, script); #endif diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f0486dae1a81..5cf395b2c9a3 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -5876,8 +5876,7 @@ PurgeJITCaches(JSCompartment *c) #ifdef JS_ION /* Discard Ion caches. */ - if (script->hasIonScript()) - script->ion->purgeCaches(c); + ion::PurgeCaches(script, c); #endif } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index ad31b3a7de95..2390bfb8009e 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -1997,7 +1997,7 @@ JITCodeHasCheck(HandleScript script, jsbytecode *pc, RecompileKind kind) } #endif - if (script->hasIonScript()) + if (script->hasAnyIonScript()) return false; return true; @@ -2026,8 +2026,14 @@ AddPendingRecompile(JSContext *cx, HandleScript script, jsbytecode *pc, RecompileInfo& info = cx->compartment->types.compiledInfo; if (info.outputIndex != RecompileInfo::NoCompilerRunning) { CompilerOutput *co = info.compilerOutput(cx); - if (co->isIon() && co->script == script) { - co->invalidate(); + switch (co->kind()) { + case CompilerOutput::MethodJIT: + break; + case CompilerOutput::Ion: + case CompilerOutput::ParallelIon: + if (co->script == script) + co->invalidate(); + break; } } @@ -2417,11 +2423,16 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop) for (unsigned i = 0; i < pending->length(); i++) { CompilerOutput &co = *(*pending)[i].compilerOutput(*this); - if (co.isJM()) { + switch (co.kind()) { + case CompilerOutput::MethodJIT: JS_ASSERT(co.isValid()); mjit::Recompiler::clearStackReferences(fop, co.script); co.mjit()->destroyChunk(fop, co.chunkIndex); JS_ASSERT(co.script == NULL); + break; + case CompilerOutput::Ion: + case CompilerOutput::ParallelIon: + break; } } @@ -2518,7 +2529,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info) bool hasJITCode = jit && jit->chunkDescriptor(co->chunkIndex).chunk; # if defined(JS_ION) - hasJITCode |= !!co->script->hasIonScript(); + hasJITCode |= !!co->script->hasAnyIonScript(); # endif if (!hasJITCode) { @@ -2585,6 +2596,9 @@ TypeCompartment::addPendingRecompile(JSContext *cx, HandleScript script, jsbytec if (script->hasIonScript()) addPendingRecompile(cx, script->ionScript()->recompileInfo()); + + if (script->hasParallelIonScript()) + addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); # endif #endif } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 93791d66673e..a95dd0dae5f0 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -1213,17 +1213,27 @@ typedef HashMap,AllocationSiteKey,Sy */ struct CompilerOutput { + enum Kind { + MethodJIT, + Ion, + ParallelIon + }; + JSScript *script; - bool isIonFlag : 1; + + // This integer will always be a member of CompilerOutput::Kind, + // but, for portability, bitfields are limited to bool, int, and + // unsigned int. You should really use the accessor below. + unsigned kindInt : 2; bool constructing : 1; bool barriers : 1; bool pendingRecompilation : 1; - uint32_t chunkIndex:28; + uint32_t chunkIndex:27; CompilerOutput(); - bool isJM() const { return !isIonFlag; } - bool isIon() const { return isIonFlag; } + Kind kind() const { return static_cast(kindInt); } + void setKind(Kind k) { kindInt = k; } mjit::JITScript *mjit() const; ion::IonScript *ion() const; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 7fd6a985633c..b28454fb6e94 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -88,7 +88,7 @@ namespace types { inline CompilerOutput::CompilerOutput() : script(NULL), - isIonFlag(false), + kindInt(MethodJIT), constructing(false), barriers(false), chunkIndex(false) @@ -99,7 +99,7 @@ inline mjit::JITScript * CompilerOutput::mjit() const { #ifdef JS_METHODJIT - JS_ASSERT(isJM() && isValid()); + JS_ASSERT(kind() == MethodJIT && isValid()); return script->getJIT(constructing, barriers); #else return NULL; @@ -110,11 +110,15 @@ inline ion::IonScript * CompilerOutput::ion() const { #ifdef JS_ION - JS_ASSERT(isIon() && isValid()); - return script->ionScript(); -#else - return NULL; + JS_ASSERT(kind() != MethodJIT && isValid()); + switch (kind()) { + case MethodJIT: break; + case Ion: return script->ionScript(); + case ParallelIon: return script->parallelIonScript(); + } #endif + JS_NOT_REACHED("Invalid kind of CompilerOutput"); + return NULL; } inline bool @@ -127,8 +131,9 @@ CompilerOutput::isValid() const TypeCompartment &types = script->compartment()->types; #endif + switch (kind()) { + case MethodJIT: { #ifdef JS_METHODJIT - if (isJM()) { mjit::JITScript *jit = script->getJIT(constructing, barriers); if (!jit) return false; @@ -137,20 +142,31 @@ CompilerOutput::isValid() const return false; JS_ASSERT(this == chunk->recompileInfo.compilerOutput(types)); return true; - } #endif + } + case Ion: #ifdef JS_ION - if (isIon()) { if (script->hasIonScript()) { JS_ASSERT(this == script->ion->recompileInfo().compilerOutput(types)); return true; } if (script->isIonCompilingOffThread()) return true; +#endif + return false; + + case ParallelIon: +#ifdef JS_ION + if (script->hasParallelIonScript()) { + JS_ASSERT(this == script->parallelIonScript()->recompileInfo().compilerOutput(types)); + return true; + } + if (script->isParallelIonCompilingOffThread()) + return true; +#endif return false; } -#endif return false; } @@ -387,17 +403,12 @@ struct AutoEnterCompilation { JSContext *cx; RecompileInfo &info; + CompilerOutput::Kind kind; - enum Compiler { - JM, - Ion - }; - Compiler mode; - - AutoEnterCompilation(JSContext *cx, Compiler mode) + AutoEnterCompilation(JSContext *cx, CompilerOutput::Kind kind) : cx(cx), info(cx->compartment->types.compiledInfo), - mode(mode) + kind(kind) { JS_ASSERT(cx->compartment->activeAnalysis); JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning); @@ -407,7 +418,7 @@ struct AutoEnterCompilation { CompilerOutput co; co.script = script; - co.isIonFlag = (mode == Ion); + co.setKind(kind); co.constructing = constructing; co.barriers = cx->compartment->compileBarriers(); co.chunkIndex = chunkIndex; diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index d39cfb46f73e..5228e2b926d7 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -1007,11 +1007,11 @@ class FastInvokeGuard public: FastInvokeGuard(JSContext *cx, const Value &fval) - : fun_(cx), - script_(cx) + : fun_(cx) + , script_(cx) #ifdef JS_ION - , ictx_(cx, cx->compartment, NULL), - useIon_(ion::IsEnabled(cx)) + , ictx_(cx, cx->compartment, NULL) + , useIon_(ion::IsEnabled(cx)) #endif { initFunction(fval); diff --git a/js/src/jsmemorymetrics.cpp b/js/src/jsmemorymetrics.cpp index 7e315a230a7c..8325c3b2cee8 100644 --- a/js/src/jsmemorymetrics.cpp +++ b/js/src/jsmemorymetrics.cpp @@ -20,6 +20,7 @@ #include "jsobjinlines.h" #include "ion/IonCode.h" +#include "ion/Ion.h" namespace js { @@ -232,8 +233,7 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin #ifdef JS_METHODJIT cStats->jaegerData += script->sizeOfJitScripts(rtStats->mallocSizeOf); # ifdef JS_ION - if (script->hasIonScript()) - cStats->ionData += script->ion->sizeOfIncludingThis(rtStats->mallocSizeOf); + cStats->ionData += ion::MemoryUsed(script, rtStats->mallocSizeOf); # endif #endif diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 79400f5ba9ef..efba258c0561 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1904,8 +1904,7 @@ JSScript::finalize(FreeOp *fop) #ifdef JS_METHODJIT mjit::ReleaseScriptCode(fop, this); # ifdef JS_ION - if (hasIonScript()) - ion::IonScript::Destroy(fop, ion); + ion::DestroyIonScripts(fop, this); # endif #endif @@ -2597,8 +2596,7 @@ JSScript::markChildren(JSTracer *trc) } #ifdef JS_ION - if (hasIonScript()) - ion::IonScript::Trace(trc, ion); + ion::TraceIonScripts(trc, this); #endif } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 717d7d11e2ff..5d9b3e7db61f 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -549,26 +549,50 @@ struct JSScript : public js::gc::Cell return needsArgsObj() && !strictModeCode; } - js::ion::IonScript *ion; /* Information attached by Ion */ + bool hasAnyIonScript() const { + return hasIonScript() || hasParallelIonScript(); + } -#if defined(JS_METHODJIT) && JS_BITS_PER_WORD == 32 - void *padding_; -#endif + /* Information attached by Ion: script for sequential mode execution */ + js::ion::IonScript *ion; bool hasIonScript() const { 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; } + /* Information attached by Ion: script for parallel mode execution */ + js::ion::IonScript *parallelIon; + + bool hasParallelIonScript() const { + return parallelIon && parallelIon != ION_DISABLED_SCRIPT && parallelIon != ION_COMPILING_SCRIPT; + } + + bool canParallelIonCompile() const { + return parallelIon != ION_DISABLED_SCRIPT; + } + + bool isParallelIonCompilingOffThread() const { + return parallelIon == ION_COMPILING_SCRIPT; + } + + js::ion::IonScript *parallelIonScript() const { + JS_ASSERT(hasParallelIonScript()); + return parallelIon; + } + /* * Original compiled function for the script, if it has a function. * NULL for global and eval scripts. diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp index 32e55c558f35..59a03e5cdcb4 100644 --- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -8,6 +8,7 @@ #if JS_ION # include "ion/IonBuilder.h" +# include "ion/ExecutionModeInlines.h" #endif using namespace js; @@ -294,7 +295,8 @@ WorkerThread::threadLoop() ionBuilder = state.ionWorklist.popCopy(); - JS_ASSERT(ionBuilder->script()->ion == ION_COMPILING_SCRIPT); + ion::ExecutionMode executionMode = ionBuilder->info().executionMode(); + JS_ASSERT(GetIonScript(ionBuilder->script().unsafeGet(), executionMode) == ION_COMPILING_SCRIPT); state.unlock(); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 5ac307c462c7..3d1485c43507 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -539,7 +539,7 @@ mjit::Compiler::performCompilation() JS_ASSERT(cx->compartment->activeInference); { - types::AutoEnterCompilation enter(cx, types::AutoEnterCompilation::JM); + types::AutoEnterCompilation enter(cx, types::CompilerOutput::MethodJIT); if (!enter.init(outerScript, isConstructing, chunkIndex)) { js_ReportOutOfMemory(cx); return Compile_Error;