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

import static org.jikesrvm.compilers.opt.driver.OptConstants.SSA_SYNTH_BCI;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.PHI;

import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;

import org.jikesrvm.VM;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.DefUse;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.controlflow.BranchOptimizations;
import org.jikesrvm.compilers.opt.controlflow.DominatorTree;
import org.jikesrvm.compilers.opt.controlflow.DominatorTreeNode;
import org.jikesrvm.compilers.opt.controlflow.LTDominators;
import org.jikesrvm.compilers.opt.driver.CompilerPhase;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.IRTools;
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.OperandEnumeration;
import org.jikesrvm.compilers.opt.ir.Phi;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.RegisterOperandEnumeration;
import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.compilers.opt.ir.operand.UnreachableOperand;
import org.jikesrvm.compilers.opt.liveness.LiveAnalysis;
import org.jikesrvm.compilers.opt.liveness.LiveSet;
import org.jikesrvm.compilers.opt.util.TreeNode;

/**
 * This compiler phase translates out of SSA form.
 *
 * @see SSA
 * @see SSAOptions
 * @see LTDominators
 */
public class LeaveSSA extends CompilerPhase {

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

  // control bias between adding blocks or adding temporaries
  private static final boolean SplitBlockToAvoidRenaming = false;
  private static final boolean SplitBlockForLocalLive = true;
  private static final boolean SplitBlockIntoInfrequent = true;

  /**
   * The IR to manipulate
   */
  private IR ir;

  private BranchOptimizations branchOpts = new BranchOptimizations(-1, true, true);

  private boolean splitSomeBlock = false;

  private final HashSet<Instruction> globalRenameTable = new HashSet<Instruction>();

  private final HashSet<Register> globalRenamePhis = new HashSet<Register>();

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

  /**
   * Constructor for this compiler phase
   */
  private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(LeaveSSA.class);

  /**
   * Get a constructor object for this compiler phase
   * @return compiler phase constructor
   */
  public Constructor<CompilerPhase> getClassConstructor() {
    return constructor;
  }

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

  /**
   * perform the main out-of-ssa transformation
   * @param ir the governing IR
   */
  public final void perform(IR ir) {
    this.ir = ir;
    translateFromSSA(ir);

    // reset ir.SSADictionary
    ir.HIRInfo.dictionary = null;
    // reset ssa options
    ir.actualSSAOptions = null;

    branchOpts.perform(ir, true);

    ir.HIRInfo.dominatorsAreComputed = false;
  }

  /**
   * This class provides an abstraction over stacks of names
   * for registers.
   */
  static class VariableStacks extends HashMap<Register, Stack<Operand>> {
    /** Support for map serialization */
    static final long serialVersionUID = -5664504465082745314L;

    /**
     * Get the name at the top of the stack for a particular register
     * @param s the register in question
     * @return the name at the top of the stack for the register
     */
    Operand peek(Register s) {
      Stack<Operand> stack = get(s);
      if (stack == null || stack.isEmpty()) {
        return null;
      } else {
        return stack.peek();
      }
    }

    /**
     * Pop the name at the top of the stack for a particular register
     * @param s the register in question
     * @return the name at the top of the stack for the register
     */
    Operand pop(Register s) {
      Stack<Operand> stack = get(s);
      if (stack == null) {
        throw new OptimizingCompilerException(
            "Failure in translating out of SSA form: trying to pop operand from non-existant stack");
      } else {
        return stack.pop();
      }
    }

    /**
     * Push a name at the top of the stack for a particular register
     * @param s the register in question
     * @param name the name to push on the stack
     */
    void push(Register s, Operand name) {
      Stack<Operand> stack = get(s);
      if (stack == null) {
        stack = new Stack<Operand>();
        put(s, stack);
      }
      stack.push(name);
    }
  }

  /**
   * An instance of this class represents a pending copy instruction
   * to be inserted.
   */
  static class Copy {
    /**
     * The right-hand side of the copy instruction
     */
    final Operand source;
    /**
     * The left-hand side of the copy instruction
     */
    final RegisterOperand destination;
    /**
     *  The phi instruction which generated this copy instruction
     */
    final Instruction phi;

    /**
     * Create a pending copy operation for an operand of a phi instruction
     * @param     phi   the phi instruction
     * @param     index which operand of the instruction to copy
     */
    Copy(Instruction phi, int index) {
      this.phi = phi;
      destination = Phi.getResult(phi).asRegister();
      source = Phi.getValue(phi, index);
    }
  }

  /**
   * substitute variables renamed in control parents
   */
  private void performRename(BasicBlock bb, DominatorTree dom, VariableStacks s) {
    if (DEBUG) VM.sysWriteln("performRename: " + bb);

    InstructionEnumeration e = bb.forwardRealInstrEnumerator();
    while (e.hasMoreElements()) {
      Instruction i = e.next();
      OperandEnumeration ee = i.getUses();
      while (ee.hasMoreElements()) {
        Operand o = ee.next();
        if (o instanceof RegisterOperand) {
          Register r1 = ((RegisterOperand) o).getRegister();
          if (r1.isValidation()) continue;
          Operand r2 = s.peek(r1);
          if (r2 != null) {
            if (DEBUG) {
              VM.sysWriteln("replace operand in " + i + "(" + r2 + " for " + o);
            }
            i.replaceOperand(o, r2.copy());
          }
        }
      }
    }

    // record renamings required in children
    e = bb.forwardRealInstrEnumerator();
    while (e.hasMoreElements()) {
      Instruction i = e.next();
      if (globalRenameTable.contains(i)) {
        Register original = Move.getVal(i).asRegister().getRegister();
        RegisterOperand rename = Move.getResult(i);
        if (DEBUG) VM.sysWriteln("record rename " + rename + " for " + original);
        s.push(original, rename);
      }
    }

    // insert copies in control children
    Enumeration<TreeNode> children = dom.getChildren(bb);
    while (children.hasMoreElements()) {
      BasicBlock c = ((DominatorTreeNode) children.nextElement()).getBlock();
      performRename(c, dom, s);
    }

    // pop renamings from this block off stack
    e = bb.forwardRealInstrEnumerator();
    while (e.hasMoreElements()) {
      Instruction i = e.next();
      if (globalRenameTable.contains(i)) {
        Register original = Move.getVal(i).asRegister().getRegister();
        s.pop(original);
      }
    }
  }

  private boolean usedBelowCopy(BasicBlock bb, Register r) {
    InstructionEnumeration ie = bb.reverseRealInstrEnumerator();
    while (ie.hasMoreElements()) {
      Instruction inst = ie.next();
      if (inst.isBranch()) {
        OperandEnumeration oe = inst.getUses();
        while (oe.hasMoreElements()) {
          Operand op = oe.next();
          if (op.isRegister() && op.asRegister().getRegister() == r) {
            return true;
          }
        }
      } else {
        break;
      }
    }

    return false;
  }

  /**
   * Record pending copy operations needed to insert at the end of a basic
   * block.
   * TODO: this procedure is getting long and ugly.  Rewrite or refactor
   * it.
   * @param bb the basic block to process
   * @param live valid liveness information for the IR
   */
  private void scheduleCopies(BasicBlock bb, LiveAnalysis live) {

    if (DEBUG) VM.sysWrite("scheduleCopies: " + bb + "\n");

    // compute out liveness from information in LiveAnalysis
    LiveSet out = new LiveSet();
    for (Enumeration<BasicBlock> outBlocks = bb.getOut(); outBlocks.hasMoreElements();) {
      BasicBlock ob = outBlocks.nextElement();
      LiveAnalysis.BBLiveElement le = live.getLiveInfo(ob);
      out.add(le.in());
    }

    // usedByAnother represents the set of registers that appear on the
    // left-hand side of subsequent phi nodes.  This is important, since
    // we be careful to order copies if the same register appears as the
    // source and dest of copies in the same basic block.
    HashSet<Register> usedByAnother = new HashSet<Register>(4);

    // for each basic block successor b of bb, if we make a block on the
    // critical edge bb->b, then store this critical block.
    HashMap<BasicBlock, BasicBlock> criticalBlocks = new HashMap<BasicBlock, BasicBlock>(4);

    // For each critical basic block b in which we are inserting copies: return the
    // mapping of registers to names implied by the copies that have
    // already been inserted into b.
    HashMap<BasicBlock, HashMap<Register, Register>> currentNames =
        new HashMap<BasicBlock, HashMap<Register, Register>>(4);

    // Additionally store the current names for the current basic block bb.
    HashMap<Register, Register> bbNames = new HashMap<Register, Register>(4);

    // copySet is a linked-list of copies we need to insert in this block.
    final LinkedList<Copy> copySet = new LinkedList<Copy>();

    /* Worklist is actually used like a stack - should we make this an Stack ?? */
    final LinkedList<Copy> workList = new LinkedList<Copy>();

    // collect copies required in this block.  These copies move
    // the appropriate rval into the lval of each phi node in
    // control children of the current block.
    Enumeration<BasicBlock> e = bb.getOut();
    while (e.hasMoreElements()) {
      BasicBlock bbs = e.nextElement();
      if (bbs.isExit()) continue;
      for (Instruction phi = bbs.firstInstruction(); phi != bbs.lastInstruction(); phi =
          phi.nextInstructionInCodeOrder()) {
        if (phi.operator() != PHI) continue;
        for (int index = 0; index < Phi.getNumberOfPreds(phi); index++) {
          if (Phi.getPred(phi, index).block != bb) continue;
          Operand rval = Phi.getValue(phi, index);
          if (rval.isRegister() && Phi.getResult(phi).asRegister().getRegister() == rval.asRegister().getRegister()) {
            continue;
          }
          Copy c = new Copy(phi, index);
          copySet.add(0, c);
          if (c.source instanceof RegisterOperand) {
            Register r = c.source.asRegister().getRegister();
            usedByAnother.add(r);
          }
        }
      }
    }
    //  the copies that need to be added to this block are processed
    //  in a worklist that ensures that copies are inserted only
    //  after the destination register has been read by any other copy
    //  that needs it.
    //
    // initialize work list with all copies whose destination is not
    // the source for any other copy, and delete such copies from
    // the set of needed copies.
    for (Iterator<Copy> copySetIter = copySet.iterator(); copySetIter.hasNext();) {
      Copy c = copySetIter.next();
      if (!usedByAnother.contains(c.destination.getRegister())) {
        workList.add(0, c);
        copySetIter.remove();
      }
    }
    // while there is any more work to do.
    while (!workList.isEmpty() || !copySet.isEmpty()) {
      // while there are copies that can be correctly inserted.
      while (!workList.isEmpty()) {
        Copy c = workList.remove(0);
        Register r = c.destination.getRegister();
        TypeReference tt = c.destination.getType();
        if (VM.VerifyAssertions && tt == null) {
          tt = TypeReference.Int;
          VM.sysWrite("SSA, warning: null type in " + c.destination + "\n");
        }

        Register rr = null;
        if (c.source.isRegister()) rr = c.source.asRegister().getRegister();
        boolean shouldSplitBlock =
            !c.phi.getBasicBlock().isExceptionHandlerBasicBlock() &&
            ((out.contains(r) && SplitBlockToAvoidRenaming) ||
             (rr != null && usedBelowCopy(bb, rr) && SplitBlockForLocalLive));

        if (SplitBlockIntoInfrequent) {
          if (!bb.getInfrequent() &&
              c.phi.getBasicBlock().getInfrequent() &&
              !c.phi.getBasicBlock().isExceptionHandlerBasicBlock()) {
            shouldSplitBlock = true;
          }
        }

        // this check captures cases when the result of a phi
        // in a control successor is live on exit of the current
        // block.  this means it is incorrect to simply insert
        // a copy of the destination in the current block.  so
        // we rename the destination to a new temporary, and
        // record the renaming so that dominator blocks get the
        // new name.
        if (out.contains(r) && !shouldSplitBlock) {
          if (!globalRenamePhis.contains(r)) {
            Register t = ir.regpool.getReg(r);
            Instruction save = SSA.makeMoveInstruction(ir, t, r, tt);
            if (DEBUG) {
              VM.sysWriteln("Inserting " + save + " before " + c.phi + " in " + c.phi.getBasicBlock());
            }
            c.phi.insertAfter(save);
            globalRenamePhis.add(r);
            globalRenameTable.add(save);
          }
        }
        Instruction ci = null;

        // insert copy operation required to remove phi
        if (c.source instanceof ConstantOperand) {
          if (c.source instanceof UnreachableOperand) {
            ci = null;
          } else {
            ci = SSA.makeMoveInstruction(ir, r, (ConstantOperand) c.source);
          }
        } else if (c.source instanceof RegisterOperand) {
          if (shouldSplitBlock) {
            if (DEBUG) VM.sysWriteln("splitting edge: " + bb + "->" + c.phi.getBasicBlock());
            BasicBlock criticalBlock = criticalBlocks.get(c.phi.getBasicBlock());
            if (criticalBlock == null) {
              criticalBlock = IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);
              if (c.phi.getBasicBlock().getInfrequent()) {
                criticalBlock.setInfrequent();
              }
              splitSomeBlock = true;
              criticalBlocks.put(c.phi.getBasicBlock(), criticalBlock);
              HashMap<Register, Register> newNames = new HashMap<Register, Register>(4);
              currentNames.put(criticalBlock, newNames);
            }
            Register sr = c.source.asRegister().getRegister();
            HashMap<Register, Register> criticalBlockNames = currentNames.get(criticalBlock);
            Register nameForSR = criticalBlockNames.get(sr);
            if (nameForSR == null) {
              nameForSR = bbNames.get(sr);
              if (nameForSR == null) nameForSR = sr;
            }
            if (DEBUG) VM.sysWriteln("dest(r): " + r);
            if (DEBUG) VM.sysWriteln("sr: " + sr + ", nameForSR: " + nameForSR);
            ci = SSA.makeMoveInstruction(ir, r, nameForSR, tt);
            criticalBlockNames.put(sr, r);
            criticalBlock.appendInstructionRespectingTerminalBranch(ci);
          } else {
            Register sr = c.source.asRegister().getRegister();
            Register nameForSR = bbNames.get(sr);
            if (nameForSR == null) nameForSR = sr;
            if (DEBUG) VM.sysWriteln("not splitting edge: " + bb + "->" + c.phi.getBasicBlock());
            if (DEBUG) VM.sysWriteln("dest(r): " + r);
            if (DEBUG) VM.sysWriteln("sr: " + sr + ", nameForSR: " + nameForSR);
            ci = SSA.makeMoveInstruction(ir, r, nameForSR, tt);
            bbNames.put(sr, r);
            SSA.addAtEnd(ir, bb, ci, c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
          }
          // ugly hack: having already added ci; set ci to null to skip remaining code;
          ci = null;
        } else {
          throw new OptimizingCompilerException("Unexpected phi operand " +
                                                    c
                                                        .source +
                                                                " encountered during SSA teardown", true);
        }
        if (ci != null) {
          if (shouldSplitBlock) {
            if (DEBUG) VM.sysWriteln("splitting edge: " + bb + "->" + c.phi.getBasicBlock());
            BasicBlock criticalBlock = criticalBlocks.get(c.phi.getBasicBlock());
            if (criticalBlock == null) {
              criticalBlock = IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);
              if (c.phi.getBasicBlock().getInfrequent()) {
                criticalBlock.setInfrequent();
              }
              splitSomeBlock = true;
              criticalBlocks.put(c.phi.getBasicBlock(), criticalBlock);
              HashMap<Register, Register> newNames = new HashMap<Register, Register>(4);
              currentNames.put(criticalBlock, newNames);
            }
            criticalBlock.appendInstructionRespectingTerminalBranch(ci);
          } else {
            SSA.addAtEnd(ir, bb, ci, c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
          }
        }

        // source has been copied and so can now be overwritten
        // safely.  so now add any copies _to_ the source of the
        // current copy to the work list.
        if (c.source instanceof RegisterOperand) {
          Register saved = c.source.asRegister().getRegister();
          Iterator<Copy> copySetIter = copySet.iterator();
          while (copySetIter.hasNext()) {
            Copy cc = copySetIter.next();
            if (cc.destination.asRegister().getRegister() == saved) {
              workList.add(0, cc);
              copySetIter.remove();
            }
          }
        }
      }
      // an empty work list with work remaining in the copy set
      // implies a cycle in the dependencies amongst copies.  deal
      // with this: break the cycle by copying the destination
      // of an arbitrary member of the copy set into a temporary.
      // this destination has thus been saved, and can now be
      // safely overwritten.  so, add that copy to the work list.
      if (!copySet.isEmpty()) {
        Copy c = copySet.remove(0);
        Register tt = ir.regpool.getReg(c.destination.getRegister());
        SSA.addAtEnd(ir,
                         bb,
                         SSA.makeMoveInstruction(ir, tt, c.destination.getRegister(), c.destination.getType()),
                         c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
        bbNames.put(c.destination.getRegister(), tt);
        workList.add(0, c);
      }
    }
  }

  /**
   * Insert copy instructions into a basic block to safely translate out
   * of SSA form.
   *
   * @param bb the basic block
   * @param dom a valid dominator tree for the IR
   * @param live valid liveness information for the IR
   */
  private void insertCopies(BasicBlock bb, DominatorTree dom, LiveAnalysis live) {
    // add copies required in this block to remove phis.
    // (record renaming required by simultaneous liveness in global tables)
    scheduleCopies(bb, live);

    // insert copies in control children
    Enumeration<TreeNode> children = dom.getChildren(bb);
    while (children.hasMoreElements()) {
      BasicBlock c = ((DominatorTreeNode) children.nextElement()).getBlock();
      insertCopies(c, dom, live);
    }
  }

  /**
   * Main driver to translate an IR out of SSA form.
   *
   * @param ir the IR in SSA form
   */
  public void translateFromSSA(IR ir) {
    // 0. Deal with guards (validation registers)
    unSSAGuards(ir);

    // 1. re-compute dominator tree in case of control flow changes
    LTDominators.perform(ir, true, true);
    DominatorTree dom = new DominatorTree(ir, true);

    // 1.5 Perform Sreedhar's naive translation from TSSA to CSSA
    //if (ir.options.UNROLL_LOG == 0) normalizeSSA(ir);

    // 2. compute liveness
    LiveAnalysis live = new LiveAnalysis(false,  // don't create GC maps
                                                 true,   // skip (final) local propagation step
                                                 // of live analysis
                                                 false,  // don't store information at handlers
                                                 false); // dont skip guards

    live.perform(ir);
    // 3. initialization
    VariableStacks s = new VariableStacks();
    // 4. convert phi nodes into copies
    BasicBlock b = ((DominatorTreeNode) dom.getRoot()).getBlock();
    insertCopies(b, dom, live);
    // 5. If necessary, recompute dominators to account for new control flow.
    if (splitSomeBlock) {
      LTDominators.perform(ir, true, true);
      dom = new DominatorTree(ir, true);
    }
    // 6. compensate for copies required by simulataneous liveness
    performRename(b, dom, s);
    // 7. phis are now redundant
    removeAllPhis(ir);
  }

  /**
   * Remove all phi instructions from the IR.
   *
   * @param ir the governing IR
   */
  static void removeAllPhis(IR ir) {
    for (Instruction s = ir.firstInstructionInCodeOrder(),
        sentinel = ir.lastInstructionInCodeOrder(),
        nextInstr = null; s != sentinel; s = nextInstr) {
      // cache because remove nulls next/prev fields
      nextInstr = s.nextInstructionInCodeOrder();
      if (Phi.conforms(s)) s.remove();
    }
  }

  /**
   * Special treatment for guard registers:
   * Remove guard-phis by evaluating operands into same register.
   * If this target register is not unique, unite the alternatives.
   */
  private void unSSAGuards(IR ir) {
    // 0. initialization
    unSSAGuardsInit(ir);
    // 1. Determine target registers
    unSSAGuardsDetermineReg(ir);
    // 2. Rename targets and remove Phis
    unSSAGuardsFinalize(ir);
  }

  Instruction guardPhis = null;

  /**
   * Initialization for removal of guard phis.
   */
  private void unSSAGuardsInit(IR ir) {
    guardPhis = null;
    InstructionEnumeration e = ir.forwardInstrEnumerator();

    // visit all instructions, looking for guard phis

    while (e.hasMoreElements()) {
      Instruction inst = e.next();
      if (!Phi.conforms(inst)) continue;
      Operand res = Phi.getResult(inst);
      if (!(res instanceof RegisterOperand)) continue;
      Register r = res.asRegister().getRegister();
      if (!r.isValidation()) continue;

      // force all operands of Phis into registers.

      inst.scratchObject = guardPhis;
      guardPhis = inst;

      int values = Phi.getNumberOfValues(inst);
      for (int i = 0; i < values; ++i) {
        Operand op = Phi.getValue(inst, i);
        if (!(op instanceof RegisterOperand)) {
          if (op instanceof TrueGuardOperand) {
            BasicBlock bb = Phi.getPred(inst, i).block;
            Instruction move = Move.create(GUARD_MOVE, res.asRegister().copyD2D(), new TrueGuardOperand());
            move.position = ir.gc.inlineSequence;
            move.bcIndex = SSA_SYNTH_BCI;
            bb.appendInstructionRespectingTerminalBranchOrPEI(move);
          } else if (op instanceof UnreachableOperand) {
            // do nothing
          } else {
            if (VM.VerifyAssertions) VM._assert(false);
          }
        }
      }
    }

    // visit all guard registers, init union/find
    for (Register r = ir.regpool.getFirstSymbolicRegister(); r != null; r = r.getNext()) {
      if (!r.isValidation()) continue;
      r.scratch = 1;
      r.scratchObject = r;
    }
  }

  /**
   * Determine target register for guard phi operands
   */
  private void unSSAGuardsDetermineReg(IR ir) {
    Instruction inst = guardPhis;
    while (inst != null) {
      Register r = Phi.getResult(inst).asRegister().getRegister();
      int values = Phi.getNumberOfValues(inst);
      for (int i = 0; i < values; ++i) {
        Operand op = Phi.getValue(inst, i);
        if (op instanceof RegisterOperand) {
          guardUnion(op.asRegister().getRegister(), r);
        } else {
          if (VM.VerifyAssertions) {
            VM._assert(op instanceof TrueGuardOperand || op instanceof UnreachableOperand);
          }
        }
      }
      inst = (Instruction) inst.scratchObject;
    }
  }

  /**
   * Rename registers and delete Phis.
   */
  private void unSSAGuardsFinalize(IR ir) {
    DefUse.computeDU(ir);
    for (Register r = ir.regpool.getFirstSymbolicRegister(); r != null; r = r.getNext()) {
      if (!r.isValidation()) continue;
      Register nreg = guardFind(r);
      RegisterOperandEnumeration uses = DefUse.uses(r);
      while (uses.hasMoreElements()) {
        RegisterOperand use = uses.next();
        use.setRegister(nreg);
      }
      RegisterOperandEnumeration defs = DefUse.defs(r);
      while (defs.hasMoreElements()) {
        RegisterOperand def = defs.next();
        def.setRegister(nreg);
      }
    }
    Instruction inst = guardPhis;
    while (inst != null) {
      inst.remove();
      inst = (Instruction) inst.scratchObject;
    }
  }

  /**
   * union step of union/find for guard registers during unSSA
   */
  private Register guardUnion(Register from, Register to) {
    Register a = guardFind(from);
    Register b = guardFind(to);
    if (a == b) return a;
    if (a.scratch == b.scratch) {
      a.scratch++;
      b.scratchObject = a;
      return a;
    }
    if (a.scratch > b.scratch) {
      b.scratchObject = a;
      return a;
    }
    a.scratchObject = b;
    return b;
  }

  /**
   * find step of union/find for guard registers during unSSA
   */
  private Register guardFind(Register r) {
    Register start = r;
    if (VM.VerifyAssertions) VM._assert(r.scratchObject != null);
    while (r.scratchObject != r) r = (Register) r.scratchObject;
    while (start.scratchObject != r) {
      start.scratchObject = r;
      start = (Register) start.scratchObject;
    }
    return r;
  }

  /**
   * Avoid potential lost copy and other associated problems by
   * Sreedhar's naive translation from TSSA to CSSA. Guards are rather
   * trivial to un-SSA so they have already been removed from the IR.
   * This algorithm is very wasteful of registers so needs good
   * coalescing.
   * @param ir the IR to work upon
   */
  private static void normalizeSSA(IR ir) {
    for (Instruction s = ir.firstInstructionInCodeOrder(),
        sentinel = ir.lastInstructionInCodeOrder(),
        nextInstr = null; s != sentinel; s = nextInstr) {
      // cache so we don't process inserted instructions
      nextInstr = s.nextInstructionInCodeOrder();
      if (Phi.conforms(s) && !s.getBasicBlock().isExceptionHandlerBasicBlock()) {
        // We ignore exception handler BBs as they cause problems when inserting copies
        if (DEBUG) VM.sysWriteln("Processing " + s + " of basic block " + s.getBasicBlock());
        // Does the phi instruction have an unreachable operand?
        boolean hasUnreachable = false;
        // 1. Naively copy source operands into predecessor blocks
        for (int index = 0; index < Phi.getNumberOfPreds(s); index++) {
          Operand op = Phi.getValue(s, index);
          if (op.isRegister()) {
            // Get rval
            Register rval = op.asRegister().getRegister();
            if (rval.isValidation()) {
              continue; // ignore guards
            } else {
              // Make rval'
              Register rvalPrime = ir.regpool.getReg(rval);
              // Make copy instruction
              Instruction copy = SSA.makeMoveInstruction(ir, rvalPrime, rval, op.getType());
              // Insert a copy of rval to rval' in predBlock
              BasicBlock pred = Phi.getPred(s, index).block;
              pred.appendInstructionRespectingTerminalBranch(copy);
              if (DEBUG) VM.sysWriteln("Inserted rval copy of " + copy + " into basic block " + pred);
              // Rename rval to rval' in phi instruction
              op.asRegister().setRegister(rvalPrime);
            }
          } else if (op instanceof UnreachableOperand) {
            hasUnreachable = true;
          }
        }
        // 2. Naively copy the result if there were no unreachable operands
        if (!hasUnreachable) {
          Operand op = Phi.getResult(s);
          if (!op.isRegister()) {
            // ignore heap operands
          } else {
            // Get lval
            Register lval = op.asRegister().getRegister();
            // Make lval'
            Register lvalPrime = ir.regpool.getReg(lval);
            // Make copy instruction
            Instruction copy = SSA.makeMoveInstruction(ir, lval, lvalPrime, op.getType());
            // Insert a copy of lval' to lval after phi instruction
            s.insertAfter(copy);
            // Rename lval to lval' in phi instruction
            op.asRegister().setRegister(lvalPrime);
            if (DEBUG) VM.sysWriteln("Inserted lval copy of " + copy + " after " + s);
          }
        }
      }
    }
  }
}
