Bug 988619 - Handle OOM when sweeping type information, r=jandem.

This commit is contained in:
Brian Hackett 2014-04-14 18:22:17 -06:00
Родитель 6a1439fd24
Коммит 78d5105331
7 изменённых файлов: 123 добавлений и 48 удалений

Просмотреть файл

@ -105,7 +105,7 @@ Zone::onTooMuchMalloc()
}
void
Zone::sweep(FreeOp *fop, bool releaseTypes)
Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
{
/*
* Periodically release observed types for all scripts. This is safe to
@ -116,7 +116,7 @@ Zone::sweep(FreeOp *fop, bool releaseTypes)
{
gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
types.sweep(fop, releaseTypes);
types.sweep(fop, releaseTypes, oom);
}
if (!fop->runtime()->debuggerList.isEmpty())

Просмотреть файл

@ -315,7 +315,7 @@ struct Zone : public JS::shadow::Zone,
js::types::TypeZone types;
void sweep(js::FreeOp *fop, bool releaseTypes);
void sweep(js::FreeOp *fop, bool releaseTypes, bool *oom);
bool hasMarkedCompartments();

Просмотреть файл

@ -2803,7 +2803,11 @@ static void
FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript)
{
types::TypeZone &types = script->zone()->types;
ionScript->recompileInfo().compilerOutput(types)->invalidate();
// Note: If the script is about to be swept, the compiler output may have
// already been destroyed.
if (types::CompilerOutput *output = ionScript->recompileInfo().compilerOutput(types))
output->invalidate();
// If this script has Ion code on the stack, invalidated() will return
// true. In this case we have to wait until destroying it.

Просмотреть файл

@ -3978,7 +3978,22 @@ BeginSweepingZoneGroup(JSRuntime *rt)
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
zone->sweep(&fop, releaseTypes && !zone->isPreservingCode());
// If there is an OOM while sweeping types, the type information
// will be deoptimized so that it is still correct (i.e.
// overapproximates the possible types in the zone), but the
// constraints might not have been triggered on the deoptimization
// or even copied over completely. In this case, destroy all JIT
// code and new script addendums in the zone, the only things whose
// correctness depends on the type constraints.
bool oom = false;
zone->sweep(&fop, releaseTypes && !zone->isPreservingCode(), &oom);
if (oom) {
zone->setPreservingCode(false);
zone->discardJitCode(&fop);
zone->types.clearAllNewScriptAddendumsOnOOM();
}
}
}

Просмотреть файл

@ -37,6 +37,8 @@
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "jit/ExecutionModeInlines.h"
using namespace js;
using namespace js::gc;
using namespace js::types;
@ -3120,6 +3122,29 @@ TypeObject::clearNewScriptAddendum(ExclusiveContext *cx)
}
}
void
TypeObject::maybeClearNewScriptAddendumOnOOM()
{
if (!isMarked())
return;
if (!addendum || addendum->kind != TypeObjectAddendum::NewScript)
return;
for (unsigned i = 0; i < getPropertyCount(); i++) {
Property *prop = getProperty(i);
if (!prop)
continue;
if (prop->types.definiteProperty())
prop->types.setNonDataPropertyIgnoringConstraints();
}
// This method is called during GC sweeping, so there is no write barrier
// that needs to be triggered.
js_free(addendum);
addendum.unsafeSet(nullptr);
}
void
TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx)
{
@ -3943,7 +3968,7 @@ ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto)
/////////////////////////////////////////////////////////////////////
void
ConstraintTypeSet::sweep(Zone *zone)
ConstraintTypeSet::sweep(Zone *zone, bool *oom)
{
/*
* Purge references to type objects that are no longer live. Type sets hold
@ -3964,9 +3989,14 @@ ConstraintTypeSet::sweep(Zone *zone)
TypeObjectKey **pentry =
HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(zone->types.typeLifoAlloc, objectSet, objectCount, object);
if (!pentry)
CrashAtUnhandlableOOM("OOM in ConstraintTypeSet::sweep");
*pentry = object;
if (pentry) {
*pentry = object;
} else {
*oom = true;
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
return;
}
}
}
setBaseObjectCount(objectCount);
@ -3987,10 +4017,12 @@ ConstraintTypeSet::sweep(Zone *zone)
while (constraint) {
TypeConstraint *copy;
if (constraint->sweep(zone->types, &copy)) {
if (!copy)
CrashAtUnhandlableOOM("OOM in ConstraintTypeSet::sweep");
copy->next = constraintList;
constraintList = copy;
if (copy) {
copy->next = constraintList;
constraintList = copy;
} else {
*oom = true;
}
}
constraint = constraint->next;
}
@ -4011,7 +4043,7 @@ TypeObject::clearProperties()
* so that type objects do not need later finalization.
*/
inline void
TypeObject::sweep(FreeOp *fop)
TypeObject::sweep(FreeOp *fop, bool *oom)
{
if (!isMarked()) {
if (addendum)
@ -4019,7 +4051,7 @@ TypeObject::sweep(FreeOp *fop)
return;
}
js::LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
/*
* Properties were allocated from the old arena, and need to be copied over
@ -4046,17 +4078,21 @@ TypeObject::sweep(FreeOp *fop)
}
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
if (!newProp)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
if (newProp) {
Property **pentry =
HashSetInsert<jsid,Property,Property>
(typeLifoAlloc, propertySet, propertyCount, prop->id);
if (pentry) {
*pentry = newProp;
newProp->types.sweep(zone(), oom);
continue;
}
}
Property **pentry =
HashSetInsert<jsid,Property,Property>
(typeLifoAlloc, propertySet, propertyCount, prop->id);
if (!pentry)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
*pentry = newProp;
newProp->types.sweep(zone());
*oom = true;
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
clearProperties();
return;
}
}
setBasePropertyCount(propertyCount);
@ -4067,18 +4103,17 @@ TypeObject::sweep(FreeOp *fop)
clearProperties();
} else {
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
if (!newProp)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
propertySet = (Property **) newProp;
newProp->types.sweep(zone());
if (newProp) {
propertySet = (Property **) newProp;
newProp->types.sweep(zone(), oom);
} else {
*oom = true;
addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
clearProperties();
return;
}
}
}
if (basePropertyCount() <= SET_ARRAY_SIZE) {
for (unsigned i = 0; i < basePropertyCount(); i++)
JS_ASSERT(propertySet[i]);
}
}
void
@ -4196,7 +4231,7 @@ TypeCompartment::~TypeCompartment()
}
/* static */ void
TypeScript::Sweep(FreeOp *fop, JSScript *script)
TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom)
{
JSCompartment *compartment = script->compartment();
JS_ASSERT(compartment->zone()->isGCSweeping());
@ -4206,7 +4241,7 @@ TypeScript::Sweep(FreeOp *fop, JSScript *script)
/* Remove constraints and references to dead objects from the persistent type sets. */
for (unsigned i = 0; i < num; i++)
typeArray[i].sweep(compartment->zone());
typeArray[i].sweep(compartment->zone(), oom);
}
void
@ -4278,7 +4313,7 @@ TypeZone::~TypeZone()
}
void
TypeZone::sweep(FreeOp *fop, bool releaseTypes)
TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
{
JS_ASSERT(zone()->isGCSweeping());
@ -4298,10 +4333,12 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
CompilerOutput &output = (*compilerOutputs)[i];
if (output.isValid()) {
JSScript *script = output.script();
if (IsScriptAboutToBeFinalized(&script))
if (IsScriptAboutToBeFinalized(&script)) {
jit::GetIonScript(script, output.mode())->recompileInfoRef() = uint32_t(-1);
output.invalidate();
else
} else {
output.setSweepIndex(newCompilerOutputCount++);
}
}
}
}
@ -4312,7 +4349,7 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
types::TypeScript::Sweep(fop, script);
types::TypeScript::Sweep(fop, script, oom);
if (releaseTypes) {
if (script->hasParallelIonScript()) {
@ -4351,7 +4388,7 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
!iter.done(); iter.next())
{
TypeObject *object = iter.get<TypeObject>();
object->sweep(fop);
object->sweep(fop, oom);
}
for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
@ -4386,6 +4423,17 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes)
}
}
void
TypeZone::clearAllNewScriptAddendumsOnOOM()
{
for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
!iter.done(); iter.next())
{
TypeObject *object = iter.get<TypeObject>();
object->maybeClearNewScriptAddendumOnOOM();
}
}
#ifdef DEBUG
void
TypeScript::printTypes(JSContext *cx, HandleScript script) const

Просмотреть файл

@ -607,7 +607,7 @@ class ConstraintTypeSet : public TypeSet
/* Add a new constraint to this set. */
bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
inline void sweep(JS::Zone *zone);
inline void sweep(JS::Zone *zone, bool *oom);
};
class StackTypeSet : public ConstraintTypeSet
@ -622,6 +622,7 @@ class HeapTypeSet : public ConstraintTypeSet
public:
/* Mark this type set as representing a non-data property. */
inline void setNonDataProperty(ExclusiveContext *cx);
inline void setNonDataPropertyIgnoringConstraints(); // Variant for use during GC.
/* Mark this type set as representing a non-writable property. */
inline void setNonWritableProperty(ExclusiveContext *cx);
@ -1122,13 +1123,14 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
void clearAddendum(ExclusiveContext *cx);
void clearNewScriptAddendum(ExclusiveContext *cx);
void clearTypedObjectAddendum(ExclusiveContext *cx);
void maybeClearNewScriptAddendumOnOOM();
bool isPropertyNonData(jsid id);
bool isPropertyNonWritable(jsid id);
void print();
inline void clearProperties();
inline void sweep(FreeOp *fop);
inline void sweep(FreeOp *fop, bool *oom);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@ -1284,7 +1286,7 @@ class TypeScript
static void Purge(JSContext *cx, HandleScript script);
static void Sweep(FreeOp *fop, JSScript *script);
static void Sweep(FreeOp *fop, JSScript *script, bool *oom);
void destroy();
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
@ -1576,7 +1578,8 @@ struct TypeZone
JS::Zone *zone() const { return zone_; }
void sweep(FreeOp *fop, bool releaseTypes);
void sweep(FreeOp *fop, bool releaseTypes, bool *oom);
void clearAllNewScriptAddendumsOnOOM();
/* Mark a script as needing recompilation once inference has finished. */
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);

Просмотреть файл

@ -1078,7 +1078,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
if (false) {
unknownObject:
type = Type::AnyObjectType();
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
}
@ -1128,13 +1127,19 @@ HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
}
}
inline void
HeapTypeSet::setNonDataPropertyIgnoringConstraints()
{
flags |= TYPE_FLAG_NON_DATA_PROPERTY;
}
inline void
HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
{
if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
return;
flags |= TYPE_FLAG_NON_DATA_PROPERTY;
setNonDataPropertyIgnoringConstraints();
newPropertyState(cx);
}