blob: f3cd492251c56b30cf32a15683c84f1697018938 [file] [log] [blame]
/*
* 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);
}
}
}