-
-
Save syg/4063118 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# Parent b857905d82b45456853866330c98b372b14f20db | |
# User Nicholas D. Matsakis <[email protected]> | |
Permit multiple ion compilation modes. 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. | |
diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp | |
--- a/js/src/ion/CodeGenerator.cpp | |
+++ b/js/src/ion/CodeGenerator.cpp | |
@@ -8,16 +8,17 @@ | |
#include "CodeGenerator.h" | |
#include "IonLinker.h" | |
#include "IonSpewer.h" | |
#include "MIRGenerator.h" | |
#include "shared/CodeGenerator-shared-inl.h" | |
#include "jsnum.h" | |
#include "jsmath.h" | |
#include "jsinterpinlines.h" | |
+#include "ExecutionModeInlines.h" | |
#include "vm/StringObject-inl.h" | |
using namespace js; | |
using namespace js::ion; | |
using mozilla::DebugOnly; | |
@@ -793,16 +794,27 @@ CodeGenerator::emitCallInvokeFunction(LI | |
if (!callVM(InvokeFunctionInfo, call)) | |
return false; | |
// Un-nestle %esp from the argument vector. No prefix was pushed. | |
masm.reserveStack(unusedStack); | |
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) | |
{ | |
Register calleereg = ToRegister(call->getFunction()); | |
Register objreg = ToRegister(call->getTempObject()); | |
Register nargsreg = ToRegister(call->getNargsReg()); | |
uint32 unusedStack = StackOffsetOfPassedArg(call->argslot()); | |
Label invoke, thunk, makeCall, end; | |
@@ -826,17 +838,18 @@ CodeGenerator::visitCallGeneric(LCallGen | |
if (!bailoutIf(Assembler::NotEqual, call->snapshot())) | |
return false; | |
// Guard that calleereg is a non-native function: | |
masm.branchIfFunctionIsNative(calleereg, &invoke); | |
// 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); | |
// Nestle the StackPointer up to the argument vector. | |
masm.freeStack(unusedStack); | |
// Construct the IonFramePrefix. | |
@@ -884,48 +897,51 @@ CodeGenerator::visitCallGeneric(LCallGen | |
masm.bind(&end); | |
dropArguments(call->numStackArgs() + 1); | |
return true; | |
} | |
bool | |
CodeGenerator::visitCallKnown(LCallKnown *call) | |
{ | |
+ JSContext *cx = GetIonContext()->cx; | |
Register calleereg = ToRegister(call->getFunction()); | |
Register objreg = ToRegister(call->getTempObject()); | |
uint32 unusedStack = StackOffsetOfPassedArg(call->argslot()); | |
JSFunction *target = call->getSingleTarget(); | |
Label end, invoke; | |
// Native single targets are handled by LCallNative. | |
JS_ASSERT(!target->isNative()); | |
// Missing arguments must have been explicitly appended by the IonBuilder. | |
JS_ASSERT(target->nargs <= call->numStackArgs()); | |
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; | |
if (call->mir()->isConstructing()) { | |
Label notPrimitive; | |
masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, ¬Primitive); | |
masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand); | |
masm.bind(¬Primitive); | |
} | |
dropArguments(call->numStackArgs() + 1); | |
return true; | |
} | |
// 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); | |
// Load the start of the target IonCode. | |
masm.movePtr(Address(objreg, IonScript::offsetOfMethod()), objreg); | |
masm.movePtr(Address(objreg, IonCode::offsetOfCode()), objreg); | |
@@ -1083,16 +1099,18 @@ CodeGenerator::emitPopArguments(LApplyAr | |
{ | |
// Pop |this| and Arguments. | |
masm.freeStack(extraStackSpace); | |
} | |
bool | |
CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) | |
{ | |
+ JSContext *cx = GetIonContext()->cx; | |
+ | |
// Holds the function object. | |
Register calleereg = ToRegister(apply->getFunction()); | |
// Temporary register for modifying the function object. | |
Register objreg = ToRegister(apply->getTempObject()); | |
Register copyreg = ToRegister(apply->getTempCopy()); | |
// Holds the function nargs. Initially undefined. | |
@@ -1107,39 +1125,40 @@ CodeGenerator::visitApplyArgsGeneric(LAp | |
} | |
// Copy the arguments of the current function. | |
emitPushArguments(apply, copyreg); | |
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; | |
// Guard that calleereg is a non-native function: | |
if (!apply->hasSingleTarget()) { | |
masm.branchIfFunctionIsNative(calleereg, &invoke); | |
} else { | |
// Native single targets are handled by LCallNative. | |
JS_ASSERT(!apply->getSingleTarget()->isNative()); | |
} | |
// 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); | |
// Call with an Ion frame or a rectifier frame. | |
{ | |
// Create the frame descriptor. | |
unsigned pushed = masm.framePushed(); | |
@@ -2980,77 +2999,81 @@ CodeGenerator::generate() | |
IonCode *code = linker.newCode(cx); | |
if (!code) | |
return false; | |
// We encode safepoints after the OSI-point offsets have been determined. | |
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_ | |
: FrameSizeClass::FromDepth(frameDepth_).frameSize(); | |
// Check to make sure we didn't have a mid-build invalidation. If so, we | |
// will trickle to ion::Compile() and return Method_Skipped. | |
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()); | |
- | |
- script->ion->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset()); | |
- script->ion->setOsrPc(gen->info().osrPc()); | |
- script->ion->setOsrEntryOffset(getOsrEntryOffset()); | |
+ (void *) ionScript, (void *) code->raw()); | |
+ | |
+ 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); | |
- | |
- script->ion->setMethod(code); | |
- script->ion->setDeoptTable(deoptTable_); | |
+ ionScript->setInvalidationEpilogueOffset(real_invalidate); | |
+ | |
+ 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(); | |
// The correct state for prebarriers is unknown until the end of compilation, | |
// 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; | |
} | |
// An out-of-line path to convert a boxed int32 to a double. | |
class OutOfLineUnboxDouble : public OutOfLineCodeBase<CodeGenerator> | |
{ | |
LUnboxDouble *unboxDouble_; | |
diff --git a/js/src/ion/CompileInfo.h b/js/src/ion/CompileInfo.h | |
--- a/js/src/ion/CompileInfo.h | |
+++ b/js/src/ion/CompileInfo.h | |
@@ -12,22 +12,33 @@ namespace js { | |
namespace ion { | |
inline unsigned | |
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); | |
} | |
JSScript *script() const { | |
return script_; | |
} | |
@@ -128,20 +139,29 @@ class CompileInfo | |
uint32 stackSlot(uint32 i) const { | |
return firstStackSlot() + i; | |
} | |
bool hasArguments() { | |
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 | |
} // namespace js | |
#endif | |
diff --git a/js/src/ion/ExecutionModeInlines.h b/js/src/ion/ExecutionModeInlines.h | |
new file mode 100644 | |
--- /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 | |
--- a/js/src/ion/Ion.cpp | |
+++ b/js/src/ion/Ion.cpp | |
@@ -32,16 +32,17 @@ | |
#include "gc/Marking.h" | |
#include "jsgcinlines.h" | |
#include "jsinferinlines.h" | |
#include "jsobjinlines.h" | |
#include "vm/Stack-inl.h" | |
#include "ion/IonFrames-inl.h" | |
#include "ion/CompilerRoot.h" | |
#include "methodjit/Retcon.h" | |
+#include "ExecutionModeInlines.h" | |
#if JS_TRACE_LOGGING | |
#include "TraceLogging.h" | |
#endif | |
using namespace js; | |
using namespace js::ion; | |
@@ -958,16 +959,26 @@ class AutoDestroyAllocator | |
~AutoDestroyAllocator() | |
{ | |
if (alloc) | |
js_delete(alloc); | |
} | |
}; | |
+class SequentialCompileContext { | |
+public: | |
+ ExecutionMode executionMode() { | |
+ return SequentialExecution; | |
+ } | |
+ | |
+ bool compile(IonBuilder *builder, MIRGraph *graph, | |
+ AutoDestroyAllocator &autoDestroy); | |
+}; | |
+ | |
void | |
AttachFinishedCompilations(JSContext *cx) | |
{ | |
#ifdef JS_THREADSAFE | |
AssertCanGC(); | |
IonCompartment *ion = cx->compartment->ionCompartment(); | |
if (!ion || !cx->runtime->workerThreadState) | |
return; | |
@@ -983,17 +994,18 @@ AttachFinishedCompilations(JSContext *cx | |
IonBuilder *builder = compilations.popCopy(); | |
if (builder->backgroundCompiledLir) { | |
RootedScript script(cx, builder->script()); | |
IonContext ictx(cx, cx->compartment, &builder->temp()); | |
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; | |
{ | |
// Release the worker thread lock and root the compiler for GC. | |
AutoTempAllocatorRooter root(cx, &builder->temp()); | |
AutoUnlockWorkerThreadState unlock(cx->runtime); | |
success = codegen.generate(); | |
@@ -1013,18 +1025,20 @@ AttachFinishedCompilations(JSContext *cx | |
} | |
compilations.clear(); | |
#endif | |
} | |
static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; | |
+template <typename CompileContext> | |
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 | |
AutoTraceLog logger(TraceLogging::defaultLogger(), | |
TraceLogging::ION_COMPILE_START, | |
TraceLogging::ION_COMPILE_STOP, | |
script); | |
#endif | |
@@ -1040,35 +1054,50 @@ IonCompile(JSContext *cx, JSScript *scri | |
return false; | |
IonContext ictx(cx, cx->compartment, temp); | |
if (!cx->compartment->ensureIonCompartmentExists(cx)) | |
return false; | |
MIRGraph *graph = alloc->new_<MIRGraph>(temp); | |
- CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing); | |
+ ExecutionMode executionMode = compileContext.executionMode(); | |
+ CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing, | |
+ executionMode); | |
if (!info) | |
return false; | |
types::AutoEnterTypeInference enter(cx, true); | |
TypeInferenceOracle oracle; | |
if (!oracle.init(cx, script)) | |
return false; | |
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_<IonBuilder>(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()); | |
if (!builder->build()) { | |
IonSpew(IonSpew_Abort, "Builder failed to build."); | |
return false; | |
} | |
builder->clearForBackEnd(); | |
@@ -1103,17 +1132,18 @@ IonCompile(JSContext *cx, JSScript *scri | |
IonSpewEndFunction(); | |
return true; | |
} | |
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; | |
} | |
return true; | |
} | |
static bool | |
@@ -1223,17 +1253,18 @@ Compile(JSContext *cx, JSScript *script, | |
// bumping the use count twice. | |
if (script->getUseCount() < js_IonOptions.usesBeforeCompile) | |
return Method_Skipped; | |
} else { | |
if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger) | |
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. | |
return script->hasIonScript() ? Method_Compiled : Method_Skipped; | |
} | |
} // namespace ion | |
} // namespace js | |
@@ -1719,17 +1750,21 @@ ion::Invalidate(types::TypeCompartment & | |
IonSpew(IonSpew_Invalidate, "Start invalidation."); | |
AutoFlushCache afc ("Invalidate"); | |
// Add an invalidation reference to all invalidated IonScripts to indicate | |
// to the traversal which frames have been invalidated. | |
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()); | |
// Keep the ion script alive during the invalidation and flag this | |
// ionScript as being invalidated. This increment is removed by the | |
// loop after the calls to InvalidateActivation. | |
co.ion()->incref(); | |
@@ -1745,39 +1780,48 @@ ion::Invalidate(types::TypeCompartment & | |
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) | |
InvalidateActivation(fop, iter.top(), false); | |
// Drop the references added above. If a script was never active, its | |
// IonScript will be immediately destroyed. Otherwise, it will be held live | |
// 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(); | |
+ 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); | |
- } | |
+ 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(); | |
+ 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(); | |
- } | |
+ // 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(); | |
} | |
} | |
void | |
ion::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses) | |
{ | |
ion::Invalidate(cx->compartment->types, cx->runtime->defaultFreeOp(), invalid, resetUses); | |
} | |
@@ -1886,8 +1930,47 @@ AutoFlushCache::AutoFlushCache(const cha | |
comp->setFlusher(this); | |
} else { | |
IonSpew(IonSpew_CacheFlush, "<%s DEAD>\n", nonce); | |
} | |
myCompartment_ = 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 | |
--- a/js/src/ion/Ion.h | |
+++ b/js/src/ion/Ion.h | |
@@ -263,13 +263,18 @@ bool TestIonCompile(JSContext *cx, JSScr | |
static inline bool IsEnabled(JSContext *cx) | |
{ | |
return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled(); | |
} | |
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 | |
#endif // jsion_ion_h__ | |
diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp | |
--- a/js/src/ion/IonBuilder.cpp | |
+++ b/js/src/ion/IonBuilder.cpp | |
@@ -10,16 +10,17 @@ | |
#include "MIRGraph.h" | |
#include "Ion.h" | |
#include "IonAnalysis.h" | |
#include "IonSpewer.h" | |
#include "frontend/BytecodeEmitter.h" | |
#include "jsscriptinlines.h" | |
#include "jstypedarrayinlines.h" | |
+#include "ExecutionModeInlines.h" | |
#ifdef JS_THREADSAFE | |
# include "prthread.h" | |
#endif | |
using namespace js; | |
using namespace js::ion; | |
@@ -199,18 +200,18 @@ IonBuilder::canInlineTarget(JSFunction * | |
} | |
if (target->getParent() != &script_->global()) { | |
IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch"); | |
return false; | |
} | |
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; | |
} | |
// Allow inlining of recursive calls, but only one level deep. | |
IonBuilder *builder = callerBuilder_; | |
while (builder) { | |
if (builder->script() == inlineScript) { | |
@@ -2824,17 +2825,18 @@ IonBuilder::jsop_call_inline(HandleFunct | |
return false; | |
for (int32 i = argc; i >= 0; i--) | |
argv[i] = current->pop(); | |
// Compilation information is allocated for the duration of the current tempLifoAlloc | |
// lifetime. | |
RootedScript calleeScript(cx, callee->script()); | |
CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(calleeScript.get(), callee, | |
- (jsbytecode *)NULL, constructing); | |
+ (jsbytecode *)NULL, constructing, | |
+ SequentialExecution); | |
if (!info) | |
return false; | |
MIRGraphExits saveExits; | |
AutoAccumulateExits aae(graph(), saveExits); | |
TypeInferenceOracle oracle; | |
if (!oracle.init(cx, calleeScript)) | |
diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp | |
--- a/js/src/jsdbgapi.cpp | |
+++ b/js/src/jsdbgapi.cpp | |
@@ -547,18 +547,19 @@ JS_SetTopFrameAnnotation(JSContext *cx, | |
// because we will never EnterIon on a frame with an annotation. | |
fp->setAnnotation(annotation); | |
RawScript script = fp->script().get(nogc); | |
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 *) | |
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg) | |
{ | |
StackFrame *fp = Valueify(fpArg); | |
JS_ASSERT(cx->stack.space().containsSlow(fp)); | |
AutoCompartment ac(cx, fp->scopeChain()); | |
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp | |
--- a/js/src/jsfun.cpp | |
+++ b/js/src/jsfun.cpp | |
@@ -127,17 +127,17 @@ fun_getProperty(JSContext *cx, HandleObj | |
#ifdef JS_ION | |
AutoAssertNoGC nogc; | |
// If this script hasn't been compiled yet, make sure it will never | |
// be compiled. IonMonkey does not guarantee |f.arguments| can be | |
// 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 | |
vp.setObject(*argsobj); | |
return true; | |
} | |
#ifdef JS_METHODJIT | |
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp | |
--- a/js/src/jsgc.cpp | |
+++ b/js/src/jsgc.cpp | |
@@ -5870,18 +5870,17 @@ PurgeJITCaches(JSCompartment *c) | |
JSScript *script = i.get<JSScript>(); | |
/* Discard JM caches. */ | |
mjit::PurgeCaches(script); | |
#ifdef JS_ION | |
/* Discard Ion caches. */ | |
- if (script->hasIonScript()) | |
- script->ion->purgeCaches(c); | |
+ ion::PurgeCaches(script, c); | |
#endif | |
} | |
#endif | |
} | |
AutoTransplantGC::AutoTransplantGC(JSContext *cx) | |
: runtime(cx->runtime), | |
diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp | |
--- a/js/src/jsinfer.cpp | |
+++ b/js/src/jsinfer.cpp | |
@@ -1992,17 +1992,17 @@ JITCodeHasCheck(HandleScript script, jsb | |
found = true; | |
} | |
if (!found) | |
return false; | |
} | |
} | |
#endif | |
- if (script->hasIonScript()) | |
+ if (script->hasAnyIonScript()) | |
return false; | |
return true; | |
} | |
/* | |
* Force recompilation of any jitcode for script at pc, or of any other script | |
* which this script was inlined into. | |
@@ -2021,18 +2021,24 @@ AddPendingRecompile(JSContext *cx, Handl | |
/* | |
* Remind Ion not to save the compile code if generating type | |
* inference information mid-compilation causes an invalidation of the | |
* script being compiled. | |
*/ | |
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; | |
} | |
} | |
/* | |
* When one script is inlined into another the caller listens to state | |
* changes on the callee's script, so trigger these to force recompilation | |
* of any such callers. | |
*/ | |
@@ -2412,21 +2418,26 @@ TypeCompartment::processPendingRecompile | |
JS_ASSERT(!pending->empty()); | |
#ifdef JS_METHODJIT | |
mjit::ExpandInlineFrames(compartment()); | |
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; | |
} | |
} | |
# ifdef JS_ION | |
ion::Invalidate(*this, fop, *pending); | |
# endif | |
#endif /* JS_METHODJIT */ | |
@@ -2513,17 +2524,17 @@ TypeCompartment::addPendingRecompile(JSC | |
return; | |
} | |
#ifdef JS_METHODJIT | |
mjit::JITScript *jit = co->script->getJIT(co->constructing, co->barriers); | |
bool hasJITCode = jit && jit->chunkDescriptor(co->chunkIndex).chunk; | |
# if defined(JS_ION) | |
- hasJITCode |= !!co->script->hasIonScript(); | |
+ hasJITCode |= !!co->script->hasAnyIonScript(); | |
# endif | |
if (!hasJITCode) { | |
/* Scripts which haven't been compiled yet don't need to be recompiled. */ | |
return; | |
} | |
#endif | |
@@ -2580,16 +2591,19 @@ TypeCompartment::addPendingRecompile(JSC | |
} | |
} | |
# ifdef JS_ION | |
CancelOffThreadIonCompile(cx->compartment, script); | |
if (script->hasIonScript()) | |
addPendingRecompile(cx, script->ionScript()->recompileInfo()); | |
+ | |
+ if (script->hasParallelIonScript()) | |
+ addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); | |
# endif | |
#endif | |
} | |
void | |
TypeCompartment::monitorBytecode(JSContext *cx, HandleScript script, uint32_t offset, | |
bool returnOnly) | |
{ | |
diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h | |
--- a/js/src/jsinfer.h | |
+++ b/js/src/jsinfer.h | |
@@ -1208,27 +1208,37 @@ typedef HashMap<AllocationSiteKey,ReadBa | |
/* | |
* Information about the result of the compilation of a script. This structure | |
* stored in the TypeCompartment is indexed by the RecompileInfo. This | |
* indirection enable the invalidation of all constraints related to the same | |
* compilation. The compiler output is build by the AutoEnterCompilation. | |
*/ | |
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<Kind>(kindInt); } | |
+ void setKind(Kind k) { kindInt = k; } | |
mjit::JITScript *mjit() const; | |
ion::IonScript *ion() const; | |
bool isValid() const; | |
void setPendingRecompilation() { | |
pendingRecompilation = true; | |
diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h | |
--- a/js/src/jsinferinlines.h | |
+++ b/js/src/jsinferinlines.h | |
@@ -83,79 +83,95 @@ namespace types { | |
///////////////////////////////////////////////////////////////////// | |
// CompilerOutput & RecompileInfo | |
///////////////////////////////////////////////////////////////////// | |
inline | |
CompilerOutput::CompilerOutput() | |
: script(NULL), | |
- isIonFlag(false), | |
+ kindInt(MethodJIT), | |
constructing(false), | |
barriers(false), | |
chunkIndex(false) | |
{ | |
} | |
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; | |
#endif | |
} | |
inline ion::IonScript * | |
CompilerOutput::ion() const | |
{ | |
#ifdef JS_ION | |
- JS_ASSERT(isIon() && isValid()); | |
- return script->ionScript(); | |
-#else | |
+ 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; | |
-#endif | |
} | |
inline bool | |
CompilerOutput::isValid() const | |
{ | |
if (!script) | |
return false; | |
#if defined(DEBUG) && (defined(JS_METHODJIT) || defined(JS_ION)) | |
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; | |
mjit::JITChunk *chunk = jit->chunkDescriptor(chunkIndex).chunk; | |
if (!chunk) | |
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; | |
} | |
inline CompilerOutput* | |
RecompileInfo::compilerOutput(TypeCompartment &types) const | |
{ | |
return &(*types.constrainedOutputs)[outputIndex]; | |
} | |
@@ -382,37 +398,32 @@ struct AutoEnterTypeInference | |
/* | |
* Structure marking the currently compiled script, for constraints which can | |
* trigger recompilation. | |
*/ | |
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); | |
} | |
bool init(JSScript *script, bool constructing, unsigned chunkIndex) | |
{ | |
CompilerOutput co; | |
co.script = script; | |
- co.isIonFlag = (mode == Ion); | |
+ co.setKind(kind); | |
co.constructing = constructing; | |
co.barriers = cx->compartment->compileBarriers(); | |
co.chunkIndex = chunkIndex; | |
// This flag is used to prevent adding the current compiled script in | |
// the list of compiler output which should be invalided. This is | |
// necessary because we can run some analysis might discard the script | |
// it-self, which can happen when the monitored value does not reflect | |
diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h | |
--- a/js/src/jsinterpinlines.h | |
+++ b/js/src/jsinterpinlines.h | |
@@ -1002,21 +1002,21 @@ class FastInvokeGuard | |
RootedScript script_; | |
#ifdef JS_ION | |
ion::IonContext ictx_; | |
bool useIon_; | |
#endif | |
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); | |
} | |
void initFunction(const Value &fval) { | |
if (fval.isObject() && fval.toObject().isFunction()) { | |
JSFunction *fun = fval.toObject().toFunction(); | |
diff --git a/js/src/jsmemorymetrics.cpp b/js/src/jsmemorymetrics.cpp | |
--- a/js/src/jsmemorymetrics.cpp | |
+++ b/js/src/jsmemorymetrics.cpp | |
@@ -15,16 +15,17 @@ | |
#include "jsgc.h" | |
#include "jsobj.h" | |
#include "jsscope.h" | |
#include "jsscript.h" | |
#include "jsobjinlines.h" | |
#include "ion/IonCode.h" | |
+#include "ion/Ion.h" | |
namespace js { | |
size_t MemoryReportingSundriesThreshold() | |
{ | |
return 8 * 1024; | |
} | |
@@ -227,18 +228,17 @@ StatsCellCallback(JSRuntime *rt, void *d | |
case JSTRACE_SCRIPT: | |
{ | |
JSScript *script = static_cast<JSScript *>(thing); | |
cStats->gcHeapScripts += thingSize; | |
cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf); | |
#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 | |
ScriptSource *ss = script->scriptSource(); | |
SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss); | |
if (!entry) { | |
closure->seenSources.add(entry, ss); // Not much to be done on failure. | |
rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf); | |
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp | |
--- a/js/src/jsscript.cpp | |
+++ b/js/src/jsscript.cpp | |
@@ -1901,18 +1901,17 @@ JSScript::finalize(FreeOp *fop) | |
JS_DropPrincipals(fop->runtime(), originPrincipals); | |
if (types) | |
types->destroy(); | |
#ifdef JS_METHODJIT | |
mjit::ReleaseScriptCode(fop, this); | |
# ifdef JS_ION | |
- if (hasIonScript()) | |
- ion::IonScript::Destroy(fop, ion); | |
+ ion::DestroyIonScripts(fop, this); | |
# endif | |
#endif | |
destroyScriptCounts(fop); | |
destroyDebugScript(fop); | |
scriptSource_->decref(fop->runtime()); | |
if (data) { | |
@@ -2594,18 +2593,17 @@ JSScript::markChildren(JSTracer *trc) | |
for (unsigned i = 0; i < length; i++) { | |
BreakpointSite *site = debugScript()->breakpoints[i]; | |
if (site && site->trapHandler) | |
MarkValue(trc, &site->trapClosure, "trap closure"); | |
} | |
} | |
#ifdef JS_ION | |
- if (hasIonScript()) | |
- ion::IonScript::Trace(trc, ion); | |
+ ion::TraceIonScripts(trc, this); | |
#endif | |
} | |
void | |
JSScript::setArgumentsHasVarBinding() | |
{ | |
argsHasVarBinding_ = true; | |
needsArgsAnalysis_ = true; | |
diff --git a/js/src/jsscript.h b/js/src/jsscript.h | |
--- a/js/src/jsscript.h | |
+++ b/js/src/jsscript.h | |
@@ -544,36 +544,60 @@ struct JSScript : public js::gc::Cell | |
* canonical location for the arguments. Note: if a formal is aliased | |
* through the scope chain, then script->formalIsAliased and JSOP_*ARG* | |
* opcodes won't be emitted at all. | |
*/ | |
bool argsObjAliasesFormals() const { | |
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. | |
*/ | |
JSFunction *function() const { return function_; } | |
void setFunction(JSFunction *fun); | |
JSFlatString *sourceData(JSContext *cx); | |
diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp | |
--- a/js/src/jsworkers.cpp | |
+++ b/js/src/jsworkers.cpp | |
@@ -3,16 +3,17 @@ | |
/* 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" | |
#if JS_ION | |
# include "ion/IonBuilder.h" | |
+# include "ion/ExecutionModeInlines.h" | |
#endif | |
using namespace js; | |
using mozilla::DebugOnly; | |
#ifdef JS_PARALLEL_COMPILATION | |
@@ -289,17 +290,18 @@ WorkerThread::threadLoop() | |
state.unlock(); | |
return; | |
} | |
state.wait(WorkerThreadState::WORKER); | |
} | |
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(); | |
{ | |
ion::IonContext ictx(NULL, ionBuilder->script()->compartment(), &ionBuilder->temp()); | |
ionBuilder->backgroundCompiledLir = ion::CompileBackEnd(ionBuilder); | |
} | |
diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp | |
--- a/js/src/methodjit/Compiler.cpp | |
+++ b/js/src/methodjit/Compiler.cpp | |
@@ -534,17 +534,17 @@ mjit::Compiler::performCompilation() | |
#ifdef JS_METHODJIT | |
outerScript->debugMode = debugMode(); | |
#endif | |
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; | |
} | |
CHECK_STATUS(checkAnalysis(outerScript)); | |
if (inlining()) | |
CHECK_STATUS(scanInlineCalls(CrossScriptSSA::OUTER_FRAME, 0)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment