-- RTRefCounts.Mesa
-- last edited 13-Oct-81 16:32:05 by Willie-Sue
-- last edited May 17, 1983 12:43 pm by Paul Rovner
-- Last Edited by: Levin, August 8, 1983 4:55 pm

DIRECTORY
Basics USING[BITXOR, BITSHIFT, LongNumber],
PrincOps USING[wordsPerPage, zMISC, alpha],
RTQuanta USING[LASTAddress],
RTTypesBasicPrivate USING[RMapTiTd],
RTZones USING[RMapQZf, PMapZiZn, ZoneIndex],
SafeStorage USING[ReclamationReason, maxNPackageRefs];

RTRefCounts: DEFINITIONS
IMPORTS Basics
= BEGIN

-- P A R A M E T E R S and T Y P E S

-- Reference count table parameters and TYPEs

AMapPiRce: TYPE = ARRAY ProbeIndex OF RCEntry; -- the TYPE of the reference count table

ProbeSize: CARDINAL = 100000B; -- NOTE ucode & the 3 MapPi fns depend on this!!
ProbeIndex: TYPE = [0..ProbeSize); -- "pi"

RCEntry: TYPE = MACHINE DEPENDENT RECORD   -- "rce" (setup for microcode)
[ rcentry(0:0..15):
SELECT eType(0:0..0): entryType FROM
normal => [ rc(0:1..7): RefCt,   -- eType = 0
   onStack(0:8..8): uBoolean ← false,
   res(0:9..15): Residue],
overflow => [ oi(0:1..14): MapOiOeIndex,  -- eType = 1
   fill(0:15..15): [0..1] ← 0],
ENDCASE];

nRCEntry: TYPE = normal RCEntry;
oRCEntry: TYPE = overflow RCEntry;

entryType: TYPE = MACHINE DEPENDENT { normal(0), overflow(1) };
RefCt: TYPE = [0..(LAST[CARDINAL]/4)/ResidueSize];
-- "rc" (7 bits) so normal RCEntry fits in one word
rcFinalize: CARDINAL = SafeStorage.maxNPackageRefs+1;
rcAbsent: CARDINAL = rcFinalize + 1;
absentRCEntry: nRCEntry = [normal [rc: rcAbsent, onStack: false, res: 0]]; -- res a don't care
nrcEntry: nRCEntry = [normal [rc: LAST[RefCt], onStack: true, res: LAST[Residue]]];
nrceMask: CARDINAL = LOOPHOLE[nrcEntry];
rceEmpty: oRCEntry = LOOPHOLE[177777B, oRCEntry];
-- empty is overflow entry that is odd; making it all ones simplifies the microcode


uBoolean: TYPE = MACHINE DEPENDENT { false(0), true(1) };
-- "don't depend on Mesa's representation of TRUE/FALSE
Residue: TYPE = [0..ResidueSize); -- "res", 7 bits
ResidueSize: CARDINAL = (RTQuanta.LASTAddress+1)/ProbeSize;

-- Reference count overflow table parameters and TYPEs

AMapOiOe: TYPE = ARRAY MapOiOeIndex OF OverflowEntry; -- the TYPE of the overflow table

MapOiOeIndex: TYPE = [0..mapOiOeLimit + mapOiOeStart);
-- depends on the layout of AGCState. Indexing cleverness simplifies the microcode.

OverflowEntry: TYPE = MACHINE DEPENDENT RECORD -- "oe"
[ rce(0:0..15): nRCEntry, oe1(1:0..15): RCEntry];

OverflowFreeListEntry: TYPE = MACHINE DEPENDENT RECORD
[ link(0:0..15): oRCEntry, oe1(1:0..15): RCEntry];

-- Entries at the end of overflow chains contain two normal entries:
-- EndOfChainOE: TYPE = MACHINE DEPENDENT RECORD [ rce: nRCEntry, oe1: nRCEntry]

-- Entries in the middle of overflow chains contain a normal and an overflow entry
-- MiddleOfChainOE: TYPE = MACHINE DEPENDENT RECORD [ rce: nRCEntry, oe1: oRCEntry]

-- NOTE the microcode uses all 16 bits of an overflow RCEntry as an index into MapOiOe,
-- which is really based at GCState. The mesa code uses only the middle 14 bits,
-- adds in an offset (see MapOiOeIndex) and multiplies by SIZE[OverflowEntry],
-- which is known to be 2.
-- The software's value of MapOiOe will be greater than the ucode's
-- by ProbeSize/SIZE[OverflowEntry]


mapOiOePages: INTEGER = 80;
OverflowEntrySize: CARDINAL = 2; -- needed to avoid circular defs

mapOiOeStart: CARDINAL = AGCStateSize/OverflowEntrySize;
mapOiOeLimit: CARDINAL = mapOiOePages*PrincOps.wordsPerPage/OverflowEntrySize;

-- The freelist in the overflow table is threaded through the even words.
-- The contents of the odd words are ignored: [ rce: oRCEntry, oe1: don't care]


-- Access (for the microcode) to runtime tables: parameters and TYPEs
AGCStateSize: CARDINAL = PrincOps.wordsPerPage;
-- using a full page simplifies the microcode

AGCState: TYPE = MACHINE DEPENDENT RECORD
[
GCStateBasic: aGCStateBasic,
toBeUsedLater: toBeUsedSpace,
GCEventCounters: aGCEventCounters,
rceToReclaim: RCEToReclaimVec, -- communication from the microcode to the collector
dumpTable: gcDumpTable
-- MapPiRce: AMapPiRce  an array 32K long
-- MapOiOe: AMapOiOe   an array 20K long
];

aGCStateBasic: TYPE = MACHINE DEPENDENT RECORD
[
-- these two goodies plus rcFnzCount are kept in microcode
-- registers, and moved to memory via
-- RTMOVESTATUS[upDate, 0],
-- from memory via
-- RTMOVESTATUS[initialize, Inline.HighHalf[GCState]
mapOiOeFrList(0:0..15): oRCEntry, -- the overflow FreeList chain
nOiFree(1:0..15): INTEGER, -- (length of FreeList chain) - gcThresholdNOiFree

-- these two goodies are kept in microcode registers, and
-- are ALWAYS moved there by RTMOVESTATUS
-- (e.g. RTMOVESTATUS[upDate] does it). Groan.
reclaimState(2:0..15): gcRState ← gcReady,
collector(3:0..15): PROCESSNIL,

mapQZf(4:0..31): LONG POINTER TO RTZones.RMapQZf ← NIL,
mapSziSz(6:0..31): LONG POINTERNIL,
mapZiZn(10B:0..31): LONG POINTER TO RTZones.PMapZiZn ← NIL,
mapTiTd(12B:0..31): LONG POINTER TO RTTypesBasicPrivate.RMapTiTd ← NIL,
rcFnzCount(14B:0..15): CARDINAL← 0,
tba1(15B:0..15): CARDINAL← 0,
tba2(16B:0..15): CARDINAL← 0,
tba3(17B:0..15): CARDINAL← 0
];

gcThresholdNOiFree: CARDINAL = mapOiOeLimit/4;
-- enuff extra (hopefully!!) for expansion during collection

gcRState: TYPE = MACHINE DEPENDENT RECORD -- software status, encoded to simplify ucode
[ fill0(0:0..0): [0..1]← 0,
rcMask(0:1..7): RefCt ← LAST[RefCt],
useStack(0:8..8): uBoolean,
fill(0:9..15): [0..177B] ← 0
];
gcReady: gcRState = [ useStack: false]; -- 77400B
gcRunning: gcRState = [ useStack: true]; -- 77600B
gcStopped: gcRState = [ fill0: 1, useStack: false]; -- 1xxxxx

toBeUsedSize: CARDINAL = 2*rceReclaimLimit-SIZE[aGCStateBasic]-SIZE[aGCEventCounters];
toBeUsedSpace: TYPE = ARRAY[0..toBeUsedSize) OF CARDINAL;

aGCEventCounters: TYPE = MACHINE DEPENDENT RECORD
[
AlterCountNIL: LONG INTEGER,  -- AlterCount with NIL or odd arg
AlterCountZeroChange: LONG INTEGER,  -- AlterCount w/o changing RefCt
AlterCountDecrement: LONG INTEGER,  -- AlterCount decrementing RefCt
AlterCountIncrement: LONG INTEGER,  -- AlterCount incrementing RefCt
OTCellDelete: LONG INTEGER,   -- OT cell deletions
OTCellCreate: LONG INTEGER,   -- OT cell creations
HTCellDelete: LONG INTEGER,   -- HT cell deletions
HTCellCreate: LONG INTEGER,   -- HT cell creations
ReclaimedRefNIL: LONG INTEGER,  -- ReclaimedRef with NIL result
ReclaimedRefNoDel: LONG INTEGER,  -- ReclaimedRef completions (not deleted)
ReclaimedRefDel: LONG INTEGER,  -- ReclaimedRef completions (already deleted)
ResetOnStackC: LONG INTEGER,  -- ResetOnStack completions
ReclaimAllC: LONG INTEGER,   -- ReclaimAll completions
CreateRefC: LONG INTEGER,   -- CreateRef completions
AssignRefC: LONG INTEGER,   -- AssignRef+AssignRefNew completions
AssignRefNewI: LONG INTEGER,  -- AssignRefNew inititations
-- (= AssignRefNew completions with page fault restarts as an error term)
RefOldEQNIL: LONG INTEGER,   -- RefOld=NIL in AssignRef
-- (plus page fault restarts & collector disabled AssignRefs as an error term)
RefNewEQNil: LONG INTEGER,   -- RefNew=NIL in AssignRef(New)
RefOldEQRefNew: LONG INTEGER  -- RefOld=RefNew in AssignRef
];

RCEToReclaimVec: TYPE = ARRAY[0..rceReclaimLimit) OF nRCEntry;
-- RCEToReclaimVec is big enough to hold the longest possible chain of overflow entries,
-- which is 64 entries (REFs are even)
rceReclaimLimit: CARDINAL = (LAST[Residue] + 1)/2;


gcDumpTable: TYPE = ARRAY[0..rceReclaimLimit) OF CARDINAL; -- for microcode debugging

-- Miscellany
MSOperation: TYPE = MACHINE DEPENDENT -- arg to RTMOVESTATUS
  { upDate(0), -- this means "move info about overflow free list to memory, and move other status info from memory to microcode.
   initialize(177777B)};


-- Parameter bundle for the microcode (AlterCount), for use with bit tweezers
rcoState: TYPE = MACHINE DEPENDENT RECORD
[ fill0(0:0..0): [0..1]← 0,
rcChange(0:1..7): [0..177B],
onStack(0:8..8): uBoolean,
fill(0:9..15): [0..177B]← 0
];

rcoAddRef: rcoState = [ rcChange: 1, onStack: true];  -- 600B
rcoDeleteRef: rcoState = [ rcChange: 177B, onStack: true]; -- 77600B
rcoJustCreated: rcoState = [ rcChange: 177B, onStack: true]; -- 77600B
rcoFinalizedRef: rcoState = [ rcChange: 1, onStack: false]; -- 400B
rcoNilifyRef: rcoState = [ rcChange: 177B, onStack: false]; -- 77400B
rcoFoundOnStack: rcoState = [ rcChange: 0, onStack: true]; -- 200B

rcoChangeMask: rcoState = [rcChange: 177B, onStack: false];
rcoOnstackMask: rcoState = [rcChange: 0, onStack: true];
rcoReclaimedRef: rcoState = [rcChange: 177B, onStack: false];
reclaimedRefDelta: CARDINAL = LOOPHOLE[rcoReclaimedRef];


-- V A R I A B L E S
GCMicrocodeExists: BOOLEAN;
useUCodedGC: BOOLEAN;
nObjectsReclaimed: INT;
nWordsReclaimed: INT;
GCState: LONG POINTER TO AGCState;
frameBodyBufferPages: CARDINAL;


-- P R O C E D U R E S

StuffZi: PROC[zi: RTZones.ZoneIndex, zone: ZONE];
StuffMapZiZn: PROC[p: LONG POINTER TO RTZones.PMapZiZn];
StuffMapQZf: PROC[p: LONG POINTER TO RTZones.RMapQZf];

IsTraceAndSweepEnabled: PROC RETURNS[BOOLEAN];
DoTraceAndSweepCollection: PROC;
ReclaimForQuanta: PROC RETURNS[INT]; -- returns number of objects reclaimed
MapReclaimableObjects: PROC;

InternalReclaim: PROC[reason: SafeStorage.ReclamationReason, suspendMe: BOOLEANFALSE, doStack: BOOLEANTRUE];

-- NOTE copied in RTMicrocode to break DEFS circularity
aCREATEREF: PrincOps.alpha = 67B;
CREATEREF: PROC[npr: rcoState, ref: REF] =
MACHINE CODE { PrincOps.zMISC, aCREATEREF };

CreateRef: PROC[npr: CARDINAL, refNew: REF] = -- called by the allocator
INLINE
{IF useUCodedGC
THEN {rco: rcoState ← [rcChange: npr, onStack: false]; [] ← CREATEREF[rco, refNew]}
ELSE SoftwareCreateRef[npr, refNew]};

SoftwareCreateRef: PROC[npr: CARDINAL, refNew: REF];

ClearRCTable: PROC; -- called by the TraceAndSweep collector
IncrementReferenceCount: PUBLIC PROC[ref: REF]; -- called by the TraceAndSweep collector
DecrementReferenceCount: PUBLIC PROC[ref: REF]; -- called by the TraceAndSweep collector

-- used when the microcode traps or there is no microcode support

DisableRC: PROC;
AlterCountEntry: PROC[rcv: rcoState, ref: REF];
ReclaimedRef: PROC[ref: REF] RETURNS[REF];
IsPiReclaimable: PROC[pi: ProbeIndex] RETURNS [fpi: ProbeIndex, npi: CARDINAL];
MapRcePiRef: PROC[rce: nRCEntry, pi: ProbeIndex] RETURNS[REF] = INLINE
{ OPEN Basics;
res: Residue = rce.res;
RETURN[LOOPHOLE[LongNumber[num[highbits: res,
     lowbits: BITSHIFT[BITXOR[pi, res], 1]]]]]};
IsOiCushion: PROC RETURNS[BOOLEAN];

GetMapPiRce: PROC RETURNS[LONG POINTER TO AMapPiRce];
GetMapOiOe: PROC RETURNS[LONG POINTER TO AMapOiOe];
InitializeCleanup: PROC[gcStateBank: CARDINAL];

SetAlwaysTrim: PROC[sense: BOOLEAN];

END.