1 /*
   2  * Copyright (c) 2011, 2018, 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 
  24 
  25 package org.graalvm.compiler.core.test.ea;
  26 
  27 import java.util.List;
  28 
  29 import org.graalvm.compiler.core.test.GraalCompilerTest;
  30 import org.graalvm.compiler.nodes.ProxyNode;
  31 import org.graalvm.compiler.nodes.ReturnNode;
  32 import org.graalvm.compiler.nodes.StructuredGraph;
  33 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  34 import org.graalvm.compiler.nodes.ValueNode;
  35 import org.graalvm.compiler.nodes.ValuePhiNode;
  36 import org.graalvm.compiler.nodes.java.LoadFieldNode;
  37 import org.graalvm.compiler.nodes.java.StoreFieldNode;
  38 import org.graalvm.compiler.nodes.memory.ReadNode;
  39 import org.graalvm.compiler.nodes.spi.LoweringTool;
  40 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  41 import org.graalvm.compiler.phases.common.LoweringPhase;
  42 import org.graalvm.compiler.phases.tiers.HighTierContext;
  43 import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase;
  44 import org.junit.Test;
  45 
  46 public class EarlyReadEliminationTest extends GraalCompilerTest {
  47 
  48     public static Object staticField;
  49 
  50     public static class TestObject {
  51 
  52         public int x;
  53         public int y;
  54 
  55         public TestObject(int x, int y) {
  56             this.x = x;
  57             this.y = y;
  58         }
  59     }
  60 
  61     public static class TestObject2 {
  62 
  63         public Object x;
  64         public Object y;
  65 
  66         public TestObject2(Object x, Object y) {
  67             this.x = x;
  68             this.y = y;
  69         }
  70     }
  71 
  72     public static class TestObject3 extends TestObject {
  73 
  74         public int z;
  75 
  76         public TestObject3(int x, int y, int z) {
  77             super(x, y);
  78             this.z = z;
  79         }
  80     }
  81 
  82     @SuppressWarnings("all")
  83     public static int testSimpleSnippet(TestObject a) {
  84         a.x = 2;
  85         staticField = a;
  86         return a.x;
  87     }
  88 
  89     @Test
  90     public void testSimple() {
  91         // Test without lowering.
  92         ValueNode result = getReturn("testSimpleSnippet", false).result();
  93         assertTrue(result.graph().getNodes().filter(LoadFieldNode.class).isEmpty());
  94         assertTrue(result.isConstant());
  95         assertDeepEquals(2, result.asJavaConstant().asInt());
  96 
  97         // Test with lowering.
  98         result = getReturn("testSimpleSnippet", true).result();
  99         assertTrue(result.graph().getNodes().filter(ReadNode.class).isEmpty());
 100         assertTrue(result.isConstant());
 101         assertDeepEquals(2, result.asJavaConstant().asInt());
 102     }
 103 
 104     @SuppressWarnings("all")
 105     public static int testSimpleConflictSnippet(TestObject a, TestObject b) {
 106         a.x = 2;
 107         b.x = 3;
 108         staticField = a;
 109         return a.x;
 110     }
 111 
 112     @Test
 113     public void testSimpleConflict() {
 114         ValueNode result = getReturn("testSimpleConflictSnippet", false).result();
 115         assertFalse(result.isConstant());
 116         assertTrue(result instanceof LoadFieldNode);
 117     }
 118 
 119     @SuppressWarnings("all")
 120     public static int testParamSnippet(TestObject a, int b) {
 121         a.x = b;
 122         return a.x;
 123     }
 124 
 125     @Test
 126     public void testParam() {
 127         ValueNode result = getReturn("testParamSnippet", false).result();
 128         assertTrue(result.graph().getNodes().filter(LoadFieldNode.class).isEmpty());
 129         assertDeepEquals(result.graph().getParameter(1), result);
 130     }
 131 
 132     @SuppressWarnings("all")
 133     public static int testMaterializedSnippet(int a) {
 134         TestObject obj = new TestObject(a, 0);
 135         staticField = obj;
 136         return obj.x;
 137     }
 138 
 139     @Test
 140     public void testMaterialized() {
 141         ValueNode result = getReturn("testMaterializedSnippet", false).result();
 142         assertTrue(result.graph().getNodes().filter(LoadFieldNode.class).isEmpty());
 143         assertDeepEquals(result.graph().getParameter(0), result);
 144     }
 145 
 146     @SuppressWarnings("all")
 147     public static int testSimpleLoopSnippet(TestObject obj, int a, int b) {
 148         obj.x = a;
 149         for (int i = 0; i < 10; i++) {
 150             staticField = obj;
 151         }
 152         return obj.x;
 153     }
 154 
 155     @Test
 156     public void testSimpleLoop() {
 157         // Test without lowering.
 158         ValueNode result = getReturn("testSimpleLoopSnippet", false).result();
 159         assertTrue(result.graph().getNodes().filter(LoadFieldNode.class).isEmpty());
 160         assertDeepEquals(result.graph().getParameter(1), result);
 161 
 162         // Now test with lowering.
 163         result = getReturn("testSimpleLoopSnippet", true).result();
 164         assertTrue(result.graph().getNodes().filter(ReadNode.class).isEmpty());
 165         assertDeepEquals(result.graph().getParameter(1), result);
 166     }
 167 
 168     @SuppressWarnings("all")
 169     public static int testBadLoopSnippet(TestObject obj, TestObject obj2, int a, int b) {
 170         obj.x = a;
 171         for (int i = 0; i < 10; i++) {
 172             staticField = obj;
 173             obj2.x = 10;
 174             obj.x = 0;
 175         }
 176         return obj.x;
 177     }
 178 
 179     @Test
 180     public void testBadLoop() {
 181         ValueNode result = getReturn("testBadLoopSnippet", false).result();
 182         assertDeepEquals(0, result.graph().getNodes().filter(LoadFieldNode.class).count());
 183         assertTrue(result instanceof ProxyNode);
 184         assertTrue(((ProxyNode) result).value() instanceof ValuePhiNode);
 185     }
 186 
 187     @SuppressWarnings("all")
 188     public static int testBadLoop2Snippet(TestObject obj, TestObject obj2, int a, int b) {
 189         obj.x = a;
 190         for (int i = 0; i < 10; i++) {
 191             obj.x = 0;
 192             obj2.x = 10;
 193         }
 194         return obj.x;
 195     }
 196 
 197     @Test
 198     public void testBadLoop2() {
 199         ValueNode result = getReturn("testBadLoop2Snippet", false).result();
 200         assertDeepEquals(1, result.graph().getNodes().filter(LoadFieldNode.class).count());
 201         assertTrue(result instanceof LoadFieldNode);
 202     }
 203 
 204     @SuppressWarnings("all")
 205     public static int testPhiSnippet(TestObject a, int b) {
 206         if (b < 0) {
 207             a.x = 1;
 208         } else {
 209             a.x = 2;
 210         }
 211         return a.x;
 212     }
 213 
 214     @Test
 215     public void testPhi() {
 216         StructuredGraph graph = processMethod("testPhiSnippet", false);
 217         assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty());
 218         List<ReturnNode> returnNodes = graph.getNodes(ReturnNode.TYPE).snapshot();
 219         assertDeepEquals(2, returnNodes.size());
 220         assertTrue(returnNodes.get(0).predecessor() instanceof StoreFieldNode);
 221         assertTrue(returnNodes.get(1).predecessor() instanceof StoreFieldNode);
 222         assertTrue(returnNodes.get(0).result().isConstant());
 223         assertTrue(returnNodes.get(1).result().isConstant());
 224     }
 225 
 226     @SuppressWarnings("all")
 227     public static void testSimpleStoreSnippet(TestObject a, int b) {
 228         a.x = b;
 229         a.x = b;
 230     }
 231 
 232     @Test
 233     public void testSimpleStore() {
 234         StructuredGraph graph = processMethod("testSimpleStoreSnippet", false);
 235         assertDeepEquals(1, graph.getNodes().filter(StoreFieldNode.class).count());
 236     }
 237 
 238     public static int testValueProxySnippet(boolean b, TestObject o) {
 239         int sum = 0;
 240         if (b) {
 241             sum += o.x;
 242         } else {
 243             TestObject3 p = (TestObject3) o;
 244             sum += p.x;
 245         }
 246         sum += o.x;
 247         return sum;
 248     }
 249 
 250     @Test
 251     public void testValueProxy() {
 252         StructuredGraph graph = processMethod("testValueProxySnippet", false);
 253         assertDeepEquals(2, graph.getNodes().filter(LoadFieldNode.class).count());
 254     }
 255 
 256     ReturnNode getReturn(String snippet, boolean doLowering) {
 257         StructuredGraph graph = processMethod(snippet, doLowering);
 258         assertDeepEquals(1, graph.getNodes(ReturnNode.TYPE).count());
 259         return graph.getNodes(ReturnNode.TYPE).first();
 260     }
 261 
 262     protected StructuredGraph processMethod(String snippet, boolean doLowering) {
 263         StructuredGraph graph = parseEager(getResolvedJavaMethod(snippet), AllowAssumptions.NO);
 264         HighTierContext context = getDefaultHighTierContext();
 265         createInliningPhase().apply(graph, context);
 266         if (doLowering) {
 267             new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
 268         }
 269         new EarlyReadEliminationPhase(new CanonicalizerPhase()).apply(graph, context);
 270         return graph;
 271     }
 272 }