| /* |
| * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| package org.graalvm.compiler.core.test; |
| |
| import org.graalvm.compiler.api.directives.GraalDirectives; |
| import org.graalvm.compiler.graph.Node; |
| import org.graalvm.compiler.graph.iterators.NodeIterable; |
| import org.graalvm.compiler.graph.spi.Canonicalizable; |
| import org.graalvm.compiler.graph.spi.SimplifierTool; |
| import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; |
| import org.graalvm.compiler.nodes.LoopBeginNode; |
| import org.graalvm.compiler.nodes.StructuredGraph; |
| import org.graalvm.compiler.nodes.ValueNode; |
| import org.graalvm.compiler.phases.BasePhase; |
| import org.graalvm.compiler.phases.common.CanonicalizerPhase; |
| import org.graalvm.compiler.phases.common.CanonicalizerPhase.CustomCanonicalizer; |
| import org.graalvm.compiler.phases.contract.NodeCostUtil; |
| import org.graalvm.compiler.phases.tiers.HighTierContext; |
| import org.graalvm.compiler.phases.tiers.PhaseContext; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class NodePropertiesTest extends GraalCompilerTest { |
| |
| public static Object sideEffect; |
| |
| public static Object[] array = new Object[]{new Object(), new Object(), new Object()}; |
| |
| public static int test1Snippet(int a) { |
| int x = 0; |
| if (a > 0) { |
| x = 1; |
| sideEffect = null; |
| } else { |
| x = 2; |
| sideEffect = null; |
| } |
| int b = 4; |
| sideEffect = null; |
| int c = b % 5; |
| // can shift |
| return a * x * c; |
| } |
| |
| public static int test2Snippet(int a) { |
| int x = 0; |
| if (a > 0) { |
| x = 1; |
| sideEffect = null; |
| } else { |
| x = 2; |
| sideEffect = null; |
| } |
| sideEffect = null; |
| // cannot shift |
| return a * x * 41; |
| } |
| |
| public static final int ITERATIONS_LOOP_1 = 128; |
| public static final int ITERATIONS_LOOP_2 = 256; |
| |
| public static int testLoop01(int a) { |
| int res = 0; |
| for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { |
| res += i; |
| } |
| return res; |
| } |
| |
| public static int testLoop02(int a) { |
| int res = 0; |
| for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_2, i < a); i++) { |
| res += i; |
| } |
| return res; |
| } |
| |
| public static int testLoop03(int a) { |
| int res = 0; |
| for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { |
| res *= i; |
| } |
| return res; |
| } |
| |
| public static int testLoop04(int a) { |
| int res = 0; |
| for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1 * ITERATIONS_LOOP_2, i < a); i++) { |
| res += i; |
| } |
| return res; |
| } |
| |
| public static int testLoop05(int a) { |
| int res = 0; |
| for (int i = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_1, i < a); i++) { |
| res += i; |
| for (int j = 0; GraalDirectives.injectIterationCount(ITERATIONS_LOOP_2, j < ITERATIONS_LOOP_2); j++) { |
| res += i; |
| } |
| } |
| return res; |
| } |
| |
| public static int dontInline(int a) { |
| int res = 1; |
| for (int i = 0; i < a; i++) { |
| for (int j = 0; j < i; j++) { |
| for (int k = 0; k < j; k++) { |
| res += i + j + k; |
| } |
| } |
| } |
| sideEffect = res; |
| return (Integer) sideEffect; |
| } |
| |
| public static int untrused01(int a) { |
| return dontInline(a); |
| } |
| |
| public static int arrayLoadTest(int a) { |
| return array[a].hashCode(); |
| } |
| |
| public static int arrayStoreTest(int a) { |
| array[2] = a; |
| return a; |
| } |
| |
| public static int fieldLoad(int a) { |
| return sideEffect.hashCode() * a; |
| } |
| |
| public static int fieldStore(int a) { |
| sideEffect = a; |
| return a; |
| } |
| |
| @Test |
| public void testCanonicalizationExample() { |
| HighTierContext htc = getDefaultHighTierContext(); |
| ImprovementSavingCanonicalizer c1 = new ImprovementSavingCanonicalizer(); |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet")); |
| new CanonicalizerPhase(c1).apply(g1, htc); |
| ImprovementSavingCanonicalizer c2 = new ImprovementSavingCanonicalizer(); |
| StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet")); |
| new CanonicalizerPhase(c2).apply(g2, htc); |
| Assert.assertEquals(0, c1.savedCycles); |
| Assert.assertEquals(0, c2.savedCycles); |
| } |
| |
| private static void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) { |
| // let canonicalizer work away branch probability nodes |
| new CanonicalizerPhase().apply(g, htc); |
| // recompute the loop frequencies |
| ComputeLoopFrequenciesClosure.compute(g); |
| } |
| |
| private static void assertFrequency(StructuredGraph g, int iterations) { |
| NodeIterable<LoopBeginNode> loopBeginNodes = g.getNodes(LoopBeginNode.TYPE); |
| LoopBeginNode loopBeginNode = loopBeginNodes.first(); |
| Assert.assertEquals("loop frequency of " + loopBeginNode, iterations, loopBeginNode.loopFrequency(), 0); |
| } |
| |
| @Test |
| public void testDifferentLoopFaster() { |
| HighTierContext htc = getDefaultHighTierContext(); |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop01")); |
| StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop03")); |
| prepareGraphForLoopFrequencies(g1, htc); |
| prepareGraphForLoopFrequencies(g2, htc); |
| assertFrequency(g1, ITERATIONS_LOOP_1); |
| assertFrequency(g2, ITERATIONS_LOOP_1); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| GraphCostPhase gc2 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| gc2.apply(g2, htc); |
| 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); |
| Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); |
| Assert.assertTrue(gc2.finalSize == gc1.finalSize); |
| } |
| |
| @Test |
| public void testSameLoopMoreIterationsCostlier() { |
| HighTierContext htc = getDefaultHighTierContext(); |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop01")); |
| StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop02")); |
| prepareGraphForLoopFrequencies(g1, htc); |
| prepareGraphForLoopFrequencies(g2, htc); |
| assertFrequency(g1, ITERATIONS_LOOP_1); |
| assertFrequency(g2, ITERATIONS_LOOP_2); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| GraphCostPhase gc2 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| gc2.apply(g2, htc); |
| 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); |
| Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); |
| Assert.assertTrue(gc2.finalSize == gc1.finalSize); |
| } |
| |
| @Test |
| public void testDifferentLoopsInnerOuter() { |
| HighTierContext htc = getDefaultHighTierContext(); |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("testLoop04")); |
| StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("testLoop05")); |
| prepareGraphForLoopFrequencies(g1, htc); |
| prepareGraphForLoopFrequencies(g2, htc); |
| assertFrequency(g1, ITERATIONS_LOOP_1 * ITERATIONS_LOOP_2); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| GraphCostPhase gc2 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| gc2.apply(g2, htc); |
| 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); |
| Assert.assertTrue(gc2.finalSize > gc1.finalSize); |
| } |
| |
| @Test |
| public void testGraphCost() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet")); |
| StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| new CanonicalizerPhase().apply(g2, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| GraphCostPhase gc2 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| gc2.apply(g2, htc); |
| g1.getDebug().log("Test Graph Cost --> 1.Graph cost:%f vs. 2.Graph cost:%f\n", gc1.finalCycles, gc2.finalCycles); |
| Assert.assertTrue(gc2.finalCycles > gc1.finalCycles); |
| Assert.assertTrue(gc2.finalSize == gc1.finalSize); |
| } |
| |
| @Test |
| public void testExpectUntrusted() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("untrused01")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| } |
| |
| @Test |
| public void testArrayLoad() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayLoadTest")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| Assert.assertEquals(15, gc1.finalCycles, 25); |
| } |
| |
| @Test |
| public void testArrayStore() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayStoreTest")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| Assert.assertEquals(15, gc1.finalCycles, 25); |
| } |
| |
| @Test |
| public void testFieldLoad() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldLoad")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| Assert.assertEquals(15, gc1.finalCycles, 25); |
| } |
| |
| @Test |
| public void testFieldStore() { |
| StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldStore")); |
| HighTierContext htc = getDefaultHighTierContext(); |
| new CanonicalizerPhase().apply(g1, htc); |
| GraphCostPhase gc1 = new GraphCostPhase(); |
| gc1.apply(g1, htc); |
| Assert.assertEquals(15, gc1.finalCycles, 25); |
| } |
| |
| static class ImprovementSavingCanonicalizer extends CustomCanonicalizer { |
| private int savedCycles; |
| |
| @Override |
| public void simplify(Node node, SimplifierTool tool) { |
| if (node instanceof Canonicalizable.Binary<?>) { |
| @SuppressWarnings("unchecked") |
| Canonicalizable.Binary<ValueNode> bc = (Canonicalizable.Binary<ValueNode>) node; |
| Node canonicalized = bc.canonical(tool, bc.getX(), bc.getY()); |
| if (canonicalized != node) { |
| savedCycles += node.estimatedNodeCycles().value - canonicalized.estimatedNodeCycles().value; |
| } |
| } |
| } |
| } |
| |
| private static class GraphCostPhase extends BasePhase<PhaseContext> { |
| private double finalCycles; |
| private double finalSize; |
| |
| @Override |
| protected void run(StructuredGraph graph, PhaseContext context) { |
| finalCycles = NodeCostUtil.computeGraphCycles(graph, true); |
| finalSize = NodeCostUtil.computeGraphSize(graph); |
| } |
| |
| } |
| |
| } |