/*
 *  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 org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.dfsolver.DF_LatticeCell;
import org.jikesrvm.compilers.opt.dfsolver.DF_System;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.IR;

/**
 * Implementation of the dataflow equation system to calculate dominators.
 */
class DominatorSystem extends DF_System {

  /**
   * The governing IR.
   */
  IR ir;

  /**
   * Default constructor.
   * @param ir the governing IR
   */
  public DominatorSystem(IR ir) {
    this.ir = ir;
    setupEquations();
  }

  /**
   * Go through each basic block in the IR, and add equations
   * to the system as required.
   * <p> Uses the algorithm contained in Dragon book, pg. 670-1.
   * <pre>
   *     D(n0) := { n0 }
   *     for n in N - { n0 } do D(n) := N;
   *     while changes to any D(n) occur do
   *       for n in N - {n0} do
   *           D(n) := {n} U (intersect of D(p) over all predecessors p of n)
   * </pre>
   */
  void setupEquations() {
    // loop through each basic block in the IR
    for (BasicBlockEnumeration e = ir.getBasicBlocks(); e.hasMoreElements();) {
      BasicBlock bb = e.next();
      // add a data-flow equation for this basic block
      // DOM(n) = {n} MEET {pred(n)}
      DF_LatticeCell dom = findOrCreateCell(bb);
      DF_LatticeCell[] pred = getCellsForPredecessors(bb);
      newEquation(dom, DominatorOperator.MEET, pred);
    }
  }

  /**
   * Initialize the lattice variables (Dominator sets) for
   * each basic block.
   */
  protected void initializeLatticeCells() {
    if (Dominators.COMPUTE_POST_DOMINATORS) {
      BasicBlock exit = ir.cfg.exit();
      DominatorCell last = (DominatorCell) getCell(exit);
      for (final DF_LatticeCell latticeCell : cells.values()) {
        DominatorCell cell = (DominatorCell) latticeCell;
        if (cell == last) {
          cell.addSingleBlock(cell.block);
        } else {
          cell.setTOP(ir);
        }
      }
    } else {
      BasicBlock start = ir.cfg.entry();
      DominatorCell first = (DominatorCell) getCell(start);
      for (final DF_LatticeCell latticeCell : cells.values()) {
        DominatorCell cell = (DominatorCell) latticeCell;
        if (cell == first) {
          cell.addSingleBlock(cell.block);
        } else {
          cell.setTOP(ir);
        }
      }
    }
  }

  /**
   * Initialize the work list for the dataflow equation system.
   * <p> The initial work list is every equation containing the start
   * node.
   */
  protected void initializeWorkList() {
    if (Dominators.COMPUTE_POST_DOMINATORS) {
      // Add every equation to work list (to be safe)
      // WARNING: an "end node" may be part of a cycle
      for (BasicBlockEnumeration e = ir.getBasicBlocks(); e.hasMoreElements();) {
        BasicBlock bb = e.next();
        addCellAppearancesToWorkList(getCell(bb));
      }
    } else {
      DominatorCell first = (DominatorCell) getCell(ir.cfg.entry());
      addCellAppearancesToWorkList(first);
    }
  }

  /**
   * Get the DF_LatticeCell key corresponding to a basic block
   * @param bb the basic block
   * @return the key (just the block itself)
   */
  Object getKey(BasicBlock bb) {
    return bb;
  }

  /**
   * Make a new DF_LatticeCell key corresponding to a basic block
   * @param key the basic block
   * @return the new cell
   */
  protected DF_LatticeCell makeCell(Object key) {
    return new DominatorCell((BasicBlock) key, ir);
  }

  /**
   * Return a list of lattice cells corresponding to the
   * predecessors of a basic block.
   * @param bb the basic block
   */
  DF_LatticeCell[] getCellsForPredecessors(BasicBlock bb) {
    if (Dominators.COMPUTE_POST_DOMINATORS) {
      /****
       if ( bb.mayThrowUncaughtException() ) {
       if (Dominators.DEBUG) VM.sysWrite("LOCATION #1 ...\n");
       // Include exit node as an output node
       DF_LatticeCell s[] = new DF_LatticeCell[bb.getNumberOfOut()+1];
       BasicBlockEnumeration e = bb.getOut();
       for (int i=0; i<s.length-1; i++ ) {
       BasicBlock p = e.next();
       s[i] = findOrCreateCell(getKey(p));
       }
       s[s.length-1] = findOrCreateCell(getKey(ir.cfg.exit()));
       return s;
       }
       else
       ****/
      {
        if (Dominators.DEBUG) {
          VM.sysWrite("LOCATION #2 ...\n");
        }
        DF_LatticeCell[] s = new DF_LatticeCell[bb.getNumberOfOut()];
        BasicBlockEnumeration e = bb.getOut();
        for (int i = 0; i < s.length; i++) {
          BasicBlock p = e.next();
          s[i] = findOrCreateCell(getKey(p));
        }
        return s;
      }
    } else {
      if (Dominators.DEBUG) {
        System.out.println("LOCATION #3 ...");
      }
      DF_LatticeCell[] s = new DF_LatticeCell[bb.getNumberOfIn()];
      BasicBlockEnumeration e = bb.getIn();
      for (int i = 0; i < s.length; i++) {
        BasicBlock p = e.next();
        s[i] = findOrCreateCell(getKey(p));
      }
      return s;
    }
  }
}
