1 /* 2 * Copyright (c) 2012, 2018, 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 static java.lang.Math.abs; 28 import static org.graalvm.compiler.loop.MathUtil.unsignedDivBefore; 29 import static org.graalvm.compiler.nodes.calc.BinaryArithmeticNode.add; 30 import static org.graalvm.compiler.nodes.calc.BinaryArithmeticNode.sub; 31 32 import org.graalvm.compiler.core.common.type.IntegerStamp; 33 import org.graalvm.compiler.core.common.type.Stamp; 34 import org.graalvm.compiler.core.common.util.UnsignedLong; 35 import org.graalvm.compiler.debug.DebugCloseable; 36 import org.graalvm.compiler.loop.InductionVariable.Direction; 37 import org.graalvm.compiler.nodes.AbstractBeginNode; 38 import org.graalvm.compiler.nodes.ConstantNode; 39 import org.graalvm.compiler.nodes.GuardNode; 40 import org.graalvm.compiler.nodes.IfNode; 41 import org.graalvm.compiler.nodes.LogicNode; 42 import org.graalvm.compiler.nodes.NodeView; 43 import org.graalvm.compiler.nodes.StructuredGraph; 44 import org.graalvm.compiler.nodes.ValueNode; 45 import org.graalvm.compiler.nodes.calc.ConditionalNode; 46 import org.graalvm.compiler.nodes.calc.NegateNode; 47 import org.graalvm.compiler.nodes.extended.GuardingNode; 48 import org.graalvm.compiler.nodes.util.GraphUtil; 49 import org.graalvm.compiler.nodes.util.IntegerHelper; 50 import org.graalvm.compiler.nodes.util.SignedIntegerHelper; 51 import org.graalvm.compiler.nodes.util.UnsignedIntegerHelper; 52 53 import jdk.vm.ci.meta.DeoptimizationAction; 54 import jdk.vm.ci.meta.DeoptimizationReason; 55 import jdk.vm.ci.meta.SpeculationLog; 56 57 public class CountedLoopInfo { 58 59 private final LoopEx loop; 60 private InductionVariable iv; 61 private ValueNode end; 62 private boolean oneOff; 63 private AbstractBeginNode body; 64 private IfNode ifNode; 65 private final boolean unsigned; 66 67 CountedLoopInfo(LoopEx loop, InductionVariable iv, IfNode ifNode, ValueNode end, boolean oneOff, AbstractBeginNode body, boolean unsigned) { 68 assert iv.direction() != null; 69 this.loop = loop; 70 this.iv = iv; 71 this.end = end; 72 this.oneOff = oneOff; 73 this.body = body; 74 this.ifNode = ifNode; 75 this.unsigned = unsigned; 76 } 77 78 /** 79 * Returns a node that computes the maximum trip count of this loop. That is the trip count of 80 * this loop assuming it is not exited by an other exit than the {@linkplain #getLimitTest() 81 * count check}. 82 * 83 * This count is exact if {@link #isExactTripCount()} returns true. 84 * 85 * THIS VALUE SHOULD BE TREATED AS UNSIGNED. 86 */ 87 public ValueNode maxTripCountNode() { 88 return maxTripCountNode(false); 89 } 90 91 public boolean isUnsignedCheck() { 92 return this.unsigned; 93 } 94 95 /** 96 * Returns a node that computes the maximum trip count of this loop. That is the trip count of 97 * this loop assuming it is not exited by an other exit than the {@linkplain #getLimitTest() 98 * count check}. 99 * 100 * This count is exact if {@link #isExactTripCount()} returns true. 101 * 102 * THIS VALUE SHOULD BE TREATED AS UNSIGNED. 103 * 104 * @param assumeLoopEntered if true the check that the loop is entered at all will be omitted. 105 */ 106 public ValueNode maxTripCountNode(boolean assumeLoopEntered) { 107 StructuredGraph graph = iv.valueNode().graph(); 108 Stamp stamp = iv.valueNode().stamp(NodeView.DEFAULT); 109 110 ValueNode max; 111 ValueNode min; 112 ValueNode absStride; 113 if (iv.direction() == Direction.Up) { 114 absStride = iv.strideNode(); 115 max = end; 116 min = iv.initNode(); 117 } else { 118 assert iv.direction() == Direction.Down; 119 absStride = NegateNode.create(iv.strideNode(), NodeView.DEFAULT); 120 max = iv.initNode(); 121 min = end; 122 } 123 ValueNode range = sub(max, min); 124 125 ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1); 126 if (oneOff) { 127 range = add(range, one); 128 } 129 // round-away-from-zero divison: (range + stride -/+ 1) / stride 130 ValueNode denominator = add(range, sub(absStride, one)); 131 ValueNode div = unsignedDivBefore(graph, loop.entryPoint(), denominator, absStride, null); 132 133 if (assumeLoopEntered) { 134 return graph.addOrUniqueWithInputs(div); 135 } 136 ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0); 137 // This check is "wide": it looks like min <= max 138 // That's OK even if the loop is strict (`!isLimitIncluded()`) 139 // because in this case, `div` will be zero when min == max 140 LogicNode noEntryCheck = getCounterIntegerHelper().createCompareNode(max, min, NodeView.DEFAULT); 141 return graph.addOrUniqueWithInputs(ConditionalNode.create(noEntryCheck, zero, div, NodeView.DEFAULT)); 142 } 143 144 /** 145 * @return true if the loop has constant bounds. 146 */ 147 public boolean isConstantMaxTripCount() { 148 return end instanceof ConstantNode && iv.isConstantInit() && iv.isConstantStride(); 149 } 150 151 public UnsignedLong constantMaxTripCount() { 152 assert isConstantMaxTripCount(); 153 return new UnsignedLong(rawConstantMaxTripCount()); 154 } 155 156 /** 157 * Compute the raw value of the trip count for this loop. THIS IS AN UNSIGNED VALUE; 158 */ 159 private long rawConstantMaxTripCount() { 160 assert iv.direction() != null; 161 long endValue = end.asJavaConstant().asLong(); 162 long initValue = iv.constantInit(); 163 long range; 164 long absStride; 165 IntegerHelper helper = getCounterIntegerHelper(64); 166 if (iv.direction() == Direction.Up) { 167 if (helper.compare(endValue, initValue) < 0) { 168 return 0; 169 } 170 range = endValue - iv.constantInit(); 171 absStride = iv.constantStride(); 172 } else { 173 assert iv.direction() == Direction.Down; 174 if (helper.compare(initValue, endValue) < 0) { 175 return 0; 176 } 177 range = iv.constantInit() - endValue; 178 absStride = -iv.constantStride(); 179 } 180 if (oneOff) { 181 range += 1; 182 } 183 long denominator = range + absStride - 1; 184 return Long.divideUnsigned(denominator, absStride); 185 } 186 187 public IntegerHelper getCounterIntegerHelper() { 188 IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); 189 return getCounterIntegerHelper(stamp.getBits()); 190 } 191 192 public IntegerHelper getCounterIntegerHelper(int bits) { 193 IntegerHelper helper; 194 if (isUnsignedCheck()) { 195 helper = new UnsignedIntegerHelper(bits); 196 } else { 197 helper = new SignedIntegerHelper(bits); 198 } 199 return helper; 200 } 201 202 public boolean isExactTripCount() { 203 return loop.loop().getNaturalExits().size() == 1; 204 } 205 206 public ValueNode exactTripCountNode() { 207 assert isExactTripCount(); 208 return maxTripCountNode(); 209 } 210 211 public boolean isConstantExactTripCount() { 212 assert isExactTripCount(); 213 return isConstantMaxTripCount(); 214 } 215 216 public UnsignedLong constantExactTripCount() { 217 assert isExactTripCount(); 218 return constantMaxTripCount(); 219 } 220 221 @Override 222 public String toString() { 223 return "iv=" + iv + " until " + end + (oneOff ? iv.direction() == Direction.Up ? "+1" : "-1" : ""); 224 } 225 226 public ValueNode getLimit() { 227 return end; 228 } 229 230 public IfNode getLimitTest() { 231 return ifNode; 232 } 233 234 public ValueNode getStart() { 235 return iv.initNode(); 236 } 237 238 public boolean isLimitIncluded() { 239 return oneOff; 240 } 241 242 public AbstractBeginNode getBody() { 243 return body; 244 } 245 246 public AbstractBeginNode getCountedExit() { 247 if (getLimitTest().trueSuccessor() == getBody()) { 248 return getLimitTest().falseSuccessor(); 249 } else { 250 assert getLimitTest().falseSuccessor() == getBody(); 251 return getLimitTest().trueSuccessor(); 252 } 253 } 254 255 public Direction getDirection() { 256 return iv.direction(); 257 } 258 259 public InductionVariable getCounter() { 260 return iv; 261 } 262 263 public GuardingNode getOverFlowGuard() { 264 return loop.loopBegin().getOverflowGuard(); 265 } 266 267 public boolean counterNeverOverflows() { 268 if (iv.isConstantStride() && abs(iv.constantStride()) == 1) { 269 return true; 270 } 271 IntegerStamp endStamp = (IntegerStamp) end.stamp(NodeView.DEFAULT); 272 ValueNode strideNode = iv.strideNode(); 273 IntegerStamp strideStamp = (IntegerStamp) strideNode.stamp(NodeView.DEFAULT); 274 GraphUtil.tryKillUnused(strideNode); 275 IntegerHelper integerHelper = getCounterIntegerHelper(); 276 if (getDirection() == Direction.Up) { 277 long max = integerHelper.maxValue(); 278 return integerHelper.compare(endStamp.upperBound(), max - (strideStamp.upperBound() - 1) - (oneOff ? 1 : 0)) <= 0; 279 } else if (getDirection() == Direction.Down) { 280 long min = integerHelper.minValue(); 281 return integerHelper.compare(min + (1 - strideStamp.lowerBound()) + (oneOff ? 1 : 0), endStamp.lowerBound()) <= 0; 282 } 283 return false; 284 } 285 286 @SuppressWarnings("try") 287 public GuardingNode createOverFlowGuard() { 288 GuardingNode overflowGuard = getOverFlowGuard(); 289 if (overflowGuard != null || counterNeverOverflows()) { 290 return overflowGuard; 291 } 292 try (DebugCloseable position = loop.loopBegin().withNodeSourcePosition()) { 293 IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); 294 IntegerHelper integerHelper = getCounterIntegerHelper(); 295 StructuredGraph graph = iv.valueNode().graph(); 296 LogicNode cond; // we use a negated guard with a < condition to achieve a >= 297 ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph); 298 if (iv.direction() == Direction.Up) { 299 ValueNode v1 = sub(ConstantNode.forIntegerStamp(stamp, integerHelper.maxValue()), sub(iv.strideNode(), one)); 300 if (oneOff) { 301 v1 = sub(v1, one); 302 } 303 cond = graph.addOrUniqueWithInputs(integerHelper.createCompareNode(v1, end, NodeView.DEFAULT)); 304 } else { 305 assert iv.direction() == Direction.Down; 306 ValueNode v1 = add(ConstantNode.forIntegerStamp(stamp, integerHelper.minValue()), sub(one, iv.strideNode())); 307 if (oneOff) { 308 v1 = add(v1, one); 309 } 310 cond = graph.addOrUniqueWithInputs(integerHelper.createCompareNode(end, v1, NodeView.DEFAULT)); 311 } 312 assert graph.getGuardsStage().allowsFloatingGuards(); 313 overflowGuard = graph.unique(new GuardNode(cond, AbstractBeginNode.prevBegin(loop.entryPoint()), DeoptimizationReason.LoopLimitCheck, DeoptimizationAction.InvalidateRecompile, true, 314 SpeculationLog.NO_SPECULATION, null)); // TODO gd: use speculation 315 loop.loopBegin().setOverflowGuard(overflowGuard); 316 return overflowGuard; 317 } 318 } 319 320 public IntegerStamp getStamp() { 321 return (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); 322 } 323 }