1 /*
   2  * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.loop;
  26 
  27 import java.util.ArrayList;
  28 import java.util.LinkedList;
  29 import java.util.List;
  30 
  31 import jdk.internal.vm.compiler.collections.EconomicMap;
  32 import jdk.internal.vm.compiler.collections.Equivalence;
  33 import org.graalvm.compiler.debug.DebugCloseable;
  34 import org.graalvm.compiler.debug.DebugContext;
  35 import org.graalvm.compiler.debug.GraalError;
  36 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
  37 import org.graalvm.compiler.graph.Node;
  38 import org.graalvm.compiler.graph.NodeBitMap;
  39 import org.graalvm.compiler.graph.iterators.NodeIterable;
  40 import org.graalvm.compiler.nodes.AbstractBeginNode;
  41 import org.graalvm.compiler.nodes.AbstractEndNode;
  42 import org.graalvm.compiler.nodes.AbstractMergeNode;
  43 import org.graalvm.compiler.nodes.BeginNode;
  44 import org.graalvm.compiler.nodes.ConstantNode;
  45 import org.graalvm.compiler.nodes.EndNode;
  46 import org.graalvm.compiler.nodes.FixedNode;
  47 import org.graalvm.compiler.nodes.FixedWithNextNode;
  48 import org.graalvm.compiler.nodes.FrameState;
  49 import org.graalvm.compiler.nodes.GuardPhiNode;
  50 import org.graalvm.compiler.nodes.IfNode;
  51 import org.graalvm.compiler.nodes.LogicNode;
  52 import org.graalvm.compiler.nodes.LoopBeginNode;
  53 import org.graalvm.compiler.nodes.LoopEndNode;
  54 import org.graalvm.compiler.nodes.LoopExitNode;
  55 import org.graalvm.compiler.nodes.MergeNode;
  56 import org.graalvm.compiler.nodes.NodeView;
  57 import org.graalvm.compiler.nodes.PhiNode;
  58 import org.graalvm.compiler.nodes.ProxyNode;
  59 import org.graalvm.compiler.nodes.SafepointNode;
  60 import org.graalvm.compiler.nodes.StateSplit;
  61 import org.graalvm.compiler.nodes.StructuredGraph;
  62 import org.graalvm.compiler.nodes.ValueNode;
  63 import org.graalvm.compiler.nodes.ValuePhiNode;
  64 import org.graalvm.compiler.nodes.VirtualState.NodeClosure;
  65 import org.graalvm.compiler.nodes.calc.AddNode;
  66 import org.graalvm.compiler.nodes.calc.CompareNode;
  67 import org.graalvm.compiler.nodes.calc.SubNode;
  68 import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
  69 import org.graalvm.compiler.nodes.util.GraphUtil;
  70 
  71 public class LoopFragmentInside extends LoopFragment {
  72 
  73     /**
  74      * mergedInitializers. When an inside fragment's (loop)ends are merged to create a unique exit
  75      * point, some phis must be created : they phis together all the back-values of the loop-phis
  76      * These can then be used to update the loop-phis' forward edge value ('initializer') in the
  77      * peeling case. In the unrolling case they will be used as the value that replace the loop-phis
  78      * of the duplicated inside fragment
  79      */
  80     private EconomicMap<PhiNode, ValueNode> mergedInitializers;
  81     private final DuplicationReplacement dataFixBefore = new DuplicationReplacement() {
  82 
  83         @Override
  84         public Node replacement(Node oriInput) {
  85             if (!(oriInput instanceof ValueNode)) {
  86                 return oriInput;
  87             }
  88             return prim((ValueNode) oriInput);
  89         }
  90     };
  91 
  92     private final DuplicationReplacement dataFixWithinAfter = new DuplicationReplacement() {
  93 
  94         @Override
  95         public Node replacement(Node oriInput) {
  96             if (!(oriInput instanceof ValueNode)) {
  97                 return oriInput;
  98             }
  99             return primAfter((ValueNode) oriInput);
 100         }
 101     };
 102 
 103     public LoopFragmentInside(LoopEx loop) {
 104         super(loop);
 105     }
 106 
 107     public LoopFragmentInside(LoopFragmentInside original) {
 108         super(null, original);
 109     }
 110 
 111     @Override
 112     public LoopFragmentInside duplicate() {
 113         assert !isDuplicate();
 114         return new LoopFragmentInside(this);
 115     }
 116 
 117     @Override
 118     public LoopFragmentInside original() {
 119         return (LoopFragmentInside) super.original();
 120     }
 121 
 122     @SuppressWarnings("unused")
 123     public void appendInside(LoopEx loop) {
 124         // TODO (gd)
 125     }
 126 
 127     @Override
 128     public LoopEx loop() {
 129         assert !this.isDuplicate();
 130         return super.loop();
 131     }
 132 
 133     @Override
 134     public void insertBefore(LoopEx loop) {
 135         assert this.isDuplicate() && this.original().loop() == loop;
 136 
 137         patchNodes(dataFixBefore);
 138 
 139         AbstractBeginNode end = mergeEnds();
 140 
 141         mergeEarlyExits();
 142 
 143         original().patchPeeling(this);
 144 
 145         AbstractBeginNode entry = getDuplicatedNode(loop.loopBegin());
 146         loop.entryPoint().replaceAtPredecessor(entry);
 147         end.setNext(loop.entryPoint());
 148     }
 149 
 150     /**
 151      * Duplicate the body within the loop after the current copy copy of the body, updating the
 152      * iteration limit to account for the duplication.
 153      *
 154      * @param loop
 155      */
 156     public void insertWithinAfter(LoopEx loop) {
 157         insertWithinAfter(loop, true);
 158     }
 159 
 160     /**
 161      * Duplicate the body within the loop after the current copy copy of the body.
 162      *
 163      * @param loop
 164      * @param updateLimit true if the iteration limit should be adjusted.
 165      */
 166     public void insertWithinAfter(LoopEx loop, boolean updateLimit) {
 167         assert isDuplicate() && original().loop() == loop;
 168 
 169         patchNodes(dataFixWithinAfter);
 170 
 171         /*
 172          * Collect any new back edges values before updating them since they might reference each
 173          * other.
 174          */
 175         LoopBeginNode mainLoopBegin = loop.loopBegin();
 176         ArrayList<ValueNode> backedgeValues = new ArrayList<>();
 177         for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
 178             ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1));
 179             if (duplicatedNode == null) {
 180                 if (mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1))) {
 181                     duplicatedNode = ((PhiNode) (mainPhiNode.valueAt(1))).valueAt(1);
 182                 } else {
 183                     assert mainPhiNode.valueAt(1).isConstant() : mainPhiNode.valueAt(1);
 184                 }
 185             }
 186             backedgeValues.add(duplicatedNode);
 187         }
 188         int index = 0;
 189         for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
 190             ValueNode duplicatedNode = backedgeValues.get(index++);
 191             if (duplicatedNode != null) {
 192                 mainPhiNode.setValueAt(1, duplicatedNode);
 193             }
 194         }
 195 
 196         placeNewSegmentAndCleanup(loop);
 197 
 198         // Remove any safepoints from the original copy leaving only the duplicated one
 199         assert loop.whole().nodes().filter(SafepointNode.class).count() == nodes().filter(SafepointNode.class).count();
 200         for (SafepointNode safepoint : loop.whole().nodes().filter(SafepointNode.class)) {
 201             graph().removeFixed(safepoint);
 202         }
 203 
 204         int unrollFactor = mainLoopBegin.getUnrollFactor();
 205         StructuredGraph graph = mainLoopBegin.graph();
 206         if (updateLimit) {
 207             // Now use the previous unrollFactor to update the exit condition to power of two
 208             InductionVariable iv = loop.counted().getCounter();
 209             CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
 210             ValueNode compareBound;
 211             if (compareNode.getX() == iv.valueNode()) {
 212                 compareBound = compareNode.getY();
 213             } else if (compareNode.getY() == iv.valueNode()) {
 214                 compareBound = compareNode.getX();
 215             } else {
 216                 throw GraalError.shouldNotReachHere();
 217             }
 218             long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor;
 219             if (iv.direction() == InductionVariable.Direction.Up) {
 220                 ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * originalStride));
 221                 ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
 222                 compareNode.replaceFirstInput(compareBound, newLimit);
 223             } else if (iv.direction() == InductionVariable.Direction.Down) {
 224                 ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * -originalStride));
 225                 ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
 226                 compareNode.replaceFirstInput(compareBound, newLimit);
 227             }
 228         }
 229         mainLoopBegin.setUnrollFactor(unrollFactor * 2);
 230         mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
 231         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
 232 
 233         mainLoopBegin.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "After insertWithinAfter %s", mainLoopBegin);
 234     }
 235 
 236     private void placeNewSegmentAndCleanup(LoopEx loop) {
 237         CountedLoopInfo mainCounted = loop.counted();
 238         LoopBeginNode mainLoopBegin = loop.loopBegin();
 239         // Discard the segment entry and its flow, after if merging it into the loop
 240         StructuredGraph graph = mainLoopBegin.graph();
 241         IfNode loopTest = mainCounted.getLimitTest();
 242         IfNode newSegmentTest = getDuplicatedNode(loopTest);
 243         AbstractBeginNode trueSuccessor = loopTest.trueSuccessor();
 244         AbstractBeginNode falseSuccessor = loopTest.falseSuccessor();
 245         FixedNode firstNode;
 246         boolean codeInTrueSide = false;
 247         if (trueSuccessor == mainCounted.getBody()) {
 248             firstNode = trueSuccessor.next();
 249             codeInTrueSide = true;
 250         } else {
 251             assert (falseSuccessor == mainCounted.getBody());
 252             firstNode = falseSuccessor.next();
 253         }
 254         trueSuccessor = newSegmentTest.trueSuccessor();
 255         falseSuccessor = newSegmentTest.falseSuccessor();
 256         for (Node usage : falseSuccessor.anchored().snapshot()) {
 257             usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor());
 258         }
 259         for (Node usage : trueSuccessor.anchored().snapshot()) {
 260             usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor());
 261         }
 262         AbstractBeginNode startBlockNode;
 263         if (codeInTrueSide) {
 264             startBlockNode = trueSuccessor;
 265         } else {
 266             graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before");
 267             startBlockNode = falseSuccessor;
 268         }
 269         FixedNode lastNode = getBlockEnd(startBlockNode);
 270         LoopEndNode loopEndNode = mainLoopBegin.getSingleLoopEnd();
 271         FixedWithNextNode lastCodeNode = (FixedWithNextNode) loopEndNode.predecessor();
 272         FixedNode newSegmentFirstNode = getDuplicatedNode(firstNode);
 273         FixedWithNextNode newSegmentLastNode = getDuplicatedNode(lastCodeNode);
 274         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
 275         if (firstNode instanceof LoopEndNode) {
 276             GraphUtil.killCFG(getDuplicatedNode(mainLoopBegin));
 277         } else {
 278             newSegmentLastNode.clearSuccessors();
 279             startBlockNode.setNext(lastNode);
 280             lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
 281             newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
 282             lastCodeNode.setNext(newSegmentFirstNode);
 283             newSegmentLastNode.setNext(loopEndNode);
 284             startBlockNode.clearSuccessors();
 285             lastNode.safeDelete();
 286             Node newSegmentTestStart = newSegmentTest.predecessor();
 287             LogicNode newSegmentIfTest = newSegmentTest.condition();
 288             newSegmentTestStart.clearSuccessors();
 289             newSegmentTest.safeDelete();
 290             newSegmentIfTest.safeDelete();
 291             trueSuccessor.safeDelete();
 292             falseSuccessor.safeDelete();
 293             newSegmentTestStart.safeDelete();
 294         }
 295         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment");
 296     }
 297 
 298     private static EndNode getBlockEnd(FixedNode node) {
 299         FixedNode curNode = node;
 300         while (curNode instanceof FixedWithNextNode) {
 301             curNode = ((FixedWithNextNode) curNode).next();
 302         }
 303         return (EndNode) curNode;
 304     }
 305 
 306     @Override
 307     public NodeBitMap nodes() {
 308         if (nodes == null) {
 309             LoopFragmentWhole whole = loop().whole();
 310             whole.nodes(); // init nodes bitmap in whole
 311             nodes = whole.nodes.copy();
 312             // remove the phis
 313             LoopBeginNode loopBegin = loop().loopBegin();
 314             for (PhiNode phi : loopBegin.phis()) {
 315                 nodes.clear(phi);
 316             }
 317             clearStateNodes(loopBegin);
 318             for (LoopExitNode exit : exits()) {
 319                 clearStateNodes(exit);
 320                 for (ProxyNode proxy : exit.proxies()) {
 321                     nodes.clear(proxy);
 322                 }
 323             }
 324         }
 325         return nodes;
 326     }
 327 
 328     private void clearStateNodes(StateSplit stateSplit) {
 329         FrameState loopState = stateSplit.stateAfter();
 330         if (loopState != null) {
 331             loopState.applyToVirtual(v -> {
 332                 if (v.usages().filter(n -> nodes.isMarked(n) && n != stateSplit).isEmpty()) {
 333                     nodes.clear(v);
 334                 }
 335             });
 336         }
 337     }
 338 
 339     public NodeIterable<LoopExitNode> exits() {
 340         return loop().loopBegin().loopExits();
 341     }
 342 
 343     @Override
 344     @SuppressWarnings("try")
 345     protected DuplicationReplacement getDuplicationReplacement() {
 346         final LoopBeginNode loopBegin = loop().loopBegin();
 347         final StructuredGraph graph = graph();
 348         return new DuplicationReplacement() {
 349 
 350             private EconomicMap<Node, Node> seenNode = EconomicMap.create(Equivalence.IDENTITY);
 351 
 352             @Override
 353             public Node replacement(Node original) {
 354                 try (DebugCloseable position = original.withNodeSourcePosition()) {
 355                     if (original == loopBegin) {
 356                         Node value = seenNode.get(original);
 357                         if (value != null) {
 358                             return value;
 359                         }
 360                         AbstractBeginNode newValue = graph.add(new BeginNode());
 361                         seenNode.put(original, newValue);
 362                         return newValue;
 363                     }
 364                     if (original instanceof LoopExitNode && ((LoopExitNode) original).loopBegin() == loopBegin) {
 365                         Node value = seenNode.get(original);
 366                         if (value != null) {
 367                             return value;
 368                         }
 369                         AbstractBeginNode newValue = graph.add(new BeginNode());
 370                         seenNode.put(original, newValue);
 371                         return newValue;
 372                     }
 373                     if (original instanceof LoopEndNode && ((LoopEndNode) original).loopBegin() == loopBegin) {
 374                         Node value = seenNode.get(original);
 375                         if (value != null) {
 376                             return value;
 377                         }
 378                         EndNode newValue = graph.add(new EndNode());
 379                         seenNode.put(original, newValue);
 380                         return newValue;
 381                     }
 382                     return original;
 383                 }
 384             }
 385         };
 386     }
 387 
 388     @Override
 389     protected void finishDuplication() {
 390         // TODO (gd) ?
 391     }
 392 
 393     @Override
 394     protected void beforeDuplication() {
 395         // Nothing to do
 396     }
 397 
 398     private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) {
 399         PhiNode ret;
 400         if (phi instanceof ValuePhiNode) {
 401             ret = new ValuePhiNode(phi.stamp(NodeView.DEFAULT), merge);
 402         } else if (phi instanceof GuardPhiNode) {
 403             ret = new GuardPhiNode(merge);
 404         } else if (phi instanceof MemoryPhiNode) {
 405             ret = new MemoryPhiNode(merge, ((MemoryPhiNode) phi).getLocationIdentity());
 406         } else {
 407             throw GraalError.shouldNotReachHere();
 408         }
 409         return graph.addWithoutUnique(ret);
 410     }
 411 
 412     private void patchPeeling(LoopFragmentInside peel) {
 413         LoopBeginNode loopBegin = loop().loopBegin();
 414         StructuredGraph graph = loopBegin.graph();
 415         List<PhiNode> newPhis = new LinkedList<>();
 416 
 417         NodeBitMap usagesToPatch = nodes.copy();
 418         for (LoopExitNode exit : exits()) {
 419             markStateNodes(exit, usagesToPatch);
 420             for (ProxyNode proxy : exit.proxies()) {
 421                 usagesToPatch.markAndGrow(proxy);
 422             }
 423         }
 424         markStateNodes(loopBegin, usagesToPatch);
 425 
 426         List<PhiNode> oldPhis = loopBegin.phis().snapshot();
 427         for (PhiNode phi : oldPhis) {
 428             if (phi.hasNoUsages()) {
 429                 continue;
 430             }
 431             ValueNode first;
 432             if (loopBegin.loopEnds().count() == 1) {
 433                 ValueNode b = phi.valueAt(loopBegin.loopEnds().first()); // back edge value
 434                 first = peel.prim(b); // corresponding value in the peel
 435             } else {
 436                 first = peel.mergedInitializers.get(phi);
 437             }
 438             // create a new phi (we don't patch the old one since some usages of the old one may
 439             // still be valid)
 440             PhiNode newPhi = patchPhi(graph, phi, loopBegin);
 441             newPhi.addInput(first);
 442             for (LoopEndNode end : loopBegin.orderedLoopEnds()) {
 443                 newPhi.addInput(phi.valueAt(end));
 444             }
 445             peel.putDuplicatedNode(phi, newPhi);
 446             newPhis.add(newPhi);
 447             for (Node usage : phi.usages().snapshot()) {
 448                 // patch only usages that should use the new phi ie usages that were peeled
 449                 if (usagesToPatch.isMarkedAndGrow(usage)) {
 450                     usage.replaceFirstInput(phi, newPhi);
 451                 }
 452             }
 453         }
 454         // check new phis to see if they have as input some old phis, replace those inputs with the
 455         // new corresponding phis
 456         for (PhiNode phi : newPhis) {
 457             for (int i = 0; i < phi.valueCount(); i++) {
 458                 ValueNode v = phi.valueAt(i);
 459                 if (loopBegin.isPhiAtMerge(v)) {
 460                     PhiNode newV = peel.getDuplicatedNode((ValuePhiNode) v);
 461                     if (newV != null) {
 462                         phi.setValueAt(i, newV);
 463                     }
 464                 }
 465             }
 466         }
 467 
 468         boolean progress = true;
 469         while (progress) {
 470             progress = false;
 471             int i = 0;
 472             outer: while (i < oldPhis.size()) {
 473                 PhiNode oldPhi = oldPhis.get(i);
 474                 for (Node usage : oldPhi.usages()) {
 475                     if (usage instanceof PhiNode && oldPhis.contains(usage)) {
 476                         // Do not mark.
 477                     } else {
 478                         // Mark alive by removing from delete set.
 479                         oldPhis.remove(i);
 480                         progress = true;
 481                         continue outer;
 482                     }
 483                 }
 484                 i++;
 485             }
 486         }
 487 
 488         for (PhiNode deadPhi : oldPhis) {
 489             deadPhi.clearInputs();
 490         }
 491 
 492         for (PhiNode deadPhi : oldPhis) {
 493             if (deadPhi.isAlive()) {
 494                 GraphUtil.killWithUnusedFloatingInputs(deadPhi);
 495             }
 496         }
 497     }
 498 
 499     private static void markStateNodes(StateSplit stateSplit, NodeBitMap marks) {
 500         FrameState exitState = stateSplit.stateAfter();
 501         if (exitState != null) {
 502             exitState.applyToVirtual(v -> marks.markAndGrow(v));
 503         }
 504     }
 505 
 506     /**
 507      * Gets the corresponding value in this fragment.
 508      *
 509      * @param b original value
 510      * @return corresponding value in the peel
 511      */
 512     @Override
 513     protected ValueNode prim(ValueNode b) {
 514         assert isDuplicate();
 515         LoopBeginNode loopBegin = original().loop().loopBegin();
 516         if (loopBegin.isPhiAtMerge(b)) {
 517             PhiNode phi = (PhiNode) b;
 518             return phi.valueAt(loopBegin.forwardEnd());
 519         } else if (nodesReady) {
 520             ValueNode v = getDuplicatedNode(b);
 521             if (v == null) {
 522                 return b;
 523             }
 524             return v;
 525         } else {
 526             return b;
 527         }
 528     }
 529 
 530     protected ValueNode primAfter(ValueNode b) {
 531         assert isDuplicate();
 532         LoopBeginNode loopBegin = original().loop().loopBegin();
 533         if (loopBegin.isPhiAtMerge(b)) {
 534             PhiNode phi = (PhiNode) b;
 535             assert phi.valueCount() == 2;
 536             return phi.valueAt(1);
 537         } else if (nodesReady) {
 538             ValueNode v = getDuplicatedNode(b);
 539             if (v == null) {
 540                 return b;
 541             }
 542             return v;
 543         } else {
 544             return b;
 545         }
 546     }
 547 
 548     @SuppressWarnings("try")
 549     private AbstractBeginNode mergeEnds() {
 550         assert isDuplicate();
 551         List<EndNode> endsToMerge = new LinkedList<>();
 552         // map peel exits to the corresponding loop exits
 553         EconomicMap<AbstractEndNode, LoopEndNode> reverseEnds = EconomicMap.create(Equivalence.IDENTITY);
 554         LoopBeginNode loopBegin = original().loop().loopBegin();
 555         for (LoopEndNode le : loopBegin.loopEnds()) {
 556             AbstractEndNode duplicate = getDuplicatedNode(le);
 557             if (duplicate != null) {
 558                 endsToMerge.add((EndNode) duplicate);
 559                 reverseEnds.put(duplicate, le);
 560             }
 561         }
 562         mergedInitializers = EconomicMap.create(Equivalence.IDENTITY);
 563         AbstractBeginNode newExit;
 564         StructuredGraph graph = graph();
 565         if (endsToMerge.size() == 1) {
 566             AbstractEndNode end = endsToMerge.get(0);
 567             assert end.hasNoUsages();
 568             try (DebugCloseable position = end.withNodeSourcePosition()) {
 569                 newExit = graph.add(new BeginNode());
 570                 end.replaceAtPredecessor(newExit);
 571                 end.safeDelete();
 572             }
 573         } else {
 574             assert endsToMerge.size() > 1;
 575             AbstractMergeNode newExitMerge = graph.add(new MergeNode());
 576             newExit = newExitMerge;
 577             FrameState state = loopBegin.stateAfter();
 578             FrameState duplicateState = null;
 579             if (state != null) {
 580                 duplicateState = state.duplicateWithVirtualState();
 581                 newExitMerge.setStateAfter(duplicateState);
 582             }
 583             for (EndNode end : endsToMerge) {
 584                 newExitMerge.addForwardEnd(end);
 585             }
 586 
 587             for (final PhiNode phi : loopBegin.phis().snapshot()) {
 588                 if (phi.hasNoUsages()) {
 589                     continue;
 590                 }
 591                 final PhiNode firstPhi = patchPhi(graph, phi, newExitMerge);
 592                 for (AbstractEndNode end : newExitMerge.forwardEnds()) {
 593                     LoopEndNode loopEnd = reverseEnds.get(end);
 594                     ValueNode prim = prim(phi.valueAt(loopEnd));
 595                     assert prim != null;
 596                     firstPhi.addInput(prim);
 597                 }
 598                 ValueNode initializer = firstPhi;
 599                 if (duplicateState != null) {
 600                     // fix the merge's state after
 601                     duplicateState.applyToNonVirtual(new NodeClosure<ValueNode>() {
 602 
 603                         @Override
 604                         public void apply(Node from, ValueNode node) {
 605                             if (node == phi) {
 606                                 from.replaceFirstInput(phi, firstPhi);
 607                             }
 608                         }
 609                     });
 610                 }
 611                 mergedInitializers.put(phi, initializer);
 612             }
 613         }
 614         return newExit;
 615     }
 616 }