1 /* 2 * Copyright (c) 2014, 2015, 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.replacements.test; 24 25 import org.junit.Assert; 26 import org.junit.Test; 27 28 import jdk.vm.ci.code.InstalledCode; 29 import jdk.vm.ci.meta.JavaKind; 30 import jdk.vm.ci.meta.ResolvedJavaMethod; 31 import sun.misc.Unsafe; 32 33 /** 34 * Tests the VM independent intrinsification of {@link Unsafe} methods. 35 */ 36 public class UnsafeSubstitutionsTest extends MethodSubstitutionTest { 37 38 public void testSubstitution(String testMethodName, Class<?> holder, String methodName, Class<?>[] parameterTypes, Object receiver, Object[] args1, Object[] args2) { 39 ResolvedJavaMethod testMethod = getResolvedJavaMethod(testMethodName); 40 ResolvedJavaMethod originalMethod = getResolvedJavaMethod(holder, methodName, parameterTypes); 41 42 // Force compilation 43 InstalledCode code = getCode(testMethod); 44 assert code != null; 45 46 // Verify that the original method and the substitution produce the same value 47 Object expected = invokeSafe(originalMethod, receiver, args1); 48 Object actual = invokeSafe(testMethod, null, args2); 49 assertDeepEquals(expected, actual); 50 51 // Verify that the generated code and the original produce the same value 52 expected = invokeSafe(originalMethod, receiver, args1); 53 actual = executeVarargsSafe(code, args2); 54 assertDeepEquals(expected, actual); 55 56 } 57 58 static long off(Object o, String name) { 59 try { 60 return UNSAFE.objectFieldOffset(o.getClass().getDeclaredField(name)); 61 } catch (Exception e) { 62 Assert.fail(e.toString()); 63 return 0L; 64 } 65 } 66 67 static class Foo { 68 boolean z; 69 byte b; 70 short s; 71 char c; 72 int i; 73 long l; 74 float f; 75 double d; 76 Object o; 77 } 78 79 @Test 80 public void testUnsafeSubstitutions() throws Exception { 81 test("unsafeCompareAndSwapInt", UNSAFE, supply(() -> new Foo()), fooOffset("i")); 82 83 testGraph("unsafeCompareAndSwapInt"); 84 testGraph("unsafeCompareAndSwapLong"); 85 testGraph("unsafeCompareAndSwapObject"); 86 87 testGraph("unsafeGetBoolean"); 88 testGraph("unsafeGetByte"); 89 testGraph("unsafeGetShort"); 90 testGraph("unsafeGetChar"); 91 testGraph("unsafeGetInt"); 92 testGraph("unsafeGetLong"); 93 testGraph("unsafeGetFloat"); 94 testGraph("unsafeGetDouble"); 95 testGraph("unsafeGetObject"); 96 97 testGraph("unsafePutBoolean"); 98 testGraph("unsafePutByte"); 99 testGraph("unsafePutShort"); 100 testGraph("unsafePutChar"); 101 testGraph("unsafePutInt"); 102 testGraph("unsafePutLong"); 103 testGraph("unsafePutFloat"); 104 testGraph("unsafePutDouble"); 105 testGraph("unsafePutObject"); 106 107 testGraph("unsafeGetAddress"); 108 testGraph("unsafePutAddress"); 109 110 testGraph("unsafeDirectMemoryRead"); 111 testGraph("unsafeDirectMemoryWrite"); 112 113 long address = UNSAFE.allocateMemory(8 * JavaKind.values().length); 114 for (Unsafe unsafeArg : new Unsafe[]{UNSAFE, null}) { 115 test("unsafeCompareAndSwapInt", unsafeArg, supply(() -> new Foo()), fooOffset("i")); 116 test("unsafeCompareAndSwapLong", unsafeArg, supply(() -> new Foo()), fooOffset("l")); 117 test("unsafeCompareAndSwapObject", unsafeArg, supply(() -> new Foo()), fooOffset("o")); 118 119 test("unsafeGetBoolean", unsafeArg, supply(() -> new Foo()), fooOffset("z")); 120 test("unsafeGetByte", unsafeArg, supply(() -> new Foo()), fooOffset("b")); 121 test("unsafeGetShort", unsafeArg, supply(() -> new Foo()), fooOffset("s")); 122 test("unsafeGetChar", unsafeArg, supply(() -> new Foo()), fooOffset("c")); 123 test("unsafeGetInt", unsafeArg, supply(() -> new Foo()), fooOffset("i")); 124 test("unsafeGetLong", unsafeArg, supply(() -> new Foo()), fooOffset("l")); 125 test("unsafeGetFloat", unsafeArg, supply(() -> new Foo()), fooOffset("f")); 126 test("unsafeGetDouble", unsafeArg, supply(() -> new Foo()), fooOffset("d")); 127 test("unsafeGetObject", unsafeArg, supply(() -> new Foo()), fooOffset("o")); 128 129 test("unsafePutBoolean", unsafeArg, supply(() -> new Foo()), fooOffset("z"), true); 130 test("unsafePutByte", unsafeArg, supply(() -> new Foo()), fooOffset("b"), (byte) 87); 131 test("unsafePutShort", unsafeArg, supply(() -> new Foo()), fooOffset("s"), (short) -93); 132 test("unsafePutChar", unsafeArg, supply(() -> new Foo()), fooOffset("c"), 'A'); 133 test("unsafePutInt", unsafeArg, supply(() -> new Foo()), fooOffset("i"), 42); 134 test("unsafePutLong", unsafeArg, supply(() -> new Foo()), fooOffset("l"), 4711L); 135 test("unsafePutFloat", unsafeArg, supply(() -> new Foo()), fooOffset("f"), 58.0F); 136 test("unsafePutDouble", unsafeArg, supply(() -> new Foo()), fooOffset("d"), -28736.243465D); 137 test("unsafePutObject", unsafeArg, supply(() -> new Foo()), fooOffset("i"), "value1", "value2", "value3"); 138 139 test("unsafeGetAddress", unsafeArg, address); 140 test("unsafePutAddress", unsafeArg, address, 0xDEAD_BEEF_DEAD_BABEL); 141 142 test("unsafeDirectMemoryRead", unsafeArg, address); 143 test("unsafeDirectMemoryWrite", unsafeArg, address, 0xCAFE_BABE_DEAD_BABEL); 144 } 145 UNSAFE.freeMemory(address); 146 } 147 148 private static long fooOffset(String name) { 149 try { 150 return UNSAFE.objectFieldOffset(Foo.class.getDeclaredField(name)); 151 } catch (NoSuchFieldException | SecurityException e) { 152 throw new AssertionError(e); 153 } 154 } 155 156 @SuppressWarnings("all") 157 public static boolean unsafeCompareAndSwapInt(Unsafe unsafe, Object obj, long offset) { 158 return unsafe.compareAndSwapInt(obj, offset, 0, 1); 159 } 160 161 @SuppressWarnings("all") 162 public static boolean unsafeCompareAndSwapLong(Unsafe unsafe, Object obj, long offset) { 163 return unsafe.compareAndSwapLong(obj, offset, 0, 1); 164 } 165 166 @SuppressWarnings("all") 167 public static boolean unsafeCompareAndSwapObject(Unsafe unsafe, Object obj, long offset) { 168 return unsafe.compareAndSwapObject(obj, offset, null, new Object()); 169 } 170 171 @SuppressWarnings("all") 172 public static boolean unsafeGetBoolean(Unsafe unsafe, Object obj, long offset) { 173 return unsafe.getBoolean(obj, offset) && unsafe.getBooleanVolatile(obj, offset); 174 } 175 176 @SuppressWarnings("all") 177 public static int unsafeGetByte(Unsafe unsafe, Object obj, long offset) { 178 return unsafe.getByte(obj, offset) + unsafe.getByteVolatile(obj, offset); 179 } 180 181 @SuppressWarnings("all") 182 public static int unsafeGetShort(Unsafe unsafe, Object obj, long offset) { 183 return unsafe.getShort(obj, offset) + unsafe.getShortVolatile(obj, offset); 184 } 185 186 @SuppressWarnings("all") 187 public static int unsafeGetChar(Unsafe unsafe, Object obj, long offset) { 188 return unsafe.getChar(obj, offset) + unsafe.getCharVolatile(obj, offset); 189 } 190 191 @SuppressWarnings("all") 192 public static int unsafeGetInt(Unsafe unsafe, Object obj, long offset) { 193 return unsafe.getInt(obj, offset) + unsafe.getIntVolatile(obj, offset); 194 } 195 196 @SuppressWarnings("all") 197 public static long unsafeGetLong(Unsafe unsafe, Object obj, long offset) { 198 return unsafe.getLong(obj, offset) + unsafe.getLongVolatile(obj, offset); 199 } 200 201 @SuppressWarnings("all") 202 public static float unsafeGetFloat(Unsafe unsafe, Object obj, long offset) { 203 return unsafe.getFloat(obj, offset) + unsafe.getFloatVolatile(obj, offset); 204 } 205 206 @SuppressWarnings("all") 207 public static double unsafeGetDouble(Unsafe unsafe, Object obj, long offset) { 208 return unsafe.getDouble(obj, offset) + unsafe.getDoubleVolatile(obj, offset); 209 } 210 211 @SuppressWarnings("all") 212 public static boolean unsafeGetObject(Unsafe unsafe, Object obj, long offset) { 213 return unsafe.getObject(obj, offset) == unsafe.getObjectVolatile(obj, offset); 214 } 215 216 @SuppressWarnings("all") 217 public static int unsafePutBoolean(Unsafe unsafe, Object obj, long offset, boolean value) { 218 int res = 1; 219 unsafe.putBoolean(obj, offset, value); 220 res += unsafe.getBoolean(obj, offset) ? 3 : 5; 221 unsafe.putBooleanVolatile(obj, offset, value); 222 res += unsafe.getBoolean(obj, offset) ? 7 : 11; 223 return res; 224 } 225 226 @SuppressWarnings("all") 227 public static int unsafePutByte(Unsafe unsafe, Object obj, long offset, byte value) { 228 int res = 1; 229 unsafe.putByte(obj, offset, (byte) (value + 1)); 230 res += unsafe.getByte(obj, offset); 231 unsafe.putByteVolatile(obj, offset, (byte) (value + 2)); 232 res += unsafe.getByte(obj, offset); 233 return res; 234 } 235 236 @SuppressWarnings("all") 237 public static int unsafePutShort(Unsafe unsafe, Object obj, long offset, short value) { 238 int res = 1; 239 unsafe.putShort(obj, offset, (short) (value + 1)); 240 res += unsafe.getShort(obj, offset); 241 unsafe.putShortVolatile(obj, offset, (short) (value + 2)); 242 res += unsafe.getShort(obj, offset); 243 return res; 244 } 245 246 @SuppressWarnings("all") 247 public static int unsafePutChar(Unsafe unsafe, Object obj, long offset, char value) { 248 int res = 1; 249 unsafe.putChar(obj, offset, (char) (value + 1)); 250 res += unsafe.getChar(obj, offset); 251 unsafe.putCharVolatile(obj, offset, (char) (value + 2)); 252 res += unsafe.getChar(obj, offset); 253 return res; 254 } 255 256 @SuppressWarnings("all") 257 public static int unsafePutInt(Unsafe unsafe, Object obj, long offset, int value) { 258 int res = 1; 259 unsafe.putInt(obj, offset, value); 260 res += unsafe.getInt(obj, offset); 261 unsafe.putIntVolatile(obj, offset, value + 1); 262 res += unsafe.getInt(obj, offset); 263 unsafe.putOrderedInt(obj, offset, value + 2); 264 res += unsafe.getInt(obj, offset); 265 return res; 266 } 267 268 @SuppressWarnings("all") 269 public static long unsafePutLong(Unsafe unsafe, Object obj, long offset, long value) { 270 long res = 1; 271 unsafe.putLong(obj, offset, value + 1); 272 res += unsafe.getLong(obj, offset); 273 unsafe.putLongVolatile(obj, offset, value + 2); 274 res += unsafe.getLong(obj, offset); 275 unsafe.putOrderedLong(obj, offset, value + 3); 276 res += unsafe.getLong(obj, offset); 277 return res; 278 } 279 280 @SuppressWarnings("all") 281 public static float unsafePutFloat(Unsafe unsafe, Object obj, long offset, float value) { 282 float res = 1; 283 unsafe.putFloat(obj, offset, value + 1.0F); 284 res += unsafe.getFloat(obj, offset); 285 unsafe.putFloatVolatile(obj, offset, value + 2.0F); 286 res += unsafe.getFloat(obj, offset); 287 return res; 288 } 289 290 @SuppressWarnings("all") 291 public static double unsafePutDouble(Unsafe unsafe, Object obj, long offset, double value) { 292 double res = 1; 293 unsafe.putDouble(obj, offset, value); 294 res += unsafe.getDouble(obj, offset); 295 unsafe.putDoubleVolatile(obj, offset, value); 296 res += unsafe.getDouble(obj, offset); 297 return res; 298 } 299 300 @SuppressWarnings("all") 301 public static Object[] unsafePutObject(Unsafe unsafe, Object obj, long offset, Object value1, Object value2, Object value3) { 302 Object[] res = new Object[3]; 303 unsafe.putObject(obj, offset, value1); 304 res[0] = unsafe.getObject(obj, offset); 305 unsafe.putObjectVolatile(obj, offset, value2); 306 res[1] = unsafe.getObject(obj, offset); 307 unsafe.putOrderedObject(obj, offset, value3); 308 res[2] = unsafe.getObject(obj, offset); 309 return res; 310 } 311 312 @SuppressWarnings("all") 313 public static long unsafeGetAddress(Unsafe unsafe, long offset) { 314 return unsafe.getAddress(offset); 315 } 316 317 @SuppressWarnings("all") 318 public static long unsafePutAddress(Unsafe unsafe, long offset, long value) { 319 long res = 1; 320 unsafe.putAddress(offset, value); 321 res += unsafe.getAddress(offset); 322 return res; 323 } 324 325 @SuppressWarnings("all") 326 public static double unsafeDirectMemoryRead(Unsafe unsafe, long address) { 327 // Unsafe.getBoolean(long) and Unsafe.getObject(long) do not exist 328 // @formatter:off 329 return unsafe.getByte(address) + 330 unsafe.getShort(address + 8) + 331 unsafe.getChar(address + 16) + 332 unsafe.getInt(address + 24) + 333 unsafe.getLong(address + 32) + 334 unsafe.getFloat(address + 40) + 335 unsafe.getDouble(address + 48); 336 // @formatter:on 337 } 338 339 @SuppressWarnings("all") 340 public static double unsafeDirectMemoryWrite(Unsafe unsafe, long address, long value) { 341 // Unsafe.putBoolean(long) and Unsafe.putObject(long) do not exist 342 unsafe.putByte(address + 0, (byte) value); 343 unsafe.putShort(address + 8, (short) value); 344 unsafe.putChar(address + 16, (char) value); 345 unsafe.putInt(address + 24, (int) value); 346 unsafe.putLong(address + 32, value); 347 unsafe.putFloat(address + 40, value); 348 unsafe.putDouble(address + 48, value); 349 return unsafeDirectMemoryRead(unsafe, address); 350 } 351 352 static class MyObject { 353 int i = 42; 354 final int j = 24; 355 final String a = "a"; 356 final String b; 357 358 MyObject(String b) { 359 this.b = b; 360 Thread.dumpStack(); 361 } 362 363 @Override 364 public String toString() { 365 return j + a + b + i; 366 } 367 } 368 369 @SuppressWarnings("all") 370 public static String unsafeAllocateInstance(Unsafe unsafe) throws InstantiationException { 371 return unsafe.allocateInstance(MyObject.class).toString(); 372 } 373 374 @Test 375 public void testAllocateInstance() throws Exception { 376 unsafeAllocateInstance(UNSAFE); 377 test("unsafeAllocateInstance", UNSAFE); 378 test("unsafeAllocateInstance", (Object) null); 379 } 380 381 @Test 382 public void testGetAndAddInt() throws Exception { 383 Foo f1 = new Foo(); 384 Foo f2 = new Foo(); 385 long offset = off(f1, "i"); 386 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, int.class}; 387 for (int delta = Integer.MAX_VALUE - 10; delta < Integer.MAX_VALUE; delta++) { 388 Object[] args1 = new Object[]{f1, offset, delta}; 389 Object[] args2 = new Object[]{f2, offset, delta}; 390 testSubstitution("getAndAddInt", Unsafe.class, "getAndAddInt", parameterTypes, UNSAFE, args1, args2); 391 } 392 } 393 394 public static int getAndAddInt(Object obj, long offset, int delta) { 395 return UNSAFE.getAndAddInt(obj, offset, delta); 396 } 397 398 @Test 399 public void testGetAndAddLong() throws Exception { 400 Foo f1 = new Foo(); 401 Foo f2 = new Foo(); 402 long offset = off(f1, "l"); 403 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, long.class}; 404 for (long delta = Long.MAX_VALUE - 10; delta < Long.MAX_VALUE; delta++) { 405 Object[] args1 = new Object[]{f1, offset, delta}; 406 Object[] args2 = new Object[]{f2, offset, delta}; 407 testSubstitution("getAndAddLong", Unsafe.class, "getAndAddLong", parameterTypes, UNSAFE, args1, args2); 408 } 409 } 410 411 public static long getAndAddLong(Object obj, long offset, long delta) { 412 return UNSAFE.getAndAddLong(obj, offset, delta); 413 } 414 415 @Test 416 public void testGetAndSetInt() throws Exception { 417 Foo f1 = new Foo(); 418 Foo f2 = new Foo(); 419 long offset = off(f1, "i"); 420 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, int.class}; 421 for (int delta = Integer.MAX_VALUE - 10; delta < Integer.MAX_VALUE; delta++) { 422 Object[] args1 = new Object[]{f1, offset, delta}; 423 Object[] args2 = new Object[]{f2, offset, delta}; 424 testSubstitution("getAndSetInt", Unsafe.class, "getAndSetInt", parameterTypes, UNSAFE, args1, args2); 425 } 426 } 427 428 public static int getAndSetInt(Object obj, long offset, int newValue) { 429 return UNSAFE.getAndSetInt(obj, offset, newValue); 430 } 431 432 @Test 433 public void testGetAndSetLong() throws Exception { 434 Foo f1 = new Foo(); 435 Foo f2 = new Foo(); 436 long offset = off(f1, "l"); 437 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, long.class}; 438 for (long newValue = Long.MAX_VALUE - 10; newValue < Long.MAX_VALUE; newValue++) { 439 Object[] args1 = new Object[]{f1, offset, newValue}; 440 Object[] args2 = new Object[]{f2, offset, newValue}; 441 testSubstitution("getAndSetLong", Unsafe.class, "getAndSetLong", parameterTypes, UNSAFE, args1, args2); 442 } 443 } 444 445 public static long getAndSetLong(Object obj, long offset, long newValue) { 446 return UNSAFE.getAndSetLong(obj, offset, newValue); 447 } 448 449 @Test 450 public void testGetAndSetObject() throws Exception { 451 Foo f1 = new Foo(); 452 Foo f2 = new Foo(); 453 long offset = off(f1, "o"); 454 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, Object.class}; 455 for (long i = 0; i < 10; i++) { 456 Object o = new Object(); 457 Object[] args1 = new Object[]{f1, offset, o}; 458 Object[] args2 = new Object[]{f2, offset, o}; 459 testSubstitution("getAndSetObject", Unsafe.class, "getAndSetObject", parameterTypes, UNSAFE, args1, args2); 460 System.gc(); 461 } 462 } 463 464 public static Object getAndSetObject(Object obj, long offset, Object newValue) { 465 return UNSAFE.getAndSetObject(obj, offset, newValue); 466 } 467 468 }