8223320: [AOT] jck test api/javax_script/ScriptEngine/PutGet.html fails when test classes are AOTed

Materialization of primitive boxes should use caches

Reviewed-by: kvn, never
This commit is contained in:
Igor Veresov 2019-06-03 13:21:02 -07:00
parent 7d63888ac8
commit e47daab7b4
18 changed files with 666 additions and 26 deletions

View File

@ -319,3 +319,24 @@ bool AOTLoader::reconcile_dynamic_invoke(InstanceKlass* holder, int index, Metho
vmassert(success || thread->last_frame().sender(&map).is_deoptimized_frame(), "caller not deoptimized on failure");
return success;
}
// This should be called very early during startup before any of the AOTed methods that use boxes can deoptimize.
// Deoptimization machinery expects the caches to be present and populated.
void AOTLoader::initialize_box_caches(TRAPS) {
if (!UseAOT || libraries_count() == 0) {
return;
}
TraceTime timer("AOT initialization of box caches", TRACETIME_LOG(Info, aot, startuptime));
Symbol* box_classes[] = { java_lang_Boolean::symbol(), java_lang_Byte_ByteCache::symbol(),
java_lang_Short_ShortCache::symbol(), java_lang_Character_CharacterCache::symbol(),
java_lang_Integer_IntegerCache::symbol(), java_lang_Long_LongCache::symbol() };
for (unsigned i = 0; i < sizeof(box_classes) / sizeof(Symbol*); i++) {
Klass* k = SystemDictionary::resolve_or_fail(box_classes[i], true, CHECK);
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->is_not_initialized()) {
ik->initialize(CHECK);
}
}
}

View File

@ -64,6 +64,7 @@ public:
static void oops_do(OopClosure* f) NOT_AOT_RETURN;
static void metadata_do(MetadataClosure* f) NOT_AOT_RETURN;
static void mark_evol_dependent_methods(InstanceKlass* dependee) NOT_AOT_RETURN;
static void initialize_box_caches(TRAPS) NOT_AOT_RETURN;
NOT_PRODUCT( static void print_statistics() NOT_AOT_RETURN; )

View File

@ -4155,6 +4155,14 @@ int java_nio_Buffer::_limit_offset;
int java_util_concurrent_locks_AbstractOwnableSynchronizer::_owner_offset;
int reflect_ConstantPool::_oop_offset;
int reflect_UnsafeStaticFieldAccessorImpl::_base_offset;
int java_lang_Integer_IntegerCache::_static_cache_offset;
int java_lang_Long_LongCache::_static_cache_offset;
int java_lang_Character_CharacterCache::_static_cache_offset;
int java_lang_Short_ShortCache::_static_cache_offset;
int java_lang_Byte_ByteCache::_static_cache_offset;
int java_lang_Boolean::_static_TRUE_offset;
int java_lang_Boolean::_static_FALSE_offset;
#define STACKTRACEELEMENT_FIELDS_DO(macro) \
@ -4314,6 +4322,192 @@ void java_util_concurrent_locks_AbstractOwnableSynchronizer::serialize_offsets(S
}
#endif
#define INTEGER_CACHE_FIELDS_DO(macro) \
macro(_static_cache_offset, k, "cache", java_lang_Integer_array_signature, true)
void java_lang_Integer_IntegerCache::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
INTEGER_CACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
objArrayOop java_lang_Integer_IntegerCache::cache(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return objArrayOop(base->obj_field(_static_cache_offset));
}
Symbol* java_lang_Integer_IntegerCache::symbol() {
return vmSymbols::java_lang_Integer_IntegerCache();
}
#if INCLUDE_CDS
void java_lang_Integer_IntegerCache::serialize_offsets(SerializeClosure* f) {
INTEGER_CACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef INTEGER_CACHE_FIELDS_DO
jint java_lang_Integer::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.i;
}
#define LONG_CACHE_FIELDS_DO(macro) \
macro(_static_cache_offset, k, "cache", java_lang_Long_array_signature, true)
void java_lang_Long_LongCache::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
LONG_CACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
objArrayOop java_lang_Long_LongCache::cache(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return objArrayOop(base->obj_field(_static_cache_offset));
}
Symbol* java_lang_Long_LongCache::symbol() {
return vmSymbols::java_lang_Long_LongCache();
}
#if INCLUDE_CDS
void java_lang_Long_LongCache::serialize_offsets(SerializeClosure* f) {
LONG_CACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef LONG_CACHE_FIELDS_DO
jlong java_lang_Long::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.j;
}
#define CHARACTER_CACHE_FIELDS_DO(macro) \
macro(_static_cache_offset, k, "cache", java_lang_Character_array_signature, true)
void java_lang_Character_CharacterCache::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
CHARACTER_CACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
objArrayOop java_lang_Character_CharacterCache::cache(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return objArrayOop(base->obj_field(_static_cache_offset));
}
Symbol* java_lang_Character_CharacterCache::symbol() {
return vmSymbols::java_lang_Character_CharacterCache();
}
#if INCLUDE_CDS
void java_lang_Character_CharacterCache::serialize_offsets(SerializeClosure* f) {
CHARACTER_CACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef CHARACTER_CACHE_FIELDS_DO
jchar java_lang_Character::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.c;
}
#define SHORT_CACHE_FIELDS_DO(macro) \
macro(_static_cache_offset, k, "cache", java_lang_Short_array_signature, true)
void java_lang_Short_ShortCache::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
SHORT_CACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
objArrayOop java_lang_Short_ShortCache::cache(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return objArrayOop(base->obj_field(_static_cache_offset));
}
Symbol* java_lang_Short_ShortCache::symbol() {
return vmSymbols::java_lang_Short_ShortCache();
}
#if INCLUDE_CDS
void java_lang_Short_ShortCache::serialize_offsets(SerializeClosure* f) {
SHORT_CACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef SHORT_CACHE_FIELDS_DO
jshort java_lang_Short::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.s;
}
#define BYTE_CACHE_FIELDS_DO(macro) \
macro(_static_cache_offset, k, "cache", java_lang_Byte_array_signature, true)
void java_lang_Byte_ByteCache::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
BYTE_CACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
objArrayOop java_lang_Byte_ByteCache::cache(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return objArrayOop(base->obj_field(_static_cache_offset));
}
Symbol* java_lang_Byte_ByteCache::symbol() {
return vmSymbols::java_lang_Byte_ByteCache();
}
#if INCLUDE_CDS
void java_lang_Byte_ByteCache::serialize_offsets(SerializeClosure* f) {
BYTE_CACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef BYTE_CACHE_FIELDS_DO
jbyte java_lang_Byte::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.b;
}
#define BOOLEAN_FIELDS_DO(macro) \
macro(_static_TRUE_offset, k, "TRUE", java_lang_Boolean_signature, true); \
macro(_static_FALSE_offset, k, "FALSE", java_lang_Boolean_signature, true)
void java_lang_Boolean::compute_offsets(InstanceKlass *k) {
guarantee(k != NULL && k->is_initialized(), "must be loaded and initialized");
BOOLEAN_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
oop java_lang_Boolean::get_TRUE(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return base->obj_field(_static_TRUE_offset);
}
oop java_lang_Boolean::get_FALSE(InstanceKlass *ik) {
oop base = ik->static_field_base_raw();
return base->obj_field(_static_FALSE_offset);
}
Symbol* java_lang_Boolean::symbol() {
return vmSymbols::java_lang_Boolean();
}
#if INCLUDE_CDS
void java_lang_Boolean::serialize_offsets(SerializeClosure* f) {
BOOLEAN_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#undef BOOLEAN_CACHE_FIELDS_DO
jboolean java_lang_Boolean::value(oop obj) {
jvalue v;
java_lang_boxing_object::get_value(obj, &v);
return v.z;
}
static int member_offset(int hardcoded_offset) {
return (hardcoded_offset * heapOopSize) + instanceOopDesc::base_offset_in_bytes();
}

View File

@ -1497,6 +1497,94 @@ class jdk_internal_misc_UnsafeConstants : AllStatic {
static void serialize_offsets(SerializeClosure* f) { }
};
class java_lang_Integer : AllStatic {
public:
static jint value(oop obj);
};
class java_lang_Long : AllStatic {
public:
static jlong value(oop obj);
};
class java_lang_Character : AllStatic {
public:
static jchar value(oop obj);
};
class java_lang_Short : AllStatic {
public:
static jshort value(oop obj);
};
class java_lang_Byte : AllStatic {
public:
static jbyte value(oop obj);
};
class java_lang_Boolean : AllStatic {
private:
static int _static_TRUE_offset;
static int _static_FALSE_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static oop get_TRUE(InstanceKlass *k);
static oop get_FALSE(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
static jboolean value(oop obj);
};
class java_lang_Integer_IntegerCache : AllStatic {
private:
static int _static_cache_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static objArrayOop cache(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
class java_lang_Long_LongCache : AllStatic {
private:
static int _static_cache_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static objArrayOop cache(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
class java_lang_Character_CharacterCache : AllStatic {
private:
static int _static_cache_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static objArrayOop cache(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
class java_lang_Short_ShortCache : AllStatic {
private:
static int _static_cache_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static objArrayOop cache(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
class java_lang_Byte_ByteCache : AllStatic {
private:
static int _static_cache_offset;
public:
static Symbol* symbol();
static void compute_offsets(InstanceKlass* k);
static objArrayOop cache(InstanceKlass *k);
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
// Use to declare fields that need to be injected into Java classes
// for the JVM to use. The name_index and signature_index are
// declared in vmSymbols. The may_be_java flag is used to declare

View File

@ -442,6 +442,11 @@
template(getProtectionDomain_name, "getProtectionDomain") \
template(getProtectionDomain_signature, "(Ljava/security/CodeSource;)Ljava/security/ProtectionDomain;") \
template(java_lang_Integer_array_signature, "[Ljava/lang/Integer;") \
template(java_lang_Long_array_signature, "[Ljava/lang/Long;") \
template(java_lang_Character_array_signature, "[Ljava/lang/Character;") \
template(java_lang_Short_array_signature, "[Ljava/lang/Short;") \
template(java_lang_Byte_array_signature, "[Ljava/lang/Byte;") \
template(java_lang_Boolean_signature, "Ljava/lang/Boolean;") \
template(url_code_signer_array_void_signature, "(Ljava/net/URL;[Ljava/security/CodeSigner;)V") \
template(module_entry_name, "module_entry") \
template(resolved_references_name, "<resolved_references>") \

View File

@ -56,7 +56,7 @@ oop DebugInfoReadStream::read_oop() {
return o;
}
ScopeValue* DebugInfoReadStream::read_object_value() {
ScopeValue* DebugInfoReadStream::read_object_value(bool is_auto_box) {
int id = read_int();
#ifdef ASSERT
assert(_obj_pool != NULL, "object pool does not exist");
@ -64,7 +64,7 @@ ScopeValue* DebugInfoReadStream::read_object_value() {
assert(_obj_pool->at(i)->as_ObjectValue()->id() != id, "should not be read twice");
}
#endif
ObjectValue* result = new ObjectValue(id);
ObjectValue* result = is_auto_box ? new AutoBoxObjectValue(id) : new ObjectValue(id);
// Cache the object since an object field could reference it.
_obj_pool->push(result);
result->read_object(this);
@ -88,18 +88,20 @@ ScopeValue* DebugInfoReadStream::get_cached_object() {
enum { LOCATION_CODE = 0, CONSTANT_INT_CODE = 1, CONSTANT_OOP_CODE = 2,
CONSTANT_LONG_CODE = 3, CONSTANT_DOUBLE_CODE = 4,
OBJECT_CODE = 5, OBJECT_ID_CODE = 6 };
OBJECT_CODE = 5, OBJECT_ID_CODE = 6,
AUTO_BOX_OBJECT_CODE = 7 };
ScopeValue* ScopeValue::read_from(DebugInfoReadStream* stream) {
ScopeValue* result = NULL;
switch(stream->read_int()) {
case LOCATION_CODE: result = new LocationValue(stream); break;
case CONSTANT_INT_CODE: result = new ConstantIntValue(stream); break;
case CONSTANT_OOP_CODE: result = new ConstantOopReadValue(stream); break;
case CONSTANT_LONG_CODE: result = new ConstantLongValue(stream); break;
case CONSTANT_DOUBLE_CODE: result = new ConstantDoubleValue(stream); break;
case OBJECT_CODE: result = stream->read_object_value(); break;
case OBJECT_ID_CODE: result = stream->get_cached_object(); break;
case LOCATION_CODE: result = new LocationValue(stream); break;
case CONSTANT_INT_CODE: result = new ConstantIntValue(stream); break;
case CONSTANT_OOP_CODE: result = new ConstantOopReadValue(stream); break;
case CONSTANT_LONG_CODE: result = new ConstantLongValue(stream); break;
case CONSTANT_DOUBLE_CODE: result = new ConstantDoubleValue(stream); break;
case OBJECT_CODE: result = stream->read_object_value(false /*is_auto_box*/); break;
case AUTO_BOX_OBJECT_CODE: result = stream->read_object_value(true /*is_auto_box*/); break;
case OBJECT_ID_CODE: result = stream->get_cached_object(); break;
default: ShouldNotReachHere();
}
return result;
@ -142,7 +144,7 @@ void ObjectValue::write_on(DebugInfoWriteStream* stream) {
stream->write_int(_id);
} else {
_visited = true;
stream->write_int(OBJECT_CODE);
stream->write_int(is_auto_box() ? AUTO_BOX_OBJECT_CODE : OBJECT_CODE);
stream->write_int(_id);
_klass->write_on(stream);
int length = _field_values.length();
@ -154,7 +156,7 @@ void ObjectValue::write_on(DebugInfoWriteStream* stream) {
}
void ObjectValue::print_on(outputStream* st) const {
st->print("obj[%d]", _id);
st->print("%s[%d]", is_auto_box() ? "box_obj" : "obj", _id);
}
void ObjectValue::print_fields_on(outputStream* st) const {

View File

@ -49,6 +49,7 @@ class ScopeValue: public ResourceObj {
// Testers
virtual bool is_location() const { return false; }
virtual bool is_object() const { return false; }
virtual bool is_auto_box() const { return false; }
virtual bool is_constant_int() const { return false; }
virtual bool is_constant_double() const { return false; }
virtual bool is_constant_long() const { return false; }
@ -94,13 +95,12 @@ class LocationValue: public ScopeValue {
// An ObjectValue describes an object eliminated by escape analysis.
class ObjectValue: public ScopeValue {
private:
protected:
int _id;
ScopeValue* _klass;
GrowableArray<ScopeValue*> _field_values;
Handle _value;
bool _visited;
public:
ObjectValue(int id, ScopeValue* klass)
: _id(id)
@ -140,6 +140,16 @@ class ObjectValue: public ScopeValue {
void print_fields_on(outputStream* st) const;
};
class AutoBoxObjectValue : public ObjectValue {
bool _cached;
public:
bool is_auto_box() const { return true; }
bool is_cached() const { return _cached; }
void set_cached(bool cached) { _cached = cached; }
AutoBoxObjectValue(int id, ScopeValue* klass) : ObjectValue(id, klass), _cached(false) { }
AutoBoxObjectValue(int id) : ObjectValue(id), _cached(false) { }
};
// A ConstantIntValue describes a constant int; i.e., the corresponding logical entity
// is either a source constant or its computation has been constant-folded.
@ -280,7 +290,7 @@ class DebugInfoReadStream : public CompressedReadStream {
assert(o == NULL || o->is_metadata(), "meta data only");
return o;
}
ScopeValue* read_object_value();
ScopeValue* read_object_value(bool is_auto_box);
ScopeValue* get_cached_object();
// BCI encoding is mostly unsigned, but -1 is a distinguished value
int read_bci() { return read_int() + InvocationEntryBci; }

View File

@ -988,9 +988,11 @@ GrowableArray<ScopeValue*>* CodeInstaller::record_virtual_objects(JVMCIObject de
JVMCIObject value = JVMCIENV->get_object_at(virtualObjects, i);
int id = jvmci_env()->get_VirtualObject_id(value);
JVMCIObject type = jvmci_env()->get_VirtualObject_type(value);
bool is_auto_box = jvmci_env()->get_VirtualObject_isAutoBox(value);
Klass* klass = jvmci_env()->asKlass(type);
oop javaMirror = klass->java_mirror();
ObjectValue* sv = new ObjectValue(id, new ConstantOopWriteValue(JNIHandles::make_local(Thread::current(), javaMirror)));
ScopeValue *klass_sv = new ConstantOopWriteValue(JNIHandles::make_local(Thread::current(), javaMirror));
ObjectValue* sv = is_auto_box ? new AutoBoxObjectValue(id, klass_sv) : new ObjectValue(id, klass_sv);
if (id < 0 || id >= objects->length()) {
JVMCI_ERROR_NULL("virtual object id %d out of bounds", id);
}

View File

@ -1213,7 +1213,7 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
}
}
}
bool realloc_failures = Deoptimization::realloc_objects(thread, fst.current(), objects, CHECK_NULL);
bool realloc_failures = Deoptimization::realloc_objects(thread, fst.current(), fst.register_map(), objects, CHECK_NULL);
Deoptimization::reassign_fields(fst.current(), fst.register_map(), objects, realloc_failures, false);
realloc_called = true;
@ -1471,7 +1471,7 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
return;
}
bool realloc_failures = Deoptimization::realloc_objects(thread, fstAfterDeopt.current(), objects, CHECK);
bool realloc_failures = Deoptimization::realloc_objects(thread, fstAfterDeopt.current(), fstAfterDeopt.register_map(), objects, CHECK);
Deoptimization::reassign_fields(fstAfterDeopt.current(), fstAfterDeopt.register_map(), objects, realloc_failures, false);
for (int frame_index = 0; frame_index < virtualFrames->length(); frame_index++) {

View File

@ -309,6 +309,7 @@
end_class \
start_class(VirtualObject, jdk_vm_ci_code_VirtualObject) \
int_field(VirtualObject, id) \
boolean_field(VirtualObject, isAutoBox) \
object_field(VirtualObject, type, "Ljdk/vm/ci/meta/ResolvedJavaType;") \
objectarray_field(VirtualObject, values, "[Ljdk/vm/ci/meta/JavaValue;") \
objectarray_field(VirtualObject, slotKinds, "[Ljdk/vm/ci/meta/JavaKind;") \

View File

@ -50,7 +50,10 @@
#include "runtime/biasedLocking.hpp"
#include "runtime/compilationPolicy.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/safepointVerifiers.hpp"
@ -232,7 +235,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
}
if (objects != NULL) {
JRT_BLOCK
realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD);
realloc_failures = realloc_objects(thread, &deoptee, &map, objects, THREAD);
JRT_END
bool skip_internal = (cm != NULL) && !cm->is_compiled_by_jvmci();
reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
@ -810,8 +813,131 @@ void Deoptimization::deoptimize_all_marked() {
Deoptimization::DeoptAction Deoptimization::_unloaded_action
= Deoptimization::Action_reinterpret;
#if INCLUDE_JVMCI || INCLUDE_AOT
template<typename CacheType>
class BoxCacheBase : public CHeapObj<mtCompiler> {
protected:
static InstanceKlass* find_cache_klass(Symbol* klass_name, TRAPS) {
ResourceMark rm;
char* klass_name_str = klass_name->as_C_string();
Klass* k = SystemDictionary::find(klass_name, Handle(), Handle(), THREAD);
guarantee(k != NULL, "%s must be loaded", klass_name_str);
InstanceKlass* ik = InstanceKlass::cast(k);
guarantee(ik->is_initialized(), "%s must be initialized", klass_name_str);
CacheType::compute_offsets(ik);
return ik;
}
};
template<typename PrimitiveType, typename CacheType, typename BoxType> class BoxCache : public BoxCacheBase<CacheType> {
PrimitiveType _low;
PrimitiveType _high;
jobject _cache;
protected:
static BoxCache<PrimitiveType, CacheType, BoxType> *_singleton;
BoxCache(Thread* thread) {
InstanceKlass* ik = BoxCacheBase<CacheType>::find_cache_klass(CacheType::symbol(), thread);
objArrayOop cache = CacheType::cache(ik);
assert(cache->length() > 0, "Empty cache");
_low = BoxType::value(cache->obj_at(0));
_high = _low + cache->length() - 1;
_cache = JNIHandles::make_global(Handle(thread, cache));
}
~BoxCache() {
JNIHandles::destroy_global(_cache);
}
public:
static BoxCache<PrimitiveType, CacheType, BoxType>* singleton(Thread* thread) {
if (_singleton == NULL) {
BoxCache<PrimitiveType, CacheType, BoxType>* s = new BoxCache<PrimitiveType, CacheType, BoxType>(thread);
if (!Atomic::replace_if_null(s, &_singleton)) {
delete s;
}
}
return _singleton;
}
oop lookup(PrimitiveType value) {
if (_low <= value && value <= _high) {
int offset = value - _low;
return objArrayOop(JNIHandles::resolve_non_null(_cache))->obj_at(offset);
}
return NULL;
}
};
typedef BoxCache<jint, java_lang_Integer_IntegerCache, java_lang_Integer> IntegerBoxCache;
typedef BoxCache<jlong, java_lang_Long_LongCache, java_lang_Long> LongBoxCache;
typedef BoxCache<jchar, java_lang_Character_CharacterCache, java_lang_Character> CharacterBoxCache;
typedef BoxCache<jshort, java_lang_Short_ShortCache, java_lang_Short> ShortBoxCache;
typedef BoxCache<jbyte, java_lang_Byte_ByteCache, java_lang_Byte> ByteBoxCache;
template<> BoxCache<jint, java_lang_Integer_IntegerCache, java_lang_Integer>* BoxCache<jint, java_lang_Integer_IntegerCache, java_lang_Integer>::_singleton = NULL;
template<> BoxCache<jlong, java_lang_Long_LongCache, java_lang_Long>* BoxCache<jlong, java_lang_Long_LongCache, java_lang_Long>::_singleton = NULL;
template<> BoxCache<jchar, java_lang_Character_CharacterCache, java_lang_Character>* BoxCache<jchar, java_lang_Character_CharacterCache, java_lang_Character>::_singleton = NULL;
template<> BoxCache<jshort, java_lang_Short_ShortCache, java_lang_Short>* BoxCache<jshort, java_lang_Short_ShortCache, java_lang_Short>::_singleton = NULL;
template<> BoxCache<jbyte, java_lang_Byte_ByteCache, java_lang_Byte>* BoxCache<jbyte, java_lang_Byte_ByteCache, java_lang_Byte>::_singleton = NULL;
class BooleanBoxCache : public BoxCacheBase<java_lang_Boolean> {
jobject _true_cache;
jobject _false_cache;
protected:
static BooleanBoxCache *_singleton;
BooleanBoxCache(Thread *thread) {
InstanceKlass* ik = find_cache_klass(java_lang_Boolean::symbol(), thread);
_true_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_TRUE(ik)));
_false_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_FALSE(ik)));
}
~BooleanBoxCache() {
JNIHandles::destroy_global(_true_cache);
JNIHandles::destroy_global(_false_cache);
}
public:
static BooleanBoxCache* singleton(Thread* thread) {
if (_singleton == NULL) {
BooleanBoxCache* s = new BooleanBoxCache(thread);
if (!Atomic::replace_if_null(s, &_singleton)) {
delete s;
}
}
return _singleton;
}
oop lookup(jboolean value) {
if (value != 0) {
return JNIHandles::resolve_non_null(_true_cache);
}
return JNIHandles::resolve_non_null(_false_cache);
}
};
BooleanBoxCache* BooleanBoxCache::_singleton = NULL;
oop Deoptimization::get_cached_box(AutoBoxObjectValue* bv, frame* fr, RegisterMap* reg_map, TRAPS) {
Klass* k = java_lang_Class::as_Klass(bv->klass()->as_ConstantOopReadValue()->value()());
BasicType box_type = SystemDictionary::box_klass_type(k);
if (box_type != T_OBJECT) {
StackValue* value = StackValue::create_stack_value(fr, reg_map, bv->field_at(0));
switch(box_type) {
case T_INT: return IntegerBoxCache::singleton(THREAD)->lookup(value->get_int());
case T_LONG: {
StackValue* low = StackValue::create_stack_value(fr, reg_map, bv->field_at(1));
jlong res = (jlong)low->get_int();
return LongBoxCache::singleton(THREAD)->lookup(res);
}
case T_CHAR: return CharacterBoxCache::singleton(THREAD)->lookup(value->get_int());
case T_SHORT: return ShortBoxCache::singleton(THREAD)->lookup(value->get_int());
case T_BYTE: return ByteBoxCache::singleton(THREAD)->lookup(value->get_int());
case T_BOOLEAN: return BooleanBoxCache::singleton(THREAD)->lookup(value->get_int());
default:;
}
}
return NULL;
}
#endif // INCLUDE_JVMCI || INCLUDE_AOT
#if COMPILER2_OR_JVMCI
bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS) {
bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, TRAPS) {
Handle pending_exception(THREAD, thread->pending_exception());
const char* exception_file = thread->exception_file();
int exception_line = thread->exception_line();
@ -827,8 +953,21 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra
oop obj = NULL;
if (k->is_instance_klass()) {
#if INCLUDE_JVMCI || INCLUDE_AOT
CompiledMethod* cm = fr->cb()->as_compiled_method_or_null();
if (cm->is_compiled_by_jvmci() && sv->is_auto_box()) {
AutoBoxObjectValue* abv = (AutoBoxObjectValue*) sv;
obj = get_cached_box(abv, fr, reg_map, THREAD);
if (obj != NULL) {
// Set the flag to indicate the box came from a cache, so that we can skip the field reassignment for it.
abv->set_cached(true);
}
}
#endif // INCLUDE_JVMCI || INCLUDE_AOT
InstanceKlass* ik = InstanceKlass::cast(k);
obj = ik->allocate_instance(THREAD);
if (obj == NULL) {
obj = ik->allocate_instance(THREAD);
}
} else if (k->is_typeArray_klass()) {
TypeArrayKlass* ak = TypeArrayKlass::cast(k);
assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length");
@ -1101,7 +1240,12 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr
if (obj.is_null()) {
continue;
}
#if INCLUDE_JVMCI || INCLUDE_AOT
// Don't reassign fields of boxes that came from a cache. Caches may be in CDS.
if (sv->is_auto_box() && ((AutoBoxObjectValue*) sv)->is_cached()) {
continue;
}
#endif // INCLUDE_JVMCI || INCLUDE_AOT
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
reassign_fields_by_klass(ik, fr, reg_map, sv, 0, obj(), skip_internal);

View File

@ -33,6 +33,7 @@ class vframeArray;
class MonitorInfo;
class MonitorValue;
class ObjectValue;
class AutoBoxObjectValue;
class ScopeValue;
class compiledVFrame;
@ -153,6 +154,7 @@ class Deoptimization : AllStatic {
#if INCLUDE_JVMCI
static address deoptimize_for_missing_exception_handler(CompiledMethod* cm);
static oop get_cached_box(AutoBoxObjectValue* bv, frame* fr, RegisterMap* reg_map, TRAPS);
#endif
private:
@ -169,7 +171,7 @@ class Deoptimization : AllStatic {
JVMCI_ONLY(public:)
// Support for restoring non-escaping objects
static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS);
static bool realloc_objects(JavaThread* thread, frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, TRAPS);
static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type);
static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj);
static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal);

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "jvm.h"
#include "aot/aotLoader.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/moduleEntry.hpp"
@ -3650,6 +3651,9 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK);
initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK);
initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK);
// Eager box cache initialization only if AOT is on and any library is loaded.
AOTLoader::initialize_box_caches(CHECK);
}
void Threads::initialize_jsr292_core_classes(TRAPS) {
@ -3912,6 +3916,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
Chunk::start_chunk_pool_cleaner_task();
}
// initialize compiler(s)
#if defined(COMPILER1) || COMPILER2_OR_JVMCI
#if INCLUDE_JVMCI

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -43,6 +43,7 @@ public final class VirtualObject implements JavaValue {
private JavaValue[] values;
private JavaKind[] slotKinds;
private final int id;
private boolean isAutoBox;
/**
* Creates a new {@link VirtualObject} for the given type, with the given fields. If
@ -58,12 +59,33 @@ public final class VirtualObject implements JavaValue {
* @return a new {@link VirtualObject} instance.
*/
public static VirtualObject get(ResolvedJavaType type, int id) {
return new VirtualObject(type, id);
return new VirtualObject(type, id, false);
}
private VirtualObject(ResolvedJavaType type, int id) {
/**
* Creates a new {@link VirtualObject} for the given type, with the given fields. If
* {@code type} is an instance class then {@code values} provides the values for the fields
* returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If
* {@code type} is an array then the length of the values array determines the reallocated array
* length.
*
* @param type the type of the object whose allocation was removed during compilation. This can
* be either an instance of an array type.
* @param id a unique id that identifies the object within the debug information for one
* position in the compiled code.
* @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and
* that it possibly needs to be obtained for the box cache instead of creating
* a new instance.
* @return a new {@link VirtualObject} instance.
*/
public static VirtualObject get(ResolvedJavaType type, int id, boolean isAutoBox) {
return new VirtualObject(type, id, isAutoBox);
}
private VirtualObject(ResolvedJavaType type, int id, boolean isAutoBox) {
this.type = type;
this.id = id;
this.isAutoBox = isAutoBox;
}
private static StringBuilder appendValue(StringBuilder buf, JavaValue value, Set<VirtualObject> visited) {
@ -143,6 +165,23 @@ public final class VirtualObject implements JavaValue {
return id;
}
/**
* Returns true if the object is a box. For boxes the deoptimization would check if the value of
* the box is in the cache range and try to return a cached object.
*/
public boolean isAutoBox() {
return isAutoBox;
}
/**
* Sets the value of the box flag.
* @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and that
* it possibly needs to be obtained for the box cache instead of creating a new instance.
*/
public void setIsAutoBox(boolean isAutoBox) {
this.isAutoBox = isAutoBox;
}
/**
* Overwrites the current set of values with a new one.
*

View File

@ -187,6 +187,9 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
// initialized.
JVMCI.getRuntime();
}
// Make sure all the primitive box caches are populated (required to properly materialize boxed primitives
// during deoptimization).
Object[] boxCaches = { Boolean.valueOf(false), Byte.valueOf((byte)0), Short.valueOf((short) 0), Character.valueOf((char) 0), Integer.valueOf(0), Long.valueOf(0) };
}
}
return result;

View File

@ -43,7 +43,9 @@ import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.spi.NodeValueMap;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
import org.graalvm.compiler.nodes.virtual.VirtualBoxingNode;
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
import org.graalvm.compiler.serviceprovider.GraalServices;
import org.graalvm.compiler.virtual.nodes.MaterializedObjectState;
import org.graalvm.compiler.virtual.nodes.VirtualObjectState;
@ -154,6 +156,10 @@ public class DebugInfoBuilder {
}
assert checkValues(vobjValue.getType(), values, slotKinds);
vobjValue.setValues(values, slotKinds);
if (vobjNode instanceof VirtualBoxingNode) {
GraalServices.markVirtualObjectAsAutoBox(vobjValue);
}
}
virtualObjectsArray = new VirtualObject[virtualObjects.size()];

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.hotspot.test;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
public class BoxDeoptimizationTest extends GraalCompilerTest {
private static boolean isJDK13OrLater = JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 13;
public static void testInteger() {
Object[] values = {42, new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Integer.valueOf(42));
}
@Test
public void test1() {
Assume.assumeTrue(isJDK13OrLater);
test("testInteger");
}
public static void testLong() {
Object[] values = {42L, new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Long.valueOf(42));
}
@Test
public void test2() {
Assume.assumeTrue(isJDK13OrLater);
test("testLong");
}
public static void testChar() {
Object[] values = {'a', new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Character.valueOf('a'));
}
@Test
public void test3() {
Assume.assumeTrue(isJDK13OrLater);
test("testChar");
}
public static void testShort() {
Object[] values = {(short) 42, new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Short.valueOf((short) 42));
}
@Test
public void test4() {
Assume.assumeTrue(isJDK13OrLater);
test("testShort");
}
public static void testByte() {
Object[] values = {(byte) 42, new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Byte.valueOf((byte) 42));
}
@Test
public void test5() {
Assume.assumeTrue(isJDK13OrLater);
test("testByte");
}
public static void testBoolean() {
Object[] values = {true, new Exception()};
GraalDirectives.deoptimize();
Assert.assertSame(values[0], Boolean.valueOf(true));
}
@Test
public void test6() {
Assume.assumeTrue(isJDK13OrLater);
test("testBoolean");
}
}

View File

@ -42,6 +42,7 @@ import java.util.function.Supplier;
import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.code.VirtualObject;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
@ -519,4 +520,12 @@ public final class GraalServices {
public static double fma(double a, double b, double c) {
return Math.fma(a, b, c);
}
/**
* Set the flag in the {@link VirtualObject} that indicates that it is a boxed primitive that
* was produced as a result of a call to a {@code valueOf} method.
*/
public static void markVirtualObjectAsAutoBox(VirtualObject virtualObject) {
virtualObject.setIsAutoBox(true);
}
}