Bug 1001359 - Store chunk location in the chunk trailer r=terrence DONTBUILD
authorJon Coppeard <jcoppeard@mozilla.com>
Sat, 26 Apr 2014 09:30:04 +0100
changeset 180764 dc3c5febcdaf772160d20975dc15335cab834fcd
parent 180763 cf70f6a324137e5f3a4362fcd92dfda026e9eecd
child 180765 154fb9a9e0bca82352b76893d86f3ad419067816
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersterrence
bugs1001359
milestone31.0a1
Bug 1001359 - Store chunk location in the chunk trailer r=terrence DONTBUILD
js/public/HeapAPI.h
js/src/gc/Heap.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testIsInsideNursery.cpp
js/src/jsgc.cpp
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -21,41 +21,51 @@ namespace js {
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessRuntime(JSRuntime *rt);
 
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessZone(JS::Zone *zone);
 
 namespace gc {
 
+struct Cell;
+
 const size_t ArenaShift = 12;
 const size_t ArenaSize = size_t(1) << ArenaShift;
 const size_t ArenaMask = ArenaSize - 1;
 
 const size_t ChunkShift = 20;
 const size_t ChunkSize = size_t(1) << ChunkShift;
 const size_t ChunkMask = ChunkSize - 1;
 
 const size_t CellShift = 3;
 const size_t CellSize = size_t(1) << CellShift;
 const size_t CellMask = CellSize - 1;
 
 /* These are magic constants derived from actual offsets in gc/Heap.h. */
-const size_t ChunkMarkBitmapOffset = 1032368;
+const size_t ChunkMarkBitmapOffset = 1032360;
 const size_t ChunkMarkBitmapBits = 129024;
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
+const size_t ChunkLocationOffset = ChunkSize - sizeof(void*) - sizeof(uintptr_t);
 
 /*
  * Live objects are marked black. How many other additional colors are available
  * depends on the size of the GCThing. Objects marked gray are eligible for
  * cycle collection.
  */
 static const uint32_t BLACK = 0;
 static const uint32_t GRAY = 1;
 
+/*
+ * Constants used to indicate whether a chunk is part of the tenured heap or the
+ * nusery.
+ */
+const uintptr_t ChunkLocationNursery = 0;
+const uintptr_t ChunkLocationTenuredHeap = 1;
+
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
 struct Zone;
 } /* namespace JS */
 
 namespace JS {
@@ -159,16 +169,34 @@ IsInsideNursery(const JS::shadow::Runtim
 {
 #ifdef JSGC_GENERATIONAL
     return uintptr_t(p) >= runtime->gcNurseryStart_ && uintptr_t(p) < runtime->gcNurseryEnd_;
 #else
     return false;
 #endif
 }
 
+MOZ_ALWAYS_INLINE bool
+IsInsideNursery(const js::gc::Cell *cell)
+{
+#ifdef JSGC_GENERATIONAL
+    if (!cell)
+        return false;
+    uintptr_t addr = uintptr_t(cell);
+    addr &= ~js::gc::ChunkMask;
+    addr |= js::gc::ChunkLocationOffset;
+    uint32_t location = *reinterpret_cast<uint32_t *>(addr);
+    JS_ASSERT(location == gc::ChunkLocationNursery ||
+              location == gc::ChunkLocationTenuredHeap);
+    return location == gc::ChunkLocationNursery;
+#else
+    return false;
+#endif
+}
+
 } /* namespace gc */
 
 } /* namespace js */
 
 namespace JS {
 
 static MOZ_ALWAYS_INLINE Zone *
 GetGCThingZone(void *thing)
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -601,34 +601,43 @@ ArenaHeader::getThingSize() const
 
 /*
  * The tail of the chunk info is shared between all chunks in the system, both
  * nursery and tenured. This structure is locatable from any GC pointer by
  * aligning to 1MiB.
  */
 struct ChunkTrailer
 {
+    /* The index the chunk in the nursery, or LocationTenuredHeap. */
+    uint32_t        location;
+
+#if JS_BITS_PER_WORD == 64
+    uint32_t        padding;
+#endif
+
     JSRuntime       *runtime;
 };
 
+static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t), "ChunkTrailer size is incorrect.");
+
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     Chunk           *next;
     Chunk           **prevp;
 
     /* Free arenas are linked together with aheader.next. */
     ArenaHeader     *freeArenasHead;
 
 #if JS_BITS_PER_WORD == 32
     /*
      * Calculating sizes and offsets is simpler if sizeof(ChunkInfo) is
      * architecture-independent.
      */
-    char            padding[16];
+    char            padding[20];
 #endif
 
     /*
      * Decommitted arenas are tracked by a bitmap in the chunk header. We use
      * this offset to start our search iteration close to a decommitted arena
      * that we can allocate.
      */
     uint32_t        lastDecommittedArenaOffset;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -899,17 +899,17 @@ js::Nursery::freeHugeSlots(JSRuntime *rt
 
 void
 js::Nursery::sweep(JSRuntime *rt)
 {
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, NurserySize);
     for (int i = 0; i < NumNurseryChunks; ++i)
-        chunk(i).trailer.runtime = runtime();
+        initChunk(i);
 
     if (rt->gcZeal_ == ZealGenerationalGCValue) {
         MOZ_ASSERT(numActiveChunks_ == NumNurseryChunks);
 
         /* Only reset the alloc point when we are close to the end. */
         if (currentChunk_ + 1 == NumNurseryChunks)
             setCurrentChunk(0);
     } else
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -196,23 +196,29 @@ class Nursery
     static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize,
                   "Nursery chunk size must match gc::Chunk size.");
     NurseryChunkLayout &chunk(int index) const {
         JS_ASSERT(index < NumNurseryChunks);
         JS_ASSERT(start());
         return reinterpret_cast<NurseryChunkLayout *>(start())[index];
     }
 
+    MOZ_ALWAYS_INLINE void initChunk(int chunkno) {
+        NurseryChunkLayout &c = chunk(chunkno);
+        c.trailer.location = gc::ChunkLocationNursery;
+        c.trailer.runtime = runtime();
+    }
+
     MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
         JS_ASSERT(chunkno < NumNurseryChunks);
         JS_ASSERT(chunkno < numActiveChunks_);
         currentChunk_ = chunkno;
         position_ = chunk(chunkno).start();
         currentEnd_ = chunk(chunkno).end();
-        chunk(chunkno).trailer.runtime = runtime();
+        initChunk(chunkno);
     }
 
     void updateDecommittedRegion() {
 #ifndef JS_GC_ZEAL
         if (numActiveChunks_ < NumNurseryChunks) {
             // Bug 994054: madvise on MacOS is too slow to make this
             //             optimization worthwhile.
 # ifndef XP_MACOSX
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -37,16 +37,17 @@ UNIFIED_SOURCES += [
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testHashTable.cpp',
     'testHashTableInit.cpp',
     'testIndexToString.cpp',
     'testIntern.cpp',
     'testIntString.cpp',
     'testIntTypesABI.cpp',
+    'testIsInsideNursery.cpp',
     'testJSEvaluateScript.cpp',
     'testLookup.cpp',
     'testLooselyEqual.cpp',
     'testMappedArrayBuffer.cpp',
     'testNewObject.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testIsInsideNursery.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* vim: set ts=8 sts=4 et sw=4 tw=99:
+*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "jsapi-tests/tests.h"
+
+BEGIN_TEST(testIsInsideNursery)
+{
+    /* Non-GC things are never inside the nursery. */
+    CHECK(!js::gc::IsInsideNursery(rt, rt));
+    CHECK(!js::gc::IsInsideNursery(rt, nullptr));
+    CHECK(!js::gc::IsInsideNursery(nullptr));
+
+    JS_GC(rt);
+
+    JS::RootedObject object(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
+
+#ifdef JSGC_GENERATIONAL
+    /* Objects are initially allocated in the nursery. */
+    CHECK(js::gc::IsInsideNursery(rt, object));
+    CHECK(js::gc::IsInsideNursery(object));
+#else
+    CHECK(!js::gc::IsInsideNursery(rt, object));
+    CHECK(!js::gc::IsInsideNursery(object));
+#endif
+
+    JS_GC(rt);
+
+    CHECK(!js::gc::IsInsideNursery(rt, object));
+    CHECK(!js::gc::IsInsideNursery(object));
+
+    return true;
+}
+END_TEST(testIsInsideNursery)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -791,16 +791,17 @@ Chunk::init(JSRuntime *rt)
     /*
      * Decommit the arenas. We do this after poisoning so that if the OS does
      * not have to recycle the pages, we still get the benefit of poisoning.
      */
     decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
     info.age = 0;
+    info.trailer.location = ChunkLocationTenuredHeap;
     info.trailer.runtime = rt;
 
     /* The rest of info fields are initialized in PickChunk. */
 }
 
 static inline Chunk **
 GetAvailableChunkList(Zone *zone)
 {