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 }