# HG changeset patch # User dnsimon # Date 1538762594 -7200 # Node ID d6aa9ea2405dfc7468611263df91dfa309837fde # Parent 2f7a2e7c322187e96f3f99959596c3e01822cab0 8208686: [AOT] JVMTI ResourceExhausted event repeated for same allocation Reviewed-by: never, kvn, sspitsyn diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/aot/aotCodeHeap.cpp --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/gc/shared/memAllocator.cpp --- 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); } } diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/jvmci/jvmciRuntime.cpp --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/jvmci/jvmciRuntime.hpp --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/jvmci/vmStructs_jvmci.cpp --- 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) \ diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/memory/universe.cpp --- 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()); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/memory/universe.hpp --- 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. diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/arrayKlass.cpp --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/instanceKlass.cpp --- 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, diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/klass.cpp --- 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 diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/klass.hpp --- 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; diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/objArrayKlass.cpp --- 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; diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/oops/typeArrayKlass.cpp --- 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) { diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/runtime/thread.cpp --- 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) { diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/hotspot/share/runtime/thread.hpp --- 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 diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java --- 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 diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java --- 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; } } diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java --- 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); diff -r 2f7a2e7c3221 -r d6aa9ea2405d src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java --- 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()); /*