// $Id: optBodyEnv.java,v 1.3 2000/09/01 10:07:38 msato Exp $
// $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 exc.flow;

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

//
// Body for Optimizer
//   with temporary variable management
//   with control flow graph
//
public class optBodyEnv {
  protected BlockList Body;
  XobjectFile env;
  protected ControlFlowGraph cfg;
  public boolean rewriteAsgOpLeftAddr = false;

  // for temp var
  int temp_counter = 0;
  protected Vector vars;	// vector of id
  protected Vector defs;  	// vector of assignement statement
  protected Vector use_sets;  	// vector of set of use-statement

  public optBodyEnv(BlockList body){
    this.Body = body;
    init();
  }

  protected void init(){
    vars = new Vector();
    defs = new Vector();
    use_sets = new Vector();
  }

  public BlockList getBody() { return Body; }

  public XobjectFile getFile() { 
    if(env == null){
      for(Block b = Body.getParent(); b != null; b = b.getParentBlock()){
	if(b.Opcode() == Xcode.FUNCTION_DEFINITION){
	  env = ((FunctionBlock)b).getFile();
	  break;
	}
      }
      if(env == null) fatal("getFile: env is not found");
    }
    return env;
  }

  /* 
   * for CFG
   */
  public void makeCFG() {  cfg = new ControlFlowGraph(Body); }
  public ControlFlowGraph getCFG() { return cfg; }
  public void buildTopSortOrder() { cfg.buildTopSortOrder(); }

  /* public interface */
  public void exposeAsgExpr() { 
    for(Block b = Body.getHead(); b != null; b = b.getNext())
      exposeConditionalAsgExpr(b);

    // finally, expose assignment.
    for(Block b = Body.getHead(); b != null; b = b.getNext())
      exposeAsgExpr(b);
  }

  /* Expose conditional assignment expression, which is a conditional 
   * expressions such as &&, ||, ? includes assignments.
   * (1) If an expression in FOR, WHILE, DO is a conditional assignment,
   *     then rewrite loop statement as follows:
   *    for(e1;e2;e3) body -> { e1; while(1){ if(!e2) break; body; e3; }}
   *    while(e) body -> while(1){ if(!e1) break; body; }
   *    do body while(e) -> do { body; if(!e1) break; } while(1);
   * (2) If a condition is a conditional expression, rewrite:
   *    if(e) body -> t = e; if(t) body
   * (3) If a conditional expression is found in the statement, expose it.
   *     t = (e1 && e2) -> if(e1){ if(e2) t=1; else t=0; } else t=0;
   *     t = (e1 || e2) -> if(e1) t = 1; else if(e2) t=1; else t=0;
   *     t = (c ? e1 : e2) -> if(c) t = e1; else t = e2;
   */

  protected void exposeConditionalAsgExpr(Block block){
    Block b;
    BasicBlock bb;
    Xobject v;

    // check assignment in condition part
    BlockIterator i = new bottomupBlockIterator(block);
    for(i.init(); !i.end(); i.next()){
      b = i.getBlock();
      switch(b.Opcode()){
      case Xcode.FOR_STATEMENT:
	if(haveConditionalAsgExpr(b.getInitBBlock()) ||
	   haveConditionalAsgExpr(b.getCondBBlock()) ||
	   haveConditionalAsgExpr(b.getIterBBlock())){
	  /* convert while loop */
	  BlockList body = Bcons.emptyBody();
	  body.add(b.getInitBBlock());
	  b.getCondBBlock().setExpr(Xcons.unaryOp(Xcode.LOG_NOT_EXPR,
						  b.getCondBBlock().getExpr()));
	  b.getBody().insert(Bcons.IF(b.getCondBBlock(),
				      new BlockList(Bcons.BREAK()),null));
	  b.getBody().add(b.getIterBBlock());
	  body.add(Bcons.WHILE(BasicBlock.Cond(Xcons.IntConstant(1)),
			       b.getBody()));
	  i.setBlock(Bcons.COMPOUND(body));
	}
	break;
      case Xcode.WHILE_STATEMENT:
	if(haveConditionalAsgExpr(b.getCondBBlock())){
	  b.getCondBBlock().setExpr(Xcons.unaryOp(Xcode.LOG_NOT_EXPR,
						  b.getCondBBlock().getExpr()));
	  b.getBody().insert(Bcons.IF(b.getCondBBlock(),
				      new BlockList(Bcons.BREAK()),null));
	  i.setBlock(Bcons.WHILE(BasicBlock.Cond(Xcons.IntConstant(1)),
				 b.getBody()));
	}
	break;
      case Xcode.DO_STATEMENT:
	if(haveConditionalAsgExpr(b.getCondBBlock())){
	  b.getCondBBlock().setExpr(Xcons.unaryOp(Xcode.LOG_NOT_EXPR,
						  b.getCondBBlock().getExpr()));
	  b.getBody().add(Bcons.IF(b.getCondBBlock(),
				   new BlockList(Bcons.BREAK()),null));
	  i.setBlock(Bcons.DO(b.getBody(),
			      BasicBlock.Cond(Xcons.IntConstant(1))));
	}
	break;
      }
    }

    BasicBlockIterator ii = new BasicBlockIterator(block);
    for(ii.init(); !ii.end(); ii.next()){
      bb = ii.getBasicBlock();
      if(bb == null) continue;
      if(haveConditionalAsgExpr(bb.getExpr())){
	v = newTempVar(bb.getExpr().Type()).Ref();
	bb.add(Xcons.Set(v.copy(),bb.getExpr()));
	bb.setExpr(v);
      }
      for(StatementIterator is = bb.statements(); is.hasMoreStatement(); ){
	Statement s = is.nextStatement();
	Xobject e = s.getExpr();
	if(haveConditionalAsgExpr(e)){
	  BlockList body = Bcons.emptyBody();
	  e = expandConditionalExpr(e,body);
	  s.setExpr(e);
	  s.insertBlock(Bcons.COMPOUND(body)); // split and insert!!
	}
      }
    }
  }

  protected Xobject expandConditionalExpr(Xobject e,BlockList body){
    Block b0,b1,b2;
    BlockList body0;
    Xobject v;

    XobjectIterator i = new topdownXobjectIterator(e);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getXobject();
      if(x == null) continue;
      switch(x.Opcode()){
      case Xcode.LOG_AND_EXPR:
	/* e1 && e2 -> if(e1){ if(e2) v = 1; else v = 0; } else v = 0; */
	v = newTempVar(Xtype.intType).Ref(); // make temporary for return value
	body0 = Bcons.emptyBody();
	b0 = Bcons.IF(expandConditionalExpr(x.right(),body0),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(1))),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(0))));
	body0.add(b0);
	b1 = Bcons.IF(expandConditionalExpr(x.left(),body),
		      Bcons.COMPOUND(body0),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(0))));
	body.add(b1);
	i.setXobject(v);
	break;
      case Xcode.LOG_OR_EXPR:
	/* e1 || e2 -> if(e1) v = 1; else { if(e2) v = 1; else v = 0; } */
	v = newTempVar(Xtype.intType).Ref(); // make temporary for return value
	body0 = Bcons.emptyBody();
	b0 = Bcons.IF(expandConditionalExpr(x.right(),body0),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(1))),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(0))));
	body0.add(b0);
	b1 = Bcons.IF(expandConditionalExpr(x.left(),body),
		      Bcons.Statement(Xcons.Set(v.copy(),
						Xcons.IntConstant(1))),
		      Bcons.COMPOUND(body0));
	body.add(b1);
	i.setXobject(v);
	break;
      case Xcode.CONDITIONAL_EXPR:
	v = newTempVar(x.Type()).Ref(); // make temporary for return value
	/* left */
	body0 = Bcons.emptyBody();
	b0 = Bcons.Statement(Xcons.Set(v.copy(),
				       expandConditionalExpr(x.right().left(),
							     body0)));
	body0.add(b0);
	b0 = Bcons.COMPOUND(body0);
	/* right */
	body0 = Bcons.emptyBody();
	b1 = Bcons.Statement(Xcons.Set(v.copy(),
				       expandConditionalExpr(x.right().right(),
							     body0)));
	body0.add(b1);
	b1 = Bcons.COMPOUND(body0);
	/* cond */
	b2 = Bcons.IF(expandConditionalExpr(x.left(),body),b0,b1);
	body.add(b2);
	i.setXobject(v);
	break;
      }
    }
    return i.topXobject();
  }

  protected boolean haveConditionalAsgExpr(BasicBlock bb){
    for(StatementIterator ii = bb.statements(); ii.hasMoreStatement(); ){
	Statement s = ii.nextStatement();
	if(haveConditionalAsgExpr(s.getExpr())) return true;
    }
    if(haveConditionalAsgExpr(bb.getExpr())) return true;
    return false;
  }

  protected boolean haveConditionalAsgExpr(Xobject x){
    if(x == null) return false;
    XobjectIterator i = new topdownXobjectIterator(x);
    for(i.init(); !i.end(); i.next()){
      Xobject xx = i.getXobject();
      if(xx == null) continue;
      switch(xx.Opcode()){
      case Xcode.LOG_AND_EXPR:
      case Xcode.LOG_OR_EXPR:
	if(haveAsgExpr(xx.left()) || haveAsgExpr(xx.right())) return true;
      case Xcode.CONDITIONAL_EXPR:
	if(haveAsgExpr(xx.right().left()) ||
	   haveAsgExpr(xx.right().right())) return true;
      }
    }
    return false;
  }

  protected boolean haveAsgExpr(Xobject x){
    if(x == null) return false;
    XobjectIterator i = new topdownXobjectIterator(x);
    for(i.init(); !i.end(); i.next()){
      Xobject xx = i.getXobject();
      if(xx != null && xx.isAsgOp()) return true;
    }
    return false;
  }

  /* Expose an assignement expression as a statement.
   * (1) If a statement contains an assignment expression inside,
   *     rewrite with temporary:
   *   ... (x asgOp y) ... -> t = (x asgOp y); ... t ...
   * (2) If a condtion contains an assignment expose it:
   *   if(x asgOp y) body -> t = (x asgOp y); if(t) body;
   * (3) Rewrite assignment operators:
   *    (x asgOp y) -> x = x Op y
   *    t = (x asgOp y) -> t = x Op y; x = t; 
   *    t = (p++) -> t = p; p = t + 1; 
   * (4) If the lvalue is reference to memory, expose pointer to 
   *   avoid address expression to be evaluated twice.
   *    (*(p) asgOp y) -> t = p; *(t) = *(t) Op y;
   */
   
  protected void exposeAsgExpr(Block block){
    Xobject x,xx,v,t;
    StatementIterator is;
    Statement s;
    XobjectIterator ii;

    BasicBlockIterator i = new BasicBlockIterator(block);
    for(i.init(); !i.end(); i.next()){
      BasicBlock bb = i.getBasicBlock();
      if(bb == null) continue;
      for(is = bb.statements(); is.hasMoreStatement(); ){
	s = is.nextStatement();
	ii = new bottomupXobjectIterator(s.getExpr());
	for(ii.init();!ii.end(); ii.next()){
	  x = ii.getXobject();
	  if(x != null && x.isAsgOp() && ii.getParent() != null){
	    v = newTempVar(x.Type()).Ref();
	    s.insert(Xcons.Set(v,x));
	    ii.setXobject(v);
	  }
	}
      }

      /* for conditonal part, (2) */
      ii = new bottomupXobjectIterator(bb.getExpr());
      for(ii.init();!ii.end(); ii.next()){
	x = ii.getXobject();
	if(x != null && x.isAsgOp()){
	  v = newTempVar(x.Type()).Ref();
	  bb.add(Xcons.Set(v,x));
	  ii.setXobject(v);
	}
      }
      bb.setExpr(ii.topXobject());

      /* re-write asg expression */
      for(is = bb.statements(); is.hasMoreStatement(); ){
	s = is.nextStatement();
	x = s.getExpr();
	if(x.isAsgOp()){
	  if(x.isSet() && x.left().isTempVar()){
	    if(!x.right().isAsgOp()) continue;
	    t = x.left();
	    x = x.right();
	  } else t = null;
	  // at this point, x is AsgOp 

	  boolean post_flag;
	  if(x.Opcode() == Xcode.POST_INCR_EXPR){
	    post_flag = true;
	    x = Xcons.asgOp(Xcode.ASG_PLUS_EXPR,x.operand(),
			    Xcons.IntConstant(1));
	  }  else if(x.Opcode() == Xcode.POST_DECR_EXPR){
	    post_flag = true;
	    x = Xcons.asgOp(Xcode.ASG_MINUS_EXPR,x.operand(),
			    Xcons.IntConstant(1));
	  } else  post_flag = false;

	  if(!x.isSet()){ /* op= */
	    if(x.left().Opcode() == Xcode.POINTER_REF &&
	       !(x.left().operand().isVariable() ||
		 x.left().operand().isConstant())){
	      if(!rewriteAsgOpLeftAddr) continue;
	      v = newTempVar(x.left().operand().Type()).Ref();
	      s.insert(Xcons.Set(v,x.left().operand()));
	      xx = Xcons.PointerRef(v);
	      x = Xcons.Set(xx.copy(),
			    Xcons.binaryOp(Xcons.unAsgOpcode(x.Opcode()),
					   xx,x.right()));
	    } else { // must be variable?
	      x = Xcons.Set(x.left().copy(),
			    Xcons.binaryOp(Xcons.unAsgOpcode(x.Opcode()),
					   x.left(),x.right()));
	    } 
	  }

	  // at this point, all SetOp
	  if(t != null){
	    if(post_flag){
	      s.insert(Xcons.Set(t.copy(),x.right().left()));
	      x.right().setLeft(t);
	    } else {
	      s.insert(Xcons.Set(t.copy(),x.right()));
	      x.setRight(t);
	    } 
	  }
	  // finally set Expr
	  s.setExpr(x);
	}
      }
    }
  }

  /*
   * temporary variable management 
   */
  public Ident newTempVar(Xtype t){
    Ident id = Ident.TempVar(temp_counter++,t);
    vars.addElement(id);
    defs.addElement(null);
    use_sets.addElement(null);
    return id;
  }

  public void Finalize(){
    boolean flags[] = new boolean[temp_counter];
    BasicBlockExprIterator ii = new BasicBlockExprIterator(Body);
    for(ii.init(); !ii.end(); ii.next()){
      Xobject e = ii.getExpr();
      if(e == null) continue;
      XobjectIterator i = new bottomupXobjectIterator(e);
      for(i.init(); !i.end(); i.next()){
	Xobject x = i.getXobject();
	if(x != null && x.Opcode() == Xcode.REG) flags[x.getInt()] = true;
      }
    }

    for(int t = 0; t < temp_counter; t++){
      if(flags[t]){
	Ident id = (Ident)(vars.elementAt(t));
	Body.addIdent(id);
      }
    }
  }

  public void makeDefUse(){
    defs = new Vector(temp_counter);
    use_sets = new Vector(temp_counter);
    defs.setSize(temp_counter);
    use_sets.setSize(temp_counter);

    BasicBlockExprIterator ii = new BasicBlockExprIterator(Body);
    for(ii.init(); !ii.end(); ii.next()){
      Xobject e = ii.getExpr();
      Statement current_statement = ii.getStatement();
      BasicBlock current_bblock = ii.getBasicBlock();

      if(e == null) continue;
      if(e.isSet() && e.left().isVariable()){
	if(e.left().Opcode() == Xcode.REG){
	  defs.setElementAt(current_statement,e.left().getInt());
	}
	e = e.right();
      }

      XobjectIterator i = new bottomupXobjectIterator(e);
      for(i.init(); !i.end(); i.next()){
	Xobject x = i.getXobject();
	if(x != null && x.Opcode() == Xcode.REG){
	  Vector use = (Vector)(use_sets.elementAt(x.getInt()));
	  if(use == null){
	    use = new Vector();
	    use_sets.setElementAt(use,x.getInt());
	  }
	  if(current_statement != null) 
	    use.addElement(current_statement);
	  else use.addElement(current_bblock);
	}
      }
    }
  }
  
  public Enumeration getTempDefs(){ return defs.elements(); }

  public Statement getDef(Xobject v){
    if(v.Opcode() != Xcode.REG) fatal("getDef: not REG");
    return (Statement)(defs.elementAt(v.getInt()));
  }

  public Vector getUseSet(Xobject v){
    if(v.Opcode() != Xcode.REG) fatal("getDef: not REG");
    Vector use = (Vector)(use_sets.elementAt(v.getInt()));
    if(use == null){
      use = new Vector();
      use_sets.setElementAt(use,v.getInt());
    }
    return use;
  }

  void removeTemp(Xobject v){
    if(v.Opcode() != Xcode.REG) fatal("getDef: not REG");
    vars.setElementAt(null,v.getInt());
  }

  static void fatal(String msg){
    System.err.println("Fatal in optBodyEnv:"+msg);
    Thread.dumpStack();
    System.exit(1);
  }
}

