/*
 *  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.BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.util.TreeNode;
import org.jikesrvm.util.BitVector;

/**
 * This class implements a node in the dominator tree.
 *
 * <p> TODO: we do not support IRs with exception handlers!!
 */
public class DominatorTreeNode extends TreeNode {

  /**
   * the basic block this node represents
   */
  private final BasicBlock block;

  /**
   * distance from the root of the dominator tree
   */
  private int depth = -1;

  /**
   * representation of the dominance frontier for this node
   */
  private BitVector dominanceFrontier;

  /**
   * the cache to hold the set of nodes that dominate this one.  This is
   * computed on demand by walking up the tree.
   */
  BitVector dominators;

  /**
   * lower bound of dominated nodes range
   */
  private int low = 0;

  /**
   * upper bound of dominated nodes range
   */
  private int high = 0;

  /**
   * Construct a dominator tree node for a given basic block.
   * @param   block the basic block
   */
  DominatorTreeNode(BasicBlock block) {
    this.block = block;
  }

  /**
   * Get the basic block for this dominator tree node
   * @return the basic block
   */
  public BasicBlock getBlock() {
    return block;
  }

  /**
   * Return the distance of this node from the root of the dominator tree.
   * @return the distance of this node from the root of the dominator tree.
   */
  int getDepth() {
    if (depth == -1) {
      DominatorTreeNode dad = (DominatorTreeNode) getParent();
      if (dad == null) {
        depth = 0;
      } else {
        depth = dad.getDepth() + 1;
      }
    }
    return depth;
  }

  /**
   * Return a bit set representing the dominance frontier for this node
   * @return a bit set representing the dominance frontier for this node
   */
  BitVector getDominanceFrontier() {
    return dominanceFrontier;
  }

  /**
   * Set a bit set representing the dominance frontier for this node
   * @param set the bit set
   */
  void setDominanceFrontier(BitVector set) {
    dominanceFrontier = set;
  }

  /**
   * Return a string representation of the dominance frontier for this
   * node.
   * @return a string representation of the dominance frontier for this
   * node
   */
  String dominanceFrontierString() {
    return dominanceFrontier.toString();
  }

  /**
   *   This method returns the set of blocks that dominates the passed
   *   block, i.e., it answers the question "Who dominates me?"
   *
   *   @param ir the governing IR
   *   @return a BitVector containing those blocks that dominate me
   */
  BitVector dominators(IR ir) {
    // Currently, this set is computed on demand,
    // but we cache it for the next time.
    if (dominators == null) {
      dominators = new BitVector(ir.getMaxBasicBlockNumber() + 1);
      dominators.set(block.getNumber());
      DominatorTreeNode node = this;
      while ((node = (DominatorTreeNode) getParent()) != null) {
        dominators.set(node.getBlock().getNumber());
      }
    }
    return dominators;
  }

  /**
   *  This method returns true if the passed node dominates this node
   *  @param master the proposed dominating node
   *  @return whether the passed node dominates me
   */
  boolean _isDominatedBy(DominatorTreeNode master) {
    DominatorTreeNode node = this;
    while ((node != null) && (node != master)) {
      node = (DominatorTreeNode) node.getParent();
    }
    return node == master;
  }

  /**
   *  This method returns true if the passed node dominates this node
   *  @param master the proposed dominating node
   *  @return whether the passed node dominates me
   */
  boolean isDominatedBy(DominatorTreeNode master) {
    if (low == 0) initializeRanges();
    return master.low <= low && master.high >= high;
  }

  private void initializeRanges() {
    DominatorTreeNode node = this;
    DominatorTreeNode parent = (DominatorTreeNode) getParent();
    while (parent != null) {
      node = parent;
      parent = (DominatorTreeNode) node.getParent();
    }
    node.initializeRanges(0);
  }

  private int initializeRanges(int i) {
    low = ++i;
    Enumeration<TreeNode> childEnum = getChildren();
    while (childEnum.hasMoreElements()) {
      DominatorTreeNode child = (DominatorTreeNode) childEnum.nextElement();
      i = child.initializeRanges(i);
    }
    high = ++i;
    return i;
  }

  /**
   * Enumerate the basic blocks in the dominance frontier for this node.
   * @param ir the governing IR
   * @return an enumeration of the basic blocks in the dominance frontier
   * for this node.
   */
  BasicBlockEnumeration domFrontierEnumerator(IR ir) {
    return ir.getBasicBlocks(dominanceFrontier);
  }

  /**
   * String-i-fies the node
   * @return the node as a string
   */
  public final String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(block);
    sb.append(" (").append(low).append(", ").append(high).append(")");
    return sb.toString();
  }
}



