mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-23 22:20:24 -08:00
(plus some edits: integrations were not not verbatim)
poolamc.c: Count the loops of amcScanNailed.
If amcScanNailed looped, ss.unfixedSummary is
not accurate, so move all of the ScanStateSummary into
ss.fixedSumamry, so that trace.c's .verify.segsummary
does not erroneously fail.
This fixes job001548.
trace.c: (comment) explain .verify.segsummary
poolamc.c: AMCSegSketch: four character sketch of seg-state
Copied from Perforce
Change: 162150
ServerID: perforce.ravenbrook.com
2243 lines
62 KiB
C
2243 lines
62 KiB
C
/* poolamc.c: AUTOMATIC MOSTLY-COPYING MEMORY POOL CLASS
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
|
|
* Portions copyright (C) 2002 Global Graphics Software.
|
|
*
|
|
* .sources: <design/poolamc/>.
|
|
*/
|
|
|
|
#include "mpscamc.h"
|
|
#include "chain.h"
|
|
#include "bt.h"
|
|
#include "mpm.h"
|
|
|
|
SRCID(poolamc, "$Id$");
|
|
|
|
|
|
/* PType enumeration -- distinguishes AMCGen and AMCNailboard */
|
|
enum {AMCPTypeGen = 1, AMCPTypeNailboard};
|
|
|
|
/* AMC typedef */
|
|
typedef struct AMCStruct *AMC;
|
|
|
|
/* amcGen typedef */
|
|
typedef struct amcGenStruct *amcGen;
|
|
|
|
/* forward declarations */
|
|
|
|
static Bool amcSegHasNailboard(Seg seg);
|
|
static Bool AMCCheck(AMC amc);
|
|
static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO);
|
|
static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO);
|
|
static PoolClass AMCPoolClassGet(void);
|
|
static BufferClass amcBufClassGet(void);
|
|
static SegClass amcSegClassGet(void);
|
|
|
|
|
|
/* amcGenStruct -- pool AMC generation descriptor */
|
|
|
|
#define amcGenSig ((Sig)0x519A3C9E) /* SIGnature AMC GEn */
|
|
|
|
typedef struct amcGenStruct {
|
|
PoolGenStruct pgen;
|
|
int type; /* AMCPTypeGen for a gen */
|
|
RingStruct amcRing; /* link in list of gens in pool */
|
|
Buffer forward; /* forwarding buffer */
|
|
Count segs; /* number of segs in gen */
|
|
Sig sig; /* <code/misc.h#sig> */
|
|
} amcGenStruct;
|
|
|
|
#define amcGenAMC(amcgen) Pool2AMC((amcgen)->pgen.pool)
|
|
#define amcGenPool(amcgen) ((amcgen)->pgen.pool)
|
|
|
|
#define amcGenNr(amcgen) ((amcgen)->pgen.nr)
|
|
|
|
|
|
enum {outsideRamp = 1, beginRamp, ramping, finishRamp, collectingRamp};
|
|
|
|
|
|
/* amcNailboard -- the nailboard */
|
|
|
|
typedef struct amcNailboardStruct *amcNailboard;
|
|
typedef struct amcNailboardStruct {
|
|
Sig sig;
|
|
int type; /* AMCPTypeNailboard for a nailboard */
|
|
amcGen gen; /* generation of this segment */
|
|
Count nails; /* number of ambigFixes, not necessarily distinct */
|
|
Count distinctNails; /* number of distinct ambigFixes */
|
|
Bool newMarks; /* set to TRUE if a new mark bit is added */
|
|
Shift markShift; /* shift to convert offset into bit index for mark */
|
|
BT mark; /* mark table used to record ambiguous fixes */
|
|
} amcNailboardStruct;
|
|
|
|
#define amcNailboardSig ((Sig)0x519A3C4B) /* SIGnature AMC Nailboard */
|
|
|
|
|
|
/* AMCGSegStruct -- AMC segment structure
|
|
*
|
|
* .segtype: AMC segs have a pointer to the type field of either
|
|
* a nailboard or a generation. This initial value is passed
|
|
* as an additional parameter when the segment is allocated.
|
|
* See <design/poolamc/#fix.nail.distinguish>.
|
|
*/
|
|
|
|
typedef struct amcSegStruct *amcSeg;
|
|
|
|
#define amcSegSig ((Sig)0x519A3C59) /* SIGnature AMC SeG */
|
|
|
|
typedef struct amcSegStruct {
|
|
GCSegStruct gcSegStruct; /* superclass fields must come first */
|
|
int *segTypeP; /* .segtype */
|
|
Bool new; /* allocated since last GC */
|
|
Sig sig; /* <code/misc.h#sig> */
|
|
} amcSegStruct;
|
|
|
|
#define Seg2amcSeg(seg) ((amcSeg)(seg))
|
|
#define amcSeg2Seg(amcseg) ((Seg)(amcseg))
|
|
|
|
#define amcSegTypeP(seg) (Seg2amcSeg(seg)->segTypeP)
|
|
#define amcSegSetTypeP(seg, type) (Seg2amcSeg(seg)->segTypeP = (type))
|
|
|
|
|
|
static Bool amcSegCheck(amcSeg amcseg)
|
|
{
|
|
CHECKS(amcSeg, amcseg);
|
|
CHECKD(GCSeg, &amcseg->gcSegStruct);
|
|
CHECKL(*amcseg->segTypeP == AMCPTypeNailboard
|
|
|| *amcseg->segTypeP == AMCPTypeGen);
|
|
if (*amcseg->segTypeP == AMCPTypeNailboard) {
|
|
CHECKL(SegNailed(amcSeg2Seg(amcseg)) != TraceSetEMPTY);
|
|
}
|
|
CHECKL(BoolCheck(amcseg->new));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* AMCSegInit -- initialise an AMC segment */
|
|
|
|
static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size,
|
|
Bool reservoirPermit, va_list args)
|
|
{
|
|
int *segtype = va_arg(args, int*); /* .segtype */
|
|
SegClass super;
|
|
amcSeg amcseg;
|
|
Res res;
|
|
|
|
AVERT(Seg, seg);
|
|
amcseg = Seg2amcSeg(seg);
|
|
/* no useful checks for base and size */
|
|
AVERT(Bool, reservoirPermit);
|
|
|
|
/* Initialize the superclass fields first via next-method call */
|
|
super = SEG_SUPERCLASS(amcSegClass);
|
|
res = super->init(seg, pool, base, size, reservoirPermit, args);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
amcseg->segTypeP = segtype; /* .segtype */
|
|
amcseg->new = TRUE;
|
|
amcseg->sig = amcSegSig;
|
|
AVERT(amcSeg, amcseg);
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* AMCSegSketch -- summarise the segment state for a human reader
|
|
*
|
|
* Write a short human-readable text representation of the segment
|
|
* state into storage indicated by pbSketch+cbSketch.
|
|
*
|
|
* A typical sketch is "bGW_", meaning the seg has a nailboard, has
|
|
* some Grey and some White objects, and has no buffer attached.
|
|
*/
|
|
|
|
static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch)
|
|
{
|
|
amcSeg amcseg;
|
|
Buffer buffer;
|
|
|
|
AVER(pbSketch);
|
|
AVER(cbSketch >= 5);
|
|
AVERT(Seg, seg);
|
|
amcseg = Seg2amcSeg(seg);
|
|
AVERT(amcSeg, amcseg);
|
|
|
|
if (SegNailed(seg) == TraceSetEMPTY) {
|
|
pbSketch[0] = 'm'; /* mobile */
|
|
} else if (amcSegHasNailboard(seg)) {
|
|
pbSketch[0] = 'b'; /* boarded */
|
|
} else {
|
|
pbSketch[0] = 's'; /* stuck */
|
|
}
|
|
|
|
if (SegGrey(seg) == TraceSetEMPTY) {
|
|
pbSketch[1] = '_';
|
|
} else {
|
|
pbSketch[1] = 'G'; /* Grey */
|
|
}
|
|
|
|
if (SegWhite(seg) == TraceSetEMPTY) {
|
|
pbSketch[2] = '_';
|
|
} else {
|
|
pbSketch[2] = 'W'; /* White */
|
|
}
|
|
|
|
buffer = SegBuffer(seg);
|
|
if (buffer == NULL) {
|
|
pbSketch[3] = '_';
|
|
} else {
|
|
Bool mut = BufferIsMutator(buffer);
|
|
Bool flipped = ((buffer->mode & BufferModeFLIPPED) != 0);
|
|
Bool trapped = BufferIsTrapped(buffer);
|
|
Bool limitzeroed = (buffer->apStruct.limit == 0);
|
|
|
|
pbSketch[3] = 'X'; /* I don't know what's going on! */
|
|
|
|
if ( (flipped == trapped) && (trapped == limitzeroed) ) {
|
|
if (mut) {
|
|
if (flipped) {
|
|
pbSketch[3] = 's'; /* stalo */
|
|
} else {
|
|
pbSketch[3] = 'n'; /* neo */
|
|
}
|
|
} else {
|
|
if (!flipped) {
|
|
pbSketch[3] = 'f'; /* forwarding */
|
|
}
|
|
}
|
|
} else {
|
|
/* I don't know what's going on! */
|
|
}
|
|
}
|
|
|
|
pbSketch[4] = '\0';
|
|
AVER(4 < cbSketch);
|
|
}
|
|
|
|
|
|
/* AMCSegDescribe -- describe the contents of a segment
|
|
*
|
|
* See <design/poolamc/#seg-describe>.
|
|
*/
|
|
static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
Pool pool;
|
|
amcSeg amcseg;
|
|
SegClass super;
|
|
Addr i, p, base, limit, init;
|
|
Align step;
|
|
Size row;
|
|
char abzSketch[5];
|
|
|
|
if (!CHECKT(Seg, seg)) return ResFAIL;
|
|
if (stream == NULL) return ResFAIL;
|
|
amcseg = Seg2amcSeg(seg);
|
|
if (!CHECKT(amcSeg, amcseg)) return ResFAIL;
|
|
|
|
/* Describe the superclass fields first via next-method call */
|
|
super = SEG_SUPERCLASS(amcSegClass);
|
|
res = super->describe(seg, stream);
|
|
if (res != ResOK) return res;
|
|
|
|
pool = SegPool(seg);
|
|
step = PoolAlignment(pool);
|
|
row = step * 64;
|
|
|
|
base = SegBase(seg);
|
|
p = AddrAdd(base, pool->format->headerSize);
|
|
limit = SegLimit(seg);
|
|
|
|
res = WriteF(stream,
|
|
"AMC seg $P [$A,$A){\n",
|
|
(WriteFP)seg, (WriteFA)base, (WriteFA)limit,
|
|
NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
if (amcSegHasNailboard(seg)) {
|
|
res = WriteF(stream, " Boarded\n", NULL);
|
|
/* @@@@ should have AMCNailboardDescribe() */
|
|
} else {
|
|
if (SegNailed(seg) == TraceSetEMPTY) {
|
|
res = WriteF(stream, " Mobile\n", NULL);
|
|
} else {
|
|
res = WriteF(stream, " Stuck\n", NULL);
|
|
}
|
|
}
|
|
if (res != ResOK) return res;
|
|
|
|
res = WriteF(stream, " Map: *===:object bbbb:buffer\n", NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
if (SegBuffer(seg) != NULL)
|
|
init = BufferGetInit(SegBuffer(seg));
|
|
else
|
|
init = limit;
|
|
|
|
for(i = base; i < limit; i = AddrAdd(i, row)) {
|
|
Addr j;
|
|
char c;
|
|
|
|
res = WriteF(stream, " $A ", i, NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
/* @@@@ This needs to describe nailboards as well */
|
|
/* @@@@ This misses a header-sized pad at the end. */
|
|
for(j = i; j < AddrAdd(i, row); j = AddrAdd(j, step)) {
|
|
if (j >= limit)
|
|
c = ' '; /* if seg is not a whole number of print rows */
|
|
else if (j >= init)
|
|
c = 'b';
|
|
else if (j == p) {
|
|
c = '*';
|
|
p = (pool->format->skip)(p);
|
|
} else
|
|
c = '=';
|
|
res = WriteF(stream, "$C", c, NULL);
|
|
if (res != ResOK) return res;
|
|
}
|
|
|
|
res = WriteF(stream, "\n", NULL);
|
|
if (res != ResOK) return res;
|
|
}
|
|
|
|
AMCSegSketch(seg, abzSketch, NELEMS(abzSketch));
|
|
res = WriteF(stream, " Sketch: $S\n", (WriteFS)abzSketch, NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
res = WriteF(stream, "} AMC Seg $P\n", (WriteFP)seg, NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* amcSegClass -- Class definition for AMC segments */
|
|
|
|
DEFINE_SEG_CLASS(amcSegClass, class)
|
|
{
|
|
INHERIT_CLASS(class, GCSegClass);
|
|
SegClassMixInNoSplitMerge(class); /* no support for this (yet) */
|
|
class->name = "AMCSEG";
|
|
class->size = sizeof(amcSegStruct);
|
|
class->init = AMCSegInit;
|
|
class->describe = AMCSegDescribe;
|
|
}
|
|
|
|
|
|
|
|
/* amcSegHasNailboard -- test whether the segment has a nailboard
|
|
*
|
|
* See <design/poolamc/#fix.nail.distinguish>.
|
|
*/
|
|
static Bool amcSegHasNailboard(Seg seg)
|
|
{
|
|
int type;
|
|
|
|
type = *amcSegTypeP(seg);
|
|
AVER(type == AMCPTypeNailboard || type == AMCPTypeGen);
|
|
return type == AMCPTypeNailboard;
|
|
}
|
|
|
|
|
|
/* amcSegNailboard -- get the nailboard for this segment */
|
|
|
|
static amcNailboard amcSegNailboard(Seg seg)
|
|
{
|
|
int *p;
|
|
|
|
p = amcSegTypeP(seg);
|
|
AVER(amcSegHasNailboard(seg));
|
|
return PARENT(amcNailboardStruct, type, p);
|
|
}
|
|
|
|
|
|
/* amcSegGen -- get the generation structure for this segment */
|
|
|
|
static amcGen amcSegGen(Seg seg)
|
|
{
|
|
if (amcSegHasNailboard(seg)) {
|
|
amcNailboard Nailboard = amcSegNailboard(seg);
|
|
return Nailboard->gen;
|
|
} else {
|
|
int *p;
|
|
p = amcSegTypeP(seg);
|
|
return PARENT(amcGenStruct, type, p);
|
|
}
|
|
}
|
|
|
|
|
|
/* AMCStruct -- pool AMC descriptor
|
|
*
|
|
* See <design/poolamc/#struct>.
|
|
*/
|
|
|
|
#define AMCSig ((Sig)0x519A3C99) /* SIGnature AMC */
|
|
|
|
typedef struct AMCStruct { /* <design/poolamc/#struct> */
|
|
PoolStruct poolStruct; /* generic pool structure */
|
|
RankSet rankSet; /* rankSet for entire pool */
|
|
RingStruct genRing; /* ring of generations */
|
|
Bool gensBooted; /* used during boot (init) */
|
|
Chain chain; /* chain used by this pool */
|
|
size_t gens; /* number of generations */
|
|
amcGen *gen; /* (pointer to) array of generations */
|
|
amcGen nursery; /* the default mutator generation */
|
|
amcGen rampGen; /* the ramp generation */
|
|
amcGen afterRampGen; /* the generation after rampGen */
|
|
unsigned rampCount; /* <design/poolamc/#ramp.count> */
|
|
int rampMode; /* <design/poolamc/#ramp.mode> */
|
|
Sig sig; /* <design/pool/#outer-structure.sig> */
|
|
} AMCStruct;
|
|
|
|
#define Pool2AMC(pool) PARENT(AMCStruct, poolStruct, (pool))
|
|
#define AMC2Pool(amc) (&(amc)->poolStruct)
|
|
|
|
|
|
/* amcGenCheck -- check consistency of a generation structure */
|
|
|
|
static Bool amcGenCheck(amcGen gen)
|
|
{
|
|
Arena arena;
|
|
AMC amc;
|
|
|
|
CHECKS(amcGen, gen);
|
|
CHECKD(PoolGen, &gen->pgen);
|
|
amc = amcGenAMC(gen);
|
|
CHECKU(AMC, amc);
|
|
CHECKL(gen->type == AMCPTypeGen);
|
|
CHECKD(Buffer, gen->forward);
|
|
CHECKL(RingCheck(&gen->amcRing));
|
|
CHECKL((gen->pgen.totalSize == 0) == (gen->segs == 0));
|
|
arena = amc->poolStruct.arena;
|
|
CHECKL(gen->pgen.totalSize >= gen->segs * ArenaAlign(arena));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* amcNailboardCheck -- check the nailboard */
|
|
|
|
static Bool amcNailboardCheck(amcNailboard board)
|
|
{
|
|
CHECKS(amcNailboard, board);
|
|
CHECKL(board->type == AMCPTypeNailboard);
|
|
CHECKD(amcGen, board->gen);
|
|
/* nails is >= number of set bits in mark, but we can't check this. */
|
|
/* We know that shift corresponds to pool->align */
|
|
CHECKL(BoolCheck(board->newMarks));
|
|
CHECKL(board->distinctNails <= board->nails);
|
|
CHECKL(1uL << board->markShift == PoolAlignment(amcGenPool(board->gen)));
|
|
/* weak check for BTs @@@@ */
|
|
CHECKL(board->mark != NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* amcBufStruct -- AMC Buffer subclass
|
|
*
|
|
* This subclass of SegBuf records a link to a generation.
|
|
*/
|
|
|
|
#define amcBufSig ((Sig)0x519A3CBF) /* SIGnature AMC BuFfer */
|
|
|
|
typedef struct amcBufStruct *amcBuf;
|
|
|
|
typedef struct amcBufStruct {
|
|
SegBufStruct segbufStruct; /* superclass fields must come first */
|
|
amcGen gen; /* The AMC generation */
|
|
Sig sig; /* <design/sig/> */
|
|
} amcBufStruct;
|
|
|
|
|
|
/* Buffer2amcBuf -- convert generic Buffer to an amcBuf */
|
|
|
|
#define Buffer2amcBuf(buffer) ((amcBuf)(buffer))
|
|
|
|
|
|
|
|
/* amcBufCheck -- check consistency of an amcBuf */
|
|
|
|
static Bool amcBufCheck(amcBuf amcbuf)
|
|
{
|
|
SegBuf segbuf;
|
|
|
|
CHECKS(amcBuf, amcbuf);
|
|
segbuf = &amcbuf->segbufStruct;
|
|
CHECKL(SegBufCheck(segbuf));
|
|
if (amcbuf->gen != NULL)
|
|
CHECKD(amcGen, amcbuf->gen);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* amcBufGen -- Return the AMC generation of an amcBuf */
|
|
|
|
static amcGen amcBufGen(Buffer buffer)
|
|
{
|
|
return Buffer2amcBuf(buffer)->gen;
|
|
}
|
|
|
|
|
|
/* amcBufSetGen -- Set the AMC generation of an amcBuf */
|
|
|
|
static void amcBufSetGen(Buffer buffer, amcGen gen)
|
|
{
|
|
amcBuf amcbuf;
|
|
|
|
if (gen != NULL)
|
|
AVERT(amcGen, gen);
|
|
amcbuf = Buffer2amcBuf(buffer);
|
|
amcbuf->gen = gen;
|
|
}
|
|
|
|
|
|
/* AMCBufInit -- Initialize an amcBuf */
|
|
|
|
static Res AMCBufInit(Buffer buffer, Pool pool, va_list args)
|
|
{
|
|
AMC amc;
|
|
amcBuf amcbuf;
|
|
BufferClass superclass;
|
|
Res res;
|
|
|
|
AVERT(Buffer, buffer);
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
|
|
/* call next method */
|
|
superclass = BUFFER_SUPERCLASS(amcBufClass);
|
|
res = (*superclass->init)(buffer, pool, args);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
amcbuf = Buffer2amcBuf(buffer);
|
|
if (BufferIsMutator(buffer)) {
|
|
/* Set up the buffer to be allocating in the nursery. */
|
|
amcbuf->gen = amc->nursery;
|
|
} else {
|
|
amcbuf->gen = NULL; /* no gen yet -- see <design/poolamc/#gen.forward> */
|
|
}
|
|
amcbuf->sig = amcBufSig;
|
|
AVERT(amcBuf, amcbuf);
|
|
|
|
BufferSetRankSet(buffer, amc->rankSet);
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* AMCBufFinish -- Finish an amcBuf */
|
|
|
|
static void AMCBufFinish(Buffer buffer)
|
|
{
|
|
BufferClass super;
|
|
amcBuf amcbuf;
|
|
|
|
AVERT(Buffer, buffer);
|
|
amcbuf = Buffer2amcBuf(buffer);
|
|
AVERT(amcBuf, amcbuf);
|
|
|
|
amcbuf->sig = SigInvalid;
|
|
|
|
/* finish the superclass fields last */
|
|
super = BUFFER_SUPERCLASS(amcBufClass);
|
|
super->finish(buffer);
|
|
}
|
|
|
|
|
|
/* amcBufClass -- The class definition */
|
|
|
|
DEFINE_BUFFER_CLASS(amcBufClass, class)
|
|
{
|
|
INHERIT_CLASS(class, SegBufClass);
|
|
class->name = "AMCBUF";
|
|
class->size = sizeof(amcBufStruct);
|
|
class->init = AMCBufInit;
|
|
class->finish = AMCBufFinish;
|
|
}
|
|
|
|
|
|
/* amcGenCreate -- create a generation */
|
|
|
|
static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr)
|
|
{
|
|
Arena arena;
|
|
Buffer buffer;
|
|
Pool pool;
|
|
amcGen gen;
|
|
Res res;
|
|
void *p;
|
|
|
|
pool = AMC2Pool(amc);
|
|
arena = pool->arena;
|
|
|
|
res = ControlAlloc(&p, arena, sizeof(amcGenStruct), FALSE);
|
|
if (res != ResOK)
|
|
goto failControlAlloc;
|
|
gen = (amcGen)p;
|
|
|
|
res = BufferCreate(&buffer, EnsureamcBufClass(), pool, FALSE);
|
|
if (res != ResOK)
|
|
goto failBufferCreate;
|
|
|
|
res = PoolGenInit(&gen->pgen, amc->chain, genNr, pool);
|
|
if (res != ResOK)
|
|
goto failGenInit;
|
|
gen->type = AMCPTypeGen;
|
|
RingInit(&gen->amcRing);
|
|
gen->segs = 0;
|
|
gen->forward = buffer;
|
|
gen->sig = amcGenSig;
|
|
|
|
AVERT(amcGen, gen);
|
|
|
|
RingAppend(&amc->genRing, &gen->amcRing);
|
|
EVENT_PP(AMCGenCreate, amc, gen);
|
|
*genReturn = gen;
|
|
return ResOK;
|
|
|
|
failGenInit:
|
|
BufferDestroy(buffer);
|
|
failBufferCreate:
|
|
ControlFree(arena, p, sizeof(amcGenStruct));
|
|
failControlAlloc:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* amcGenDestroy -- destroy a generation */
|
|
|
|
static void amcGenDestroy(amcGen gen)
|
|
{
|
|
Arena arena;
|
|
|
|
AVERT(amcGen, gen);
|
|
AVER(gen->segs == 0);
|
|
AVER(gen->pgen.totalSize == 0);
|
|
|
|
EVENT_P(AMCGenDestroy, gen);
|
|
arena = PoolArena(amcGenPool(gen));
|
|
gen->sig = SigInvalid;
|
|
RingRemove(&gen->amcRing);
|
|
RingFinish(&gen->amcRing);
|
|
PoolGenFinish(&gen->pgen);
|
|
BufferDestroy(gen->forward);
|
|
ControlFree(arena, gen, sizeof(amcGenStruct));
|
|
}
|
|
|
|
|
|
/* amcGenDescribe -- describe an AMC generation */
|
|
|
|
static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
|
|
if (!CHECKT(amcGen, gen)) return ResFAIL;
|
|
|
|
res = WriteF(stream,
|
|
" amcGen $P ($U) {\n", (WriteFP)gen, (WriteFU)amcGenNr(gen),
|
|
" buffer $P\n", gen->forward,
|
|
" segs $U, totalSize $U, newSize $U\n", (WriteFU)gen->segs,
|
|
(WriteFU)gen->pgen.totalSize, (WriteFU)gen->pgen.newSize,
|
|
" } amcGen\n", NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* amcSegCreateNailboard -- create nailboard for segment */
|
|
|
|
static Res amcSegCreateNailboard(Seg seg, Pool pool)
|
|
{
|
|
amcNailboard board;
|
|
Arena arena;
|
|
Count bits;
|
|
Res res;
|
|
void *p;
|
|
|
|
AVER(!amcSegHasNailboard(seg));
|
|
|
|
arena = PoolArena(pool);
|
|
|
|
res = ControlAlloc(&p, arena, sizeof(amcNailboardStruct), FALSE);
|
|
if (res != ResOK)
|
|
goto failAllocNailboard;
|
|
board = p;
|
|
board->type = AMCPTypeNailboard;
|
|
board->gen = amcSegGen(seg);
|
|
board->nails = (Count)0;
|
|
board->distinctNails = (Count)0;
|
|
board->newMarks = FALSE;
|
|
board->markShift = SizeLog2((Size)pool->alignment);
|
|
/* See d.m.p.Nailboard.size. */
|
|
bits = (SegSize(seg) + pool->format->headerSize) >> board->markShift;
|
|
res = ControlAlloc(&p, arena, BTSize(bits), FALSE);
|
|
if (res != ResOK)
|
|
goto failMarkTable;
|
|
board->mark = p;
|
|
BTResRange(board->mark, 0, bits);
|
|
board->sig = amcNailboardSig;
|
|
AVERT(amcNailboard, board);
|
|
amcSegSetTypeP(seg, &board->type); /* .segtype */
|
|
return ResOK;
|
|
|
|
failMarkTable:
|
|
ControlFree(arena, board, sizeof(amcNailboardStruct));
|
|
failAllocNailboard:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* amcSegDestroyNailboard -- destroy the nailboard of a segment */
|
|
|
|
static void amcSegDestroyNailboard(Seg seg, Pool pool)
|
|
{
|
|
amcNailboard board;
|
|
amcGen gen;
|
|
Arena arena;
|
|
Count bits;
|
|
|
|
gen = amcSegGen(seg);
|
|
board = amcSegNailboard(seg);
|
|
AVERT(amcNailboard, board);
|
|
|
|
arena = PoolArena(pool);
|
|
bits = SegSize(seg) >> board->markShift;
|
|
ControlFree(arena, board->mark, BTSize(bits));
|
|
board->sig = SigInvalid;
|
|
ControlFree(arena, board, sizeof(amcNailboardStruct));
|
|
amcSegSetTypeP(seg, &gen->type); /* .segtype */
|
|
}
|
|
|
|
|
|
/* amcNailGetMark -- get the mark bit for ref from the nailboard */
|
|
|
|
static Bool amcNailGetMark(Seg seg, Ref ref)
|
|
{
|
|
amcNailboard board;
|
|
Index i;
|
|
|
|
board = amcSegNailboard(seg);
|
|
AVERT(amcNailboard, board);
|
|
|
|
i = AddrOffset(SegBase(seg), ref) >> board->markShift;
|
|
return BTGet(board->mark, i);
|
|
}
|
|
|
|
|
|
/* amcNailGetAndSetMark -- set the mark bit for ref in the nailboard
|
|
*
|
|
* Returns the old value.
|
|
*/
|
|
static Bool amcNailGetAndSetMark(Seg seg, Ref ref)
|
|
{
|
|
amcNailboard board;
|
|
Index i;
|
|
|
|
board = amcSegNailboard(seg);
|
|
AVERT(amcNailboard, board);
|
|
|
|
++board->nails;
|
|
i = AddrOffset(SegBase(seg), ref) >> board->markShift;
|
|
if (!BTGet(board->mark, i)) {
|
|
BTSet(board->mark, i);
|
|
board->newMarks = TRUE;
|
|
++board->distinctNails;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* amcNailMarkRange -- nail a range in the board
|
|
*
|
|
* We nail the objects laying between base and limit, i.e., mark the
|
|
* bits that correspond to client pointers for them. We may assume that
|
|
* the range is unmarked.
|
|
*/
|
|
static void amcNailMarkRange(Seg seg, Addr base, Addr limit)
|
|
{
|
|
amcNailboard board;
|
|
Index ibase, ilimit;
|
|
Size headerSize;
|
|
|
|
AVER(SegBase(seg) <= base);
|
|
AVER(base < SegLimit(seg));
|
|
AVER(SegBase(seg) <= limit);
|
|
AVER(limit <= SegLimit(seg));
|
|
AVER(base < limit);
|
|
|
|
board = amcSegNailboard(seg);
|
|
AVERT(amcNailboard, board);
|
|
headerSize = SegPool(seg)->format->headerSize;
|
|
ibase = (AddrOffset(SegBase(seg), base) + headerSize) >> board->markShift;
|
|
ilimit = (AddrOffset(SegBase(seg), limit) + headerSize) >> board->markShift;
|
|
AVER(BTIsResRange(board->mark, ibase, ilimit));
|
|
|
|
BTSetRange(board->mark, ibase, ilimit);
|
|
board->nails += ilimit - ibase;
|
|
board->distinctNails += ilimit - ibase;
|
|
}
|
|
|
|
|
|
/* amcNailRangeIsMarked -- check that a range in the board is marked
|
|
*
|
|
* Like amcNailMarkRange, we take the arguments as referring to base
|
|
* pointers and look at the bits of the corresponding client pointers.
|
|
*/
|
|
static Bool amcNailRangeIsMarked(Seg seg, Addr base, Addr limit)
|
|
{
|
|
amcNailboard board;
|
|
Index ibase, ilimit;
|
|
Size headerSize;
|
|
|
|
AVER(SegBase(seg) <= base);
|
|
AVER(base < SegLimit(seg));
|
|
AVER(SegBase(seg) <= limit);
|
|
AVER(limit <= SegLimit(seg));
|
|
AVER(base < limit);
|
|
|
|
board = amcSegNailboard(seg);
|
|
AVERT(amcNailboard, board);
|
|
headerSize = SegPool(seg)->format->headerSize;
|
|
ibase = (AddrOffset(SegBase(seg), base) + headerSize) >> board->markShift;
|
|
ilimit = (AddrOffset(SegBase(seg), limit) + headerSize) >> board->markShift;
|
|
return BTIsSetRange(board->mark, ibase, ilimit);
|
|
}
|
|
|
|
|
|
/* amcInitComm -- initialize AMC/Z pool
|
|
*
|
|
* See <design/poolamc/#init>.
|
|
* Shared by AMCInit and AMCZinit.
|
|
*/
|
|
static Res amcInitComm(Pool pool, RankSet rankSet, va_list arg)
|
|
{
|
|
AMC amc;
|
|
Res res;
|
|
Arena arena;
|
|
Index i;
|
|
size_t genArraySize;
|
|
size_t genCount;
|
|
|
|
AVER(pool != NULL);
|
|
|
|
amc = Pool2AMC(pool);
|
|
arena = PoolArena(pool);
|
|
|
|
pool->format = va_arg(arg, Format);
|
|
AVERT(Format, pool->format);
|
|
pool->alignment = pool->format->alignment;
|
|
amc->chain = va_arg(arg, Chain);
|
|
AVERT(Chain, amc->chain);
|
|
amc->rankSet = rankSet;
|
|
|
|
RingInit(&amc->genRing);
|
|
/* amc gets checked before the generations get created, but they */
|
|
/* do get created later in this function. */
|
|
amc->gen = NULL;
|
|
amc->nursery = NULL;
|
|
amc->rampGen = NULL;
|
|
amc->afterRampGen = NULL;
|
|
amc->gensBooted = FALSE;
|
|
|
|
amc->rampCount = 0;
|
|
amc->rampMode = outsideRamp;
|
|
|
|
if (pool->format->headerSize == 0) {
|
|
pool->fix = AMCFix;
|
|
} else {
|
|
pool->fix = AMCHeaderFix;
|
|
}
|
|
|
|
amc->sig = AMCSig;
|
|
AVERT(AMC, amc);
|
|
|
|
/* Init generations. */
|
|
genCount = ChainGens(amc->chain);
|
|
{
|
|
void *p;
|
|
|
|
genArraySize = sizeof(amcGen) * (genCount + 1); /* chain plus dynamic gen */
|
|
res = ControlAlloc(&p, arena, genArraySize, FALSE);
|
|
if (res != ResOK)
|
|
goto failGensAlloc;
|
|
amc->gen = p;
|
|
for(i = 0; i < genCount + 1; ++i) {
|
|
res = amcGenCreate(&amc->gen[i], amc, (Serial)i);
|
|
if (res != ResOK) {
|
|
goto failGenAlloc;
|
|
}
|
|
}
|
|
/* Set up forwarding buffers. */
|
|
for(i = 0; i < genCount; ++i) {
|
|
amcBufSetGen(amc->gen[i]->forward, amc->gen[i+1]);
|
|
}
|
|
/* Dynamic gen forwards to itself. */
|
|
amcBufSetGen(amc->gen[genCount]->forward, amc->gen[genCount]);
|
|
}
|
|
amc->nursery = amc->gen[0];
|
|
amc->rampGen = amc->gen[genCount-1]; /* last ephemeral gen */
|
|
amc->afterRampGen = amc->gen[genCount];
|
|
amc->gensBooted = TRUE;
|
|
|
|
AVERT(AMC, amc);
|
|
EVENT_PP(AMCInit, pool, amc);
|
|
if (rankSet == RankSetEMPTY)
|
|
EVENT_PP(PoolInitAMCZ, pool, pool->format);
|
|
else
|
|
EVENT_PP(PoolInitAMC, pool, pool->format);
|
|
return ResOK;
|
|
|
|
failGenAlloc:
|
|
while(i > 0) {
|
|
--i;
|
|
amcGenDestroy(amc->gen[i]);
|
|
}
|
|
ControlFree(arena, amc->gen, genArraySize);
|
|
failGensAlloc:
|
|
return res;
|
|
}
|
|
|
|
static Res AMCInit(Pool pool, va_list arg)
|
|
{
|
|
return amcInitComm(pool, RankSetSingle(RankEXACT), arg);
|
|
}
|
|
|
|
static Res AMCZInit(Pool pool, va_list arg)
|
|
{
|
|
return amcInitComm(pool, RankSetEMPTY, arg);
|
|
}
|
|
|
|
|
|
/* AMCFinish -- finish AMC pool
|
|
*
|
|
* See <design/poolamc/#finish>.
|
|
*/
|
|
static void AMCFinish(Pool pool)
|
|
{
|
|
AMC amc;
|
|
Ring ring;
|
|
Ring node, nextNode;
|
|
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
|
|
EVENT_P(AMCFinish, amc);
|
|
|
|
/* @@@@ Make sure that segments aren't buffered by forwarding buffers. */
|
|
/* This is a hack which allows the pool to be destroyed */
|
|
/* while it is collecting. Note that there aren't any mutator */
|
|
/* buffers by this time. */
|
|
RING_FOR(node, &amc->genRing, nextNode) {
|
|
amcGen gen = RING_ELT(amcGen, amcRing, node);
|
|
BufferDetach(gen->forward, pool);
|
|
gen->pgen.newSize = (Size)0; /* to maintain invariant < totalSize */
|
|
}
|
|
|
|
ring = PoolSegRing(pool);
|
|
RING_FOR(node, ring, nextNode) {
|
|
Seg seg = SegOfPoolRing(node);
|
|
Size size;
|
|
amcGen gen = amcSegGen(seg);
|
|
|
|
--gen->segs;
|
|
size = SegSize(seg);
|
|
gen->pgen.totalSize -= size;
|
|
|
|
SegFree(seg);
|
|
}
|
|
|
|
/* Disassociate forwarding buffers from gens before they are destroyed */
|
|
ring = &amc->genRing;
|
|
RING_FOR(node, ring, nextNode) {
|
|
amcGen gen = RING_ELT(amcGen, amcRing, node);
|
|
amcBufSetGen(gen->forward, NULL);
|
|
}
|
|
RING_FOR(node, ring, nextNode) {
|
|
amcGen gen = RING_ELT(amcGen, amcRing, node);
|
|
amcGenDestroy(gen);
|
|
}
|
|
|
|
amc->sig = SigInvalid;
|
|
}
|
|
|
|
|
|
/* AMCBufferFill -- refill an allocation buffer
|
|
*
|
|
* See <design/poolamc/#fill>.
|
|
*/
|
|
static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
|
|
Pool pool, Buffer buffer, Size size,
|
|
Bool withReservoirPermit)
|
|
{
|
|
Seg seg;
|
|
AMC amc;
|
|
Res res;
|
|
Addr base, limit;
|
|
Arena arena;
|
|
Size alignedSize;
|
|
amcGen gen;
|
|
Serial genNr;
|
|
SegPrefStruct segPrefStruct;
|
|
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
AVER(baseReturn != NULL);
|
|
AVER(limitReturn != NULL);
|
|
AVERT(Buffer, buffer);
|
|
AVER(BufferIsReset(buffer));
|
|
AVER(size > 0);
|
|
AVERT(Bool, withReservoirPermit);
|
|
|
|
gen = amcBufGen(buffer);
|
|
AVERT(amcGen, gen);
|
|
|
|
/* Create and attach segment. The location of this segment is */
|
|
/* expressed as a generation number. We rely on the arena to */
|
|
/* organize locations appropriately. */
|
|
arena = PoolArena(pool);
|
|
alignedSize = SizeAlignUp(size, ArenaAlign(arena));
|
|
segPrefStruct = *SegPrefDefault();
|
|
SegPrefExpress(&segPrefStruct, SegPrefCollected, NULL);
|
|
genNr = PoolGenNr(&gen->pgen);
|
|
SegPrefExpress(&segPrefStruct, SegPrefGen, &genNr);
|
|
res = SegAlloc(&seg, amcSegClassGet(), &segPrefStruct,
|
|
alignedSize, pool, withReservoirPermit,
|
|
&gen->type); /* .segtype */
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
/* <design/seg/#field.rankSet.start> */
|
|
if (BufferRankSet(buffer) == RankSetEMPTY)
|
|
SegSetRankAndSummary(seg, BufferRankSet(buffer), RefSetEMPTY);
|
|
else
|
|
SegSetRankAndSummary(seg, BufferRankSet(buffer), RefSetUNIV);
|
|
|
|
/* Put the segment in the generation indicated by the buffer. */
|
|
++gen->segs;
|
|
gen->pgen.totalSize += alignedSize;
|
|
/* If ramping, don't count survivors in newSize. */
|
|
if (amc->rampMode != ramping
|
|
|| buffer != amc->rampGen->forward || gen != amc->rampGen) {
|
|
gen->pgen.newSize += alignedSize;
|
|
} else {
|
|
Seg2amcSeg(seg)->new = FALSE;
|
|
}
|
|
PoolGenUpdateZones(&gen->pgen, seg);
|
|
|
|
/* Give the buffer the entire segment to allocate in. */
|
|
base = SegBase(seg);
|
|
*baseReturn = base;
|
|
limit = AddrAdd(base, alignedSize);
|
|
AVER(limit == SegLimit(seg));
|
|
*limitReturn = limit;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* amcBufferEmpty -- detach a buffer from a segment
|
|
*
|
|
* See <design/poolamc/#flush>.
|
|
*/
|
|
static void AMCBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
|
|
{
|
|
AMC amc;
|
|
Size size;
|
|
Arena arena;
|
|
Seg seg;
|
|
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
AVERT(Buffer, buffer);
|
|
AVER(BufferIsReady(buffer));
|
|
seg = BufferSeg(buffer);
|
|
AVERT(Seg, seg);
|
|
AVER(init <= limit);
|
|
AVER(SegLimit(seg) == limit);
|
|
|
|
arena = BufferArena(buffer);
|
|
|
|
/* <design/poolamc/#flush.pad> */
|
|
size = AddrOffset(init, limit);
|
|
if (size > 0) {
|
|
ShieldExpose(arena, seg);
|
|
(*pool->format->pad)(init, size);
|
|
ShieldCover(arena, seg);
|
|
}
|
|
}
|
|
|
|
|
|
/* AMCRampBegin -- note an entry into a ramp pattern */
|
|
|
|
static void AMCRampBegin(Pool pool, Buffer buf, Bool collectAll)
|
|
{
|
|
AMC amc;
|
|
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
AVERT(Buffer, buf);
|
|
AVERT(Bool, collectAll);
|
|
UNUSED(collectAll); /* obsolete */
|
|
|
|
AVER(amc->rampCount < UINT_MAX);
|
|
++amc->rampCount;
|
|
if (amc->rampCount == 1) {
|
|
if (amc->rampMode != finishRamp)
|
|
amc->rampMode = beginRamp;
|
|
}
|
|
}
|
|
|
|
|
|
/* AMCRampEnd -- note an exit from a ramp pattern */
|
|
|
|
static void AMCRampEnd(Pool pool, Buffer buf)
|
|
{
|
|
AMC amc;
|
|
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
AVERT(Buffer, buf);
|
|
|
|
AVER(amc->rampCount > 0);
|
|
--amc->rampCount;
|
|
if (amc->rampCount == 0) {
|
|
PoolGen pgen = &amc->rampGen->pgen;
|
|
Ring node, nextNode;
|
|
|
|
if (amc->rampMode == ramping) /* if we are ramping, clean up */
|
|
amc->rampMode = finishRamp;
|
|
else
|
|
amc->rampMode = outsideRamp;
|
|
|
|
/* Adjust amc->rampGen->pgen.newSize: Now count all the segments in the */
|
|
/* ramp generation as new (except if they're white). */
|
|
RING_FOR(node, PoolSegRing(pool), nextNode) {
|
|
Seg seg = SegOfPoolRing(node);
|
|
|
|
if (amcSegGen(seg) == amc->rampGen && !Seg2amcSeg(seg)->new
|
|
&& SegWhite(seg) == TraceSetEMPTY) {
|
|
pgen->newSize += SegSize(seg);
|
|
Seg2amcSeg(seg)->new = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* AMCWhiten -- condemn the segment for the trace
|
|
*
|
|
* If the segment has a mutator buffer on it, we nail the buffer,
|
|
* because we can't scan or reclaim uncommitted buffers.
|
|
*/
|
|
static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
|
|
{
|
|
amcGen gen;
|
|
AMC amc;
|
|
Buffer buffer;
|
|
Res res;
|
|
|
|
AVERT(Pool, pool);
|
|
AVERT(Trace, trace);
|
|
AVERT(Seg, seg);
|
|
|
|
buffer = SegBuffer(seg);
|
|
if (buffer != NULL) {
|
|
AVERT(Buffer, buffer);
|
|
|
|
if (!BufferIsMutator(buffer)) { /* forwarding buffer */
|
|
AVER(BufferIsReady(buffer));
|
|
BufferDetach(buffer, pool);
|
|
} else { /* mutator buffer */
|
|
if (BufferScanLimit(buffer) == SegBase(seg)) {
|
|
/* There's nothing but the buffer, don't condemn. */
|
|
return ResOK;
|
|
}
|
|
/* [The following else-if section is just a comment added in */
|
|
/* 1998-10-08. It has never worked. RHSK 2007-01-16] */
|
|
/* else if (BufferScanLimit(buffer) == BufferLimit(buffer)) { */
|
|
/* The buffer is full, so it won't be used by the mutator. */
|
|
/* @@@@ We should detach it, but can't for technical reasons. */
|
|
/* BufferDetach(buffer, pool); */
|
|
/* } */
|
|
else {
|
|
/* There is an active buffer, make sure it's nailed. */
|
|
if (!amcSegHasNailboard(seg)) {
|
|
if (SegNailed(seg) == TraceSetEMPTY) {
|
|
res = amcSegCreateNailboard(seg, pool);
|
|
if (res != ResOK)
|
|
return ResOK; /* can't create nailboard, don't condemn */
|
|
if (BufferScanLimit(buffer) != BufferLimit(buffer))
|
|
amcNailMarkRange(seg, BufferScanLimit(buffer),
|
|
BufferLimit(buffer));
|
|
++trace->nailCount;
|
|
SegSetNailed(seg, TraceSetSingle(trace));
|
|
} else {
|
|
/* Segment is nailed already, cannot create a nailboard */
|
|
/* (see .nail.new), just give up condemning. */
|
|
return ResOK;
|
|
}
|
|
} else {
|
|
/* We have a nailboard, the buffer must be nailed already. */
|
|
AVER((BufferScanLimit(buffer) == BufferLimit(buffer))
|
|
|| amcNailRangeIsMarked(seg, BufferScanLimit(buffer),
|
|
BufferLimit(buffer)));
|
|
/* Nail it for this trace as well. */
|
|
SegSetNailed(seg, TraceSetAdd(SegNailed(seg), trace));
|
|
}
|
|
/* We didn't condemn the buffer, subtract it from the count. */
|
|
/* @@@@ We could subtract all the nailed grains. */
|
|
/* Relies on unsigned arithmetic wrapping round */
|
|
/* on under- and overflow (which it does). */
|
|
trace->condemned -= AddrOffset(BufferScanLimit(buffer),
|
|
BufferLimit(buffer));
|
|
}
|
|
}
|
|
}
|
|
|
|
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
|
|
trace->condemned += SegSize(seg);
|
|
|
|
gen = amcSegGen(seg);
|
|
AVERT(amcGen, gen);
|
|
if (Seg2amcSeg(seg)->new) {
|
|
gen->pgen.newSize -= SegSize(seg);
|
|
Seg2amcSeg(seg)->new = FALSE;
|
|
}
|
|
|
|
/* Ensure we are forwarding into the right generation. */
|
|
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
/* see <design/poolamc/#gen.ramp> */
|
|
/* This switching needs to be more complex for multiple traces. */
|
|
AVER(TraceSetIsSingle(PoolArena(pool)->busyTraces));
|
|
if (amc->rampMode == beginRamp && gen == amc->rampGen) {
|
|
BufferDetach(gen->forward, pool);
|
|
amcBufSetGen(gen->forward, gen);
|
|
amc->rampMode = ramping;
|
|
} else {
|
|
if (amc->rampMode == finishRamp && gen == amc->rampGen) {
|
|
BufferDetach(gen->forward, pool);
|
|
amcBufSetGen(gen->forward, amc->afterRampGen);
|
|
amc->rampMode = collectingRamp;
|
|
}
|
|
}
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* amcScanNailedOnce -- make one scanning pass over a nailed segment
|
|
*
|
|
* *totalReturn set to TRUE iff all objects in segment scanned.
|
|
* *moreReturn set to FALSE only if there are no more objects
|
|
* on the segment that need scanning (which is normally the case).
|
|
* It is set to TRUE if scanning had to be abandoned early on, and
|
|
* also if during emergency fixing any new marks got added to the
|
|
* nailboard.
|
|
*/
|
|
static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn,
|
|
ScanState ss, Pool pool, Seg seg, AMC amc)
|
|
{
|
|
Addr p, limit;
|
|
Format format;
|
|
Res res;
|
|
Bool total = TRUE;
|
|
Size bytesScanned = 0;
|
|
|
|
UNUSED(amc); /* Actually only unused when telemetry is off. @@@@ */
|
|
|
|
EVENT_PPP(AMCScanBegin, amc, seg, ss); /* @@@@ use own event */
|
|
|
|
format = pool->format;
|
|
amcSegNailboard(seg)->newMarks = FALSE;
|
|
|
|
p = AddrAdd(SegBase(seg), format->headerSize);
|
|
while(SegBuffer(seg) != NULL) {
|
|
limit = AddrAdd(BufferScanLimit(SegBuffer(seg)), format->headerSize);
|
|
if (p >= limit) {
|
|
AVER(p == limit);
|
|
goto returnGood;
|
|
}
|
|
while(p < limit) {
|
|
Addr q;
|
|
q = (*format->skip)(p);
|
|
if (amcNailGetMark(seg, p)) {
|
|
res = (*format->scan)(ss, p, q);
|
|
if (res != ResOK) {
|
|
*totalReturn = FALSE; *moreReturn = TRUE;
|
|
return res;
|
|
}
|
|
bytesScanned += AddrOffset(p, q);
|
|
} else {
|
|
total = FALSE;
|
|
}
|
|
p = q;
|
|
}
|
|
AVER(p == limit);
|
|
}
|
|
|
|
/* Should have a ScanMarkedRange or something like that @@@@ */
|
|
/* to abstract common code. */
|
|
limit = AddrAdd(SegLimit(seg), format->headerSize);
|
|
/* @@@@ Shouldn't p be set to BufferLimit here?! */
|
|
while(p < limit) {
|
|
Addr q;
|
|
q = (*format->skip)(p);
|
|
if (amcNailGetMark(seg, p)) {
|
|
res = (*format->scan)(ss, p, q);
|
|
if (res != ResOK) {
|
|
*totalReturn = FALSE; *moreReturn = TRUE;
|
|
return res;
|
|
}
|
|
bytesScanned += AddrOffset(p, q);
|
|
} else {
|
|
total = FALSE;
|
|
}
|
|
p = q;
|
|
}
|
|
AVER(p == limit);
|
|
|
|
returnGood:
|
|
EVENT_PPP(AMCScanEnd, amc, seg, ss); /* @@@@ use own event */
|
|
|
|
AVER(bytesScanned <= SegSize(seg));
|
|
ss->scannedSize += bytesScanned;
|
|
*totalReturn = total;
|
|
*moreReturn = amcSegNailboard(seg)->newMarks;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* amcScanNailed -- scan a nailed segment */
|
|
|
|
static Res amcScanNailed(Bool *totalReturn, ScanState ss, Pool pool,
|
|
Seg seg, AMC amc)
|
|
{
|
|
Bool total, moreScanning;
|
|
size_t loops = 0;
|
|
|
|
do {
|
|
Res res;
|
|
res = amcScanNailedOnce(&total, &moreScanning, ss, pool, seg, amc);
|
|
if (res != ResOK) {
|
|
*totalReturn = FALSE;
|
|
return res;
|
|
}
|
|
loops += 1;
|
|
} while(moreScanning);
|
|
|
|
if (loops > 1)
|
|
{
|
|
{
|
|
/* looped: should only happen under emergency tracing */
|
|
TraceId ti;
|
|
Trace trace;
|
|
Bool emerg = FALSE;
|
|
TraceSet ts = ss->traces;
|
|
Arena arena = pool->arena;
|
|
|
|
TRACE_SET_ITER(ti, trace, ts, arena)
|
|
if (trace->emergency) {
|
|
emerg = TRUE;
|
|
}
|
|
TRACE_SET_ITER_END(ti, trace, ts, arena);
|
|
AVER(emerg);
|
|
}
|
|
{
|
|
/* looped: fixed refs (from 1st pass) were seen by MPS_FIX1
|
|
* (in later passes), so the "ss.unfixedSummary" is _not_
|
|
* purely unfixed. In this one case, unfixedSummary is not
|
|
* accurate, and cannot be used to verify the SegSummary (see
|
|
* impl/trace/#verify.segsummary). Use ScanStateSetSummary to
|
|
* store ScanStateSummary in ss.fixedSummary and reset
|
|
* ss.unfixedSummary. See job001548.
|
|
*/
|
|
RefSet refset;
|
|
|
|
refset = ScanStateSummary(ss);
|
|
|
|
#if 0
|
|
/* This might become useful diagnostic feedback.
|
|
* (A rare event, which might prompt a rare defect to appear).
|
|
* For now (diagnostic feedback system still being developed),
|
|
* use if0 to comment it out. RHSK 2007-04-18.
|
|
*/
|
|
(void) WriteF(mps_lib_get_stdout(),
|
|
"amcScanNailed completed, but had to loop $U times:\n", (WriteFU)loops,
|
|
" SegSummary: $B\n", (WriteFB)SegSummary(seg),
|
|
" ss.white: $B\n", (WriteFB)ss->white,
|
|
" ss.unfixedSummary: $B", (WriteFB)ss->unfixedSummary,
|
|
"$S\n", (WriteFS)(
|
|
(RefSetSub(ss->unfixedSummary, SegSummary(seg)))
|
|
? ""
|
|
: " <=== This would have failed .verify.segsummary!"
|
|
),
|
|
" ss.fixedSummary: $B\n", (WriteFB)ss->fixedSummary,
|
|
"ScanStateSummary: $B\n", (WriteFB)refset,
|
|
"MOVING ScanStateSummary TO fixedSummary, "
|
|
"RESETTING unfixedSummary.\n", NULL
|
|
);
|
|
#endif
|
|
|
|
ScanStateSetSummary(ss, refset);
|
|
}
|
|
}
|
|
|
|
*totalReturn = total;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* AMCScan -- scan a single seg, turning it black
|
|
*
|
|
* See <design/poolamc/#seg-scan>.
|
|
*/
|
|
static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)
|
|
{
|
|
Addr base, limit;
|
|
Arena arena;
|
|
Format format;
|
|
AMC amc;
|
|
Res res;
|
|
|
|
AVER(totalReturn != NULL);
|
|
AVERT(ScanState, ss);
|
|
AVERT(Seg, seg);
|
|
AVERT(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
|
|
|
|
format = pool->format;
|
|
arena = pool->arena;
|
|
|
|
if (amcSegHasNailboard(seg)) {
|
|
return amcScanNailed(totalReturn, ss, pool, seg, amc);
|
|
}
|
|
|
|
EVENT_PPP(AMCScanBegin, amc, seg, ss);
|
|
|
|
base = AddrAdd(SegBase(seg), format->headerSize);
|
|
while(SegBuffer(seg) != NULL) { /* <design/poolamc/#seg-scan.loop> */
|
|
limit = AddrAdd(BufferScanLimit(SegBuffer(seg)), format->headerSize);
|
|
if (base >= limit) {
|
|
/* @@@@ Are we sure we don't need scan the rest of the segment? */
|
|
AVER(base == limit);
|
|
*totalReturn = TRUE;
|
|
return ResOK;
|
|
}
|
|
res = (*format->scan)(ss, base, limit);
|
|
if (res != ResOK) {
|
|
*totalReturn = FALSE;
|
|
return res;
|
|
}
|
|
ss->scannedSize += AddrOffset(base, limit);
|
|
base = limit;
|
|
}
|
|
|
|
/* <design/poolamc/#seg-scan.finish> @@@@ base? */
|
|
limit = AddrAdd(SegLimit(seg), format->headerSize);
|
|
AVER(SegBase(seg) <= base);
|
|
AVER(base <= AddrAdd(SegLimit(seg), format->headerSize));
|
|
if (base < limit) {
|
|
res = (*format->scan)(ss, base, limit);
|
|
if (res != ResOK) {
|
|
*totalReturn = FALSE;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
ss->scannedSize += AddrOffset(base, limit);
|
|
EVENT_PPP(AMCScanEnd, amc, seg, ss);
|
|
|
|
*totalReturn = TRUE;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* amcFixInPlace -- fix an reference without moving the object
|
|
*
|
|
* Usually this function is used for ambiguous references, but during
|
|
* emergency tracing may be used for references of any rank.
|
|
*
|
|
* If the segment has a nailboard then we use that to record the fix.
|
|
* Otherwise we simply grey and nail the entire segment.
|
|
*/
|
|
static void amcFixInPlace(Pool pool, Seg seg, ScanState ss, Ref *refIO)
|
|
{
|
|
Addr ref;
|
|
|
|
UNUSED(pool);
|
|
|
|
ref = (Addr)*refIO;
|
|
/* An ambiguous reference can point before the header. */
|
|
AVER(SegBase(seg) <= ref);
|
|
/* .ref-limit: A reference passed to Fix can't be beyond the segment, */
|
|
/* because then TraceFix would not have picked this segment. */
|
|
AVER(ref < SegLimit(seg));
|
|
|
|
EVENT_0(AMCFixInPlace);
|
|
if (amcSegHasNailboard(seg)) {
|
|
Bool wasMarked = amcNailGetAndSetMark(seg, ref);
|
|
/* If there are no new marks (i.e., no new traces for which we */
|
|
/* are marking, and no new mark bits set) then we can return */
|
|
/* immediately, without changing colour. */
|
|
if (TraceSetSub(ss->traces, SegNailed(seg)) && wasMarked)
|
|
return;
|
|
} else if (TraceSetSub(ss->traces, SegNailed(seg))) {
|
|
return;
|
|
}
|
|
SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces));
|
|
if (SegRankSet(seg) != RankSetEMPTY)
|
|
SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces));
|
|
}
|
|
|
|
|
|
/* AMCFixEmergency -- fix a reference, without allocating
|
|
*
|
|
* See <design/poolamc/#emergency.fix>.
|
|
*/
|
|
static Res AMCFixEmergency(Pool pool, ScanState ss, Seg seg, Ref *refIO)
|
|
{
|
|
Arena arena;
|
|
AMC amc;
|
|
Addr newRef;
|
|
|
|
AVERT(Pool, pool);
|
|
AVERT(ScanState, ss);
|
|
AVERT(Seg, seg);
|
|
AVER(refIO != NULL);
|
|
|
|
arena = PoolArena(pool);
|
|
AVERT(Arena, arena);
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
|
|
ss->wasMarked = TRUE;
|
|
|
|
if (ss->rank == RankAMBIG)
|
|
goto fixInPlace;
|
|
|
|
ShieldExpose(arena, seg);
|
|
newRef = (*pool->format->isMoved)(*refIO);
|
|
ShieldCover(arena, seg);
|
|
if (newRef != (Addr)0) {
|
|
/* Object has been forwarded already, so snap-out pointer. */
|
|
/* Useful weak pointer semantics not implemented. @@@@ */
|
|
*refIO = newRef;
|
|
return ResOK;
|
|
}
|
|
|
|
fixInPlace: /* see <design/poolamc/>.Nailboard.emergency */
|
|
amcFixInPlace(pool, seg, ss, refIO);
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* AMCFix -- fix a reference to the pool
|
|
*
|
|
* See <design/poolamc/#fix>.
|
|
*/
|
|
Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)
|
|
{
|
|
Arena arena;
|
|
AMC amc;
|
|
Res res;
|
|
Format format; /* cache of pool->format */
|
|
Ref ref; /* reference to be fixed */
|
|
Ref newRef; /* new location, if moved */
|
|
Size length; /* length of object to be relocated */
|
|
Buffer buffer; /* buffer to allocate new copy into */
|
|
amcGen gen; /* generation of old copy of object */
|
|
TraceSet grey; /* greyness of object being relocated */
|
|
TraceSet toGrey; /* greyness of object's destination */
|
|
RefSet summary; /* summary of object being relocated */
|
|
RefSet toSummary; /* summary of object's destination */
|
|
Seg toSeg; /* segment to which object is being relocated */
|
|
|
|
/* <design/trace/#fix.noaver> */
|
|
AVERT_CRITICAL(Pool, pool);
|
|
AVERT_CRITICAL(ScanState, ss);
|
|
AVERT_CRITICAL(Seg, seg);
|
|
AVER_CRITICAL(refIO != NULL);
|
|
EVENT_0(AMCFix);
|
|
|
|
/* For the moment, assume that the object was already marked. */
|
|
/* (See <design/fix/#protocol.was-marked>.) */
|
|
ss->wasMarked = TRUE;
|
|
|
|
/* If the reference is ambiguous, set up the datastructures for */
|
|
/* managing a nailed segment. This involves marking the segment */
|
|
/* as nailed, and setting up a per-word mark table */
|
|
if (ss->rank == RankAMBIG) {
|
|
/* .nail.new: Check to see whether we need a Nailboard for */
|
|
/* this seg. We use "SegNailed(seg) == TraceSetEMPTY" */
|
|
/* rather than "!amcSegHasNailboard(seg)" because this avoids */
|
|
/* setting up a new nailboard when the segment was nailed, but had */
|
|
/* no nailboard. This must be avoided because otherwise */
|
|
/* assumptions in AMCFixEmergency will be wrong (essentially */
|
|
/* we will lose some pointer fixes because we introduced a */
|
|
/* nailboard). */
|
|
if (SegNailed(seg) == TraceSetEMPTY) {
|
|
res = amcSegCreateNailboard(seg, pool);
|
|
if (res != ResOK)
|
|
return res;
|
|
++ss->nailCount;
|
|
SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces));
|
|
}
|
|
amcFixInPlace(pool, seg, ss, refIO);
|
|
return ResOK;
|
|
}
|
|
|
|
amc = Pool2AMC(pool);
|
|
AVERT_CRITICAL(AMC, amc);
|
|
format = pool->format;
|
|
ref = *refIO;
|
|
AVER_CRITICAL(SegBase(seg) <= ref);
|
|
AVER_CRITICAL(ref < SegLimit(seg));
|
|
arena = pool->arena;
|
|
|
|
ShieldExpose(arena, seg);
|
|
newRef = (*format->isMoved)(ref);
|
|
ShieldCover(arena, seg);
|
|
|
|
if (newRef == (Addr)0) {
|
|
/* If object is nailed already then we mustn't copy it: */
|
|
if (SegNailed(seg) != TraceSetEMPTY
|
|
&& (!amcSegHasNailboard(seg) || amcNailGetMark(seg, ref))) {
|
|
/* Segment only needs greying if there are new traces for which */
|
|
/* we are nailing. */
|
|
if (!TraceSetSub(ss->traces, SegNailed(seg))) {
|
|
if (SegRankSet(seg) != RankSetEMPTY)
|
|
SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces));
|
|
SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces));
|
|
}
|
|
res = ResOK;
|
|
goto returnRes;
|
|
} else if (ss->rank == RankWEAK) {
|
|
/* object is not preserved (neither moved, nor nailed) */
|
|
/* hence, reference should be splatted */
|
|
goto updateReference;
|
|
}
|
|
/* object is not preserved yet (neither moved, nor nailed) */
|
|
/* so should be preserved by forwarding */
|
|
EVENT_A(AMCFixForward, newRef);
|
|
/* <design/fix/#protocol.was-marked> */
|
|
ss->wasMarked = FALSE;
|
|
|
|
/* Get the forwarding buffer from the object's generation. */
|
|
gen = amcSegGen(seg);
|
|
buffer = gen->forward;
|
|
AVER_CRITICAL(buffer != NULL);
|
|
|
|
length = AddrOffset(ref, (*format->skip)(ref));
|
|
STATISTIC_STAT(++ss->forwardedCount);
|
|
ss->forwardedSize += length;
|
|
do {
|
|
res = BUFFER_RESERVE(&newRef, buffer, length, FALSE);
|
|
if (res != ResOK)
|
|
goto returnRes;
|
|
|
|
toSeg = BufferSeg(buffer);
|
|
ShieldExpose(arena, toSeg);
|
|
|
|
/* Since we're moving an object from one segment to another, */
|
|
/* union the greyness and the summaries together. */
|
|
grey = TraceSetUnion(ss->traces, SegGrey(seg));
|
|
toGrey = SegGrey(toSeg);
|
|
if (TraceSetDiff(grey, toGrey) != TraceSetEMPTY
|
|
&& SegRankSet(seg) != RankSetEMPTY)
|
|
SegSetGrey(toSeg, TraceSetUnion(toGrey, grey));
|
|
summary = SegSummary(seg);
|
|
toSummary = SegSummary(toSeg);
|
|
if (RefSetDiff(summary, toSummary) != RefSetEMPTY)
|
|
SegSetSummary(toSeg, RefSetUnion(toSummary, summary));
|
|
|
|
/* <design/trace/#fix.copy> */
|
|
(void)AddrCopy(newRef, ref, length);
|
|
|
|
ShieldCover(arena, toSeg);
|
|
} while (!BUFFER_COMMIT(buffer, newRef, length));
|
|
ss->copiedSize += length;
|
|
|
|
ShieldExpose(arena, seg);
|
|
(*format->move)(ref, newRef);
|
|
ShieldCover(arena, seg);
|
|
} else {
|
|
/* reference to broken heart (which should be snapped out -- */
|
|
/* consider adding to (non-existant) snap-out cache here) */
|
|
STATISTIC_STAT(++ss->snapCount);
|
|
}
|
|
|
|
/* .fix.update: update the reference to whatever the above code */
|
|
/* decided it should be */
|
|
updateReference:
|
|
*refIO = newRef;
|
|
res = ResOK;
|
|
|
|
returnRes:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* AMCHeaderFix -- fix a reference to the pool, with headers
|
|
*
|
|
* See <design/poolamc/#header.fix>.
|
|
*/
|
|
static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)
|
|
{
|
|
Arena arena;
|
|
AMC amc;
|
|
Res res;
|
|
Format format; /* cache of pool->format */
|
|
Ref ref; /* reference to be fixed */
|
|
Ref newRef; /* new location, if moved */
|
|
Addr newBase; /* base address of new copy */
|
|
Size length; /* length of object to be relocated */
|
|
Buffer buffer; /* buffer to allocate new copy into */
|
|
amcGen gen; /* generation of old copy of object */
|
|
TraceSet grey; /* greyness of object being relocated */
|
|
TraceSet toGrey; /* greyness of object's destination */
|
|
RefSet summary; /* summary of object being relocated */
|
|
RefSet toSummary; /* summary of object's destination */
|
|
Seg toSeg; /* segment to which object is being relocated */
|
|
|
|
/* <design/trace/#fix.noaver> */
|
|
AVERT_CRITICAL(Pool, pool);
|
|
AVERT_CRITICAL(ScanState, ss);
|
|
AVERT_CRITICAL(Seg, seg);
|
|
AVER_CRITICAL(refIO != NULL);
|
|
EVENT_0(AMCFix);
|
|
|
|
/* For the moment, assume that the object was already marked. */
|
|
/* (See <design/fix/#protocol.was-marked>.) */
|
|
ss->wasMarked = TRUE;
|
|
|
|
/* If the reference is ambiguous, set up the datastructures for */
|
|
/* managing a nailed segment. This involves marking the segment */
|
|
/* as nailed, and setting up a per-word mark table */
|
|
if (ss->rank == RankAMBIG) {
|
|
/* .nail.new: Check to see whether we need a Nailboard for this seg. */
|
|
/* We use "SegNailed(seg) == TraceSetEMPTY" rather than */
|
|
/* "!amcSegHasNailboard(seg)" because this avoids setting up a new */
|
|
/* nailboard when the segment was nailed, but had no nailboard. */
|
|
/* This must be avoided because otherwise assumptions in */
|
|
/* AMCFixEmergency will be wrong (essentially we will lose some */
|
|
/* pointer fixes because we introduced a nailboard). */
|
|
if (SegNailed(seg) == TraceSetEMPTY) {
|
|
res = amcSegCreateNailboard(seg, pool);
|
|
if (res != ResOK)
|
|
return res;
|
|
++ss->nailCount;
|
|
SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces));
|
|
}
|
|
amcFixInPlace(pool, seg, ss, refIO);
|
|
return ResOK;
|
|
}
|
|
|
|
amc = Pool2AMC(pool);
|
|
AVERT_CRITICAL(AMC, amc);
|
|
format = pool->format;
|
|
ref = *refIO;
|
|
AVER_CRITICAL(AddrAdd(SegBase(seg), pool->format->headerSize) <= ref);
|
|
AVER_CRITICAL(ref < SegLimit(seg)); /* see .ref-limit */
|
|
arena = pool->arena;
|
|
|
|
ShieldExpose(arena, seg);
|
|
newRef = (*format->isMoved)(ref);
|
|
ShieldCover(arena, seg);
|
|
|
|
if (newRef == (Addr)0) {
|
|
/* If object is nailed already then we mustn't copy it: */
|
|
if (SegNailed(seg) != TraceSetEMPTY
|
|
&& (!amcSegHasNailboard(seg) || amcNailGetMark(seg, ref))) {
|
|
/* Segment only needs greying if there are new traces for which */
|
|
/* we are nailing. */
|
|
if (!TraceSetSub(ss->traces, SegNailed(seg))) {
|
|
if (SegRankSet(seg) != RankSetEMPTY)
|
|
SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces));
|
|
SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces));
|
|
}
|
|
res = ResOK;
|
|
goto returnRes;
|
|
} else if (ss->rank == RankWEAK) {
|
|
/* object is not preserved (neither moved, nor nailed) */
|
|
/* hence, reference should be splatted */
|
|
goto updateReference;
|
|
}
|
|
/* object is not preserved yet (neither moved, nor nailed) */
|
|
/* so should be preserved by forwarding */
|
|
EVENT_A(AMCFixForward, newRef);
|
|
/* <design/fix/#protocol.was-marked> */
|
|
ss->wasMarked = FALSE;
|
|
|
|
/* Get the forwarding buffer from the object's generation. */
|
|
gen = amcSegGen(seg);
|
|
buffer = gen->forward;
|
|
AVER_CRITICAL(buffer != NULL);
|
|
|
|
length = AddrOffset(ref, (*format->skip)(ref));
|
|
STATISTIC_STAT(++ss->forwardedCount);
|
|
ss->forwardedSize += length;
|
|
do {
|
|
Size headerSize = format->headerSize;
|
|
|
|
res = BUFFER_RESERVE(&newBase, buffer, length, FALSE);
|
|
if (res != ResOK)
|
|
goto returnRes;
|
|
newRef = AddrAdd(newBase, headerSize);
|
|
|
|
toSeg = BufferSeg(buffer);
|
|
ShieldExpose(arena, toSeg);
|
|
|
|
/* Since we're moving an object from one segment to another, */
|
|
/* union the greyness and the summaries together. */
|
|
grey = TraceSetUnion(ss->traces, SegGrey(seg));
|
|
toGrey = SegGrey(toSeg);
|
|
if (TraceSetDiff(grey, toGrey) != TraceSetEMPTY
|
|
&& SegRankSet(seg) != RankSetEMPTY)
|
|
SegSetGrey(toSeg, TraceSetUnion(toGrey, grey));
|
|
summary = SegSummary(seg);
|
|
toSummary = SegSummary(toSeg);
|
|
if (RefSetDiff(summary, toSummary) != RefSetEMPTY)
|
|
SegSetSummary(toSeg, RefSetUnion(toSummary, summary));
|
|
|
|
/* <design/trace/#fix.copy> */
|
|
(void)AddrCopy(newBase, AddrSub(ref, headerSize), length);
|
|
|
|
ShieldCover(arena, toSeg);
|
|
} while (!BUFFER_COMMIT(buffer, newBase, length));
|
|
ss->copiedSize += length;
|
|
|
|
ShieldExpose(arena, seg);
|
|
(*format->move)(ref, newRef);
|
|
ShieldCover(arena, seg);
|
|
} else {
|
|
/* reference to broken heart (which should be snapped out -- */
|
|
/* consider adding to (non-existent) snap-out cache here) */
|
|
STATISTIC_STAT(++ss->snapCount);
|
|
}
|
|
|
|
/* .fix.update: update the reference to whatever the above code */
|
|
/* decided it should be */
|
|
updateReference:
|
|
*refIO = newRef;
|
|
res = ResOK;
|
|
|
|
returnRes:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* amcReclaimNailed -- reclaim what you can from a nailed segment */
|
|
|
|
static void amcReclaimNailed(Pool pool, Trace trace, Seg seg)
|
|
{
|
|
Addr p, limit;
|
|
Arena arena;
|
|
Format format;
|
|
Size bytesReclaimed = (Size)0;
|
|
Count preservedInPlaceCount = (Count)0;
|
|
Size preservedInPlaceSize = (Size)0;
|
|
AMC amc;
|
|
Size headerSize;
|
|
|
|
/* All arguments AVERed by AMCReclaim */
|
|
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
format = pool->format;
|
|
|
|
arena = PoolArena(pool);
|
|
AVERT(Arena, arena);
|
|
|
|
/* see <design/poolamc/#nailboard.limitations> for improvements */
|
|
headerSize = format->headerSize;
|
|
ShieldExpose(arena, seg);
|
|
p = AddrAdd(SegBase(seg), headerSize);
|
|
if (SegBuffer(seg) != NULL)
|
|
limit = BufferScanLimit(SegBuffer(seg));
|
|
else
|
|
limit = SegLimit(seg);
|
|
limit = AddrAdd(limit, headerSize);
|
|
while(p < limit) {
|
|
Addr q;
|
|
Size length;
|
|
q = (*format->skip)(p);
|
|
length = AddrOffset(p, q);
|
|
if (amcSegHasNailboard(seg)
|
|
? !amcNailGetMark(seg, p)
|
|
/* If there's no mark table, retain all that hasn't been forwarded. In
|
|
* this case, preservedInPlace* become somewhat overstated. */
|
|
: (*format->isMoved)(p) != NULL) {
|
|
(*format->pad)(AddrSub(p, headerSize), length);
|
|
bytesReclaimed += length;
|
|
} else {
|
|
++preservedInPlaceCount;
|
|
preservedInPlaceSize += length;
|
|
}
|
|
|
|
AVER(p < q);
|
|
p = q;
|
|
}
|
|
AVER(p == limit);
|
|
ShieldCover(arena, seg);
|
|
|
|
SegSetNailed(seg, TraceSetDel(SegNailed(seg), trace));
|
|
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
|
|
if (SegNailed(seg) == TraceSetEMPTY && amcSegHasNailboard(seg)) {
|
|
amcSegDestroyNailboard(seg, pool);
|
|
}
|
|
|
|
AVER(bytesReclaimed <= SegSize(seg));
|
|
trace->reclaimSize += bytesReclaimed;
|
|
trace->preservedInPlaceCount += preservedInPlaceCount;
|
|
trace->preservedInPlaceSize += preservedInPlaceSize;
|
|
}
|
|
|
|
|
|
/* AMCReclaim -- recycle a segment if it is still white
|
|
*
|
|
* See <design/poolamc/#reclaim>.
|
|
*/
|
|
static void AMCReclaim(Pool pool, Trace trace, Seg seg)
|
|
{
|
|
AMC amc;
|
|
amcGen gen;
|
|
Size size;
|
|
|
|
AVERT_CRITICAL(Pool, pool);
|
|
amc = Pool2AMC(pool);
|
|
AVERT_CRITICAL(AMC, amc);
|
|
AVERT_CRITICAL(Trace, trace);
|
|
AVERT_CRITICAL(Seg, seg);
|
|
|
|
gen = amcSegGen(seg);
|
|
AVERT_CRITICAL(amcGen, gen);
|
|
|
|
EVENT_PPP(AMCReclaim, gen, trace, seg);
|
|
|
|
/* This switching needs to be more complex for multiple traces. */
|
|
AVER_CRITICAL(TraceSetIsSingle(PoolArena(pool)->busyTraces));
|
|
if (amc->rampMode == collectingRamp) {
|
|
if (amc->rampCount > 0)
|
|
/* Entered ramp mode before previous one was cleaned up */
|
|
amc->rampMode = beginRamp;
|
|
else
|
|
amc->rampMode = outsideRamp;
|
|
}
|
|
|
|
if (SegNailed(seg) != TraceSetEMPTY) {
|
|
amcReclaimNailed(pool, trace, seg);
|
|
return;
|
|
}
|
|
|
|
--gen->segs;
|
|
size = SegSize(seg);
|
|
gen->pgen.totalSize -= size;
|
|
|
|
trace->reclaimSize += size;
|
|
|
|
SegFree(seg);
|
|
}
|
|
|
|
|
|
/* AMCWalk -- Apply function to (black) objects in segment */
|
|
|
|
static void AMCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f,
|
|
void *p, unsigned long s)
|
|
{
|
|
Addr object, nextObject, limit;
|
|
AMC amc;
|
|
Format format;
|
|
|
|
AVERT(Pool, pool);
|
|
AVERT(Seg, seg);
|
|
AVER(FUNCHECK(f));
|
|
/* p and s are arbitrary closures so can't be checked */
|
|
|
|
/* Avoid applying the function to grey or white objects. */
|
|
/* White objects might not be alive, and grey objects */
|
|
/* may have pointers to old-space. */
|
|
|
|
/* NB, segments containing a mix of colours (i.e., nailed segs) */
|
|
/* are not handled properly: No objects are walked @@@@ */
|
|
if (SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY
|
|
&& SegNailed(seg) == TraceSetEMPTY) {
|
|
amc = Pool2AMC(pool);
|
|
AVERT(AMC, amc);
|
|
format = pool->format;
|
|
|
|
/* If the segment is buffered, only walk as far as the end */
|
|
/* of the initialized objects. cf. AMCScan */
|
|
if (SegBuffer(seg) != NULL)
|
|
limit = BufferScanLimit(SegBuffer(seg));
|
|
else
|
|
limit = SegLimit(seg);
|
|
limit = AddrAdd(limit, format->headerSize);
|
|
|
|
object = AddrAdd(SegBase(seg), format->headerSize);
|
|
while(object < limit) {
|
|
/* Check not a broken heart. */
|
|
AVER((*format->isMoved)(object) == NULL);
|
|
(*f)(object, pool->format, pool, p, s);
|
|
nextObject = (*pool->format->skip)(object);
|
|
AVER(nextObject > object);
|
|
object = nextObject;
|
|
}
|
|
AVER(object == limit);
|
|
}
|
|
}
|
|
|
|
|
|
/* amcWalkAll -- Apply a function to all (black) objects in a pool */
|
|
|
|
static void amcWalkAll(Pool pool, FormattedObjectsStepMethod f,
|
|
void *p, unsigned long s)
|
|
{
|
|
Arena arena;
|
|
Ring ring, next, node;
|
|
|
|
AVER(IsSubclassPoly(pool->class, AMCPoolClassGet()));
|
|
|
|
arena = PoolArena(pool);
|
|
ring = PoolSegRing(pool);
|
|
node = RingNext(ring);
|
|
RING_FOR(node, ring, next) {
|
|
Seg seg = SegOfPoolRing(node);
|
|
|
|
ShieldExpose(arena, seg);
|
|
AMCWalk(pool, seg, f, p, s);
|
|
ShieldCover(arena, seg);
|
|
}
|
|
}
|
|
|
|
|
|
/* AMCDescribe -- describe the contents of the AMC pool
|
|
*
|
|
* See <design/poolamc/#describe>.
|
|
*/
|
|
static Res AMCDescribe(Pool pool, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
AMC amc;
|
|
Ring node, nextNode;
|
|
char *rampmode;
|
|
|
|
if (!CHECKT(Pool, pool)) return ResFAIL;
|
|
amc = Pool2AMC(pool);
|
|
if (!CHECKT(AMC, amc)) return ResFAIL;
|
|
if (stream == NULL) return ResFAIL;
|
|
|
|
res = WriteF(stream,
|
|
(amc->rankSet == RankSetEMPTY) ? "AMCZ" : "AMC",
|
|
" $P {\n", (WriteFP)amc, " pool $P ($U)\n",
|
|
(WriteFP)AMC2Pool(amc), (WriteFU)AMC2Pool(amc)->serial,
|
|
NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
switch(amc->rampMode) {
|
|
case outsideRamp: rampmode = "outside ramp"; break;
|
|
case beginRamp: rampmode = "begin ramp"; break;
|
|
case ramping: rampmode = "ramping"; break;
|
|
case finishRamp: rampmode = "finish ramp"; break;
|
|
case collectingRamp: rampmode = "collecting ramp"; break;
|
|
default: rampmode = "unknown ramp mode"; break;
|
|
}
|
|
res = WriteF(stream,
|
|
" ", rampmode, " ($U)\n", (WriteFU)amc->rampCount,
|
|
NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
RING_FOR(node, &amc->genRing, nextNode) {
|
|
amcGen gen = RING_ELT(amcGen, amcRing, node);
|
|
res = amcGenDescribe(gen, stream);
|
|
if (res != ResOK) return res;
|
|
}
|
|
|
|
res = WriteF(stream, "} AMC $P\n", (WriteFP)amc, NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* AMCPoolClass -- the class definition */
|
|
|
|
DEFINE_POOL_CLASS(AMCPoolClass, this)
|
|
{
|
|
INHERIT_CLASS(this, AbstractCollectPoolClass);
|
|
PoolClassMixInFormat(this);
|
|
this->name = "AMC";
|
|
this->size = sizeof(AMCStruct);
|
|
this->offset = offsetof(AMCStruct, poolStruct);
|
|
this->attr |= AttrMOVINGGC;
|
|
this->init = AMCInit;
|
|
this->finish = AMCFinish;
|
|
this->bufferFill = AMCBufferFill;
|
|
this->bufferEmpty = AMCBufferEmpty;
|
|
this->whiten = AMCWhiten;
|
|
this->scan = AMCScan;
|
|
this->fix = AMCFix;
|
|
this->fixEmergency = AMCFixEmergency;
|
|
this->reclaim = AMCReclaim;
|
|
this->rampBegin = AMCRampBegin;
|
|
this->rampEnd = AMCRampEnd;
|
|
this->walk = AMCWalk;
|
|
this->bufferClass = amcBufClassGet;
|
|
this->describe = AMCDescribe;
|
|
}
|
|
|
|
|
|
/* AMCZPoolClass -- the class definition */
|
|
|
|
DEFINE_POOL_CLASS(AMCZPoolClass, this)
|
|
{
|
|
INHERIT_CLASS(this, AMCPoolClass);
|
|
this->name = "AMCZ";
|
|
this->attr &= ~(AttrSCAN | AttrINCR_RB);
|
|
this->init = AMCZInit;
|
|
this->grey = PoolNoGrey;
|
|
this->scan = PoolNoScan;
|
|
}
|
|
|
|
|
|
/* mps_class_amc -- return the pool class descriptor to the client */
|
|
|
|
mps_class_t mps_class_amc(void)
|
|
{
|
|
return (mps_class_t)AMCPoolClassGet();
|
|
}
|
|
|
|
/* mps_class_amcz -- return the pool class descriptor to the client */
|
|
|
|
mps_class_t mps_class_amcz(void)
|
|
{
|
|
return (mps_class_t)AMCZPoolClassGet();
|
|
}
|
|
|
|
|
|
/* mps_amc_apply -- apply function to all objects in pool
|
|
*
|
|
* The iterator that is passed by the client is stored in a closure
|
|
* structure which is passed to a local iterator in order to ensure that
|
|
* any type conversion necessary between Addr and mps_addr_t happen.
|
|
* They are almost certainly the same on all platforms, but this is the
|
|
* correct way to do it.
|
|
*/
|
|
|
|
typedef struct mps_amc_apply_closure_s {
|
|
void (*f)(mps_addr_t object, void *p, size_t s);
|
|
void *p;
|
|
size_t s;
|
|
} mps_amc_apply_closure_s;
|
|
|
|
static void mps_amc_apply_iter(Addr addr, Format format, Pool pool,
|
|
void *p, unsigned long s)
|
|
{
|
|
mps_amc_apply_closure_s *closure = p;
|
|
/* Can't check addr */
|
|
AVERT(Format, format);
|
|
AVERT(Pool, pool);
|
|
/* We could check that s is the sizeof *p, but it would be slow */
|
|
UNUSED(format);
|
|
UNUSED(pool);
|
|
UNUSED(s);
|
|
(*closure->f)(addr, closure->p, closure->s);
|
|
}
|
|
|
|
void mps_amc_apply(mps_pool_t mps_pool,
|
|
void (*f)(mps_addr_t object, void *p, size_t s),
|
|
void *p, size_t s)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
mps_amc_apply_closure_s closure_s;
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
ArenaEnter(arena);
|
|
AVERT(Pool, pool);
|
|
|
|
closure_s.f = f; closure_s.p = p; closure_s.s = s;
|
|
amcWalkAll(pool, mps_amc_apply_iter, &closure_s, sizeof(closure_s));
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* AMCCheck -- check consistency of the AMC pool
|
|
*
|
|
* See <design/poolamc/#check>.
|
|
*/
|
|
static Bool AMCCheck(AMC amc)
|
|
{
|
|
CHECKS(AMC, amc);
|
|
CHECKD(Pool, &amc->poolStruct);
|
|
CHECKL(IsSubclassPoly(amc->poolStruct.class, EnsureAMCPoolClass()));
|
|
CHECKL(RankSetCheck(amc->rankSet));
|
|
CHECKL(RingCheck(&amc->genRing));
|
|
CHECKL(BoolCheck(amc->gensBooted));
|
|
if (amc->gensBooted) {
|
|
CHECKD(amcGen, amc->nursery);
|
|
CHECKL(amc->gen != NULL);
|
|
CHECKD(amcGen, amc->rampGen);
|
|
CHECKD(amcGen, amc->afterRampGen);
|
|
}
|
|
/* nothing to check for rampCount */
|
|
CHECKL(amc->rampMode >= outsideRamp);
|
|
CHECKL(amc->rampMode <= collectingRamp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* All rights reserved. This is an open source license. Contact
|
|
* Ravenbrook for commercial licensing options.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for this software and any accompanying
|
|
* software that uses this software. The source code must either be
|
|
* included in the distribution or be available for no more than the cost
|
|
* of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source
|
|
* code means the source code for all modules it contains. It does not
|
|
* include source code for modules or files that typically accompany the
|
|
* major components of the operating system on which the executable file
|
|
* runs.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|