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 
  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() || 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 }