// $Id: ControlFlowGraph.java,v 1.9 2000/09/27 04:50: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.flow;

import exc.object.*;
import exc.block.*;
import exc.util.Set;
import java.util.*;
import exc.openmp.OMPBlock;

public class ControlFlowGraph {
  BasicBlock enterBBlock,exitBBlock;
  BlockList thisBody;
  BasicBlock top_sort_order,rev_sort_order;
  int num_of_bb;
    
  public ControlFlowGraph(BlockList body){
    thisBody = body;

    /* insert landing and exit pad */
    BlockIterator i = new topdownBlockIterator(body);
    for(i.init(); !i.end(); i.next()){
      Block b = i.getBlock();
      if(b.Opcode() == Xcode.WHILE_STATEMENT ||
	 b.Opcode() == Xcode.DO_STATEMENT){
	if(b.getPrev() == null || b.getPrev().Opcode() != Xcode.LIST)
	  b.insert(Bcons.emptyBlock());
	if(b.getNext() == null || b.getNext().Opcode() == Xcode.LIST)
	  b.add(Bcons.emptyBlock());
      }
    }

    buildCFG();
    buildTopSortOrder();
  }
    
  public BasicBlock getHead() { return top_sort_order; }
  public BasicBlock getTail() { return rev_sort_order; }

  public BasicBlock enterBasicBlock() { return enterBBlock; }
  public BasicBlock exitBasicBlock() { return exitBBlock; }

  public int numOfBasicBlock(){ return num_of_bb; }

  public void solveReverseUnionFlow(DataFlowSets d){
    BasicBlock bb;
    for(bb = top_sort_order; bb != null; bb = bb.topNext())
      d.initSet(bb);
    Set s = d.emptySet();
    boolean change = true;
    while(change){
      change = false;
      for(bb = rev_sort_order; bb != null; bb = bb.topPrev()){
	s.clear();
	for(Enumeration e = bb.getCflowOut().elements();
	    e.hasMoreElements(); )
	  s.add(d.InSet((BasicBlock)e.nextElement()));
	// System.out.println("("+bb.Id()+") in="+s+"out="+d.OutSet(bb));
	if(d.OutSet(bb).update(s)){
	  Set in_s = d.InSet(bb);
	  in_s.copy(d.OutSet(bb));
	  in_s.remove(d.KillSet(bb));
	  in_s.add(d.GenSet(bb));
	  change = true;
	}
      }
    }
  }

  public void solveForwardUnionFlow(DataFlowSets d){
    BasicBlock bb;
    for(bb = top_sort_order; bb != null; bb = bb.topNext())
      d.initSet(bb);
    Set s = d.emptySet();
    boolean change = true;
    while(change){
      change = false;
      for(bb = top_sort_order; bb != null; bb = bb.topNext()){
	s.clear();
	for(Enumeration e = bb.getCflowIn().elements();
	    e.hasMoreElements(); )
	  s.add(d.OutSet((BasicBlock)e.nextElement()));
	// System.out.println("In("+bb.Id()+") ="+s);
	if(d.InSet(bb).update(s)){
	  Set out_s = d.OutSet(bb);
	  out_s.copy(d.InSet(bb));
	  out_s.remove(d.KillSet(bb));
	  out_s.add(d.GenSet(bb));
	  change = true;
	}
      }
    }
  }

  public void solveForwardIntersectFlow(DataFlowSets d){
    BasicBlock bb;
    for(bb = top_sort_order; bb != null; bb = bb.topNext())
      d.initSet(bb);
    Set s = d.emptySet();
    boolean change = true;
    while(change){
      change = false;
      for(bb = top_sort_order; bb != null; bb = bb.topNext()){
	Enumeration e = bb.getCflowIn().elements();
	if(e.hasMoreElements()){
	  s.all();
	  while(e.hasMoreElements())
	    s.dot(d.OutSet((BasicBlock)e.nextElement()));
	} else s.clear();
	// System.out.println("["+bb.Id()+"] in="+s+",kill="+d.KillSet(bb));
	if(d.InSet(bb).update(s)){
	  Set out_s = d.OutSet(bb);
	  out_s.copy(d.InSet(bb));
	  out_s.remove(d.KillSet(bb));
	  out_s.add(d.GenSet(bb));
	  change = true;
	}
      }
    }
  }
    
  void buildCFG(){
    BasicBlock bb,bb_next;
    Block b,bp;
    BlockList body;
    BasicBlockIterator ii = new BasicBlockIterator(thisBody);
    Hashtable labelBlocks = new Hashtable();

    for(ii.init(); !ii.end(); ii.next()){
      bb = ii.getBasicBlock();
      b = bb.getParent();
      if(b.Opcode() == Xcode.STATEMENT_LABEL)
	labelBlocks.put(b.getLabel(),bb);
    }

    enterBBlock = firstBBlock(thisBody);
    exitBBlock = new BasicBlock();
    thisBody.add(exitBBlock);

    for(ii.init(); !ii.end(); ii.next()){
      bb = ii.getBasicBlock();
      b = bb.getParent();
      switch(b.Opcode()){
      case Xcode.IF_STATEMENT:
	if((body = b.getThenBody()) != null)
	  bb_next = firstBBlock(body);
	else bb_next = nextBBlock(b);
	bb.addCflowTo(bb_next);
	if((body = b.getElseBody()) != null)
	  bb_next = firstBBlock(body);
	else bb_next = nextBBlock(b);
	bb.addCflowTo(bb_next);
	break;
      case Xcode.FOR_STATEMENT: 
	if(b.getInitBBlock() == bb){ /* init part */
	  bb.addCflowTo(b.getCondBBlock());
	} else if(b.getCondBBlock() == bb){
	  bb.addCflowTo(firstBBlock(b.getBody()));
	  bb.addCflowTo(nextBBlock(b));
	} else if(b.getIterBBlock() == bb){
	  bb.addCflowTo(b.getCondBBlock());
	} else fatal("bad FOR_STATEMENT");
	break;
      case Xcode.WHILE_STATEMENT: 
	// bb is CondBBlock
	bb.addCflowTo(firstBBlock(b.getBody()));
	bb.addCflowTo(nextBBlock(b));
	break;
      case Xcode.DO_STATEMENT: 
	// bb is CondBBlock
	bb.addCflowTo(firstBBlock(b.getBody()));
	bb.addCflowTo(nextBBlock(b));
	break;
      case Xcode.SWITCH_STATEMENT: 
	/* find CASE_LABEL */
	for(bp = b.getBody().getHead(); bp != null; bp = bp.getNext()){
	  if(bp.Opcode() == Xcode.CASE_LABEL)
	    bb.addCflowTo(bp.getBasicBlock());
	}
	break;
      case Xcode.BREAK_STATEMENT:
	/* find outer SWITCH/FOR/WHILE/DO */
	for(bp = b.getParentBlock(); bp != null; 
	    bp = bp.getParentBlock()){
	  if(bp.Opcode() ==  Xcode.FOR_STATEMENT ||
	     bp.Opcode() == Xcode.SWITCH_STATEMENT ||
	     bp.Opcode() == Xcode.WHILE_STATEMENT ||
	     bp.Opcode() == Xcode.DO_STATEMENT){
	    bb.addCflowTo(nextBBlock(bp));
	    break;
	  }
	}
	if(bp == null) fatal("bad BREAK_STATEMENT");
	break;
      case Xcode.CONTINUE_STATEMENT: 
	/* find outer SWITCH/FOR/WHILE/DO */
	for(bp = b.getParentBlock(); bp != null; 
	    bp = bp.getParentBlock()){
	  if(bp.Opcode() == Xcode.FOR_STATEMENT){
	    bb.addCflowTo(bp.getIterBBlock());
	    break;
	  } else if(bp.Opcode() == Xcode.WHILE_STATEMENT ||
		    bp.Opcode() == Xcode.DO_STATEMENT){
	    bb.addCflowTo(bp.getCondBBlock());
	    break;
	  }
	}
	if(bp == null) fatal("bad CONTINUE_STATEMENT");
	break;
		
      case Xcode.GOTO_STATEMENT:	/* (GOTO_STATEMENT label) */
	bb_next = (BasicBlock)labelBlocks.get(b.getLabel());
	if(bb_next == null) fatal("GOTO label is not found");
	bb.addCflowTo(bb_next);
	break;
      case Xcode.STATEMENT_LABEL:
      case Xcode.CASE_LABEL: 
      case Xcode.DEFAULT_LABEL: 
      case Xcode.LIST:	/* simple block */
	bb.addCflowTo(nextBBlock(b));
	break;
      case Xcode.RETURN_STATEMENT: /* (RETURN_STATEMENT value) */
	bb.addCflowTo(exitBBlock);
	break;
      default:
	/* check OpenMP block */
	if(b instanceof OMPBlock){
	  OMPBlock ompb = (OMPBlock)b;
	  if(ompb.isBeginBasicBlock(bb)){
	    if(ompb.getBody() != null){
	      bb.addCflowTo(firstBBlock(ompb.getBody()));
	    } else {
	      bb.addCflowTo(ompb.endBasicBlock());
	    }
	  } else { // endBasicBlock
	    bb.addCflowTo(nextBBlock(b));
	  }
	  break;
	}
	fatal("Bad Block code="+b);
      }
    }
  }
    
  BasicBlock firstBBlock(BlockList body){
    Block b = body.getHead();
    if(b == null)	/* null body, move to next ?? */
      return nextBBlock(body.getParent());
    return firstBBlock(b);
  }

  // give first BasicBlock of a statement Block b
  BasicBlock firstBBlock(Block b){
    switch(b.Opcode()){
    case Xcode.COMPOUND_STATEMENT:
    case Xcode.OMP_PRAGMA:
    case Xcode.FUNCTION_DEFINITION:
    case Xcode.DO_STATEMENT: 
      /* compound block */
      return firstBBlock(b.getBody());
    case Xcode.FOR_STATEMENT: 
      return b.getInitBBlock();
    default:
      return b.getBasicBlock();
    }
  }

  BasicBlock nextBBlock(Block b){
    if(b == null) return exitBBlock;
    if(b.getNext() == null){  /* the last of body */
      Block pb = b.getParentBlock(); 
      if(pb == null) return exitBBlock;
      switch(pb.Opcode()){
      case Xcode.FOR_STATEMENT:      // 'for' block body
	return pb.getIterBBlock();   // execute iteration part
      case Xcode.WHILE_STATEMENT: 
      case Xcode.DO_STATEMENT: 
	return pb.getCondBBlock();
      default:
	if(pb instanceof OMPBlock){
	  return ((OMPBlock)pb).endBasicBlock();
	}
	return nextBBlock(pb);
      }
    } else
      return firstBBlock(b.getNext());
  }

  // toplogical sort order
  public void buildTopSortOrder(){
    BasicBlock bb;
    num_of_bb = 0;
    BasicBlockIterator ii = new BasicBlockIterator(thisBody);
    for(ii.init(); !ii.end(); ii.next()){
      bb = ii.getBasicBlock();
      bb.resetMark();
      bb.setId(num_of_bb++);
    }
    top_sort_order = null;	
    rev_sort_order = null;
    visitTopSortOrder(enterBBlock);
    int n = 0;
    for(bb = top_sort_order; bb != null; bb=bb.topNext()){
      bb.setTopPrev(rev_sort_order);
      rev_sort_order = bb;
      bb.setDFN(n++);
    }
    for(ii.init(); !ii.end(); ii.next()){
      bb = ii.getBasicBlock();
      if(!bb.isMarked()) bb.setDFN(-1);
      bb.resetMark();
    }
  }

  void visitTopSortOrder(BasicBlock bb){
    if(bb.isMarked()) return;
    bb.setMark();
    Vector out = bb.getCflowOut();
    for(int i = out.size()-1; i >= 0; i--)
      visitTopSortOrder((BasicBlock)(out.elementAt(i)));
    bb.setTopNext(top_sort_order);
    top_sort_order = bb;
  }

  // for debug
  public void printCFG(){
    for(BasicBlock bb = top_sort_order; bb != null; bb = bb.topNext()){
      printCFG(" "+bb+"(DFN="+bb.DFN()+"):",bb);
    }
  }
	
  void printCFG(String msg,BasicBlock bb){
    BasicBlock bbp;
    System.out.println(msg);
    System.out.print("  IN:");
    for(Enumeration e = bb.getCflowIn().elements();e.hasMoreElements(); ){
      bbp = (BasicBlock)e.nextElement();
      System.out.print(" "+bbp);
    }
    System.out.println();
    System.out.print("  OUT:");
    for(Enumeration e = bb.getCflowOut().elements();e.hasMoreElements(); ){
      bbp = (BasicBlock)e.nextElement();
      System.out.print(" "+bbp);
    }
    System.out.println();
  }

  void fatal(String msg){
    System.err.println("Fatal in ControlFlowGraph:"+msg);
    Thread.dumpStack();
    System.exit(1);
  }
}







