001/* 002 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements.test; 024 025import static jdk.internal.jvmci.common.UnsafeAccess.*; 026import jdk.internal.jvmci.code.*; 027import jdk.internal.jvmci.meta.*; 028 029import org.junit.*; 030 031import sun.misc.*; 032 033/** 034 * Tests the VM independent intrinsification of {@link Unsafe} methods. 035 */ 036public class UnsafeSubstitutionsTest extends MethodSubstitutionTest { 037 038 public void testSubstitution(String testMethodName, Class<?> holder, String methodName, Class<?>[] parameterTypes, Object receiver, Object[] args1, Object[] args2) { 039 ResolvedJavaMethod testMethod = getResolvedJavaMethod(testMethodName); 040 ResolvedJavaMethod originalMethod = getResolvedJavaMethod(holder, methodName, parameterTypes); 041 042 // Force compilation 043 InstalledCode code = getCode(testMethod); 044 assert code != null; 045 046 // Verify that the original method and the substitution produce the same value 047 Object expected = invokeSafe(originalMethod, receiver, args1); 048 Object actual = invokeSafe(testMethod, null, args2); 049 assertDeepEquals(expected, actual); 050 051 // Verify that the generated code and the original produce the same value 052 expected = invokeSafe(originalMethod, receiver, args1); 053 actual = executeVarargsSafe(code, args2); 054 assertDeepEquals(expected, actual); 055 056 } 057 058 static long off(Object o, String name) { 059 try { 060 return unsafe.objectFieldOffset(o.getClass().getDeclaredField(name)); 061 } catch (Exception e) { 062 Assert.fail(e.toString()); 063 return 0L; 064 } 065 } 066 067 static class Foo { 068 boolean z; 069 byte b; 070 short s; 071 char c; 072 int i; 073 long l; 074 float f; 075 double d; 076 Object o; 077 } 078 079 @Test 080 public void testUnsafeSubstitutions() throws Exception { 081 test("unsafeCompareAndSwapInt", unsafe, supply(() -> new Foo()), fooOffset("i")); 082 083 testGraph("unsafeCompareAndSwapInt"); 084 testGraph("unsafeCompareAndSwapLong"); 085 testGraph("unsafeCompareAndSwapObject"); 086 087 testGraph("unsafeGetBoolean"); 088 testGraph("unsafeGetByte"); 089 testGraph("unsafeGetShort"); 090 testGraph("unsafeGetChar"); 091 testGraph("unsafeGetInt"); 092 testGraph("unsafeGetLong"); 093 testGraph("unsafeGetFloat"); 094 testGraph("unsafeGetDouble"); 095 testGraph("unsafeGetObject"); 096 097 testGraph("unsafePutBoolean"); 098 testGraph("unsafePutByte"); 099 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 * Kind.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 @Test 353 public void testGetAndAddInt() throws Exception { 354 Foo f1 = new Foo(); 355 Foo f2 = new Foo(); 356 long offset = off(f1, "i"); 357 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, int.class}; 358 for (int delta = Integer.MAX_VALUE - 10; delta < Integer.MAX_VALUE; delta++) { 359 Object[] args1 = new Object[]{f1, offset, delta}; 360 Object[] args2 = new Object[]{f2, offset, delta}; 361 testSubstitution("getAndAddInt", Unsafe.class, "getAndAddInt", parameterTypes, unsafe, args1, args2); 362 } 363 } 364 365 public static int getAndAddInt(Object obj, long offset, int delta) { 366 return unsafe.getAndAddInt(obj, offset, delta); 367 } 368 369 @Test 370 public void testGetAndAddLong() throws Exception { 371 Foo f1 = new Foo(); 372 Foo f2 = new Foo(); 373 long offset = off(f1, "l"); 374 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, long.class}; 375 for (long delta = Long.MAX_VALUE - 10; delta < Long.MAX_VALUE; delta++) { 376 Object[] args1 = new Object[]{f1, offset, delta}; 377 Object[] args2 = new Object[]{f2, offset, delta}; 378 testSubstitution("getAndAddLong", Unsafe.class, "getAndAddLong", parameterTypes, unsafe, args1, args2); 379 } 380 } 381 382 public static long getAndAddLong(Object obj, long offset, long delta) { 383 return unsafe.getAndAddLong(obj, offset, delta); 384 } 385 386 @Test 387 public void testGetAndSetInt() throws Exception { 388 Foo f1 = new Foo(); 389 Foo f2 = new Foo(); 390 long offset = off(f1, "i"); 391 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, int.class}; 392 for (int delta = Integer.MAX_VALUE - 10; delta < Integer.MAX_VALUE; delta++) { 393 Object[] args1 = new Object[]{f1, offset, delta}; 394 Object[] args2 = new Object[]{f2, offset, delta}; 395 testSubstitution("getAndSetInt", Unsafe.class, "getAndSetInt", parameterTypes, unsafe, args1, args2); 396 } 397 } 398 399 public static int getAndSetInt(Object obj, long offset, int newValue) { 400 return unsafe.getAndSetInt(obj, offset, newValue); 401 } 402 403 @Test 404 public void testGetAndSetLong() throws Exception { 405 Foo f1 = new Foo(); 406 Foo f2 = new Foo(); 407 long offset = off(f1, "l"); 408 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, long.class}; 409 for (long newValue = Long.MAX_VALUE - 10; newValue < Long.MAX_VALUE; newValue++) { 410 Object[] args1 = new Object[]{f1, offset, newValue}; 411 Object[] args2 = new Object[]{f2, offset, newValue}; 412 testSubstitution("getAndSetLong", Unsafe.class, "getAndSetLong", parameterTypes, unsafe, args1, args2); 413 } 414 } 415 416 public static long getAndSetLong(Object obj, long offset, long newValue) { 417 return unsafe.getAndSetLong(obj, offset, newValue); 418 } 419 420 @Test 421 public void testGetAndSetObject() throws Exception { 422 Foo f1 = new Foo(); 423 Foo f2 = new Foo(); 424 long offset = off(f1, "o"); 425 Class<?>[] parameterTypes = new Class<?>[]{Object.class, long.class, Object.class}; 426 for (long i = 0; i < 10; i++) { 427 Object o = new Object(); 428 Object[] args1 = new Object[]{f1, offset, o}; 429 Object[] args2 = new Object[]{f2, offset, o}; 430 testSubstitution("getAndSetObject", Unsafe.class, "getAndSetObject", parameterTypes, unsafe, args1, args2); 431 System.gc(); 432 } 433 } 434 435 public static Object getAndSetObject(Object obj, long offset, Object newValue) { 436 return unsafe.getAndSetObject(obj, offset, newValue); 437 } 438 439}