Declare RTS-private prototypes with __attribute__((visibility("hidden")))
[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 #pragma GCC visibility push(hidden)
13
14 /* -----------------------------------------------------------------------------
15    Updates
16
17    We have two layers of update macros.  The top layer, UPD_IND() and
18    friends perform all the work of an update.  In detail:
19
20       - if the closure being updated is a blocking queue, then all the
21         threads waiting on the blocking queue are updated.
22
23       - then the lower level updateWithIndirection() macro is invoked 
24         to actually replace the closure with an indirection (see below).
25
26    -------------------------------------------------------------------------- */
27
28 #  define SEMI ;
29 # define UPD_IND(updclosure, heapptr) \
30    UPD_REAL_IND(updclosure,INFO_PTR(stg_IND_info),heapptr,SEMI)
31 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
32    UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
33
34 /* These macros have to work in both C and C--, so here's the
35  * impedance matching:
36  */
37 #ifdef CMINUSMINUS
38 #define BLOCK_BEGIN
39 #define BLOCK_END
40 #define INFO_PTR(info)      info
41 #else
42 #define BLOCK_BEGIN         {
43 #define BLOCK_END           }
44 #define INFO_PTR(info)      &info
45 #define StgBlockingQueue_blocking_queue(closure) \
46     (((StgBlockingQueue *)closure)->blocking_queue)
47 #endif
48
49 /* krc: there used to be an UPD_REAL_IND and an
50    UPD_PERM_IND, the latter of which was used for
51    ticky and cost-centre profiling.
52    for now, we just have UPD_REAL_IND. */
53 #define UPD_REAL_IND(updclosure, ind_info, heapptr, and_then)   \
54         BLOCK_BEGIN                                             \
55         updateWithIndirection(ind_info,                         \
56                               updclosure,                       \
57                               heapptr,                          \
58                               and_then);                        \
59         BLOCK_END
60
61 /* -----------------------------------------------------------------------------
62    Awaken any threads waiting on a blocking queue (BLACKHOLE_BQ).
63    -------------------------------------------------------------------------- */
64
65 /* -----------------------------------------------------------------------------
66    Updates: lower-level macros which update a closure with an
67    indirection to another closure.
68
69    There are several variants of this code.
70
71        PROFILING:
72    -------------------------------------------------------------------------- */
73
74 /* LDV profiling:
75  * We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in 
76  * which p1 resides.
77  *
78  * Note: 
79  *   After all, we do *NOT* need to call LDV_RECORD_CREATE() for both IND and 
80  *   IND_OLDGEN closures because they are inherently used. But, it corrupts
81  *   the invariants that every closure keeps its creation time in the profiling
82  *  field. So, we call LDV_RECORD_CREATE().
83  */
84
85 /* In the DEBUG case, we also zero out the slop of the old closure,
86  * so that the sanity checker can tell where the next closure is.
87  *
88  * Two important invariants: we should never try to update a closure
89  * to point to itself, and the closure being updated should not
90  * already have been updated (the mutable list will get messed up
91  * otherwise).
92  *
93  * NB. We do *not* do this in THREADED_RTS mode, because when we have the
94  * possibility of multiple threads entering the same closure, zeroing
95  * the slop in one of the threads would have a disastrous effect on
96  * the other (seen in the wild!).
97  */
98 #ifdef CMINUSMINUS
99
100 #define FILL_SLOP(p)                                                    \
101   W_ inf;                                                               \
102   W_ sz;                                                                \
103   W_ i;                                                                 \
104   inf = %GET_STD_INFO(p);                                               \
105   if (%INFO_TYPE(inf) != HALF_W_(BLACKHOLE)                             \
106         && %INFO_TYPE(inf) != HALF_W_(CAF_BLACKHOLE)) {                 \
107       if (%INFO_TYPE(inf) == HALF_W_(THUNK_SELECTOR)) {                 \
108           sz = BYTES_TO_WDS(SIZEOF_StgSelector_NoThunkHdr);             \
109      } else {                                                           \
110           if (%INFO_TYPE(inf) == HALF_W_(AP_STACK)) {                   \
111               sz = StgAP_STACK_size(p) + BYTES_TO_WDS(SIZEOF_StgAP_STACK_NoThunkHdr); \
112           } else {                                                      \
113               if (%INFO_TYPE(inf) == HALF_W_(AP)) {                     \
114                   sz = TO_W_(StgAP_n_args(p)) +  BYTES_TO_WDS(SIZEOF_StgAP_NoThunkHdr); \
115               } else {                                                  \
116                   sz = TO_W_(%INFO_PTRS(inf)) + TO_W_(%INFO_NPTRS(inf)); \
117               }                                                         \
118           }                                                             \
119       }                                                                 \
120       i = 0;                                                            \
121       for:                                                              \
122         if (i < sz) {                                                   \
123           StgThunk_payload(p,i) = 0;                                    \
124           i = i + 1;                                                    \
125           goto for;                                                     \
126         }                                                               \
127   }
128
129 #else /* !CMINUSMINUS */
130
131 INLINE_HEADER void
132 FILL_SLOP(StgClosure *p)
133 {                                               
134     StgInfoTable *inf = get_itbl(p);            
135     nat i, sz;
136
137     switch (inf->type) {
138     case BLACKHOLE:
139     case CAF_BLACKHOLE:
140         goto no_slop;
141         // we already filled in the slop when we overwrote the thunk
142         // with BLACKHOLE, and also an evacuated BLACKHOLE is only the
143         // size of an IND.
144     case THUNK_SELECTOR:
145         sz = sizeofW(StgSelector) - sizeofW(StgThunkHeader);
146         break;
147     case AP:
148         sz = ((StgAP *)p)->n_args + sizeofW(StgAP) - sizeofW(StgThunkHeader);
149         break;
150     case AP_STACK:
151         sz = ((StgAP_STACK *)p)->size + sizeofW(StgAP_STACK) - sizeofW(StgThunkHeader);
152         break;
153     default:
154         sz = inf->layout.payload.ptrs + inf->layout.payload.nptrs;
155         break;
156     }
157     for (i = 0; i < sz; i++) {
158         ((StgThunk *)p)->payload[i] = 0;
159     }
160 no_slop:
161     ;
162 }
163
164 #endif /* CMINUSMINUS */
165
166 #if !defined(DEBUG) || defined(THREADED_RTS)
167 #define DEBUG_FILL_SLOP(p) /* do nothing */
168 #else
169 #define DEBUG_FILL_SLOP(p) FILL_SLOP(p)
170 #endif
171
172 /* We have two versions of this macro (sadly), one for use in C-- code,
173  * and the other for C.
174  *
175  * The and_then argument is a performance hack so that we can paste in
176  * the continuation code directly.  It helps shave a couple of
177  * instructions off the common case in the update code, which is
178  * worthwhile (the update code is often part of the inner loop).
179  * (except that gcc now appears to common up this code again and
180  * invert the optimisation.  Grrrr --SDM).
181  */
182 #ifdef CMINUSMINUS
183 #define generation(n) (W_[generations] + n*SIZEOF_generation)
184 #define updateWithIndirection(ind_info, p1, p2, and_then)       \
185     W_ bd;                                                      \
186                                                                 \
187     DEBUG_FILL_SLOP(p1);                                        \
188     LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);                      \
189     StgInd_indirectee(p1) = p2;                                 \
190     prim %write_barrier() [];                                   \
191     bd = Bdescr(p1);                                            \
192     if (bdescr_gen_no(bd) != 0 :: CInt) {                       \
193       recordMutableCap(p1, TO_W_(bdescr_gen_no(bd)), R1);       \
194       SET_INFO(p1, stg_IND_OLDGEN_info);                        \
195       LDV_RECORD_CREATE(p1);                                    \
196       TICK_UPD_OLD_IND();                                       \
197       and_then;                                                 \
198     } else {                                                    \
199       SET_INFO(p1, ind_info);                                   \
200       LDV_RECORD_CREATE(p1);                                    \
201       TICK_UPD_NEW_IND();                                       \
202       and_then;                                                 \
203   }
204 #else
205 #define updateWithIndirection(ind_info, p1, p2, and_then)       \
206   {                                                             \
207     bdescr *bd;                                                 \
208                                                                 \
209     ASSERT( (P_)p1 != (P_)p2 );                                 \
210     /* not necessarily true: ASSERT( !closure_IND(p1) ); */     \
211     /* occurs in RaiseAsync.c:raiseAsync() */                   \
212     DEBUG_FILL_SLOP(p1);                                        \
213     LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);                      \
214     ((StgInd *)p1)->indirectee = p2;                            \
215     write_barrier();                                            \
216     bd = Bdescr((P_)p1);                                        \
217     if (bd->gen_no != 0) {                                      \
218       recordMutableGenLock(p1, bd->gen_no);                     \
219       SET_INFO(p1, &stg_IND_OLDGEN_info);                       \
220       TICK_UPD_OLD_IND();                                       \
221       and_then;                                                 \
222     } else {                                                    \
223       SET_INFO(p1, ind_info);                                   \
224       LDV_RECORD_CREATE(p1);                                    \
225       TICK_UPD_NEW_IND();                                       \
226       and_then;                                                 \
227     }                                                           \
228   }
229 #endif /* CMINUSMINUS */
230
231 #pragma GCC visibility pop
232
233 #endif /* UPDATES_H */