|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef js_HeapAPI_h |
|
8 #define js_HeapAPI_h |
|
9 |
|
10 #include <limits.h> |
|
11 |
|
12 #include "jspubtd.h" |
|
13 |
|
14 #include "js/Utility.h" |
|
15 |
|
16 /* These values are private to the JS engine. */ |
|
17 namespace js { |
|
18 |
|
19 // Whether the current thread is permitted access to any part of the specified |
|
20 // runtime or zone. |
|
21 JS_FRIEND_API(bool) |
|
22 CurrentThreadCanAccessRuntime(JSRuntime *rt); |
|
23 |
|
24 JS_FRIEND_API(bool) |
|
25 CurrentThreadCanAccessZone(JS::Zone *zone); |
|
26 |
|
27 namespace gc { |
|
28 |
|
29 struct Cell; |
|
30 |
|
31 const size_t ArenaShift = 12; |
|
32 const size_t ArenaSize = size_t(1) << ArenaShift; |
|
33 const size_t ArenaMask = ArenaSize - 1; |
|
34 |
|
35 const size_t ChunkShift = 20; |
|
36 const size_t ChunkSize = size_t(1) << ChunkShift; |
|
37 const size_t ChunkMask = ChunkSize - 1; |
|
38 |
|
39 const size_t CellShift = 3; |
|
40 const size_t CellSize = size_t(1) << CellShift; |
|
41 const size_t CellMask = CellSize - 1; |
|
42 |
|
43 /* These are magic constants derived from actual offsets in gc/Heap.h. */ |
|
44 const size_t ChunkMarkBitmapOffset = 1032360; |
|
45 const size_t ChunkMarkBitmapBits = 129024; |
|
46 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); |
|
47 const size_t ChunkLocationOffset = ChunkSize - sizeof(void*) - sizeof(uintptr_t); |
|
48 |
|
49 /* |
|
50 * Live objects are marked black. How many other additional colors are available |
|
51 * depends on the size of the GCThing. Objects marked gray are eligible for |
|
52 * cycle collection. |
|
53 */ |
|
54 static const uint32_t BLACK = 0; |
|
55 static const uint32_t GRAY = 1; |
|
56 |
|
57 /* |
|
58 * Constants used to indicate whether a chunk is part of the tenured heap or the |
|
59 * nusery. |
|
60 */ |
|
61 const uintptr_t ChunkLocationNursery = 0; |
|
62 const uintptr_t ChunkLocationTenuredHeap = 1; |
|
63 |
|
64 } /* namespace gc */ |
|
65 } /* namespace js */ |
|
66 |
|
67 namespace JS { |
|
68 struct Zone; |
|
69 } /* namespace JS */ |
|
70 |
|
71 namespace JS { |
|
72 namespace shadow { |
|
73 |
|
74 struct ArenaHeader |
|
75 { |
|
76 JS::Zone *zone; |
|
77 }; |
|
78 |
|
79 struct Zone |
|
80 { |
|
81 protected: |
|
82 JSRuntime *const runtime_; |
|
83 JSTracer *const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|. |
|
84 |
|
85 public: |
|
86 bool needsBarrier_; |
|
87 |
|
88 Zone(JSRuntime *runtime, JSTracer *barrierTracerArg) |
|
89 : runtime_(runtime), |
|
90 barrierTracer_(barrierTracerArg), |
|
91 needsBarrier_(false) |
|
92 {} |
|
93 |
|
94 bool needsBarrier() const { |
|
95 return needsBarrier_; |
|
96 } |
|
97 |
|
98 JSTracer *barrierTracer() { |
|
99 MOZ_ASSERT(needsBarrier_); |
|
100 MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); |
|
101 return barrierTracer_; |
|
102 } |
|
103 |
|
104 JSRuntime *runtimeFromMainThread() const { |
|
105 MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); |
|
106 return runtime_; |
|
107 } |
|
108 |
|
109 // Note: Unrestricted access to the zone's runtime from an arbitrary |
|
110 // thread can easily lead to races. Use this method very carefully. |
|
111 JSRuntime *runtimeFromAnyThread() const { |
|
112 return runtime_; |
|
113 } |
|
114 |
|
115 static JS::shadow::Zone *asShadowZone(JS::Zone *zone) { |
|
116 return reinterpret_cast<JS::shadow::Zone*>(zone); |
|
117 } |
|
118 }; |
|
119 |
|
120 } /* namespace shadow */ |
|
121 } /* namespace JS */ |
|
122 |
|
123 namespace js { |
|
124 namespace gc { |
|
125 |
|
126 static MOZ_ALWAYS_INLINE uintptr_t * |
|
127 GetGCThingMarkBitmap(const void *thing) |
|
128 { |
|
129 MOZ_ASSERT(thing); |
|
130 uintptr_t addr = uintptr_t(thing); |
|
131 addr &= ~js::gc::ChunkMask; |
|
132 addr |= js::gc::ChunkMarkBitmapOffset; |
|
133 return reinterpret_cast<uintptr_t *>(addr); |
|
134 } |
|
135 |
|
136 static MOZ_ALWAYS_INLINE JS::shadow::Runtime * |
|
137 GetGCThingRuntime(const void *thing) |
|
138 { |
|
139 MOZ_ASSERT(thing); |
|
140 uintptr_t addr = uintptr_t(thing); |
|
141 addr &= ~js::gc::ChunkMask; |
|
142 addr |= js::gc::ChunkRuntimeOffset; |
|
143 return *reinterpret_cast<JS::shadow::Runtime **>(addr); |
|
144 } |
|
145 |
|
146 static MOZ_ALWAYS_INLINE void |
|
147 GetGCThingMarkWordAndMask(const void *thing, uint32_t color, |
|
148 uintptr_t **wordp, uintptr_t *maskp) |
|
149 { |
|
150 uintptr_t addr = uintptr_t(thing); |
|
151 size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color; |
|
152 MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits); |
|
153 uintptr_t *bitmap = GetGCThingMarkBitmap(thing); |
|
154 const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT; |
|
155 *maskp = uintptr_t(1) << (bit % nbits); |
|
156 *wordp = &bitmap[bit / nbits]; |
|
157 } |
|
158 |
|
159 static MOZ_ALWAYS_INLINE JS::shadow::ArenaHeader * |
|
160 GetGCThingArena(void *thing) |
|
161 { |
|
162 uintptr_t addr = uintptr_t(thing); |
|
163 addr &= ~js::gc::ArenaMask; |
|
164 return reinterpret_cast<JS::shadow::ArenaHeader *>(addr); |
|
165 } |
|
166 |
|
167 MOZ_ALWAYS_INLINE bool |
|
168 IsInsideNursery(const JS::shadow::Runtime *runtime, const void *p) |
|
169 { |
|
170 #ifdef JSGC_GENERATIONAL |
|
171 return uintptr_t(p) >= runtime->gcNurseryStart_ && uintptr_t(p) < runtime->gcNurseryEnd_; |
|
172 #else |
|
173 return false; |
|
174 #endif |
|
175 } |
|
176 |
|
177 MOZ_ALWAYS_INLINE bool |
|
178 IsInsideNursery(const js::gc::Cell *cell) |
|
179 { |
|
180 #ifdef JSGC_GENERATIONAL |
|
181 if (!cell) |
|
182 return false; |
|
183 uintptr_t addr = uintptr_t(cell); |
|
184 addr &= ~js::gc::ChunkMask; |
|
185 addr |= js::gc::ChunkLocationOffset; |
|
186 uint32_t location = *reinterpret_cast<uint32_t *>(addr); |
|
187 JS_ASSERT(location == gc::ChunkLocationNursery || |
|
188 location == gc::ChunkLocationTenuredHeap); |
|
189 return location == gc::ChunkLocationNursery; |
|
190 #else |
|
191 return false; |
|
192 #endif |
|
193 } |
|
194 |
|
195 } /* namespace gc */ |
|
196 |
|
197 } /* namespace js */ |
|
198 |
|
199 namespace JS { |
|
200 |
|
201 static MOZ_ALWAYS_INLINE Zone * |
|
202 GetGCThingZone(void *thing) |
|
203 { |
|
204 MOZ_ASSERT(thing); |
|
205 return js::gc::GetGCThingArena(thing)->zone; |
|
206 } |
|
207 |
|
208 static MOZ_ALWAYS_INLINE Zone * |
|
209 GetObjectZone(JSObject *obj) |
|
210 { |
|
211 return GetGCThingZone(obj); |
|
212 } |
|
213 |
|
214 static MOZ_ALWAYS_INLINE bool |
|
215 GCThingIsMarkedGray(void *thing) |
|
216 { |
|
217 #ifdef JSGC_GENERATIONAL |
|
218 /* |
|
219 * GC things residing in the nursery cannot be gray: they have no mark bits. |
|
220 * All live objects in the nursery are moved to tenured at the beginning of |
|
221 * each GC slice, so the gray marker never sees nursery things. |
|
222 */ |
|
223 JS::shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing); |
|
224 if (js::gc::IsInsideNursery(rt, thing)) |
|
225 return false; |
|
226 #endif |
|
227 uintptr_t *word, mask; |
|
228 js::gc::GetGCThingMarkWordAndMask(thing, js::gc::GRAY, &word, &mask); |
|
229 return *word & mask; |
|
230 } |
|
231 |
|
232 static MOZ_ALWAYS_INLINE bool |
|
233 IsIncrementalBarrierNeededOnGCThing(shadow::Runtime *rt, void *thing, JSGCTraceKind kind) |
|
234 { |
|
235 if (!rt->needsBarrier_) |
|
236 return false; |
|
237 JS::Zone *zone = GetGCThingZone(thing); |
|
238 return reinterpret_cast<shadow::Zone *>(zone)->needsBarrier_; |
|
239 } |
|
240 |
|
241 } /* namespace JS */ |
|
242 |
|
243 #endif /* js_HeapAPI_h */ |