// $Id: SSAtransform.java,v 1.8 2001/02/05 10:38:40 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 exc.util.*;

import java.util.*;

public class SSAtransform {
  public static boolean debugFlag;
  optBodyEnv optEnv;

  XobjectAList current_varlist;
  XobjectAList current_joinlist;
  static String propVarList = "SSAvarlist";
  static String propJoinList = "SSAjoin";

  XobjectAList getVarOut(BasicBlock bb){
    return (XobjectAList)bb.getProp(propVarList);
  }

  void setVarOut(BasicBlock bb,XobjectAList alist){
    bb.setProp(propVarList,alist);
  }

  XobjectAList getVarJoins(BasicBlock bb){
    return (XobjectAList)bb.getProp(propJoinList);
  }

  void setVarJoins(BasicBlock bb,XobjectAList alist){
    bb.setProp(propJoinList,alist);
  }

  /*
   * transform to SSA
   */
  public void run(optBodyEnv env, liveVariableInfo lv_info){
    this.optEnv = env;
    
    if(debugFlag) System.out.println("run SSA transform ...");
    
    ControlFlowGraph cfg = optEnv.getCFG();
    for(BasicBlock bb = cfg.getHead(); bb != null; bb = bb.topNext()){
      current_varlist = new XobjectAList();
      current_joinlist = new XobjectAList();

      XobjectSet defset = lv_info.getDefVars(bb);
      for(Enumeration e = defset.elements(); e.hasMoreElements();)
	current_varlist.put((Xobject)e.nextElement(),null);
      XobjectSet useset = lv_info.getUseVars(bb);
      for(Enumeration e = useset.elements(); e.hasMoreElements();)
	current_varlist.put((Xobject)e.nextElement(),null);
      if(debugFlag) System.out.println("varlist="+current_varlist);

      Vector in_edge = bb.getCflowIn();
      XobjectSet inset = lv_info.getLiveInVars(bb);
      if(debugFlag) System.out.println("--makeSSA at "+bb+", inset="+inset);
      for(Enumeration e = inset.elements(); e.hasMoreElements();){
	varIn((Xobject)e.nextElement(),bb,in_edge);
      }

      if(debugFlag) System.out.println(" "+bb+" +varlist="+current_varlist);
      // rewrite inside
      for(StatementIterator i = bb.statements(); i.hasMoreStatement(); ){
	Statement s = i.nextStatement();
	s.setExpr(rewriteExpr(s.getExpr()));
      }
      bb.setExpr(rewriteExpr(bb.getExpr()));
      if(debugFlag) System.out.println(" "+bb+" -varlist="+current_varlist);

      // set alists as property list
      setVarOut(bb,current_varlist);
      setVarJoins(bb,current_joinlist);

      // if it has backedge, fixup join
      Vector out_edges = bb.getCflowOut();
      XobjectSet outset = lv_info.getLiveOutVars(bb);
      for(int i = 0; i < out_edges.size(); i++){
	BasicBlock bp = (BasicBlock)out_edges.elementAt(i);
	XobjectAList joinlist = getVarJoins(bp);
	if(joinlist == null) continue;
	if(debugFlag) 
	  System.out.println("fixup joinlist="+joinlist+
			     " bb="+(BasicBlock)out_edges.elementAt(i));
	int j = bp.getCflowIn().indexOf(bb);
	for(Enumeration e = outset.elements(); e.hasMoreElements();){
	  Xobject x = (Xobject)e.nextElement();
	  Xobject join = (Xobject)joinlist.get(x);
	  if(join == null) continue; // no flow to backedge
	  if(debugFlag)
	    System.out.println("fixup("+j+") x="+x
			       +","+current_varlist.get(x));
	  join.setArg(j,current_varlist.get(x));
	}
      }
    }
    removeTrivialAsg(); // clean up, trivial assignment
  }

  void varIn(Xobject x,BasicBlock bb,Vector in_edges){
    BasicBlock bp;
    Xobject v,join;

    if(in_edges.isEmpty()) return;
    if(in_edges.size() == 1){
      bp = (BasicBlock)in_edges.firstElement();
      v = (Xobject)getVarOut(bp).get(x);
      if(v != null) current_varlist.put(x,v);
    } else {
      join = Xcons.List(Xcode.JOIN);
      for(int i = 0; i < in_edges.size(); i++){
	bp = (BasicBlock)in_edges.elementAt(i);
	XobjectAList varlist_out = getVarOut(bp);
	if(varlist_out == null) join.add(null); // backedge
	else if((v = (Xobject)varlist_out.get(x)) != null)
	  join.add(v);
	else join.add(x);
      }
      v = optEnv.newTempVar(x.Type()).Ref();
      current_varlist.put(x,v);
      current_joinlist.put(x,join);
      bb.insert(Xcons.Set(v,join));
    }
  }

  Xobject rewriteExpr(Xobject e){
    Xobject v,lv;
    if(e == null) return e;
    // don't rewrite JOIN statement
    if(e.isSet() && e.right().Opcode() == Xcode.JOIN) return e;
    if(e.isSet() && e.left().isLocalVar()){
      lv = e.left();
      e.setRight(rewriteVarUseExpr(e.right()));
      // def, assign a new variable after use
      if(current_varlist.contain(lv)){
	v = optEnv.newTempVar(lv.Type()).Ref();
	e.setLeft(v);
	current_varlist.put(lv,v);
      }
      return e;
    } else return rewriteVarUseExpr(e);
  }

  Xobject rewriteVarUseExpr(Xobject e){
    Xobject v;
    XobjectIterator i = new bottomupXobjectIterator(e);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getXobject();
      if(x != null && x.isLocalVar() && current_varlist.contain(x)){
	// use, rewrite
	if((v = current_varlist.get(x)) != null)
	  i.setXobject(v);
      }
    }
    return i.topXobject();
  }
    
  /*
   * remove JOIN to convert into normal form.
   */
  public void normalize(){
    ControlFlowGraph cfg = optEnv.getCFG();
    optEnv.makeDefUse();	// re-build use-def
    for(BasicBlock bb = cfg.getHead(); bb != null; bb = bb.topNext()){
      Vector in_edges = bb.getCflowIn();
      for(StatementIterator i = bb.statements(); i.hasMoreStatement(); ){
	Statement s = i.nextStatement();
	Xobject e = s.getExpr();
	if(e != null && e.isSet() && e.right().Opcode() == Xcode.JOIN){
	  if(debugFlag) System.out.println("join="+e);
	  s.remove();
	  Xobject v = e.left();  // v is lvalue
	  int k = 0;
	  for(XobjArgs a = e.right().getArgs(); a != null; a = a.nextArgs()){
	    Xobject vv = a.getArg();
	    if(vv.Opcode() == Xcode.REG){
	      Vector use = optEnv.getUseSet(vv);
	      if(use.size() == 1 && use.elementAt(0) == (Object)s){
		// this is only-one use, then replace it 
		Xobject def = optEnv.getDef(vv).getExpr();
		if(def.right().Opcode() == Xcode.PLUS_EXPR ||
		   def.right().Opcode() == Xcode.MINUS_EXPR){
		  // for updating induction variable
		  // v = JOIN(...vv...); .. vv is not used ...; vv = op(v)..
		  def.setLeft(v.copy());
		  if(debugFlag) System.out.println("replace "+v+" -> "+vv);
		  continue;
		}
	      }
	    }
	    BasicBlock bp = (BasicBlock)in_edges.elementAt(k++);
	    bp.add(Xcons.Set(v,vv));
	    if(debugFlag) System.out.println("assign "+v+" = "+vv);
	  }
	}
      }
    }
  }

  /*
   * remove Trivial Assignment in SSA form
   */
  Vector trivialAsgSet;

  public void removeTrivialAsg(){
    optEnv.makeDefUse();
    trivialAsgSet = new Vector();

    for(Enumeration e = optEnv.getTempDefs(); e.hasMoreElements(); ){
      Statement s = (Statement)(e.nextElement());
      if(s == null) continue;
      if(isTrivialAsg(s.getExpr())) trivialAsgSet.addElement(s);
    }

    while(!trivialAsgSet.isEmpty()){
      Statement s = (Statement)(trivialAsgSet.firstElement());
      trivialAsgSet.removeElementAt(0);
      Xobject e = s.getExpr();
      if(debugFlag) System.out.println("removeTrivialAsg:"+e);
      s.remove();
      rewriteVarUse(e.left(),e.right());
    }
  }

  public boolean isTrivialAsg(Xobject e){
    Xobject v,vv;
    if(e.isSet() && e.left().isTempVar()){
      Xobject lv = e.left();
      Xobject rv = e.right();
      if(e.right().isTempVar()) return true;
      if(e.right().isConstant()) return true;

      if(e.right().Opcode() == Xcode.JOIN){
	/* a = JOIN(b,b), a = JOIN(a,b) => a = b */
	vv = null;
	for(XobjArgs a = rv.getArgs(); a != null; a = a.nextArgs()){
	  v = a.getArg();
	  if(v.equals(lv)) continue;
	  if(vv == null) vv = v;
	  else if(!v.equals(vv)) return false;
	}
	e.setRight(vv);
	if(vv.isTempVar()) return true;
      }
    }
    return false;
  }

  /* rewrite the use of "lvar" with "rvar", lvar=rvar */
  void rewriteVarUse(Xobject lvar,Xobject rvar){
    Vector lvar_use = (Vector)(optEnv.getUseSet(lvar));
    Vector rvar_use = 
      rvar.isConstant()? null : (Vector)(optEnv.getUseSet(rvar));
    for(Enumeration e = lvar_use.elements(); e.hasMoreElements(); ){
      Object o = e.nextElement();
      if(o instanceof Statement){
	Statement s = (Statement)o;
	if(s.isRemoved()) continue;
	s.setExpr(rewriteVarUseExpr(lvar,rvar,s.getExpr()));
	if(isTrivialAsg(s.getExpr())) trivialAsgSet.addElement(s);
	else if(rvar_use != null) rvar_use.addElement(s);
      } else if(o instanceof BasicBlock){
	BasicBlock bb = (BasicBlock)o;
	bb.setExpr(rewriteVarUseExpr(lvar,rvar,bb.getExpr()));
	if(rvar_use != null) rvar_use.addElement(bb);
      }
    }
  }

  Xobject rewriteVarUseExpr(Xobject lvar,Xobject rvar,Xobject e){
    XobjectIterator i = new bottomupXobjectIterator(e);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getXobject();
      if(x.equals(lvar)) i.setXobject(rvar);
    }
    return i.topXobject();
  }
}

