for review: split mercury_memory.{c,h}

Tyson Dowd trd at cs.mu.OZ.AU
Thu May 7 17:04:46 AEST 1998


Estimated hours taken: 5

Split mercury_memory.{c,h} into several files.


runtime/Mmakefile:
	Add new files.

runtime/mercury_context.c:
	Add a #include "mercury_memory_handlers.h" as this module
	creates new zones using default_handler.

runtime/mercury_memory.c:
runtime/mercury_memory.h:
	Other code (mostly code to set up the actual memory
	zones we use in the Mercury runtime).

runtime/mercury_memory_handlers.c:
runtime/mercury_memory_handlers.h:
	Signal handlers for memory access violations.

runtime/mercury_memory_zones.c:
runtime/mercury_memory_zones.h:
	The implementation MemoryZone data type.


Index: runtime/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/Mmakefile,v
retrieving revision 1.25
diff -u -r1.25 Mmakefile
--- Mmakefile	1998/03/30 13:03:13	1.25
+++ Mmakefile	1998/04/28 06:35:45
@@ -42,8 +42,9 @@
 			mercury_imp.h		\
 			mercury_init.h		\
 			mercury_label.h		\
-			mercury_stack_layout.h	\
 			mercury_memory.h	\
+			mercury_memory_zones.h	\
+			mercury_memory_handlers.h	\
 			mercury_misc.h		\
 			mercury_overflow.h	\
 			mercury_prof.h		\
@@ -53,6 +54,7 @@
 			mercury_spinlock.h	\
 			mercury_std.h		\
 			mercury_stacks.h	\
+			mercury_stack_layout.h	\
 			mercury_stack_trace.h	\
 			mercury_string.h	\
 			mercury_table.h		\
@@ -91,6 +93,8 @@
 			mercury_ho_call.c	\
 			mercury_label.c		\
 			mercury_memory.c	\
+			mercury_memory_zones.c	\
+			mercury_memory_handlers.c	\
 			mercury_misc.c		\
 			mercury_prof.c		\
 			mercury_prof_mem.c	\
Index: runtime/mercury_context.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_context.c,v
retrieving revision 1.1
diff -u -r1.1 mercury_context.c
--- mercury_context.c	1997/11/20 01:59:46	1.1
+++ mercury_context.c	1998/04/28 07:23:15
@@ -18,6 +18,7 @@
 #include <signal.h>
 #endif
 
+#include "mercury_memory_handlers.h"
 #include "mercury_context.h"
 #include "mercury_engine.h"	/* for `memdebug' */
 
Index: runtime/mercury_memory.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_memory.c,v
retrieving revision 1.6
diff -u -r1.6 mercury_memory.c
--- mercury_memory.c	1998/04/08 11:33:56	1.6
+++ mercury_memory.c	1998/04/29 06:02:43
@@ -25,6 +25,11 @@
 ** If the operating system of the machine supports the mprotect syscall,
 ** we also protect a chunk at the end of each area against access,
 ** thus detecting area overflow.
+**
+** The code for handling the allocation and management of different
+** memory zones is in mercury_memory_zones.{c,h}.
+** The code for handling overflows and memory access errors in general
+** is in mercury_memory_handlers.{c,h}.
 */
 
 /*---------------------------------------------------------------------------*/
@@ -81,6 +86,7 @@
 
 #include "mercury_imp.h"
 #include "mercury_trace.h"
+#include "mercury_memory_handlers.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -90,71 +96,8 @@
   #define	getpagesize()	8192
 #endif
 
-#ifdef	CONSERVATIVE_GC
-  #define	memalign(a,s)   GC_MALLOC_UNCOLLECTABLE(s)
-#elif defined(HAVE_MEMALIGN)
-  extern void	*memalign(size_t, size_t);
-#else
-  #define	memalign(a,s)	malloc(s)
-#endif
-
-/*
-** DESCRIPTION
-**  The function mprotect() changes the  access  protections  on
-**  the mappings specified by the range [addr, addr + len) to be
-**  that specified by prot.  Legitimate values for prot are  the
-**  same  as  those  permitted  for  mmap  and  are  defined  in
-**  <sys/mman.h> as:
-**
-** PROT_READ    page can be read
-** PROT_WRITE   page can be written
-** PROT_EXEC    page can be executed
-** PROT_NONE    page can not be accessed
-*/
-#ifdef  HAVE_MPROTECT
-
-  #ifdef CONSERVATIVE_GC
-    /*
-    ** The conservative garbage collectors scans through
-    ** all these areas, so we need to allow reads.
-    ** XXX This probably causes efficiency problems:
-    ** too much memory for the GC to scan, and it probably
-    ** all gets paged in.
-    */
-    #define MY_PROT PROT_READ
-  #else
-    #define MY_PROT PROT_NONE
-  #endif
-
-  /* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
-  #ifndef PROT_NONE
-    #define PROT_NONE 0
-  #endif
-
-#endif /* HAVE_MPROTECT */
-
 /*---------------------------------------------------------------------------*/
 
-#ifdef HAVE_SIGINFO
-  #if defined(HAVE_SIGCONTEXT_STRUCT)
-    static	void	complex_sighandler(int, struct sigcontext_struct);
-  #elif defined(HAVE_SIGINFO_T)
-    static	void	complex_bushandler(int, siginfo_t *, void *);
-    static	void	complex_segvhandler(int, siginfo_t *, void *);
-  #else
-    #error "HAVE_SIGINFO defined but don't know how to get it"
-  #endif
-#else
-  static	void	simple_sighandler(int);
-#endif
-
-/*
-** round_up(amount, align) returns `amount' rounded up to the nearest
-** alignment boundary.  `align' must be a power of 2.
-*/
-
-#define round_up(amount, align) ((((amount) - 1) | ((align) - 1)) + 1)
-
 static	void	setup_mprotect(void);
 
 #ifdef	HAVE_SIGINFO
@@ -162,19 +105,6 @@
   static	char	*explain_context(void *context);
 #endif /* HAVE_SIGINFO */
 
-static	void	setup_signal(void);
-
-Word	fake_reg[MAX_FAKE_REG];
-
-Word	virtual_reg_map[MAX_REAL_REG] = VIRTUAL_REG_MAP_BODY;
-
-unsigned long	num_uses[MAX_RN];
-
-MemoryZone *zone_table;
-
-MemoryZone *used_memory_zones;
-MemoryZone *free_memory_zones;
-
 MemoryZone *detstack_zone;
 MemoryZone *nondetstack_zone;
 #ifndef CONSERVATIVE_GC
@@ -186,29 +116,8 @@
   int	dumpindex;
 #endif
 
-static	size_t		unit;
-static	size_t		page_size;
-
-static MemoryZone	*get_zone(void);
-static void		unget_zone(MemoryZone *zone);
-
-	/*
-	** We manage the handing out of offets through the cache by
-	** computing the offsets once and storing them in an array
-	** (in shared memory if necessary). We then maintain a global
-	** counter used to index the array which we increment (modulo
-	** the size of the array) after handing out each offset.
-	*/
-
-#define	CACHE_SLICES	8
-
-static	size_t		*offset_vector;
-static	int		*offset_counter;
-static	SpinLock	*offset_lock;
-size_t	next_offset(void);
-
-static void init_memory_arena(void);
-static void init_zones(void);
+size_t		unit;
+size_t		page_size;
 
 void 
 init_memory(void)
@@ -281,128 +190,6 @@
 	if (memdebug) debug_memory();
 } /* end init_memory() */
 
-void
-debug_memory(void)
-{
-	MemoryZone	*zone;
-
-	fprintf(stderr, "\n");
-	fprintf(stderr, "pcache_size  = %lu (0x%lx)\n",
-		(unsigned long) pcache_size, (unsigned long) pcache_size);
-	fprintf(stderr, "page_size    = %lu (0x%lx)\n",
-		(unsigned long) page_size, (unsigned long) page_size);
-	fprintf(stderr, "unit         = %lu (0x%lx)\n",
-		(unsigned long) unit, (unsigned long) unit);
-
-	fprintf(stderr, "\n");
-	fprintf(stderr, "fake_reg       = %p (offset %ld)\n",
-		(void *) fake_reg, (long) fake_reg & (unit-1));
-	fprintf(stderr, "\n");
-
-	for (zone = used_memory_zones; zone; zone = zone->next)
-	{
-		fprintf(stderr, "%-16s#%d-base	= %p\n",
-			zone->name, zone->id, (void *) zone->bottom);
-		fprintf(stderr, "%-16s#%d-min		= %p\n",
-			zone->name, zone->id, (void *) zone->min);
-		fprintf(stderr, "%-16s#%d-top		= %p\n",
-			zone->name, zone->id, (void *) zone->top);
-#ifdef HAVE_MPROTECT
-  #ifdef HAVE_SIGINFO
-		fprintf(stderr, "%-16s#%d-redzone	= %p\n",
-			zone->name, zone->id, (void *) zone->redzone);
-  #endif /* HAVE_SIGINFO */
-		fprintf(stderr, "%-16s#%d-hardmax		= %p\n",
-			zone->name, zone->id, (void *) zone->hardmax);
-		fprintf(stderr, "%-16s#%d-size		= %lu\n",
-			zone->name, zone->id, (unsigned long)
-			((char *)zone->hardmax - (char *)zone->min));
-#else
-		fprintf(stderr, "%-16s#%d-size		= %lu\n",
-			zone->name, zone->id, (unsigned long)
-			((char *)zone->top - (char *)zone->min));
-#endif /* HAVE_MPROTECT */
-		fprintf(stderr, "\n");
-	}
-}
-
-/*
-** init_memory_arena() allocates (if necessary) the top-level memory pool
-** from which all allocations should come. If PARALLEL is defined, then
-** this pool should be shared memory. In the absence of PARALLEL, it
-** doesn't need to do anything, since with CONSERVATIVE_GC, the collector
-** manages the heap, and without GC, we can allocate memory using memalign
-** or malloc.
-*/
-static void 
-init_memory_arena()
-{
-#ifdef	PARALLEL
-  #ifndef CONSERVATIVE_GC
-	if (numprocs > 1) {
-		fatal_error("shared memory not implemented");
-	}
-  #else
-	if (numprocs > 1) {
-		fatal_error("shared memory not implemented with conservative gc");
-	}
-  #endif
-#endif
-}
-
-static void 
-init_zones()
-{
-	int i;
-	size_t fake_reg_offset;
-
-	/*
-	** Allocate the MemoryZone table.
-	*/
-	zone_table = allocate_array(MemoryZone, MAX_ZONES);
-
-	/*
-	** Initialize the MemoryZone table.
-	*/
-	used_memory_zones = NULL;
-	free_memory_zones = zone_table;
-	for(i = 0; i < MAX_ZONES; i++) {
-		zone_table[i].name = "unused";
-		zone_table[i].id = i;
-		zone_table[i].bottom = NULL;
-		zone_table[i].top = NULL;
-		zone_table[i].min = NULL;
-#ifdef MR_LOWLEVEL_DEBUG
-		zone_table[i].max = NULL;
-#endif
-#ifdef HAVE_MPROTECT
-  #ifdef HAVE_SIGINFO
-		zone_table[i].redzone = NULL;
-  #endif
-		zone_table[i].hardmax = NULL;
-#endif
-		if (i+1 < MAX_ZONES) {
-			zone_table[i].next = &(zone_table[i+1]);
-		} else {
-			zone_table[i].next = NULL;
-		}
-	}
-
-	offset_counter = allocate_object(int);
-	*offset_counter = 0;
-
-	offset_vector = allocate_array(size_t, CACHE_SLICES - 1);
-
-	fake_reg_offset = (Unsigned) fake_reg % pcache_size;
-
-	for (i = 0; i < CACHE_SLICES - 1; i++) {
-		offset_vector[i] =
-			(fake_reg_offset + pcache_size / CACHE_SLICES)
-			% pcache_size;
-	}
-} /* end init_zones() */
-
-
 void 
 init_heap(void)
 {
@@ -436,718 +223,6 @@
 #endif
 } /* end init_heap() */
 
-MemoryZone *
-get_zone(void)
-{
-	MemoryZone *zone;
-
-	/*
-	** unlink the first zone on the free-list,
-	** link it onto the used-list and return it.
-	*/
-	zone = free_memory_zones;
-	if (zone == NULL) {
-		fatal_error("no more memory zones");
-	}
-	free_memory_zones = free_memory_zones->next;
-
-	zone->next = used_memory_zones;
-	used_memory_zones = zone;
-
-	return zone;
-}
-
-void 
-unget_zone(MemoryZone *zone)
-{
-	MemoryZone *prev, *tmp;
-
-	/*
-	** Find the zone on the used list, and unlink it from
-	** the list, then link it onto the start of the free-list.
-	*/
-	for(prev = NULL, tmp = used_memory_zones;
-		tmp && tmp != zone; prev = tmp, tmp = tmp->next) 
-	{
-		/* VOID */
-	}
-	if (tmp == NULL) {
-		fatal_error("memory zone not found!");
-	}
-	if (prev == NULL) {
-		used_memory_zones = used_memory_zones->next;
-	} else {
-		prev->next = tmp->next;
-	}
-
-	zone->next = free_memory_zones;
-	free_memory_zones = zone;
-}
-
-/*
-** successive calls to next_offset return offsets modulo the primary
-** cache size (carefully avoiding ever giving an offset that clashes
-** with fake_reg_array). This is used to give different memory zones
-** different starting points across the caches so that it is better
-** utilized.
-** An alternative implementation would be to increment the offset by
-** a fixed amount (eg 2Kb) so that as primary caches get bigger, we
-** allocate more offsets across them.
-*/
-size_t	
-next_offset(void)
-{
-	size_t offset;
-
-	get_lock(offset_lock);
-
-	offset = offset_vector[*offset_counter];
-
-	*offset_counter = (*offset_counter + 1) % (CACHE_SLICES - 1);
-
-	release_lock(offset_lock);
-
-	return offset;
-}
-
-MemoryZone *
-create_zone(const char *name, int id, size_t size,
-		size_t offset, size_t redsize,
-		bool ((*handler)(Word *addr, MemoryZone *zone, void *context)))
-{
-	Word		*base;
-	size_t		total_size;
-
-		/*
-		** total allocation is:
-		**	unit		(roundup to page boundary)
-		**	size		(including redzone)
-		**	unit		(an extra page for protection if
-		**			 mprotect is being used)
-		*/
-#ifdef	HAVE_MPROTECT
-	total_size = size + 2 * unit;
-#else
-	total_size = size + unit;
-#endif
-
-#ifdef	PARALLEL
-	if (numprocs > 1) {
-		fatal_error("shared memory not supported yet");
-	}
-#endif
-	base = memalign(unit, total_size);
-	if (base == NULL) {
-		char buf[2560];
-		sprintf(buf, "unable allocate memory zone: %s#%d", name, id);
-		fatal_error(buf);
-	}
-
-	return construct_zone(name, id, base, size, offset, redsize, handler);
-} /* end create_zone() */
-
-MemoryZone *
-construct_zone(const char *name, int id, Word *base,
-		size_t size, size_t offset, size_t redsize,
-		ZoneHandler handler)
-{
-	MemoryZone	*zone;
-	size_t		total_size;
-
-	if (base == NULL) {
-		fatal_error("construct_zone called with NULL pointer");
-	}
-
-	zone = get_zone();
-
-	zone->name = name;
-	zone->id = id;
-
-#if	defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
-	zone->handler = handler;
-#endif
-
-	zone->bottom = base;
-
-#ifdef 	HAVE_MPROTECT
-	total_size = size + unit;
-#else
-	total_size = size;
-#endif
-
-	zone->top = (Word *) ((char *)base+total_size);
-	zone->min = (Word *) ((char *)base+offset);
-#ifdef MR_LOWLEVEL_DEBUG
-	zone->max = zone->min;
-#endif
-
-	/*
-	** setup the redzone+hardzone
-	*/
-#ifdef	HAVE_MPROTECT
-  #ifdef HAVE_SIGINFO
-	zone->redzone_base = zone->redzone = (Word *)
-			round_up((Unsigned)base + size - redsize, unit);
-	if (mprotect((char *)zone->redzone, redsize + unit, MY_PROT) < 0) {
-		char buf[2560];
-		sprintf(buf, "unable to set %s#%d redzone\n"
-			"base=%p, redzone=%p",
-			zone->name, zone->id, zone->bottom, zone->redzone);
-		fatal_error(buf);
-	}
-  #else	/* not HAVE_SIGINFO */
-	zone->hardmax = (Word *) ((char *)zone->top-unit);
-	if (mprotect((char *)zone->hardmax, unit, MY_PROT) < 0) {
-		char buf[2560];
-		sprintf(buf, "unable to set %s#%d hardmax\n"
-			"base=%p, hardmax=%p",
-			zone->name, zone->id, zone->bottom, zone->hardmax);
-		fatal_error(buf);
-	}
-  #endif /* not HAVE_SIGINFO */
-#endif	/* not HAVE_MPROTECT */
-
-	return zone;
-} /* end construct_zone() */
-
-void 
-reset_zone(MemoryZone *zone)
-{
-#if	defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
-	zone->redzone = zone->redzone_base;
-
-	if (mprotect((char *)zone->redzone,
-		((char *)zone->top) - ((char *) zone->redzone), MY_PROT) < 0)
-	{
-		char buf[2560];
-		sprintf(buf, "unable to reset %s#%d redzone\n"
-			"base=%p, redzone=%p",
-			zone->name, zone->id, zone->bottom, zone->redzone);
-		fatal_error(buf);
-	}
-#endif
-}
-
-#define STDERR 2
-
-#ifndef	MR_LOWLEVEL_DEBUG
-
-static void 
-print_dump_stack(void)
-{
-	const char *msg =
-		"You can get a stack dump by using `--low-level-debug'\n";
-	write(STDERR, msg, strlen(msg));
-}
-
-#else /* MR_LOWLEVEL_DEBUG */
-
-static void 
-print_dump_stack(void)
-{
-	int	i;
-	int	start;
-	int	count;
-	char	buf[2560];
-
-	strcpy(buf, "A dump of the det stack follows\n\n");
-	write(STDERR, buf, strlen(buf));
-
-	i = 0;
-	while (i < dumpindex) {
-		start = i;
-		count = 1;
-		i++;
-
-		while (i < dumpindex &&
-			strcmp(((char **)(dumpstack_zone->min))[i],
-				((char **)(dumpstack_zone->min))[start]) == 0)
-		{
-			count++;
-			i++;
-		}
-
-		if (count > 1) {
-			sprintf(buf, "%s * %d\n",
-				((char **)(dumpstack_zone->min))[start], count);
-		} else {
-			sprintf(buf, "%s\n",
-				((char **)(dumpstack_zone->min))[start]);
-		}
-
-		write(STDERR, buf, strlen(buf));
-	} /* end while */
-
-	strcpy(buf, "\nend of stack dump\n");
-	write(STDERR, buf, strlen(buf));
-
-} /* end print_dump_stack() */
-
-#endif /* MR_LOWLEVEL_DEBUG */
-
-#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
-	/* try_munprotect is only useful if we have SIGINFO */
-
-/*
-** fatal_abort() prints an error message, possibly a stack dump, and then exits.
-** It is like fatal_error(), except that it is safe to call
-** from a signal handler.
-*/
-
-static void 
-fatal_abort(void *context, const char *main_msg, int dump)
-{
-	char	*context_msg;
-
-	context_msg = explain_context(context);
-	write(STDERR, main_msg, strlen(main_msg));
-	write(STDERR, context_msg, strlen(context_msg));
-	MR_trace_report_raw(STDERR);
-
-	if (dump) {
-		print_dump_stack();
-	}
-
-	_exit(1);
-}
-
-static bool 
-try_munprotect(void *addr, void *context)
-{
-	Word *    fault_addr;
-	Word *    new_zone;
-	MemoryZone *zone;
-
-	fault_addr = (Word *) addr;
-
-	zone = used_memory_zones;
-
-	if (memdebug) {
-		fprintf(stderr, "caught fault at %p\n", (void *)addr);
-	}
-
-	while(zone != NULL) {
-		if (memdebug) {
-			fprintf(stderr, "checking %s#%d: %p - %p\n",
-				zone->name, zone->id, (void *) zone->redzone,
-				(void *) zone->top);
-		}
-
-		if (zone->redzone <= fault_addr && fault_addr <= zone->top) {
-
-			if (memdebug) {
-				fprintf(stderr, "address is in %s#%d redzone\n",
-					zone->name, zone->id);
-			}
-
-			return zone->handler(fault_addr, zone, context);
-		}
-		zone = zone->next;
-	}
-
-	if (memdebug) {
-		fprintf(stderr, "address not in any redzone.\n");
-	}
-
-	return FALSE;
-} /* end try_munprotect() */
-
-bool 
-default_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
-    Word *new_zone;
-    size_t zone_size;
-
-    new_zone = (Word *) round_up((Unsigned) fault_addr + sizeof(Word), unit);
-
-    if (new_zone <= zone->hardmax) {
-	zone_size = (char *)new_zone - (char *)zone->redzone;
-
-	if (memdebug) {
-	    fprintf(stderr, "trying to unprotect %s#%d from %p to %p (%x)\n",
-	    zone->name, zone->id, (void *) zone->redzone, (void *) new_zone,
-	    (int)zone_size);
-	}
-	if (mprotect((char *)zone->redzone, zone_size,
-	    PROT_READ|PROT_WRITE) < 0)
-	{
-	    char buf[2560];
-	    sprintf(buf, "Mercury runtime: cannot unprotect %s#%d zone",
-		zone->name, zone->id);
-	    perror(buf);
-	    exit(1);
-	}
-
-	zone->redzone = new_zone;
-
-	if (memdebug) {
-	    fprintf(stderr, "successful: %s#%d redzone now %p to %p\n",
-		zone->name, zone->id, (void *) zone->redzone,
-		(void *) zone->top);
-	}
-	return TRUE;
-    } else {
-	char buf[2560];
-	if (memdebug) {
-	    fprintf(stderr, "can't unprotect last page of %s#%d\n",
-		zone->name, zone->id);
-	    fflush(stdout);
-	}
-	sprintf(buf, "\nMercury runtime: memory zone %s#%d overflowed\n",
-		zone->name, zone->id);
-	fatal_abort(context, buf, TRUE);
-    }
-
-    return FALSE;
-} /* end default_handler() */
-
-bool 
-null_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
-	return FALSE;
-}
-
-#else
-/* not HAVE_MPROTECT || not HAVE_SIGINFO */
-
-static bool 
-try_munprotect(void *addr, void *context)
-{
-	return FALSE;
-}
-
-bool 
-default_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
-	return FALSE;
-}
-
-bool 
-null_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
-	return FALSE;
-}
-
-#endif /* not HAVE_MPROTECT || not HAVE_SIGINFO */
-
-#if defined(HAVE_SIGCONTEXT_STRUCT)
-
-static void
-setup_signal(void)
-{
-	if (signal(SIGBUS, (void(*)(int)) complex_sighandler) == SIG_ERR)
-	{
-		perror("cannot set SIGBUS handler");
-		exit(1);
-	}
-
-	if (signal(SIGSEGV, (void(*)(int)) complex_sighandler) == SIG_ERR)
-	{
-		perror("cannot set SIGSEGV handler");
-		exit(1);
-	}
-}
-
-static void
-complex_sighandler(int sig, struct sigcontext_struct sigcontext)
-{
-	void *address = (void *) sigcontext.cr2;
-  #ifdef PC_ACCESS
-	void *pc_at_signal = (void *) sigcontext.PC_ACCESS;
-  #endif
-
-	switch(sig) {
-		case SIGSEGV:
-			/*
-			** If we're debugging, print the segv explanation
-			** messages before we call try_munprotect.  But if
-			** we're not debugging, only print them if
-			** try_munprotect fails.
-			*/
-			if (memdebug) {
-				fflush(stdout);
-				fprintf(stderr, "\n*** Mercury runtime: "
-					"caught segmentation violation ***\n");
-			}
-			if (try_munprotect(address, &sigcontext)) {
-				if (memdebug) {
-					fprintf(stderr, "returning from "
-						"signal handler\n\n");
-				}
-				return;
-			}
-			if (!memdebug) {
-				fflush(stdout);
-				fprintf(stderr, "\n*** Mercury runtime: "
-					"caught segmentation violation ***\n");
-			}
-			break;
-
-		case SIGBUS:
-			fflush(stdout);
-			fprintf(stderr, "\n*** Mercury runtime: "
-					"caught bus error ***\n");
-			break;
-
-		default:
-			fflush(stdout);
-			fprintf(stderr, "\n*** Mercury runtime: "
-					"caught unknown signal %d ***\n", sig);
-			break;
-	}
-
-  #ifdef PC_ACCESS
-	fprintf(stderr, "PC at signal: %ld (%lx)\n",
-		(long) pc_at_signal, (long) pc_at_signal);
-  #endif
-	fprintf(stderr, "address involved: %p\n", address);
-
-	MR_trace_report(stderr);
-	print_dump_stack();
-	dump_prev_locations();
-	fprintf(stderr, "exiting from signal handler\n");
-	exit(1);
-} /* end complex_sighandler() */
-
-static char *
-explain_context(void *the_context)
-{
-	static	char	buf[100];
-  #ifdef PC_ACCESS
-	struct sigcontext_struct *context = the_context;
-	void *pc_at_signal = (void *) context->PC_ACCESS;
-
-	sprintf(buf, "PC at signal: %ld (%lx)\n",
-		(long)pc_at_signal, (long)pc_at_signal);
-  #else
-	buf[0] = '\0';
-  #endif
-
-	return buf;
-}
-
-#elif defined(HAVE_SIGINFO_T)
-
-static void 
-setup_signal(void)
-{
-	struct sigaction	act;
-
-	act.sa_flags = SA_SIGINFO | SA_RESTART;
-	if (sigemptyset(&act.sa_mask) != 0) {
-		perror("Mercury runtime: cannot set clear signal mask");
-		exit(1);
-	}
-
-	act.SIGACTION_FIELD = complex_bushandler;
-	if (sigaction(SIGBUS, &act, NULL) != 0) {
-		perror("Mercury runtime: cannot set SIGBUS handler");
-		exit(1);
-	}
-
-	act.SIGACTION_FIELD = complex_segvhandler;
-	if (sigaction(SIGSEGV, &act, NULL) != 0) {
-		perror("Mercury runtime: cannot set SIGSEGV handler");
-		exit(1);
-	}
-}
-
-static void 
-complex_bushandler(int sig, siginfo_t *info, void *context)
-{
-	fflush(stdout);
-
-	if (sig != SIGBUS || !info || info->si_signo != SIGBUS) {
-		fprintf(stderr, "\n*** Mercury runtime: ");
-		fprintf(stderr, "caught strange bus error ***\n");
-		exit(1);
-	}
-
-	fprintf(stderr, "\n*** Mercury runtime: ");
-	fprintf(stderr, "caught bus error ***\n");
-
-	if (info->si_code > 0) {
-		fprintf(stderr, "cause: ");
-		switch (info->si_code)
-		{
-		case BUS_ADRALN:
-			fprintf(stderr, "invalid address alignment\n");
-			break;
-
-		case BUS_ADRERR:
-			fprintf(stderr, "non-existent physical address\n");
-			break;
-
-		case BUS_OBJERR:
-			fprintf(stderr, "object specific hardware error\n");
-			break;
-
-		default:
-			fprintf(stderr, "unknown\n");
-			break;
-
-		} /* end switch */
-
-		fprintf(stderr, "%s", explain_context(context));
-		fprintf(stderr, "address involved: %p\n",
-			(void *) info->si_addr);
-	} /* end if */
-
-	MR_trace_report(stderr);
-	print_dump_stack();
-	dump_prev_locations();
-	fprintf(stderr, "exiting from signal handler\n");
-	exit(1);
-} /* end complex_bushandler() */
-
-static void 
-explain_segv(siginfo_t *info, void *context)
-{
-	fflush(stdout);
-
-	fprintf(stderr, "\n*** Mercury runtime: ");
-	fprintf(stderr, "caught segmentation violation ***\n");
-
-	if (!info) {
-		return;
-	}
-
-	if (info->si_code > 0) {
-		fprintf(stderr, "cause: ");
-		switch (info->si_code)
-		{
-		case SEGV_MAPERR:
-			fprintf(stderr, "address not mapped to object\n");
-			break;
-
-		case SEGV_ACCERR:
-			fprintf(stderr, "bad permissions for mapped object\n");
-			break;
-
-		default:
-			fprintf(stderr, "unknown\n");
-			break;
-		}
-
-		fprintf(stderr, "%s", explain_context(context));
-		fprintf(stderr, "address involved: %p\n",
-			(void *) info->si_addr);
-
-	} /* end if */
-} /* end explain_segv() */
-
-static void 
-complex_segvhandler(int sig, siginfo_t *info, void *context)
-{
-	if (sig != SIGSEGV || !info || info->si_signo != SIGSEGV) {
-		fprintf(stderr, "\n*** Mercury runtime: ");
-		fprintf(stderr, "caught strange segmentation violation ***\n");
-		exit(1);
-	}
-
-	/*
-	** If we're debugging, print the segv explanation messages
-	** before we call try_munprotect.  But if we're not debugging,
-	** only print them if try_munprotect fails.
-	*/
-
-	if (memdebug) {
-		explain_segv(info, context);
-	}
-
-	if (try_munprotect(info->si_addr, context)) {
-		if (memdebug) {
-			fprintf(stderr, "returning from signal handler\n\n");
-		}
-
-		return;
-	}
-
-	if (!memdebug) {
-		explain_segv(info, context);
-	}
-
-	MR_trace_report(stderr);
-	print_dump_stack();
-	dump_prev_locations();
-	fprintf(stderr, "exiting from signal handler\n");
-	exit(1);
-} /* end complex_segvhandler */
-
-static char *
-explain_context(void *the_context)
-{
-	static	char	buf[100];
-
-  #ifdef PC_ACCESS
-
-	ucontext_t *context = the_context;
-
-    #ifdef PC_ACCESS_GREG
-	sprintf(buf, "PC at signal: %ld (%lx)\n",
-		(long) context->uc_mcontext.gregs[PC_ACCESS],
-		(long) context->uc_mcontext.gregs[PC_ACCESS]);
-    #else
-	sprintf(buf, "PC at signal: %ld (%lx)\n",
-		(long) context->uc_mcontext.PC_ACCESS,
-		(long) context->uc_mcontext.PC_ACCESS);
-    #endif
-
-  #else /* not PC_ACCESS */
-
-	/* if PC_ACCESS is not set, we don't know the context */
-	/* therefore we return an empty string to be printed  */
-	buf[0] = '\0';
-
-  #endif /* not PC_ACCESS */
-
-	return buf;
-}
-
-#else /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
-
-static void 
-setup_signal(void)
-{
-	if (signal(SIGBUS, simple_sighandler) == SIG_ERR) {
-		perror("cannot set SIGBUS handler");
-		exit(1);
-	}
-
-	if (signal(SIGSEGV, simple_sighandler) == SIG_ERR) {
-		perror("cannot set SIGSEGV handler");
-		exit(1);
-	}
-}
-
-static void 
-simple_sighandler(int sig)
-{
-	fflush(stdout);
-	fprintf(stderr, "*** Mercury runtime: ");
-
-	switch (sig)
-	{
-	case SIGBUS:
-		fprintf(stderr, "caught bus error ***\n");
-		break;
-
-	case SIGSEGV:
-		fprintf(stderr, "caught segmentation violation ***\n");
-		break;
-
-	default:
-		fprintf(stderr, "caught unknown signal %d ***\n", sig);
-		break;
-	}
-
-	print_dump_stack();
-	dump_prev_locations();
-	fprintf(stderr, "exiting from signal handler\n");
-	exit(1);
-}
-
-#endif /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
 
 #ifdef	CONSERVATIVE_GC
 
Index: runtime/mercury_memory.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_memory.h,v
retrieving revision 1.4
diff -u -r1.4 mercury_memory.h
--- mercury_memory.h	1998/03/30 03:08:38	1.4
+++ mercury_memory.h	1998/04/29 06:14:16
@@ -18,113 +18,7 @@
 #ifndef	MERCURY_MEMORY_H
 #define	MERCURY_MEMORY_H
 
-#include "mercury_regs.h"		/* for NUM_REAL_REGS */
-
-#include <stdlib.h>		/* for size_t */
-
-#include "mercury_types.h"	/* for Word */
-#include "mercury_std.h"		/* for bool */
-
-
-/* these cannot be changed without lots of modifications elsewhere */
-#define MAX_REAL_REG 32		/* r1 .. r32 */
-#define NUM_SPECIAL_REG 5	/* succip, sp, hp, maxfr, curfr */
-
-/* this can be changed at will, including by -D options to the C compiler */
-#ifndef MAX_VIRTUAL_REG
-#define MAX_VIRTUAL_REG	1024
-#endif
-
-/* allocate enough fake_regs to hold both the special regs */
-/* and all the virtual registers */
-#define MAX_FAKE_REG	(NUM_SPECIAL_REG + MAX_VIRTUAL_REG)
-				/* mr0 .. mr36, mr(37) ... mr(1028) */
-
-/* reserve MAX_FAKE_REG virtual regs, numbered from 0 to MAX_FAKE_REG-1 */
-extern	Word	fake_reg[MAX_FAKE_REG];
-
-/* used to lookup the fake_reg for a given real reg */
-extern	Word	virtual_reg_map[MAX_REAL_REG];
-
-/* used for counting register usage */
-extern	unsigned long 	num_uses[MAX_RN];
-
-/*
-** The Mercury runtime uses a number of memory areas or *zones*. These
-** hold the detstack, the nondetstack, the heap, and potentially other
-** areas such as a trail, a "solutions"-heap, and so on.
-** These memory areas are each represented by a structure that contains
-** the following fields:
-**	name	- a string constant used to name the allocated area
-**	id	- an integer which together with the name should uniquely
-**		  identify the allocated area.
-**	bottom	- the address of the bottom of the allocated area
-**			(should be on a page boundary)
-**	top	- the address one word past the top of the allocated area
-**			(should be on a page boundary)
-**	min	- the address of the lowest part of the allocated that
-**			will be used. This may be different to `bottom'
-**			so that the use of different memory zones doesn't
-**			beat the cache.
-**	max	- the highest address in this memory area that has been
-**			used so far. This is only defined in debugging grades.
-**	hardmax	- the address of the bottom of the last page of the allocated
-**			area. This is one higher than the highest address that
-**			can be used in this zone. We never unprotect the
-**			last page of a zone so that we retain protection
-**			against overrunning the end of the zone. This is
-**			obviously only available on platforms that have
-**			mprotect.
-**			(should be on a page boundary)
-**	redzone	- the address of the start of the region that has been
-**			mprotected as a redzone. Since without SIGINFO
-**			it is not possible [portably] to figure out
-**			where the fault occured, redzone is only available
-**			on platforms that have both mprotect and SIGINFO.
-**			(should be on a page boundary)
-**	handler - the address of a function to handle accesses in the
-**			redzone of this allocated area. This is only
-**			specified if mprotect and SIGINFO are available.
-*/
-
-typedef struct MEMORY_ZONE	MemoryZone;
-
-typedef bool ZoneHandler(Word *addr, struct MEMORY_ZONE *zone, void *context);
-
-struct MEMORY_ZONE {
-	struct MEMORY_ZONE *next; /* the memory zones are organized as a
-				  ** linked list of free zones and linked
-				  ** list of used zones. The next field
-				  ** is NULL or a pointer to the next memory
-				  ** zone in the list.
-				  */
-	const char *name;	/* name identifier */
-	int	id;		/* number */
-	Word	*bottom;	/* beginning of the allocated area */
-	Word	*top;		/* end of the allocated area */
-	Word	*min;		/* lowest word of the area to be used */
-	Word	*max;		/* highest word of the area to be used;
-				   computed only if MR_LOWLEVEL_DEBUG is
-				   enabled */
-#ifdef HAVE_MPROTECT
-	Word	*redzone_base;	/* beginning of the original redzone */
-	Word	*redzone;	/* beginning of the current redzone */
-	Word	*hardmax;	/* last page of the zone which can't be
-				   unprotected */
-  #ifdef HAVE_SIGINFO
-	ZoneHandler *handler;   /* handler for page faults in the redzone */
-  #endif /* HAVE_SIGINFO */
-#endif	/* HAVE_MPROTECT */
-};
-
-#define MAX_ZONES	16
-
-extern MemoryZone	*zone_table;
-
-	/* A linked list of all the unused zones */
-extern MemoryZone	*free_memory_zones;
-	/* A linked list of all the used zones */
-extern MemoryZone	*used_memory_zones;
+#include "mercury_memory_zones.h"
 
 extern MemoryZone	*detstack_zone;
 extern MemoryZone	*nondetstack_zone;
@@ -139,73 +33,21 @@
 #endif
 
 /*
-** create_zone(Name, Id, Size, Offset, RedZoneSize, FaultHandler)
-** allocates a new memory zone with name Name, and number Id, size
-** Size (in bytes - which gets rounded up to the nearest multiple of
-** the page size), an offset Offset from the page boundary at which
-** to start using the memory region (used to help avoid beating the cache),
-** the amount Redzone of memory (in bytes) to be protected as a redzone
-** (must be less than Size), and the address of a function to handle
-** memory references in the redzone.
-** If it fails to allocate or protect the zone, then it exits.
-** If mprotect or SIGINFO are unavailable, then the last two arguments
-** are ignored.
-*/
-
-MemoryZone	*create_zone(const char *name, int id,
-			size_t size, size_t offset, size_t redsize,
-			ZoneHandler *handler);
-
-/*
-** construct_zone(Name, Id, Base, Size, Offset, RedZoneSize, FaultHandler)
-** has the same behaviour as create_zone, except instread of allocating
-** the memory, it takes a pointer to a region of memory that must be at
-** least Size bytes, or if HAVE_MPROTECT is defined, then it must be at
-** least Size + unit[*] bytes.
-** If it fails to protect the redzone then it exits
-** If mprotect or SIGINFO are unavailable, then the last two arguments
-** are ignored.
-**
-** [*] unit is a global variable containing the page size in bytes
+** round_up(amount, align) returns `amount' rounded up to the nearest
+** alignment boundary.  `align' must be a power of 2.
 */
 
-MemoryZone	*construct_zone(const char *name, int Id, Word *base,
-			size_t size, size_t offset, size_t redsize,
-			ZoneHandler *handler);
+#define round_up(amount, align) ((((amount) - 1) | ((align) - 1)) + 1)
 
-/*
-** reset_zone(Zone) resets the redzone on the given MemoryZone to the
-** original zone specified in the call to {create,construct}_zone() if
-** HAVE_MPROTECT and HAVE_SIGINFO. If either HAVE_MPROTECT or HAVE_SIGINFO
-** are not defined, it does nothing.
+/* 
+** For these functions, see the comments in mercury_memory.c and 
+** mercury_engine.c 
 */
-void	reset_zone(MemoryZone *zone);
-
-/*
-** default_handler is a function that can be passed to create_zone to
-** unprotect enough of the redzone to allow the access to succeed, or
-** fail if there is no space left in the zone.
-*/
-ZoneHandler default_handler;
-
-/*
-** null_handler is a function that can be passed to create_zone which always
-** fails.
-*/
-ZoneHandler null_handler;
-
-/* for these functions, see the comments in memory.c and engine.mod */
 extern	void	init_memory(void);
 extern	void	init_heap(void);
 extern	void	debug_memory(void);
 
 /*
-** next_offset() returns sucessive offsets across the primary cache. Useful
-** when calling {create,construct}_zone().
-*/
-extern	size_t	next_offset(void);
-
-/*
 ** allocate_bytes() allocates the given number of bytes.
 **
 ** allocate_object(type) allocates space for an object of the specified type.
@@ -246,5 +88,14 @@
 #include <stddef.h>	/* for size_t */
 void *checked_malloc(size_t n);
 void *checked_realloc(void *old, size_t n);
+
+/*
+** `unit' is the size of the minimum unit of memory we allocate (in bytes).
+** `page_size' is the size of a single page of memory.
+*/
+
+extern size_t          unit;
+extern size_t          page_size;
+
 
 #endif /* not MERCURY_MEMORY_H */

New File: runtime/mercury_memory_handlers.c
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** This module defines the signal handlers for memory zones.
** These handlers are invoked when memory is accessed outside of
** the memory zones, or at the protected region at the end of a
** memory zone (if available).
*/

/*---------------------------------------------------------------------------*/

#include "mercury_imp.h"

#ifdef HAVE_SIGCONTEXT_STRUCT
  /*
  ** Some versions of Linux call it struct sigcontext_struct, some call it
  ** struct sigcontext.  The following #define eliminates the differences.
  */
  #define sigcontext_struct sigcontext /* must be before #include <signal.h> */

  /*
  ** On some systems (e.g. most versions of Linux) we need to #define
  ** __KERNEL__ to get sigcontext_struct from <signal.h>.
  ** This stuff must come before anything else that might include <signal.h>,
  ** otherwise the #define __KERNEL__ may not work.
  */
  #define __KERNEL__
  #include <signal.h>	/* must come third */
  #undef __KERNEL__

  /*
  ** Some versions of Linux define it in <signal.h>, others define it in
  ** <asm/sigcontext.h>.  We try both.
  */
  #ifdef HAVE_ASM_SIGCONTEXT
    #include <asm/sigcontext.h>
  #endif 
#else
  #include <signal.h>
#endif

#include <unistd.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_SYS_SIGINFO
  #include <sys/siginfo.h>
#endif 

#ifdef	HAVE_MPROTECT
  #include <sys/mman.h>
#endif

#ifdef	HAVE_UCONTEXT
  #include <ucontext.h>
#endif

#ifdef	HAVE_SYS_UCONTEXT
  #include <sys/ucontext.h>
#endif

#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_memory_zones.h"
#include "mercury_memory_handlers.h"

/*---------------------------------------------------------------------------*/

#ifdef HAVE_SIGINFO
  #if defined(HAVE_SIGCONTEXT_STRUCT)
    static	void	complex_sighandler(int, struct sigcontext_struct);
  #elif defined(HAVE_SIGINFO_T)
    static	void	complex_bushandler(int, siginfo_t *, void *);
    static	void	complex_segvhandler(int, siginfo_t *, void *);
  #else
    #error "HAVE_SIGINFO defined but don't know how to get it"
  #endif
#else
  static	void	simple_sighandler(int);
#endif

/*
** round_up(amount, align) returns `amount' rounded up to the nearest
** alignment boundary.  `align' must be a power of 2.
*/

static	void	setup_mprotect(void);
static void	print_dump_stack(void);

#ifdef	HAVE_SIGINFO
  static	bool	try_munprotect(void *address, void *context);
  static	char	*explain_context(void *context);
#endif /* HAVE_SIGINFO */

#define STDERR 2

#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
	/* try_munprotect is only useful if we have SIGINFO */

/*
** fatal_abort() prints an error message, possibly a stack dump, and then exits.
** It is like fatal_error(), except that it is safe to call
** from a signal handler.
*/

static void 
fatal_abort(void *context, const char *main_msg, int dump)
{
	char	*context_msg;

	context_msg = explain_context(context);
	write(STDERR, main_msg, strlen(main_msg));
	write(STDERR, context_msg, strlen(context_msg));
	MR_trace_report_raw(STDERR);

	if (dump) {
		print_dump_stack();
	}

	_exit(1);
}

static bool 
try_munprotect(void *addr, void *context)
{
	Word *    fault_addr;
	Word *    new_zone;
	MemoryZone *zone;

	fault_addr = (Word *) addr;

	zone = get_used_memory_zones();

	if (memdebug) {
		fprintf(stderr, "caught fault at %p\n", (void *)addr);
	}

	while(zone != NULL) {
		if (memdebug) {
			fprintf(stderr, "checking %s#%d: %p - %p\n",
				zone->name, zone->id, (void *) zone->redzone,
				(void *) zone->top);
		}

		if (zone->redzone <= fault_addr && fault_addr <= zone->top) {

			if (memdebug) {
				fprintf(stderr, "address is in %s#%d redzone\n",
					zone->name, zone->id);
			}

			return zone->handler(fault_addr, zone, context);
		}
		zone = zone->next;
	}

	if (memdebug) {
		fprintf(stderr, "address not in any redzone.\n");
	}

	return FALSE;
} /* end try_munprotect() */

bool 
default_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
    Word *new_zone;
    size_t zone_size;

    new_zone = (Word *) round_up((Unsigned) fault_addr + sizeof(Word), unit);

    if (new_zone <= zone->hardmax) {
	zone_size = (char *)new_zone - (char *)zone->redzone;

	if (memdebug) {
	    fprintf(stderr, "trying to unprotect %s#%d from %p to %p (%x)\n",
	    zone->name, zone->id, (void *) zone->redzone, (void *) new_zone,
	    (int)zone_size);
	}
	if (mprotect((char *)zone->redzone, zone_size,
	    PROT_READ|PROT_WRITE) < 0)
	{
	    char buf[2560];
	    sprintf(buf, "Mercury runtime: cannot unprotect %s#%d zone",
		zone->name, zone->id);
	    perror(buf);
	    exit(1);
	}

	zone->redzone = new_zone;

	if (memdebug) {
	    fprintf(stderr, "successful: %s#%d redzone now %p to %p\n",
		zone->name, zone->id, (void *) zone->redzone,
		(void *) zone->top);
	}
	return TRUE;
    } else {
	char buf[2560];
	if (memdebug) {
	    fprintf(stderr, "can't unprotect last page of %s#%d\n",
		zone->name, zone->id);
	    fflush(stdout);
	}
	sprintf(buf, "\nMercury runtime: memory zone %s#%d overflowed\n",
		zone->name, zone->id);
	fatal_abort(context, buf, TRUE);
    }

    return FALSE;
} /* end default_handler() */

bool 
null_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
	return FALSE;
}

#else
/* not HAVE_MPROTECT || not HAVE_SIGINFO */

static bool 
try_munprotect(void *addr, void *context)
{
	return FALSE;
}

bool 
default_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
	return FALSE;
}

bool 
null_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
	return FALSE;
}

#endif /* not HAVE_MPROTECT || not HAVE_SIGINFO */

#if defined(HAVE_SIGCONTEXT_STRUCT)

void
setup_signal(void)
{
	if (signal(SIGBUS, (void(*)(int)) complex_sighandler) == SIG_ERR)
	{
		perror("cannot set SIGBUS handler");
		exit(1);
	}

	if (signal(SIGSEGV, (void(*)(int)) complex_sighandler) == SIG_ERR)
	{
		perror("cannot set SIGSEGV handler");
		exit(1);
	}
}

static void
complex_sighandler(int sig, struct sigcontext_struct sigcontext)
{
	void *address = (void *) sigcontext.cr2;
  #ifdef PC_ACCESS
	void *pc_at_signal = (void *) sigcontext.PC_ACCESS;
  #endif

	switch(sig) {
		case SIGSEGV:
			/*
			** If we're debugging, print the segv explanation
			** messages before we call try_munprotect.  But if
			** we're not debugging, only print them if
			** try_munprotect fails.
			*/
			if (memdebug) {
				fflush(stdout);
				fprintf(stderr, "\n*** Mercury runtime: "
					"caught segmentation violation ***\n");
			}
			if (try_munprotect(address, &sigcontext)) {
				if (memdebug) {
					fprintf(stderr, "returning from "
						"signal handler\n\n");
				}
				return;
			}
			if (!memdebug) {
				fflush(stdout);
				fprintf(stderr, "\n*** Mercury runtime: "
					"caught segmentation violation ***\n");
			}
			break;

		case SIGBUS:
			fflush(stdout);
			fprintf(stderr, "\n*** Mercury runtime: "
					"caught bus error ***\n");
			break;

		default:
			fflush(stdout);
			fprintf(stderr, "\n*** Mercury runtime: "
					"caught unknown signal %d ***\n", sig);
			break;
	}

  #ifdef PC_ACCESS
	fprintf(stderr, "PC at signal: %ld (%lx)\n",
		(long) pc_at_signal, (long) pc_at_signal);
  #endif
	fprintf(stderr, "address involved: %p\n", address);

	MR_trace_report(stderr);
	print_dump_stack();
	dump_prev_locations();
	fprintf(stderr, "exiting from signal handler\n");
	exit(1);
} /* end complex_sighandler() */

static char *
explain_context(void *the_context)
{
	static	char	buf[100];
  #ifdef PC_ACCESS
	struct sigcontext_struct *context = the_context;
	void *pc_at_signal = (void *) context->PC_ACCESS;

	sprintf(buf, "PC at signal: %ld (%lx)\n",
		(long)pc_at_signal, (long)pc_at_signal);
  #else
	buf[0] = '\0';
  #endif

	return buf;
}

#elif defined(HAVE_SIGINFO_T)

void 
setup_signal(void)
{
	struct sigaction	act;

	act.sa_flags = SA_SIGINFO | SA_RESTART;
	if (sigemptyset(&act.sa_mask) != 0) {
		perror("Mercury runtime: cannot set clear signal mask");
		exit(1);
	}

	act.SIGACTION_FIELD = complex_bushandler;
	if (sigaction(SIGBUS, &act, NULL) != 0) {
		perror("Mercury runtime: cannot set SIGBUS handler");
		exit(1);
	}

	act.SIGACTION_FIELD = complex_segvhandler;
	if (sigaction(SIGSEGV, &act, NULL) != 0) {
		perror("Mercury runtime: cannot set SIGSEGV handler");
		exit(1);
	}
}

static void 
complex_bushandler(int sig, siginfo_t *info, void *context)
{
	fflush(stdout);

	if (sig != SIGBUS || !info || info->si_signo != SIGBUS) {
		fprintf(stderr, "\n*** Mercury runtime: ");
		fprintf(stderr, "caught strange bus error ***\n");
		exit(1);
	}

	fprintf(stderr, "\n*** Mercury runtime: ");
	fprintf(stderr, "caught bus error ***\n");

	if (info->si_code > 0) {
		fprintf(stderr, "cause: ");
		switch (info->si_code)
		{
		case BUS_ADRALN:
			fprintf(stderr, "invalid address alignment\n");
			break;

		case BUS_ADRERR:
			fprintf(stderr, "non-existent physical address\n");
			break;

		case BUS_OBJERR:
			fprintf(stderr, "object specific hardware error\n");
			break;

		default:
			fprintf(stderr, "unknown\n");
			break;

		} /* end switch */

		fprintf(stderr, "%s", explain_context(context));
		fprintf(stderr, "address involved: %p\n",
			(void *) info->si_addr);
	} /* end if */

	MR_trace_report(stderr);
	print_dump_stack();
	dump_prev_locations();
	fprintf(stderr, "exiting from signal handler\n");
	exit(1);
} /* end complex_bushandler() */

static void 
explain_segv(siginfo_t *info, void *context)
{
	fflush(stdout);

	fprintf(stderr, "\n*** Mercury runtime: ");
	fprintf(stderr, "caught segmentation violation ***\n");

	if (!info) {
		return;
	}

	if (info->si_code > 0) {
		fprintf(stderr, "cause: ");
		switch (info->si_code)
		{
		case SEGV_MAPERR:
			fprintf(stderr, "address not mapped to object\n");
			break;

		case SEGV_ACCERR:
			fprintf(stderr, "bad permissions for mapped object\n");
			break;

		default:
			fprintf(stderr, "unknown\n");
			break;
		}

		fprintf(stderr, "%s", explain_context(context));
		fprintf(stderr, "address involved: %p\n",
			(void *) info->si_addr);

	} /* end if */
} /* end explain_segv() */

static void 
complex_segvhandler(int sig, siginfo_t *info, void *context)
{
	if (sig != SIGSEGV || !info || info->si_signo != SIGSEGV) {
		fprintf(stderr, "\n*** Mercury runtime: ");
		fprintf(stderr, "caught strange segmentation violation ***\n");
		exit(1);
	}

	/*
	** If we're debugging, print the segv explanation messages
	** before we call try_munprotect.  But if we're not debugging,
	** only print them if try_munprotect fails.
	*/

	if (memdebug) {
		explain_segv(info, context);
	}

	if (try_munprotect(info->si_addr, context)) {
		if (memdebug) {
			fprintf(stderr, "returning from signal handler\n\n");
		}

		return;
	}

	if (!memdebug) {
		explain_segv(info, context);
	}

	MR_trace_report(stderr);
	print_dump_stack();
	dump_prev_locations();
	fprintf(stderr, "exiting from signal handler\n");
	exit(1);
} /* end complex_segvhandler */

static char *
explain_context(void *the_context)
{
	static	char	buf[100];

  #ifdef PC_ACCESS

	ucontext_t *context = the_context;

    #ifdef PC_ACCESS_GREG
	sprintf(buf, "PC at signal: %ld (%lx)\n",
		(long) context->uc_mcontext.gregs[PC_ACCESS],
		(long) context->uc_mcontext.gregs[PC_ACCESS]);
    #else
	sprintf(buf, "PC at signal: %ld (%lx)\n",
		(long) context->uc_mcontext.PC_ACCESS,
		(long) context->uc_mcontext.PC_ACCESS);
    #endif

  #else /* not PC_ACCESS */

	/* if PC_ACCESS is not set, we don't know the context */
	/* therefore we return an empty string to be printed  */
	buf[0] = '\0';

  #endif /* not PC_ACCESS */

	return buf;
}

#else /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */

void 
setup_signal(void)
{
	if (signal(SIGBUS, simple_sighandler) == SIG_ERR) {
		perror("cannot set SIGBUS handler");
		exit(1);
	}

	if (signal(SIGSEGV, simple_sighandler) == SIG_ERR) {
		perror("cannot set SIGSEGV handler");
		exit(1);
	}
}

static void 
simple_sighandler(int sig)
{
	fflush(stdout);
	fprintf(stderr, "*** Mercury runtime: ");

	switch (sig)
	{
	case SIGBUS:
		fprintf(stderr, "caught bus error ***\n");
		break;

	case SIGSEGV:
		fprintf(stderr, "caught segmentation violation ***\n");
		break;

	default:
		fprintf(stderr, "caught unknown signal %d ***\n", sig);
		break;
	}

	print_dump_stack();
	dump_prev_locations();
	fprintf(stderr, "exiting from signal handler\n");
	exit(1);
}

#endif /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */

#ifndef	MR_LOWLEVEL_DEBUG

static void 
print_dump_stack(void)
{
	const char *msg =
		"You can get a stack dump by using `--low-level-debug'\n";
	write(STDERR, msg, strlen(msg));
}

#else /* MR_LOWLEVEL_DEBUG */

static void 
print_dump_stack(void)
{
	int	i;
	int	start;
	int	count;
	char	buf[2560];

	strcpy(buf, "A dump of the det stack follows\n\n");
	write(STDERR, buf, strlen(buf));

	i = 0;
	while (i < dumpindex) {
		start = i;
		count = 1;
		i++;

		while (i < dumpindex &&
			strcmp(((char **)(dumpstack_zone->min))[i],
				((char **)(dumpstack_zone->min))[start]) == 0)
		{
			count++;
			i++;
		}

		if (count > 1) {
			sprintf(buf, "%s * %d\n",
				((char **)(dumpstack_zone->min))[start], count);
		} else {
			sprintf(buf, "%s\n",
				((char **)(dumpstack_zone->min))[start]);
		}

		write(STDERR, buf, strlen(buf));
	} /* end while */

	strcpy(buf, "\nend of stack dump\n");
	write(STDERR, buf, strlen(buf));

} /* end print_dump_stack() */

#endif /* MR_LOWLEVEL_DEBUG */


New File: runtime/mercury_memory_handlers.h
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** mercury_memory_handlers.h - signal handlers for the memory zones.
**
** This defines various signal handlers for memory access violations,
** including accesses to the redzones at the end of each zone.
*/

#ifndef	MERCURY_MEMORY_HANDLERS_H
#define	MERCURY_MEMORY_HANDLERS_H

#include "mercury_memory_zones.h"

/*
** default_handler is a function that can be passed to create_zone to
** unprotect enough of the redzone to allow the access to succeed, or
** fail if there is no space left in the zone.
*/
ZoneHandler default_handler;

/*
** null_handler is a function that can be passed to create_zone which always
** fails.
*/
ZoneHandler null_handler;

/*
**
** setup_signal() will setup the default signal handlers.
**
*/

void	setup_signal(void);

#endif /* not MERCURY_MEMORY_HANDLERS_H */

New File: runtime/mercury_memory_zones.c
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** This module defines the MemoryZone data structure and operations
** for managing the memory zones.
**
** The offset of each zone can be supplied to allow us to control how
** they map onto direct mapped caches.  The provided next_offset()
** function can be used to generate these offsets.
**
** We allocate a large arena, preferably aligned on a boundary that
** is a multiple of both the page size and the primary cache size.
**
** If the operating system of the machine supports the mprotect syscall,
** we also protect a chunk at the end of each area against access,
** thus detecting area overflow.
*/

/*---------------------------------------------------------------------------*/

#include "mercury_imp.h"

#include <unistd.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_SYS_SIGINFO
  #include <sys/siginfo.h>
#endif 

#ifdef	HAVE_MPROTECT
  #include <sys/mman.h>
#endif

#ifdef	HAVE_UCONTEXT
  #include <ucontext.h>
#endif

#ifdef	HAVE_SYS_UCONTEXT
  #include <sys/ucontext.h>
#endif

#include "mercury_imp.h"
#include "mercury_trace.h"

/*---------------------------------------------------------------------------*/

#ifdef	CONSERVATIVE_GC
  #define	memalign(a,s)   GC_MALLOC_UNCOLLECTABLE(s)
#elif defined(HAVE_MEMALIGN)
  extern void	*memalign(size_t, size_t);
#else
  #define	memalign(a,s)	malloc(s)
#endif

/*
** DESCRIPTION
**  The function mprotect() changes the  access  protections  on
**  the mappings specified by the range [addr, addr + len) to be
**  that specified by prot.  Legitimate values for prot are  the
**  same  as  those  permitted  for  mmap  and  are  defined  in
**  <sys/mman.h> as:
**
** PROT_READ    page can be read
** PROT_WRITE   page can be written
** PROT_EXEC    page can be executed
** PROT_NONE    page can not be accessed
*/
#ifdef  HAVE_MPROTECT

  #ifdef CONSERVATIVE_GC
    /*
    ** The conservative garbage collectors scans through
    ** all these areas, so we need to allow reads.
    ** XXX This probably causes efficiency problems:
    ** too much memory for the GC to scan, and it probably
    ** all gets paged in.
    */
    #define MY_PROT PROT_READ
  #else
    #define MY_PROT PROT_NONE
  #endif

  /* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
  #ifndef PROT_NONE
    #define PROT_NONE 0
  #endif

#endif /* HAVE_MPROTECT */

/*---------------------------------------------------------------------------*/

Word	fake_reg[MAX_FAKE_REG];
Word	virtual_reg_map[MAX_REAL_REG] = VIRTUAL_REG_MAP_BODY;

unsigned long	num_uses[MAX_RN];

#define MAX_ZONES	16

static MemoryZone *zone_table;

static MemoryZone *used_memory_zones;
static MemoryZone *free_memory_zones;

static MemoryZone	*get_zone(void);
static void		unget_zone(MemoryZone *zone);

	/*
	** We manage the handing out of offets through the cache by
	** computing the offsets once and storing them in an array
	** (in shared memory if necessary). We then maintain a global
	** counter used to index the array which we increment (modulo
	** the size of the array) after handing out each offset.
	*/

#define	CACHE_SLICES	8

static	size_t		*offset_vector;
static	int		*offset_counter;
static	SpinLock	*offset_lock;
size_t	next_offset(void);

void 
init_memory_arena()
{
#ifdef	PARALLEL
  #ifndef CONSERVATIVE_GC
	if (numprocs > 1) {
		fatal_error("shared memory not implemented");
	}
  #else
	if (numprocs > 1) {
		fatal_error("shared memory not implemented with conservative gc");
	}
  #endif
#endif
}

void 
init_zones()
{
	int i;
	size_t fake_reg_offset;

	/*
	** Allocate the MemoryZone table.
	*/
	zone_table = allocate_array(MemoryZone, MAX_ZONES);

	/*
	** Initialize the MemoryZone table.
	*/
	used_memory_zones = NULL;
	free_memory_zones = zone_table;
	for(i = 0; i < MAX_ZONES; i++) {
		zone_table[i].name = "unused";
		zone_table[i].id = i;
		zone_table[i].bottom = NULL;
		zone_table[i].top = NULL;
		zone_table[i].min = NULL;
#ifdef MR_LOWLEVEL_DEBUG
		zone_table[i].max = NULL;
#endif
#ifdef HAVE_MPROTECT
  #ifdef HAVE_SIGINFO
		zone_table[i].redzone = NULL;
  #endif
		zone_table[i].hardmax = NULL;
#endif
		if (i+1 < MAX_ZONES) {
			zone_table[i].next = &(zone_table[i+1]);
		} else {
			zone_table[i].next = NULL;
		}
	}

	offset_counter = allocate_object(int);
	*offset_counter = 0;

	offset_vector = allocate_array(size_t, CACHE_SLICES - 1);

	fake_reg_offset = (Unsigned) fake_reg % pcache_size;

	for (i = 0; i < CACHE_SLICES - 1; i++) {
		offset_vector[i] =
			(fake_reg_offset + pcache_size / CACHE_SLICES)
			% pcache_size;
	}
} /* end init_zones() */

MemoryZone *
get_zone(void)
{
	MemoryZone *zone;

	/*
	** unlink the first zone on the free-list,
	** link it onto the used-list and return it.
	*/
	zone = free_memory_zones;
	if (zone == NULL) {
		fatal_error("no more memory zones");
	}
	free_memory_zones = free_memory_zones->next;

	zone->next = used_memory_zones;
	used_memory_zones = zone;

	return zone;
}

void 
unget_zone(MemoryZone *zone)
{
	MemoryZone *prev, *tmp;

	/*
	** Find the zone on the used list, and unlink it from
	** the list, then link it onto the start of the free-list.
	*/
	for(prev = NULL, tmp = used_memory_zones;
		tmp && tmp != zone; prev = tmp, tmp = tmp->next) 
	{
		/* VOID */
	}
	if (tmp == NULL) {
		fatal_error("memory zone not found!");
	}
	if (prev == NULL) {
		used_memory_zones = used_memory_zones->next;
	} else {
		prev->next = tmp->next;
	}

	zone->next = free_memory_zones;
	free_memory_zones = zone;
}

/*
** successive calls to next_offset return offsets modulo the primary
** cache size (carefully avoiding ever giving an offset that clashes
** with fake_reg_array). This is used to give different memory zones
** different starting points across the caches so that it is better
** utilized.
** An alternative implementation would be to increment the offset by
** a fixed amount (eg 2Kb) so that as primary caches get bigger, we
** allocate more offsets across them.
*/
size_t	
next_offset(void)
{
	size_t offset;

	get_lock(offset_lock);

	offset = offset_vector[*offset_counter];

	*offset_counter = (*offset_counter + 1) % (CACHE_SLICES - 1);

	release_lock(offset_lock);

	return offset;
}

MemoryZone *
create_zone(const char *name, int id, size_t size,
		size_t offset, size_t redsize,
		bool ((*handler)(Word *addr, MemoryZone *zone, void *context)))
{
	Word		*base;
	size_t		total_size;

		/*
		** total allocation is:
		**	unit		(roundup to page boundary)
		**	size		(including redzone)
		**	unit		(an extra page for protection if
		**			 mprotect is being used)
		*/
#ifdef	HAVE_MPROTECT
	total_size = size + 2 * unit;
#else
	total_size = size + unit;
#endif

#ifdef	PARALLEL
	if (numprocs > 1) {
		fatal_error("shared memory not supported yet");
	}
#endif
	base = memalign(unit, total_size);
	if (base == NULL) {
		char buf[2560];
		sprintf(buf, "unable allocate memory zone: %s#%d", name, id);
		fatal_error(buf);
	}

	return construct_zone(name, id, base, size, offset, redsize, handler);
} /* end create_zone() */

MemoryZone *
construct_zone(const char *name, int id, Word *base,
		size_t size, size_t offset, size_t redsize,
		ZoneHandler handler)
{
	MemoryZone	*zone;
	size_t		total_size;

	if (base == NULL) {
		fatal_error("construct_zone called with NULL pointer");
	}

	zone = get_zone();

	zone->name = name;
	zone->id = id;

#if	defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
	zone->handler = handler;
#endif

	zone->bottom = base;

#ifdef 	HAVE_MPROTECT
	total_size = size + unit;
#else
	total_size = size;
#endif

	zone->top = (Word *) ((char *)base+total_size);
	zone->min = (Word *) ((char *)base+offset);
#ifdef MR_LOWLEVEL_DEBUG
	zone->max = zone->min;
#endif

	/*
	** setup the redzone+hardzone
	*/
#ifdef	HAVE_MPROTECT
  #ifdef HAVE_SIGINFO
	zone->redzone_base = zone->redzone = (Word *)
			round_up((Unsigned)base + size - redsize, unit);
	if (mprotect((char *)zone->redzone, redsize + unit, MY_PROT) < 0) {
		char buf[2560];
		sprintf(buf, "unable to set %s#%d redzone\n"
			"base=%p, redzone=%p",
			zone->name, zone->id, zone->bottom, zone->redzone);
		fatal_error(buf);
	}
  #else	/* not HAVE_SIGINFO */
	zone->hardmax = (Word *) ((char *)zone->top-unit);
	if (mprotect((char *)zone->hardmax, unit, MY_PROT) < 0) {
		char buf[2560];
		sprintf(buf, "unable to set %s#%d hardmax\n"
			"base=%p, hardmax=%p",
			zone->name, zone->id, zone->bottom, zone->hardmax);
		fatal_error(buf);
	}
  #endif /* not HAVE_SIGINFO */
#endif	/* not HAVE_MPROTECT */

	return zone;
} /* end construct_zone() */

void 
reset_zone(MemoryZone *zone)
{
#if	defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
	zone->redzone = zone->redzone_base;

	if (mprotect((char *)zone->redzone,
		((char *)zone->top) - ((char *) zone->redzone), MY_PROT) < 0)
	{
		char buf[2560];
		sprintf(buf, "unable to reset %s#%d redzone\n"
			"base=%p, redzone=%p",
			zone->name, zone->id, zone->bottom, zone->redzone);
		fatal_error(buf);
	}
#endif
}

MemoryZone *
get_used_memory_zones(void)
{
	return used_memory_zones;
}

void
debug_memory(void)
{
	MemoryZone	*zone;

	fprintf(stderr, "\n");
	fprintf(stderr, "pcache_size  = %lu (0x%lx)\n",
		(unsigned long) pcache_size, (unsigned long) pcache_size);
	fprintf(stderr, "page_size    = %lu (0x%lx)\n",
		(unsigned long) page_size, (unsigned long) page_size);
	fprintf(stderr, "unit         = %lu (0x%lx)\n",
		(unsigned long) unit, (unsigned long) unit);

	fprintf(stderr, "\n");
	fprintf(stderr, "fake_reg       = %p (offset %ld)\n",
		(void *) fake_reg, (long) fake_reg & (unit-1));
	fprintf(stderr, "\n");

	for (zone = used_memory_zones; zone; zone = zone->next)
	{
		fprintf(stderr, "%-16s#%d-base	= %p\n",
			zone->name, zone->id, (void *) zone->bottom);
		fprintf(stderr, "%-16s#%d-min		= %p\n",
			zone->name, zone->id, (void *) zone->min);
		fprintf(stderr, "%-16s#%d-top		= %p\n",
			zone->name, zone->id, (void *) zone->top);
#ifdef HAVE_MPROTECT
  #ifdef HAVE_SIGINFO
		fprintf(stderr, "%-16s#%d-redzone	= %p\n",
			zone->name, zone->id, (void *) zone->redzone);
  #endif /* HAVE_SIGINFO */
		fprintf(stderr, "%-16s#%d-hardmax		= %p\n",
			zone->name, zone->id, (void *) zone->hardmax);
		fprintf(stderr, "%-16s#%d-size		= %lu\n",
			zone->name, zone->id, (unsigned long)
			((char *)zone->hardmax - (char *)zone->min));
#else
		fprintf(stderr, "%-16s#%d-size		= %lu\n",
			zone->name, zone->id, (unsigned long)
			((char *)zone->top - (char *)zone->min));
#endif /* HAVE_MPROTECT */
		fprintf(stderr, "\n");
	}
}


New File: runtime/mercury_memory_zones.h
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** mercury_memory_zones.h - functions and data structures for handling
** 			    memory zones.
**
** This defines a generic memory zone handler, which can be used for
** stacks and heaps in the Mercury runtime.  It provides functions for
** generating offsets so that different memory zones begin at different
** offsets (improves performance with direct mapped caches).  It also
** handles the fake_reg array for holding Mercury virtual registers.
*/

#ifndef	MERCURY_MEMORY_ZONES_H
#define	MERCURY_MEMORY_ZONES_H

#include "mercury_regs.h"		/* for NUM_REAL_REGS */

#include <stdlib.h>		/* for size_t */

#include "mercury_types.h"	/* for Word */
#include "mercury_std.h"		/* for bool */


/* these cannot be changed without lots of modifications elsewhere */
#define MAX_REAL_REG 32		/* r1 .. r32 */
#define NUM_SPECIAL_REG 5	/* succip, sp, hp, maxfr, curfr */

/* this can be changed at will, including by -D options to the C compiler */
#ifndef MAX_VIRTUAL_REG
#define MAX_VIRTUAL_REG	1024
#endif

/* allocate enough fake_regs to hold both the special regs */
/* and all the virtual registers */
#define MAX_FAKE_REG	(NUM_SPECIAL_REG + MAX_VIRTUAL_REG)
				/* mr0 .. mr36, mr(37) ... mr(1028) */

/* reserve MAX_FAKE_REG virtual regs, numbered from 0 to MAX_FAKE_REG-1 */
extern	Word	fake_reg[MAX_FAKE_REG];

/* used to lookup the fake_reg for a given real reg */
extern	Word	virtual_reg_map[MAX_REAL_REG];

/* used for counting register usage */
extern	unsigned long 	num_uses[MAX_RN];

/*
** The Mercury runtime uses a number of memory areas or *zones*. These
** hold the detstack, the nondetstack, the heap, and potentially other
** areas such as a trail, a "solutions"-heap, and so on.
** These memory areas are each represented by a structure that contains
** the following fields:
**	name	- a string constant used to name the allocated area
**	id	- an integer which together with the name should uniquely
**		  identify the allocated area.
**	bottom	- the address of the bottom of the allocated area
**			(should be on a page boundary)
**	top	- the address one word past the top of the allocated area
**			(should be on a page boundary)
**	min	- the address of the lowest part of the allocated that
**			will be used. This may be different to `bottom'
**			so that the use of different memory zones doesn't
**			beat the cache.
**	max	- the highest address in this memory area that has been
**			used so far. This is only defined in debugging grades.
**	hardmax	- the address of the bottom of the last page of the allocated
**			area. This is one higher than the highest address that
**			can be used in this zone. We never unprotect the
**			last page of a zone so that we retain protection
**			against overrunning the end of the zone. This is
**			obviously only available on platforms that have
**			mprotect.
**			(should be on a page boundary)
**	redzone	- the address of the start of the region that has been
**			mprotected as a redzone. Since without SIGINFO
**			it is not possible [portably] to figure out
**			where the fault occured, redzone is only available
**			on platforms that have both mprotect and SIGINFO.
**			(should be on a page boundary)
**	handler - the address of a function to handle accesses in the
**			redzone of this allocated area. This is only
**			specified if mprotect and SIGINFO are available.
*/

typedef struct MEMORY_ZONE	MemoryZone;

typedef bool ZoneHandler(Word *addr, struct MEMORY_ZONE *zone, void *context);

struct MEMORY_ZONE {
	struct MEMORY_ZONE *next; /* the memory zones are organized as a
				  ** linked list of free zones and linked
				  ** list of used zones. The next field
				  ** is NULL or a pointer to the next memory
				  ** zone in the list.
				  */
	const char *name;	/* name identifier */
	int	id;		/* number */
	Word	*bottom;	/* beginning of the allocated area */
	Word	*top;		/* end of the allocated area */
	Word	*min;		/* lowest word of the area to be used */
	Word	*max;		/* highest word of the area to be used;
				   computed only if MR_LOWLEVEL_DEBUG is
				   enabled */
#ifdef HAVE_MPROTECT
	Word	*redzone_base;	/* beginning of the original redzone */
	Word	*redzone;	/* beginning of the current redzone */
	Word	*hardmax;	/* last page of the zone which can't be
				   unprotected */
  #ifdef HAVE_SIGINFO
	ZoneHandler *handler;   /* handler for page faults in the redzone */
  #endif /* HAVE_SIGINFO */
#endif	/* HAVE_MPROTECT */
};


/*
** init_memory_arena() allocates (if necessary) the top-level memory pool
** from which all allocations should come. If PARALLEL is defined, then
** this pool should be shared memory. In the absence of PARALLEL, it
** doesn't need to do anything, since with CONSERVATIVE_GC, the collector
** manages the heap, and without GC, we can allocate memory using memalign
** or malloc.
*/
void init_memory_arena(void);

/*
** init_zones() initializes the memory zone pool and the offset
** generator.  It should be used before any zones are created or
** offsets requested.
*/
void init_zones(void);

/*
** create_zone(Name, Id, Size, Offset, RedZoneSize, FaultHandler)
** allocates a new memory zone with name Name, and number Id, size
** Size (in bytes - which gets rounded up to the nearest multiple of
** the page size), an offset Offset from the page boundary at which
** to start using the memory region (used to help avoid beating the cache),
** the amount Redzone of memory (in bytes) to be protected as a redzone
** (must be less than Size), and the address of a function to handle
** memory references in the redzone.
** If it fails to allocate or protect the zone, then it exits.
** If mprotect or SIGINFO are unavailable, then the last two arguments
** are ignored.
*/

MemoryZone	*create_zone(const char *name, int id,
			size_t size, size_t offset, size_t redsize,
			ZoneHandler *handler);

/*
** construct_zone(Name, Id, Base, Size, Offset, RedZoneSize, FaultHandler)
** has the same behaviour as create_zone, except instread of allocating
** the memory, it takes a pointer to a region of memory that must be at
** least Size bytes, or if HAVE_MPROTECT is defined, then it must be at
** least Size + unit[*] bytes.
** If it fails to protect the redzone then it exits
** If mprotect or SIGINFO are unavailable, then the last two arguments
** are ignored.
**
** [*] unit is a global variable containing the page size in bytes
*/

MemoryZone	*construct_zone(const char *name, int Id, Word *base,
			size_t size, size_t offset, size_t redsize,
			ZoneHandler *handler);

/*
** reset_zone(Zone) resets the redzone on the given MemoryZone to the
** original zone specified in the call to {create,construct}_zone() if
** HAVE_MPROTECT and HAVE_SIGINFO. If either HAVE_MPROTECT or HAVE_SIGINFO
** are not defined, it does nothing.
*/
void	reset_zone(MemoryZone *zone);

/*
** get_used_memory_zones() returns a pointer to the linked list of
** used memory zones.
*/
MemoryZone	*get_used_memory_zones(void);

/*
** debug_memory() prints out debugging information about the current
** memory zones.
*/
void	debug_memory(void);

/*
** next_offset() returns sucessive offsets across the primary cache. Useful
** when calling {create,construct}_zone().
*/
extern	size_t	next_offset(void);

#endif /* not MERCURY_MEMORY_ZONES_H */


-- 
       Tyson Dowd           # There isn't any reason why Linux can't be
                            # implemented as an enterprise computing solution.
     trd at cs.mu.oz.au        # Find out what you've been missing while you've
http://www.cs.mu.oz.au/~trd # been rebooting Windows NT. -- InfoWorld, 1998.



More information about the developers mailing list