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