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.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 184 final String className; 185 final String classDesc; 186 final String fieldDesc; 187 188 Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) { 189 this.type = t; 190 this.flags = flags; 191 this.stable = stable; 192 this.hasDefaultValue = hasDefaultValue; 193 this.nameSuffix = suffix; 194 195 fieldDesc = type.desc(); 196 className = String.format("%s$Test%s%s__f=%d__s=%b__d=%b", internalName(THIS_CLASS), type.typeName, 197 suffix, flags, stable, hasDefaultValue); 198 classDesc = String.format("L%s;", className); 199 } 200 201 byte[] generateClassFile() { 202 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 203 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object", 204 new String[]{ internalName(Test.class) }); 205 206 // Declare fields 207 cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd(); 208 cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd(); 209 cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd(); 210 if (isStatic()) { 211 cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd(); 212 } 213 214 FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null); 215 if (stable) { 216 fv.visitAnnotation(descriptor(Stable.class), true); 217 } 218 fv.visitEnd(); 219 220 // Methods 221 { // <init> 222 MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null); 223 mv.visitCode(); 224 225 mv.visitVarInsn(ALOAD, 0); 226 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 227 if (!isStatic()) { 228 initField(mv); 229 } 230 mv.visitInsn(RETURN); 231 232 mv.visitMaxs(0, 0); 233 mv.visitEnd(); 234 } 235 236 { // public Object testDirect() { return t.f; } 237 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null); 238 mv.visitCode(); 239 240 getFieldValue(mv); 241 wrapResult(mv); 242 mv.visitInsn(ARETURN); 243 244 mv.visitMaxs(0, 0); 245 mv.visitEnd(); 246 } 247 248 { // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } 249 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null); 250 mv.visitCode(); 251 252 getFieldValueUnsafe(mv); 253 wrapResult(mv); 254 mv.visitInsn(ARETURN); 255 256 mv.visitMaxs(0, 0); 257 mv.visitEnd(); 258 } 259 260 { // <clinit> 261 MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 262 mv.visitCode(); 263 264 // Cache Unsafe instance 265 mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false); 266 mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC); 267 268 // Create test object instance 269 mv.visitTypeInsn(NEW, className); 270 mv.visitInsn(DUP); 271 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); 272 mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc); 273 274 // Compute field offset 275 getUnsafe(mv); 276 getField(mv); 277 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"), 278 "(Ljava/lang/reflect/Field;)J", false); 279 mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J"); 280 281 // Compute base offset for static field 282 if (isStatic()) { 283 getUnsafe(mv); 284 getField(mv); 285 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false); 286 mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 287 initField(mv); 288 } 289 290 mv.visitInsn(RETURN); 291 mv.visitMaxs(0, 0); 292 mv.visitEnd(); 293 } 294 295 return cw.toByteArray(); 296 } 297 298 Test generate() { 299 byte[] classFile = generateClassFile(); 300 Class<?> c = U.defineClass(className, classFile, 0, classFile.length, THIS_CLASS.getClassLoader(), null); 301 try { 302 return (Test) c.newInstance(); 303 } catch(Exception e) { 304 throw new Error(e); 305 } 306 } 307 308 boolean isStatic() { 309 return (flags & ACC_STATIC) > 0; 310 } 311 boolean isFinal() { 312 return (flags & ACC_FINAL) > 0; 313 } 314 void getUnsafe(MethodVisitor mv) { 315 mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC); 316 } 317 void getField(MethodVisitor mv) { 318 mv.visitLdcInsn(Type.getType(classDesc)); 319 mv.visitLdcInsn(FIELD_NAME); 320 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); 321 } 322 void getFieldValue(MethodVisitor mv) { 323 if (isStatic()) { 324 mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc); 325 } else { 326 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 327 mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc); 328 } 329 } 330 void getFieldValueUnsafe(MethodVisitor mv) { 331 getUnsafe(mv); 332 if (isStatic()) { 333 mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); 334 } else { 335 mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); 336 } 337 mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); 338 String name = "get" + type.typeName + nameSuffix; 339 mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false); 340 } 341 void wrapResult(MethodVisitor mv) { 342 if (type != JavaType.L) { 343 String desc = String.format("(%s)L%s;", type.desc(), type.wrapper); 344 mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false); 345 } 346 } 347 void initField(MethodVisitor mv) { 348 if (hasDefaultValue) { 349 return; // Nothing to do 350 } 351 if (!isStatic()) { 352 mv.visitVarInsn(ALOAD, 0); 353 } 354 switch (type) { 355 case L: { 356 mv.visitTypeInsn(NEW, "java/lang/Object"); 357 mv.visitInsn(DUP); 358 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 359 360 break; 361 } 362 default: { 363 mv.visitLdcInsn(type.value); 364 break; 365 } 366 } 367 mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc); 368 } 369 } 370 }