1 /* 2 * Copyright (c) 2011, 2014, 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.ea; 24 25 import java.nio.ByteBuffer; 26 27 import org.graalvm.compiler.api.directives.GraalDirectives; 28 import org.graalvm.compiler.graph.Graph; 29 import org.graalvm.compiler.graph.Node; 30 import org.graalvm.compiler.nodes.NamedLocationIdentity; 31 import org.graalvm.compiler.nodes.PhiNode; 32 import org.graalvm.compiler.nodes.ValuePhiNode; 33 import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode; 34 import org.graalvm.compiler.nodes.extended.RawLoadNode; 35 import org.graalvm.compiler.nodes.extended.RawStoreNode; 36 import org.graalvm.compiler.nodes.extended.UnsafeAccessNode; 37 import org.graalvm.compiler.nodes.java.LoadFieldNode; 38 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 39 import org.junit.Assert; 40 import org.junit.Test; 41 42 import jdk.vm.ci.meta.JavaConstant; 43 import jdk.vm.ci.meta.JavaKind; 44 import jdk.vm.ci.meta.ResolvedJavaMethod; 45 46 public class UnsafeEATest extends EATestBase { 47 48 public static int zero = 0; 49 50 private static final long fieldOffset1; 51 private static final long fieldOffset2; 52 53 static { 54 try { 55 long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x")); 56 // Make the fields 8 byte aligned (Required for testing setLong on Architectures which 57 // does not support unaligned memory access 58 if (localFieldOffset1 % 8 == 0) { 59 fieldOffset1 = localFieldOffset1; 60 fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("y")); 61 } else { 62 fieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("y")); 63 fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z")); 64 } 65 assert fieldOffset2 == fieldOffset1 + 4; 66 } catch (Exception e) { 67 throw new RuntimeException(e); 68 } 69 } 70 71 @Override 72 protected void testEscapeAnalysis(String snippet, JavaConstant expectedConstantResult, boolean iterativeEscapeAnalysis) { 73 // Exercise both a graph containing UnsafeAccessNodes and one which has been possibly been 74 // canonicalized into AccessFieldNodes. 75 testingUnsafe = true; 76 super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis); 77 testingUnsafe = false; 78 super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis); 79 if (expectedConstantResult != null) { 80 // Check that a compiled version of this method returns the same value if we expect a 81 // constant result. 82 ResolvedJavaMethod method = getResolvedJavaMethod(snippet); 83 JavaKind[] javaKinds = method.getSignature().toParameterKinds(false); 84 Object[] args = new Object[javaKinds.length]; 85 int i = 0; 86 for (JavaKind k : javaKinds) { 87 args[i++] = JavaConstant.defaultForKind(k).asBoxedPrimitive(); 88 } 89 Result result = executeExpected(method, null, args); 90 assertTrue(result.returnValue.equals(expectedConstantResult.asBoxedPrimitive())); 91 } 92 } 93 94 @Override 95 protected void canonicalizeGraph() { 96 if (testingUnsafe) { 97 // For testing purposes we'd like to ensure that our raw unsafe operations stay as 98 // unsafe nodes, so force them to appear to have LocationIdentity.any to disable 99 // transformation into field access nodes. 100 for (Node node : graph.getNodes().filter(x -> x instanceof UnsafeAccessNode).snapshot()) { 101 if (node instanceof RawStoreNode) { 102 RawStoreNode store = (RawStoreNode) node; 103 RawStoreNode newStore = graph.add(new RawStoreNode(store.object(), store.offset(), store.value(), store.accessKind(), NamedLocationIdentity.any(), 104 store.needsBarrier(), store.stateAfter(), true)); 105 graph.replaceFixedWithFixed(store, newStore); 106 } else if (node instanceof RawLoadNode) { 107 RawLoadNode load = (RawLoadNode) node; 108 RawLoadNode newLoad = graph.add(new RawLoadNode(load.object(), load.offset(), load.accessKind(), NamedLocationIdentity.any(), 109 true)); 110 graph.replaceFixedWithFixed(load, newLoad); 111 } 112 } 113 } 114 super.canonicalizeGraph(); 115 } 116 117 @Override 118 protected void postEACanonicalizeGraph() { 119 // Simplify any UnpackEndianHalfNode so we end up with constants. 120 Graph.Mark mark = graph.getMark(); 121 for (UnpackEndianHalfNode node : graph.getNodes().filter(UnpackEndianHalfNode.class)) { 122 node.lower(getTarget().arch.getByteOrder()); 123 } 124 new CanonicalizerPhase().applyIncremental(graph, context, mark); 125 } 126 127 private boolean testingUnsafe; 128 129 @Test 130 public void testSimpleInt() { 131 testEscapeAnalysis("testSimpleIntSnippet", JavaConstant.forInt(101), false); 132 } 133 134 public static int testSimpleIntSnippet() { 135 TestClassInt x = new TestClassInt(); 136 UNSAFE.putInt(x, fieldOffset1, 101); 137 return UNSAFE.getInt(x, fieldOffset1); 138 } 139 140 @Test 141 public void testMaterializedInt() { 142 test("testMaterializedIntSnippet"); 143 } 144 145 public static TestClassInt testMaterializedIntSnippet() { 146 TestClassInt x = new TestClassInt(); 147 UNSAFE.putInt(x, fieldOffset1, 101); 148 return x; 149 } 150 151 @Test 152 public void testSimpleDouble() { 153 testEscapeAnalysis("testSimpleDoubleSnippet", JavaConstant.forDouble(10.1), false); 154 } 155 156 public static double testSimpleDoubleSnippet() { 157 TestClassInt x = new TestClassInt(); 158 UNSAFE.putDouble(x, fieldOffset1, 10.1); 159 return UNSAFE.getDouble(x, fieldOffset1); 160 } 161 162 @Test 163 public void testSimpleDoubleOverwriteWithInt() { 164 testEscapeAnalysis("testSimpleDoubleOverwriteWithIntSnippet", JavaConstant.forInt(10), false); 165 } 166 167 public static int testSimpleDoubleOverwriteWithIntSnippet() { 168 TestClassInt x = new TestClassInt(); 169 UNSAFE.putDouble(x, fieldOffset1, 10.1); 170 UNSAFE.putInt(x, fieldOffset1, 10); 171 return UNSAFE.getInt(x, fieldOffset1); 172 } 173 174 @Test 175 public void testSimpleDoubleOverwriteWithSecondInt() { 176 ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); 177 bb.putDouble(10.1); 178 int value = bb.getInt(4); 179 180 testEscapeAnalysis("testSimpleDoubleOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false); 181 } 182 183 public static int testSimpleDoubleOverwriteWithSecondIntSnippet() { 184 TestClassInt x = new TestClassInt(); 185 UNSAFE.putDouble(x, fieldOffset1, 10.1); 186 UNSAFE.putInt(x, fieldOffset1, 10); 187 return UNSAFE.getInt(x, fieldOffset2); 188 } 189 190 @Test 191 public void testSimpleDoubleOverwriteWithFirstInt() { 192 ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); 193 bb.putDouble(10.1); 194 int value = bb.getInt(0); 195 196 testEscapeAnalysis("testSimpleDoubleOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false); 197 } 198 199 public static int testSimpleDoubleOverwriteWithFirstIntSnippet() { 200 TestClassInt x = new TestClassInt(); 201 UNSAFE.putDouble(x, fieldOffset1, 10.1); 202 UNSAFE.putInt(x, fieldOffset2, 10); 203 return UNSAFE.getInt(x, fieldOffset1); 204 } 205 206 @Test 207 public void testSimpleLongOverwriteWithSecondInt() { 208 ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); 209 bb.putLong(0, 0x1122334455667788L); 210 int value = bb.getInt(4); 211 212 testEscapeAnalysis("testSimpleLongOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false); 213 } 214 215 public static int testSimpleLongOverwriteWithSecondIntSnippet() { 216 TestClassInt x = new TestClassInt(); 217 UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L); 218 UNSAFE.putInt(x, fieldOffset1, 10); 219 return UNSAFE.getInt(x, fieldOffset2); 220 } 221 222 @Test 223 public void testSimpleLongOverwriteWithFirstInt() { 224 ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); 225 bb.putLong(0, 0x1122334455667788L); 226 int value = bb.getInt(0); 227 228 testEscapeAnalysis("testSimpleLongOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false); 229 } 230 231 public static int testSimpleLongOverwriteWithFirstIntSnippet() { 232 TestClassInt x = new TestClassInt(); 233 UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L); 234 UNSAFE.putInt(x, fieldOffset2, 10); 235 return UNSAFE.getInt(x, fieldOffset1); 236 } 237 238 @Test 239 public void testMergedDouble() { 240 testEscapeAnalysis("testMergedDoubleSnippet", null, false); 241 Assert.assertEquals(1, returnNodes.size()); 242 Assert.assertTrue(returnNodes.get(0).result() instanceof ValuePhiNode); 243 PhiNode phi = (PhiNode) returnNodes.get(0).result(); 244 Assert.assertTrue(phi.valueAt(0) instanceof LoadFieldNode); 245 Assert.assertTrue(phi.valueAt(1) instanceof LoadFieldNode); 246 } 247 248 public static double testMergedDoubleSnippet(boolean a) { 249 TestClassInt x; 250 if (a) { 251 x = new TestClassInt(0, 0); 252 UNSAFE.putDouble(x, fieldOffset1, doubleField); 253 } else { 254 x = new TestClassInt(); 255 UNSAFE.putDouble(x, fieldOffset1, doubleField2); 256 } 257 return UNSAFE.getDouble(x, fieldOffset1); 258 } 259 260 static class ExtendedTestClassInt extends TestClassInt { 261 public long l; 262 } 263 264 @Test 265 public void testMergedVirtualObjects() { 266 testEscapeAnalysis("testMergedVirtualObjectsSnippet", null, false); 267 } 268 269 public static TestClassInt testMergedVirtualObjectsSnippet(int value) { 270 TestClassInt x; 271 if (value == 1) { 272 x = new TestClassInt(); 273 UNSAFE.putDouble(x, fieldOffset1, 10); 274 } else { 275 x = new TestClassInt(); 276 UNSAFE.putInt(x, fieldOffset1, 0); 277 } 278 UNSAFE.putInt(x, fieldOffset1, 0); 279 if (value == 2) { 280 UNSAFE.putInt(x, fieldOffset2, 0); 281 } 282 GraalDirectives.deoptimizeAndInvalidate(); 283 return x; 284 } 285 286 @Test 287 public void testMaterializedDouble() { 288 test("testMaterializedDoubleSnippet"); 289 } 290 291 public static TestClassInt testMaterializedDoubleSnippet() { 292 TestClassInt x = new TestClassInt(); 293 UNSAFE.putDouble(x, fieldOffset1, 10.1); 294 return x; 295 } 296 297 @Test 298 public void testDeoptDoubleVar() { 299 test("testDeoptDoubleVarSnippet"); 300 } 301 302 public static double doubleField = 10.1e99; 303 public static double doubleField2; 304 305 public static TestClassInt testDeoptDoubleVarSnippet() { 306 TestClassInt x = new TestClassInt(); 307 UNSAFE.putDouble(x, fieldOffset1, doubleField); 308 doubleField2 = 123; 309 try { 310 doubleField = ((int) UNSAFE.getDouble(x, fieldOffset1)) / zero; 311 } catch (RuntimeException e) { 312 return x; 313 } 314 return x; 315 } 316 317 @Test 318 public void testDeoptDoubleConstant() { 319 test("testDeoptDoubleConstantSnippet"); 320 } 321 322 public static TestClassInt testDeoptDoubleConstantSnippet() { 323 TestClassInt x = new TestClassInt(); 324 UNSAFE.putDouble(x, fieldOffset1, 10.123); 325 doubleField2 = 123; 326 try { 327 doubleField = ((int) UNSAFE.getDouble(x, fieldOffset1)) / zero; 328 } catch (RuntimeException e) { 329 return x; 330 } 331 return x; 332 } 333 334 @Test 335 public void testDeoptLongVar() { 336 test("testDeoptLongVarSnippet"); 337 } 338 339 public static long longField = 0x133443218aaaffffL; 340 public static long longField2; 341 342 public static TestClassInt testDeoptLongVarSnippet() { 343 TestClassInt x = new TestClassInt(); 344 UNSAFE.putLong(x, fieldOffset1, longField); 345 longField2 = 123; 346 try { 347 longField = UNSAFE.getLong(x, fieldOffset1) / zero; 348 } catch (RuntimeException e) { 349 return x; 350 } 351 return x; 352 } 353 354 @Test 355 public void testDeoptLongConstant() { 356 test("testDeoptLongConstantSnippet"); 357 } 358 359 public static TestClassInt testDeoptLongConstantSnippet() { 360 TestClassInt x = new TestClassInt(); 361 UNSAFE.putLong(x, fieldOffset1, 0x2222222210123L); 362 longField2 = 123; 363 try { 364 longField = UNSAFE.getLong(x, fieldOffset1) / zero; 365 } catch (RuntimeException e) { 366 return x; 367 } 368 return x; 369 } 370 371 }