--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java 2016-12-09 00:54:40.983051394 -0800 @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2012, 2015, 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 static org.graalvm.compiler.graph.Node.newIdentityMap; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; + +import org.graalvm.compiler.core.common.calc.Condition; +import org.graalvm.compiler.core.common.cfg.Loop; +import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeBitMap; +import org.graalvm.compiler.graph.iterators.NodePredicate; +import org.graalvm.compiler.loop.InductionVariable.Direction; +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.AbstractEndNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedGuardNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.FullInfopointNode; +import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.PhiNode; +import org.graalvm.compiler.nodes.PiNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.ValuePhiNode; +import org.graalvm.compiler.nodes.calc.AddNode; +import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; +import org.graalvm.compiler.nodes.calc.CompareNode; +import org.graalvm.compiler.nodes.calc.IntegerBelowNode; +import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; +import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; +import org.graalvm.compiler.nodes.calc.LeftShiftNode; +import org.graalvm.compiler.nodes.calc.MulNode; +import org.graalvm.compiler.nodes.calc.NegateNode; +import org.graalvm.compiler.nodes.calc.SignExtendNode; +import org.graalvm.compiler.nodes.calc.SubNode; +import org.graalvm.compiler.nodes.calc.ZeroExtendNode; +import org.graalvm.compiler.nodes.cfg.Block; +import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; +import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode; +import org.graalvm.compiler.nodes.extended.ValueAnchorNode; +import org.graalvm.compiler.nodes.util.GraphUtil; + +import jdk.vm.ci.code.BytecodeFrame; + +public class LoopEx { + + private final Loop loop; + private LoopFragmentInside inside; + private LoopFragmentWhole whole; + private CountedLoopInfo counted; + private LoopsData data; + private Map ivs; + + LoopEx(Loop loop, LoopsData data) { + this.loop = loop; + this.data = data; + } + + public Loop loop() { + return loop; + } + + public LoopFragmentInside inside() { + if (inside == null) { + inside = new LoopFragmentInside(this); + } + return inside; + } + + public LoopFragmentWhole whole() { + if (whole == null) { + whole = new LoopFragmentWhole(this); + } + return whole; + } + + public void invalidateFragments() { + inside = null; + whole = null; + } + + @SuppressWarnings("unused") + public LoopFragmentInsideFrom insideFrom(FixedNode point) { + // TODO (gd) + return null; + } + + @SuppressWarnings("unused") + public LoopFragmentInsideBefore insideBefore(FixedNode point) { + // TODO (gd) + return null; + } + + public boolean isOutsideLoop(Node n) { + return !whole().contains(n); + } + + public LoopBeginNode loopBegin() { + return (LoopBeginNode) loop().getHeader().getBeginNode(); + } + + public FixedNode predecessor() { + return (FixedNode) loopBegin().forwardEnd().predecessor(); + } + + public FixedNode entryPoint() { + return loopBegin().forwardEnd(); + } + + public boolean isCounted() { + return counted != null; + } + + public CountedLoopInfo counted() { + return counted; + } + + public LoopEx parent() { + if (loop.getParent() == null) { + return null; + } + return data.loop(loop.getParent()); + } + + public int size() { + return whole().nodes().count(); + } + + @Override + public String toString() { + return (isCounted() ? "CountedLoop [" + counted() + "] " : "Loop ") + "(depth=" + loop().getDepth() + ") " + loopBegin(); + } + + private class InvariantPredicate implements NodePredicate { + + @Override + public boolean apply(Node n) { + return isOutsideLoop(n); + } + } + + public void reassociateInvariants() { + InvariantPredicate invariant = new InvariantPredicate(); + StructuredGraph graph = loopBegin().graph(); + for (BinaryArithmeticNode binary : whole().nodes().filter(BinaryArithmeticNode.class)) { + if (!binary.isAssociative()) { + continue; + } + BinaryArithmeticNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY()); + if (result != binary) { + if (Debug.isLogEnabled()) { + Debug.log("%s : Reassociated %s into %s", graph.method().format("%H::%n"), binary, result); + } + if (!result.isAlive()) { + assert !result.isDeleted(); + result = graph.addOrUniqueWithInputs(result); + } + binary.replaceAtUsages(result); + GraphUtil.killWithUnusedFloatingInputs(binary); + } + } + } + + public boolean detectCounted() { + LoopBeginNode loopBegin = loopBegin(); + FixedNode next = loopBegin.next(); + while (next instanceof FixedGuardNode || next instanceof ValueAnchorNode || next instanceof FullInfopointNode) { + next = ((FixedWithNextNode) next).next(); + } + if (next instanceof IfNode) { + IfNode ifNode = (IfNode) next; + boolean negated = false; + if (!loopBegin.isLoopExit(ifNode.falseSuccessor())) { + if (!loopBegin.isLoopExit(ifNode.trueSuccessor())) { + return false; + } + negated = true; + } + LogicNode ifTest = ifNode.condition(); + if (!(ifTest instanceof IntegerLessThanNode) && !(ifTest instanceof IntegerEqualsNode)) { + if (ifTest instanceof IntegerBelowNode) { + Debug.log("Ignored potential Counted loop at %s with |<|", loopBegin); + } + return false; + } + CompareNode lessThan = (CompareNode) ifTest; + Condition condition = null; + InductionVariable iv = null; + ValueNode limit = null; + if (isOutsideLoop(lessThan.getX())) { + iv = getInductionVariables().get(lessThan.getY()); + if (iv != null) { + condition = lessThan.condition().mirror(); + limit = lessThan.getX(); + } + } else if (isOutsideLoop(lessThan.getY())) { + iv = getInductionVariables().get(lessThan.getX()); + if (iv != null) { + condition = lessThan.condition(); + limit = lessThan.getY(); + } + } + if (condition == null) { + return false; + } + if (negated) { + condition = condition.negate(); + } + boolean oneOff = false; + switch (condition) { + case EQ: + return false; + case NE: { + if (!iv.isConstantStride() || Math.abs(iv.constantStride()) != 1) { + return false; + } + IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp(); + IntegerStamp limitStamp = (IntegerStamp) limit.stamp(); + if (iv.direction() == Direction.Up) { + if (initStamp.upperBound() > limitStamp.lowerBound()) { + return false; + } + } else if (iv.direction() == Direction.Down) { + if (initStamp.lowerBound() < limitStamp.upperBound()) { + return false; + } + } else { + return false; + } + break; + } + case LE: + oneOff = true; + if (iv.direction() != Direction.Up) { + return false; + } + break; + case LT: + if (iv.direction() != Direction.Up) { + return false; + } + break; + case GE: + oneOff = true; + if (iv.direction() != Direction.Down) { + return false; + } + break; + case GT: + if (iv.direction() != Direction.Down) { + return false; + } + break; + default: + throw GraalError.shouldNotReachHere(); + } + counted = new CountedLoopInfo(this, iv, limit, oneOff, negated ? ifNode.falseSuccessor() : ifNode.trueSuccessor()); + return true; + } + return false; + } + + public LoopsData loopsData() { + return data; + } + + public void nodesInLoopBranch(NodeBitMap branchNodes, AbstractBeginNode branch) { + Collection blocks = new LinkedList<>(); + Collection exits = new LinkedList<>(); + Queue work = new LinkedList<>(); + ControlFlowGraph cfg = loopsData().getCFG(); + work.add(cfg.blockFor(branch)); + while (!work.isEmpty()) { + Block b = work.remove(); + if (loop().getExits().contains(b)) { + exits.add((LoopExitNode) b.getBeginNode()); + } else { + blocks.add(b.getBeginNode()); + for (Block d : b.getDominated()) { + if (loop.getBlocks().contains(d)) { + work.add(d); + } + } + } + } + LoopFragment.computeNodes(branchNodes, branch.graph(), blocks, exits); + } + + public Map getInductionVariables() { + if (ivs == null) { + ivs = findInductionVariables(this); + } + return ivs; + } + + /** + * Collect all the basic induction variables for the loop and the find any induction variables + * which are derived from the basic ones. + * + * @param loop + * @return a map from node to induction variable + */ + private static Map findInductionVariables(LoopEx loop) { + Map ivs = newIdentityMap(); + + Queue scanQueue = new LinkedList<>(); + LoopBeginNode loopBegin = loop.loopBegin(); + AbstractEndNode forwardEnd = loopBegin.forwardEnd(); + for (PhiNode phi : loopBegin.phis().filter(ValuePhiNode.class)) { + ValueNode backValue = phi.singleBackValue(); + if (backValue == PhiNode.MULTIPLE_VALUES) { + continue; + } + ValueNode stride = addSub(loop, backValue, phi); + if (stride != null) { + BasicInductionVariable biv = new BasicInductionVariable(loop, (ValuePhiNode) phi, phi.valueAt(forwardEnd), stride, (BinaryArithmeticNode) backValue); + ivs.put(phi, biv); + scanQueue.add(biv); + } + } + + while (!scanQueue.isEmpty()) { + InductionVariable baseIv = scanQueue.remove(); + ValueNode baseIvNode = baseIv.valueNode(); + for (ValueNode op : baseIvNode.usages().filter(ValueNode.class)) { + if (loop.isOutsideLoop(op)) { + continue; + } + if (op.usages().count() == 1 && op.usages().first() == baseIvNode) { + /* + * This is just the base induction variable increment with no other uses so + * don't bother reporting it. + */ + continue; + } + InductionVariable iv = null; + ValueNode offset = addSub(loop, op, baseIvNode); + ValueNode scale; + if (offset != null) { + iv = new DerivedOffsetInductionVariable(loop, baseIv, offset, (BinaryArithmeticNode) op); + } else if (op instanceof NegateNode) { + iv = new DerivedScaledInductionVariable(loop, baseIv, (NegateNode) op); + } else if ((scale = mul(loop, op, baseIvNode)) != null) { + iv = new DerivedScaledInductionVariable(loop, baseIv, scale, op); + } else { + boolean isValidConvert = op instanceof PiNode || op instanceof SignExtendNode; + if (!isValidConvert && op instanceof ZeroExtendNode) { + IntegerStamp inputStamp = (IntegerStamp) ((ZeroExtendNode) op).getValue().stamp(); + isValidConvert = inputStamp.isPositive(); + } + + if (isValidConvert) { + iv = new DerivedConvertedInductionVariable(loop, baseIv, op.stamp(), op); + } + } + + if (iv != null) { + ivs.put(op, iv); + scanQueue.offer(iv); + } + } + } + return Collections.unmodifiableMap(ivs); + } + + private static ValueNode addSub(LoopEx loop, ValueNode op, ValueNode base) { + if (op.stamp() instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) { + BinaryArithmeticNode aritOp = (BinaryArithmeticNode) op; + if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) { + return aritOp.getY(); + } else if (aritOp.getY() == base && loop.isOutsideLoop(aritOp.getX())) { + return aritOp.getX(); + } + } + return null; + } + + private static ValueNode mul(LoopEx loop, ValueNode op, ValueNode base) { + if (op instanceof MulNode) { + MulNode mul = (MulNode) op; + if (mul.getX() == base && loop.isOutsideLoop(mul.getY())) { + return mul.getY(); + } else if (mul.getY() == base && loop.isOutsideLoop(mul.getX())) { + return mul.getX(); + } + } + if (op instanceof LeftShiftNode) { + LeftShiftNode shift = (LeftShiftNode) op; + if (shift.getX() == base && shift.getY().isConstant()) { + return ConstantNode.forIntegerStamp(base.stamp(), 1 << shift.getY().asJavaConstant().asInt(), base.graph()); + } + } + return null; + } + + /** + * Deletes any nodes created within the scope of this object that have no usages. + */ + public void deleteUnusedNodes() { + if (ivs != null) { + for (InductionVariable iv : ivs.values()) { + iv.deleteUnusedNodes(); + } + } + } + + /** + * @return true if all nodes in the loop can be duplicated. + */ + public boolean canDuplicateLoop() { + for (Node node : inside().nodes()) { + if (node instanceof ControlFlowAnchorNode) { + return false; + } + if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + if (frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || frameState.bci == BytecodeFrame.UNWIND_BCI) { + return false; + } + } + } + return true; + } +}