8208686: [AOT] JVMTI ResourceExhausted event repeated for same allocation
Reviewed-by: never, kvn, sspitsyn
This commit is contained in:
parent
2223e083ba
commit
548eb6860a
src
hotspot/share
aot
gc/shared
jvmci
memory
oops
runtime
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());
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user