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

import java.util.HashSet;

import org.jikesrvm.compilers.opt.DefUse;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.driver.CompilerPhase;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.liveness.LiveAnalysis;

/**
 * Coalesce registers in move instructions where possible.
 */
public class CoalesceMoves extends CompilerPhase {

  /**
   *  verbose debugging flag
   */
  static final boolean DEBUG = false;

  /**
   * Return this instance of this phase. This phase contains no
   * per-compilation instance fields.
   * @param ir not used
   * @return this
   */
  public CompilerPhase newExecution(IR ir) {
    return this;
  }

  /**
   * Should we perform this phase?
   * @param options controlling compiler options
   */
  public final boolean shouldPerform(OptOptions options) {
    return options.COALESCE_AFTER_SSA;
  }

  /**
   * Return a string name for this phase.
   * @return "Coalesce Moves"
   */
  public final String getName() {
    return "Coalesce Moves";
  }

  /**
   * perform the transformation
   * @param ir the governing IR
   */
  public final void perform(IR ir) {
    // Compute liveness.
    LiveAnalysis live = new LiveAnalysis(false /* GC Maps */, false /* don't skip local
                                                         propagation */);
    live.perform(ir);

    // Compute def-use information.
    DefUse.computeDU(ir);

    // Number the instructions
    ir.numberInstructions();

    // Maintain a set of dead move instructions.
    HashSet<Instruction> dead = new HashSet<Instruction>(5);

    // for each Move instruction ...
    for (InstructionEnumeration e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
      Instruction s = e.nextElement();
      if (s.operator.isMove()) {
        Register r = Move.getResult(s).asRegister().getRegister();
        if (r.isSymbolic()) {
          Operand val = Move.getVal(s);
          if (val != null && val.isRegister()) {
            Register r2 = val.asRegister().getRegister();
            if (r2.isSymbolic()) {
              if (Coalesce.attempt(ir, live, r, r2)) {
                if (DEBUG) System.out.println("COALESCED " + r + " " + r2);
                dead.add(s);
              }
            }
          }
        }
      }
    }

    // Now remove all dead Move instructions.
    for (Instruction s : dead) {
      DefUse.removeInstructionAndUpdateDU(s);
    }
  }
}
