1 /* 2 * Copyright (c) 2015, 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.core.test; 26 27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; 28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; 29 30 import org.graalvm.compiler.api.directives.GraalDirectives; 31 import org.graalvm.compiler.graph.NodeClass; 32 import org.graalvm.compiler.loop.InductionVariable; 33 import org.graalvm.compiler.loop.LoopsData; 34 import org.graalvm.compiler.nodeinfo.NodeInfo; 35 import org.graalvm.compiler.nodes.ConstantNode; 36 import org.graalvm.compiler.nodes.NodeView; 37 import org.graalvm.compiler.nodes.StructuredGraph; 38 import org.graalvm.compiler.nodes.ValueNode; 39 import org.graalvm.compiler.nodes.calc.FloatingNode; 40 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 41 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; 42 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 43 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; 44 import org.graalvm.compiler.nodes.spi.LIRLowerable; 45 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 46 import org.graalvm.compiler.phases.OptimisticOptimizations; 47 import org.graalvm.compiler.phases.tiers.HighTierContext; 48 import org.junit.Test; 49 50 import jdk.vm.ci.meta.JavaKind; 51 import jdk.vm.ci.meta.ResolvedJavaMethod; 52 53 public class CountedLoopTest extends GraalCompilerTest { 54 55 @FunctionalInterface 56 private interface IVProperty { 57 ValueNode get(InductionVariable iv); 58 } 59 60 @FunctionalInterface 61 private interface StaticIVProperty { 62 long get(InductionVariable iv); 63 } 64 65 @FunctionalInterface 66 private interface IVPredicate { 67 boolean test(InductionVariable iv); 68 } 69 70 /** 71 * Get a property of an induction variable. 72 */ 73 private static int get(@SuppressWarnings("unused") IVProperty property, @SuppressWarnings("unused") StaticIVProperty staticProperty, @SuppressWarnings("unused") IVPredicate constantCheck, 74 int iv) { 75 return iv; 76 } 77 78 private static int get(@SuppressWarnings("unused") IVProperty property, int iv) { 79 return iv; 80 } 81 82 private static long get(@SuppressWarnings("unused") IVProperty property, @SuppressWarnings("unused") StaticIVProperty staticProperty, @SuppressWarnings("unused") IVPredicate constantCheck, 83 long iv) { 84 return iv; 85 } 86 87 private static long get(@SuppressWarnings("unused") IVProperty property, long iv) { 88 return iv; 89 } 90 91 private static class Result { 92 public long extremum; 93 public long exitValue; 94 95 @Override 96 public int hashCode() { 97 final int prime = 31; 98 int result = 1; 99 result = prime * result + Long.hashCode(exitValue); 100 result = prime * result + Long.hashCode(extremum); 101 return result; 102 } 103 104 @Override 105 public boolean equals(Object obj) { 106 if (!(obj instanceof Result)) { 107 return false; 108 } 109 Result other = (Result) obj; 110 return extremum == other.extremum && exitValue == other.exitValue; 111 } 112 113 @Override 114 public String toString() { 115 return String.format("extremum = %d, exitValue = %d", extremum, exitValue); 116 } 117 } 118 119 public static Result incrementSnippet(int start, int limit, int step) { 120 int i; 121 int inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive 122 Result ret = new Result(); 123 for (i = start; i < limit; i += inc) { 124 GraalDirectives.controlFlowAnchor(); 125 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 126 } 127 ret.exitValue = get(InductionVariable::exitValueNode, i); 128 return ret; 129 } 130 131 @Test 132 public void increment1() { 133 testCounted("incrementSnippet", 0, 256, 1); 134 } 135 136 @Test 137 public void increment2() { 138 testCounted("incrementSnippet", 0, 256, 2); 139 } 140 141 @Test 142 public void increment3() { 143 testCounted("incrementSnippet", 0, 256, 3); 144 } 145 146 @Test 147 public void increment4() { 148 testCounted("incrementSnippet", -10, 1, Integer.MAX_VALUE); 149 } 150 151 @Test 152 public void increment5() { 153 testCounted("incrementSnippet", 256, 256, 1); 154 } 155 156 @Test 157 public void increment6() { 158 testCounted("incrementSnippet", 257, 256, 1); 159 } 160 161 @Test 162 public void increment7() { 163 testCounted("incrementSnippet", -10, Integer.MAX_VALUE, 1); 164 } 165 166 @Test 167 public void increment8() { 168 testCounted("incrementSnippet", -10, Integer.MAX_VALUE - 1, 2); 169 } 170 171 public static Result incrementEqSnippet(int start, int limit, int step) { 172 int i; 173 int inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive 174 Result ret = new Result(); 175 for (i = start; i <= limit; i += inc) { 176 GraalDirectives.controlFlowAnchor(); 177 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 178 } 179 ret.exitValue = get(InductionVariable::exitValueNode, i); 180 return ret; 181 } 182 183 @Test 184 public void incrementEq1() { 185 testCounted("incrementEqSnippet", 0, 256, 1); 186 } 187 188 @Test 189 public void incrementEq2() { 190 testCounted("incrementEqSnippet", 0, 256, 2); 191 } 192 193 @Test 194 public void incrementEq3() { 195 testCounted("incrementEqSnippet", 0, 256, 3); 196 } 197 198 @Test 199 public void incrementEq4() { 200 testCounted("incrementEqSnippet", -10, 0, Integer.MAX_VALUE); 201 } 202 203 @Test 204 public void incrementEq5() { 205 testCounted("incrementEqSnippet", 256, 256, 1); 206 } 207 208 @Test 209 public void incrementEq6() { 210 testCounted("incrementEqSnippet", 257, 256, 1); 211 } 212 213 @Test 214 public void incrementEq7() { 215 testCounted("incrementEqSnippet", -10, Integer.MAX_VALUE - 1, 1); 216 } 217 218 @Test 219 public void incrementEq8() { 220 testCounted("incrementEqSnippet", -10, Integer.MAX_VALUE - 2, 2); 221 } 222 223 public static Result decrementSnippet(int start, int limit, int step) { 224 int i; 225 int dec = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive 226 Result ret = new Result(); 227 for (i = start; i > limit; i -= dec) { 228 GraalDirectives.controlFlowAnchor(); 229 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 230 } 231 ret.exitValue = get(InductionVariable::exitValueNode, i); 232 return ret; 233 } 234 235 @Test 236 public void decrement1() { 237 testCounted("decrementSnippet", 256, 0, 1); 238 } 239 240 @Test 241 public void decrement2() { 242 testCounted("decrementSnippet", 256, 0, 2); 243 } 244 245 @Test 246 public void decrement3() { 247 testCounted("decrementSnippet", 256, 0, 3); 248 } 249 250 @Test 251 public void decrement4() { 252 testCounted("decrementSnippet", Integer.MAX_VALUE, -10, 1); 253 } 254 255 @Test 256 public void decrement5() { 257 testCounted("decrementSnippet", Integer.MAX_VALUE, -10, 2); 258 } 259 260 public static Result decrementEqSnippet(int start, int limit, int step) { 261 int i; 262 int dec = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive 263 Result ret = new Result(); 264 for (i = start; i >= limit; i -= dec) { 265 GraalDirectives.controlFlowAnchor(); 266 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 267 } 268 ret.exitValue = get(InductionVariable::exitValueNode, i); 269 return ret; 270 } 271 272 @Test 273 public void decrementEq1() { 274 testCounted("decrementEqSnippet", 256, 0, 1); 275 } 276 277 @Test 278 public void decrementEq2() { 279 testCounted("decrementEqSnippet", 256, 0, 2); 280 } 281 282 @Test 283 public void decrementEq3() { 284 testCounted("decrementEqSnippet", 256, 0, 3); 285 } 286 287 @Test 288 public void decrementEq4() { 289 testCounted("decrementEqSnippet", -10, 0, Integer.MAX_VALUE); 290 } 291 292 @Test 293 public void decrementEq5() { 294 testCounted("decrementEqSnippet", Integer.MAX_VALUE, -10, 1); 295 } 296 297 @Test 298 public void decrementEq6() { 299 testCounted("decrementEqSnippet", Integer.MAX_VALUE, -10, 2); 300 } 301 302 public static Result twoVariablesSnippet() { 303 Result ret = new Result(); 304 int j = 0; 305 for (int i = 0; i < 1024; i++) { 306 j += 5; 307 GraalDirectives.controlFlowAnchor(); 308 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, j); 309 } 310 ret.exitValue = get(InductionVariable::exitValueNode, j); 311 return ret; 312 } 313 314 @Test 315 public void testTwoVariables() { 316 testCounted("twoVariablesSnippet"); 317 } 318 319 public static Result incrementNeqSnippet(int limit) { 320 int i; 321 int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive 322 Result ret = new Result(); 323 for (i = 0; i != posLimit; i++) { 324 GraalDirectives.controlFlowAnchor(); 325 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 326 } 327 ret.exitValue = get(InductionVariable::exitValueNode, i); 328 return ret; 329 } 330 331 @Test 332 public void decrementNeq() { 333 testCounted("decrementNeqSnippet", 256); 334 } 335 336 public static Result decrementNeqSnippet(int limit) { 337 int i; 338 int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive 339 Result ret = new Result(); 340 for (i = posLimit; i != 0; i--) { 341 GraalDirectives.controlFlowAnchor(); 342 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 343 } 344 ret.exitValue = get(InductionVariable::exitValueNode, i); 345 return ret; 346 } 347 348 @Test 349 public void incrementNeq() { 350 testCounted("incrementNeqSnippet", 256); 351 } 352 353 public static Result incrementLongSnippet(long start, long limit, long step) { 354 long i; 355 long inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive 356 Result ret = new Result(); 357 for (i = start; i < limit; i += inc) { 358 GraalDirectives.controlFlowAnchor(); 359 ret.extremum = get(InductionVariable::extremumNode, InductionVariable::constantExtremum, InductionVariable::isConstantExtremum, i); 360 } 361 ret.exitValue = get(InductionVariable::exitValueNode, i); 362 return ret; 363 } 364 365 @Test 366 public void incrementLong1() { 367 testCounted("incrementLongSnippet", 0L, 256L, 1L); 368 } 369 370 @Test 371 public void incrementLong2() { 372 testCounted("incrementLongSnippet", 0L, 256L, 2L); 373 } 374 375 @Test 376 public void incrementLong3() { 377 testCounted("incrementLongSnippet", 0L, 256L, 3L); 378 } 379 380 @Test 381 public void incrementLong4() { 382 testCounted("incrementLongSnippet", -10L, 1L, Long.MAX_VALUE); 383 } 384 385 @Test 386 public void incrementLong5() { 387 testCounted("incrementLongSnippet", 256L, 256L, 1L); 388 } 389 390 @Test 391 public void incrementLong6() { 392 testCounted("incrementLongSnippet", 257L, 256L, 1L); 393 } 394 395 @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 396 private static class IVPropertyNode extends FloatingNode implements LIRLowerable { 397 398 public static final NodeClass<IVPropertyNode> TYPE = NodeClass.create(IVPropertyNode.class); 399 400 private final IVProperty property; 401 private final StaticIVProperty staticProperty; 402 private final IVPredicate staticCheck; 403 @Input private ValueNode iv; 404 405 protected IVPropertyNode(IVProperty property, StaticIVProperty staticProperty, IVPredicate staticCheck, ValueNode iv) { 406 super(TYPE, iv.stamp(NodeView.DEFAULT).unrestricted()); 407 this.property = property; 408 this.staticProperty = staticProperty; 409 this.staticCheck = staticCheck; 410 this.iv = iv; 411 } 412 413 public void rewrite(LoopsData loops) { 414 InductionVariable inductionVariable = loops.getInductionVariable(iv); 415 assert inductionVariable != null; 416 assertTrue(inductionVariable.getLoop().isCounted(), "must be counted"); 417 ValueNode node = null; 418 if (staticCheck != null) { 419 assert staticProperty != null; 420 if (staticCheck.test(inductionVariable)) { 421 node = ConstantNode.forLong(staticProperty.get(inductionVariable), graph()); 422 } 423 } 424 if (node == null) { 425 node = property.get(inductionVariable); 426 } 427 replaceAtUsagesAndDelete(node); 428 } 429 430 @Override 431 public void generate(NodeLIRBuilderTool gen) { 432 gen.setResult(this, gen.operand(iv)); 433 } 434 } 435 436 @Override 437 protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) { 438 Registration r = new Registration(invocationPlugins, CountedLoopTest.class); 439 registerPlugins(r, JavaKind.Int); 440 registerPlugins(r, JavaKind.Long); 441 super.registerInvocationPlugins(invocationPlugins); 442 } 443 444 private void registerPlugins(Registration r, JavaKind ivKind) { 445 r.register2("get", IVProperty.class, ivKind.toJavaClass(), new InvocationPlugin() { 446 @Override 447 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) { 448 IVProperty property = null; 449 if (arg1.isConstant()) { 450 property = getSnippetReflection().asObject(IVProperty.class, arg1.asJavaConstant()); 451 } 452 if (property != null) { 453 b.addPush(ivKind, new IVPropertyNode(property, null, null, arg2)); 454 return true; 455 } else { 456 return false; 457 } 458 } 459 }); 460 r.register4("get", IVProperty.class, StaticIVProperty.class, IVPredicate.class, ivKind.toJavaClass(), new InvocationPlugin() { 461 @Override 462 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) { 463 IVProperty property = null; 464 StaticIVProperty staticProperty = null; 465 IVPredicate staticCheck = null; 466 if (arg1.isConstant()) { 467 property = getSnippetReflection().asObject(IVProperty.class, arg1.asJavaConstant()); 468 } 469 if (arg2.isConstant()) { 470 staticProperty = getSnippetReflection().asObject(StaticIVProperty.class, arg2.asJavaConstant()); 471 } 472 if (arg3.isConstant()) { 473 staticCheck = getSnippetReflection().asObject(IVPredicate.class, arg3.asJavaConstant()); 474 } 475 if (property != null && staticProperty != null && staticCheck != null) { 476 b.addPush(ivKind, new IVPropertyNode(property, staticProperty, staticCheck, arg4)); 477 return true; 478 } else { 479 return false; 480 } 481 } 482 }); 483 } 484 485 @Override 486 protected void checkHighTierGraph(StructuredGraph graph) { 487 LoopsData loops = new LoopsData(graph); 488 loops.detectedCountedLoops(); 489 for (IVPropertyNode node : graph.getNodes().filter(IVPropertyNode.class)) { 490 node.rewrite(loops); 491 } 492 assert graph.getNodes().filter(IVPropertyNode.class).isEmpty(); 493 } 494 495 @Override 496 protected HighTierContext getDefaultHighTierContext() { 497 // Don't convert unreached paths into Guard 498 return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.NONE); 499 } 500 501 private Object[] argsToBind; 502 503 @Override 504 protected Object[] getArgumentToBind() { 505 return argsToBind; 506 } 507 508 public void testCounted(String snippetName, Object... args) { 509 test(snippetName, args); 510 argsToBind = args; 511 test(snippetName, args); 512 argsToBind = null; 513 } 514 }