reclaim.c 23.8 KB
Newer Older
Tom Tromey committed
1 2
/* 
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 4 5
 * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
 * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
 * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
Tom Tromey committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

#include <stdio.h>
#include "gc_priv.h"

signed_word GC_mem_found = 0;
			/* Number of words of memory reclaimed     */

static void report_leak(p, sz)
ptr_t p;
word sz;
{
    if (HDR(p) -> hb_obj_kind == PTRFREE) {
        GC_err_printf0("Leaked atomic object at ");
    } else {
        GC_err_printf0("Leaked composite object at ");
    }
    if (GC_debugging_started && GC_has_debug_info(p)) {
        GC_print_obj(p);
    } else {
        GC_err_printf2("0x%lx (appr. size = %ld)\n",
       		      (unsigned long)p,
       		      (unsigned long)WORDS_TO_BYTES(sz));
    }
}

#   define FOUND_FREE(hblk, word_no) \
42
      { \
Tom Tromey committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
         report_leak((ptr_t)hblk + WORDS_TO_BYTES(word_no), \
         	     HDR(hblk) -> hb_sz); \
      }

/*
 * reclaim phase
 *
 */


/*
 * Test whether a block is completely empty, i.e. contains no marked
 * objects.  This does not require the block to be in physical
 * memory.
 */
 
GC_bool GC_block_empty(hhdr)
register hdr * hhdr;
{
    register word *p = (word *)(&(hhdr -> hb_marks[0]));
    register word * plim =
	    		(word *)(&(hhdr -> hb_marks[MARK_BITS_SZ]));
    while (p < plim) {
	if (*p++) return(FALSE);
    }
    return(TRUE);
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/* The following functions sometimes return a DONT_KNOW value. */
#define DONT_KNOW  2

#ifdef SMALL_CONFIG
# define GC_block_nearly_full1(hhdr, pat1) DONT_KNOW
# define GC_block_nearly_full3(hhdr, pat1, pat2) DONT_KNOW
# define GC_block_nearly_full(hhdr) DONT_KNOW
#else

/*
 * Test whether nearly all of the mark words consist of the same
 * repeating pattern.
 */
#define FULL_THRESHOLD (MARK_BITS_SZ/16)

GC_bool GC_block_nearly_full1(hhdr, pat1)
hdr *hhdr;
word pat1;
{
    unsigned i;
    unsigned misses = 0;
    GC_ASSERT((MARK_BITS_SZ & 1) == 0);
    for (i = 0; i < MARK_BITS_SZ; ++i) {
	if ((hhdr -> hb_marks[i] | ~pat1) != ONES) {
	    if (++misses > FULL_THRESHOLD) return FALSE;
	}
    }
    return TRUE;
}

/*
 * Test whether the same repeating 3 word pattern occurs in nearly
 * all the mark bit slots.
 * This is used as a heuristic, so we're a bit sloppy and ignore
 * the last one or two words.
 */
GC_bool GC_block_nearly_full3(hhdr, pat1, pat2, pat3)
hdr *hhdr;
word pat1, pat2, pat3;
{
    unsigned i;
    unsigned misses = 0;

    if (MARK_BITS_SZ < 4) {
      return DONT_KNOW;
    }
    for (i = 0; i < MARK_BITS_SZ - 2; i += 3) {
	if ((hhdr -> hb_marks[i] | ~pat1) != ONES) {
	    if (++misses > FULL_THRESHOLD) return FALSE;
	}
	if ((hhdr -> hb_marks[i+1] | ~pat2) != ONES) {
	    if (++misses > FULL_THRESHOLD) return FALSE;
	}
	if ((hhdr -> hb_marks[i+2] | ~pat3) != ONES) {
	    if (++misses > FULL_THRESHOLD) return FALSE;
	}
    }
    return TRUE;
}

/* Check whether a small object block is nearly full by looking at only */
/* the mark bits.							*/
/* We manually precomputed the mark bit patterns that need to be 	*/
/* checked for, and we give up on the ones that are unlikely to occur,	*/
/* or have period > 3.							*/
/* This would be a lot easier with a mark bit per object instead of per	*/
/* word, but that would rewuire computing object numbers in the mark	*/
/* loop, which would require different data structures ...		*/
GC_bool GC_block_nearly_full(hhdr)
hdr *hhdr;
{
    int sz = hhdr -> hb_sz;

#   if CPP_WORDSZ != 32 && CPP_WORDSZ != 64
      return DONT_KNOW;	/* Shouldn't be used in any standard config.	*/
#   endif
    if (0 != HDR_WORDS) return DONT_KNOW;
	/* Also shouldn't happen */
#   if CPP_WORDSZ == 32
      switch(sz) {
        case 1:
	  return GC_block_nearly_full1(hhdr, 0xffffffffl);
	case 2:
	  return GC_block_nearly_full1(hhdr, 0x55555555l);
	case 4:
	  return GC_block_nearly_full1(hhdr, 0x11111111l);
	case 6:
	  return GC_block_nearly_full3(hhdr, 0x41041041l,
					      0x10410410l,
					       0x04104104l);
	case 8:
	  return GC_block_nearly_full1(hhdr, 0x01010101l);
	case 12:
	  return GC_block_nearly_full3(hhdr, 0x01001001l,
					      0x10010010l,
					       0x00100100l);
	case 16:
	  return GC_block_nearly_full1(hhdr, 0x00010001l);
	case 32:
	  return GC_block_nearly_full1(hhdr, 0x00000001l);
	default:
	  return DONT_KNOW;
      }
#   endif
#   if CPP_WORDSZ == 64
      switch(sz) {
        case 1:
	  return GC_block_nearly_full1(hhdr, 0xffffffffffffffffl);
	case 2:
	  return GC_block_nearly_full1(hhdr, 0x5555555555555555l);
	case 4:
	  return GC_block_nearly_full1(hhdr, 0x1111111111111111l);
	case 6:
	  return GC_block_nearly_full3(hhdr, 0x1041041041041041l,
					       0x4104104104104104l,
					         0x0410410410410410l);
	case 8:
	  return GC_block_nearly_full1(hhdr, 0x0101010101010101l);
	case 12:
	  return GC_block_nearly_full3(hhdr, 0x1001001001001001l,
					       0x0100100100100100l,
					         0x0010010010010010l);
	case 16:
	  return GC_block_nearly_full1(hhdr, 0x0001000100010001l);
	case 32:
	  return GC_block_nearly_full1(hhdr, 0x0000000100000001l);
	default:
	  return DONT_KNOW;
      }
#   endif
}
#endif /* !SMALL_CONFIG */

Tom Tromey committed
204 205 206 207 208 209 210 211 212 213 214
# ifdef GATHERSTATS
#   define INCR_WORDS(sz) n_words_found += (sz)
# else
#   define INCR_WORDS(sz)
# endif
/*
 * Restore unmarked small objects in h of size sz to the object
 * free list.  Returns the new list.
 * Clears unmarked objects.
 */
/*ARGSUSED*/
215
ptr_t GC_reclaim_clear(hbp, hhdr, sz, list)
Tom Tromey committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
register struct hblk *hbp;	/* ptr to current heap block		*/
register hdr * hhdr;
register ptr_t list;
register word sz;
{
    register int word_no;
    register word *p, *q, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif        
    
    p = (word *)(hbp->hb_body);
    word_no = HDR_WORDS;
    plim = (word *)((((word)hbp) + HBLKSIZE)
		   - WORDS_TO_BYTES(sz));

    /* go through all words in block */
	while( p <= plim )  {
	    if( mark_bit_from_hdr(hhdr, word_no) ) {
		p += sz;
	    } else {
		INCR_WORDS(sz);
		/* object is available - put on list */
		    obj_link(p) = list;
		    list = ((ptr_t)p);
		/* Clear object, advance p to next object in the process */
		    q = p + sz;
                    p++; /* Skip link field */
244 245 246 247 248 249 250 251 252
#		    if defined(SMALL_CONFIG) && defined(ALIGN_DOUBLE)
		      /* We assert that sz must be even	*/
		      *p++ = 0;
		      while (p < q) {
			CLEAR_DOUBLE(p);
			p += 2;
		      }
#		    else
                      while (p < q) {
Tom Tromey committed
253
			*p++ = 0;
254 255
		      }
#		    endif
Tom Tromey committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
	    }
	    word_no += sz;
	}
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
}

#ifndef SMALL_CONFIG

/*
 * A special case for 2 word composite objects (e.g. cons cells):
 */
/*ARGSUSED*/
271
ptr_t GC_reclaim_clear2(hbp, hhdr, list)
Tom Tromey committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
register struct hblk *hbp;	/* ptr to current heap block		*/
hdr * hhdr;
register ptr_t list;
{
    register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]);
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    register word mark_word;
    register int i;
#   define DO_OBJ(start_displ) \
	if (!(mark_word & ((word)1 << start_displ))) { \
	    p[start_displ] = (word)list; \
	    list = (ptr_t)(p+start_displ); \
	    p[start_displ+1] = 0; \
	    INCR_WORDS(2); \
	}
    
    p = (word *)(hbp->hb_body);
    plim = (word *)(((word)hbp) + HBLKSIZE);

    /* go through all words in block */
	while( p < plim )  {
	    mark_word = *mark_word_addr++;
	    for (i = 0; i < WORDSZ; i += 8) {
		DO_OBJ(0);
		DO_OBJ(2);
		DO_OBJ(4);
		DO_OBJ(6);
		p += 8;
		mark_word >>= 8;
	    }
	}	        
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
#   undef DO_OBJ
}

/*
 * Another special case for 4 word composite objects:
 */
/*ARGSUSED*/
317
ptr_t GC_reclaim_clear4(hbp, hhdr, list)
Tom Tromey committed
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
register struct hblk *hbp;	/* ptr to current heap block		*/
hdr * hhdr;
register ptr_t list;
{
    register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]);
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    register word mark_word;
#   define DO_OBJ(start_displ) \
	if (!(mark_word & ((word)1 << start_displ))) { \
	    p[start_displ] = (word)list; \
	    list = (ptr_t)(p+start_displ); \
	    p[start_displ+1] = 0; \
333
	    CLEAR_DOUBLE(p + start_displ + 2); \
Tom Tromey committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
	    INCR_WORDS(4); \
	}
    
    p = (word *)(hbp->hb_body);
    plim = (word *)(((word)hbp) + HBLKSIZE);

    /* go through all words in block */
	while( p < plim )  {
	    mark_word = *mark_word_addr++;
	    DO_OBJ(0);
	    DO_OBJ(4);
	    DO_OBJ(8);
	    DO_OBJ(12);
	    DO_OBJ(16);
	    DO_OBJ(20);
	    DO_OBJ(24);
	    DO_OBJ(28);
#	    if CPP_WORDSZ == 64
	      DO_OBJ(32);
	      DO_OBJ(36);
	      DO_OBJ(40);
	      DO_OBJ(44);
	      DO_OBJ(48);
	      DO_OBJ(52);
	      DO_OBJ(56);
	      DO_OBJ(60);
#	    endif
	    p += WORDSZ;
	}	        
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
#   undef DO_OBJ
}

#endif /* !SMALL_CONFIG */

/* The same thing, but don't clear objects: */
/*ARGSUSED*/
374
ptr_t GC_reclaim_uninit(hbp, hhdr, sz, list)
Tom Tromey committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
register struct hblk *hbp;	/* ptr to current heap block		*/
register hdr * hhdr;
register ptr_t list;
register word sz;
{
    register int word_no;
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    
    p = (word *)(hbp->hb_body);
    word_no = HDR_WORDS;
    plim = (word *)((((word)hbp) + HBLKSIZE)
		   - WORDS_TO_BYTES(sz));

    /* go through all words in block */
	while( p <= plim )  {
	    if( !mark_bit_from_hdr(hhdr, word_no) ) {
		INCR_WORDS(sz);
		/* object is available - put on list */
		    obj_link(p) = list;
		    list = ((ptr_t)p);
	    }
	    p += sz;
	    word_no += sz;
	}
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
}

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
/* Don't really reclaim objects, just check for unmarked ones: */
/*ARGSUSED*/
void GC_reclaim_check(hbp, hhdr, sz)
register struct hblk *hbp;	/* ptr to current heap block		*/
register hdr * hhdr;
register word sz;
{
    register int word_no;
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    
    p = (word *)(hbp->hb_body);
    word_no = HDR_WORDS;
    plim = (word *)((((word)hbp) + HBLKSIZE)
		   - WORDS_TO_BYTES(sz));

    /* go through all words in block */
	while( p <= plim )  {
	    if( !mark_bit_from_hdr(hhdr, word_no) ) {
		FOUND_FREE(hbp, word_no);
	    }
	    p += sz;
	    word_no += sz;
	}
}

Tom Tromey committed
436 437 438 439 440
#ifndef SMALL_CONFIG
/*
 * Another special case for 2 word atomic objects:
 */
/*ARGSUSED*/
441
ptr_t GC_reclaim_uninit2(hbp, hhdr, list)
Tom Tromey committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
register struct hblk *hbp;	/* ptr to current heap block		*/
hdr * hhdr;
register ptr_t list;
{
    register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]);
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    register word mark_word;
    register int i;
#   define DO_OBJ(start_displ) \
	if (!(mark_word & ((word)1 << start_displ))) { \
	    p[start_displ] = (word)list; \
	    list = (ptr_t)(p+start_displ); \
	    INCR_WORDS(2); \
	}
    
    p = (word *)(hbp->hb_body);
    plim = (word *)(((word)hbp) + HBLKSIZE);

    /* go through all words in block */
	while( p < plim )  {
	    mark_word = *mark_word_addr++;
	    for (i = 0; i < WORDSZ; i += 8) {
		DO_OBJ(0);
		DO_OBJ(2);
		DO_OBJ(4);
		DO_OBJ(6);
		p += 8;
		mark_word >>= 8;
	    }
	}	        
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
#   undef DO_OBJ
}

/*
 * Another special case for 4 word atomic objects:
 */
/*ARGSUSED*/
486
ptr_t GC_reclaim_uninit4(hbp, hhdr, list)
Tom Tromey committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
register struct hblk *hbp;	/* ptr to current heap block		*/
hdr * hhdr;
register ptr_t list;
{
    register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]);
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    register word mark_word;
#   define DO_OBJ(start_displ) \
	if (!(mark_word & ((word)1 << start_displ))) { \
	    p[start_displ] = (word)list; \
	    list = (ptr_t)(p+start_displ); \
	    INCR_WORDS(4); \
	}
    
    p = (word *)(hbp->hb_body);
    plim = (word *)(((word)hbp) + HBLKSIZE);

    /* go through all words in block */
	while( p < plim )  {
	    mark_word = *mark_word_addr++;
	    DO_OBJ(0);
	    DO_OBJ(4);
	    DO_OBJ(8);
	    DO_OBJ(12);
	    DO_OBJ(16);
	    DO_OBJ(20);
	    DO_OBJ(24);
	    DO_OBJ(28);
#	    if CPP_WORDSZ == 64
	      DO_OBJ(32);
	      DO_OBJ(36);
	      DO_OBJ(40);
	      DO_OBJ(44);
	      DO_OBJ(48);
	      DO_OBJ(52);
	      DO_OBJ(56);
	      DO_OBJ(60);
#	    endif
	    p += WORDSZ;
	}	        
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
#   undef DO_OBJ
}

/* Finally the one word case, which never requires any clearing: */
/*ARGSUSED*/
539
ptr_t GC_reclaim1(hbp, hhdr, list)
Tom Tromey committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
register struct hblk *hbp;	/* ptr to current heap block		*/
hdr * hhdr;
register ptr_t list;
{
    register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]);
    register word *p, *plim;
#   ifdef GATHERSTATS
        register int n_words_found = 0;
#   endif
    register word mark_word;
    register int i;
#   define DO_OBJ(start_displ) \
	if (!(mark_word & ((word)1 << start_displ))) { \
	    p[start_displ] = (word)list; \
	    list = (ptr_t)(p+start_displ); \
	    INCR_WORDS(1); \
	}
    
    p = (word *)(hbp->hb_body);
    plim = (word *)(((word)hbp) + HBLKSIZE);

    /* go through all words in block */
	while( p < plim )  {
	    mark_word = *mark_word_addr++;
	    for (i = 0; i < WORDSZ; i += 4) {
		DO_OBJ(0);
		DO_OBJ(1);
		DO_OBJ(2);
		DO_OBJ(3);
		p += 4;
		mark_word >>= 4;
	    }
	}	        
#   ifdef GATHERSTATS
	GC_mem_found += n_words_found;
#   endif
    return(list);
#   undef DO_OBJ
}

#endif /* !SMALL_CONFIG */

/*
 * Restore unmarked small objects in the block pointed to by hbp
 * to the appropriate object free list.
 * If entirely empty blocks are to be completely deallocated, then
 * caller should perform that check.
 */
588
void GC_reclaim_small_nonempty_block(hbp, report_if_found)
Tom Tromey committed
589
register struct hblk *hbp;	/* ptr to current heap block		*/
590
int report_if_found;		/* Abort if a reclaimable object is found */
Tom Tromey committed
591 592
{
    hdr * hhdr;
593 594 595 596 597
    word sz;		/* size of objects in current block	*/
    struct obj_kind * ok;
    ptr_t * flh;
    int kind;
    GC_bool full;
Tom Tromey committed
598 599 600 601 602 603 604 605
    
    hhdr = HDR(hbp);
    sz = hhdr -> hb_sz;
    hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no;
    kind = hhdr -> hb_obj_kind;
    ok = &GC_obj_kinds[kind];
    flh = &(ok -> ok_freelist[sz]);

606 607 608
    if (report_if_found) {
	GC_reclaim_check(hbp, hhdr, sz);
    } else if (ok -> ok_init) {
Tom Tromey committed
609 610 611
      switch(sz) {
#      ifndef SMALL_CONFIG
        case 1:
612 613 614 615 616
	    full = GC_block_nearly_full1(hhdr, 0xffffffffl);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
	    /* In the DONT_KNOW case, we let reclaim fault.	*/
            *flh = GC_reclaim1(hbp, hhdr, *flh);
Tom Tromey committed
617 618
            break;
        case 2:
619 620 621 622
	    full = GC_block_nearly_full1(hhdr, 0x55555555l);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_clear2(hbp, hhdr, *flh);
Tom Tromey committed
623 624
            break;
        case 4:
625 626 627 628
	    full = GC_block_nearly_full1(hhdr, 0x11111111l);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_clear4(hbp, hhdr, *flh);
Tom Tromey committed
629 630 631
            break;
#      endif
        default:
632 633 634 635
	    full = GC_block_nearly_full(hhdr);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_clear(hbp, hhdr, sz, *flh);
Tom Tromey committed
636 637 638 639 640 641
            break;
      }
    } else {
      switch(sz) {
#      ifndef SMALL_CONFIG
        case 1:
642 643 644 645
	    full = GC_block_nearly_full1(hhdr, 0xffffffffl);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim1(hbp, hhdr, *flh);
Tom Tromey committed
646 647
            break;
        case 2:
648 649 650 651
	    full = GC_block_nearly_full1(hhdr, 0x55555555l);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_uninit2(hbp, hhdr, *flh);
Tom Tromey committed
652 653
            break;
        case 4:
654 655 656 657
	    full = GC_block_nearly_full1(hhdr, 0x11111111l);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_uninit4(hbp, hhdr, *flh);
Tom Tromey committed
658 659 660
            break;
#      endif
        default:
661 662 663 664
	    full = GC_block_nearly_full(hhdr);
	    if (TRUE == full) goto out;
	    if (FALSE == full) GC_write_hint(hbp);
            *flh = GC_reclaim_uninit(hbp, hhdr, sz, *flh);
Tom Tromey committed
665 666 667
            break;
      }
    } 
668
out:
Tom Tromey committed
669 670 671 672 673 674 675 676
    if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(hhdr);
}

/*
 * Restore an unmarked large object or an entirely empty blocks of small objects
 * to the heap block free list.
 * Otherwise enqueue the block for later processing
 * by GC_reclaim_small_nonempty_block.
677 678
 * If report_if_found is TRUE, then process any block immediately, and
 * simply report free objects; do not actually reclaim them.
Tom Tromey committed
679
 */
680
void GC_reclaim_block(hbp, report_if_found)
Tom Tromey committed
681
register struct hblk *hbp;	/* ptr to current heap block		*/
682
word report_if_found;		/* Abort if a reclaimable object is found */
Tom Tromey committed
683 684 685 686 687 688 689 690 691 692 693 694
{
    register hdr * hhdr;
    register word sz;		/* size of objects in current block	*/
    register struct obj_kind * ok;
    struct hblk ** rlh;

    hhdr = HDR(hbp);
    sz = hhdr -> hb_sz;
    ok = &GC_obj_kinds[hhdr -> hb_obj_kind];

    if( sz > MAXOBJSZ ) {  /* 1 big object */
        if( !mark_bit_from_hdr(hhdr, HDR_WORDS) ) {
695 696 697 698
	    if (report_if_found) {
	      FOUND_FREE(hbp, HDR_WORDS);
	    } else {
#	      ifdef GATHERSTATS
Tom Tromey committed
699
	        GC_mem_found += sz;
700 701 702
#	      endif
	      GC_freehblk(hbp);
	    }
Tom Tromey committed
703 704 705
	}
    } else {
        GC_bool empty = GC_block_empty(hhdr);
706 707
        if (report_if_found) {
    	  GC_reclaim_small_nonempty_block(hbp, (int)report_if_found);
Tom Tromey committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
        } else if (empty) {
#	  ifdef GATHERSTATS
            GC_mem_found += BYTES_TO_WORDS(HBLKSIZE);
#	  endif
          GC_freehblk(hbp);
        } else {
          /* group of smaller objects, enqueue the real work */
          rlh = &(ok -> ok_reclaim_list[sz]);
          hhdr -> hb_next = *rlh;
          *rlh = hbp;
        }
    }
}

#if !defined(NO_DEBUGGING)
/* Routines to gather and print heap block info 	*/
/* intended for debugging.  Otherwise should be called	*/
/* with lock.						*/
static size_t number_of_blocks;
static size_t total_bytes;

/* Number of set bits in a word.  Not performance critical.	*/
static int set_bits(n)
word n;
{
    register word m = n;
    register int result = 0;
    
    while (m > 0) {
    	if (m & 1) result++;
    	m >>= 1;
    }
    return(result);
}

/* Return the number of set mark bits in the given header	*/
int GC_n_set_marks(hhdr)
hdr * hhdr;
{
    register int result = 0;
    register int i;
    
    for (i = 0; i < MARK_BITS_SZ; i++) {
        result += set_bits(hhdr -> hb_marks[i]);
    }
    return(result);
}

/*ARGSUSED*/
void GC_print_block_descr(h, dummy)
struct hblk *h;
word dummy;
{
    register hdr * hhdr = HDR(h);
    register size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
    
    GC_printf3("(%lu:%lu,%lu)", (unsigned long)(hhdr -> hb_obj_kind),
    			        (unsigned long)bytes,
    			        (unsigned long)(GC_n_set_marks(hhdr)));
    bytes += HDR_BYTES + HBLKSIZE-1;
    bytes &= ~(HBLKSIZE-1);
    total_bytes += bytes;
    number_of_blocks++;
}

void GC_print_block_list()
{
    GC_printf0("(kind(0=ptrfree,1=normal,2=unc.,3=stubborn):size_in_bytes, #_marks_set)\n");
    number_of_blocks = 0;
    total_bytes = 0;
    GC_apply_to_all_blocks(GC_print_block_descr, (word)0);
    GC_printf2("\nblocks = %lu, bytes = %lu\n",
    	       (unsigned long)number_of_blocks,
    	       (unsigned long)total_bytes);
}

#endif /* NO_DEBUGGING */

/*
787 788
 * Perform GC_reclaim_block on the entire heap, after first clearing
 * small object free lists (if we are not just looking for leaks).
Tom Tromey committed
789
 */
790 791
void GC_start_reclaim(report_if_found)
int report_if_found;		/* Abort if a GC_reclaimable object is found */
Tom Tromey committed
792 793 794 795 796 797 798 799 800 801 802 803
{
    int kind;
    
    /* Clear reclaim- and free-lists */
      for (kind = 0; kind < GC_n_kinds; kind++) {
        register ptr_t *fop;
        register ptr_t *lim;
        register struct hblk ** rlp;
        register struct hblk ** rlim;
        register struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list;
        
        if (rlist == 0) continue;	/* This kind not used.	*/
804
        if (!report_if_found) {
Tom Tromey committed
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
            lim = &(GC_obj_kinds[kind].ok_freelist[MAXOBJSZ+1]);
	    for( fop = GC_obj_kinds[kind].ok_freelist; fop < lim; fop++ ) {
	      *fop = 0;
	    }
	} /* otherwise free list objects are marked, 	*/
	  /* and its safe to leave them			*/
	rlim = rlist + MAXOBJSZ+1;
	for( rlp = rlist; rlp < rlim; rlp++ ) {
	    *rlp = 0;
	}
      }
    
#   ifdef PRINTBLOCKS
        GC_printf0("GC_reclaim: current block sizes:\n");
        GC_print_block_list();
#   endif

  /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */
  /* or enqueue the block for later processing.				   */
824
    GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found);
825 826 827 828 829 830

# ifdef EAGER_SWEEP
    /* This is a very stupid thing to do.  We make it possible anyway,	*/
    /* so that you can convince yourself that it really is very stupid.	*/
    GC_reclaim_all((GC_stop_func)0, FALSE);
# endif
Tom Tromey committed
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
    
}

/*
 * Sweep blocks of the indicated object size and kind until either the
 * appropriate free list is nonempty, or there are no more blocks to
 * sweep.
 */
void GC_continue_reclaim(sz, kind)
word sz;	/* words */
int kind;
{
    register hdr * hhdr;
    register struct hblk * hbp;
    register struct obj_kind * ok = &(GC_obj_kinds[kind]);
    struct hblk ** rlh = ok -> ok_reclaim_list;
    ptr_t *flh = &(ok -> ok_freelist[sz]);
    
    if (rlh == 0) return;	/* No blocks of this kind.	*/
    rlh += sz;
    while ((hbp = *rlh) != 0) {
        hhdr = HDR(hbp);
        *rlh = hhdr -> hb_next;
        GC_reclaim_small_nonempty_block(hbp, FALSE);
        if (*flh != 0) break;
    }
}

/*
 * Reclaim all small blocks waiting to be reclaimed.
 * Abort and return FALSE when/if (*stop_func)() returns TRUE.
 * If this returns TRUE, then it's safe to restart the world
 * with incorrectly cleared mark bits.
864
 * If ignore_old is TRUE, then reclaim only blocks that have been 
Tom Tromey committed
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
 * recently reclaimed, and discard the rest.
 * Stop_func may be 0.
 */
GC_bool GC_reclaim_all(stop_func, ignore_old)
GC_stop_func stop_func;
GC_bool ignore_old;
{
    register word sz;
    register int kind;
    register hdr * hhdr;
    register struct hblk * hbp;
    register struct obj_kind * ok;
    struct hblk ** rlp;
    struct hblk ** rlh;
#   ifdef PRINTTIMES
	CLOCK_TYPE start_time;
	CLOCK_TYPE done_time;
	
	GET_TIME(start_time);
#   endif
    
    for (kind = 0; kind < GC_n_kinds; kind++) {
    	ok = &(GC_obj_kinds[kind]);
    	rlp = ok -> ok_reclaim_list;
    	if (rlp == 0) continue;
    	for (sz = 1; sz <= MAXOBJSZ; sz++) {
    	    rlh = rlp + sz;
    	    while ((hbp = *rlh) != 0) {
    	        if (stop_func != (GC_stop_func)0 && (*stop_func)()) {
    	            return(FALSE);
    	        }
        	hhdr = HDR(hbp);
        	*rlh = hhdr -> hb_next;
        	if (!ignore_old || hhdr -> hb_last_reclaimed == GC_gc_no - 1) {
        	    /* It's likely we'll need it this time, too	*/
        	    /* It's been touched recently, so this	*/
        	    /* shouldn't trigger paging.		*/
        	    GC_reclaim_small_nonempty_block(hbp, FALSE);
        	}
            }
        }
    }
#   ifdef PRINTTIMES
	GET_TIME(done_time);
	GC_printf1("Disposing of reclaim lists took %lu msecs\n",
	           MS_TIME_DIFF(done_time,start_time));
#   endif
    return(TRUE);
}