/*
 *  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.ir;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.liveness.LiveSet;
import org.jikesrvm.compilers.opt.liveness.LiveSetEnumerator;
import org.jikesrvm.util.LinkedListRVM;

/**
 *
 *  This class holds GC maps for various program points.
 *  This data structure is IR-based.  In a later phase, this information
 *  will be used to create the final GC map (see OptMachineCodeMap.java)
 */
public final class GCIRMap implements Iterable<GCIRMapElement> {
  /**
   *  This is the list of maps
   *   Each element on the list is an GCIRMapElement, which is a pair
   *          - an IR instruction (the GC point)
   *          - a list of RegSpillListElement, which initially hold symbolic
   *               registers that are references
   *               (these are expanded to either physical regs or spills
   *                by the register allocator)
   */
  private LinkedListRVM<GCIRMapElement> list = new LinkedListRVM<GCIRMapElement>();

  /**
   *  Used for class-wide debugging
   */
  private static final boolean DEBUG = false;

  /**
   * returns the number of GC points in this map, i.e., the number of
   * instructions we have maps for.
   * @return the number of GC points in this map
   */
  public int getNumInstructionMaps() {
    return list.size();
  }

  /**
   *  Calculates the number of spill entries in this GCIRMap
   *  This is the total number of spills for all instructions
   *  in this map.
   *  @return the number of spill entries in this map
   */
  public int countNumSpillElements() {
    // Since spill locations are not determined until after
    // register allocation occurs, i.e., after the initial
    // IR-based maps are created, we actually count the
    // number of spills.
    int count = 0;
    for (GCIRMapElement elem : this) {
      count += elem.countNumSpillElements();
    }
    return count;
  }

  /**
   * TODO What is this method doing in this class ?? RJG
   *
   * This method creates a regSpillList from the passed live set.
   * @param set the set of registers, encoded as a LiveSet object
   * @return a list corresponding to the set passed
   */
  public List<RegSpillListElement> createDU(LiveSet set) {
    if (DEBUG) {
      System.out.println("creating a RegList for " + set);
    }

    // construct register list
    List<RegSpillListElement> regList = new LinkedListRVM<RegSpillListElement>();
    LiveSetEnumerator lsEnum = set.enumerator();
    while (lsEnum.hasMoreElements()) {
      RegisterOperand regOp = lsEnum.nextElement();

      // add this register to the regList, if it is a reference
      //  and not a physcial register
      if (regOp.getType().isReferenceType() && !regOp.getRegister().isPhysical()) {
        RegSpillListElement elem = new RegSpillListElement(regOp.getRegister());
        regList.add(elem);
      }
    }
    return regList;
  }

  /**
   * This method inserts a new entry into the GCIRMap
   * @param inst    the IR instruction we care about
   * @param regList the set of symbolic registers as a list
   */
  public void insert(Instruction inst, List<RegSpillListElement> regList) {

    // make a GCIRMapElement and put it on the big list
    GCIRMapElement item = new GCIRMapElement(inst, regList);

    if (DEBUG) {
      System.out.println("Inserting new item: " + item);
    }

    list.add(item);
  }

  /**
   * This method removes an entry in the GCIRMap that is specified
   * by inst. Only one element of the list will be removed per call.
   * If the instruction is not found in the GC Map and exeception is thrown.
   * @param inst    the IR instruction we want to remove
   */
  public void delete(Instruction inst) {

    Iterator<GCIRMapElement> iter = list.iterator();
    while (iter.hasNext()) {
      GCIRMapElement ptr = iter.next();
      if (ptr.getInstruction() == inst) {
        iter.remove();
        return;
      }
    }
    throw new OptimizingCompilerException("GCIRMap.delete(" +
                                              inst +
                                              ") did not delete instruction from GC Map ");
  }

  /**
   * This method moves an entry in the GCIRMap that is specified
   * by inst to the end of the list. Only one element of the list will be moved per call.
   * If the instruction is not found in the GC Map and exeception is thrown.
   * @param inst    the IR instruction we want to remove
   */
  public void moveToEnd(Instruction inst) {
    Iterator<GCIRMapElement> iter = list.iterator();
    while (iter.hasNext()) {
      GCIRMapElement newPtr = iter.next();
      if (newPtr.getInstruction() == inst) {
        iter.remove();
        list.add(newPtr);
        return;
      }
    }
    throw new OptimizingCompilerException("GCIRMap.moveToEnd(" +
                                              inst +
                                              ") did not delete instruction from GC Map ");
  }

  /**
   * This method inserts an entry for a "twin" instruction immediately after the
   * original entry.
   * If the instruction is not found in the GC Map an exeception is thrown.
   * @param inst    the orignal IR instruction
   * @param twin    the new twin IR instruction
   */
  public void insertTwin(Instruction inst, Instruction twin) {
    ListIterator<GCIRMapElement> iter = list.listIterator();
    while (iter.hasNext()) {
      GCIRMapElement newPtr = iter.next();
      if (newPtr.getInstruction() == inst) {
        iter.add(newPtr.createTwin(twin));
        return;
      }
    }
    throw new OptimizingCompilerException("GCIRMap.createTwin: " + inst + " not found");
  }

  public Iterator<GCIRMapElement> iterator() {
    return list.iterator();
  }

  /**
   * dumps the map
   */
  public void dump() {
    System.out.println(toString());
  }

  /**
   * @return string version of this object
   */
  public String toString() {
    StringBuilder buf = new StringBuilder("");
    if (list.isEmpty()) {
      buf.append("empty");
    } else {
      for (GCIRMapElement ptr : list) {
        buf.append(ptr);
      }
    }
    return buf.toString();
  }
}



