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 } 226 227 static String internalName(Class cls) { 228 return cls.getName().replace('.', '/'); 229 } 230 static String descriptor(Class cls) { 231 return String.format("L%s;", internalName(cls)); 232 } 233 234 /** 235 * Sample generated class: 236 * static class T1 implements Test { 237 * final int f = -1; 238 * static final long FIELD_OFFSET; 239 * static final T1 t = new T1(); 240 * static { 241 * FIELD_OFFSET = U.objectFieldOffset(T1.class.getDeclaredField("f")); 242 * } 243 * public Object testDirect() { return t.f; } 244 * public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } 245 * public void changeToDefault() { U.putInt(t, 0, FIELD_OFFSET); } 246 * } 247 */ 248 static class Generator { 249 static final String FIELD_NAME = "f"; 250 static final String UNSAFE_NAME = internalName(Unsafe.class); 251 static final String UNSAFE_DESC = descriptor(Unsafe.class); 252 253 final JavaType type; 254 final int flags; 255 final boolean stable; 256 final boolean hasDefaultValue; 257 final String nameSuffix; 258 259 final String name; 260 final String className; 261 final String classDesc; 262 final String fieldDesc; 263 final byte[] classFile; 264 265 Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) { 266 this.type = t; 267 this.flags = flags; 268 this.stable = stable; 269 this.hasDefaultValue = hasDefaultValue; 270 this.nameSuffix = suffix; 271 272 fieldDesc = type.desc(); 273 name = String.format("Test%s%s__f=%d__s=%b__d=%b", 274 type.typeName, suffix, flags, stable, hasDefaultValue); 275 className = "java/lang/invoke/" + name; 276 classDesc = String.format("L%s;", className); 277 classFile = generateClassFile(); 278 } 279 280 byte[] generateClassFile() { 281 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 282 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object", 283 new String[]{ internalName(Test.class) }); 284 285 // Declare fields 286 cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd(); 287 cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd(); 288 cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd(); 289 if (isStatic()) { 290 cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd(); 291 } 292 293 FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null); 294 if (stable) { 295 fv.visitAnnotation(descriptor(Stable.class), true); 296 } 297 fv.visitEnd(); 298 299 // Methods 300 { // <init> 301 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 302 mv.visitCode(); 303 304 mv.visitVarInsn(ALOAD, 0); 305 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 306 if (!isStatic()) { 307 initField(mv); 308 } 309 mv.visitInsn(RETURN); 310 311 mv.visitMaxs(0, 0); 312 mv.visitEnd(); 313 } 314 315 { // public Object testDirect() { return t.f; } 316 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null); 317 mv.visitCode(); 318 319 getFieldValue(mv); 320 wrapResult(mv); 321 mv.visitInsn(ARETURN); 322 323 mv.visitMaxs(0, 0); 324 mv.visitEnd(); 325 } 326 327 { // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } 328 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null); 329 mv.visitCode(); 330 331 getFieldValueUnsafe(mv); 332 wrapResult(mv); 333 mv.visitInsn(ARETURN); 334 335 mv.visitMaxs(0, 0); 336 mv.visitEnd(); 337 } 338 339 { // public void changeToDefault() { U.putInt(t, FIELD_OFFSET, 0); } 340 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "changeToDefault", "()V", null, null); 341 mv.visitCode(); 342 getUnsafe(mv); 343 if (isStatic()) { 344 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 345 } else { 346 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 347 } 348 mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); 349 350 if (type.defaultValue != null) { 351 mv.visitLdcInsn(type.defaultValue); 352 } else { 353 mv.visitInsn(ACONST_NULL); 354 } 355 String name = "put" + type.typeName + nameSuffix; 356 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J" + type.desc()+ ")V", false); 357 mv.visitInsn(RETURN); 358 359 mv.visitMaxs(0, 0); 360 mv.visitEnd(); 361 } 362 363 { // <clinit> 364 MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 365 mv.visitCode(); 366 367 // Cache Unsafe instance 368 mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false); 369 mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC); 370 371 // Create test object instance 372 mv.visitTypeInsn(NEW, className); 373 mv.visitInsn(DUP); 374 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); 375 mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc); 376 377 // Compute field offset 378 getUnsafe(mv); 379 getField(mv); 380 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"), 381 "(Ljava/lang/reflect/Field;)J", false); 382 mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J"); 383 384 // Compute base offset for static field 385 if (isStatic()) { 386 getUnsafe(mv); 387 getField(mv); 388 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false); 389 mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 390 initField(mv); 391 } 392 393 mv.visitInsn(RETURN); 394 mv.visitMaxs(0, 0); 395 mv.visitEnd(); 396 } 397 398 return cw.toByteArray(); 399 } 400 401 Test generate() { 402 try { 403 Lookup lookup = MethodHandleHelper.IMPL_LOOKUP.in(MethodHandles.class); 404 Class<?> c = lookup.defineClass(classFile); 405 return (Test) c.newInstance(); 406 } catch(Exception e) { 407 throw new Error(e); 408 } 409 } 410 411 boolean isStatic() { 412 return (flags & ACC_STATIC) > 0; 413 } 414 boolean isFinal() { 415 return (flags & ACC_FINAL) > 0; 416 } 417 void getUnsafe(MethodVisitor mv) { 418 mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC); 419 } 420 void getField(MethodVisitor mv) { 421 mv.visitLdcInsn(Type.getType(classDesc)); 422 mv.visitLdcInsn(FIELD_NAME); 423 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); 424 } 425 void getFieldValue(MethodVisitor mv) { 426 if (isStatic()) { 427 mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc); 428 } else { 429 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 430 mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc); 431 } 432 } 433 void getFieldValueUnsafe(MethodVisitor mv) { 434 getUnsafe(mv); 435 if (isStatic()) { 436 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 437 } else { 438 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 439 } 440 mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); 441 String name = "get" + type.typeName + nameSuffix; 442 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false); 443 } 444 void wrapResult(MethodVisitor mv) { 445 if (type != JavaType.L) { 446 String desc = String.format("(%s)L%s;", type.desc(), type.wrapper); 447 mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false); 448 } 449 } 450 void initField(MethodVisitor mv) { 451 if (hasDefaultValue) { 452 return; // Nothing to do 453 } 454 if (!isStatic()) { 455 mv.visitVarInsn(ALOAD, 0); 456 } 457 mv.visitLdcInsn(type.value); 458 mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc); 459 } 460 461 public void dump() throws IOException { 462 Path path = Paths.get(".", name + ".class").toAbsolutePath(); 463 System.err.println("dumping test class to " + path); 464 Files.write(path, classFile); 465 } 466 } 467 }