2 * sgen-los.c: Large objects space.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
14 * Copyright 2001-2003 Ximian, Inc
15 * Copyright 2003-2010 Novell, Inc.
16 * Copyright (C) 2012 Xamarin Inc
18 * This library is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU Library General Public
20 * License 2.0 as published by the Free Software Foundation;
22 * This library is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * Library General Public License for more details.
27 * You should have received a copy of the GNU Library General Public
28 * License 2.0 along with this library; if not, write to the Free
29 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38 #include "mono/sgen/sgen-gc.h"
39 #include "mono/sgen/sgen-protocol.h"
40 #include "mono/sgen/sgen-cardtable.h"
41 #include "mono/sgen/sgen-memory-governor.h"
42 #include "mono/sgen/sgen-client.h"
44 #define LOS_SECTION_SIZE (1024 * 1024)
47 * This shouldn't be much smaller or larger than MAX_SMALL_OBJ_SIZE.
48 * Must be at least sizeof (LOSSection).
50 #define LOS_CHUNK_SIZE 4096
51 #define LOS_CHUNK_BITS 12
53 /* Largest object that can be allocated in a section. */
54 #define LOS_SECTION_OBJECT_LIMIT (LOS_SECTION_SIZE - LOS_CHUNK_SIZE - sizeof (LOSObject))
55 //#define LOS_SECTION_OBJECT_LIMIT 0
56 #define LOS_SECTION_NUM_CHUNKS ((LOS_SECTION_SIZE >> LOS_CHUNK_BITS) - 1)
58 #define LOS_SECTION_FOR_OBJ(obj) ((LOSSection*)((mword)(obj) & ~(mword)(LOS_SECTION_SIZE - 1)))
59 #define LOS_CHUNK_INDEX(obj,section) (((char*)(obj) - (char*)(section)) >> LOS_CHUNK_BITS)
61 #define LOS_NUM_FAST_SIZES 32
63 typedef struct _LOSFreeChunks LOSFreeChunks
;
64 struct _LOSFreeChunks
{
65 LOSFreeChunks
*next_size
;
69 typedef struct _LOSSection LOSSection
;
72 size_t num_free_chunks
;
73 unsigned char *free_chunk_map
;
76 LOSObject
*los_object_list
= NULL
;
77 mword los_memory_usage
= 0;
79 static LOSSection
*los_sections
= NULL
;
80 static LOSFreeChunks
*los_fast_free_lists
[LOS_NUM_FAST_SIZES
]; /* 0 is for larger sizes */
81 static mword los_num_objects
= 0;
82 static int los_num_sections
= 0;
85 //#define LOS_CONSISTENCY_CHECK
89 #define LOS_SEGMENT_SIZE (4096 * 1024)
91 static char *los_segment
= NULL
;
92 static int los_segment_index
= 0;
96 sgen_los_object_size (LOSObject
*obj
)
98 return obj
->size
& ~1L;
101 #ifdef LOS_CONSISTENCY_CHECK
103 los_consistency_check (void)
108 mword memory_usage
= 0;
110 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
111 mword obj_size
= sgen_los_object_size (obj
);
112 char *end
= obj
->data
+ obj_size
;
113 int start_index
, num_chunks
;
115 memory_usage
+= obj_size
;
117 if (obj_size
> LOS_SECTION_OBJECT_LIMIT
)
120 section
= LOS_SECTION_FOR_OBJ (obj
);
122 g_assert (end
<= (char*)section
+ LOS_SECTION_SIZE
);
124 start_index
= LOS_CHUNK_INDEX (obj
, section
);
125 num_chunks
= (obj_size
+ sizeof (LOSObject
) + LOS_CHUNK_SIZE
- 1) >> LOS_CHUNK_BITS
;
126 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
)
127 g_assert (!section
->free_chunk_map
[i
]);
130 for (i
= 0; i
< LOS_NUM_FAST_SIZES
; ++i
) {
131 LOSFreeChunks
*size_chunks
;
132 for (size_chunks
= los_fast_free_lists
[i
]; size_chunks
; size_chunks
= size_chunks
->next_size
) {
133 LOSSection
*section
= LOS_SECTION_FOR_OBJ (size_chunks
);
134 int j
, num_chunks
, start_index
;
137 g_assert (size_chunks
->size
>= LOS_NUM_FAST_SIZES
* LOS_CHUNK_SIZE
);
139 g_assert (size_chunks
->size
== i
* LOS_CHUNK_SIZE
);
141 num_chunks
= size_chunks
->size
>> LOS_CHUNK_BITS
;
142 start_index
= LOS_CHUNK_INDEX (size_chunks
, section
);
143 for (j
= start_index
; j
< start_index
+ num_chunks
; ++j
)
144 g_assert (section
->free_chunk_map
[j
]);
148 g_assert (los_memory_usage
== memory_usage
);
153 add_free_chunk (LOSFreeChunks
*free_chunks
, size_t size
)
155 size_t num_chunks
= size
>> LOS_CHUNK_BITS
;
157 free_chunks
->size
= size
;
159 if (num_chunks
>= LOS_NUM_FAST_SIZES
)
161 free_chunks
->next_size
= los_fast_free_lists
[num_chunks
];
162 los_fast_free_lists
[num_chunks
] = free_chunks
;
165 static LOSFreeChunks
*
166 get_from_size_list (LOSFreeChunks
**list
, size_t size
)
168 LOSFreeChunks
*free_chunks
= NULL
;
170 size_t i
, num_chunks
, start_index
;
173 g_assert ((size
& (LOS_CHUNK_SIZE
- 1)) == 0);
177 if (free_chunks
->size
>= size
)
179 list
= &(*list
)->next_size
;
185 *list
= free_chunks
->next_size
;
187 if (free_chunks
->size
> size
)
188 add_free_chunk ((LOSFreeChunks
*)((char*)free_chunks
+ size
), free_chunks
->size
- size
);
190 num_chunks
= size
>> LOS_CHUNK_BITS
;
192 section
= LOS_SECTION_FOR_OBJ (free_chunks
);
194 start_index
= LOS_CHUNK_INDEX (free_chunks
, section
);
195 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
) {
196 g_assert (section
->free_chunk_map
[i
]);
197 section
->free_chunk_map
[i
] = 0;
200 section
->num_free_chunks
-= size
>> LOS_CHUNK_BITS
;
201 g_assert (section
->num_free_chunks
>= 0);
207 randomize_los_object_start (gpointer addr
, size_t obj_size
, size_t alloced_size
, size_t addr_alignment
)
210 if (alloced_size
!= obj_size
) {
212 * We want to get a random offset between 0 and (alloced_size - obj_size)
213 * We do a prime multiplication to avoid usage of functions which might not
214 * be thread/signal safe (like rand ()). We subtract 1 to avoid common
215 * power by 2 factors.
217 offset
= SGEN_ALIGN_DOWN ((((size_t)addr
- 1) * 2654435761u) % (alloced_size
- obj_size
));
219 SGEN_ASSERT (0, (alloced_size
- obj_size
) < addr_alignment
, "Why are we wasting one entire chunk for a los object ?");
220 /* Randomize the location within the reserved chunks to improve cache performance */
221 return (LOSObject
*)((guint8
*)addr
+ offset
);
226 get_los_section_memory (size_t size
)
229 LOSFreeChunks
*free_chunks
;
231 size_t obj_size
= size
;
233 size
= SGEN_ALIGN_UP_TO (size
, LOS_CHUNK_SIZE
);
235 num_chunks
= size
>> LOS_CHUNK_BITS
;
237 g_assert (size
> 0 && size
- sizeof (LOSObject
) <= LOS_SECTION_OBJECT_LIMIT
);
238 g_assert (num_chunks
> 0);
241 if (num_chunks
>= LOS_NUM_FAST_SIZES
) {
242 free_chunks
= get_from_size_list (&los_fast_free_lists
[0], size
);
245 for (i
= num_chunks
; i
< LOS_NUM_FAST_SIZES
; ++i
) {
246 free_chunks
= get_from_size_list (&los_fast_free_lists
[i
], size
);
251 free_chunks
= get_from_size_list (&los_fast_free_lists
[0], size
);
255 return randomize_los_object_start (free_chunks
, obj_size
, size
, LOS_CHUNK_SIZE
);
258 if (!sgen_memgov_try_alloc_space (LOS_SECTION_SIZE
, SPACE_LOS
))
261 section
= (LOSSection
*)sgen_alloc_os_memory_aligned (LOS_SECTION_SIZE
, LOS_SECTION_SIZE
, (SgenAllocFlags
)(SGEN_ALLOC_HEAP
| SGEN_ALLOC_ACTIVATE
), NULL
);
266 free_chunks
= (LOSFreeChunks
*)((char*)section
+ LOS_CHUNK_SIZE
);
267 free_chunks
->size
= LOS_SECTION_SIZE
- LOS_CHUNK_SIZE
;
268 free_chunks
->next_size
= los_fast_free_lists
[0];
269 los_fast_free_lists
[0] = free_chunks
;
271 section
->num_free_chunks
= LOS_SECTION_NUM_CHUNKS
;
273 section
->free_chunk_map
= (unsigned char*)section
+ sizeof (LOSSection
);
274 g_assert (sizeof (LOSSection
) + LOS_SECTION_NUM_CHUNKS
+ 1 <= LOS_CHUNK_SIZE
);
275 section
->free_chunk_map
[0] = 0;
276 memset (section
->free_chunk_map
+ 1, 1, LOS_SECTION_NUM_CHUNKS
);
278 section
->next
= los_sections
;
279 los_sections
= section
;
287 free_los_section_memory (LOSObject
*obj
, size_t size
)
289 LOSSection
*section
= LOS_SECTION_FOR_OBJ (obj
);
290 size_t num_chunks
, i
, start_index
;
292 size
= SGEN_ALIGN_UP_TO (size
, LOS_CHUNK_SIZE
);
294 num_chunks
= size
>> LOS_CHUNK_BITS
;
296 g_assert (size
> 0 && size
- sizeof (LOSObject
) <= LOS_SECTION_OBJECT_LIMIT
);
297 g_assert (num_chunks
> 0);
299 section
->num_free_chunks
+= num_chunks
;
300 g_assert (section
->num_free_chunks
<= LOS_SECTION_NUM_CHUNKS
);
303 * We could free the LOS section here if it's empty, but we
304 * can't unless we also remove its free chunks from the fast
305 * free lists. Instead, we do it in los_sweep().
308 start_index
= LOS_CHUNK_INDEX (obj
, section
);
309 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
) {
310 g_assert (!section
->free_chunk_map
[i
]);
311 section
->free_chunk_map
[i
] = 1;
314 add_free_chunk ((LOSFreeChunks
*)SGEN_ALIGN_DOWN_TO ((mword
)obj
, LOS_CHUNK_SIZE
), size
);
318 sgen_los_free_object (LOSObject
*obj
)
320 SGEN_ASSERT (0, !obj
->cardtable_mod_union
, "We should never free a LOS object with a mod-union table.");
323 mword size
= sgen_los_object_size (obj
);
324 SGEN_LOG (4, "Freed large object %p, size %lu", obj
->data
, (unsigned long)size
);
325 binary_protocol_empty (obj
->data
, size
);
327 los_memory_usage
-= size
;
333 if (size
> LOS_SECTION_OBJECT_LIMIT
) {
334 int pagesize
= mono_pagesize ();
335 size
+= sizeof (LOSObject
);
336 size
= SGEN_ALIGN_UP_TO (size
, pagesize
);
337 sgen_free_os_memory ((gpointer
)SGEN_ALIGN_DOWN_TO ((mword
)obj
, pagesize
), size
, SGEN_ALLOC_HEAP
);
338 sgen_memgov_release_space (size
, SPACE_LOS
);
340 free_los_section_memory (obj
, size
+ sizeof (LOSObject
));
341 #ifdef LOS_CONSISTENCY_CHECKS
342 los_consistency_check ();
350 * Objects with size >= MAX_SMALL_SIZE are allocated in the large object space.
351 * They are currently kept track of with a linked list.
352 * They don't move, so there is no need to pin them during collection
353 * and we avoid the memcpy overhead.
356 sgen_los_alloc_large_inner (GCVTable vtable
, size_t size
)
358 LOSObject
*obj
= NULL
;
361 g_assert (size
> SGEN_MAX_SMALL_OBJ_SIZE
);
362 g_assert ((size
& 1) == 0);
365 * size + sizeof (LOSObject) <= SSIZE_MAX - (mono_pagesize () - 1)
369 * size <= SSIZE_MAX - (mono_pagesize () - 1) - sizeof (LOSObject)
371 if (size
> SSIZE_MAX
- (mono_pagesize () - 1) - sizeof (LOSObject
))
376 los_segment
= sgen_alloc_os_memory (LOS_SEGMENT_SIZE
, SGEN_ALLOC_HEAP
| SGEN_ALLOC_ACTIVATE
, NULL
);
377 los_segment_index
= ALIGN_UP (los_segment_index
);
379 obj
= (LOSObject
*)(los_segment
+ los_segment_index
);
380 los_segment_index
+= size
+ sizeof (LOSObject
);
381 g_assert (los_segment_index
<= LOS_SEGMENT_SIZE
);
383 sgen_ensure_free_space (size
);
386 obj
= malloc (size
+ sizeof (LOSObject
));
387 memset (obj
, 0, size
+ sizeof (LOSObject
));
389 if (size
> LOS_SECTION_OBJECT_LIMIT
) {
390 size_t obj_size
= size
+ sizeof (LOSObject
);
391 int pagesize
= mono_pagesize ();
392 size_t alloc_size
= SGEN_ALIGN_UP_TO (obj_size
, pagesize
);
393 if (sgen_memgov_try_alloc_space (alloc_size
, SPACE_LOS
)) {
394 obj
= (LOSObject
*)sgen_alloc_os_memory (alloc_size
, (SgenAllocFlags
)(SGEN_ALLOC_HEAP
| SGEN_ALLOC_ACTIVATE
), NULL
);
396 obj
= randomize_los_object_start (obj
, obj_size
, alloc_size
, pagesize
);
399 obj
= get_los_section_memory (size
+ sizeof (LOSObject
));
401 memset (obj
, 0, size
+ sizeof (LOSObject
));
407 g_assert (!((mword
)obj
->data
& (SGEN_ALLOC_ALIGN
- 1)));
409 vtslot
= (void**)obj
->data
;
411 sgen_update_heap_boundaries ((mword
)obj
->data
, (mword
)obj
->data
+ size
);
412 obj
->next
= los_object_list
;
413 los_object_list
= obj
;
414 los_memory_usage
+= size
;
416 SGEN_LOG (4, "Allocated large object %p, vtable: %p (%s), size: %zd", obj
->data
, vtable
, sgen_client_vtable_get_name (vtable
), size
);
417 binary_protocol_alloc (obj
->data
, vtable
, size
, sgen_client_get_provenance ());
419 #ifdef LOS_CONSISTENCY_CHECK
420 los_consistency_check ();
426 static void sgen_los_unpin_object (GCObject
*data
);
429 sgen_los_sweep (void)
431 LOSObject
*bigobj
, *prevbo
;
432 LOSSection
*section
, *prev
;
434 int num_sections
= 0;
436 /* sweep the big objects list */
438 for (bigobj
= los_object_list
; bigobj
;) {
439 SGEN_ASSERT (0, !SGEN_OBJECT_IS_PINNED (bigobj
->data
), "Who pinned a LOS object?");
441 if (bigobj
->cardtable_mod_union
) {
442 sgen_card_table_free_mod_union (bigobj
->cardtable_mod_union
, (char*)bigobj
->data
, sgen_los_object_size (bigobj
));
443 bigobj
->cardtable_mod_union
= NULL
;
446 if (sgen_los_object_is_pinned (bigobj
->data
)) {
447 sgen_los_unpin_object (bigobj
->data
);
448 sgen_update_heap_boundaries ((mword
)bigobj
->data
, (mword
)bigobj
->data
+ sgen_los_object_size (bigobj
));
451 /* not referenced anywhere, so we can free it */
453 prevbo
->next
= bigobj
->next
;
455 los_object_list
= bigobj
->next
;
457 bigobj
= bigobj
->next
;
458 sgen_los_free_object (to_free
);
462 bigobj
= bigobj
->next
;
465 /* Try to free memory */
466 for (i
= 0; i
< LOS_NUM_FAST_SIZES
; ++i
)
467 los_fast_free_lists
[i
] = NULL
;
470 section
= los_sections
;
472 if (section
->num_free_chunks
== LOS_SECTION_NUM_CHUNKS
) {
473 LOSSection
*next
= section
->next
;
478 sgen_free_os_memory (section
, LOS_SECTION_SIZE
, SGEN_ALLOC_HEAP
);
479 sgen_memgov_release_space (LOS_SECTION_SIZE
, SPACE_LOS
);
485 for (i
= 0; i
<= LOS_SECTION_NUM_CHUNKS
; ++i
) {
486 if (section
->free_chunk_map
[i
]) {
488 for (j
= i
+ 1; j
<= LOS_SECTION_NUM_CHUNKS
&& section
->free_chunk_map
[j
]; ++j
)
490 add_free_chunk ((LOSFreeChunks
*)((char*)section
+ (i
<< LOS_CHUNK_BITS
)), (j
- i
) << LOS_CHUNK_BITS
);
496 section
= section
->next
;
501 #ifdef LOS_CONSISTENCY_CHECK
502 los_consistency_check ();
506 g_print ("LOS sections: %d objects: %d usage: %d\n", num_sections, los_num_objects, los_memory_usage);
507 for (i = 0; i < LOS_NUM_FAST_SIZES; ++i) {
509 LOSFreeChunks *free_chunks;
510 for (free_chunks = los_fast_free_lists [i]; free_chunks; free_chunks = free_chunks->next_size)
512 g_print (" %d: %d\n", i, num_chunks);
516 g_assert (los_num_sections
== num_sections
);
520 sgen_ptr_is_in_los (char *ptr
, char **start
)
525 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
526 char *end
= (char*)obj
->data
+ sgen_los_object_size (obj
);
528 if (ptr
>= (char*)obj
->data
&& ptr
< end
) {
529 *start
= (char*)obj
->data
;
537 sgen_los_iterate_objects (IterateObjectCallbackFunc cb
, void *user_data
)
541 for (obj
= los_object_list
; obj
; obj
= obj
->next
)
542 cb (obj
->data
, sgen_los_object_size (obj
), user_data
);
546 sgen_los_is_valid_object (char *object
)
550 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
551 if ((char*)obj
->data
== object
)
558 mono_sgen_los_describe_pointer (char *ptr
)
562 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
563 const char *los_kind
;
567 if ((char*)obj
->data
> ptr
|| (char*)obj
->data
+ sgen_los_object_size (obj
) <= ptr
)
570 size
= sgen_los_object_size (obj
);
571 pinned
= sgen_los_object_is_pinned (obj
->data
);
573 if (size
> LOS_SECTION_OBJECT_LIMIT
)
574 los_kind
= "huge-los-ptr";
576 los_kind
= "los-ptr";
578 if ((char*)obj
->data
== ptr
) {
579 SGEN_LOG (0, "%s (size %d pin %d)\n", los_kind
, (int)size
, pinned
? 1 : 0);
581 SGEN_LOG (0, "%s (interior-ptr offset %zd size %d pin %d)",
582 los_kind
, ptr
- (char*)obj
->data
, (int)size
, pinned
? 1 : 0);
591 sgen_los_iterate_live_block_ranges (sgen_cardtable_block_callback callback
)
594 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
595 GCVTable vt
= SGEN_LOAD_VTABLE (obj
->data
);
596 if (SGEN_VTABLE_HAS_REFERENCES (vt
))
597 callback ((mword
)obj
->data
, sgen_los_object_size (obj
));
602 get_cardtable_mod_union_for_object (LOSObject
*obj
)
604 mword size
= sgen_los_object_size (obj
);
605 guint8
*mod_union
= obj
->cardtable_mod_union
;
609 mod_union
= sgen_card_table_alloc_mod_union ((char*)obj
->data
, size
);
610 other
= (guint8
*)SGEN_CAS_PTR ((gpointer
*)&obj
->cardtable_mod_union
, mod_union
, NULL
);
612 SGEN_ASSERT (0, obj
->cardtable_mod_union
== mod_union
, "Why did CAS not replace?");
615 sgen_card_table_free_mod_union (mod_union
, (char*)obj
->data
, size
);
620 sgen_los_scan_card_table (gboolean mod_union
, ScanCopyContext ctx
)
624 binary_protocol_los_card_table_scan_start (sgen_timestamp (), mod_union
);
625 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
628 if (!SGEN_OBJECT_HAS_REFERENCES (obj
->data
))
632 if (!sgen_los_object_is_pinned (obj
->data
))
635 cards
= get_cardtable_mod_union_for_object (obj
);
641 sgen_cardtable_scan_object (obj
->data
, sgen_los_object_size (obj
), cards
, mod_union
, ctx
);
643 binary_protocol_los_card_table_scan_end (sgen_timestamp (), mod_union
);
647 sgen_los_count_cards (long long *num_total_cards
, long long *num_marked_cards
)
650 long long total_cards
= 0;
651 long long marked_cards
= 0;
653 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
655 guint8
*cards
= sgen_card_table_get_card_scan_address ((mword
) obj
->data
);
656 guint8
*cards_end
= sgen_card_table_get_card_scan_address ((mword
) obj
->data
+ sgen_los_object_size (obj
) - 1);
657 mword num_cards
= (cards_end
- cards
) + 1;
659 if (!SGEN_OBJECT_HAS_REFERENCES (obj
->data
))
662 total_cards
+= num_cards
;
663 for (i
= 0; i
< num_cards
; ++i
) {
669 *num_total_cards
= total_cards
;
670 *num_marked_cards
= marked_cards
;
674 sgen_los_update_cardtable_mod_union (void)
678 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
679 if (!SGEN_OBJECT_HAS_REFERENCES (obj
->data
))
681 sgen_card_table_update_mod_union (get_cardtable_mod_union_for_object (obj
),
682 (char*)obj
->data
, sgen_los_object_size (obj
), NULL
);
687 sgen_los_header_for_object (GCObject
*data
)
690 return (LOSObject
*)((char*)data
- (int)(&(((LOSObject
*)0)->data
)));
692 return (LOSObject
*)((char*)data
- sizeof (LOSObject
));
697 sgen_los_pin_object (GCObject
*data
)
699 LOSObject
*obj
= sgen_los_header_for_object (data
);
700 obj
->size
= obj
->size
| 1;
701 binary_protocol_pin (data
, (gpointer
)SGEN_LOAD_VTABLE (data
), sgen_safe_object_get_size (data
));
705 sgen_los_unpin_object (GCObject
*data
)
707 LOSObject
*obj
= sgen_los_header_for_object (data
);
708 obj
->size
= sgen_los_object_size (obj
);
712 sgen_los_object_is_pinned (GCObject
*data
)
714 LOSObject
*obj
= sgen_los_header_for_object (data
);
715 return obj
->size
& 1;
719 sgen_los_mark_mod_union_card (GCObject
*mono_obj
, void **ptr
)
721 LOSObject
*obj
= sgen_los_header_for_object (mono_obj
);
722 guint8
*mod_union
= get_cardtable_mod_union_for_object (obj
);
723 /* The LOSObject structure is not represented within the card space */
724 size_t offset
= sgen_card_table_get_card_offset ((char*)ptr
, (char*)sgen_card_table_align_pointer((char*)mono_obj
));
725 SGEN_ASSERT (0, mod_union
, "FIXME: optionally allocate the mod union if it's not here and CAS it in.");
726 mod_union
[offset
] = 1;
729 #endif /* HAVE_SGEN_GC */