Blame headers.c

Packit d28291
/*
Packit d28291
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
Packit d28291
 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
Packit d28291
 * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
Packit d28291
 *
Packit d28291
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
Packit d28291
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
Packit d28291
 *
Packit d28291
 * Permission is hereby granted to use or copy this program
Packit d28291
 * for any purpose,  provided the above notices are retained on all copies.
Packit d28291
 * Permission to modify the code and to distribute modified code is granted,
Packit d28291
 * provided the above notices are retained, and a notice that the code was
Packit d28291
 * modified is included with the above copyright notice.
Packit d28291
 */
Packit d28291
Packit d28291
#include "private/gc_priv.h"
Packit d28291
Packit d28291
/*
Packit d28291
 * This implements:
Packit d28291
 * 1. allocation of heap block headers
Packit d28291
 * 2. A map from addresses to heap block addresses to heap block headers
Packit d28291
 *
Packit d28291
 * Access speed is crucial.  We implement an index structure based on a 2
Packit d28291
 * level tree.
Packit d28291
 */
Packit d28291
Packit d28291
STATIC bottom_index * GC_all_bottom_indices = 0;
Packit d28291
                                /* Pointer to first (lowest addr) */
Packit d28291
                                /* bottom_index.                  */
Packit d28291
Packit d28291
STATIC bottom_index * GC_all_bottom_indices_end = 0;
Packit d28291
                                /* Pointer to last (highest addr) */
Packit d28291
                                /* bottom_index.                  */
Packit d28291
Packit d28291
/* Non-macro version of header location routine */
Packit d28291
GC_INNER hdr * GC_find_header(ptr_t h)
Packit d28291
{
Packit d28291
#   ifdef HASH_TL
Packit d28291
        hdr * result;
Packit d28291
        GET_HDR(h, result);
Packit d28291
        return(result);
Packit d28291
#   else
Packit d28291
        return(HDR_INNER(h));
Packit d28291
#   endif
Packit d28291
}
Packit d28291
Packit d28291
/* Handle a header cache miss.  Returns a pointer to the        */
Packit d28291
/* header corresponding to p, if p can possibly be a valid      */
Packit d28291
/* object pointer, and 0 otherwise.                             */
Packit d28291
/* GUARANTEED to return 0 for a pointer past the first page     */
Packit d28291
/* of an object unless both GC_all_interior_pointers is set     */
Packit d28291
/* and p is in fact a valid object pointer.                     */
Packit d28291
/* Never returns a pointer to a free hblk.                      */
Packit d28291
GC_INNER hdr *
Packit d28291
#ifdef PRINT_BLACK_LIST
Packit d28291
  GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source)
Packit d28291
#else
Packit d28291
  GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce)
Packit d28291
#endif
Packit d28291
{
Packit d28291
  hdr *hhdr;
Packit d28291
  HC_MISS();
Packit d28291
  GET_HDR(p, hhdr);
Packit d28291
  if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
Packit d28291
    if (GC_all_interior_pointers) {
Packit d28291
      if (hhdr != 0) {
Packit d28291
        ptr_t current = p;
Packit d28291
Packit d28291
        current = (ptr_t)HBLKPTR(current);
Packit d28291
        do {
Packit d28291
            current = current - HBLKSIZE*(word)hhdr;
Packit d28291
            hhdr = HDR(current);
Packit d28291
        } while(IS_FORWARDING_ADDR_OR_NIL(hhdr));
Packit d28291
        /* current points to near the start of the large object */
Packit d28291
        if (hhdr -> hb_flags & IGNORE_OFF_PAGE)
Packit d28291
            return 0;
Packit d28291
        if (HBLK_IS_FREE(hhdr)
Packit d28291
            || p - current >= (ptrdiff_t)(hhdr->hb_sz)) {
Packit d28291
            GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
Packit d28291
            /* Pointer past the end of the block */
Packit d28291
            return 0;
Packit d28291
        }
Packit d28291
      } else {
Packit d28291
        GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
Packit d28291
        /* And return zero: */
Packit d28291
      }
Packit d28291
      GC_ASSERT(hhdr == 0 || !HBLK_IS_FREE(hhdr));
Packit d28291
      return hhdr;
Packit d28291
      /* Pointers past the first page are probably too rare     */
Packit d28291
      /* to add them to the cache.  We don't.                   */
Packit d28291
      /* And correctness relies on the fact that we don't.      */
Packit d28291
    } else {
Packit d28291
      if (hhdr == 0) {
Packit d28291
        GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
Packit d28291
      }
Packit d28291
      return 0;
Packit d28291
    }
Packit d28291
  } else {
Packit d28291
    if (HBLK_IS_FREE(hhdr)) {
Packit d28291
      GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
Packit d28291
      return 0;
Packit d28291
    } else {
Packit d28291
      hce -> block_addr = (word)(p) >> LOG_HBLKSIZE;
Packit d28291
      hce -> hce_hdr = hhdr;
Packit d28291
      return hhdr;
Packit d28291
    }
Packit d28291
  }
Packit d28291
}
Packit d28291
Packit d28291
/* Routines to dynamically allocate collector data structures that will */
Packit d28291
/* never be freed.                                                      */
Packit d28291
Packit d28291
static ptr_t scratch_free_ptr = 0;
Packit d28291
Packit d28291
/* GC_scratch_last_end_ptr is end point of last obtained scratch area.  */
Packit d28291
/* GC_scratch_end_ptr is end point of current scratch area.             */
Packit d28291
Packit d28291
GC_INNER ptr_t GC_scratch_alloc(size_t bytes)
Packit d28291
{
Packit d28291
    ptr_t result = scratch_free_ptr;
Packit d28291
    size_t bytes_to_get;
Packit d28291
Packit d28291
    bytes = ROUNDUP_GRANULE_SIZE(bytes);
Packit d28291
    for (;;) {
Packit d28291
        scratch_free_ptr += bytes;
Packit d28291
        if ((word)scratch_free_ptr <= (word)GC_scratch_end_ptr) {
Packit d28291
            /* Unallocated space of scratch buffer has enough size. */
Packit d28291
            return result;
Packit d28291
        }
Packit d28291
Packit d28291
        if (bytes >= MINHINCR * HBLKSIZE) {
Packit d28291
            bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes);
Packit d28291
            result = (ptr_t)GET_MEM(bytes_to_get);
Packit d28291
            GC_add_to_our_memory(result, bytes_to_get);
Packit d28291
            /* Undo scratch free area pointer update; get memory directly. */
Packit d28291
            scratch_free_ptr -= bytes;
Packit d28291
            if (result != NULL) {
Packit d28291
                /* Update end point of last obtained area (needed only  */
Packit d28291
                /* by GC_register_dynamic_libraries for some targets).  */
Packit d28291
                GC_scratch_last_end_ptr = result + bytes;
Packit d28291
            }
Packit d28291
            return result;
Packit d28291
        }
Packit d28291
Packit d28291
        bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR * HBLKSIZE);
Packit d28291
                                                /* round up for safety */
Packit d28291
        result = (ptr_t)GET_MEM(bytes_to_get);
Packit d28291
        GC_add_to_our_memory(result, bytes_to_get);
Packit d28291
        if (NULL == result) {
Packit d28291
            WARN("Out of memory - trying to allocate requested amount"
Packit d28291
                 " (%" WARN_PRIdPTR " bytes)...\n", (word)bytes);
Packit d28291
            scratch_free_ptr -= bytes; /* Undo free area pointer update */
Packit d28291
            bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes);
Packit d28291
            result = (ptr_t)GET_MEM(bytes_to_get);
Packit d28291
            GC_add_to_our_memory(result, bytes_to_get);
Packit d28291
            return result;
Packit d28291
        }
Packit d28291
        /* Update scratch area pointers and retry.      */
Packit d28291
        scratch_free_ptr = result;
Packit d28291
        GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get;
Packit d28291
        GC_scratch_last_end_ptr = GC_scratch_end_ptr;
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
static hdr * hdr_free_list = 0;
Packit d28291
Packit d28291
/* Return an uninitialized header */
Packit d28291
static hdr * alloc_hdr(void)
Packit d28291
{
Packit d28291
    register hdr * result;
Packit d28291
Packit d28291
    if (hdr_free_list == 0) {
Packit d28291
        result = (hdr *)GC_scratch_alloc(sizeof(hdr));
Packit d28291
    } else {
Packit d28291
        result = hdr_free_list;
Packit d28291
        hdr_free_list = (hdr *) (result -> hb_next);
Packit d28291
    }
Packit d28291
    return(result);
Packit d28291
}
Packit d28291
Packit d28291
GC_INLINE void free_hdr(hdr * hhdr)
Packit d28291
{
Packit d28291
    hhdr -> hb_next = (struct hblk *) hdr_free_list;
Packit d28291
    hdr_free_list = hhdr;
Packit d28291
}
Packit d28291
Packit d28291
#ifdef COUNT_HDR_CACHE_HITS
Packit d28291
  /* Used for debugging/profiling (the symbols are externally visible). */
Packit d28291
  word GC_hdr_cache_hits = 0;
Packit d28291
  word GC_hdr_cache_misses = 0;
Packit d28291
#endif
Packit d28291
Packit d28291
GC_INNER void GC_init_headers(void)
Packit d28291
{
Packit d28291
    register unsigned i;
Packit d28291
Packit d28291
    GC_all_nils = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index));
Packit d28291
    if (GC_all_nils == NULL) {
Packit d28291
      GC_err_printf("Insufficient memory for GC_all_nils\n");
Packit d28291
      EXIT();
Packit d28291
    }
Packit d28291
    BZERO(GC_all_nils, sizeof(bottom_index));
Packit d28291
    for (i = 0; i < TOP_SZ; i++) {
Packit d28291
        GC_top_index[i] = GC_all_nils;
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
/* Make sure that there is a bottom level index block for address addr  */
Packit d28291
/* Return FALSE on failure.                                             */
Packit d28291
static GC_bool get_index(word addr)
Packit d28291
{
Packit d28291
    word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
Packit d28291
    bottom_index * r;
Packit d28291
    bottom_index * p;
Packit d28291
    bottom_index ** prev;
Packit d28291
    bottom_index *pi;
Packit d28291
Packit d28291
#   ifdef HASH_TL
Packit d28291
      word i = TL_HASH(hi);
Packit d28291
      bottom_index * old;
Packit d28291
Packit d28291
      old = p = GC_top_index[i];
Packit d28291
      while(p != GC_all_nils) {
Packit d28291
          if (p -> key == hi) return(TRUE);
Packit d28291
          p = p -> hash_link;
Packit d28291
      }
Packit d28291
      r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index));
Packit d28291
      if (r == 0) return(FALSE);
Packit d28291
      BZERO(r, sizeof (bottom_index));
Packit d28291
      r -> hash_link = old;
Packit d28291
      GC_top_index[i] = r;
Packit d28291
#   else
Packit d28291
      if (GC_top_index[hi] != GC_all_nils) return(TRUE);
Packit d28291
      r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index));
Packit d28291
      if (r == 0) return(FALSE);
Packit d28291
      GC_top_index[hi] = r;
Packit d28291
      BZERO(r, sizeof (bottom_index));
Packit d28291
#   endif
Packit d28291
    r -> key = hi;
Packit d28291
    /* Add it to the list of bottom indices */
Packit d28291
      prev = &GC_all_bottom_indices;    /* pointer to p */
Packit d28291
      pi = 0;                           /* bottom_index preceding p */
Packit d28291
      while ((p = *prev) != 0 && p -> key < hi) {
Packit d28291
        pi = p;
Packit d28291
        prev = &(p -> asc_link);
Packit d28291
      }
Packit d28291
      r -> desc_link = pi;
Packit d28291
      if (0 == p) {
Packit d28291
        GC_all_bottom_indices_end = r;
Packit d28291
      } else {
Packit d28291
        p -> desc_link = r;
Packit d28291
      }
Packit d28291
      r -> asc_link = p;
Packit d28291
      *prev = r;
Packit d28291
    return(TRUE);
Packit d28291
}
Packit d28291
Packit d28291
/* Install a header for block h.        */
Packit d28291
/* The header is uninitialized.         */
Packit d28291
/* Returns the header or 0 on failure.  */
Packit d28291
GC_INNER struct hblkhdr * GC_install_header(struct hblk *h)
Packit d28291
{
Packit d28291
    hdr * result;
Packit d28291
Packit d28291
    if (!get_index((word) h)) return(0);
Packit d28291
    result = alloc_hdr();
Packit d28291
    if (result) {
Packit d28291
      SET_HDR(h, result);
Packit d28291
#     ifdef USE_MUNMAP
Packit d28291
        result -> hb_last_reclaimed = (unsigned short)GC_gc_no;
Packit d28291
#     endif
Packit d28291
    }
Packit d28291
    return(result);
Packit d28291
}
Packit d28291
Packit d28291
/* Set up forwarding counts for block h of size sz */
Packit d28291
GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */)
Packit d28291
{
Packit d28291
    struct hblk * hbp;
Packit d28291
Packit d28291
    for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) {
Packit d28291
        if (!get_index((word) hbp)) return(FALSE);
Packit d28291
    }
Packit d28291
    if (!get_index((word)h + sz - 1)) return(FALSE);
Packit d28291
    for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) {
Packit d28291
        word i = HBLK_PTR_DIFF(hbp, h);
Packit d28291
Packit d28291
        SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i));
Packit d28291
    }
Packit d28291
    return(TRUE);
Packit d28291
}
Packit d28291
Packit d28291
/* Remove the header for block h */
Packit d28291
GC_INNER void GC_remove_header(struct hblk *h)
Packit d28291
{
Packit d28291
    hdr **ha;
Packit d28291
    GET_HDR_ADDR(h, ha);
Packit d28291
    free_hdr(*ha);
Packit d28291
    *ha = 0;
Packit d28291
}
Packit d28291
Packit d28291
/* Remove forwarding counts for h */
Packit d28291
GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */)
Packit d28291
{
Packit d28291
    register struct hblk * hbp;
Packit d28291
    for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) {
Packit d28291
        SET_HDR(hbp, 0);
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
/* Apply fn to all allocated blocks */
Packit d28291
/*VARARGS1*/
Packit d28291
void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data),
Packit d28291
                            word client_data)
Packit d28291
{
Packit d28291
    signed_word j;
Packit d28291
    bottom_index * index_p;
Packit d28291
Packit d28291
    for (index_p = GC_all_bottom_indices; index_p != 0;
Packit d28291
         index_p = index_p -> asc_link) {
Packit d28291
        for (j = BOTTOM_SZ-1; j >= 0;) {
Packit d28291
            if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) {
Packit d28291
                if (!HBLK_IS_FREE(index_p->index[j])) {
Packit d28291
                    (*fn)(((struct hblk *)
Packit d28291
                              (((index_p->key << LOG_BOTTOM_SZ) + (word)j)
Packit d28291
                               << LOG_HBLKSIZE)),
Packit d28291
                          client_data);
Packit d28291
                }
Packit d28291
                j--;
Packit d28291
             } else if (index_p->index[j] == 0) {
Packit d28291
                j--;
Packit d28291
             } else {
Packit d28291
                j -= (signed_word)(index_p->index[j]);
Packit d28291
             }
Packit d28291
         }
Packit d28291
     }
Packit d28291
}
Packit d28291
Packit d28291
/* Get the next valid block whose address is at least h */
Packit d28291
/* Return 0 if there is none.                           */
Packit d28291
GC_INNER struct hblk * GC_next_used_block(struct hblk *h)
Packit d28291
{
Packit d28291
    register bottom_index * bi;
Packit d28291
    register word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
Packit d28291
Packit d28291
    GET_BI(h, bi);
Packit d28291
    if (bi == GC_all_nils) {
Packit d28291
        register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
Packit d28291
        bi = GC_all_bottom_indices;
Packit d28291
        while (bi != 0 && bi -> key < hi) bi = bi -> asc_link;
Packit d28291
        j = 0;
Packit d28291
    }
Packit d28291
    while(bi != 0) {
Packit d28291
        while (j < BOTTOM_SZ) {
Packit d28291
            hdr * hhdr = bi -> index[j];
Packit d28291
            if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
Packit d28291
                j++;
Packit d28291
            } else {
Packit d28291
                if (!HBLK_IS_FREE(hhdr)) {
Packit d28291
                    return((struct hblk *)
Packit d28291
                              (((bi -> key << LOG_BOTTOM_SZ) + j)
Packit d28291
                               << LOG_HBLKSIZE));
Packit d28291
                } else {
Packit d28291
                    j += divHBLKSZ(hhdr -> hb_sz);
Packit d28291
                }
Packit d28291
            }
Packit d28291
        }
Packit d28291
        j = 0;
Packit d28291
        bi = bi -> asc_link;
Packit d28291
    }
Packit d28291
    return(0);
Packit d28291
}
Packit d28291
Packit d28291
/* Get the last (highest address) block whose address is        */
Packit d28291
/* at most h.  Return 0 if there is none.                       */
Packit d28291
/* Unlike the above, this may return a free block.              */
Packit d28291
GC_INNER struct hblk * GC_prev_block(struct hblk *h)
Packit d28291
{
Packit d28291
    register bottom_index * bi;
Packit d28291
    register signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
Packit d28291
Packit d28291
    GET_BI(h, bi);
Packit d28291
    if (bi == GC_all_nils) {
Packit d28291
        register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
Packit d28291
        bi = GC_all_bottom_indices_end;
Packit d28291
        while (bi != 0 && bi -> key > hi) bi = bi -> desc_link;
Packit d28291
        j = BOTTOM_SZ - 1;
Packit d28291
    }
Packit d28291
    while(bi != 0) {
Packit d28291
        while (j >= 0) {
Packit d28291
            hdr * hhdr = bi -> index[j];
Packit d28291
            if (0 == hhdr) {
Packit d28291
                --j;
Packit d28291
            } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
Packit d28291
                j -= (signed_word)hhdr;
Packit d28291
            } else {
Packit d28291
                return((struct hblk *)
Packit d28291
                          (((bi -> key << LOG_BOTTOM_SZ) + j)
Packit d28291
                               << LOG_HBLKSIZE));
Packit d28291
            }
Packit d28291
        }
Packit d28291
        j = BOTTOM_SZ - 1;
Packit d28291
        bi = bi -> desc_link;
Packit d28291
    }
Packit d28291
    return(0);
Packit d28291
}