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.
This commit is contained in:
Nicholas D. Matsakis 2012-11-13 18:58:48 -05:00
parent a9094a7158
commit cdee8bbc50
18 changed files with 414 additions and 119 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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 <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
@ -1045,7 +1059,9 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
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;
@ -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_<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());
@ -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);
}

View File

@ -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

View File

@ -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_<CompileInfo>(calleeScript.get(), callee,
(jsbytecode *)NULL, constructing);
(jsbytecode *)NULL, constructing,
SequentialExecution);
if (!info)
return false;

View File

@ -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 *)

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -1213,17 +1213,27 @@ typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,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<Kind>(kindInt); }
void setKind(Kind k) { kindInt = k; }
mjit::JITScript *mjit() const;
ion::IonScript *ion() const;

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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
}

View File

@ -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.

View File

@ -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();

View File

@ -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;