1 /*
   2  * Copyright (c) 2015, 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 package org.graalvm.compiler.core.test;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  27 
  28 import org.graalvm.compiler.nodes.NodeView;
  29 import org.junit.Test;
  30 
  31 import org.graalvm.compiler.api.directives.GraalDirectives;
  32 import org.graalvm.compiler.graph.NodeClass;
  33 import org.graalvm.compiler.loop.InductionVariable;
  34 import org.graalvm.compiler.loop.LoopsData;
  35 import org.graalvm.compiler.nodeinfo.NodeInfo;
  36 import org.graalvm.compiler.nodes.StructuredGraph;
  37 import org.graalvm.compiler.nodes.ValueNode;
  38 import org.graalvm.compiler.nodes.calc.FloatingNode;
  39 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  40 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  41 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  42 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  43 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  44 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  45 
  46 import jdk.vm.ci.meta.JavaKind;
  47 import jdk.vm.ci.meta.ResolvedJavaMethod;
  48 
  49 public class CountedLoopTest extends GraalCompilerTest {
  50 
  51     @FunctionalInterface
  52     private interface IVProperty {
  53         ValueNode get(InductionVariable iv);
  54     }
  55 
  56     /**
  57      * Get a property of an induction variable.
  58      *
  59      * @param property
  60      */
  61     private static int get(IVProperty property, int iv) {
  62         return iv;
  63     }
  64 
  65     private static class Result {
  66         public int extremum;
  67         public int exitValue;
  68 
  69         @Override
  70         public int hashCode() {
  71             final int prime = 31;
  72             int result = 1;
  73             result = prime * result + exitValue;
  74             result = prime * result + extremum;
  75             return result;
  76         }
  77 
  78         @Override
  79         public boolean equals(Object obj) {
  80             if (!(obj instanceof Result)) {
  81                 return false;
  82             }
  83             Result other = (Result) obj;
  84             return extremum == other.extremum && exitValue == other.exitValue;
  85         }
  86 
  87         @Override
  88         public String toString() {
  89             return String.format("extremum = %d, exitValue = %d", extremum, exitValue);
  90         }
  91     }
  92 
  93     public static Result incrementSnippet(int start, int limit, int step) {
  94         int i;
  95         int inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive
  96         Result ret = new Result();
  97         for (i = start; i < limit; i += inc) {
  98             GraalDirectives.controlFlowAnchor();
  99             ret.extremum = get(InductionVariable::extremumNode, i);
 100         }
 101         ret.exitValue = get(InductionVariable::exitValueNode, i);
 102         return ret;
 103     }
 104 
 105     @Test
 106     public void increment1() {
 107         test("incrementSnippet", 0, 256, 1);
 108     }
 109 
 110     @Test
 111     public void increment2() {
 112         test("incrementSnippet", 0, 256, 2);
 113     }
 114 
 115     @Test
 116     public void increment3() {
 117         test("incrementSnippet", 0, 256, 3);
 118     }
 119 
 120     public static Result incrementEqSnippet(int start, int limit, int step) {
 121         int i;
 122         int inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive
 123         Result ret = new Result();
 124         for (i = start; i <= limit; i += inc) {
 125             GraalDirectives.controlFlowAnchor();
 126             ret.extremum = get(InductionVariable::extremumNode, i);
 127         }
 128         ret.exitValue = get(InductionVariable::exitValueNode, i);
 129         return ret;
 130     }
 131 
 132     @Test
 133     public void incrementEq1() {
 134         test("incrementEqSnippet", 0, 256, 1);
 135     }
 136 
 137     @Test
 138     public void incrementEq2() {
 139         test("incrementEqSnippet", 0, 256, 2);
 140     }
 141 
 142     @Test
 143     public void incrementEq3() {
 144         test("incrementEqSnippet", 0, 256, 3);
 145     }
 146 
 147     public static Result decrementSnippet(int start, int limit, int step) {
 148         int i;
 149         int dec = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive
 150         Result ret = new Result();
 151         for (i = start; i > limit; i -= dec) {
 152             GraalDirectives.controlFlowAnchor();
 153             ret.extremum = get(InductionVariable::extremumNode, i);
 154         }
 155         ret.exitValue = get(InductionVariable::exitValueNode, i);
 156         return ret;
 157     }
 158 
 159     @Test
 160     public void decrement1() {
 161         test("decrementSnippet", 256, 0, 1);
 162     }
 163 
 164     @Test
 165     public void decrement2() {
 166         test("decrementSnippet", 256, 0, 2);
 167     }
 168 
 169     @Test
 170     public void decrement3() {
 171         test("decrementSnippet", 256, 0, 3);
 172     }
 173 
 174     public static Result decrementEqSnippet(int start, int limit, int step) {
 175         int i;
 176         int dec = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive
 177         Result ret = new Result();
 178         for (i = start; i >= limit; i -= dec) {
 179             GraalDirectives.controlFlowAnchor();
 180             ret.extremum = get(InductionVariable::extremumNode, i);
 181         }
 182         ret.exitValue = get(InductionVariable::exitValueNode, i);
 183         return ret;
 184     }
 185 
 186     @Test
 187     public void decrementEq1() {
 188         test("decrementEqSnippet", 256, 0, 1);
 189     }
 190 
 191     @Test
 192     public void decrementEq2() {
 193         test("decrementEqSnippet", 256, 0, 2);
 194     }
 195 
 196     @Test
 197     public void decrementEq3() {
 198         test("decrementEqSnippet", 256, 0, 3);
 199     }
 200 
 201     public static Result twoVariablesSnippet() {
 202         Result ret = new Result();
 203         int j = 0;
 204         for (int i = 0; i < 1024; i++) {
 205             j += 5;
 206             GraalDirectives.controlFlowAnchor();
 207             ret.extremum = get(InductionVariable::extremumNode, j);
 208         }
 209         ret.exitValue = get(InductionVariable::exitValueNode, j);
 210         return ret;
 211     }
 212 
 213     @Test
 214     public void testTwoVariables() {
 215         test("twoVariablesSnippet");
 216     }
 217 
 218     @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
 219     private static class IVPropertyNode extends FloatingNode implements LIRLowerable {
 220 
 221         public static final NodeClass<IVPropertyNode> TYPE = NodeClass.create(IVPropertyNode.class);
 222 
 223         private final IVProperty property;
 224         @Input private ValueNode iv;
 225 
 226         protected IVPropertyNode(IVProperty property, ValueNode iv) {
 227             super(TYPE, iv.stamp(NodeView.DEFAULT).unrestricted());
 228             this.property = property;
 229             this.iv = iv;
 230         }
 231 
 232         public void rewrite(LoopsData loops) {
 233             InductionVariable inductionVariable = loops.getInductionVariable(iv);
 234             assert inductionVariable != null;
 235             ValueNode node = property.get(inductionVariable);
 236             replaceAtUsagesAndDelete(node);
 237         }
 238 
 239         @Override
 240         public void generate(NodeLIRBuilderTool gen) {
 241             gen.setResult(this, gen.operand(iv));
 242         }
 243     }
 244 
 245     @Override
 246     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
 247         Registration r = new Registration(invocationPlugins, CountedLoopTest.class);
 248         r.register2("get", IVProperty.class, int.class, new InvocationPlugin() {
 249             @Override
 250             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) {
 251                 IVProperty property = null;
 252                 if (arg1.isConstant()) {
 253                     property = getSnippetReflection().asObject(IVProperty.class, arg1.asJavaConstant());
 254                 }
 255                 if (property != null) {
 256                     b.addPush(JavaKind.Int, new IVPropertyNode(property, arg2));
 257                     return true;
 258                 } else {
 259                     return false;
 260                 }
 261             }
 262         });
 263         super.registerInvocationPlugins(invocationPlugins);
 264     }
 265 
 266     @Override
 267     protected boolean checkHighTierGraph(StructuredGraph graph) {
 268         LoopsData loops = new LoopsData(graph);
 269         loops.detectedCountedLoops();
 270         for (IVPropertyNode node : graph.getNodes().filter(IVPropertyNode.class)) {
 271             node.rewrite(loops);
 272         }
 273         assert graph.getNodes().filter(IVPropertyNode.class).isEmpty();
 274         return true;
 275     }
 276 
 277     public static Result incrementNeqSnippet(int limit) {
 278         int i;
 279         int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive
 280         Result ret = new Result();
 281         for (i = 0; i != posLimit; i++) {
 282             GraalDirectives.controlFlowAnchor();
 283             ret.extremum = get(InductionVariable::extremumNode, i);
 284         }
 285         ret.exitValue = get(InductionVariable::exitValueNode, i);
 286         return ret;
 287     }
 288 
 289     @Test
 290     public void decrementNeq() {
 291         test("decrementNeqSnippet", 256);
 292     }
 293 
 294     public static Result decrementNeqSnippet(int limit) {
 295         int i;
 296         int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive
 297         Result ret = new Result();
 298         for (i = posLimit; i != 0; i--) {
 299             GraalDirectives.controlFlowAnchor();
 300             ret.extremum = get(InductionVariable::extremumNode, i);
 301         }
 302         ret.exitValue = get(InductionVariable::exitValueNode, i);
 303         return ret;
 304     }
 305 
 306     @Test
 307     public void incrementNeq() {
 308         test("incrementNeqSnippet", 256);
 309     }
 310 }