1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=79 ft=cpp:
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #ifndef gc_Nursery_inl_h
9 #define gc_Nursery_inl_h
10
11 #include "gc/Nursery.h"
12
13 #include "gc/Heap.h"
14 #include "gc/RelocationOverlay.h"
15 #include "gc/Zone.h"
16 #include "js/TracingAPI.h"
17 #include "vm/JSContext.h"
18 #include "vm/Runtime.h"
19 #include "vm/SharedMem.h"
20
21 template <typename T>
isInside(const SharedMem<T> & p)22 bool js::Nursery::isInside(const SharedMem<T>& p) const {
23 return isInside(p.unwrap(/*safe - used for value in comparison above*/));
24 }
25
getForwardedPointer(js::gc::Cell ** ref)26 MOZ_ALWAYS_INLINE /* static */ bool js::Nursery::getForwardedPointer(
27 js::gc::Cell** ref) {
28 MOZ_ASSERT(ref);
29 MOZ_ASSERT(IsInsideNursery(*ref));
30 const gc::RelocationOverlay* overlay =
31 reinterpret_cast<const gc::RelocationOverlay*>(*ref);
32 if (!overlay->isForwarded()) return false;
33 *ref = overlay->forwardingAddress();
34 return true;
35 }
36
maybeSetForwardingPointer(JSTracer * trc,void * oldData,void * newData,bool direct)37 inline void js::Nursery::maybeSetForwardingPointer(JSTracer* trc, void* oldData,
38 void* newData, bool direct) {
39 if (trc->isTenuringTracer())
40 setForwardingPointerWhileTenuring(oldData, newData, direct);
41 }
42
setForwardingPointerWhileTenuring(void * oldData,void * newData,bool direct)43 inline void js::Nursery::setForwardingPointerWhileTenuring(void* oldData,
44 void* newData,
45 bool direct) {
46 if (isInside(oldData)) setForwardingPointer(oldData, newData, direct);
47 }
48
setSlotsForwardingPointer(HeapSlot * oldSlots,HeapSlot * newSlots,uint32_t nslots)49 inline void js::Nursery::setSlotsForwardingPointer(HeapSlot* oldSlots,
50 HeapSlot* newSlots,
51 uint32_t nslots) {
52 // Slot arrays always have enough space for a forwarding pointer, since the
53 // number of slots is never zero.
54 MOZ_ASSERT(nslots > 0);
55 setDirectForwardingPointer(oldSlots, newSlots);
56 }
57
setElementsForwardingPointer(ObjectElements * oldHeader,ObjectElements * newHeader,uint32_t capacity)58 inline void js::Nursery::setElementsForwardingPointer(ObjectElements* oldHeader,
59 ObjectElements* newHeader,
60 uint32_t capacity) {
61 // Only use a direct forwarding pointer if there is enough space for one.
62 setForwardingPointer(oldHeader->elements(), newHeader->elements(),
63 capacity > 0);
64 }
65
setForwardingPointer(void * oldData,void * newData,bool direct)66 inline void js::Nursery::setForwardingPointer(void* oldData, void* newData,
67 bool direct) {
68 if (direct) {
69 setDirectForwardingPointer(oldData, newData);
70 return;
71 }
72
73 setIndirectForwardingPointer(oldData, newData);
74 }
75
setDirectForwardingPointer(void * oldData,void * newData)76 inline void js::Nursery::setDirectForwardingPointer(void* oldData,
77 void* newData) {
78 MOZ_ASSERT(isInside(oldData));
79
80 // Bug 1196210: If a zero-capacity header lands in the last 2 words of a
81 // jemalloc chunk abutting the start of a nursery chunk, the (invalid)
82 // newData pointer will appear to be "inside" the nursery.
83 MOZ_ASSERT(!isInside(newData) ||
84 (uintptr_t(newData) & js::gc::ChunkMask) == 0);
85
86 *reinterpret_cast<void**>(oldData) = newData;
87 }
88
89 namespace js {
90
91 // The allocation methods below will not run the garbage collector. If the
92 // nursery cannot accomodate the allocation, the malloc heap will be used
93 // instead.
94
95 template <typename T>
AllocateObjectBuffer(JSContext * cx,uint32_t count)96 static inline T* AllocateObjectBuffer(JSContext* cx, uint32_t count) {
97 size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
98 T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(cx->zone(), nbytes));
99 if (!buffer) ReportOutOfMemory(cx);
100 return buffer;
101 }
102
103 template <typename T>
AllocateObjectBuffer(JSContext * cx,JSObject * obj,uint32_t count)104 static inline T* AllocateObjectBuffer(JSContext* cx, JSObject* obj,
105 uint32_t count) {
106 if (cx->helperThread()) return cx->zone()->pod_malloc<T>(count);
107 size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
108 T* buffer = static_cast<T*>(cx->nursery().allocateBuffer(obj, nbytes));
109 if (!buffer) ReportOutOfMemory(cx);
110 return buffer;
111 }
112
113 // If this returns null then the old buffer will be left alone.
114 template <typename T>
ReallocateObjectBuffer(JSContext * cx,JSObject * obj,T * oldBuffer,uint32_t oldCount,uint32_t newCount)115 static inline T* ReallocateObjectBuffer(JSContext* cx, JSObject* obj,
116 T* oldBuffer, uint32_t oldCount,
117 uint32_t newCount) {
118 if (cx->helperThread())
119 return obj->zone()->pod_realloc<T>(oldBuffer, oldCount, newCount);
120 T* buffer = static_cast<T*>(cx->nursery().reallocateBuffer(
121 obj, oldBuffer, oldCount * sizeof(T), newCount * sizeof(T)));
122 if (!buffer) ReportOutOfMemory(cx);
123 return buffer;
124 }
125
126 static inline void EvictAllNurseries(
127 JSRuntime* rt, JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) {
128 rt->gc.evictNursery(reason);
129 }
130
131 } // namespace js
132
133 #endif /* gc_Nursery_inl_h */
134