diff options
Diffstat (limited to 'src/runtime/ralc.r')
-rw-r--r-- | src/runtime/ralc.r | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/src/runtime/ralc.r b/src/runtime/ralc.r new file mode 100644 index 0000000..9f55671 --- /dev/null +++ b/src/runtime/ralc.r @@ -0,0 +1,784 @@ +/* + * File: ralc.r + * Contents: allocation routines + */ + +/* + * Prototypes. + */ +static struct region *findgap (struct region *curr, word nbytes); +static struct region *newregion (word nbytes, word stdsize); + +extern word alcnum; + +#ifndef MultiThread +word coexp_ser = 2; /* serial numbers for co-expressions; &main is 1 */ +word list_ser = 1; /* serial numbers for lists */ +word set_ser = 1; /* serial numbers for sets */ +word table_ser = 1; /* serial numbers for tables */ +#endif /* MultiThread */ + + +/* + * AlcBlk - allocate a block. + */ +#begdef AlcBlk(var, struct_nm, t_code, nbytes) +{ +#ifdef MultiThread + EVVal((word)nbytes, typech[t_code]); +#endif /* MultiThread */ + + /* + * Ensure that there is enough room in the block region. + */ + if (DiffPtrs(blkend,blkfree) < nbytes && !reserve(Blocks, nbytes)) + return NULL; + + /* + * If monitoring, show the allocation. + */ +#ifndef MultiThread + EVVal((word)nbytes, typech[t_code]); +#endif + + /* + * Decrement the free space in the block region by the number of bytes + * allocated and return the address of the first byte of the allocated + * block. + */ + blktotal += nbytes; + var = (struct struct_nm *)blkfree; + blkfree += nbytes; + var->title = t_code; +} +#enddef + +/* + * AlcFixBlk - allocate a fixed length block. + */ +#define AlcFixBlk(var, struct_nm, t_code)\ + AlcBlk(var, struct_nm, t_code, sizeof(struct struct_nm)) + +/* + * AlcVarBlk - allocate a variable-length block. + */ +#begdef AlcVarBlk(var, struct_nm, t_code, n_desc) + { +#ifdef EventMon + uword size; +#else /* EventMon */ + register uword size; +#endif /* EventMon */ + + /* + * Variable size blocks are declared with one descriptor, thus + * we need add in only n_desc - 1 descriptors. + */ + size = sizeof(struct struct_nm) + (n_desc - 1) * sizeof(struct descrip); + AlcBlk(var, struct_nm, t_code, size) + var->blksize = size; + } +#enddef + +/* + * alcactiv - allocate a co-expression activation block. + */ + +struct astkblk *alcactiv() + { + struct astkblk *abp; + + abp = (struct astkblk *)malloc(sizeof(struct astkblk)); + + /* + * If malloc failed, attempt to free some co-expression blocks and retry. + */ + if (abp == NULL) { + collect(Static); + abp = (struct astkblk *)malloc(sizeof(struct astkblk)); + } + + if (abp == NULL) + ReturnErrNum(305, NULL); + abp->nactivators = 0; + abp->astk_nxt = NULL; + return abp; + } + +#ifdef LargeInts +/* + * alcbignum - allocate an n-digit bignum in the block region + */ + +struct b_bignum *alcbignum(n) +word n; + { + register struct b_bignum *blk; + register uword size; + + size = sizeof(struct b_bignum) + ((n - 1) * sizeof(DIGIT)); + /* ensure whole number of words allocated */ + size = (size + WordSize - 1) & -WordSize; + AlcBlk(blk, b_bignum, T_Lrgint, size); + blk->blksize = size; + blk->msd = blk->sign = 0; + blk->lsd = n - 1; + return blk; + } +#endif /* LargeInts */ + +/* + * alccoexp - allocate a co-expression stack block. + */ + +#if COMPILER +struct b_coexpr *alccoexp() + { + struct b_coexpr *ep; + static int serial = 2; /* main co-expression is allocated elsewhere */ + ep = (struct b_coexpr *)malloc(stksize); + + /* + * If malloc failed or if there have been too many co-expression allocations + * since a collection, attempt to free some co-expression blocks and retry. + */ + + if (ep == NULL || alcnum > AlcMax) { + collect(Static); + ep = (struct b_coexpr *)malloc(stksize); + } + + if (ep == NULL) + ReturnErrNum(305, NULL); + + alcnum++; /* increment allocation count since last g.c. */ + + ep->title = T_Coexpr; + ep->size = 0; + ep->id = serial++; + ep->nextstk = stklist; + ep->es_tend = NULL; + ep->file_name = ""; + ep->line_num = 0; + ep->freshblk = nulldesc; + ep->es_actstk = NULL; + ep->cstate[0] = 0; /* zero the first two cstate words as a flag */ + ep->cstate[1] = 0; + stklist = ep; + return ep; + } +#else /* COMPILER */ +#ifdef MultiThread +/* + * If this is a new program being loaded, an icodesize>0 gives the + * hdr.hsize and a stacksize to use; allocate + * sizeof(progstate) + icodesize + mstksize + * Otherwise (icodesize==0), allocate a normal stksize... + */ +struct b_coexpr *alccoexp(icodesize, stacksize) +long icodesize, stacksize; +#else /* MultiThread */ +struct b_coexpr *alccoexp() +#endif /* MultiThread */ + + { + struct b_coexpr *ep; + +#ifdef MultiThread + if (icodesize > 0) { + ep = (struct b_coexpr *) + calloc(1, stacksize+ + icodesize+ + sizeof(struct progstate)+ + sizeof(struct b_coexpr)); + } + else +#endif /* MultiThread */ + + ep = (struct b_coexpr *)malloc(stksize); + + /* + * If malloc failed or if there have been too many co-expression allocations + * since a collection, attempt to free some co-expression blocks and retry. + */ + + if (ep == NULL || alcnum > AlcMax) { + + collect(Static); + +#ifdef MultiThread + if (icodesize>0) { + ep = (struct b_coexpr *) + malloc(mstksize+icodesize+sizeof(struct progstate)); + } + else +#endif /* MultiThread */ + + ep = (struct b_coexpr *)malloc(stksize); + } + if (ep == NULL) + ReturnErrNum(305, NULL); + + alcnum++; /* increment allocation count since last g.c. */ + + ep->title = T_Coexpr; + ep->es_actstk = NULL; + ep->size = 0; +#ifdef MultiThread + ep->es_pfp = NULL; + ep->es_gfp = NULL; + ep->es_argp = NULL; + ep->tvalloc = NULL; + + if (icodesize > 0) + ep->id = 1; + else +#endif /* MultiThread */ + ep->id = coexp_ser++; + ep->nextstk = stklist; + ep->es_tend = NULL; + ep->cstate[0] = 0; /* zero the first two cstate words as a flag */ + ep->cstate[1] = 0; + +#ifdef MultiThread + /* + * Initialize program state to self for &main; curpstate for others. + */ + if(icodesize>0) ep->program = (struct progstate *)(ep+1); + else ep->program = curpstate; +#endif /* MultiThread */ + + stklist = ep; + return ep; + } +#endif /* COMPILER */ + +/* + * alccset - allocate a cset in the block region. + */ + +struct b_cset *alccset() + { + register struct b_cset *blk; + register int i; + + AlcFixBlk(blk, b_cset, T_Cset) + blk->size = -1; /* flag size as not yet computed */ + + /* + * Zero the bit array. + */ + for (i = 0; i < CsetSize; i++) + blk->bits[i] = 0; + return blk; + } + +/* + * alcfile - allocate a file block in the block region. + */ + +struct b_file *alcfile(fd, status, name) +FILE *fd; +int status; +dptr name; + { + tended struct descrip tname = *name; + register struct b_file *blk; + + AlcFixBlk(blk, b_file, T_File) + blk->fd = fd; + blk->status = status; + blk->fname = tname; + return blk; + } + +/* + * alchash - allocate a hashed structure (set or table header) in the block + * region. + */ +union block *alchash(tcode) +int tcode; + { + register int i; + register struct b_set *ps; + register struct b_table *pt; + + if (tcode == T_Table) { + AlcFixBlk(pt, b_table, T_Table); + ps = (struct b_set *)pt; + ps->id = table_ser++; + } + else { /* tcode == T_Set */ + AlcFixBlk(ps, b_set, T_Set); + ps->id = set_ser++; + } + ps->size = 0; + ps->mask = 0; + for (i = 0; i < HSegs; i++) + ps->hdir[i] = NULL; + return (union block *)ps; + } + +/* + * alcsegment - allocate a slot block in the block region. + */ + +struct b_slots *alcsegment(nslots) +word nslots; + { + uword size; + register struct b_slots *blk; + + size = sizeof(struct b_slots) + WordSize * (nslots - HSlots); + AlcBlk(blk, b_slots, T_Slots, size); + blk->blksize = size; + while (--nslots >= 0) + blk->hslots[nslots] = NULL; + return blk; + } + +/* + * alclist - allocate a list header block in the block region. + * + * Forces a g.c. if there's not enough room for the whole list. + */ + +struct b_list *alclist(size) +uword size; + { + register struct b_list *blk; + + if (!reserve(Blocks, (word)(sizeof(struct b_list) + sizeof (struct b_lelem) + + (size - 1) * sizeof(struct descrip)))) return NULL; + AlcFixBlk(blk, b_list, T_List) + blk->size = size; + blk->id = list_ser++; + blk->listhead = NULL; + blk->listtail = NULL; + return blk; + } + +/* + * alclstb - allocate a list element block in the block region. + */ + +struct b_lelem *alclstb(nslots, first, nused) +uword nslots, first, nused; + { + register struct b_lelem *blk; + register word i; + + AlcVarBlk(blk, b_lelem, T_Lelem, nslots) + blk->nslots = nslots; + blk->first = first; + blk->nused = nused; + blk->listprev = NULL; + blk->listnext = NULL; + /* + * Set all elements to &null. + */ + for (i = 0; i < nslots; i++) + blk->lslots[i] = nulldesc; + return blk; + } + +/* + * alcreal - allocate a real value in the block region. + */ + +struct b_real *alcreal(val) +double val; + { + register struct b_real *blk; + + AlcFixBlk(blk, b_real, T_Real) + +#ifdef Double +/* access real values one word at a time */ + { int *rp, *rq; + rp = (int *) &(blk->realval); + rq = (int *) &val; + *rp++ = *rq++; + *rp = *rq; + } +#else /* Double */ + blk->realval = val; +#endif /* Double */ + + return blk; + } + +/* + * alcrecd - allocate record with nflds fields in the block region. + */ + +struct b_record *alcrecd(nflds, recptr) +int nflds; +union block *recptr; + { + tended union block *trecptr = recptr; + register struct b_record *blk; + + AlcVarBlk(blk, b_record, T_Record, nflds) + blk->recdesc = trecptr; + blk->id = (((struct b_proc *)recptr)->recid)++; + return blk; + } + +/* + * alcrefresh - allocate a co-expression refresh block. + */ + +#if COMPILER +struct b_refresh *alcrefresh(na, nl, nt, wrk_sz) +int na; +int nl; +int nt; +int wrk_sz; + { + struct b_refresh *blk; + + AlcVarBlk(blk, b_refresh, T_Refresh, na + nl) + blk->nlocals = nl; + blk->nargs = na; + blk->ntemps = nt; + blk->wrk_size = wrk_sz; + return blk; + } +#else /* COMPILER */ +struct b_refresh *alcrefresh(entryx, na, nl) +word *entryx; +int na, nl; + { + struct b_refresh *blk; + + AlcVarBlk(blk, b_refresh, T_Refresh, na + nl); + blk->ep = entryx; + blk->numlocals = nl; + return blk; + } +#endif /* COMPILER */ + +/* + * alcselem - allocate a set element block. + */ + +struct b_selem *alcselem(mbr,hn) +uword hn; +dptr mbr; + + { + tended struct descrip tmbr = *mbr; + register struct b_selem *blk; + + AlcFixBlk(blk, b_selem, T_Selem) + blk->clink = NULL; + blk->setmem = tmbr; + blk->hashnum = hn; + return blk; + } + +/* + * alcstr - allocate a string in the string space. + */ + +char *alcstr(s, slen) +register char *s; +register word slen; + { + tended struct descrip ts; + register char *d; + char *ofree; + +#ifdef MultiThread + StrLen(ts) = slen; + StrLoc(ts) = s; +#ifdef EventMon + if (!noMTevents) +#endif /* EventMon */ + EVVal(slen, E_String); + s = StrLoc(ts); +#endif /* MultiThread */ + + /* + * Make sure there is enough room in the string space. + */ + if (DiffPtrs(strend,strfree) < slen) { + StrLen(ts) = slen; + StrLoc(ts) = s; + if (!reserve(Strings, slen)) + return NULL; + s = StrLoc(ts); + } + + strtotal += slen; + + /* + * Copy the string into the string space, saving a pointer to its + * beginning. Note that s may be null, in which case the space + * is still to be allocated but nothing is to be copied into it. + */ + ofree = d = strfree; + if (s) { + while (slen-- > 0) + *d++ = *s++; + } + else + d += slen; + + strfree = d; + return ofree; + } + +/* + * alcsubs - allocate a substring trapped variable in the block region. + */ + +struct b_tvsubs *alcsubs(len, pos, var) +word len, pos; +dptr var; + { + tended struct descrip tvar = *var; + register struct b_tvsubs *blk; + + AlcFixBlk(blk, b_tvsubs, T_Tvsubs) + blk->sslen = len; + blk->sspos = pos; + blk->ssvar = tvar; + return blk; + } + +/* + * alctelem - allocate a table element block in the block region. + */ + +struct b_telem *alctelem() + { + register struct b_telem *blk; + + AlcFixBlk(blk, b_telem, T_Telem) + blk->hashnum = 0; + blk->clink = NULL; + blk->tref = nulldesc; + return blk; + } + +/* + * alctvtbl - allocate a table element trapped variable block in the block + * region. + */ + +struct b_tvtbl *alctvtbl(tbl, ref, hashnum) +register dptr tbl, ref; +uword hashnum; + { + tended struct descrip ttbl = *tbl; + tended struct descrip tref = *ref; + register struct b_tvtbl *blk; + + AlcFixBlk(blk, b_tvtbl, T_Tvtbl) + blk->hashnum = hashnum; + blk->clink = BlkLoc(ttbl); + blk->tref = tref; + return blk; + } + +/* + * deallocate - return a block to the heap. + * + * The block must be the one that is at the very end of a block region. + */ +void deallocate (bp) +union block *bp; +{ + word nbytes; + struct region *rp; + + nbytes = BlkSize(bp); + for (rp = curblock; rp; rp = rp->next) + if ((char *)bp + nbytes == rp->free) + break; + if (!rp) + for (rp = curblock->prev; rp; rp = rp->prev) + if ((char *)bp + nbytes == rp->free) + break; + if (!rp) + syserr ("deallocation botch"); + rp->free = (char *)bp; + blktotal -= nbytes; + EVVal(nbytes, E_BlkDeAlc); +} + +/* + * reserve -- ensure space in either string or block region. + * + * 1. check for space in current region. + * 2. check for space in older regions. + * 3. check for space in newer regions. + * 4. set goal of 10% of size of newest region. + * 5. collect regions, newest to oldest, until goal met. + * 6. allocate new region at 200% the size of newest existing. + * 7. reset goal back to original request. + * 8. collect regions that were too small to bother with before. + * 9. search regions, newest to oldest. + * 10. give up and signal error. + */ + +char *reserve(region, nbytes) +int region; +word nbytes; +{ + struct region **pcurr, *curr, *rp; + word want, newsize; + extern int qualfail; + + if (region == Strings) + pcurr = &curstring; + else + pcurr = &curblock; + curr = *pcurr; + + /* + * Check for space available now. + */ + if (DiffPtrs(curr->end, curr->free) >= nbytes) + return curr->free; /* quick return: current region is OK */ + + if ((rp = findgap(curr, nbytes)) != 0) { /* check all regions on chain */ + *pcurr = rp; /* switch regions */ + return rp->free; + } + + /* + * Set "curr" to point to newest region. + */ + while (curr->next) + curr = curr->next; + + /* + * Need to collect garbage. To reduce thrashing, set a minimum requirement + * of 10% of the size of the newest region, and collect regions until that + * amount of free space appears in one of them. + */ + want = (curr->size / 100) * memcushion; + if (want < nbytes) + want = nbytes; + + for (rp = curr; rp; rp = rp->prev) + if (rp->size >= want) { /* if large enough to possibly succeed */ + *pcurr = rp; + collect(region); + if (DiffPtrs(rp->end,rp->free) >= want) + return rp->free; + } + + /* + * That didn't work. Allocate a new region with a size based on the + * newest previous region. + */ + newsize = (curr->size / 100) * memgrowth; + if (newsize < nbytes) + newsize = nbytes; + if (newsize < MinAbrSize) + newsize = MinAbrSize; + + if ((rp = newregion(nbytes, newsize)) != 0) { + rp->prev = curr; + rp->next = NULL; + curr->next = rp; + rp->Gnext = curr; + rp->Gprev = curr->Gprev; + if (curr->Gprev) curr->Gprev->Gnext = rp; + curr->Gprev = rp; + *pcurr = rp; +#ifdef EventMon + if (!noMTevents) { + if (region == Strings) { + EVVal(rp->size, E_TenureString); + } + else { + EVVal(rp->size, E_TenureBlock); + } + } +#endif /* EventMon */ + return rp->free; + } + + /* + * Allocation failed. Try to continue, probably thrashing all the way. + * Collect the regions that weren't collected before and see if any + * region has enough to satisfy the original request. + */ + for (rp = curr; rp; rp = rp->prev) + if (rp->size < want) { /* if not collected earlier */ + *pcurr = rp; + collect(region); + if (DiffPtrs(rp->end,rp->free) >= want) + return rp->free; + } + if ((rp = findgap(curr, nbytes)) != 0) { + *pcurr = rp; + return rp->free; + } + + /* + * All attempts failed. + */ + if (region == Blocks) + ReturnErrNum(307, 0); + else if (qualfail) + ReturnErrNum(304, 0); + else + ReturnErrNum(306, 0); +} + +/* + * findgap - search region chain for a region having at least nbytes available + */ +static struct region *findgap(curr, nbytes) +struct region *curr; +word nbytes; + { + struct region *rp; + + for (rp = curr; rp; rp = rp->prev) + if (DiffPtrs(rp->end, rp->free) >= nbytes) + return rp; + for (rp = curr->next; rp; rp = rp->next) + if (DiffPtrs(rp->end, rp->free) >= nbytes) + return rp; + return NULL; + } + +/* + * newregion - try to malloc a new region and tenure the old one, + * backing off if the requested size fails. + */ +static struct region *newregion(nbytes,stdsize) +word nbytes,stdsize; +{ + uword minSize = MinAbrSize; + struct region *rp; + + if ((uword)nbytes > minSize) + minSize = (uword)nbytes; + rp = (struct region *)malloc(sizeof(struct region)); + if (rp) { + rp->size = stdsize; + if (rp->size < nbytes) + rp->size = Max(nbytes+stdsize, nbytes); + do { + rp->free = rp->base = (char *)AllocReg(rp->size); + if (rp->free != NULL) { + rp->end = rp->base + rp->size; + return rp; + } + else { + } + rp->size = (rp->size + nbytes)/2 - 1; + } + while (rp->size >= minSize); + free((char *)rp); + } + return NULL; +} |