8208686: [AOT] JVMTI ResourceExhausted event repeated for same allocation
Reviewed-by: never, kvn, sspitsyn
--- a/src/hotspot/share/aot/aotCodeHeap.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -436,14 +436,19 @@
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_instance", address, JVMCIRuntime::new_instance);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_array", address, JVMCIRuntime::new_array);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_multi_array", address, JVMCIRuntime::new_multi_array);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_dynamic_new_instance", address, JVMCIRuntime::dynamic_new_instance);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_dynamic_new_array", address, JVMCIRuntime::dynamic_new_array);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_instance_or_null", address, JVMCIRuntime::new_instance_or_null);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_array_or_null", address, JVMCIRuntime::new_array_or_null);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_new_multi_array_or_null", address, JVMCIRuntime::new_multi_array_or_null);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_dynamic_new_instance_or_null", address, JVMCIRuntime::dynamic_new_instance_or_null);
+ SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_dynamic_new_array_or_null", address, JVMCIRuntime::dynamic_new_array_or_null);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_validate_object", address, JVMCIRuntime::validate_object);
#if INCLUDE_G1GC
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_write_barrier_pre", address, JVMCIRuntime::write_barrier_pre);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_write_barrier_post", address, JVMCIRuntime::write_barrier_post);
#endif
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_identity_hash_code", address, JVMCIRuntime::identity_hash_code);
- SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_dynamic_new_instance", address, JVMCIRuntime::dynamic_new_instance);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_thread_is_interrupted", address, JVMCIRuntime::thread_is_interrupted);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_exception_handler_for_pc", address, JVMCIRuntime::exception_handler_for_pc);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_test_deoptimize_call_int", address, JVMCIRuntime::test_deoptimize_call_int);
--- a/src/hotspot/share/gc/shared/memAllocator.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/gc/shared/memAllocator.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -120,27 +120,22 @@
return false;
}
- if (!_overhead_limit_exceeded) {
+ const char* message = _overhead_limit_exceeded ? "GC overhead limit exceeded" : "Java heap space";
+ if (!THREAD->in_retryable_allocation()) {
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
- report_java_out_of_memory("Java heap space");
+ report_java_out_of_memory(message);
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
- "Java heap space");
+ message);
}
- THROW_OOP_(Universe::out_of_memory_error_java_heap(), true);
+ oop exception = _overhead_limit_exceeded ?
+ Universe::out_of_memory_error_gc_overhead_limit() :
+ Universe::out_of_memory_error_java_heap();
+ THROW_OOP_(exception, true);
} else {
- // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
- report_java_out_of_memory("GC overhead limit exceeded");
-
- if (JvmtiExport::should_post_resource_exhausted()) {
- JvmtiExport::post_resource_exhausted(
- JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
- "GC overhead limit exceeded");
- }
-
- THROW_OOP_(Universe::out_of_memory_error_gc_overhead_limit(), true);
+ THROW_OOP_(Universe::out_of_memory_error_retry(), true);
}
}
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -109,22 +109,72 @@
}
}
-JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_instance(JavaThread* thread, Klass* klass))
+// Manages a scope for a JVMCI runtime call that attempts a heap allocation.
+// If there is a pending exception upon closing the scope and the runtime
+// call is of the variety where allocation failure returns NULL without an
+// exception, the following action is taken:
+// 1. The pending exception is cleared
+// 2. NULL is written to JavaThread::_vm_result
+// 3. Checks that an OutOfMemoryError is Universe::out_of_memory_error_retry().
+class RetryableAllocationMark: public StackObj {
+ private:
+ JavaThread* _thread;
+ public:
+ RetryableAllocationMark(JavaThread* thread, bool activate) {
+ if (activate) {
+ assert(!thread->in_retryable_allocation(), "retryable allocation scope is non-reentrant");
+ _thread = thread;
+ _thread->set_in_retryable_allocation(true);
+ } else {
+ _thread = NULL;
+ }
+ }
+ ~RetryableAllocationMark() {
+ if (_thread != NULL) {
+ _thread->set_in_retryable_allocation(false);
+ JavaThread* THREAD = _thread;
+ if (HAS_PENDING_EXCEPTION) {
+ oop ex = PENDING_EXCEPTION;
+ CLEAR_PENDING_EXCEPTION;
+ oop retry_oome = Universe::out_of_memory_error_retry();
+ if (ex->is_a(retry_oome->klass()) && retry_oome != ex) {
+ ResourceMark rm;
+ fatal("Unexpected exception in scope of retryable allocation: " INTPTR_FORMAT " of type %s", p2i(ex), ex->klass()->external_name());
+ }
+ _thread->set_vm_result(NULL);
+ }
+ }
+ }
+};
+
+JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_instance_common(JavaThread* thread, Klass* klass, bool null_on_fail))
JRT_BLOCK;
assert(klass->is_klass(), "not a class");
Handle holder(THREAD, klass->klass_holder()); // keep the klass alive
InstanceKlass* ik = InstanceKlass::cast(klass);
- ik->check_valid_for_instantiation(true, CHECK);
- // make sure klass is initialized
- ik->initialize(CHECK);
- // allocate instance and return via TLS
- oop obj = ik->allocate_instance(CHECK);
- thread->set_vm_result(obj);
+ {
+ RetryableAllocationMark ram(thread, null_on_fail);
+ ik->check_valid_for_instantiation(true, CHECK);
+ oop obj;
+ if (null_on_fail) {
+ if (!ik->is_initialized()) {
+ // Cannot re-execute class initialization without side effects
+ // so return without attempting the initialization
+ return;
+ }
+ } else {
+ // make sure klass is initialized
+ ik->initialize(CHECK);
+ }
+ // allocate instance and return via TLS
+ obj = ik->allocate_instance(CHECK);
+ thread->set_vm_result(obj);
+ }
JRT_BLOCK_END;
SharedRuntime::on_slowpath_allocation_exit(thread);
JRT_END
-JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_array(JavaThread* thread, Klass* array_klass, jint length))
+JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_array_common(JavaThread* thread, Klass* array_klass, jint length, bool null_on_fail))
JRT_BLOCK;
// Note: no handle for klass needed since they are not used
// anymore after new_objArray() and no GC can happen before.
@@ -133,10 +183,12 @@
oop obj;
if (array_klass->is_typeArray_klass()) {
BasicType elt_type = TypeArrayKlass::cast(array_klass)->element_type();
+ RetryableAllocationMark ram(thread, null_on_fail);
obj = oopFactory::new_typeArray(elt_type, length, CHECK);
} else {
Handle holder(THREAD, array_klass->klass_holder()); // keep the klass alive
Klass* elem_klass = ObjArrayKlass::cast(array_klass)->element_klass();
+ RetryableAllocationMark ram(thread, null_on_fail);
obj = oopFactory::new_objArray(elem_klass, length, CHECK);
}
thread->set_vm_result(obj);
@@ -146,8 +198,12 @@
static int deopts = 0;
// Alternate between deoptimizing and raising an error (which will also cause a deopt)
if (deopts++ % 2 == 0) {
- ResourceMark rm(THREAD);
- THROW(vmSymbols::java_lang_OutOfMemoryError());
+ if (null_on_fail) {
+ return;
+ } else {
+ ResourceMark rm(THREAD);
+ THROW(vmSymbols::java_lang_OutOfMemoryError());
+ }
} else {
deopt_caller();
}
@@ -156,32 +212,43 @@
SharedRuntime::on_slowpath_allocation_exit(thread);
JRT_END
-JRT_ENTRY(void, JVMCIRuntime::new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims))
+JRT_ENTRY(void, JVMCIRuntime::new_multi_array_common(JavaThread* thread, Klass* klass, int rank, jint* dims, bool null_on_fail))
assert(klass->is_klass(), "not a class");
assert(rank >= 1, "rank must be nonzero");
Handle holder(THREAD, klass->klass_holder()); // keep the klass alive
+ RetryableAllocationMark ram(thread, null_on_fail);
oop obj = ArrayKlass::cast(klass)->multi_allocate(rank, dims, CHECK);
thread->set_vm_result(obj);
JRT_END
-JRT_ENTRY(void, JVMCIRuntime::dynamic_new_array(JavaThread* thread, oopDesc* element_mirror, jint length))
+JRT_ENTRY(void, JVMCIRuntime::dynamic_new_array_common(JavaThread* thread, oopDesc* element_mirror, jint length, bool null_on_fail))
+ RetryableAllocationMark ram(thread, null_on_fail);
oop obj = Reflection::reflect_new_array(element_mirror, length, CHECK);
thread->set_vm_result(obj);
JRT_END
-JRT_ENTRY(void, JVMCIRuntime::dynamic_new_instance(JavaThread* thread, oopDesc* type_mirror))
+JRT_ENTRY(void, JVMCIRuntime::dynamic_new_instance_common(JavaThread* thread, oopDesc* type_mirror, bool null_on_fail))
InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(type_mirror));
if (klass == NULL) {
ResourceMark rm(THREAD);
THROW(vmSymbols::java_lang_InstantiationException());
}
+ RetryableAllocationMark ram(thread, null_on_fail);
// Create new instance (the receiver)
klass->check_valid_for_instantiation(false, CHECK);
- // Make sure klass gets initialized
- klass->initialize(CHECK);
+ if (null_on_fail) {
+ if (!klass->is_initialized()) {
+ // Cannot re-execute class initialization without side effects
+ // so return without attempting the initialization
+ return;
+ }
+ } else {
+ // Make sure klass gets initialized
+ klass->initialize(CHECK);
+ }
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp Fri Oct 05 20:03:14 2018 +0200
@@ -121,13 +121,35 @@
static BasicType kindToBasicType(Handle kind, TRAPS);
- // The following routines are all called from compiled JVMCI code
+ static void new_instance_common(JavaThread* thread, Klass* klass, bool null_on_fail);
+ static void new_array_common(JavaThread* thread, Klass* klass, jint length, bool null_on_fail);
+ static void new_multi_array_common(JavaThread* thread, Klass* klass, int rank, jint* dims, bool null_on_fail);
+ static void dynamic_new_array_common(JavaThread* thread, oopDesc* element_mirror, jint length, bool null_on_fail);
+ static void dynamic_new_instance_common(JavaThread* thread, oopDesc* type_mirror, bool null_on_fail);
+
+ // The following routines are called from compiled JVMCI code
- static void new_instance(JavaThread* thread, Klass* klass);
- static void new_array(JavaThread* thread, Klass* klass, jint length);
- static void new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims);
- static void dynamic_new_array(JavaThread* thread, oopDesc* element_mirror, jint length);
- static void dynamic_new_instance(JavaThread* thread, oopDesc* type_mirror);
+ // When allocation fails, these stubs:
+ // 1. Exercise -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError handling and also
+ // post a JVMTI_EVENT_RESOURCE_EXHAUSTED event if the failure is an OutOfMemroyError
+ // 2. Return NULL with a pending exception.
+ // Compiled code must ensure these stubs are not called twice for the same allocation
+ // site due to the non-repeatable side effects in the case of OOME.
+ static void new_instance(JavaThread* thread, Klass* klass) { new_instance_common(thread, klass, false); }
+ static void new_array(JavaThread* thread, Klass* klass, jint length) { new_array_common(thread, klass, length, false); }
+ static void new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims) { new_multi_array_common(thread, klass, rank, dims, false); }
+ static void dynamic_new_array(JavaThread* thread, oopDesc* element_mirror, jint length) { dynamic_new_array_common(thread, element_mirror, length, false); }
+ static void dynamic_new_instance(JavaThread* thread, oopDesc* type_mirror) { dynamic_new_instance_common(thread, type_mirror, false); }
+
+ // When allocation fails, these stubs return NULL and have no pending exception. Compiled code
+ // can use these stubs if a failed allocation will be retried (e.g., by deoptimizing and
+ // re-executing in the interpreter).
+ static void new_instance_or_null(JavaThread* thread, Klass* klass) { new_instance_common(thread, klass, true); }
+ static void new_array_or_null(JavaThread* thread, Klass* klass, jint length) { new_array_common(thread, klass, length, true); }
+ static void new_multi_array_or_null(JavaThread* thread, Klass* klass, int rank, jint* dims) { new_multi_array_common(thread, klass, rank, dims, true); }
+ static void dynamic_new_array_or_null(JavaThread* thread, oopDesc* element_mirror, jint length) { dynamic_new_array_common(thread, element_mirror, length, true); }
+ static void dynamic_new_instance_or_null(JavaThread* thread, oopDesc* type_mirror) { dynamic_new_instance_common(thread, type_mirror, true); }
+
static jboolean thread_is_interrupted(JavaThread* thread, oopDesc* obj, jboolean clear_interrupted);
static void vm_message(jboolean vmError, jlong format, jlong v1, jlong v2, jlong v3);
static jint identity_hash_code(JavaThread* thread, oopDesc* obj);
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -622,6 +622,12 @@
declare_function(JVMCIRuntime::dynamic_new_array) \
declare_function(JVMCIRuntime::dynamic_new_instance) \
\
+ declare_function(JVMCIRuntime::new_instance_or_null) \
+ declare_function(JVMCIRuntime::new_array_or_null) \
+ declare_function(JVMCIRuntime::new_multi_array_or_null) \
+ declare_function(JVMCIRuntime::dynamic_new_array_or_null) \
+ declare_function(JVMCIRuntime::dynamic_new_instance_or_null) \
+ \
declare_function(JVMCIRuntime::thread_is_interrupted) \
declare_function(JVMCIRuntime::vm_message) \
declare_function(JVMCIRuntime::identity_hash_code) \
--- a/src/hotspot/share/memory/universe.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/memory/universe.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -113,6 +113,7 @@
oop Universe::_out_of_memory_error_array_size = NULL;
oop Universe::_out_of_memory_error_gc_overhead_limit = NULL;
oop Universe::_out_of_memory_error_realloc_objects = NULL;
+oop Universe::_out_of_memory_error_retry = NULL;
oop Universe::_delayed_stack_overflow_error_message = NULL;
objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL;
volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0;
@@ -195,6 +196,7 @@
f->do_oop((oop*)&_out_of_memory_error_array_size);
f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit);
f->do_oop((oop*)&_out_of_memory_error_realloc_objects);
+ f->do_oop((oop*)&_out_of_memory_error_retry);
f->do_oop((oop*)&_delayed_stack_overflow_error_message);
f->do_oop((oop*)&_preallocated_out_of_memory_error_array);
f->do_oop((oop*)&_null_ptr_exception_instance);
@@ -565,7 +567,8 @@
(!oopDesc::equals(throwable(), Universe::_out_of_memory_error_class_metaspace)) &&
(!oopDesc::equals(throwable(), Universe::_out_of_memory_error_array_size)) &&
(!oopDesc::equals(throwable(), Universe::_out_of_memory_error_gc_overhead_limit)) &&
- (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_realloc_objects)));
+ (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_realloc_objects)) &&
+ (!oopDesc::equals(throwable(), Universe::_out_of_memory_error_retry)));
}
@@ -974,6 +977,7 @@
Universe::_out_of_memory_error_gc_overhead_limit =
ik->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_realloc_objects = ik->allocate_instance(CHECK_false);
+ Universe::_out_of_memory_error_retry = ik->allocate_instance(CHECK_false);
// Setup preallocated cause message for delayed StackOverflowError
if (StackReservedPages > 0) {
@@ -1019,6 +1023,9 @@
msg = java_lang_String::create_from_str("Java heap space: failed reallocation of scalar replaced objects", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_realloc_objects, msg());
+ msg = java_lang_String::create_from_str("Java heap space: failed retryable allocation", CHECK_false);
+ java_lang_Throwable::set_message(Universe::_out_of_memory_error_retry, msg());
+
msg = java_lang_String::create_from_str("/ by zero", CHECK_false);
java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg());
--- a/src/hotspot/share/memory/universe.hpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/memory/universe.hpp Fri Oct 05 20:03:14 2018 +0200
@@ -148,6 +148,7 @@
static oop _out_of_memory_error_array_size;
static oop _out_of_memory_error_gc_overhead_limit;
static oop _out_of_memory_error_realloc_objects;
+ static oop _out_of_memory_error_retry;
// preallocated cause message for delayed StackOverflowError
static oop _delayed_stack_overflow_error_message;
@@ -363,6 +364,8 @@
static oop out_of_memory_error_array_size() { return gen_out_of_memory_error(_out_of_memory_error_array_size); }
static oop out_of_memory_error_gc_overhead_limit() { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit); }
static oop out_of_memory_error_realloc_objects() { return gen_out_of_memory_error(_out_of_memory_error_realloc_objects); }
+ // Throw default _out_of_memory_error_retry object as it will never propagate out of the VM
+ static oop out_of_memory_error_retry() { return _out_of_memory_error_retry; }
static oop delayed_stack_overflow_error_message() { return _delayed_stack_overflow_error_message; }
// The particular choice of collected heap.
--- a/src/hotspot/share/oops/arrayKlass.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/arrayKlass.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -130,14 +130,7 @@
}
objArrayOop ArrayKlass::allocate_arrayArray(int n, int length, TRAPS) {
- if (length < 0) {
- THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length));
- }
- if (length > arrayOopDesc::max_array_length(T_ARRAY)) {
- report_java_out_of_memory("Requested array size exceeds VM limit");
- JvmtiExport::post_array_size_exhausted();
- THROW_OOP_0(Universe::out_of_memory_error_array_size());
- }
+ check_array_allocation_length(length, arrayOopDesc::max_array_length(T_ARRAY), CHECK_0);
int size = objArrayOopDesc::object_size(length);
Klass* k = array_klass(n+dimension(), CHECK_0);
ArrayKlass* ak = ArrayKlass::cast(k);
--- a/src/hotspot/share/oops/instanceKlass.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/instanceKlass.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -1201,14 +1201,7 @@
}
objArrayOop InstanceKlass::allocate_objArray(int n, int length, TRAPS) {
- if (length < 0) {
- THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length));
- }
- if (length > arrayOopDesc::max_array_length(T_OBJECT)) {
- report_java_out_of_memory("Requested array size exceeds VM limit");
- JvmtiExport::post_array_size_exhausted();
- THROW_OOP_0(Universe::out_of_memory_error_array_size());
- }
+ check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL);
int size = objArrayOopDesc::object_size(length);
Klass* ak = array_klass(n, CHECK_NULL);
objArrayOop o = (objArrayOop)Universe::heap()->array_allocate(ak, size, length,
--- a/src/hotspot/share/oops/klass.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/klass.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -611,6 +611,20 @@
return NULL;
}
+void Klass::check_array_allocation_length(int length, int max_length, TRAPS) {
+ if (length > max_length) {
+ if (!THREAD->in_retryable_allocation()) {
+ report_java_out_of_memory("Requested array size exceeds VM limit");
+ JvmtiExport::post_array_size_exhausted();
+ THROW_OOP(Universe::out_of_memory_error_array_size());
+ } else {
+ THROW_OOP(Universe::out_of_memory_error_retry());
+ }
+ } else if (length < 0) {
+ THROW_MSG(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length));
+ }
+}
+
oop Klass::class_loader() const { return class_loader_data()->class_loader(); }
// In product mode, this function doesn't have virtual function calls so
--- a/src/hotspot/share/oops/klass.hpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/klass.hpp Fri Oct 05 20:03:14 2018 +0200
@@ -514,6 +514,9 @@
virtual Klass* array_klass_impl(bool or_null, int rank, TRAPS);
virtual Klass* array_klass_impl(bool or_null, TRAPS);
+ // Error handling when length > max_length or length < 0
+ static void check_array_allocation_length(int length, int max_length, TRAPS);
+
void set_vtable_length(int len) { _vtable_len= len; }
vtableEntry* start_of_vtable() const;
--- a/src/hotspot/share/oops/objArrayKlass.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/objArrayKlass.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -170,19 +170,10 @@
}
objArrayOop ObjArrayKlass::allocate(int length, TRAPS) {
- if (length >= 0) {
- if (length <= arrayOopDesc::max_array_length(T_OBJECT)) {
- int size = objArrayOopDesc::object_size(length);
- return (objArrayOop)Universe::heap()->array_allocate(this, size, length,
- /* do_zero */ true, THREAD);
- } else {
- report_java_out_of_memory("Requested array size exceeds VM limit");
- JvmtiExport::post_array_size_exhausted();
- THROW_OOP_0(Universe::out_of_memory_error_array_size());
- }
- } else {
- THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length));
- }
+ check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_0);
+ int size = objArrayOopDesc::object_size(length);
+ return (objArrayOop)Universe::heap()->array_allocate(this, size, length,
+ /* do_zero */ true, THREAD);
}
static int multi_alloc_counter = 0;
--- a/src/hotspot/share/oops/typeArrayKlass.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/oops/typeArrayKlass.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -99,19 +99,10 @@
typeArrayOop TypeArrayKlass::allocate_common(int length, bool do_zero, TRAPS) {
assert(log2_element_size() >= 0, "bad scale");
- if (length >= 0) {
- if (length <= max_length()) {
- size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
- return (typeArrayOop)Universe::heap()->array_allocate(this, (int)size, length,
- do_zero, CHECK_NULL);
- } else {
- report_java_out_of_memory("Requested array size exceeds VM limit");
- JvmtiExport::post_array_size_exhausted();
- THROW_OOP_0(Universe::out_of_memory_error_array_size());
- }
- } else {
- THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length));
- }
+ check_array_allocation_length(length, max_length(), CHECK_NULL);
+ size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
+ return (typeArrayOop)Universe::heap()->array_allocate(this, (int)size, length,
+ do_zero, CHECK_NULL);
}
oop TypeArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) {
--- a/src/hotspot/share/runtime/thread.cpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/runtime/thread.cpp Fri Oct 05 20:03:14 2018 +0200
@@ -1547,6 +1547,7 @@
_pending_failed_speculation = 0;
_pending_transfer_to_interpreter = false;
_adjusting_comp_level = false;
+ _in_retryable_allocation = false;
_jvmci._alternate_call_target = NULL;
assert(_jvmci._implicit_exception_pc == NULL, "must be");
if (JVMCICounterSize > 0) {
--- a/src/hotspot/share/runtime/thread.hpp Fri Oct 05 18:25:15 2018 +0100
+++ b/src/hotspot/share/runtime/thread.hpp Fri Oct 05 20:03:14 2018 +0200
@@ -281,6 +281,14 @@
void leave_signal_handler() { _num_nested_signal--; }
bool is_inside_signal_handler() const { return _num_nested_signal > 0; }
+ // Determines if a heap allocation failure will be retried
+ // (e.g., by deoptimizing and re-executing in the interpreter).
+ // In this case, the failed allocation must raise
+ // Universe::out_of_memory_error_retry() and omit side effects
+ // such as JVMTI events and handling -XX:+HeapDumpOnOutOfMemoryError
+ // and -XX:OnOutOfMemoryError.
+ virtual bool in_retryable_allocation() const { return false; }
+
#ifdef ASSERT
void set_suspendible_thread() {
_suspendible_thread = true;
@@ -1048,6 +1056,10 @@
// Guard for re-entrant call to JVMCIRuntime::adjust_comp_level
bool _adjusting_comp_level;
+ // True if in a runtime call from compiled code that will deoptimize
+ // and re-execute a failed heap allocation in the interpreter.
+ bool _in_retryable_allocation;
+
// An id of a speculation that JVMCI compiled code can use to further describe and
// uniquely identify the speculative optimization guarded by the uncommon trap
long _pending_failed_speculation;
@@ -1458,7 +1470,7 @@
#if INCLUDE_JVMCI
int pending_deoptimization() const { return _pending_deoptimization; }
- long pending_failed_speculation() const { return _pending_failed_speculation; }
+ long pending_failed_speculation() const { return _pending_failed_speculation; }
bool adjusting_comp_level() const { return _adjusting_comp_level; }
void set_adjusting_comp_level(bool b) { _adjusting_comp_level = b; }
bool has_pending_monitorenter() const { return _pending_monitorenter; }
@@ -1468,6 +1480,9 @@
void set_pending_transfer_to_interpreter(bool b) { _pending_transfer_to_interpreter = b; }
void set_jvmci_alternate_call_target(address a) { assert(_jvmci._alternate_call_target == NULL, "must be"); _jvmci._alternate_call_target = a; }
void set_jvmci_implicit_exception_pc(address a) { assert(_jvmci._implicit_exception_pc == NULL, "must be"); _jvmci._implicit_exception_pc = a; }
+
+ virtual bool in_retryable_allocation() const { return _in_retryable_allocation; }
+ void set_in_retryable_allocation(bool b) { _in_retryable_allocation = b; }
#endif // INCLUDE_JVMCI
// Exception handling for compiled methods
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java Fri Oct 05 18:25:15 2018 +0100
+++ b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java Fri Oct 05 20:03:14 2018 +0200
@@ -240,14 +240,20 @@
{"JVMCIRuntime::log_printf", "_aot_jvmci_runtime_log_printf"},
{"JVMCIRuntime::vm_message", "_aot_jvmci_runtime_vm_message"},
{"JVMCIRuntime::new_instance", "_aot_jvmci_runtime_new_instance"},
- {"JVMCIRuntime::log_primitive", "_aot_jvmci_runtime_log_primitive"},
+ {"JVMCIRuntime::new_array", "_aot_jvmci_runtime_new_array"},
{"JVMCIRuntime::new_multi_array", "_aot_jvmci_runtime_new_multi_array"},
+ {"JVMCIRuntime::dynamic_new_instance", "_aot_jvmci_runtime_dynamic_new_instance"},
+ {"JVMCIRuntime::dynamic_new_array", "_aot_jvmci_runtime_dynamic_new_array"},
+ {"JVMCIRuntime::new_instance_or_null", "_aot_jvmci_runtime_new_instance_or_null"},
+ {"JVMCIRuntime::new_array_or_null", "_aot_jvmci_runtime_new_array_or_null"},
+ {"JVMCIRuntime::new_multi_array_or_null", "_aot_jvmci_runtime_new_multi_array_or_null"},
+ {"JVMCIRuntime::dynamic_new_instance_or_null", "_aot_jvmci_runtime_dynamic_new_instance_or_null"},
+ {"JVMCIRuntime::dynamic_new_array_or_null", "_aot_jvmci_runtime_dynamic_new_array_or_null"},
+ {"JVMCIRuntime::log_primitive", "_aot_jvmci_runtime_log_primitive"},
{"JVMCIRuntime::validate_object", "_aot_jvmci_runtime_validate_object"},
- {"JVMCIRuntime::dynamic_new_array", "_aot_jvmci_runtime_dynamic_new_array"},
{"JVMCIRuntime::write_barrier_pre", "_aot_jvmci_runtime_write_barrier_pre"},
{"JVMCIRuntime::identity_hash_code", "_aot_jvmci_runtime_identity_hash_code"},
{"JVMCIRuntime::write_barrier_post", "_aot_jvmci_runtime_write_barrier_post"},
- {"JVMCIRuntime::dynamic_new_instance", "_aot_jvmci_runtime_dynamic_new_instance"},
{"JVMCIRuntime::thread_is_interrupted", "_aot_jvmci_runtime_thread_is_interrupted"},
{"JVMCIRuntime::exception_handler_for_pc", "_aot_jvmci_runtime_exception_handler_for_pc"},
{"JVMCIRuntime::test_deoptimize_call_int", "_aot_jvmci_runtime_test_deoptimize_call_int"},
@@ -256,8 +262,7 @@
{"JVMCIRuntime::throw_klass_external_name_exception", "_aot_jvmci_runtime_throw_klass_external_name_exception"},
{"JVMCIRuntime::throw_class_cast_exception", "_aot_jvmci_runtime_throw_class_cast_exception"},
- {"JVMCIRuntime::vm_error", "_aot_jvmci_runtime_vm_error"},
- {"JVMCIRuntime::new_array", "_aot_jvmci_runtime_new_array"}
+ {"JVMCIRuntime::vm_error", "_aot_jvmci_runtime_vm_error"}
};
//@formatter:on
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Fri Oct 05 18:25:15 2018 +0100
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Fri Oct 05 20:03:14 2018 +0200
@@ -648,11 +648,33 @@
public final long unsafeArraycopy = getFieldValue("StubRoutines::_unsafe_arraycopy", Long.class, "address");
public final long genericArraycopy = getFieldValue("StubRoutines::_generic_arraycopy", Long.class, "address");
+ // Allocation stubs that throw an exception when allocation fails
public final long newInstanceAddress = getAddress("JVMCIRuntime::new_instance");
public final long newArrayAddress = getAddress("JVMCIRuntime::new_array");
public final long newMultiArrayAddress = getAddress("JVMCIRuntime::new_multi_array");
- public final long dynamicNewArrayAddress = getAddress("JVMCIRuntime::dynamic_new_array");
- public final long dynamicNewInstanceAddress = getAddress("JVMCIRuntime::dynamic_new_instance");
+
+ // Allocation stubs that return null when allocation fails
+ public final long newInstanceOrNullAddress = getAddress("JVMCIRuntime::new_instance_or_null", 0L);
+ public final long newArrayOrNullAddress = getAddress("JVMCIRuntime::new_array_or_null", 0L);
+ public final long newMultiArrayOrNullAddress = getAddress("JVMCIRuntime::new_multi_array_or_null", 0L);
+
+ public boolean areNullAllocationStubsAvailable() {
+ return newInstanceOrNullAddress != 0L;
+ }
+
+ /**
+ * Checks that HotSpot implements all or none of the allocate-or-null stubs.
+ */
+ private boolean checkNullAllocationStubs() {
+ if (newInstanceOrNullAddress == 0L) {
+ assert newArrayOrNullAddress == 0L;
+ assert newMultiArrayOrNullAddress == 0L;
+ } else {
+ assert newArrayOrNullAddress != 0L;
+ assert newMultiArrayOrNullAddress != 0L;
+ }
+ return true;
+ }
public final long threadIsInterruptedAddress = getAddress("JVMCIRuntime::thread_is_interrupted");
public final long vmMessageAddress = getAddress("JVMCIRuntime::vm_message");
@@ -757,6 +779,7 @@
}
assert codeEntryAlignment > 0 : codeEntryAlignment;
+ assert checkNullAllocationStubs();
return true;
}
}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java Fri Oct 05 18:25:15 2018 +0100
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java Fri Oct 05 20:03:14 2018 +0200
@@ -279,21 +279,36 @@
public static final ForeignCallDescriptor VM_ERROR = new ForeignCallDescriptor("vm_error", void.class, Object.class, Object.class, long.class);
/**
- * New multi array stub call.
+ * New multi array stub that throws an {@link OutOfMemoryError} on allocation failure.
*/
public static final ForeignCallDescriptor NEW_MULTI_ARRAY = new ForeignCallDescriptor("new_multi_array", Object.class, KlassPointer.class, int.class, Word.class);
/**
- * New array stub.
+ * New multi array stub that will return null on allocation failure.
+ */
+ public static final ForeignCallDescriptor NEW_MULTI_ARRAY_OR_NULL = new ForeignCallDescriptor("new_multi_array_or_null", Object.class, KlassPointer.class, int.class, Word.class);
+
+ /**
+ * New array stub that throws an {@link OutOfMemoryError} on allocation failure.
*/
public static final ForeignCallDescriptor NEW_ARRAY = new ForeignCallDescriptor("new_array", Object.class, KlassPointer.class, int.class);
/**
- * New instance stub.
+ * New array stub that will return null on allocation failure.
+ */
+ public static final ForeignCallDescriptor NEW_ARRAY_OR_NULL = new ForeignCallDescriptor("new_array_or_null", Object.class, KlassPointer.class, int.class);
+
+ /**
+ * New instance stub that throws an {@link OutOfMemoryError} on allocation failure.
*/
public static final ForeignCallDescriptor NEW_INSTANCE = new ForeignCallDescriptor("new_instance", Object.class, KlassPointer.class);
/**
+ * New instance stub that will return null on allocation failure.
+ */
+ public static final ForeignCallDescriptor NEW_INSTANCE_OR_NULL = new ForeignCallDescriptor("new_instance_or_null", Object.class, KlassPointer.class);
+
+ /**
* @see ResolveConstantStubCall
*/
public static final ForeignCallDescriptor RESOLVE_STRING_BY_SYMBOL = new ForeignCallDescriptor("resolve_string_by_symbol", Object.class, Word.class, Word.class);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java Fri Oct 05 18:25:15 2018 +0100
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java Fri Oct 05 20:03:14 2018 +0200
@@ -45,8 +45,11 @@
import static org.graalvm.compiler.hotspot.HotSpotBackend.MULTIPLY_TO_LEN;
import static org.graalvm.compiler.hotspot.HotSpotBackend.MUL_ADD;
import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_ARRAY;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_ARRAY_OR_NULL;
import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_INSTANCE;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_INSTANCE_OR_NULL;
import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_MULTI_ARRAY;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_MULTI_ARRAY_OR_NULL;
import static org.graalvm.compiler.hotspot.HotSpotBackend.RESOLVE_DYNAMIC_INVOKE;
import static org.graalvm.compiler.hotspot.HotSpotBackend.RESOLVE_KLASS_BY_SYMBOL;
import static org.graalvm.compiler.hotspot.HotSpotBackend.RESOLVE_METHOD_BY_SYMBOL_AND_LOAD_COUNTERS;
@@ -78,8 +81,6 @@
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.TLAB_TOP_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.MonitorSnippets.MONITORENTER;
import static org.graalvm.compiler.hotspot.replacements.MonitorSnippets.MONITOREXIT;
-import static org.graalvm.compiler.hotspot.replacements.NewObjectSnippets.DYNAMIC_NEW_ARRAY;
-import static org.graalvm.compiler.hotspot.replacements.NewObjectSnippets.DYNAMIC_NEW_INSTANCE;
import static org.graalvm.compiler.hotspot.replacements.ThreadSubstitutions.THREAD_IS_INTERRUPTED;
import static org.graalvm.compiler.hotspot.replacements.WriteBarrierSnippets.G1WBPOSTCALL;
import static org.graalvm.compiler.hotspot.replacements.WriteBarrierSnippets.G1WBPRECALL;
@@ -288,6 +289,14 @@
linkForeignCall(options, providers, NEW_INSTANCE, c.newInstanceAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
linkForeignCall(options, providers, NEW_ARRAY, c.newArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
+ linkForeignCall(options, providers, NEW_MULTI_ARRAY, c.newMultiArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
+
+ if (c.areNullAllocationStubsAvailable()) {
+ linkForeignCall(options, providers, NEW_INSTANCE_OR_NULL, c.newInstanceOrNullAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
+ linkForeignCall(options, providers, NEW_ARRAY_OR_NULL, c.newArrayOrNullAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
+ linkForeignCall(options, providers, NEW_MULTI_ARRAY_OR_NULL, c.newMultiArrayOrNullAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
+ }
+
link(new ExceptionHandlerStub(options, providers, foreignCalls.get(EXCEPTION_HANDLER)));
link(new UnwindExceptionToCallerStub(options, providers, registerStubCall(UNWIND_EXCEPTION_TO_CALLER, SAFEPOINT, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any())));
link(new VerifyOopStub(options, providers, registerStubCall(VERIFY_OOP, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS)));
@@ -305,11 +314,8 @@
linkForeignCall(options, providers, REGISTER_FINALIZER, c.registerFinalizerAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any());
linkForeignCall(options, providers, MONITORENTER, c.monitorenterAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any());
linkForeignCall(options, providers, MONITOREXIT, c.monitorexitAddress, PREPEND_THREAD, STACK_INSPECTABLE_LEAF, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any());
- linkForeignCall(options, providers, NEW_MULTI_ARRAY, c.newMultiArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, TLAB_TOP_LOCATION, TLAB_END_LOCATION);
linkForeignCall(options, providers, NOTIFY, c.notifyAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any());
linkForeignCall(options, providers, NOTIFY_ALL, c.notifyAllAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE_ONLY_AFTER_EXCEPTION, any());
- linkForeignCall(options, providers, DYNAMIC_NEW_ARRAY, c.dynamicNewArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE);
- linkForeignCall(options, providers, DYNAMIC_NEW_INSTANCE, c.dynamicNewInstanceAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE);
linkForeignCall(options, providers, LOG_PRINTF, c.logPrintfAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
linkForeignCall(options, providers, LOG_OBJECT, c.logObjectAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
linkForeignCall(options, providers, LOG_PRIMITIVE, c.logPrimitiveAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java Fri Oct 05 18:25:15 2018 +0100
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java Fri Oct 05 20:03:14 2018 +0200
@@ -24,9 +24,17 @@
package org.graalvm.compiler.hotspot.replacements;
+import static jdk.vm.ci.meta.DeoptimizationAction.None;
+import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint;
import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
import static org.graalvm.compiler.core.common.calc.UnsignedMath.belowThan;
-import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
+import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_VMCONFIG;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_ARRAY;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_ARRAY_OR_NULL;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_INSTANCE;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_INSTANCE_OR_NULL;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_MULTI_ARRAY;
+import static org.graalvm.compiler.hotspot.HotSpotBackend.NEW_MULTI_ARRAY_OR_NULL;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_ARRAY_KLASS_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.HUB_WRITE_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.MARK_WORD_LOCATION;
@@ -70,6 +78,7 @@
import static org.graalvm.compiler.replacements.nodes.ExplodeLoopNode.explodeLoop;
import org.graalvm.compiler.api.replacements.Fold;
+import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
@@ -80,7 +89,6 @@
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
-import org.graalvm.compiler.hotspot.HotSpotBackend;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
import org.graalvm.compiler.hotspot.nodes.DimensionsNode;
@@ -126,8 +134,6 @@
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
-import jdk.vm.ci.meta.DeoptimizationAction;
-import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -220,14 +226,25 @@
if (counters != null && counters.stub != null) {
counters.stub.inc();
}
- result = newInstance(HotSpotBackend.NEW_INSTANCE, hub);
+ result = newInstanceStub(hub);
}
profileAllocation("instance", size, typeContext, options);
return verifyOop(result);
}
+ public static Object newInstanceStub(KlassPointer hub) {
+ if (useNullAllocationStubs(INJECTED_VMCONFIG)) {
+ return nonNullOrDeopt(newInstanceOrNull(NEW_INSTANCE_OR_NULL, hub));
+ } else {
+ return newInstance(NEW_INSTANCE, hub);
+ }
+ }
+
@NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
- public static native Object newInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub);
+ private static native Object newInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub);
+
+ @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = false)
+ private static native Object newInstanceOrNull(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub);
@Snippet
public static Object allocateInstancePIC(@ConstantParameter int size, KlassPointer hub, Word prototypeMarkWord, @ConstantParameter boolean fillContents,
@@ -244,18 +261,18 @@
public static Object allocateInstanceDynamic(Class<?> type, Class<?> classClass, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister,
@ConstantParameter OptionValues options, @ConstantParameter Counters counters) {
if (probability(SLOW_PATH_PROBABILITY, type == null)) {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
Class<?> nonNullType = PiNode.piCastNonNullClass(type, SnippetAnchorNode.anchor());
if (probability(SLOW_PATH_PROBABILITY, DynamicNewInstanceNode.throwsInstantiationException(type, classClass))) {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
- return PiNode.piCastToSnippetReplaceeStamp(allocateInstanceDynamicHelper(type, fillContents, threadRegister, options, counters, nonNullType));
+ return PiNode.piCastToSnippetReplaceeStamp(allocateInstanceDynamicHelper(fillContents, threadRegister, options, counters, nonNullType));
}
- private static Object allocateInstanceDynamicHelper(Class<?> type, boolean fillContents, Register threadRegister, OptionValues options, Counters counters, Class<?> nonNullType) {
+ private static Object allocateInstanceDynamicHelper(boolean fillContents, Register threadRegister, OptionValues options, Counters counters, Class<?> nonNullType) {
KlassPointer hub = ClassGetHubNode.readClass(nonNullType);
if (probability(FAST_PATH_PROBABILITY, !hub.isNull())) {
KlassPointer nonNullHub = ClassGetHubNode.piCastNonNull(hub, SnippetAnchorNode.anchor());
@@ -277,10 +294,11 @@
return allocateInstanceHelper(layoutHelper, nonNullHub, prototypeMarkWord, fillContents, threadRegister, false, "", options, counters);
}
} else {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
}
- return dynamicNewInstanceStub(type);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
+ return null;
}
/**
@@ -307,13 +325,30 @@
}
@Snippet
- public static Object allocateArray(KlassPointer hub, int length, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter int log2ElementSize,
- @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter boolean maybeUnroll, @ConstantParameter String typeContext,
- @ConstantParameter OptionValues options, @ConstantParameter Counters counters) {
+ public static Object allocateArray(KlassPointer hub,
+ int length,
+ Word prototypeMarkWord,
+ @ConstantParameter int headerSize,
+ @ConstantParameter int log2ElementSize,
+ @ConstantParameter boolean fillContents,
+ @ConstantParameter Register threadRegister,
+ @ConstantParameter boolean maybeUnroll,
+ @ConstantParameter String typeContext,
+ @ConstantParameter OptionValues options,
+ @ConstantParameter Counters counters) {
Object result = allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext, false, options, counters);
return piArrayCastToSnippetReplaceeStamp(verifyOop(result), length);
}
+ /**
+ * When allocating on the slow path, determines whether to use a version of the runtime call
+ * that returns {@code null} on a failed allocation instead of raising an OutOfMemoryError.
+ */
+ @Fold
+ static boolean useNullAllocationStubs(@InjectedParameter GraalHotSpotVMConfig config) {
+ return config.areNullAllocationStubsAvailable();
+ }
+
private static Object allocateArrayImpl(KlassPointer hub, int length, Word prototypeMarkWord, int headerSize, int log2ElementSize, boolean fillContents, Register threadRegister,
boolean maybeUnroll, String typeContext, boolean skipNegativeCheck, OptionValues options, Counters counters) {
Object result;
@@ -331,27 +366,41 @@
}
result = formatArray(hub, allocationSize, length, headerSize, top, prototypeMarkWord, fillContents, maybeUnroll, counters);
} else {
- result = newArray(HotSpotBackend.NEW_ARRAY, hub, length);
+ result = newArrayStub(hub, length);
}
profileAllocation("array", allocationSize, typeContext, options);
return result;
}
- @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
- public static native Object newArray(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int length);
-
- public static final ForeignCallDescriptor DYNAMIC_NEW_ARRAY = new ForeignCallDescriptor("dynamic_new_array", Object.class, Class.class, int.class);
- public static final ForeignCallDescriptor DYNAMIC_NEW_INSTANCE = new ForeignCallDescriptor("dynamic_new_instance", Object.class, Class.class);
-
- @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
- public static native Object dynamicNewArrayStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType, int length);
-
- public static Object dynamicNewInstanceStub(Class<?> elementType) {
- return dynamicNewInstanceStubCall(DYNAMIC_NEW_INSTANCE, elementType);
+ public static Object newArrayStub(KlassPointer hub, int length) {
+ if (useNullAllocationStubs(INJECTED_VMCONFIG)) {
+ return nonNullOrDeopt(newArrayOrNull(NEW_ARRAY_OR_NULL, hub, length));
+ } else {
+ return newArray(NEW_ARRAY, hub, length);
+ }
}
@NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
- public static native Object dynamicNewInstanceStubCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType);
+ private static native Object newArray(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int length);
+
+ @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = false)
+ private static native Object newArrayOrNull(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int length);
+
+ /**
+ * Deoptimizes if {@code obj == null} otherwise returns {@code obj}.
+ */
+ private static Object nonNullOrDeopt(Object obj) {
+ if (obj == null) {
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
+ }
+ return obj;
+ }
+
+ @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
+ public static native Object dynamicNewInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType);
+
+ @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = false)
+ public static native Object dynamicNewInstanceOrNull(@ConstantNodeParameter ForeignCallDescriptor descriptor, Class<?> elementType);
@Snippet
public static Object allocateArrayDynamic(Class<?> elementType, Class<?> voidClass, int length, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister,
@@ -369,17 +418,17 @@
*/
staticAssert(knownElementKind != JavaKind.Void, "unsupported knownElementKind");
if (knownElementKind == JavaKind.Illegal && probability(SLOW_PATH_PROBABILITY, elementType == null || DynamicNewArrayNode.throwsIllegalArgumentException(elementType, voidClass))) {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
KlassPointer klass = loadKlassFromObject(elementType, arrayKlassOffset(INJECTED_VMCONFIG), CLASS_ARRAY_KLASS_LOCATION);
if (klass.isNull()) {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
KlassPointer nonNullKlass = ClassGetHubNode.piCastNonNull(klass, SnippetAnchorNode.anchor());
if (length < 0) {
- DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+ DeoptimizeNode.deopt(None, RuntimeConstraint);
}
int layoutHelper;
if (knownElementKind == JavaKind.Illegal) {
@@ -412,24 +461,35 @@
* Calls the runtime stub for implementing MULTIANEWARRAY.
*/
@Snippet
- public static Object newmultiarray(KlassPointer hub, @ConstantParameter int rank, @VarargsParameter int[] dimensions) {
+ private static Object newmultiarray(KlassPointer hub, @ConstantParameter int rank, @VarargsParameter int[] dimensions) {
Word dims = DimensionsNode.allocaDimsArray(rank);
ExplodeLoopNode.explodeLoop();
for (int i = 0; i < rank; i++) {
dims.writeInt(i * 4, dimensions[i], LocationIdentity.init());
}
- return newArrayCall(HotSpotBackend.NEW_MULTI_ARRAY, hub, rank, dims);
+ return newMultiArrayStub(hub, rank, dims);
+ }
+
+ private static Object newMultiArrayStub(KlassPointer hub, int rank, Word dims) {
+ if (useNullAllocationStubs(INJECTED_VMCONFIG)) {
+ return nonNullOrDeopt(newMultiArrayOrNull(NEW_MULTI_ARRAY_OR_NULL, hub, rank, dims));
+ } else {
+ return newMultiArray(NEW_MULTI_ARRAY, hub, rank, dims);
+ }
}
@Snippet
- public static Object newmultiarrayPIC(KlassPointer hub, @ConstantParameter int rank, @VarargsParameter int[] dimensions) {
+ private static Object newmultiarrayPIC(KlassPointer hub, @ConstantParameter int rank, @VarargsParameter int[] dimensions) {
// Array type would be resolved by dominating resolution.
KlassPointer picHub = LoadConstantIndirectlyFixedNode.loadKlass(hub);
return newmultiarray(picHub, rank, dimensions);
}
@NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true)
- public static native Object newArrayCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int rank, Word dims);
+ private static native Object newMultiArray(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int rank, Word dims);
+
+ @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = false)
+ private static native Object newMultiArrayOrNull(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub, int rank, Word dims);
/**
* Maximum number of long stores to emit when zeroing an object with a constant size. Larger
@@ -509,17 +569,9 @@
}
/**
- * Formats some allocated memory with an object header and zeroes out the rest. Disables asserts
- * since they can't be compiled in stubs.
- */
- public static Object formatObjectForStub(KlassPointer hub, int size, Word memory, Word compileTimePrototypeMarkWord) {
- return formatObject(hub, size, memory, compileTimePrototypeMarkWord, true, false, null);
- }
-
- /**
* Formats some allocated memory with an object header and zeroes out the rest.
*/
- protected static Object formatObject(KlassPointer hub, int size, Word memory, Word compileTimePrototypeMarkWord, boolean fillContents, boolean constantSize, Counters counters) {
+ private static Object formatObject(KlassPointer hub, int size, Word memory, Word compileTimePrototypeMarkWord, boolean fillContents, boolean constantSize, Counters counters) {
Word prototypeMarkWord = useBiasedLocking(INJECTED_VMCONFIG) ? hub.readWord(prototypeMarkWordOffset(INJECTED_VMCONFIG), PROTOTYPE_MARK_WORD_LOCATION) : compileTimePrototypeMarkWord;
initializeObjectHeader(memory, prototypeMarkWord, hub);
if (fillContents) {
@@ -532,7 +584,7 @@
}
@Snippet
- protected static void verifyHeap(@ConstantParameter Register threadRegister) {
+ private static void verifyHeap(@ConstantParameter Register threadRegister) {
Word thread = registerAsWord(threadRegister);
Word topValue = readTlabTop(thread);
if (!topValue.equal(WordFactory.zero())) {
@@ -546,7 +598,7 @@
/**
* Formats some allocated memory with an object header and zeroes out the rest.
*/
- public static Object formatArray(KlassPointer hub, int allocationSize, int length, int headerSize, Word memory, Word prototypeMarkWord, boolean fillContents, boolean maybeUnroll,
+ private static Object formatArray(KlassPointer hub, int allocationSize, int length, int headerSize, Word memory, Word prototypeMarkWord, boolean fillContents, boolean maybeUnroll,
Counters counters) {
memory.writeInt(arrayLengthOffset(INJECTED_VMCONFIG), length, LocationIdentity.init());
/*