/* * 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.loop.MathUtil.add; import static org.graalvm.compiler.loop.MathUtil.sub; import static org.graalvm.compiler.loop.MathUtil.unsignedDivBefore; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.util.UnsignedLong; import org.graalvm.compiler.debug.DebugCloseable; import org.graalvm.compiler.loop.InductionVariable.Direction; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.GuardNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; import org.graalvm.compiler.nodes.calc.ConditionalNode; import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; import org.graalvm.compiler.nodes.calc.NegateNode; import org.graalvm.compiler.nodes.extended.GuardingNode; import jdk.vm.ci.code.CodeUtil; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.SpeculationLog; public class CountedLoopInfo { private final LoopEx loop; private InductionVariable iv; private ValueNode end; private boolean oneOff; private AbstractBeginNode body; private IfNode ifNode; CountedLoopInfo(LoopEx loop, InductionVariable iv, IfNode ifNode, ValueNode end, boolean oneOff, AbstractBeginNode body) { assert iv.direction() != null; this.loop = loop; this.iv = iv; this.end = end; this.oneOff = oneOff; this.body = body; this.ifNode = ifNode; } /** * Returns a node that computes the maximum trip count of this loop. That is the trip count of * this loop assuming it is not exited by an other exit than the {@linkplain #getLimitTest() * count check}. * * This count is exact if {@link #isExactTripCount()} returns true. * * THIS VALUE SHOULD BE TREATED AS UNSIGNED. */ public ValueNode maxTripCountNode() { return maxTripCountNode(false); } /** * Returns a node that computes the maximum trip count of this loop. That is the trip count of * this loop assuming it is not exited by an other exit than the {@linkplain #getLimitTest() * count check}. * * This count is exact if {@link #isExactTripCount()} returns true. * * THIS VALUE SHOULD BE TREATED AS UNSIGNED. * * @param assumePositive if true the check that the loop is entered at all will be omitted. */ public ValueNode maxTripCountNode(boolean assumePositive) { StructuredGraph graph = iv.valueNode().graph(); Stamp stamp = iv.valueNode().stamp(NodeView.DEFAULT); ValueNode max; ValueNode min; ValueNode range; ValueNode absStride; if (iv.direction() == Direction.Up) { absStride = iv.strideNode(); range = sub(graph, end, iv.initNode()); max = end; min = iv.initNode(); } else { assert iv.direction() == Direction.Down; absStride = graph.maybeAddOrUnique(NegateNode.create(iv.strideNode(), NodeView.DEFAULT)); range = sub(graph, iv.initNode(), end); max = iv.initNode(); min = end; } ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph); if (oneOff) { range = add(graph, range, one); } // round-away-from-zero divison: (range + stride -/+ 1) / stride ValueNode denominator = add(graph, range, sub(graph, absStride, one)); ValueNode div = unsignedDivBefore(graph, loop.entryPoint(), denominator, absStride, null); if (assumePositive) { return div; } ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0, graph); return graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(max, min)), zero, div)); } /** * @return true if the loop has constant bounds. */ public boolean isConstantMaxTripCount() { return end instanceof ConstantNode && iv.isConstantInit() && iv.isConstantStride(); } public UnsignedLong constantMaxTripCount() { assert isConstantMaxTripCount(); return new UnsignedLong(rawConstantMaxTripCount()); } /** * Compute the raw value of the trip count for this loop. THIS IS AN UNSIGNED VALUE; */ private long rawConstantMaxTripCount() { assert iv.direction() != null; long endValue = end.asJavaConstant().asLong(); long initValue = iv.constantInit(); long range; long absStride; if (iv.direction() == Direction.Up) { if (endValue < initValue) { return 0; } range = endValue - iv.constantInit(); absStride = iv.constantStride(); } else { assert iv.direction() == Direction.Down; if (initValue < endValue) { return 0; } range = iv.constantInit() - endValue; absStride = -iv.constantStride(); } if (oneOff) { range += 1; } long denominator = range + absStride - 1; return Long.divideUnsigned(denominator, absStride); } public boolean isExactTripCount() { return loop.loopBegin().loopExits().count() == 1; } public ValueNode exactTripCountNode() { assert isExactTripCount(); return maxTripCountNode(); } public boolean isConstantExactTripCount() { assert isExactTripCount(); return isConstantMaxTripCount(); } public UnsignedLong constantExactTripCount() { assert isExactTripCount(); return constantMaxTripCount(); } @Override public String toString() { return "iv=" + iv + " until " + end + (oneOff ? iv.direction() == Direction.Up ? "+1" : "-1" : ""); } public ValueNode getLimit() { return end; } public IfNode getLimitTest() { return ifNode; } public ValueNode getStart() { return iv.initNode(); } public boolean isLimitIncluded() { return oneOff; } public AbstractBeginNode getBody() { return body; } public Direction getDirection() { return iv.direction(); } public InductionVariable getCounter() { return iv; } public GuardingNode getOverFlowGuard() { return loop.loopBegin().getOverflowGuard(); } @SuppressWarnings("try") public GuardingNode createOverFlowGuard() { GuardingNode overflowGuard = getOverFlowGuard(); if (overflowGuard != null) { return overflowGuard; } try (DebugCloseable position = loop.loopBegin().withNodeSourcePosition()) { IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); StructuredGraph graph = iv.valueNode().graph(); CompareNode cond; // we use a negated guard with a < condition to achieve a >= ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph); if (iv.direction() == Direction.Up) { ValueNode v1 = sub(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.maxValue(stamp.getBits()), graph), sub(graph, iv.strideNode(), one)); if (oneOff) { v1 = sub(graph, v1, one); } cond = graph.unique(new IntegerLessThanNode(v1, end)); } else { assert iv.direction() == Direction.Down; ValueNode v1 = add(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.minValue(stamp.getBits()), graph), sub(graph, one, iv.strideNode())); if (oneOff) { v1 = add(graph, v1, one); } cond = graph.unique(new IntegerLessThanNode(end, v1)); } assert graph.getGuardsStage().allowsFloatingGuards(); overflowGuard = graph.unique(new GuardNode(cond, AbstractBeginNode.prevBegin(loop.entryPoint()), DeoptimizationReason.LoopLimitCheck, DeoptimizationAction.InvalidateRecompile, true, SpeculationLog.NO_SPECULATION, null)); // TODO gd: use speculation loop.loopBegin().setOverflowGuard(overflowGuard); return overflowGuard; } } public IntegerStamp getStamp() { return (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); } }