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 }