RCMicrocodeImpl.mesa
Software Implementation of the Cedar reference-counting microcode
Paul Rovner, November 10, 1983 1:21 pm
DIRECTORY
Allocator USING[FNHeaderP, NHeaderP, RefCount, maxSmallBlockSize, BlockSizeIndex, bsiEscape, NormalHeader],
AllocatorOps USING[REFToNHP, NHPToREF],
Basics USING[BITAND, BITOR, BITSHIFT, BITXOR, HighHalf, LowHalf],
Collector USING[Disposition],
PrincOpsUtils USING[ZERO],
RCMicrocodeOps USING[],
SafeStorage USING[Type, nullType],
ZCT USING[FOSTableIndex, FOSTableLength, FOSTableResidue, fosWildCard, zctBlockWords, ZeroCountTable, zct, ZCTObject, FOSTableObject];
RCMicrocodeImpl: PROGRAM
all but FOSTableHash are really INTERNAL procs of the ZCTImpl monitor
IMPORTS AllocatorOps, Basics, PrincOpsUtils, ZCT--zct--
EXPORTS RCMicrocodeOps
= BEGIN OPEN Allocator, Collector, ZCT;
FLAGS
checking: BOOL = TRUE;
ucDisabled: BOOL; private flag kept in ucode registers
ERRORS
RCOverflowOccurred: PUBLIC ERROR = CODE;
RCUnderflowOccurred: PUBLIC ERROR = CODE;
LookFurtherAtReclaimedRef: PUBLIC ERROR = CODE;
ZCTFull: PUBLIC ERROR = CODE;
NormalFreeListEmpty: PUBLIC ERROR = CODE;
PROCEDURES
Software implementation of the Allocate MISC
zct must also include: sizeToBSI, bsiToFreeList, bsiToSize
Returns NIL IF size> Allocator.maxSmallBlockSize
may pageFault or raise ZCTFull or NormalFreeListEmpty; ifso, no changes have been made
ALLOCATE: PUBLIC --INTERNAL-- PROC[--requested--size: CARDINAL, type: SafeStorage.Type]
RETURNS[r: REF ← NIL] = {
IF ucDisabled THEN Allocate MISC TRAP with trapParam = 2
IF size <= maxSmallBlockSize
THEN {
bsi: BlockSizeIndex = zct.sizeToBSI[size];
fnhp: FNHeaderP ← zct.bsiToFreeList[bsi];
nhp: NHeaderP;
IF fnhp = NIL THEN ERROR NormalFreeListEmpty;
Allocate MISC TRAP with trapParam = 6
nhp ← @fnhp.fnh;
r ← AllocatorOps.NHPToREF[nhp];
XXX touch nhp.type, .maybeOnStack, zct.markingDecrements, fnhp.nextFree
OnZ[nhp]; -- may pageFault or raise ZCTFull; ifso, no changes have been made
COMMITTED if here
zct.bsiToFreeList[bsi] ← fnhp.nextFree;
fnhp.nextFree ← NIL; -- CLEAR THE NEW OBJECT
nhp.type ← type;
nhp.maybeOnStack ← zct.markingDecrements;
};
};
Software implementation of the Free MISC
Returns FASE IF bsi = bsiEscape
may pageFault; ifso, no changes have been made
FREEPLEASE: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP] RETURNS[success: BOOLFALSE] = {
IF ucDisabled THEN Allocate MISC TRAP with trapParam = 2
bsi: BlockSizeIndex = nhp.blockSizeIndex;
IF (success ← bsi # bsiEscape)
THEN {
fnhp: FNHeaderP = LOOPHOLE[nhp, FNHeaderP];
PrincOpsUtils.ZERO[nhp + SIZE[NormalHeader], zct.bsiToSize[bsi] - SIZE[NormalHeader]];
fnhp.fnh.type ← SafeStorage.nullType; -- mark the object as free
fnhp.nextFree ← zct.bsiToFreeList[bsi];
zct.bsiToFreeList[bsi] ← fnhp;
};
};
Software implementation of the AssignRef and AssignRefNew opCodes
This might pageFault or raise ZCTFull, RCOverflowOccurred or RCUnderflowOccurred
If this guy pageFaults or raises a signal no changes have been made
ASSIGNREF: PUBLIC --INTERNAL-- PROC[rhs: REF, lhs: LONG POINTER TO REF] = {
lhsRef: REF;
rnhp: NHeaderP;
rrc: RefCount;
IF ucDisabled THEN opCode TRAP with trapParam = 2
lhs ← AddAlphaByte[lhs, XXX];
lhsRef ← lhs^;
IF rhs # NIL THEN {
check first for rcoverflow of rhs; after changes are made to lhs we must guarantee completion
IF lhsRef = rhs THEN RETURN;--no-op. Bug: x←x. Either do this check or re-read rrc below
rnhp ← AllocatorOps.REFToNHP[rhs]; -- subtract 2
rrc ← rnhp.refCount;
IF rrc = LAST[RefCount] THEN ERROR RCOverflowOccurred;
i.e. opCode TRAP with trapParam = 1
increment, assignment were NOT done
The handler for RCOverflowOccurred makes adjustments, not changes
};
IF lhsRef # NIL THEN {
lnhp: NHeaderP = AllocatorOps.REFToNHP[lhsRef];
lrc: RefCount ← lnhp.refCount;
CheckForRCUnderflow[lnhp]; -- might raise RCUnderflowOccurred
IF lrc = 1 AND NOT lnhp.rcOverflowed THEN OnZ[lnhp]; -- might raise ZCTFull
Everything that might pageFault has been touched when we get here
lnhp.maybeOnStack ← zct.markingDecrements--ASSUME zct^ is pinned--;
COMMITTED NOW to finish this call
lnhp.refCount ← lrc - 1;
};
Everything that might pageFault has been touched when we get here.
IF rhs # NIL THEN {
rnhp.refCount ← rrc + 1; -- COMMITTED NOW to finish this call
rnhp.maybeOnStack ← FALSE;
};
lhs^ ← rhs; -- NOT counted
}; -- end ASSIGNREF
Software implementation of the OnZ microcode subroutine
This can pageFault or raise ZCTFull. If so, no changes have been made.
This is the only proc that
explicitly raises ZCTFull
changes zct.wp
sets nhp.inZCT ← TRUE
OnZ: PUBLIC --INTERNAL-- PROC [nhp: NHeaderP] = {
wp: LONG POINTER TO NHeaderP;
IF nhp.inZCT THEN RETURN;
wp ← zct.wp;
wp^ ← nhp; -- might pageFault
wp will not point to the link word; wp^ may be left with garbage if ZCTFull is raised
IF Basics.BITAND[Basics.LowHalf[LOOPHOLE[wp, LONG CARDINAL]], zctBlockWords - 1]
= zctBlockWords - 2*SIZE[LONG POINTER]
THEN { -- wp points to the cell before the link to the next zctBlock
IF (wp ← LOOPHOLE[(wp+SIZE[LONG POINTER])^, -- pick up the link
      LONG POINTER TO NHeaderP]) = NIL
THEN ERROR ZCTFull -- i.e. XXX TRAP with trapParam = 4
ELSE zct.wp ← wp;
now COMMITTED to finish this call
}
ELSE zct.wp ← wp + SIZE[LONG POINTER]; -- will NOT pageFault
nhp.inZCT ← TRUE; -- will NOT pagefault
};
Software implementation of the CreateRef MISC
CREATEREF: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP] = {
IF ucDisabled THEN CreateRef MISC TRAP with trapParam = 2
IF checking AND (nhp.refCount # 0 OR nhp.rcOverflowed) THEN ERROR;
nhp.maybeOnStack ← zct.markingDecrements;
OnZ[nhp]; -- may pageFault or raise ZCTFull; ifso, no changes have been made
};
Software implementation of the ReclaimableRef MISC
This may pageFault or raise ZCTFull; ifso, no changes have been made
RECLAIMABLEREF: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP]
RETURNS[Disposition] = {
IF ucDisabled THEN ReclaimableRef MISC TRAP with trapParam = 2
RETURN[MCRECLAIMABLEREF[nhp, FALSE]];
};
Software implementation of the MCRECLAIMABLEREF microcode subroutine
This is called from RECLAIMEDREF and from RECLAIMABLEREF
This may pageFault or raise ZCTFull; ifso, no changes have been made
MCRECLAIMABLEREF: PROC[nhp: NHeaderP, decrPending: BOOL]
RETURNS[d: Disposition ← continue] = {
rc: RefCount ← nhp.refCount;
IF decrPending THEN rc ← rc - 1; -- won't underflow
IF rc # 0 OR nhp.inZCT OR nhp.rcOverflowed THEN RETURN; -- leave it alone
IF nhp.maybeOnStack OR FoundInFHSnapshot[nhp]
THEN OnZ[nhp] -- put it back; this may pageFault or raise ZCTFull
ELSE d ← (IF nhp.f THEN finalizeIt ELSE reclaimIt);
};
Software implementation of the FoundInFHSnapshot microcode subroutine
This is called only from MCRECLAIMABLEREF
FoundInFHSnapshot: PROC[nhp: NHeaderP]
RETURNS[found: BOOLFALSE] = {
index: FOSTableIndex;
residue, entry: FOSTableResidue;
fosTable: LONG POINTER TO FOSTableObject
= LOOPHOLE[zct+SIZE[ZCTObject], LONG POINTER TO FOSTableObject];
[index, residue] ← FOSTableHash[nhp];
entry ← fosTable[index];
RETURN[entry = residue OR entry = fosWildCard];
};
Software implementation of the FOSTableHash microcode subroutine
FOSTableHash: PUBLIC PROC[nhp: NHeaderP--maybe bogus--]
RETURNS[x: FOSTableIndex, r: FOSTableResidue] = { OPEN Basics;
lc: LONG CARDINAL = LOOPHOLE[nhp, LONG CARDINAL];
r ← BITOR[BITSHIFT[HighHalf[lc], 3], BITSHIFT[LowHalf[lc], -13]];
r is all but the least significant 13 bits
x ← BITAND[BITXOR[BITSHIFT[LowHalf[lc], -1], r], FOSTableLength-1];
REFs are EVEN => NHeaderP's are even
non-unique for addresses bigger than 25 bits
};
Software implementation of the ReclaimedRef MISC
This might pageFault or raise ZCTFull, RCUnderflowOccurred or LookFurtherAtReclaimedRef
This is the only proc that raises LookFurtherAtReclaimedRef explicitly
If this proc pageFaults or raises a signal then no changes have been made.
RECLAIMEDREF: PUBLIC -- INTERNAL-- PROC[ref: REF] RETURNS[ans: REF ANYNIL] = {
nhp: NHeaderP = AllocatorOps.REFToNHP[ref];
IF ucDisabled THEN ReclaimedRef MISC TRAP with trapParam = 2
CheckForRCUnderflow[nhp]; -- might raise RCUnderflowOccurred
SELECT MCRECLAIMABLEREF[nhp, TRUE] FROM
continue => NULL;
reclaimIt => ans ← ref;
finalizeIt =>
ERROR LookFurtherAtReclaimedRef; --ReclaimedRef MISC TRAP, trapParam = 6
ENDCASE => ERROR;
nhp.refCount ← nhp.refCount - 1;
};
Software implementation of the CheckForRCUnderflow microcode subroutine
This is called only from ASSIGNREF and from RECLAIMEDREF
CheckForRCUnderflow: PROC[nhp: NHeaderP] = {
rc: RefCount ← nhp.refCount;
IF checking AND rc = 0 AND NOT nhp.rcOverflowed THEN ERROR;
i.e. XXX TRAP with trapParam = 5
IF rc = 0 --was RCMicrocodeOps.rcBottom-- AND nhp.rcOverflowed
THEN ERROR RCUnderflowOccurred; -- no changes have been made
i.e. XXX TRAP with trapParam = 3
};
Software implementation of the DisableMicrocode MISC.
In addition to "disabling" rc microcode, this guy should cause the store of ucode registers back into memory (zct^).
DISABLEMICROCODE: PUBLIC -- INTERNAL-- PROC[z: ZeroCountTable] = {
cause rc operations to trap (except EnableMicrocode)
NULL; -- the real uc: ucDisabled ← TRUE
};
Software implementation of the EnableMicrocode MISC
In addition to "enabling" rc microcode, this guy should load ucode registers from memory (zct^).
ENABLEMICROCODE: PUBLIC -- INTERNAL-- PROC[z: ZeroCountTable]
RETURNS[ucVersion: NAT] = {
RETURN[0]; -- the real uc: ucDisabled ← FALSE
};
END.