/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.opt.controlflow;

import java.util.Enumeration;

import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.util.Tree;
import org.jikesrvm.compilers.opt.util.TreeNode;
import org.jikesrvm.util.BitVector;

/**
 * This class provides the abstraction of a dominator tree
 *
 * TODO: we do not support IRs with exception handlers.
 */
public class DominatorTree extends Tree {
  /**
   * control for debugging messages.
   */
  static final boolean DEBUG = false;

  /**
   * True if we are computing regular dominators, false for post-dominators
   */
  private boolean forward;

  /**
   * The governing IR
   */
  private IR ir;

  /**
   * A structure used to quickly index into the DominatorVertex tree
   */
  private DominatorTreeNode[] dominatorInfoMap;

  /**
   * Build a dominator tree from an IR. NOTE: the dominator
   * information MUST be computed BEFORE calling this method!
   * We assume the scratch object of each basic block contains
   * the LTDominatorInfo object holding this information.
   *
   * @param ir the governing IR
   * @param forward are we building regular dominators or post-dominators?
   */
  public static void perform(IR ir, boolean forward) {
    if (forward) {
      ir.HIRInfo.dominatorTree = new DominatorTree(ir, forward);
      if (ir.options.PRINT_DOMINATORS) {
        if (DEBUG) {
          System.out.println("Here is the CFG for method " + ir.method.getName() + "\n" + ir.cfg);
        }
        System.out.println("Here is the Dominator Tree for method " +
                           ir.method.getName() +
                           "\n" +
                           ir.HIRInfo
                               .dominatorTree);
      }
    } else {
      ir.HIRInfo.postDominatorTree = new DominatorTree(ir, forward);
      if (ir.options.PRINT_POST_DOMINATORS) {
        if (DEBUG) {
          System.out.println("Here is the CFG for method " + ir.method.getName() + "\n" + ir.cfg);
        }
        System.out.println("Here is the Post-Dominator Tree for method " +
                           ir.method.getName() +
                           "\n" +
                           ir.HIRInfo
                               .postDominatorTree);
      }
    }
  }

  /**
   * Build a dominator tree from an IR. NOTE: the dominator
   * information MUST be computed BEFORE calling this
   * constructor!
   *
   * @param ir the governing IR
   * @param forward are we building regular dominators or post-dominators?
   */
  public DominatorTree(IR ir, boolean forward) {
    this.ir = ir;
    this.forward = forward;

    // The query methods of dominator information, such as
    // getDominanceFrontier, dominates, and domFrontierEnumerator
    // all use ir.getBasicBlock(int).  This method relies on
    // the basic block map being up to date.  Here we ensure this
    // property, even though it is needed for computing the dominator
    // tree.
    ir.resetBasicBlockMap();

    // allocate the dominator vertex map
    dominatorInfoMap = new DominatorTreeNode[ir.getMaxBasicBlockNumber() + 1];

    // allocate the tree and root node
    // Step 1: add all basic blocks to the tree as nodes
    for (Enumeration<BasicBlock> bbEnum = ir.cfg.basicBlocks(); bbEnum.hasMoreElements();) {
      BasicBlock block = bbEnum.nextElement();
      // We treat the exit node as not being part of the CFG
      if (!forward || !block.isExit()) {
        addNode(block);
      }
    }

    // Step 2: set the root value
    setRoot(dominatorInfoMap[getFirstNode().getNumber()]);

    // Step 3: Walk the nodes, for each node create link with parent
    //   Leaf nodes have no links to add
    for (Enumeration<BasicBlock> bbEnum = ir.cfg.basicBlocks(); bbEnum.hasMoreElements();) {
      BasicBlock block = bbEnum.nextElement();
      // skip the exit node
      if (forward && block.isExit()) {
        continue;
      }

      // get the tree node corresponding to "block"
      DominatorTreeNode blockNode = dominatorInfoMap[block.getNumber()];

      // if parent = null, this is the root of the tree, nothing to do
      if (LTDominatorInfo.getInfo(block) == null) {
        System.out.println("info field is null for block: " + block);
      }
      BasicBlock parent = LTDominatorInfo.getInfo(block).getDominator();
      if (parent != null) {
        DominatorTreeNode parentNode = dominatorInfoMap[parent.getNumber()];

        // tell the parent they have a child
        parentNode.addChild(blockNode);
      }
    }           // for loop
  }             // method

  /**
   * Get the first node, either entry or exit
   * depending on which way we are viewing the graph
   * @return the entry node or exit node
   */
  private BasicBlock getFirstNode() {
    if (forward) {
      return ir.cfg.entry();
    } else {
      return ir.cfg.exit();
    }
  }

  /**
   * Enumerate the children of the vertex corresponding to a basic
   * block
   *
   * @param bb the basic block
   * @return an Enumeration of bb's children
   */
  public Enumeration<TreeNode> getChildren(BasicBlock bb) {
    DominatorTreeNode node = dominatorInfoMap[bb.getNumber()];
    return node.getChildren();
  }

  /**
   * Return the parent of the vertex corresponding to a basic
   * block
   *
   * @param bb the basic block
   * @return bb's parent
   */
  public BasicBlock getParent(BasicBlock bb) {
    DominatorTreeNode node = dominatorInfoMap[bb.getNumber()];
    return ((DominatorTreeNode) node.getParent()).getBlock();
  }

  /**
   * Return the (already calculated) dominance frontier for
   * a basic block
   *
   * @param bb the basic block
   * @return a BitVector representing the dominance frontier
   */
  public BitVector getDominanceFrontier(BasicBlock bb) {
    DominatorTreeNode info = dominatorInfoMap[bb.getNumber()];
    return info.getDominanceFrontier();
  }

  /**
   * Return the (already calculated) dominance frontier for
   * a basic block
   *
   * @param number the number of the basic block
   * @return a BitVector representing the dominance frontier
   */
  public BitVector getDominanceFrontier(int number) {
    return getDominanceFrontier(ir.getBasicBlock(number));
  }

  /**
   * Does basic block number b dominate all basic blocks in a set?
   *
   * @param b the number of the basic block to test
   * @param bits BitVector representation of the set of basic blocks to test
   * @return true or false
   */
  public boolean dominates(int b, BitVector bits) {
    for (int i = 0; i < bits.length(); i++) {
      if (!bits.get(i)) {
        continue;
      }
      if (!dominates(b, i)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Does basic block number "master" dominate basic block number "slave"?
   *
   * @param master the number of the proposed "master" basic block
   * @param slave  the number of the proposed "slave block
   * @return "master dominates slave"
   */
  public boolean dominates(int master, int slave) {
    BasicBlock masterBlock = ir.getBasicBlock(master);
    BasicBlock slaveBlock = ir.getBasicBlock(slave);
    DominatorTreeNode masterNode = dominatorInfoMap[masterBlock.getNumber()];
    DominatorTreeNode slaveNode = dominatorInfoMap[slaveBlock.getNumber()];
    return slaveNode.isDominatedBy(masterNode);
  }

  /**
   * Does basic block number "master" dominate basic block number "slave"?
   *
   * @param master the number of the proposed "master" basic block
   * @param slave  the number of the proposed "slave block
   * @return "master dominates slave"
   */
  public boolean dominates(BasicBlock master, BasicBlock slave) {
    DominatorTreeNode masterNode = dominatorInfoMap[master.getNumber()];
    DominatorTreeNode slaveNode = dominatorInfoMap[slave.getNumber()];
    return slaveNode.isDominatedBy(masterNode);
  }

  /**
   * Creates domniator tree nodes for the passed block and adds them to the
   * map.
   * @param b the basic block
   */
  private void addNode(BasicBlock b) {
    DominatorTreeNode node = new DominatorTreeNode(b);
    dominatorInfoMap[b.getNumber()] = node;
  }

  /**
   * Return the distance from the root of the dominator tree to a given
   * basic block
   *
   * @param b the basic block in question
   * @return <code>b</code>'s depth
   */
  public int depth(BasicBlock b) {
    return dominatorInfoMap[b.getNumber()].getDepth();
  }

  /**
   * Return the deepest common dominance ancestor of blocks <code>a</code> and <code>b</code>
   *
   * @param a The first basic block
   * @param b The second basic block
   * @return the deepest common dominance ancestor of blocks <code>a</code>
   *        and <code>b</code>
   */
  public BasicBlock deepestCommonAncestor(BasicBlock a, BasicBlock b) {
    DominatorTreeNode n_a = dominatorInfoMap[a.getNumber()];
    DominatorTreeNode n_b = dominatorInfoMap[b.getNumber()];
    while (n_a != n_b) {
      if (n_a.getDepth() > n_b.getDepth()) {
        n_a = (DominatorTreeNode) n_a.getParent();
      } else {
        n_b = (DominatorTreeNode) n_b.getParent();
      }
    }
    return n_a.getBlock();
  }

}



