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