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 */ 23package org.graalvm.compiler.core.test; 24 25import java.lang.reflect.Field; 26 27import org.junit.Assert; 28import org.junit.Test; 29 30import org.graalvm.compiler.nodes.IfNode; 31import org.graalvm.compiler.nodes.StructuredGraph; 32import org.graalvm.compiler.phases.common.CanonicalizerPhase; 33import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase; 34import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase; 35 36import sun.misc.Unsafe; 37 38public class ConditionalEliminationLoadFieldConstantFoldTest extends GraalCompilerTest { 39 public static int intSideEffect; 40 41 public static final B FinalField = new B(10); 42 43 private abstract static class A { 44 45 } 46 47 private static class B extends A { 48 final int a; 49 50 B(int a) { 51 this.a = a; 52 } 53 } 54 55 private static class C extends A { 56 final B b; 57 58 C(B b) { 59 this.b = b; 60 } 61 } 62 63 private static class D extends A { 64 final C c; 65 66 D(C c) { 67 this.c = c; 68 } 69 } 70 71 private static class E extends D { 72 final Object o; 73 74 E(C c, Object o) { 75 super(c); 76 this.o = o; 77 } 78 } 79 80 public static final B CONST_B = new B(10); 81 public static final C CONST_C = new C(CONST_B); 82 public static final D CONST_D = new D(CONST_C); 83 84 public int testReadConstInBranch(B b) { 85 if (b == CONST_B) { 86 if (b.a == 5) { 87 intSideEffect = b.a; 88 } else { 89 intSideEffect = 10; 90 } 91 } 92 return 0; 93 } 94 95 public int testMultipleReadsConstInBranch(D d) { 96 if (d == CONST_D) { 97 C c = d.c; 98 B b = c.b; 99 int res = b.a + 12; 100 if (res == 125) { 101 intSideEffect = 12; 102 } 103 } 104 return 0; 105 } 106 107 public int testLoadFinalInstanceOf(E e) { 108 Object o = e.o; 109 if (o == CONST_C) { 110 if (o instanceof A) { 111 // eliminate, implied by a.x == Const(Subclass) 112 intSideEffect = 1; 113 } else { 114 intSideEffect = 10; 115 } 116 } 117 return 0; 118 } 119 120 public int testLoadFinalTwiceInstanceOf(E e) { 121 if (e.o == CONST_C) { 122 if (e.o instanceof A) { 123 intSideEffect = 1; 124 } else { 125 intSideEffect = 10; 126 } 127 } 128 return 0; 129 } 130 131 static class C1 { 132 final int a; 133 134 C1(int a) { 135 this.a = a; 136 } 137 } 138 139 static class C2 { 140 final C1 c1; 141 142 C2(C1 c1) { 143 this.c1 = c1; 144 } 145 } 146 147 public static int foldThatIsNotAllowed(C2 c2) { 148 // read before, this will be used to load through when folding 149 C1 c1Unknown = c2.c1; 150 151 // be naughty (will be a store field after canonicalization as it has a constant offset, so 152 // we would be able to eliminate the inner if after an early read elimination but we would 153 // fold before and ce the inner if already) 154 // 155 // note: if the offset would not be constant but a parameter we would not even be able to 156 // remove in inner most if as we cannot rewrite the unsafe store to a store field node as 157 // the store might kill ANY_LOCATION 158 UNSAFE.putObject(c2, C2_C1_OFFSET, C1_AFTER_READ_CONST); 159 160 if (c2 == C2_CONST) { 161 if (c1Unknown == C1_CONST) { 162 /* 163 * This if can be eliminated (as we rewrite the unsafe store with a constant offset 164 * to a store field node) but the remaining branch must be the false branch. If we 165 * do not fold through both field loads we will canonicalize the unsafe store to a 166 * store field, see the new value and can thus eliminate the true branch 167 * 168 * if we fold through the load fields we would load from the object read before the 169 * store so we miss the unsafe update 170 */ 171 if (c2.c1.a == 10) { 172 intSideEffect = 1; 173 return 1; 174 } else { 175 intSideEffect = 2; 176 return 2; 177 } 178 } else { 179 intSideEffect = -2; 180 return -2; 181 } 182 } else { 183 intSideEffect = -1; 184 return -1; 185 } 186 } 187 188 public int testLoadFinalTwiceNoReadEliminationInstanceOf(E e) { 189 if (e.o == CONST_C) { 190 /* 191 * we cannot eliminate the second read of e.o although it is a final field. the call to 192 * System.gc (or any other memory checkpoint killing ANY_LOCATION) will prohibit the 193 * elimination of the second load, thus we have two different load nodes, we know that 194 * that first load field is a constant but we do not know for the second one, assuming 195 * e.o is final, as it might have been written in between 196 * 197 * this prohibits us to remove the if (fold through all loads to final fields) and the 198 * instance of e.o 199 */ 200 System.gc(); 201 C c = (C) e.o; 202 if (c.b.a == 10) { 203 intSideEffect = 1; 204 } else { 205 intSideEffect = 10; 206 } 207 } 208 return 0; 209 210 } 211 212 private static final C1 C1_CONST = new C1(0); 213 private static final C2 C2_CONST = new C2(C1_CONST); 214 private static final C1 C1_AFTER_READ_CONST = new C1(10); 215 216 private static Unsafe getUnsafe() { 217 try { 218 return Unsafe.getUnsafe(); 219 } catch (SecurityException e) { 220 } 221 try { 222 Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); 223 theUnsafeInstance.setAccessible(true); 224 return (Unsafe) theUnsafeInstance.get(Unsafe.class); 225 } catch (Exception e) { 226 throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); 227 } 228 } 229 230 private static final sun.misc.Unsafe UNSAFE = getUnsafe(); 231 private static final long C2_C1_OFFSET; 232 233 static { 234 try { 235 Field f = C2.class.getDeclaredField("c1"); 236 C2_C1_OFFSET = UNSAFE.objectFieldOffset(f); 237 } catch (NoSuchFieldException | SecurityException e) { 238 throw new RuntimeException(e); 239 } 240 } 241 242 @Test 243 public void test01() { 244 checkGraph("testReadConstInBranch", 1); 245 test("testReadConstInBranch", new B(1)); 246 } 247 248 @Test 249 public void test02() { 250 checkGraph("testMultipleReadsConstInBranch", 1); 251 } 252 253 @Test 254 public void test03() { 255 checkGraph("testLoadFinalInstanceOf", 1); 256 } 257 258 @Test 259 public void test04() { 260 checkGraph("testLoadFinalTwiceInstanceOf", 1); 261 } 262 263 @Test 264 public void test05() { 265 checkGraph("testLoadFinalTwiceNoReadEliminationInstanceOf", 2); 266 } 267 268 @Test(expected = AssertionError.class) 269 @SuppressWarnings("try") 270 public void test06() { 271 Result actual = executeActual(getResolvedJavaMethod("foldThatIsNotAllowed"), null, C2_CONST); 272 UNSAFE.putObject(C2_CONST, C2_C1_OFFSET, C1_CONST); 273 Result expected = executeExpected(getResolvedJavaMethod("foldThatIsNotAllowed"), null, C2_CONST); 274 Assert.assertEquals(expected.returnValue, actual.returnValue); 275 } 276 277 @SuppressWarnings("try") 278 private StructuredGraph checkGraph(String name, int nrOfIfsAfter) { 279 StructuredGraph g = parseForCompile(getResolvedJavaMethod(name)); 280 CanonicalizerPhase c = new CanonicalizerPhase(); 281 c.apply(g, getDefaultHighTierContext()); 282 new EarlyReadEliminationPhase(c).apply(g, getDefaultHighTierContext()); 283 new IterativeConditionalEliminationPhase(c, false).apply(g, getDefaultHighTierContext()); 284 Assert.assertEquals("Nr of Ifs left does not match", nrOfIfsAfter, g.getNodes().filter(IfNode.class).count()); 285 c.apply(g, getDefaultHighTierContext()); 286 return g; 287 } 288 289} 290