StackOverflowTrap:
PROC [underflowLabel: Label] = {
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.
area: HandCodingSupport.Area ← HandCodingSupport.GetCurrentArea[];
internalSaveSetup: Label ← GenLabel[];
exitLabel: Label ← GenLabel[];
dispatchLabel: Label = GenLabel[];
dispatchData: Label = GenDataLabel[area, StackSize*DragOpsCross.bytesPerWord];
{
DragonStack.IFUStackOverflowTrap: This part is for IFU stack overflow. Note that we are NOT assured of a non-empty frame on the IFU stack.
ifuEntryLabel: Label = GenLabel[];
ProcedureEntry[ifuEntryLabel, 0,
TRUE];
We get no arguments.
MakeLabelGlobal["DragonStack.IFUStackOverflowTrap", ifuEntryLabel];
FillTrap[IFUStackOverflowTrap, ifuEntryLabel];
GetEldestPC[];
discard the bogus frame
[S] holds the PC for the bogus frame
drRUADD[topDst, const0, const0];
Capture the carry bit
[S] holds the carry bit
GetEldestStatus[];
[S] holds the eldest status, [S-1] holds the carry bit
AllocNacho[];
Allocate the next save block to use
([S] = newBlock, [S-1] = eldest status)
drLFC[UseLabel16[internalSaveSetup]];
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
SetLabel[dispatchLabel];
We get here to do the dispatch to the appropriate register saving code.
PushWordAddr[area, dispatchData];
drQRX[topAtop, belowSrc];
Fetch the dispatch address
drSFC[];
Dispatch to restore the proper # of regs
On return: [S] = eldest status, [S-1] = sampled carry bit
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).
SetLabel[exitLabel];
MakeLabelGlobal["
DragonStack.StackOverflowExit", exitLabel];
At this point: [S] holds the eldest status, [S-1] holds the carry bit
PushByteAddr[area, underflowLabel];
SetEldestPC[];
Install the new bogus frame & set its PC (creating a new eldest frame)
ExtractField[first: 32-StackLog, bits: StackLog]; drDUP[];
drADDQB[bogusStatusWord];
SetEldestStatus[];
Calculate & set the L for the bogus frame. [S] still holds that L.
drSUBB[StackMargin];
ExtractField[first: 32-StackLog, bits: StackLog];
SetSPLimit[];
Calculate & set the new stack limit. S is now back to its original depth.
[S] holds the carry bit
MakeLabelGlobal["DragonStack.StackOverflowReturn", GenLabelHere[]];
drRUSUB[topDst, const0, popSrc];
Set the carry bit that was saved on the overflow
drRETN[];
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?
euEntryLabel: Label = GenLabel[];
ProcedureEntry[euEntryLabel, 0,
TRUE];
We get no arguments.
MakeLabelGlobal["DragonStack.EUStackOverflowTrap", euEntryLabel];
FillTrap[EUStackOverflowTrap, euEntryLabel];
GetEldestPC[];
discard the bogus frame
[S] holds the PC for the bogus frame
drRUADD[topDst, const0, const0];
Capture the carry bit
[S] holds the carry bit
GetEldestStatus[];
[S] holds the eldest status, [S-1] holds the carry bit
{
loopLabel: Label = GenLabelHere[];
[S] = eldest status
AllocNacho[];
Allocate the next save block to use
[S] = newBlock, [S-1] = eldest status
drLFC[UseLabel16[internalSaveSetup]];
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
drRJNEBJ[left: topSrc, right: const0, dist: UseLabel8B[dispatchLabel]];
If we have registers to save, then go do them.
drRJEBJ[popSrc, topSrc, UseLabel8B[loopLabel]];
Discard [S] and go loop (unconditionally, really).
[S] = eldest status
};
};
{
InstallBogusFrame is useful for external callers of DragonStack.NakedSaveFrame. Once the necessary frames have been saved, the bogus frame can be created using this routine.
exitEntryLabel: Label = GenLabel[];
ProcedureEntry[exitEntryLabel, 0, TRUE];
drQADD[pushA0, const0];
Capture (& clear) the carry bit
GetEldestStatus[];
push the eldest status
[S] holds the eldest status, [S-1] holds the carry bit
MakeLabelGlobal["DragonStack.InstallBogusFrame", exitEntryLabel];
drJB[UseLabel8A[exitLabel]];
};
{
RemoveBogusFrame is useful for external callers of DragonStack.NakedSaveFrame. Before NakedSaveFrame can be called, the bogus frame must be removed, and the carry bit & current eldest status must be pushed.
exitEntryLabel: Label = GenLabel[];
ProcedureEntry[exitEntryLabel, 0];
MakeLabelGlobal["DragonStack.RemoveBogusFrame", exitEntryLabel];
GetEldestPC[];
discard the bogus frame
[S] holds the PC for the bogus frame
drRUADD[topDst, const0, const0];
Capture (& clear) the carry bit
[S] holds the carry bit
GetEldestStatus[];
[S] holds the eldest status, [S-1] holds the carry bit
ProcedureExit[1];
return the new eldest status & carry bit
};
{
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
At call requires at least 1 IFU frame & 4 EU stack regs.
eldestStatus: RegSpec = reg0;
localHook: RegSpec = reg1;
localStatus: RegSpec = reg2;
ProcedureEntry[internalSaveSetup, 2];
Make our local L good for a few locals, we get one sent to us on the stack
LReg[hook]; drPSB[linkOffset];
store the hook into the new dump block
[S] = localHook = newBlock; (localHook+linkOffset)^ = hook
PReg[hook]; PReg[temp];
store the new dump block addr into hook and temp
[S] = localHook = hook = temp = newBlock
LReg[eldestStatus];
ExtractField[first: 32-StackLog, bits: StackLog];
SetYoungestStatus[];
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])
GetEldestPC[]; drSRIn[localHook, lastPCOffset];
Store the continuation PC and remove the eldest PC from the ifuStack.
((localHook+lastPCOffset)^ = PC of frame to save)
GetEldestStatus[];
Sample & save the new eldest status
[S] (aka localStatus) = eldest status (the new version)
[S-1] = localHook
[S-2] = eldest status (the old version)
drRVSUB[c: pushDst, a: localStatus, b: eldestStatus];
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
MoveReg[eldestStatus, localStatus];
[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)
drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[insert:
TRUE, mask: StackLog]]];
insert # of regs into the saved status word (aka localStatus)
[S] (aka localStatus) = #regs (+ status bits)
[S-1] (aka localHook)
[S-2] = eldest status
drWRI[localStatus, localHook, nRegsOffset];
Save #regs into the save block & into eldestStatus
(localHook+nRegsOffset)^ ← localStatus
[S] = #regs, [S-1] = localHook, [S-2] = eldest status
drSHDR[DragOpsCrossUtils.FieldDescriptorToCard[[mask: StackLog]]];
[S] = #regs, [S-1] = eldest status
ProcedureExit[2];
return the values
[S] = #regs, [S-1] = eldest status
};
{
DragonStack.NakedSaveFrame: This is the entry point for saving the eldest stack frame in the IFU stack. We assume that the bogus frame has been removed, and that we are in kernel mode with traps disabled. The top two words must be as they were set by RemoveBogusFrame.
At entry:
[S] = the sampled eldest status
[S-1] = the sampled carry bit
At the point of call there must be at least 2 available frames on the IFU stack.
{max: caller frame, internalSaveSetup/AllocNacho frame}
At the point of call there must be at least 6 available registers on the EU stack.
{max: 6 = 2 at first use of AllocNacho + 4 for AllocNacho}
At exit:
[S] = the sampled eldest status
[S-1] = the sampled carry bit
singleLabel: Label = GenLabel[];
multiLabel: Label = GenLabel[];
fatalLabel: Label = GenLabel[];
shortLabel: Label = GenLabel[];
SetLabel[nakedSaveFrame];
MakeLabelGlobal["
DragonStack.NakedSaveFrame", nakedSaveFrame];
AllocNacho[];
Allocate the next save block to use
[S] = newBlock, [S-1] = eldest status, [S-2] = sampled carry bit
drLFC[UseLabel16[internalSaveSetup]];
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, [S-2] = sampled carry bit
PushWordAddr[area, dispatchData];
drQRX[topAtop, belowSrc];
Fetch the dispatch address
drJSD[];
Dispatch to save the proper # of regs
after JSD: [S] = #regs, [S-1] = eldest status, [S-2] = sampled carry bit
SetLabel[singleLabel];
FOR i:
NAT
DECREASING
IN [0..regsPerNacho)
DO
drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i];
ENDLOOP;
drRETN[];
This return does not enable traps, and does not disturb S, but does restore L, which was previously clobbered.
[S] = new eldest status, [S-1] = sampled carry bit
SetLabel[fatalLabel];
For now we just crash. Eventually we should be more robust, saving away the regs, then giving the offending process a "stack fault".
Pause[];
SetLabel[multiLabel];
FOR i:
NAT
IN [0..regsPerNacho)
DO
drWAI[reg1: [reg[i]], reg2: temp, disp: regOff+i];
ENDLOOP;
At this point we have regsPerNacho more EU regs available than when we started, since we just saved that many. The EU high-water mark must therefore occur before registers are saved.
LReg[temp];
Push the address of the completed save block
[S] = temp = oldBlock; [S-1] = #regs; [S-2] = eldest status; [S-3] = sampled carry
AllocNacho[];
-- EU stack high water mark: 8 regs
Allocate the next save block to use (it gets left on the stack)
[S] = newBlock; [S-1] = temp = oldBlock; [S-2] = #regs
[S-3] = eldest status; [S-4] = sampled carry
PReg[temp]; drWSB[othersOffset];
set temp to the new dump block addr
also store the new block addr into the old block
(temp = (temp'+othersOffset)^ = newBlock; [S] = #regs)
drSUBB[regsPerNacho];
adjust #regs
[S] = #regs, [S-1] = eldest status
PushWordAddr[area, dispatchData];
drQRX[topAtop, belowSrc];
Fetch the dispatch address
drAL[regsPerNacho];
adjust L for the restored regs
drJSD[];
Dispatch to save the proper # of regs
[S] = #regs, [S-1] = eldest status, [S-2] = sampled carry bit
{
Generate the dispatch table
oldPC: CARD ← HandCodingSupport.GetOutputPC[];
HandCodingSupport.SetOutputPC[dispatchData.offset];
FOR nr:
NAT
DECREASING
IN [0..regsPerNacho]
DO
OutputByteAddr[area, singleLabel, 3*nr];
ENDLOOP;
FOR nr:
NAT
IN (regsPerNacho..regsPerNacho*4]
DO
OutputByteAddr[area, multiLabel, 0];
ENDLOOP;
FOR nr:
NAT
IN (4*regsPerNacho..StackSize)
DO
OutputByteAddr[area, fatalLabel, 0];
ENDLOOP;
HandCodingSupport.SetOutputPC[oldPC];
};
};
};