1 /* 2 * Copyright (c) 2016, 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 org.graalvm.compiler.api.directives.GraalDirectives; 26 import org.graalvm.compiler.graph.Node; 27 import org.graalvm.compiler.graph.iterators.NodeIterable; 28 import org.graalvm.compiler.graph.spi.Canonicalizable; 29 import org.graalvm.compiler.graph.spi.SimplifierTool; 30 import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; 31 import org.graalvm.compiler.nodes.LoopBeginNode; 32 import org.graalvm.compiler.nodes.StructuredGraph; 33 import org.graalvm.compiler.nodes.ValueNode; 34 import org.graalvm.compiler.phases.BasePhase; 35 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 36 import org.graalvm.compiler.phases.common.CanonicalizerPhase.CustomCanonicalizer; 37 import org.graalvm.compiler.phases.contract.NodeCostUtil; 38 import org.graalvm.compiler.phases.tiers.HighTierContext; 39 import org.graalvm.compiler.phases.tiers.PhaseContext; 40 import org.junit.Assert; 41 import org.junit.Test; 42 43 public class NodePropertiesTest extends GraalCompilerTest { 44 45 public static Object sideEffect; 46 47 public static Object[] array = new Object[]{new Object(), new Object(), new Object()}; 48 49 public static int test1Snippet(int a) { 50 int x = 0; 51 if (a > 0) { 52 x = 1; 53 sideEffect = null; 54 } else { 55 x = 2; 56 sideEffect = null; 57 } 58 int b = 4; 59 sideEffect = null; 60 int c = b % 5; 61 // can shift 62 return a * x * c; 63 } 64 65 public static int test2Snippet(int a) { 66 int x = 0; 67 if (a > 0) { 68 x = 1; 69 sideEffect = null; 70 } else { 71 x = 2; 72 sideEffect = null; 73 } 74 sideEffect = null; 75 // cannot shift 76 return a * x * 41; 77 } 78 79 public static final int ITERATIONS_LOOP_1 = 128; 80 public static final int ITERATIONS_LOOP_2 = 256; 81 82 public static int testLoop01(int a) { 83 int res = 0; 84 for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { 85 res += i; 86 } 87 return res; 88 } 89 90 public static int testLoop02(int a) { 91 int res = 0; 92 for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_2, i < a); i++) { 93 res += i; 94 } 95 return res; 96 } 97 98 public static int testLoop03(int a) { 99 int res = 0; 100 for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { 101 res *= i; 102 } 103 return res; 104 } 105 106 public static int testLoop04(int a) { 107 int res = 0; 108 for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1 * ITERATIONS_LOOP_2, i < a); i++) { 109 res += i; 110 } 111 return res; 112 } 113 114 public static int testLoop05(int a) { 115 int res = 0; 116 for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { 117 res += i; 118 for (int j = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_2, j < ITERATIONS_LOOP_2); j++) { 119 res += i; 120 } 121 } 122 return res; 123 } 124 125 public static int dontInline(int a) { 126 int res = 1; 127 for (int i = 0; i < a; i++) { 128 for (int j = 0; j < i; j++) { 129 for (int k = 0; k < j; k++) { 130 res += i + j + k; 131 } 132 } 133 } 134 sideEffect = res; 135 return (Integer) sideEffect; 136 } 137 138 public static int untrused01(int a) { 139 return dontInline(a); 140 } 141 142 public static int arrayLoadTest(int a) { 143 return array[a].hashCode(); 144 } 145 146 public static int arrayStoreTest(int a) { 147 array[2] = a; 148 return a; 149 } 150 151 public static int fieldLoad(int a) { 152 return sideEffect.hashCode() * a; 153 } 154 155 public static int fieldStore(int a) { 156 sideEffect = a; 157 return a; 158 } 159 160 @Test 161 public void testCanonicalizationExample() { 162 HighTierContext htc = getDefaultHighTierContext(); 163 ImprovementSavingCanonicalizer c1 = new ImprovementSavingCanonicalizer(); 164 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet")); 165 new CanonicalizerPhase(c1).apply(g1, htc); 166 ImprovementSavingCanonicalizer c2 = new ImprovementSavingCanonicalizer(); 167 StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet")); 168 new CanonicalizerPhase(c2).apply(g2, htc); 169 Assert.assertTrue(c1.savedCycles > c2.savedCycles); 170 } 171 172 private static void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) { 173 // let canonicalizer work away branch probability nodes 174 new CanonicalizerPhase().apply(g, htc); 175 // recompute the loop frequencies 176 ComputeLoopFrequenciesClosure.compute(g); 177 } 178 179 private static void assertFrequency(StructuredGraph g, int iterations) { 180 NodeIterable<LoopBeginNode> loopBeginNodes = g.getNodes(LoopBeginNode.TYPE); 181 LoopBeginNode loopBeginNode = loopBeginNodes.first(); 182 Assert.assertEquals("loop frequency of " + loopBeginNode, iterations, loopBeginNode.loopFrequency(), 0); 183 } 184 185 @Test 186 public void testDifferentLoopFaster() { 187 HighTierContext htc = getDefaultHighTierContext(); 188 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop01")); 189 StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop03")); 190 prepareGraphForLoopFrequencies(g1, htc); 191 prepareGraphForLoopFrequencies(g2, htc); 192 assertFrequency(g1, ITERATIONS_LOOP_1); 193 assertFrequency(g2, ITERATIONS_LOOP_1); 194 GraphCostPhase gc1 = new GraphCostPhase(); 195 GraphCostPhase gc2 = new GraphCostPhase(); 196 gc1.apply(g1, htc); 197 gc2.apply(g2, htc); 198 g1.getDebug().log("Test testDifferentLoopFaster --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize); 199 Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); 200 Assert.assertTrue(gc2.finalSize == gc1.finalSize); 201 } 202 203 @Test 204 public void testSameLoopMoreIterationsCostlier() { 205 HighTierContext htc = getDefaultHighTierContext(); 206 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop01")); 207 StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop02")); 208 prepareGraphForLoopFrequencies(g1, htc); 209 prepareGraphForLoopFrequencies(g2, htc); 210 assertFrequency(g1, ITERATIONS_LOOP_1); 211 assertFrequency(g2, ITERATIONS_LOOP_2); 212 GraphCostPhase gc1 = new GraphCostPhase(); 213 GraphCostPhase gc2 = new GraphCostPhase(); 214 gc1.apply(g1, htc); 215 gc2.apply(g2, htc); 216 g1.getDebug().log("Test testSameLoopMoreIterationsCostlier --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize); 217 Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); 218 Assert.assertTrue(gc2.finalSize == gc1.finalSize); 219 } 220 221 @Test 222 public void testDifferentLoopsInnerOuter() { 223 HighTierContext htc = getDefaultHighTierContext(); 224 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop04")); 225 StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop05")); 226 prepareGraphForLoopFrequencies(g1, htc); 227 prepareGraphForLoopFrequencies(g2, htc); 228 assertFrequency(g1, ITERATIONS_LOOP_1 * ITERATIONS_LOOP_2); 229 GraphCostPhase gc1 = new GraphCostPhase(); 230 GraphCostPhase gc2 = new GraphCostPhase(); 231 gc1.apply(g1, htc); 232 gc2.apply(g2, htc); 233 g1.getDebug().log("Test testDifferentLoopsInnerOuter --> 1.Graph cycles:%f size:%f vs. 2.Graph cycles:%f size:%f\n", gc1.finalCycles, gc1.finalSize, gc2.finalCycles, gc2.finalSize); 234 Assert.assertTrue(gc2.finalSize > gc1.finalSize); 235 } 236 237 @Test 238 public void testGraphCost() { 239 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet")); 240 StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet")); 241 HighTierContext htc = getDefaultHighTierContext(); 242 new CanonicalizerPhase().apply(g1, htc); 243 new CanonicalizerPhase().apply(g2, htc); 244 GraphCostPhase gc1 = new GraphCostPhase(); 245 GraphCostPhase gc2 = new GraphCostPhase(); 246 gc1.apply(g1, htc); 247 gc2.apply(g2, htc); 248 g1.getDebug().log("Test Graph Cost --> 1.Graph cost:%f vs. 2.Graph cost:%f\n", gc1.finalCycles, gc2.finalCycles); 249 Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); 250 Assert.assertTrue(gc2.finalSize == gc1.finalSize); 251 } 252 253 @Test 254 public void testExpectUntrusted() { 255 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("untrused01")); 256 HighTierContext htc = getDefaultHighTierContext(); 257 new CanonicalizerPhase().apply(g1, htc); 258 GraphCostPhase gc1 = new GraphCostPhase(); 259 gc1.apply(g1, htc); 260 } 261 262 @Test 263 public void testArrayLoad() { 264 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayLoadTest")); 265 HighTierContext htc = getDefaultHighTierContext(); 266 new CanonicalizerPhase().apply(g1, htc); 267 GraphCostPhase gc1 = new GraphCostPhase(); 268 gc1.apply(g1, htc); 269 Assert.assertEquals(15, gc1.finalCycles, 25); 270 } 271 272 @Test 273 public void testArrayStore() { 274 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayStoreTest")); 275 HighTierContext htc = getDefaultHighTierContext(); 276 new CanonicalizerPhase().apply(g1, htc); 277 GraphCostPhase gc1 = new GraphCostPhase(); 278 gc1.apply(g1, htc); 279 Assert.assertEquals(15, gc1.finalCycles, 25); 280 } 281 282 @Test 283 public void testFieldLoad() { 284 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldLoad")); 285 HighTierContext htc = getDefaultHighTierContext(); 286 new CanonicalizerPhase().apply(g1, htc); 287 GraphCostPhase gc1 = new GraphCostPhase(); 288 gc1.apply(g1, htc); 289 Assert.assertEquals(15, gc1.finalCycles, 25); 290 } 291 292 @Test 293 public void testFieldStore() { 294 StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldStore")); 295 HighTierContext htc = getDefaultHighTierContext(); 296 new CanonicalizerPhase().apply(g1, htc); 297 GraphCostPhase gc1 = new GraphCostPhase(); 298 gc1.apply(g1, htc); 299 Assert.assertEquals(15, gc1.finalCycles, 25); 300 } 301 302 static class ImprovementSavingCanonicalizer extends CustomCanonicalizer { 303 private int savedCycles; 304 305 @Override 306 public void simplify(Node node, SimplifierTool tool) { 307 if (node instanceof Canonicalizable.Binary<?>) { 308 @SuppressWarnings("unchecked") 309 Canonicalizable.Binary<ValueNode> bc = (Canonicalizable.Binary<ValueNode>) node; 310 Node canonicalized = bc.canonical(tool, bc.getX(), bc.getY()); 311 if (canonicalized != node) { 312 savedCycles += node.estimatedNodeCycles().value - canonicalized.estimatedNodeCycles().value; 313 } 314 } 315 } 316 } 317 318 private static class GraphCostPhase extends BasePhase<PhaseContext> { 319 private double finalCycles; 320 private double finalSize; 321 322 @Override 323 protected void run(StructuredGraph graph, PhaseContext context) { 324 finalCycles = NodeCostUtil.computeGraphCycles(graph, true); 325 finalSize = NodeCostUtil.computeGraphSize(graph); 326 } 327 328 } 329 330 }