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