-- Final.mesa
-- last modified by Sweet, 15-Sep-82 16:42:40
-- edited by Satterthwaite, December 16, 1982 9:25 am 

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [
    bodyRecurLabel, CodePassInconsistency, codeptr, tailJumpOK],
  CodeDefs: TYPE USING [
    Base, CCIndex, CCInfoType, CCNull, codeType, JumpCCIndex, JumpCCNull,
    JumpType, LabelCCIndex, LabelCCNull, RelativePC, TableCodeBytes],
  ComData: TYPE USING [bodyIndex, switches],
  FOpCodes: TYPE USING [qADD, qDIS, qLFC, qLI, qLL, qRET, qSELFC, qSFC],
  OpTableDefs: TYPE USING [InstLength],
  P5: TYPE USING [C0, C1, PeepHole],
  P5F: TYPE USING [BindJump, CodeJump, CPass5, FillInPCEstimates],
  P5U: TYPE USING [DeleteCell, OutJump],
  PeepholeDefs: TYPE USING [
    CJump, NextInteresting, PrevInteresting, RemoveThisPop, SetRealInst],
  PrincOps: TYPE USING [framelink],
  Symbols: TYPE USING [Base, CBTIndex, BTIndex, bodyType];

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

  cb: CodeDefs.Base;		-- code base (local copy)
  bb: Symbols.Base;

  FinalNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[codeType];
    bb ← base[Symbols.bodyType];
    END;

  DidSomething: PUBLIC BOOL;
  StartIndex, EndIndex: PUBLIC CCIndex;
  SeenSwitch: BOOL;
  JumpCellCount: CARDINAL;
  
  ccInfo: PUBLIC CCInfoType ← generating;

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

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

    PeepholeDefs.SetRealInst[FALSE];
    TailJump[crossJump AND CPtr.tailJumpOK];
    CPtr.bodyRecurLabel ← LabelCCNull;	-- avoid dangling ref if deleted
    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[];
      ENDLOOP;
    -- pass 9: resolve (remaining) jump instructions
    IF jumpsafter # 0 THEN CPass9[];
    -- pass 10: code jumps
    ccInfo ← coding;
    IF totalJumps # 0 THEN CPass10[];
    -- pass 11: Remove extra source chunks
    CPass11[];
    END;


  TailJump: PROC [jumpOK: BOOL] =
    BEGIN  -- remove simple tail recursion
    enableLevel: CARDINAL ← 0;
    next: CCIndex;
    FOR c: CCIndex ← cb[StartIndex].flink, next WHILE c # CCNull DO
      next ← cb[c].flink;
      WITH cc: cb[c] SELECT FROM
	code =>
	  IF ~cc.realinst AND cc.inst = FOpCodes.qSELFC THEN {
	    CPtr.codeptr ← cb[c].blink;
	    IF jumpOK AND enableLevel = 0 AND UCreturn[next] THEN
	      BEGIN
	      P5U.OutJump[Jump, CPtr.bodyRecurLabel];
	      P5U.DeleteCell[c]
	      END
	    ELSE
	      BEGIN
	      bti: Symbols.CBTIndex = MPtr.bodyIndex;
	      WITH body: bb[bti] SELECT FROM
	        Outer => {P5.C1[FOpCodes.qLFC, body.entryIndex]; P5U.DeleteCell[c]};
		Inner => {
		  P5.C1[FOpCodes.qLL, PrincOps.framelink];
		  P5.C1[FOpCodes.qLI, body.frameOffset];
		  P5.C0[FOpCodes.qADD];
		  P5.C0[FOpCodes.qSFC];
		  P5U.DeleteCell[c]};
		ENDCASE => ERROR;
	      END};
	other => WITH oc: cc SELECT FROM
	  markbody => {
	    index: Symbols.BTIndex = oc.index;
	    WITH bb[index] SELECT FROM 
	      Callable => EXIT;
	      ENDCASE};
	  markCatch => 
	    IF oc.start THEN enableLevel ← enableLevel + 1
	    ELSE enableLevel← enableLevel - 1;
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;

  UCreturn: PROC [start: CCIndex] RETURNS [BOOL] =
    BEGIN  -- find (unconditional) path to RET
    next: CCIndex;
    FOR c: CCIndex ← start, next WHILE c # CCNull DO
      WITH cc: cb[c] SELECT FROM
	code => RETURN [~cc.realinst AND cc.inst = FOpCodes.qRET];
	label => next ← cc.flink;
	jump =>
	  BEGIN
	  IF ~UCjump[c] THEN EXIT;
	  next ← cc.destlabel;
	  END;
	other => WITH cc SELECT FROM
	  table, markCatch => EXIT;
	  ENDCASE => next ← cc.flink;
	ENDCASE => EXIT;
      ENDLOOP;
    RETURN [FALSE]
    END;

  CPass0: PROC =
    BEGIN  -- pass 0: distinguish forward and backward jumps
    JumpCellCount ← 0;
    FOR c: CCIndex ← 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;
    END;


  CPass1: PROC =
    BEGIN   -- pass 1: eliminate multiple labels, unreferenced labels,
	    --         and jumps to .+1
    nextc, c: CCIndex;
    FOR c ← cb[StartIndex].flink, nextc WHILE c # CCNull DO
      nextc ← NextInteresting[c];
      WITH cc:cb[c] SELECT FROM
	jump =>
	  IF DotPlusOneJump[LOOPHOLE[c], nextc] AND 
	    (UCjump[c] OR cc.jtype IN [JumpE..UJumpLE]) THEN
	    DeleteJump[LOOPHOLE[c]];
	label =>
	  IF cc.jumplist = JumpCCNull AND ~cc.catch THEN
	    BEGIN
	    unreferencedlabel: LabelCCIndex ← LOOPHOLE[c, LabelCCIndex];
	    DidSomething ← TRUE; P5U.DeleteCell[unreferencedlabel];
	    END
	  ELSE 
	    BEGIN
	    duplabel: LabelCCIndex ← LOOPHOLE[c, LabelCCIndex];
	    IF nextc = CCNull THEN RETURN;
	    WITH cb[nextc] SELECT FROM
	      label =>
		BEGIN
		DeleteLabel[duplabel, LOOPHOLE[nextc, LabelCCIndex]];
		DidSomething ← TRUE;
		END;
	      ENDCASE;
	    END;
	ENDCASE;
      ENDLOOP;
   END;

  DotPlusOneJump: PROC [jc: JumpCCIndex, next: CCIndex] RETURNS [BOOL] = INLINE
    BEGIN
    RETURN [IF next = CCNull THEN FALSE -- RRA fix
      ELSE WITH cb[next] SELECT FROM
        label => next = cb[jc].destlabel,
        ENDCASE => FALSE]
    END;

  DeleteJump: PROC [jc: JumpCCIndex] =
    BEGIN
    IF cb[jc].jtype IN [JumpE..UJumpLE] THEN
      THROUGH [0..2) DO
	CPtr.codeptr ← cb[jc].blink;
	P5.C0[FOpCodes.qDIS];
	[] ← PeepholeDefs.RemoveThisPop[CPtr.codeptr];
	ENDLOOP;
    UnthreadJump[jc];
    DidSomething ← TRUE; P5U.DeleteCell[jc];
    END;


  CPass2: PROC =
    BEGIN   -- pass 2: eliminate jump to jumps
    FOR c: CCIndex ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH jj: cb[c] SELECT FROM
	jump =>
	  IF jj.destlabel # LabelCCNull THEN
	    BEGIN
	    jtojexists: BOOL ← FALSE;
	    jccount: CARDINAL ← 0;
	    jc: JumpCCIndex ← LOOPHOLE[c, JumpCCIndex];
	    jclabel: LabelCCIndex;
	    cc: CCIndex;
	    DO
	      jclabel ← cb[jc].destlabel;
	      IF (cc ← NextInteresting[jclabel]) = 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;
	      UnthreadJump[LOOPHOLE[c, JumpCCIndex]];
	      jj.thread ← cb[jclabel].jumplist;
	      cb[jclabel].jumplist ← LOOPHOLE[c, JumpCCIndex];
	      jj.destlabel ← jclabel;
	      IF jj.jtype = JumpLIO THEN cb[jclabel].offsetLoaded ← TRUE;
	      END;
	    END;
	ENDCASE
      ENDLOOP;
    END;


  CPass3: PROC =
    BEGIN   -- pass 3: eliminate unreachable code
    FOR c: CCIndex← 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: CCIndex ← flink;
	    oldc: CCIndex;
	    DO
	      IF (oldc ← cc) = CCNull THEN RETURN;
	      cc ← cb[cc].flink;
	      WITH cb[oldc] SELECT FROM
		label => IF jumplist # JumpCCNull OR catch THEN EXIT;
		jump => UnthreadJump[LOOPHOLE[oldc, JumpCCIndex]];
		other => IF otag # table THEN LOOP; --body start/stop, source
		ENDCASE;
	      P5U.DeleteCell[oldc];
	      DidSomething ← TRUE;
	      ENDLOOP;
	    END;
	ENDCASE;
      ENDLOOP;
    END;

  CPass4: PROC =
    BEGIN   -- pass 4: replace cj-j seq. with ccj
    c, nextc: CCIndex;
    FOR c ← cb[StartIndex].flink, nextc WHILE c # CCNull DO
      WITH oldc:cb[c] SELECT FROM
	jump =>
	  BEGIN
	  nextc ← IF MPtr.switches['j] THEN NextInteresting[c]
	    ELSE cb[c].flink; -- don't ignore source chunks here
	  IF oldc.jtype IN [JumpE..ZJumpN] THEN
	    BEGIN
	    IF nextc = CCNull THEN RETURN;
	    WITH nc:cb[nextc] SELECT FROM
	      jump =>
		IF oldc.destlabel = nc.destlabel AND
	          (UCjump[c] OR oldc.jtype IN [JumpE..UJumpLE]) THEN
	          DeleteJump[LOOPHOLE[c]]
		ELSE IF UCjump[nextc] AND
		  (PrevInteresting[oldc.destlabel] = nextc) THEN
		  BEGIN
		  newLbl: LabelCCIndex = nc.destlabel;
		  nxt: CCIndex;
		  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;
		  nxt ← nc.flink;
		  P5U.DeleteCell[nextc];
		  nextc ← nxt;
		  END;
	      ENDCASE;
	    END;
	  END;
	ENDCASE => nextc ← cb[c].flink;
      ENDLOOP;
    END;

  CPass7: PROC RETURNS [unboundJumps: CARDINAL] =
    BEGIN -- pass 7: set length, count jumps
    c, next: CCIndex;
    -- look for body starting with a loop
    IF ~MPtr.switches['j] THEN
      BEGIN
      c ← NextInteresting[cb[StartIndex].flink];
      IF c # CCNull THEN  -- RRA fix
        WITH cb[c] SELECT FROM
	 label => IF jumplist # JumpCCNull THEN
	  BEGIN
	  CPtr.codeptr ← cb[c].blink;
	  P5U.OutJump[Jump, LOOPHOLE[c]];
	  cb[LOOPHOLE[CPtr.codeptr, JumpCCIndex]].forward ← TRUE;
	  END;
	ENDCASE;
      END;
    unboundJumps ← 0;
    FOR c ← cb[StartIndex].flink, next WHILE c # CCNull DO
      next ← cb[c].flink;
      WITH cb[c] SELECT FROM
	code => IF isize = 0 THEN isize ← OpTableDefs.InstLength[inst];
	jump =>
	  IF jtype = JumpRet THEN P5U.DeleteCell[c]
	  ELSE unboundJumps ← unboundJumps+1;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;

  CPass8: PROC RETURNS [unboundJumps: CARDINAL ← 0] =
    BEGIN -- pass 8: resolve easy jumps
    P5F.FillInPCEstimates[];
    FOR c: CCIndex ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump => IF ~fixedup THEN
	  BEGIN
	  min, max: CARDINAL;
	  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 ~P5F.BindJump[min, max, LOOPHOLE[c, JumpCCIndex]]
	    THEN unboundJumps ← unboundJumps+1;
	  END;
	ENDCASE;
      ENDLOOP;
    RETURN
    END;


  CPass9: PROC =
    BEGIN   -- pass 9: resolve (remaining) jump instructions
    P5F.FillInPCEstimates[];
    FOR c: CCIndex ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  IF ~fixedup THEN
	    BEGIN
	    nbytes: CARDINAL = IF forward
		THEN cb[destlabel].maxPC - maxPC
		ELSE maxPC - cb[destlabel].maxPC;
	    [] ← P5F.BindJump[nbytes, nbytes, LOOPHOLE[c, JumpCCIndex]];
	    END;
	ENDCASE;
      ENDLOOP;
    END;

  CPass10: PROC =
    BEGIN   -- pass 10: code jumps
    FillInPC[];
    FOR c: CCIndex ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cb[c] SELECT FROM
	jump =>
	  BEGIN
	  IF ~fixedup THEN SIGNAL CPtr.CodePassInconsistency 
	  ELSE P5F.CodeJump[(IF forward THEN cb[destlabel].pc - pc
	    ELSE pc - cb[destlabel].pc), LOOPHOLE[c, JumpCCIndex]];
	  END;
	ENDCASE;
      ENDLOOP;
    END;


  DeleteLabel: PROC [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;
    IF cb[oldc].offsetLoaded THEN cb[c].offsetLoaded ← TRUE;
    P5U.DeleteCell[oldc];
    END;


  UnthreadJump: PUBLIC PROC [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;
    END;


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


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


  FillInPC: PROC =
    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)
    rpc: RelativePC ← 0;
    nbytes: CARDINAL;
    FOR k: CCIndex ← StartIndex, cb[k].flink UNTIL k = CCNull DO
      nbytes ← (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 cc.pc ← rpc;
	label => cc.pc ← rpc;
	ENDCASE;
      rpc ← rpc+nbytes;
      ENDLOOP;
    END;

  CPass11: PROC =
    BEGIN  -- pass 11: Remove extra source chunks
    prev: CCIndex ← CCNull;
    FOR c: CCIndex ← cb[StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cc: cb[c] SELECT FROM
	code => prev ← CCNull;
	other => WITH cc SELECT FROM
	  table => prev ← CCNull;
	  source => {
	    IF prev # CCNull THEN P5U.DeleteCell[prev];
	    prev ← c};
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;


  END.