$NetBSD: patch-ag,v 1.1.1.1 2001/01/31 07:30:47 jtb Exp $

--- gc/gc_priv.h.orig	Sun May  9 13:16:49 1999
+++ gc/gc_priv.h
@@ -1,6 +1,9 @@
 /* 
  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  * Copyright (c) 1991-1994 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.
+ *
  *
  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
@@ -41,7 +44,7 @@
 typedef GC_word word;
 typedef GC_signed_word signed_word;
 
-# ifndef CONFIG_H
+# ifndef GCCONFIG_H
 #   include "gcconfig.h"
 # endif
 
@@ -64,21 +67,22 @@
 #       include <stddef.h>
 #   endif
 #   define VOLATILE volatile
-#   define CONST const
 #else
 #   ifdef MSWIN32
 #   	include <stdlib.h>
 #   endif
 #   define VOLATILE
-#   define CONST
 #endif
 
-#ifdef AMIGA
+#define CONST GC_CONST
+
+#if 0 /* was once defined for AMIGA */
 #   define GC_FAR __far
 #else
 #   define GC_FAR
 #endif
 
+
 /*********************************/
 /*                               */
 /* Definitions for conservative  */
@@ -170,15 +174,6 @@
 		    /* May save significant amounts of space for obj_map  */
 		    /* entries.						  */
 
-#ifndef OLD_BLOCK_ALLOC
-   /* Macros controlling large block allocation strategy.	*/
-#  define EXACT_FIRST  	/* Make a complete pass through the large object */
-			/* free list before splitting a block		 */
-#  define PRESERVE_LAST /* Do not divide last allocated heap segment	 */
-			/* unless we would otherwise need to expand the	 */
-			/* heap.					 */
-#endif
-
 /* ALIGN_DOUBLE requires MERGE_SIZES at present. */
 # if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES)
 #   define MERGE_SIZES
@@ -278,6 +273,13 @@
 #   define MS_TIME_DIFF(a,b) ((double) (a.tv_sec - b.tv_sec) * 1000.0 \
                                + (double) (a.tv_usec - b.tv_usec) / 1000.0)
 #else /* !BSD_TIME */
+# ifdef MSWIN32
+#   include <windows.h>
+#   include <winbase.h>
+#   define CLOCK_TYPE DWORD
+#   define GET_TIME(x) x = GetTickCount()
+#   define MS_TIME_DIFF(a,b) ((long)((a)-(b)))
+# else /* !MSWIN32, !BSD_TIME */
 #   include <time.h>
 #   if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4)
       clock_t clock();	/* Not in time.h, where it belongs	*/
@@ -303,6 +305,7 @@
 #   define GET_TIME(x) x = clock()
 #   define MS_TIME_DIFF(a,b) ((unsigned long) \
 		(1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC))
+# endif /* !MSWIN32 */
 #endif /* !BSD_TIME */
 
 /* We use bzero and bcopy internally.  They may not be available.	*/
@@ -350,7 +353,7 @@
 				    + GC_page_size) \
                                     + GC_page_size-1)
 #   else
-#     if defined(AMIGA) || defined(NEXT) || defined(DOS4GW)
+#     if defined(AMIGA) || defined(NEXT) || defined(MACOSX) || defined(DOS4GW)
 #       define GET_MEM(bytes) HBLKPTR((size_t) \
 				      calloc(1, (size_t)bytes + GC_page_size) \
                                       + GC_page_size-1)
@@ -434,9 +437,12 @@
 #    define LOCK() mutex_lock(&GC_allocate_ml);
 #    define UNLOCK() mutex_unlock(&GC_allocate_ml);
 #  endif
-#  ifdef LINUX_THREADS
+#  if defined(LINUX_THREADS) 
+#   if defined(I386)|| defined(POWERPC) || defined(ALPHA) || defined(IA64) \
+    || defined(M68K)
 #    include <pthread.h>
-#    ifdef __i386__
+#    define USE_SPIN_LOCK
+#    if defined(I386)
        inline static int GC_test_and_set(volatile unsigned int *addr) {
 	  int oldval;
 	  /* Note: the "xchg" instruction does not need a "lock" prefix */
@@ -445,15 +451,107 @@
 		: "0"(1), "m"(*(addr)));
 	  return oldval;
        }
-#    else
-       -- > Need implementation of GC_test_and_set()
 #    endif
-#    define GC_clear(addr) (*(addr) = 0)
+#    if defined(IA64)
+       inline static int GC_test_and_set(volatile unsigned int *addr) {
+	  int oldval;
+	  __asm__ __volatile__("xchg4 %0=%1,%2"
+		: "=r"(oldval), "=m"(*addr)
+		: "r"(1), "1"(*addr));
+	  return oldval;
+       }
+       inline static void GC_clear(volatile unsigned int *addr) {
+	 __asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr));
+       }
+#      define GC_CLEAR_DEFINED
+#    endif
+#    ifdef M68K
+       /* Contributed by Tony Mantler.  I'm not sure how well it was	*/
+       /* tested.							*/
+       inline static int GC_test_and_set(volatile unsigned int *addr) {
+          char oldval; /* this must be no longer than 8 bits */
+
+          /* The return value is semi-phony. */
+          /* 'tas' sets bit 7 while the return */
+          /* value pretends bit 0 was set */
+          __asm__ __volatile__(
+                 "tas %1@; sne %0; negb %0"
+                 : "=d" (oldval)
+                 : "a" (addr));
+          return oldval;
+       }
+#    endif
+#    if defined(POWERPC)
+      inline static int GC_test_and_set(volatile unsigned int *addr) {
+        int oldval;
+        int temp = 1; // locked value
+
+        __asm__ __volatile__(
+               "1:\tlwarx %0,0,%3\n"   // load and reserve
+               "\tcmpwi %0, 0\n"       // if load is
+               "\tbne 2f\n"            //   non-zero, return already set
+               "\tstwcx. %2,0,%1\n"    // else store conditional
+               "\tbne- 1b\n"           // retry if lost reservation
+               "2:\t\n"                // oldval is zero if we set
+              : "=&r"(oldval), "=p"(addr)
+              : "r"(temp), "1"(addr)
+              : "memory");
+        return (int)oldval;
+      }
+      inline static void GC_clear(volatile unsigned int *addr) {
+	 __asm__ __volatile__("eieio");
+         *(addr) = 0;
+      }
+#     define GC_CLEAR_DEFINED
+#    endif
+#    ifdef ALPHA
+      inline static int GC_test_and_set(volatile unsigned int * addr)
+      {
+        unsigned long oldvalue;
+        unsigned long temp;
+
+        __asm__ __volatile__(
+                             "1:     ldl_l %0,%1\n"
+                             "       and %0,%3,%2\n"
+                             "       bne %2,2f\n"
+                             "       xor %0,%3,%0\n"
+                             "       stl_c %0,%1\n"
+                             "       beq %0,3f\n"
+                             "       mb\n"
+                             "2:\n"
+                             ".section .text2,\"ax\"\n"
+                             "3:     br 1b\n"
+                             ".previous"
+                             :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue)
+                             :"Ir" (1), "m" (*addr));
+
+        return oldvalue;
+      }
+      /* Should probably also define GC_clear, since it needs	*/
+      /* a memory barrier ??					*/
+#    endif /* ALPHA */
+#    ifdef ARM32
+      inline static int GC_test_and_set(volatile unsigned int *addr) {
+        int oldval;
+        /* SWP on ARM is very similar to XCHG on x86.  Doesn't lock the
+         * bus because there are no SMP ARM machines.  If/when there are,
+         * this code will likely need to be updated. */
+        /* See linuxthreads/sysdeps/arm/pt-machine.h in glibc-2.1 */
+        __asm__ __volatile__("swp %0, %1, [%2]"
+      			     : "=r"(oldval)
+      			     : "r"(1), "r"(addr));
+        return oldval;
+      }
+#    endif
+#    ifndef GC_CLEAR_DEFINED
+       inline static void GC_clear(volatile unsigned int *addr) {
+	  /* Try to discourage gcc from moving anything past this. */
+	  __asm__ __volatile__(" ");
+          *(addr) = 0;
+       }
+#    endif
 
      extern volatile unsigned int GC_allocate_lock;
-	/* This is not a mutex because mutexes that obey the (optional)     */
-	/* POSIX scheduling rules are subject to convoys in high contention */
-	/* applications.  This is basically a spin lock.		    */
      extern pthread_t GC_lock_holder;
      extern void GC_lock(void);
 	/* Allocation lock holder.  Only set if acquired by client through */
@@ -462,31 +560,48 @@
 #    define NO_THREAD (pthread_t)(-1)
 #    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
 #    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
-#    ifdef UNDEFINED
-#    	define LOCK() pthread_mutex_lock(&GC_allocate_ml)
-#    	define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
-#    else
-#	define LOCK() \
+#    define LOCK() \
 		{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); }
-#	define UNLOCK() \
+#    define UNLOCK() \
 		GC_clear(&GC_allocate_lock)
-#    endif
-     extern GC_bool GC_collecting;
+     extern VOLATILE GC_bool GC_collecting;
 #    define ENTER_GC() \
 		{ \
 		    GC_collecting = 1; \
 		}
 #    define EXIT_GC() GC_collecting = 0;
+#   else /* LINUX_THREADS on hardware for which we don't know how	*/
+	 /* to do test and set.						*/
+#    include <pthread.h>
+     extern pthread_mutex_t GC_allocate_ml;
+#    define LOCK() pthread_mutex_lock(&GC_allocate_ml)
+#    define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+#   endif
 #  endif /* LINUX_THREADS */
-#  if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS)
+#  if defined(HPUX_THREADS)
+#    include <pthread.h>
+     extern pthread_mutex_t GC_allocate_ml;
+#    define LOCK() pthread_mutex_lock(&GC_allocate_ml)
+#    define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+#  endif
+#  if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) 
+     /* This may also eventually be appropriate for HPUX_THREADS */
 #    include <pthread.h>
-#    include <mutex.h>
+#    ifndef HPUX_THREADS
+	/* This probably should never be included, but I can't test	*/
+	/* on Irix anymore.						*/
+#       include <mutex.h>
+#    endif
 
-#    if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
+#    ifndef HPUX_THREADS
+#      if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
 	|| !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700
 #        define GC_test_and_set(addr, v) test_and_set(addr,v)
-#    else
+#      else
 #	 define GC_test_and_set(addr, v) __test_and_set(addr,v)
+#      endif
+#    else
+       /* I couldn't find a way to do this inline on HP/UX	*/
 #    endif
      extern unsigned long GC_allocate_lock;
 	/* This is not a mutex because mutexes that obey the (optional) 	*/
@@ -500,15 +615,17 @@
 #    define NO_THREAD (pthread_t)(-1)
 #    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
 #    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
-#    ifdef UNDEFINED
-#    	define LOCK() pthread_mutex_lock(&GC_allocate_ml)
-#    	define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+#    ifdef HPUX_THREADS
+#      define LOCK() { if (!GC_test_and_clear(&GC_allocate_lock)) GC_lock(); }
+       /* The following is INCORRECT, since the memory model is too weak. */
+#      define UNLOCK() { GC_noop1(&GC_allocate_lock); \
+			*(volatile unsigned long *)(&GC_allocate_lock) = 1; }
 #    else
-#	define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); }
-#       if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \
+#      define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); }
+#      if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \
 	   && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 700
 #	    define UNLOCK() __lock_release(&GC_allocate_lock)
-#	else
+#      else
 	    /* The function call in the following should prevent the	*/
 	    /* compiler from moving assignments to below the UNLOCK.	*/
 	    /* This is probably not necessary for ucode or gcc 2.8.	*/
@@ -516,9 +633,9 @@
 	    /* versions.						*/
 #           define UNLOCK() { GC_noop1(&GC_allocate_lock); \
 			*(volatile unsigned long *)(&GC_allocate_lock) = 0; }
-#	endif
+#      endif
 #    endif
-     extern GC_bool GC_collecting;
+     extern VOLATILE GC_bool GC_collecting;
 #    define ENTER_GC() \
 		{ \
 		    GC_collecting = 1; \
@@ -607,7 +724,7 @@
 # else
 #   if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \
 	|| defined(IRIX_THREADS) || defined(LINUX_THREADS) \
-	|| defined(IRIX_JDK_THREADS)
+	|| defined(IRIX_JDK_THREADS) || defined(HPUX_THREADS)
       void GC_stop_world();
       void GC_start_world();
 #     define STOP_WORLD() GC_stop_world()
@@ -823,6 +940,7 @@
     struct hblk * hb_next; 	/* Link field for hblk free list	 */
     				/* and for lists of chunks waiting to be */
     				/* reclaimed.				 */
+    struct hblk * hb_prev;	/* Backwards link for free list.	*/
     word hb_descr;   		/* object descriptor for marking.  See	*/
     				/* mark.h.				*/
     char* hb_map;	/* A pointer to a pointer validity map of the block. */
@@ -837,14 +955,28 @@
 #	define IGNORE_OFF_PAGE	1	/* Ignore pointers that do not	*/
 					/* point to the first page of 	*/
 					/* this object.			*/
+#	define WAS_UNMAPPED 2	/* This is a free block, which has	*/
+				/* been unmapped from the address 	*/
+				/* space.				*/
+				/* GC_remap must be invoked on it	*/
+				/* before it can be reallocated.	*/
+				/* Only set with USE_MUNMAP.		*/
     unsigned short hb_last_reclaimed;
     				/* Value of GC_gc_no when block was	*/
     				/* last allocated or swept. May wrap.   */
+				/* For a free block, this is maintained */
+				/* unly for USE_MUNMAP, and indicates	*/
+				/* when the header was allocated, or	*/
+				/* when the size of the block last	*/
+				/* changed.				*/
     word hb_marks[MARK_BITS_SZ];
 			    /* Bit i in the array refers to the             */
 			    /* object starting at the ith word (header      */
 			    /* INCLUDED) in the heap block.                 */
 			    /* The lsb of word 0 is numbered 0.		    */
+			    /* Unused bits are invalid, and are 	    */
+			    /* occasionally set, e.g for uncollectable	    */
+			    /* objects.					    */
 };
 
 /*  heap block body */
@@ -879,8 +1011,10 @@
 /* The type of mark procedures.  This really belongs in gc_mark.h.	*/
 /* But we put it here, so that we can avoid scanning the mark proc	*/
 /* table.								*/
-typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr,
-					  mark_stack_limit, env */);
+typedef struct ms_entry * (*mark_proc)(/* word * addr,
+					  struct ms_entry *mark_stack_ptr,
+					  struct ms_entry *mark_stack_limit,
+					  word env */);
 # define LOG_MAX_MARK_PROCS 6
 # define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS)
 
@@ -957,8 +1091,12 @@
 struct _GC_arrays {
   word _heapsize;
   word _max_heapsize;
+  word _requested_heapsize;	/* Heap size due to explicit expansion */
   ptr_t _last_heap_addr;
   ptr_t _prev_heap_addr;
+  word _large_free_bytes;
+	/* Total bytes contained in blocks on large object free */
+	/* list.						*/
   word _words_allocd_before_gc;
 		/* Number of words allocated before this	*/
 		/* collection cycle.				*/
@@ -978,6 +1116,10 @@
   word _mem_freed;
   	/* Number of explicitly deallocated words of memory	*/
   	/* since last collection.				*/
+  ptr_t _scratch_end_ptr;
+  ptr_t _scratch_last_end_ptr;
+	/* Used by headers.c, and can easily appear to point to	*/
+	/* heap.						*/
   mark_proc _mark_procs[MAX_MARK_PROCS];
   	/* Table of user-defined mark procedures.  There is	*/
 	/* a small number of these, which can be referenced	*/
@@ -1005,6 +1147,9 @@
    		/* Number of words in accessible atomic		*/
 		/* objects.					*/
 # endif
+# ifdef USE_MUNMAP
+    word _unmapped_bytes;
+# endif
 # ifdef MERGE_SIZES
     unsigned _size_map[WORDS_TO_BYTES(MAXOBJSZ+1)];
     	/* Number of words to allocate for a given allocation request in */
@@ -1022,7 +1167,7 @@
     		       /* to an object at				*/
     		       /* block_start+i&~3 - WORDS_TO_BYTES(j).		*/
     		       /* (If ALL_INTERIOR_POINTERS is defined, then	*/
-    		       /* instead ((short *)(hbh_map[sz])[i] is j if	*/
+    		       /* instead ((short *)(hb_map[sz])[i] is j if	*/
     		       /* block_start+WORDS_TO_BYTES(i) is in the	*/
     		       /* interior of an object starting at		*/
     		       /* block_start+WORDS_TO_BYTES(i-j)).		*/
@@ -1135,15 +1280,22 @@
 # define GC_prev_heap_addr GC_arrays._prev_heap_addr
 # define GC_words_allocd GC_arrays._words_allocd
 # define GC_words_wasted GC_arrays._words_wasted
+# define GC_large_free_bytes GC_arrays._large_free_bytes
 # define GC_words_finalized GC_arrays._words_finalized
 # define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc
 # define GC_mem_freed GC_arrays._mem_freed
+# define GC_scratch_end_ptr GC_arrays._scratch_end_ptr
+# define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr
 # define GC_mark_procs GC_arrays._mark_procs
 # define GC_heapsize GC_arrays._heapsize
 # define GC_max_heapsize GC_arrays._max_heapsize
+# define GC_requested_heapsize GC_arrays._requested_heapsize
 # define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc
 # define GC_heap_sects GC_arrays._heap_sects
 # define GC_last_stack GC_arrays._last_stack
+# ifdef USE_MUNMAP
+#   define GC_unmapped_bytes GC_arrays._unmapped_bytes
+# endif
 # ifdef MSWIN32
 #   define GC_heap_bases GC_arrays._heap_bases
 # endif
@@ -1172,6 +1324,8 @@
 # define beginGC_arrays ((ptr_t)(&GC_arrays))
 # define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays))
 
+#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes)
+
 /* Object kinds: */
 # define MAXOBJKINDS 16
 
@@ -1236,7 +1390,7 @@
 			/* Pointer to the nowhere valid hblk map */
 			/* Blocks pointing to this map are free. */
 
-extern struct hblk * GC_hblkfreelist;
+extern struct hblk * GC_hblkfreelist[];
 				/* List of completely empty heap blocks	*/
 				/* Linked through hb_next field of 	*/
 				/* header structure associated with	*/
@@ -1251,7 +1405,7 @@
   extern GC_bool GC_incremental;
 			/* Using incremental/generational collection. */
 #else
-# define GC_incremental TRUE
+# define GC_incremental FALSE
 			/* Hopefully allow optimizer to remove some code. */
 #endif
 
@@ -1304,14 +1458,16 @@
 ptr_t GC_approx_sp();
 
 GC_bool GC_should_collect();
-#ifdef PRESERVE_LAST
-    GC_bool GC_in_last_heap_sect(/* ptr_t */);
-	/* In last added heap section?  If so, avoid breaking up.	*/
-#endif
+
 void GC_apply_to_all_blocks(/*fn, client_data*/);
 			/* Invoke fn(hbp, client_data) for each 	*/
 			/* allocated heap block.			*/
-struct hblk * GC_next_block(/* struct hblk * h */);
+struct hblk * GC_next_used_block(/* struct hblk * h */);
+			/* Return first in-use block >= h	*/
+struct hblk * GC_prev_block(/* struct hblk * h */);
+			/* Return last block <= h.  Returned block	*/
+			/* is managed by GC, but may or may not be in	*/
+			/* use.						*/
 void GC_mark_init();
 void GC_clear_marks();	/* Clear mark bits for all heap objects. */
 void GC_invalidate_mark_state();	/* Tell the marker that	marked 	   */
@@ -1384,8 +1540,14 @@
 			/* lock held.					*/
 			/* 0 by default.				*/
 void GC_push_regs();	/* Push register contents onto mark stack.	*/
+			/* If NURSERY is defined, the default push	*/
+			/* action can be overridden with GC_push_proc	*/
 void GC_remark();	/* Mark from all marked objects.  Used	*/
 		 	/* only if we had to drop something.	*/
+
+# ifdef NURSERY
+    extern void (*GC_push_proc)(ptr_t);
+# endif
 # if defined(MSWIN32)
   void __cdecl GC_push_one();
 # else
@@ -1573,9 +1735,10 @@
 				/* head.				*/
 
 void GC_init_headers();
-GC_bool GC_install_header(/*h*/);
+struct hblkhdr * GC_install_header(/*h*/);
 				/* Install a header for block h.	*/
-				/* Return FALSE on failure.		*/
+				/* Return 0 on failure, or the header	*/
+				/* otherwise.				*/
 GC_bool GC_install_counts(/*h, sz*/);
 				/* Set up forwarding counts for block	*/
 				/* h of size sz.			*/
@@ -1608,6 +1771,15 @@
 			/* detailed description of the object 		*/
 			/* referred to by p.				*/
 			
+/* Memory unmapping: */
+#ifdef USE_MUNMAP
+  void GC_unmap_old(void);
+  void GC_merge_unmapped(void);
+  void GC_unmap(ptr_t start, word bytes);
+  void GC_remap(ptr_t start, word bytes);
+  void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2);
+#endif
+
 /* Virtual dirty bit implementation:		*/
 /* Each implementation exports the following:	*/
 void GC_read_dirty();	/* Retrieve dirty bits.	*/
@@ -1640,6 +1812,16 @@
 void GC_print_static_roots();
 void GC_dump();
 
+#ifdef KEEP_BACK_PTRS
+   void GC_store_back_pointer(ptr_t source, ptr_t dest);
+   void GC_marked_for_finalization(ptr_t dest);
+#  define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest)
+#  define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest)
+#else
+#  define GC_STORE_BACK_PTR(source, dest) 
+#  define GC_MARKED_FOR_FINALIZATION(dest)
+#endif
+
 /* Make arguments appear live to compiler */
 # ifdef __WATCOMC__
   void GC_noop(void*, ...);
@@ -1689,5 +1871,14 @@
 			/* Write s to stderr, don't buffer, don't add	*/
 			/* newlines, don't ...				*/
 
+
+#   ifdef GC_ASSERTIONS
+#	define GC_ASSERT(expr) if(!(expr)) {\
+		GC_err_printf2("Assertion failure: %s:%ld\n", \
+				__FILE__, (unsigned long)__LINE__); \
+		ABORT("assertion failure"); }
+#   else 
+#	define GC_ASSERT(expr)
+#   endif
 
 # endif /* GC_PRIVATE_H */
