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