// $Id: mergeRWcheck.java,v 1.8 2001/02/05 10:38:44 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 ompd;

import exc.object.*;
import exc.block.*;
import exc.flow.*;
import exc.openmp.*;
import exc.util.*;

import java.util.Vector;

public class mergeRWcheck implements loopVisitor {
  public boolean debugFlag = true;

  /*
   * For optimization of check codes.
   *  merge RWcheckCode in loop
   *  move RWcheckCode from Master/signle Block
   */
  int num_objs;
  Vector shared_objs;

  public void run(instrumentRW instRW){
    shared_objs = instRW.shared_objs;
    num_objs = shared_objs.size();
    ControlFlowGraph cfg = instRW.optEnv.getCFG();
    for(BasicBlock bb = cfg.getTail(); bb != null; bb = bb.topPrev()){
      Block b = bb.getParent();
      if(b.Opcode() == Xcode.OMP_MASTER || b.Opcode() == Xcode.OMP_SINGLE){
	OMPBlock bp = (OMPBlock)b;
	if(bp.beginBasicBlock() == bb) removeMasterRW(bp);
      }
      // for loop
      loopInfo info = loopAnalyzer.getLoopInfo(bb);
      if(info != null) visitLoop(info);
    }
  }

  /* 
   * Hosist out for single/master
   *  At begining of block, unknown address access must be flushed.
   *  RWcheckCodes are moved to the begining(R) and the end (W).
   */

  XobjectSet modifiedMasterVars;

  public void removeMasterRW(OMPBlock b){
    Xobject x;
    RWcheckCode chkcode;
    BasicBlockExprIterator i = new BasicBlockExprIterator(b.getBody());

    /* collect modified variable in this block */
    modifiedMasterVars = new XobjectSet();
    for(i.init(); !i.end(); i.next()){
      if((x = i.getExpr()) == null) continue;
      if(x.isSet() && x.left().isVariable()) 
	modifiedMasterVars.add(x.left());
    }

    /* check RWcode */
    for(i.init(); !i.end(); i.next()){
      if((x = i.getExpr()) == null) continue;
      if(x.Opcode() != RWcheckCode.RCHK && x.Opcode() != RWcheckCode.WCHK)
	continue;
      chkcode = (RWcheckCode)x;
      i.getStatement().remove(); // remove it

      // if any address, move it because all known data must be in master.
      if(chkcode.getIndex() < 0) continue; 
      chkcode.setContext(RWcheckCode.IN_MASTER);
      if(modifiedMasterExpr(chkcode.getArgs())) chkcode.setArgs(null);
      if(x.Opcode() == RWcheckCode.RCHK)
	addRWcheckCode(b.beginBasicBlock(),chkcode);
      else 
	addRWcheckCode(b.endBasicBlock(),chkcode);
    }
  }

  boolean modifiedMasterExpr(XobjArgs l){
    for(; l != null; l = l.nextArgs()) {
      if(modifiedMasterExpr(l.getArg())) return true;
    }
    return false;
  }

  boolean modifiedMasterExpr(Xobject x){
    XobjectIterator i = new topdownXobjectIterator(x);
    for(i.init(); !i.end(); i.next()){
      Xobject v = i.getXobject();
      if(v == null) continue;
      switch(v.Opcode()){
      case Xcode.FUNCTION_CALL:
      case Xcode.POINTER_REF:   // memory reference
        return true;
      }
      if(modifiedMasterVars.contain(x)) return true;
    }
    return false;
  }

  void addRWcheckCode(BasicBlock bb,RWcheckCode c){
    int code = c.Opcode();
    RWcheckCode chkcode;
    XobjArgs l,ll;

    for(Statement s = bb.getHead(); s != null; s = s.getNext()){
      Xobject x = s.getExpr();
      if(x.Opcode() != code) continue;
      chkcode = (RWcheckCode)x;
      if(chkcode.getIndex() == c.getIndex() && 
	 chkcode.getContext() == c.getContext()){
	if(chkcode.getArgs() == null || c.getArgs() == null){
	  chkcode.setArgs(null);
	  return;
	}
	// merge checkCode
	for(l = c.getArgs(); l != null; l = l.nextArgs()){
	  Xobject v = l.getArg();
	  for(ll = chkcode.getArgs(); ll != null; ll = ll.nextArgs()){
	    if(v.equals(ll.getArg())) break;
	  }
	  if(ll == null) chkcode.add(v);
	}
	return;
      }
    }
    bb.add(c);
  }

  /* 
   * Hoist out checkCode
   */
  loopInfo current_loop;
  boolean R_set[];
  boolean W_set[];
  boolean haveFlush;

  public void visitLoop(loopInfo info){
    current_loop = info;
    haveFlush = checkFlush();
    XobjectSet readOnly = checkRWset();

    info.analyzeLoop(readOnly);
    if(debugFlag){
      System.out.println("mergeRWCheck loop="+info);
      System.out.println("readonly="+readOnly);
    }
    loopHoistRWcode();	/* call merge */
  }

  boolean checkFlush(){
    loopInfo info = current_loop;
    BasicBlock bb = info.getHeader(); 
    while(true){
      switch(bb.getParent().Opcode()){
      case Xcode.OMP_FLUSH:
      case Xcode.OMP_BARRIER:
	return true;
      }
      if(bb == info.getTail()) break;
      bb = bb.topNext();
    }
    return false;
  }

  XobjectSet checkRWset(){
    int idx;
    R_set = new boolean[num_objs];
    W_set = new boolean[num_objs];

    BasicBlockExprIterator i = new loopExprIterator(current_loop);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getExpr();
      if(x.Opcode() == RWcheckCode.RCHK || 
	 x.Opcode() == RWcheckCode.WCHK){
	RWcheckCode chkcode = (RWcheckCode)x;
	if((idx = chkcode.getIndex()) < 0) continue;
	if(x.Opcode() == RWcheckCode.RCHK) R_set[idx] = true;
	else W_set[idx] = true;
      }
    }
    
    XobjectSet s = new XobjectSet();
    for(idx = 0; idx < num_objs; idx++){
      if(R_set[idx] && !W_set[idx]){
	// System.out.println("Rset["+idx+"]="+shared_objs.elementAt(idx));
	s.add((Xobject)shared_objs.elementAt(idx));
      }
    }
    return s;
  }

  final static int LOOP_VARIANT = 0;
  final static int LOOP_INDUCTION = 1;
  final static int LOOP_INVARIANT = 2;
  
  int checkRWargs(XobjArgs s){
    loopInfo info = current_loop;
    int flag = LOOP_INVARIANT;
    for( ; s != null; s = s.nextArgs()){
      System.out.println("chk expr: "+s.getArg());
      int f = LOOP_INVARIANT;
      for(XobjArgs a = s.getArg().getArgs() ; a != null; a = a.nextArgs()){
	Xobject x = a.getArg();
	System.out.println("  chk dim: "+x);
	if(info.isLoopInvariantExpr(x)){
	  System.out.println("->loop Invariant");
	  continue;
	}
	if(info.isInductionExpr(x) || isInspectableExpr(info,x)){
	  System.out.println("->loop Induction");
	  if(f == LOOP_INDUCTION){
	    /* if already other dimension is induction, fail */
	    return LOOP_VARIANT;
	  }
	  f = LOOP_INDUCTION;
	  continue;
	}
	System.out.println("->loop Variant");
	return LOOP_VARIANT;
      }
      if(f == LOOP_INDUCTION) flag = f;
    }
    return flag;
  }

  void rewriteInductionArgs(XobjArgs s){
    loopInfo info = current_loop;
    for( ; s != null; s = s.nextArgs()){
      for(XobjArgs a = s.getArg().getArgs() ; a != null; a = a.nextArgs()){
	Xobject x = a.getArg();
	if(info.isLoopInvariantExpr(x)) continue;
	if(info.isInductionExpr(x))
	  a.setArg(info.rangeExpr(x));
	else if(isInspectableExpr(info,x))
	  a.setArg(inspectableRangeExpr(info,x));
	else fatal("rewriteInductionArgs: bad expr");
      }
    }
  }

  /*
   * check expression is inspectable:
   *  A[a*i+b]+off
   */
  public boolean isInspectableExpr(loopInfo info,Xobject e){
    if(e == null) return false;

    if(e.Opcode() == rangeExpr.FOR){
      rangeExpr r = (rangeExpr)e;
      Xobject lb = r.getLowerBound();
      Xobject ub = r.getUpperBound();
      if((info.isLoopInvariantExpr(lb)||
	  info.isInductionExpr(lb) ||
	  isInspectableExpr(info,lb)) &&
	 (info.isLoopInvariantExpr(ub)||
	  info.isInductionExpr(ub) ||
	  isInspectableExpr(info,ub)) &&
	 info.isLoopInvariantExpr(r.getStep())) return true;
      return false;
    }
    switch(e.Opcode()){
    case Xcode.REG:
      return isInspectableExpr(info,info.regDefExpr(e));
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      if(info.isLoopInvariantExpr(e.right()) &&
	 isInspectableExpr(info,e.left())) 
	return true;
      else return false;
    case Xcode.POINTER_REF:
      e = e.operand();
      if(e.Opcode() == Xcode.PLUS_EXPR &&
	 info.isReadOnlyAddr(e.left()) &&
	 info.isInductionExpr(e.right())){
	Xobject addr = e.left();
	Xobject idx = (Xobject)addr.getProp(RWcheckCode.idxProp);
	int i;
	if(idx != null) i = idx.getInt();
	else {
	  i = shared_objs.indexOf(baseAddr(addr));
	  addr.setProp(RWcheckCode.idxProp,Xcons.IntConstant(i));
	}
	if(i >= 0 && !R_set[i]) return true;
      } 
      return false;
    }
    return false;
  }

  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;
  }

  rangeExpr inspectableRangeExpr(loopInfo info,Xobject e){
    if(e.Opcode() == rangeExpr.FOR){
      rangeExpr r = (rangeExpr)e;
      Xobject lb = r.getLowerBound();
      if(!info.isLoopInvariantExpr(lb)){
	if(info.isInductionExpr(lb))
	  lb =  Xcons.List(info.stepExpr(lb,Xcons.IntConstant(1)),
			   info.inductionExpr(lb,Xcons.IntConstant(0)));
	else lb = inspectableIndirectExpr(info,lb);
	r.setLowerBound(lb);
      }
      Xobject ub = r.getUpperBound();
      if(!info.isLoopInvariantExpr(ub)){
	if(info.isInductionExpr(ub))
	  ub =  Xcons.List(info.stepExpr(ub,Xcons.IntConstant(1)),
			   info.inductionExpr(ub,Xcons.IntConstant(0)));
	else ub = inspectableIndirectExpr(info,ub);
	r.setUpperBound(ub);
      }
      return info.rangeExprWithExpr(e);
    } 
    return info.rangeExprWithExpr(inspectableIndirectExpr(info,e));
  }

  Xobject inspectableIndirectExpr(loopInfo info,Xobject e){
    switch(e.Opcode()){
    case Xcode.REG:
      return inspectableIndirectExpr(info,info.regDefExpr(e));
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      Xobject x = inspectableIndirectExpr(info,e.left());
      e = Xcons.binaryOp(e.Opcode(),x.getArg(3),e.right());
      x.setArg(3,Xcons.Reduce(e));
      return x;
    case Xcode.POINTER_REF:
      e = e.operand();
      /* must be e.Opcode() == Xcode.PLUS_EXPR */
      return Xcons.List(info.stepExpr(e.right(),Xcons.IntConstant(1)),
			info.inductionExpr(e.right(),Xcons.IntConstant(0)),
			e.left(),Xcons.IntConstant(0));
    }
    return null; // fatal error?
  }

  /* Merge check code by hoisting out R/W codes in the loop:
   *  1. Any read code can be hosted out in the begining of block if possible.
   *  2. If read code remains in the loop, write code must be exact.
   *  3. If all read code are hosted out, write code can be hoisted out.
   *  4. If read/write code remains, mark it IN_MASTER_ALL for forall loop.
   */
  void loopHoistRWcode(){
    loopInfo info = current_loop;
    BasicBlock landing_pad_bb,exit_pad_bb;
    Xobject e;
    Block b;
    RWcheckCode chkcode;
    boolean is_forall = false;
    boolean R_remains[] = new boolean[num_objs];
    boolean W_remains[] = new boolean[num_objs];
    int k;

    landing_pad_bb = info.getLandingPad();
    exit_pad_bb = info.getExitPad();
    b = landing_pad_bb.getParent();
    if(b.Opcode() == Xcode.OMP_FORALL) is_forall = true;

    BasicBlockExprIterator i = new loopExprIterator(info);
    boolean move_flag;

    // for RCHK
    do {
      move_flag = false;
      for(k = 0; k < num_objs; k++) R_remains[k] = false;
      for(i.init(); !i.end(); i.next()){
	if((e = i.getExpr()) == null) continue;
	if(e.Opcode() != RWcheckCode.RCHK) continue;
	chkcode = (RWcheckCode)e;
	if((k = chkcode.getIndex()) < 0) continue;

	if(haveFlush && W_set[k]) continue;
	
	switch(checkRWargs(chkcode.getArgs())){
	case LOOP_INDUCTION:
	  rewriteInductionArgs(chkcode.getArgs());
	  /* go through */
	case LOOP_INVARIANT:
	  move_flag = true;
	  i.getStatement().remove();  // remove it
	  if(is_forall) chkcode.setContext(RWcheckCode.IN_PARALLEL);
	  addRWcheckCode(landing_pad_bb,chkcode);
	  break;
	default:
	  R_remains[k] = true;
	}
      }
      for(k = 0; k < num_objs; k++) R_set[k] = R_remains[k];
    } while(move_flag);

    if(!haveFlush){
      // for WCHK
      for(i.init(); !i.end(); i.next()){
	if((e = i.getExpr()) == null) continue;
	if(e.Opcode() != RWcheckCode.WCHK) continue;
	chkcode = (RWcheckCode)e;
	if(chkcode.getIndex() < 0) continue;
	if(R_remains[chkcode.getIndex()]) continue;
	switch(checkRWargs(chkcode.getArgs())){
	case LOOP_INDUCTION:
	  rewriteInductionArgs(chkcode.getArgs());
	  /* go through */
	case LOOP_INVARIANT:
	  i.getStatement().remove();  // remove it
	  if(is_forall){
	    chkcode.setContext(RWcheckCode.IN_PARALLEL);
	    addRWcheckCode(exit_pad_bb,chkcode);
	  } else
	    exit_pad_bb.insert(chkcode);
	  break;
	default:
	  W_remains[chkcode.getIndex()] = true;
	}
      }
    }
      
    if(is_forall){
      // add Flush for random read
      for(k = 0; k < num_objs; k++)
	if(R_remains[k] || W_remains[k]) 
	  addRWcheckCode(landing_pad_bb,RWcheckCode.FlushR(k));
    }
  }

  static void fatal(String msg){
    System.err.println("mergeRWcheck fatal error: "+msg);
    System.exit(1);
  }
}


