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