[project @ 1996-01-11 14:06:51 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 malloc pointer 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 #ifndef 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->MallocPtrList = NULL;
92 #if defined(GCap) || defined(GCgn)
93   sm->OldMallocPtrList = NULL;
94 #endif
95
96   sm->StablePointerTable = (P_) EmptySPTable_closure;
97 }
98
99 extern void FreeMallocPtr PROTO(( StgMallocPtr mp ));
100 \end{code}
101
102 \begin{code}
103 #if defined(DEBUG)
104 \end{code}
105
106 When a Malloc Pointer is released, there should be absolutely no
107 references to it.  To encourage and dangling references to show
108 themselves, we'll trash its contents when we're done with it.
109
110 \begin{code}
111 #define TRASH_MallocPtr_CLOSURE( mptr ) Trash_MallocPtr_Closure(mptr)
112
113 void
114 Trash_MallocPtr_Closure(mptr)
115   P_ mptr;
116 {
117     int i;
118     for( i = 0; i < MallocPtr_SIZE + _FHS; i++ ) {
119       mptr[ i ] = DEALLOCATED_TRASH;
120     }
121 }
122 \end{code}
123
124 Also, every time we fiddle with the MallocPtr list, we should check it
125 still makes sense.  This function returns @0@ if the list is sensible.
126
127 (Would maintaining a separate Malloc Ptr count allow better testing?)
128
129 \begin{code}
130 void
131 Validate_MallocPtrList( MallocPtrList )
132   P_ MallocPtrList;
133 {
134   P_ MPptr;
135
136   for(MPptr = MallocPtrList; 
137       MPptr != NULL;
138       MPptr = MallocPtr_CLOSURE_LINK(MPptr) ) {
139     CHECK_MallocPtr_CLOSURE(MPptr);
140   }
141 }
142 \end{code}
143
144 \begin{code}
145 #else /* !DEBUG */
146
147 #define TRASH_MallocPtr_CLOSURE( mp ) /* nothing */
148
149 #endif /* !DEBUG */  
150 \end{code}
151
152 \begin{code}
153 #ifdef DEBUG
154
155 #define TRACE_MallocPtr(MPptr) Trace_MallocPtr( MPptr )
156 #define TRACE_MPdies(MPptr) Trace_MPdies()
157 #define TRACE_MPlives(MPptr) Trace_MPlives()
158 #define TRACE_MPforwarded(MPptr, newAddress) Trace_MPforwarded( MPptr, newAddress )
159
160 void
161 Trace_MallocPtr( MPptr )
162   P_ MPptr;
163 {
164   if (RTSflags.GcFlags.trace & DEBUG_TRACE_MALLOCPTRS) {
165     printf("DEBUG: MallocPtr(%lx)=<%lx,_,%lx,%lx,%lx>\n", (W_) MPptr, (W_) MPptr[0], (W_) MPptr[1], (W_) MPptr[2], (W_) MPptr[3]);
166     printf(" Data = %lx, Next = %lx\n", 
167         (W_) MallocPtr_CLOSURE_DATA(MPptr), (W_) MallocPtr_CLOSURE_LINK(MPptr) );
168   }
169 }
170
171 void
172 Trace_MPdies()
173 {
174   if (RTSflags.GcFlags.trace & DEBUG_TRACE_MALLOCPTRS) {
175     printf(" dying\n");
176   }
177 }
178
179 void
180 Trace_MPlives()
181 {
182   if (RTSflags.GcFlags.trace & DEBUG_TRACE_MALLOCPTRS) { 
183     printf(" lived to tell the tale\n"); 
184   }
185 }
186
187 void
188 Trace_MPforwarded( MPPtr, newAddress )
189   P_ MPPtr, newAddress;
190 {
191   if (RTSflags.GcFlags.trace & DEBUG_TRACE_MALLOCPTRS) {
192     printf(" forwarded to %lx\n", (W_) newAddress);
193   }
194 }
195
196 #else
197
198 #define TRACE_MallocPtr(MPptr) /* nothing */
199 #define TRACE_MPdies(MPptr) /* nothing */
200 #define TRACE_MPlives(MPptr) /* nothing */
201 #define TRACE_MPforwarded(MPptr, newAddress) /* nothing */
202
203 #endif /* DEBUG */
204 \end{code}
205
206
207 \section[SM-extensions-compacting-code]{Compacting Collector Code}
208
209
210 \begin{code}
211 #if defined(_INFO_COMPACTING)
212
213 /* Sweep up the dead MallocPtrs */
214
215 /* Note that this has to happen before the linking phase trashes
216    the stable pointer table so that the FreeMallocPtr function can
217    safely call freeStablePointer. 
218 */
219
220 void
221 sweepUpDeadMallocPtrs( MallocPtrList, base, bits )
222   P_ MallocPtrList;
223   P_ base;
224   BitWord *bits;
225 {
226     P_ MPptr, temp;
227     I_ MallocPtr_deaths = 0;
228     long _hp_word, bit_index, bit;
229
230     /* At this point, the MallocPtrList is in an invalid state (since
231        some info ptrs will have been mangled) so we can't validate
232        it. ADR */
233
234     DEBUG_STRING("Reporting Dead Malloc Ptrs:");
235     MPptr = MallocPtrList;
236     while ( MPptr != NULL ) {
237
238       TRACE_MallocPtr(MPptr);
239
240       _hp_word = MPptr - base;
241       bit_index = _hp_word / BITS_IN(BitWord);
242       bit = 1L << (_hp_word & (BITS_IN(BitWord) - 1));
243       if ( !( bits[bit_index] & bit ) ) { /* dead */
244
245         TRACE_MPdies( MPptr );
246         FreeMallocPtr( MallocPtr_CLOSURE_DATA(MPptr) );
247         MallocPtr_deaths++;
248
249         temp = MPptr;
250         MPptr = MallocPtr_CLOSURE_LINK(MPptr);
251         /* Now trash the closure to encourage bugs to show themselves */
252         TRASH_MallocPtr_CLOSURE( temp );
253
254       } else { 
255
256         TRACE_MPlives(MPptr);
257         MPptr = MallocPtr_CLOSURE_LINK(MPptr);
258       }
259     }
260 }
261
262 #endif /* _INFO_COMPACTING */
263 \end{code}
264
265 \section[SM-extensions-copying-code]{Copying Collector Code}
266
267 \begin{code}
268 #if defined(_INFO_COPYING)
269
270 /* ToDo: a possible optimisation would be to maintain a flag that
271    told us whether the SPTable had been updated (with new
272    pointers) and so needs to be GC'd.  A simple way of doing this
273    might be to generalise the MUTUPLE closures to MUGEN closures.
274 */
275 void evacSPTable( sm )
276 smInfo *sm;
277 {
278   DEBUG_STRING("Evacuate Stable Pointer Table:");
279   {
280     P_ evac = sm->StablePointerTable;
281     sm->StablePointerTable = EVACUATE_CLOSURE(evac);
282   }
283 }
284
285
286
287 /* First attempt at Malloc Ptr hackery... Later versions might 
288    do something useful with the two counters. [ADR]      */
289
290 #if defined(DEBUG)
291 #if defined(GCgn)
292
293 EXTDATA_RO(Forward_Ref_New_info);
294 EXTDATA_RO(Forward_Ref_Old_info);
295 EXTDATA_RO(OldRoot_Forward_Ref_info);
296
297 #else
298
299 EXTDATA_RO(Forward_Ref_info);
300
301 #endif
302 #endif
303
304 /* 
305   Call FreeMallocPtr on any dead MPs in oldMPList, add the remainder
306   to new sticking the result into newMPList.
307 */
308 void
309 reportDeadMallocPtrs(oldMPList, new, newMPList)
310   P_ oldMPList;
311   P_ new;
312   P_ *newMPList;
313 {
314     P_ MPptr, temp;
315     I_ MP_no = 0, MP_deaths = 0;
316
317     /* At this point, the MallocPtrList is in an invalid state (since
318        some info ptrs will have been mangled) so we can't validate
319        it. ADR */
320
321     DEBUG_STRING("Updating MallocPtr List and reporting casualties:");
322     MPptr = oldMPList;
323     while ( MPptr != NULL ) {
324       TRACE_MallocPtr(MPptr);
325
326       if ((P_) INFO_PTR(MPptr) == MallocPtr_info ) {
327         /* can't have been forwarded - must be dead */
328
329         TRACE_MPdies(MPptr);
330         FreeMallocPtr( MallocPtr_CLOSURE_DATA(MPptr) );
331         MP_deaths++;
332
333         temp = MPptr;
334         MPptr = MallocPtr_CLOSURE_LINK(MPptr);
335
336         /* Now trash the closure to encourage bugs to show themselves */
337         TRASH_MallocPtr_CLOSURE( temp );
338       } else { /* Must have been forwarded - so it must be live */
339
340         P_ newAddress = (P_) FORWARD_ADDRESS(MPptr);
341
342 #if defined(GCgn)
343         ASSERT( ( (P_) INFO_PTR(MPptr) == Forward_Ref_New_info ) ||
344                 ( (P_) INFO_PTR(MPptr) == Forward_Ref_Old_info ) ||
345                 ( (P_) INFO_PTR(MPptr) == OldRoot_Forward_Ref_info ) );
346 #else
347         ASSERT( (P_) INFO_PTR(MPptr) == Forward_Ref_info );
348 #endif
349
350         TRACE_MPforwarded( MPptr, newAddress );
351         MallocPtr_CLOSURE_LINK(newAddress) = new;
352         new = newAddress;
353         MP_no++;
354         MPptr = MallocPtr_CLOSURE_LINK(MPptr);
355       }
356     }
357
358     VALIDATE_MallocPtrList( new );
359     *newMPList = new;
360 }
361 #endif /* _INFO_COPYING */
362 \end{code}
363
364 \upsection
365
366 \begin{code}
367 #endif /* !PAR */
368 \end{code}