-- file: PeepholeQ.mesa, edited by Sweet on March 19, 1979  10:53 AM

DIRECTORY
  Code: FROM "code" USING [CodeNotImplemented, CodePassInconsistency, codeptr, dStar],
  P5U: FROM "p5u" USING [DeleteCell],
  CodeDefs: FROM "codedefs" USING [CCIndex, CCNull, CodeCCIndex, JumpCCIndex, JumpType, NULLfileindex],
  ComData: FROM "comdata" USING [switches],
  FOpCodes: FROM "fopcodes" USING [qADD, qAND, qBCAST, qBCASTL, qBLT, qBLTC, qBLTCL, qBLTL, qDADD, qDBL, qDEC, qDESCB, qDESCBS, qDST, qDUP, qDWDC, qEFC, qEXCH, qFDESCBS, qGADRB, qINC, qIWDC, qKFCB, qLADRB, qLFC, qLG, qLGD, qLI, qLINKB, qLINT, qLL, qLLD, qLLK, qLP, qLST, qLSTF, qME, qMEL, qMRE, qMREL, qMUL, qMXD, qMXDL, qMXW, qMXWL, qNEG, qNILCK, qNILCKL, qNOOP, qNOTIFY, qNOTIFYL, qOR, qPL, qPOP, qPORTI, qPORTO, qPS, qPSD, qPSF, qPUSH, qR, qRD, qRDL, qREQUEUE, qREQUEUEL, qRET, qRF, qRFL, qRFS, qRFSL, qRIG, qRIGL, qRIL, qRILF, qRILL, qRL, qRSTR, qRSTRL, qRXG, qRXGL, qRXL, qRXLL, qSDIV, qSFC, qSG, qSGD, qSHIFT, qSL, qSLD, qSUB, qW, qWD, qWDL, qWF, qWFL, qWIG, qWIGL, qWIL, qWILL, qWL, qWS, qWSD, qWSF, qWSTR, qWSTRL, qWXG, qWXGL, qWXL, qWXLL, qXOR],
  InlineDefs: FROM "inlinedefs" USING [BITAND, BITSHIFT],
  OpCodeParams: FROM "opcodeparams" USING [BYTE, GlobalHB, HB, LocalBase, LocalHB, LocalPutSlots],
  P5: FROM "p5" USING [PopEffect, PushEffect, C0, C1, C2, LoadConstant],
  PeepholeDefs: FROM "peepholedefs" USING [PeepZ, Delete2, Delete3, HalfByteLocal, InitJParametersBC, InitParametersABC, InitParametersBC, InitParametersC, JumpPeepState, LoadInst, MC0, PeepholeUNotify, PeepholeZNotify, PeepState, SetRealInst, SetSourceIndex, SlidePeepState1, SlidePeepState2, UnpackFD],
  SDDefs: FROM "sddefs" USING [sBLTE, sBLTEC, sBLTECL, sBLTEL, sBYTBLTE, sBYTBLTEC, sBYTBLTECL, sBYTBLTEL, sSignedDiv],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType];

PeepholeQ: PROGRAM
  IMPORTS CPtr: Code, MPtr: ComData, InlineDefs, P5U, P5, PeepholeDefs
  EXPORTS CodeDefs, P5, PeepholeDefs =
  BEGIN OPEN PeepholeDefs, OpCodeParams, CodeDefs;

  -- imported definitions

  BYTE: TYPE = OpCodeParams.BYTE;
  qNOOP: BYTE = FOpCodes.qNOOP;

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

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

  DummyProc: PROCEDURE =
    BEGIN -- every 2 minutes of compile time helps
    s: PeepState;
    js: JumpPeepState;
    IF FALSE THEN [] ← s;
    IF FALSE THEN [] ← js;
    END;

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

  
  start: CodeCCIndex;


  PeepHole: PUBLIC PROCEDURE [s: CCIndex] =
    BEGIN
    start ← LOOPHOLE[s];
    SetRealInst[FALSE];
    IF ~(CPtr.dStar OR MPtr.switches['l]) THEN RemoveLongs[];
    Peep0[];
    Peep1[];
    Peep2[];
    Peep3[];
    Peep4[];
    Peep5[];
    Peep6[];
    Peep7[];
    SetRealInst[TRUE];
    PeepZ[start];
    SetSourceIndex[NULLfileindex];
    END;

  RemoveLongs: PROCEDURE =
    BEGIN -- remove long instructions
    OPEN FOpCodes;
    next: CodeCCIndex;
    state: PeepState;
    newinst: BYTE;

    BEGIN OPEN state;
     next ← start;
     UNTIL (c ← next) = CCNull DO
      next ← LOOPHOLE[cb[c].flink];
      newinst ← qNOOP;
      WITH cb[LOOPHOLE[c,CCIndex]] SELECT FROM
	code =>
	  BEGIN
	  InitParametersC[@state];
	  SELECT cInst FROM
	    qRL => BEGIN newinst ← qR; GOTO pop0 END;
	    qRDL => BEGIN newinst ← qRD; GOTO pop0 END;
	    qRFL => BEGIN newinst ← qRF; GOTO pop0 END;
	    qWL => BEGIN newinst ← qW; GOTO pop0 END;
	    qWFL => BEGIN newinst ← qWF; GOTO pop0 END;
	    qRFSL => BEGIN newinst ← qRFS; GOTO pop1 END;
	    qRSTRL => BEGIN newinst ← qRSTR; GOTO pop1 END;
	    qWDL => BEGIN newinst ← qWD; GOTO pop0 END;
	    qWSTRL => BEGIN newinst ← qWSTR; GOTO pop1 END;
	    qRXLL => newinst ← qRXL;
	    qWXLL => newinst ← qWXL;
	    qRXGL => newinst ← qRXG;
	    qWXGL => newinst ← qWXG;
	    qRILL => newinst ← qRIL;
	    qWILL => newinst ← qWIL;
	    qRIGL => newinst ← qRIG;
	    qWIGL => newinst ← qWIG;
	    qBLTCL => BEGIN newinst ← qBLTC; GOTO pop0 END;
	    qBLTL => BEGIN newinst ← qBLT; InsertPOP[0]; GOTO pop2 END;
	    qMEL => BEGIN newinst ← qME; GOTO pop0 END;
	    qMREL => BEGIN newinst ← qMRE; InsertPOP[0]; GOTO pop1 END;
	    qMXWL => BEGIN newinst ← qMXW; InsertPOP[1]; GOTO pop2 END;
	    qMXDL => BEGIN newinst ← qMXD; GOTO pop0 END;
	    qNOTIFYL => BEGIN newinst ← qNOTIFY; GOTO pop0 END;
	    qBCASTL => BEGIN newinst ← qBCAST; GOTO pop0 END;
	    qREQUEUEL => BEGIN newinst ← qREQUEUE; InsertPOP[1]; GOTO pop2 END;
	    qKFCB =>
	      BEGIN OPEN SDDefs;
	      newp1: WORD;
	      SELECT cP[1] FROM
		sBLTEL => BEGIN newp1 ← sBLTE; InsertPOP[0] END;
		sBYTBLTEL => BEGIN newp1 ← sBYTBLTE; InsertPOP[0] END;
		sBLTECL => newp1 ← sBLTEC;
		sBYTBLTECL => newp1 ← sBYTBLTEC;
		ENDCASE => GO TO notspecial;
	      cb[c].parameters[1] ← newp1;
	      GO TO pop2;
	      EXITS notspecial => NULL;
	      END;
	    ENDCASE;
	  EXITS
	    pop0 => InsertPOP[0];
	    pop1 => InsertPOP[1];
	    pop2 => InsertPOP[2];
	  END;
	ENDCASE; -- of WITH
      IF newinst # qNOOP THEN cb[c].inst ← newinst;
      ENDLOOP;
    END; -- of OPEN
    RETURN
    END;

  BackupCP: PROCEDURE [n: INTEGER] RETURNS [INTEGER] =
    BEGIN OPEN FOpCodes; -- back up codeptr n stack positions
    cc: CCIndex ← CPtr.codeptr;
    neteffect: INTEGER;
    WHILE (cc ← cb[cc].blink) # CCNull AND n # 0 DO
      WITH cb[cc] SELECT FROM
	code =>
	  BEGIN
	  IF realinst THEN EXIT;
	  SELECT inst FROM
	    qEFC, qLFC, qSFC, qKFCB, qRET, qPORTO, qPORTI, qLST, qLSTF, qDST =>
	      EXIT;
	    ENDCASE;
	  neteffect ← P5.PushEffect[inst] - P5.PopEffect[inst];
	  IF n-neteffect < 0 THEN EXIT;
	  n ← n - neteffect;
	  END;
	ENDCASE => EXIT;
      ENDLOOP;
    CPtr.codeptr ← cc;
    RETURN[n]
    END;

  InsertPOP: PROCEDURE [n: INTEGER] =
    BEGIN OPEN FOpCodes; -- insert (or simulate) a POP of the word at tos-n
    savecodeptr: CCIndex ← CPtr.codeptr;
    n ← BackupCP[n];
    SELECT n FROM
      0 => P5.C0[qPOP];
      1 => BEGIN P5.C0[qEXCH]; P5.C0[qPOP] END;
      2 => BEGIN P5.C0[qPOP]; P5.C0[qEXCH]; P5.C0[qPUSH]; P5.C0[qEXCH]; P5.C0[qPOP] END;
      3 => BEGIN P5.C0[qPOP]; P5.C0[qPOP]; P5.C0[qEXCH]; P5.C0[qPUSH]; P5.C0[qEXCH]; P5.C0[qPUSH]; P5.C0[qEXCH]; P5.C0[qPOP] END;
      ENDCASE => SIGNAL CPtr.CodePassInconsistency;
    CPtr.codeptr ← savecodeptr;
    RETURN
    END;


  Peep0: PROCEDURE =
    BEGIN -- undo doubles
    OPEN FOpCodes;
    next: CodeCCIndex;
    state: PeepState;

    next ← start;
    BEGIN OPEN state;
     UNTIL (c ← next) = CCNull DO
      next ← LOOPHOLE[cb[c].flink];
      WITH cb[LOOPHOLE[c,CCIndex]] SELECT FROM
	code =>
	  BEGIN
	  InitParametersC[@state];
	  SELECT cInst FROM
	    qLGD =>
	      BEGIN inst ← qLG; P5.C1[qLG, cP[1]+1]; END;
	    qLLD =>
	      BEGIN inst ← qLL; P5.C1[qLL, cP[1]+1]; END;
	    ENDCASE;
	  END;
	ENDCASE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  Peep1: PROCEDURE =
    BEGIN -- remove POPs by modifying previous instruction
    OPEN FOpCodes;
    next, ci: CCIndex;
    didsomething: BOOLEAN ← TRUE;

    WHILE didsomething DO
      next ← start;
      didsomething ← FALSE;
      UNTIL (ci ← next) = CCNull DO
        next ← cb[ci].flink;
        WITH cb[ci] SELECT FROM
	  code =>
	    IF inst = qPOP AND ~realinst THEN
              didsomething ← didsomething OR RemoveThisPop[ci];
	  ENDCASE;
        ENDLOOP;
      ENDLOOP;
    RETURN
    END;

  RemoveThisPop: PUBLIC PROCEDURE [ci: CCIndex] RETURNS [didThisTime: BOOLEAN] =
    BEGIN -- remove POP by modifying previous instruction, if possible
    OPEN FOpCodes;
    state: PeepState;
    didThisTime ← FALSE;
    WITH cb[ci] SELECT FROM
      code =>
        BEGIN OPEN state;
        c ← LOOPHOLE[ci];
        InitParametersABC[@state];
        SELECT cInst FROM
          qPOP =>
            IF Popable[bInst] THEN
              BEGIN
              P5U.DeleteCell[b];
              P5U.DeleteCell[c];
              didThisTime ← TRUE;
              END
            ELSE
              SELECT bInst FROM
                qR, qRF, qRXL, qNEG, qDESCBS, qINC, qDEC =>
                  BEGIN
                  P5U.DeleteCell[b];
		  [] ← RemoveThisPop[c]; -- the blink may be popable now
		      -- above is unnecessary if called from Peep1
		      -- but useful if called from jump elimination
                  didThisTime ← TRUE;
                  END;
                qDADD =>
                  IF Popable[aInst] THEN
                    BEGIN
                    Delete2[a,b];
                    InsertPOP[1];
                    MC0[qADD, bMin];
                    P5U.DeleteCell[c];
                    didThisTime ← TRUE;
                    END;
                qRD =>
                  BEGIN
                  cb[b].inst ← qR;
                  P5U.DeleteCell[c];
                  didThisTime ← TRUE;
                  END;
                qIWDC, qDWDC =>
                  BEGIN
                  CommuteCells[b,c];
                  didThisTime ← TRUE;
                  END;
                qNILCKL =>
                  BEGIN
                  cb[b].inst ← qNILCK;
                  CommuteCells[b,c];
		    -- no recursive call since jump elimination needs code
		    -- to go to nothing.
                  didThisTime ← TRUE;
                  END;
                ENDCASE;
          ENDCASE;
        END;
      ENDCASE; -- of WITH
    END;

  Popable: PROCEDURE [inst: BYTE] RETURNS [BOOLEAN] =
    BEGIN OPEN FOpCodes;
    RETURN[inst#qNOOP AND
      (P5.PopEffect[inst]=0 AND P5.PushEffect[inst]=1 OR inst = qLP OR inst = qDUP)]
    END;

  Peep2: PROCEDURE =
    BEGIN -- expand families
    OPEN FOpCodes;
    next, ci: CCIndex;
    state: PeepState;
    canSlide: BOOLEAN ← FALSE;

    next ← start;
    BEGIN OPEN state;
     UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cb[ci] SELECT FROM
	code =>
	  BEGIN
	  IF canSlide THEN SlidePeepState2[@state, LOOPHOLE[ci]]
	  ELSE 
	    BEGIN
	    c ← LOOPHOLE[ci];
	    InitParametersABC[@state];
	    END;
	  canSlide ← FALSE;
	  SELECT cInst FROM
	    -- expand out-of-range families
	    qRIG =>
	      IF cP[1] ~IN GlobalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLG, cP[1]]; P5.C1[qR, cP[2]]; P5U.DeleteCell[c]; END;
	    qRIL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLL, cP[1]]; P5.C1[qR, cP[2]]; P5U.DeleteCell[c]; END;
	    qRXL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLL, cP[1]]; MC0[qADD, cMin]; P5.C1[qR, cP[2]]; P5U.DeleteCell[c]; END;
	    qWXL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLL, cP[1]]; P5.C0[qADD]; P5.C1[qW, cP[2]]; P5U.DeleteCell[c]; END;
	    qWIL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLL, cP[1]]; P5.C1[qW, cP[2]]; P5U.DeleteCell[c]; END;
	    qRXG =>
	      IF TRUE THEN
		BEGIN P5.C1[qLG, cP[1]]; MC0[qADD, cMin]; P5.C1[qR, cP[2]]; P5U.DeleteCell[c]; END;
	    qWXG =>
	      IF TRUE THEN
		BEGIN P5.C1[qLG, cP[1]]; P5.C0[qADD]; P5.C1[qW, cP[2]]; P5U.DeleteCell[c]; END;
	    qWIG =>
	      IF TRUE THEN
		BEGIN P5.C1[qLG, cP[1]]; P5.C1[qW, cP[2]]; P5U.DeleteCell[c]; END;
	    qRIGL =>
	      IF cP[1] ~IN GlobalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLGD, cP[1]]; P5.C1[qRL, cP[2]]; P5U.DeleteCell[c]; END;
	    qRILL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLLD, cP[1]]; P5.C1[qRL, cP[2]]; P5U.DeleteCell[c]; END;
	    qRXLL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.LoadConstant[0]; P5.C1[qLLD, cP[1]]; P5.C0[qDADD]; P5.C1[qRL, cP[2]]; P5U.DeleteCell[c]; END;
	    qWXLL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.LoadConstant[0]; P5.C1[qLLD, cP[1]]; P5.C0[qDADD]; P5.C1[qWL, cP[2]]; P5U.DeleteCell[c]; END;
	    qWILL =>
	      IF cP[1] ~IN LocalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLLD, cP[1]]; P5.C1[qWL, cP[2]]; P5U.DeleteCell[c]; END;
	    qRXGL =>
	      IF cP[1] ~IN GlobalHB OR cP[2] ~IN HB THEN
		BEGIN P5.LoadConstant[0]; P5.C1[qLGD, cP[1]]; P5.C0[qDADD]; P5.C1[qRL, cP[2]]; P5U.DeleteCell[c]; END;
	    qWXGL =>
	      IF cP[1] ~IN GlobalHB OR cP[2] ~IN HB THEN
		BEGIN P5.LoadConstant[0]; P5.C1[qLGD, cP[1]]; P5.C0[qDADD]; P5.C1[qWL, cP[2]]; P5U.DeleteCell[c]; END;
	    qWIGL =>
	      IF cP[1] ~IN GlobalHB OR cP[2] ~IN HB THEN
		BEGIN P5.C1[qLGD, cP[1]]; P5.C1[qWL, cP[2]]; P5U.DeleteCell[c]; END;
	    qRILF =>
	      IF TRUE THEN
		BEGIN P5.C1[qLL, cP[1]]; P5.C2[qRF, cP[2], cP[3]]; P5U.DeleteCell[c]; END;
	    qEFC, qLLK =>
	      IF cP[1] ~IN BYTE THEN
		SIGNAL CPtr.CodeNotImplemented;
	    qLINKB =>
	      IF cP[1] ~IN BYTE THEN
		BEGIN
		cb[c].parameters[1] ← 377B;
		P5.C1[qLL, LocalBase];
		P5.LoadConstant[cP[1]-377B];
		P5.C0[qSUB]; P5.C1[qSL, LocalBase];
		END;
	    qDESCBS, qDESCB, qFDESCBS =>
		BEGIN
	        IF cP[1]/2 ~IN BYTE OR cP[1] MOD 2 = 0 THEN
		  SIGNAL CPtr.CodeNotImplemented;
		parameters[1] ← cP[1]/2;
		IF cInst = qFDESCBS THEN 
		  BEGIN inst ← qDESCBS; P5.C0[qSFC]; END;
		END;
	    qSDIV =>
	      BEGIN
	      P5.C1[qKFCB, SDDefs.sSignedDiv];
	      P5U.DeleteCell[c];
	      END;
	    qDEC => IF cMin THEN
	        BEGIN P5.LoadConstant[0-1]; MC0[qADD, TRUE]; P5U.DeleteCell[c] END
	      ELSE BEGIN P5.LoadConstant[1]; P5.C0[qSUB]; P5U.DeleteCell[c] END;
	    qLINT =>
	      BEGIN
	      P5.C0[qDUP];
	      P5.LoadConstant[0-15];
	      P5.C0[qSHIFT];
	      P5.C0[qNEG];
	      P5U.DeleteCell[c];
	      END;
	    qGADRB, qLADRB =>
	      IF cP[1] ~IN BYTE THEN
		BEGIN
		parameters[1] ← LAST[BYTE];
		P5.LoadConstant[cP[1]-LAST[BYTE]]; MC0[qADD, cMin];
		END;
	    qWS, qPS, qWSF, qPSF, qWSD, qPSD =>
	      IF cP[1] ~IN BYTE THEN
	        SIGNAL CPtr.CodePassInconsistency;
	    -- discover family members from sequences
	    qR =>
	      IF cP[1] IN HB THEN
		SELECT bInst FROM
		  qADD =>
		    IF HalfByteLocal[a] THEN
		      BEGIN P5.C2[qRXL, aP[1], cP[1]]; Delete3[a,b,c]; END;
		  qLL =>
		    IF bP[1] IN LocalHB THEN
		      BEGIN P5.C2[qRIL, bP[1], cP[1]]; Delete2[b,c]; END;
		  qLG =>
		    IF bP[1] IN GlobalHB THEN
		      BEGIN P5.C2[qRIG, bP[1], cP[1]]; Delete2[b,c]; END;
		  ENDCASE;
	    qW =>
	      IF cP[1] IN HB THEN
		SELECT bInst FROM
		  qADD =>
		    IF HalfByteLocal[a] THEN
		      BEGIN P5.C2[qWXL, aP[1], cP[1]]; Delete3[a,b,c]; END;
		  qLL =>
		    IF bP[1] IN LocalHB THEN
		      BEGIN P5.C2[qWIL, bP[1], cP[1]]; Delete2[b,c]; END;
		  ENDCASE;
	    ENDCASE => canSlide ← TRUE;
	  END;
	ENDCASE => canSlide ← FALSE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  Peep3: PROCEDURE =
    BEGIN -- sprinkle DUPs
    OPEN FOpCodes;
    next, ci: CCIndex;
    state: PeepState;
    canSlide: BOOLEAN ← FALSE;

    next ← start;
    BEGIN OPEN state;
     UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cb[ci] SELECT FROM
	code =>
	  BEGIN
	  IF canSlide THEN SlidePeepState2[@state, LOOPHOLE[ci]]
	  ELSE 
	    BEGIN
	    c ← LOOPHOLE[ci];
	    InitParametersABC[@state];
	    END;
	  canSlide ← FALSE;
	  SELECT cInst FROM
	    -- replace load,load with load,DUP
	    qLL, qLG, qLI =>
	      IF bInst = cInst AND cP[1] = bP[1] THEN
		BEGIN P5.C0[qDUP]; P5U.DeleteCell[c] END;
	    qRIL, qRIG, qRILL, qRIGL =>
	      IF bInst = cInst AND cP[1] = bP[1] AND cP[2] = bP[2] THEN
		BEGIN P5.C0[qDUP]; P5U.DeleteCell[c] END;
	    ENDCASE => canSlide ← TRUE;
	  END;
	ENDCASE => canSlide ← FALSE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  Peep4: PROCEDURE =
    BEGIN -- PUTs and PUSHs, RF and WF to RSTR and WSTR
    OPEN FOpCodes;
    next, ci: CCIndex;
    state: PeepState;
    pos, size: [0..16);
    canSlide: BOOLEAN ← FALSE;

    next ← start;
    BEGIN OPEN state;
     UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cb[ci] SELECT FROM
	code =>
	  BEGIN
	  IF canSlide THEN SlidePeepState2[@state, LOOPHOLE[ci]]
	  ELSE 
	    BEGIN
	    c ← LOOPHOLE[ci];
	    InitParametersABC[@state];
	    END;
	  canSlide ← FALSE;
	  SELECT cInst FROM
	    qLL =>
	      IF bInst = qSL AND cP[1] = bP[1] THEN
		IF cP[1] IN LocalPutSlots THEN
		  BEGIN cb[b].inst ← qPL; P5U.DeleteCell[c]; END
		ELSE BEGIN P5.C0[qPUSH]; P5U.DeleteCell[c]; END
	      ELSE GO TO Slide;
	    qPUSH =>
	      IF bInst = qSL AND bP[1] IN LocalPutSlots THEN
		  BEGIN cb[b].inst ← qPL; P5U.DeleteCell[c]; END
	      ELSE GO TO Slide;
	    qLG =>
	      IF bInst = qSG AND cP[1] = bP[1] THEN
		BEGIN P5.C0[qPUSH]; P5U.DeleteCell[c]; END
	      ELSE GO TO Slide;
	    qRIL =>
	      IF bInst = qWIL AND cP[1] = bP[1] AND cP[2] = bP[2] THEN
		BEGIN P5.C0[qPUSH]; P5U.DeleteCell[c] END
	      ELSE GO TO Slide;
	    qRILL =>
	      IF bInst = qWILL AND cP[1] = bP[1] AND cP[2] = bP[2] THEN
		BEGIN P5.C0[qPUSH]; P5U.DeleteCell[c] END
	      ELSE GO TO Slide;
	    qRIGL =>
	      IF bInst = qWIGL AND cP[1] = bP[1] AND cP[2] = bP[2] THEN
		BEGIN P5.C0[qPUSH]; P5U.DeleteCell[c] END
	      ELSE GO TO Slide;
	    qRF, qWF, qRFL, qWFL =>
	      BEGIN
	      [pos, size] ← UnpackFD[LOOPHOLE[cP[2]]];
	      IF size = 8 AND cP[1] <= LAST[BYTE]/2 THEN
		SELECT pos FROM
		  0, 8 => 
		    BEGIN 
		    P5.LoadConstant[0];
		    P5.C1[(SELECT cInst FROM
		      qRF => qRSTR,
		      qWF => qWSTR,
		      qRFL => qRSTRL,
		      ENDCASE => qWSTRL), cP[1]*2+pos/8];
		    P5U.DeleteCell[c];
		    END;
		  ENDCASE => GO TO Slide
	      ELSE GO TO Slide; 
	      END;
	    ENDCASE => GO TO Slide;
	  EXITS
	    Slide => canSlide ← TRUE;
	  END;
	ENDCASE => canSlide ← FALSE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  NonWS: ARRAY [FOpCodes.qWS..FOpCodes.qWSD] OF BYTE =
			[FOpCodes.qW, FOpCodes.qWF, FOpCodes.qWD];

  Peep5: PROCEDURE =
    BEGIN -- put doubles back, eliminate EXCH preceding commutative operator
    OPEN FOpCodes;
    next, ci: CCIndex;
    state: PeepState;
    canSlide: BOOLEAN ← FALSE;

    next ← start;
    BEGIN OPEN state;
     UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cc:cb[ci] SELECT FROM
	code =>
	  BEGIN
	  IF canSlide THEN SlidePeepState2[@state, LOOPHOLE[ci]]
	  ELSE 
	    BEGIN
	    c ← LOOPHOLE[ci];
	    InitParametersABC[@state];
	    END;
	  canSlide ← FALSE;
	  SELECT cInst FROM
	    qLL =>
	      IF bInst = qLL AND cP[1] = bP[1]+1 THEN
		BEGIN cb[b].inst ← qLLD; P5U.DeleteCell[c]; END
	      ELSE GO TO Slide;
	    qSL =>
	      IF bInst = qSL AND cP[1] = bP[1]-1 THEN
		BEGIN cb[c].inst ← qSLD; P5U.DeleteCell[b]; END
	      ELSE GO TO Slide;
	    qLG =>
	      IF bInst = qLG AND cP[1] = bP[1]+1 THEN
		BEGIN cb[b].inst ← qLGD; P5U.DeleteCell[c]; END
	      ELSE GO TO Slide;
	    qSG =>
	      IF bInst = qSG AND cP[1] = bP[1]-1 THEN
		BEGIN cb[c].inst ← qSGD; P5U.DeleteCell[b]; END
	      ELSE GO TO Slide;
	    qADD, qMUL, qAND, qOR, qXOR =>
	      IF bInst = qEXCH THEN P5U.DeleteCell[b]
	      ELSE GO TO Slide;
	    qWS, qWSF, qWSD =>
	      IF bInst = qEXCH THEN 
		BEGIN P5U.DeleteCell[b]; cc.inst ← NonWS[cInst]; END
	      ELSE GO TO Slide;
	    qEXCH =>
	      IF bInst = qEXCH THEN Delete2[b,c]
	      ELSE IF LoadInst[b] AND LoadInst[a] THEN
		BEGIN
		P5U.DeleteCell[c];
		CommuteCells[a,b];
		cb[a].minimalStack ← bMin;
		cb[b].minimalStack ← aMin;
		END
	      ELSE GO TO Slide;
	    ENDCASE => GO TO Slide;
	  EXITS
	    Slide => canSlide ← TRUE;
	  END;
	jump =>
	  BEGIN
	  canSlide ← FALSE;
	  IF cc.jtype IN [JumpE..UJumpLE] THEN
	    WITH cb[cc.blink] SELECT FROM
	      code => IF ~realinst AND inst = qEXCH AND
			 ~PushFollows[LOOPHOLE[ci,JumpCCIndex]] THEN 
		BEGIN P5U.DeleteCell[cc.blink]; cc.jtype ← RJump[cc.jtype]; END;
	      ENDCASE;
	  END;
	ENDCASE => canSlide ← FALSE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  PushFollows: PROCEDURE [c: JumpCCIndex] RETURNS [BOOLEAN] =
    BEGIN -- c is conditional jump; TRUE if PUSH follows on either branch
    next: CCIndex;
    FOR next ← cb[c].flink, cb[next].flink WHILE next # CCNull DO
      WITH cb[next] SELECT FROM
        code => IF ~realinst AND inst = FOpCodes.qPUSH THEN RETURN[TRUE]
		ELSE EXIT;
	label => NULL;
        ENDCASE => EXIT;
      ENDLOOP;
    IF (next←cb[cb[c].destlabel].flink) # CCNull THEN
      WITH cb[next] SELECT FROM
        code => IF ~realinst AND inst = FOpCodes.qPUSH THEN RETURN[TRUE];
        ENDCASE;
    RETURN[FALSE]
    END;

  CommuteCells: PROCEDURE [a, b: CCIndex] =
    BEGIN
    prev, next: CCIndex;
    prev ← cb[a].blink; -- never Null
    next ← cb[b].flink;
    cb[prev].flink ← b;
    cb[b].blink ← prev;
    cb[b].flink ← a;
    cb[a].blink ← b;
    cb[a].flink ← next;
    IF next # CCNull THEN cb[next].blink ← a;
    RETURN
    END;

  Peep6: PROCEDURE =
    BEGIN -- store double/load double, INC and DEC, MUL to SHIFT etc
    OPEN FOpCodes;
    next, ci: CCIndex;
    canSlide: BOOLEAN ← FALSE;
    state: PeepState;
    negate, powerof2: BOOLEAN;
    log: CARDINAL;
    d2: PROCEDURE =
	BEGIN
	Delete2[state.b, state.c];
	IF negate THEN P5.C0[qNEG];
	RETURN
	END;

    next ← start;
    BEGIN OPEN state;
     UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cb[ci] SELECT FROM
	code =>
	  BEGIN
	  IF canSlide THEN SlidePeepState1[@state, LOOPHOLE[ci]]
	  ELSE 
	    BEGIN
	    c ← LOOPHOLE[ci];
	    InitParametersBC[@state];
	    END;
	  canSlide ← FALSE;
	  SELECT cInst FROM
	    qLLD =>
	      IF bInst = qSLD AND cP[1] = bP[1] THEN
		IF cP[1] IN LocalPutSlots THEN
		  BEGIN
		  P5.C1[qSL, cP[1]+1]; P5.C1[qPL, cP[1]]; P5.C0[qPUSH];
		  Delete2[b,c];
		  END
		ELSE BEGIN P5.C0[qPUSH]; P5.C0[qPUSH]; P5U.DeleteCell[c] END
	      ELSE GO TO Slide;
	    qLGD =>
	      IF bInst = qSGD AND cP[1] = bP[1] THEN
		BEGIN P5.C0[qPUSH]; P5.C0[qPUSH]; P5U.DeleteCell[c] END
	      ELSE GO TO Slide;
	    qADD, qSUB =>
	      IF bInst = qLI THEN
		BEGIN
		SELECT LOOPHOLE[bP[1], INTEGER] FROM
		  0 => Delete2[b,c];
		  1 => IF cInst = qADD THEN
		    BEGIN cb[c].inst ← qINC; P5U.DeleteCell[b]; END;
		  -1 => IF cInst = qSUB THEN
		    BEGIN cb[c].inst ← qINC; P5U.DeleteCell[b]; END;
		  ENDCASE => GO TO Slide;
		END 
	      ELSE IF bInst = qNEG THEN
		BEGIN
		cb[c].inst ← IF cInst = qADD THEN qSUB ELSE qADD;
		P5U.DeleteCell[b];
		END
	      ELSE GO TO Slide;
	    qSHIFT =>
	      IF bInst = qLI THEN
		SELECT bP[1] FROM
		  1 => BEGIN cb[c].inst ← qDBL; P5U.DeleteCell[b] END;
		  0 => Delete2[b,c];
		  ENDCASE => GO TO Slide
	      ELSE GO TO Slide;
	    qMUL =>
	      IF bInst = qLI THEN
		BEGIN
		negate ← FALSE;
		IF LOOPHOLE[bP[1], INTEGER] < 0 THEN
		  BEGIN negate ← TRUE; bP[1] ← -LOOPHOLE[bP[1],INTEGER]; END;
		SELECT bP[1] FROM
		  1 => d2[];
		  2 => BEGIN P5.C0[qDBL]; d2[]; END;
		  3 => BEGIN P5.C0[qDUP]; P5.C0[qDBL]; MC0[qADD, cMin]; d2[]; END;
		  4 => BEGIN P5.C0[qDBL]; P5.C0[qDBL]; d2[]; END;
		  5 => BEGIN P5.C0[qDUP]; P5.C0[qDBL]; P5.C0[qDBL]; MC0[qADD, cMin]; d2[]; END;
		  6 => BEGIN P5.C0[qDBL]; P5.C0[qDUP]; P5.C0[qDBL]; MC0[qADD, cMin]; d2[]; END;
		  ENDCASE =>
		    BEGIN
		    [powerof2, log] ← Log2[LOOPHOLE[bP[1]]];
		    IF powerof2 THEN
		      BEGIN P5.LoadConstant[log]; P5.C0[qSHIFT]; d2[]; END
		    ELSE GO TO Slide;
		    END;
		END;
	    ENDCASE => GO TO Slide;
	  EXITS
	    Slide => canSlide ← TRUE;
	  END;
	ENDCASE => canSlide ← FALSE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  Log2: PROCEDURE [i: INTEGER] RETURNS [BOOLEAN, CARDINAL] =
    BEGIN OPEN InlineDefs;
    shift: CARDINAL;

    IF i = 0 THEN RETURN [FALSE, 0];
    i ← ABS[i];
    IF BITAND[i, i-1] # 0 THEN RETURN [FALSE, 0];
    FOR shift IN [0..16) DO
      IF BITAND[i,1] = 1 THEN RETURN[TRUE, shift];
      i ← BITSHIFT[i, -1];
      ENDLOOP;
    ERROR; -- can't be reached
    END;


  Peep7: PROCEDURE =
    BEGIN -- find special jumps
    OPEN FOpCodes;
    next: JumpCCIndex;
    jstate: JumpPeepState;

    next ← LOOPHOLE[start];
    BEGIN OPEN jstate;
     UNTIL (c ← next) = CCNull DO
      next ← LOOPHOLE[cb[c].flink];
      WITH cb[LOOPHOLE[c,CCIndex]] SELECT FROM
	jump =>
	  BEGIN
	  InitJParametersBC[@jstate];
	  CPtr.codeptr ← c;
	  SELECT jtype FROM
	    JumpE =>
	      IF bInst = qLI THEN
		IF bP[1] = 0 THEN BEGIN jtype ← ZJumpE; P5U.DeleteCell[b] END;
	    JumpN =>
	      IF bInst = qLI THEN
		IF bP[1] = 0 THEN BEGIN jtype ← ZJumpN; P5U.DeleteCell[b] END;
	    ENDCASE;
	  END;
	ENDCASE; -- of WITH
      ENDLOOP;
    END; -- of OPEN state
    RETURN
    END;

  END...