DIRECTORY DragonProcessOffsets, DragOpsCross, DragOpsCrossProcess, DragOpsCrossUtils, HandCoding, HandCodingSupport, HandCodingPseudos; GenBigStack: CEDAR PROGRAM IMPORTS DragOpsCrossUtils, HandCoding, HandCodingSupport, HandCodingPseudos = BEGIN OPEN DragonProcessOffsets, HandCoding, HandCodingSupport, HandCodingPseudos; StackedStatusWord: TYPE = DragOpsCross.StackedStatusWord; Word: TYPE = DragOpsCross.Word; initialFrames: NAT = 5*256; localFrameArraySize: NAT = 2*16; framesToTransfer: NAT = localFrameArraySize/2; StackLog: NAT = 7; StackSize: NAT = 128; statusMask: StackedStatusWord = [userMode: TRUE, trapsEnabled: TRUE, lBase: 377B]; regsPerNacho: NAT = 16; RegArray: TYPE = ARRAY [0..regsPerNacho) OF Word; Nacho: TYPE = LONG POINTER TO NachoRep; NachoRep: TYPE = RECORD [ link: Nacho, -- link to previous frame lastPC: Word, -- continuation PC for this frame nRegs: Word, -- number of regs used for this frame at last save others: Nacho, -- if # NIL, then it is a pointer to an aux frame regs: RegArray -- addressable by local regs ]; linkOffset: NAT = 0; constLink: ConstSpec = const0; lastPCOffset: NAT = 1; constLastPC: ConstSpec = const1; nRegsOffset: NAT = 2; constNRegs: ConstSpec = const2; othersOffset: NAT = 3; constOthers: ConstSpec = const3; regOff: CARDINAL = 4; -- offset of DumpBlock.regs LocalAllocationRep: TYPE = RECORD [ lockPtr: GlobalAllocationPtr, -- pointer to global lock for frame allocation ptrs: LocalArray -- start of frame pointers ]; LocalArray: TYPE = ARRAY [0..localFrameArraySize] OF Nacho; GlobalAllocationPtr: TYPE = LONG POINTER TO GlobalAllocationRep; GlobalAllocationRep: TYPE = RECORD [ lock: Word, -- global spin lock for frame allocation gFree: Word, -- pointer to the next free frame in frames emergency: Word, -- pointer to the emergency limit for frames frames: GlobalArray -- space for the global pool of nachos ]; GlobalArray: TYPE = ARRAY [0..initialFrames+1] OF Nacho; StackMargin: NAT _ 17; stackUnderflowTrap: Label _ NIL; internalSaveEldest: Label _ NIL; globalFreeNacho: Label _ NIL; globalAllocNacho: Label _ NIL; StackUnderflowTrap: PROC [stackUnderflowTrap: Label] = { { underflowEntry1: Label = GenLabel[]; underflowEntry2: Label = GenLabel[]; underflowEntry3: Label = GenLabel[]; WordAlign[]; drJ1[]; SetLabel[stackUnderflowTrap]; MakeLabelGlobal["DragonStack.StackUnderflowTrap", stackUnderflowTrap]; drLFC[UseLabel16[underflowEntry1]]; drSUBB[StackMargin]; ExtractField[first: 32-StackLog, bits: StackLog]; SetSPLimit[]; drLFC[UseLabel16[underflowEntry2]]; drJB[UseLabel8A[stackUnderflowTrap]]; WordAlign[]; SetLabel[underflowEntry1]; LRegI[hook, constNRegs]; GetYoungestStatus[]; drRVSUB[belowDst, popSrc, belowSrc]; ExtractField[first: 32-StackLog, bits: StackLog]; PReg[temp]; drDUP[]; SetYoungestStatus[]; drRETN[]; WordAlign[]; SetLabel[underflowEntry2]; LRegI[hook, constLink]; drLFC[UseLabel16[underflowEntry3]]; WordAlign[]; SetLabel[underflowEntry3]; }; { noHookLabel: Label = GenLabel[]; underflowNoSplice: Label = GenLabel[]; singleLabel: Label = GenLabel[]; multiLabel: Label = GenLabel[]; fatalLabel: Label = GenLabel[]; dispatchPC: CARD = HandCodingSupport.ReserveData[StackSize]; dispatchWord: Word = DragOpsCrossUtils.CardToWord[dispatchPC / DragOpsCross.bytesPerWord]; drRJEB[topSrc, const0, UseLabel8B[noHookLabel]]; drRSB[nRegsOffset]; drDUP[]; ExtractField[first: 32-StackLog, bits: StackLog]; drRJNEBJ[popSrc, const0, UseLabel8B[underflowNoSplice]]; drRRX[topDst, belowSrc, constNRegs]; LReg[temp]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert: TRUE, mask: StackLog]]]; SetYoungestStatus[]; drDUP[]; drRRX[belowDst, topSrc, constLink]; drRSB[lastPCOffset]; SetYoungestPC[]; FreeNacho[]; LRegI[hook, constNRegs]; drLFC[UseLabel16[underflowNoSplice]]; SetLabel[noHookLabel]; drLC0[]; WordAlign[]; SetLabel[underflowNoSplice]; LReg[temp]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert: TRUE, mask: StackLog]]]; SetYoungestStatus[]; LRegI[hook, constLastPC]; SetYoungestPC[]; LRegI[hook, constNRegs]; ExtractField[first: 32-StackLog, bits: StackLog]; drLIQB[dispatchWord]; drQRX[topAtop, belowSrc]; drJSD[]; SetLabel[singleLabel]; FOR i: NAT DECREASING IN [0..regsPerNacho) DO drRAI[reg1: [reg[i]], reg2: hook, disp: regOff+i]; ENDLOOP; MoveReg[topDst, hook]; FreeNacho[]; MakeLabelGlobal["DragonStack.StackUnderflowReturn", GenLabelHere[]]; SReg[hook]; drRETN[]; SetLabel[fatalLabel]; Pause[]; SetLabel[multiLabel]; FOR i: NAT IN [0..regsPerNacho) DO drRAI[reg1: [reg[i]], reg2: hook, disp: regOff+i]; ENDLOOP; LReg[hook]; MoveRegI[hook, hook, constOthers]; FreeNacho[]; drAL[regsPerNacho]; drSUBB[regsPerNacho]; drLIQB[dispatchWord]; drQRX[topAtop, belowSrc]; drJSD[]; { area: HandCodingSupport.Area _ HandCodingSupport.GetCurrentArea[]; oldPC: CARD _ HandCodingSupport.GetOutputPC[]; HandCodingSupport.SetOutputPC[dispatchPC]; FOR nr: NAT DECREASING IN [0..regsPerNacho] DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[singleLabel.offset+3*nr]]; ENDLOOP; FOR nr: NAT IN (regsPerNacho..regsPerNacho*4] DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[multiLabel.offset]]; ENDLOOP; FOR nr: NAT IN (4*regsPerNacho..StackSize) DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[fatalLabel.offset]]; ENDLOOP; HandCodingSupport.SetOutputPC[oldPC]; }; }; }; StackOverflowTrap: PROC [underflowLabel: Label] = { internalSaveSetup: Label _ GenLabel[]; exitLabel: Label _ GenLabel[]; dispatchLabel: Label = GenLabel[]; dispatchPC: CARD = HandCodingSupport.ReserveData[StackSize]; dispatchWord: Word = DragOpsCrossUtils.CardToWord[dispatchPC / DragOpsCross.bytesPerWord]; { ifuEntryLabel: Label = GenLabel[]; ProcedureEntry[ifuEntryLabel, 0, TRUE]; MakeLabelGlobal["DragonStack.IFUStackOverflowTrap", ifuEntryLabel]; FillTrap[IFUStackOverflowTrap, ifuEntryLabel]; GetEldestPC[]; drDIS[]; GetEldestStatus[]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; SetLabel[dispatchLabel]; drLIQB[dispatchWord]; drQRX[topAtop, belowSrc]; drSFC[]; SetLabel[exitLabel]; MakeLabelGlobal["DragonStack.StackOverflowExit", exitLabel]; drLIQB[UseLabel32[underflowLabel]]; SetEldestPC[]; ExtractField[first: 32-StackLog, bits: StackLog]; drDUP[]; SetEldestStatus[]; drSUBB[StackMargin]; ExtractField[first: 32-StackLog, bits: StackLog]; SetSPLimit[]; MakeLabelGlobal["DragonStack.StackOverflowReturn", GenLabelHere[]]; drRETN[]; }; { euEntryLabel: Label = GenLabel[]; ProcedureEntry[euEntryLabel, 0, TRUE]; MakeLabelGlobal["DragonStack.EUStackOverflowTrap", euEntryLabel]; FillTrap[EUStackOverflowTrap, euEntryLabel]; GetEldestPC[]; drDIS[]; GetEldestStatus[]; { loopLabel: Label = GenLabelHere[]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; drRJNEBJ[left: topSrc, right: const0, dist: UseLabel8B[dispatchLabel]]; drRJEBJ[popSrc, const0, UseLabel8B[loopLabel]]; }; }; { exitEntryLabel: Label = GenLabel[]; ProcedureEntry[exitEntryLabel, 0, TRUE]; GetEldestStatus[]; MakeLabelGlobal["DragonStack.InstallBogusFrame", exitEntryLabel]; drJB[UseLabel8A[exitLabel]]; }; { exitEntryLabel: Label = GenLabel[]; ProcedureEntry[exitEntryLabel, 0]; MakeLabelGlobal["DragonStack.RemoveBogusFrame", exitEntryLabel]; GetEldestPC[]; drDIS[]; GetEldestStatus[]; ProcedureExit[1]; }; { eldestStatus: RegSpec = reg0; localHook: RegSpec = reg1; localStatus: RegSpec = reg2; ProcedureEntry[internalSaveSetup, 2]; LReg[hook]; drPSB[linkOffset]; PReg[hook]; PReg[temp]; LReg[eldestStatus]; ExtractField[first: 32-StackLog, bits: StackLog]; SetYoungestStatus[]; GetEldestPC[]; drSRIn[localHook, lastPCOffset]; GetEldestStatus[]; drRVSUB[c: pushDst, a: localStatus, b: eldestStatus]; MoveReg[eldestStatus, localStatus]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert: TRUE, mask: StackLog]]]; drWRI[localStatus, localHook, nRegsOffset]; drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[mask: StackLog]]]; ProcedureExit[2]; }; { singleLabel: Label = GenLabel[]; multiLabel: Label = GenLabel[]; fatalLabel: Label = GenLabel[]; shortLabel: Label = GenLabel[]; SetLabel[internalSaveEldest]; MakeLabelGlobal["DragonStack.InternalSaveEldest", internalSaveEldest]; AllocNacho[]; drLFC[UseLabel16[internalSaveSetup]]; drLIQB[dispatchWord]; drQRX[topAtop, belowSrc]; drJSD[]; SetLabel[singleLabel]; FOR i: NAT DECREASING IN [0..regsPerNacho) DO drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; drDIS[]; drRETN[]; SetLabel[fatalLabel]; Pause[]; SetLabel[multiLabel]; FOR i: NAT IN [0..regsPerNacho) DO drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i]; ENDLOOP; LReg[temp]; AllocNacho[]; PReg[temp]; drWSB[othersOffset]; drAL[regsPerNacho]; drSUBB[regsPerNacho]; drLIQB[dispatchWord]; drQRX[topAtop, belowSrc]; drJSD[]; { area: HandCodingSupport.Area _ HandCodingSupport.GetCurrentArea[]; oldPC: CARD _ HandCodingSupport.GetOutputPC[]; HandCodingSupport.SetOutputPC[dispatchPC]; FOR nr: NAT DECREASING IN [0..regsPerNacho] DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[singleLabel.offset+3*nr]]; ENDLOOP; FOR nr: NAT IN (regsPerNacho..regsPerNacho*4] DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[multiLabel.offset]]; ENDLOOP; FOR nr: NAT IN (4*regsPerNacho..StackSize) DO HandCodingSupport.OutputWord[area, DragOpsCrossUtils.IntToWord[fatalLabel.offset]]; ENDLOOP; HandCodingSupport.SetOutputPC[oldPC]; }; }; }; AllocNacho: PROC = { exitLabel: Label = GenLabel[]; enterLabel: Label = GenLabelHere[]; LRegI[free, const0]; drLC0[]; drRJNEBJ[left: topSrc, right: belowSrc, dist: UseLabel8B[exitLabel]]; drLFC[UseLabel16[globalAllocNacho]]; drJB[UseLabel8A[enterLabel]]; SetLabel[exitLabel]; AddReg[free, const1]; drPSB[othersOffset]; }; GlobalAllocNacho: PROC = { exitLabel: Label = GenLabel[]; globalAllocNacho _ GenLabelHere[]; MakeLabelGlobal["DragonStack.GlobalAllocNacho", globalAllocNacho]; drRRX[c: belowDst, a: base, b: popSrc]; LReg[process]; drLC0[]; {retryLabel: Label = GenLabelHere[]; drCST[0]; drRJNEB[left: popSrc, right: belowSrc, dist: UseLabel8B[retryLabel]]; drAS[256-2]; }; drRSB[1]; drLIB[framesToTransfer]; drRVSUB[free, free, popSrc]; FOR i: NAT IN [0..framesToTransfer) DO drRSB[i]; LReg[free]; drWB[i]; ENDLOOP; drADDB[framesToTransfer]; drPSB[1]; drLC0[]; drWSB[0]; drRETN[]; }; FreeNacho: PROC [] = { exitLabel: Label = GenLabel[]; enterLabel: Label = GenLabelHere[]; drRVSUB[c: pushDst, a: free, b: const1]; drRJNEBJ[left: topSrc, right: base, dist: UseLabel8B[exitLabel]]; drLFC[UseLabel16[globalFreeNacho]]; SetLabel[exitLabel]; PReg[free]; drWB[0]; }; GlobalFreeNacho: PROC = { globalFreeNacho _ GenLabelHere[]; MakeLabelGlobal["DragonStack.GlobalFreeNacho", globalFreeNacho]; drRRX[c: topDst, a: base, b: const0]; LReg[process]; drLC0[]; {retryLabel: Label = GenLabelHere[]; drCST[0]; drRJNEB[left: popSrc, right: belowSrc, dist: UseLabel8B[retryLabel]]; drAS[256-2]; }; drRSB[1]; drSUBB[framesToTransfer]; FOR i: NAT IN [0..framesToTransfer) DO LReg[free]; drRB[i]; drPSB[i]; ENDLOOP; drPSB[1]; drLC0[]; drWSB[0]; drLIB[framesToTransfer-1]; drRVADD[c: topDst, a: topSrc, b: free]; drRETN[]; }; GenFramesInit: PROC = { allocator: Label = HandCodingPseudos.GetGlobalLabel["Basics.AllocVector"]; rZero: RegSpec = reg0; rLB: RegSpec = reg1; -- base of local frame table rSB: RegSpec = reg2; -- base of shared frame table tPtr: RegSpec = reg3; -- pointer to next slot in global table drLC0[]; -- init rZero drLIB[SIZE[LocalAllocationRep]/2]; drLFC[UseLabel16[allocator]]; drROR[base, topSrc, const0]; drLIDB[framesToTransfer*2+initialFrames]; drLFC[UseLabel16[allocator]]; drWRI[rSB, rLB, 0]; drWRI[rZero, rSB, 0]; drWRI[rZero, rSB, 2]; drRVADD[pushDst, rSB, const3]; drWRI[tPtr, rSB, 1]; { loopLabel: Label = GenLabelHere[]; drLIB[20]; drLFC[UseLabel16[allocator]]; drSRIn[tPtr, 0]; drRVADD[tPtr, tPtr, const1]; drLRn[tPtr]; drSUBDB[initialFrames]; drRJNEBJ[left: popSrc, right: rSB, dist: UseLabel8B[loopLabel]]; }; drLIB[17]; drRADD[free, base, popSrc]; drWRI[rZero, rLB, 17]; drAS[256-4]; }; All: PROC = { continueLabel: Label = GenLabel[]; underflowLabel: Label = GenLabel[]; internalSaveEldest _ GenLabel[]; GenFramesInit[]; drLFC[UseLabel16[continueLabel]]; GlobalAllocNacho[]; GlobalFreeNacho[]; StackUnderflowTrap[underflowLabel]; StackOverflowTrap[underflowLabel]; SetLabel[continueLabel]; drLIQB[UseLabel32[underflowLabel]]; SetYoungestPC[]; GetYoungestStatus[]; drLIQB[LOOPHOLE[DragOpsCross.StackedStatusWord[trapsEnabled: TRUE]]]; drOR[]; drDFC[UseLabel32[HandCodingPseudos.GetGlobalLabel["Basics.SetStatus"]]]; MakeLabelGlobal["DragonStack.ExitToUser", GenLabelHere[]]; }; FillTrap: PROC [tx: DragOpsCross.TrapIndex, dest: Label] = { area: HandCodingSupport.Area = HandCodingSupport.GetCurrentArea[]; oldPC: CARD = HandCodingSupport.GetOutputPC[area]; SetOutputPC[DragOpsCrossUtils.TrapIndexToBytePC[tx]]; drJDB[UseLabel16[dest]]; HandCodingSupport.SetOutputPC[oldPC]; }; END. E|GenBigStack.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) September 19, 1986 10:07:30 am PDT GenBigStack.All[] generates code for stack saving and restoring. Size parameters (the only important difference between this GenStack.mesa and GenBigStack.mesa) Initial frames to allocate. # of frames in localArray Number of frames to transfer between local and global frame pools. (this # should be explored) Note: StackSize = 2^StackLog The mask for the "interesting" bits in the stacked status word. type of holder for frames per processor, limit is quite arbitrary (need not be fixed) NIL is stored at the very end to make test for empty easy type of holder for frames, limit is quite arbitrary (need not be fixed) NIL is stored at the very end to make test for empty easy Number of words in EU stack to reserve to handle stack overflow. Will this really be enough? This has hardware implications as well (see Don Curry) Global labels Entry point for handling stack underflow (aka DragonStack.StackUnderflowTrap). Also used as the PC for the bogus frame. Entry point for the internal routine (aka DragonStack.InternalSaveEldest) that saves the eldest frame. Entry point for the internal routine that frees up a nacho to the central pool. Entry point for the internal routine that allocates up a nacho to the central pool. This code is called when we return to the bogus frame, which has an invalid PC. At entry, maskable traps are disabled, and the IFU stack has exactly 1 frame (if the world is valid). The top of stack has no words that we can depend on. The idea is to just bring in one frame from the hook, and then "return" to it, enabling traps as we go. At the underflow point we have no frames on the IFU stack, provided that we are in kernel mode. If not in kernel mode, then we will soon trap. No code in user mode can fake this entry in kernel mode, so we don't have to check. At this point the stack may contain return values from the procedure that just returned to us. Make the LFC take up the remainder of the bytes, so the following instruction does not take straddle a word. This is believed to help the IFU. This is the bogus frame PC when the bogus frame is created using the stack overflow code. The saved status will have traps disabled and be in kernel mode, and the saved L value will be the current L. This call sets L and the temp register to the new L. [S] = new L Calculate & set the new stack limit. S is back to the point it had at entry. This call creates a frame with return PC pointing at the alternate stack underflow entry point. This is the bogus frame PC when the bogus frame is created using the stack underflow code. It does the same thing (obviously) as the other entry point. At this point we have one frame on the IFU stack, which will become the bogus frame, but its saved value for L is not yet correct. So we fix it here, which sets L on return. This code also sets the temp reg to that new value of L, and pushes it onto the stack. We assume that the hook register points at a valid nacho! [S] holds the new value for L, but it needs masking. Both temp and [S] hold the new value for L, properly masked. Set the new youngest L, which will set L on return. At this point we have created the bogus frame with a good values for the PC and status words. Also, L and the temp register have good values, and S is as it was at stack entry. Note: this assumes that hook # NIL! [S] = next hook This call creates a frame that will be used for a genuine restored frame, although both PC and status bits will need to be set. [S] = next hook At this point we have two frames on the IFU stack, and the elder one (the bogus frame) is completely correct. At this point we have two frames on the IFU stack. The elder (which is the bogus frame) is correct, but the younger one has nothing of interest (actually, the L part is correct, but we can't use that fact). The frame we want to restore may have an immediately elder frame (in the saved chain) that has zero words in it. If so, we have to restore that one first. Also, we have pushed the next hook: [S] = next hook Just in case there is no next hook! At this point we have a zero-word frame to splice in. This involves setting the PC and status properly, then returning the nacho, then creating a new youngest frame. [S] = #regs (+ status bits), but #regs = 0! [S-1] = frame to splice temp = current L (also L for the bogus frame, as well as for the restored frame) [S] = #regs (+ status bits) for the frame to splice [S-1] = frame to splice push the new L insert the new L into the status word [S] = frame to splice, youngest status is now correct Fetch the next elder frame to the spliced frame [S] = frame to splice, [S-1] = next elder frame Set the new frame's PC [S] = frame to splice, [S-1] = next elder frame free the frame we just spliced in [S] = next elder frame (next hook) [S] = #regs (+ status bits) for the hook [S-1] = next hook We will never return here! Show that we have no registers! At this point we have a frame to splice in. [S] = #regs (+ status bits) for the hook [S-1] = next hook (may be 0) temp holds the bogus frame L. push the new L insert the new L into the status word set the youngest frame status word [S] = next value for hook, youngest status is now correct [S] = next value for hook, youngest status & PC is now correct Push regsPerNacho, then push the # of registers to restore [S] = #regs, [S-1] = next hook Dispatch to save the proper # of regs [S] = # of regs to save [S-1] = next hook At this point we execute a varying number of RAIs to restore the regs [S] = # of regs to restore [S-1] = next hook free the nacho pointed to by hook (& flush the # regs) [S] = next hook Now S is at its initial value, hook is properly set, and the IFU stack is correct for the coming return. Return from kernel, no change to S. We should never have this happen! Control only gets here for a truly smashed nacho! Now do regsPerNacho reads into the stack registers from the dump block push the nacho to free; point hook at the next nacho in the chain [S] = nextNacho, [S-1] = #regs free the nacho pointed to by [S] [S] = # regs adjust L and #regs [S] = #regs left Dispatch to save the proper # of regs [S] = # of regs to save [S-1] = next hook Generate the dispatch table Note: we assume that there is sufficient reserve space on the EU stack and the IFU stack to complete the saving of one frame. Traps are automatically disabled on entry to this routine, and must be reenabled on the way out. DragonStack.IFUStackOverflowTrap: This part is for IFU stack overflow. Note that we are NOT assured of a non-empty frame on the IFU stack. We get no arguments. discard the bogus frame [S] holds the eldest status Allocate the next save block to use ([S] = newBlock, [S-1] = eldest status) Init the new save block. Its address is left in hook (and in temp). L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status We get here to do the dispatch to the appropriate register saving code. Dispatch to restore the proper # of regs On return: [S] = eldest status This is the common exit stuff for the stack overflow handlers. The idea is to keep the stack limit register set with enough reserve space to be able to prevent wraparound when we execute code where traps are disabled. Also, the saved L for the bogus frame must be consistent with the number of registers in the youngest saved frame (referenced by hook). At this point: [S] holds the eldest status Install the new bogus frame & set its PC (creating a new eldest frame) Calculate & set the L for the bogus frame. [S] still holds that L. Calculate & set the new stack limit. S is now back to its original depth. At this point we go back to the code that overflowed. DragonStack.EUStackOverflowTrap: If we save a frame, but don't get any more space in the EU stack, then we have to keep saving frames until a frame is saved that has at least one EU register saved with it. We know that there is a non-empty frame on the register stack, otherwise why would we get an EU stack overflow? We get no arguments. discard the bogus frame [S] holds the eldest status [S] = eldest status Allocate the next save block to use ([S] = newBlock, [S-1] = eldest status) Init the new save block. Its address is left in hook (and in temp). L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status If we have registers to save, then go do them. Discard [S] and go loop (unconditionally, really). [S] = eldest status InstallBogusFrame is useful for external callers of DragonStack.InternalSaveEldest. Once the necessary frames have been saved, the bogus frame can be created using this routine. push the eldest status RemoveBogusFrame is useful for external callers of DragonStack.InternalSaveEldest. Before InternalSaveEldest can be called, the bogus frame must be removed, and the current eldest status must be pushed. flush the eldest PC (removes the eldest frame) push the new eldest status (as needed by InternalSaveEldest) return the new eldest status This is an internal routine that we use to setup stuff (especially L) prior to saving the frame. It needs to be a procedure because we have to set L (sigh). before entry: hook = frame chain [S] = newBlock, [S-1] = eldest status after exit: L = L of frame to save [S] = #regs, [S-1] = eldest status Make our local L good for a few locals, we get one sent to us on the stack store the hook into the new dump block [S] = localHook = newBlock; (localHook+linkOffset)^ = hook store the new dump block addr into hook and temp [S] = localHook = hook = temp = newBlock Get the L for the frame we wish to save, put it into localStatus and youngest L [S] = localStatus = L of frame to save; [S-1] = localHook, [S-2] = eldest status (note that the status bits must be clear in [S]) Store the continuation PC and remove the eldest PC from the ifuStack. ((localHook+lastPCOffset)^ = PC of frame to save) Sample & save the new eldest status [S] (aka localStatus) = eldest status (the new version) [S-1] = localHook [S-2] = eldest status (the old version) Calculate the number of regs in this frame [S] = # of regs to save (with garbage in top bits) [S-1] (aka localStatus) = status [S-2] = localHook [S-3] = eldest status [S] = # of regs to save (with garbage in top bits) [S-1] (aka localStatus) = eldest status (the new version) [S-2] = localHook [S-3] = eldest status (the new version) insert # of regs into the saved status word (aka localStatus) [S] (aka localStatus) = #regs (+ status bits) [S-1] (aka localHook) [S-2] = eldest status Save #regs into the save block & into eldestStatus (localHook+nRegsOffset)^ _ localStatus [S] = #regs, [S-1] = localHook, [S-2] = eldest status [S] = #regs, [S-1] = eldest status return the values [S] = #regs, [S-1] = eldest status DragonStack.InternalSaveEldest: This is the entry point for internally saving the eldest stack frame in the IFU stack. The bogus frame has been removed. [S] holds the sampled eldest status. Allocate the next save block to use ([S] = newBlock, [S-1] = eldest status) Init the new save block. Its address is left in hook. L gets set to the L for the frame we want to save. [S] = #regs, [S-1] = eldest status Dispatch to restore the proper # of regs after JSD: [S] = #regs, [S-1] = eldest status Put the stack back to the original level [S] = eldest status This return does not enable traps, and does not disturb S, but does restore L, which was previously clobbered. S must have the same value that it did at entry! [S] = eldest status For now we just crash. Eventually we should be more robust, saving away the regs, then giving the offending process a "stack fault". Push the address of the completed save block ([S] = temp = oldBlock; [S-1] = #regs; [S-2] = eldest status) Allocate the next save block to use (it gets left on the stack) ([S] = newBlock; [S-1] = temp = oldBlock; [S-2] = #regs) set temp to the new dump block addr also store the new block addr into the old block (temp = (temp'+othersOffset)^ = newBlock; [S] = #regs) adjust L and #regs [S] = #regs, [S-1] = eldest status Dispatch to restore the proper # of regs [S] = #regs, [S-1] = eldest status Generate the dispatch table Allocate a nacho using the local array of nachos, defaulting to the global array of nachos if not immediately successful. The address of the allocated nacho is left on the stack. There is no provision for failure from the global array at this time. Push the next nacho address. If none in our local cache, a 0 will be pushed. push 0 for comparison (this cycle is free due to the preceding fetch) Jump if we got the nacho; leave 0 on stack if not. Predict success. Call to do this the expensive way (S_S-2) Go to retry the allocation from the start (stack is back to ground level). This is the successful exit point. The allocated nacho is on the stack. Adjust the free pointer, since we got a nacho [S] = 0, [S-1] = new nacho always clears the others field on allocation [S] = new nacho Replenish the local nacho array from the global nacho array. There is no provision for failure from the global array at this time. At entry, [S] = [S-1] = 0, at exit S_S-2, but the local array has nachos. AllocNacho is the only caller of this routine! Replace [S-1] (was 0) with @lock; also S_S-1 ([S] = @lock) Push the desired new value for the lock field ([S] = process; [S-1] = @lock) Push the assumed old value of the lock field (i.e. free) ([S] = 0; [S-1] = process; [S-2] = @lock) Try for the global lock for the nacho allocator. We store our process ID into the lock if the lock was free, and return the sampled value of the lock at the start of the instruction in any case. ([S] = sample; [S-1] = 0; [S-2] = process; [S-3] = @lock) Jump back to retry if we did not get it (predict success), and pop the sampled value. If this loop causes us to hold the bus too often, we can always insert a spin loop that does not use CST. ([S] = 0; [S-1] = process; [S-2] = @lock) On success, flush the old & new values, since we don't need them. ([S] = @lock) Get the global free pointer (gFree) on the stack (@base.gFree = @base.lock + 1) ([S] = gFree; [S-1] = @lock) Adjust the free pointer to accomodate the next transfers. (free+i)^ _ (gFree+i)^ adjust gFree for the number of nachos allocated ([S] = new gFree; [S-1] = @lock) Write gFree back, leaving the addr of the lock on the stack. ([S] = @lock) Clear out the lock, which lets other processors get their chance. (stack is now at original level) Free a nacho (in [S]) using the local array of nachos, defaulting to the global array of nachos if not immediately successful. There is no provision for failure from the global array at this time. [S] = free-1; [S-1] = nacho to free Determine if free-1 will collide with base, jump if not. Free-1 is left on the stack for use in storing away the freed frame. Predict success. In the hard case we call a routine to munch on the global table. At this point the address of the slot to hold the nacho is on the stack. First we store that address to free, then we load the nacho to be freed and store it into the slot. (free _ [S])^ _ [S-1]; S_S-2 Free up a nacho (in [S-1]) to the central pool. We have already tested for the fast case, and we call this when [S] = free-1; [S-1] = nacho to free. At exit, [S] = new free, [S-1] = nacho to free. Determine if free-1 will collide with base, jump if not. Free-1 is left on the stack for use in storing away the freed frame. Predict success. Put the addr of the lock on the stack. We get to have a pop for free here. ([S] = @lock) Push the desired new value for the lock field ([S] = process; [S-1] = @lock) Push the assumed old value of the lock field (i.e. free) ([S] = 0; [S-1] = process; [S-2] = @lock) Try for the global lock for the nacho allocator. We store our process ID into the lock if the lock was free, and return the sampled value of the lock at the start of the instruction in any case. ([S] = sample; [S-1] = 0; [S-2] = process; [S-3] = @lock) Jump back to retry if we did not get it (predict success), and pop the sampled value. If this loop causes us to hold the bus too often, we can always insert a spin loop that does not use CST. ([S] = 0; [S-1] = process; [S-2] = @lock) On success, flush the old & new values, since we don't need them. The lock address is left on the stack. ([S] = @lock) get the global free pointer (gFree) on the stack (leaving the lock addr under it) ([S] = gFree; [S-1] = @lock) gFree _ gFree - framesToTransfer; leave gFree on stack ([S] = new gFree; [S-1] = @lock) (gFree+i)^ _ (free+i)^ write gFree back, leaving the lock addr on the stack ([S] = @lock) clear out the lock, which lets other processors get theirs (stack is now at original level) ([S] = free + (framesToTransfer-1)) This is the successful exit point. There is room between free and base. allocate the per processor frame base (LocalAllocationRep) also put that frame into base and leave in rLB allocate space for the shared frame base (GlobalAllocationRep) also store that address into rLB.lockPtr and leave it in rSB clear out the shared frame base init rSB.gFree and tPtr Allocate the new frames from the basic system allocator. Each frame gets stuck into the shared frame base (GlobalAllocationRep) in a new slot. Allocate and store the new frame stop when tPtr gets far enough from the base now make free point at the next local slot to fill also, clear out that slot Finally, drop the junk from the stack Entry point for the internal routine (aka DragonStack.InternalSaveEldest) that saves the eldest frame. Order of generation is important! Setup the bogus frame & enable traps (to detect stack overflow). Κ˜˜codešœ™Kšœ Οmœ7™BK™6—˜šœ@™@K™šΟk ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜———headšœ žœž˜KšžœD˜KKšœžœžœH˜TK˜K–20 sp tabStopsšœžœ"˜9K–20 sp tabStopsšœžœ˜K–20 sp tabStops˜–20 sp tabStops™_J–20 sp tabStops˜–20 sp tabStopsšœžœ ˜J–20 sp tabStopsšœ™—–20 sp tabStopsšœžœ˜ J–20 sp tabStopsšœ™—šœžœ˜.šœB™BK™——J–20 sp tabStops™—K–20 sp tabStopsšœ žœ˜–20 sp tabStopsšœ žœ˜K–20 sp tabStopsšœ™—K–20 sp tabStops˜šœ+žœžœ˜RKšœ?™?K˜—K–20 sp tabStopsšœžœ˜K–20 sp tabStopsšœ žœžœžœ˜1K–20 sp tabStopsš œžœžœžœžœ ˜'–36 sp tabStopsšœ žœžœ˜K–36 sp tabStopsšœ Οc˜&K–36 sp tabStopsšœŸ!˜/K–36 sp tabStopsšœ Ÿ2˜?K–36 sp tabStopsšœŸ1˜@K–36 sp tabStopsšœŸ˜+K–36 sp tabStopsšœ˜–36 sp tabStopsšœ žœ˜K–36 sp tabStopsšœ˜—–36 sp tabStopsšœžœ˜K–36 sp tabStopsšœ ˜ —–36 sp tabStopsšœ žœ˜K–36 sp tabStopsšœ˜—–36 sp tabStopsšœžœ˜K–36 sp tabStopsšœ ˜ —K–36 sp tabStopsšœžœŸ˜1—K˜–36 sp tabStopsšœžœžœ˜#K–36 sp tabStopsšœŸ.˜LK–36 sp tabStopsšœŸ˜+K–36 sp tabStopsšœ˜–24 sp tabStopsšœ žœžœžœ˜;K–24 sp tabStopsšœU™UK–24 sp tabStops™9——K–24 sp tabStops˜K–24 sp tabStopsš œžœžœžœžœ˜@–24 sp tabStopsšœžœžœ˜$K–24 sp tabStopsšœ Ÿ(˜4K–24 sp tabStopsšœ Ÿ+˜8K–24 sp tabStopsšœŸ,˜=K–24 sp tabStopsšœŸ&˜:K–24 sp tabStopsšœ˜–24 sp tabStopsšœ žœžœžœ˜8K–24 sp tabStopsšœG™GK–24 sp tabStops™9——K˜šœ žœ˜Kšœ•™•—K–24 sp tabStops™–24 sp tabStopsšœ ™ K–24 sp tabStops˜–24 sp tabStopsšœžœ˜ K–24 sp tabStopsšœx™x—–24 sp tabStopsšœžœ˜ K–24 sp tabStopsšœf™f—–24 sp tabStopsšœžœ˜K–24 sp tabStopsšœO™O—–24 sp tabStopsšœžœ˜K–24 sp tabStopsšœS™S——K–24 sp tabStops™šΟnœžœ ˜8K™ΤK˜šœ˜KšœΔ™ΔK™Kšœ$˜$Kšœ$˜$Kšœ$˜$K˜K˜ ˜Kšœ™—Kšœ˜šœΟbœ˜FK™KšœΘ™ΘK˜˜#šœ4™4K™ ——Kšœ˜Kšœ1˜1š‘ œ˜ KšœM™M—˜#Kšœ_™_—šœ%˜%K™˜——K˜K˜ šœ˜K˜KšœΑ™ΑK™Kšœ˜Kš‘œ˜šœ$˜$K™4—šœ>˜>K™<—K˜š‘œ˜Kšœ3™3—K˜ —K˜K˜ šœ˜K™Kšœ±™±K˜šœ˜™#K™——˜#šœ™K™———K˜K˜ šœ˜Kšœm™m—K˜K˜—˜šœ‘™‘K™—K˜K˜ K˜&Kšœ ˜ K˜Kšœ˜Kšœ žœ,˜—K˜šœK˜Kšœ:™:Kšœ™——K˜Kšœ˜Kšœ˜˜šœ%™%K™Kšœ™——K˜—šœ˜™EK™K™—š žœžœž œžœž˜-Kšœ2˜2Kšžœ˜—šœ#˜#šœ6™6Kšœ™——K˜Kšœ‘ œ˜Dšœ ˜ Kšœh™h—šœ ˜ Kšœ#™#——K˜šœ˜K™TK˜K˜—˜KšŸœ Ÿ3™Fšžœžœžœž˜"Kšœ2˜2Kšžœ˜K˜—šœ.˜.šœA™AKšœ™——˜ šœ ™ Kšœ ™ ——šœ)˜)šœ™Kšœ™——Kšœ˜Kšœ˜˜™%K™Kšœ™——K˜—˜Kšœ™KšœB˜BKšœžœ#˜.Kšœ*˜*š žœžœž œžœž˜.KšœY˜YKšžœ˜—šžœžœžœ ž˜0KšœS˜SKšžœ˜—šžœžœžœž˜-KšœS˜SKšžœ˜—Kšœ%˜%K˜—K˜—K˜K˜—š œžœ˜3Kšœί™ίKšœ&˜&K˜Kšœ"˜"Kšœ žœ,˜‘E™ƒK™yK˜Kšœ˜Kšœ"˜"Kšœ‘œ˜BK˜˜'™,K™ ——šœ˜šœ-™-K™——˜šœ8™8K™)——K˜˜$šœ ˜ šœΓ™ΓK™9——šœE˜Ešœΐ™ΐK™)——˜ šœA™AK™ ——K˜—K˜˜ šœ0™0Kšœ™K™——šœ6˜6Kšœ9™9—šžœžœžœž˜&šœ ˜ K™—Kšžœ˜—šœ˜šœ/™/K™ ——˜ šœ<™K˜Kšœ Ÿ ˜Kšœžœ˜"šœ;˜;Kšœ:™:Kšœ.™.—K˜Kšœ)˜)Kšœ˜šœ˜Kšœ>™>Kšœ<™<—K˜šœ,˜,Kšœ™—K˜šœ4˜4Kšœ™—K˜˜Kšœ‘™‘K˜"šœ;˜;Kšœ ™ —Kšœ˜Kšœ%˜%šœ@˜@Kšœ,™,—K˜K˜—šœ?˜?Kšœ2™2K™—K˜˜ Kšœ%™%—K˜K˜—š œžœ˜ K˜K˜"K˜#K˜–24 sp tabStopsšœ ˜ K–24 sp tabStopsšœf™f—Kšœ˜K˜K˜!K˜Kšœ!™!K˜K˜Kšœ#˜#Kšœ"˜"K˜K˜K˜Kšœ@™@K˜Kšœ#˜#K˜K˜Kšœžœ.žœ˜EK˜KšœH˜HK˜Kšœ‘œ˜:K˜K˜K˜—š œžœ.˜