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