-- Calls.mesa
-- last modified by Sweet, 15-Sep-82 13:49:11
-- last modified by Satterthwaite, May 11, 1983 9:21 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [
    CodePassInconsistency, inlineRetErrorLabel, inlineRetErrorListLabel],
  CodeDefs: TYPE USING [
    Base, BoVarIndex, BYTE, CodeCCIndex, codeType, LabelCCIndex, LabelCCNull,
    Lexeme, MaxParmsInStack, NullLex, VarComponent, VarIndex, wordlength],
  ComData: TYPE USING [bodyIndex, stopping],
  Counting: TYPE USING [CheckArgRefs, Free],
  FOpCodes: TYPE USING [
    qAF, qBLT, qCATCH, qDDUP, qEFC, qFF, qGA, qKFCB, qLFC, qLI, qLLOB, 
    qLP, qMR, qMX, qMW, qPI, qPO, qREC, qR, qRDL, qRL, qSELFC, qSFC],
  Log: TYPE USING [Error, Warning],
  OpTableDefs: TYPE USING [InstLength],
  P5: TYPE USING [
    CatchPhrase, Exp, GenTempLex, PushLex, PushRhs, ReleaseLock, 
    SAssign, TransferConstruct],
  P5L: TYPE USING [
    CopyToTemp, CopyVarItem, EasilyLoadable, GenVarItem, LoadAddress,
    LoadComponent, LoadVar, MakeBo, OVarItem, ReleaseLex, ReusableCopies,
    TOSAddrLex, TOSLex, VarForLex, VarVarAssign],
  P5S: TYPE USING [],
  P5U: TYPE USING [
    AllocCodeCCItem, BitsForOperand, BitsForType, ComputeFrameSize,
    CreateLabel, NextVar, 
    OperandType, Out0, Out1, OutJump, PushLitVal,
    TreeLiteralValue, WordsForOperand, WordsForSei],
  PrincOps: TYPE USING [AVHeapSize, returnOffset],
  RTSD: TYPE USING [sProcCheck],
  SDDefs: TYPE USING [
    sCopy, sError, sErrorList, sFork, sJoin, sRestart, sReturnError,
    sReturnErrorList, sSignal, sSignalList, sStart, sUnnamedError],
  Stack: TYPE USING [DeleteToMark, Dump, Incr, Load, Mark, TempStore, Top],
  SDExtra: TYPE USING [sFork12],
  SymbolOps: TYPE USING [
    FindExtension, FirstCtxSe, NextSe, TransferTypes, WordsForType, XferMode],
  Symbols: TYPE USING [
    Base, BitAddress, bodyType, CBTIndex, CBTNull, CCBTIndex, ContextLevel, CSEIndex,
    CTXIndex, ctxType, ISEIndex, lG, RecordSEIndex, SENull, seType],
  Tree: TYPE USING [Base, Index, Link, Null, treeType],
  TreeOps: TYPE USING [FreeNode, GetNode, OpName, NthSon, ScanList, UpdateList];

Calls: PROGRAM
    IMPORTS 
      MPtr: ComData, CPtr: Code, Counting, Log, OpTableDefs,
      P5, P5L, P5U, Stack, SymbolOps, TreeOps 
    EXPORTS CodeDefs, P5, P5S =
  BEGIN
  OPEN CodeDefs;

  -- imported definitions

  BitAddress: TYPE = Symbols.BitAddress;
  CBTIndex: TYPE = Symbols.CBTIndex;
  CCBTIndex: TYPE = Symbols.CCBTIndex;
  CBTNull: CBTIndex = Symbols.CBTNull;
  ContextLevel: TYPE = Symbols.ContextLevel;
  CSEIndex: TYPE = Symbols.CSEIndex;
  CTXIndex: TYPE = Symbols.CTXIndex;
  ISEIndex: TYPE = Symbols.ISEIndex;
  lG: ContextLevel = Symbols.lG;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;


  tb: Tree.Base;		-- tree base (local copy)
  seb: Symbols.Base;		-- semantic entry base (local copy)
  ctxb: Symbols.Base;		-- context entry base (local copy)
  bb: Symbols.Base;		-- body entry base (local copy)
  cb: CodeDefs.Base;		-- code base (local copy)

  CallsNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    ctxb ← base[Symbols.ctxType];
    bb ← base[Symbols.bodyType];
    tb ← base[Tree.treeType];
    cb ← base[codeType];
    END;

  SysError: PUBLIC PROC =
    BEGIN
    Stack.Dump[]; Stack.Mark[];
    SysCall[SDDefs.sUnnamedError];
    P5U.OutJump[JumpRet,LabelCCNull];
    END;


  SysErrExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = P5U.WordsForSei[tb[node].info];
    Stack.Dump[]; Stack.Mark[];
    SysCall[SDDefs.sUnnamedError];
    P5U.OutJump[JumpRet,LabelCCNull];
    RETURN [PRetLex[nrets, node, TRUE]]
    END;


  Create: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN -- generate code for NEW
    Stack.Dump[]; Stack.Mark[];
    IF tb[node].attr1 THEN P5.PushRhs[tb[node].son[1]]
    ELSE P5U.Out1[FOpCodes.qGA, 0];
    SysCall[SDDefs.sCopy];
    CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    Stack.Incr[1];
    RETURN [P5L.TOSLex[1]]
    END;


  SStart: PROC [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    Stack.Dump[]; Stack.Mark[];
    [] ← PushParms[
      argsBuilt: tb[node].attr1, t: tb[node].son[2], ptsei: ptsei, saveOne: TRUE];
    P5.PushRhs[tb[node].son[1]];
    SysCall[SDDefs.sStart];
    CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    RETURN [P5U.WordsForSei[SymbolOps.TransferTypes[ptsei].typeOut]]
    END;

  Start: PUBLIC PROC [node: Tree.Index] = {[] ← SStart[node]};

  StartExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = SStart[node];
    RETURN [PRetLex[nrets, node, FALSE]]
    END;

  Restart: PUBLIC PROC [node: Tree.Index] =
    BEGIN
    Stack.Dump[]; Stack.Mark[];
    P5.PushRhs[tb[node].son[1]];
    SysCall[SDDefs.sRestart];
    CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    END;


  Stop: PUBLIC PROC [node: Tree.Index]  =
    BEGIN OPEN FOpCodes;
    IF ~MPtr.stopping THEN SIGNAL CPtr.CodePassInconsistency;
    P5U.Out1[qLLOB, PrincOps.returnOffset]; P5U.Out0[qSFC];
    END;


  CallCatch: PUBLIC PROC [t: Tree.Link] =
    BEGIN
    IF t # Tree.Null THEN P5.CatchPhrase[TreeOps.GetNode[t]];
    END;


  SCall: PROC [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN -- generates code for procedure call statement
    OPEN FOpCodes;
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    portcall: BOOL = (SymbolOps.XferMode[ptsei] = port);
    computedtarget: BOOL;
    nparms: CARDINAL;
    sei: ISEIndex;
    inlineCall: BOOL;
    WITH tb[node].son[1] SELECT FROM
      symbol =>
	BEGIN
	sei ← index;
	inlineCall ← seb[sei].constant AND seb[sei].extended;
	computedtarget ← FALSE;
	END;
      ENDCASE => {inlineCall ← FALSE; computedtarget ← TRUE};
    IF ~inlineCall THEN Stack.Dump[]; 
    Stack.Mark[];
    nparms ← PushParms[
      argsBuilt: tb[node].attr1, 
      t: tb[node].son[2], ptsei: ptsei, saveOne: FALSE, refSafe: ~portcall];
    IF inlineCall THEN 
      BEGIN
      inlineTree: Tree.Link = SymbolOps.FindExtension[sei].tree;
      Stack.DeleteToMark[];
      TreeOps.ScanList[TreeOps.NthSon[inlineTree, 1], CodeInline];
      END 
    ELSE IF computedtarget THEN
      IF portcall THEN 
	BEGIN
	[] ← P5L.LoadAddress[P5L.VarForLex[P5.Exp[tb[node].son[1]]]];
	Stack.DeleteToMark[]; Stack.Incr[1];
	P5U.Out0[qPO]; P5U.Out0[qPI];
	END
      ELSE
	BEGIN
	P5.PushRhs[tb[node].son[1]];
	Stack.DeleteToMark[]; Stack.Incr[1];
	P5U.Out0[qSFC];
	END
    ELSE
      BEGIN
      Stack.DeleteToMark[]; -- assert that loading pdesc won't dump stack
      -- and that it won't need more than two words to load a symbol
      -- the worst case being up level addressing of the indirect word of an uncle
      IF seb[sei].constant THEN 
        BEGIN
	bti: CBTIndex = seb[sei].idInfo;
	IF bti = MPtr.bodyIndex THEN P5U.Out0[qSELFC]
	ELSE IF bti # CBTNull AND bb[bti].nesting = Outer THEN
	  P5U.Out1[qLFC, bb[bti].entryIndex]
        ELSE {P5.PushLex[[se[sei]]]; P5U.Out0[qSFC]};
        END
      ELSE IF portcall THEN
	BEGIN
	[] ← P5L.LoadAddress[P5L.VarForLex[[se[sei]]]];
	P5U.Out0[qPO]; P5U.Out0[qPI];
	END
      ELSE IF seb[sei].linkSpace THEN
	{a: BitAddress = seb[sei].idValue; P5U.Out1[qEFC, a.wd]}
      ELSE {P5.PushLex[[se[sei]]]; P5U.Out0[qSFC]};
      END;
    nrets ← P5U.WordsForSei[SymbolOps.TransferTypes[ptsei].typeOut];
    IF inlineCall THEN 
      {IF tb[node].nSons > 2 THEN P5.CatchPhrase[TreeOps.GetNode[tb[node].son[3]]]}
    ELSE CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    RETURN
    END;

  ConstructOnStack: PUBLIC PROC [maint: Tree.Link, rcsei: RecordSEIndex] =
    BEGIN OPEN SymbolOps;
    ctx: CTXIndex = seb[rcsei].fieldCtx;
    sei: ISEIndex;
    firstArg: BOOL ← TRUE;
    
    DoSafen: PROC [t: Tree.Link] RETURNS [v: Tree.Link] =
      BEGIN
      SELECT TreeOps.OpName[t] FROM
        safen =>
          BEGIN
          node: Tree.Index = TreeOps.GetNode[t];
          IF firstArg OR ~tb[node].attr2 THEN
	    BEGIN -- this dies horribly if there is only a single
		  -- parameter, i.e., no list node, since the call node
		  -- then contains a pointer to the safen which we free
		  -- Therefore, we test below for safen.
	    v ← tb[node].son[1];
	    tb[node].son[1] ← Tree.Null;  TreeOps.FreeNode[node];
	    END
          ELSE
	    BEGIN
	    r: VarIndex = P5L.VarForLex[P5.Exp[tb[node].son[1]]];
	    sei: ISEIndex = P5L.CopyToTemp[r].sei;
	    seb[sei].idType ← tb[node].info;
	    v ← [symbol[sei]];
	    TreeOps.FreeNode[node];
	    END;
	  firstArg ← FALSE;
          END;
        cast, pad =>
          BEGIN
          node: Tree.Index = TreeOps.GetNode[t];
          tb[node].son[1] ← DoSafen[tb[node].son[1]];
          v ← t;
          END;
        ENDCASE =>  -- don't unroll nested constructors
          BEGIN v ← t; firstArg ← FALSE END;
      RETURN
      END;
    
    LoadOne: PROC [t: Tree.Link] =
      BEGIN
      IF t = Tree.Null THEN
	THROUGH [0..SymbolOps.WordsForType[seb[sei].idType]) DO
	  P5U.Out1[FOpCodes.qLI, 0];
	  ENDLOOP
      ELSE IF TreeOps.OpName[t] = pad THEN
	BEGIN
	t1: Tree.Link = TreeOps.NthSon[t, 1];
	delta: CARDINAL = P5U.BitsForType[seb[sei].idType] - P5U.BitsForOperand[t1];
	P5.PushRhs[t1];
	IF delta MOD wordlength # 0 THEN ERROR;
	THROUGH [0.. delta/wordlength) DO P5U.Out1[FOpCodes.qLI, 0] ENDLOOP;
	END
      ELSE P5.PushRhs[t];
      sei ← P5U.NextVar[SymbolOps.NextSe[sei]];
      END;
    
    SELECT TreeOps.OpName[maint] FROM
      list =>
	BEGIN
	maint ← TreeOps.UpdateList[maint, DoSafen];
	sei ← SymbolOps.FirstCtxSe[ctx];
	TreeOps.ScanList[maint, LoadOne];
	END;
      safen => P5.PushRhs[TreeOps.NthSon[maint, 1]];
      ENDCASE => P5.PushRhs[maint];
    END;

  SSigErr: PROC [node: Tree.Index, error: BOOL] RETURNS [nrets: CARDINAL] =
    BEGIN -- generates code for signal/error
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    nparms: CARDINAL;
    sysFn: ARRAY BOOL OF ARRAY BOOL OF BYTE = [
      [SDDefs.sSignal, SDDefs.sSignalList],
      [SDDefs.sError, SDDefs.sErrorList]];
    Stack.Dump[]; Stack.Mark[];
    nparms ← PushParms[
      argsBuilt: tb[node].attr1, 
      t: tb[node].son[2], ptsei: ptsei, saveOne: TRUE];
    IF tb[node].son[1] = Tree.Null THEN P5U.PushLitVal[-1]
    ELSE P5.PushRhs[tb[node].son[1]];
    SysCall[sysFn[error][nparms > MaxParmsInStack-1]];
    nrets ← P5U.WordsForSei[SymbolOps.TransferTypes[ptsei].typeOut];
    CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    RETURN
    END;

  RetWithError: PUBLIC PROC [node: Tree.Index] =
    BEGIN -- generates code for RETURN WITH error
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    nparms: CARDINAL;
    monitored: BOOL ← tb[node].attr1;
    IF monitored AND tb[node].attr2 THEN {P5.ReleaseLock[]; monitored ← FALSE};
    Stack.Dump[]; Stack.Mark[];
    nparms ← PushParms[
      argsBuilt: FALSE, 
      t: tb[node].son[2], ptsei: ptsei, saveOne: TRUE, refSafe: FALSE];
    IF monitored THEN
      BEGIN
      Stack.Dump[];
      P5.ReleaseLock[];
      IF nparms # 0 THEN Stack.Load[Stack.Top[nparms], nparms];
      END;
    IF tb[node].son[1] = Tree.Null THEN P5U.PushLitVal[-1]
    ELSE P5.PushRhs[tb[node].son[1]];
    IF tb[node].attr3 THEN  -- inline expanded
      BEGIN
      Stack.DeleteToMark[];
      P5U.OutJump[Jump, 
        IF nparms > MaxParmsInStack-1 THEN CPtr.inlineRetErrorListLabel
        ELSE CPtr.inlineRetErrorLabel];
      END
    ELSE SysCall[IF nparms > MaxParmsInStack-1 THEN
      SDDefs.sReturnErrorList ELSE SDDefs.sReturnError];
    P5U.OutJump[JumpRet,LabelCCNull];
    END;

  CodeInline: PROC [t: Tree.Link] =
    BEGIN
    opByte: ARRAY [0..7) OF BYTE;
    iLength: CARDINAL ← 0;
    tLength: CARDINAL;
    c: CodeCCIndex;

    PickUpByte: PROC [t: Tree.Link] =
      BEGIN
      IF iLength < 7 THEN
	BEGIN
	opByte[iLength] ← WITH t SELECT FROM
	  symbol => seb[index].idValue,
	  ENDCASE => P5U.TreeLiteralValue[t];
	iLength ← iLength + 1;
	END
      ELSE Log.Error[instLength];
      END;

    TreeOps.ScanList[t, PickUpByte];
    IF iLength = 0 THEN RETURN;
    tLength ← OpTableDefs.InstLength[opByte[0]];
    IF tLength # 0 AND iLength # tLength THEN Log.Warning[instLength];
    c ← P5U.AllocCodeCCItem[iLength-1];
    cb[c].realinst ← TRUE;
    cb[c].inst ← opByte[0];
    cb[c].isize ← iLength;
    FOR i: CARDINAL IN [1..iLength) DO cb[c].parameters[i] ← opByte[i] ENDLOOP;
    END;


  PushParms: PROC [
        argsBuilt: BOOL, t: Tree.Link, ptsei: CSEIndex, saveOne: BOOL, refSafe: BOOL←TRUE] 
      RETURNS [nparms: CARDINAL] =
    BEGIN
    rsei: RecordSEIndex = SymbolOps.TransferTypes[ptsei].typeIn;
    RETURN [IF argsBuilt
      THEN PushArgRecord[t, rsei, saveOne, refSafe]
      ELSE BuildArgRecord[t, rsei, saveOne, refSafe]]
    END;

  BuildArgRecord: PUBLIC PROC [t: Tree.Link, rsei: RecordSEIndex, sigerr, refSafe: BOOL]
      RETURNS [nparms: CARDINAL] =
    BEGIN
    nparms ← IF rsei = Symbols.SENull THEN 0 ELSE P5U.WordsForSei[rsei];
    IF nparms > MaxParmsInStack OR (sigerr AND nparms > MaxParmsInStack-1) THEN
      BEGIN
      IF ~refSafe AND seb[rsei].hints.refField THEN Log.Warning[unsafeArgs];
      P5.TransferConstruct[nparms, bb[MPtr.bodyIndex].resident, t, rsei];
      END
    ELSE IF nparms # 0 THEN ConstructOnStack[t, rsei];
    RETURN
    END;

  PushArgRecord: PUBLIC PROC [t: Tree.Link, rsei: RecordSEIndex, sigerr, refSafe: BOOL]
      RETURNS [nparms: CARDINAL] =
    BEGIN
    offStack: BOOL;
    frameExists: BOOL ← FALSE;
    nparms ← IF rsei = Symbols.SENull THEN 0 ELSE P5U.WordsForSei[rsei];
    offStack ← (nparms > MaxParmsInStack OR (sigerr AND nparms > MaxParmsInStack-1));
    IF t # Tree.Null THEN
      BEGIN
      l: Lexeme;
      refSafe ← refSafe OR Counting.CheckArgRefs[t, rsei];
      l ← P5.Exp[t ! LogHeapFree => 
	    IF calltree = t AND offStack THEN
	      {frameExists ← TRUE; RESUME [TRUE, NullLex]}
	    ELSE RESUME [FALSE, NullLex] ];
      SELECT TRUE FROM
        frameExists => P5L.ReleaseLex[l];
	offStack =>
	  BEGIN
	  source: VarIndex = P5L.VarForLex[l];
	  dest: VarIndex;
	  temp: VarComponent;
	  fs: CARDINAL ← P5U.ComputeFrameSize[nparms];
	  IF bb[MPtr.bodyIndex].resident THEN
	    fs ← fs + PrincOps.AVHeapSize;
	  IF ~refSafe THEN Log.Warning[unsafeArgs];
	  P5U.PushLitVal[fs];  P5U.Out0[FOpCodes.qAF];
	  temp ← Stack.TempStore[1];
	  dest ← P5L.GenVarItem[bo];
	  cb[dest] ← [body: bo[base: temp, offset: [wSize: nparms, space: frame[]]]];
	  l ← P5L.VarVarAssign[to: dest, from: source, isexp: FALSE];
	  P5L.LoadComponent[temp];
	  END;
	ENDCASE => P5.PushLex[l];
      END;
    RETURN
    END;

  Call: PUBLIC PROC [node: Tree.Index] = {[] ← SCall[node]};

  SigErr: PUBLIC PROC [node: Tree.Index] =
    BEGIN
    error: BOOL = (tb[node].name = error);
    [] ← SSigErr[node, error];
    IF error THEN P5U.OutJump[JumpRet,LabelCCNull];
    END;

  CallExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = SCall[node];
    RETURN [PRetLex[nrets, node, FALSE]];
    END;

  LogHeapFree: PUBLIC SIGNAL [calltree: Tree.Link] RETURNS [BOOL, Lexeme.se] = CODE;

  IndirectReturnRecord: PUBLIC PROC [node: Tree.Index, nrets: CARDINAL]
      RETURNS [Lexeme] =
    BEGIN -- also called by SubstExp
    OPEN FOpCodes;
    tlex, hlex: Lexeme.se;
    logged: BOOL;
    [logged, hlex] ← SIGNAL LogHeapFree[[subtree[node]]];
    IF ~logged THEN
      BEGIN
      tlex ← P5.GenTempLex[1];
      P5.SAssign[tlex.lexsei];
      P5U.Out0[qREC];
      hlex ← P5.GenTempLex[nrets];
      P5U.PushLitVal[nrets];
      [] ← P5L.LoadAddress[P5L.VarForLex[hlex]];
      P5U.Out0[qBLT];
      P5.PushLex[tlex];
      P5U.Out0[qFF];
      RETURN [hlex]
      END;
    IF hlex # NullLex THEN
      BEGIN
      P5.SAssign[hlex.lexsei]; 
      P5.PushLex[hlex]; -- will become PUSH, helps stack model
      END;
    RETURN [P5L.TOSAddrLex[nrets, FALSE]]
    END;

  SigExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = SSigErr[node, FALSE];
    RETURN [PRetLex[nrets, node, TRUE]]
    END;

  ErrExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = P5U.WordsForSei[tb[node].info];
    [] ← SSigErr[node, TRUE];
    P5U.OutJump[JumpRet,LabelCCNull];
    RETURN [PRetLex[nrets, node, TRUE]]
    END;

  SysCall: PUBLIC PROC [alpha: BYTE] =
    BEGIN -- puts out call via system transfer vector
    Stack.DeleteToMark[];
    P5U.Out1[FOpCodes.qKFCB, alpha];
    END;

  SysCallN: PUBLIC PROC [alpha: BYTE, n: CARDINAL] =
    BEGIN -- puts out call via system transfer vector
    Stack.DeleteToMark[];
    P5U.Out1[FOpCodes.qKFCB, alpha];
    Stack.Incr[n];
    END;

  Wait: PUBLIC PROC [node: Tree.Index] =
    BEGIN OPEN FOpCodes;
    retry: LabelCCIndex;
    IF ~P5L.LoadAddress[P5L.VarForLex[P5.Exp[tb[node].son[1]]]] THEN P5U.Out0[qLP];
    IF ~P5L.LoadAddress[P5L.VarForLex[P5.Exp[tb[node].son[2]]]] THEN P5U.Out0[qLP];
    P5U.Out0[qDDUP];
    P5U.Out1[qRL, 1];
    P5U.Out0[qMW];
    retry ← P5U.CreateLabel[];
    IF ~P5L.LoadAddress[P5L.VarForLex[P5.Exp[tb[node].son[1]]]] THEN P5U.Out0[qLP];
    IF ~P5L.LoadAddress[P5L.VarForLex[P5.Exp[tb[node].son[2]]]] THEN P5U.Out0[qLP];
    P5U.Out0[qMR];
    CallCatch[IF tb[node].nSons > 2 THEN tb[node].son[3] ELSE Tree.Null];
    P5U.Out1[FOpCodes.qLI, 0];
    P5U.OutJump[JumpE, retry];
    END;

  ForkExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nparms: CARDINAL;
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    Stack.Dump[]; Stack.Mark[];
    nparms ← PushParms[
      argsBuilt: FALSE, t: tb[node].son[2], ptsei: ptsei, saveOne: TRUE, refSafe: FALSE];
    P5.PushRhs[tb[node].son[1]];
    SysCall[IF nparms = MaxParmsInStack THEN SDExtra.sFork12 ELSE SDDefs.sFork];
    IF tb[node].nSons > 2 THEN CallCatch[tb[node].son[3]];
    Stack.Incr[1];
    RETURN [P5L.TOSLex[1]]
    END;

  SJoin: PUBLIC PROC [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN
    localcatch: BOOL = (tb[node].nSons > 2);
    cbti: CCBTIndex;
    ptsei: CSEIndex = P5U.OperandType[tb[node].son[1]];
    Stack.Dump[]; Stack.Mark[];
    P5.PushRhs[tb[node].son[1]];
    SysCall[SDDefs.sJoin];
    IF localcatch THEN
      BEGIN
      n3: Tree.Index = TreeOps.GetNode[tb[node].son[3]];
      cbti ← tb[node].info;
      P5.CatchPhrase[n3];
      END;
    Stack.Incr[1];
    P5U.Out0[FOpCodes.qSFC];
    IF localcatch THEN P5U.Out1[FOpCodes.qCATCH, bb[cbti].index];
    RETURN [P5U.WordsForSei[SymbolOps.TransferTypes[ptsei].typeOut]]
    END;

  JoinExp: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL = SJoin[node];
    RETURN [PRetLex[nrets, node, FALSE]]
    END;

  Join: PUBLIC PROC [node: Tree.Index] = {[] ← SJoin[node]};

  Unlock: PUBLIC PROC [node: Tree.Index] =
    BEGIN
    mlock: Tree.Link = tb[node].son[1];
    IF mlock # Tree.Null THEN
      BEGIN 
      long: BOOL = P5L.LoadAddress[P5L.VarForLex[P5.Exp[mlock]]];
      IF ~long THEN P5U.Out0[FOpCodes.qLP]; 
      P5U.Out0[FOpCodes.qMX]; 
      END;
    END;

  ProcCheck: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    Stack.Dump[]; Stack.Mark[];
    P5.PushRhs[tb[node].son[1]];
    SysCallN[RTSD.sProcCheck, 1];
    RETURN [P5L.TOSLex[1]]
    END;

  PRetLex: PUBLIC PROC [nrets: CARDINAL, node: Tree.Index, sig: BOOL←FALSE]
      RETURNS [Lexeme] =
    BEGIN
    IF nrets > MaxParmsInStack OR (sig AND nrets > MaxParmsInStack-1) THEN
      BEGIN
      Stack.Incr[1];
      RETURN [IndirectReturnRecord[node, nrets]]
      END
    ELSE
      BEGIN
      Stack.Incr[nrets];
      RETURN [P5L.TOSLex[nrets]]
      END
    END;

  Free: PUBLIC PROC [node: Tree.Index] =
    BEGIN
    countedVar: BOOL = tb[node].attr1;
    counted: BOOL = tb[node].attr3;
    zoneLink: Tree.Link = tb[node].son[1];
    varLink: Tree.Link = tb[node].son[2];
    catchLink: Tree.Link = IF tb[node].nSons > 3 THEN tb[node].son[4] ELSE Tree.Null;
    r: VarIndex ← P5L.VarForLex[P5.Exp[varLink]];
    IF counted THEN Counting.Free[r, countedVar, zoneLink, catchLink]
    ELSE
      BEGIN
      rr: VarIndex;
      pLength: CARDINAL = P5U.WordsForOperand[varLink];
      c0: VarIndex ← P5L.OVarItem[[wSize: pLength, space: const[d1:0, d2:0]]];
      long: BOOL = tb[node].attr2;
      bor: BoVarIndex ← P5L.MakeBo[r];
      
      PushVar: PROC =
        BEGIN
	P5L.LoadVar[bor]; [] ← P5L.VarVarAssign[rr, c0, FALSE];
        END;
        
      cb[bor].base ← P5L.EasilyLoadable[cb[bor].base, load];
      rr ← P5L.CopyVarItem[bor];
      ZoneOp[zoneLink, 1, PushVar, catchLink, long];
      END;
    END;

  ZoneOp: PUBLIC PROC [
      zone: Tree.Link, index: CARDINAL, pushArg: PROC, catch: Tree.Link, long: BOOL] =
    BEGIN
    z, zCopy: VarIndex;
    z ← P5L.VarForLex[P5.Exp[zone]];
    [first: z, next: zCopy] ← P5L.ReusableCopies[z, load, FALSE];
    Stack.Dump[];  Stack.Mark[];
    P5L.LoadVar[z];
    pushArg[];
    P5L.LoadVar[zCopy];
    IF long THEN {P5U.Out1[FOpCodes.qRDL, 0]; P5U.Out1[FOpCodes.qRL, index]}
    ELSE {P5U.Out1[FOpCodes.qR, 0]; P5U.Out1[FOpCodes.qR, index]};
    Stack.DeleteToMark[];  Stack.Incr[1];
    P5U.Out0[FOpCodes.qSFC];
    CallCatch[catch];
    END;

  END.