New implementation of BLACKHOLEs
[ghc-hetmet.git] / rts / Updates.h
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 1998-2004
4  *
5  * Performing updates.
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #ifndef UPDATES_H
10 #define UPDATES_H
11
12 #ifndef CMINUSMINUS
13 BEGIN_RTS_PRIVATE
14 #endif
15
16 /* -----------------------------------------------------------------------------
17    Updates
18    -------------------------------------------------------------------------- */
19
20 /* LDV profiling:
21  * We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in 
22  * which p1 resides.
23  *
24  * Note: 
25  *   After all, we do *NOT* need to call LDV_RECORD_CREATE() for both IND and 
26  *   IND_OLDGEN closures because they are inherently used. But, it corrupts
27  *   the invariants that every closure keeps its creation time in the profiling
28  *  field. So, we call LDV_RECORD_CREATE().
29  */
30
31 /* In the DEBUG case, we also zero out the slop of the old closure,
32  * so that the sanity checker can tell where the next closure is.
33  *
34  * Two important invariants: we should never try to update a closure
35  * to point to itself, and the closure being updated should not
36  * already have been updated (the mutable list will get messed up
37  * otherwise).
38  *
39  * NB. We do *not* do this in THREADED_RTS mode, because when we have the
40  * possibility of multiple threads entering the same closure, zeroing
41  * the slop in one of the threads would have a disastrous effect on
42  * the other (seen in the wild!).
43  */
44 #ifdef CMINUSMINUS
45
46 #define FILL_SLOP(p)                                                    \
47   W_ inf;                                                               \
48   W_ sz;                                                                \
49   W_ i;                                                                 \
50   inf = %GET_STD_INFO(p);                                               \
51   if (%INFO_TYPE(inf) != HALF_W_(BLACKHOLE)) {                          \
52       if (%INFO_TYPE(inf) == HALF_W_(THUNK_SELECTOR)) {                 \
53           sz = BYTES_TO_WDS(SIZEOF_StgSelector_NoThunkHdr);             \
54      } else {                                                           \
55           if (%INFO_TYPE(inf) == HALF_W_(AP_STACK)) {                   \
56               sz = StgAP_STACK_size(p) + BYTES_TO_WDS(SIZEOF_StgAP_STACK_NoThunkHdr); \
57           } else {                                                      \
58               if (%INFO_TYPE(inf) == HALF_W_(AP)) {                     \
59                   sz = TO_W_(StgAP_n_args(p)) +  BYTES_TO_WDS(SIZEOF_StgAP_NoThunkHdr); \
60               } else {                                                  \
61                   sz = TO_W_(%INFO_PTRS(inf)) + TO_W_(%INFO_NPTRS(inf)); \
62               }                                                         \
63           }                                                             \
64       }                                                                 \
65       i = 0;                                                            \
66       for:                                                              \
67         if (i < sz) {                                                   \
68           StgThunk_payload(p,i) = 0;                                    \
69           i = i + 1;                                                    \
70           goto for;                                                     \
71         }                                                               \
72   }
73
74 #else /* !CMINUSMINUS */
75
76 INLINE_HEADER void
77 FILL_SLOP(StgClosure *p)
78 {                                               
79     StgInfoTable *inf = get_itbl(p);            
80     nat i, sz;
81
82     switch (inf->type) {
83     case BLACKHOLE:
84         goto no_slop;
85         // we already filled in the slop when we overwrote the thunk
86         // with BLACKHOLE, and also an evacuated BLACKHOLE is only the
87         // size of an IND.
88     case THUNK_SELECTOR:
89         sz = sizeofW(StgSelector) - sizeofW(StgThunkHeader);
90         break;
91     case AP:
92         sz = ((StgAP *)p)->n_args + sizeofW(StgAP) - sizeofW(StgThunkHeader);
93         break;
94     case AP_STACK:
95         sz = ((StgAP_STACK *)p)->size + sizeofW(StgAP_STACK) - sizeofW(StgThunkHeader);
96         break;
97     default:
98         sz = inf->layout.payload.ptrs + inf->layout.payload.nptrs;
99         break;
100     }
101     for (i = 0; i < sz; i++) {
102         ((StgThunk *)p)->payload[i] = 0;
103     }
104 no_slop:
105     ;
106 }
107
108 #endif /* CMINUSMINUS */
109
110 #if !defined(DEBUG) || defined(THREADED_RTS)
111 #define DEBUG_FILL_SLOP(p) /* do nothing */
112 #else
113 #define DEBUG_FILL_SLOP(p) FILL_SLOP(p)
114 #endif
115
116 /* We have two versions of this macro (sadly), one for use in C-- code,
117  * and the other for C.
118  *
119  * The and_then argument is a performance hack so that we can paste in
120  * the continuation code directly.  It helps shave a couple of
121  * instructions off the common case in the update code, which is
122  * worthwhile (the update code is often part of the inner loop).
123  * (except that gcc now appears to common up this code again and
124  * invert the optimisation.  Grrrr --SDM).
125  */
126 #ifdef CMINUSMINUS
127
128 #define updateWithIndirection(p1, p2, and_then) \
129     W_ bd;                                                      \
130                                                                 \
131     DEBUG_FILL_SLOP(p1);                                        \
132     LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);                      \
133     StgInd_indirectee(p1) = p2;                                 \
134     prim %write_barrier() [];                                   \
135     SET_INFO(p1, stg_BLACKHOLE_info);                           \
136     LDV_RECORD_CREATE(p1);                                      \
137     bd = Bdescr(p1);                                            \
138     if (bdescr_gen_no(bd) != 0 :: bits16) {                     \
139       recordMutableCap(p1, TO_W_(bdescr_gen_no(bd)), R1);       \
140       TICK_UPD_OLD_IND();                                       \
141       and_then;                                                 \
142     } else {                                                    \
143       TICK_UPD_NEW_IND();                                       \
144       and_then;                                                 \
145   }
146
147 #else /* !CMINUSMINUS */
148
149 INLINE_HEADER void updateWithIndirection (Capability *cap, 
150                                           StgClosure *p1, 
151                                           StgClosure *p2)
152 {
153     bdescr *bd;
154     
155     ASSERT( (P_)p1 != (P_)p2 );
156     /* not necessarily true: ASSERT( !closure_IND(p1) ); */
157     /* occurs in RaiseAsync.c:raiseAsync() */
158     DEBUG_FILL_SLOP(p1);
159     LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);
160     ((StgInd *)p1)->indirectee = p2;
161     write_barrier();
162     SET_INFO(p1, &stg_BLACKHOLE_info);
163     LDV_RECORD_CREATE(p1);
164     bd = Bdescr((StgPtr)p1);
165     if (bd->gen_no != 0) {
166         recordMutableCap(p1, cap, bd->gen_no);
167         TICK_UPD_OLD_IND();
168     } else {
169         TICK_UPD_NEW_IND();
170     }
171 }
172
173 #endif /* CMINUSMINUS */
174
175 #ifndef CMINUSMINUS
176 END_RTS_PRIVATE
177 #endif
178
179 #endif /* UPDATES_H */