-- CrossJump.mesa, 
-- last modified by Sweet, 4-Dec-81 14:20:33
-- last modified by Satterthwaite, December 16, 1982 9:11 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [codeptr],
  CodeDefs: TYPE USING [
    Base, CCIndex, CCNull, CJItem, codeType, EINull, EnableIndex,
    JumpCCIndex, JumpCCNull, LabelCCIndex, LabelCCNull],
  OpTableDefs: TYPE USING [InstLength],
  P5F: TYPE USING [DidSomething, StartIndex, UCjump, UnthreadJump],
  P5U: TYPE USING [CreateLabel, DeleteCell, OutJump, ParamCount];

CrossJump: PROGRAM
    IMPORTS CPtr: Code, OpTableDefs, P5U, P5F 
    EXPORTS CodeDefs, P5F =
  BEGIN
  OPEN CodeDefs;

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

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


  CJcheck: PUBLIC BOOL ← FALSE;
  CJNull: CJItem = [CCNull, fallIn[LabelCCNull]];

  FunnyJump: SIGNAL [c: CCIndex] = CODE;
  
  CPass5: PUBLIC PROC =
    BEGIN
    CPass5a[];
    CPass5b[];
    END;
    
  CPass5a: PROC =
    BEGIN -- set enable index in jumps and labels
    stack: ARRAY [0..15) OF EnableIndex;
    sptr: CARDINAL ← 0;
    curEi: EnableIndex ← EINull;
    c: CCIndex;
    Push: PROC [new: EnableIndex] =
      BEGIN
      stack[sptr] ← curEi;
      curEi ← new;
      sptr ← sptr + 1;
      END;
    Pop: PROC =
      BEGIN
      sptr ← sptr - 1;
      curEi ← stack[sptr];
      END;
    FOR c ← cb[P5F.StartIndex].flink, cb[c].flink WHILE c # CCNull DO
      WITH cc: cb[c] SELECT FROM
	label => cc.ei ← curEi;
	jump => cc.ei ← curEi;
	other => WITH cc SELECT FROM
	  markCatch => IF start THEN Push[index] ELSE Pop[];
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;

  CPass5b:  PROC =
    BEGIN  --  pass 5: cross jumping
    i1, i2, next2: CJItem;
    nextc, c: CCIndex;
    CJed:  BOOL;
    FOR c ← cb[P5F.StartIndex].flink, nextc WHILE c # CCNull DO
      WITH cc: cb[c] SELECT FROM
	label =>
	  BEGIN
	  lc: LabelCCIndex = LOOPHOLE[c];
	  i1 ← FirstItem[lc];
	  CJed ← FALSE;
	  UNTIL i1.inst = CCNull DO
	    i2 ← NextItem[i1];
	    UNTIL i2.inst = CCNull DO
	      next2 ← NextItem[i2];
	      IF i1.inst # i2.inst AND EqualItems[i1, i2] THEN
		BEGIN
		CrossJumpIt[i1, i2];
		P5F.DidSomething ← CJed ← TRUE;
		END;
	      i2 ← next2;
	      ENDLOOP;
	    IF CJed THEN EXIT;
	    i1 ← NextItem[i1];
	    ENDLOOP;
	  nextc ← cc.flink;
	  END;
	 ENDCASE => nextc ← cc.flink;
      ENDLOOP;
    END;
    
  EqualItems: PROC [i1, i2: CJItem] RETURNS [BOOL] =
    BEGIN -- can't cross jump into different enable scopes
    ei1, ei2: EnableIndex;
    IF ~EqualInst[i1.inst, i2.inst] THEN RETURN[FALSE];
    WITH i1 SELECT FROM
      fallIn => ei1 ← cb[lc].ei;
      jumpIn => ei1 ← cb[jc].ei;
      ENDCASE;
    WITH i2 SELECT FROM
      fallIn => ei2 ← cb[lc].ei;
      jumpIn => ei2 ← cb[jc].ei;
      ENDCASE;
    RETURN [ei1 = ei2];
    END;

  Executable: PROC [c: CCIndex] RETURNS [BOOL] =
    BEGIN
    RETURN [WITH cb[c] SELECT FROM
      code => TRUE,
      jump => ~(P5F.UCjump[c] OR FunnyUCjump[c]),
      ENDCASE => FALSE]
    END;

  FirstItem: PROC[lc:  LabelCCIndex] RETURNS [item: CJItem] =
    BEGIN
    j: JumpCCIndex ← cb[lc].jumplist;
    pred: CCIndex;
    IF j = JumpCCNull THEN RETURN [CJNull];
    IF (pred←PrevInst[lc]) = CCNull THEN RETURN [CJNull];
    item ← [pred, fallIn[lc]];
    IF Executable[pred] THEN RETURN [item];
    RETURN [NextItem[item]]
    END;

  NextItem: PROC [item: CJItem] RETURNS [CJItem] =
    BEGIN
    j: JumpCCIndex;
    inst: CCIndex;
    DO
      WITH ii: item SELECT FROM
        fallIn => j ← cb[ii.lc].jumplist;
        jumpIn => j ← cb[ii.jc].thread;
        ENDCASE;
      IF j = CCNull THEN RETURN [CJNull];
      IF FunnyUCjump[j] THEN {item ← [CCNull, jumpIn[j]]; LOOP};
      IF Executable[j] THEN {inst ← j; EXIT};
      inst ← PrevInst[j];
      IF inst # CCNull AND Executable[inst] THEN EXIT;
      item ← [CCNull, jumpIn[j]];
      ENDLOOP;
    RETURN [[inst, jumpIn[j]]]
    END;


  FunnyUCjump: PROC [j: CCIndex] RETURNS [BOOL] =
    BEGIN -- predicate testing if c is not interesting jump for crossjumping
    RETURN [WITH cb[j] SELECT FROM
      jump => (jtype = JumpC) OR (jtype = JumpA)
	   OR (jtype = JumpCA) OR (jtype = JumpRet),
      ENDCASE => FALSE]
    END;

  EqualInst: PROC [c, cc: CCIndex] RETURNS [BOOL] =
    BEGIN
    np: CARDINAL;
    WITH c1: cb[c] SELECT FROM
      code =>
	WITH c2 : cb[cc] SELECT FROM
	  code =>
	    BEGIN
	    IF c1.realinst # c2.realinst THEN RETURN [FALSE];
	    IF c1.inst # c2.inst THEN RETURN [FALSE];
	    np ← IF c1.realinst THEN OpTableDefs.InstLength[c1.inst]-1
	      ELSE P5U.ParamCount[LOOPHOLE[c]];
	    FOR i: CARDINAL IN [1..np] DO
	      IF c1.parameters[i] # c2.parameters[i] THEN RETURN [FALSE];
	      ENDLOOP;
	    RETURN [TRUE]
	    END;
	  ENDCASE;
      jump =>
	WITH c2 : cb[cc] SELECT FROM
	  jump =>
	    BEGIN
	    c1fwd, c2fwd: CCIndex;
	    IF c1.jtype # c2.jtype THEN RETURN [FALSE];
	    IF c1.destlabel # c2.destlabel THEN RETURN [FALSE];
	    c1fwd ← NextInst[c]; c2fwd ← NextInst[cc];
	    WITH c1f : cb[c1fwd] SELECT FROM
	      jump =>
		WITH c2f : cb[c2fwd] SELECT FROM
		  jump => IF c1f.destlabel = c2f.destlabel THEN RETURN [TRUE];
		  label => IF c1f.destlabel = c2fwd THEN RETURN [TRUE];
		  ENDCASE;
	      label =>
		WITH c2f : cb[c2fwd] SELECT FROM
		  jump => IF c2f.destlabel = c1fwd THEN RETURN [TRUE];
		  ENDCASE;
	      ENDCASE;
	    END;
	  ENDCASE;
      ENDCASE;
    RETURN [FALSE]
    END;

  CrossJumpIt: PROC [i1, i2: CJItem] =
    BEGIN
    l: LabelCCIndex;
    fb: CCIndex = PrevInst[i1.inst];
    WITH cb[fb] SELECT FROM
      label => l ← LOOPHOLE[fb];
      ENDCASE => {CPtr.codeptr ← fb; l ← P5U.CreateLabel[]};
    CPtr.codeptr ← cb[i2.inst].blink; -- don't skip over source here
    P5U.OutJump[Jump,l];
    WITH cb[i2.inst] SELECT FROM
      jump => P5F.UnthreadJump[LOOPHOLE[i2.inst]];
      ENDCASE;
    P5U.DeleteCell[i2.inst];
    WITH ii: i2 SELECT FROM
      jumpIn => IF ii.jc # i2.inst THEN
	{P5F.UnthreadJump[ii.jc]; P5U.DeleteCell[ii.jc]};
      ENDCASE;
    END;
    
  -- the following two procs are like NextInteresting and PrevInteresting
  --  in PeepholeU, only they won't skip over a MarkCatch

  NextInst: PROC [c: CCIndex] RETURNS [CCIndex] =
    BEGIN -- skip over startbody, endbody, and source other CCItems
    WHILE (c ← cb[c].flink) # CCNull DO
      WITH cc: cb[c] SELECT FROM
	other => WITH cc SELECT FROM
	  table, markCatch => EXIT;
	  ENDCASE;
	ENDCASE => EXIT;
      ENDLOOP;
    RETURN [c]
    END;

  PrevInst: PROC [c: CCIndex] RETURNS [CCIndex] =
    BEGIN -- skip over startbody, endbody, and source other CCItems
    WHILE (c ← cb[c].blink) # CCNull DO
      WITH cc: cb[c] SELECT FROM
	other => WITH cc SELECT FROM
	  table, markCatch => EXIT;
	  ENDCASE;
	ENDCASE => EXIT;
      ENDLOOP;
    RETURN [c]
    END;

  END.