Bug 772509 - Freeze a compilation output instead of a script. r=bhackett,dvander

This commit is contained in:
Nicolas Pierron 2012-07-16 23:19:26 +02:00
parent b8745962e9
commit 7bc01053bb
9 changed files with 246 additions and 71 deletions

View File

@ -108,6 +108,25 @@ IonBailoutIterator::dump() const
}
}
static JSScript*
GetBailedJSScript(JSContext *cx)
{
// Just after the frame conversion, we can safely interpret the ionTop as JS
// frame because it targets the bailed JS frame converted to an exit frame.
IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop);
switch (GetCalleeTokenTag(frame->calleeToken())) {
case CalleeToken_Function: {
JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
return fun->script();
}
case CalleeToken_Script:
return CalleeTokenToScript(frame->calleeToken());
default:
JS_NOT_REACHED("unexpected callee token kind");
return NULL;
}
}
void
StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
{
@ -521,7 +540,7 @@ uint32
ion::BoundsCheckFailure()
{
JSContext *cx = GetIonContext()->cx;
JSScript *script = cx->fp()->script();
JSScript *script = GetBailedJSScript(cx);
IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename,
script->lineno);
@ -530,8 +549,9 @@ ion::BoundsCheckFailure()
script->failedBoundsCheck = true;
// Invalidate the script to force a recompile.
Vector<types::RecompileInfo> scripts(cx);
if (!scripts.append(types::RecompileInfo(script)))
Vector<types::CompilerOutput> scripts(cx);
types::CompilerOutput co(script);
if (!scripts.append(co))
return BAILOUT_RETURN_FATAL_ERROR;
IonSpew(IonSpew_Invalidate, "Invalidating due to bounds check failure");

View File

@ -876,7 +876,8 @@ IonCompile(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecode *osrPc)
if (!oracle.init(cx, script))
return false;
types::AutoEnterCompilation enterCompiler(cx, script, false, 0);
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
enterCompiler.init(script, false, 0);
AutoCompilerRoots roots(script->compartment()->rt);
IonBuilder builder(cx, temp, graph, &oracle, *info);
@ -1309,20 +1310,31 @@ ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
}
void
ion::Invalidate(FreeOp *fop, const Vector<types::RecompileInfo> &invalid, bool resetUses)
ion::Invalidate(FreeOp *fop, const Vector<types::CompilerOutput> &invalid, bool resetUses)
{
IonSpew(IonSpew_Invalidate, "Start invalidation.");
// 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++) {
JSScript *script = invalid[i].script;
if (script->hasIonScript()) {
const types::CompilerOutput &co = invalid[i];
JS_ASSERT(co.isValid());
if (co.isIon()) {
JS_ASSERT(co.script->hasIonScript() && co.out.ion == co.script->ionScript());
IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p",
script->filename, script->lineno, script->ion);
script->ion->incref();
co.script->filename, co.script->lineno, co.out.ion);
// Cause the IonScript to be invalidated in InvalidateActivation.
co.out.ion->incref();
anyInvalidation = true;
}
}
if (!anyInvalidation) {
IonSpew(IonSpew_Invalidate, " No IonScript invalidation.");
return;
}
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter)
InvalidateActivation(fop, iter.top(), false);
@ -1361,8 +1373,9 @@ ion::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
{
JS_ASSERT(script->hasIonScript());
Vector<types::RecompileInfo> scripts(cx);
if (!scripts.append(types::RecompileInfo(script)))
Vector<types::CompilerOutput> scripts(cx);
types::CompilerOutput co(script);
if (!scripts.append(co))
return false;
Invalidate(cx->runtime->defaultFreeOp(), scripts, resetUses);

View File

@ -251,7 +251,7 @@ IonExecStatus Cannon(JSContext *cx, StackFrame *fp);
IonExecStatus SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc);
// Walk the stack and invalidate active Ion frames for the invalid scripts.
void Invalidate(FreeOp *fop, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
void Invalidate(FreeOp *fop, const Vector<types::CompilerOutput> &invalid, bool resetUses = true);
bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
void MarkFromIon(JSCompartment *comp, Value *vp);

View File

@ -71,8 +71,9 @@ InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *
if (fun->isInterpreted() && !fun->script()->canIonCompile()) {
JSScript *script = GetTopIonJSScript(cx);
if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
Vector<types::RecompileInfo> scripts(cx);
if (!scripts.append(types::RecompileInfo(script)))
Vector<types::CompilerOutput> scripts(cx);
types::CompilerOutput co(script);
if (!scripts.append(co))
return false;
Invalidate(cx->runtime->defaultFreeOp(), scripts);

View File

@ -601,6 +601,11 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
JSScript *script = i.get<JSScript>();
script->clearAnalysis();
}
if (types.constrainedOutputs) {
fop->delete_(types.constrainedOutputs);
types.constrainedOutputs = NULL;
}
}
{

View File

@ -1467,7 +1467,9 @@ TypeSet::getKnownTypeTag(JSContext *cx)
bool empty = flags == 0 && baseObjectCount() == 0;
JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
if (cx->compartment->types.compiledInfo.script && (empty || type != JSVAL_TYPE_UNKNOWN)) {
if (cx->compartment->types.compiledInfo.compilerOutput(cx)->script &&
(empty || type != JSVAL_TYPE_UNKNOWN))
{
add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
cx->compartment->types.compiledInfo), false);
}
@ -1845,6 +1847,8 @@ TypeCompartment::init(JSContext *cx)
{
PodZero(this);
compiledInfo.outputIndex = RecompileInfo::NoCompilerRunning;
if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE) {
#ifdef JS_METHODJIT
JSC::MacroAssembler masm;
@ -2041,7 +2045,7 @@ void
TypeCompartment::processPendingRecompiles(FreeOp *fop)
{
/* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
Vector<RecompileInfo> *pending = pendingRecompiles;
Vector<CompilerOutput> *pending = pendingRecompiles;
pendingRecompiles = NULL;
JS_ASSERT(!pending->empty());
@ -2051,11 +2055,14 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop)
mjit::ExpandInlineFrames(compartment());
for (unsigned i = 0; i < pending->length(); i++) {
const RecompileInfo &info = (*pending)[i];
mjit::JITScript *jit = info.script->getJIT(info.constructing, info.barriers);
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
mjit::Recompiler::clearStackReferences(fop, info.script);
jit->destroyChunk(fop, info.chunkIndex);
const CompilerOutput &info = (*pending)[i];
JS_ASSERT(info.isValid());
if (info.isJM()) {
mjit::JITScript *jit = info.out.mjit;
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
mjit::Recompiler::clearStackReferences(fop, info.script);
jit->destroyChunk(fop, info.chunkIndex);
}
}
}
@ -2133,14 +2140,17 @@ TypeCompartment::nukeTypes(FreeOp *fop)
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
TypeCompartment::addPendingRecompile(JSContext *cx, CompilerOutput &co)
{
if (!co.isValid())
return;
#ifdef JS_METHODJIT
mjit::JITScript *jit = info.script->getJIT(info.constructing, info.barriers);
bool hasJITCode = jit && jit->chunkDescriptor(info.chunkIndex).chunk;
mjit::JITScript *jit = co.script->getJIT(co.constructing, co.barriers);
bool hasJITCode = jit && jit->chunkDescriptor(co.chunkIndex).chunk;
# if defined(JS_ION)
hasJITCode |= !!info.script->hasIonScript();
hasJITCode |= !!co.script->hasIonScript();
# endif
if (!hasJITCode) {
@ -2149,31 +2159,35 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
}
if (!pendingRecompiles) {
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
pendingRecompiles = cx->new_< Vector<CompilerOutput> >(cx);
if (!pendingRecompiles) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
}
for (unsigned i = 0; i < pendingRecompiles->length(); i++) {
if (info == (*pendingRecompiles)[i])
return;
}
if (!pendingRecompiles->append(info)) {
if (!pendingRecompiles->append(co)) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
co.invalidate();
#endif
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
{
addPendingRecompile(cx, (*constrainedOutputs)[info.outputIndex]);
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
{
#ifdef JS_METHODJIT
RecompileInfo info;
CompilerOutput info;
info.script = script;
info.isIonFlag = false;
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {
@ -2181,13 +2195,16 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode
info.constructing = constructing;
info.barriers = barriers;
info.chunkIndex = jit->chunkIndex(pc);
info.out.mjit = jit;
addPendingRecompile(cx, info);
}
}
}
# ifdef JS_ION
if (script->hasIonScript())
addPendingRecompile(cx, RecompileInfo(script));
if (script->hasIonScript()) {
info = CompilerOutput(script);
addPendingRecompile(cx, info);
}
# endif
#endif
}

View File

@ -28,6 +28,14 @@ namespace js {
class CallObject;
namespace mjit {
struct JITScript;
}
namespace ion {
struct IonScript;
}
namespace types {
/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
@ -1003,31 +1011,47 @@ typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy
struct AllocationSiteKey;
typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
struct RecompileInfo
/*
* 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 built by the AutoEnterCompilation.
*/
struct CompilerOutput
{
JSScript *script;
bool isIonFlag : 1;
bool constructing : 1;
bool barriers : 1;
uint32_t chunkIndex:30;
uint32_t chunkIndex:29;
/* Result of the compilation */
union {
mjit::JITScript *mjit;
ion::IonScript *ion;
} out;
CompilerOutput();
explicit CompilerOutput(JSScript *script, bool isIonFlag = true);
bool isJM() const { return !isIonFlag; }
bool isIon() const { return isIonFlag; }
bool isValid() const;
void invalidate() {
out.ion = NULL;
}
};
struct RecompileInfo
{
static const uint32_t NoCompilerRunning = uint32_t(-1);
uint32_t outputIndex;
bool operator == (const RecompileInfo &o) const {
return script == o.script
&& constructing == o.constructing
&& barriers == o.barriers
&& chunkIndex == o.chunkIndex;
}
RecompileInfo()
{
}
explicit RecompileInfo(JSScript *script)
: script(script),
constructing(false),
barriers(false),
chunkIndex(0)
{
return outputIndex == o.outputIndex;
}
CompilerOutput *compilerOutput(JSContext *cx) const;
};
/* Type information for a compartment. */
@ -1064,8 +1088,11 @@ struct TypeCompartment
/* Number of scripts in this compartment. */
unsigned scriptCount;
/* Valid & Invalid script referenced by type constraints. */
Vector<CompilerOutput> *constrainedOutputs;
/* Pending recompilations to perform before execution of JIT code can resume. */
Vector<RecompileInfo> *pendingRecompiles;
Vector<CompilerOutput> *pendingRecompiles;
/*
* Number of recompilation events and inline frame expansions that have
@ -1136,6 +1163,7 @@ struct TypeCompartment
void setPendingNukeTypesNoReport();
/* Mark a script as needing recompilation once inference has finished. */
void addPendingRecompile(JSContext *cx, CompilerOutput &co);
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);

View File

@ -21,13 +21,63 @@
#ifndef jsinferinlines_h___
#define jsinferinlines_h___
namespace js {
namespace types {
/////////////////////////////////////////////////////////////////////
// CompilerOutput & RecompileInfo
/////////////////////////////////////////////////////////////////////
inline
CompilerOutput::CompilerOutput()
: script(script),
isIonFlag(false),
constructing(false),
barriers(false),
chunkIndex(false)
{
out.mjit = NULL;
}
inline
CompilerOutput::CompilerOutput(JSScript *script, bool isIonFlag)
: script(script),
isIonFlag(isIonFlag),
constructing(false),
barriers(false),
chunkIndex(false)
{
out.mjit = NULL;
#ifdef JS_METHODJIT
if (isJM() && script->hasJITInfo())
out.mjit = script->getJIT(constructing, barriers);
#endif
if (isIon() && script->hasIonScript())
out.ion = script->ionScript();
}
inline bool
CompilerOutput::isValid() const
{
#ifdef JS_METHODJIT
if (isJM() && out.mjit != NULL && script->getJIT(constructing, barriers) == out.mjit)
return true;
#endif
if (isIon() && script->hasIonScript() && script->ionScript() == out.ion)
return true;
return false;
}
inline CompilerOutput*
RecompileInfo::compilerOutput(JSContext *cx) const
{
return &(*cx->compartment->types.constrainedOutputs)[outputIndex];
}
/////////////////////////////////////////////////////////////////////
// Types
/////////////////////////////////////////////////////////////////////
namespace js {
namespace types {
/* static */ inline Type
Type::ObjectType(JSObject *obj)
{
@ -243,25 +293,62 @@ struct AutoEnterTypeInference
*/
struct AutoEnterCompilation
{
JSContext *cx;
RecompileInfo &info;
enum Compiler { JM, Ion };
Compiler mode;
AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
: info(cx->compartment->types.compiledInfo)
AutoEnterCompilation(JSContext *cx, Compiler mode)
: cx(cx),
info(cx->compartment->types.compiledInfo),
mode(mode)
{
JS_ASSERT(!info.script);
info.script = script;
info.constructing = constructing;
info.barriers = cx->compartment->needsBarrier();
info.chunkIndex = chunkIndex;
JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning);
}
bool init(JSScript *script, bool constructing, unsigned chunkIndex)
{
CompilerOutput co;
co.script = script;
co.constructing = constructing;
co.barriers = cx->compartment->needsBarrier();
co.chunkIndex = chunkIndex;
TypeCompartment &types = cx->compartment->types;
if (!types.constrainedOutputs) {
types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
if (!types.constrainedOutputs) {
types.setPendingNukeTypes(cx);
return false;
}
}
info.outputIndex = cx->compartment->types.constrainedOutputs->length();
// I hope we GC before we reach 64k of compilation attempts.
if (info.outputIndex >= RecompileInfo::NoCompilerRunning)
return false;
if (!cx->compartment->types.constrainedOutputs->append(co))
return false;
return true;
}
~AutoEnterCompilation()
{
JS_ASSERT(info.script);
info.script = NULL;
info.constructing = false;
info.barriers = false;
info.chunkIndex = 0;
CompilerOutput *co = info.compilerOutput(cx);
#ifdef JS_METHODJIT
if (mode == JM) {
co->isIonFlag = false;
if (co->script->hasJITInfo())
co->out.mjit = co->script->getJIT(co->constructing, co->barriers);
}
#endif
if (mode == Ion) {
co->isIonFlag = true;
if (co->script->hasIonScript())
co->out.ion = co->script->ionScript();
}
info.outputIndex = RecompileInfo::NoCompilerRunning;
}
};

View File

@ -506,7 +506,11 @@ mjit::Compiler::performCompilation()
JS_ASSERT(cx->compartment->activeInference);
{
types::AutoEnterCompilation enter(cx, outerScript, isConstructing, chunkIndex);
types::AutoEnterCompilation enter(cx, types::AutoEnterCompilation::JM);
if (!enter.init(outerScript, isConstructing, chunkIndex)) {
js_ReportOutOfMemory(cx);
return Compile_Error;
}
CHECK_STATUS(checkAnalysis(outerScript));
if (inlining())