
<<CgenUtil.mesa, >>
<<last modified by Sweet, 20-Sep-82  9:54:03>>
<<last modified by Satterthwaite, April 14, 1986 9:48:41 am PST>>

DIRECTORY
Alloc: TYPE USING [FreeChunk, GetChunk, Handle, Notifier, Words],
Code: TYPE USING [
CodeNotImplemented, codeptr, codeStart, enableLevel, enableList,
fileLoc, xtracting, xtractsei, ZEROlexeme],
CodeDefs: TYPE USING [
Base, BYTE, CCIndex, CCItem, CCNull, ChunkIndex, CodeCCIndex, 
CodeChunkType, codeType, EINull, EnableIndex, EnableItem, JumpCCIndex,
JumpCCNull, JumpType, LabelCCIndex, LabelCCNull, Lexeme, OpWordCount],
ComData: TYPE USING [typeSTRING],
FOpCodes: TYPE USING [qJREL, qLI],
LiteralOps: TYPE USING [Find, FindDescriptor, Value],
OpTableDefs: TYPE USING [InstLength],
P5: TYPE USING [NumberOfParams, P5Error, PushEffect],
P5U: TYPE USING [],
PackageSymbols: TYPE USING [ConstRecord, constType, WordIndex],
PrincOps: TYPE USING [FrameVec, LocalOverhead],
SourceMap: TYPE USING [Loc, nullLoc],
Stack: TYPE USING [Also, Check, stking],
SymbolOps: TYPE USING [
FirstCtxSe, NextSe, NormalType, RecordRoot, TypeRoot, UnderType, WordsForType],
Symbols: TYPE USING [
Base, BitAddress, CCBTIndex, CSEIndex, ISEIndex, ISENull, 
RecordSEIndex, CTXIndex, SEIndex, SENull,
seType, typeANY, typeTYPE, WordLength],
Table: TYPE USING [Base, Limit],
Tree: TYPE USING [Base, Index, Link, Null, NullIndex, treeType],
TreeOps: TYPE USING [PopTree, PushNode, PushTree, ScanList, SetInfo];

CgenUtil: PROGRAM
IMPORTS
Alloc, MPtr: ComData, CPtr: Code, LiteralOps, OpTableDefs, 
P5, Stack, SymbolOps, TreeOps
EXPORTS P5U =
BEGIN
OPEN SymbolOps, CodeDefs;

<<imported definitions>>

BYTE: TYPE = CodeDefs.BYTE;

BitAddress: TYPE = Symbols.BitAddress;
CSEIndex: TYPE = Symbols.CSEIndex;
ISEIndex: TYPE = Symbols.ISEIndex;
ISENull: ISEIndex = Symbols.ISENull;
RecordSEIndex: TYPE = Symbols.RecordSEIndex;
SEIndex: TYPE = Symbols.SEIndex;
SENull: SEIndex = Symbols.SENull;
WordLength: CARDINAL = Symbols.WordLength;

table: Alloc.Handle _ NIL;

tb: Tree.Base;        -- tree base (local copy)
seb: Symbols.Base;        -- semantic entry base (local copy)
cb: CodeDefs.Base;        -- code base (local copy)
cstb: Table.Base;        -- constant table base (local copy)

CgenUtilNotify: PUBLIC Alloc.Notifier =
BEGIN  -- called by allocator whenever table area is repacked
seb _ base[Symbols.seType];
tb _ base[Tree.treeType];
cb _ base[codeType];
cstb _ base[PackageSymbols.constType];
END;

AllocCodeCCItem: PUBLIC PROC [n: [0..3]] RETURNS [c: CodeCCIndex] =
BEGIN
c _ GetChunk[CCItem.code.SIZE + n];
cb[c] _ CCItem[
free: FALSE, flink: CCNull, blink: CCNull,
ccvalue: code[inst: 0, realinst: FALSE, isize: 0, fill: 0, parameters:]];
LinkCCItem[c];
RETURN
END;

BeginCatch: PUBLIC PROC RETURNS [first, cur: CCIndex] =
BEGIN
lbl: LabelCCIndex;
first _ CPtr.codeStart;
cur _ CPtr.codeptr;
lbl _ LabelAlloc[];
cb[lbl].catch _ TRUE;
CPtr.codeStart _ CPtr.codeptr _ lbl;
END;

BitsForOperand: PUBLIC PROC [t: Tree.Link] RETURNS [CARDINAL] =
BEGIN
RETURN [WITH t SELECT FROM
literal => WordLength,    -- not always TRUE, but good enough
ENDCASE => BitsForType[OperandType[t]]]
END;

BitsForType: PUBLIC PROC [sei: SEIndex] RETURNS [CARDINAL] =
BEGIN
csei: CSEIndex = UnderType[sei];
RETURN [WITH seb[csei] SELECT FROM
record => length,
ENDCASE => CARDINAL[WordsForType[csei]]*WordLength]
END;

CCellAlloc: PUBLIC PROC [t: CodeChunkType] =
BEGIN -- allocates a cell for other than code or label
c: CCIndex;
nwords: CARDINAL;
SELECT t FROM
code => P5.P5Error[262];
label => P5.P5Error[263];
jump => nwords _ CCItem.jump.SIZE;
other => nwords _ CCItem.other.SIZE;
ENDCASE;
c _ GetChunk[nwords];
SELECT t FROM
jump => cb[c] _ CCItem[free: FALSE, flink: , blink: , ccvalue: jump[, , , , , , , ]];
other => cb[c] _ CCItem[free: FALSE, flink: , blink: , ccvalue: other[obody: ]];
ENDCASE;
LinkCCItem[c];
END;

CgenUtilInit: PUBLIC PROC [ownTable: Alloc.Handle] =
BEGIN
table _ ownTable;
CPtr.ZEROlexeme _ Lexeme[literal[word[LiteralOps.Find[0].lti]]];
CPtr.fileLoc _ SourceMap.nullLoc;
IF GetChunk[2] # CCNull THEN ERROR;
END;

ComputeFrameSize: PUBLIC PROC [fs: CARDINAL] RETURNS [CARDINAL] =
BEGIN -- finds alloc-vector index for frame of size fs
OPEN PrincOps;
fs _ fs + PrincOps.LocalOverhead.SIZE;
FOR fx: CARDINAL IN [0..FrameVec.LENGTH) DO
IF fs <= FrameVec[fx] THEN RETURN [fx] ENDLOOP;
ERROR
END;

CreateLabel: PUBLIC PROC RETURNS [c: LabelCCIndex] =
BEGIN -- allocates and inserts a label at codeptr
c _ LabelAlloc[];
InsertLabel[c];
END;

DeleteCell: PUBLIC PROC [c: CCIndex] =
BEGIN -- deletes cell from code stream
nwords: CARDINAL;
IF cb[c].blink # CCNull THEN cb[cb[c].blink].flink _ cb[c].flink;
IF cb[c].flink # CCNull THEN cb[cb[c].flink].blink _ cb[c].blink;
nwords _ WITH cc: cb[c] SELECT FROM
code => ParamCount[LOOPHOLE[c]] + CCItem.code.SIZE,
label => CCItem.label.SIZE,
jump => CCItem.jump.SIZE,
other => CCItem.other.SIZE,
ENDCASE => ERROR;
FreeChunk[c, nwords];
END;

EndCatch: PUBLIC PROC [oldfirst, oldcur: CCIndex] =
BEGIN
cfirst: CCIndex = CPtr.codeStart;
c: CCIndex _ oldcur;
CPtr.codeStart _ oldfirst;
CPtr.codeptr _ oldcur;
WHILE cb[c].flink # CCNull DO c _ cb[c].flink; ENDLOOP;
cb[cfirst].blink _ c;
cb[c].flink _ cfirst;
END;

EnumerateCaseArms: PUBLIC PROC [node: Tree.Index, action: PROC [t: Tree.Link]] =
BEGIN

ProcessItem: PROC [t: Tree.Link] =
BEGIN
inode: Tree.Index;
WITH t SELECT FROM subtree => inode _ index; ENDCASE;
SELECT tb[inode].name FROM
item, casetest => action[tb[inode].son[2]];
caseswitch => TreeOps.ScanList[tb[inode].son[3], ProcessItem];
ENDCASE;
END;

TreeOps.ScanList[tb[node].son[2], ProcessItem];
IF tb[node].son[3] # Tree.Null THEN action[tb[node].son[3]];
END;

FreeChunk: PUBLIC PROC [i: CodeDefs.ChunkIndex, size: CARDINAL] =
BEGIN
<<FOR p: LONG POINTER TO MonitorRecord _ monList, p.next WHILE p # NIL DO>>
<<IF p.cell = i AND p.action = free THEN Runtime.CallDebugger["From FreeChunk"L];>>
<<ENDLOOP;>>
table.FreeChunk[LOOPHOLE[i], size, codeType];
END;

FullWordBits: PUBLIC PROC [bits: CARDINAL] RETURNS [CARDINAL] =
BEGIN
RETURN [((bits + WordLength - 1)/WordLength)*WordLength]
END;

GetChunk: PUBLIC PROC [size: CARDINAL] RETURNS [c: CodeDefs.ChunkIndex] =
BEGIN
c _ LOOPHOLE[table.GetChunk[size, CodeDefs.codeType]];
<<FOR p: LONG POINTER TO MonitorRecord _ monList, p.next WHILE p # NIL DO>>
<<IF p.cell = c AND p.action = allocate THEN Runtime.CallDebugger["From GetChunk"L];>>
<<ENDLOOP;>>
RETURN [c]
END;

InsertLabel: PUBLIC PROC [c: LabelCCIndex] = LinkCCItem;

LabelAlloc: PUBLIC PROC RETURNS [c: LabelCCIndex] =
BEGIN -- gets a chunk for a label but does not insert it in stream
c _ GetChunk[CCItem.label.SIZE];
cb[c] _ CCItem[
free: FALSE, flink: CCNull, blink: CCNull,
ccvalue: label[
offsetLoaded: FALSE,
labelinfo:
generating[catch: FALSE, labelseen: FALSE, ei: EINull, jumplist: JumpCCNull]]];
RETURN
END;

LinkCCItem: PROC [c: CCIndex] =
BEGIN -- inserts a CCItem in list @ codeptr
IF CPtr.codeptr # CCNull THEN
BEGIN
cb[c].flink _ cb[CPtr.codeptr].flink;
IF cb[CPtr.codeptr].flink # CCNull THEN cb[cb[CPtr.codeptr].flink].blink _ c;
cb[CPtr.codeptr].flink _ c;
END
ELSE cb[c].flink _ CCNull;
cb[c].blink _ CPtr.codeptr;
CPtr.codeptr _ c;
END;

LongTreeAddress: PUBLIC PROC [t: Tree.Link] RETURNS [long: BOOL_FALSE] =
BEGIN
WITH t SELECT FROM
subtree =>
BEGIN
node: Tree.Index = index;
IF node # Tree.NullIndex THEN
SELECT tb[node].name FROM
loophole, cast, openx, pad, chop =>
long _ LongTreeAddress[tb[node].son[1]];
dot, uparrow, dindex, seqindex, dollar, index, new, reloc =>
long _ tb[node].attr2;
assignx => WITH tb[node].son[2] SELECT FROM
subtree => IF tb[index].name = mwconst THEN
long _ LongTreeAddress[tb[node].son[1]]
ELSE long _ LongTreeAddress[tb[node].son[2]];
ENDCASE => long _ LongTreeAddress[tb[node].son[2]];
ifx => long _ LongTreeAddress[tb[node].son[2]] OR
LongTreeAddress[tb[node].son[3]];
casex =>
BEGIN
LongArm: PROC [t: Tree.Link] = {long _ long OR LongTreeAddress[t]};
EnumerateCaseArms[node, LongArm];
END;
ENDCASE => NULL;
END;
ENDCASE => NULL;
RETURN
END;

MakeLongTreeLiteral: PUBLIC PROC [d: DESCRIPTOR FOR ARRAY OF WORD, type: CSEIndex]
RETURNS [Tree.Link] =
BEGIN
TreeOps.PushTree[[literal[LiteralOps.FindDescriptor[d]]]];
TreeOps.PushNode[mwconst, 1];  TreeOps.SetInfo[type];
RETURN [TreeOps.PopTree[]]
END;

MakeTreeLiteral: PUBLIC PROC [val: WORD] RETURNS [Tree.Link] =
BEGIN
RETURN [[literal[LiteralOps.Find[val]]]]
END;

MarkedType: PUBLIC PROC [type: SEIndex] RETURNS [CSEIndex] =
BEGIN
subType: CSEIndex = NormalType[UnderType[type]];
RETURN [WITH t: seb[subType] SELECT FROM
ref => UnderType[TypeRoot[t.refType]],
transfer => subType,
ENDCASE => Symbols.typeANY]
END;

MonitorAction: TYPE = {allocate, free};
MonitorRecord: TYPE = RECORD [cell: CCIndex, action: MonitorAction];
monList: LIST OF MonitorRecord _ NIL;

Monitor: PROC [cell: CCIndex, action: MonitorAction] =
BEGIN
monList _ --(MPtr.zone).--CONS[[cell, action], monList];
END;

NewEnableItem: PUBLIC PROC [bti: Symbols.CCBTIndex] RETURNS [et: EnableIndex] =
BEGIN -- don't bother to keep sorted, OutCode sorts them
IF CPtr.enableLevel = CPtr.enableList.LENGTH THEN 
ERROR CPtr.CodeNotImplemented;
et _ GetChunk[EnableItem.SIZE];
cb[et] _ [
free: FALSE, next: CPtr.enableList[CPtr.enableLevel], 
bti: bti, startPC:, bytes:];
CPtr.enableList[CPtr.enableLevel] _ et;
END;

NextVar: PUBLIC PROC [sei: ISEIndex] RETURNS [ISEIndex] =
BEGIN -- starting at sei returns first variable on ctx-list
RETURN [SELECT TRUE FROM
(sei = ISENull) => ISENull,
(seb[sei].idType # Symbols.typeTYPE) => sei,
ENDCASE => NextVar[NextSe[sei]]]
END;

    NilTree: PUBLIC PROC [type: CSEIndex] RETURNS [Tree.Link] =
        BEGIN
        SELECT SymbolOps.WordsForType[type] FROM
            1 => RETURN[MakeTreeLiteral[0]];
            2 =>
                BEGIN
                zeros: ARRAY [0..2) OF WORD _ [0, 0];
                RETURN[MakeLongTreeLiteral[DESCRIPTOR[zeros], type]];
                END;
            ENDCASE => ERROR;
        END;

OperandType: PUBLIC PROC [t: Tree.Link] RETURNS [sei: CSEIndex] =
BEGIN -- compute type of tree
RETURN [WITH e:t SELECT FROM
symbol => UnderType[seb[e.index].idType],
literal => IF e.index.litTag = string THEN MPtr.typeSTRING ELSE ERROR,
subtree =>
IF e = Tree.Null THEN
IF CPtr.xtracting THEN UnderType[seb[CPtr.xtractsei].idType] ELSE ERROR
ELSE UnderType[tb[e.index].info],
ENDCASE => ERROR]
END;

Out0: PUBLIC PROC [i: BYTE] =
BEGIN -- outputs an parameter-less instruction
c: CodeCCIndex;
pushEffect: CARDINAL = P5.PushEffect[i];
IF Stack.stking THEN Stack.Check[i];
IF P5.NumberOfParams[i] # 0 THEN P5.P5Error[257];
c _ AllocCodeCCItem[0];
cb[c].inst _ i;
END;

Out1: PUBLIC PROC [i: BYTE, p1: WORD] =
BEGIN -- outputs an one-parameter instruction
c: CodeCCIndex;
pushEffect: CARDINAL = P5.PushEffect[i];
IF Stack.stking THEN {
Stack.Check[i]; IF i = FOpCodes.qLI THEN Stack.Also[[const[p1]]]};
IF P5.NumberOfParams[i] # 1 THEN P5.P5Error[258];
c _ AllocCodeCCItem[1];
cb[c].inst _ i;
cb[c].parameters[1] _ p1;
END;

Out2: PUBLIC PROC [i: BYTE, p1, p2: WORD] =
BEGIN -- outputs an two-parameter instruction
c: CodeCCIndex;
pushEffect: CARDINAL = P5.PushEffect[i];
IF Stack.stking THEN Stack.Check[i];
IF P5.NumberOfParams[i] # 2 THEN P5.P5Error[259];
c _ AllocCodeCCItem[2];
cb[c].inst _ i;
cb[c].parameters[1] _ p1;
cb[c].parameters[2] _ p2;
END;

Out3: PUBLIC PROC [i: BYTE, p1, p2, p3: WORD] =
BEGIN -- outputs an three-parameter instruction
c: CodeCCIndex;
pushEffect: CARDINAL = P5.PushEffect[i];
IF Stack.stking THEN Stack.Check[i];
IF P5.NumberOfParams[i] # 3 THEN P5.P5Error[260];
c _ AllocCodeCCItem[3];
cb[c].inst _ i;
cb[c].parameters[1] _ p1;
cb[c].parameters[2] _ p2;
cb[c].parameters[3] _ p3;
END;

OutCatchMark: PUBLIC PROC [index: EnableIndex, start: BOOL] =
BEGIN
CCellAlloc[other];
cb[CPtr.codeptr] _ [free: FALSE, flink:, blink:,
ccvalue: other[markCatch[start: start, index: index]]];
END;

OutJump: PUBLIC PROC [jt: JumpType, l: LabelCCIndex] =
BEGIN -- outputs a jump-type code ceel into the code stream
SELECT jt FROM
Jump, JumpA, JumpC, JumpCA, JumpRet, JumpLIO => NULL;
ENDCASE => IF Stack.stking THEN Stack.Check[FOpCodes.qJREL];
CCellAlloc[jump];
WITH cb[CPtr.codeptr] SELECT FROM
jump =>
BEGIN
fixedup _ FALSE;
completed _ FALSE;
jtype _ jt;
destlabel _ l;
IF l # LabelCCNull THEN
BEGIN
thread _ cb[l].jumplist;
cb[l].jumplist _ LOOPHOLE[CPtr.codeptr, JumpCCIndex];
END
ELSE thread _ JumpCCNull;
END;
ENDCASE
END;

OutSource: PUBLIC PROC [index: SourceMap.Loc] =
BEGIN
CCellAlloc[other];
cb[CPtr.codeptr] _ [free: FALSE, flink:, blink:,
ccvalue: other[source[loc: index]]];
END;

ParamCount: PUBLIC PROC [c: CodeCCIndex] RETURNS [CARDINAL] =
BEGIN
RETURN [SELECT TRUE FROM
(cb[c].isize # 0) => cb[c].isize-1,
cb[c].realinst => OpTableDefs.InstLength[cb[c].inst]-1,
ENDCASE => P5.NumberOfParams[cb[c].inst]]
END;

PrevVar: PUBLIC PROC [ssei, sei: ISEIndex] RETURNS [ISEIndex] =
BEGIN -- returns vars in reverse order as those returned by  nextvar
psei: ISEIndex _ NextVar[ssei];
rsei: ISEIndex;
IF psei = sei THEN RETURN [psei];
UNTIL psei = sei DO rsei _ psei; psei _ NextVar[NextSe[psei]] ENDLOOP;
RETURN [rsei];
END;

PushLitVal: PUBLIC PROC [v: UNSPECIFIED] =
BEGIN -- forces a constant onto the stack
Out1[FOpCodes.qLI, v];
END;


RecordConstant: PUBLIC PROC [offset: PackageSymbols.WordIndex, length: CARDINAL] =
BEGIN OPEN PackageSymbols;
csti: Table.Base RELATIVE POINTER [0..Table.Limit) TO ConstRecord = 
table.Words[constType, ConstRecord.SIZE];
cstb[csti] _ [offset: offset, length: length];
END;


ReferentType: PUBLIC PROC [type: SEIndex] RETURNS [SEIndex] =
BEGIN
subType: CSEIndex = NormalType[UnderType[type]];
RETURN [WITH t: seb[subType] SELECT FROM
ref => t.refType,
ENDCASE => Symbols.typeANY]
END;


TreeLiteral: PUBLIC PROC [t: Tree.Link] RETURNS [BOOL] =
BEGIN
RETURN [WITH t SELECT FROM
literal => index.litTag = word,
subtree =>
SELECT tb[index].name FROM
cast => TreeLiteral[tb[index].son[1]],
mwconst => TRUE,
ENDCASE => FALSE,
ENDCASE => FALSE]
END;

TreeLiteralValue: PUBLIC PROC [t: Tree.Link] RETURNS [WORD] =
BEGIN
RETURN [WITH e:t SELECT FROM
literal =>
WITH e.index SELECT FROM
word => LiteralOps.Value[lti],
ENDCASE => ERROR,
subtree =>
SELECT tb[e.index].name FROM
cast, mwconst =>  TreeLiteralValue[tb[e.index].son[1]],
ENDCASE => ERROR,
ENDCASE => ERROR]
END;

TypeForTree: PUBLIC PROC [t: Tree.Link] RETURNS [SEIndex] =
BEGIN
RETURN [WITH t SELECT FROM
subtree => tb[index].info,
symbol => index,
ENDCASE => ERROR]
END;

UnMonitor: PROC [cell: CCIndex, action: MonitorAction] =
BEGIN
p, q: LIST OF MonitorRecord;
IF monList = NIL THEN RETURN;
IF monList.first.cell = cell AND monList.first.action = action THEN
{p _ monList.rest; --(MPtr.zone).--FREE[@monList]; monList _ p};
FOR p _ monList, p.rest UNTIL p.rest = NIL DO
IF p.rest.first.cell = cell AND p.rest.first.action = action THEN {
q _ p.rest.rest; --(MPtr.zone).--FREE[@p.rest]; p.rest _ q; RETURN};
ENDLOOP;
END;

VariantTag: PUBLIC PROC [type: SEIndex, ctx: Symbols.CTXIndex] RETURNS [WORD] =
BEGIN
next: SEIndex;
FOR sei: SEIndex _ type, next UNTIL sei = SENull DO
WITH se: seb[sei] SELECT FROM
id =>
BEGIN
IF se.idCtx = ctx THEN RETURN [se.idValue];
next _ se.idInfo;
END;
ENDCASE => EXIT;
ENDLOOP;
ERROR
END;

WordAligned: PUBLIC PROC [tsei: RecordSEIndex] RETURNS [BOOL] =
BEGIN -- sees if a word-aligned record (never TRUE for a variant record)
<<always true for an argument record>>
sei: ISEIndex;
wa: INTEGER _ 0;
a: BitAddress;
tsei _ RecordRoot[tsei];
IF seb[tsei].hints.variant THEN RETURN [FALSE];
IF seb[tsei].argument THEN RETURN [TRUE];
sei _ NextVar[FirstCtxSe[seb[tsei].fieldCtx]];
UNTIL sei = ISENull DO
a _ seb[sei].idValue;
IF a.bd # 0 THEN RETURN [FALSE];
IF a.wd < wa THEN RETURN [FALSE];
wa _ a.wd;
sei _ NextVar[NextSe[sei]];
ENDLOOP;
RETURN [TRUE]
END;

WordsForOperand: PUBLIC PROC [t: Tree.Link] RETURNS [OpWordCount] =
BEGIN -- compute number of words for storing value of tree
RETURN [WITH t SELECT FROM
literal => 1, -- multiwords will be subtrees
symbol => WordsForSei[seb[index].idType],
ENDCASE => OpWordCount[WordsForType[OperandType[t]]]]
END;

WordsForSei: PUBLIC PROC [sei: SEIndex] RETURNS [OpWordCount] =
BEGIN
RETURN [IF sei # SENull THEN OpWordCount[WordsForType[UnderType[sei]]] ELSE 0]
END;

WordsForString: PUBLIC PROC [nChars: CARDINAL] RETURNS [CARDINAL] =
BEGIN  -- computed for the object machine
RETURN [(nChars+1)/2 + 2]
END;

END.

