8001107: @Stable annotation for constant folding of lazily evaluated variables
Co-authored-by: John Rose <john.r.rose@oracle.com> Reviewed-by: rbackman, twisti, kvn
This commit is contained in:
parent
19d50345e1
commit
f0e77ac67f
@ -24,13 +24,92 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "ci/ciArray.hpp"
|
||||
#include "ci/ciArrayKlass.hpp"
|
||||
#include "ci/ciConstant.hpp"
|
||||
#include "ci/ciKlass.hpp"
|
||||
#include "ci/ciUtilities.hpp"
|
||||
#include "oops/objArrayOop.hpp"
|
||||
#include "oops/typeArrayOop.hpp"
|
||||
|
||||
// ciArray
|
||||
//
|
||||
// This class represents an arrayOop in the HotSpot virtual
|
||||
// machine.
|
||||
static BasicType fixup_element_type(BasicType bt) {
|
||||
if (bt == T_ARRAY) return T_OBJECT;
|
||||
if (bt == T_BOOLEAN) return T_BYTE;
|
||||
return bt;
|
||||
}
|
||||
|
||||
ciConstant ciArray::element_value_impl(BasicType elembt,
|
||||
arrayOop ary,
|
||||
int index) {
|
||||
if (ary == NULL)
|
||||
return ciConstant();
|
||||
assert(ary->is_array(), "");
|
||||
if (index < 0 || index >= ary->length())
|
||||
return ciConstant();
|
||||
ArrayKlass* ak = (ArrayKlass*) ary->klass();
|
||||
BasicType abt = ak->element_type();
|
||||
if (fixup_element_type(elembt) !=
|
||||
fixup_element_type(abt))
|
||||
return ciConstant();
|
||||
switch (elembt) {
|
||||
case T_ARRAY:
|
||||
case T_OBJECT:
|
||||
{
|
||||
assert(ary->is_objArray(), "");
|
||||
objArrayOop objary = (objArrayOop) ary;
|
||||
oop elem = objary->obj_at(index);
|
||||
ciEnv* env = CURRENT_ENV;
|
||||
ciObject* box = env->get_object(elem);
|
||||
return ciConstant(T_OBJECT, box);
|
||||
}
|
||||
}
|
||||
assert(ary->is_typeArray(), "");
|
||||
typeArrayOop tary = (typeArrayOop) ary;
|
||||
jint value = 0;
|
||||
switch (elembt) {
|
||||
case T_LONG: return ciConstant(tary->long_at(index));
|
||||
case T_FLOAT: return ciConstant(tary->float_at(index));
|
||||
case T_DOUBLE: return ciConstant(tary->double_at(index));
|
||||
default: return ciConstant();
|
||||
case T_BYTE: value = tary->byte_at(index); break;
|
||||
case T_BOOLEAN: value = tary->byte_at(index) & 1; break;
|
||||
case T_SHORT: value = tary->short_at(index); break;
|
||||
case T_CHAR: value = tary->char_at(index); break;
|
||||
case T_INT: value = tary->int_at(index); break;
|
||||
}
|
||||
return ciConstant(elembt, value);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciArray::element_value
|
||||
//
|
||||
// Current value of an element.
|
||||
// Returns T_ILLEGAL if there is no element at the given index.
|
||||
ciConstant ciArray::element_value(int index) {
|
||||
BasicType elembt = element_basic_type();
|
||||
GUARDED_VM_ENTRY(
|
||||
return element_value_impl(elembt, get_arrayOop(), index);
|
||||
)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciArray::element_value_by_offset
|
||||
//
|
||||
// Current value of an element at the specified offset.
|
||||
// Returns T_ILLEGAL if there is no element at the given offset.
|
||||
ciConstant ciArray::element_value_by_offset(intptr_t element_offset) {
|
||||
BasicType elembt = element_basic_type();
|
||||
intptr_t shift = exact_log2(type2aelembytes(elembt));
|
||||
intptr_t header = arrayOopDesc::base_offset_in_bytes(elembt);
|
||||
intptr_t index = (element_offset - header) >> shift;
|
||||
intptr_t offset = header + ((intptr_t)index << shift);
|
||||
if (offset != element_offset || index != (jint)index)
|
||||
return ciConstant();
|
||||
return element_value((jint) index);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciArray::print_impl
|
||||
|
@ -25,6 +25,8 @@
|
||||
#ifndef SHARE_VM_CI_CIARRAY_HPP
|
||||
#define SHARE_VM_CI_CIARRAY_HPP
|
||||
|
||||
#include "ci/ciArrayKlass.hpp"
|
||||
#include "ci/ciConstant.hpp"
|
||||
#include "ci/ciObject.hpp"
|
||||
#include "oops/arrayOop.hpp"
|
||||
#include "oops/objArrayOop.hpp"
|
||||
@ -45,15 +47,30 @@ protected:
|
||||
|
||||
ciArray(ciKlass* klass, int len) : ciObject(klass), _length(len) {}
|
||||
|
||||
arrayOop get_arrayOop() { return (arrayOop)get_oop(); }
|
||||
arrayOop get_arrayOop() const { return (arrayOop)get_oop(); }
|
||||
|
||||
const char* type_string() { return "ciArray"; }
|
||||
|
||||
void print_impl(outputStream* st);
|
||||
|
||||
ciConstant element_value_impl(BasicType elembt, arrayOop ary, int index);
|
||||
|
||||
public:
|
||||
int length() { return _length; }
|
||||
|
||||
// Convenience routines.
|
||||
ciArrayKlass* array_type() { return klass()->as_array_klass(); }
|
||||
ciType* element_type() { return array_type()->element_type(); }
|
||||
BasicType element_basic_type() { return element_type()->basic_type(); }
|
||||
|
||||
// Current value of an element.
|
||||
// Returns T_ILLEGAL if there is no element at the given index.
|
||||
ciConstant element_value(int index);
|
||||
|
||||
// Current value of an element at the specified offset.
|
||||
// Returns T_ILLEGAL if there is no element at the given offset.
|
||||
ciConstant element_value_by_offset(intptr_t element_offset);
|
||||
|
||||
// What kind of ciObject is this?
|
||||
bool is_array() { return true; }
|
||||
bool is_java_object() { return true; }
|
||||
|
@ -41,7 +41,6 @@ private:
|
||||
union {
|
||||
jint _int;
|
||||
jlong _long;
|
||||
jint _long_half[2];
|
||||
jfloat _float;
|
||||
jdouble _double;
|
||||
ciObject* _object;
|
||||
@ -111,6 +110,20 @@ public:
|
||||
return _value._object;
|
||||
}
|
||||
|
||||
bool is_null_or_zero() const {
|
||||
if (!is_java_primitive(basic_type())) {
|
||||
return as_object()->is_null_object();
|
||||
} else if (type2size[basic_type()] == 1) {
|
||||
// treat float bits as int, to avoid comparison with -0 and NaN
|
||||
return (_value._int == 0);
|
||||
} else if (type2size[basic_type()] == 2) {
|
||||
// treat double bits as long, to avoid comparison with -0 and NaN
|
||||
return (_value._long == 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Debugging output
|
||||
void print();
|
||||
};
|
||||
|
@ -189,12 +189,14 @@ void ciField::initialize_from(fieldDescriptor* fd) {
|
||||
_holder = CURRENT_ENV->get_instance_klass(fd->field_holder());
|
||||
|
||||
// Check to see if the field is constant.
|
||||
if (_holder->is_initialized() && this->is_final()) {
|
||||
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 (trust_final_non_static_fields(_holder)) {
|
||||
if (is_stable || trust_final_non_static_fields(_holder)) {
|
||||
_is_constant = true;
|
||||
return;
|
||||
}
|
||||
@ -227,7 +229,6 @@ void ciField::initialize_from(fieldDescriptor* fd) {
|
||||
|
||||
Handle mirror = k->java_mirror();
|
||||
|
||||
_is_constant = true;
|
||||
switch(type()->basic_type()) {
|
||||
case T_BYTE:
|
||||
_constant_value = ciConstant(type()->basic_type(), mirror->byte_field(_offset));
|
||||
@ -273,6 +274,12 @@ void ciField::initialize_from(fieldDescriptor* fd) {
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
_is_constant = false;
|
||||
}
|
||||
@ -373,8 +380,11 @@ void ciField::print() {
|
||||
tty->print(" signature=");
|
||||
_signature->print_symbol();
|
||||
tty->print(" offset=%d type=", _offset);
|
||||
if (_type != NULL) _type->print_name();
|
||||
else tty->print("(reference)");
|
||||
if (_type != NULL)
|
||||
_type->print_name();
|
||||
else
|
||||
tty->print("(reference)");
|
||||
tty->print(" flags=%04x", flags().as_int());
|
||||
tty->print(" is_constant=%s", bool_to_str(_is_constant));
|
||||
if (_is_constant && is_static()) {
|
||||
tty->print(" constant_value=");
|
||||
|
@ -139,7 +139,10 @@ public:
|
||||
// non-constant fields. These are java.lang.System.in
|
||||
// and java.lang.System.out. Abomination.
|
||||
//
|
||||
// Note: the check for case 4 is not yet implemented.
|
||||
// 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; }
|
||||
|
||||
// Get the constant value of this field.
|
||||
@ -173,6 +176,7 @@ public:
|
||||
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(); }
|
||||
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; }
|
||||
bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; }
|
||||
bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; }
|
||||
bool is_stable () const { return (_flags & JVM_ACC_FIELD_STABLE) != 0; }
|
||||
|
||||
// Conversion
|
||||
jint as_int() { return _flags; }
|
||||
|
@ -127,6 +127,8 @@ ciConstant ciInstance::field_value(ciField* field) {
|
||||
ciConstant ciInstance::field_value_by_offset(int field_offset) {
|
||||
ciInstanceKlass* ik = klass()->as_instance_klass();
|
||||
ciField* field = ik->get_field_by_offset(field_offset, false);
|
||||
if (field == NULL)
|
||||
return ciConstant(); // T_ILLEGAL
|
||||
return field_value(field);
|
||||
}
|
||||
|
||||
|
@ -39,5 +39,10 @@
|
||||
jchar ciTypeArray::char_at(int index) {
|
||||
VM_ENTRY_MARK;
|
||||
assert(index >= 0 && index < length(), "out of range");
|
||||
return get_typeArrayOop()->char_at(index);
|
||||
jchar c = get_typeArrayOop()->char_at(index);
|
||||
#ifdef ASSERT
|
||||
jchar d = element_value(index).as_char();
|
||||
assert(c == d, "");
|
||||
#endif //ASSERT
|
||||
return c;
|
||||
}
|
||||
|
@ -1774,6 +1774,10 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d
|
||||
if (_location != _in_method) break; // only allow for methods
|
||||
if (!privileged) break; // only allow in privileged code
|
||||
return _method_LambdaForm_Hidden;
|
||||
case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_invoke_Stable_signature):
|
||||
if (_location != _in_field) break; // only allow for fields
|
||||
if (!privileged) break; // only allow in privileged code
|
||||
return _field_Stable;
|
||||
case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature):
|
||||
if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes
|
||||
if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges
|
||||
@ -1786,6 +1790,8 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d
|
||||
void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) {
|
||||
if (is_contended())
|
||||
f->set_contended_group(contended_group());
|
||||
if (is_stable())
|
||||
f->set_stable(true);
|
||||
}
|
||||
|
||||
ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() {
|
||||
|
@ -125,6 +125,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||
_method_LambdaForm_Compiled,
|
||||
_method_LambdaForm_Hidden,
|
||||
_sun_misc_Contended,
|
||||
_field_Stable,
|
||||
_annotation_LIMIT
|
||||
};
|
||||
const Location _location;
|
||||
@ -143,14 +144,23 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
|
||||
_annotations_present |= nth_bit((int)id);
|
||||
}
|
||||
|
||||
void remove_annotation(ID id) {
|
||||
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
|
||||
_annotations_present &= ~nth_bit((int)id);
|
||||
}
|
||||
|
||||
// Report if the annotation is present.
|
||||
bool has_any_annotations() { return _annotations_present != 0; }
|
||||
bool has_annotation(ID id) { return (nth_bit((int)id) & _annotations_present) != 0; }
|
||||
bool has_any_annotations() const { return _annotations_present != 0; }
|
||||
bool has_annotation(ID id) const { return (nth_bit((int)id) & _annotations_present) != 0; }
|
||||
|
||||
void set_contended_group(u2 group) { _contended_group = group; }
|
||||
u2 contended_group() { return _contended_group; }
|
||||
u2 contended_group() const { return _contended_group; }
|
||||
|
||||
bool is_contended() { return has_annotation(_sun_misc_Contended); }
|
||||
bool is_contended() const { return has_annotation(_sun_misc_Contended); }
|
||||
|
||||
void set_stable(bool stable) { set_annotation(_field_Stable); }
|
||||
bool is_stable() const { return has_annotation(_field_Stable); }
|
||||
};
|
||||
|
||||
// This class also doubles as a holder for metadata cleanup.
|
||||
|
@ -270,6 +270,7 @@
|
||||
template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \
|
||||
template(java_lang_invoke_ForceInline_signature, "Ljava/lang/invoke/ForceInline;") \
|
||||
template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \
|
||||
template(sun_invoke_Stable_signature, "Lsun/invoke/Stable;") \
|
||||
template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \
|
||||
template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \
|
||||
template(java_lang_invoke_MagicLambdaImpl, "java/lang/invoke/MagicLambdaImpl") \
|
||||
|
@ -240,6 +240,14 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC {
|
||||
return (access_flags() & JVM_ACC_FIELD_INTERNAL) != 0;
|
||||
}
|
||||
|
||||
bool is_stable() const {
|
||||
return (access_flags() & JVM_ACC_FIELD_STABLE) != 0;
|
||||
}
|
||||
void set_stable(bool z) {
|
||||
if (z) _shorts[access_flags_offset] |= JVM_ACC_FIELD_STABLE;
|
||||
else _shorts[access_flags_offset] &= ~JVM_ACC_FIELD_STABLE;
|
||||
}
|
||||
|
||||
Symbol* lookup_symbol(int symbol_index) const {
|
||||
assert(is_internal(), "only internal fields");
|
||||
return vmSymbols::symbol_at((vmSymbols::SID)symbol_index);
|
||||
|
@ -448,6 +448,9 @@
|
||||
product(bool, EliminateAutoBox, true, \
|
||||
"Control optimizations for autobox elimination") \
|
||||
\
|
||||
experimental(bool, UseImplicitStableValues, false, \
|
||||
"Mark well-known stable fields as such (e.g. String.value)") \
|
||||
\
|
||||
product(intx, AutoBoxCacheMax, 128, \
|
||||
"Sets max value cached by the java.lang.Integer autobox cache") \
|
||||
\
|
||||
|
@ -1297,6 +1297,10 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const {
|
||||
|
||||
// Array pointers need some flattening
|
||||
const TypeAryPtr *ta = tj->isa_aryptr();
|
||||
if (ta && ta->is_stable()) {
|
||||
// Erase stability property for alias analysis.
|
||||
tj = ta = ta->cast_to_stable(false);
|
||||
}
|
||||
if( ta && is_known_inst ) {
|
||||
if ( offset != Type::OffsetBot &&
|
||||
offset > arrayOopDesc::length_offset_in_bytes() ) {
|
||||
@ -1497,6 +1501,7 @@ void Compile::AliasType::Init(int i, const TypePtr* at) {
|
||||
_index = i;
|
||||
_adr_type = at;
|
||||
_field = NULL;
|
||||
_element = NULL;
|
||||
_is_rewritable = true; // default
|
||||
const TypeOopPtr *atoop = (at != NULL) ? at->isa_oopptr() : NULL;
|
||||
if (atoop != NULL && atoop->is_known_instance()) {
|
||||
@ -1615,6 +1620,16 @@ Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_cr
|
||||
&& flat->is_instptr()->klass() == env()->Class_klass())
|
||||
alias_type(idx)->set_rewritable(false);
|
||||
}
|
||||
if (flat->isa_aryptr()) {
|
||||
#ifdef ASSERT
|
||||
const int header_size_min = arrayOopDesc::base_offset_in_bytes(T_BYTE);
|
||||
// (T_BYTE has the weakest alignment and size restrictions...)
|
||||
assert(flat->offset() < header_size_min, "array body reference must be OffsetBot");
|
||||
#endif
|
||||
if (flat->offset() == TypePtr::OffsetBot) {
|
||||
alias_type(idx)->set_element(flat->is_aryptr()->elem());
|
||||
}
|
||||
}
|
||||
if (flat->isa_klassptr()) {
|
||||
if (flat->offset() == in_bytes(Klass::super_check_offset_offset()))
|
||||
alias_type(idx)->set_rewritable(false);
|
||||
@ -1677,7 +1692,7 @@ Compile::AliasType* Compile::alias_type(ciField* field) {
|
||||
else
|
||||
t = TypeOopPtr::make_from_klass_raw(field->holder());
|
||||
AliasType* atp = alias_type(t->add_offset(field->offset_in_bytes()), field);
|
||||
assert(field->is_final() == !atp->is_rewritable(), "must get the rewritable bits correct");
|
||||
assert((field->is_final() || field->is_stable()) == !atp->is_rewritable(), "must get the rewritable bits correct");
|
||||
return atp;
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,7 @@ class Scope;
|
||||
class StartNode;
|
||||
class SafePointNode;
|
||||
class JVMState;
|
||||
class Type;
|
||||
class TypeData;
|
||||
class TypePtr;
|
||||
class TypeOopPtr;
|
||||
@ -119,6 +120,7 @@ class Compile : public Phase {
|
||||
int _index; // unique index, used with MergeMemNode
|
||||
const TypePtr* _adr_type; // normalized address type
|
||||
ciField* _field; // relevant instance field, or null if none
|
||||
const Type* _element; // relevant array element type, or null if none
|
||||
bool _is_rewritable; // false if the memory is write-once only
|
||||
int _general_index; // if this is type is an instance, the general
|
||||
// type that this is an instance of
|
||||
@ -129,6 +131,7 @@ class Compile : public Phase {
|
||||
int index() const { return _index; }
|
||||
const TypePtr* adr_type() const { return _adr_type; }
|
||||
ciField* field() const { return _field; }
|
||||
const Type* element() const { return _element; }
|
||||
bool is_rewritable() const { return _is_rewritable; }
|
||||
bool is_volatile() const { return (_field ? _field->is_volatile() : false); }
|
||||
int general_index() const { return (_general_index != 0) ? _general_index : _index; }
|
||||
@ -137,7 +140,14 @@ class Compile : public Phase {
|
||||
void set_field(ciField* f) {
|
||||
assert(!_field,"");
|
||||
_field = f;
|
||||
if (f->is_final()) _is_rewritable = false;
|
||||
if (f->is_final() || f->is_stable()) {
|
||||
// In the case of @Stable, multiple writes are possible but may be assumed to be no-ops.
|
||||
_is_rewritable = false;
|
||||
}
|
||||
}
|
||||
void set_element(const Type* e) {
|
||||
assert(_element == NULL, "");
|
||||
_element = e;
|
||||
}
|
||||
|
||||
void print_on(outputStream* st) PRODUCT_RETURN;
|
||||
|
@ -3825,8 +3825,13 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
TypeAry::make(TypeInt::CHAR,TypeInt::POS),
|
||||
ciTypeArrayKlass::make(T_CHAR), true, 0);
|
||||
int value_field_idx = C->get_alias_index(value_field_type);
|
||||
return make_load(ctrl, basic_plus_adr(str, str, value_offset),
|
||||
value_type, T_OBJECT, value_field_idx);
|
||||
Node* load = make_load(ctrl, basic_plus_adr(str, str, value_offset),
|
||||
value_type, T_OBJECT, value_field_idx);
|
||||
// String.value field is known to be @Stable.
|
||||
if (UseImplicitStableValues) {
|
||||
load = cast_array_to_stable(load, value_type);
|
||||
}
|
||||
return load;
|
||||
}
|
||||
|
||||
void GraphKit::store_String_offset(Node* ctrl, Node* str, Node* value) {
|
||||
@ -3844,9 +3849,6 @@ void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) {
|
||||
const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
|
||||
false, NULL, 0);
|
||||
const TypePtr* value_field_type = string_type->add_offset(value_offset);
|
||||
const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull,
|
||||
TypeAry::make(TypeInt::CHAR,TypeInt::POS),
|
||||
ciTypeArrayKlass::make(T_CHAR), true, 0);
|
||||
int value_field_idx = C->get_alias_index(value_field_type);
|
||||
store_to_memory(ctrl, basic_plus_adr(str, value_offset),
|
||||
value, T_OBJECT, value_field_idx);
|
||||
@ -3861,3 +3863,9 @@ void GraphKit::store_String_length(Node* ctrl, Node* str, Node* value) {
|
||||
store_to_memory(ctrl, basic_plus_adr(str, count_offset),
|
||||
value, T_INT, count_field_idx);
|
||||
}
|
||||
|
||||
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.
|
||||
return _gvn.transform(new(C) CastPPNode(ary, ary_type->cast_to_stable(true)));
|
||||
}
|
||||
|
@ -836,6 +836,9 @@ class GraphKit : public Phase {
|
||||
// Insert a loop predicate into the graph
|
||||
void add_predicate(int nargs = 0);
|
||||
void add_predicate_impl(Deoptimization::DeoptReason reason, int nargs);
|
||||
|
||||
// Produce new array node of stable type
|
||||
Node* cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type);
|
||||
};
|
||||
|
||||
// Helper class to support building of control flow branches. Upon
|
||||
|
@ -1280,6 +1280,11 @@ Node* LibraryCallKit::string_indexOf(Node* string_object, ciTypeArray* target_ar
|
||||
const TypeAry* target_array_type = TypeAry::make(TypeInt::CHAR, TypeInt::make(0, target_length, Type::WidenMin));
|
||||
const TypeAryPtr* target_type = TypeAryPtr::make(TypePtr::BotPTR, target_array_type, target_array->klass(), true, Type::OffsetBot);
|
||||
|
||||
// String.value field is known to be @Stable.
|
||||
if (UseImplicitStableValues) {
|
||||
target = cast_array_to_stable(target, target_type);
|
||||
}
|
||||
|
||||
IdealKit kit(this, false, true);
|
||||
#define __ kit.
|
||||
Node* zero = __ ConI(0);
|
||||
|
@ -962,6 +962,19 @@ uint LoadNode::hash() const {
|
||||
return (uintptr_t)in(Control) + (uintptr_t)in(Memory) + (uintptr_t)in(Address);
|
||||
}
|
||||
|
||||
static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp, bool eliminate_boxing) {
|
||||
if ((atp != NULL) && (atp->index() >= Compile::AliasIdxRaw)) {
|
||||
bool non_volatile = (atp->field() != NULL) && !atp->field()->is_volatile();
|
||||
bool is_stable_ary = FoldStableValues &&
|
||||
(tp != NULL) && (tp->isa_aryptr() != NULL) &&
|
||||
tp->isa_aryptr()->is_stable();
|
||||
|
||||
return (eliminate_boxing && non_volatile) || is_stable_ary;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------can_see_stored_value------------------------------
|
||||
// This routine exists to make sure this set of tests is done the same
|
||||
// everywhere. We need to make a coordinated change: first LoadNode::Ideal
|
||||
@ -976,11 +989,9 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
|
||||
const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr();
|
||||
Compile::AliasType* atp = (tp != NULL) ? phase->C->alias_type(tp) : NULL;
|
||||
// This is more general than load from boxing objects.
|
||||
if (phase->C->eliminate_boxing() && (atp != NULL) &&
|
||||
(atp->index() >= Compile::AliasIdxRaw) &&
|
||||
(atp->field() != NULL) && !atp->field()->is_volatile()) {
|
||||
if (skip_through_membars(atp, tp, phase->C->eliminate_boxing())) {
|
||||
uint alias_idx = atp->index();
|
||||
bool final = atp->field()->is_final();
|
||||
bool final = !atp->is_rewritable();
|
||||
Node* result = NULL;
|
||||
Node* current = st;
|
||||
// Skip through chains of MemBarNodes checking the MergeMems for
|
||||
@ -1015,7 +1026,6 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Loop around twice in the case Load -> Initialize -> Store.
|
||||
// (See PhaseIterGVN::add_users_to_worklist, which knows about this case.)
|
||||
for (int trip = 0; trip <= 1; trip++) {
|
||||
@ -1577,6 +1587,40 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Try to constant-fold a stable array element.
|
||||
static const Type* fold_stable_ary_elem(const TypeAryPtr* ary, int off, BasicType loadbt) {
|
||||
assert(ary->is_stable(), "array should be stable");
|
||||
|
||||
if (ary->const_oop() != NULL) {
|
||||
// Decode the results of GraphKit::array_element_address.
|
||||
ciArray* aobj = ary->const_oop()->as_array();
|
||||
ciConstant con = aobj->element_value_by_offset(off);
|
||||
|
||||
if (con.basic_type() != T_ILLEGAL && !con.is_null_or_zero()) {
|
||||
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( PhaseTransform *phase ) const {
|
||||
// Either input is TOP ==> the result is TOP
|
||||
@ -1591,8 +1635,31 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
|
||||
Compile* C = phase->C;
|
||||
|
||||
// Try to guess loaded type from pointer type
|
||||
if (tp->base() == Type::AryPtr) {
|
||||
const Type *t = tp->is_aryptr()->elem();
|
||||
if (tp->isa_aryptr()) {
|
||||
const TypeAryPtr* ary = tp->is_aryptr();
|
||||
const Type *t = ary->elem();
|
||||
|
||||
// Determine whether the reference is beyond the header or not, by comparing
|
||||
// the offset against the offset of the start of the array's data.
|
||||
// Different array types begin at slightly different offsets (12 vs. 16).
|
||||
// We choose T_BYTE as an example base type that is least restrictive
|
||||
// as to alignment, which will therefore produce the smallest
|
||||
// possible base offset.
|
||||
const int min_base_off = arrayOopDesc::base_offset_in_bytes(T_BYTE);
|
||||
const bool off_beyond_header = ((uint)off >= (uint)min_base_off);
|
||||
|
||||
// Try to constant-fold a stable array element.
|
||||
if (FoldStableValues && ary->is_stable()) {
|
||||
// Make sure the reference is not into the header
|
||||
if (off_beyond_header && off != Type::OffsetBot) {
|
||||
assert(adr->is_AddP() && adr->in(AddPNode::Offset)->is_Con(), "offset is a constant");
|
||||
const Type* con_type = fold_stable_ary_elem(ary, off, memory_type());
|
||||
if (con_type != NULL) {
|
||||
return con_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do this for integer types. There is only potential profit if
|
||||
// the element type t is lower than _type; that is, for int types, if _type is
|
||||
// more restrictive than t. This only happens here if one is short and the other
|
||||
@ -1613,14 +1680,7 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
|
||||
&& Opcode() != Op_LoadKlass && Opcode() != Op_LoadNKlass) {
|
||||
// t might actually be lower than _type, if _type is a unique
|
||||
// concrete subclass of abstract class t.
|
||||
// Make sure the reference is not into the header, by comparing
|
||||
// the offset against the offset of the start of the array's data.
|
||||
// Different array types begin at slightly different offsets (12 vs. 16).
|
||||
// We choose T_BYTE as an example base type that is least restrictive
|
||||
// as to alignment, which will therefore produce the smallest
|
||||
// possible base offset.
|
||||
const int min_base_off = arrayOopDesc::base_offset_in_bytes(T_BYTE);
|
||||
if ((uint)off >= (uint)min_base_off) { // is the offset beyond the header?
|
||||
if (off_beyond_header) { // is the offset beyond the header?
|
||||
const Type* jt = t->join(_type);
|
||||
// In any case, do not allow the join, per se, to empty out the type.
|
||||
if (jt->empty() && !t->empty()) {
|
||||
|
@ -518,7 +518,7 @@ class Parse : public GraphKit {
|
||||
|
||||
// loading from a constant field or the constant pool
|
||||
// returns false if push failed (non-perm field constants only, not ldcs)
|
||||
bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false);
|
||||
bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false, const Type* basic_type = NULL);
|
||||
|
||||
// implementation of object creation bytecodes
|
||||
void emit_guard_for_new(ciInstanceKlass* klass);
|
||||
|
@ -147,7 +147,15 @@ void Parse::do_field_access(bool is_get, bool is_field) {
|
||||
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 field
|
||||
// final or stable field
|
||||
const Type* stable_type = NULL;
|
||||
if (FoldStableValues && field->is_stable()) {
|
||||
stable_type = Type::get_const_type(field->type());
|
||||
if (field->type()->is_array_klass()) {
|
||||
int stable_dimension = field->type()->as_array_klass()->dimension();
|
||||
stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension);
|
||||
}
|
||||
}
|
||||
if (field->is_static()) {
|
||||
// final static field
|
||||
if (C->eliminate_boxing()) {
|
||||
@ -167,11 +175,10 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (push_constant(field->constant_value()))
|
||||
if (push_constant(field->constant_value(), false, false, stable_type))
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// final non-static field
|
||||
} else {
|
||||
// final or stable non-static field
|
||||
// Treat final non-static fields of trusted classes (classes in
|
||||
// java.lang.invoke and sun.invoke packages and subpackages) as
|
||||
// compile time constants.
|
||||
@ -179,8 +186,12 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr();
|
||||
ciObject* constant_oop = oop_ptr->const_oop();
|
||||
ciConstant constant = field->constant_value_of(constant_oop);
|
||||
if (push_constant(constant, true))
|
||||
return;
|
||||
if (FoldStableValues && field->is_stable() && constant.is_null_or_zero()) {
|
||||
// fall through to field load; the field is not yet initialized
|
||||
} else {
|
||||
if (push_constant(constant, true, false, stable_type))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -301,7 +312,8 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
// Note the presence of writes to final non-static fields, so that we
|
||||
// can insert a memory barrier later on to keep the writes from floating
|
||||
// out of the constructor.
|
||||
if (is_field && field->is_final()) {
|
||||
// Any method can write a @Stable field; insert memory barriers after those also.
|
||||
if (is_field && (field->is_final() || field->is_stable())) {
|
||||
set_wrote_final(true);
|
||||
// Preserve allocation ptr to create precedent edge to it in membar
|
||||
// generated on exit from constructor.
|
||||
@ -314,35 +326,21 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
}
|
||||
|
||||
|
||||
bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache) {
|
||||
|
||||
bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache, const Type* stable_type) {
|
||||
const Type* con_type = Type::make_from_constant(constant, require_constant, is_autobox_cache);
|
||||
switch (constant.basic_type()) {
|
||||
case T_BOOLEAN: push( intcon(constant.as_boolean()) ); break;
|
||||
case T_INT: push( intcon(constant.as_int()) ); break;
|
||||
case T_CHAR: push( intcon(constant.as_char()) ); break;
|
||||
case T_BYTE: push( intcon(constant.as_byte()) ); break;
|
||||
case T_SHORT: push( intcon(constant.as_short()) ); break;
|
||||
case T_FLOAT: push( makecon(TypeF::make(constant.as_float())) ); break;
|
||||
case T_DOUBLE: push_pair( makecon(TypeD::make(constant.as_double())) ); break;
|
||||
case T_LONG: push_pair( longcon(constant.as_long()) ); break;
|
||||
case T_ARRAY:
|
||||
case T_OBJECT: {
|
||||
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()) {
|
||||
push( zerocon(T_OBJECT) );
|
||||
break;
|
||||
} else if (require_constant || oop_constant->should_be_constant()) {
|
||||
push( makecon(TypeOopPtr::make_from_constant(oop_constant, require_constant, is_autobox_cache)) );
|
||||
break;
|
||||
} else {
|
||||
// we cannot inline the oop, but we can use it later to narrow a type
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case T_ILLEGAL: {
|
||||
if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr())
|
||||
con_type = con_type->join(stable_type);
|
||||
break;
|
||||
|
||||
case T_ILLEGAL:
|
||||
// Invalid ciConstant returned due to OutOfMemoryError in the CI
|
||||
assert(C->env()->failing(), "otherwise should not see this");
|
||||
// These always occur because of object types; we are going to
|
||||
@ -350,17 +348,16 @@ bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_au
|
||||
push( zerocon(T_OBJECT) );
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
return false;
|
||||
}
|
||||
|
||||
// success
|
||||
if (con_type == NULL)
|
||||
// we cannot inline the oop, but we can use it later to narrow a type
|
||||
return false;
|
||||
|
||||
push_node(constant.basic_type(), makecon(con_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
void Parse::do_anewarray() {
|
||||
bool will_link;
|
||||
|
@ -189,6 +189,38 @@ const Type* Type::get_typeflow_type(ciType* type) {
|
||||
}
|
||||
|
||||
|
||||
//-----------------------make_from_constant------------------------------------
|
||||
const Type* Type::make_from_constant(ciConstant constant,
|
||||
bool require_constant, 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, is_autobox_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fall through to failure
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------make-------------------------------------------
|
||||
// Create a simple Type, with default empty symbol sets. Then hashcons it
|
||||
// and look for an existing copy in the type dictionary.
|
||||
@ -1824,12 +1856,12 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) {
|
||||
}
|
||||
|
||||
//------------------------------make-------------------------------------------
|
||||
const TypeAry *TypeAry::make( const Type *elem, const TypeInt *size) {
|
||||
const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable) {
|
||||
if (UseCompressedOops && elem->isa_oopptr()) {
|
||||
elem = elem->make_narrowoop();
|
||||
}
|
||||
size = normalize_array_size(size);
|
||||
return (TypeAry*)(new TypeAry(elem,size))->hashcons();
|
||||
return (TypeAry*)(new TypeAry(elem,size,stable))->hashcons();
|
||||
}
|
||||
|
||||
//------------------------------meet-------------------------------------------
|
||||
@ -1850,7 +1882,8 @@ const Type *TypeAry::xmeet( const Type *t ) const {
|
||||
case Array: { // Meeting 2 arrays?
|
||||
const TypeAry *a = t->is_ary();
|
||||
return TypeAry::make(_elem->meet(a->_elem),
|
||||
_size->xmeet(a->_size)->is_int());
|
||||
_size->xmeet(a->_size)->is_int(),
|
||||
_stable & a->_stable);
|
||||
}
|
||||
case Top:
|
||||
break;
|
||||
@ -1863,7 +1896,7 @@ const Type *TypeAry::xmeet( const Type *t ) const {
|
||||
const Type *TypeAry::xdual() const {
|
||||
const TypeInt* size_dual = _size->dual()->is_int();
|
||||
size_dual = normalize_array_size(size_dual);
|
||||
return new TypeAry( _elem->dual(), size_dual);
|
||||
return new TypeAry(_elem->dual(), size_dual, !_stable);
|
||||
}
|
||||
|
||||
//------------------------------eq---------------------------------------------
|
||||
@ -1871,13 +1904,14 @@ const Type *TypeAry::xdual() const {
|
||||
bool TypeAry::eq( const Type *t ) const {
|
||||
const TypeAry *a = (const TypeAry*)t;
|
||||
return _elem == a->_elem &&
|
||||
_stable == a->_stable &&
|
||||
_size == a->_size;
|
||||
}
|
||||
|
||||
//------------------------------hash-------------------------------------------
|
||||
// Type-specific hashing function.
|
||||
int TypeAry::hash(void) const {
|
||||
return (intptr_t)_elem + (intptr_t)_size;
|
||||
return (intptr_t)_elem + (intptr_t)_size + (_stable ? 43 : 0);
|
||||
}
|
||||
|
||||
//----------------------interface_vs_oop---------------------------------------
|
||||
@ -1894,6 +1928,7 @@ bool TypeAry::interface_vs_oop(const Type *t) const {
|
||||
//------------------------------dump2------------------------------------------
|
||||
#ifndef PRODUCT
|
||||
void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const {
|
||||
if (_stable) st->print("stable:");
|
||||
_elem->dump2(d, depth, st);
|
||||
st->print("[");
|
||||
_size->dump2(d, depth, st);
|
||||
@ -3457,11 +3492,39 @@ const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const {
|
||||
assert(new_size != NULL, "");
|
||||
new_size = narrow_size_type(new_size);
|
||||
if (new_size == size()) return this;
|
||||
const TypeAry* new_ary = TypeAry::make(elem(), new_size);
|
||||
const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable());
|
||||
return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------cast_to_stable---------------------------------
|
||||
const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) const {
|
||||
if (stable_dimension <= 0 || (stable_dimension == 1 && stable == this->is_stable()))
|
||||
return this;
|
||||
|
||||
const Type* elem = this->elem();
|
||||
const TypePtr* elem_ptr = elem->make_ptr();
|
||||
|
||||
if (stable_dimension > 1 && elem_ptr != NULL && elem_ptr->isa_aryptr()) {
|
||||
// If this is widened from a narrow oop, TypeAry::make will re-narrow it.
|
||||
elem = elem_ptr = elem_ptr->is_aryptr()->cast_to_stable(stable, stable_dimension - 1);
|
||||
}
|
||||
|
||||
const TypeAry* new_ary = TypeAry::make(elem, size(), stable);
|
||||
|
||||
return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id);
|
||||
}
|
||||
|
||||
//-----------------------------stable_dimension--------------------------------
|
||||
int TypeAryPtr::stable_dimension() const {
|
||||
if (!is_stable()) return 0;
|
||||
int dim = 1;
|
||||
const TypePtr* elem_ptr = elem()->make_ptr();
|
||||
if (elem_ptr != NULL && elem_ptr->isa_aryptr())
|
||||
dim += elem_ptr->is_aryptr()->stable_dimension();
|
||||
return dim;
|
||||
}
|
||||
|
||||
//------------------------------eq---------------------------------------------
|
||||
// Structural equality check for Type representations
|
||||
bool TypeAryPtr::eq( const Type *t ) const {
|
||||
@ -3570,7 +3633,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
|
||||
// Something like byte[int+] meets char[int+].
|
||||
// This must fall to bottom, not (int[-128..65535])[int+].
|
||||
instance_id = InstanceBot;
|
||||
tary = TypeAry::make(Type::BOTTOM, tary->_size);
|
||||
tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
|
||||
}
|
||||
} else // Non integral arrays.
|
||||
// Must fall to bottom if exact klasses in upper lattice
|
||||
@ -3584,7 +3647,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
|
||||
(tap ->_klass_is_exact && !tap->klass()->is_subtype_of(klass())) ||
|
||||
// 'this' is exact and super or unrelated:
|
||||
(this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) {
|
||||
tary = TypeAry::make(Type::BOTTOM, tary->_size);
|
||||
tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
|
||||
return make( NotNull, NULL, tary, lazy_klass, false, off, InstanceBot );
|
||||
}
|
||||
|
||||
|
@ -372,6 +372,10 @@ public:
|
||||
// Mapping from CI type system to compiler type:
|
||||
static const Type* get_typeflow_type(ciType* type);
|
||||
|
||||
static const Type* make_from_constant(ciConstant constant,
|
||||
bool require_constant = false,
|
||||
bool is_autobox_cache = false);
|
||||
|
||||
private:
|
||||
// support arrays
|
||||
static const BasicType _basic_type[];
|
||||
@ -588,8 +592,8 @@ public:
|
||||
//------------------------------TypeAry----------------------------------------
|
||||
// Class of Array Types
|
||||
class TypeAry : public Type {
|
||||
TypeAry( const Type *elem, const TypeInt *size) : Type(Array),
|
||||
_elem(elem), _size(size) {}
|
||||
TypeAry(const Type* elem, const TypeInt* size, bool stable) : Type(Array),
|
||||
_elem(elem), _size(size), _stable(stable) {}
|
||||
public:
|
||||
virtual bool eq( const Type *t ) const;
|
||||
virtual int hash() const; // Type specific hashing
|
||||
@ -599,10 +603,11 @@ public:
|
||||
private:
|
||||
const Type *_elem; // Element type of array
|
||||
const TypeInt *_size; // Elements in array
|
||||
const bool _stable; // Are elements @Stable?
|
||||
friend class TypeAryPtr;
|
||||
|
||||
public:
|
||||
static const TypeAry *make( const Type *elem, const TypeInt *size);
|
||||
static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false);
|
||||
|
||||
virtual const Type *xmeet( const Type *t ) const;
|
||||
virtual const Type *xdual() const; // Compute dual right now.
|
||||
@ -988,6 +993,7 @@ public:
|
||||
const TypeAry* ary() const { return _ary; }
|
||||
const Type* elem() const { return _ary->_elem; }
|
||||
const TypeInt* size() const { return _ary->_size; }
|
||||
bool is_stable() const { return _ary->_stable; }
|
||||
|
||||
bool is_autobox_cache() const { return _is_autobox_cache; }
|
||||
|
||||
@ -1011,6 +1017,9 @@ public:
|
||||
virtual const Type *xmeet( const Type *t ) const;
|
||||
virtual const Type *xdual() const; // Compute dual right now.
|
||||
|
||||
const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const;
|
||||
int stable_dimension() const;
|
||||
|
||||
// Convenience common pre-built types.
|
||||
static const TypeAryPtr *RANGE;
|
||||
static const TypeAryPtr *OOPS;
|
||||
|
@ -3649,6 +3649,9 @@ class CommandLineFlags {
|
||||
experimental(bool, TrustFinalNonStaticFields, false, \
|
||||
"trust final non-static declarations for constant folding") \
|
||||
\
|
||||
experimental(bool, FoldStableValues, false, \
|
||||
"Private flag to control optimizations for stable variables") \
|
||||
\
|
||||
develop(bool, TraceInvokeDynamic, false, \
|
||||
"trace internal invoke dynamic operations") \
|
||||
\
|
||||
|
@ -78,11 +78,13 @@ enum {
|
||||
JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000, // field access is watched by JVMTI
|
||||
JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000, // field modification is watched by JVMTI
|
||||
JVM_ACC_FIELD_INTERNAL = 0x00000400, // internal field, same as JVM_ACC_ABSTRACT
|
||||
JVM_ACC_FIELD_STABLE = 0x00000020, // @Stable field, same as JVM_ACC_SYNCHRONIZED
|
||||
JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE = 0x00000800, // field has generic signature
|
||||
|
||||
JVM_ACC_FIELD_INTERNAL_FLAGS = JVM_ACC_FIELD_ACCESS_WATCHED |
|
||||
JVM_ACC_FIELD_MODIFICATION_WATCHED |
|
||||
JVM_ACC_FIELD_INTERNAL |
|
||||
JVM_ACC_FIELD_STABLE |
|
||||
JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE,
|
||||
|
||||
// flags accepted by set_field_flags()
|
||||
@ -148,6 +150,7 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC {
|
||||
{ return (_flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; }
|
||||
bool on_stack() const { return (_flags & JVM_ACC_ON_STACK) != 0; }
|
||||
bool is_internal() const { return (_flags & JVM_ACC_FIELD_INTERNAL) != 0; }
|
||||
bool is_stable() const { return (_flags & JVM_ACC_FIELD_STABLE) != 0; }
|
||||
bool field_has_generic_signature() const
|
||||
{ return (_flags & JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE) != 0; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user