// $Id: OMPtransPragma.java,v 1.31 2002/02/27 16:05:04 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 java.io.*;
import exc.object.*;
import exc.block.*;

import java.util.*;

public class OMPtransPragma {
  protected XobjectDef current_def;
  protected XobjectFile env;

  public String barrierFunc;
  public String doParallelFunc;
  public String doParallelIfFunc;

  public String defaultShedFunc;
  public String blockShedFunc;
  public String cyclicShedFunc;
  public String staticShedInitFunc;
  public String staticShedNextFunc;
  public String dynamicShedInitFunc;
  public String dynamicShedNextFunc;
  public String guidedShedInitFunc;
  public String guidedShedNextFunc;
  public String runtimeShedInitFunc;
  public String runtimeShedNextFunc;
  public String affinityShedInitFunc;
  public String affinityShedNextFunc;

  public String orderedInitFunc;
  public String orderedSetLoopIdFunc;
  public String orderedBeginFunc;
  public String orderedEndFunc;

  public String threadIdFunc;
  public String sectionInitFunc;
  public String sectionIdFunc;
  public String doSingleFunc;
  public String enterCriticalFunc;
  public String exitCriticalFunc;
  public String isMasterFunc;
  public String atomicLockFunc;
  public String atomicUnlockFunc;
  public String bcopyFunc;
  public String isLastFunc;

  public String flushFunc;
  public String getThdprvFunc;
  public String copyinThdprvFunc;

  public String allocShared;
  public String freeShared;

  public String allocaFunc;

  public String doReduction;
  public String doReductionInit;

  public String OMPfuncPrefix;
  public String criticalLockPrefix;
  public String reductionLockPrefix;


  public OMPtransPragma(){
    OMPfuncPrefix = "__ompc_func";
    barrierFunc = "_ompc_barrier";
    doParallelFunc = "_ompc_do_parallel";
    doParallelIfFunc = "_ompc_do_parallel_if";

    defaultShedFunc = "_ompc_default_sched";
    blockShedFunc = "_ompc_static_bsched";
    cyclicShedFunc = "_ompc_static_csched";
    staticShedInitFunc = "_ompc_static_sched_init";
    staticShedNextFunc = "_ompc_static_sched_next";
    dynamicShedInitFunc = "_ompc_dynamic_sched_init";
    dynamicShedNextFunc = "_ompc_dynamic_sched_next";
    guidedShedInitFunc = "_ompc_guided_sched_init";
    guidedShedNextFunc = "_ompc_guided_sched_next";
    runtimeShedInitFunc = "_ompc_runtime_sched_init";
    runtimeShedNextFunc = "_ompc_runtime_sched_next";
    affinityShedInitFunc = "_ompc_affinity_sched_init";
    affinityShedNextFunc = "_ompc_affinity_sched_next";
    
    orderedInitFunc = "_ompc_init_ordered";
    orderedSetLoopIdFunc = "_ompc_set_loop_id";
    orderedBeginFunc = "_ompc_ordered_begin";
    orderedEndFunc = "_ompc_ordered_end";
    
    threadIdFunc = "_ompc_thread_id";
    sectionInitFunc = "_ompc_section_init";
    sectionIdFunc = "_ompc_section_id";
    doSingleFunc = "_ompc_do_single";
    enterCriticalFunc = "_ompc_enter_critical";
    exitCriticalFunc = "_ompc_exit_critical";
    isMasterFunc = "_ompc_is_master";
    atomicLockFunc = "_ompc_atomic_lock";
    atomicUnlockFunc = "_ompc_atomic_unlock";
    bcopyFunc = "_ompc_bcopy";
    isLastFunc = "_ompc_is_last";
    
    flushFunc = "_ompc_flush";
    getThdprvFunc = "_ompc_get_thdprv";	// pointer
    copyinThdprvFunc = "_ompc_copyin_thdprv";
    
    allocShared = "_ompc_alloc_shared"; // pointer
    freeShared = "_ompc_free_shared";

    allocaFunc = "alloca";

    doReduction = "_ompc_reduction";
    doReductionInit = "_ompc_reduction_init";

    criticalLockPrefix = "__ompc_lock_critical";
    reductionLockPrefix = "__ompc_lock_reduction";
  }

  // pass3: do transformation for OMP pragma
  public void run(FuncDefBlock def){
    Block b;
    Block fblock = def.getBlock();
    current_def = def.getDef();
    env = current_def.getFile();

    if(OMP.debugFlag) System.out.println("pass3:");
    BlockIterator i = new bottomupBlockIterator(fblock);
    for(i.init(); !i.end(); i.next()){
      b = i.getBlock();
      if(b.Opcode() == Xcode.OMP_PRAGMA){
	b = transPragma((PragmaBlock)b);
	if(b != null) i.setBlock(b);
      } 
    }
    transFunctionBody((CompoundBlock)fblock.getBody().getHead());
  }

  public Ident OMPfuncIdent(String name){
    Xtype t;
    if(name == getThdprvFunc || name == allocShared)
	t = Xtype.Function(Xtype.Pointer(Xtype.voidType));
    else t = Xtype.Function(Xtype.intType);
    return env.declExternIdent(name,t);
  }

  // pass3:
  // write pragma
  public Block transPragma(PragmaBlock pb,XobjectDef current_def){
    this.current_def = current_def;
    env = current_def.getFile();
    return transPragma(pb);
  }
  
  Block transPragma(PragmaBlock pb){
    OMPinfo i = (OMPinfo)pb.getProp(OMP.prop);

    switch(i.pragma){
    case OMP.BARRIER:         /* barrier */
      return transBarrier(pb,i);
	    
    case OMP.PARALLEL: 	/* new parallel section */
      return transParallelRegion(pb,i);
	    
    case OMP.FOR:      	/* for <clause_list> */
      return transFor(pb,i);
	    
    case OMP.SECTIONS:      /* sections <clause_list> */
      return transSections(pb,i);

    case OMP.SINGLE:
      return transSingle(pb,i);
	    
    case OMP.MASTER:        /* master */
      return transMaster(pb,i);
	    
    case OMP.CRITICAL:      /* critical <name> */
      return transCritical(pb,i);

    case OMP.ATOMIC:
      return transAtomic(pb,i);

    case OMP.FLUSH:
      return transFlush(pb,i);

    case OMP.ORDERED:
      return transOrdered(pb,i);

    default:
      // OMP.fatal("unknown pragma");
      // ignore it
      return null;
    }
  }

  public Block transBarrier(PragmaBlock b,OMPinfo i){
    return Bcons.Statement(OMPfuncIdent(barrierFunc).Call(null));
  }

  public Block transParallelRegion(PragmaBlock b, OMPinfo i){
    BasicBlock bblock;
    Ident func_id = 
      env.declStaticIdent(env.genSym(OMPfuncPrefix),
			  Xtype.Function(Xtype.voidType));
    Xobject param_id_list = Xcons.emptyIdList();
    Xobject s;
    Block bp;

    // move blocks to body
    BlockList body = Bcons.emptyBody(i.id_list,null);
    body.add(Bcons.COMPOUND(b.getBody()));

    //  new block to be replaced
    BlockList ret_body = Bcons.emptyBody();
	
    if(i.region_args.size() != 0){
      bblock = new BasicBlock();
      // caller side, create local reference structure
      Xtype voidP_t = Xtype.Pointer(Xtype.voidType);
      Ident av = 
	Ident.Local("__ompc_argv",
		    Xtype.Array(voidP_t,i.region_args.size()));
      ret_body.addIdent(av);
      for(int ii = 0; ii < i.region_args.size(); ii++){
	s = (Xobject)i.region_args.elementAt(ii);
	s = Xcons.Set(av.Index(ii),Xcons.Cast(voidP_t,s));
	bblock.add(s);
      }
      if(i.getIfExpr() != null)
	bblock.add(OMPfuncIdent(doParallelIfFunc).
		   Call(Xcons.List(i.if_expr,func_id.Ref(),av.Ref())));
      else
	bblock.add(OMPfuncIdent(doParallelFunc).
		   Call(Xcons.List(func_id.Ref(),av.Ref())));
      ret_body.add(bblock);
    } else {
      if(i.getIfExpr() != null)
	ret_body.add(Bcons.Statement(OMPfuncIdent(doParallelIfFunc).
				     Call(Xcons.List(i.if_expr,
						     func_id.Ref()))));
      else 
	ret_body.add(Bcons.Statement(OMPfuncIdent(doParallelFunc).
				     Call(Xcons.List(func_id.Ref()))));
    }

    bp = dataSetupBlock(i);
    if(bp != null) body.insert(bp);
    if(i.region_params.size() != 0){
      bblock = new BasicBlock();
      Xtype voidP_t = Xtype.Pointer(Xtype.voidType);
      Ident pv = Ident.Param("__ompc_args",Xtype.Pointer(voidP_t));
      param_id_list.add(pv);
      for(int ii = 0; ii < i.region_params.size(); ii++){
	s = ((Ident)i.region_params.elementAt(ii)).Ref();
	s = Xcons.Set(s,Xcons.Cast(s.Type(),pv.Index(ii)));
	bblock.add(s);
      }
      body.insert(bblock);
    } 
    bp = dataUpdateBlock(i);
    if(bp != null) body.add(bp);
    threadprivateSetup(body,i);
	
    // make prototype for func_id
    ((FunctionType)func_id.Type()).setFuncParamIdList(param_id_list);

    current_def.insert(XobjectDef.Func(func_id.getName(),
				       param_id_list,null,
				       body.toXobject()));
    return Bcons.COMPOUND(ret_body);
  }

  public Block transFor(PragmaBlock b, OMPinfo i){
    ForBlock for_block = (ForBlock)b.getBody().getHead();
    for_block.Canonicalize();  // canonicalize again
    Block bp;

    if(!for_block.isCanonical())  OMP.fatal("transFor: not canonical");

    Xobject ind_var = for_block.getInductionVar();
    BlockList ret_body = Bcons.emptyBody(i.id_list,null);

    bp = dataSetupBlock(i);
    if(bp != null) ret_body.add(bp);

    Ident lb_var = 
      Ident.Local(env.genSym(ind_var.getName()),ind_var.Type());
    Ident ub_var = 
      Ident.Local(env.genSym(ind_var.getName()),ind_var.Type());
    Ident step_var = 
      Ident.Local(env.genSym(ind_var.getName()),ind_var.Type());
    ret_body.addIdent(lb_var);
    ret_body.addIdent(ub_var);
    ret_body.addIdent(step_var);

    BasicBlock bb = new BasicBlock();
    ret_body.add(bb);
    for(StatementIterator si = for_block.getInitBBlock().statements();
	si.hasMoreStatement(); ){
      Statement s = si.nextStatement();
      if(s.getNext() == null) break;
      s.remove();
      bb.add(s);
    }
    bb.add(Xcons.Set(lb_var.Ref(),for_block.getLowerBound()));
    bb.add(Xcons.Set(ub_var.Ref(),for_block.getUpperBound()));
    bb.add(Xcons.Set(step_var.Ref(),for_block.getStep()));

    for_block.setLowerBound(lb_var.Ref());
    for_block.setUpperBound(ub_var.Ref());
    for_block.setStep(step_var.Ref());

    if(i.ordered) {
      bb.add(OMPfuncIdent(orderedInitFunc).
		    Call(Xcons.List(lb_var.Ref(),step_var.Ref())));
      for_block.getBody().insert(OMPfuncIdent(orderedSetLoopIdFunc).
				 Call(Xcons.List(ind_var)));
    }

    switch(i.sched){
    case OMP.SCHED_NONE:
      bb.add(OMPfuncIdent(defaultShedFunc).
		     Call(Xcons.List(lb_var.getAddr(),ub_var.getAddr(),
				     step_var.getAddr())));
      ret_body.add(for_block);
      break;
    case OMP.SCHED_STATIC:
      if(i.sched_chunk == null){
	/* same as SCHED_NONE */
	bb.add(OMPfuncIdent(blockShedFunc).
		      Call(Xcons.List(lb_var.getAddr(),ub_var.getAddr(),
				      step_var.getAddr())));
	ret_body.add(for_block);
	break;
      }
      if(i.sched_chunk.Opcode() == Xcode.INT_CONSTANT &&
	 i.sched_chunk.getInt() == 1){
	/* cyclic schedule */
	Ident step_var0 = 
	  Ident.Local(env.genSym(ind_var.getName()),ind_var.Type());
	ret_body.addIdent(step_var0);
	bb.add(Xcons.Set(step_var0.Ref(),step_var.Ref()));

	bb.add(OMPfuncIdent(cyclicShedFunc).
		      Call(Xcons.List(lb_var.getAddr(),ub_var.getAddr(),
				      step_var.getAddr())));
	ret_body.add(for_block);

	/* adjust last value of loop variable */
	Xobject adjust = 
	  Xcons.Set(ind_var,
		    Xcons.binaryOp(Xcode.PLUS_EXPR,
				   Xcons.binaryOp(Xcode.MINUS_EXPR,ind_var,
						  step_var.Ref()),
				   step_var0.Ref()));
	ret_body.add(Bcons.Statement(adjust));
	break;
      }

      /* otherwise, block scheduling */
      bb.add(OMPfuncIdent(staticShedInitFunc).
		    Call(Xcons.List(lb_var.Ref(),ub_var.Ref(),step_var.Ref(),
				    i.sched_chunk)));
      ret_body.add(Bcons.WHILE(OMPfuncIdent(staticShedNextFunc).
			       Call(Xcons.List(lb_var.getAddr(),
					       ub_var.getAddr())),
			       for_block));
      break;

    case OMP.SCHED_AFFINITY0:
      Xobject arg = Xcons.List(lb_var.Ref(),ub_var.Ref(),step_var.Ref());
      arg.add(i.sched_chunk.getArg(0));
      arg.add(i.sched_chunk.getArg(1));
      arg.add(i.sched_chunk.getArg(2));
      arg.add(i.sched_chunk.getArg(3));
      bb.add(OMPfuncIdent(affinityShedInitFunc).Call(arg));
      ret_body.add(Bcons.WHILE(OMPfuncIdent(affinityShedNextFunc).
			       Call(Xcons.List(lb_var.getAddr(),
					       ub_var.getAddr())),
			       for_block));
      break;
	      
    case OMP.SCHED_DYNAMIC:
      if(i.sched_chunk == null) i.sched_chunk = Xcons.IntConstant(1);
      bb.add(OMPfuncIdent(dynamicShedInitFunc).
	     Call(Xcons.List(lb_var.Ref(),ub_var.Ref(),step_var.Ref(),
			     i.sched_chunk)));
      ret_body.add(Bcons.WHILE(OMPfuncIdent(dynamicShedNextFunc).
			       Call(Xcons.List(lb_var.getAddr(),
					       ub_var.getAddr())),
			       for_block));
      break;
    case OMP.SCHED_GUIDED:
      if(i.sched_chunk == null) i.sched_chunk = Xcons.IntConstant(1);
      bb.add(OMPfuncIdent(guidedShedInitFunc).
	     Call(Xcons.List(lb_var.Ref(),ub_var.Ref(),step_var.Ref(),
			     i.sched_chunk)));
      ret_body.add(Bcons.WHILE(OMPfuncIdent(guidedShedNextFunc).
			       Call(Xcons.List(lb_var.getAddr(),
					       ub_var.getAddr())),
			       for_block));
      break;
    case OMP.SCHED_RUNTIME:
      bb.add(OMPfuncIdent(runtimeShedInitFunc).
	     Call(Xcons.List(lb_var.Ref(),ub_var.Ref(),step_var.Ref())));
      ret_body.add(Bcons.WHILE(OMPfuncIdent(runtimeShedNextFunc).
			       Call(Xcons.List(lb_var.getAddr(),
					       ub_var.getAddr())),
			       for_block));
      break;
    default:
      OMP.fatal("unknown schedule: "+i.sched);
    }

    // ret_body.add(Bcons.COMPOUND(b.getBody()));

    bp = dataUpdateBlock(i);
    if(bp != null) ret_body.add(bp);

    if(!i.no_wait)
      ret_body.add(Bcons.Statement(OMPfuncIdent(barrierFunc).Call(null)));

    return Bcons.COMPOUND(ret_body);
  }

  public Block transSections(PragmaBlock b, OMPinfo i){
    Block bp;
    BlockList ret_body = Bcons.emptyBody(i.id_list,null);
    Xobject label = Xcons.Symbol(Xcode.IDENT,env.genSym("L"));
    int n = 0;

    bp = dataSetupBlock(i);
    if(bp != null) ret_body.add(bp);

    n = 0;
    for(bp = b.getBody().getHead(); bp != null; bp = bp.getNext()) n++;

    BasicBlock bblock = new BasicBlock();
    bblock.add(OMPfuncIdent(sectionInitFunc).
	       Call(Xcons.List(Xcons.IntConstant(n))));
    ret_body.add(bblock);
    ret_body.add(Bcons.LABEL(label));

    BlockList body = Bcons.emptyBody();

    n = 0;
    while((bp = b.getBody().getHead()) != null){
      bp.remove();
      body.add(Bcons.CASE(Xcons.IntConstant(n++)));
      body.add(bp);
      body.add(Bcons.GOTO(label));
    }
    ret_body.add(Bcons.SWITCH(OMPfuncIdent(sectionIdFunc).Call(null),
			      Bcons.COMPOUND(body)));

    bp = dataUpdateBlock(i);
    if(bp != null) ret_body.add(bp);
	
    if(!i.no_wait)
      ret_body.add(Bcons.Statement(OMPfuncIdent(barrierFunc).Call(null)));
    return Bcons.COMPOUND(ret_body);
  }

  public Block transSingle(PragmaBlock b, OMPinfo i){
    BlockList body = Bcons.emptyBody(i.id_list,null);
    BlockList ret_body = Bcons.emptyBody();
    Block bp;

    bp = dataSetupBlock(i);
    if(bp != null) body.add(bp);
    body.add(Bcons.COMPOUND(b.getBody()));
    ret_body.add(Bcons.IF(BasicBlock.Cond(OMPfuncIdent(doSingleFunc).
					  Call(null)),body,null));
    if(!i.no_wait)
      ret_body.add(Bcons.Statement(OMPfuncIdent(barrierFunc).Call(null)));
    return Bcons.COMPOUND(ret_body);
  }

  public Block transMaster(PragmaBlock b, OMPinfo i){
    return Bcons.IF(BasicBlock.Cond(OMPfuncIdent(isMasterFunc).Call(null)),
		    b.getBody(),null);
  }

  public Block transCritical(PragmaBlock b, OMPinfo i){
    String critical_lock_name = criticalLockPrefix;
    if(i.arg != null) critical_lock_name += "_"+i.arg.getName();
    Ident lock_id = env.declGlobalIdent(critical_lock_name,Xtype.Pointer(Xtype.voidType));
    BlockList body = b.getBody();
    body.insert(OMPfuncIdent(enterCriticalFunc).
		Call(Xcons.List(lock_id.getAddr())));
    body.add(OMPfuncIdent(exitCriticalFunc).
	     Call(Xcons.List(lock_id.getAddr())));
    return Bcons.COMPOUND(body);
  }
    
  public Block transAtomic(PragmaBlock b, OMPinfo i){
    BlockList body = b.getBody();
    BasicBlock bb = body.getHead().getBasicBlock();
    Statement s = bb.getHead();
    Xobject x = s.getExpr();
    if(x.Opcode() != Xcode.POST_INCR_EXPR &&
       x.Opcode() != Xcode.POST_DECR_EXPR){
      Xobject rv = x.right();
      Ident rv_var = Ident.Local(env.genSym("t"),rv.Type());
      body.addIdent(rv_var);
      s.insert(Xcons.Set(rv_var.Ref(),rv));
      x.setRight(rv_var.Ref());
    }
    Xobject lv = x.operand();
    Ident addr_var = Ident.Local(env.genSym("t"),Xtype.Pointer(lv.Type()));
    body.addIdent(addr_var);
    s.insert(Xcons.Set(addr_var.Ref(),Xcons.AddrOf(lv)));
    x.setOperand(Xcons.PointerRef(addr_var.Ref()));
    s.insert(OMPfuncIdent(atomicLockFunc).Call(null));
    s.add(OMPfuncIdent(atomicUnlockFunc).Call(null));
    return Bcons.COMPOUND(body);
  }

  Block transFlush(PragmaBlock b, OMPinfo i){
    if(i.flush_vars == null){
      return Bcons.Statement(OMPfuncIdent(flushFunc).
			     Call(Xcons.List(Xcons.IntConstant(0),
					     Xcons.IntConstant(0))));
    } else {
      BasicBlock bb = new BasicBlock();
      for(Enumeration e = i.flush_vars.elements(); e.hasMoreElements();){
	Xobject v = (Xobject)e.nextElement();
	bb.add(OMPfuncIdent(flushFunc).Call(v));
      }
      return Bcons.BasicBlock(bb);
    }
  }

  public Block transOrdered(PragmaBlock b, OMPinfo i){
    BlockList body = b.getBody();
    body.insert(OMPfuncIdent(orderedBeginFunc).Call(null));
    body.add(OMPfuncIdent(orderedEndFunc).Call(null));
    return Bcons.COMPOUND(body);
  }

  protected Block dataSetupBlock(OMPinfo i){
    boolean flag = false;
    for(OMPvar v = i.varlist; v != null; v = v.next){
      if(v.is_first_private || v.is_reduction || 
	 v.is_copyin || v.adj_array_size != null){
	flag = true;
	break;
      }
    }
    if(!flag) return null;
	    
    BasicBlock bb = new BasicBlock();

    // process first private first */
    for(OMPvar v = i.varlist; v != null; v = v.next){
      if(v.adj_array_size != null && v.private_addr != null) continue;
      if(v.is_first_private){
	if(v.id.Type().isArray()){
	  bb.add(OMPfuncIdent(bcopyFunc).
		 Call(Xcons.List(v.private_addr,v.shared_addr,v.getSize())));
	} else {
	  bb.add(Xcons.Set(Xcons.PointerRef(v.private_addr),
			   Xcons.PointerRef(v.shared_addr)));
	}
      }
    }

    for(OMPvar v = i.varlist; v != null; v = v.next){

      if(v.adj_array_size != null && v.private_addr != null){  
	// allocate adjustable local array
	Ident alloca_f = OMPfuncIdent(allocaFunc);
	alloca_f.Declared();	// make already declared 
	bb.add(Xcons.Set(v.private_addr,
			 Xcons.Cast(v.private_addr.Type(),
				    alloca_f.Call(Xcons.List(v.adj_array_size)))));
      }

      if(v.is_first_private) continue;

      if(v.is_reduction){
	// only scalar variable and Fortran Complex
	if(Xtype.isFComplex(v.id.Type())){
	  int bt = Xtype.FComplexBasicType(v.id.Type());
	  bb.add(OMPfuncIdent(doReductionInit).
		 Call(Xcons.List(v.private_addr,
				 Xcons.IntConstant(bt),
				 Xcons.IntConstant(v.reduction_op))));
	} else 
	  bb.add(Xcons.Set(Xcons.PointerRef(v.private_addr),
			   v.reductionInitValue()));
      } else if(v.is_copyin){
	Ident thdprv_id = v.id;
	int idx = i.thdprv_id_list.indexOf(thdprv_id);
	if(idx < 0) OMP.fatal("bad copyin: "+thdprv_id.getName());
	Ident local_id = (Ident)(i.thdprv_local_id_list.elementAt(idx));
	bb.add(OMPfuncIdent(copyinThdprvFunc).
	       Call(Xcons.List(local_id.Ref(),thdprv_id.getAddr(),
			       Xcons.IntConstant(thdprv_id.Type().getSize()))));
      }
    }
    return Bcons.BasicBlock(bb);
  }

  public Block dataUpdateBlock(OMPinfo i){
    boolean reduction_flag = false;
    boolean lastprivate_flag = false;
    for(OMPvar v = i.varlist; v != null; v = v.next){
      if(v.is_last_private) lastprivate_flag = true;
      if(v.is_reduction) reduction_flag = true;
    }
    if(!lastprivate_flag && !reduction_flag) return null;

    BlockList body = Bcons.emptyBody();
    if(reduction_flag){
      BasicBlock bb = new BasicBlock();
      for(OMPvar v = i.varlist; v != null; v = v.next){
	if(!v.is_reduction) continue;
	int bt;
	if(v.id.Type().isEnum()) bt = BasicType.INT;
	else if(v.id.Type().isScalar()) bt = v.id.Type().getBasicType();
	else if(Xtype.isFComplex(v.id.Type()))
	  bt = Xtype.FComplexBasicType(v.id.Type());
	else {
	  OMP.fatal("bad reduction variable type");
	  continue;
	}
	bb.add(OMPfuncIdent(doReduction).
	       Call(Xcons.List(v.private_addr,v.shared_addr,
			       Xcons.IntConstant(bt),
			       Xcons.IntConstant(v.reduction_op))));
      }
      body.add(bb);
    } 

    if(lastprivate_flag){
      BasicBlock bb = new BasicBlock();
      for(OMPvar v = i.varlist; v != null; v = v.next){
	if(!v.is_last_private) continue;
	if(v.id.Type().isArray()){
	  bb.add(OMPfuncIdent(bcopyFunc).
		 Call(Xcons.List(v.shared_addr,v.private_addr,v.getSize())));
	} else {
	  bb.add(Xcons.Set(Xcons.PointerRef(v.shared_addr),
			   Xcons.PointerRef(v.private_addr)));
	}
      }
      body.add(Bcons.IF(OMPfuncIdent(isLastFunc).Call(null),
			Bcons.BasicBlock(bb),null));
    }
    return Bcons.COMPOUND(body);
  }

  public void transFunctionBody(CompoundBlock b,XobjectDef current_def){
    this.current_def = current_def;
    env = current_def.getFile();
    transFunctionBody(b);
  }

  public void transFunctionBody(CompoundBlock b){
    OMPinfo i;
    if((i = (OMPinfo)b.getProp(OMP.prop)) == null) return;
    BlockList body = b.getBody();
    threadprivateSetup(body,i);
  }

  public void threadprivateSetup(BlockList body,OMPinfo i){
    if(OMP.leaveThreadPrivateFlag) return;
    if(i.thdprv_local_id_list.isEmpty()) return;
    BasicBlock bb = new BasicBlock();
    Enumeration ee = i.thdprv_id_list.elements();
    for(Enumeration e = i.thdprv_local_id_list.elements();
	e.hasMoreElements(); ){
      Ident id = (Ident)e.nextElement();
      body.addIdent(id);
      Ident thdprv_id = (Ident)ee.nextElement();
      Xobject x = OMPfuncIdent(getThdprvFunc).
	Call(Xcons.List(Xcons.Symbol(Xcode.VAR_ADDR,
				     "_thdprv_"+thdprv_id.getName()),
			Xcons.IntConstant(thdprv_id.Type().getSize()),
			thdprv_id.getAddr()));
      bb.add(Xcons.Set(id.Ref(),Xcons.Cast(id.Type(),x)));
    }
    body.insert(bb);
  }
}


