[project @ 1996-06-27 16:13:29 by partain]
[ghc-hetmet.git] / ghc / runtime / storage / SMextn.lc
1 \section[SM-extensions]{Storage Manager Extensions}
2
3 ToDo ADR: Maybe this should be split between SMcopying.lc and
4 SMcompacting.lc?
5
6
7 This is a collection of C functions use in implementing the stable
8 pointer and foreign object extensions. 
9
10 The motivation for making this a separate file/section is twofold:
11
12 1) It let's us focus on one thing.
13
14 2) If we don't do this, there will be a huge amount of repetition
15    between the various GC schemes --- a maintenance nightmare.
16
17 The second is the major motivation.  
18
19 There are three main parts to this file:
20
21 1) Code which is common to all GC schemes.
22
23 2) Code for use in a compacting collector used in the 1-space, dual
24    mode and for collecting old generations in generational collectors.
25
26 3) Code for use in a copying collector used in the 2-space, dual mode
27    and for collecting young generations in generational collectors.
28
29 When debugging, it is incredibly helpful to trash part of the heap
30 (say) once you're done with it.
31
32 Remembering that @sm->hp@ points to the next word to be allocated, a
33 typical use is
34
35 \begin{pseudocode}
36 #ifdef DEBUG
37   TrashMem(sm->hp+1, sm->hplim);
38 #endif
39 \end{pseudocode}
40
41 \begin{code} 
42
43 #if defined(GC1s)
44
45 #define  SCAN_REG_DUMP
46 #include "SMinternal.h"
47 REGDUMP(ScanRegDump);
48
49 #else /* GC2s, GCdu, GCap, GCgn */
50
51 #define SCAV_REG_MAP
52 #include "SMinternal.h"
53 REGDUMP(ScavRegDump);
54
55 #endif
56 #include "SMextn.h"
57
58 #ifdef DEBUG
59
60 void
61 TrashMem(from, to)
62   P_ from, to;
63 {
64 /* assertion overly strong - if free_mem == 0, sm->hp == sm->hplim */
65 /*  ASSERT( from <= to ); */
66     if (RTSflags.GcFlags.trace)
67         printf("Trashing from 0x%lx to 0x%lx inclusive\n", (W_) from, (W_) to);
68     while (from <= to) {
69         *from++ = DEALLOCATED_TRASH;
70     }
71 }
72
73 #endif /* DEBUG */
74 \end{code}
75
76 \begin{code}
77
78 #if !defined(PAR)       /* To end of the file */
79
80 \end{code}
81
82 \downsection
83 \section[SM-extensions-common-code]{Code common to all GC schemes}
84
85 \begin{code}
86 EXTDATA(EmptySPTable_closure);
87
88 void initExtensions( sm )
89   smInfo *sm;
90 {
91   sm->ForeignObjList = NULL;
92 #if defined(GCap) || defined(GCgn)
93   sm->OldForeignObjList = NULL;
94 #endif
95
96   sm->StablePointerTable = (P_) EmptySPTable_closure;
97 }
98
99 \end{code}
100
101 \begin{code}
102 #if defined(DEBUG)
103 \end{code}
104
105 When a Foreign Object is released, there should be absolutely no
106 references to it.  To encourage and dangling references to show
107 themselves, we'll trash its contents when we're done with it.
108
109 \begin{code}
110 #define TRASH_ForeignObj_CLOSURE( mptr ) Trash_ForeignObj_Closure(mptr)
111
112 void
113 Trash_ForeignObj_Closure(mptr)
114   P_ mptr;
115 {
116     int i;
117     for( i = 0; i < ForeignObj_SIZE + _FHS; i++ ) {
118       mptr[ i ] = DEALLOCATED_TRASH;
119     }
120 }
121 \end{code}
122
123 Also, every time we fiddle with the ForeignObj list, we should check it
124 still makes sense.  This function returns @0@ if the list is sensible.
125
126 (Would maintaining a separate Foreign Obj count allow better testing?)
127
128 \begin{code}
129 void
130 Validate_ForeignObjList( ForeignObjList )
131   P_ ForeignObjList;
132 {
133   P_ FOptr;
134
135   for(FOptr = ForeignObjList; 
136       FOptr != NULL;
137       FOptr = ForeignObj_CLOSURE_LINK(FOptr) ) {
138     CHECK_ForeignObj_CLOSURE(FOptr);
139   }
140 }
141 \end{code}
142
143 \begin{code}
144 #else /* !DEBUG */
145
146 #define TRASH_ForeignObj_CLOSURE( mp ) /* nothing */
147
148 #endif /* !DEBUG */  
149 \end{code}
150
151 \begin{code}
152 #ifdef DEBUG
153
154 #define TRACE_ForeignObj(FOptr) Trace_ForeignObj( FOptr )
155 #define TRACE_FOdies(FOptr) Trace_FOdies()
156 #define TRACE_FOlives(FOptr) Trace_FOlives()
157 #define TRACE_FOforwarded(FOptr, newAddress) Trace_FOforwarded( FOptr, newAddress )
158
159 void
160 Trace_ForeignObj( FOptr )
161   P_ FOptr;
162 {
163   if (RTSflags.GcFlags.trace & DEBUG_TRACE_FOREIGNOBJS) {
164     printf("DEBUG: ForeignObj(%lx)=<%lx,_,%lx,%lx,%lx>\n", (W_) FOptr, (W_) FOptr[0], (W_) FOptr[1], (W_) FOptr[2], (W_) FOptr[3]);
165     printf(" Data = %lx, Finaliser = %lx, Next = %lx\n", 
166         (W_) ForeignObj_CLOSURE_DATA(FOptr), 
167         (W_) ForeignObj_CLOSURE_FINALISER(FOptr), 
168         (W_) ForeignObj_CLOSURE_LINK(FOptr) );
169   }
170 }
171
172 void
173 Trace_FOdies()
174 {
175   if (RTSflags.GcFlags.trace & DEBUG_TRACE_FOREIGNOBJS) {
176     printf(" dying\n");
177   }
178 }
179
180 void
181 Trace_FOlives()
182 {
183   if (RTSflags.GcFlags.trace & DEBUG_TRACE_FOREIGNOBJS) { 
184     printf(" lived to tell the tale\n"); 
185   }
186 }
187
188 void
189 Trace_FOforwarded( FOPtr, newAddress )
190   P_ FOPtr, newAddress;
191 {
192   if (RTSflags.GcFlags.trace & DEBUG_TRACE_FOREIGNOBJS) {
193     printf(" forwarded to %lx\n", (W_) newAddress);
194   }
195 }
196
197 #else
198
199 #define TRACE_ForeignObj(FOptr) /* nothing */
200 #define TRACE_FOdies(FOptr) /* nothing */
201 #define TRACE_FOlives(FOptr) /* nothing */
202 #define TRACE_FOforwarded(FOptr, newAddress) /* nothing */
203
204 #endif /* DEBUG */
205 \end{code}
206
207
208 \section[SM-extensions-compacting-code]{Compacting Collector Code}
209
210
211 \begin{code}
212 #if defined(_INFO_COMPACTING)
213
214 /* Sweep up the dead ForeignObjs */
215
216 /* Note that this has to happen before the linking phase trashes
217    the stable pointer table so that the finaliser functions can
218    safely call freeStablePointer. 
219 */
220
221 void
222 sweepUpDeadForeignObjs( ForeignObjList, base, bits )
223   P_ ForeignObjList;
224   P_ base;
225   BitWord *bits;
226 {
227     P_ FOptr, temp;
228     I_ ForeignObj_deaths = 0;
229     long _hp_word, bit_index, bit;
230
231     /* At this point, the ForeignObjList is in an invalid state (since
232        some info ptrs will have been mangled) so we can't validate
233        it. ADR */
234
235     DEBUG_STRING("Reporting Dead Foreign objects:");
236     FOptr = ForeignObjList;
237     while ( FOptr != NULL ) {
238
239       TRACE_ForeignObj(FOptr);
240
241       _hp_word = FOptr - base;
242       bit_index = _hp_word / BITS_IN(BitWord);
243       bit = 1L << (_hp_word & (BITS_IN(BitWord) - 1));
244       if ( !( bits[bit_index] & bit ) ) { /* dead */
245
246         TRACE_FOdies( FOptr );
247         (*(void (*)(StgAddr))((StgAddr)ForeignObj_CLOSURE_FINALISER(FOptr)))((StgAddr)ForeignObj_CLOSURE_DATA(FOptr));
248         ForeignObj_deaths++;
249
250         temp = FOptr;
251         FOptr = ForeignObj_CLOSURE_LINK(FOptr);
252         /* Now trash the closure to encourage bugs to show themselves */
253         TRASH_ForeignObj_CLOSURE( temp );
254
255       } else { 
256
257         TRACE_FOlives(FOptr);
258         FOptr = ForeignObj_CLOSURE_LINK(FOptr);
259       }
260     }
261 }
262
263 #endif /* _INFO_COMPACTING */
264 \end{code}
265
266 \section[SM-extensions-copying-code]{Copying Collector Code}
267
268 \begin{code}
269 #if defined(_INFO_COPYING)
270
271 /* ToDo: a possible optimisation would be to maintain a flag that
272    told us whether the SPTable had been updated (with new
273    pointers) and so needs to be GC'd.  A simple way of doing this
274    might be to generalise the MUTUPLE closures to MUGEN closures.
275 */
276 void evacSPTable( sm )
277 smInfo *sm;
278 {
279   DEBUG_STRING("Evacuate Stable Pointer Table:");
280   {
281     P_ evac = sm->StablePointerTable;
282     sm->StablePointerTable = EVACUATE_CLOSURE(evac);
283   }
284 }
285
286
287
288 /* First attempt at Foreign Obj hackery... Later versions might 
289    do something useful with the two counters. [ADR]      */
290
291 #if defined(DEBUG)
292 #if defined(GCgn)
293
294 EXTDATA_RO(Forward_Ref_New_info);
295 EXTDATA_RO(Forward_Ref_Old_info);
296 EXTDATA_RO(OldRoot_Forward_Ref_info);
297
298 #else
299
300 EXTDATA_RO(Forward_Ref_info);
301
302 #endif
303 #endif
304
305 /* 
306   Call ForeignObj finalising routine on any dead FOs in oldFOList,
307   add the remainder to new sticking the result into newFOList.
308 */
309 void
310 reportDeadForeignObjs(oldFOList, new, newFOList)
311   P_ oldFOList;
312   P_ new;
313   P_ *newFOList;
314 {
315     P_ FOptr, temp;
316     I_ FO_no = 0, FO_deaths = 0;
317
318     /* At this point, the ForeignObjList is in an invalid state (since
319        some info ptrs will have been mangled) so we can't validate
320        it. ADR */
321
322     DEBUG_STRING("Updating Foreign Objects List and reporting casualties:");
323     FOptr = oldFOList;
324     while ( FOptr != NULL ) {
325       TRACE_ForeignObj(FOptr);
326
327       if ((P_) INFO_PTR(FOptr) == ForeignObj_info ) {
328         /* can't have been forwarded - must be dead */
329
330         TRACE_FOdies(FOptr);
331         (*(void (*)(StgAddr))(ForeignObj_CLOSURE_FINALISER(FOptr)))((StgAddr)ForeignObj_CLOSURE_DATA(FOptr));
332         FO_deaths++;
333
334         temp  = FOptr;
335         FOptr = ForeignObj_CLOSURE_LINK(FOptr);
336
337         /* Now trash the closure to encourage bugs to show themselves */
338         TRASH_ForeignObj_CLOSURE( temp );
339       } else { /* Must have been forwarded - so it must be live */
340
341         P_ newAddress = (P_) FORWARD_ADDRESS(FOptr);
342
343 #if defined(GCgn)
344         ASSERT( ( (P_) INFO_PTR(FOptr) == Forward_Ref_New_info ) ||
345                 ( (P_) INFO_PTR(FOptr) == Forward_Ref_Old_info ) ||
346                 ( (P_) INFO_PTR(FOptr) == OldRoot_Forward_Ref_info ) );
347 #else
348         ASSERT( (P_) INFO_PTR(FOptr) == Forward_Ref_info );
349 #endif
350
351         TRACE_FOforwarded( FOptr, newAddress );
352         ForeignObj_CLOSURE_LINK(newAddress) = new;
353         new = newAddress;
354         FO_no++;
355         FOptr = ForeignObj_CLOSURE_LINK(FOptr);
356       }
357     }
358
359     VALIDATE_ForeignObjList( new );
360     *newFOList = new;
361 }
362 #endif /* _INFO_COPYING */
363 \end{code}
364
365 \upsection
366
367 \begin{code}
368 #endif /* !PAR */
369 \end{code}