--- old/src/share/vm/ci/ciField.hpp 2015-06-24 15:32:31.000000000 +0300 +++ new/src/share/vm/ci/ciField.hpp 2015-06-24 15:32:31.000000000 +0300 @@ -181,6 +181,17 @@ return (holder()->is_subclass_of(callsite_klass) && (name() == ciSymbol::target_name())); } + bool is_autobox_cache() { + ciSymbol* klass_name = holder()->name(); + return (name() == ciSymbol::cache_field_name() && + holder()->uses_default_loader() && + (klass_name == ciSymbol::java_lang_Character_CharacterCache() || + klass_name == ciSymbol::java_lang_Byte_ByteCache() || + klass_name == ciSymbol::java_lang_Short_ShortCache() || + klass_name == ciSymbol::java_lang_Integer_IntegerCache() || + klass_name == ciSymbol::java_lang_Long_LongCache())); + } + // Debugging output void print(); void print_name_on(outputStream* st); --- old/src/share/vm/opto/library_call.cpp 2015-06-24 15:32:32.000000000 +0300 +++ new/src/share/vm/opto/library_call.cpp 2015-06-24 15:32:32.000000000 +0300 @@ -2645,35 +2645,48 @@ // of safe & unsafe memory. if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); - if (!is_store) { - MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; - // To be valid, unsafe loads may depend on other conditions than - // the one that guards them: pin the Load node - Node* p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile); - // load value - switch (type) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - case T_LONG: - case T_FLOAT: - case T_DOUBLE: - break; - case T_OBJECT: - if (need_read_barrier) { - insert_pre_barrier(heap_base_oop, offset, p, !(is_volatile || need_mem_bar)); + if (!is_store) { + Node* p = NULL; + // Try to constant fold a load from a constant field + ciField* field = alias_type->field(); + if (heap_base_oop != top() && + field != NULL && field->is_constant() && field->layout_type() == type) { + // final or stable field + const Type* con_type = Type::make_constant(alias_type->field(), heap_base_oop); + if (con_type != NULL) { + p = makecon(con_type); + } + } + if (p == NULL) { + MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; + // To be valid, unsafe loads may depend on other conditions than + // the one that guards them: pin the Load node + p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile); + // load value + switch (type) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_LONG: + case T_FLOAT: + case T_DOUBLE: + break; + case T_OBJECT: + if (need_read_barrier) { + insert_pre_barrier(heap_base_oop, offset, p, !(is_volatile || need_mem_bar)); + } + break; + case T_ADDRESS: + // Cast to an int type. + p = _gvn.transform(new CastP2XNode(NULL, p)); + p = ConvX2UL(p); + break; + default: + fatal(err_msg_res("unexpected type %d: %s", type, type2name(type))); + break; } - break; - case T_ADDRESS: - // Cast to an int type. - p = _gvn.transform(new CastP2XNode(NULL, p)); - p = ConvX2UL(p); - break; - default: - fatal(err_msg_res("unexpected type %d: %s", type, type2name(type))); - break; } // The load node has the control of the preceding MemBarCPUOrder. All // following nodes will have the control of the MemBarCPUOrder inserted at --- old/src/share/vm/opto/parse.hpp 2015-06-24 15:32:32.000000000 +0300 +++ new/src/share/vm/opto/parse.hpp 2015-06-24 15:32:32.000000000 +0300 @@ -539,10 +539,6 @@ void do_get_xxx(Node* obj, ciField* field, bool is_field); void do_put_xxx(Node* obj, ciField* field, bool is_field); - // loading from a constant field or the constant pool - // returns false if push failed (non-perm field constants only, not ldcs) - bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false, const Type* basic_type = NULL); - // implementation of object creation bytecodes void emit_guard_for_new(ciInstanceKlass* klass); void do_new(); --- old/src/share/vm/opto/parse2.cpp 2015-06-24 15:32:33.000000000 +0300 +++ new/src/share/vm/opto/parse2.cpp 2015-06-24 15:32:33.000000000 +0300 @@ -1478,8 +1478,10 @@ } assert(constant.basic_type() != T_OBJECT || constant.as_object()->is_instance(), "must be java_mirror of klass"); - bool pushed = push_constant(constant, true); - guarantee(pushed, "must be possible to push this constant"); + const Type* con_type = Type::make_from_constant(constant); + if (con_type != NULL) { + push_node(con_type->basic_type(), makecon(con_type)); + } } break; --- old/src/share/vm/opto/parse3.cpp 2015-06-24 15:32:33.000000000 +0300 +++ new/src/share/vm/opto/parse3.cpp 2015-06-24 15:32:33.000000000 +0300 @@ -149,54 +149,13 @@ // Does this field have a constant value? If so, just push the value. if (field->is_constant()) { // final or stable field - const Type* stable_type = NULL; - if (FoldStableValues && field->is_stable()) { - stable_type = Type::get_const_type(field->type()); - if (field->type()->is_array_klass()) { - int stable_dimension = field->type()->as_array_klass()->dimension(); - stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension); - } - } - if (field->is_static()) { - // final static field - if (C->eliminate_boxing()) { - // The pointers in the autobox arrays are always non-null. - ciSymbol* klass_name = field->holder()->name(); - if (field->name() == ciSymbol::cache_field_name() && - field->holder()->uses_default_loader() && - (klass_name == ciSymbol::java_lang_Character_CharacterCache() || - klass_name == ciSymbol::java_lang_Byte_ByteCache() || - klass_name == ciSymbol::java_lang_Short_ShortCache() || - klass_name == ciSymbol::java_lang_Integer_IntegerCache() || - klass_name == ciSymbol::java_lang_Long_LongCache())) { - bool require_const = true; - bool autobox_cache = true; - if (push_constant(field->constant_value(), require_const, autobox_cache)) { - return; - } - } - } - if (push_constant(field->constant_value(), false, false, stable_type)) - return; - } else { - // final or stable non-static field - // Treat final non-static fields of trusted classes (classes in - // java.lang.invoke and sun.invoke packages and subpackages) as - // compile time constants. - if (obj->is_Con()) { - const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); - ciObject* constant_oop = oop_ptr->const_oop(); - ciConstant constant = field->constant_value_of(constant_oop); - if (FoldStableValues && field->is_stable() && constant.is_null_or_zero()) { - // fall through to field load; the field is not yet initialized - } else { - if (push_constant(constant, true, false, stable_type)) - return; - } - } + const Type* con_type = Type::make_constant(field, obj); + if (con_type != NULL) { + push_node(con_type->basic_type(), makecon(con_type)); + return; } } - + ciType* field_klass = field->type(); bool is_vol = field->is_volatile(); @@ -362,39 +321,6 @@ } } - - -bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache, const Type* stable_type) { - const Type* con_type = Type::make_from_constant(constant, require_constant, is_autobox_cache); - switch (constant.basic_type()) { - case T_ARRAY: - case T_OBJECT: - // cases: - // can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0) - // should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2) - // An oop is not scavengable if it is in the perm gen. - if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr()) - con_type = con_type->join_speculative(stable_type); - break; - - case T_ILLEGAL: - // Invalid ciConstant returned due to OutOfMemoryError in the CI - assert(C->env()->failing(), "otherwise should not see this"); - // These always occur because of object types; we are going to - // bail out anyway, so make the stack depths match up - push( zerocon(T_OBJECT) ); - return false; - } - - if (con_type == NULL) - // we cannot inline the oop, but we can use it later to narrow a type - return false; - - push_node(constant.basic_type(), makecon(con_type)); - return true; -} - - //============================================================================= void Parse::do_anewarray() { bool will_link; --- old/src/share/vm/opto/type.cpp 2015-06-24 15:32:34.000000000 +0300 +++ new/src/share/vm/opto/type.cpp 2015-06-24 15:32:33.000000000 +0300 @@ -200,8 +200,7 @@ //-----------------------make_from_constant------------------------------------ -const Type* Type::make_from_constant(ciConstant constant, - bool require_constant, bool is_autobox_cache) { +const Type* Type::make_from_constant(ciConstant constant, bool require_constant) { switch (constant.basic_type()) { case T_BOOLEAN: return TypeInt::make(constant.as_boolean()); case T_CHAR: return TypeInt::make(constant.as_char()); @@ -222,15 +221,58 @@ if (oop_constant->is_null_object()) { return Type::get_zero_type(T_OBJECT); } else if (require_constant || oop_constant->should_be_constant()) { - return TypeOopPtr::make_from_constant(oop_constant, require_constant, is_autobox_cache); + return TypeOopPtr::make_from_constant(oop_constant, require_constant); } } + case T_ILLEGAL: + // Invalid ciConstant returned due to OutOfMemoryError in the CI + assert(Compile::current()->env()->failing(), "otherwise should not see this"); + return NULL; } // Fall through to failure return NULL; } +const Type* Type::make_constant(ciField* field, Node* obj) { + if (!field->is_constant()) return NULL; + + const Type* con_type = NULL; + if (field->is_static()) { + // final static field + con_type = Type::make_from_constant(field->constant_value(), /*require_const=*/true); + if (Compile::current()->eliminate_boxing() && field->is_autobox_cache() && con_type != NULL) { + con_type = con_type->is_aryptr()->cast_to_autobox_cache(true); + } + } else { + // final or stable non-static field + // Treat final non-static fields of trusted classes (classes in + // java.lang.invoke and sun.invoke packages and subpackages) as + // compile time constants. + if (obj->is_Con()) { + const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); + ciObject* constant_oop = oop_ptr->const_oop(); + ciConstant constant = field->constant_value_of(constant_oop); + con_type = Type::make_from_constant(constant, /*require_const=*/true); + } + } + if (FoldStableValues && field->is_stable() && con_type != NULL) { + if (con_type->is_zero_type()) { + return NULL; // the field hasn't been initialized yet + } else if (con_type->isa_oopptr()) { + const Type* stable_type = Type::get_const_type(field->type()); + if (field->type()->is_array_klass()) { + int stable_dimension = field->type()->as_array_klass()->dimension(); + stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension); + } + if (stable_type != NULL) { + con_type = con_type->join_speculative(stable_type); + } + } + } + return con_type; +} + //------------------------------make------------------------------------------- // Create a simple Type, with default empty symbol sets. Then hashcons it // and look for an existing copy in the type dictionary. @@ -3009,9 +3051,7 @@ //------------------------------make_from_constant----------------------------- // Make a java pointer from an oop constant -const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, - bool require_constant, - bool is_autobox_cache) { +const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_constant) { assert(!o->is_null_object(), "null object not yet handled here."); ciKlass* klass = o->klass(); if (klass->is_instance_klass()) { @@ -3026,10 +3066,6 @@ // Element is an object array. Recursively call ourself. const TypeOopPtr *etype = TypeOopPtr::make_from_klass_raw(klass->as_obj_array_klass()->element_klass()); - if (is_autobox_cache) { - // The pointers in the autobox arrays are always non-null. - etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); - } const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length())); // We used to pass NotNull in here, asserting that the sub-arrays // are all not-null. This is not true in generally, as code can @@ -3039,7 +3075,7 @@ } else if (!o->should_be_constant()) { return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); } - const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, NULL, InlineDepthBottom, is_autobox_cache); + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); return arr; } else if (klass->is_type_array_klass()) { // Element is an typeArray @@ -3940,7 +3976,6 @@ return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth); } - //------------------------------cast_to_stable--------------------------------- const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) const { if (stable_dimension <= 0 || (stable_dimension == 1 && stable == this->is_stable())) @@ -3969,6 +4004,18 @@ return dim; } +//----------------------cast_to_autobox_cache----------------------------------- +const TypeAryPtr* TypeAryPtr::cast_to_autobox_cache(bool cache) const { + if (is_autobox_cache() == cache) return this; + const TypeOopPtr* etype = elem()->make_oopptr(); + if (etype == NULL) return this; + // The pointers in the autobox arrays are always non-null. + TypePtr::PTR ptr_type = cache ? TypePtr::NotNull : TypePtr::AnyNull; + etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); + const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable()); + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth, cache); +} + //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeAryPtr::eq( const Type *t ) const { @@ -4455,7 +4502,7 @@ // TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants bool TypeMetadataPtr::singleton(void) const { - // detune optimizer to not generate constant metadta + constant offset as a constant! + // detune optimizer to not generate constant metadata + constant offset as a constant! // TopPTR, Null, AnyNull, Constant are all singletons return (_offset == 0) && !below_centerline(_ptr); } --- old/src/share/vm/opto/type.hpp 2015-06-24 15:32:34.000000000 +0300 +++ new/src/share/vm/opto/type.hpp 2015-06-24 15:32:34.000000000 +0300 @@ -412,8 +412,9 @@ static const Type* get_typeflow_type(ciType* type); static const Type* make_from_constant(ciConstant constant, - bool require_constant = false, - bool is_autobox_cache = false); + bool require_constant = false); + + static const Type* make_constant(ciField* field, Node* obj); // Speculative type helper methods. See TypePtr. virtual const TypePtr* speculative() const { return NULL; } @@ -973,8 +974,7 @@ // may return a non-singleton type. // If require_constant, produce a NULL if a singleton is not possible. static const TypeOopPtr* make_from_constant(ciObject* o, - bool require_constant = false, - bool not_null_elements = false); + bool require_constant = false); // Make a generic (unclassed) pointer to an oop. static const TypeOopPtr* make(PTR ptr, int offset, int instance_id, @@ -1184,6 +1184,8 @@ const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const; int stable_dimension() const; + const TypeAryPtr* cast_to_autobox_cache(bool cache) const; + // Convenience common pre-built types. static const TypeAryPtr *RANGE; static const TypeAryPtr *OOPS; @@ -1674,12 +1676,12 @@ inline const TypePtr* Type::make_ptr() const { return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype() : - ((_base == NarrowKlass) ? is_narrowklass()->get_ptrtype() : - (isa_ptr() ? is_ptr() : NULL)); + ((_base == NarrowKlass) ? is_narrowklass()->get_ptrtype() : + isa_ptr()); } inline const TypeOopPtr* Type::make_oopptr() const { - return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype()->is_oopptr() : is_oopptr(); + return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype()->isa_oopptr() : isa_oopptr(); } inline const TypeNarrowOop* Type::make_narrowoop() const { @@ -1689,7 +1691,7 @@ inline const TypeNarrowKlass* Type::make_narrowklass() const { return (_base == NarrowKlass) ? is_narrowklass() : - (isa_ptr() ? TypeNarrowKlass::make(is_ptr()) : NULL); + (isa_ptr() ? TypeNarrowKlass::make(is_ptr()) : NULL); } inline bool Type::is_floatingpoint() const { --- /dev/null 2015-06-24 15:32:35.000000000 +0300 +++ new/test/compiler/unsafe/UnsafeGetConstantField.java 2015-06-24 15:32:34.000000000 +0300 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary tests on constant folding of unsafe get operations + * @library /testlibrary /../../test/lib + * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions + * -Xbatch -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:+UseUnalignedAccesses + * java.lang.invoke.UnsafeGetConstantField + * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions + * -Xbatch -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:-UseUnalignedAccesses + * java.lang.invoke.UnsafeGetConstantField + */ +package java.lang.invoke; + +import jdk.internal.org.objectweb.asm.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import sun.misc.Unsafe; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class UnsafeGetConstantField { + static final Class THIS_CLASS = UnsafeGetConstantField.class; + + static final Unsafe U = Utils.getUnsafe(); + + public static void main(String[] args) { + testUnsafeGetAddress(); + testUnsafeGetField(); + testUnsafeGetFieldUnaligned(); + System.out.println("TEST PASSED"); + } + + static final long nativeAddr = U.allocateMemory(16); + static void testUnsafeGetAddress() { + long cookie = 0x12345678L; + U.putAddress(nativeAddr, cookie); + for (int i = 0; i < 20_000; i++) { + Asserts.assertEquals(checkGetAddress(), cookie); + } + } + @DontInline + static long checkGetAddress() { + return U.getAddress(nativeAddr); + } + + static void testUnsafeGetField() { + int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; + boolean[] boolValues = new boolean[] { false, true }; + String[] modes = new String[] { "", "Volatile" }; + + for (JavaType t : JavaType.values()) { + for (int flags : testedFlags) { + for (boolean stable : boolValues) { + for (boolean hasDefaultValue : boolValues) { + for (String suffix : modes) { + runTest(t, flags, stable, hasDefaultValue, suffix); + } + } + } + } + } + } + + static void testUnsafeGetFieldUnaligned() { + JavaType[] types = new JavaType[] { JavaType.S, JavaType.C, JavaType.I, JavaType.J }; + int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; + boolean[] boolValues = new boolean[] { false, true }; + + for (JavaType t : types) { + for (int flags : testedFlags) { + for (boolean stable : boolValues) { + for (boolean hasDefaultValue : boolValues) { + runTest(t, flags, stable, hasDefaultValue, "Unaligned"); + } + } + } + } + } + + static void runTest(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String postfix) { + Generator g = new Generator(t, flags, stable, hasDefaultValue, postfix); + Test test = g.generate(); + System.out.printf("type=%s flags=%d stable=%b default=%b post=%s\n", + t.typeName, flags, stable, hasDefaultValue, postfix); + // Trigger compilation + for (int i = 0; i < 20_000; i++) { + Asserts.assertEQ(test.testDirect(), test.testUnsafe()); + } + } + + interface Test { + Object testDirect(); + Object testUnsafe(); + } + + enum JavaType { + Z("Boolean", true), + B("Byte", new Byte((byte)-1)), + S("Short", new Short((short)-1)), + C("Char", Character.MAX_VALUE), + I("Int", -1), + J("Long", -1L), + F("Float", -1F), + D("Double", -1D), + L("Object", new Object()); + + String typeName; + Object value; + String wrapper; + JavaType(String name, Object value) { + this.typeName = name; + this.value = value; + this.wrapper = internalName(value.getClass()); + } + + String desc() { + if (this == JavaType.L) { + return "Ljava/lang/Object;"; + } else { + return toString(); + } + } + } + + static String internalName(Class cls) { + return cls.getName().replace('.', '/'); + } + static String descriptor(Class cls) { + return String.format("L%s;", internalName(cls)); + } + + /** + * Sample generated class: + * static class T1 implements Test { + * final int f = -1; + * static final long FIELD_OFFSET; + * static final T1 t = new T1(); + * static { + * FIELD_OFFSET = U.objectFieldOffset(T1.class.getDeclaredField("f")); + * } + * public Object testDirect() { return t.f; } + * public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } + * } + */ + static class Generator { + static final String FIELD_NAME = "f"; + static final String UNSAFE_NAME = internalName(Unsafe.class); + static final String UNSAFE_DESC = descriptor(Unsafe.class); + + final JavaType type; + final int flags; + final boolean stable; + final boolean hasDefaultValue; + final String nameSuffix; + + final String className; + final String classDesc; + final String fieldDesc; + + Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) { + this.type = t; + this.flags = flags; + this.stable = stable; + this.hasDefaultValue = hasDefaultValue; + this.nameSuffix = suffix; + + fieldDesc = type.desc(); + className = String.format("%s$Test%s%s__f=%d__s=%b__d=%b", internalName(THIS_CLASS), type.typeName, + suffix, flags, stable, hasDefaultValue); + classDesc = String.format("L%s;", className); + } + + byte[] generateClassFile() { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object", + new String[]{ internalName(Test.class) }); + + // Declare fields + cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd(); + cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd(); + cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd(); + if (isStatic()) { + cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd(); + } + + FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null); + if (stable) { + fv.visitAnnotation(descriptor(Stable.class), true); + } + fv.visitEnd(); + + // Methods + { // + MethodVisitor mv = cw.visitMethod(0, "", "()V", null, null); + mv.visitCode(); + + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + if (!isStatic()) { + initField(mv); + } + mv.visitInsn(RETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // public Object testDirect() { return t.f; } + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + + getFieldValue(mv); + wrapResult(mv); + mv.visitInsn(ARETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + + getFieldValueUnsafe(mv); + wrapResult(mv); + mv.visitInsn(ARETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // + MethodVisitor mv = cw.visitMethod(ACC_STATIC, "", "()V", null, null); + mv.visitCode(); + + // Cache Unsafe instance + mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false); + mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC); + + // Create test object instance + mv.visitTypeInsn(NEW, className); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, className, "", "()V", false); + mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc); + + // Compute field offset + getUnsafe(mv); + getField(mv); + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"), + "(Ljava/lang/reflect/Field;)J", false); + mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J"); + + // Compute base offset for static field + if (isStatic()) { + getUnsafe(mv); + getField(mv); + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false); + mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); + initField(mv); + } + + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + return cw.toByteArray(); + } + + Test generate() { + byte[] classFile = generateClassFile(); + Class c = U.defineClass(className, classFile, 0, classFile.length, THIS_CLASS.getClassLoader(), null); + try { + return (Test) c.newInstance(); + } catch(Exception e) { + throw new Error(e); + } + } + + boolean isStatic() { + return (flags & ACC_STATIC) > 0; + } + boolean isFinal() { + return (flags & ACC_FINAL) > 0; + } + void getUnsafe(MethodVisitor mv) { + mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC); + } + void getField(MethodVisitor mv) { + mv.visitLdcInsn(Type.getType(classDesc)); + mv.visitLdcInsn(FIELD_NAME); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); + } + void getFieldValue(MethodVisitor mv) { + if (isStatic()) { + mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc); + } else { + mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); + mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc); + } + } + void getFieldValueUnsafe(MethodVisitor mv) { + getUnsafe(mv); + if (isStatic()) { + mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); + } else { + mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); + } + mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); + String name = "get" + type.typeName + nameSuffix; + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false); + } + void wrapResult(MethodVisitor mv) { + if (type != JavaType.L) { + String desc = String.format("(%s)L%s;", type.desc(), type.wrapper); + mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false); + } + } + void initField(MethodVisitor mv) { + if (hasDefaultValue) { + return; // Nothing to do + } + if (!isStatic()) { + mv.visitVarInsn(ALOAD, 0); + } + switch (type) { + case L: { + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + + break; + } + default: { + mv.visitLdcInsn(type.value); + break; + } + } + mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc); + } + } +}