mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-17 16:41:17 -08:00
563 lines
14 KiB
C
563 lines
14 KiB
C
/* impl.c.root: ROOT IMPLEMENTATION
|
|
*
|
|
* $HopeName: MMsrc!root.c(trunk.26) $
|
|
* Copyright (C) 1997 The Harlequin Group Limited. All rights reserved.
|
|
*
|
|
* .scope: This is the implementation of the root datatype.
|
|
* .design: For design, see design.mps.root and
|
|
* design.mps.root-interface
|
|
*/
|
|
|
|
#include "mpm.h"
|
|
|
|
SRCID(root, "$HopeName: MMsrc!root.c(trunk.26) $");
|
|
|
|
|
|
/* RootVarCheck -- check a Root union discriminator
|
|
*
|
|
* .rootvarcheck: Synchronize with impl.h.mpmtypes.rootvar
|
|
*/
|
|
|
|
Bool RootVarCheck(RootVar rootVar)
|
|
{
|
|
AVER(rootVar == RootTABLE ||
|
|
rootVar == RootTABLE_MASKED ||
|
|
rootVar == RootFUN ||
|
|
rootVar == RootFMT ||
|
|
rootVar == RootREG);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* RootModeCheck */
|
|
|
|
Bool RootModeCheck(RootMode mode)
|
|
{
|
|
AVER((mode & (RootModeCONSTANT |
|
|
RootModePROTECTABLE |
|
|
RootModePROTECTABLE_INNER)) == mode);
|
|
/* RootModePROTECTABLE_INNER implies RootModePROTECTABLE */
|
|
AVER((mode & RootModePROTECTABLE_INNER) == 0 ||
|
|
(mode & RootModePROTECTABLE));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* RootCheck -- check the consistency of a root structure
|
|
*
|
|
* .rootcheck: Keep synchonized with impl.h.mpmst.root
|
|
*/
|
|
|
|
Bool RootCheck(Root root)
|
|
{
|
|
CHECKS(Root, root);
|
|
CHECKU(Arena, root->arena);
|
|
CHECKL(root->serial < root->arena->rootSerial);
|
|
CHECKL(RingCheck(&root->arenaRing));
|
|
CHECKL(RankCheck(root->rank));
|
|
CHECKL(TraceSetCheck(root->grey));
|
|
/* Don't need to check var here, because of the switch below */
|
|
switch(root->var)
|
|
{
|
|
case RootTABLE:
|
|
CHECKL(root->the.table.base != 0);
|
|
CHECKL(root->the.table.base < root->the.table.limit);
|
|
break;
|
|
|
|
case RootTABLE_MASKED:
|
|
CHECKL(root->the.tableMasked.base != 0);
|
|
CHECKL(root->the.tableMasked.base < root->the.tableMasked.limit);
|
|
/* Can't check anything about the mask. */
|
|
break;
|
|
|
|
case RootFUN:
|
|
CHECKL(root->the.fun.scan != NULL);
|
|
break;
|
|
|
|
case RootREG:
|
|
CHECKL(root->the.reg.scan != NULL);
|
|
CHECKD(Thread, root->the.reg.thread);
|
|
break;
|
|
|
|
case RootFMT:
|
|
CHECKL(root->the.fmt.scan != NULL);
|
|
CHECKL(root->the.fmt.base != 0);
|
|
CHECKL(root->the.fmt.base < root->the.fmt.limit);
|
|
break;
|
|
|
|
default:
|
|
NOTREACHED;
|
|
}
|
|
CHECKL(RootModeCheck(root->mode));
|
|
CHECKL(BoolCheck(root->protectable));
|
|
if(root->protectable) {
|
|
CHECKL(root->protBase != NULL);
|
|
CHECKL(root->protLimit != NULL);
|
|
CHECKL(root->protBase < root->protLimit);
|
|
/* there is no AccessSetCheck */
|
|
} else {
|
|
CHECKL(root->protBase == NULL);
|
|
CHECKL(root->protLimit == NULL);
|
|
CHECKL(root->pm == (AccessSet)0);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* .create: create, RootCreateTable, RootCreateReg, RootCreateFmt,
|
|
* RootCreateFun:
|
|
* RootCreate* set up the appropriate union member, and call the generic
|
|
* create function to do the actual creation
|
|
*
|
|
* See design.mps.root.init for initial value
|
|
*/
|
|
|
|
static Res rootCreate(Root *rootReturn, Arena arena,
|
|
Rank rank, RootMode mode, RootVar type,
|
|
union RootUnion *theUnionP)
|
|
{
|
|
Root root;
|
|
Res res;
|
|
void *p;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVERT(Rank, rank);
|
|
AVERT(RootVar, type);
|
|
|
|
res = ArenaAlloc(&p, arena, sizeof(RootStruct));
|
|
if(res != ResOK)
|
|
return res;
|
|
root = (Root)p; /* Avoid pun */
|
|
|
|
root->arena = arena;
|
|
root->rank = rank;
|
|
root->var = type;
|
|
root->the = *theUnionP;
|
|
root->grey = TraceSetEMPTY;
|
|
root->summary = RefSetUNIV;
|
|
root->mode = mode;
|
|
root->pm = AccessSetEMPTY;
|
|
root->protectable = FALSE;
|
|
root->protBase = 0;
|
|
root->protLimit = 0;
|
|
|
|
/* See design.mps.arena.root-ring */
|
|
RingInit(&root->arenaRing);
|
|
|
|
root->serial = arena->rootSerial;
|
|
++arena->rootSerial;
|
|
root->sig = RootSig;
|
|
|
|
AVERT(Root, root);
|
|
|
|
RingAppend(ArenaRootRing(arena), &root->arenaRing);
|
|
|
|
*rootReturn = root;
|
|
return ResOK;
|
|
}
|
|
|
|
static Res rootCreateProtectable(Root *rootReturn, Arena arena,
|
|
Rank rank, RootMode mode,
|
|
RootVar var,
|
|
Addr base, Addr limit,
|
|
union RootUnion *theUnion)
|
|
{
|
|
Res res;
|
|
Root root;
|
|
Ring node, next;
|
|
|
|
res = rootCreate(&root, arena, rank, mode, var, theUnion);
|
|
if(res != ResOK) {
|
|
return res;
|
|
}
|
|
if(mode & RootModePROTECTABLE) {
|
|
root->protectable = TRUE;
|
|
if(mode & RootModePROTECTABLE_INNER) {
|
|
root->protBase = AddrAlignUp(base, ArenaAlign(arena));
|
|
root->protLimit = AddrAlignDown(limit, ArenaAlign(arena));
|
|
if(!(root->protBase < root->protLimit)) {
|
|
/* root had no inner pages */
|
|
root->protectable = FALSE;
|
|
root->mode &=~ (RootModePROTECTABLE|RootModePROTECTABLE_INNER);
|
|
}
|
|
} else {
|
|
root->protBase = AddrAlignDown(base, ArenaAlign(arena));
|
|
root->protLimit = AddrAlignUp(limit, ArenaAlign(arena));
|
|
}
|
|
}
|
|
|
|
/* Check that this root doesn't intersect with any other root */
|
|
RING_FOR(node, &arena->rootRing, next) {
|
|
Root trial = RING_ELT(Root, arenaRing, node);
|
|
if(trial != root) {
|
|
/* (trial->protLimit <= root->protBase || */
|
|
/* root->protLimit <= trial->protBase) */
|
|
/* is the "okay" state. The negation of this is: */
|
|
if(root->protBase < trial->protLimit &&
|
|
trial->protBase < root->protLimit) {
|
|
NOTREACHED;
|
|
RootDestroy(root);
|
|
return ResFAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
AVERT(Root, root);
|
|
|
|
*rootReturn = root;
|
|
return ResOK;
|
|
}
|
|
|
|
Res RootCreateTable(Root *rootReturn, Arena arena,
|
|
Rank rank, RootMode mode, Addr *base, Addr *limit)
|
|
{
|
|
Res res;
|
|
union RootUnion theUnion;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(RankCheck(rank));
|
|
AVER(base != 0);
|
|
AVER(base < limit);
|
|
|
|
theUnion.table.base = base;
|
|
theUnion.table.limit = limit;
|
|
|
|
res = rootCreateProtectable(rootReturn, arena, rank, mode,
|
|
RootTABLE, (Addr)base, (Addr)limit, &theUnion);
|
|
return res;
|
|
}
|
|
|
|
Res RootCreateTableMasked(Root *rootReturn, Arena arena,
|
|
Rank rank, RootMode mode,
|
|
Addr *base, Addr *limit,
|
|
Word mask)
|
|
{
|
|
union RootUnion theUnion;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(RankCheck(rank));
|
|
AVER(base != 0);
|
|
AVER(base < limit);
|
|
/* Can't check anything about mask. */
|
|
|
|
theUnion.tableMasked.base = base;
|
|
theUnion.tableMasked.limit = limit;
|
|
theUnion.tableMasked.mask = mask;
|
|
|
|
return rootCreateProtectable(rootReturn, arena, rank, mode,
|
|
RootTABLE_MASKED,
|
|
(Addr)base, (Addr)limit, &theUnion);
|
|
}
|
|
|
|
Res RootCreateReg(Root *rootReturn, Arena arena,
|
|
Rank rank, Thread thread,
|
|
RootScanRegMethod scan, void *p, size_t s)
|
|
{
|
|
union RootUnion theUnion;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(RankCheck(rank));
|
|
AVERT(Thread, thread);
|
|
AVER(scan != NULL);
|
|
|
|
theUnion.reg.scan = scan;
|
|
theUnion.reg.thread = thread;
|
|
theUnion.reg.p = p;
|
|
theUnion.reg.s = s;
|
|
|
|
return rootCreate(rootReturn, arena, rank, (RootMode)0, RootREG, &theUnion);
|
|
}
|
|
|
|
Res RootCreateFmt(Root *rootReturn, Arena arena,
|
|
Rank rank, RootMode mode, FormatScanMethod scan,
|
|
Addr base, Addr limit)
|
|
{
|
|
union RootUnion theUnion;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(RankCheck(rank));
|
|
AVER(FUNCHECK(scan));
|
|
AVER(base != 0);
|
|
AVER(base < limit);
|
|
|
|
theUnion.fmt.scan = scan;
|
|
theUnion.fmt.base = base;
|
|
theUnion.fmt.limit = limit;
|
|
|
|
return rootCreateProtectable(rootReturn, arena, rank, mode,
|
|
RootFMT, base, limit, &theUnion);
|
|
}
|
|
|
|
Res RootCreateFun(Root *rootReturn, Arena arena,
|
|
Rank rank,
|
|
RootScanMethod scan,
|
|
void *p, size_t s)
|
|
{
|
|
union RootUnion theUnion;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(RankCheck(rank));
|
|
AVER(FUNCHECK(scan));
|
|
|
|
theUnion.fun.scan = scan;
|
|
theUnion.fun.p = p;
|
|
theUnion.fun.s = s;
|
|
|
|
return rootCreate(rootReturn, arena, rank, (RootMode)0, RootFUN, &theUnion);
|
|
}
|
|
|
|
void RootDestroy(Root root)
|
|
{
|
|
Arena arena;
|
|
|
|
AVERT(Root, root);
|
|
|
|
arena = RootArena(root);
|
|
|
|
AVERT(Arena, arena);
|
|
|
|
RingRemove(&root->arenaRing);
|
|
RingFinish(&root->arenaRing);
|
|
|
|
root->sig = SigInvalid;
|
|
|
|
ArenaFree(arena, root, sizeof(RootStruct));
|
|
}
|
|
|
|
Rank RootRank(Root root)
|
|
{
|
|
AVERT(Root, root);
|
|
return root->rank;
|
|
}
|
|
|
|
void RootGrey(Root root, Trace trace)
|
|
{
|
|
AVERT(Root, root);
|
|
AVERT(Trace, trace);
|
|
|
|
root->grey = TraceSetAdd(root->grey, trace->ti);
|
|
}
|
|
|
|
static void rootSetSummary(Root root, RefSet summary)
|
|
{
|
|
AVERT(Root, root);
|
|
/* Can't check summary */
|
|
if(root->protectable) {
|
|
if(summary == RefSetUNIV) {
|
|
root->summary = summary;
|
|
root->pm &= ~AccessWRITE;
|
|
} else {
|
|
root->pm |= AccessWRITE;
|
|
root->summary = summary;
|
|
}
|
|
} else
|
|
AVER(root->summary == RefSetUNIV);
|
|
}
|
|
|
|
Res RootScan(ScanState ss, Root root)
|
|
{
|
|
Res res;
|
|
|
|
AVERT(Root, root);
|
|
AVERT(ScanState, ss);
|
|
AVER(root->rank == ss->rank);
|
|
|
|
if(TraceSetInter(root->grey, ss->traces) == TraceSetEMPTY)
|
|
return ResOK;
|
|
|
|
AVER(ScanStateSummary(ss) == RefSetEMPTY);
|
|
|
|
if(RootPM(root) != AccessSetEMPTY) {
|
|
ProtSet(root->protBase, root->protLimit, AccessSetEMPTY);
|
|
}
|
|
|
|
switch(root->var) {
|
|
case RootTABLE:
|
|
res = TraceScanArea(ss, root->the.table.base, root->the.table.limit);
|
|
if(res != ResOK)
|
|
goto failScan;
|
|
break;
|
|
|
|
case RootTABLE_MASKED:
|
|
res = TraceScanAreaMasked(ss,
|
|
root->the.tableMasked.base,
|
|
root->the.tableMasked.limit,
|
|
root->the.tableMasked.mask);
|
|
if(res != ResOK)
|
|
goto failScan;
|
|
break;
|
|
|
|
case RootFUN:
|
|
res = (*root->the.fun.scan)(ss, root->the.fun.p, root->the.fun.s);
|
|
if(res != ResOK)
|
|
goto failScan;
|
|
break;
|
|
|
|
case RootREG:
|
|
res = (*root->the.reg.scan)(ss, root->the.reg.thread,
|
|
root->the.reg.p, root->the.reg.s);
|
|
if(res != ResOK)
|
|
goto failScan;
|
|
break;
|
|
|
|
case RootFMT:
|
|
res = (*root->the.fmt.scan)(ss, root->the.fmt.base,
|
|
root->the.fmt.limit);
|
|
if(res != ResOK)
|
|
goto failScan;
|
|
break;
|
|
|
|
default:
|
|
NOTREACHED;
|
|
res = ResUNIMPL;
|
|
goto failScan;
|
|
}
|
|
|
|
AVER(res == ResOK);
|
|
root->grey = TraceSetDiff(root->grey, ss->traces);
|
|
rootSetSummary(root, ScanStateSummary(ss));
|
|
EVENT_PWW(RootScan, root, ss->traces, ScanStateSummary(ss));
|
|
|
|
failScan:
|
|
if(RootPM(root) != AccessSetEMPTY) {
|
|
ProtSet(root->protBase, root->protLimit, RootPM(root));
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Must be thread-safe. See design.mps.interface.c.thread-safety. */
|
|
Arena RootArena(Root root)
|
|
{
|
|
/* Can't AVER root as that would not be thread-safe */
|
|
/* AVERT(Root, root); */
|
|
return root->arena;
|
|
}
|
|
|
|
/* return TRUE if the addr is in a root (and the root is in *rootReturn) */
|
|
/* otherwise returns FALSE */
|
|
Bool RootOfAddr(Root *rootReturn, Arena arena, Addr addr)
|
|
{
|
|
Ring node, next;
|
|
|
|
AVER(rootReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
/* addr is arbitrary and can't be checked */
|
|
|
|
RING_FOR(node, &arena->rootRing, next) {
|
|
Root root = RING_ELT(Root, arenaRing, node);
|
|
|
|
if(root->protectable &&
|
|
root->protBase <= addr && addr < root->protLimit) {
|
|
*rootReturn = root;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void RootAccess(Root root, AccessSet mode)
|
|
{
|
|
AVERT(Root, root);
|
|
/* Can't AVERT mode. */
|
|
AVER((root->pm & mode) != AccessSetEMPTY);
|
|
AVER(mode == AccessWRITE); /* only write protection supported */
|
|
|
|
rootSetSummary(root, RefSetUNIV);
|
|
|
|
/* Access must now be allowed. */
|
|
AVER((root->pm & mode) == AccessSetEMPTY);
|
|
ProtSet(root->protBase, root->protLimit, root->pm);
|
|
}
|
|
|
|
|
|
AccessSet RootPM(Root root)
|
|
{
|
|
AVERT(Root, root);
|
|
return root->pm;
|
|
}
|
|
|
|
|
|
Res RootDescribe(Root root, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
|
|
AVERT(Root, root);
|
|
AVER(stream != NULL);
|
|
|
|
res = WriteF(stream,
|
|
"Root $P ($U) {\n", (WriteFP)root, (WriteFU)root->serial,
|
|
" arena $P ($U)\n", (WriteFP)root->arena,
|
|
(WriteFU)root->arena->serial,
|
|
" rank $U\n", (WriteFU)root->rank,
|
|
" grey $B\n", (WriteFB)root->grey,
|
|
" summary $B\n", (WriteFB)root->summary,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
|
|
switch(root->var) {
|
|
case RootTABLE:
|
|
res = WriteF(stream,
|
|
" table base $A limit $A\n",
|
|
root->the.table.base,
|
|
root->the.table.limit,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
break;
|
|
|
|
case RootTABLE_MASKED:
|
|
res = WriteF(stream,
|
|
" table base $A limit $A mask $B\n",
|
|
root->the.tableMasked.base,
|
|
root->the.tableMasked.limit,
|
|
root->the.tableMasked.mask,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
break;
|
|
|
|
case RootFUN:
|
|
res = WriteF(stream,
|
|
" scan function $F\n", (WriteFF)root->the.fun.scan,
|
|
" environment p $P s $W\n",
|
|
root->the.fun.p, (WriteFW)root->the.fun.s,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
break;
|
|
|
|
case RootREG:
|
|
res = WriteF(stream,
|
|
" thread $P\n", (WriteFP)root->the.reg.thread,
|
|
" environment p $P", root->the.reg.p,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
break;
|
|
|
|
case RootFMT:
|
|
res = WriteF(stream,
|
|
" scan function $F\n", (WriteFF)root->the.fmt.scan,
|
|
" format base $A limit $A\n",
|
|
root->the.fmt.base,
|
|
root->the.fmt.limit,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
break;
|
|
|
|
default:
|
|
NOTREACHED;
|
|
}
|
|
|
|
res = WriteF(stream,
|
|
"} Root $P ($U)\n", (WriteFP)root, (WriteFU)root->serial,
|
|
NULL);
|
|
if(res != ResOK) return res;
|
|
|
|
return ResOK;
|
|
}
|