# HG changeset patch # User vlivanov # Date 1460400175 -10800 # Node ID 93f24e7b3c4330d9667a69786c311b90d8e9a616 # Parent c645d414429f8387879b52a71398b17d737b871f 8152590: C2: @Stable support doesn't always work w/ incremental inlining Reviewed-by: kvn diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/c1/c1_Canonicalizer.cpp --- a/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/c1/c1_Canonicalizer.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -247,7 +247,7 @@ } else if ((lf = x->array()->as_LoadField()) != NULL) { ciField* field = lf->field(); - if (field->is_constant() && field->is_static()) { + if (field->is_static_constant()) { assert(PatchALot || ScavengeRootsInCode < 2, "Constant field loads are folded during parsing"); ciObject* c = field->constant_value().as_object(); if (!c->is_null_object()) { diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/c1/c1_GraphBuilder.cpp --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -1520,6 +1520,8 @@ } Value GraphBuilder::make_constant(ciConstant field_value, ciField* field) { + if (!field_value.is_valid()) return NULL; + BasicType field_type = field_value.basic_type(); ValueType* value = as_ValueType(field_value); @@ -1587,9 +1589,8 @@ case Bytecodes::_getstatic: { // check for compile-time constants, i.e., initialized static final fields Value constant = NULL; - if (field->is_constant() && !PatchALot) { + if (field->is_static_constant() && !PatchALot) { ciConstant field_value = field->constant_value(); - // Stable static fields are checked for non-default values in ciField::initialize_from(). assert(!field->is_stable() || !field_value.is_null_or_zero(), "stable static w/ default value shouldn't be a constant"); constant = make_constant(field_value, field); @@ -1618,31 +1619,18 @@ Value constant = NULL; obj = apop(); ObjectType* obj_type = obj->type()->as_ObjectType(); - if (obj_type->is_constant() && !PatchALot) { + if (field->is_constant() && obj_type->is_constant() && !PatchALot) { ciObject* const_oop = obj_type->constant_value(); if (!const_oop->is_null_object() && const_oop->is_loaded()) { - if (field->is_constant()) { - ciConstant field_value = field->constant_value_of(const_oop); - if (FoldStableValues && field->is_stable() && field_value.is_null_or_zero()) { - // Stable field with default value can't be constant. - constant = NULL; - } else { - constant = make_constant(field_value, field); - } - } else { - // For CallSite objects treat the target field as a compile time constant. - if (const_oop->is_call_site()) { + ciConstant field_value = field->constant_value_of(const_oop); + if (field_value.is_valid()) { + constant = make_constant(field_value, field); + // For CallSite objects add a dependency for invalidation of the optimization. + if (field->is_call_site_target()) { ciCallSite* call_site = const_oop->as_call_site(); - if (field->is_call_site_target()) { - ciMethodHandle* target = call_site->get_target(); - if (target != NULL) { // just in case - ciConstant field_val(T_OBJECT, target); - constant = new Constant(as_ValueType(field_val)); - // Add a dependence for invalidation of the optimization. - if (!call_site->is_constant_call_site()) { - dependency_recorder()->assert_call_site_target_value(call_site, target); - } - } + if (!call_site->is_constant_call_site()) { + ciMethodHandle* target = field_value.as_object()->as_method_handle(); + dependency_recorder()->assert_call_site_target_value(call_site, target); } } } diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciConstant.hpp --- a/hotspot/src/share/vm/ci/ciConstant.hpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciConstant.hpp Mon Apr 11 21:42:55 2016 +0300 @@ -124,6 +124,9 @@ } } + bool is_valid() const { + return basic_type() != T_ILLEGAL; + } // Debugging output void print(); }; diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciField.cpp --- a/hotspot/src/share/vm/ci/ciField.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciField.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -235,94 +235,75 @@ _holder = CURRENT_ENV->get_instance_klass(fd->field_holder()); // Check to see if the field is constant. - bool is_final = this->is_final(); - bool is_stable = FoldStableValues && this->is_stable(); - if (_holder->is_initialized() && (is_final || is_stable)) { - if (!this->is_static()) { - // A field can be constant if it's a final static field or if + Klass* k = _holder->get_Klass(); + bool is_stable_field = FoldStableValues && is_stable(); + if (is_final() || is_stable_field) { + if (is_static()) { + // This field just may be constant. The only case where it will + // not be constant is when the field is a *special* static & final field + // whose value may change. The three examples are java.lang.System.in, + // java.lang.System.out, and java.lang.System.err. + assert(SystemDictionary::System_klass() != NULL, "Check once per vm"); + if (k == SystemDictionary::System_klass()) { + // Check offsets for case 2: System.in, System.out, or System.err + if( _offset == java_lang_System::in_offset_in_bytes() || + _offset == java_lang_System::out_offset_in_bytes() || + _offset == java_lang_System::err_offset_in_bytes() ) { + _is_constant = false; + return; + } + } + _is_constant = true; + } else { + // An instance field can be constant if it's a final static field or if // it's a final non-static field of a trusted class (classes in // java.lang.invoke and sun.invoke packages and subpackages). - if (is_stable || trust_final_non_static_fields(_holder)) { - _is_constant = true; - return; - } - _is_constant = false; - return; - } - - // This field just may be constant. The only case where it will - // not be constant is when the field is a *special* static&final field - // whose value may change. The three examples are java.lang.System.in, - // java.lang.System.out, and java.lang.System.err. - - KlassHandle k = _holder->get_Klass(); - assert( SystemDictionary::System_klass() != NULL, "Check once per vm"); - if( k() == SystemDictionary::System_klass() ) { - // Check offsets for case 2: System.in, System.out, or System.err - if( _offset == java_lang_System::in_offset_in_bytes() || - _offset == java_lang_System::out_offset_in_bytes() || - _offset == java_lang_System::err_offset_in_bytes() ) { - _is_constant = false; - return; - } - } - - Handle mirror = k->java_mirror(); - - switch(type()->basic_type()) { - case T_BYTE: - _constant_value = ciConstant(type()->basic_type(), mirror->byte_field(_offset)); - break; - case T_CHAR: - _constant_value = ciConstant(type()->basic_type(), mirror->char_field(_offset)); - break; - case T_SHORT: - _constant_value = ciConstant(type()->basic_type(), mirror->short_field(_offset)); - break; - case T_BOOLEAN: - _constant_value = ciConstant(type()->basic_type(), mirror->bool_field(_offset)); - break; - case T_INT: - _constant_value = ciConstant(type()->basic_type(), mirror->int_field(_offset)); - break; - case T_FLOAT: - _constant_value = ciConstant(mirror->float_field(_offset)); - break; - case T_DOUBLE: - _constant_value = ciConstant(mirror->double_field(_offset)); - break; - case T_LONG: - _constant_value = ciConstant(mirror->long_field(_offset)); - break; - case T_OBJECT: - case T_ARRAY: - { - oop o = mirror->obj_field(_offset); - - // A field will be "constant" if it is known always to be - // a non-null reference to an instance of a particular class, - // or to a particular array. This can happen even if the instance - // or array is not perm. In such a case, an "unloaded" ciArray - // or ciInstance is created. The compiler may be able to use - // information about the object's class (which is exact) or length. - - if (o == NULL) { - _constant_value = ciConstant(type()->basic_type(), ciNullObject::make()); - } else { - _constant_value = ciConstant(type()->basic_type(), CURRENT_ENV->get_object(o)); - assert(_constant_value.as_object() == CURRENT_ENV->get_object(o), "check interning"); - } - } - } - if (is_stable && _constant_value.is_null_or_zero()) { - // It is not a constant after all; treat it as uninitialized. - _is_constant = false; - } else { - _is_constant = true; + _is_constant = is_stable_field || trust_final_non_static_fields(_holder); } } else { - _is_constant = false; + // For CallSite objects treat the target field as a compile time constant. + assert(SystemDictionary::CallSite_klass() != NULL, "should be already initialized"); + if (k == SystemDictionary::CallSite_klass() && + _offset == java_lang_invoke_CallSite::target_offset_in_bytes()) { + _is_constant = true; + } else { + // Non-final & non-stable fields are not constants. + _is_constant = false; + } + } +} + +// ------------------------------------------------------------------ +// ciField::constant_value +// Get the constant value of a this static field. +ciConstant ciField::constant_value() { + assert(is_static() && is_constant(), "illegal call to constant_value()"); + if (!_holder->is_initialized()) { + return ciConstant(); // Not initialized yet } + if (_constant_value.basic_type() == T_ILLEGAL) { + // Static fields are placed in mirror objects. + VM_ENTRY_MARK; + ciInstance* mirror = CURRENT_ENV->get_instance(_holder->get_Klass()->java_mirror()); + _constant_value = mirror->field_value_impl(type()->basic_type(), offset()); + } + if (FoldStableValues && is_stable() && _constant_value.is_null_or_zero()) { + return ciConstant(); + } + return _constant_value; +} + +// ------------------------------------------------------------------ +// ciField::constant_value_of +// Get the constant value of non-static final field in the given object. +ciConstant ciField::constant_value_of(ciObject* object) { + assert(!is_static() && is_constant(), "only if field is non-static constant"); + assert(object->is_instance(), "must be instance"); + ciConstant field_value = object->as_instance()->field_value(this); + if (FoldStableValues && is_stable() && field_value.is_null_or_zero()) { + return ciConstant(); + } + return field_value; } // ------------------------------------------------------------------ diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciField.hpp --- a/hotspot/src/share/vm/ci/ciField.hpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciField.hpp Mon Apr 11 21:42:55 2016 +0300 @@ -62,7 +62,7 @@ void initialize_from(fieldDescriptor* fd); public: - ciFlags flags() { return _flags; } + ciFlags flags() const { return _flags; } // Of which klass is this field a member? // @@ -89,13 +89,13 @@ // // In that case the declared holder of f would be B and // the canonical holder of f would be A. - ciInstanceKlass* holder() { return _holder; } + ciInstanceKlass* holder() const { return _holder; } // Name of this field? - ciSymbol* name() { return _name; } + ciSymbol* name() const { return _name; } // Signature of this field? - ciSymbol* signature() { return _signature; } + ciSymbol* signature() const { return _signature; } // Of what type is this field? ciType* type() { return (_type == NULL) ? compute_type() : _type; } @@ -107,13 +107,13 @@ int size_in_bytes() { return type2aelembytes(layout_type()); } // What is the offset of this field? - int offset() { + int offset() const { assert(_offset >= 1, "illegal call to offset()"); return _offset; } // Same question, explicit units. (Fields are aligned to the byte level.) - int offset_in_bytes() { + int offset_in_bytes() const { return offset(); } @@ -127,31 +127,27 @@ // // Clarification: A field is considered constant if: // 1. The field is both static and final - // 2. The canonical holder of the field has undergone - // static initialization. - // 3. The field is not one of the special static/final + // 2. The field is not one of the special static/final // non-constant fields. These are java.lang.System.in // and java.lang.System.out. Abomination. // // A field is also considered constant if it is marked @Stable // and is non-null (or non-zero, if a primitive). - // For non-static fields, the null/zero check must be - // arranged by the user, as constant_value().is_null_or_zero(). - bool is_constant() { return _is_constant; } + // + // A user should also check the field value (constant_value().is_valid()), since + // constant fields of non-initialized classes don't have values yet. + bool is_constant() const { return _is_constant; } - // Get the constant value of this field. - ciConstant constant_value() { - assert(is_static() && is_constant(), "illegal call to constant_value()"); - return _constant_value; + // Get the constant value of the static field. + ciConstant constant_value(); + + bool is_static_constant() { + return is_static() && is_constant() && constant_value().is_valid(); } // Get the constant value of non-static final field in the given // object. - ciConstant constant_value_of(ciObject* object) { - assert(!is_static() && is_constant(), "only if field is non-static constant"); - assert(object->is_instance(), "must be instance"); - return object->as_instance()->field_value(this); - } + ciConstant constant_value_of(ciObject* object); // Check for link time errors. Accessing a field from a // certain class via a certain bytecode may or may not be legal. @@ -165,14 +161,14 @@ Bytecodes::Code bc); // Java access flags - bool is_public () { return flags().is_public(); } - bool is_private () { return flags().is_private(); } - bool is_protected () { return flags().is_protected(); } - bool is_static () { return flags().is_static(); } - bool is_final () { return flags().is_final(); } - bool is_stable () { return flags().is_stable(); } - bool is_volatile () { return flags().is_volatile(); } - bool is_transient () { return flags().is_transient(); } + bool is_public () const { return flags().is_public(); } + bool is_private () const { return flags().is_private(); } + bool is_protected () const { return flags().is_protected(); } + bool is_static () const { return flags().is_static(); } + bool is_final () const { return flags().is_final(); } + bool is_stable () const { return flags().is_stable(); } + bool is_volatile () const { return flags().is_volatile(); } + bool is_transient () const { return flags().is_transient(); } bool is_call_site_target() { ciInstanceKlass* callsite_klass = CURRENT_ENV->CallSite_klass(); diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciInstance.cpp --- a/hotspot/src/share/vm/ci/ciInstance.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciInstance.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -56,49 +56,21 @@ } // ------------------------------------------------------------------ -// ciInstance::field_value -// -// Constant value of a field. -ciConstant ciInstance::field_value(ciField* field) { - assert(is_loaded(), "invalid access - must be loaded"); - assert(field->holder()->is_loaded(), "invalid access - holder must be loaded"); - assert(klass()->is_subclass_of(field->holder()), "invalid access - must be subclass"); - - VM_ENTRY_MARK; - ciConstant result; +// ciInstance::field_value_impl +ciConstant ciInstance::field_value_impl(BasicType field_btype, int offset) { Handle obj = get_oop(); assert(!obj.is_null(), "bad oop"); - BasicType field_btype = field->type()->basic_type(); - int offset = field->offset(); - switch(field_btype) { - case T_BYTE: - return ciConstant(field_btype, obj->byte_field(offset)); - break; - case T_CHAR: - return ciConstant(field_btype, obj->char_field(offset)); - break; - case T_SHORT: - return ciConstant(field_btype, obj->short_field(offset)); - break; - case T_BOOLEAN: - return ciConstant(field_btype, obj->bool_field(offset)); - break; - case T_INT: - return ciConstant(field_btype, obj->int_field(offset)); - break; - case T_FLOAT: - return ciConstant(obj->float_field(offset)); - break; - case T_DOUBLE: - return ciConstant(obj->double_field(offset)); - break; - case T_LONG: - return ciConstant(obj->long_field(offset)); - break; - case T_OBJECT: - case T_ARRAY: - { + case T_BYTE: return ciConstant(field_btype, obj->byte_field(offset)); + case T_CHAR: return ciConstant(field_btype, obj->char_field(offset)); + case T_SHORT: return ciConstant(field_btype, obj->short_field(offset)); + case T_BOOLEAN: return ciConstant(field_btype, obj->bool_field(offset)); + case T_INT: return ciConstant(field_btype, obj->int_field(offset)); + case T_FLOAT: return ciConstant(obj->float_field(offset)); + case T_DOUBLE: return ciConstant(obj->double_field(offset)); + case T_LONG: return ciConstant(obj->long_field(offset)); + case T_OBJECT: // fall through + case T_ARRAY: { oop o = obj->obj_field(offset); // A field will be "constant" if it is known always to be @@ -115,12 +87,23 @@ } } } - ShouldNotReachHere(); - // to shut up the compiler + fatal("no field value: %s", type2name(field_btype)); return ciConstant(); } // ------------------------------------------------------------------ +// ciInstance::field_value +// +// Constant value of a field. +ciConstant ciInstance::field_value(ciField* field) { + assert(is_loaded(), "invalid access - must be loaded"); + assert(field->holder()->is_loaded(), "invalid access - holder must be loaded"); + assert(field->is_static() || klass()->is_subclass_of(field->holder()), "invalid access - must be subclass"); + + GUARDED_VM_ENTRY(return field_value_impl(field->type()->basic_type(), field->offset());) +} + +// ------------------------------------------------------------------ // ciInstance::field_value_by_offset // // Constant value of a field at the specified offset. diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciInstance.hpp --- a/hotspot/src/share/vm/ci/ciInstance.hpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciInstance.hpp Mon Apr 11 21:42:55 2016 +0300 @@ -36,6 +36,7 @@ // instance of java.lang.Object. class ciInstance : public ciObject { CI_PACKAGE_ACCESS + friend class ciField; protected: ciInstance(instanceHandle h_i) : ciObject(h_i) { @@ -50,6 +51,8 @@ void print_impl(outputStream* st); + ciConstant field_value_impl(BasicType field_btype, int offset); + public: // If this object is a java mirror, return the corresponding type. // Otherwise, return NULL. diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciKlass.cpp --- a/hotspot/src/share/vm/ci/ciKlass.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciKlass.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -88,12 +88,7 @@ assert(this->is_loaded(), "must be loaded: %s", this->name()->as_quoted_ascii()); assert(that->is_loaded(), "must be loaded: %s", that->name()->as_quoted_ascii()); - VM_ENTRY_MARK; - Klass* this_klass = get_Klass(); - Klass* that_klass = that->get_Klass(); - bool result = this_klass->is_subclass_of(that_klass); - - return result; + GUARDED_VM_ENTRY(return get_Klass()->is_subclass_of(that->get_Klass());) } // ------------------------------------------------------------------ diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/ci/ciSymbol.cpp --- a/hotspot/src/share/vm/ci/ciSymbol.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciSymbol.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -58,9 +58,7 @@ // // The text of the symbol as a null-terminated C string. const char* ciSymbol::as_utf8() { - VM_QUICK_ENTRY_MARK; - Symbol* s = get_symbol(); - return s->as_utf8(); + GUARDED_VM_QUICK_ENTRY(return get_symbol()->as_utf8();) } // The text of the symbol as a null-terminated C string. diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/graphKit.cpp --- a/hotspot/src/share/vm/opto/graphKit.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/graphKit.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -4466,6 +4466,25 @@ set_memory(st, TypeAryPtr::BYTES); } +Node* GraphKit::make_constant_from_field(ciField* field, Node* obj) { + if (!field->is_constant()) { + return NULL; // Field not marked as constant. + } + ciInstance* holder = NULL; + if (!field->is_static()) { + ciObject* const_oop = obj->bottom_type()->is_oopptr()->const_oop(); + if (const_oop != NULL && const_oop->is_instance()) { + holder = const_oop->as_instance(); + } + } + const Type* con_type = Type::make_constant_from_field(field, holder, field->layout_type(), + /*is_unsigned_load=*/false); + if (con_type != NULL) { + return makecon(con_type); + } + return NULL; +} + Node* GraphKit::cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type) { // Reify the property as a CastPP node in Ideal graph to comply with monotonicity // assumption of CCP analysis. diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/graphKit.hpp --- a/hotspot/src/share/vm/opto/graphKit.hpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/graphKit.hpp Mon Apr 11 21:42:55 2016 +0300 @@ -910,6 +910,8 @@ void add_predicate(int nargs = 0); void add_predicate_impl(Deoptimization::DeoptReason reason, int nargs); + Node* make_constant_from_field(ciField* field, Node* obj); + // Produce new array node of stable type Node* cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type); }; diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/library_call.cpp --- a/hotspot/src/share/vm/opto/library_call.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/library_call.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -2550,13 +2550,9 @@ 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() && !mismatched) { + if (heap_base_oop != top() && field != NULL && field->is_constant() && !mismatched) { // 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); - } + p = make_constant_from_field(field, heap_base_oop); } if (p == NULL) { // To be valid, unsafe loads may depend on other conditions than diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/macro.cpp --- a/hotspot/src/share/vm/opto/macro.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/macro.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -860,7 +860,7 @@ if (basic_elem_type == T_OBJECT || basic_elem_type == T_ARRAY) { if (!elem_type->is_loaded()) { field_type = TypeInstPtr::BOTTOM; - } else if (field != NULL && field->is_constant() && field->is_static()) { + } else if (field != NULL && field->is_static_constant()) { // This can happen if the constant oop is non-perm. ciObject* con = field->constant_value().as_object(); // Do not "join" in the previous type; it doesn't add value, diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/memnode.cpp --- a/hotspot/src/share/vm/opto/memnode.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/memnode.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -796,7 +796,7 @@ #endif { assert(!adr->bottom_type()->is_ptr_to_narrowoop() && !adr->bottom_type()->is_ptr_to_narrowklass(), "should have got back a narrow oop"); - load = new LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr(), mo, control_dependency); + load = new LoadPNode(ctl, mem, adr, adr_type, rt->is_ptr(), mo, control_dependency); } break; } @@ -1620,72 +1620,6 @@ return NULL; } -static ciConstant check_mismatched_access(ciConstant con, BasicType loadbt, bool is_unsigned) { - BasicType conbt = con.basic_type(); - switch (conbt) { - case T_BOOLEAN: conbt = T_BYTE; break; - case T_ARRAY: conbt = T_OBJECT; break; - } - switch (loadbt) { - case T_BOOLEAN: loadbt = T_BYTE; break; - case T_NARROWOOP: loadbt = T_OBJECT; break; - case T_ARRAY: loadbt = T_OBJECT; break; - case T_ADDRESS: loadbt = T_OBJECT; break; - } - if (conbt == loadbt) { - if (is_unsigned && conbt == T_BYTE) { - // LoadB (T_BYTE) with a small mask (<=8-bit) is converted to LoadUB (T_BYTE). - return ciConstant(T_INT, con.as_int() & 0xFF); - } else { - return con; - } - } - if (conbt == T_SHORT && loadbt == T_CHAR) { - // LoadS (T_SHORT) with a small mask (<=16-bit) is converted to LoadUS (T_CHAR). - return ciConstant(T_INT, con.as_int() & 0xFFFF); - } - return ciConstant(); // T_ILLEGAL -} - -// Try to constant-fold a stable array element. -static const Type* fold_stable_ary_elem(const TypeAryPtr* ary, int off, bool is_unsigned_load, BasicType loadbt) { - assert(ary->const_oop(), "array should be constant"); - assert(ary->is_stable(), "array should be stable"); - - // Decode the results of GraphKit::array_element_address. - ciArray* aobj = ary->const_oop()->as_array(); - ciConstant element_value = aobj->element_value_by_offset(off); - if (element_value.basic_type() == T_ILLEGAL) { - return NULL; // wrong offset - } - ciConstant con = check_mismatched_access(element_value, loadbt, is_unsigned_load); - assert(con.basic_type() != T_ILLEGAL, "elembt=%s; loadbt=%s; unsigned=%d", - type2name(element_value.basic_type()), type2name(loadbt), is_unsigned_load); - - if (con.basic_type() != T_ILLEGAL && // not a mismatched access - !con.is_null_or_zero()) { // not a default value - const Type* con_type = Type::make_from_constant(con); - if (con_type != NULL) { - if (con_type->isa_aryptr()) { - // Join with the array element type, in case it is also stable. - int dim = ary->stable_dimension(); - con_type = con_type->is_aryptr()->cast_to_stable(true, dim-1); - } - if (loadbt == T_NARROWOOP && con_type->isa_oopptr()) { - con_type = con_type->make_narrowoop(); - } -#ifndef PRODUCT - if (TraceIterativeGVN) { - tty->print("FoldStableValues: array element [off=%d]: con_type=", off); - con_type->dump(); tty->cr(); - } -#endif //PRODUCT - return con_type; - } - } - return NULL; -} - //------------------------------Value----------------------------------------- const Type* LoadNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP @@ -1714,10 +1648,14 @@ const bool off_beyond_header = ((uint)off >= (uint)min_base_off); // Try to constant-fold a stable array element. - if (FoldStableValues && !is_mismatched_access() && ary->is_stable() && ary->const_oop() != NULL) { + if (FoldStableValues && !is_mismatched_access() && ary->is_stable()) { // Make sure the reference is not into the header and the offset is constant - if (off_beyond_header && adr->is_AddP() && off != Type::OffsetBot) { - const Type* con_type = fold_stable_ary_elem(ary, off, is_unsigned(), memory_type()); + ciObject* aobj = ary->const_oop(); + if (aobj != NULL && off_beyond_header && adr->is_AddP() && off != Type::OffsetBot) { + int stable_dimension = (ary->stable_dimension() > 0 ? ary->stable_dimension() - 1 : 0); + const Type* con_type = Type::make_constant_from_array_element(aobj->as_array(), off, + stable_dimension, + memory_type(), is_unsigned()); if (con_type != NULL) { return con_type; } @@ -1784,28 +1722,10 @@ // For oop loads, we expect the _type to be precise. // Optimizations for constant objects ciObject* const_oop = tinst->const_oop(); - if (const_oop != NULL) { - // For constant CallSites treat the target field as a compile time constant. - if (const_oop->is_call_site()) { - ciCallSite* call_site = const_oop->as_call_site(); - ciField* field = call_site->klass()->as_instance_klass()->get_field_by_offset(off, /*is_static=*/ false); - if (field != NULL && field->is_call_site_target()) { - ciMethodHandle* target = call_site->get_target(); - if (target != NULL) { // just in case - ciConstant constant(T_OBJECT, target); - const Type* t; - if (adr->bottom_type()->is_ptr_to_narrowoop()) { - t = TypeNarrowOop::make_from_constant(constant.as_object(), true); - } else { - t = TypeOopPtr::make_from_constant(constant.as_object(), true); - } - // Add a dependence for invalidation of the optimization. - if (!call_site->is_constant_call_site()) { - C->dependencies()->assert_call_site_target_value(call_site, target); - } - return t; - } - } + if (const_oop != NULL && const_oop->is_instance()) { + const Type* con_type = Type::make_constant_from_field(const_oop->as_instance(), off, is_unsigned(), memory_type()); + if (con_type != NULL) { + return con_type; } } } else if (tp->base() == Type::KlassPtr) { diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/parse3.cpp --- a/hotspot/src/share/vm/opto/parse3.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/parse3.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -149,9 +149,9 @@ // Does this field have a constant value? If so, just push the value. if (field->is_constant()) { // final or stable field - const Type* con_type = Type::make_constant(field, obj); - if (con_type != NULL) { - push_node(con_type->basic_type(), makecon(con_type)); + Node* con = make_constant_from_field(field, obj); + if (con != NULL) { + push_node(field->layout_type(), con); return; } } @@ -174,12 +174,16 @@ if (!field->type()->is_loaded()) { type = TypeInstPtr::BOTTOM; must_assert_null = true; - } else if (field->is_constant() && field->is_static()) { + } else if (field->is_static_constant()) { // This can happen if the constant oop is non-perm. ciObject* con = field->constant_value().as_object(); // Do not "join" in the previous type; it doesn't add value, // and may yield a vacuous result if the field is of interface type. - type = TypeOopPtr::make_from_constant(con)->isa_oopptr(); + if (con->is_null_object()) { + type = TypePtr::NULL_PTR; + } else { + type = TypeOopPtr::make_from_constant(con)->isa_oopptr(); + } assert(type != NULL, "field singleton type must be consistent"); } else { type = TypeOopPtr::make_from_klass(field_klass->as_klass()); diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/stringopts.cpp --- a/hotspot/src/share/vm/opto/stringopts.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/stringopts.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -1112,7 +1112,7 @@ if( bt == T_OBJECT ) { if (!field->type()->is_loaded()) { type = TypeInstPtr::BOTTOM; - } else if (field->is_constant()) { + } else if (field->is_static_constant()) { // This can happen if the constant oop is non-perm. ciObject* con = field->constant_value().as_object(); // Do not "join" in the previous type; it doesn't add value, diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/type.cpp --- a/hotspot/src/share/vm/opto/type.cpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/type.cpp Mon Apr 11 21:42:55 2016 +0300 @@ -225,74 +225,156 @@ //-----------------------make_from_constant------------------------------------ -const Type* Type::make_from_constant(ciConstant constant, bool require_constant) { +const Type* Type::make_from_constant(ciConstant constant, bool require_constant, + int stable_dimension, bool is_narrow_oop, + bool is_autobox_cache) { switch (constant.basic_type()) { - case T_BOOLEAN: return TypeInt::make(constant.as_boolean()); - case T_CHAR: return TypeInt::make(constant.as_char()); - case T_BYTE: return TypeInt::make(constant.as_byte()); - case T_SHORT: return TypeInt::make(constant.as_short()); - case T_INT: return TypeInt::make(constant.as_int()); - case T_LONG: return TypeLong::make(constant.as_long()); - case T_FLOAT: return TypeF::make(constant.as_float()); - case T_DOUBLE: return TypeD::make(constant.as_double()); - 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. - ciObject* oop_constant = constant.as_object(); - 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); + case T_BOOLEAN: return TypeInt::make(constant.as_boolean()); + case T_CHAR: return TypeInt::make(constant.as_char()); + case T_BYTE: return TypeInt::make(constant.as_byte()); + case T_SHORT: return TypeInt::make(constant.as_short()); + case T_INT: return TypeInt::make(constant.as_int()); + case T_LONG: return TypeLong::make(constant.as_long()); + case T_FLOAT: return TypeF::make(constant.as_float()); + case T_DOUBLE: return TypeD::make(constant.as_double()); + 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. + const Type* con_type = NULL; + ciObject* oop_constant = constant.as_object(); + if (oop_constant->is_null_object()) { + con_type = Type::get_zero_type(T_OBJECT); + } else if (require_constant || oop_constant->should_be_constant()) { + con_type = TypeOopPtr::make_from_constant(oop_constant, require_constant); + if (con_type != NULL) { + if (Compile::current()->eliminate_boxing() && is_autobox_cache) { + con_type = con_type->is_aryptr()->cast_to_autobox_cache(true); + } + if (stable_dimension > 0) { + assert(FoldStableValues, "sanity"); + assert(!con_type->is_zero_type(), "default value for stable field"); + con_type = con_type->is_aryptr()->cast_to_stable(true, stable_dimension); + } + } + } + if (is_narrow_oop) { + con_type = con_type->make_narrowoop(); + } + return con_type; } - } - case T_ILLEGAL: - // Invalid ciConstant returned due to OutOfMemoryError in the CI - assert(Compile::current()->env()->failing(), "otherwise should not see this"); - return NULL; + 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; +static ciConstant check_mismatched_access(ciConstant con, BasicType loadbt, bool is_unsigned) { + BasicType conbt = con.basic_type(); + switch (conbt) { + case T_BOOLEAN: conbt = T_BYTE; break; + case T_ARRAY: conbt = T_OBJECT; break; + } + switch (loadbt) { + case T_BOOLEAN: loadbt = T_BYTE; break; + case T_NARROWOOP: loadbt = T_OBJECT; break; + case T_ARRAY: loadbt = T_OBJECT; break; + case T_ADDRESS: loadbt = T_OBJECT; break; + } + if (conbt == loadbt) { + if (is_unsigned && conbt == T_BYTE) { + // LoadB (T_BYTE) with a small mask (<=8-bit) is converted to LoadUB (T_BYTE). + return ciConstant(T_INT, con.as_int() & 0xFF); + } else { + return con; + } + } + if (conbt == T_SHORT && loadbt == T_CHAR) { + // LoadS (T_SHORT) with a small mask (<=16-bit) is converted to LoadUS (T_CHAR). + return ciConstant(T_INT, con.as_int() & 0xFFFF); + } + return ciConstant(); // T_ILLEGAL +} + +// Try to constant-fold a stable array element. +const Type* Type::make_constant_from_array_element(ciArray* array, int off, int stable_dimension, + BasicType loadbt, bool is_unsigned_load) { + // Decode the results of GraphKit::array_element_address. + ciConstant element_value = array->element_value_by_offset(off); + if (element_value.basic_type() == T_ILLEGAL) { + return NULL; // wrong offset + } + ciConstant con = check_mismatched_access(element_value, loadbt, is_unsigned_load); + + assert(con.basic_type() != T_ILLEGAL, "elembt=%s; loadbt=%s; unsigned=%d", + type2name(element_value.basic_type()), type2name(loadbt), is_unsigned_load); + + if (con.is_valid() && // not a mismatched access + !con.is_null_or_zero()) { // not a default value + bool is_narrow_oop = (loadbt == T_NARROWOOP); + return Type::make_from_constant(con, /*require_constant=*/true, stable_dimension, is_narrow_oop, /*is_autobox_cache=*/false); + } + return NULL; +} + +const Type* Type::make_constant_from_field(ciInstance* holder, int off, bool is_unsigned_load, BasicType loadbt) { + ciField* field; + ciType* type = holder->java_mirror_type(); + if (type != NULL && type->is_instance_klass() && off >= InstanceMirrorKlass::offset_of_static_fields()) { + // Static field + field = type->as_instance_klass()->get_field_by_offset(off, /*is_static=*/true); + } else { + // Instance field + field = holder->klass()->as_instance_klass()->get_field_by_offset(off, /*is_static=*/false); + } + if (field == NULL) { + return NULL; // Wrong offset + } + return Type::make_constant_from_field(field, holder, loadbt, is_unsigned_load); +} + +const Type* Type::make_constant_from_field(ciField* field, ciInstance* holder, + BasicType loadbt, bool is_unsigned_load) { + if (!field->is_constant()) { + return NULL; // Non-constant field + } + ciConstant field_value; 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 { + field_value = field->constant_value(); + } else if (holder != NULL) { // 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); - } + field_value = field->constant_value_of(holder); + } + if (!field_value.is_valid()) { + return NULL; // Not a constant } - 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); - } + + ciConstant con = check_mismatched_access(field_value, loadbt, is_unsigned_load); + + assert(con.is_valid(), "elembt=%s; loadbt=%s; unsigned=%d", + type2name(field_value.basic_type()), type2name(loadbt), is_unsigned_load); + + bool is_stable_array = FoldStableValues && field->is_stable() && field->type()->is_array_klass(); + int stable_dimension = (is_stable_array ? field->type()->as_array_klass()->dimension() : 0); + bool is_narrow_oop = (loadbt == T_NARROWOOP); + + const Type* con_type = make_from_constant(con, /*require_constant=*/ true, + stable_dimension, is_narrow_oop, + field->is_autobox_cache()); + if (con_type != NULL && field->is_call_site_target()) { + ciCallSite* call_site = holder->as_call_site(); + if (!call_site->is_constant_call_site()) { + ciMethodHandle* target = call_site->get_target(); + Compile::current()->dependencies()->assert_call_site_target_value(call_site, target); } } return con_type; diff -r c645d414429f -r 93f24e7b3c43 hotspot/src/share/vm/opto/type.hpp --- a/hotspot/src/share/vm/opto/type.hpp Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/src/share/vm/opto/type.hpp Mon Apr 11 21:42:55 2016 +0300 @@ -417,9 +417,26 @@ static const Type* get_typeflow_type(ciType* type); static const Type* make_from_constant(ciConstant constant, - bool require_constant = false); + bool require_constant = false, + int stable_dimension = 0, + bool is_narrow = false, + bool is_autobox_cache = false); + + static const Type* make_constant_from_field(ciInstance* holder, + int off, + bool is_unsigned_load, + BasicType loadbt); - static const Type* make_constant(ciField* field, Node* obj); + static const Type* make_constant_from_field(ciField* field, + ciInstance* holder, + BasicType loadbt, + bool is_unsigned_load); + + static const Type* make_constant_from_array_element(ciArray* array, + int off, + int stable_dimension, + BasicType loadbt, + bool is_unsigned_load); // Speculative type helper methods. See TypePtr. virtual const TypePtr* speculative() const { return NULL; } diff -r c645d414429f -r 93f24e7b3c43 hotspot/test/compiler/unsafe/UnsafeGetConstantField.java --- a/hotspot/test/compiler/unsafe/UnsafeGetConstantField.java Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/test/compiler/unsafe/UnsafeGetConstantField.java Mon Apr 11 21:42:55 2016 +0300 @@ -33,6 +33,7 @@ * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc + * * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions * -Xbatch -XX:-TieredCompilation * -XX:+FoldStableValues @@ -63,7 +64,6 @@ import jdk.internal.misc.Unsafe; import java.io.IOException; -import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; diff -r c645d414429f -r 93f24e7b3c43 hotspot/test/compiler/unsafe/UnsafeGetStableArrayElement.java --- a/hotspot/test/compiler/unsafe/UnsafeGetStableArrayElement.java Mon Apr 11 21:42:31 2016 +0300 +++ b/hotspot/test/compiler/unsafe/UnsafeGetStableArrayElement.java Mon Apr 11 21:42:55 2016 +0300 @@ -26,24 +26,28 @@ /* * @test * @summary tests on constant folding of unsafe get operations from stable arrays - * @library /testlibrary /test/lib - * @ignore 8151137 + * @library /testlibrary * * @requires vm.flavor != "client" * + * @modules java.base/jdk.internal.vm.annotation + * java.base/jdk.internal.misc + * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions * -Xbatch -XX:-TieredCompilation * -XX:+FoldStableValues * -XX:CompileCommand=dontinline,*Test::test* - * UnsafeGetStableArrayElement + * compiler.unsafe.UnsafeGetStableArrayElement */ +package compiler.unsafe; + import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.Stable; import java.util.concurrent.Callable; +import jdk.test.lib.Platform; import static jdk.internal.misc.Unsafe.*; import static jdk.test.lib.Asserts.*; -import static jdk.test.lib.Platform; public class UnsafeGetStableArrayElement { @Stable static final boolean[] STABLE_BOOLEAN_ARRAY = new boolean[16]; @@ -219,13 +223,7 @@ Setter.reset(); } - public static void main(String[] args) throws Exception { - if (Platform.isServer()) { - test(); - } - } - - static void test() throws Exception { + static void testUnsafeAccess() throws Exception { // boolean[], aligned accesses testMatched( Test::testZ_Z, Test::changeZ); testMismatched(Test::testZ_B, Test::changeZ); @@ -329,4 +327,11 @@ run(Test::testL_I); run(Test::testL_F); } + + public static void main(String[] args) throws Exception { + if (Platform.isServer()) { + testUnsafeAccess(); + } + System.out.println("TEST PASSED"); + } }