ZCTImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Implementation of the Zero Count Table for the Cedar incremental collector
created by Paul Rovner, January 18, 1984 9:36:15 am PST
Russ Atkinson (RRA) October 29, 1985 10:17:50 am PST
DIRECTORY
Allocator USING [NHeaderP, NormalHeader, RefCount, BlockSizeIndex, FNHeaderP],
AllocatorOps USING [PlausibleRef, REFToNHP, sizeToBSI, bsiToSize, ExpandNormalFreeList, FreeObject],
Basics USING [Byte, BITAND, BITOR, BITSHIFT, BITXOR, CARD, LowHalf, LongNumber],
CedarMicrocode USING [RECLAIMABLEREF, DISABLEMICROCODE, ENABLEMICROCODE],
Collector USING [InternalReclaim, Disposition, Reclaim],
DebuggerSwap USING [CallDebugger],
PrincOps USING [ControlLink, StateVector, zRET],
PrincOpsUtils USING [Codebase, GetReturnLink, MyLocalFrame],
RCMap USING [nullIndex],
RCMicrocodeOps USING [FOSTableHash, ASSIGNREF, OnZ, CREATEREF, RECLAIMABLEREF, RECLAIMEDREF, DISABLEMICROCODE, ENABLEMICROCODE, RCOverflowOccurred, RCUnderflowOccurred, LookFurtherAtReclaimedRef, ZCTFull, rcMicrocodeExists, rcMicrocodeWasEnabled, ALLOCATE, FREEPLEASE, NormalFreeListEmpty],
RTFrameHeapSnapshot USING [AcquireFrameHeapSnapshot, MapUncountedBodies, ReleaseFrameHeapSnapshot],
RTTypesBasicPrivate USING [MapTiTd],
SafeStorage USING [Type, nullType],
StorageTraps USING [],
TrapSupport USING [BumpPC, GetTrapParam],
VM USING [Allocate, Free, Interval, AddressForPageNumber],
ZCT USING [fosEmpty, FOSTableIndex, FOSTableObject, FOSTableResidue, HandleRCOverflow, HandleRCUnderflow, InitializeCleanup, logZCTBlockPages, TryToQItForFinalization, zct, zctBlockPages, zctBlockWords, ZCTObject, ZeroCountTable];
ZCTImpl: MONITOR
protects the ZeroCountTable and object headers, notably their refCounts
IMPORTS AllocatorOps, Basics, CedarMicrocode, Collector, DebuggerSwap, PrincOpsUtils, RCMicrocodeOps, RTFrameHeapSnapshot, RTTypesBasicPrivate, TrapSupport, VM, ZCT
EXPORTS RCMicrocodeOps, StorageTraps, ZCT
= BEGIN OPEN Allocator, AllocatorOps, Collector, RCMicrocodeOps, ZCT;
CARD: TYPE = Basics.CARD;
CONSTANTS
checking: BOOL = FALSE;
STATISTICS
takingStatistics: BOOL = TRUE;
Bump: PROC [p: POINTER TO INT, delta: INT ← 1] = INLINE {
usage: Bump[@stats.nExpandZCTCalls];
IF takingStatistics THEN p^ ← p^+delta;
};
StatsRec: TYPE = RECORD [
nExpandZCTCalls: INT ← 0,
nZCTBlocks: INT ← 0
];
stats: StatsRec ← []; -- the one and only
PROCEDURES
REFERENCE COUNTING
SoftwareCreateRef: PUBLIC PROC [nhp: NHeaderP] = {
See CREATEREF, the ucode replacement for this guy
DO
{
DoCreateRef[nhp ! ZCTFull => GOTO zctFull];
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
};
AssignRefNew: PROC [rhs: REF ANY, lhs: LONG POINTER TO REF ANY] = {
See ASSIGNREF, the ucode replacement for this guy
IF checking AND lhs^ # NIL THEN ERROR;
AssignRef[rhs, lhs];
};
AssignRef: --PUBLIC-- PROC [rhs: REF, lhs: LONG POINTER TO REF] = {
See ASSIGNREF, the ucode replacement for this guy
DO
{
DoAssignRef[rhs, lhs
! -- PageFault => {SatisfyPageFault[]; RETRY};
RCUnderflowOccurred => GOTO handleRCUnderflow;
RCOverflowOccurred => GOTO handleRCOverflow;
this is the only catch for RCOverflowOccurred
ZCTFull => GOTO zctFull;
];
RETURN;
EXITS
handleRCUnderflow => HandleRCUnderflow[lhs^];
handleRCOverflow => HandleRCOverflow[rhs];
increment, assnmt were NOT DONE
zctFull => ExpandZCT[];
};
ENDLOOP;
};
SoftwareReclaimedRef: PUBLIC PROC [ref: REF ANY] RETURNS [ans: REF ANYNIL] = {
See RECLAIMEDREF, the ucode replacement for this guy
ReclaimedRef is called by the reclaimer on each (non-NIL) ref that it finds within an object being reclaimed. ReclaimedRef will decrement the reference count on ref^ and return a non-NIL result (= ref) if the referenced object can be reclaimed (it will not enter it in the zct in this case). If ref^ can't be reclaimed, ReclaimedRef will return NIL. ReclaimedRef will queue ref^ for finalization if necessary. It will deal with rc underflow. It is possible (a finalized object, no room on its finalization queue) that ReclaimedRef will enter the ref on the zct. If the zct is full, ReclaimedRef will expand the zct.
DO
{
ans ← DoReclaimedRef[ref -- if a signal, the decrement was NOT DONE
! RCUnderflowOccurred => GOTO handleRCUnderflow;
ZCTFull => GOTO zctFull;
LookFurtherAtReclaimedRef => GOTO lookFurther;
];
RETURN;
EXITS
zctFull => ExpandZCT[];
handleRCUnderflow => HandleRCUnderflow[ref];
};
ENDLOOP;
EXITS lookFurther => FinalizeReclaimedRef[ref];
};
ALLOCATION AND RECLAMATION
SoftwareAllocate: PUBLIC PROC [size: CARDINAL, type: SafeStorage.Type] RETURNS [r: REF ← NIL] = {
DO
{
r ← SoftwareAllocateEntry[size, type
! NormalFreeListEmpty => GOTO expandNormalFreeList;
ZCTFull => GOTO zctFull];
RETURN;
EXITS
expandNormalFreeList => ExpandNormalFreeList[sizeToBSI[size]];
zctFull => ExpandZCT[];
};
ENDLOOP;
};
SoftwareFree: PUBLIC PROC [nhp: NHeaderP] RETURNS [success: BOOL] = {
success ← SoftwareFreeEntry[nhp];
};
COLLECTOR, ZCT
ScanTheFrameHeap: PUBLIC PROC = {
PlausibleRefFoundOnStack: PROC [lc: CARD] = {
index: FOSTableIndex;
residue: FOSTableResidue;
fosTable: LONG POINTER TO FOSTableObject
= LOOPHOLE[zct+SIZE[ZCTObject], LONG POINTER TO FOSTableObject];
[index, residue]
← FOSTableHash[LOOPHOLE[lc - SIZE[NormalHeader], NHeaderP--maybe bogus--]];
SELECT fosTable[index] FROM
fosEmpty =>
There was no previous entry, so just store the residue
fosTable[index] ← residue;
residue => {
We just found the single same thing that was there before
};
ENDCASE =>
A collision, so OR in the bits from the new residue (also OR in the fosEmpty value to show the collision).
fosTable[index] ← Basics.BITOR[fosEmpty, Basics.BITOR[fosTable[index], residue]];
};
ConservativeScanner: PROC [d: LONG DESCRIPTOR FOR ARRAY OF WORD] = {
pa: LONG POINTER TO CARD
= LOOPHOLE[BASE[d], LONG POINTER TO CARD];
nWords: CARDINAL = LENGTH[d];
IF nWords >= SIZE[REF] THEN
FOR i: CARDINAL IN [0..nWords-SIZE[REF]] DO
addr: CARD = (pa+i)^;
IF PlausibleRef[addr] THEN PlausibleRefFoundOnStack[addr];
ENDLOOP;
};
RTFrameHeapSnapshot.AcquireFrameHeapSnapshot[];
RTFrameHeapSnapshot.MapUncountedBodies[ConservativeScanner
! UNWIND => RTFrameHeapSnapshot.ReleaseFrameHeapSnapshot[]];
RTFrameHeapSnapshot.ReleaseFrameHeapSnapshot[];
};
MapReclaimableObjects: PUBLIC PROC [reclaim: PROC [NHeaderP]] = {
and compactify zct
wp: LONG POINTER TO NHeaderP = ReadWP[];
IF reclaim # NIL THEN ERROR;
FOR nhp: NHeaderP ← PullNextEntry[wp, FALSE], PullNextEntry[wp, FALSE] UNTIL nhp = NIL DO
nhp has been removed from zct
d: Disposition;
IF nhp.refCount # 0
THEN DO
{
OnZEntry[nhp ! ZCTFull => GOTO zctFull];
put it back on the ZCT so that ResetStackBits will eliminate it and clean the maybeOnStack bit
d ← continue;
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP
ELSE DO
{
d ← ReclaimableRef[nhp ! ZCTFull => GOTO zctFull];
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
SELECT d FROM
continue => NULL; -- either it was re-queued on zct or left off
reclaimIt =>
IF RTTypesBasicPrivate.MapTiTd[nhp.type].rcmx = RCMap.nullIndex
THEN AllocatorOps.FreeObject[nhp] ELSE Collector.Reclaim[nhp];
rc = 0 & not onstack & ~f: reclaim it (it was removed from zct)!
finalizeIt =>
rc = 0 & ~ not onstack & f
IF NOT TryToQItForFinalization[nhp] THEN DO
finalization q is full
{
it should stay in the zct
OnZEntry[nhp ! ZCTFull => GOTO zctFull];
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
ENDCASE => ERROR;
ENDLOOP;
};
ReclaimableRef: PROC [nhp: NHeaderP] RETURNS [Disposition] = {
This may raise ZCTFull. Ifso, no changes have been made
This is called only from MapReclaimableObjects
IF rcMicrocodeExists
THEN RETURN [CedarMicrocode.RECLAIMABLEREF[nhp]]
ELSE RETURN [SoftwareReclaimableRef[nhp]];
};
SoftwareReclaimableRef: PROC [nhp: NHeaderP] RETURNS [d: Disposition] = {
See RECLAIMABLEREF, the ucode replacement for this guy
This is called only from ReclaimableRef
DO
{
d ← DoReclaimableRef[nhp ! ZCTFull => GOTO zctFull];
RETURN;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
};
ResetStackBits: PUBLIC PROC = { -- and compactify zct
wp: LONG POINTER TO NHeaderP = ReadWP[];
FOR nhp: NHeaderP ← PullNextEntry[wp, TRUE], PullNextEntry[wp, TRUE] UNTIL nhp = NIL DO
nhp has been removed from zct
IF nhp.refCount = 0 AND NOT nhp.rcOverflowed THEN DO
{
- put it back
OnZEntry[nhp ! ZCTFull => GOTO zctFull];
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
ENDLOOP;
ClearFOSTable[];
};
PullNextEntry: PROC [wp: LONG POINTER TO NHeaderP, clearOnStack: BOOL] RETURNS [nhp: NHeaderP ← NIL] = {
This is called by MapReclaimableObjects and ResetStackBits
This is the only proc that sets nhp.inZCT ← FALSE or reads or writes zct.rp
wp will never point to a link
This returns NIL if there are no more ZCT entries to read
This is the next good candidate for implementation in microcode
DO
n: CARDINAL;
IF zct.rp = wp THEN RETURN [NIL]; -- nomore
nhp ← zct.rp^;
IF checking AND nhp = NIL THEN ERROR;
Here with non-NIL nhp; maybe a link
n ← Basics.BITAND[Basics.LowHalf[LOOPHOLE[zct.rp, CARD]], zctBlockWords-1];
IF n = zctBlockWords - SIZE[LONG POINTER] -- rp points to the link; nhp has it
THEN {
we're done with this zctBlock
zct.rp^ ← NIL; -- clears the link to the next zctBlock.
[] ← DoExpandZCT[zct.rp + SIZE[LONG POINTER] - zctBlockWords, FALSE];
zct.rp ← LOOPHOLE[nhp, LONG POINTER TO NHeaderP];
and go around again
}
ELSE {
zct.rp ← zct.rp + SIZE[LONG POINTER];
nhp.inZCT ← FALSE; -- was TRUE (no race!!)
IF clearOnStack THEN nhp.maybeOnStack ← FALSE; -- called from ResetStackBits
IF nhp.refCount = 0 OR (~clearOnStack AND nhp.maybeOnStack) THEN RETURN;
to scrutinize this guy further
};
ENDLOOP;
};
ExpandZCT: PUBLIC PROC = {
success: BOOL;
zbInterval: VM.Interval = VM.Allocate[count: zctBlockPages, alignment: logZCTBlockPages];
p: LONG POINTERVM.AddressForPageNumber[zbInterval.page];
Bump[@stats.nExpandZCTCalls];
InternalReclaim[reason: rcTableOverflow, suspendMe: FALSE]; -- just poke it
LOOPHOLE[p + zctBlockWords - SIZE[LONG POINTER], LONG POINTER TO NHeaderP]^ ← NIL; -- clear the link
success ← DoExpandZCT[p, TRUE];
IF success THEN Bump[@stats.nZCTBlocks] ELSE VM.Free[zbInterval];
};
ClearFOSTable: PROC = {
LOOPHOLE[zct+SIZE[ZCTObject], LONG POINTER TO FOSTableObject]^ ← ALL[fosEmpty];
};
ENTRY PROCS
0. Enter ... used by trap handlers and TraceAndSweepImpl
Enter: PUBLIC ENTRY PROC = {ENABLE UNWIND => NULL; NULL};
EnterAndCallBack: PUBLIC ENTRY PROC [proc: PROC] = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
proc[];
[] ← EnableMicrocode[zct];
};
1. AssignRef
DoAssignRef: ENTRY PROC [rhs: REF, lhs: LONG POINTER TO REF] = {
This is called only from AssignRef
This may raise ZCTFull, RCOverflowOccurred, RCUnderflowOccurred
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
ASSIGNREF[rhs, lhs];
[] ← EnableMicrocode[zct];
};
2. CreateRef
DoCreateRef: ENTRY PROC [nhp: NHeaderP] = {
This is called only from SoftwareCreateRef
DoCreateRef may raise ZCTFull
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
CREATEREF[nhp];
[] ← EnableMicrocode[zct];
};
3. Collector and reclaimer
StartMarkingDecrements: PUBLIC ENTRY PROC = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
zct.markingDecrements ← TRUE;
zct.residueMask ← Basics.BITOR[Basics.BITSHIFT[zct.residueMask, 3], Basics.BITSHIFT[zct.residueMask, -13]];
[] ← EnableMicrocode[zct];
};
StopMarkingDecrements: PUBLIC ENTRY PROC = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
zct.markingDecrements ← FALSE;
[] ← EnableMicrocode[zct];
};
DoReclaimableRef: ENTRY PROC [nhp: NHeaderP] RETURNS [d: Disposition] = {
This is called only from SoftwareReclaimableRef
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
d ← RECLAIMABLEREF[nhp]; -- The only call on RECLAIMABLEREF
[] ← EnableMicrocode[zct];
};
DoReclaimedRef: ENTRY PROC [ref: REF ANY] RETURNS [ans: REF ANYNIL] = {
This is called only from SoftwareReclaimedRef
This may raise ZCTFull, RCUnderflowOccurred, LookFurtherAtReclaimedRef
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
ans ← RECLAIMEDREF[ref];
[] ← EnableMicrocode[zct];
};
ReadWP: ENTRY PROC RETURNS [wp: LONG POINTER TO Allocator.NHeaderP] = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
wp ← zct.wp;
[] ← EnableMicrocode[zct];
};
4. Finalization ... called from FQImpl
FinalizeReclaimedRef: PROC [ref: REF] = {
Called from ReclaimedRefTrap and from SoftwareReclaimedRef
refCount = 1 & ~ rcOverflowed & ~ maybeOnStack & ~ inZCT & f
i.e. if the object has npr + 1 REFs, is referenced from an object that is being reclaimed and is to be finalized. Decrement its rc and try to q it.
nhp: NHeaderP = REFToNHP[ref];
IF Basics.LowHalf[LOOPHOLE[nhp]] MOD 2 = 1 THEN
This must NOT be odd!
DebuggerSwap.CallDebugger["ZCT disaster"L];
DO
{
DecrementForFinalizeReclaimedRef[nhp
! RCUnderflowOccurred => GOTO handleRCUnderflow];
EXIT;
EXITS handleRCUnderflow => HandleRCUnderflow[ref];
};
ENDLOOP;
IF NOT TryToQItForFinalization[nhp] THEN DO
finalization q is full
{
it should stay in the zct
OnZEntry[nhp ! ZCTFull => GOTO zctFull];
EXIT;
EXITS zctFull => ExpandZCT[];
};
ENDLOOP;
};
DecrementForFinalizeReclaimedRef: ENTRY PROC [nhp: NHeaderP] = {
called only from FinalizeReclaimedRef. May raise RCUnderflowOccurred.
ENABLE UNWIND => [] ← EnableMicrocode[zct];
rc: RefCount;
DisableMicrocode[zct];
rc ← nhp.refCount;
IF checking AND rc = 0 AND NOT nhp.rcOverflowed THEN ERROR;
IF rc = 0 AND nhp.rcOverflowed
THEN ERROR RCUnderflowOccurred; -- no changes have been made
nhp.refCount ← rc - 1;
[] ← EnableMicrocode[zct];
};
DoEnableFinalization: PUBLIC ENTRY PROC [npr: NAT, nhp: NHeaderP] = {
called only from FQImpl.EnableFinalization. May raise ZCTFull or RCUnderflowOccurred.
ENABLE UNWIND => [] ← EnableMicrocode[zct];
rc: RefCount;
DisableMicrocode[zct];
IF checking AND nhp.f THEN ERROR;
rc ← nhp.refCount;
IF checking AND rc < npr AND NOT nhp.rcOverflowed THEN ERROR;
IF rc < npr AND nhp.rcOverflowed THEN ERROR RCUnderflowOccurred;
no changes have been made
NOTE if npr > 1 then this will be reported as a finalization error but shouldn't be. The (implementation) problem is that an RCUnderflowOccurred handler can't deal with npr > 1
IF rc = npr AND NOT nhp.rcOverflowed THEN OnZ[nhp]; -- might raise ZCTFull
nhp.refCount ← rc - npr;
nhp.f ← TRUE;
nhp.maybeOnStack ← zct.markingDecrements;
[] ← EnableMicrocode[zct];
};
DisableFinalization: PUBLIC ENTRY PROC [npr: NAT, nhp: NHeaderP] = {
called only from FQImpl.TryToQItForFinalization. May raise RCOverflowOccurred.
ENABLE UNWIND => [] ← EnableMicrocode[zct];
rc: RefCount;
DisableMicrocode[zct];
IF checking AND NOT nhp.f THEN ERROR;
rc ← nhp.refCount;
FOR i: NAT IN [0..npr) DO
IF rc + i = LAST[RefCount] THEN ERROR RCOverflowOccurred;
ENDLOOP;
NOTE if npr > 1 then this will be reported as a finalization error but shouldn't be. The (implementation) problem is that an RCOverflowOccurred handler can't deal with npr > 1
increment was NOT done
The handler for RCOverflowOccurred makes adjustments, not changes
nhp.refCount ← rc + npr;
nhp.f ← FALSE;
nhp.maybeOnStack ← FALSE;
[] ← EnableMicrocode[zct];
};
5. RC overflow ... called from RCOvImpl
DecrForOverflow: PUBLIC ENTRY PROC [rcDelta: NAT, nhp: NHeaderP] RETURNS [success: BOOLFALSE] = {
called only from RCOvImpl.HandleRCOverflow
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
IF nhp.refCount = LAST[RefCount] THEN {
nhp.refCount ← nhp.refCount - rcDelta;
success ← TRUE;
};
[] ← EnableMicrocode[zct];
};
IncrForUnderflow: PUBLIC ENTRY PROC [rcDelta: NAT, nhp: NHeaderP] RETURNS [success: BOOLFALSE] = {
called only from RCOvImpl.HandleRCUnderflow
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
IF nhp.refCount = 0 THEN {
nhp.refCount ← nhp.refCount + rcDelta;
success ← TRUE;
};
[] ← EnableMicrocode[zct];
};
6. ZCT expansion. These are called during recovery from a ZCTFull signal.
DoExpandZCT: ENTRY PROC [newZCTBlock: LONG POINTER, onlyIfNeeded: BOOL] RETURNS [success: BOOLTRUE] = {
called from ExpandZCT and from PullNextEntry
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
IF NOT onlyIfNeeded OR zct.lastNP = ComputeLastNP[zct.wp]
THEN {
zct.lastNP^ = NIL
zct.lastNP^ ← newZCTBlock;
zct.lastNP ← LOOPHOLE[
newZCTBlock + zctBlockWords-SIZE[LONG POINTER],
LONG
POINTER TO LONG POINTER];
}
ELSE success ← FALSE;
[] ← EnableMicrocode[zct];
};
ComputeLastNP: INTERNAL PROC [wp: LONG POINTER TO NHeaderP] RETURNS [lastNP: LONG POINTER TO LONG POINTER] = {
n: Basics.LongNumber ← LOOPHOLE[wp, Basics.LongNumber];
n.lowbits ← Basics.BITXOR[n.lowbits, Basics.BITAND[n.lowbits, zctBlockWords-1]];
RETURN [LOOPHOLE[n, LONG POINTER TO NHeaderP]
+ zctBlockWords
- SIZE[LONG POINTER]
];
};
7. Allocation, reclamation
SoftwareAllocateEntry: ENTRY PROC [size: CARDINAL, type: SafeStorage.Type] RETURNS [r: REF ← NIL] = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
r ← RCMicrocodeOps.ALLOCATE[size, type];
[] ← EnableMicrocode[zct];
};
SoftwareFreeEntry: ENTRY PROC [nhp: NHeaderP] RETURNS [success: BOOL] = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
successRCMicrocodeOps.FREEPLEASE[nhp];
[] ← EnableMicrocode[zct];
};
InsertQuanta: PUBLIC ENTRY PROC [bsi: BlockSizeIndex, first, last: FNHeaderP] = {
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
last.nextFree ← zct.bsiToFreeList[bsi];
zct.bsiToFreeList[bsi] ← first;
[] ← EnableMicrocode[zct];
};
DoFREE: PUBLIC ENTRY PROC [fnhp: FNHeaderP, bsi: BlockSizeIndex] = {
See AllocatorImpl.TAndSDoFreeNormalFragment
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
fnhp.fnh.type ← SafeStorage.nullType; -- mark the object as free
fnhp.nextFree ← zct.bsiToFreeList[bsi];
zct.bsiToFreeList[bsi] ← fnhp;
[] ← EnableMicrocode[zct];
};
8. Utilities
Initialize: ENTRY PROC = {
ENABLE UNWIND => NULL;
zct.rp ← zct.wp ← LOOPHOLE[
VM.AddressForPageNumber[
VM.Allocate[count: zctBlockPages, alignment: logZCTBlockPages].page
],
LONG POINTER TO Allocator.NHeaderP
];
zct.lastNP ← LOOPHOLE[
zct.wp+zctBlockWords-SIZE[LONG POINTER],
LONG POINTER TO LONG POINTER];
zct.lastNP^ ← NIL;
ClearFOSTable[];
zct.markingDecrements ← FALSE;
zct.residueMask ← 76031B;
zct.bsiToFreeList ← ALL[NIL];
zct.sizeToBSI ← sizeToBSI^;
zct.bsiToSize ← bsiToSize^;
InitializeCleanup[];
};
OnZEntry: ENTRY PROC [nhp: NHeaderP] = {
This may raise ZCTFull.
ENABLE UNWIND => [] ← EnableMicrocode[zct];
DisableMicrocode[zct];
OnZ[nhp];
[] ← EnableMicrocode[zct];
};
DisableMicrocode: INTERNAL PROC [zct: ZeroCountTable] = {
See DISABLEMICROCODE, the ucode replacement for this guy
cause rc operations to trap (except EnableMicrocode)
IF rcMicrocodeExists
THEN CedarMicrocode.DISABLEMICROCODE[zct]
ELSE DISABLEMICROCODE[zct];
rcMicrocodeWasEnabled ← FALSE;
};
EnableMicrocode: INTERNAL PROC [zct: ZeroCountTable] RETURNS [ucVersion: NAT] = {
See ENABLEMICROCODE, the ucode replacement for this guy
IF rcMicrocodeExists
THEN ucVersion ← CedarMicrocode.ENABLEMICROCODE[zct]
ELSE ucVersion ← ENABLEMICROCODE[zct];
rcMicrocodeWasEnabled ← TRUE;
};
TRAP HANDLERS
DisableMicrocodeTrap: PUBLIC PROC [zct: ZeroCountTable] = {
state: PrincOps.StateVector;
state ← STATE; -- incantation
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => NULL; -- no microcode
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
EnableMicrocodeTrap: PUBLIC PROC [zct: ZeroCountTable] RETURNS [ucVersion: NAT ← 0] = {
state: PrincOps.StateVector;
state ← STATE; -- incantation
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => NULL; -- no microcode
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
ReclaimableRefTrap: PUBLIC PROC [nhp: NHeaderP] RETURNS [d: Disposition] = {
state: PrincOps.StateVector;
state ← STATE; -- incantation
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => d ← SoftwareReclaimableRef[nhp];
no microcode
2 => {
uCode is disabled; someone is inside this monitor
p: PROC [nhp: NHeaderP] = MACHINE CODE{PrincOps.zRET};
Enter[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[nhp]; -- try again
};
4 => {
zctFull
p: PROC [nhp: NHeaderP] = MACHINE CODE{PrincOps.zRET};
ExpandZCT[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[nhp];
};
5 => DebuggerSwap.CallDebugger["ZCT disaster"L]; -- DISASTER
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
ReclaimedRefTrap: PUBLIC PROC [ref: REF ANY] RETURNS [ans: REF ANYNIL] = {
p: PROC [ref: REF ANY] = MACHINE CODE{PrincOps.zRET};
state: PrincOps.StateVector;
state ← STATE; -- incantation
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => ans ← SoftwareReclaimedRef[ref]; -- no microcode
2 => {
uCode is disabled; someone is inside this monitor
Enter[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[ref]; -- try again
};
3 => {
handleRCUnderflow: decrement was NOT DONE
HandleRCUnderflow[ref];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[ref]; -- try again
};
4 => {
zctFull
ExpandZCT[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[ref];
};
5 => DebuggerSwap.CallDebugger["ZCT disaster"L]; -- DISASTER
6 => FinalizeReclaimedRef[ref]; -- LookFurtherAtReclaimedRef
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
CreateRefTrap: PUBLIC PROC [nhp: NHeaderP] = {
state: PrincOps.StateVector;
state ← STATE; -- incantation
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => SoftwareCreateRef[nhp]; -- no microcode
2 => {
uCode is disabled; someone is inside this monitor
p: PROC [nhp: NHeaderP] = MACHINE CODE{PrincOps.zRET};
Enter[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[nhp];
};
4 => {
zctFull
p: PROC [nhp: NHeaderP] = MACHINE CODE{PrincOps.zRET};
ExpandZCT[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[nhp];
};
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
AssignRefTrap: PUBLIC PROC [refNew: REF, ptrRef: LONG POINTER TO REF] = {
state: PrincOps.StateVector;
alphaRef: LONG POINTER TO REF;
p: PROC [refNew: REF, ptrRef: LONG POINTER TO REF] = MACHINE CODE {
PrincOps.zRET;
};
state ← STATE; -- incantation
alphaRef ← AddAlphaByte[ptrRef, PrincOpsUtils.GetReturnLink[]];
SELECT (IF rcMicrocodeExists THEN TrapSupport.GetTrapParam[] ELSE 0) FROM
0 => AssignRef[refNew, alphaRef]; -- no microcode
1 => {
handleRCOverflow: rc ops and assnmt were NOT DONE
HandleRCOverflow[refNew];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[refNew, ptrRef]; -- try again
};
2 => {
uCode is disabled; someone is inside this monitor
Enter[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[refNew, ptrRef]; -- try again
};
3 => {
handleRCUnderflow: decrement, assnmt were NOT DONE (this does the decrement, assigns NIL, then goes around again)
HandleRCUnderflow[alphaRef^];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[refNew, ptrRef]; -- try again
};
4 => {
zctFull
ExpandZCT[];
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
p[refNew, ptrRef]; -- try again
};
5 => DebuggerSwap.CallDebugger["ZCT disaster"L]; -- DISASTER
ENDCASE => ERROR;
TrapSupport.BumpPC[2]; -- length of opcode
state.dest ← LOOPHOLE[PrincOpsUtils.MyLocalFrame[]];
TRANSFER WITH state; -- incantation
};
AddAlphaByte: PROC [ptrRef: LONG POINTER TO REF, ctL: PrincOps.ControlLink] RETURNS [LONG POINTER TO REF] = {
read the alpha byte for the trapping opcode & add it to ptrRef
codeB: LONG POINTER TO PACKED ARRAY OF Basics.Byte
LOOPHOLE[PrincOpsUtils.Codebase[LOOPHOLE[ctL.frame.accesslink]]];
alpha: Basics.Byte ← codeB[ctL.frame.pc + 1];
RETURN [LOOPHOLE[ptrRef + alpha]];
};
Initialize[];
END.