// $Id: loopInfo.java,v 1.5 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.XobjectSet;

import java.util.Vector;

//
// optimizable loop Information (base class)
//
public class loopInfo {
  BasicBlock landing_pad;
  BasicBlock exit_pad;
  BasicBlock header,tail;

  // for caluclating loop invariant.
  optBodyEnv env;
  tempVarFlags flags;

  Xobject loop_var;
  Xobject lb;	// lower bound
  Xobject ub;  	// upper bound
  Xobject step;
  int incOp,chkOp;

  public loopInfo(optBodyEnv env, 
		  BasicBlock header,BasicBlock tail,
		  BasicBlock landing_pad,BasicBlock exit_pad){
    this.env = env;
    this.header = header;
    this.tail = tail;
    this.landing_pad = landing_pad;
    this.exit_pad = exit_pad;
  }

  public BasicBlock getLandingPad() { return landing_pad; }
  public BasicBlock getExitPad() { return exit_pad; }
  public BasicBlock getHeader() { return header; }
  public BasicBlock getTail() { return tail; }

  public Xobject getLoopVar() { return loop_var; }
  public Xobject getLowerBound(){ return lb; }
  public Xobject getUpperBound(){ return ub; }
  public Xobject getStep() { return step; }
  public int getIncOp() { return incOp; }
  public int getChkOp() { return chkOp; }

  public Xobject regDefExpr(Xobject e){ 
    Statement s = env.getDef(e);
    if(s == null) return null; // not defined
    return s.getExpr().right();
  }

  //
  // for debug.
  //
  public String toString(){
    String s;
    s = "[header="+header+",tail="+tail+
      ",\n\t landing_pad="+landing_pad+",exit_pad="+exit_pad+"]\n";
    if(flags != null){
      s += "\t variant=[";
      for(int i = 0; i < flags.size(); i++) 
	if(flags.isSet(i)) s += " "+i;
      s += " ]\n";
    } else {
      s+= "\t loop variant is not analyzed\n";
    }
    if(getLoopVar() != null){
      s += "\t {loop_var="+getLoopVar()+"\n\t lb="+lb+",\n\t ub="+ub+
	"\n\t step="+step+"\n\t incOp="+Xcode.getName(incOp)+
	"\n\t chkOp="+Xcode.getName(chkOp)+"}\n";
    } else {
      s += "\t {loop_var=none}\n";
    }
    return s;
  }

  //
  // analyze loop
  //
  XobjectSet readOnlySet;
  boolean haveMemInvariant;

  public void analyzeLoop(){  analyzeLoop(null);  }

  public void analyzeLoop(XobjectSet readOnlySet){
    this.readOnlySet = readOnlySet;

    if(loopAnalyzer.debugFlag) System.out.println("collect Invariant ...");

    collectLoopInvariant();

    if(loopAnalyzer.debugFlag) System.out.println("find inductionVar ...");

    // in loop header, find loop variable
    for(Statement s = header.getHead(); s != null; s = s.getNext()){
      Xobject x = s.getExpr();
      if(x.isSet() && x.right().Opcode() == Xcode.JOIN){
	if(isLoopVarRelation(x.left(),
			     x.right().left(),x.right().right()))
	  break;
      }
    }
  }

  public boolean isReadOnlyAddr(Xobject e){
    if(readOnlySet != null && 
       isLoopInvariantExpr(e) &&
       readOnlySet.contain(baseAddr(e))) return true;
    return false;
  }

  boolean isLoopVarRelation(Xobject v2,Xobject v0,Xobject v1){
    Xobject e;
    BasicBlock last_bb;

    /* v2 = JOIN(v1,v0) */
    if(loopAnalyzer.debugFlag)
      System.out.println("analyze relation v2:"+v2+
			 "=JOIN(v1:"+v1+",v0:"+v0+")");

    loop_var = null;  // clear!

    /* check condition part, v1 relOp ub */
    last_bb = exit_pad.getCflowIn(0);
    e = last_bb.getExpr();
    if(e == null){
      if(loopAnalyzer.debugFlag) System.out.println("no loop condition");
      return false;
    }
    chkOp = e.Opcode();
    if((chkOp == Xcode.LOG_GT_EXPR || chkOp == Xcode.LOG_GE_EXPR ||
	chkOp == Xcode.LOG_LT_EXPR || chkOp == Xcode.LOG_LE_EXPR)
       && (e.left().equals(v1) || e.left().equals(v2)) &&
       isLoopInvariantExpr(e.right())){
      ub = e.right();
      chkOp = e.Opcode();
    } else {
      if(loopAnalyzer.debugFlag) System.out.println("bad loop condition");
      return false;
    }

    /* check def of v1. v1 = v2 + step */
    Statement s = env.getDef(v1);
    e = s.getExpr();
    if(!e.isSet()){
      if(loopAnalyzer.debugFlag) System.out.println("no step");
      return false;
    }
    if(!e.left().equals(v1)){
      if(loopAnalyzer.debugFlag) System.out.println("bad step");
      return false; // need ?
    }
    incOp = e.right().Opcode();
    if((incOp == Xcode.PLUS_EXPR || incOp == Xcode.MINUS_EXPR) &&
       e.right().left().equals(v2))
      step = e.right().right();
    else {
      if(loopAnalyzer.debugFlag) System.out.println("no incr step");
      return false;
    }

    if(!isLoopInvariantExpr(step)){
      if(loopAnalyzer.debugFlag) System.out.println("no invriant step");
      return false;
    }
    lb = v0;
    loop_var = v2;
    return true;
  }

  //
  // collect loop variant.
  //
  protected void collectLoopInvariant(){
    flags = new tempVarFlags(env);
    boolean invariant_found;
    BasicBlockExprIterator i = new loopExprIterator(this);
    Xobject x;
    
    for(i.init(); !i.end(); i.next()){
      x = i.getExpr();
      if(x != null && x.isSet() && x.left().isTempVar())
	flags.set(x.left());		// mark as variant
    }

    // for forall loop
    if(loop_var != null && loop_var.isTempVar())
      flags.set(loop_var);

    do {
      invariant_found = false;
      for(i.init(); !i.end(); i.next()){
	x = i.getExpr();
	if(x != null && x.isSet() && 
	   x.left().isTempVar() && flags.isSet(x.left()) &&
	   isLoopInvariantExpr(x.right()) &&
	   !haveMemInvariant){  // don't move memory reference
	  if(loopAnalyzer.debugFlag) 
	    System.out.println("move loop invariant:"+x);
	  flags.reset(x.left());
	  Statement s = i.getStatement();
	  s.remove();
	  landing_pad.add(s);
	  invariant_found = true;
	}
      }
    } while(invariant_found);
  }

  /* 
   * return true if 'e' is loop invariant 
   */
  public boolean isLoopInvariantExpr(Xobject e){
    haveMemInvariant = false;

    if(e == null) return true;	// don't care

    if(e.Opcode() == rangeExpr.FOR ||
       e.Opcode() == rangeExpr.FORALL)
      return ((rangeExpr)e).isLoopInvariant(this);

    XobjectIterator i = new bottomupXobjectIterator(e);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getXobject();
      if(x == null) continue;
      if(x.isConstant()) continue;

      if(x.isTempVar()){
	if(flags.isSet(x)) return false;
	else continue;
      }

      if(x.isVariable()){
	if(i.getParent() != null &&
	   i.getParent().Opcode() == Xcode.ADDR_OF)
	  continue;	/* variable address */
	else if(x.Opcode() == Xcode.VAR){
	  if(readOnlySet != null &&
	     readOnlySet.contain(Xcons.AddrOfVar(x))){
	    haveMemInvariant = true;
	    continue;
	  }
	}
	return false;
      }

      switch(x.Opcode()){
      case Xcode.POINTER_REF:   // memory reference
	if(readOnlySet != null && 
	   isLoopInvariantExpr(e.operand()) &&
	   readOnlySet.contain(baseAddr(e.operand()))){
	  haveMemInvariant = true;
	  continue;
	}
      case Xcode.FUNCTION_CALL:
      case Xcode.JOIN:
      case Xcode.LIST:
	return false;
      }
      if(x.isExternalCode()) return false;
    }
    return true;
  }

  Xobject baseAddr(Xobject e){
    switch(e.Opcode()){
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
    case Xcode.ARRAY_AREF:
      return baseAddr(e.left());
    }
    return e;
  }

  /* 
   * return true if 'e' is induction expression.
   */
  public boolean isInductionExpr(Xobject e){
    if(e == null) return false;
    Xobject v = getLoopVar();
    if(v == null) return false;
    if(e.equals(v)) return true; // loop var itself!

    switch(e.Opcode()){
    case Xcode.REG:	// tempoary
      Statement s = env.getDef(e);
      if(s == null) return false; // not defined, ignore
      return isInductionExpr(s.getExpr().right());
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      if(isInductionExpr(e.left())){
	return (isInductionExpr(e.right()) || 		/* i + i */
		isLoopInvariantExpr(e.right())); 	/* i + c */
      } else if(isInductionExpr(e.right())){ 
	return isLoopInvariantExpr(e.left()); 		/* c + i */
      } 
      break;
    case Xcode.MUL_EXPR:
      if(isInductionExpr(e.left())){
	return isLoopInvariantExpr(e.right()); 		/* i * c */
      } else if(isInductionExpr(e.right())){
	return isLoopInvariantExpr(e.left());  		/* c * i */
      } 
      break;
    case Xcode.DIV_EXPR:
      if(isInductionExpr(e.left())){
	return isLoopInvariantExpr(e.right());  	/* i / c */
      }
      break;
    case Xcode.UNARY_MINUS_EXPR:
      if(isInductionExpr(e.operand())) return true; 	/* -i */
      break;
    case Xcode.ARRAY_AREF:
      if(isInductionExpr(e.operand())) return true; 	/* *(A+i) => A+i */
      break;
    }
    return false;
  }

  public Xobject lowerBoundExpr(Xobject e){
    return inductionExpr(e,getLowerBound());
  }

  public Xobject upperBoundExpr(Xobject e){
    return inductionExpr(e,getUpperBound());
  }

  /* e must be induction expression */
  public rangeExpr rangeExpr(Xobject e){
    return rangeExpr.For(lowerBoundExpr(e),
			 upperBoundExpr(e),
			 stepExpr(e,step),incOp,chkOp,null);
  }

  public rangeExpr rangeExprWithExpr(Xobject e){
    return rangeExpr.For(lb,ub,step,incOp,chkOp,e);
  }

  /* return the value of induction expression 'e'
   * when induction variable is 'x'.
   */
  public Xobject inductionExpr(Xobject e,Xobject x){
    if(e == null) return null;
    Xobject v = getLoopVar();
    if(v == null) return null;
    if(e.equals(v)) return x; // loop var itself!

    switch(e.Opcode()){
    case Xcode.REG:	// tempoary
      if(isLoopInvariantExpr(e)) return e;
      Statement s = env.getDef(e);
      return inductionExpr(s.getExpr().right(),x);
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
    case Xcode.MUL_EXPR:
    case Xcode.DIV_EXPR:
      return Xcons.binaryOp(e.Opcode(),
			    inductionExpr(e.left(),x),
			    inductionExpr(e.right(),x));
    case Xcode.UNARY_MINUS_EXPR:
      return Xcons.unaryOp(e.Opcode(),
			   inductionExpr(e.operand(),x));
    case Xcode.ARRAY_AREF:
      return Xcons.List(e.Opcode(),e.Type(),
			   inductionExpr(e.operand(),x));
    }
    return e;
  }

  /* return the step value for induction expression 'e' */
  public Xobject stepExpr(Xobject e,Xobject x){
    if(e == null) return null;
    Xobject v = getLoopVar();
    if(v == null) return null;
    if(e.equals(v)) return x;

    switch(e.Opcode()){
    case Xcode.REG:	// tempoary
      Statement s = env.getDef(e);
      return stepExpr(s.getExpr().right(),x);
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      if(isInductionExpr(e.left())){
	if(isInductionExpr(e.right()))
	  return Xcons.binaryOp(e.Opcode(),
				stepExpr(e.left(),x),stepExpr(e.right(),x));
	else return stepExpr(e.left(),x);
      } else if(isInductionExpr(e.right())){
	if(e.Opcode() == Xcode.MINUS_EXPR)
	  return Xcons.unaryOp(Xcode.UNARY_MINUS_EXPR,stepExpr(e.right(),x));
	else 
	  return stepExpr(e.right(),x);
      } 
      break;
    case Xcode.MUL_EXPR:
    case Xcode.DIV_EXPR:
      if(isInductionExpr(e.left())){ // right must be loop invariant
	return Xcons.binaryOp(e.Opcode(),stepExpr(e.left(),x),e.right());
      } else if(isInductionExpr(e.right())){ // left must be invariant
	return Xcons.binaryOp(e.Opcode(),e.left(),stepExpr(e.right(),x));
      } 
      break;
    case Xcode.UNARY_MINUS_EXPR:
      return Xcons.unaryOp(e.Opcode(),stepExpr(e.operand(),x));
    case Xcode.ARRAY_AREF:
      return Xcons.binaryOp(Xcode.MUL_EXPR,stepExpr(e.operand(),x),
			    Xcons.IntConstant(e.operand().Type().getSize()));
    }
    return null;
  }
}







