177
156
source, exit_label) \
180
ptr_t my_current = current; \
182
GET_HDR(my_current, my_hhdr); \
183
if (IS_FORWARDING_ADDR_OR_NIL(my_hhdr)) { \
184
hdr * new_hdr = GC_invalid_header; \
185
my_current = GC_find_start(my_current, my_hhdr, &new_hdr); \
188
PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
189
source, exit_label, my_hhdr); \
193
/* As above, but use header cache for header lookup. */
194
# define HC_PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
195
source, exit_label) \
198
ptr_t my_current = current; \
200
HC_GET_HDR(my_current, my_hhdr, source); \
201
PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
202
source, exit_label, my_hhdr); \
160
HC_GET_HDR(current, my_hhdr, source, exit_label); \
161
PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
162
source, exit_label, my_hhdr, TRUE); \
206
166
/* Set mark bit, exit if it was already set. */
208
# ifdef USE_MARK_BYTES
209
/* Unlike the mark bit case, there is a race here, and we may set */
210
/* the bit twice in the concurrent case. This can result in the */
211
/* object being pushed twice. But that's only a performance issue. */
212
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \
214
register VOLATILE char * mark_byte_addr = \
215
hhdr -> hb_marks + ((displ) >> 1); \
216
register char mark_byte = *mark_byte_addr; \
168
# ifdef USE_MARK_BITS
169
# ifdef PARALLEL_MARK
170
/* The following may fail to exit even if the bit was already set. */
171
/* For our uses, that's benign: */
172
# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
174
if (!(*(addr) & (mask))) { \
175
AO_or((AO_t *)(addr), (mask); \
181
# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
183
word old = *(addr); \
184
word my_bits = (bits); \
185
if (old & my_bits) goto exit_label; \
186
*(addr) = (old | my_bits); \
188
# endif /* !PARALLEL_MARK */
189
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
191
word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(bit_no); \
193
OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(bit_no), \
199
#ifdef USE_MARK_BYTES
200
# if defined(I386) && defined(__GNUC__)
201
# define LONG_MULT(hprod, lprod, x, y) { \
202
asm("mull %2" : "=a"(lprod), "=d"(hprod) : "g"(y), "0"(x)); \
204
# else /* No in-line X86 assembly code */
205
# define LONG_MULT(hprod, lprod, x, y) { \
206
unsigned long long prod = (unsigned long long)x \
207
* (unsigned long long)y; \
208
hprod = prod >> 32; \
209
lprod = (unsigned32)prod; \
213
/* There is a race here, and we may set */
214
/* the bit twice in the concurrent case. This can result in the */
215
/* object being pushed twice. But that's only a performance issue. */
216
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
218
char * mark_byte_addr = (char *)hhdr -> hb_marks + (bit_no); \
219
char mark_byte = *mark_byte_addr; \
218
221
if (mark_byte) goto exit_label; \
219
222
*mark_byte_addr = 1; \
222
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \
224
register word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(displ); \
226
OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(displ), \
229
# endif /* USE_MARK_BYTES */
224
#endif /* USE_MARK_BYTES */
227
# define INCR_MARKS(hhdr) \
228
AO_store(&(hhdr -> hb_n_marks), AO_load(&(hhdr -> hb_n_marks))+1);
230
# define INCR_MARKS(hhdr) ++(hhdr -> hb_n_marks)
234
# define TRACE(source, cmd) \
235
if (GC_trace_addr != 0 && (ptr_t)(source) == GC_trace_addr) cmd
236
# define TRACE_TARGET(target, cmd) \
237
if (GC_trace_addr != 0 && (target) == *(ptr_t *)GC_trace_addr) cmd
239
# define TRACE(source, cmd)
240
# define TRACE_TARGET(source, cmd)
231
242
/* If the mark bit corresponding to current is not set, set it, and */
232
/* push the contents of the object on the mark stack. For a small */
233
/* object we assume that current is the (possibly interior) pointer */
234
/* to the object. For large objects we assume that current points */
235
/* to somewhere inside the first page of the object. If */
236
/* GC_all_interior_pointers is set, it may have been previously */
237
/* adjusted to make that true. */
238
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
239
source, exit_label, hhdr) \
241
int displ; /* Displacement in block; first bytes, then words */ \
244
displ = HBLKDISPL(current); \
245
map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \
246
displ = BYTES_TO_WORDS(displ); \
247
if (map_entry > CPP_MAX_OFFSET) { \
248
if (map_entry == OFFSET_TOO_BIG) { \
249
map_entry = displ % (hhdr -> hb_sz); \
250
displ -= map_entry; \
251
if (displ + (hhdr -> hb_sz) > BYTES_TO_WORDS(HBLKSIZE) \
253
GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); \
257
GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); goto exit_label; \
260
displ -= map_entry; \
262
GC_ASSERT(displ >= 0 && displ < MARK_BITS_PER_HBLK); \
263
SET_MARK_BIT_EXIT_IF_SET(hhdr, displ, exit_label); \
264
GC_STORE_BACK_PTR((ptr_t)source, (ptr_t)HBLKPTR(current) \
265
+ WORDS_TO_BYTES(displ)); \
266
PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \
267
mark_stack_top, mark_stack_limit) \
243
/* push the contents of the object on the mark stack. Current points */
244
/* to the bginning of the object. We rely on the fact that the */
245
/* preceding header calculation will succeed for a pointer past the */
246
/* first page of an object, only if it is in fact a valid pointer */
247
/* to the object. Thus we can omit the otherwise necessary tests */
248
/* here. Note in particular that the "displ" value is the displacement */
249
/* from the beginning of the heap block, which may itself be in the */
250
/* interior of a large object. */
251
#ifdef MARK_BIT_PER_GRANULE
252
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
253
source, exit_label, hhdr, do_offset_check) \
255
size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
256
/* displ is always within range. If current doesn't point to */ \
257
/* first block, then we are in the all_interior_pointers case, and */ \
258
/* it is safe to use any displacement value. */ \
259
size_t gran_displ = BYTES_TO_GRANULES(displ); \
260
size_t gran_offset = hhdr -> hb_map[gran_displ]; \
261
size_t byte_offset = displ & (GRANULE_BYTES - 1); \
262
ptr_t base = current; \
263
/* The following always fails for large block references. */ \
264
if (EXPECT((gran_offset | byte_offset) != 0, FALSE)) { \
265
if (hhdr -> hb_large_block) { \
266
/* gran_offset is bogus. */ \
268
base = (ptr_t)(hhdr -> hb_block); \
269
obj_displ = (ptr_t)(current) - base; \
270
if (obj_displ != displ) { \
271
GC_ASSERT(obj_displ < hhdr -> hb_sz); \
272
/* Must be in all_interior_pointer case, not first block */ \
273
/* already did validity check on cache miss. */ \
276
if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
277
GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
282
GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
283
hhdr -> hb_block == HBLKPTR(current)); \
284
GC_ASSERT((ptr_t)(hhdr -> hb_block) <= (ptr_t) current); \
286
size_t obj_displ = GRANULES_TO_BYTES(gran_offset) \
288
if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
289
GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
292
gran_displ -= gran_offset; \
296
GC_ASSERT(hhdr == GC_find_header(base)); \
297
GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); \
298
TRACE(source, GC_log_printf("GC:%d: passed validity tests\n",GC_gc_no)); \
299
SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ, exit_label); \
300
TRACE(source, GC_log_printf("GC:%d: previously unmarked\n",GC_gc_no)); \
302
GC_log_printf("GC:%d: marking %p from %p instead\n", GC_gc_no, \
305
GC_STORE_BACK_PTR((ptr_t)source, base); \
306
PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
308
#endif /* MARK_BIT_PER_GRANULE */
310
#ifdef MARK_BIT_PER_OBJ
311
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
312
source, exit_label, hhdr, do_offset_check) \
314
size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
315
unsigned32 low_prod, high_prod, offset_fraction; \
316
unsigned32 inv_sz = hhdr -> hb_inv_sz; \
317
ptr_t base = current; \
318
LONG_MULT(high_prod, low_prod, displ, inv_sz); \
319
/* product is > and within sz_in_bytes of displ * sz_in_bytes * 2**32 */ \
320
if (EXPECT(low_prod >> 16 != 0, FALSE)) { \
321
FIXME: fails if offset is a multiple of HBLKSIZE which becomes 0 \
322
if (inv_sz == LARGE_INV_SZ) { \
324
base = (ptr_t)(hhdr -> hb_block); \
325
obj_displ = (ptr_t)(current) - base; \
326
if (obj_displ != displ) { \
327
GC_ASSERT(obj_displ < hhdr -> hb_sz); \
328
/* Must be in all_interior_pointer case, not first block */ \
329
/* already did validity check on cache miss. */ \
332
if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
333
GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
337
GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
338
hhdr -> hb_block == HBLKPTR(current)); \
339
GC_ASSERT((ptr_t)(hhdr -> hb_block) < (ptr_t) current); \
341
/* Accurate enough if HBLKSIZE <= 2**15. */ \
342
GC_ASSERT(HBLKSIZE <= (1 << 15)); \
343
size_t obj_displ = (((low_prod >> 16) + 1) * (hhdr -> hb_sz)) >> 16; \
344
if (do_offset_check && !GC_valid_offsets[obj_displ]) { \
345
GC_ADD_TO_BLACK_LIST_NORMAL(current, source); \
351
/* May get here for pointer to start of block not at */ \
352
/* beginning of object. If so, it's valid, and we're fine. */ \
353
GC_ASSERT(high_prod >= 0 && high_prod <= HBLK_OBJS(hhdr -> hb_sz)); \
354
TRACE(source, GC_log_printf("GC:%d: passed validity tests\n",GC_gc_no)); \
355
SET_MARK_BIT_EXIT_IF_SET(hhdr, high_prod, exit_label); \
356
TRACE(source, GC_log_printf("GC:%d: previously unmarked\n",GC_gc_no)); \
358
GC_log_printf("GC:%d: marking %p from %p instead\n", GC_gc_no, \
361
GC_STORE_BACK_PTR((ptr_t)source, base); \
362
PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
364
#endif /* MARK_BIT_PER_OBJ */
270
366
#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
271
367
# define PUSH_ONE_CHECKED_STACK(p, source) \