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 }