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