Bug 777537 - Ensure validity of CompilerOutput without a dangling pointer. r=bhackett

This commit is contained in:
Nicolas B. Pierron 2012-08-15 01:30:07 +02:00
parent 98d1923ea5
commit e9cdb7efac
6 changed files with 124 additions and 45 deletions

View File

@ -2019,7 +2019,7 @@ void
TypeCompartment::processPendingRecompiles(FreeOp *fop)
{
/* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
Vector<CompilerOutput> *pending = pendingRecompiles;
Vector<RecompileInfo> *pending = pendingRecompiles;
pendingRecompiles = NULL;
JS_ASSERT(!pending->empty());
@ -2029,11 +2029,12 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop)
mjit::ExpandInlineFrames(compartment());
for (unsigned i = 0; i < pending->length(); i++) {
const CompilerOutput &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);
CompilerOutput &co = *(*pending)[i].compilerOutput(*this);
if (co.isJM()) {
JS_ASSERT(co.isValid());
mjit::Recompiler::clearStackReferences(fop, co.script);
co.mjit()->destroyChunk(fop, co.chunkIndex);
JS_ASSERT(co.script == NULL);
}
}
@ -2103,56 +2104,69 @@ TypeCompartment::nukeTypes(FreeOp *fop)
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, CompilerOutput &co)
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
{
if (!co.isValid())
CompilerOutput *co = info.compilerOutput(cx);
if (co->pendingRecompilation)
return;
#ifdef JS_METHODJIT
mjit::JITScript *jit = co.script->getJIT(co.constructing, co.barriers);
if (!jit || !jit->chunkDescriptor(co.chunkIndex).chunk) {
/* Scripts which haven't been compiled yet don't need to be recompiled. */
if (!co->isValid()) {
JS_ASSERT(co->script == NULL);
return;
}
#ifdef JS_METHODJIT
mjit::JITScript *jit = co->script->getJIT(co->constructing, co->barriers);
if (!jit || !jit->chunkDescriptor(co->chunkIndex).chunk) {
/* Scripts which haven't been compiled yet don't need to be recompiled. */
return;
}
#endif
if (!pendingRecompiles) {
pendingRecompiles = cx->new_< Vector<CompilerOutput> >(cx);
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
if (!pendingRecompiles) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
}
co.invalidate();
if (!pendingRecompiles->append(co)) {
#if DEBUG
for (size_t i = 0; i < pendingRecompiles->length(); i++) {
RecompileInfo pr = (*pendingRecompiles)[i];
JS_ASSERT(info.outputIndex != pr.outputIndex);
}
#endif
if (!pendingRecompiles->append(info)) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
#endif
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
{
addPendingRecompile(cx, (*constrainedOutputs)[info.outputIndex]);
co->setPendingRecompilation();
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
{
#ifdef JS_METHODJIT
CompilerOutput info;
info.script = script;
JS_ASSERT(script);
if (!constrainedOutputs)
return;
#ifdef JS_METHODJIT
for (int constructing = 0; constructing <= 1; constructing++) {
for (int barriers = 0; barriers <= 1; barriers++) {
if (mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers)) {
info.constructing = constructing;
info.barriers = barriers;
info.chunkIndex = jit->chunkIndex(pc);
info.mjit = jit;
addPendingRecompile(cx, info);
}
mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
if (!jit)
continue;
unsigned int chunkIndex = jit->chunkIndex(pc);
mjit::JITChunk *chunk = jit->chunkDescriptor(chunkIndex).chunk;
if (!chunk)
continue;
addPendingRecompile(cx, chunk->recompileInfo);
}
}
#endif

View File

@ -1013,18 +1013,22 @@ struct CompilerOutput
JSScript *script;
bool constructing : 1;
bool barriers : 1;
uint32_t chunkIndex:30;
bool pendingRecompilation : 1;
uint32_t chunkIndex:29;
#ifdef JS_METHODJIT
js::mjit::JITScript *mjit; /* Information attached by JM */
#endif
CompilerOutput();
bool isJM() const { return true; }
mjit::JITScript * mjit() const;
bool isValid() const;
void setPendingRecompilation() {
pendingRecompilation = true;
}
void invalidate() {
#ifdef JS_METHODJIT
mjit = NULL;
#endif
script = NULL;
}
};
@ -1033,9 +1037,15 @@ struct RecompileInfo
static const uint32_t NoCompilerRunning = uint32_t(-1);
uint32_t outputIndex;
RecompileInfo()
: outputIndex(NoCompilerRunning)
{
}
bool operator == (const RecompileInfo &o) const {
return outputIndex == o.outputIndex;
}
CompilerOutput *compilerOutput(TypeCompartment &types) const;
CompilerOutput *compilerOutput(JSContext *cx) const;
};
@ -1077,7 +1087,7 @@ struct TypeCompartment
Vector<CompilerOutput> *constrainedOutputs;
/* Pending recompilations to perform before execution of JIT code can resume. */
Vector<CompilerOutput> *pendingRecompiles;
Vector<RecompileInfo> *pendingRecompiles;
/*
* Number of recompilation events and inline frame expansions that have
@ -1148,7 +1158,6 @@ 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

@ -27,16 +27,57 @@ namespace types {
// CompilerOutput & RecompileInfo
/////////////////////////////////////////////////////////////////////
inline
CompilerOutput::CompilerOutput()
: script(NULL),
constructing(false),
barriers(false),
chunkIndex(false)
{
}
inline mjit::JITScript *
CompilerOutput::mjit() const
{
#ifdef JS_METHODJIT
JS_ASSERT(isJM() && isValid());
return script->getJIT(constructing, barriers);
#else
return NULL;
#endif
}
inline bool
CompilerOutput::isValid() const
{
if (!script)
return false;
#ifdef DEBUG
TypeCompartment &types = script->compartment()->types;
#endif
#ifdef JS_METHODJIT
if (mjit != NULL && script->getJIT(constructing, barriers) == mjit)
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
return false;
}
inline CompilerOutput*
RecompileInfo::compilerOutput(TypeCompartment &types) const
{
return &(*types.constrainedOutputs)[outputIndex];
}
inline CompilerOutput*
RecompileInfo::compilerOutput(JSContext *cx) const
{
@ -261,6 +302,14 @@ struct AutoEnterCompilation
co.barriers = cx->compartment->needsBarrier();
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
// the types propagated by the type inference.
co.pendingRecompilation = true;
JS_ASSERT(!co.isValid());
TypeCompartment &types = cx->compartment->types;
if (!types.constrainedOutputs) {
types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
@ -282,10 +331,10 @@ struct AutoEnterCompilation
~AutoEnterCompilation()
{
CompilerOutput *co = info.compilerOutput(cx);
#ifdef JS_METHODJIT
if (co->script->hasMJITInfo())
co->mjit = co->script->getJIT(co->constructing, co->barriers);
#endif
co->pendingRecompilation = false;
if (!co->isValid())
co->invalidate();
info.outputIndex = RecompileInfo::NoCompilerRunning;
}
};

View File

@ -1857,6 +1857,7 @@ mjit::Compiler::finishThisUp()
}
}
chunk->recompileInfo = cx->compartment->types.compiledInfo;
return Compile_Okay;
}

View File

@ -1267,6 +1267,10 @@ JITScript::destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses)
ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
if (desc.chunk) {
// Invalidates the CompilerOutput of the chunk.
types::TypeCompartment &types = script->compartment()->types;
desc.chunk->recompileInfo.compilerOutput(types)->invalidate();
/*
* Write barrier: Before we destroy the chunk, trace through the objects
* it holds.

View File

@ -667,6 +667,8 @@ struct JITChunk
ExecPoolVector execPools;
#endif
types::RecompileInfo recompileInfo;
// Additional ExecutablePools for native call and getter stubs.
Vector<NativeCallStub, 0, SystemAllocPolicy> nativeCallStubs;