OSDN Git Service

* config/m68k/m68k.c (notice_update_cc): Use SET_DEST and
[pf3gnuchains/gcc-fork.git] / boehm-gc / misc.c
index 32288a9..2b69a00 100644 (file)
@@ -1,6 +1,7 @@
 /* 
  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved.
  *
  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 
 
 #include <stdio.h>
+#include <limits.h>
+#ifndef _WIN32_WCE
 #include <signal.h>
+#endif
 
 #define I_HIDE_POINTERS        /* To make GC_call_with_alloc_lock visible */
-#include "gc_priv.h"
+#include "private/gc_pmark.h"
 
-#ifdef SOLARIS_THREADS
+#ifdef GC_SOLARIS_THREADS
 # include <sys/syscall.h>
 #endif
-#ifdef MSWIN32
+#if defined(MSWIN32) || defined(MSWINCE)
+# define WIN32_LEAN_AND_MEAN
+# define NOSERVICE
 # include <windows.h>
+# include <tchar.h>
 #endif
 
 # ifdef THREADS
        /* Critical section counter is defined in the M3 runtime        */
        /* That's all we use.                                           */
 #     else
-#      ifdef SOLARIS_THREADS
+#      ifdef GC_SOLARIS_THREADS
          mutex_t GC_allocate_ml;       /* Implicitly initialized.      */
 #      else
-#          ifdef WIN32_THREADS
-             GC_API CRITICAL_SECTION GC_allocate_ml;
-#          else
-#             if defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-                || defined(IRIX_JDK_THREADS)
-               pthread_t GC_lock_holder = NO_THREAD;
+#          if defined(GC_WIN32_THREADS) 
+#             if defined(GC_PTHREADS)
+                 pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
+#            elif defined(GC_DLL)
+                __declspec(dllexport) CRITICAL_SECTION GC_allocate_ml;
 #            else
-#              if defined(HPUX_THREADS)
+                CRITICAL_SECTION GC_allocate_ml;
+#            endif
+#          else
+#             if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS)
+#              if defined(USE_SPIN_LOCK)
+                 pthread_t GC_lock_holder = NO_THREAD;
+#              else
                  pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
-#              else 
+                 pthread_t GC_lock_holder = NO_THREAD;
+                       /* Used only for assertions, and to prevent      */
+                       /* recursive reentry in the system call wrapper. */
+#              endif 
+#            else
                  --> declare allocator lock here
-#              endif
 #            endif
 #         endif
 #      endif
 #   endif
 # endif
 
-#ifdef ECOS
+#if defined(NOSYS) || defined(ECOS)
 #undef STACKBASE
 #endif
 
+/* Dont unnecessarily call GC_register_main_static_data() in case      */
+/* dyn_load.c isn't linked in.                                         */
+#ifdef DYNAMIC_LOADING
+# define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data()
+#else
+# define GC_REGISTER_MAIN_STATIC_DATA() TRUE
+#endif
+
 GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */;
 
 
 GC_bool GC_debugging_started = FALSE;
        /* defined here so we don't have to load debug_malloc.o */
 
-void (*GC_check_heap)() = (void (*)())0;
+void (*GC_check_heap) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
+void (*GC_print_all_smashed) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
 
-void (*GC_start_call_back)() = (void (*)())0;
+void (*GC_start_call_back) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0;
 
 ptr_t GC_stackbottom = 0;
 
+#ifdef IA64
+  ptr_t GC_register_stackbottom = 0;
+#endif
+
 GC_bool GC_dont_gc = 0;
 
+GC_bool GC_dont_precollect = 0;
+
 GC_bool GC_quiet = 0;
 
+GC_bool GC_print_stats = 0;
+
+GC_bool GC_print_back_height = 0;
+
+#ifndef NO_DEBUGGING
+  GC_bool GC_dump_regularly = 0;  /* Generate regular debugging dumps. */
+#endif
+
+#ifdef KEEP_BACK_PTRS
+  long GC_backtraces = 0;      /* Number of random backtraces to       */
+                               /* generate for each GC.                */
+#endif
+
 #ifdef FIND_LEAK
   int GC_find_leak = 1;
 #else
   int GC_find_leak = 0;
 #endif
 
+#ifdef ALL_INTERIOR_POINTERS
+  int GC_all_interior_pointers = 1;
+#else
+  int GC_all_interior_pointers = 0;
+#endif
+
+long GC_large_alloc_warn_interval = 5;
+       /* Interval between unsuppressed warnings.      */
+
+long GC_large_alloc_warn_suppressed = 0;
+       /* Number of warnings suppressed so far.        */
+
 /*ARGSUSED*/
 GC_PTR GC_default_oom_fn GC_PROTO((size_t bytes_requested))
 {
@@ -94,6 +149,13 @@ GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn;
 
 extern signed_word GC_mem_found;
 
+void * GC_project2(arg1, arg2)
+void *arg1;
+void *arg2;
+{
+  return arg2;
+}
+
 # ifdef MERGE_SIZES
     /* Set things up so that GC_size_map[i] >= words(i),               */
     /* but not too much bigger                                         */
@@ -106,23 +168,33 @@ extern signed_word GC_mem_found;
     {
        register unsigned i;
 
-       /* Map size 0 to 1.  This avoids problems at lower levels. */
-         GC_size_map[0] = 1;
-       /* One word objects don't have to be 2 word aligned.       */
-         for (i = 1; i < sizeof(word); i++) {
-             GC_size_map[i] = 1;
+       /* Map size 0 to something bigger.                      */
+       /* This avoids problems at lower levels.                */
+       /* One word objects don't have to be 2 word aligned,    */
+       /* unless we're using mark bytes.                       */
+         for (i = 0; i < sizeof(word); i++) {
+             GC_size_map[i] = MIN_WORDS;
          }
-         GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word));
+#        if MIN_WORDS > 1
+           GC_size_map[sizeof(word)] = MIN_WORDS;
+#        else
+           GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word));
+#        endif
        for (i = sizeof(word) + 1; i <= 8 * sizeof(word); i++) {
-#           ifdef ALIGN_DOUBLE
-             GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1);
-#           else
-             GC_size_map[i] = ROUNDED_UP_WORDS(i);
-#           endif
+           GC_size_map[i] = ALIGNED_WORDS(i);
        }
        for (i = 8*sizeof(word) + 1; i <= 16 * sizeof(word); i++) {
              GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1);
        }
+#      ifdef GC_GCJ_SUPPORT
+          /* Make all sizes up to 32 words predictable, so that a      */
+          /* compiler can statically perform the same computation,     */
+          /* or at least a computation that results in similar size    */
+          /* classes.                                                  */
+          for (i = 16*sizeof(word) + 1; i <= 32 * sizeof(word); i++) {
+             GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 3) & (~3);
+          }
+#      endif
        /* We leave the rest of the array to be filled in on demand. */
     }
     
@@ -172,10 +244,10 @@ extern signed_word GC_mem_found;
 #          endif
        }
        byte_sz = WORDS_TO_BYTES(word_sz);
-#      ifdef ADD_BYTE_AT_END
+       if (GC_all_interior_pointers) {
            /* We need one extra byte; don't fill in GC_size_map[byte_sz] */
            byte_sz--;
-#      endif
+       }
 
        for (j = low_limit; j <= byte_sz; j++) GC_size_map[j] = word_sz;  
     }
@@ -192,39 +264,24 @@ extern signed_word GC_mem_found;
  */
 word GC_stack_last_cleared = 0;        /* GC_no when we last did this */
 # ifdef THREADS
-#   define CLEAR_SIZE 2048
-# else
-#   define CLEAR_SIZE 213
+#   define BIG_CLEAR_SIZE 2048 /* Clear this much now and then.        */
+#   define SMALL_CLEAR_SIZE 256 /* Clear this much every time.         */
 # endif
+# define CLEAR_SIZE 213  /* Granularity for GC_clear_stack_inner */
 # define DEGRADE_RATE 50
 
 word GC_min_sp;                /* Coolest stack pointer value from which we've */
                        /* already cleared the stack.                   */
                        
-# ifdef STACK_GROWS_DOWN
-#   define COOLER_THAN >
-#   define HOTTER_THAN <
-#   define MAKE_COOLER(x,y) if ((word)(x)+(y) > (word)(x)) {(x) += (y);} \
-                           else {(x) = (word)ONES;}
-#   define MAKE_HOTTER(x,y) (x) -= (y)
-# else
-#   define COOLER_THAN <
-#   define HOTTER_THAN >
-#   define MAKE_COOLER(x,y) if ((word)(x)-(y) < (word)(x)) {(x) -= (y);} else {(x) = 0;}
-#   define MAKE_HOTTER(x,y) (x) += (y)
-# endif
-
 word GC_high_water;
                        /* "hottest" stack pointer value we have seen   */
                        /* recently.  Degrades over time.               */
 
 word GC_words_allocd_at_reset;
 
-#if defined(ASM_CLEAR_CODE) && !defined(THREADS)
+#if defined(ASM_CLEAR_CODE)
   extern ptr_t GC_clear_stack_inner();
-#endif  
-
-#if !defined(ASM_CLEAR_CODE) && !defined(THREADS)
+#else  
 /* Clear the stack up to about limit.  Return arg. */
 /*ARGSUSED*/
 ptr_t GC_clear_stack_inner(arg, limit)
@@ -252,10 +309,13 @@ ptr_t arg;
 {
     register word sp = (word)GC_approx_sp();  /* Hotter than actual sp */
 #   ifdef THREADS
-        word dummy[CLEAR_SIZE];
-#   else
-       register word limit;
+        word dummy[SMALL_CLEAR_SIZE];
+       static unsigned random_no = 0;
+                                        /* Should be more random than it is ... */
+                                /* Used to occasionally clear a bigger  */
+                                /* chunk.                               */
 #   endif
+    register word limit;
     
 #   define SLOP 400
        /* Extra bytes we clear every time.  This clears our own        */
@@ -273,7 +333,16 @@ ptr_t arg;
        /* thus more junk remains accessible, thus the heap gets        */
        /* larger ...                                                   */
 # ifdef THREADS
-    BZERO(dummy, CLEAR_SIZE*sizeof(word));
+    if (++random_no % 13 == 0) {
+       limit = sp;
+       MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word));
+        limit &= ~0xf; /* Make it sufficiently aligned for assembly    */
+                       /* implementations of GC_clear_stack_inner.     */
+       return GC_clear_stack_inner(arg, limit);
+    } else {
+       BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word));
+       return arg;
+    }
 # else
     if (GC_gc_no > GC_stack_last_cleared) {
         /* Start things over, so we clear the entire stack again */
@@ -303,8 +372,8 @@ ptr_t arg;
        if (GC_min_sp HOTTER_THAN GC_high_water) GC_min_sp = GC_high_water;
        GC_words_allocd_at_reset = GC_words_allocd;
     }  
+    return(arg);
 # endif
-  return(arg);
 }
 
 
@@ -333,37 +402,27 @@ ptr_t arg;
     /* to the beginning.                                               */
        while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) {
           h = FORWARDED_ADDR(h,candidate_hdr);
-          r = (word)h + HDR_BYTES;
+          r = (word)h;
           candidate_hdr = HDR(h);
        }
     if (candidate_hdr -> hb_map == GC_invalid_map) return(0);
     /* Make sure r points to the beginning of the object */
        r &= ~(WORDS_TO_BYTES(1) - 1);
         {
-           register int offset = (char *)r - (char *)(HBLKPTR(r));
+           register int offset = HBLKDISPL(r);
            register signed_word sz = candidate_hdr -> hb_sz;
-           
-#          ifdef ALL_INTERIOR_POINTERS
-             register map_entry_type map_entry;
-             
-             map_entry = MAP_ENTRY((candidate_hdr -> hb_map), offset);
-             if (map_entry == OBJ_INVALID) {
-               return(0);
-              }
-              r -= WORDS_TO_BYTES(map_entry);
-              limit = r + WORDS_TO_BYTES(sz);
-#          else
-             register int correction;
+           register signed_word map_entry;
              
-             offset = BYTES_TO_WORDS(offset - HDR_BYTES);
-             correction = offset % sz;
-             r -= (WORDS_TO_BYTES(correction));
-             limit = r + WORDS_TO_BYTES(sz);
-             if (limit > (word)(h + 1)
-               && sz <= BYTES_TO_WORDS(HBLKSIZE) - HDR_WORDS) {
+           map_entry = MAP_ENTRY((candidate_hdr -> hb_map), offset);
+           if (map_entry > CPP_MAX_OFFSET) {
+               map_entry = (signed_word)(BYTES_TO_WORDS(offset)) % sz;
+            }
+            r -= WORDS_TO_BYTES(map_entry);
+            limit = r + WORDS_TO_BYTES(sz);
+           if (limit > (word)(h + 1)
+               && sz <= BYTES_TO_WORDS(HBLKSIZE)) {
                return(0);
-             }
-#          endif
+           }
            if ((word)p >= limit) return(0);
        }
     return((GC_PTR)r);
@@ -384,11 +443,7 @@ ptr_t arg;
     register hdr * hhdr = HDR(p);
     
     sz = WORDS_TO_BYTES(hhdr -> hb_sz);
-    if (sz < 0) {
-        return(-sz);
-    } else {
-        return(sz);
-    }
+    return(sz);
 }
 
 size_t GC_get_heap_size GC_PROTO(())
@@ -406,6 +461,11 @@ size_t GC_get_bytes_since_gc GC_PROTO(())
     return ((size_t) WORDS_TO_BYTES(GC_words_allocd));
 }
 
+size_t GC_get_total_bytes GC_PROTO(())
+{
+    return ((size_t) WORDS_TO_BYTES(GC_words_allocd+GC_words_allocd_before_gc));
+}
+
 GC_bool GC_is_initialized = FALSE;
 
 void GC_init()
@@ -413,65 +473,226 @@ void GC_init()
     DCL_LOCK_STATE;
     
     DISABLE_SIGNALS();
+
+#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
+    if (!GC_is_initialized) {
+      BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
+      HMODULE hK32 = GetModuleHandle("kernel32.dll");
+      if (hK32)
+         pfn = (BOOL (WINAPI *) (LPCRITICAL_SECTION, DWORD))
+               GetProcAddress (hK32,
+                               "InitializeCriticalSectionAndSpinCount");
+      if (pfn)
+          pfn(&GC_allocate_ml, 4000);
+      else
+         InitializeCriticalSection (&GC_allocate_ml);
+    }
+#endif /* MSWIN32 */
+
     LOCK();
     GC_init_inner();
     UNLOCK();
     ENABLE_SIGNALS();
 
+#   if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
+       /* Make sure marker threads and started and thread local */
+       /* allocation is initialized, in case we didn't get      */
+       /* called from GC_init_parallel();                       */
+        {
+         extern void GC_init_parallel(void);
+         GC_init_parallel();
+       }
+#   endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
+
+#   if defined(DYNAMIC_LOADING) && defined(DARWIN)
+    {
+        /* This must be called WITHOUT the allocation lock held
+        and before any threads are created */
+        extern void GC_init_dyld();
+        GC_init_dyld();
+    }
+#   endif
 }
 
+#if defined(MSWIN32) || defined(MSWINCE)
+    CRITICAL_SECTION GC_write_cs;
+#endif
+
 #ifdef MSWIN32
-    extern void GC_init_win32();
+    extern void GC_init_win32 GC_PROTO((void));
 #endif
 
 extern void GC_setpagesize();
 
+
+#ifdef MSWIN32
+extern GC_bool GC_no_win32_dlls;
+#else
+# define GC_no_win32_dlls FALSE
+#endif
+
+void GC_exit_check GC_PROTO((void))
+{
+   GC_gcollect();
+}
+
+#ifdef SEARCH_FOR_DATA_START
+  extern void GC_init_linux_data_start GC_PROTO((void));
+#endif
+
+#ifdef UNIX_LIKE
+
+extern void GC_set_and_save_fault_handler GC_PROTO((void (*handler)(int)));
+
+static void looping_handler(sig)
+int sig;
+{
+    GC_err_printf1("Caught signal %d: looping in handler\n", sig);
+    for(;;);
+}
+
+static GC_bool installed_looping_handler = FALSE;
+
+static void maybe_install_looping_handler()
+{
+    /* Install looping handler before the write fault handler, so we   */
+    /* handle write faults correctly.                                  */
+      if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) {
+        GC_set_and_save_fault_handler(looping_handler);
+        installed_looping_handler = TRUE;
+      }
+}
+
+#else /* !UNIX_LIKE */
+
+# define maybe_install_looping_handler()
+
+#endif
+
 void GC_init_inner()
 {
-#   ifndef THREADS
+#   if !defined(THREADS) && defined(GC_ASSERTIONS)
         word dummy;
 #   endif
+    word initial_heap_sz = (word)MINHINCR;
     
     if (GC_is_initialized) return;
-    GC_setpagesize();
-    GC_exclude_static_roots(beginGC_arrays, end_gc_area);
 #   ifdef PRINTSTATS
-       if ((ptr_t)endGC_arrays != (ptr_t)(&GC_obj_kinds)) {
-           GC_printf0("Reordering linker, didn't exclude obj_kinds\n");
-       }
+      GC_print_stats = 1;
+#   endif
+#   if defined(MSWIN32) || defined(MSWINCE)
+      InitializeCriticalSection(&GC_write_cs);
+#   endif
+    if (0 != GETENV("GC_PRINT_STATS")) {
+      GC_print_stats = 1;
+    } 
+#   ifndef NO_DEBUGGING
+      if (0 != GETENV("GC_DUMP_REGULARLY")) {
+        GC_dump_regularly = 1;
+      }
+#   endif
+#   ifdef KEEP_BACK_PTRS
+      {
+        char * backtraces_string = GETENV("GC_BACKTRACES");
+        if (0 != backtraces_string) {
+          GC_backtraces = atol(backtraces_string);
+         if (backtraces_string[0] == '\0') GC_backtraces = 1;
+        }
+      }
+#   endif
+    if (0 != GETENV("GC_FIND_LEAK")) {
+      GC_find_leak = 1;
+#     ifdef __STDC__
+        atexit(GC_exit_check);
+#     endif
+    }
+    if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) {
+      GC_all_interior_pointers = 1;
+    }
+    if (0 != GETENV("GC_DONT_GC")) {
+      GC_dont_gc = 1;
+    }
+    if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) {
+      GC_print_back_height = 1;
+    }
+    if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) {
+      GC_large_alloc_warn_interval = LONG_MAX;
+    }
+    {
+      char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET");
+      if (0 != time_limit_string) {
+        long time_limit = atol(time_limit_string);
+        if (time_limit < 5) {
+         WARN("GC_PAUSE_TIME_TARGET environment variable value too small "
+              "or bad syntax: Ignoring\n", 0);
+        } else {
+         GC_time_limit = time_limit;
+        }
+      }
+    }
+    {
+      char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL");
+      if (0 != interval_string) {
+        long interval = atol(interval_string);
+        if (interval <= 0) {
+         WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has "
+              "bad value: Ignoring\n", 0);
+        } else {
+         GC_large_alloc_warn_interval = interval;
+        }
+      }
+    }
+    maybe_install_looping_handler();
+    /* Adjust normal object descriptor for extra allocation.   */
+    if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) {
+      GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH);
+    }
+    GC_setpagesize();
+    GC_exclude_static_roots(beginGC_arrays, endGC_arrays);
+    GC_exclude_static_roots(beginGC_obj_kinds, endGC_obj_kinds);
+#   ifdef SEPARATE_GLOBALS
+      GC_exclude_static_roots(beginGC_objfreelist, endGC_objfreelist);
+      GC_exclude_static_roots(beginGC_aobjfreelist, endGC_aobjfreelist);
 #   endif
 #   ifdef MSWIN32
        GC_init_win32();
 #   endif
-#   if defined(LINUX) && \
-       (defined(POWERPC) || defined(ALPHA) || defined(SPARC) || defined(IA64))
+#   if defined(SEARCH_FOR_DATA_START)
        GC_init_linux_data_start();
 #   endif
-#   ifdef SOLARIS_THREADS
-       GC_thr_init();
-       /* We need dirty bits in order to find live stack sections.     */
-        GC_dirty_init();
+#   if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
+       GC_init_netbsd_elf();
 #   endif
-#   if defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-       || defined(IRIX_JDK_THREADS) || defined(HPUX_THREADS)
+#   if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \
+       || defined(GC_WIN32_THREADS)
         GC_thr_init();
 #   endif
-#   if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \
-       || defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-       || defined(HPUX_THREADS)
+#   ifdef GC_SOLARIS_THREADS
+       /* We need dirty bits in order to find live stack sections.     */
+        GC_dirty_init();
+#   endif
+#   if !defined(THREADS) || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) \
+       || defined(GC_SOLARIS_THREADS)
       if (GC_stackbottom == 0) {
        GC_stackbottom = GC_get_stack_base();
+#       if (defined(LINUX) || defined(HPUX)) && defined(IA64)
+         GC_register_stackbottom = GC_get_register_stack_base();
+#       endif
+      } else {
+#       if (defined(LINUX) || defined(HPUX)) && defined(IA64)
+         if (GC_register_stackbottom == 0) {
+           WARN("GC_register_stackbottom should be set with GC_stackbottom", 0);
+           /* The following may fail, since we may rely on             */
+           /* alignment properties that may not hold with a user set   */
+           /* GC_stackbottom.                                          */
+           GC_register_stackbottom = GC_get_register_stack_base();
+         }
+#      endif
       }
 #   endif
-    if  (sizeof (ptr_t) != sizeof(word)) {
-        ABORT("sizeof (ptr_t) != sizeof(word)\n");
-    }
-    if  (sizeof (signed_word) != sizeof(word)) {
-        ABORT("sizeof (signed_word) != sizeof(word)\n");
-    }
-    if  (sizeof (struct hblk) != HBLKSIZE) {
-        ABORT("sizeof (struct hblk) != HBLKSIZE\n");
-    }
+    GC_STATIC_ASSERT(sizeof (ptr_t) == sizeof(word));
+    GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word));
+    GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
 #   ifndef THREADS
 #     if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN)
        ABORT(
@@ -482,48 +703,47 @@ void GC_init_inner()
          "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd\n");
 #     endif
 #     ifdef STACK_GROWS_DOWN
-        if ((word)(&dummy) > (word)GC_stackbottom) {
-          GC_err_printf0(
-               "STACK_GROWS_DOWN is defd, but stack appears to grow up\n");
-#        ifndef UTS4  /* Compiler bug workaround */
-            GC_err_printf2("sp = 0x%lx, GC_stackbottom = 0x%lx\n",
-                          (unsigned long) (&dummy),
-                          (unsigned long) GC_stackbottom);
-#        endif
-          ABORT("stack direction 3\n");
-        }
+        GC_ASSERT((word)(&dummy) <= (word)GC_stackbottom);
 #     else
-        if ((word)(&dummy) < (word)GC_stackbottom) {
-          GC_err_printf0(
-               "STACK_GROWS_UP is defd, but stack appears to grow down\n");
-          GC_err_printf2("sp = 0x%lx, GC_stackbottom = 0x%lx\n",
-                        (unsigned long) (&dummy),
-                        (unsigned long) GC_stackbottom);
-          ABORT("stack direction 4");
-        }
+        GC_ASSERT((word)(&dummy) >= (word)GC_stackbottom);
 #     endif
 #   endif
 #   if !defined(_AUX_SOURCE) || defined(__GNUC__)
-      if ((word)(-1) < (word)0) {
-       GC_err_printf0("The type word should be an unsigned integer type\n");
-       GC_err_printf0("It appears to be signed\n");
-       ABORT("word");
-      }
+      GC_ASSERT((word)(-1) > (word)0);
+      /* word should be unsigned */
 #   endif
-    if ((signed_word)(-1) >= (signed_word)0) {
-       GC_err_printf0(
-               "The type signed_word should be a signed integer type\n");
-       GC_err_printf0("It appears to be unsigned\n");
-       ABORT("signed_word");
-    }
+    GC_ASSERT((signed_word)(-1) < (signed_word)0);
     
     /* Add initial guess of root sets.  Do this first, since sbrk(0)   */
     /* might be used.                                                  */
-      GC_register_data_segments();
+      if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments();
     GC_init_headers();
     GC_bl_init();
     GC_mark_init();
-    if (!GC_expand_hp_inner((word)MINHINCR)) {
+    {
+       char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE");
+       if (sz_str != NULL) {
+         initial_heap_sz = atoi(sz_str);
+         if (initial_heap_sz <= MINHINCR * HBLKSIZE) {
+           WARN("Bad initial heap size %s - ignoring it.\n",
+                sz_str);
+         } 
+         initial_heap_sz = divHBLKSZ(initial_heap_sz);
+       }
+    }
+    {
+       char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE");
+       if (sz_str != NULL) {
+         word max_heap_sz = (word)atol(sz_str);
+         if (max_heap_sz < initial_heap_sz * HBLKSIZE) {
+           WARN("Bad maximum heap size %s - ignoring it.\n",
+                sz_str);
+         } 
+         if (0 == GC_max_retries) GC_max_retries = 2;
+         GC_set_max_heap_size(max_heap_sz);
+       }
+    }
+    if (!GC_expand_hp_inner(initial_heap_sz)) {
         GC_err_printf0("Can't start up: not enough memory\n");
         EXIT();
     }
@@ -547,12 +767,24 @@ void GC_init_inner()
       PCR_IL_Unlock();
       GC_pcr_install();
 #   endif
-    /* Get black list set up */
-      GC_gcollect_inner();
+#   if !defined(SMALL_CONFIG)
+      if (!GC_no_win32_dlls && 0 != GETENV("GC_ENABLE_INCREMENTAL")) {
+       GC_ASSERT(!GC_incremental);
+        GC_setpagesize();
+#       ifndef GC_SOLARIS_THREADS
+          GC_dirty_init();
+#       endif
+        GC_ASSERT(GC_words_allocd == 0)
+       GC_incremental = TRUE;
+      }
+#   endif /* !SMALL_CONFIG */
+    COND_DUMP;
+    /* Get black list set up and/or incremental GC started */
+      if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner();
+    GC_is_initialized = TRUE;
 #   ifdef STUBBORN_ALLOC
        GC_stubborn_init();
 #   endif
-    GC_is_initialized = TRUE;
     /* Convince lint that some things are used */
 #   ifdef LINT
       {
@@ -581,20 +813,15 @@ void GC_enable_incremental GC_PROTO(())
     LOCK();
     if (GC_incremental) goto out;
     GC_setpagesize();
-#   ifdef MSWIN32
-      {
-        extern GC_bool GC_is_win32s();
-
-       /* VirtualProtect is not functional under win32s.       */
-       if (GC_is_win32s()) goto out;
-      }
-#   endif /* MSWIN32 */
-#   ifndef SOLARIS_THREADS
-        GC_dirty_init();
+    if (GC_no_win32_dlls) goto out;
+#   ifndef GC_SOLARIS_THREADS 
+      maybe_install_looping_handler();  /* Before write fault handler! */
+      GC_dirty_init();
 #   endif
     if (!GC_is_initialized) {
         GC_init_inner();
     }
+    if (GC_incremental) goto out;
     if (GC_dont_gc) {
         /* Can't easily do it. */
         UNLOCK();
@@ -617,25 +844,41 @@ out:
 }
 
 
-#ifdef MSWIN32
-# define LOG_FILE "gc.log"
+#if defined(MSWIN32) || defined(MSWINCE)
+# define LOG_FILE _T("gc.log")
 
-  HANDLE GC_stdout = 0, GC_stderr;
-  int GC_tmp;
-  DWORD GC_junk;
+  HANDLE GC_stdout = 0;
 
-  void GC_set_files()
+  void GC_deinit()
   {
-    if (!GC_stdout) {
-        GC_stdout = CreateFile(LOG_FILE, GENERIC_WRITE,
-                              FILE_SHARE_READ | FILE_SHARE_WRITE,
-                              NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH,
-                              NULL); 
-       if (INVALID_HANDLE_VALUE == GC_stdout) ABORT("Open of log file failed");
-    }
-    if (GC_stderr == 0) {
-       GC_stderr = GC_stdout;
-    }
+      if (GC_is_initialized) {
+       DeleteCriticalSection(&GC_write_cs);
+      }
+  }
+
+  int GC_write(buf, len)
+  GC_CONST char * buf;
+  size_t len;
+  {
+      BOOL tmp;
+      DWORD written;
+      if (len == 0)
+         return 0;
+      EnterCriticalSection(&GC_write_cs);
+      if (GC_stdout == INVALID_HANDLE_VALUE) {
+         return -1;
+      } else if (GC_stdout == 0) {
+         GC_stdout = CreateFile(LOG_FILE, GENERIC_WRITE,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH,
+                                NULL); 
+         if (GC_stdout == INVALID_HANDLE_VALUE) ABORT("Open of log file failed");
+      }
+      tmp = WriteFile(GC_stdout, buf, len, &written, NULL);
+      if (!tmp)
+         DebugBreak();
+      LeaveCriticalSection(&GC_write_cs);
+      return tmp ? (int)written : -1;
   }
 
 #endif
@@ -656,7 +899,7 @@ int GC_tmp;  /* Should really be local ... */
   }
 #endif
 
-#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32)
+#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE)
   int GC_stdout = 1;
   int GC_stderr = 2;
 # if !defined(AMIGA)
@@ -664,17 +907,18 @@ int GC_tmp;  /* Should really be local ... */
 # endif
 #endif
 
-#if !defined(MSWIN32)  && !defined(OS2) && !defined(MACOS) && !defined(ECOS)
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) \
+    && !defined(MACOS)  && !defined(ECOS) && !defined(NOSYS)
 int GC_write(fd, buf, len)
 int fd;
-char *buf;
+GC_CONST char *buf;
 size_t len;
 {
      register int bytes_written = 0;
      register int result;
      
      while (bytes_written < len) {
-#      ifdef SOLARIS_THREADS
+#      ifdef GC_SOLARIS_THREADS
            result = syscall(SYS_write, fd, buf + bytes_written,
                                            len - bytes_written);
 #      else
@@ -687,7 +931,7 @@ size_t len;
 }
 #endif /* UN*X */
 
-#if defined(ECOS)
+#ifdef ECOS
 int GC_write(fd, buf, len)
 {
   _Jv_diag_write (buf, len);
@@ -695,12 +939,17 @@ int GC_write(fd, buf, len)
 }
 #endif
 
+#ifdef NOSYS
+int GC_write(fd, buf, len)
+{
+  /* No writing.  */
+  return len;
+}
+#endif
 
-#ifdef MSWIN32
-#   define WRITE(f, buf, len) (GC_set_files(), \
-                              GC_tmp = WriteFile((f), (buf), \
-                                                 (len), &GC_junk, NULL),\
-                              (GC_tmp? 1 : -1))
+
+#if defined(MSWIN32) || defined(MSWINCE)
+#   define WRITE(f, buf, len) GC_write(buf, len)
 #else
 #   if defined(OS2) || defined(MACOS)
 #   define WRITE(f, buf, len) (GC_set_files(), \
@@ -718,7 +967,7 @@ int GC_write(fd, buf, len)
 /* same size as long, and that the format conversions expect something   */
 /* of that size.                                                         */
 void GC_printf(format, a, b, c, d, e, f)
-char * format;
+GC_CONST char * format;
 long a, b, c, d, e, f;
 {
     char buf[1025];
@@ -731,7 +980,7 @@ long a, b, c, d, e, f;
 }
 
 void GC_err_printf(format, a, b, c, d, e, f)
-char * format;
+GC_CONST char * format;
 long a, b, c, d, e, f;
 {
     char buf[1025];
@@ -743,11 +992,20 @@ long a, b, c, d, e, f;
 }
 
 void GC_err_puts(s)
-char *s;
+GC_CONST char *s;
 {
     if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed");
 }
 
+#if defined(LINUX) && !defined(SMALL_CONFIG)
+void GC_err_write(buf, len)
+GC_CONST char *buf;
+size_t len;
+{
+    if (WRITE(GC_stderr, buf, len) < 0) ABORT("write to stderr failed");
+}
+#endif
+
 # if defined(__STDC__) || defined(__cplusplus)
     void GC_default_warn_proc(char *msg, GC_word arg)
 # else
@@ -770,6 +1028,9 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
 {
     GC_warn_proc result;
 
+#   ifdef GC_WIN32_THREADS
+      GC_ASSERT(GC_is_initialized);
+#   endif
     LOCK();
     result = GC_current_warn_proc;
     GC_current_warn_proc = p;
@@ -777,60 +1038,124 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
     return(result);
 }
 
-
-#ifndef PCR
-void GC_abort(msg)
-char * msg;
+# if defined(__STDC__) || defined(__cplusplus)
+    GC_word GC_set_free_space_divisor (GC_word value)
+# else
+    GC_word GC_set_free_space_divisor (value)
+    GC_word value;
+# endif
 {
-    GC_err_printf1("%s\n", msg);
-    (void) abort();
+    GC_word old = GC_free_space_divisor;
+    GC_free_space_divisor = value;
+    return old;
 }
-#endif
 
-#ifdef NEED_CALLINFO
-
-void GC_print_callers (info)
-struct callinfo info[NFRAMES];
+#ifndef PCR
+void GC_abort(msg)
+GC_CONST char * msg;
 {
-    register int i;
-    
-#   if NFRAMES == 1
-      GC_err_printf0("\tCaller at allocation:\n");
+#   if defined(MSWIN32)
+      (void) MessageBoxA(NULL, msg, "Fatal error in gc", MB_ICONERROR|MB_OK);
 #   else
-      GC_err_printf0("\tCall chain at allocation:\n");
+      GC_err_printf1("%s\n", msg);
 #   endif
-    for (i = 0; i < NFRAMES; i++) {
-       if (info[i].ci_pc == 0) break;
-#      if NARGS > 0
-       {
-         int j;
-
-         GC_err_printf0("\t\targs: ");
-         for (j = 0; j < NARGS; j++) {
-           if (j != 0) GC_err_printf0(", ");
-           GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]),
-                                       ~(info[i].ci_arg[j]));
-         }
-         GC_err_printf0("\n");
-       }
-#      endif
-       GC_err_printf1("\t\t##PC##= 0x%X\n", info[i].ci_pc);
+    if (GETENV("GC_LOOP_ON_ABORT") != NULL) {
+           /* In many cases it's easier to debug a running process.    */
+           /* It's arguably nicer to sleep, but that makes it harder   */
+           /* to look at the thread if the debugger doesn't know much  */
+           /* about threads.                                           */
+           for(;;) {}
     }
+#   if defined(MSWIN32) || defined(MSWINCE)
+       DebugBreak();
+#   else
+        (void) abort();
+#   endif
 }
+#endif
 
-#endif /* SAVE_CALL_CHAIN */
-
-# ifdef SRC_M3
 void GC_enable()
 {
+    LOCK();
     GC_dont_gc--;
+    UNLOCK();
 }
 
 void GC_disable()
 {
+    LOCK();
     GC_dont_gc++;
+    UNLOCK();
 }
-# endif
+
+/* Helper procedures for new kind creation.    */
+void ** GC_new_free_list_inner()
+{
+    void *result = GC_INTERNAL_MALLOC((MAXOBJSZ+1)*sizeof(ptr_t), PTRFREE);
+    if (result == 0) ABORT("Failed to allocate freelist for new kind");
+    BZERO(result, (MAXOBJSZ+1)*sizeof(ptr_t));
+    return result;
+}
+
+void ** GC_new_free_list()
+{
+    void *result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_free_list_inner();
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
+}
+
+int GC_new_kind_inner(fl, descr, adjust, clear)
+void **fl;
+GC_word descr;
+int adjust;
+int clear;
+{
+    int result = GC_n_kinds++;
+
+    if (GC_n_kinds > MAXOBJKINDS) ABORT("Too many kinds");
+    GC_obj_kinds[result].ok_freelist = (ptr_t *)fl;
+    GC_obj_kinds[result].ok_reclaim_list = 0;
+    GC_obj_kinds[result].ok_descriptor = descr;
+    GC_obj_kinds[result].ok_relocate_descr = adjust;
+    GC_obj_kinds[result].ok_init = clear;
+    return result;
+}
+
+int GC_new_kind(fl, descr, adjust, clear)
+void **fl;
+GC_word descr;
+int adjust;
+int clear;
+{
+    int result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_kind_inner(fl, descr, adjust, clear);
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
+}
+
+int GC_new_proc_inner(proc)
+GC_mark_proc proc;
+{
+    int result = GC_n_mark_procs++;
+
+    if (GC_n_mark_procs > MAX_MARK_PROCS) ABORT("Too many mark procedures");
+    GC_mark_procs[result] = proc;
+    return result;
+}
+
+int GC_new_proc(proc)
+GC_mark_proc proc;
+{
+    int result;
+    LOCK(); DISABLE_SIGNALS();
+    result = GC_new_proc_inner(proc);
+    UNLOCK(); ENABLE_SIGNALS();
+    return result;
+}
+
 
 #if !defined(NO_DEBUGGING)
 
@@ -844,6 +1169,8 @@ void GC_dump()
     GC_print_hblkfreelist();
     GC_printf0("\n***Blocks in use:\n");
     GC_print_block_list();
+    GC_printf0("\n***Finalization statistics:\n");
+    GC_print_finalization_stats();
 }
 
-# endif /* NO_DEBUGGING */
+#endif /* NO_DEBUGGING */