// $Id: RWcheckCode.java,v 1.10 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.flow.*;
import java.util.Vector;

//
// constant for RWcheckCode
//
public class RWcheckCode extends XobjList {
  // code name
  public final static int RCHK = Xobject.newCode();
  public final static int WCHK = Xobject.newCode();
  public final static int SHARE = Xobject.newCode();
  public final static int FLUSH = Xobject.newCode();
  public final static int FLUSHR = Xobject.newCode();

  public final static String idxProp = "RWCheckCode.idxProp";

  // context name
  public final static int IN_PARALLEL = 0;	// executed in multiple thread
  public final static int IN_FORALL = 1;	// in forall
  public final static int IN_MASTER = 2;	// in master/single section

  public final static String propInstrumented = "RWcheckCode.instrumented";
  public final static String shared_table_name = "__ompd__";
  
  public final static String RCHK_ptr_fname = "_ompd_R_p";
  public final static String WCHK_ptr_fname = "_ompd_W_p";

  public final static String alloc_var_desc_fname = "_ompd_D_v";
  public final static String RCHK_var_fname = "_ompd_R_v";
  public final static String WCHK_var_fname = "_ompd_W_v";

  public final static String alloc_array_desc_fname = "_ompd_D_a";
  public final static String RCHK_array_fname = "_ompd_R_a";
  public final static String WCHK_array_fname = "_ompd_W_a";

  public final static String FLUSH_fname = "_ompd_flush";
  public final static String FLUSHR_fname = "_ompd_flushR";
  public final static String for_range_fname = "_ompd_acc_for";
  public final static String forall_range_fname = "_ompd_acc_forall";
  public final static String set_index_fname = "_ompd_acc_index";
  public final static String set_range_fname = "_ompd_acc_range";
  public final static String add_for_fname = "_ompd_add_for";
  public final static String add_expr_fname = "_ompd_add_expr";

  static Ident RCHK_ptr_f,RCHK_var_f,RCHK_array_f;
  static Ident WCHK_ptr_f,WCHK_var_f,WCHK_array_f;
  static Ident FLUSH_f,FLUSHR_f;
  static Ident for_range_f,forall_range_f,set_index_f;
  static Ident add_for_f,add_expr_f;
  static Ident alloc_array_desc_f;
  static Ident alloc_var_desc_f;
  static Ident set_range_f;

  static void declareRuntimeFunc(XobjectFile env){
    Xtype ftype = Xtype.Function(Xtype.intType);
    RCHK_ptr_f = env.declExternIdent(RCHK_ptr_fname,ftype);
    RCHK_var_f = env.declExternIdent(RCHK_var_fname,ftype);
    RCHK_array_f = env.declExternIdent(RCHK_array_fname,ftype);
    WCHK_ptr_f = env.declExternIdent(WCHK_ptr_fname,ftype);
    WCHK_var_f = env.declExternIdent(WCHK_var_fname,ftype);
    WCHK_array_f = env.declExternIdent(WCHK_array_fname,ftype);

    FLUSH_f = env.declExternIdent(FLUSH_fname,ftype);
    FLUSHR_f = env.declExternIdent(FLUSHR_fname,ftype);

    for_range_f = env.declExternIdent(for_range_fname,ftype);
    forall_range_f = env.declExternIdent(forall_range_fname,ftype);
    set_index_f = env.declExternIdent(set_index_fname,ftype);
    set_range_f = env.declExternIdent(set_range_fname,ftype);
    add_for_f = env.declExternIdent(add_for_fname,ftype);
    add_expr_f = env.declExternIdent(add_expr_fname,ftype);

    alloc_var_desc_f = env.declExternIdent(alloc_var_desc_fname,ftype);
    alloc_array_desc_f = env.declExternIdent(alloc_array_desc_fname,ftype);
  }

  int index;
  int context;	// default IN_PARALLEL

  public RWcheckCode(int code, int index, Xobject x){
    super(code,x);
    this.index = index;
  }

  public RWcheckCode(int code, int index, XobjArgs a){
    super(code,a);
    this.index = index;
  }

  public int getIndex() { return index; }
  public int getContext() { return context; }
  public void setContext(int context) { this.context = context; }

  static public RWcheckCode Read(int idx, Xobject a){
    return new RWcheckCode(RCHK,idx,a);
  }

  static public RWcheckCode Write(int idx, Xobject a){
    return new RWcheckCode(WCHK,idx,a);
  }

  static public RWcheckCode Read(int idx){
    return new RWcheckCode(RCHK,idx,(XobjArgs)null);
  }

  static public RWcheckCode Write(int idx){
    return new RWcheckCode(WCHK,idx,(XobjArgs)null);
  }

  static public RWcheckCode Share(int idx, XobjArgs a){
    return new RWcheckCode(SHARE,idx,a);
  }

  static public RWcheckCode Share(int idx, Xobject x){
    return  new RWcheckCode(SHARE,idx,x);
  }

  static public RWcheckCode Flush(int idx){
    return  new RWcheckCode(FLUSH,idx,(XobjArgs)null);
  }

  static public RWcheckCode FlushR(int idx){
    return  new RWcheckCode(FLUSHR,idx,(XobjArgs)null);
  }

  /*
   *
   */
  static Vector shared_objs;
  static Ident shared_table_id;
  
  static public void initTransCode(optBodyEnv optEnv, Vector objs){
    if(objs == null || objs.isEmpty()) return;

    shared_objs = objs;
    /* declare __ompd__[n] */
    shared_table_id = Ident.Local(shared_table_name,
				  Xtype.Array(Xtype.Pointer(Xtype.voidType),
					      shared_objs.size()));
    optEnv.getBody().addIdent(shared_table_id);

    XobjectFile env = optEnv.getFile();
    if(env.getProp(propInstrumented) == null){
      env.setProp(propInstrumented,propInstrumented);
      declareRuntimeFunc(env);
    }
  }
       
  /*
   * return runtime function calls
   */
  public Xobject transCode(){
    int code = Opcode();
    if(code == RCHK) return ReadCode();
    if(code == WCHK) return WriteCode();
    if(code == FLUSH) 
      return FLUSH_f.Call(Xcons.List(shared_table_id.Index(index)));
    if(code == FLUSHR) 
      return FLUSHR_f.Call(Xcons.List(shared_table_id.Index(index)));
    if(code == SHARE) return AllocDescCode();
    return null;
  }

  /* allocate descriptor:
   *  for variable,
   *    _ompd_D_v(desc_addr,var_addr,size)
   *  for array,
   *    _ompd_D_a(desc_addr,array_addr,size,elem_size,ndim,d1,d2,d3,d4)
   */
  Xobject AllocDescCode(){
    Xobject p = getArg(0);
    Ident id = (Ident)getArg(1);
    if(id == null) return null; /* this is a dummy */

    Xobject args = Xcons.List(Xcons.AddrOf(shared_table_id.Index(index)),
			      p,Xcons.IntConstant(id.Type().getSize()));

    Xtype t = id.Type();
    if(t.isArray()){
      args.add(Xcons.IntConstant(t.getArrayElementType().getSize()));
      args.add(Xcons.IntConstant(t.getNumDimensions()));
      addDimArg(args,t);
      return alloc_array_desc_f.Call(args);
    } else {
      return alloc_var_desc_f.Call(args);
    }
  }

  /* put dimensions in reverse order */
  void addDimArg(Xobject args,Xtype t){
    if(t.isArray()){
      addDimArg(args,t.getRef());
      args.add(Xcons.IntConstant(t.getArrayDim()));
    }
  }

  /* 
   * if index  < 0, access by pointer
   *   _ompd_R/W_p(context,address,size)
   * if null arg, access variable
   *   _ompd_R/W_v(context,desc)
   * otherwise, access array
   *   _ompd_R/W_a(context)
   */
  Xobject ReadCode(){
    Xobject ctx = Xcons.IntConstant(context);
    if(index < 0){	/* read any */
      Xobject p = getArg(0);  // first argument
      return RCHK_ptr_f.
	Call(Xcons.List(ctx,p,Xcons.IntConstant(p.Type().getRef().getSize())));
    }
    if(getArgs() == null){	/* scalar variable */
      return RCHK_var_f.Call(Xcons.List(ctx,shared_table_id.Index(index)));
    }
    /* otherwise, array */
    Xobject e = rangeSetCode(getArgs());
    e.add(RCHK_array_f.Call(Xcons.List(ctx,shared_table_id.Index(index))));
    return e;
  }

  Xobject WriteCode(){
    Xobject ctx = Xcons.IntConstant(context);
    if(index < 0){	/* read any */
      Xobject p = getArg(0);  // first argument
      return WCHK_ptr_f.
	Call(Xcons.List(ctx,p,Xcons.IntConstant(p.Type().getRef().getSize())));
    }
    if(getArgs() == null){	/* scalar variable */
      return WCHK_var_f.Call(Xcons.List(ctx,shared_table_id.Index(index)));
    }
    /* otherwise, array */
    Xobject e = rangeSetCode(getArgs());
    e.add(WCHK_array_f.Call(Xcons.List(ctx,shared_table_id.Index(index))));
    return e;
  }
  
  /*
   * embed rangeExpr structure 
   */
  Xobject rangeSetCode(XobjArgs l){
    Xobject x,args;
    Xobject e = Xcons.List(Xcode.COMMA_EXPR);
    for( ; l != null; l = l.nextArgs()){
      int i = 0;
      for(XobjArgs a = l.getArg().getArgs(); a != null; a = a.nextArgs(),i++){
	x = a.getArg();
	if(x instanceof rangeExpr){
	  rangeExpr r = (rangeExpr)x;
	  if(r.Opcode() == rangeExpr.FOR){
	    args = Xcons.List(Xcons.IntConstant(i),
			      r.getLowerBound(),r.getUpperBound(),
			      r.getStep(),r.getIncFlagValue());
	    e.add(for_range_f.Call(args));
	  } else if(r.Opcode() == rangeExpr.FORALL){
	    args = Xcons.List(Xcons.IntConstant(i),
			      r.getLowerBound(),r.getUpperBound(),
			      r.getStep(),r.getIncFlagValue());
	    // args.add(r.getScale()); // optional
	    // args.add(r.getOffset()); // optional
	    args.add(Xcons.IntConstant(r.getSched()));
	    if(r.getChunk() != null) args.add(r.getChunk());
	    else args.add(Xcons.IntConstant(0));
	    e.add(forall_range_f.Call(args));
	  } else 
	    fatal("unknown rangeExpr code ="+r.Opcode());
	  if(r.getExpr() != null) addRangeExprCode(e,r.getExpr());
	  continue;
	}
	e.add(set_index_f.Call(Xcons.List(Xcons.IntConstant(i),x)));
      }
      e.add(set_range_f.Call(Xcons.List(Xcons.IntConstant(i))));
    }
    return e;
  }

  void addRangeExprCode(Xobject l,Xobject e){
    Xobject addr,idx;
    if(e.Opcode() == rangeExpr.FOR){
      rangeExpr r = (rangeExpr)e;
      Xobject args = Xcons.emptyList();
      e = r.getLowerBound();
      args.add(rangeExpr.exprScale(e));
      args.add(rangeExpr.exprOffset(e));
      addr = rangeExpr.exprIndirectAddr(e);
      idx = (Xobject)addr.getProp(idxProp);
      if(idx == null) idx = Xcons.IntConstant(0);
      else idx = shared_table_id.Index(idx);
      args.add(addr);
      args.add(idx);
      args.add(rangeExpr.exprIndirectOffset(e));
      e = r.getUpperBound();
      args.add(rangeExpr.exprScale(e));
      args.add(rangeExpr.exprOffset(e));
      addr = rangeExpr.exprIndirectAddr(e);
      idx = (Xobject)addr.getProp(idxProp);
      if(idx == null) idx = Xcons.IntConstant(0);
      else idx = shared_table_id.Index(idx);
      args.add(addr);
      args.add(idx);
      args.add(rangeExpr.exprIndirectOffset(e));
      args.add(r.getStep());
      args.add(r.getIncFlagValue());
      l.add(add_for_f.Call(args));
      if(r.getExpr() != null) addRangeExprCode(l,r.getExpr());
    } else {
      addr = rangeExpr.exprIndirectAddr(e);
      idx = (Xobject)addr.getProp(idxProp);
      if(idx == null) idx = Xcons.IntConstant(0);
      else idx = shared_table_id.Index(idx);
      Xobject args = Xcons.List(rangeExpr.exprScale(e),
				rangeExpr.exprOffset(e),
				addr,idx,
				rangeExpr.exprIndirectOffset(e));
      l.add(add_expr_f.Call(args));
    }
  }

  public String OpcodeName(){
    String name;
    if(Opcode() == RCHK){
      name = "RCHK<"+index+">";
    } else if(Opcode() == WCHK){
      name = "WCHK<"+index+">";
    } else if(Opcode() == SHARE){
      name = "SHARE<"+index+">";
    } else if(Opcode() == FLUSH){
      name = "FLUSH<"+index+">";
    } else if(Opcode() == FLUSHR){
      name = "FLUSHR<"+index+">";
    } else {
      name = "RWcheckCode(code=" + Opcode()+")";
    }
    return name;
  }

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