// $Id: instrumentRW.java,v 1.5 2000/12/20 06:16:55 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 java.util.Vector;

public class instrumentRW {
  public static boolean debugFlag;
  protected optBodyEnv optEnv;

  Vector shared_objs;

  public instrumentRW(){ }

  int addSharedObj(Xobject x){
    int indx = shared_objs.indexOf(x);
    if(indx < 0){
      indx = shared_objs.size();
      shared_objs.addElement(x);
    }
    return indx;
  }

  BasicBlockExprIterator current_expr_i;

  public void run(optBodyEnv env){
    optEnv = env;
    shared_objs = new Vector();
    Xobject x;

    current_expr_i = new BasicBlockExprIterator(optEnv.getBody());
    for(current_expr_i.init(); !current_expr_i.end(); current_expr_i.next()){
      x = current_expr_i.getExpr();
      if(x == null) continue;
      if(x.Opcode() == Xcode.OMP_SHARE){
	// collect shared object.
	current_expr_i.setExpr(RWcheckCode.Share(addSharedObj(x.getArg(0)),
						 x.getArgs()));
	continue;
      }
      if(x.isExternalCode()) continue; // skip my codes!

      /* check Expr */
      instrumentExpr(x);
    }

    if(debugFlag){
      System.out.println("shared_obj="+shared_objs); 
    }

    // set context
    BlockIterator i = new bottomupBlockIterator(optEnv.getBody());
    for(i.init(); !i.end(); i.next()){
      Block b = i.getBlock();
      switch(b.Opcode()){
      case Xcode.OMP_FORALL:
	setContextRWcodes(b,RWcheckCode.IN_FORALL);
	break;
      case Xcode.OMP_MASTER:
      case Xcode.OMP_SINGLE:
	setContextRWcodes(b,RWcheckCode.IN_MASTER);
	break;
      }
    }

    // finally, re-make def/use info
    env.makeDefUse();
  }

  void setContextRWcodes(Block b,int context){
    BasicBlockExprIterator i = new BasicBlockExprIterator(b);
    for(i.init(); !i.end(); i.next()){
      Xobject x = i.getExpr();
      if(x.Opcode() == RWcheckCode.RCHK ||
	 x.Opcode() == RWcheckCode.WCHK)
	((RWcheckCode)x).setContext(context);
    }
  }

  void instrumentExpr(Xobject x){
    Xobject addr;
    XobjectIterator i = new bottomupXobjectIterator(x);
    for(i.init(); !i.end(); i.next()){
      Xobject xx = i.getXobject();
      if(xx == null) continue;

      // check lvalue reference
      if(i.getParent() != null && i.getParent().isAsgOp() && 
	 i.getParent().left() == xx){
	/* write memory */
	switch(xx.Opcode()){
	case Xcode.VAR:	/* global variable reference */
	  if(i.getParent().isSet())
	    instrumentAddr(Xcons.AddrOfVar(xx),true,false);
	  else
	    instrumentAddr(Xcons.AddrOfVar(xx),true,true);
	  break;
	case Xcode.POINTER_REF:
	  if(i.getParent().isSet())
	    addr = instrumentAddr(xx.operand(),true,false);
	  else
	    addr = instrumentAddr(xx.operand(),true,true);
	  if(addr != null) xx.setOperand(addr);
	  break;
	}
	continue;
      }

      /* read memory */
      switch(xx.Opcode()){
      case Xcode.VAR:	/* global variable reference */
	if(i.getParent() != null && 
	   i.getParent().Opcode() == Xcode.ADDR_OF)
	  break;
	instrumentAddr(Xcons.AddrOfVar(xx),false,true);
	break;
      case Xcode.POINTER_REF:
	addr = instrumentAddr(xx.operand(),false,true);
	if(addr != null) xx.setOperand(addr);
	break;
      }
    }
  }
  
  Xobject instrumentAddr(Xobject addr,boolean write_flag,boolean read_flag){
    int idx;
    Xobject base_addr;
    XobjArgs subs = null;
    Xobject chkcode;

    base_addr = baseAddr(addr);
    if(base_addr == null || isPrivateAddr(base_addr)) return null; // private
    
    if((idx = shared_objs.indexOf(base_addr)) >= 0){
      if(base_addr == addr) subs = null;
      else if((subs = getSubs(base_addr,addr)) == null){
	idx = -1;
	OMP.warning(current_expr_i.getExpr().getLineNo(),
		    "bad shared object reference");
      }
    } 

    if(idx < -1){	// pointer to any address, idx = -1
      addr = storeTempVar(addr);
      subs = XobjArgs.cons(addr,null);
    }

    if(read_flag){
      if(subs == null) chkcode = RWcheckCode.Read(idx);
      else chkcode = RWcheckCode.Read(idx,Xcons.List(subs));
      current_expr_i.insertStatement(chkcode);
    }
    if(write_flag){
      if(subs == null) chkcode = RWcheckCode.Write(idx);
      else chkcode = RWcheckCode.Write(idx,Xcons.List(subs));
      current_expr_i.addStatement(chkcode);
    }

    return addr;
  }
      
  XobjArgs getSubs(Xobject base_addr,Xobject addr){
    XobjArgs subs = null;
    if(addr == null) return null; // error
    switch(addr.Opcode()){
    case Xcode.PLUS_EXPR:
      if(addr.left() == base_addr ||
	 (subs = getSubs(base_addr,addr.left())) != null){
	addr.setRight(storeTempVar(addr.right()));
	return XobjArgs.cons(addr.right().copy(),subs);
      } 
      if(addr.right() == base_addr ||
	 (subs = getSubs(base_addr,addr.right())) != null){
	addr.setLeft(storeTempVar(addr.left()));
	return XobjArgs.cons(addr.left().copy(),subs);
      } 
      break;
    case Xcode.MINUS_EXPR:
      if(addr.left() == base_addr ||
	 (subs = getSubs(base_addr,addr.left())) != null){
	addr.setRight(storeTempVar(addr.right()));
	return XobjArgs.cons(Xcons.unaryOp(Xcode.UNARY_MINUS_EXPR,
					   addr.right().copy()),subs);
      } 
      break;
    case Xcode.ARRAY_AREF:
      return getSubs(base_addr,addr.operand());
    }
    return null;
  }

  Xobject storeTempVar(Xobject v){
    if(v.isVariable() || v.isConstant()) return v;
    Ident temp_var =  optEnv.newTempVar(v.Type());
    Xobject s = Xcons.Set(temp_var.Ref(),v);
    current_expr_i.insertStatement(s);
    return temp_var.Ref();
  }

  // Check whether address e point to private memory.
  boolean isPrivateAddr(Xobject e){ // e must be a pointer
    switch(e.Opcode()){
    case Xcode.LVAR_ADDR:
    case Xcode.LARRAY_ADDR:
    case Xcode.PARAM_ADDR:
      return true;
    default:
      // should check threadprivate
      return false;
    }
  }

  Xobject baseAddr(Xobject e){
    Xobject x;
    Xtype t = e.Type();
    if(t == null || !t.isPointer()) return null;

    switch(e.Opcode()){
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      x = baseAddr(e.left());
      if(x != null) return x;
      return baseAddr(e.right());

    case Xcode.MEMBER_ADDR:
    case Xcode.MEMBER_ARRAY_ADDR:
    case Xcode.ARRAY_AREF:
      return baseAddr(e.left());

      /* terminal */
    case Xcode.LVAR_ADDR:	// local
    case Xcode.LARRAY_ADDR:	// local
    case Xcode.PARAM_ADDR:	// local
    case Xcode.LVAR:  		// may be shared, threadprivate
    case Xcode.REG:		// may be shared, threadprivate
    case Xcode.ARRAY_ADDR:	// may be shared, threadprivate
    case Xcode.VAR_ADDR:	// may be shared, threadprivate
      return e;
    default:
      return null;
    }
  }

  mayWriteAnalyzer may_write_info = new mayWriteAnalyzer();
  mustWriteAnalyzer must_write_info = new mustWriteAnalyzer();
  readAvailableAnalyzer read_available_info = new readAvailableAnalyzer();

  public void analyzeMayWrite(){
    may_write_info.run(optEnv,shared_objs.size());
  }

  // Add flush code:
  //  if object "may" be modified, check actual write with the runtime lib.
  public void addFlush(){
    BlockIterator i = new bottomupBlockIterator(optEnv.getBody());
    for(i.init(); !i.end(); i.next()){
      Block b = i.getBlock();
      if(b.Opcode() == Xcode.OMP_FLUSH || b.Opcode() == Xcode.OMP_BARRIER){
	BasicBlock bb = ((OMPBlock)b).beginBasicBlock();
	for(int idx = 0; idx < shared_objs.size(); idx++){
	  if(may_write_info.mayWrite(bb,idx)) bb.add(RWcheckCode.Flush(idx));
	}
      }
    }
    /* flush at exit */
    BasicBlock exit_bb = optEnv.getCFG().exitBasicBlock();
    for(int idx = 0; idx < shared_objs.size(); idx++){
      if(may_write_info.mayWrite(exit_bb,idx)) 
	exit_bb.add(RWcheckCode.Flush(idx));
    }
  }

  // Remove redundant read:
  //  .. r1: read/write(a) -> r2: read(a) ...
  //  the second r2 can be removed
  public void removeRedundantRead(){
    int num_obj = shared_objs.size();
    int idx;
    boolean available[] = new boolean[num_obj];
    read_available_info.run(optEnv,num_obj,may_write_info);
    
    BasicBlockIterator i = new BasicBlockIterator(optEnv.getBody());
    for(i.init(); !i.end(); i.next()){
      BasicBlock bb = i.getBasicBlock();
      for(idx = 0; idx < num_obj; idx++)
	available[idx] = read_available_info.readAvailable(bb,idx);
      for(Statement s = bb.getHead(); s != null; s = s.getNext()){
	Xobject x = s.getExpr();
	if(x == null) continue;
	if(x.Opcode() != RWcheckCode.RCHK && x.Opcode() != RWcheckCode.WCHK)
	  continue;
	RWcheckCode chk_x = (RWcheckCode)x;
	idx = chk_x.getIndex();
	if(idx < 0) continue;

	// currently, support only scalar
	if(x.Opcode() == RWcheckCode.RCHK && available[idx] && 
	   chk_x.getArgs() == null)
	  s.setExpr(null); // remove
	available[idx] = true;
      }
    }
  }

  // Remove redundant write:
  //  .. w1: write(a) -> w2: write(a) ...
  //  the second w2 can be removed, because write just notify modified,
  //  write actually occurs at flush.
  // this optimization can be applied only for variable.
  public void removeRedundantWrite(){
    int num_obj = shared_objs.size();
    int idx;
    boolean written[] = new boolean[num_obj];
    must_write_info.run(optEnv,num_obj);
    
    BasicBlockIterator i = new BasicBlockIterator(optEnv.getBody());
    for(i.init(); !i.end(); i.next()){
      BasicBlock bb = i.getBasicBlock();
      for(idx = 0; idx < num_obj; idx++)
	written[idx] = must_write_info.mustWrite(bb,idx);
      for(Statement s = bb.getHead(); s != null; s = s.getNext()){
	Xobject x = s.getExpr();
	if(x == null) continue;
	if(x.Opcode() != RWcheckCode.WCHK) continue;
	RWcheckCode chk_x = (RWcheckCode)x;
	idx = chk_x.getIndex();
	if(idx < 0) continue;

	// currently, support only scalar
	if(written[idx] && chk_x.getArgs() == null)
	  s.setExpr(null); // remove
	written[idx] = true;
      }
    }
  }

  public void transCode(){
    RWcheckCode.initTransCode(optEnv,shared_objs);
    BasicBlockExprIterator ii = new BasicBlockExprIterator(optEnv.getBody());
    for(ii.init(); !ii.end(); ii.next()){
      Xobject x = ii.getExpr();
      if(x == null) continue;
      if(x instanceof RWcheckCode)
	ii.setExpr(((RWcheckCode)x).transCode());
      }
  }
}






