// $Id: OMPanalyzePragma.java,v 1.4 1999/11/09 07:16:11 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.openmp;

import exc.object.*;
import exc.block.*;

//
// process analyze OpenMP pragma
// pass1:
// check directive and allocate descriptor
public class OMPanalyzePragma {
  FuncDefBlock def;
  OMPfileEnv omp_env;

  public void run(FuncDefBlock def,OMPfileEnv omp_env){
    this.def = def;
    this.omp_env = omp_env;
    Block b;
    Block fblock = def.getBlock();

    // pass1: traverse to collect information about OMP pramga
    if(OMP.debugFlag) System.out.println("pass1:");
    b = fblock.getBody().getHead();
    b.setProp(OMP.prop,new OMPinfo(OMP.FUNCTION_BODY,null,b,omp_env));

    BlockIterator i = new topdownBlockIterator(fblock);
    for(i.init(); !i.end(); i.next()){
      b = i.getBlock();
      if(OMP.debugFlag) System.out.println("pass1="+b);
      if(b.Opcode() == Xcode.OMP_PRAGMA) checkPragma((PragmaBlock)b);
    }
  }

  OMPinfo outerOMPinfo(Block b){
    for(Block bp = b.getParentBlock();
	bp != null; bp = bp.getParentBlock()){
      if(bp.Opcode() == Xcode.OMP_PRAGMA){
	return (OMPinfo) bp.getProp(OMP.prop);
      }
    }
    return null;
  }

  void checkPragma(PragmaBlock pb){
    OMPinfo outer = outerOMPinfo(pb);
    OMPinfo info = new OMPinfo(pb.getPragma(),outer,pb,omp_env);
    pb.setProp(OMP.prop,info);

    int c,p;
    Xobject t;

    switch(p = info.pragma){
    case OMP.PARALLEL: 		/* new parallel section */
    case OMP.FOR:      		/* loop <clause_list> */
    case OMP.SECTIONS:      	/* sections <clause_list> */
      // Assume that the kinds of the cluases are already checked
      for(XobjArgs a = pb.getClauses().getArgs();
	  a != null; a = a.nextArgs()){
	t = a.getArg();
	c = t.getArg(0).getInt();
	switch(c){
	case OMP.DIR_IF:
	  info.setIfExpr(t.getArg(1));
	  break;
	case OMP.DIR_NOWAIT:
	  info.no_wait = true;
	  break;
	case OMP.DIR_ORDERED:
	  info.ordered = true;
	  break;
	case OMP.DIR_SCHEDULE:
	  info.setSchedule(t.getArg(1));
	  break;
	case OMP.DATA_DEFAULT:
	  info.data_default = t.getArg(1).getInt();
	  break;
	default:	// DATA_*
	  for(XobjArgs aa = t.getArg(1).getArgs();
	      aa != null; aa = aa.nextArgs()){
	    info.declOMPvar(aa.getArg().getName(),c);
	  }
	}
      }
      if(p == OMP.FOR){
	if(outer!= null && outer.pragma != OMP.PARALLEL){
	  OMP.error(pb.getLineNo(),"'FOR/DO' directive is nested");
	  return;
	}
	/* check indiction variable */
	if(pb.getBody().getHead().Opcode() != Xcode.FOR_STATEMENT){
	  OMP.error(pb.getLineNo(),
		    "FOR loop must follows FOR/DO directive");
	  return;
	}
	ForBlock for_block = (ForBlock)pb.getBody().getHead();
	for_block.Canonicalize();
	if(!for_block.isCanonical()){
	  OMP.error(pb.getLineNo(),"not cannonical FOR/DO loop");
	  return;
	}
	Xobject ind_var = for_block.getInductionVar();
	if(!info.isPrivateOMPvar(ind_var.getName())){
	  OMPvar v = info.findOMPvar(ind_var.getName());
	  if(v == null){
	    info.declOMPvar(ind_var.getName(),OMP.DATA_PRIVATE);
	  } else if(!v.is_last_private && v.is_shared){
	    /* loop variable may be lastprivate */
	    OMP.error(pb.getLineNo(),"FOR/DO loop variable '"+v.id.getName()+
		      "' is declared as shared");
	    return;
	  } 
	}
      }

      if(p == OMP.SECTIONS &&
	 outer!= null && outer.pragma != OMP.PARALLEL)
	OMP.error(pb.getLineNo(),"'sections' directive is nested");
      break;

    case OMP.SINGLE:          /* single <clause list> */
      for(XobjArgs a = pb.getClauses().getArgs();
	  a != null; a = a.nextArgs()){
	t = a.getArg();
	c = t.getArg(0).getInt();
	switch(c){
	case OMP.DIR_NOWAIT:
	  info.no_wait = true;
	  break;
	default:	// DATA_*
	  for(XobjArgs aa = t.getArg(1).getArgs();
	      aa != null; aa = aa.nextArgs())
	    info.declOMPvar(aa.getArg().getName(),c);
	}
      }
      if(outer!= null && outer.pragma != OMP.PARALLEL)
	OMP.error(pb.getLineNo(),"'single' directive is nested");
      break;

    case OMP.MASTER:           /* master */
      if(info.findContext(OMP.FOR) != null)
	OMP.error(pb.getLineNo(),
		  "'master' not permitted in the extent of 'for'");
      if(info.findContext(OMP.SECTIONS) != null)
	OMP.error(pb.getLineNo(),
		  "'master' not permitted in the extent of 'sections'");
      if(info.findContext(OMP.SINGLE) != null)
	OMP.error(pb.getLineNo(),
		  "'master' not permitted in the extent of 'single'");
      break;

    case OMP.CRITICAL:         /* critical <name> */
      t = pb.getClauses();
      if(t != null) info.arg = t.getArg(0);
      else info.arg = null;
      if(info.findContext(OMP.CRITICAL,info.arg) != null)
	OMP.error(pb.getLineNo(),
		  "'critical' with the same name is nested");
      break;

    case OMP.BARRIER:         /* barrier */
      if(outer != null && outer.pragma != OMP.PARALLEL)
	OMP.error(pb.getLineNo(),"'barrier' in bad context");
      break;

    case OMP.FLUSH:           /* flush <namelist> */
      info.setFlushVars(pb.getClauses());
      break;

    case OMP.ATOMIC:          /* atomic */
      if(pb.getBody().isSingle() &&
	 pb.getBody().getHead().Opcode() == Xcode.LIST){
	BasicBlock bb = pb.getBody().getHead().getBasicBlock();
	if(bb.isSingle()){
	  Xobject x = bb.getHead().getExpr();
	  if(isAtomicExpr(x)) break;
	  if(x.isSet()){
	    if((x = makeAtomicExpr(x)) != null){
	      bb.getHead().setExpr(x);
	      break;
	    }
	  }
	}
      }
      OMP.error(pb.getLineNo(),"bad expression in 'atomic' directive");
      break;

    case OMP.ORDERED:         /* ordered */
      if(info.findContext(OMP.CRITICAL) != null)
	OMP.error(pb.getLineNo(),
		  "'ordered' not permitted in the extent of 'critical'");
      break;
    case OMP.THREADPRIVATE: // for FORTRAN
      omp_env.declThreadPrivate(pb.getClauses());
      break;
    default:
      OMP.fatal("unknown OpenMP pramga = "+p);
      break;
    }
  }

  boolean isAtomicExpr(Xobject x){
    switch(x.Opcode()){
    case Xcode.POST_INCR_EXPR:
    case Xcode.POST_DECR_EXPR:
    case Xcode.ASG_PLUS_EXPR:
    case Xcode.ASG_MUL_EXPR:
    case Xcode.ASG_MINUS_EXPR:
    case Xcode.ASG_DIV_EXPR:
    case Xcode.ASG_BIT_AND_EXPR:
    case Xcode.ASG_BIT_OR_EXPR:
    case Xcode.ASG_BIT_XOR_EXPR:
    case Xcode.ASG_LSHIFT_EXPR:
    case Xcode.ASG_RSHIFT_EXPR:
      return true;
    }
    return false;
  }

  Xobject makeAtomicExpr(Xobject x){
    Xobject lv = x.left();
    Xobject rv = x.right();
    switch(rv.Opcode()){
    case Xcode.PLUS_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_PLUS_EXPR,lv,rv.right());
      if(lv.equals(rv.right()))
	return Xcons.asgOp(Xcode.ASG_PLUS_EXPR,lv,rv.left());
      break;
    case Xcode.MUL_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_MUL_EXPR,lv,rv.right());
      if(lv.equals(rv.right()))
	return Xcons.asgOp(Xcode.ASG_MUL_EXPR,lv,rv.left());
      break;
    case Xcode.MINUS_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_MINUS_EXPR,lv,rv.right());
      break;
    case Xcode.DIV_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_DIV_EXPR,lv,rv.right());
      break;
    case Xcode.BIT_AND_EXPR:
    case Xcode.LOG_AND_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_BIT_AND_EXPR,lv,rv.right());
      if(lv.equals(rv.right()))
	return Xcons.asgOp(Xcode.ASG_BIT_AND_EXPR,lv,rv.left());
      break;
    case Xcode.BIT_OR_EXPR:
    case Xcode.LOG_OR_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_BIT_OR_EXPR,lv,rv.right());
      if(lv.equals(rv.right()))
	return Xcons.asgOp(Xcode.ASG_BIT_OR_EXPR,lv,rv.left());
      break;
    case Xcode.BIT_XOR_EXPR:
      if(lv.equals(rv.left()))
	return Xcons.asgOp(Xcode.ASG_BIT_XOR_EXPR,lv,rv.right());
      if(lv.equals(rv.right()))
	return Xcons.asgOp(Xcode.ASG_BIT_XOR_EXPR,lv,rv.left());
      break;
    }
    return null;
  }
}


