8223320: [AOT] jck test api/javax_script/ScriptEngine/PutGet.html fails when test classes are AOTed
Summary: Materialization of primitive boxes should use caches
Reviewed-by: kvn, never
--- a/src/hotspot/share/aot/aotLoader.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/aot/aotLoader.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -319,3 +319,24 @@
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);
+ }
+ }
+}
--- a/src/hotspot/share/aot/aotLoader.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/aot/aotLoader.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -64,6 +64,7 @@
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; )
--- a/src/hotspot/share/classfile/javaClasses.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/classfile/javaClasses.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -4155,6 +4155,14 @@
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 @@
}
#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();
}
--- a/src/hotspot/share/classfile/javaClasses.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/classfile/javaClasses.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -1497,6 +1497,94 @@
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
--- a/src/hotspot/share/classfile/vmSymbols.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -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>") \
--- a/src/hotspot/share/code/debugInfo.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/code/debugInfo.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -56,7 +56,7 @@
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 @@
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 @@
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 @@
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::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 {
--- a/src/hotspot/share/code/debugInfo.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/code/debugInfo.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -49,6 +49,7 @@
// 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 @@
// 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 @@
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 @@
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; }
--- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -988,9 +988,11 @@
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);
}
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -1213,7 +1213,7 @@
}
}
}
- 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 @@
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++) {
--- a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -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;") \
--- a/src/hotspot/share/runtime/deoptimization.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/runtime/deoptimization.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -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 @@
}
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 @@
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 @@
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 @@
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);
--- a/src/hotspot/share/runtime/deoptimization.hpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/runtime/deoptimization.hpp Mon Jun 03 13:21:02 2019 -0700
@@ -33,6 +33,7 @@
class MonitorInfo;
class MonitorValue;
class ObjectValue;
+class AutoBoxObjectValue;
class ScopeValue;
class compiledVFrame;
@@ -153,6 +154,7 @@
#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 @@
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);
--- a/src/hotspot/share/runtime/thread.cpp Mon Jun 03 21:28:45 2019 +0200
+++ b/src/hotspot/share/runtime/thread.cpp Mon Jun 03 13:21:02 2019 -0700
@@ -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 @@
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 @@
Chunk::start_chunk_pool_cleaner_task();
}
+
// initialize compiler(s)
#if defined(COMPILER1) || COMPILER2_OR_JVMCI
#if INCLUDE_JVMCI
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/VirtualObject.java Mon Jun 03 21:28:45 2019 +0200
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/VirtualObject.java Mon Jun 03 13:21:02 2019 -0700
@@ -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 @@
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 @@
* @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) {
@@ -144,6 +166,23 @@
}
/**
+ * 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.
*
* @param values an array containing all the values to be stored into the object when it is
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java Mon Jun 03 21:28:45 2019 +0200
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java Mon Jun 03 13:21:02 2019 -0700
@@ -187,6 +187,9 @@
// 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;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java Mon Jun 03 21:28:45 2019 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java Mon Jun 03 13:21:02 2019 -0700
@@ -43,7 +43,9 @@
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 @@
}
assert checkValues(vobjValue.getType(), values, slotKinds);
vobjValue.setValues(values, slotKinds);
+
+ if (vobjNode instanceof VirtualBoxingNode) {
+ GraalServices.markVirtualObjectAsAutoBox(vobjValue);
+ }
}
virtualObjectsArray = new VirtualObject[virtualObjects.size()];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/BoxDeoptimizationTest.java Mon Jun 03 13:21:02 2019 -0700
@@ -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");
+ }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java Mon Jun 03 21:28:45 2019 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java Mon Jun 03 13:21:02 2019 -0700
@@ -42,6 +42,7 @@
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 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);
+ }
}