// $Id: TEAanalyzeDecl.java,v 1.8 2001/03/21 07:57:24 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.tea;

import exc.object.*; 
import exc.block.*;
import exc.openmp.*;
import java.util.Vector;

public class TEAanalyzeDecl implements XobjectDefVisitor {
  protected XobjectFile env;
  protected Vector templates;

  Xobject localIdentList;

  public TEAanalyzeDecl(){ }

  public TEAanalyzeDecl(XobjectFile env){
    init(env);
  }

  public void init(XobjectFile env){
    this.env = env;
    this.templates = new Vector();
  }

  static LineNo current_ln;	/* for error message */
  void error(String msg){    OMP.error(current_ln,msg);  }

  //
  // XobjectDefVisitor interface
  //
  public void doDef(XobjectDef d){
    localIdentList = null;
    if(!d.isFuncDef()) 
      analyze(d.getDef());
    else { 
      int old_size = templates.size();

      // in case of Fortran, pragma appear in body.
      Xobject decls = d.getFuncBody().getArg(1);
      localIdentList = d.getFuncBody().getArg(0);
      if(decls == null) return;
      for(XobjArgs a = decls.getArgs(); a != null; a = a.nextArgs()){
	analyze(a.getArg());
      }

      // check affinity clause in OMP FOR pragma
      XobjectIterator i = new bottomupXobjectIterator(d.getFuncBody());
      for(i.init(); !i.end(); i.next()){
	Xobject x = i.getXobject();
	if(x != null && 
	   x.Opcode() == Xcode.OMP_PRAGMA && 
	   x.getArg(0).getInt() == OMP.FOR){
	  current_ln = x.getLineNo();	// for error msg
	  analyzeLoopSchedule(x.getArg(1));
	}
      }
      
      // remove template bounded for local Ident
      templates.setSize(old_size);
    }
  }

  public void analyze(Xobject x){
    if(x.Opcode() != Xcode.TEA_PRAGMA) return;

    // System.out.println("TEA_PRAGMA="+x);

    int pg = x.getArg(0).getInt();
    current_ln = x.getLineNo();	// for error msg

    switch(pg){
    case TEA.DATAMAP:
      Xobject l = x.getArg(1);
      if(l == null) break;
      Xobject a1 = l.getArg(0);
      Xobject a2 = l.getArg(1);
      if(a2 == null){	// no align
	for(XobjArgs a = a1.getArgs(); a != null; a = a.nextArgs()){
	  analyzeDataMapping(a.getArg());
	}
      } else {
	MapTemplate target = parseMapping(a2);	// align target
	MapTemplate tp;
	if(target == null) break;
	switch(target.getMode()){
	case TEA.MAP_NONE:
	case TEA.MAP_EXPR:
	  if((tp =findTemplate(target.getName())) == null){
	    error("target template '"+target.getName()+"' is not defined");
	    target = null;
	    break;
	  }
	  if(target.getMode() == TEA.MAP_NONE)
	    target = tp;
	  else {
	    if(tp.getDistDim() != target.getDistDim() ||
	       tp.getNumDimensions() != target.getNumDimensions()){
	      error("target template mismatch to mapping definition");
	      target = null;
	    }
	    target.setRef(tp);
	  }
	  break;
	case TEA.MAP_BLOCK:
	case TEA.MAP_CYCLIC:
	  defineTemplate(target);
	  break;
	}
	  
	if(target == null) break;
	for(XobjArgs a = a1.getArgs(); a != null; a = a.nextArgs()){
	  analyzeDataMapping(a.getArg(),target);
	}
      }
      break;
    default:
      OMP.error(x.getLineNo(),"omni/tea pragma '"+TEA.pragmaName(pg)+
		"' cannot appear at top level");
    }
  }
  
  void analyzeDataMapping(Xobject d){
    MapTemplate tp = parseMapping(d);
    if(tp == null) return;
    switch(tp.getMode()){
    case TEA.MAP_NONE:
      error("mapping on '"+tp.getName()+"', dimension is not specified");
      return;
    case TEA.MAP_EXPR:
      error("mapping on '"+tp.getName()+"', align target is missing");
      return;
    case TEA.MAP_BLOCK:
    case TEA.MAP_CYCLIC:
      defineTemplate(tp);
    }
  }

  // for alignment
  void analyzeDataMapping(Xobject d,MapTemplate target){
    MapTemplate tp = parseMapping(d);
    if(tp == null) return;

    switch(tp.getMode()){
    case TEA.MAP_NONE:	/* same as a[i] */
      if(target.getMode() == TEA.MAP_NONE) break; // error

      MapTemplate tpp = 
	MapTemplate.Expr(tp.getIdent(),tp.getDimInfo(),
			 target.getNumDimensions(),
			 target.getDistDim(),
			 target.getIndex(),
			 1,0);
      tpp.setRef(target);
      defineTemplate(tpp);
      return;

    case TEA.MAP_EXPR:
      if(target.getMode() != TEA.MAP_EXPR) break; // error
      if(tp.getScale() != 1 || tp.getOffset() != 0 ||
	 !tp.getIndex().equals(target.getIndex())){
	error("bad alignee index");
	break;
      }
      tp.setRef(target);
      defineTemplate(tp);
      return;

    case TEA.MAP_BLOCK:
    case TEA.MAP_CYCLIC:
      break;
    }
    error("illegal alignee form '"+tp.getName()+"'");
  }

  void analyzeLoopSchedule(Xobject clauses){
    if(clauses == null) return;

    Xobject c = null;
    MapTemplate target,tp;

    target = null;
    for(XobjArgs a = clauses.getArgs(); a != null; a = a.nextArgs()){
      c = a.getArg();
      if(c.getArg(0).getInt() == OMP.DIR_SCHEDULE &&
	 c.getArg(1) != null && 
	 c.getArg(1).getArg(0).getInt() == OMP.SCHED_AFFINITY){
	target = parseMapping(c.getArg(1).getArg(1));
	c.setArg(1,null);	// for error recovery
	break;
      }
    }

    if(target == null) return;	// error

    if(target.getMode() != TEA.MAP_NONE &&
       target.getMode() != TEA.MAP_EXPR){
      error("invalid loop affinity schedule target");
      return;
    }
    tp =findTemplate(target.getName());
    if(tp == null){
      error("target template '"+target.getName()+"' is not defined");
      return;
    }

    if(target.getMode() == TEA.MAP_NONE)
      target = tp;
    else {
      if(tp.getDistDim() != target.getDistDim() ||
	 tp.getNumDimensions() != target.getNumDimensions()){
	error("target template mismatch to mapping definition");
	return;
      }
      target.setRef(tp);
    }
    
    target.Reduce();
    if(target.getScale() != 1 && target.getScale() != 0){
      error("no scaling is supported in affinity scheduling, sorry");
    }

    if(target.getBlockSize() <= 0){
      error("unknown block size in mapping template");
    }

    int offset = target.getOffset()-target.getLowerBound();

    Xobject t = Xcons.List(Xcons.IntConstant(target.getMode()),
			   Xcons.IntConstant(target.getBlockSize()),
			   Xcons.IntConstant(target.getScale()),
			   Xcons.IntConstant(offset));

    c.setArg(1,Xcons.List(Xcons.IntConstant(OMP.SCHED_AFFINITY0),t));
  }

  void defineTemplate(MapTemplate tp){
    if(findTemplate(tp.getName()) != null){
      error("mapping on '"+tp.getName()+"', already defined");
      return;
    }
    templates.addElement(tp);

    Ident id = null;
    if(localIdentList != null){
      // find local ident if any
      for(XobjArgs a = localIdentList.getArgs(); a != null; a = a.nextArgs()){
	Ident idp = (Ident) a.getArg();
	if(tp.getName().equals(idp.getName())){
	  id = idp;
	  break;
	}
      } 
    }
    if(id == null) id = env.findIdent(tp.getName());
    if(id == null) return; // just template
    
    if(id.Type().getNumDimensions() != tp.getNumDimensions()){
      /* System.out.println("Type Dim="+id.Type().getNumDimensions()
	 +", tp Dim="+tp.getNumDimensions()); */
      error("mapping on '"+tp.getName()+"', number of dimensions mismatch");
      return;
    }

    MapTemplate prev_tp = (MapTemplate)(id.getProp(TEA.mapProp));
    if(prev_tp == null){
      tp.boundIdent(id);
      id.setProp(TEA.mapProp,tp);
    } else {
      error("mapping on identifier '"+id.getName()+"' is already defined");
    }

    /* for common */
    if(id.getStorageClass() == StorageClass.FCOMM){
      id = env.findFCommMemberIdent(id);
      prev_tp = (MapTemplate)(id.getProp(TEA.mapProp));
      if(prev_tp == null){
	tp.boundIdent(id);
	id.setProp(TEA.mapProp,tp);
      } else {
	error("mapping on common name '"+id.getName()+"' is already defined");
      }
    }
  }

  MapTemplate findTemplate(String name){
    for(int i = 0; i < templates.size(); i++){
      MapTemplate tp = (MapTemplate) templates.elementAt(i);
      if(tp.getName() == name) return tp;
    }
    return null;
  }
  

  MapTemplate parseMapping(Xobject map_expr){
    Xobject name,sub,d;
    int n_dim,dist_i,dist;
    Xobject dist_arg = null;
    Xobject dim_info = null;
    int dist_mode;

    name = map_expr.getArg(0);
    sub = map_expr.getArg(1);

    // fortran case
    if(map_expr.getArgs().nextArgs().nextArgs() != null)
      dim_info = map_expr.getArg(2);

    if(sub == null) return MapTemplate.None(name,dim_info);

    dist_mode = TEA.MAP_NONE;
    n_dim = 0;
    dist_i = 0;
    for(XobjArgs a = sub.getArgs(); a != null; a = a.nextArgs()){
      d = a.getArg();
      n_dim++;
      if(d != null){
	if(dist_mode != TEA.MAP_NONE){
	  error("multi-dimension mapping not supported");
	  return null;
	}
	dist_i = n_dim;
	dist_mode = d.getArg(0).getInt();
	dist_arg = d.getArg(1);
      }
    }

    if(dist_mode == TEA.MAP_NONE){
      error("unknown mapping");
      return null;
    }
    
    dist_i = n_dim - dist_i;
    // System.out.println("n_dim="+n_dim+",dist_i="+dist_i);
    
    switch(dist_mode){
    case TEA.MAP_BLOCK:
      if(dist_arg == null){
	return MapTemplate.Block(name,dim_info,n_dim,dist_i);
      }
      /* fall through */
    case TEA.MAP_CYCLIC:
      if(dist_arg == null)
	return MapTemplate.Cyclic(name,dim_info,n_dim,dist_i,1);
      if(dist_arg.Opcode() != Xcode.INT_CONSTANT){
	error("block/cyclic chunk size must be integer");
	return null;
      }
      return MapTemplate.Cyclic(name,dim_info,n_dim,dist_i,dist_arg.getInt());
    case TEA.MAP_EXPR:
      Xobject x;
      int scale = 1;
      int offset = 0;
      if(dist_arg.Opcode() == Xcode.PLUS_EXPR||
	 dist_arg.Opcode() == Xcode.MINUS_EXPR){
	x = dist_arg.right();
	if(x.Opcode() != Xcode.INT_CONSTANT){
	  error("mapping expression must be a*<index>+b");
	  return null;
	}
	offset = x.getInt();
	if(dist_arg.Opcode() == Xcode.MINUS_EXPR) offset = -offset;
	dist_arg = dist_arg.left();
      }
      if(dist_arg.Opcode() == Xcode.MUL_EXPR){
	x = dist_arg.left();
	if(x.Opcode() != Xcode.INT_CONSTANT){
	  error("mapping expression must be a*<index>+b");
	  return null;
	}
	scale = x.getInt();
	dist_arg = dist_arg.right();
      }
      if(dist_arg.Opcode() != Xcode.IDENT){
	error("mapping expression must be a*<index>+b");
	return null;
      }
      return MapTemplate.Expr(name,dim_info,n_dim,dist_i,dist_arg,
			      scale,offset);
    }
    
    OMP.fatal("unknown MAPPING");
    return null;
  }
}









