Backed out 2 changesets (bug 813381) for build bustage
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 27 Nov 2012 16:28:51 -0500
changeset 114290 82e61145a1dce2148e93c7e85ee193c38504ce48
parent 114289 f5e2eba79e1b28591e414801f5a39015f0e8b484
child 114291 5de5d395032832e27e570aaa7cd616b53f490ca4
push id23913
push useremorley@mozilla.com
push dateWed, 28 Nov 2012 17:11:31 +0000
treeherdermozilla-central@17c267a881cf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs813381
milestone20.0a1
backs outfb86e02eb4201befb72b8e8b03cebfe1135edadf
415cbab797d5be3728ec9ec6d89a8a4920411b5d
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
Backed out 2 changesets (bug 813381) for build bustage Backed out changeset fb86e02eb420 (bug 813381) Backed out changeset 415cbab797d5 (bug 813381)
js/src/gc/Root.h
js/src/jsgc.cpp
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -540,41 +540,41 @@ class Rooted : public RootedBase<T>
         PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg);
         commonInit(pt->thingGCRooters);
 #endif
     }
 
   public:
     Rooted(JSRuntime *rt
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(RootMethods<T>::initial()), scanned(false)
+      : ptr(RootMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(rt);
     }
 
     Rooted(JSRuntime *rt, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial), scanned(false)
+      : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(rt);
     }
 
     Rooted(JSContext *cx
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(RootMethods<T>::initial()), scanned(false)
+      : ptr(RootMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
     Rooted(JSContext *cx, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial), scanned(false)
+      : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
     Rooted(js::PerThreadData *pt
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(RootMethods<T>::initial())
@@ -589,17 +589,17 @@ class Rooted : public RootedBase<T>
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(pt);
     }
 
     template <typename S>
     Rooted(JSContext *cx, const Return<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial.ptr_), scanned(false)
+      : ptr(initial.ptr_)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
     template <typename S>
     Rooted(js::PerThreadData *pt, const Return<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -662,22 +662,16 @@ class Rooted : public RootedBase<T>
 
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
     Rooted<T> **stack, *prev;
 #endif
     T ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     Rooted(const Rooted &) MOZ_DELETE;
-
-#if defined(JSGC_ROOT_ANALYSIS)
-  public:
-    /* Has the rooting analysis ever scanned this Rooted's stack location? */
-    bool scanned;
-#endif
 };
 
 #if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
 // Defined in vm/String.h.
 template <>
 class Rooted<JSStableString *>;
 #endif
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5,18 +5,16 @@
  * 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/. */
 
 /* JS Mark-and-Sweep Garbage Collector. */
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Util.h"
 
-#include "jsutil.h"
-
 /*
  * This code implements a mark-and-sweep garbage collector. The mark phase is
  * incremental. Most sweeping is done on a background thread. A GC is divided
  * into slices as follows:
  *
  * Slice 1: Roots pushed onto the mark stack. The mark stack is processed by
  * popping an element, marking it, and pushing its children.
  *   ... JS code runs ...
@@ -5243,164 +5241,100 @@ CheckStackRootThing(uintptr_t *w, void *
 {
     if (kind != THING_ROOT_BINDINGS)
         return address == static_cast<void*>(w);
 
     Bindings *bp = static_cast<Bindings*>(address);
     return w >= (uintptr_t*)bp && w < (uintptr_t*)(bp + 1);
 }
 
-struct Rooter {
-    Rooted<void*> *rooter;
-    ThingRootKind kind;
-};
+JS_ALWAYS_INLINE void
+CheckStackRootThings(uintptr_t *w, Rooted<void*> *rooter, ThingRootKind kind, bool *matched)
+{
+    while (rooter) {
+        if (CheckStackRootThing(w, rooter->address(), kind))
+            *matched = true;
+        rooter = rooter->previous();
+    }
+}
 
 static void
-CheckStackRoot(JSRuntime *rt, uintptr_t *w, Rooter *begin, Rooter *end)
+CheckStackRoot(JSTracer *trc, uintptr_t *w)
 {
     /* Mark memory as defined for valgrind, as in MarkWordConservatively. */
 #ifdef JS_VALGRIND
     VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w));
 #endif
 
-    void *thing;
-    ArenaHeader *aheader;
-    AllocKind thingKind;
-    ConservativeGCTest status =
-        IsAddressableGCThing(rt, *w, false, &thingKind, &aheader, &thing);
-    if (status != CGCT_VALID)
-        return;
-    /*
-     * Note that |thing| may be in a free list (InFreeList(aheader, thing)),
-     * but we can skip that check because poisoning the pointer can't hurt; the
-     * pointer still cannot be used for a non-gcthing.
-     */
-
-    for (Rooter *p = begin; p != end; p++) {
-        if (CheckStackRootThing(w, p->rooter->address(), p->kind))
-            return;
-    }
-
-    for (ContextIter cx(rt); !cx.done(); cx.next()) {
-        SkipRoot *skip = cx->skipGCRooters;
-        while (skip) {
-            if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
-                return;
-            skip = skip->previous();
+    ConservativeGCTest test = MarkIfGCThingWord(trc, *w);
+
+    if (test == CGCT_VALID) {
+        bool matched = false;
+        JSRuntime *rt = trc->runtime;
+        for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
+            CheckStackRootThings(w, rt->mainThread.thingGCRooters[i],
+                                 ThingRootKind(i), &matched);
+            for (ContextIter cx(rt); !cx.done(); cx.next()) {
+                CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched);
+                SkipRoot *skip = cx->skipGCRooters;
+                while (skip) {
+                    if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
+                        matched = true;
+                    skip = skip->previous();
+                }
+            }
         }
-    }
-
-    /*
-     * Only poison the last byte in the word. It is easy to get accidental
-     * collisions when a value that does not occupy a full word is used to
-     * overwrite a now-dead GC thing pointer. In this case we want to avoid
-     * damaging the smaller value.
-     */
-    PoisonPtr(w);
+        if (!matched) {
+            /*
+             * Only poison the last byte in the word. It is easy to get
+             * accidental collisions when a value that does not occupy a full
+             * word is used to overwrite a now-dead GC thing pointer. In this
+             * case we want to avoid damaging the smaller value.
+             */
+            PoisonPtr(w);
+        }
+    }
 }
 
 static void
-CheckStackRootsRange(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
+CheckStackRootsRange(JSTracer *trc, uintptr_t *begin, uintptr_t *end)
 {
     JS_ASSERT(begin <= end);
     for (uintptr_t *i = begin; i != end; ++i)
-        CheckStackRoot(rt, i, rbegin, rend);
+        CheckStackRoot(trc, i);
 }
 
 static void
-CheckStackRootsRangeAndSkipIon(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
+CheckStackRootsRangeAndSkipIon(JSRuntime *rt, JSTracer *trc, uintptr_t *begin, uintptr_t *end)
 {
     /*
      * Regions of the stack between Ion activiations are marked exactly through
      * a different mechanism. We need to skip these regions when checking the
      * stack so that we do not poison IonMonkey's things.
      */
     uintptr_t *i = begin;
 
 #if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION)
     for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
         uintptr_t *ionMin, *ionEnd;
         ion.ionStackRange(ionMin, ionEnd);
 
-        uintptr_t *upto = Min(ionMin, end);
-        if (upto > i)
-            CheckStackRootsRange(rt, i, upto, rbegin, rend);
-        else
-            break;
+        CheckStackRootsRange(trc, i, ionMin);
         i = ionEnd;
     }
 #endif
 
     /* The topmost Ion activiation may be beyond our prior top. */
-    if (i < end)
-        CheckStackRootsRange(rt, i, end, rbegin, rend);
-}
-
-static int
-CompareRooters(const void *vpA, const void *vpB)
-{
-    const Rooter *a = static_cast<const Rooter *>(vpA);
-    const Rooter *b = static_cast<const Rooter *>(vpB);
-    // There should be no duplicates, and we wouldn't care about their order anyway.
-    return (a->rooter < b->rooter) ? -1 : 1;
-}
-
-/*
- * In the pathological cases that dominate much of the test case runtime,
- * rooting analysis spends tons of time scanning the stack during a tight-ish
- * loop. Since statically, everything is either rooted or it isn't, these scans
- * are almost certain to be worthless. Detect these cases by checking whether
- * the addresses of the top several rooters in the stack are recurring. Note
- * that there may be more than one CheckRoots call within the loop, so we may
- * alternate between a couple of stacks rather than just repeating the same one
- * over and over, so we need more than a depth-1 memory.
- */
-static bool
-SuppressCheckRoots(Vector<Rooter, 0, SystemAllocPolicy> &rooters)
-{
-    static const unsigned int NumStackMemories = 6;
-    static const size_t StackCheckDepth = 10;
-
-    static uint32_t stacks[NumStackMemories];
-    static unsigned int numMemories = 0;
-    static unsigned int oldestMemory = 0;
-
-    // Ugh. Sort the rooters. This should really be an O(n) rank selection
-    // followed by a sort. Interestingly, however, the overall scan goes a bit
-    // *faster* with this sort. Better branch prediction of the later
-    // partitioning pass, perhaps.
-    qsort(rooters.begin(), rooters.length(), sizeof(Rooter), CompareRooters);
-
-    // Forward-declare a variable so its address can be used to mark the
-    // current top of the stack
-    unsigned int pos;
-
-    // Compute the hash of the current stack
-    uint32_t hash = mozilla::HashGeneric(&pos);
-    for (unsigned int i = 0; i < Min(StackCheckDepth, rooters.length()); i++)
-        hash = mozilla::AddToHash(hash, rooters[rooters.length() - i - 1].rooter);
-
-    // Scan through the remembered stacks to find the current stack
-    for (pos = 0; pos < numMemories; pos++) {
-        if (stacks[pos] == hash) {
-            // Skip this check. Technically, it is incorrect to not update the
-            // LRU queue position, but it'll cost us at most one extra check
-            // for every time a hot stack falls out of the window.
-            return true;
-        }
-    }
-
-    // Replace the oldest remembered stack with our current stack
-    stacks[oldestMemory] = hash;
-    oldestMemory = (oldestMemory + 1) % NumStackMemories;
-    if (numMemories < NumStackMemories)
-        numMemories++;
-
-    return false;
-}
+    if (i <= end)
+        CheckStackRootsRange(trc, i, end);
+}
+
+static void
+EmptyMarkCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
+{}
 
 void
 JS::CheckStackRoots(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
 
     if (rt->gcZeal_ != ZealStackRootingSafeValue && rt->gcZeal_ != ZealStackRootingValue)
         return;
@@ -5427,93 +5361,69 @@ JS::CheckStackRoots(JSContext *cx)
         for (CompartmentsIter c(rt); !c.done(); c.next()) {
             if (c.get()->activeAnalysis)
                 return;
         }
     }
 
     AutoCopyFreeListToArenas copy(rt);
 
+    JSTracer checker;
+    JS_TracerInit(&checker, rt, EmptyMarkCallback);
+
     ConservativeGCData *cgcd = &rt->conservativeGC;
     cgcd->recordStackTop();
 
     JS_ASSERT(cgcd->hasStackToScan());
     uintptr_t *stackMin, *stackEnd;
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackMin = rt->nativeStackBase;
     stackEnd = cgcd->nativeStackTop;
 #else
     stackMin = cgcd->nativeStackTop + 1;
     stackEnd = reinterpret_cast<uintptr_t *>(rt->nativeStackBase);
-#endif
-
-    // Gather up all of the rooters
-    Vector< Rooter, 0, SystemAllocPolicy> rooters;
-    for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-        Rooted<void*> *rooter = rt->mainThread.thingGCRooters[i];
-        while (rooter) {
-            Rooter r = { rooter, ThingRootKind(i) };
-            JS_ALWAYS_TRUE(rooters.append(r));
-            rooter = rooter->previous();
-        }
-        for (ContextIter cx(rt); !cx.done(); cx.next()) {
-            rooter = cx->thingGCRooters[i];
-            while (rooter) {
-                Rooter r = { rooter, ThingRootKind(i) };
-                JS_ALWAYS_TRUE(rooters.append(r));
-                rooter = rooter->previous();
-            }
+
+    uintptr_t *&oldStackMin = cgcd->oldStackMin, *&oldStackEnd = cgcd->oldStackEnd;
+    uintptr_t *&oldStackData = cgcd->oldStackData;
+    uintptr_t &oldStackCapacity = cgcd->oldStackCapacity;
+
+    /*
+     * Adjust the stack to remove regions which have not changed since the
+     * stack was last scanned, and update the last scanned state.
+     */
+    if (stackEnd != oldStackEnd) {
+        js_free(oldStackData);
+        oldStackCapacity = rt->nativeStackQuota / sizeof(uintptr_t);
+        oldStackData = (uintptr_t *) rt->malloc_(oldStackCapacity * sizeof(uintptr_t));
+        if (!oldStackData) {
+            oldStackCapacity = 0;
+        } else {
+            uintptr_t *existing = stackEnd - 1, *copy = oldStackData;
+            while (existing >= stackMin && size_t(copy - oldStackData) < oldStackCapacity)
+                *copy++ = *existing--;
+            oldStackEnd = stackEnd;
+            oldStackMin = existing + 1;
         }
-    }
-
-    if (SuppressCheckRoots(rooters))
-        return;
-
-    // Truncate stackEnd to just after the address of the youngest
-    // already-scanned rooter on the stack, to avoid re-scanning the rest of
-    // the stack.
-    void *firstScanned = rooters.begin();
-    for (Rooter *p = rooters.begin(); p != rooters.end(); p++) {
-        if (p->rooter->scanned) {
-            uintptr_t *addr = reinterpret_cast<uintptr_t*>(p->rooter);
-#if JS_STACK_GROWTH_DIRECTION < 0
-            if (stackEnd > addr)
-#else
-            if (stackEnd < addr)
+    } else {
+        uintptr_t *existing = stackEnd - 1, *copy = oldStackData;
+        while (existing >= stackMin && existing >= oldStackMin && *existing == *copy) {
+            copy++;
+            existing--;
+        }
+        stackEnd = existing + 1;
+        while (existing >= stackMin && size_t(copy - oldStackData) < oldStackCapacity)
+            *copy++ = *existing--;
+        oldStackMin = existing + 1;
+    }
 #endif
-            {
-                stackEnd = addr;
-                firstScanned = p->rooter;
-            }
-        }
-    }
-
-    // Partition the stack by the already-scanned start address. Put everything
-    // that needs to be searched at the end of the vector.
-    Rooter *firstToScan = rooters.begin();
-    for (Rooter *p = rooters.begin(); p != rooters.end(); p++) {
-#if JS_STACK_GROWTH_DIRECTION < 0
-        if (p->rooter >= firstScanned)
-#else
-        if (p->rooter <= firstScanned)
-#endif
-        {
-            Swap(*firstToScan, *p);
-            ++firstToScan;
-        }
-    }
 
     JS_ASSERT(stackMin <= stackEnd);
-    CheckStackRootsRangeAndSkipIon(rt, stackMin, stackEnd, firstToScan, rooters.end());
-    CheckStackRootsRange(rt, cgcd->registerSnapshot.words,
-                         ArrayEnd(cgcd->registerSnapshot.words), firstToScan, rooters.end());
-
-    // Mark all rooters as scanned
-    for (Rooter *p = rooters.begin(); p != rooters.end(); p++)
-        p->rooter->scanned = true;
+    CheckStackRootsRangeAndSkipIon(rt, &checker, stackMin, stackEnd);
+    CheckStackRootsRange(&checker, cgcd->registerSnapshot.words,
+                         ArrayEnd(cgcd->registerSnapshot.words));
 }
 
 #endif /* DEBUG && JS_GC_ZEAL && JSGC_ROOT_ANALYSIS && !JS_THREADSAFE */
 
 #ifdef JS_GC_ZEAL
 
 /*
  * Write barrier verification