-- Final.mesa, modified by Sweet, March 20, 1979  10:57 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [BYTE],
  Code: FROM "code" USING [CodePassInconsistency, codeptr, dStar],
  CodeDefs: FROM "codedefs" USING [CCIndex, CCInfoType, CCNull, JumpCCIndex, JumpCCNull, JumpType, LabelCCIndex, LabelCCNull, NULLfileindex, RelativePC],
  ComData: FROM "comdata" USING [switches],
  FOpCodes: FROM "fopcodes",
    Mopcodes: FROM "mopcodes" USING [zJB, zJEQ4, zJEQB, zJGB, zJGEB, zJLB, zJLEB, zJNE4, zJNEB, zJUGB, zJUGEB, zJULB, zJULEB, 
    zJW, zJZEQB, zJZNEB],
    OpCodeParams: FROM "opcodeparams" USING [zJEQn, zJn, zJNEn],
  OpTableDefs: FROM "optabledefs" USING [instaligned, instlength],
  P5: FROM "p5" USING [C0, C1, C1W, PeepHole],
  P5F: FROM "p5f",
  P5U: FROM "p5u" USING [DeleteCell],
  PeepholeDefs: FROM "peepholedefs" USING [RemoveThisPop, SetRealInst, SetSourceIndex],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType];

Final: PROGRAM
    IMPORTS CPtr: Code, MPtr: ComData, OpTableDefs, P5U, P5, P5F, PeepholeDefs 
    EXPORTS CodeDefs, P5, P5F =
  BEGIN
  OPEN CodeDefs;

  cb: Table.Base;		-- code base (local copy)

  CJump: ARRAY JumpType[JumpE..ZJumpN] OF JumpType = [
	JumpN, JumpE, JumpGE, JumpL, JumpLE, JumpG,
	UJumpGE, UJumpL, UJumpLE, UJumpG, ZJumpN, ZJumpE];

  FinalNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[Tree.treeType];
    RETURN
    END;

  DidSomething: PUBLIC BOOLEAN;
  StartIndex, EndIndex: PUBLIC CCIndex;
  SeenSwitch: BOOLEAN;
  JumpCellCount: CARDINAL;


  ccInfo: CCInfoType ← generating;

  CCInfoMeaning: PUBLIC PROCEDURE RETURNS [CCInfoType] =
    BEGIN
    RETURN[ccInfo]
    END;

  Fixup: PUBLIC PROCEDURE [start: CCIndex] =
    BEGIN -- a final pass over the code to fix up jumps
    jumpsbefore, jumpsafter, totalJumps: CARDINAL;
    crossJump: BOOLEAN ← MPtr.switches['j];
    dStar: BOOLEAN = CPtr.dStar;
    ccInfo ← generating;
    DidSomething ← TRUE;
    SeenSwitch ← TRUE;
    StartIndex ← start;

    --ComplementCursor[];
    PeepholeDefs.SetRealInst[FALSE];
    PeepholeDefs.SetSourceIndex[NULLfileindex];
    DO
      -- pass 0: distinguish forward and backward jumps
      CPass0[];
      IF ~DidSomething THEN EXIT;
      DidSomething ← FALSE;
      SeenSwitch ← ~SeenSwitch;
      -- pass 1: eliminate multiple labels
      CPass1[];
      -- pass 2: eliminate jump to jumps
      CPass2[];
      -- pass 3: eliminate unreachable code
      CPass3[];
      -- pass 4: replace cj-j seq. with ccj
      CPass4[];
      -- pass 5: cross jumping
      IF crossJump THEN P5F.CPass5[];
      ENDLOOP; -- end of the meta-pass consisting of passes 0-5

    -- pass 6: do some peephole optimization: load-store, EXCH-commutative op.
    P5.PeepHole[StartIndex];
    -- jump threads are now pc's, debug output take note
    ccInfo ← binding;
    -- pass 7: set length and alignment, count jumps
    totalJumps ← jumpsafter ← CPass7[];
    jumpsbefore ← jumpsafter+1;
    -- pass 8: resolve (most) jump instructions
    THROUGH [1..3] WHILE jumpsafter # 0 AND jumpsafter < jumpsbefore DO
      jumpsbefore ← jumpsafter;
      jumpsafter ← CPass8[dStar];
      ENDLOOP;
    -- pass 9: resolve (remaining) jump instructions
    IF jumpsafter # 0 THEN CPass9[dStar];
    -- pass 10: set pad fields
    CPass10[dStar];
    -- pass 11: code jumps
    ccInfo ← coding;
    IF totalJumps # 0 THEN CPass11[dStar];
    --ComplementCursor[];
    RETURN
    END;


  CPass0: PROCEDURE =
    BEGIN  -- pass 0: distinguish forward and backward jumps
    c: CCIndex;

    JumpCellCount ← 0;
    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      EndIndex ← c;
      WITH cb[c] SELECT FROM
	label => labelseen ← SeenSwitch;
	jump =>
	  BEGIN
	  forward ←
	    IF destlabel = LabelCCNull THEN TRUE
	    ELSE ~(cb[destlabel].labelseen = SeenSwitch);
	  JumpCellCount ← JumpCellCount + 1;
	  END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;


  CPass1: PROCEDURE =
    BEGIN   -- pass 1: eliminate multiple labels, unreferenced labels,
	    --         and jumps to .+1
    duplabel, unreferencedlabel: LabelCCIndex;
    nextc, c: CCIndex;

    FOR c ← cb[StartIndex].flink, nextc WHILE c # CCNull DO
      WITH cc:cb[c] SELECT FROM
	jump =>
	  IF DotPlusOneJump[LOOPHOLE[c]] THEN
	    BEGIN
	    IF UCjump[c] THEN GO TO gunIt
	    ELSE IF cc.jtype IN [JumpE..UJumpLE] THEN
	      BEGIN
	      THROUGH [0..2) DO
		CPtr.codeptr ← cc.blink;
		P5.C0[FOpCodes.qPOP];
		[] ← PeepholeDefs.RemoveThisPop[CPtr.codeptr];
		ENDLOOP;
	      GO TO gunIt;
	      END;
	    nextc ← cc.flink;
	    EXITS
	      gunIt =>
		BEGIN
		UnthreadJump[LOOPHOLE[c]]; nextc ← cc.flink;
		DidSomething ← TRUE; P5U.DeleteCell[c];
		END;
	    END
	  ELSE nextc ← cc.flink;
	label =>
	  IF cc.jumplist = JumpCCNull THEN
	    BEGIN
	    unreferencedlabel ← LOOPHOLE[c, LabelCCIndex]; nextc ← cc.flink;
	    DidSomething ← TRUE; P5U.DeleteCell[unreferencedlabel];
	    END
	  ELSE 
	    BEGIN
	    duplabel ← LOOPHOLE[c, LabelCCIndex]; nextc ← cc.flink;
	    IF cc.flink = CCNull THEN RETURN;
	    WITH cb[cc.flink] SELECT FROM
	      label =>
		BEGIN
		DeleteLabel[duplabel, LOOPHOLE[cc.flink, LabelCCIndex]];
		DidSomething ← TRUE;
		END;
	      ENDCASE;
	    END;
	ENDCASE => nextc ← cc.flink;
      ENDLOOP;
   RETURN
   END;

  DotPlusOneJump: PROCEDURE [jc: JumpCCIndex] RETURNS [BOOLEAN] =
    BEGIN
    c: CCIndex;
    target: CCIndex ← cb[jc].destlabel;

    FOR c ← cb[jc].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	other => IF otag = table THEN RETURN[FALSE];
	label => RETURN [c = target];
	ENDCASE => RETURN[FALSE];
      ENDLOOP;
    RETURN[FALSE];
    END;


  CPass2: PROCEDURE =
    BEGIN   -- pass 2: eliminate jump to jumps
    c, cc: CCIndex;
    jc: JumpCCIndex;
    jtojexists: BOOLEAN;
    jclabel, formerlabel: LabelCCIndex;
    jccount: CARDINAL;

    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  IF destlabel # LabelCCNull THEN
	    BEGIN
	    jtojexists ← FALSE;
	    jccount ← 0;
	    jc ← LOOPHOLE[c, JumpCCIndex];
	    DO
	      jclabel ← cb[jc].destlabel;
	      IF (cc ← cb[jclabel].flink) = CCNull THEN EXIT;
	      IF ~UCjump[cc] THEN EXIT;
	      jc ← LOOPHOLE[cc, JumpCCIndex];
	      IF jc = c THEN BEGIN jtojexists ← FALSE; EXIT END;
	      jccount ← jccount +1;
	      IF jccount > JumpCellCount THEN
		BEGIN jtojexists ← FALSE; EXIT END;
	      jtojexists ← TRUE;
	      ENDLOOP;
	    IF jtojexists THEN
	      BEGIN
	      DidSomething ← TRUE;
	      formerlabel ← destlabel;
	      UnthreadJump[LOOPHOLE[c, JumpCCIndex]];
	      thread ← cb[jclabel].jumplist;
	      cb[jclabel].jumplist ← LOOPHOLE[c, JumpCCIndex];
	      destlabel ← jclabel;
	      END;
	    END;
	ENDCASE
      ENDLOOP;
    RETURN
    END;


  CPass3: PROCEDURE =
    BEGIN   -- pass 3: eliminate unreachable code
    c, cc, oldc: CCIndex;

    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  IF UCjump[c] OR jtype = JumpRet OR jtype = JumpCA THEN
	    BEGIN
	    cc ← flink;
	    DO
	      IF (oldc ← cc) = CCNull THEN RETURN;
	      cc ← cb[cc].flink;
	      WITH cb[oldc] SELECT FROM
		label => IF jumplist # JumpCCNull THEN EXIT;
		jump => UnthreadJump[LOOPHOLE[oldc, JumpCCIndex]];
		other => IF otag # table THEN LOOP; --body start/stop
		ENDCASE;
	      P5U.DeleteCell[oldc];
	      DidSomething ← TRUE;
	      ENDLOOP;
	    END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;

  CPass4: PROCEDURE =
    BEGIN   -- pass 4: replace cj-j seq. with ccj
    c, nextc: CCIndex;

    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH oldc:cb[c] SELECT FROM
	jump =>
	  IF oldc.jtype IN [JumpE..ZJumpN] THEN
	    BEGIN
	    nextc ← oldc.flink;
	    IF nextc = CCNull THEN RETURN;
	    WITH nc:cb[nextc] SELECT FROM
	      jump =>
		IF UCjump[nextc] AND (cb[oldc.destlabel].blink = nextc) THEN
		  BEGIN
		  newLbl: LabelCCIndex = nc.destlabel;
		  UnthreadJump[LOOPHOLE[nextc, JumpCCIndex]];
		  UnthreadJump[LOOPHOLE[c, JumpCCIndex]];
		  oldc.destlabel ← newLbl;
		  oldc.thread ← cb[newLbl].jumplist;
		  cb[newLbl].jumplist ← LOOPHOLE[c, JumpCCIndex];
		  oldc.jtype ← CJump[oldc.jtype];
		  oldc.forward ← nc.forward;
		  P5U.DeleteCell[nextc];
		  END;
	      ENDCASE;
	    END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;

  CPass7: PROCEDURE RETURNS [unboundJumps: CARDINAL] =
    BEGIN -- pass 7: set length and alignment, count jumps
    c, next: CCIndex;

    unboundJumps ← 0;
    FOR c ← cb[StartIndex].flink, next WHILE c # CCNull DO
      next ← cb[c].flink;
      WITH cb[c] SELECT FROM
	code => IF ~realinst THEN -- ignore dummies for fgt info
	  BEGIN
	  aligned ← FALSE; isize ← 0;
	  END
	ELSE
	  BEGIN
	  IF isize = 0 THEN isize ← OpTableDefs.instlength[inst];
	  aligned ← isize = 3 OR OpTableDefs.instaligned[inst];
	  END;
	jump =>
	  IF jtype = JumpRet THEN
	    P5U.DeleteCell[c]
	  ELSE unboundJumps ← unboundJumps+1;
	ENDCASE;
      ENDLOOP;
    END;

  CPass8: PROCEDURE [dStar: BOOLEAN] RETURNS [unboundJumps: CARDINAL] =
    BEGIN -- pass 8: resolve easy jumps
    c: CCIndex;
    min, max: CARDINAL;
    binder: PROCEDURE [min, max: INTEGER, c: JumpCCIndex] RETURNS [bindable: BOOLEAN] = IF dStar THEN
        P5F.BindJumpDStar
      ELSE P5F.BindJump;

    unboundJumps ← 0;
    IF dStar THEN P5F.FillInPCEstimatesDStar[]
    ELSE P5F.FillInPCEstimates[];
    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump => IF ~fixedup THEN
	  BEGIN
	  target: LabelCCIndex ← destlabel;
	  IF forward THEN 
	    BEGIN 
	    min ← cb[target].minPC - minPC;
	    max ← cb[target].maxPC - maxPC;
	    END
	  ELSE
	    BEGIN 
	    min ← minPC - cb[target].minPC;
	    max ← maxPC - cb[target].maxPC;
	    END;
	  IF ~binder[min, max, LOOPHOLE[c, JumpCCIndex]]
	      THEN unboundJumps ← unboundJumps+1;
	  END;
	ENDCASE;
      ENDLOOP;
    END;


  CPass9: PROCEDURE [dStar: BOOLEAN] =
    BEGIN   -- pass 9: resolve (remaining) jump instructions
    c: CCIndex;
    nbytes: CARDINAL;
    binder: PROCEDURE [min, max: INTEGER, c: JumpCCIndex] RETURNS [bindable: BOOLEAN] = IF dStar THEN
        P5F.BindJumpDStar
      ELSE P5F.BindJump;

    IF dStar THEN P5F.FillInPCEstimatesDStar[]
    ELSE P5F.FillInPCEstimates[];
    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  IF ~fixedup THEN
	      BEGIN
	      IF forward THEN 
	        BEGIN
		nbytes ← cb[destlabel].maxPC - maxPC;
	        END
	      ELSE
	        BEGIN 
	        nbytes ← maxPC - cb[destlabel].maxPC;
	        END;
	      [] ← binder[nbytes, nbytes, LOOPHOLE[c, JumpCCIndex]];
	      END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;

  CPass10: PROCEDURE [dStar: BOOLEAN] =
    BEGIN -- pass 10: set pad field of chunks
    c: CCIndex;
    parity: [0..2] ← 0;
    cpad: [0..1];
    aligned: BOOLEAN;
    t: CARDINAL;

    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      IF dStar THEN
	BEGIN cb[c].pad ← 0; LOOP END;
      WITH cc:cb[c] SELECT FROM
	code =>
	  BEGIN
	  t ← cc.isize;
	  aligned ← cc.aligned;
	  END;
	other => WITH cc SELECT FROM
	  table =>
	    BEGIN
	    t ← tablecodebytes;
	    aligned ← TRUE;
	    END;
	  ENDCASE =>
	    BEGIN
	    t ← 0;
	    aligned ← FALSE;
	    END;
	jump =>
	  IF cc.completed THEN BEGIN t ← 0; aligned ← FALSE END
	  ELSE
	    BEGIN
	    t ← cc.jsize;
	    aligned ←  t > 1;
	    END;
        label =>
	  BEGIN
	  t ← 0;
	  aligned ← FALSE;
	  END;
	ENDCASE;
      parity ← (parity+t) MOD 2;
      IF aligned AND parity # 0 THEN 
	BEGIN
	cpad ← 1;
	parity ← 0;
	END
      ELSE cpad ← 0;
      cb[c].pad ← cpad;
      ENDLOOP;
    END;


  CPass11: PROCEDURE [dStar: BOOLEAN] =
    BEGIN   -- pass 11: code jumps
    c: CCIndex;
    coder: PROCEDURE [nbytes: INTEGER, c: JumpCCIndex] = IF dStar THEN
	P5F.CodeJumpDStar
      ELSE P5F.CodeJump;

    FillInPC[];
    FOR c ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  BEGIN
	  IF ~fixedup THEN SIGNAL CPtr.CodePassInconsistency 
	  ELSE coder[(IF forward THEN cb[destlabel].pc - pc
	    ELSE pc - cb[destlabel].pc), LOOPHOLE[c, JumpCCIndex]];
	  END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;


  DeleteLabel: PROCEDURE [oldc, c: LabelCCIndex] =
    BEGIN -- removes extra label from code stream
    lq, q: JumpCCIndex;

    IF cb[c].jumplist = JumpCCNull THEN cb[c].jumplist ← cb[oldc].jumplist
    ELSE
      BEGIN
      q ← cb[c].jumplist;
      UNTIL q = JumpCCNull DO
	lq ← q;
	q ← cb[q].thread;
	ENDLOOP;
      cb[lq].thread ← cb[oldc].jumplist;
      END;
    FOR q ← cb[oldc].jumplist, cb[q].thread UNTIL q = JumpCCNull
      DO cb[q].destlabel ← c ENDLOOP;
    P5U.DeleteCell[oldc];
    RETURN
    END;


  UnthreadJump: PUBLIC PROCEDURE [c: JumpCCIndex] =
    BEGIN -- pull jump cell out of thread from label
    l: LabelCCIndex ← cb[c].destlabel;
    jc: JumpCCIndex;

    IF l = LabelCCNull THEN RETURN;
    jc ← cb[l].jumplist;
    IF jc = c THEN cb[l].jumplist ← cb[jc].thread
    ELSE
      BEGIN
      UNTIL cb[jc].thread = c DO jc ← cb[jc].thread ENDLOOP;
      cb[jc].thread ← cb[c].thread;
      END;
    RETURN
    END;


  UCjump: PUBLIC PROCEDURE [c: CCIndex] RETURNS [BOOLEAN] =
    BEGIN -- predicate testing if c is an unconditonal jump
    WITH cb[c] SELECT FROM
      jump => RETURN[jtype = Jump];
      ENDCASE =>  RETURN[FALSE]
    END;


  Removeablejump: PROCEDURE [c: CCIndex] RETURNS [BOOLEAN] =
    BEGIN -- predicate testing if c is an unconditonal jump
    WITH cb[c] SELECT FROM
      jump => RETURN[(jtype = Jump OR jtype = JumpA OR jtype = JumpCA)];
      ENDCASE =>  RETURN[FALSE]
    END;


  FillInPC: PROCEDURE =
    BEGIN -- fills in relative PC of all labels and jumps.
    -- all jump lengths have been resolved and pad values set
    -- PC of forward jump is end of instruction
    -- PC of backward jump is start of pad (if any)
    k: CCIndex;
    rpc: RelativePC;
    nbytes: CARDINAL;

    rpc ← 0;
    FOR k ← StartIndex, cb[k].flink UNTIL k = CCNull DO
      nbytes ← cb[k].pad + (WITH cc:cb[k] SELECT FROM
	code => cc.isize,
	jump => IF cc.completed THEN 0 ELSE cc.jsize,
	other => (WITH cc SELECT FROM
	  table => tablecodebytes,
	  ENDCASE => 0),
	ENDCASE => 0);
      WITH cc:cb[k] SELECT FROM
	jump => 
	  IF cc.forward THEN BEGIN rpc ← rpc+nbytes; cc.pc ← rpc; LOOP END
	  ELSE BEGIN cc.pc ← rpc; END;
	label => cc.pc ← rpc;
	ENDCASE;
      rpc ← rpc+nbytes;
      ENDLOOP;
    RETURN
    END;

  CodeJumpDist: PUBLIC PROCEDURE [jDist: INTEGER, l: [0..7], pad: [0..1], c: JumpCCIndex, dStar: BOOLEAN] =
    BEGIN -- code all jump instruction(s)
    OPEN Mopcodes, OpCodeParams;
    t: JumpType;
    RelJumpOps: ARRAY JumpType[JumpL..ZJumpN] OF AltoDefs.BYTE = [
      zJLB, zJGEB, zJGB, zJLEB, zJULB, zJUGEB, zJUGB, zJULEB,
      zJZEQB, zJZNEB];
  
    t ← cb[c].jtype;
    SELECT t FROM
     Jump, JumpA, JumpCA =>
      SELECT l FROM
       1 =>
        BEGIN
        IF jDist ~IN [2..9] THEN SIGNAL CPtr.CodePassInconsistency;
        P5.C0[zJn+jDist-2];
        END;
       2 =>
        BEGIN
        IF jDist ~IN [-128..128) THEN SIGNAL CPtr.CodePassInconsistency;
        P5.C1[zJB, jDist];
        cb[CPtr.codeptr].pad ← pad;
        END;
       ENDCASE =>
        BEGIN
        P5.C1W[zJW, jDist];
        cb[CPtr.codeptr].pad ← pad;
        END;
     JumpE, JumpN =>
      SELECT l FROM
       1 =>
        BEGIN
        IF jDist ~IN [2..9] THEN SIGNAL CPtr.CodePassInconsistency;
        P5.C0[(IF t=JumpE THEN zJEQn ELSE zJNEn)+jDist-2];
        END;
       2 =>
        BEGIN
        IF jDist ~IN [-128..128) THEN SIGNAL CPtr.CodePassInconsistency;
        P5.C1[(IF t = JumpE THEN zJEQB ELSE zJNEB), jDist];
        cb[CPtr.codeptr].pad ← pad;
        END;
       ENDCASE =>
        BEGIN
        P5.C0[(IF t = JumpE THEN zJNE4 ELSE zJEQ4)+pad];
        P5.C1W[zJW, jDist]; cb[CPtr.codeptr].pad ← pad;
        END;
     JumpC => NULL;
     ENDCASE =>
      SELECT l FROM
       2 =>
        BEGIN
        IF jDist ~IN [-128..128) THEN SIGNAL CPtr.CodePassInconsistency;
        P5.C1[RelJumpOps[t], jDist];
        cb[CPtr.codeptr].pad ← pad;
        END;
       ENDCASE =>
        BEGIN
        P5.C1[RelJumpOps[CJump[t]], 5]; cb[CPtr.codeptr].pad ← pad;
        P5.C1W[zJW, jDist]; cb[CPtr.codeptr].pad ← IF dStar THEN 0 ELSE 1;
        END;
    cb[c].completed ← TRUE;
    cb[c].pad ← 0; -- so it doesn't have to be ignored in ComputeJumpDistance
    cb[c].jsize ← 0; -- so it doesn't have to be ignored in ComputeJumpDistance
    RETURN
    END;
  

  END...