/*
 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package org.graalvm.compiler.loop;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import jdk.internal.vm.compiler.collections.EconomicMap;
import jdk.internal.vm.compiler.collections.Equivalence;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeBitMap;
import org.graalvm.compiler.graph.iterators.NodeIterable;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractEndNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.EndNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.GuardPhiNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.LoopEndNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.MergeNode;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.ProxyNode;
import org.graalvm.compiler.nodes.SafepointNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.VirtualState.NodeClosure;
import org.graalvm.compiler.nodes.calc.AddNode;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.calc.SubNode;
import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
import org.graalvm.compiler.nodes.util.GraphUtil;

public class LoopFragmentInside extends LoopFragment {

    
mergedInitializers. When an inside fragment's (loop)ends are merged to create a unique exit point, some phis must be created : they phis together all the back-values of the loop-phis These can then be used to update the loop-phis' forward edge value ('initializer') in the peeling case. In the unrolling case they will be used as the value that replace the loop-phis of the duplicated inside fragment
/** * mergedInitializers. When an inside fragment's (loop)ends are merged to create a unique exit * point, some phis must be created : they phis together all the back-values of the loop-phis * These can then be used to update the loop-phis' forward edge value ('initializer') in the * peeling case. In the unrolling case they will be used as the value that replace the loop-phis * of the duplicated inside fragment */
private EconomicMap<PhiNode, ValueNode> mergedInitializers; private final DuplicationReplacement dataFixBefore = new DuplicationReplacement() { @Override public Node replacement(Node oriInput) { if (!(oriInput instanceof ValueNode)) { return oriInput; } return prim((ValueNode) oriInput); } }; private final DuplicationReplacement dataFixWithinAfter = new DuplicationReplacement() { @Override public Node replacement(Node oriInput) { if (!(oriInput instanceof ValueNode)) { return oriInput; } return primAfter((ValueNode) oriInput); } }; public LoopFragmentInside(LoopEx loop) { super(loop); } public LoopFragmentInside(LoopFragmentInside original) { super(null, original); } @Override public LoopFragmentInside duplicate() { assert !isDuplicate(); return new LoopFragmentInside(this); } @Override public LoopFragmentInside original() { return (LoopFragmentInside) super.original(); } @SuppressWarnings("unused") public void appendInside(LoopEx loop) { // TODO (gd) } @Override public LoopEx loop() { assert !this.isDuplicate(); return super.loop(); } @Override public void insertBefore(LoopEx loop) { assert this.isDuplicate() && this.original().loop() == loop; patchNodes(dataFixBefore); AbstractBeginNode end = mergeEnds(); mergeEarlyExits(); original().patchPeeling(this); AbstractBeginNode entry = getDuplicatedNode(loop.loopBegin()); loop.entryPoint().replaceAtPredecessor(entry); end.setNext(loop.entryPoint()); }
Duplicate the body within the loop after the current copy copy of the body, updating the iteration limit to account for the duplication.
Params:
  • loop –
/** * Duplicate the body within the loop after the current copy copy of the body, updating the * iteration limit to account for the duplication. * * @param loop */
public void insertWithinAfter(LoopEx loop) { insertWithinAfter(loop, true); }
Duplicate the body within the loop after the current copy copy of the body.
Params:
  • loop –
  • updateLimit – true if the iteration limit should be adjusted.
/** * Duplicate the body within the loop after the current copy copy of the body. * * @param loop * @param updateLimit true if the iteration limit should be adjusted. */
public void insertWithinAfter(LoopEx loop, boolean updateLimit) { assert isDuplicate() && original().loop() == loop; patchNodes(dataFixWithinAfter); /* * Collect any new back edges values before updating them since they might reference each * other. */ LoopBeginNode mainLoopBegin = loop.loopBegin(); ArrayList<ValueNode> backedgeValues = new ArrayList<>(); for (PhiNode mainPhiNode : mainLoopBegin.phis()) { ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1)); if (duplicatedNode == null) { if (mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1))) { duplicatedNode = ((PhiNode) (mainPhiNode.valueAt(1))).valueAt(1); } else { assert mainPhiNode.valueAt(1).isConstant() : mainPhiNode.valueAt(1); } } backedgeValues.add(duplicatedNode); } int index = 0; for (PhiNode mainPhiNode : mainLoopBegin.phis()) { ValueNode duplicatedNode = backedgeValues.get(index++); if (duplicatedNode != null) { mainPhiNode.setValueAt(1, duplicatedNode); } } placeNewSegmentAndCleanup(loop); // Remove any safepoints from the original copy leaving only the duplicated one assert loop.whole().nodes().filter(SafepointNode.class).count() == nodes().filter(SafepointNode.class).count(); for (SafepointNode safepoint : loop.whole().nodes().filter(SafepointNode.class)) { graph().removeFixed(safepoint); } int unrollFactor = mainLoopBegin.getUnrollFactor(); StructuredGraph graph = mainLoopBegin.graph(); if (updateLimit) { // Now use the previous unrollFactor to update the exit condition to power of two InductionVariable iv = loop.counted().getCounter(); CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition(); ValueNode compareBound; if (compareNode.getX() == iv.valueNode()) { compareBound = compareNode.getY(); } else if (compareNode.getY() == iv.valueNode()) { compareBound = compareNode.getX(); } else { throw GraalError.shouldNotReachHere(); } long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor; if (iv.direction() == InductionVariable.Direction.Up) { ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * originalStride)); ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal)); compareNode.replaceFirstInput(compareBound, newLimit); } else if (iv.direction() == InductionVariable.Direction.Down) { ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * -originalStride)); ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal)); compareNode.replaceFirstInput(compareBound, newLimit); } } mainLoopBegin.setUnrollFactor(unrollFactor * 2); mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2); graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop); mainLoopBegin.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "After insertWithinAfter %s", mainLoopBegin); } private void placeNewSegmentAndCleanup(LoopEx loop) { CountedLoopInfo mainCounted = loop.counted(); LoopBeginNode mainLoopBegin = loop.loopBegin(); // Discard the segment entry and its flow, after if merging it into the loop StructuredGraph graph = mainLoopBegin.graph(); IfNode loopTest = mainCounted.getLimitTest(); IfNode newSegmentTest = getDuplicatedNode(loopTest); AbstractBeginNode trueSuccessor = loopTest.trueSuccessor(); AbstractBeginNode falseSuccessor = loopTest.falseSuccessor(); FixedNode firstNode; boolean codeInTrueSide = false; if (trueSuccessor == mainCounted.getBody()) { firstNode = trueSuccessor.next(); codeInTrueSide = true; } else { assert (falseSuccessor == mainCounted.getBody()); firstNode = falseSuccessor.next(); } trueSuccessor = newSegmentTest.trueSuccessor(); falseSuccessor = newSegmentTest.falseSuccessor(); for (Node usage : falseSuccessor.anchored().snapshot()) { usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor()); } for (Node usage : trueSuccessor.anchored().snapshot()) { usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor()); } AbstractBeginNode startBlockNode; if (codeInTrueSide) { startBlockNode = trueSuccessor; } else { graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before"); startBlockNode = falseSuccessor; } FixedNode lastNode = getBlockEnd(startBlockNode); LoopEndNode loopEndNode = mainLoopBegin.getSingleLoopEnd(); FixedWithNextNode lastCodeNode = (FixedWithNextNode) loopEndNode.predecessor(); FixedNode newSegmentFirstNode = getDuplicatedNode(firstNode); FixedWithNextNode newSegmentLastNode = getDuplicatedNode(lastCodeNode); graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment"); if (firstNode instanceof LoopEndNode) { GraphUtil.killCFG(getDuplicatedNode(mainLoopBegin)); } else { newSegmentLastNode.clearSuccessors(); startBlockNode.setNext(lastNode); lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode); newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode); lastCodeNode.setNext(newSegmentFirstNode); newSegmentLastNode.setNext(loopEndNode); startBlockNode.clearSuccessors(); lastNode.safeDelete(); Node newSegmentTestStart = newSegmentTest.predecessor(); LogicNode newSegmentIfTest = newSegmentTest.condition(); newSegmentTestStart.clearSuccessors(); newSegmentTest.safeDelete(); newSegmentIfTest.safeDelete(); trueSuccessor.safeDelete(); falseSuccessor.safeDelete(); newSegmentTestStart.safeDelete(); } graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment"); } private static EndNode getBlockEnd(FixedNode node) { FixedNode curNode = node; while (curNode instanceof FixedWithNextNode) { curNode = ((FixedWithNextNode) curNode).next(); } return (EndNode) curNode; } @Override public NodeBitMap nodes() { if (nodes == null) { LoopFragmentWhole whole = loop().whole(); whole.nodes(); // init nodes bitmap in whole nodes = whole.nodes.copy(); // remove the phis LoopBeginNode loopBegin = loop().loopBegin(); for (PhiNode phi : loopBegin.phis()) { nodes.clear(phi); } clearStateNodes(loopBegin); for (LoopExitNode exit : exits()) { clearStateNodes(exit); for (ProxyNode proxy : exit.proxies()) { nodes.clear(proxy); } } } return nodes; } private void clearStateNodes(StateSplit stateSplit) { FrameState loopState = stateSplit.stateAfter(); if (loopState != null) { loopState.applyToVirtual(v -> { if (v.usages().filter(n -> nodes.isMarked(n) && n != stateSplit).isEmpty()) { nodes.clear(v); } }); } } public NodeIterable<LoopExitNode> exits() { return loop().loopBegin().loopExits(); } @Override @SuppressWarnings("try") protected DuplicationReplacement getDuplicationReplacement() { final LoopBeginNode loopBegin = loop().loopBegin(); final StructuredGraph graph = graph(); return new DuplicationReplacement() { private EconomicMap<Node, Node> seenNode = EconomicMap.create(Equivalence.IDENTITY); @Override public Node replacement(Node original) { try (DebugCloseable position = original.withNodeSourcePosition()) { if (original == loopBegin) { Node value = seenNode.get(original); if (value != null) { return value; } AbstractBeginNode newValue = graph.add(new BeginNode()); seenNode.put(original, newValue); return newValue; } if (original instanceof LoopExitNode && ((LoopExitNode) original).loopBegin() == loopBegin) { Node value = seenNode.get(original); if (value != null) { return value; } AbstractBeginNode newValue = graph.add(new BeginNode()); seenNode.put(original, newValue); return newValue; } if (original instanceof LoopEndNode && ((LoopEndNode) original).loopBegin() == loopBegin) { Node value = seenNode.get(original); if (value != null) { return value; } EndNode newValue = graph.add(new EndNode()); seenNode.put(original, newValue); return newValue; } return original; } } }; } @Override protected void finishDuplication() { // TODO (gd) ? } @Override protected void beforeDuplication() { // Nothing to do } private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) { PhiNode ret; if (phi instanceof ValuePhiNode) { ret = new ValuePhiNode(phi.stamp(NodeView.DEFAULT), merge); } else if (phi instanceof GuardPhiNode) { ret = new GuardPhiNode(merge); } else if (phi instanceof MemoryPhiNode) { ret = new MemoryPhiNode(merge, ((MemoryPhiNode) phi).getLocationIdentity()); } else { throw GraalError.shouldNotReachHere(); } return graph.addWithoutUnique(ret); } private void patchPeeling(LoopFragmentInside peel) { LoopBeginNode loopBegin = loop().loopBegin(); StructuredGraph graph = loopBegin.graph(); List<PhiNode> newPhis = new LinkedList<>(); NodeBitMap usagesToPatch = nodes.copy(); for (LoopExitNode exit : exits()) { markStateNodes(exit, usagesToPatch); for (ProxyNode proxy : exit.proxies()) { usagesToPatch.markAndGrow(proxy); } } markStateNodes(loopBegin, usagesToPatch); List<PhiNode> oldPhis = loopBegin.phis().snapshot(); for (PhiNode phi : oldPhis) { if (phi.hasNoUsages()) { continue; } ValueNode first; if (loopBegin.loopEnds().count() == 1) { ValueNode b = phi.valueAt(loopBegin.loopEnds().first()); // back edge value first = peel.prim(b); // corresponding value in the peel } else { first = peel.mergedInitializers.get(phi); } // create a new phi (we don't patch the old one since some usages of the old one may // still be valid) PhiNode newPhi = patchPhi(graph, phi, loopBegin); newPhi.addInput(first); for (LoopEndNode end : loopBegin.orderedLoopEnds()) { newPhi.addInput(phi.valueAt(end)); } peel.putDuplicatedNode(phi, newPhi); newPhis.add(newPhi); for (Node usage : phi.usages().snapshot()) { // patch only usages that should use the new phi ie usages that were peeled if (usagesToPatch.isMarkedAndGrow(usage)) { usage.replaceFirstInput(phi, newPhi); } } } // check new phis to see if they have as input some old phis, replace those inputs with the // new corresponding phis for (PhiNode phi : newPhis) { for (int i = 0; i < phi.valueCount(); i++) { ValueNode v = phi.valueAt(i); if (loopBegin.isPhiAtMerge(v)) { PhiNode newV = peel.getDuplicatedNode((ValuePhiNode) v); if (newV != null) { phi.setValueAt(i, newV); } } } } boolean progress = true; while (progress) { progress = false; int i = 0; outer: while (i < oldPhis.size()) { PhiNode oldPhi = oldPhis.get(i); for (Node usage : oldPhi.usages()) { if (usage instanceof PhiNode && oldPhis.contains(usage)) { // Do not mark. } else { // Mark alive by removing from delete set. oldPhis.remove(i); progress = true; continue outer; } } i++; } } for (PhiNode deadPhi : oldPhis) { deadPhi.clearInputs(); } for (PhiNode deadPhi : oldPhis) { if (deadPhi.isAlive()) { GraphUtil.killWithUnusedFloatingInputs(deadPhi); } } } private static void markStateNodes(StateSplit stateSplit, NodeBitMap marks) { FrameState exitState = stateSplit.stateAfter(); if (exitState != null) { exitState.applyToVirtual(v -> marks.markAndGrow(v)); } }
Gets the corresponding value in this fragment.
Params:
  • b – original value
Returns:corresponding value in the peel
/** * Gets the corresponding value in this fragment. * * @param b original value * @return corresponding value in the peel */
@Override protected ValueNode prim(ValueNode b) { assert isDuplicate(); LoopBeginNode loopBegin = original().loop().loopBegin(); if (loopBegin.isPhiAtMerge(b)) { PhiNode phi = (PhiNode) b; return phi.valueAt(loopBegin.forwardEnd()); } else if (nodesReady) { ValueNode v = getDuplicatedNode(b); if (v == null) { return b; } return v; } else { return b; } } protected ValueNode primAfter(ValueNode b) { assert isDuplicate(); LoopBeginNode loopBegin = original().loop().loopBegin(); if (loopBegin.isPhiAtMerge(b)) { PhiNode phi = (PhiNode) b; assert phi.valueCount() == 2; return phi.valueAt(1); } else if (nodesReady) { ValueNode v = getDuplicatedNode(b); if (v == null) { return b; } return v; } else { return b; } } @SuppressWarnings("try") private AbstractBeginNode mergeEnds() { assert isDuplicate(); List<EndNode> endsToMerge = new LinkedList<>(); // map peel exits to the corresponding loop exits EconomicMap<AbstractEndNode, LoopEndNode> reverseEnds = EconomicMap.create(Equivalence.IDENTITY); LoopBeginNode loopBegin = original().loop().loopBegin(); for (LoopEndNode le : loopBegin.loopEnds()) { AbstractEndNode duplicate = getDuplicatedNode(le); if (duplicate != null) { endsToMerge.add((EndNode) duplicate); reverseEnds.put(duplicate, le); } } mergedInitializers = EconomicMap.create(Equivalence.IDENTITY); AbstractBeginNode newExit; StructuredGraph graph = graph(); if (endsToMerge.size() == 1) { AbstractEndNode end = endsToMerge.get(0); assert end.hasNoUsages(); try (DebugCloseable position = end.withNodeSourcePosition()) { newExit = graph.add(new BeginNode()); end.replaceAtPredecessor(newExit); end.safeDelete(); } } else { assert endsToMerge.size() > 1; AbstractMergeNode newExitMerge = graph.add(new MergeNode()); newExit = newExitMerge; FrameState state = loopBegin.stateAfter(); FrameState duplicateState = null; if (state != null) { duplicateState = state.duplicateWithVirtualState(); newExitMerge.setStateAfter(duplicateState); } for (EndNode end : endsToMerge) { newExitMerge.addForwardEnd(end); } for (final PhiNode phi : loopBegin.phis().snapshot()) { if (phi.hasNoUsages()) { continue; } final PhiNode firstPhi = patchPhi(graph, phi, newExitMerge); for (AbstractEndNode end : newExitMerge.forwardEnds()) { LoopEndNode loopEnd = reverseEnds.get(end); ValueNode prim = prim(phi.valueAt(loopEnd)); assert prim != null; firstPhi.addInput(prim); } ValueNode initializer = firstPhi; if (duplicateState != null) { // fix the merge's state after duplicateState.applyToNonVirtual(new NodeClosure<ValueNode>() { @Override public void apply(Node from, ValueNode node) { if (node == phi) { from.replaceFirstInput(phi, firstPhi); } } }); } mergedInitializers.put(phi, initializer); } } return newExit; } }