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