// $RWC_Release: Omni-1.6 $
// $RWC_Copyright:
//  Omni Compiler Software Version 1.5-1.6
//  Copyright (C) 2002 PC Cluster Consortium
//  
//  This software is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License version
//  2.1 published by the Free Software Foundation.
//  
//  Omni Compiler Software Version 1.0-1.4
//  Copyright (C) 1999, 2000, 2001.
//   Tsukuba Research Center, Real World Computing Partnership, Japan.
//  
//  Please check the Copyright and License information in the files named
//  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
//  Software release kit.
//  
//  
//  $
package st;

import java.io.*;
import java.util.*;
import exc.object.*;
import exc.block.*;
import exc.flow.*;

public class InsertStPolling implements XobjectDefVisitor
{
  final int Lmax = 2000;
  final int Lmin = 1800;
  final int D = Lmax - Lmin;
  final int E = D / 10;
  
  BlockList funcBody;
  Xobject check;
  Hashtable table;

  ControlFlowGraphSt cfgs;
  ControlFlowGraph cfg;
  
  BlockIterator bi;
  BasicBlockIterator bbi;
  XobjectIterator xi;

  private boolean haveInnerLoop(BlockList bl) {
    if (bl == null) return false;
    for (Block b = bl.getHead(); b != null; b = b.getNext()) {
      if (b.Opcode() == Xcode.FOR_STATEMENT ||
	  b.Opcode() == Xcode.WHILE_STATEMENT ||
	  b.Opcode() == Xcode.DO_STATEMENT) {
	return true;
      } else if (b.Opcode() == Xcode.COMPOUND_STATEMENT ||
		 b.Opcode() == Xcode.SWITCH_STATEMENT) {
	boolean flag = haveInnerLoop(b.getBody());
	if (flag) return true;
      } else if (b.Opcode() == Xcode.IF_STATEMENT) {
	boolean flag_then = haveInnerLoop(b.getThenBody());
	boolean flag_else = haveInnerLoop(b.getElseBody());
	if (flag_then || flag_else) return true;
      }
    }
    return false;
  }

  private boolean InnermostTightLoop(Block b) {
    BlockList body = b.getBody();
    Block parent = b.getParentBlock();
    BlockList children = parent.getBody();
    if (haveInnerLoop(body)) return false;
    if (parent == null) return false;
    if (parent.Opcode() == Xcode.FOR_STATEMENT ||
	parent.Opcode() == Xcode.WHILE_STATEMENT) {
      int count = 0;
      int body_count = Count(body);
      for (Block child = children.getHead(); child != null; child = child.getNext()) {
	if (child != b) count += Count(child);
      }
      if (count <= body_count) return true;
    }
    return false;
  }

  private void checkLoopEnd(BasicBlock bb, int e) {
    for (int i = 0; i < bb.getCflowIn().size(); i++) {
      BasicBlock prev_bb = bb.getCflowIn(i);
      if (!prev_bb.isMarked()) {
	InstructionCount prev_ic 
	  = (InstructionCount)table.get(prev_bb);
	prev_ic.setLoopEnd();
	prev_ic.setCondEpsilon(e);
      }
    }
  }
  private void SetupBBlock(BasicBlock bb) {
    int delta = 0, epsilon = D, count = 0;
    int prev_delta, prev_epsilon, prev_count;
    BasicBlock prev_bb;
    InstructionCount ic, prev_ic;

    ic = (InstructionCount)table.get(bb);

    for (int i = 0; i < bb.getCflowIn().size(); i++) {
      prev_bb = bb.getCflowIn(i);
      if (!prev_bb.isMarked()) continue;
      prev_ic = (InstructionCount)table.get(prev_bb);
      prev_delta   = prev_ic.Delta();
      prev_epsilon = prev_ic.Epsilon();
      prev_count   = prev_ic.Count();
      if (delta < prev_delta) delta = prev_delta;
      if (epsilon > prev_epsilon) epsilon = prev_epsilon;
      if (count < prev_count) count = prev_count;
    }
    ic.setDelta(delta);
    ic.setEpsilon(epsilon);
    ic.setCount(count);
  }

  private void InsertPolling(BasicBlock bb, int e) {
    int delta, epsilon, count;
    int prev_epsilon;
    Block b = bb.getParent(), prev_b;
    BasicBlock prev_bb;
    InstructionCount ic, prev_ic;

    bb.setMark();

    SetupBBlock(bb);
    ic = (InstructionCount)table.get(bb);
    delta = ic.Delta();
    epsilon = ic.Epsilon();
    count = ic.Count();

    epsilon += e;
    if (epsilon > 0) {
      b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
      epsilon = 0;
    }
    if (delta + e > D) {
      b.insert(check); delta = 0; count++;
    }
    ic.setDelta(delta);
    ic.setEpsilon(epsilon);
    ic.setCount(count);    
  }
  private void InsertPolling(BasicBlock bb) {
    int delta, epsilon, count;
    int prev_delta, prev_epsilon, prev_count;
    BasicBlock prev_bb;
    Block prev_b;
    Vector c_in, c_out;
    InstructionCount ic, prev_ic;

    bb.setMark();

    Block b = bb.getParent();
    c_in  = bb.getCflowIn();
    c_out = bb.getCflowOut();

    if (c_out.size() == 0) {
      for (int i = 0; i < c_in.size(); i++) {
	prev_bb = bb.getCflowIn(i);
	prev_ic = (InstructionCount)table.get(prev_bb);
	prev_b = prev_bb.getParent();
	epsilon = prev_ic.Epsilon();
	delta   = prev_ic.Delta();
	count   = prev_ic.Count();
	if (prev_b.Opcode() == Xcode.RETURN_STATEMENT) {
	  if (epsilon > 0)
	    prev_b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	  if (delta > E && count > 0) {
	    prev_b.insert(check);
	  }
	} else {
	  if (epsilon > 0)
	    prev_b.add(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	  if (delta > E && count > 0) {
	    prev_b.add(check);
	  }
	}
      }
      return;
    }
    if (c_in.size() == 0) {
      ic = (InstructionCount)table.get(bb);
      delta = D - E;
      epsilon = 0;
      count = 0;
    } else {
      SetupBBlock(bb);
      ic = (InstructionCount)table.get(bb);
      delta = ic.Delta();
      epsilon = ic.Epsilon();
      count = ic.Count();
      for (int i = 0; i < c_in.size(); i++) {
	if (!bb.isMarked()) continue;
	prev_bb = bb.getCflowIn(i);
	prev_ic = (InstructionCount)table.get(prev_bb);
	prev_b = prev_bb.getParent();
	prev_epsilon = prev_ic.Epsilon();
	if (prev_epsilon > epsilon) {
	  int diff = prev_epsilon - epsilon;
	  if (prev_b.Opcode() == Xcode.BREAK_STATEMENT) {
	    prev_b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+diff+")"));
	  } else {
	    prev_b.add(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+diff+")"));
	  }
	}
      }
    }
    if (b.Opcode() != Xcode.STATEMENT_LABEL &&
	b.Opcode() != Xcode.CASE_LABEL &&
	b.Opcode() != Xcode.DEFAULT_LABEL &&
	b.Opcode() != Xcode.CONTINUE_STATEMENT &
	b.Opcode() != Xcode.BREAK_STATEMENT) {
      for (Statement s = bb.getHead(); s != null; s = s.getNext()) {
	int d = Count(s.getExpr());
	int e = CountEpsilon(s.getExpr());
	if (delta + d > D) {
	  if (epsilon > 0)
	    s.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	  s.insert(check); delta = 0; epsilon = 0; count++;
	} else if (d != e) {
	  if (epsilon > 0) {
	    s.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	    epsilon = 0;
	  }
	}
	delta += d; epsilon += e;
      }
      if (bb.getExpr() != null) {
	int d = Count(bb.getExpr());
	int e = CountEpsilon(bb.getExpr());
	if (delta + d > D) {
	  if (epsilon > 0)
	    b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	  b.insert(check); delta = 0; epsilon = 0; count++;
	} else if (d != e) {
	  if (epsilon > 0) {
	    b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+epsilon+")"));
	    epsilon = 0;
	  }
	}
	delta += d; epsilon += e;
      }
    }
    if (ic.isLoopEnd()) {
      int diff = epsilon + ic.CondEpsilon();
      if (b.Opcode() == Xcode.CONTINUE_STATEMENT) {
	if (diff > 0) {
	  b.insert(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+diff+")"));
	}
      } else {
	if (diff >0) {
	  b.add(Xcons.Symbol(Xcode.VAR, "ST_INCREMENT("+diff+")"));	
	}
      }
    }
    ic.setDelta(delta);
    ic.setEpsilon(epsilon);
    ic.setCount(count);
  }
  private void InsertPolling(Block b) {
    int diff, delta;
    switch(b.Opcode()) {
    case Xcode.IF_STATEMENT:
      InsertPolling(b.getCondBBlock());
      InsertPolling(b.getThenBody());
      InsertPolling(b.getElseBody());
      break;
    case Xcode.FOR_STATEMENT:
      InsertPolling(b.getInitBBlock(),
		    CountEpsilon(b.getCondBBlock()) +
		    CountEpsilon(b.getInitBBlock()));
      InsertPolling(b.getCondBBlock(), 0);
      delta = ((InstructionCount)table.get(b.getCondBBlock())).Delta();
      ((InstructionCount)table.get(b.getCondBBlock())).setDelta(0);
      checkLoopEnd(b.getIterBBlock(),
		   CountEpsilon(b.getCondBBlock()) +
		   CountEpsilon(b.getIterBBlock()));
      InsertPolling(b.getIterBBlock());
      if (InnermostTightLoop(b)) {
	b.getBody().add(check);
      } else {
	b.getBody().insert(check);
      }
      InsertPolling(b.getBody());
      ((InstructionCount)table.get(b.getCondBBlock())).setDelta(delta);
      break;
    case Xcode.WHILE_STATEMENT:
      checkLoopEnd(b.getCondBBlock(),
		   CountEpsilon(b.getCondBBlock()));
      InsertPolling(b.getCondBBlock(), 
		    CountEpsilon(b.getCondBBlock()));
      delta = ((InstructionCount)table.get(b.getCondBBlock())).Delta();
      ((InstructionCount)table.get(b.getCondBBlock())).setDelta(0);
      if (InnermostTightLoop(b)) {
	b.getBody().add(check);
      } else {
	b.getBody().insert(check);
      }
      InsertPolling(b.getBody());
      ((InstructionCount)table.get(b.getCondBBlock())).setDelta(delta);
      break;
    case Xcode.DO_STATEMENT:
      checkLoopEnd(b.getCondBBlock(),
		   CountEpsilon(b.getCondBBlock()));
      b.getBody().insert(check);
      InsertPolling(b.getBody());
      InsertPolling(b.getCondBBlock(), 
		    CountEpsilon(b.getCondBBlock()));
      break;
    case Xcode.SWITCH_STATEMENT:
      InsertPolling(b.getCondBBlock());
      InsertPolling(b.getBody());
      break;
    case Xcode.COMPOUND_STATEMENT:
      InsertPolling(b.getBody());
      break;
    case Xcode.BREAK_STATEMENT:
    case Xcode.CONTINUE_STATEMENT:
    case Xcode.GOTO_STATEMENT:
    case Xcode.STATEMENT_LABEL:
    case Xcode.CASE_LABEL: 
    case Xcode.DEFAULT_LABEL: 
    case Xcode.LIST:  /* simple block */
    case Xcode.RETURN_STATEMENT:
      InsertPolling(b.getBasicBlock());
      break;
    default:
      System.out.println("Error at InsertPolling(Block)");
    }
  }
  private void InsertPolling(BlockList bl) {
    for (Block b = bl.getHead(); b != null; b = b.getNext())
      InsertPolling(b);
  }

  private int CountEpsilon(Xobject x) {
    int total = 0;    
    if (x == null) return 0;
    if (x.isConstant()) return 0;
    xi = new bottomupXobjectIterator(x);
    for (xi.init(); !xi.end(); xi.next()) {
      Xobject xx = xi.getXobject();
      if (xx == null) continue;
      if (xx.Opcode() == Xcode.FUNCTION_CALL) {
	total = 0;
      } else {
	total += 1;
      }
    }
    return total;    
  }
  private int CountEpsilon(BasicBlock bb) {
    int total = 0;
    if (bb == null) return 0;
    for (Statement s = bb.getHead(); s != null; s = s.getNext()) {
      total += CountEpsilon(s.getExpr());
    }
    total += CountEpsilon(bb.getExpr());
    return total;
  }
    
  private int Count(Xobject x) {
    int total = 0;    
    if (x == null) return 0;
    if (x.isConstant()) return 0;
    xi = new bottomupXobjectIterator(x);
    for (xi.init(); !xi.end(); xi.next()) {
      Xobject xx = xi.getXobject();
      if (xx == null) continue;
      if (xx.Opcode() == Xcode.FUNCTION_CALL) {
	total = E;
      } else {
	total += 1;
      }
    }
    return total;
  }
  private int Count(BasicBlock bb) {
    int total = 0;
    if (bb == null) return 0;
    for (Statement s = bb.getHead(); s != null; s = s.getNext()) {
      total += Count(s.getExpr());
    }
    total += Count(bb.getExpr());
    return total;
  }
  private int Count(Block b) {
    int total = 0;
    int init_delta, cond_delta, iter_delta;
    int then_delta, else_delta, body_delta;
    if (b == null) return 0;
    switch(b.Opcode()) {
    case Xcode.IF_STATEMENT:
      cond_delta = Count(b.getCondBBlock());
      then_delta = Count(b.getThenBody());
      else_delta = Count(b.getElseBody());
      Block tb = b.getElseBody().getTail();
      if (then_delta >= else_delta ||
	  tb.Opcode() == Xcode.BREAK_STATEMENT ||
	  tb.Opcode() == Xcode.CONTINUE_STATEMENT ||
	  tb.Opcode() == Xcode.RETURN_STATEMENT) {
	total = cond_delta + then_delta;
      } else {
	total = cond_delta + else_delta;
      }	
      break;
    case Xcode.FOR_STATEMENT:
      init_delta = Count(b.getInitBBlock());
      cond_delta = Count(b.getCondBBlock());
      iter_delta = Count(b.getIterBBlock());
      body_delta = Count(b.getBody());
      total = init_delta + cond_delta;
      break;
    case Xcode.WHILE_STATEMENT:
    case Xcode.DO_STATEMENT:
      cond_delta = Count(b.getCondBBlock());
      body_delta = Count(b.getBody());
      total = cond_delta;
      break;
    case Xcode.SWITCH_STATEMENT:
      cond_delta = Count(b.getCondBBlock());
      body_delta = Count(b.getBody());
      total = cond_delta + body_delta;
      break;
    case Xcode.COMPOUND_STATEMENT:
      total = Count(b.getBody());
      break;
    case Xcode.LIST:  /* simple block */
    case Xcode.RETURN_STATEMENT:
      total = Count(b.getBasicBlock());
      break;
    case Xcode.BREAK_STATEMENT:
    case Xcode.CONTINUE_STATEMENT:
    case Xcode.GOTO_STATEMENT:
    case Xcode.STATEMENT_LABEL:
    case Xcode.CASE_LABEL: 
    case Xcode.DEFAULT_LABEL: 
      break;
    default:
      System.out.println("Default error"+b.Opcode());
      System.exit(1);
    }
    return total; 
  }
  private int Count(BlockList bl) {
    int total = 0;
    if (bl == null) return 0;
    Block pb = bl.getParent();
    if (pb.Opcode() == Xcode.SWITCH_STATEMENT) {
      int new_total = 0;
      for (Block b = bl.getHead(); b != null; b = b.getNext()) {
	new_total += Count(b);
	if (b.Opcode() == Xcode.BREAK_STATEMENT) {
	  if (total < new_total) total = new_total;
	  new_total = 0;
	} else if (b.Opcode() == Xcode.RETURN_STATEMENT ||
		   b.Opcode() == Xcode.GOTO_STATEMENT) {
	  new_total = 0;
	}
      }
      if (total < new_total) total = new_total;
    } else {
      for (Block b = bl.getHead(); b != null; b = b.getNext()) {
	total += Count(b);
      }
    }
    return total;
  }

  private void InsertTmp(BlockList bl) {
    Ident temp 
      = new Ident("__tmp",
		  StorageClass.REGISTER,
		  BasicType.intType,
		  Xcons.Symbol(Xcode.LVAR_ADDR,
			       Xtype.Pointer(BasicType.intType),
			       "__tmp"));
    funcBody.addIdent(temp);
    Xobject in = Xcons.Symbol(Xcode.VAR, "ST_IN()");
    Xobject out = Xcons.Symbol(Xcode.VAR, "ST_OUT()");

    funcBody.insert(in);

    bbi = new BasicBlockIterator(funcBody);
    for (bbi.init(); !bbi.end(); bbi.next()) {
      BasicBlock bb = bbi.getBasicBlock();
      Block b = bb.getParent();
      if (b.Opcode() == Xcode.LIST) {
	for (Statement s = bb.getHead(); s != null; s = s.getNext()) {
	  Xobject x = s.getExpr();
	  if (x == null) continue;
	  xi = new bottomupXobjectIterator(x);
	  for (xi.init(); !xi.end(); xi.next()) {
	    Xobject xx = xi.getXobject();
	    if (xx == null) continue;
	    if (xx.Opcode() == Xcode.FUNCTION_CALL) {
	      Statement ss = s.getPrev();
	      if (ss == null) {
		s.insert(out);
	      } else if (ss.getExpr() == in) {
		s.getPrev().remove();
	      } else {
		s.insert(out);
	      }
	      s.add(in);
	      break;
	    }
	  }
	}
      }
    }

    BasicBlock bb = cfg.exitBasicBlock();
    for (int i = 0; i < bb.getCflowIn().size(); i++) {
      Block b = bb.getCflowIn(i).getParent();
      if (b.Opcode() == Xcode.RETURN_STATEMENT) {
	b.insert(out);
      } else if (b.Opcode() == Xcode.LIST) {
	Statement s = bb.getCflowIn(i).getTail();
	if (s != null && s.getExpr() == in) {
	  s.remove();
	} else if (s != null &&
		   s.getExpr() == check &&
		   s.getPrev() != null &&
		   s.getPrev().getExpr() == in) {
	  s.getPrev().remove();
	} else {
	  b.add(out);
	}
      } else {
	b.add(out);
      }
    }
  }

  private void MakeTable() {
    table = new Hashtable();
    bbi = new BasicBlockIterator(funcBody);
    for (bbi.init(); !bbi.end(); bbi.next()) {
      BasicBlock bb = bbi.getBasicBlock();
      table.put(bb, new InstructionCount());
    }
  }

  public void doDef(XobjectDef d){
    if (d.getFuncBody() == null) return;

    FuncDefBlock fdblock = new FuncDefBlock(d);
    Block fblock = fdblock.getBlock();
    funcBody = fblock.getBody().getHead().getBody();
    check = Xcons.Symbol(Xcode.VAR, "ST_CHECK("+Lmin+")");

    cfgs = new ControlFlowGraphSt(funcBody);
    cfg = cfgs.get();

    MakeTable();
    InsertPolling(funcBody);
    InsertTmp(funcBody);

    fdblock.Finalize();
  }
}
