754 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			754 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | 
						|
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 | 
						|
 */
 | 
						|
#ifndef lint
 | 
						|
static char rcsid[] = "$Id$";
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
 * Memory manager. Memory is divided into NMEMS pieces. There is a struct
 | 
						|
 * for each piece telling where it is, how many bytes are used, and how may
 | 
						|
 * are left. If a request for core doesn't fit in the left bytes, an sbrk()
 | 
						|
 * is done and pieces after the one that requested the growth are moved up.
 | 
						|
 *
 | 
						|
 * Unfortunately, we cannot use sbrk to request more memory, because its
 | 
						|
 * result cannot be trusted. More specifically, it does not work properly
 | 
						|
 * on 2.9 BSD, and probably does not work properly on 2.8 BSD and V7 either.
 | 
						|
 * The problem is that "sbrk" adds the increment to the current "break"
 | 
						|
 * WITHOUT testing the carry bit. So, if your break is at 40000, and
 | 
						|
 * you "sbrk(30000)", it will succeed, but your break will be at 4464
 | 
						|
 * (70000 - 65536).
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * USEMALLOC tells the allocator to use malloc() and realloc(), not brk().
 | 
						|
 * This might help systems where brk() doesn't work, or where malloc() can
 | 
						|
 * allocate outside the brk area.
 | 
						|
 */
 | 
						|
#define USEMALLOC
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdbool.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <out.h>
 | 
						|
#include "const.h"
 | 
						|
#include "debug.h"
 | 
						|
#include "memory.h"
 | 
						|
#include "object.h"
 | 
						|
#include "sym.h"
 | 
						|
#include "finish.h"
 | 
						|
#include "write.h"
 | 
						|
 | 
						|
#ifndef USEMALLOC
 | 
						|
static void copy_down(struct memory* mem, ind_t dist);
 | 
						|
static void copy_up(struct memory* mem, ind_t dist);
 | 
						|
#endif
 | 
						|
static void free_saved_moduls(void);
 | 
						|
 | 
						|
struct memory	mems[NMEMS];
 | 
						|
 | 
						|
bool	incore = TRUE;	/* TRUE while everything can be kept in core. */
 | 
						|
ind_t	core_position = (ind_t)0;	/* Index of current module. */
 | 
						|
 | 
						|
#ifdef USEMALLOC
 | 
						|
static size_t modl_initial_size;
 | 
						|
static bool frozen = FALSE;		/* TRUE after freeze_core(). */
 | 
						|
 | 
						|
#else /* ifndef USEMALLOC */
 | 
						|
#define GRANULE		64	/* power of 2 */
 | 
						|
 | 
						|
static char *BASE;
 | 
						|
static ind_t refused;
 | 
						|
 | 
						|
static int sbreak(ind_t incr)
 | 
						|
{
 | 
						|
	incr = (incr + (GRANULE - 1)) & ~(GRANULE - 1);
 | 
						|
 | 
						|
	if ((refused && refused < incr) ||
 | 
						|
	    BASE + incr < BASE ||
 | 
						|
	    brk(BASE + incr) == -1) {
 | 
						|
		if (!refused || refused > incr)
 | 
						|
			refused = incr;
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	BASE += incr;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
#endif /* ndef USEMALLOC */
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize some pieces of core. We hope that this will be our last
 | 
						|
 * real allocation, meaning we've made the right choices.
 | 
						|
 */
 | 
						|
void init_core(void)
 | 
						|
{
 | 
						|
#ifdef USEMALLOC
 | 
						|
	struct memory *failed_mem = NULL;
 | 
						|
	struct memory *mem;
 | 
						|
	bool string_area;
 | 
						|
 | 
						|
#include "mach.h"
 | 
						|
	modl_initial_size = mems[ALLOMODL].mem_left;
 | 
						|
 | 
						|
	for (mem = mems; mem < &mems[NMEMS]; mem++) {
 | 
						|
		string_area = mem == &mems[ALLOLCHR] || mem == &mems[ALLOGCHR];
 | 
						|
		/* String areas need at least one byte. */
 | 
						|
		if (string_area && mem->mem_left == 0)
 | 
						|
			mem->mem_left++;
 | 
						|
		/* Don't malloc() size zero. */
 | 
						|
		if (mem->mem_left > 0) {
 | 
						|
			mem->mem_base = malloc(mem->mem_left);
 | 
						|
			if (mem->mem_base == NULL) {
 | 
						|
				failed_mem = mem;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		mem->mem_full = 0;
 | 
						|
		if (string_area) {
 | 
						|
			mem->mem_left--;
 | 
						|
			mem->mem_full++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (failed_mem != NULL) {
 | 
						|
		incore = FALSE;	/* In core strategy failed. */
 | 
						|
		/* Undo allocations. */
 | 
						|
		for (mem = mems; mem != failed_mem; mem++)
 | 
						|
			free(mem->mem_base);
 | 
						|
		/* Allocate only the string areas. */
 | 
						|
		for (mem = mems; mem < &mems[NMEMS]; mem++) {
 | 
						|
			if (mem == &mems[ALLOLCHR] || mem == &mems[ALLOGCHR]) {
 | 
						|
				mem->mem_base = malloc(1);
 | 
						|
				if (mem->mem_base == NULL)
 | 
						|
					fatal("no core at all");
 | 
						|
				mem->mem_left = 0;
 | 
						|
				mem->mem_full = 1;
 | 
						|
			} else {
 | 
						|
				mem->mem_base = NULL;
 | 
						|
				mem->mem_left = mem->mem_full = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#else /* ifndef USEMALLOC */
 | 
						|
	register char		*base;
 | 
						|
	register ind_t		total_size;
 | 
						|
	register struct memory	*mem;
 | 
						|
 | 
						|
#include "mach.h"
 | 
						|
#define ALIGN 8			/* minimum alignment for pieces */
 | 
						|
#define AT_LEAST	(ind_t)2*ALIGN	/* See comment about string areas. */
 | 
						|
 | 
						|
	total_size = (ind_t)0;	/* Will accumulate the sizes. */
 | 
						|
	BASE = base = sbrk(0);		/* First free. */
 | 
						|
	if ((int)base % ALIGN) {
 | 
						|
		base  = sbrk(ALIGN - (int)base % ALIGN);
 | 
						|
		BASE = base = sbrk(0);
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * String areas are special-cased. The first byte is unused as a way to
 | 
						|
	 * distinguish a name without string from a name which has the first
 | 
						|
	 * string in the string area.
 | 
						|
	 */
 | 
						|
	for (mem = mems; mem < &mems[NMEMS]; mem++) {
 | 
						|
		mem->mem_base = base;
 | 
						|
		mem->mem_full = (ind_t)0;
 | 
						|
		if (mem == &mems[ALLOLCHR] || mem == &mems[ALLOGCHR]) {
 | 
						|
			if (mem->mem_left == 0) {
 | 
						|
				mem->mem_left = ALIGN;
 | 
						|
				total_size += ALIGN;
 | 
						|
				base += ALIGN;
 | 
						|
			}
 | 
						|
			base += mem->mem_left;
 | 
						|
			total_size += mem->mem_left;
 | 
						|
			mem->mem_left--;
 | 
						|
			mem->mem_full++;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			base += mem->mem_left;	/* Each piece will start after prev. */
 | 
						|
			total_size += mem->mem_left;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (sbreak(total_size) == -1) {
 | 
						|
		incore = FALSE;	/* In core strategy failed. */
 | 
						|
		if (sbreak(AT_LEAST) == -1)
 | 
						|
			fatal("no core at all");
 | 
						|
 | 
						|
		base = BASE;
 | 
						|
		for (mem = mems; mem < &mems[NMEMS]; mem++) {
 | 
						|
			mem->mem_base = base;
 | 
						|
			if (mem == &mems[ALLOLCHR] || mem == &mems[ALLOGCHR]) {
 | 
						|
				base += ALIGN;
 | 
						|
				mem->mem_left = ALIGN - 1;
 | 
						|
				mem->mem_full = 1;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				mem->mem_full = (ind_t)0;
 | 
						|
				mem->mem_left = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif /* ndef USEMALLOC */
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Allocate an extra block of `incr' bytes and move all pieces with index
 | 
						|
 * higher than `piece' up with the size of the block.
 | 
						|
 * Move up as much as possible, if "incr" fails.
 | 
						|
 */
 | 
						|
static ind_t
 | 
						|
move_up(piece, incr)
 | 
						|
	register int		piece;
 | 
						|
	register ind_t		incr;
 | 
						|
{
 | 
						|
#ifdef USEMALLOC
 | 
						|
	size_t oldsize = mems[piece].mem_full + mems[piece].mem_left;
 | 
						|
	size_t newsize;
 | 
						|
	char *newbase;
 | 
						|
 | 
						|
	if (frozen)
 | 
						|
		return 0;	/* Can't realloc() frozen core. */
 | 
						|
 | 
						|
	/* We realloc() this piece without moving the other pieces. */
 | 
						|
	while (incr > 0) {
 | 
						|
		newsize = oldsize + incr;
 | 
						|
		if (newsize > oldsize) {
 | 
						|
			newbase = realloc(mems[piece].mem_base, newsize);
 | 
						|
			if (newbase != NULL) {
 | 
						|
				mems[piece].mem_base = newbase;
 | 
						|
				mems[piece].mem_left += incr;
 | 
						|
				return incr;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		incr -= INCRSIZE < incr ? INCRSIZE : incr;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
#else /* ifndef USEMALLOC */
 | 
						|
	register struct memory	*mem;
 | 
						|
#ifndef NOSTATISTICS
 | 
						|
	extern int statistics;
 | 
						|
#endif
 | 
						|
 | 
						|
	debug("move_up(%d, %d)\n", piece, (int)incr, 0, 0);
 | 
						|
	while (incr > 0 && sbreak(incr) == -1)
 | 
						|
		incr -= INCRSIZE < incr ? INCRSIZE : incr;
 | 
						|
 | 
						|
	if (incr == 0)
 | 
						|
		return (ind_t) 0;
 | 
						|
#ifndef NOSTATISTICS
 | 
						|
	if (statistics) fprintf(stderr,"moving up %lx\n", (long) incr);
 | 
						|
#endif
 | 
						|
	for (mem = &mems[NMEMS - 1]; mem > &mems[piece]; mem--)
 | 
						|
		copy_up(mem, incr);
 | 
						|
 | 
						|
	mems[piece].mem_left += incr;
 | 
						|
	return incr;
 | 
						|
#endif /* ndef USEMALLOC */
 | 
						|
}
 | 
						|
 | 
						|
extern int	passnumber;
 | 
						|
 | 
						|
/*
 | 
						|
 * This routine is called if `piece' needs `incr' bytes and the system won't
 | 
						|
 * give them. We first steal the free bytes of all lower pieces and move them
 | 
						|
 * and `piece' down. If that doesn't give us enough bytes, we steal the free
 | 
						|
 * bytes of all higher pieces and move them up. We return whether we have
 | 
						|
 * enough bytes, the first or the second time.
 | 
						|
 */
 | 
						|
static bool
 | 
						|
compact(int piece, ind_t incr, int flag)
 | 
						|
#define NORMAL 0
 | 
						|
#define FREEZE 1
 | 
						|
#define FORCED 2
 | 
						|
{
 | 
						|
#ifdef USEMALLOC
 | 
						|
	struct memory	*mem;
 | 
						|
	size_t		newsize, oldsize;
 | 
						|
	char		*newbase;
 | 
						|
 | 
						|
	if (frozen)
 | 
						|
		return incr == 0; /* Can't realloc() frozen core. */
 | 
						|
	/*
 | 
						|
	 * We realloc() to shrink most pieces.
 | 
						|
	 * We can't control how realloc() moves the pieces.
 | 
						|
	 */
 | 
						|
	for (mem = mems; mem < &mems[NMEMS]; mem++) {
 | 
						|
		if (mem == &mems[piece])
 | 
						|
			continue;
 | 
						|
		if (flag == FREEZE && mem == &mems[ALLOMODL])
 | 
						|
			continue;
 | 
						|
		if (mem->mem_full == 0) {
 | 
						|
			/* Don't try to realloc() to size zero. */
 | 
						|
			free(mem->mem_base);
 | 
						|
			mem->mem_base = NULL;
 | 
						|
			mem->mem_left = 0;
 | 
						|
		} else {
 | 
						|
			newbase = realloc(mem->mem_base, mem->mem_full);
 | 
						|
			if (newbase != NULL) {
 | 
						|
				mem->mem_base = newbase;
 | 
						|
				mem->mem_left = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * After FREEZE, we must be able to grow ALLOMODL without
 | 
						|
	 * moving it.  The old allocator allowed ALLOMODL to grow in
 | 
						|
	 * the brk area, but we can't call realloc() later, so we must
 | 
						|
	 * leave some extra space in ALLOMODL now.
 | 
						|
	 */
 | 
						|
	if (flag == FREEZE) {
 | 
						|
		mem = &mems[ALLOMODL];
 | 
						|
		oldsize = mem->mem_full + mem->mem_left;
 | 
						|
		newsize = mem->mem_full + modl_initial_size / 2;
 | 
						|
		/* Don't shrink ALLOMODL. */
 | 
						|
		while (newsize > oldsize) {
 | 
						|
			newbase = realloc(mem->mem_base, newsize);
 | 
						|
			if (newbase != NULL) {
 | 
						|
				mem->mem_base = newbase;
 | 
						|
				mem->mem_left = newsize - mem->mem_full;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			newsize -= INCRSIZE < newsize ? INCRSIZE : newsize;
 | 
						|
		}
 | 
						|
		frozen = TRUE;	/* Prevent later realloc(). */
 | 
						|
	}
 | 
						|
	/* Now grow our piece. */
 | 
						|
	if (incr == 0)
 | 
						|
		return TRUE;
 | 
						|
	mem = &mems[piece];
 | 
						|
	oldsize = mem->mem_full + mem->mem_left;
 | 
						|
	newsize = oldsize + incr;
 | 
						|
	if (newsize < mem->mem_full)
 | 
						|
		return FALSE;	/* The size overflowed. */
 | 
						|
	newbase = realloc(mem->mem_base, newsize);
 | 
						|
	if (newbase == NULL)
 | 
						|
		return FALSE;
 | 
						|
	mem->mem_base = newbase;
 | 
						|
	mem->mem_left += incr;
 | 
						|
	return TRUE;
 | 
						|
 | 
						|
#else /* ifndef USEMALLOC */
 | 
						|
	register ind_t		gain, size;
 | 
						|
	register struct memory	*mem;
 | 
						|
	int min = piece, max = piece;
 | 
						|
#define SHIFT_COUNT 2		/* let pieces only contribute if their free
 | 
						|
				   memory is more than 1/2**SHIFT_COUNT * 100 %
 | 
						|
				   of its occupied memory
 | 
						|
				*/
 | 
						|
 | 
						|
	debug("compact(%d, %d, %d)\n", piece, (int)incr, flag, 0);
 | 
						|
	for (mem = &mems[0]; mem < &mems[NMEMS - 1]; mem++) {
 | 
						|
		assert(mem->mem_base + mem->mem_full + mem->mem_left == (mem+1)->mem_base);
 | 
						|
	}
 | 
						|
 | 
						|
	mem = &mems[piece];
 | 
						|
	if (flag == NORMAL) {
 | 
						|
		/* try and gain a bit more than needed */
 | 
						|
		gain = (mem->mem_full + incr) >> SHIFT_COUNT;
 | 
						|
		if (incr < gain) incr = gain;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * First, check that moving will result in enough space
 | 
						|
	 */
 | 
						|
	if (flag != FREEZE) {
 | 
						|
		gain = mem->mem_left;
 | 
						|
		for (mem = &mems[piece-1]; mem >= &mems[0]; mem--) {
 | 
						|
			/*
 | 
						|
			 * Don't give it all away!
 | 
						|
			 * If this does not give us enough, bad luck
 | 
						|
			 */
 | 
						|
			if (flag == FORCED)
 | 
						|
				size = 0;
 | 
						|
			else {
 | 
						|
				size = mem->mem_full >> SHIFT_COUNT;
 | 
						|
				if (size == 0) size = mem->mem_left >> 1;
 | 
						|
			}
 | 
						|
			if (mem->mem_left >= size)
 | 
						|
				gain += (mem->mem_left - size) & ~(ALIGN - 1);
 | 
						|
			if (gain >= incr) {
 | 
						|
				min = mem - &mems[0];
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (min == piece)
 | 
						|
		    for (mem = &mems[piece+1]; mem <= &mems[NMEMS - 1]; mem++) {
 | 
						|
			/*
 | 
						|
			 * Don't give it all away!
 | 
						|
			 * If this does not give us enough, bad luck
 | 
						|
			 */
 | 
						|
			if (flag == FORCED)
 | 
						|
				size = 0;
 | 
						|
			else {
 | 
						|
				size = mem->mem_full >> SHIFT_COUNT;
 | 
						|
				if (size == 0) size = mem->mem_left >> 1;
 | 
						|
			}
 | 
						|
			if (mem->mem_left >= size)
 | 
						|
				gain += (mem->mem_left - size) & ~(ALIGN - 1);
 | 
						|
			if (gain >= incr) {
 | 
						|
				max = mem - &mems[0];
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (min == piece) {
 | 
						|
			min = 0;
 | 
						|
			if (max == piece) max = 0;
 | 
						|
		}
 | 
						|
		if (gain < incr) return 0;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		min = 0;
 | 
						|
		max = NMEMS - 1;
 | 
						|
	}
 | 
						|
 | 
						|
	gain = 0;
 | 
						|
	for (mem = &mems[min]; mem != &mems[piece]; mem++) {
 | 
						|
		/* Here memory is inserted before a piece. */
 | 
						|
		assert(passnumber == FIRST || gain == (ind_t)0);
 | 
						|
		if (gain) copy_down(mem, gain);
 | 
						|
		if (flag == FREEZE || gain < incr) {
 | 
						|
			if (flag != NORMAL) size = 0;
 | 
						|
			else {
 | 
						|
				size = mem->mem_full >> SHIFT_COUNT;
 | 
						|
				if (size == 0) size = mem->mem_left >> 1;
 | 
						|
			}
 | 
						|
			if (mem->mem_left >= size) {
 | 
						|
				size = (mem->mem_left - size) & ~(ALIGN - 1);
 | 
						|
				gain += size;
 | 
						|
				mem->mem_left -= size;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Now mems[piece]:
 | 
						|
	 */
 | 
						|
	if (gain) copy_down(mem, gain);
 | 
						|
	gain += mem->mem_left;
 | 
						|
	mem->mem_left = 0;
 | 
						|
 | 
						|
	if (gain < incr) {
 | 
						|
		register ind_t	up = (ind_t)0;
 | 
						|
 | 
						|
		for (mem = &mems[max]; mem > &mems[piece]; mem--) {
 | 
						|
			/* Here memory is appended after a piece. */
 | 
						|
			if (flag == FREEZE || gain + up < incr) {
 | 
						|
				if (flag != NORMAL) size = 0;
 | 
						|
				else {
 | 
						|
					size = mem->mem_full >> SHIFT_COUNT;
 | 
						|
					if (size == 0) size = mem->mem_left >> 1;
 | 
						|
				}
 | 
						|
				if (mem->mem_left >= size) {
 | 
						|
					size = (mem->mem_left - size) & ~(ALIGN - 1);
 | 
						|
					up += size;
 | 
						|
					mem->mem_left -= size;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (up) copy_up(mem, up);
 | 
						|
		}
 | 
						|
		gain += up;
 | 
						|
	}
 | 
						|
	mems[piece].mem_left += gain;
 | 
						|
	assert(flag == FREEZE || gain >= incr);
 | 
						|
	for (mem = &mems[0]; mem < &mems[NMEMS - 1]; mem++) {
 | 
						|
		assert(mem->mem_base + mem->mem_full + mem->mem_left == (mem+1)->mem_base);
 | 
						|
	}
 | 
						|
	return gain >= incr;
 | 
						|
#endif /* ndef USEMALLOC */
 | 
						|
}
 | 
						|
 | 
						|
#ifndef USEMALLOC
 | 
						|
/*
 | 
						|
 * The bytes of `mem' must be moved `dist' down in the address space.
 | 
						|
 * We copy the bytes from low to high, because the tail of the new area may
 | 
						|
 * overlap with the old area, but we do not want to overwrite them before they
 | 
						|
 * are copied.
 | 
						|
 */
 | 
						|
static void
 | 
						|
copy_down(struct memory* mem, ind_t dist)
 | 
						|
{
 | 
						|
	register char		*old;
 | 
						|
	register char		*new;
 | 
						|
	register ind_t		size;
 | 
						|
 | 
						|
	size = mem->mem_full;
 | 
						|
	old = mem->mem_base;
 | 
						|
	new = old - dist;
 | 
						|
	mem->mem_base = new;
 | 
						|
	while (size--)
 | 
						|
		*new++ = *old++;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The bytes of `mem' must be moved `dist' up in the address space.
 | 
						|
 * We copy the bytes from high to low, because the tail of the new area may
 | 
						|
 * overlap with the old area, but we do not want to overwrite them before they
 | 
						|
 * are copied.
 | 
						|
 */
 | 
						|
static void copy_up(struct memory* mem, ind_t dist)
 | 
						|
{
 | 
						|
	register char		*old;
 | 
						|
	register char		*new;
 | 
						|
	register ind_t		size;
 | 
						|
 | 
						|
	size = mem->mem_full;
 | 
						|
	old = mem->mem_base + size;
 | 
						|
	new = old + dist;
 | 
						|
	while (size--)
 | 
						|
		*--new = *--old;
 | 
						|
	mem->mem_base = new;
 | 
						|
}
 | 
						|
#endif /* ndef USEMALLOC */
 | 
						|
 | 
						|
static int alloctype = NORMAL;
 | 
						|
 | 
						|
/*
 | 
						|
 * Add `size' bytes to the bytes already allocated for `piece'. If it has no
 | 
						|
 * free bytes left, ask them from memory or, if that fails, from the free
 | 
						|
 * bytes of other pieces. The offset of the new area is returned. No matter
 | 
						|
 * how many times the area is moved, because of another allocate, this offset
 | 
						|
 * remains valid.
 | 
						|
 */
 | 
						|
ind_t alloc(int piece, size_t size)
 | 
						|
{
 | 
						|
	register ind_t		incr = 0;
 | 
						|
	ind_t			left = mems[piece].mem_left;
 | 
						|
	register ind_t		full = mems[piece].mem_full;
 | 
						|
 | 
						|
	assert(passnumber == FIRST || (!incore && piece == ALLOMODL));
 | 
						|
	if (size == 0)
 | 
						|
		return full;
 | 
						|
	if (size != (ind_t)size)
 | 
						|
		return BADOFF;
 | 
						|
	switch(piece) {
 | 
						|
	case ALLOMODL:
 | 
						|
	case ALLORANL:
 | 
						|
		size = int_align(size);
 | 
						|
		if (size == 0)
 | 
						|
			return BADOFF;
 | 
						|
	}
 | 
						|
 | 
						|
	if (size > left) {
 | 
						|
		incr = ((size - left + (INCRSIZE - 1)) / INCRSIZE) * INCRSIZE;
 | 
						|
		if (incr == 0)
 | 
						|
			return BADOFF;
 | 
						|
	}
 | 
						|
 | 
						|
	if (incr == 0 ||
 | 
						|
	    (incr < left + full && move_up(piece, left + full) >= incr) ||
 | 
						|
	    move_up(piece, incr) == incr ||
 | 
						|
	    compact(piece, size, alloctype)) {
 | 
						|
		mems[piece].mem_full += size;
 | 
						|
		mems[piece].mem_left -= size;
 | 
						|
		return full;
 | 
						|
	} else {
 | 
						|
		incore = FALSE;
 | 
						|
		return BADOFF;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Same as alloc() but for a piece which really needs it. If the first
 | 
						|
 * attempt fails, release the space occupied by other pieces and try again.
 | 
						|
 */
 | 
						|
ind_t
 | 
						|
hard_alloc(int piece, size_t size)
 | 
						|
{
 | 
						|
	register ind_t	ret;
 | 
						|
	register int	i;
 | 
						|
 | 
						|
	if (size != (ind_t)size)
 | 
						|
		return BADOFF;
 | 
						|
	if ((ret = alloc(piece, size)) != BADOFF) {
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Deallocate what we don't need.
 | 
						|
	 */
 | 
						|
	for (i = 0; i < NMEMS; i++) {
 | 
						|
		switch (i) {
 | 
						|
		case ALLOGLOB:
 | 
						|
		case ALLOGCHR:
 | 
						|
		case ALLOSYMB:
 | 
						|
		case ALLOARCH:
 | 
						|
		case ALLOMODL:
 | 
						|
		case ALLORANL:
 | 
						|
			break;	/* Do not try to deallocate this. */
 | 
						|
		default:
 | 
						|
			dealloc(i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	free_saved_moduls();
 | 
						|
 | 
						|
	if ((ret = alloc(piece, size)) != BADOFF) {
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	alloctype = FORCED;
 | 
						|
	ret = alloc(piece, size);
 | 
						|
	alloctype = NORMAL;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * We don't need the previous modules, so we put the current module
 | 
						|
 * at the start of the piece allocated for module contents, thereby
 | 
						|
 * overwriting the saved modules, and release its space.
 | 
						|
 */
 | 
						|
static void
 | 
						|
free_saved_moduls(void)
 | 
						|
{
 | 
						|
	register ind_t		size;
 | 
						|
	register char		*old, *new;
 | 
						|
	register struct memory	*mem = &mems[ALLOMODL];
 | 
						|
 | 
						|
	size = mem->mem_full - core_position;
 | 
						|
	new = mem->mem_base;
 | 
						|
	old = new + core_position;
 | 
						|
	while (size--)
 | 
						|
		*new++ = *old++;
 | 
						|
	mem->mem_full -= core_position;
 | 
						|
	mem->mem_left += core_position;
 | 
						|
	core_position = (ind_t)0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The piece of memory with index `piece' is no longer needed.
 | 
						|
 * We take care that it can be used by compact() later, if needed.
 | 
						|
 */
 | 
						|
void
 | 
						|
dealloc(int piece)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Some pieces need their memory throughout the program.
 | 
						|
	 */
 | 
						|
	assert(piece != ALLOGLOB);
 | 
						|
	assert(piece != ALLOGCHR);
 | 
						|
	assert(piece != ALLOSYMB);
 | 
						|
	assert(piece != ALLOARCH);
 | 
						|
	mems[piece].mem_left += mems[piece].mem_full;
 | 
						|
	mems[piece].mem_full = (ind_t)0;
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
core_alloc(int piece, size_t size)
 | 
						|
{
 | 
						|
	register ind_t	off;
 | 
						|
 | 
						|
	if ((off = alloc(piece, size)) == BADOFF)
 | 
						|
		return (char *)0;
 | 
						|
	return address(piece, off);
 | 
						|
}
 | 
						|
 | 
						|
void core_free(int piece, char* p)
 | 
						|
{
 | 
						|
	char	*q = address(piece, mems[piece].mem_full);
 | 
						|
 | 
						|
	assert(p < q);
 | 
						|
	mems[piece].mem_full -= (ind_t) q - (ind_t) p;
 | 
						|
	mems[piece].mem_left += (ind_t) q - (ind_t) p;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Reset index into piece of memory for modules and
 | 
						|
 * take care that the allocated pieces will not be moved.
 | 
						|
 */
 | 
						|
void freeze_core(void)
 | 
						|
{
 | 
						|
	register int	i;
 | 
						|
 | 
						|
	core_position = (ind_t)0;
 | 
						|
 | 
						|
	if (incore)
 | 
						|
		return;
 | 
						|
 | 
						|
	for (i = 0; i < NMEMS; i++) {
 | 
						|
		switch (i) {
 | 
						|
		case ALLOGLOB:
 | 
						|
		case ALLOGCHR:
 | 
						|
		case ALLOSYMB:
 | 
						|
		case ALLOARCH:
 | 
						|
			break;	/* Do not try to deallocate this. */
 | 
						|
		default:
 | 
						|
			dealloc(i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	compact(NMEMS - 1, (ind_t)0, FREEZE);
 | 
						|
}
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
 | 
						|
/*
 | 
						|
 * To transform the various pieces of the output in core to the file format,
 | 
						|
 * we must order the bytes in the unsigned shorts and longs as ACK prescribes.
 | 
						|
 */
 | 
						|
void write_bytes(void)
 | 
						|
{
 | 
						|
	unsigned short		nsect;
 | 
						|
	long			offchar;
 | 
						|
	register struct memory	*mem;
 | 
						|
	extern long		NLChars, NGChars;
 | 
						|
	extern int		flagword;
 | 
						|
	extern struct outhead	outhead;
 | 
						|
	extern struct outsect	outsect[];
 | 
						|
	extern char		*outputname;
 | 
						|
	int			sectionno = 0;
 | 
						|
 | 
						|
	nsect = outhead.oh_nsect;
 | 
						|
	offchar = OFF_CHAR(outhead);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We allocated two areas: one for local and one for global names.
 | 
						|
	 * Also, we used another kind of on_foff than on file.
 | 
						|
	 * At the end of the global area we have put the section names.
 | 
						|
	 */
 | 
						|
	if (!(flagword & SFLAG)) {
 | 
						|
		do_crs((struct outname *)mems[ALLOLOCL].mem_base, NLocals);
 | 
						|
		namecpy((struct outname *)mems[ALLOLOCL].mem_base,
 | 
						|
			NLocals,
 | 
						|
			offchar
 | 
						|
		);
 | 
						|
		namecpy((struct outname *)mems[ALLOGLOB].mem_base,
 | 
						|
			NGlobals + nsect,
 | 
						|
			offchar + NLChars
 | 
						|
		);
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * These pieces must always be written.
 | 
						|
	 */
 | 
						|
	wr_ohead(&outhead);
 | 
						|
	wr_sect(outsect, nsect);
 | 
						|
	for (mem = &mems[ALLOEMIT]; mem < &mems[ALLORELO]; mem++)
 | 
						|
		wrt_emit(mem->mem_base, sectionno++, mem->mem_full);
 | 
						|
	/*
 | 
						|
	 * The rest depends on the flags.
 | 
						|
	 */
 | 
						|
	if (flagword & (RFLAG|CFLAG))
 | 
						|
		wr_relo((struct outrelo *) mems[ALLORELO].mem_base,
 | 
						|
			outhead.oh_nrelo);
 | 
						|
	if (!(flagword & SFLAG)) {
 | 
						|
		wr_name((struct outname *) mems[ALLOLOCL].mem_base,
 | 
						|
			NLocals);
 | 
						|
		wr_name((struct outname *) mems[ALLOGLOB].mem_base,
 | 
						|
			NGlobals+nsect);
 | 
						|
		wr_string(mems[ALLOLCHR].mem_base + 1, (long)NLChars);
 | 
						|
		wr_string(mems[ALLOGCHR].mem_base + 1, (long)NGChars);
 | 
						|
#ifdef SYMDBUG
 | 
						|
		wr_dbug(mems[ALLODBUG].mem_base, mems[ALLODBUG].mem_full);
 | 
						|
#endif /* SYMDBUG */
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void namecpy(struct outname* name, unsigned nname, long offchar)
 | 
						|
{
 | 
						|
	while (nname--) {
 | 
						|
		if (name->on_foff)
 | 
						|
			name->on_foff += offchar - 1;
 | 
						|
		name++;
 | 
						|
	}
 | 
						|
}
 |