1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2011
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
44 #ifdef MMGC_MARKSTACK_ALLOWANCE
45 GCMarkStack::GCMarkStack(int32_t allowance
)
47 GCMarkStack::GCMarkStack()
55 , m_extraSegment(NULL
)
57 #ifdef MMGC_MARKSTACK_ALLOWANCE
58 , m_allowance(allowance
> 0 ? allowance
: 2147483647)
60 #ifdef MMGC_MARKSTACK_DEPTH
65 GCAssert(Invariants());
68 GCMarkStack::~GCMarkStack()
70 while (m_topSegment
!= NULL
)
73 FreeStackSegment(m_extraSegment
);
76 void GCMarkStack::SetDeadItem(void* item
)
78 GCAssert(item
!= NULL
);
79 m_deadItem
= uintptr_t(item
);
82 bool GCMarkStack::Push_LargeExactObjectTail(const void* p
, size_t cursor
)
84 uintptr_t* top
= allocSpace(3);
87 top
[0] = (kLargeExactObjectTail
<< 2) | kFirstWord
;
88 top
[-1] = uintptr_t(p
) | kMiddleWord
;
89 top
[-2] = (uintptr_t(cursor
) << 2) | kLastWord
;
93 void GCMarkStack::Pop_LargeExactObjectTail(const void* &p
, size_t &cursor
)
95 GCAssert(PeekTypetag() == kLargeExactObjectTail
);
97 uintptr_t* top
= m_top
-1;
98 p
= (void*)(top
[-1] & ~3);
99 cursor
= size_t(top
[-2] & ~3) >> 2;
103 bool GCMarkStack::Push_StackMemory(const void* p
, uint32_t size
, const void* baseptr
)
105 uintptr_t* top
= allocSpace(4);
108 top
[0] = (kStackMemory
<< 2) | kFirstWord
;
109 top
[-1] = uintptr_t(p
) | kMiddleWord
;
110 top
[-2] = uintptr_t(size
) | kMiddleWord
;
111 top
[-3] = uintptr_t(baseptr
) | kLastWord
;
115 void GCMarkStack::Pop_StackMemory(const void* &p
, uint32_t &size
, const void* &baseptr
)
117 GCAssert(PeekTypetag() == kStackMemory
);
119 uintptr_t* top
= m_top
-1;
120 p
= (void*)(top
[-1] & ~3);
121 size
= uint32_t(top
[-2] & ~3);
122 baseptr
= (void*)(top
[-3] & ~3);
126 bool GCMarkStack::Push_LargeObjectChunk(const void* p
, uint32_t size
, const void* baseptr
)
128 uintptr_t* top
= allocSpace(4);
131 top
[0] = (kLargeObjectChunk
<< 2) | kFirstWord
;
132 top
[-1] = uintptr_t(p
) | kMiddleWord
;
133 top
[-2] = uintptr_t(size
) | kMiddleWord
;
134 top
[-3] = uintptr_t(baseptr
) | kLastWord
;
138 void GCMarkStack::Pop_LargeObjectChunk(const void* &p
, uint32_t &size
, const void* &baseptr
)
140 GCAssert(PeekTypetag() == kLargeObjectChunk
);
142 uintptr_t* top
= m_top
-1;
143 p
= (void*)(top
[-1] & ~3);
144 size
= uint32_t(top
[-2] & ~3);
145 baseptr
= (void*)(top
[-3] & ~3);
149 bool GCMarkStack::Push_LargeRootChunk(const void* p
, uint32_t size
, const void* baseptr
)
151 uintptr_t* top
= allocSpace(4);
154 top
[0] = (kLargeRootChunk
<< 2) | kFirstWord
;
155 top
[-1] = uintptr_t(p
) | kMiddleWord
;
156 top
[-2] = uintptr_t(size
) | kMiddleWord
;
157 top
[-3] = uintptr_t(baseptr
) | kLastWord
;
161 void GCMarkStack::Pop_LargeRootChunk(const void* &p
, uint32_t &size
, const void* &baseptr
)
163 GCAssert(PeekTypetag() == kLargeRootChunk
);
165 uintptr_t* top
= m_top
-1;
166 p
= (void*)(top
[-1] & ~3);
167 size
= uint32_t(top
[-2] & ~3);
168 baseptr
= (void*)(top
[-3] & ~3);
172 bool GCMarkStack::Push_LargeObjectProtector(const void* p
)
174 uintptr_t* top
= allocSpace(2);
177 top
[0] = (kLargeObjectProtector
<< 2) | kFirstWord
;
178 top
[-1] = uintptr_t(p
) | kLastWord
;
182 void GCMarkStack::Pop_LargeObjectProtector(const void* &p
)
184 GCAssert(PeekTypetag() == kLargeObjectProtector
);
186 uintptr_t* top
= m_top
-1;
187 p
= (void*)(top
[-1] & ~3);
191 bool GCMarkStack::Push_RootProtector(const void* p
)
193 uintptr_t* top
= allocSpace(2);
196 top
[0] = (kRootProtector
<< 2) | kFirstWord
;
197 top
[-1] = uintptr_t(p
) | kLastWord
;
201 void GCMarkStack::Pop_RootProtector(const void* &p
)
203 GCAssert(PeekTypetag() == kRootProtector
);
205 uintptr_t* top
= m_top
-1;
206 p
= (void*)(top
[-1] & ~3);
210 void GCMarkStack::ClearRootProtectorAndChunkAbove(uintptr_t index
, const void* rootptr
)
213 uintptr_t* top
= (uintptr_t*)index
;
214 GCAssert(top
[0] == ((kRootProtector
<< 2) | kFirstWord
));
215 GCAssert((void*)(top
[-1] & ~3) == rootptr
);
220 uintptr_t* itemptr
= (uintptr_t*)index
;
221 while ((itemptr
= GetNextItemAbove(itemptr
)) != NULL
) {
222 if (*itemptr
== ((kLargeRootChunk
<< 2) | kFirstWord
)) {
223 const void* baseptr
= (void*)(itemptr
[-3] & ~3);
224 if (baseptr
== rootptr
) {
225 ClearItemAt((uintptr_t)itemptr
);
232 void GCMarkStack::Clear()
234 // Clear out the elements
235 while (m_topSegment
->m_prev
!= NULL
)
239 // Discard the cached segment
240 if (m_extraSegment
!= NULL
) {
241 FreeStackSegment(m_extraSegment
);
242 m_extraSegment
= NULL
;
244 GCAssert(Invariants());
247 inline uintptr_t* GCMarkStack::items(GCMarkStack::StackSegment
* seg
)
249 GCAssert(sizeof(StackSegment
) % sizeof(uintptr_t) == 0);
250 return (uintptr_t*)seg
+ sizeof(StackSegment
)/sizeof(uintptr_t);
253 inline uintptr_t* GCMarkStack::limit(GCMarkStack::StackSegment
* seg
)
255 static const size_t kMarkStackItems
= (GCHeap::kBlockSize
- sizeof(StackSegment
)) / sizeof(uintptr_t);
257 return items(seg
) + kMarkStackItems
- 2; // Two sentinel words at the end
259 return items(seg
) + kMarkStackItems
;
263 inline GCMarkStack::StackSegment::StackSegment()
268 sentinel1
= 0; // catch overwrites
269 sentinel2
= 0; // as soon as they happen
270 limit(this)[0] = 0; // also
271 limit(this)[1] = 0; // at the end
275 bool GCMarkStack::PopulateExtraSegment(bool mustSucceed
)
277 if (m_extraSegment
== NULL
) {
278 void *memory
= AllocStackSegment(mustSucceed
);
281 m_extraSegment
= new (memory
) StackSegment();
286 bool GCMarkStack::PushSegment(bool mustSucceed
)
288 GCAssert(sizeof(StackSegment
) <= GCHeap::kBlockSize
);
289 if (!PopulateExtraSegment(mustSucceed
))
291 if (m_topSegment
!= NULL
) {
293 m_hiddenCount
+= uint32_t(m_top
- m_base
);
294 m_topSegment
->m_savedTop
= m_top
;
296 StackSegment
* seg
= m_extraSegment
;
297 m_extraSegment
= NULL
;
298 seg
->m_prev
= m_topSegment
;
300 m_base
= items(m_topSegment
);
301 m_limit
= limit(m_topSegment
);
306 void GCMarkStack::PopSegment_UnlessLast()
308 if (m_topSegment
->m_prev
!= NULL
)
312 void GCMarkStack::PopSegment()
314 StackSegment
* seg
= m_topSegment
;
315 m_topSegment
= seg
->m_prev
;
317 if (m_topSegment
!= NULL
) {
318 m_base
= items(m_topSegment
);
319 m_limit
= limit(m_topSegment
);
320 m_top
= m_topSegment
->m_savedTop
;
321 m_hiddenCount
-= uint32_t(m_top
- m_base
);
323 m_topSegment
->m_savedTop
= NULL
;
326 m_base
= m_top
= m_limit
= NULL
;
327 GCAssert(m_hiddenCount
== 0);
328 GCAssert(m_hiddenSegments
== 0);
331 if (m_extraSegment
== NULL
) {
333 m_extraSegment
= seg
;
336 FreeStackSegment(seg
);
339 GCMarkStack::StackSegment
* GCMarkStack::FindLastSegment(StackSegment
* first
)
341 while (first
->m_prev
!= NULL
)
342 first
= first
->m_prev
;
346 #ifdef MMGC_MARKSTACK_ALLOWANCE
347 bool GCMarkStack::MakeSpaceForSegments(int32_t nseg
)
349 // If we have no space, and aren't going to pop a segment that's empty
350 // before we return, and there's not an extra segment to give up, then
352 if (m_allowance
>= nseg
)
354 if (m_allowance
+1 == nseg
&& m_top
== m_base
)
356 if (m_allowance
+1 == nseg
&& m_extraSegment
!= NULL
) {
357 FreeStackSegment(m_extraSegment
);
358 m_extraSegment
= NULL
;
365 void GCMarkStack::TransferOneInactiveSegmentFrom(GCMarkStack
& other
)
367 GCAssert(other
.InactiveSegments() > 0);
369 #ifdef MMGC_MARKSTACK_ALLOWANCE
370 if (!MakeSpaceForSegments(1))
374 // Pick off the one below the top always.
376 StackSegment
* subject
= other
.m_topSegment
->m_prev
;
377 uint32_t subject_hc
= uint32_t(subject
->m_savedTop
- items(subject
));
379 other
.m_topSegment
->m_prev
= subject
->m_prev
;
380 other
.m_hiddenCount
-= subject_hc
;
381 other
.m_hiddenSegments
--;
382 #ifdef MMGC_MARKSTACK_ALLOWANCE
386 FindLastSegment(m_topSegment
)->m_prev
= subject
;
387 subject
->m_prev
= NULL
;
389 m_hiddenCount
+= subject_hc
;
391 #ifdef MMGC_MARKSTACK_ALLOWANCE
392 // The allowance may temporarily go negative until we pop a segment below.
396 // If a segment was inserted into an empty stack then pop the now empty top segment.
400 GCAssert(Invariants());
401 GCAssert(other
.Invariants());
404 // Move all the segments from the other stack into the bottom of
407 bool GCMarkStack::TransferEverythingFrom(GCMarkStack
& other
)
412 uint32_t incoming_segments
= other
.m_hiddenSegments
+ 1;
414 #ifdef MMGC_MARKSTACK_ALLOWANCE
415 if (!MakeSpaceForSegments(incoming_segments
))
419 // Make sure we have a segment in the other stack with which to reestablish invariants.
420 #ifdef MMGC_MARKSTACK_ALLOWANCE
421 // Optimistically assume we'll succeed so that there will be an allowance for the other
422 // stack to allocate in.
423 other
.m_allowance
+= incoming_segments
;
424 if (!other
.PopulateExtraSegment(false)) {
425 other
.m_allowance
-= incoming_segments
; // Won't be moving anything after all
429 if (!other
.PopulateExtraSegment(false))
433 // Seal the other stack and obtain its data
434 other
.m_topSegment
->m_savedTop
= other
.m_top
;
435 StackSegment
* subject
= other
.m_topSegment
;
436 uint32_t subject_hc
= other
.m_hiddenCount
+ uint32_t(other
.m_topSegment
->m_savedTop
- items(other
.m_topSegment
));
437 uint32_t subject_hs
= incoming_segments
;
439 // Reestablish the null state in the other stack
440 GCAssert(other
.m_extraSegment
!= NULL
);
441 other
.m_topSegment
= other
.m_extraSegment
;
442 other
.m_extraSegment
= NULL
;
443 other
.m_hiddenCount
= 0;
444 other
.m_hiddenSegments
= 0;
445 other
.m_base
= items(other
.m_topSegment
);
446 other
.m_limit
= limit(other
.m_topSegment
);
447 other
.m_top
= other
.m_base
;
449 // Link the segments into our stack and update our state
450 FindLastSegment(m_topSegment
)->m_prev
= subject
;
451 m_hiddenCount
+= subject_hc
;
452 m_hiddenSegments
+= subject_hs
;
453 #ifdef MMGC_MARKSTACK_ALLOWANCE
454 // The allowance may temporarily go negative until we pop a segment below.
455 m_allowance
-= incoming_segments
;
458 // If the segments were inserted into an empty stack then pop the now empty top segment.
462 GCAssert(Invariants());
463 GCAssert(other
.Invariants());
468 uintptr_t* GCMarkStack::GetNextItemAbove(uintptr_t* item
)
473 m_topSegment
->m_savedTop
= m_top
;
474 StackSegment
* seg
= m_topSegment
;
475 StackSegment
* prev
= NULL
;
476 while (seg
!= NULL
) {
477 if(item
>= items(seg
) && item
< seg
->m_savedTop
) {
478 // If the current item is the top item in the segment then the item
479 // above is in the segment above, otherwise it's in this segment
480 uintptr_t* p
= (item
== seg
->m_savedTop
-1) ? items(prev
) : item
+1;
482 // Scan forward in the item to find the top word.
483 while ((*p
& 1) != 0)
493 // About overwriting dead items with a distinguished non-NULL GC object pointer:
495 // It's not strictly necessary to do it this way. The two alternatives are to use
496 // a distinguished type of stack item (kDeadItem), or using a NULL pointer. A kDeadItem
497 // type could be used at zero cost, dead items would be handled on the slow path.
498 // However a kDeadItem would always be at least two words long and could not be used
499 // to clear out a GCObject. Also, a kDeadItem would be variable-length, complicating
500 // code that parses stack items. A NULL GCObject pointer is possible as the current
501 // marker architecture would just divert those items onto the slow path at zero cost.
502 // However, the invariant that there are no NULL pointers on the stack is worth more
505 void GCMarkStack::ClearItemAt(uintptr_t index
)
507 GCAssert(m_deadItem
!= 0);
508 uintptr_t* p
= (uintptr_t*)index
;
512 switch (TypeTag(w
>> 2)) {
514 GCAssert(!"Unknown TypeTag in ClearItemAt");
517 case kLargeObjectChunk
:
518 case kLargeRootChunk
:
520 case kLargeExactObjectTail
:
523 case kLargeObjectProtector
:
527 GCAssert(Invariants());
530 inline void* GCMarkStack::AllocStackSegment(bool mustSucceed
)
532 #ifdef MMGC_MARKSTACK_ALLOWANCE
533 if (m_allowance
== 0) {
534 // This is fine, mustSucceed is only used for the first segment in
535 // a stack, and the allowance is per-stack.
536 GCAssert(!mustSucceed
);
542 return GCHeap::GetGCHeap()->Alloc(1, GCHeap::flags_Alloc
);
544 return GCHeap::GetGCHeap()->AllocNoOOM(1, GCHeap::flags_Alloc
| GCHeap::kCanFail
);
547 inline void GCMarkStack::FreeStackSegment(void* p
)
549 #ifdef MMGC_MARKSTACK_ALLOWANCE
552 GCHeap::GetGCHeap()->FreeNoOOM(p
);
556 bool GCMarkStack::Invariants()
558 #ifdef MMGC_MARKSTACK_ALLOWANCE
559 GCAssert(m_allowance
>= 0);
561 GCAssert(m_topSegment
->sentinel1
== 0);
562 GCAssert(m_topSegment
->sentinel2
== 0);
563 GCAssert(limit(m_topSegment
)[0] == 0);
564 GCAssert(limit(m_topSegment
)[1] == 0);
565 GCAssert(limit(m_topSegment
) == m_limit
);
566 GCAssert(m_top
>= m_base
);
567 GCAssert(m_top
<= m_limit
);
568 GCAssert(m_topSegment
->m_prev
== NULL
|| m_top
> m_base
);
571 for ( StackSegment
* seg
=m_topSegment
->m_prev
; seg
!= NULL
; seg
= seg
->m_prev
) {
572 GCAssert(seg
->sentinel1
== 0);
573 GCAssert(seg
->sentinel2
== 0);
574 GCAssert(limit(seg
)[0] == 0);
575 GCAssert(limit(seg
)[1] == 0);
576 hc
+= uint32_t(seg
->m_savedTop
- items(seg
));
579 GCAssert(ns
== InactiveSegments());
580 GCAssert(hc
== m_hiddenCount
);
581 GCAssert(Count() == hc
+ (m_top
- m_base
));