}
  private void scheduleCopies(OPT_BasicBlock bb, OPT_LiveAnalysis live) {    if (DEBUG) VM.sysWrite("scheduleCopies: " + bb + "\n");        // compute out liveness from information in LiveAnalysis    OPT_LiveSet out = new OPT_LiveSet();    for (Enumeration outBlocks = bb.getOut(); outBlocks.hasMoreElements(); ) {      OPT_BasicBlock ob = (OPT_BasicBlock)outBlocks.nextElement();      OPT_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 usedByAnother = new HashSet(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 criticalBlocks = new HashMap(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 currentNames = new HashMap(4);        // Additionally store the current names for the current basic block bb.    HashMap bbNames = new HashMap(4);        // copySet is a linked-list of copies we need to insert in this block.    OPT_LinkedListObjectElement copySet = null;    OPT_LinkedListObjectElement workList = null;       // 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 e = bb.getOut();    while (e.hasMoreElements()) {      OPT_BasicBlock bbs = (OPT_BasicBlock)e.nextElement();      if (bbs.isExit()) continue;      for (OPT_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;          OPT_Operand rval = Phi.getValue(phi, index);          if (rval.isRegister() && Phi.getResult(phi).asRegister().register == rval.asRegister().register) {            continue;          }          Copy c = new Copy(phi, index);          copySet = OPT_LinkedListObjectElement.cons(c, copySet);          if (c.source instanceof OPT_RegisterOperand) {            OPT_Register r = c.source.asRegister().register;            usedByAnother.add(r);          }          if (c.destination instanceof OPT_RegisterOperand) {            OPT_Register r = c.destination.asRegister().register;          }        }      }    }    //  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.    OPT_LinkedListObjectElement ptr = OPT_LinkedListObjectElement.cons(null, copySet);    OPT_LinkedListObjectElement head = ptr;    while (ptr.getNext() != null) {      Copy c = (Copy)((OPT_LinkedListObjectElement)ptr.getNext()).getValue();      if (!usedByAnother.contains(c.destination.register)) {        workList = OPT_LinkedListObjectElement.cons(c, workList);        ptr.setNext(ptr.getNext().getNext());      }       else         ptr = (OPT_LinkedListObjectElement)ptr.getNext();    }    copySet = (OPT_LinkedListObjectElement)head.getNext();    // while there is any more work to do.    while (workList != null || copySet != null) {      // while there are copies that can be correctly inserted.      while (workList != null) {        Copy c = (Copy)workList.getValue();        workList = (OPT_LinkedListObjectElement)workList.getNext();        OPT_Register r = c.destination.register;        VM_Type tt = c.destination.type;        if (VM.VerifyAssertions && tt == null) {          tt = OPT_ClassLoaderProxy.IntType;          VM.sysWrite("OPT_SSA, warning: null type in " + c.destination + "\n");        }        OPT_Register rr = null;        if (c.source.isRegister()) rr = c.source.asRegister().register;        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)) {            OPT_Register t = ir.regpool.getReg(r);            OPT_Instruction save = OPT_SSA.makeMoveInstruction(ir, t, r, tt);            if (DEBUG) VM.sysWriteln("Inserting " + save + " before " +                                     c.phi + " in " + c.phi.getBasicBlock());            c.phi.insertFront(save);            globalRenamePhis.add(r);            globalRenameTable.add(save);          }        }        OPT_Instruction ci = null;                // insert copy operation required to remove phi        if (c.source instanceof OPT_NullConstantOperand) {          if (tt.isReferenceType()) {            ci = OPT_SSA.makeMoveInstruction(ir, r, (OPT_ConstantOperand)c.source);          }        }         else if (c.source instanceof OPT_ConstantOperand) {          if (c.source instanceof OPT_UnreachableOperand) {            ci = null;          } else {            ci = OPT_SSA.makeMoveInstruction(ir, r, (OPT_ConstantOperand)c.source);          }        }         else if (c.source instanceof OPT_RegisterOperand) {          if (shouldSplitBlock) {            if (DEBUG) VM.sysWriteln("splitting edge: " + bb + "->" + c.phi.getBasicBlock());            OPT_BasicBlock criticalBlock = (OPT_BasicBlock)criticalBlocks.get(c.phi.getBasicBlock());            if (criticalBlock == null) {              criticalBlock = OPT_IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);              if (c.phi.getBasicBlock().getInfrequent()) {                criticalBlock.setInfrequent();              }              splitSomeBlock = true;              criticalBlocks.put(c.phi.getBasicBlock(),criticalBlock);              HashMap newNames = new HashMap(4);              currentNames.put(criticalBlock,newNames);            }            OPT_Register sr = c.source.asRegister().register;            HashMap criticalBlockNames = (HashMap)currentNames.get(criticalBlock);            OPT_Register nameForSR = (OPT_Register)criticalBlockNames.get(sr);            if (nameForSR == null) nameForSR = sr;            if (DEBUG) VM.sysWriteln("dest(r): " + r);            if (DEBUG) VM.sysWriteln("sr: " + sr + ", nameForSR: " + nameForSR);            ci = OPT_SSA.makeMoveInstruction(ir, r, nameForSR, tt);            criticalBlockNames.put(sr, r);            criticalBlock.appendInstructionRespectingTerminalBranch(ci);          } else {            OPT_Register sr = c.source.asRegister().register;            OPT_Register nameForSR = (OPT_Register)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 = OPT_SSA.makeMoveInstruction(ir, r, nameForSR, tt);            bbNames.put(sr, r);            OPT_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 OPT_OptimizingCompilerException("Unexpected phi operand "                                                     + c.source + " encountered during SSA teardown", true);        }        if (ci != null)          if (shouldSplitBlock) {            OPT_BasicBlock criticalBlock = OPT_IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);            criticalBlock.appendInstructionRespectingTerminalBranch(ci);          } else            OPT_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 OPT_RegisterOperand) {            OPT_Register saved = c.source.asRegister().register;            OPT_LinkedListObjectElement ptr1 = OPT_LinkedListObjectElement.cons(                                                                                null, copySet);            OPT_LinkedListObjectElement head1 = ptr1;            while (ptr1.getNext() != null) {              Copy cc = (Copy)((OPT_LinkedListObjectElement)ptr1.getNext()).                getValue();              if (cc.destination.asRegister().register == saved) {                workList = OPT_LinkedListObjectElement.cons(cc, workList);                ptr1.setNext(ptr1.getNext().getNext());              }               else                 ptr1 = (OPT_LinkedListObjectElement)ptr1.getNext();            }            copySet = (OPT_LinkedListObjectElement)head1.getNext();          }      }      // 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 != null) {        Copy c = (Copy)copySet.getValue();        copySet = (OPT_LinkedListObjectElement)copySet.getNext();        OPT_Register tt = ir.regpool.getReg(c.destination.register);        OPT_SSA.addAtEnd(ir, bb, OPT_SSA.makeMoveInstruction(ir, tt, c.destination.register,                                                              c.destination.type),                                                              c.phi.getBasicBlock().isExceptionHandlerBasicBlock());        bbNames.put(c.destination.register, tt);        workList = OPT_LinkedListObjectElement.cons(c, workList);      }    }  }