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