-- Calls.mesa, modified by Sweet, January 15, 1980  1:37 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [BYTE, wordlength],
  Code: FROM "code" USING [
    actenable, catchcount, cfs, CodePassInconsistency, codeptr, fileindex],
  CodeDefs: FROM "codedefs" USING [
    CodeCCIndex, LabelCCIndex, LabelCCNull, Lexeme, MaxParmsInStack, NullLex, 
    VarComponent, VarIndex],
  ComData: FROM "comdata" USING [bodyIndex, stopping],
  ControlDefs: FROM "controldefs" USING [AllocationVectorSize, returnOffset],
  FOpCodes: FROM "fopcodes" USING [
    qALLOC, qBLT, qCATCH, qDUP, qEFC, qFREE, qGADRB, qKFCB, qLFC, qLI, qLL, 
    qLP, qMRE, qMREL, qMXD, qMXDL, qMXW, qMXWL, qPORTI, qPORTO, qPUSH, qR, 
    qRL, qSFC],
  Literals: FROM "literals" USING [STIndex],
  Log: FROM "log" USING [Error, Warning],
  OpTableDefs: FROM "optabledefs" USING [instlength],
  P5: FROM "p5" USING [
    CatchPhrase, Exp, GenTempLex, P5Error, PushLex, PushRhs, ReleaseLock, 
    SAssign, SCatchPhrase, TransferConstruct],
  P5L: FROM "p5l" USING [
    AddrForVar, CopyToTemp, EasilyLoadable, LoadAddress, LoadComponent, 
    TOSAddrLex, TOSLex, VarForLex],
  P5S: FROM "p5s",
  P5U: FROM "p5u" USING [
    AllocCodeCCItem, BitsForOperand, BitsForType, ComputeFrameSize, 
    CreateLabel, InsertLabel, LabelAlloc, LongTreeAddress, NextVar, 
    OperandType, Out0, Out1, OutJump, PushLitVal, SetCodeIndex, 
    TreeLiteralValue, WordsForSei],
  SDDefs: FROM "sddefs" USING [
    sCopy, sError, sErrorList, sFork, sJoin, sRestart, sReturnError, 
    sReturnErrorList, sSignal, sSignalList, sStart, sUnnamedError],
  Stack: FROM "stack" USING [DeleteToMark, Dump, Incr, Load, Mark, Top],
  SymbolOps: FROM "symbolops" USING [
    FindExtension, FirstCtxSe, NextSe, UnderType, WordsForType, XferMode],
  Symbols: FROM "symbols" USING [
    BitAddress, bodyType, BTIndex, BTNull, CBTIndex, ContextLevel, CSEIndex, 
    CTXIndex, ctxType, HTIndex, ISEIndex, lG, MDIndex, RecordSEIndex, SEIndex, 
    SENull, SERecord, seType, TypeClass],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [Index, Link, Null, treeType],
  TreeOps: FROM "treeops" USING [FreeNode, ScanList, UpdateList];

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

  -- imported definitions

  BYTE: TYPE = AltoDefs.BYTE;
  wordlength: CARDINAL = AltoDefs.wordlength;

  sStart: BYTE = SDDefs.sStart;
  sRestart: BYTE = SDDefs.sRestart;
  sFork: BYTE = SDDefs.sFork;
  sJoin: BYTE = SDDefs.sJoin;

  STIndex: TYPE = Literals.STIndex;

  BitAddress: TYPE = Symbols.BitAddress;
  BTIndex: TYPE = Symbols.BTIndex;
  CBTIndex: TYPE = Symbols.CBTIndex;
  BTNull: BTIndex = Symbols.BTNull;
  ContextLevel: TYPE = Symbols.ContextLevel;
  CSEIndex: TYPE = Symbols.CSEIndex;
  CTXIndex: TYPE = Symbols.CTXIndex;
  HTIndex: TYPE = Symbols.HTIndex;
  ISEIndex: TYPE = Symbols.ISEIndex;
  MDIndex: TYPE = Symbols.MDIndex;
  lG: ContextLevel = Symbols.lG;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;
  SEIndex: TYPE = Symbols.SEIndex;
  SERecord: TYPE = Symbols.SERecord;
  TypeClass: TYPE = Symbols.TypeClass;


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

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

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


  SysErrExp: PUBLIC PROCEDURE [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;


  New: PUBLIC PROCEDURE [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.qGADRB, 0];
    SysCall[SDDefs.sCopy];
    CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    Stack.Incr[1];
    RETURN[P5L.TOSLex[1]]
    END;


  SStart: PROCEDURE [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN
    OPEN FOpCodes;
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    Stack.Dump[]; Stack.Mark[];
    [] ← PushParms[tb[node].son[2], ptsei, FALSE];
    P5.PushRhs[tb[node].son[1]];
    SysCall[sStart];
    CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    WITH seb[ptsei] SELECT FROM
      transfer => nrets ← P5U.WordsForSei[outRecord];
      ENDCASE;
    RETURN
    END;

  Start: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN
    [] ← SStart[node];
    RETURN
    END;

  StartExp: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL ← SStart[node];

    RETURN [PRetLex[nrets, node, FALSE]];
    END;

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


  Stop: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN OPEN FOpCodes;
    IF ~MPtr.stopping THEN SIGNAL CPtr.CodePassInconsistency;
    P5U.Out1[qLL, ControlDefs.returnOffset]; P5U.Out0[qSFC];
    CallCatch[IF tb[node].nSons = 1 THEN tb[node].son[1] ELSE Tree.Null];
    RETURN
    END;


  CallCatch: PROCEDURE [t: Tree.Link] =
    BEGIN
    clabel: LabelCCIndex;

    WITH t SELECT FROM
      subtree =>
	IF t # Tree.Null THEN P5.CatchPhrase[index]
	ELSE
	  IF CPtr.actenable # LabelCCNull THEN
	    BEGIN
	    clabel ← P5U.LabelAlloc[];
	    P5U.Out1[FOpCodes.qCATCH, CPtr.cfs];
	    P5U.OutJump[JumpA, clabel];
	    P5U.OutJump[Jump, CPtr.actenable];
	    P5U.InsertLabel[clabel];
	    END;
      ENDCASE;
    RETURN
    END;


  SCall: PROCEDURE [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN -- generates code for procedure call statement
    OPEN FOpCodes;
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    portcall: BOOLEAN ← SymbolOps.XferMode[ptsei] = port;
    computedtarget: BOOLEAN;
    inlineTree: Tree.Link;
    nparms: CARDINAL;
    sei: ISEIndex;
    bti: CBTIndex;
    a: BitAddress;
    inlineCall: BOOLEAN;

    WITH tb[node].son[1] SELECT FROM
      symbol =>
	BEGIN
	sei ← index;
	inlineCall ← seb[sei].constant AND seb[sei].extended;
	computedtarget ← ctxb[seb[sei].idCtx].level # lG;
	END;
      ENDCASE => 
	BEGIN
	inlineCall ← FALSE;
	computedtarget ← TRUE;
	END;
    IF ~inlineCall THEN Stack.Dump[]; 
    Stack.Mark[];
    nparms ← PushParms[tb[node].son[2], ptsei, FALSE];
    IF inlineCall THEN 
      BEGIN
      Stack.DeleteToMark[];
      inlineTree ← SymbolOps.FindExtension[sei].tree;
      WITH inlineTree SELECT FROM
        subtree => TreeOps.ScanList[tb[index].son[1], CodeInline];
        ENDCASE => P5.P5Error[513];
      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[qPORTO]; P5U.Out0[qPORTI];
	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
      IF seb[sei].constant THEN 
        BEGIN
        bti ← seb[sei].idInfo;
	IF bti # BTNull AND bb[bti].nesting = Outer THEN
	  P5U.Out1[qLFC, bb[bti].entryIndex]
        ELSE
	  BEGIN
	  P5.PushLex[[se[sei]]];
	  P5U.Out0[qSFC];
	  END;
        END
      ELSE IF portcall THEN
	BEGIN
	[] ← P5L.LoadAddress[P5L.VarForLex[[se[sei]]]];
	P5U.Out0[qPORTO]; P5U.Out0[qPORTI];
	END
      ELSE IF seb[sei].linkSpace THEN
	BEGIN a ← seb[sei].idValue; P5U.Out1[qEFC, a.wd]; END
      ELSE BEGIN P5.PushLex[[se[sei]]]; P5U.Out0[qSFC] END;
      END;
    WITH seb[ptsei] SELECT FROM
      transfer => nrets ← P5U.WordsForSei[outRecord];
      ENDCASE => P5.P5Error[514];
    IF inlineCall THEN 
      BEGIN
      IF tb[node].nSons = 3 THEN
        WITH tb[node].son[3] SELECT FROM
          subtree => P5.CatchPhrase[index];
          ENDCASE;
      END
    ELSE CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    RETURN
    END;

  ConstructOnStack: PUBLIC PROCEDURE [maint: Tree.Link, rcsei: RecordSEIndex] =
    BEGIN OPEN SymbolOps;
    ctx: CTXIndex = seb[rcsei].fieldCtx;
    sei: ISEIndex;
    
    DoSafen: PROCEDURE [t: Tree.Link] RETURNS [Tree.Link] =
      BEGIN
      WITH t SELECT FROM
	subtree => 
	  BEGIN
	  node: Tree.Index = index;
	  SELECT tb[index].name FROM
	    safen =>
	      BEGIN
	      r: VarIndex = P5L.VarForLex[P5.Exp[tb[index].son[1]]];
	      sei: ISEIndex = P5L.CopyToTemp[r].sei;
	      seb[sei].idType ← tb[node].info;
	      TreeOps.FreeNode[node];
	      RETURN [[symbol[sei]]];
	      END;
	    cast, pad =>
	      tb[node].son[1] ← TreeOps.UpdateList[tb[node].son[1], DoSafen];
	    ENDCASE; -- dont unroll nested constructors
	  END;
	ENDCASE;
      RETURN[t];
      END;
    
    LoadOne: PROCEDURE [t: Tree.Link] =
      BEGIN
      WITH t SELECT FROM
        subtree =>
	  BEGIN
	  IF t = Tree.Null THEN
	    BEGIN
	    THROUGH [0..SymbolOps.WordsForType[seb[sei].idType]) DO
	      P5U.Out1[FOpCodes.qLI, 0];
	      ENDLOOP;
	    END
          ELSE SELECT tb[index].name FROM
	    pad =>
	      BEGIN
	      delta: CARDINAL;
	      t1: Tree.Link = tb[index].son[1];
	      delta ←
		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;
	    ENDCASE => P5.PushRhs[t];
	  END;
	ENDCASE => P5.PushRhs[t];
      sei ← P5U.NextVar[SymbolOps.NextSe[sei]];
      END;
    
    maint ← TreeOps.UpdateList[maint, DoSafen];
    sei ← SymbolOps.FirstCtxSe[ctx];
    TreeOps.ScanList[maint, LoadOne];
    END;

  SSigErr: PROCEDURE [node: Tree.Index, error: BOOLEAN] RETURNS [nrets: CARDINAL] =
    BEGIN -- generates code for signal/error
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    nparms: CARDINAL;
    sysFn: ARRAY BOOLEAN OF ARRAY BOOLEAN OF BYTE = [
      [SDDefs.sSignal, SDDefs.sSignalList],
      [SDDefs.sError, SDDefs.sErrorList]];

    Stack.Dump[]; Stack.Mark[];
    IF tb[node].son[1] = Tree.Null THEN P5U.PushLitVal[-1]
    ELSE P5.PushRhs[tb[node].son[1]];
    nparms ← PushParms[tb[node].son[2], ptsei, TRUE];
    SysCall[sysFn[error][nparms > 1]];
    WITH seb[ptsei] SELECT FROM
      transfer => nrets ← P5U.WordsForSei[outRecord];
      ENDCASE;
    CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    RETURN
    END;

  RetWithError: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN -- generates code for RETURN WITH error
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    nparms: CARDINAL;
    monitored: BOOLEAN ← tb[node].attr1;

    IF monitored AND tb[node].attr2 THEN
      BEGIN P5.ReleaseLock[]; monitored ← FALSE END;
    Stack.Dump[]; Stack.Mark[];
    IF tb[node].son[1] = Tree.Null THEN P5U.PushLitVal[-1]
    ELSE P5.PushRhs[tb[node].son[1]];
    nparms ← PushParms[tb[node].son[2], ptsei, TRUE];
    IF monitored THEN
      BEGIN
      Stack.Dump[];
      P5.ReleaseLock[];
      Stack.Load[Stack.Top[2],2];
      END;
    SysCall[IF nparms > 1 THEN SDDefs.sReturnErrorList 
		ELSE SDDefs.sReturnError];
    P5U.OutJump[JumpRet,LabelCCNull];
    RETURN
    END;

  CodeInline: PROCEDURE [t: Tree.Link] =
    BEGIN
    opByte: ARRAY [0..3] OF BYTE;
    iLength: CARDINAL ← 0;
    tLength: CARDINAL;
    i: CARDINAL;
    c: CodeCCIndex;

    PickUpByte: PROCEDURE [t: Tree.Link] =
      BEGIN
      opByte[iLength] ← WITH t SELECT FROM
	symbol => seb[index].idValue,
	ENDCASE => P5U.TreeLiteralValue[t];
      IF (iLength ← iLength+1) > 3 THEN 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];
    P5U.SetCodeIndex[CPtr.fileindex];
    c ← P5U.AllocCodeCCItem[iLength-1];
    cb[c].realinst ← TRUE;
    cb[c].inst ← opByte[0];
    cb[c].isize ← iLength;
    FOR i IN [1..iLength) DO cb[c].parameters[i] ← opByte[i]; ENDLOOP;
    END;


  PushParms: PROCEDURE [t: Tree.Link, ptsei: CSEIndex, sigerr: BOOLEAN] 
      RETURNS [nparms: CARDINAL] =
    BEGIN
    rsei: RecordSEIndex;

    WITH seb[ptsei] SELECT FROM
      transfer => rsei ← LOOPHOLE[SymbolOps.UnderType[inRecord]];
      ENDCASE => P5.P5Error[515];
    RETURN [BuildArgRecord[t, rsei, sigerr]];
    END;

  BuildArgRecord: PUBLIC PROCEDURE [
    t: Tree.Link, rsei: RecordSEIndex, sigerr, isResume: BOOLEAN ← FALSE]
      RETURNS [nparms: CARDINAL] =
    BEGIN
    nparms ← IF rsei = Symbols.SENull THEN 0 ELSE P5U.WordsForSei[rsei];
    IF nparms > MaxParmsInStack OR (sigerr AND nparms > 1) THEN
      BEGIN
      fs: CARDINAL ← P5U.ComputeFrameSize[nparms];
      IF bb[MPtr.bodyIndex].resident THEN
        fs ← fs+ControlDefs.AllocationVectorSize;
      P5U.PushLitVal[fs];
      P5U.Out0[FOpCodes.qALLOC];
      P5.TransferConstruct[P5L.TOSAddrLex[nparms], t, rsei];
      END
    ELSE IF sigerr AND ~isResume AND nparms = 0 THEN P5U.PushLitVal[-1]
    ELSE IF nparms # 0 THEN ConstructOnStack[t, rsei];
    RETURN
    END;

  Call: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN
    [] ← SCall[node];
    RETURN
    END;

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

  CallExp: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    nrets: CARDINAL ← SCall[node];

    RETURN[PRetLex[nrets, node, FALSE]];
    END;

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

  IndirectReturnRecord: PUBLIC PROCEDURE [node: Tree.Index, nrets: CARDINAL] RETURNS[Lexeme] =
    BEGIN -- also called by SubstExp
    OPEN FOpCodes;
    tlex, hlex: se Lexeme;
    logged: BOOLEAN;

    [logged, hlex] ← SIGNAL LogHeapFree[Tree.Link[subtree[node]]];
    IF ~logged THEN
      BEGIN
      tlex ← P5.GenTempLex[1];
      P5.SAssign[tlex.lexsei];
      P5U.Out0[qPUSH];
      hlex ← P5.GenTempLex[nrets];
      P5U.PushLitVal[nrets];
      [] ← P5L.LoadAddress[P5L.VarForLex[hlex]];
      P5U.Out0[qBLT];
      P5.PushLex[tlex];
      P5U.Out0[qFREE];
      RETURN [hlex]
      END;
    IF hlex # NullLex THEN
      BEGIN P5.SAssign[hlex.lexsei]; P5U.Out0[qPUSH]; END;
    RETURN [P5L.TOSAddrLex[nrets, FALSE]]
    END;

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

  ErrExp: PUBLIC PROCEDURE [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 PROCEDURE [alpha: BYTE] =
    BEGIN -- puts out call via system transfer vector
    Stack.DeleteToMark[];
    P5U.Out1[FOpCodes.qKFCB, alpha];
    RETURN
    END;

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

  Wait: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN OPEN FOpCodes;
    retry: LabelCCIndex;
    t1Long: BOOLEAN ← P5U.LongTreeAddress[tb[node].son[1]];
    t2Long: BOOLEAN ← P5U.LongTreeAddress[tb[node].son[2]];
    longWait: BOOLEAN;
    t1avar, t2avar: VarComponent;
    t1avar ← P5L.AddrForVar[P5L.VarForLex[P5.Exp[tb[node].son[1]]]];
    t1avar ← P5L.EasilyLoadable[t1avar, store];
    P5L.LoadComponent[t1avar];
    IF ~t1Long AND t2Long THEN P5U.Out0[qLP];
    t2avar ← P5L.AddrForVar[P5L.VarForLex[P5.Exp[tb[node].son[2]]]];
    t2avar ← P5L.EasilyLoadable[t2avar, store];
    P5L.LoadComponent[t2avar];
    longWait ← t1Long OR t2Long;
    IF ~longWait THEN
      BEGIN 
      P5U.Out0[qDUP]; P5U.Out1[qR,1];  -- load timeout 
      P5U.Out0[qMXW];
      END 
    ELSE
      BEGIN
      IF ~t2Long THEN P5U.Out0[qLP];
      P5L.LoadComponent[t2avar]; -- since DUP doesn't work well for longs
      P5U.Out1[IF t2Long THEN qRL ELSE qR, 1];
      P5U.Out0[qMXWL];
      END;
    retry ← P5U.CreateLabel[];
    P5L.LoadComponent[t1avar];
    IF longWait AND ~t1Long THEN P5U.Out0[qLP];
    P5L.LoadComponent[t2avar];
    IF longWait AND ~t2Long THEN P5U.Out0[qLP];
    P5U.Out0[IF longWait THEN qMREL ELSE qMRE];
    CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    P5U.Out1[FOpCodes.qLI, 0];
    P5U.OutJump[JumpE, retry];
    END;

  ForkExp: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    Stack.Dump[]; Stack.Mark[];
    [] ← PushParms[tb[node].son[2], ptsei, FALSE];
    P5.PushRhs[tb[node].son[1]];
    SysCall[sFork];
    CallCatch[IF tb[node].nSons = 3 THEN tb[node].son[3] ELSE Tree.Null];
    Stack.Incr[1];
    RETURN [P5L.TOSLex[1]];
    END;

  SJoin: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [nrets: CARDINAL] =
    BEGIN
    localcatch: BOOLEAN ← tb[node].nSons = 3;
    ptsei: CSEIndex ← P5U.OperandType[tb[node].son[1]];
    aroundlabel, firstcatch: LabelCCIndex;
    savcfs: CARDINAL ← CPtr.cfs;
    Stack.Dump[]; Stack.Mark[];
    P5.PushRhs[tb[node].son[1]];
    SysCall[sJoin];
    IF localcatch THEN
      BEGIN
      cr: CodeCCIndex;
      aroundlabel ← P5U.LabelAlloc[];
      firstcatch ← P5U.LabelAlloc[];
      CPtr.catchcount ← CPtr.catchcount + 1;
      P5U.Out1[FOpCodes.qCATCH, 0];
      cr ← LOOPHOLE[CPtr.codeptr, CodeCCIndex];
      P5U.OutJump[JumpA, aroundlabel];
      P5U.InsertLabel[firstcatch];
      WITH tb[node].son[3] SELECT FROM
	subtree => P5.SCatchPhrase[index];
	ENDCASE => P5.P5Error[516];
      cb[cr].parameters[1] ← CPtr.cfs;
      P5U.InsertLabel[aroundlabel];
      CPtr.catchcount ← CPtr.catchcount - 1;
      END 
    ELSE CallCatch[Tree.Null];
    Stack.Incr[1];
    P5U.Out0[FOpCodes.qSFC];
    IF localcatch THEN 
      BEGIN
      aroundlabel ← P5U.LabelAlloc[];
      P5U.Out1[FOpCodes.qCATCH, CPtr.cfs];
      P5U.OutJump[JumpA, aroundlabel];
      P5U.OutJump[Jump, firstcatch];
      P5U.InsertLabel[aroundlabel];
      CPtr.cfs ← savcfs;
      END
    ELSE CallCatch[Tree.Null];
    WITH seb[ptsei] SELECT FROM
      transfer => nrets ← P5U.WordsForSei[outRecord];
      ENDCASE;
    END;

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

  Join: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN
    [] ← SJoin[node];
    END;

  Unlock: PUBLIC PROCEDURE [node: Tree.Index] =
    BEGIN
    mlock: Tree.Link ← tb[node].son[1];
    IF mlock # Tree.Null THEN
      BEGIN 
      long: BOOLEAN ← P5L.LoadAddress[P5L.VarForLex[P5.Exp[mlock]]]; 
      P5U.Out0[IF long THEN FOpCodes.qMXDL ELSE FOpCodes.qMXD]; 
      END;
    RETURN
    END;

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

  END...