mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
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:
parent
a9094a7158
commit
cdee8bbc50
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
103
js/src/ion/ExecutionModeInlines.h
Normal file
103
js/src/ion/ExecutionModeInlines.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 *)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user