diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index 90a4d31833b7..47ff022b8f2d 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -26,6 +26,8 @@ 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; @@ -39,9 +41,10 @@ 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 @@ -51,6 +54,13 @@ const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); 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 */ @@ -164,6 +174,24 @@ IsInsideNursery(const JS::shadow::Runtime *runtime, const void *p) #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(addr); + JS_ASSERT(location == gc::ChunkLocationNursery || + location == gc::ChunkLocationTenuredHeap); + return location == gc::ChunkLocationNursery; +#else + return false; +#endif +} + } /* namespace gc */ } /* namespace js */ diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index 4acbfef5aa9c..d6d2664fd669 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -606,9 +606,18 @@ ArenaHeader::getThingSize() const */ 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 { @@ -623,7 +632,7 @@ struct ChunkInfo * Calculating sizes and offsets is simpler if sizeof(ChunkInfo) is * architecture-independent. */ - char padding[16]; + char padding[20]; #endif /* diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 873cea6c8df5..3eeb0ce2320f 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -904,7 +904,7 @@ js::Nursery::sweep(JSRuntime *rt) /* 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); diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 78476b30dfdf..0631e867f738 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -201,13 +201,19 @@ class Nursery return reinterpret_cast(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() { diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 64f9d56ef540..ab952be20a88 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -42,6 +42,7 @@ UNIFIED_SOURCES += [ 'testIntern.cpp', 'testIntString.cpp', 'testIntTypesABI.cpp', + 'testIsInsideNursery.cpp', 'testJSEvaluateScript.cpp', 'testLookup.cpp', 'testLooselyEqual.cpp', diff --git a/js/src/jsapi-tests/testIsInsideNursery.cpp b/js/src/jsapi-tests/testIsInsideNursery.cpp new file mode 100644 index 000000000000..7ac5dffa09f8 --- /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) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 2ead125c3ca8..8b1a97a0d5a7 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -796,6 +796,7 @@ Chunk::init(JSRuntime *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. */