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