1 /* 2 * Copyright (c) 2015, 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 * @test 26 * @summary tests on constant folding of unsafe get operations 27 * @library /test/lib 28 * 29 * @requires vm.flavor == "server" & !vm.emulatedClient 30 * 31 * @modules java.base/jdk.internal.org.objectweb.asm 32 * java.base/jdk.internal.vm.annotation 33 * java.base/jdk.internal.misc 34 * 35 * @library ../jsr292/patches 36 * @build java.base/java.lang.invoke.MethodHandleHelper 37 * 38 * @run main/bootclasspath/othervm -XX:+UnlockDiagnosticVMOptions 39 * -Xbatch -XX:-TieredCompilation 40 * -XX:+FoldStableValues 41 * -XX:CompileCommand=dontinline,compiler.unsafe.UnsafeGetConstantField::checkGetAddress 42 * -XX:CompileCommand=dontinline,*::test* 43 * -XX:+UseUnalignedAccesses 44 * --add-reads=java.base=ALL-UNNAMED 45 * compiler.unsafe.UnsafeGetConstantField 46 * 47 * @run main/bootclasspath/othervm -XX:+UnlockDiagnosticVMOptions 48 * -Xbatch -XX:-TieredCompilation 49 * -XX:+FoldStableValues 50 * -XX:CompileCommand=dontinline,compiler.unsafe.UnsafeGetConstantField::checkGetAddress 51 * -XX:CompileCommand=dontinline,*::test* 52 * -XX:CompileCommand=inline,*Unsafe::get* 53 * -XX:-UseUnalignedAccesses 54 * --add-reads=java.base=ALL-UNNAMED 55 * compiler.unsafe.UnsafeGetConstantField 56 */ 57 58 package compiler.unsafe; 59 60 import jdk.internal.misc.Unsafe; 61 import jdk.internal.org.objectweb.asm.ClassWriter; 62 import jdk.internal.org.objectweb.asm.FieldVisitor; 63 import jdk.internal.org.objectweb.asm.MethodVisitor; 64 import jdk.internal.org.objectweb.asm.Opcodes; 65 import jdk.internal.org.objectweb.asm.Type; 66 import jdk.internal.vm.annotation.Stable; 67 import jdk.test.lib.Asserts; 68 import jdk.test.lib.Platform; 69 70 import java.lang.invoke.MethodHandleHelper; 71 import java.lang.invoke.MethodHandles; 72 import java.lang.invoke.MethodHandles.Lookup; 73 import java.io.IOException; 74 import java.nio.file.Files; 75 import java.nio.file.Path; 76 import java.nio.file.Paths; 77 78 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 79 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 80 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 81 import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; 82 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 83 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; 84 import static jdk.internal.org.objectweb.asm.Opcodes.DUP; 85 import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; 86 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 87 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 88 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; 89 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 90 import static jdk.internal.org.objectweb.asm.Opcodes.NEW; 91 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; 92 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; 93 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 94 95 public class UnsafeGetConstantField { 96 static final Class<?> THIS_CLASS = UnsafeGetConstantField.class; 97 static final Unsafe U = Unsafe.getUnsafe(); 98 99 public static void main(String[] args) { 100 if (!Platform.isServer() || Platform.isEmulatedClient()) { 101 throw new Error("TESTBUG: Not server mode"); 102 } 103 testUnsafeGetAddress(); 104 testUnsafeGetField(); 105 testUnsafeGetFieldUnaligned(); 106 System.out.println("TEST PASSED"); 107 } 108 109 static final long nativeAddr = U.allocateMemory(16); 110 static void testUnsafeGetAddress() { 111 long cookie = 0x12345678L; 112 U.putAddress(nativeAddr, cookie); 113 for (int i = 0; i < 20_000; i++) { 114 Asserts.assertEquals(checkGetAddress(), cookie); 115 } 116 } 117 118 static long checkGetAddress() { 119 return U.getAddress(nativeAddr); 120 } 121 122 static void testUnsafeGetField() { 123 int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; 124 boolean[] boolValues = new boolean[] { false, true }; 125 String[] modes = new String[] { "", "Volatile" }; 126 127 for (JavaType t : JavaType.values()) { 128 for (int flags : testedFlags) { 129 for (boolean stable : boolValues) { 130 for (boolean hasDefaultValue : boolValues) { 131 for (String suffix : modes) { 132 runTest(t, flags, stable, hasDefaultValue, suffix); 133 } 134 } 135 } 136 } 137 } 138 } 139 140 static void testUnsafeGetFieldUnaligned() { 141 JavaType[] types = new JavaType[] { JavaType.S, JavaType.C, JavaType.I, JavaType.J }; 142 int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; 143 boolean[] boolValues = new boolean[] { false, true }; 144 145 for (JavaType t : types) { 146 for (int flags : testedFlags) { 147 for (boolean stable : boolValues) { 148 for (boolean hasDefaultValue : boolValues) { 149 runTest(t, flags, stable, hasDefaultValue, "Unaligned"); 150 } 151 } 152 } 153 } 154 } 155 156 static void runTest(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String postfix) { 157 Generator g = new Generator(t, flags, stable, hasDefaultValue, postfix); 158 Test test = g.generate(); 159 System.err.printf("type=%s flags=%d stable=%b default=%b post=%s\n", 160 t.typeName, flags, stable, hasDefaultValue, postfix); 161 try { 162 Object expected = hasDefaultValue ? t.defaultValue : t.value; 163 // Trigger compilation 164 for (int i = 0; i < 20_000; i++) { 165 Asserts.assertEQ(expected, test.testDirect(), "i = "+ i +" direct read returns wrong value"); 166 Asserts.assertEQ(expected, test.testUnsafe(), "i = "+ i +" unsafe read returns wrong value"); 167 } 168 169 test.changeToDefault(); 170 if (!hasDefaultValue && (stable || g.isFinal())) { 171 Asserts.assertEQ(t.value, test.testDirect(), 172 "direct read doesn't return prev value"); 173 Asserts.assertEQ(test.testDirect(), test.testUnsafe()); 174 } else { 175 Asserts.assertEQ(t.defaultValue, test.testDirect(), 176 "direct read doesn't return default value"); 177 Asserts.assertEQ(test.testDirect(), test.testUnsafe(), 178 "direct and unsafe reads return different values"); 179 } 180 } catch (Throwable e) { 181 try { 182 g.dump(); 183 } catch (IOException io) { 184 io.printStackTrace(); 185 } 186 throw e; 187 } 188 } 189 190 public interface Test { 191 Object testDirect(); 192 Object testUnsafe(); 193 void changeToDefault(); 194 } 195 196 enum JavaType { 197 Z("Boolean", true, false), 198 B("Byte", new Byte((byte) -1), new Byte((byte) 0)), 199 S("Short", new Short((short) -1), new Short((short) 0)), 200 C("Char", Character.MAX_VALUE, '\0'), 201 I("Int", -1, 0), 202 J("Long", -1L, 0L), 203 F("Float", -1F, 0F), 204 D("Double", -1D, 0D), 205 L("Object", "", null); 206 207 String typeName; 208 Object value; 209 Object defaultValue; 210 String wrapper; 211 JavaType(String name, Object value, Object defaultValue) { 212 this.typeName = name; 213 this.value = value; 214 this.defaultValue = defaultValue; 215 this.wrapper = internalName(value.getClass()); 216 } 217 218 String desc() { 219 if (this == JavaType.L) { 220 return "Ljava/lang/Object;"; 221 } else { 222 return name(); 223 } 224 } 225 String unsafeTypeName() { 226 return typeName.equals("Object") ? "Reference" : typeName; 227 } 228 } 229 230 static String internalName(Class cls) { 231 return cls.getName().replace('.', '/'); 232 } 233 static String descriptor(Class cls) { 234 return String.format("L%s;", internalName(cls)); 235 } 236 237 /** 238 * Sample generated class: 239 * static class T1 implements Test { 240 * final int f = -1; 241 * static final long FIELD_OFFSET; 242 * static final T1 t = new T1(); 243 * static { 244 * FIELD_OFFSET = U.objectFieldOffset(T1.class.getDeclaredField("f")); 245 * } 246 * public Object testDirect() { return t.f; } 247 * public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } 248 * public void changeToDefault() { U.putInt(t, 0, FIELD_OFFSET); } 249 * } 250 */ 251 static class Generator { 252 static final String FIELD_NAME = "f"; 253 static final String UNSAFE_NAME = internalName(Unsafe.class); 254 static final String UNSAFE_DESC = descriptor(Unsafe.class); 255 256 final JavaType type; 257 final int flags; 258 final boolean stable; 259 final boolean hasDefaultValue; 260 final String nameSuffix; 261 262 final String name; 263 final String className; 264 final String classDesc; 265 final String fieldDesc; 266 final byte[] classFile; 267 268 Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) { 269 this.type = t; 270 this.flags = flags; 271 this.stable = stable; 272 this.hasDefaultValue = hasDefaultValue; 273 this.nameSuffix = suffix; 274 275 fieldDesc = type.desc(); 276 name = String.format("Test%s%s__f=%d__s=%b__d=%b", 277 type.typeName, suffix, flags, stable, hasDefaultValue); 278 className = "java/lang/invoke/" + name; 279 classDesc = String.format("L%s;", className); 280 classFile = generateClassFile(); 281 } 282 283 byte[] generateClassFile() { 284 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 285 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object", 286 new String[]{ internalName(Test.class) }); 287 288 // Declare fields 289 cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd(); 290 cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd(); 291 cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd(); 292 if (isStatic()) { 293 cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd(); 294 } 295 296 FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null); 297 if (stable) { 298 fv.visitAnnotation(descriptor(Stable.class), true); 299 } 300 fv.visitEnd(); 301 302 // Methods 303 { // <init> 304 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 305 mv.visitCode(); 306 307 mv.visitVarInsn(ALOAD, 0); 308 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 309 if (!isStatic()) { 310 initField(mv); 311 } 312 mv.visitInsn(RETURN); 313 314 mv.visitMaxs(0, 0); 315 mv.visitEnd(); 316 } 317 318 { // public Object testDirect() { return t.f; } 319 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null); 320 mv.visitCode(); 321 322 getFieldValue(mv); 323 wrapResult(mv); 324 mv.visitInsn(ARETURN); 325 326 mv.visitMaxs(0, 0); 327 mv.visitEnd(); 328 } 329 330 { // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } 331 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null); 332 mv.visitCode(); 333 334 getFieldValueUnsafe(mv); 335 wrapResult(mv); 336 mv.visitInsn(ARETURN); 337 338 mv.visitMaxs(0, 0); 339 mv.visitEnd(); 340 } 341 342 { // public void changeToDefault() { U.putInt(t, FIELD_OFFSET, 0); } 343 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "changeToDefault", "()V", null, null); 344 mv.visitCode(); 345 getUnsafe(mv); 346 if (isStatic()) { 347 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 348 } else { 349 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 350 } 351 mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); 352 353 if (type.defaultValue != null) { 354 mv.visitLdcInsn(type.defaultValue); 355 } else { 356 mv.visitInsn(ACONST_NULL); 357 } 358 String name = "put" + type.unsafeTypeName() + nameSuffix; 359 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J" + type.desc()+ ")V", false); 360 mv.visitInsn(RETURN); 361 362 mv.visitMaxs(0, 0); 363 mv.visitEnd(); 364 } 365 366 { // <clinit> 367 MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 368 mv.visitCode(); 369 370 // Cache Unsafe instance 371 mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false); 372 mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC); 373 374 // Create test object instance 375 mv.visitTypeInsn(NEW, className); 376 mv.visitInsn(DUP); 377 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); 378 mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc); 379 380 // Compute field offset 381 getUnsafe(mv); 382 getField(mv); 383 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"), 384 "(Ljava/lang/reflect/Field;)J", false); 385 mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J"); 386 387 // Compute base offset for static field 388 if (isStatic()) { 389 getUnsafe(mv); 390 getField(mv); 391 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false); 392 mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 393 initField(mv); 394 } 395 396 mv.visitInsn(RETURN); 397 mv.visitMaxs(0, 0); 398 mv.visitEnd(); 399 } 400 401 return cw.toByteArray(); 402 } 403 404 Test generate() { 405 try { 406 Lookup lookup = MethodHandleHelper.IMPL_LOOKUP.in(MethodHandles.class); 407 Class<?> c = lookup.defineClass(classFile); 408 return (Test) c.newInstance(); 409 } catch(Exception e) { 410 throw new Error(e); 411 } 412 } 413 414 boolean isStatic() { 415 return (flags & ACC_STATIC) > 0; 416 } 417 boolean isFinal() { 418 return (flags & ACC_FINAL) > 0; 419 } 420 void getUnsafe(MethodVisitor mv) { 421 mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC); 422 } 423 void getField(MethodVisitor mv) { 424 mv.visitLdcInsn(Type.getType(classDesc)); 425 mv.visitLdcInsn(FIELD_NAME); 426 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); 427 } 428 void getFieldValue(MethodVisitor mv) { 429 if (isStatic()) { 430 mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc); 431 } else { 432 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 433 mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc); 434 } 435 } 436 void getFieldValueUnsafe(MethodVisitor mv) { 437 getUnsafe(mv); 438 if (isStatic()) { 439 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 440 } else { 441 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 442 } 443 mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); 444 String name = "get" + type.unsafeTypeName() + nameSuffix; 445 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false); 446 } 447 void wrapResult(MethodVisitor mv) { 448 if (type != JavaType.L) { 449 String desc = String.format("(%s)L%s;", type.desc(), type.wrapper); 450 mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false); 451 } 452 } 453 void initField(MethodVisitor mv) { 454 if (hasDefaultValue) { 455 return; // Nothing to do 456 } 457 if (!isStatic()) { 458 mv.visitVarInsn(ALOAD, 0); 459 } 460 mv.visitLdcInsn(type.value); 461 mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc); 462 } 463 464 public void dump() throws IOException { 465 Path path = Paths.get(".", name + ".class").toAbsolutePath(); 466 System.err.println("dumping test class to " + path); 467 Files.write(path, classFile); 468 } 469 } 470 }