<> <> DIRECTORY DebuggerSwap USING [WorryCallDebugger], PrincOps USING [ AllocationVectorSize, flagsClean, flagsVacant, Frame, FrameSizeIndex, logWordsPerPage, PageState, RealPageNumber], PrincOpsUtils USING [GetPageState, SetPageFlags, SetPageState], VM USING [ DataState, Interval, LogPageCount, PageCount, PageState, PageNumber, VMPartition, wordsPerPage]; VMInternal: DEFINITIONS LOCKS vmStateLock IMPORTS PrincOpsUtils, DebuggerSwap = BEGIN <<>> <> <<>> <> <<>> <> PageState: TYPE = VM.PageState; DataState: TYPE = VM.DataState; Interval: TYPE = VM.Interval; PageNumber: TYPE = VM.PageNumber; PageCount: TYPE = VM.PageCount; RealPageNumber: TYPE = PrincOps.RealPageNumber; -- 16M words of real memory VMPartition: TYPE = VM.VMPartition; DataLocation: PRIVATE TYPE = {in, out}; PinCount: PRIVATE TYPE = CARDINAL; VMMapEntry: PRIVATE TYPE = MACHINE DEPENDENT RECORD [ state(0): PrincOps.PageState, body(1): SELECT COMPUTED DataLocation FROM in => [real(1): RealPageNumber], out => [ fill(1:0..11): [0..7777B] _ 0, <> checkedOut(1:12..12): BOOL, readOnly(1:13..13): BOOL, dataState(1:14..15): DataState], ENDCASE]; RMState: PRIVATE TYPE = {free, reclaimable, pinned}; RMEntryPointer: PRIVATE TYPE = LONG POINTER TO RMMapEntry; RMMapEntry: PRIVATE TYPE = MACHINE DEPENDENT RECORD [ <> dataState(0:0..1): DataState, needsBackingStoreWrite(0:2..2): BOOL, available(0:3..4): [0..3] _ 0, body(0:5..31): SELECT rmState(0:5..6): RMState FROM free => [ availableF(0:7..15): [0..777B] _ 0, next(1:0..15): RealPageNumber ], reclaimable => [ <> referenced(0:7..7): BOOL _ FALSE, <> virtualHigh(0:8..15): [0..377B] _ 0, -- will eventually extend 'virtual' to 24 bits. virtual(1:0..15): WORD ], pinned => [ pinReason(0:7..15): PinReason _ normal, -- not essential, could be encoded in large 'pinCount' values pinCount(1:0..15): PinCount ] ENDCASE]; PinReason: TYPE = { normal, <> noSuchRealPage, <> permanentlyPinned, <> specialRealPageAvailable, <> specialRealPageInUse, <> cantBeWritten <> }; maxPinCount: PinCount = PinCount.LAST; lastVMPage: READONLY PageNumber; <> <> RMMapEntries: TYPE = RECORD[SEQUENCE COMPUTED RealPageNumber OF RMMapEntry]; GetVMMap: PROC [vmPage: PageNumber] RETURNS [entry: VMMapEntry] = <> INLINE {RETURN[LOOPHOLE[PrincOpsUtils.GetPageState[vmPage]]]}; SetVMMap: PROC [vmPage: PageNumber, entry: VMMapEntry] = INLINE {PrincOpsUtils.SetPageState[vmPage, LOOPHOLE[entry, in VMMapEntry].real, entry.state]}; InOut: PROC [entry: VMMapEntry] RETURNS [DataLocation] = INLINE {RETURN[IF entry.state.flags = PrincOps.flagsVacant THEN out ELSE in]}; Outcome: TYPE = {ok, addressFault}; <<>> <> <<>> vmStateLock: PRIVATE MONITORLOCK; freeList: PRIVATE RealPageNumber; freePages: PRIVATE INT; rmMap: PRIVATE LONG POINTER TO RMMapEntries; lastRealPage: PRIVATE RealPageNumber; AllocateRealMemoryForLocalFrames: ENTRY PROC [vmPage: PageNumber] = INLINE { <> victim: Victim = AllocateRealMemoryInternal[vmPage: vmPage, dirtyVictimOK: FALSE, pin: TRUE]; PrincOpsUtils.SetPageFlags[virtual: vmPage, real: victim.realPage, flags: PrincOps.flagsClean]; }; AllocateRealMemoryInternal: PRIVATE READONLY PROC [ vmPage: PageNumber, dirtyVictimOK: BOOL _ TRUE, pin: BOOL _ FALSE] RETURNS [victim: Victim]; <> Unpin: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; AddToFreeList: PRIVATE PROC [realPage: RealPageNumber] = INLINE { rmMap[realPage].body _ free[next: freeList]; freeList _ realPage; freePages _ freePages.SUCC; }; <> <<>> pagesIn64K: PageCount = 65536/VM.wordsPerPage; logPagesIn64K: VM.LogPageCount = 16 - PrincOps.logWordsPerPage; allocationLock: MONITORLOCK; <> AllocationOutcome: TYPE = {ok, notFound, badParameters}; AllocCount: TYPE = RECORD [ --*stats*-- requests: INT, requestsSatisfied: INT, pagesAllocated: INT, pagesFreed: INT ]; Partitions: TYPE = ARRAY VMPartition OF Interval; --*stats*-- allocCounts: ARRAY VMPartition OF AllocCount; partitions: Partitions; <<>> NoteFreePages: PROC [interval: Interval]; UpdateVMLimit: PROC [pages: PageCount]; AllocateVirtualMemoryInternal: READONLY PROC [ count: PageCount, partition: VM.VMPartition _ normalVM, subRange: Interval _ [0, 0], start: PageNumber _ 0, alignment: VM.LogPageCount _ 0, in64K: BOOL _ FALSE] RETURNS [outcome: AllocationOutcome, interval: Interval]; <= count, then 'interval' is contained in 'subRange', (4) interval.page MOD 2^alignment is zero, (5) if start is non-zero, then interval.page-start is minimal, (6) if in64K is TRUE, then the interval doesn't span a 64K word boundary. If such an interval is found, it is returned ('outcome' is 'ok') and, for every page in 'interval', State[page] = [data: undefined, readOnly: FALSE, hasRealMemory: FALSE, pinCount: 0]. If no such interval can be found, 'outcome' becomes 'notFound'.>> IsFree: --ENTRY-- PROC [vmPage: PageNumber] RETURNS [BOOL] = INLINE { <> vmEntry: VMMapEntry = GetVMMap[vmPage]; WITH vmE: vmEntry SELECT InOut[vmEntry] FROM in => RETURN[FALSE]; out => RETURN[vmE.dataState = none AND ~vmE.checkedOut]; ENDCASE => ERROR; }; MarkAllocated: PROC [vmPage: PageNumber] = INLINE { <> vmEntry: VMMapEntry _ GetVMMap[vmPage]; WITH vmE: vmEntry SELECT InOut[vmEntry] FROM in => Crash[]; out => IF vmE.dataState = none THEN {vmE.dataState _ undefined; SetVMMap[vmPage, vmE]} ELSE Crash[]; ENDCASE; }; <> State: PROC [vmPage: PageNumber] RETURNS [state: PageState]; SetDataState: PROC [vmPage: PageNumber, dataState: DataState] RETURNS [outcome: Outcome]; < ReadWrite>> MakeReadOnly: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; MakeReadWrite: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; <> swapBufferSize: PageCount = 50; SwapInOutcome: TYPE = { noReadNecessary, -- victim has been claimed; read unnecessary (dataState = undefined) alreadyIn, -- page already swapped in; no victim has been claimed needsRead, -- victim claimed; read required addressFault, -- page not allocated writeFault -- page is read only }; VictimState: TYPE = {clean, dirty}; Victim: TYPE = RECORD [ realPage: RealPageNumber, body: SELECT victimState: VictimState FROM clean => NULL, dirty => [vmPage: PageNumber], ENDCASE]; AllocateForSwapIn: PROC [vmPage: PageNumber, kill, pin: BOOL] RETURNS [outcome: SwapInOutcome, victim: Victim]; SwapInDone: PROC [vmPage, bufferPage: PageNumber, worked: BOOL]; SwapInDoneWithoutIO: PROC [vmPage: PageNumber, victim: Victim]; VictimWriteDone: PROC [ vmPage, bufferPage: PageNumber, victim: dirty Victim, worked: BOOL]; CleanOutcome: TYPE = { cantWrite, -- none of the following cases apply checkedOutClean, -- page is clean and swappable and checkOutClean = TRUE needsWrite, -- page is dirty and swappable addressFault -- page not allocated }; ConsiderCleaning: PROC [vmPage: PageNumber, checkOutClean: BOOL] RETURNS [outcome: CleanOutcome, real: RealPageNumber]; CleanDone: PROC [vmPage, bufferPage: PageNumber, worked: BOOL]; <<>> <> Age: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; <> GetCleaningCandidate: PROC [ desired: PageCount, comfortLevel: PageCount, tryHard: BOOL _ FALSE] RETURNS [Interval]; NoteDirtyVictim: PROC; <<>> <> IODirection: TYPE = {read, write}; IOResult: TYPE = {ok, labelCheck, someOtherError}; DoIO: PROC [ direction: IODirection, backingPage: PageNumber, interval: Interval, subsequentSeek: PageNumber _ 0] RETURNS [result: IOResult, done: PageCount]; HasBackingStorage: SAFE PROC [page: PageNumber] RETURNS [BOOL]; <> <<>> AnyFrameSizeIndex: TYPE = CARDINAL [0..PrincOps.AllocationVectorSize); -- note that an AnyFrameSizeIndex immediately precedes a Frame. NormalFrameSizeIndex: TYPE = PrincOps.FrameSizeIndex; FsiFrame: TYPE = MACHINE DEPENDENT RECORD [ fsi(0): AnyFrameSizeIndex, -- must be at 3 MOD 4 boundary. frame(1): local PrincOps.Frame]; FrameHeapPiece: TYPE = POINTER TO FrameHeapPieceHeader; FrameHeapPieceHeader: TYPE = MACHINE DEPENDENT RECORD [ next(0): FrameHeapPiece, -- must be at 0 MOD 4 boundary. wordsAllocated(1): CARDINAL, -- actually [0..VM.PagesToWords[PrincOps.shortPointerSpan]] wordsInUse(2): CARDINAL, -- assert: wordsInUse MOD 4 = 3 fsiFrame(3): ARRAY [0..0) OF --WORD-- FsiFrame]; -- must be at 3 MOD 4 boundary. largeFrameThresholdFsi: NormalFrameSizeIndex = 14; -- frames this size or larger will be allocated in an integral number of pages, and reclaimed when they are freed. LargeFrame: TYPE = POINTER TO LargeFrameHeader; LargeFrameHeader: TYPE = MACHINE DEPENDENT RECORD [ next(0): LargeFrame, prev(1): LargeFrame, trueFsi(2): NormalFrameSizeIndex, fsiFrame(3): ARRAY [0..0) OF --WORD-- FsiFrame]; -- must be at 3 MOD 4 boundary. <> frameHeapPieceList: FrameHeapPiece; -- list of frame heap pieces, excluding large frames largeFrameList: LargeFrame; -- list of large frames (some allocated, some freed) <<>> <> InitializeTables: PROC; Crash: PROC = INLINE {DebuggerSwap.WorryCallDebugger[NIL]}; END.