8208686: [AOT] JVMTI ResourceExhausted event repeated for same allocation

Reviewed-by: never, kvn, sspitsyn
This commit is contained in:
Doug Simon 2018-10-05 20:03:14 +02:00
parent 2223e083ba
commit 548eb6860a
20 changed files with 348 additions and 141 deletions
src
hotspot/share
jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat
jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot

@ -436,14 +436,19 @@ void AOTCodeHeap::link_graal_runtime_symbols() {
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);

@ -120,27 +120,22 @@ bool MemAllocator::Allocation::check_out_of_memory() {
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);
}
}

@ -109,22 +109,72 @@ static void deopt_caller() {
}
}
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 @@ JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_array(JavaThread* thread, Klass* array_k
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 @@ JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_array(JavaThread* thread, Klass* array_k
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 @@ JRT_BLOCK_ENTRY(void, JVMCIRuntime::new_array(JavaThread* thread, Klass* array_k
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);

@ -121,13 +121,35 @@ class JVMCIRuntime: public AllStatic {
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
// 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 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);
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);

@ -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) \

@ -113,6 +113,7 @@ oop Universe::_out_of_memory_error_class_metaspace = NULL;
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 @@ void Universe::oops_do(OopClosure* f) {
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 @@ bool Universe::should_fill_in_stack_trace(Handle throwable) {
(!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 @@ bool universe_post_init() {
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 @@ bool universe_post_init() {
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());

@ -148,6 +148,7 @@ class Universe: AllStatic {
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 @@ class Universe: AllStatic {
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.

@ -130,14 +130,7 @@ bool ArrayKlass::compute_is_subtype_of(Klass* k) {
}
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);

@ -1201,14 +1201,7 @@ bool InstanceKlass::is_same_or_direct_interface(Klass *k) const {
}
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,

@ -611,6 +611,20 @@ Klass* Klass::array_klass_impl(bool or_null, TRAPS) {
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

@ -514,6 +514,9 @@ protected:
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;

@ -170,19 +170,10 @@ int ObjArrayKlass::oop_size(oop obj) const {
}
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;

@ -99,19 +99,10 @@ TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name,
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) {

@ -1547,6 +1547,7 @@ void JavaThread::initialize() {
_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) {

@ -281,6 +281,14 @@ class Thread: public ThreadShadow {
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 @@ class JavaThread: public Thread {
// 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 @@ class JavaThread: public Thread {
#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 @@ class JavaThread: public Thread {
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

@ -240,14 +240,20 @@ public final class BinaryContainer implements SymbolTable {
{"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::validate_object", "_aot_jvmci_runtime_validate_object"},
{"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::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 @@ public final class BinaryContainer implements SymbolTable {
{"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

@ -648,11 +648,33 @@ public class GraalHotSpotVMConfig extends GraalHotSpotVMConfigBase {
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 @@ public class GraalHotSpotVMConfig extends GraalHotSpotVMConfigBase {
}
assert codeEntryAlignment > 0 : codeEntryAlignment;
assert checkNullAllocationStubs();
return true;
}
}

@ -279,20 +279,35 @@ public abstract class HotSpotBackend extends Backend implements FrameMap.Referen
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
*/

@ -45,8 +45,11 @@ import static org.graalvm.compiler.hotspot.HotSpotBackend.MONTGOMERY_SQUARE;
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.
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 @@ public abstract class HotSpotHostForeignCallsProvider extends HotSpotForeignCall
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 @@ public abstract class HotSpotHostForeignCallsProvider extends HotSpotForeignCall
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);

@ -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.CStringConstant.cstring;
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.debug.GraalError;
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.MemoryBarriers;
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 @@ public class NewObjectSnippets implements Snippets {
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 class NewObjectSnippets implements Snippets {
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 @@ public class NewObjectSnippets implements Snippets {
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 @@ public class NewObjectSnippets implements Snippets {
}
@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 @@ public class NewObjectSnippets implements Snippets {
}
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 @@ public class NewObjectSnippets implements Snippets {
*/
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 @@ public class NewObjectSnippets implements Snippets {
* 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
@ -508,18 +568,10 @@ public class NewObjectSnippets implements Snippets {
fillMemory(0xfefefefefefefefeL, size, memory, constantSize, startOffset, manualUnroll, counters);
}
/**
* 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 @@ public class NewObjectSnippets implements Snippets {
}
@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 @@ public class NewObjectSnippets implements Snippets {
/**
* 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());
/*