9ba0d129522e677b98bf87768d72b09c1e2fc2c4
[mono.git] / mono / sgen / sgen-minor-copy-object.h
1 /**
2  * \file
3  * Copy functions for nursery collections.
4  *
5  * Copyright 2001-2003 Ximian, Inc
6  * Copyright 2003-2010 Novell, Inc.
7  * Copyright (C) 2012 Xamarin Inc
8  *
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #undef SERIAL_COPY_OBJECT
13 #undef SERIAL_COPY_OBJECT_FROM_OBJ
14
15 #if defined(SGEN_SIMPLE_NURSERY)
16
17 #ifdef SGEN_CONCURRENT_MAJOR
18 #define SERIAL_COPY_OBJECT simple_nursery_serial_with_concurrent_major_copy_object
19 #define SERIAL_COPY_OBJECT_FROM_OBJ simple_nursery_serial_with_concurrent_major_copy_object_from_obj
20 #else
21 #define SERIAL_COPY_OBJECT simple_nursery_serial_copy_object
22 #define SERIAL_COPY_OBJECT_FROM_OBJ simple_nursery_serial_copy_object_from_obj
23 #endif
24
25 #elif defined (SGEN_SPLIT_NURSERY)
26
27 #ifdef SGEN_CONCURRENT_MAJOR
28 #define SERIAL_COPY_OBJECT split_nursery_serial_with_concurrent_major_copy_object
29 #define SERIAL_COPY_OBJECT_FROM_OBJ split_nursery_serial_with_concurrent_major_copy_object_from_obj
30 #else
31 #define SERIAL_COPY_OBJECT split_nursery_serial_copy_object
32 #define SERIAL_COPY_OBJECT_FROM_OBJ split_nursery_serial_copy_object_from_obj
33 #endif
34
35 #else
36 #error "No nursery configuration specified"
37 #endif
38
39
40 extern guint64 stat_nursery_copy_object_failed_to_space; /* from sgen-gc.c */
41
42 /*
43  * This is how the copying happens from the nursery to the old generation.
44  * We assume that at this time all the pinned objects have been identified and
45  * marked as such.
46  * We run scan_object() for each pinned object so that each referenced
47  * objects if possible are copied. The new gray objects created can have
48  * scan_object() run on them right away, too.
49  * Then we run copy_object() for the precisely tracked roots. At this point
50  * all the roots are either gray or black. We run scan_object() on the gray
51  * objects until no more gray objects are created.
52  * At the end of the process we walk again the pinned list and we unmark
53  * the pinned flag. As we go we also create the list of free space for use
54  * in the next allocation runs.
55  *
56  * We need to remember objects from the old generation that point to the new one
57  * (or just addresses?).
58  *
59  * copy_object could be made into a macro once debugged (use inline for now).
60  */
61
62 static MONO_ALWAYS_INLINE void
63 SERIAL_COPY_OBJECT (GCObject **obj_slot, SgenGrayQueue *queue) 
64 {
65         GCObject *forwarded;
66         GCObject *copy;
67         GCObject *obj = *obj_slot;
68
69         SGEN_ASSERT (9, current_collection_generation == GENERATION_NURSERY, "calling minor-serial-copy from a %d generation collection", current_collection_generation);
70
71         HEAVY_STAT (++stat_copy_object_called_nursery);
72
73         if (!sgen_ptr_in_nursery (obj)) {
74                 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
75                 return;
76         }
77
78         SGEN_LOG (9, "Precise copy of %p from %p", obj, obj_slot);
79
80         /*
81          * Before we can copy the object we must make sure that we are
82          * allowed to, i.e. that the object not pinned, not already
83          * forwarded or belongs to the nursery To Space.
84          */
85
86         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
87                 SGEN_ASSERT (9, sgen_obj_get_descriptor (forwarded),  "forwarded object %p has no gc descriptor", forwarded);
88                 SGEN_LOG (9, " (already forwarded to %p)", forwarded);
89                 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
90                 SGEN_UPDATE_REFERENCE (obj_slot, forwarded);
91                 return;
92         }
93         if (G_UNLIKELY (SGEN_OBJECT_IS_PINNED (obj))) {
94                 SGEN_ASSERT (9, sgen_vtable_get_descriptor (SGEN_LOAD_VTABLE(obj)), "pinned object %p has no gc descriptor", obj);
95                 SGEN_LOG (9, " (pinned, no change)");
96                 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
97                 return;
98         }
99
100 #ifndef SGEN_SIMPLE_NURSERY
101         if (sgen_nursery_is_to_space (obj)) {
102                 SGEN_ASSERT (9, sgen_vtable_get_descriptor (SGEN_LOAD_VTABLE(obj)), "to space object %p has no gc descriptor", obj);
103                 SGEN_LOG (9, " (tospace, no change)");
104                 HEAVY_STAT (++stat_nursery_copy_object_failed_to_space);                
105                 return;
106         }
107 #endif
108
109         HEAVY_STAT (++stat_objects_copied_nursery);
110
111         copy = copy_object_no_checks (obj, queue);
112         SGEN_UPDATE_REFERENCE (obj_slot, copy);
113 }
114
115 /*
116  * SERIAL_COPY_OBJECT_FROM_OBJ:
117  *
118  *   Similar to SERIAL_COPY_OBJECT, but assumes that OBJ_SLOT is part of an object, so it handles global remsets as well.
119  */
120 static MONO_ALWAYS_INLINE void
121 SERIAL_COPY_OBJECT_FROM_OBJ (GCObject **obj_slot, SgenGrayQueue *queue)
122 {
123         GCObject *forwarded;
124         GCObject *obj = *obj_slot;
125         GCObject *copy;
126
127         SGEN_ASSERT (9, current_collection_generation == GENERATION_NURSERY, "calling minor-serial-copy-from-obj from a %d generation collection", current_collection_generation);
128
129         HEAVY_STAT (++stat_copy_object_called_nursery);
130
131         if (!sgen_ptr_in_nursery (obj)) {
132                 HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
133                 return;
134         }
135
136         SGEN_LOG (9, "Precise copy of %p from %p", obj, obj_slot);
137
138         /*
139          * Before we can copy the object we must make sure that we are
140          * allowed to, i.e. that the object not pinned, not already
141          * forwarded or belongs to the nursery To Space.
142          */
143
144         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
145                 SGEN_ASSERT (9, sgen_obj_get_descriptor (forwarded),  "forwarded object %p has no gc descriptor", forwarded);
146                 SGEN_LOG (9, " (already forwarded to %p)", forwarded);
147                 HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
148 #ifdef SGEN_CONCURRENT_MAJOR
149                 /* See comment on STORE_STORE_FENCE below. */
150                 STORE_STORE_FENCE;
151 #endif
152                 SGEN_UPDATE_REFERENCE (obj_slot, forwarded);
153 #ifndef SGEN_SIMPLE_NURSERY
154                 if (G_UNLIKELY (sgen_ptr_in_nursery (forwarded) && !sgen_ptr_in_nursery (obj_slot) && !SGEN_OBJECT_IS_CEMENTED (forwarded)))
155                         sgen_add_to_global_remset (obj_slot, forwarded);
156 #endif
157                 return;
158         }
159         if (G_UNLIKELY (SGEN_OBJECT_IS_PINNED (obj))) {
160                 SGEN_ASSERT (9, sgen_vtable_get_descriptor (SGEN_LOAD_VTABLE(obj)), "pinned object %p has no gc descriptor", obj);
161                 SGEN_LOG (9, " (pinned, no change)");
162                 HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
163                 if (!sgen_ptr_in_nursery (obj_slot) && !SGEN_OBJECT_IS_CEMENTED (obj))
164                         sgen_add_to_global_remset (obj_slot, obj);
165                 return;
166         }
167
168 #ifndef SGEN_SIMPLE_NURSERY
169         if (sgen_nursery_is_to_space (obj)) {
170                 /* FIXME: all of these could just use `sgen_obj_get_descriptor_safe()` */
171                 SGEN_ASSERT (9, sgen_vtable_get_descriptor (SGEN_LOAD_VTABLE(obj)), "to space object %p has no gc descriptor", obj);
172                 SGEN_LOG (9, " (tospace, no change)");
173                 HEAVY_STAT (++stat_nursery_copy_object_failed_to_space);                
174
175                 /*
176                  * FIXME:
177                  *
178                  * The card table scanning code sometimes clears cards
179                  * that have just been set for a global remset.  In
180                  * the split nursery the following situation can
181                  * occur:
182                  *
183                  * Let's say object A starts in card C but continues
184                  * into C+1.  Within A, at offset O there's a
185                  * reference to a new nursery object X.  A+O is in
186                  * card C+1.  Now card C is scanned, and as part of
187                  * it, object A.  The reference at A+O is processed by
188                  * copying X into nursery to-space at Y.  Since it's
189                  * still in the nursery, a global remset must be added
190                  * for A+O, so card C+1 is marked.  Now, however, card
191                  * C+1 is scanned, which means that it's cleared
192                  * first.  This wouldn't be terribly bad if reference
193                  * A+O were re-scanned and the global remset re-added,
194                  * but since the reference points to to-space, that
195                  * doesn't happen, and C+1 remains cleared: the remset
196                  * is lost.
197                  *
198                  * There's at least two ways to fix this.  The easy
199                  * one is to re-add the remset on the re-scan.  This
200                  * is that - the following two lines of code.
201                  *
202                  * The proper solution appears to be to first make a
203                  * copy of the cards before scanning a block, then to
204                  * clear all the cards and scan from the copy, so no
205                  * remsets will be overwritten.  Scanning objects at
206                  * most once would be the icing on the cake.
207                  */
208                 if (!sgen_ptr_in_nursery (obj_slot) && !SGEN_OBJECT_IS_CEMENTED (obj))
209                         sgen_add_to_global_remset (obj_slot, obj);
210
211                 return;
212         }
213 #endif
214
215         HEAVY_STAT (++stat_objects_copied_nursery);
216
217         copy = copy_object_no_checks (obj, queue);
218 #ifdef SGEN_CONCURRENT_MAJOR
219         /*
220          * If an object is evacuated to the major heap and a reference to it, from the major
221          * heap, updated, the concurrent major collector might follow that reference and
222          * scan the new major object.  To make sure the object contents are seen by the
223          * major collector we need this write barrier, so that the reference is seen after
224          * the object.
225          */
226         STORE_STORE_FENCE;
227 #endif
228         SGEN_UPDATE_REFERENCE (obj_slot, copy);
229 #ifndef SGEN_SIMPLE_NURSERY
230         if (G_UNLIKELY (sgen_ptr_in_nursery (copy) && !sgen_ptr_in_nursery (obj_slot) && !SGEN_OBJECT_IS_CEMENTED (copy)))
231                 sgen_add_to_global_remset (obj_slot, copy);
232 #else
233         /* copy_object_no_checks () can return obj on OOM */
234         if (G_UNLIKELY (obj == copy)) {
235                 if (G_UNLIKELY (sgen_ptr_in_nursery (copy) && !sgen_ptr_in_nursery (obj_slot) && !SGEN_OBJECT_IS_CEMENTED (copy)))
236                         sgen_add_to_global_remset (obj_slot, copy);
237         }
238 #endif
239 }