8152590: C2: @Stable support doesn't always work w/ incremental inlining

Reviewed-by: kvn
This commit is contained in:
Vladimir Ivanov 2016-04-11 21:42:55 +03:00
parent 0c233de7ff
commit 7fc81004e3
20 changed files with 350 additions and 358 deletions

View File

@ -247,7 +247,7 @@ void Canonicalizer::do_ArrayLength (ArrayLength* x) {
} 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()) {

View File

@ -1520,6 +1520,8 @@ void GraphBuilder::method_return(Value x) {
}
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 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
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 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
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);
}
}
}

View File

@ -124,6 +124,9 @@ public:
}
}
bool is_valid() const {
return basic_type() != T_ILLEGAL;
}
// Debugging output
void print();
};

View File

@ -235,96 +235,77 @@ void ciField::initialize_from(fieldDescriptor* fd) {
_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
// 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");
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;
}
}
}
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;
} 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).
_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;
}
// ------------------------------------------------------------------
// ciField::compute_type
//

View File

@ -62,7 +62,7 @@ private:
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 @@ public:
//
// 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 @@ public:
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 @@ public:
//
// 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 @@ public:
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();

View File

@ -56,49 +56,21 @@ ciType* ciInstance::java_mirror_type() {
}
// ------------------------------------------------------------------
// 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,11 +87,22 @@ ciConstant ciInstance::field_value(ciField* field) {
}
}
}
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
//

View File

@ -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 @@ protected:
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.

View File

@ -88,12 +88,7 @@ bool ciKlass::is_subclass_of(ciKlass* that) {
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());)
}
// ------------------------------------------------------------------

View File

@ -58,9 +58,7 @@ ciSymbol::ciSymbol(Symbol* s)
//
// 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.

View File

@ -4466,6 +4466,25 @@ void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* coun
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.

View File

@ -910,6 +910,8 @@ class GraphKit : public Phase {
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);
};

View File

@ -2550,13 +2550,9 @@ bool LibraryCallKit::inline_unsafe_access(const bool is_native_ptr, bool is_stor
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

View File

@ -860,7 +860,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <Sa
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,

View File

@ -796,7 +796,7 @@ Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypeP
#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 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls,
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 Type* LoadNode::Value(PhaseGVN* phase) const {
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 @@ const Type* LoadNode::Value(PhaseGVN* phase) const {
// 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) {

View File

@ -149,9 +149,9 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
// 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 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
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());

View File

@ -1112,7 +1112,7 @@ Node* PhaseStringOpts::fetch_static_field(GraphKit& kit, ciField* field) {
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,

View File

@ -225,74 +225,156 @@ const Type* Type::get_typeflow_type(ciType* type) {
//-----------------------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;
}
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
}
const Type* Type::make_constant(ciField* field, Node* obj) {
if (!field->is_constant()) return NULL;
// 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);
const Type* con_type = NULL;
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 (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);
}
if (!field_value.is_valid()) {
return NULL; // Not a constant
}
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;

View File

@ -417,9 +417,26 @@ public:
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(ciField* field, Node* obj);
static const Type* make_constant_from_field(ciInstance* holder,
int off,
bool is_unsigned_load,
BasicType loadbt);
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; }

View File

@ -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.test.lib.Platform;
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;

View File

@ -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 @@ public class UnsafeGetStableArrayElement {
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 @@ public class UnsafeGetStableArrayElement {
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");
}
}