Bug 1447989 - Add some asserts to TI code. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 16 Apr 2018 13:26:32 +0200
changeset 467371 a466495618d0468b0fd43f9c10fed8ae8112b6d9
parent 467370 438494d2d17bec92e4f4e38661a85b60680ab087
child 467372 efac2c840d9be2cd675a541bf60443906330f3b6
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1447989
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1447989 - Add some asserts to TI code. r=tcampbell
js/src/gc/GC.cpp
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -7967,16 +7967,17 @@ GCRuntime::mergeCompartments(JSCompartme
     // Merge the allocator, stats and UIDs in source's zone into target's zone.
     target->zone()->arenas.adoptArenas(rt, &source->zone()->arenas, targetZoneIsCollecting);
     target->zone()->usage.adopt(source->zone()->usage);
     target->zone()->adoptUniqueIds(source->zone());
     target->zone()->adoptMallocBytes(source->zone());
 
     // Merge other info in source's zone into target's zone.
     target->zone()->types.typeLifoAlloc().transferFrom(&source->zone()->types.typeLifoAlloc());
+    MOZ_RELEASE_ASSERT(source->zone()->types.sweepTypeLifoAlloc.ref().isEmpty());
 
     // Atoms which are marked in source's zone are now marked in target's zone.
     atomMarking.adoptMarkedAtoms(target->zone(), source->zone());
 
     // Merge script name maps in the target compartment's map.
     if (rt->lcovOutput().isEnabled() && source->scriptNameMap) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
 
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -950,24 +950,25 @@ TypeSet::setBaseObjectCount(uint32_t cou
 }
 
 inline void
 HeapTypeSet::newPropertyState(JSContext* cx)
 {
     checkMagic();
 
     /* Propagate the change to all constraints. */
+    AutoAssertNoTISweeping nosweeping(cx->zone()->types);
     if (!cx->helperThread()) {
-        TypeConstraint* constraint = constraintList();
+        TypeConstraint* constraint = constraintList(nosweeping);
         while (constraint) {
             constraint->newPropertyState(cx, this);
             constraint = constraint->next();
         }
     } else {
-        MOZ_ASSERT(!constraintList());
+        MOZ_ASSERT(!constraintList(nosweeping));
     }
 }
 
 inline void
 HeapTypeSet::setNonDataProperty(JSContext* cx)
 {
     checkMagic();
 
@@ -1103,16 +1104,17 @@ ObjectGroup::setBasePropertyCount(uint32
 inline HeapTypeSet*
 ObjectGroup::getProperty(JSContext* cx, JSObject* obj, jsid id)
 {
     MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
     MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
     MOZ_ASSERT(!unknownProperties());
     MOZ_ASSERT_IF(obj, obj->group() == this);
     MOZ_ASSERT_IF(singleton(), obj);
+    MOZ_ASSERT(cx->compartment() == compartment());
 
     if (HeapTypeSet* types = maybeGetProperty(id))
         return types;
 
     Property* base = cx->typeLifoAlloc().new_<Property>(id);
     if (!base) {
         markUnknown(cx);
         return nullptr;
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -708,17 +708,18 @@ ConstraintTypeSet::addType(JSContext* cx
     postWriteBarrier(cx, type);
 
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type).get());
 
     /* Propagate the type to all constraints. */
     if (!cx->helperThread()) {
-        TypeConstraint* constraint = constraintList();
+        AutoAssertNoTISweeping nosweeping(cx->zone()->types);
+        TypeConstraint* constraint = constraintList(nosweeping);
         while (constraint) {
             constraint->newType(cx, this, type);
             constraint = constraint->next();
         }
     } else {
         MOZ_ASSERT(!constraintList_);
     }
 }
@@ -1969,28 +1970,29 @@ ObjectStateChange(JSContext* cx, ObjectG
 {
     if (group->unknownProperties())
         return;
 
     /* All constraints listening to state changes are on the empty id. */
     HeapTypeSet* types = group->maybeGetProperty(JSID_EMPTY);
 
     /* Mark as unknown after getting the types, to avoid assertion. */
+    AutoAssertNoTISweeping nosweeping(cx->zone()->types);
     if (markingUnknown)
         group->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
 
     if (types) {
         if (!cx->helperThread()) {
-            TypeConstraint* constraint = types->constraintList();
+            TypeConstraint* constraint = types->constraintList(nosweeping);
             while (constraint) {
                 constraint->newObjectState(cx, group);
                 constraint = constraint->next();
             }
         } else {
-            MOZ_ASSERT(!types->constraintList());
+            MOZ_ASSERT(!types->constraintList(nosweeping));
         }
     }
 }
 
 namespace {
 
 class ConstraintDataFreezePropertyState
 {
@@ -2829,30 +2831,33 @@ ObjectGroup::markPropertyNonWritable(JSC
     HeapTypeSet* types = getProperty(cx, obj, id);
     if (types)
         types->setNonWritableProperty(cx);
 }
 
 void
 ObjectGroup::markStateChange(JSContext* cx)
 {
+    MOZ_ASSERT(cx->compartment() == compartment());
+
     if (unknownProperties())
         return;
 
     AutoEnterAnalysis enter(cx);
     HeapTypeSet* types = maybeGetProperty(JSID_EMPTY);
     if (types) {
+        AutoAssertNoTISweeping nosweeping(cx->zone()->types);
         if (!cx->helperThread()) {
-            TypeConstraint* constraint = types->constraintList();
+            TypeConstraint* constraint = types->constraintList(nosweeping);
             while (constraint) {
                 constraint->newObjectState(cx, this);
                 constraint = constraint->next();
             }
         } else {
-            MOZ_ASSERT(!types->constraintList());
+            MOZ_ASSERT(!types->constraintList(nosweeping));
         }
     }
 }
 
 void
 ObjectGroup::setFlags(JSContext* cx, ObjectGroupFlags flags)
 {
     MOZ_ASSERT(!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES),
@@ -3298,16 +3303,18 @@ js::TypeMonitorResult(JSContext* cx, JSS
 void
 js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, StackTypeSet* types,
                       TypeSet::Type type)
 {
     assertSameCompartment(cx, script, type);
 
     AutoEnterAnalysis enter(cx);
 
+    script->maybeSweepTypes(nullptr);
+
     MOZ_ASSERT(types == TypeScript::BytecodeTypes(script, pc));
     MOZ_ASSERT(!types->hasType(type));
 
     InferSpew(ISpewOps, "bytecodeType: %p %05zu: %s",
               script, script->pcToOffset(pc), TypeSet::TypeString(type).get());
     types->addType(cx, type);
 }
 
@@ -3327,16 +3334,17 @@ js::TypeMonitorResult(JSContext* cx, JSS
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
 bool
 JSScript::makeTypes(JSContext* cx)
 {
     MOZ_ASSERT(!types_);
+    assertSameCompartment(cx, this);
 
     AutoEnterAnalysis enter(cx);
 
     unsigned count = TypeScript::NumTypeSets(this);
 
     TypeScript* typeScript = (TypeScript*)
         zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
     if (!typeScript) {
@@ -4221,17 +4229,18 @@ ConstraintTypeSet::sweep(Zone* zone, Aut
             setBaseObjectCount(0);
         }
     }
 
     /*
      * Type constraints only hold weak references. Copy constraints referring
      * to data that is still live into the zone's new arena.
      */
-    TypeConstraint* constraint = constraintList();
+    AutoAssertNoTISweeping nosweeping(zone->types);
+    TypeConstraint* constraint = constraintList(nosweeping);
     constraintList_ = nullptr;
     while (constraint) {
         MOZ_ASSERT(zone->types.sweepTypeLifoAlloc.ref().contains(constraint));
         TypeConstraint* copy;
         if (constraint->sweep(zone->types, &copy)) {
             if (copy) {
                 MOZ_ASSERT(zone->types.typeLifoAlloc().contains(copy));
                 copy->setNext(constraintList_);
@@ -4276,19 +4285,20 @@ EnsureHasAutoClearTypeInferenceStateOnOO
  * objects, and singleton JS objects whose type is not referenced elsewhere.
  * This is done either incrementally as part of the sweep, or on demand as type
  * objects are accessed before their contents have been swept.
  */
 void
 ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
 {
     MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation);
-
     setGeneration(zone()->types.generation);
 
+    MOZ_RELEASE_ASSERT(!zone()->types.assertNoTISweeping);
+
     AssertGCStateForSweep(zone());
 
     Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
     EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
 
     AutoTouchingGrayThings tgt;
 
     if (maybeUnboxedLayout()) {
@@ -4313,16 +4323,17 @@ ObjectGroup::sweep(AutoClearTypeInferenc
         newScript()->sweep();
 
     LifoAlloc& typeLifoAlloc = zone()->types.typeLifoAlloc();
 
     /*
      * Properties were allocated from the old arena, and need to be copied over
      * to the new one.
      */
+    AutoAssertNoTISweeping nosweeping(zone()->types);
     unsigned propertyCount = basePropertyCount();
     if (propertyCount >= 2) {
         unsigned oldCapacity = TypeHashSet::Capacity(propertyCount);
         Property** oldArray = propertySet;
 
         MOZ_RELEASE_ASSERT(uintptr_t(oldArray[-1]) == oldCapacity);
 
         unsigned oldPropertyCount = propertyCount;
@@ -4330,17 +4341,20 @@ ObjectGroup::sweep(AutoClearTypeInferenc
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property* prop = oldArray[i];
             if (prop) {
                 oldPropertiesFound++;
                 prop->types.checkMagic();
-                if (singleton() && !prop->types.constraintList() && !zone()->isPreservingCode()) {
+                if (singleton() &&
+                    !prop->types.constraintList(nosweeping) &&
+                    !zone()->isPreservingCode())
+                {
                     /*
                      * Don't copy over properties of singleton objects when their
                      * presence will not be required by jitcode or type constraints
                      * (i.e. for the definite properties analysis). The contents of
                      * these type sets will be regenerated as necessary.
                      */
                     JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property),
                               MemCheckKind::MakeUndefined);
@@ -4366,17 +4380,20 @@ ObjectGroup::sweep(AutoClearTypeInferenc
                 return;
             }
         }
         MOZ_RELEASE_ASSERT(oldPropertyCount == oldPropertiesFound);
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property* prop = (Property*) propertySet;
         prop->types.checkMagic();
-        if (singleton() && !prop->types.constraintList() && !zone()->isPreservingCode()) {
+        if (singleton() &&
+            !prop->types.constraintList(nosweeping) &&
+            !zone()->isPreservingCode())
+        {
             // Skip, as above.
             JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property), MemCheckKind::MakeUndefined);
             clearProperties();
         } else {
             Property* newProp = typeLifoAlloc.new_<Property>(*prop);
             JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property), MemCheckKind::MakeUndefined);
             if (newProp) {
                 propertySet = (Property**) newProp;
@@ -4396,16 +4413,18 @@ ObjectGroup::sweep(AutoClearTypeInferenc
 /* static */ void
 JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
 {
     MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
 
     if (!types_ || typesGeneration() == zone()->types.generation)
         return;
 
+    MOZ_RELEASE_ASSERT(!zone()->types.assertNoTISweeping);
+
     setTypesGeneration(zone()->types.generation);
 
     AssertGCStateForSweep(zone());
 
     Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
     EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
 
     TypeZone& types = zone()->types;
@@ -4486,16 +4505,17 @@ TypeZone::TypeZone(Zone* zone)
   : zone_(zone),
     typeLifoAlloc_(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     currentCompilationId_(zone),
     generation(zone, 0),
     sweepTypeLifoAlloc(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepReleaseTypes(zone, false),
     sweepingTypes(zone, false),
     keepTypeScripts(zone, false),
+    assertNoTISweeping(zone, false),
     activeAnalysis(zone, nullptr)
 {
 }
 
 TypeZone::~TypeZone()
 {
     MOZ_RELEASE_ASSERT(!sweepingTypes);
     MOZ_ASSERT(!keepTypeScripts);
@@ -4534,16 +4554,17 @@ TypeZone::clearAllNewScriptsOnOOM()
     }
 }
 
 AutoClearTypeInferenceStateOnOOM::AutoClearTypeInferenceStateOnOOM(Zone* zone)
   : zone(zone), oom(false)
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
+    MOZ_ASSERT(!zone->types.assertNoTISweeping);
     zone->types.setSweepingTypes(true);
 }
 
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
     zone->types.setSweepingTypes(false);
 
     if (oom) {
@@ -4610,15 +4631,28 @@ TypeScript::printTypes(JSContext* cx, Ha
             fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 }
 #endif /* DEBUG */
 
+AutoAssertNoTISweeping::AutoAssertNoTISweeping(TypeZone& zone)
+  : zone_(zone),
+    prev_(zone_.assertNoTISweeping)
+{
+    zone_.assertNoTISweeping = true;
+}
+
+AutoAssertNoTISweeping::~AutoAssertNoTISweeping()
+{
+    MOZ_ASSERT(zone_.assertNoTISweeping);
+    zone_.assertNoTISweeping = prev_;
+}
+
 JS::ubi::Node::Size
 JS::ubi::Concrete<js::ObjectGroup>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
     Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
     size += get().sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -651,16 +651,29 @@ class AutoClearTypeInferenceStateOnOOM
     void setOOM() {
         oom = true;
     }
     bool hadOOM() const {
         return oom;
     }
 };
 
+// Assert no sweeping of TI data (ObjectGroup::sweep, JSScript::maybeSweepTypes)
+// happens in this scope.
+class MOZ_RAII AutoAssertNoTISweeping
+{
+    TypeZone& zone_;
+    bool prev_;
+    JS::AutoCheckCannotGC nogc_;
+
+  public:
+    explicit AutoAssertNoTISweeping(TypeZone& zone);
+    ~AutoAssertNoTISweeping();
+};
+
 /* Superclass common to stack and heap type sets. */
 class ConstraintTypeSet : public TypeSet
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     uintptr_t magic_;
 #endif
 
   protected:
@@ -685,17 +698,17 @@ class ConstraintTypeSet : public TypeSet
 
     void checkMagic() const {
 #ifdef JS_CRASH_DIAGNOSTICS
         if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic))
             ReportMagicWordFailure(magic_, ConstraintTypeSetMagic, uintptr_t(flags), uintptr_t(objectSet));
 #endif
     }
 
-    TypeConstraint* constraintList() const {
+    TypeConstraint* constraintList(const AutoAssertNoTISweeping&) const {
         checkMagic();
         if (constraintList_)
             constraintList_->checkMagic();
         return constraintList_;
     }
     void setConstraintList(TypeConstraint* constraint) {
         MOZ_ASSERT(!constraintList_);
         checkMagic();
@@ -1357,16 +1370,18 @@ class TypeZone
     // During incremental sweeping, whether to try to destroy all type
     // information attached to scripts.
     ZoneData<bool> sweepReleaseTypes;
 
     ZoneData<bool> sweepingTypes;
 
     ZoneData<bool> keepTypeScripts;
 
+    ZoneData<bool> assertNoTISweeping;
+
     // The topmost AutoEnterAnalysis on the stack, if there is one.
     ZoneData<AutoEnterAnalysis*> activeAnalysis;
 
     explicit TypeZone(JS::Zone* zone);
     ~TypeZone();
 
     JS::Zone* zone() const { return zone_; }