8152590: C2: @Stable support doesn't always work w/ incremental inlining
Reviewed-by: kvn
--- 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()) {
--- 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);
}
}
}
--- 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();
};
--- 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;
}
// ------------------------------------------------------------------
--- 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();
--- 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.
--- 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.
--- 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());)
}
// ------------------------------------------------------------------
--- 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.
--- 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.
--- 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);
};
--- 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
--- 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,
--- 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) {
--- 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());
--- 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,
--- 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;
--- 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; }
--- 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;
--- 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");
+ }
}