Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Commit

Permalink
[GR-30774] Optimize c2v_iterateFrames.
Browse files Browse the repository at this point in the history
PullRequest: labsjdk-ce-17/1
  • Loading branch information
woess committed Jul 14, 2021
2 parents 5698252 + daaaeab commit ce28598
Show file tree
Hide file tree
Showing 6 changed files with 406 additions and 124 deletions.
289 changes: 174 additions & 115 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include "runtime/stackFrameStream.inline.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vframe.inline.hpp"

JVMCIKlassHandle::JVMCIKlassHandle(Thread* thread, Klass* klass) {
_thread = thread;
Expand Down Expand Up @@ -1147,30 +1148,99 @@ C2V_VMENTRY_NULL(jobject, getSymbol, (JNIEnv* env, jobject, jlong symbol))
return JVMCIENV->get_jobject(sym);
C2V_END

bool matches(jobjectArray methods, Method* method, JVMCIEnv* JVMCIENV) {
/*
* Used by matches() to convert a ResolvedJavaMethod[] to an array of Method*.
*/
GrowableArray<Method*>* init_resolved_methods(jobjectArray methods, JVMCIEnv* JVMCIENV) {
objArrayOop methods_oop = (objArrayOop) JNIHandles::resolve(methods);

GrowableArray<Method*>* resolved_methods = new GrowableArray<Method*>(methods_oop->length());
for (int i = 0; i < methods_oop->length(); i++) {
oop resolved = methods_oop->obj_at(i);
if ((resolved->klass() == HotSpotJVMCI::HotSpotResolvedJavaMethodImpl::klass()) && HotSpotJVMCI::asMethod(JVMCIENV, resolved) == method) {
Method* resolved_method = NULL;
if (resolved->klass() == HotSpotJVMCI::HotSpotResolvedJavaMethodImpl::klass()) {
resolved_method = HotSpotJVMCI::asMethod(JVMCIENV, resolved);
}
resolved_methods->append(resolved_method);
}
return resolved_methods;
}

/*
* Used by c2v_iterateFrames to check if `method` matches one of the ResolvedJavaMethods in the `methods` array.
* The ResolvedJavaMethod[] array is converted to a Method* array that is then cached in the resolved_methods_ref in/out parameter.
* In case of a match, the matching ResolvedJavaMethod is returned in matched_jvmci_method_ref.
*/
bool matches(jobjectArray methods, Method* method, GrowableArray<Method*>** resolved_methods_ref, Handle* matched_jvmci_method_ref, Thread* THREAD, JVMCIEnv* JVMCIENV) {
GrowableArray<Method*>* resolved_methods = *resolved_methods_ref;
if (resolved_methods == NULL) {
resolved_methods = init_resolved_methods(methods, JVMCIENV);
*resolved_methods_ref = resolved_methods;
}
assert(method != NULL, "method should not be NULL");
assert(resolved_methods->length() == ((objArrayOop) JNIHandles::resolve(methods))->length(), "arrays must have the same length");
for (int i = 0; i < resolved_methods->length(); i++) {
Method* m = resolved_methods->at(i);
if (m == method) {
*matched_jvmci_method_ref = Handle(THREAD, ((objArrayOop) JNIHandles::resolve(methods))->obj_at(i));
return true;
}
}
return false;
}

void call_interface(JavaValue* result, Klass* spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) {
/*
* Resolves an interface call to a concrete method handle.
*/
methodHandle resolve_interface_call(Klass* spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) {
CallInfo callinfo;
Handle receiver = args->receiver();
Klass* recvrKlass = receiver.is_null() ? (Klass*)NULL : receiver->klass();
LinkInfo link_info(spec_klass, name, signature);
LinkResolver::resolve_interface_call(
callinfo, receiver, recvrKlass, link_info, true, CHECK);
callinfo, receiver, recvrKlass, link_info, true, CHECK_(methodHandle()));
methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
return method;
}

/*
* Used by c2v_iterateFrames to make a new vframeStream at the given compiled frame id (stack pointer) and vframe id.
*/
void resync_vframestream_to_compiled_frame(vframeStream& vfst, intptr_t* stack_pointer, int vframe_id, JavaThread* thread, TRAPS) {
vfst = vframeStream(thread);
while (vfst.frame_id() != stack_pointer && !vfst.at_end()) {
vfst.next();
}
if (vfst.frame_id() != stack_pointer) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "stack frame not found after deopt")
}
if (vfst.is_interpreted_frame()) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "compiled stack frame expected")
}
while (vfst.vframe_id() != vframe_id) {
if (vfst.at_end()) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "vframe not found after deopt")
}
vfst.next();
assert(!vfst.is_interpreted_frame(), "Wrong frame type");
}
}

// Invoke the method
JavaCalls::call(result, method, args, CHECK);
/*
* Used by c2v_iterateFrames. Returns an array of any unallocated scope objects or NULL if none.
*/
GrowableArray<ScopeValue*>* get_unallocated_objects_or_null(GrowableArray<ScopeValue*>* scope_objects) {
GrowableArray<ScopeValue*>* unallocated = NULL;
for (int i = 0; i < scope_objects->length(); i++) {
ObjectValue* sv = (ObjectValue*) scope_objects->at(i);
if (sv->value().is_null()) {
if (unallocated == NULL) {
unallocated = new GrowableArray<ScopeValue*>(scope_objects->length());
}
unallocated->append(sv);
}
}
return unallocated;
}

C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, jobjectArray initial_methods, jobjectArray match_methods, jint initialSkip, jobject visitor_handle))
Expand All @@ -1183,90 +1253,100 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
requireInHotSpot("iterateFrames", JVMCI_CHECK_NULL);

HotSpotJVMCI::HotSpotStackFrameReference::klass()->initialize(CHECK_NULL);
Handle frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);

StackFrameStream fst(thread, true /* update */, true /* process_frames */);
vframeStream vfst(thread);
jobjectArray methods = initial_methods;
methodHandle visitor_method;
GrowableArray<Method*>* resolved_methods = NULL;

int frame_number = 0;
vframe* vf = vframe::new_vframe(fst, thread);

while (true) {
// look for the given method
while (!vfst.at_end()) { // frame loop
bool realloc_called = false;
while (true) {
StackValueCollection* locals = NULL;
if (vf->is_compiled_frame()) {
// compiled method frame
compiledVFrame* cvf = compiledVFrame::cast(vf);
if (methods == NULL || matches(methods, cvf->method(), JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
} else {
ScopeDesc* scope = cvf->scope();
// native wrappers do not have a scope
if (scope != NULL && scope->objects() != NULL) {
GrowableArray<ScopeValue*>* objects;
if (!realloc_called) {
objects = scope->objects();
} else {
// some object might already have been re-allocated, only reallocate the non-allocated ones
objects = new GrowableArray<ScopeValue*>(scope->objects()->length());
for (int i = 0; i < scope->objects()->length(); i++) {
ObjectValue* sv = (ObjectValue*) scope->objects()->at(i);
if (sv->value().is_null()) {
objects->append(sv);
}
}
}
bool realloc_failures = Deoptimization::realloc_objects(thread, fst.current(), fst.register_map(), objects, CHECK_NULL);
Deoptimization::reassign_fields(fst.current(), fst.register_map(), objects, realloc_failures, false);
intptr_t* frame_id = vfst.frame_id();

// Previous compiledVFrame of this frame; use with at_scope() to reuse scope object pool.
compiledVFrame* prev_cvf = NULL;

for (; !vfst.at_end() && vfst.frame_id() == frame_id; vfst.next()) { // vframe loop
int frame_number = 0;
Method *method = vfst.method();
int bci = vfst.bci();

Handle matched_jvmci_method;
if (methods == NULL || matches(methods, method, &resolved_methods, &matched_jvmci_method, THREAD, JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
continue;
}
javaVFrame* vf;
if (prev_cvf != NULL && prev_cvf->frame_pointer()->id() == frame_id) {
assert(prev_cvf->is_compiled_frame(), "expected compiled Java frame");
vf = prev_cvf->at_scope(vfst.decode_offset(), vfst.vframe_id());
} else {
vf = vfst.asJavaVFrame();
}

StackValueCollection* locals = NULL;
typeArrayHandle localIsVirtual_h;
if (vf->is_compiled_frame()) {
// compiled method frame
compiledVFrame* cvf = compiledVFrame::cast(vf);

ScopeDesc* scope = cvf->scope();
// native wrappers do not have a scope
if (scope != NULL && scope->objects() != NULL) {
prev_cvf = cvf;

GrowableArray<ScopeValue*>* objects = NULL;
if (!realloc_called) {
objects = scope->objects();
} else {
// some object might already have been re-allocated, only reallocate the non-allocated ones
objects = get_unallocated_objects_or_null(scope->objects());
}

if (objects != NULL) {
RegisterMap reg_map(vf->register_map());
bool realloc_failures = Deoptimization::realloc_objects(thread, vf->frame_pointer(), &reg_map, objects, CHECK_NULL);
Deoptimization::reassign_fields(vf->frame_pointer(), &reg_map, objects, realloc_failures, false);
realloc_called = true;
}

GrowableArray<ScopeValue*>* local_values = scope->locals();
assert(local_values != NULL, "NULL locals");
typeArrayOop array_oop = oopFactory::new_boolArray(local_values->length(), CHECK_NULL);
typeArrayHandle array(THREAD, array_oop);
for (int i = 0; i < local_values->length(); i++) {
ScopeValue* value = local_values->at(i);
if (value->is_object()) {
array->bool_at_put(i, true);
GrowableArray<ScopeValue*>* local_values = scope->locals();
for (int i = 0; i < local_values->length(); i++) {
ScopeValue* value = local_values->at(i);
if (value->is_object()) {
if (localIsVirtual_h.is_null()) {
typeArrayOop array_oop = oopFactory::new_boolArray(local_values->length(), CHECK_NULL);
localIsVirtual_h = typeArrayHandle(THREAD, array_oop);
}
localIsVirtual_h->bool_at_put(i, true);
}
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), array());
} else {
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), NULL);
}

locals = cvf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), cvf->bci());
methodHandle mh(THREAD, cvf->method());
JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
}

locals = cvf->locals();
frame_number = cvf->vframe_id();
} else {
// interpreted method frame
interpretedVFrame* ivf = interpretedVFrame::cast(vf);

locals = ivf->locals();
}
} else if (vf->is_interpreted_frame()) {
// interpreted method frame
interpretedVFrame* ivf = interpretedVFrame::cast(vf);
if (methods == NULL || matches(methods, ivf->method(), JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
} else {
locals = ivf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), ivf->bci());
methodHandle mh(THREAD, ivf->method());
JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), NULL);
}
assert(bci == vf->bci(), "wrong bci");
assert(method == vf->method(), "wrong method");

Handle frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), bci);
if (matched_jvmci_method.is_null()) {
methodHandle mh(THREAD, method);
JVMCIObject jvmci_method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
matched_jvmci_method = Handle(THREAD, JNIHandles::resolve(jvmci_method.as_jobject()));
}
}
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), matched_jvmci_method());
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), localIsVirtual_h());

// locals != NULL means that we found a matching frame and result is already partially initialized
if (locals != NULL) {
methods = match_methods;
HotSpotJVMCI::HotSpotStackFrameReference::set_compilerToVM(JVMCIENV, frame_reference(), JNIHandles::resolve(compilerToVM));
HotSpotJVMCI::HotSpotStackFrameReference::set_stackPointer(JVMCIENV, frame_reference(), (jlong) fst.current()->sp());
HotSpotJVMCI::HotSpotStackFrameReference::set_stackPointer(JVMCIENV, frame_reference(), (jlong) frame_id);
HotSpotJVMCI::HotSpotStackFrameReference::set_frameNumber(JVMCIENV, frame_reference(), frame_number);

// initialize the locals array
Expand All @@ -1283,51 +1363,30 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job

JavaValue result(T_OBJECT);
JavaCallArguments args(visitor);
if (visitor_method.is_null()) {
visitor_method = resolve_interface_call(HotSpotJVMCI::InspectedFrameVisitor::klass(), vmSymbols::visitFrame_name(), vmSymbols::visitFrame_signature(), &args, CHECK_NULL);
}

args.push_oop(frame_reference);
call_interface(&result, HotSpotJVMCI::InspectedFrameVisitor::klass(), vmSymbols::visitFrame_name(), vmSymbols::visitFrame_signature(), &args, CHECK_NULL);
JavaCalls::call(&result, visitor_method, &args, CHECK_NULL);
if (result.get_oop() != NULL) {
return JNIHandles::make_local(thread, result.get_oop());
}
if (methods == initial_methods) {
methods = match_methods;
if (resolved_methods != NULL && JNIHandles::resolve(match_methods) != JNIHandles::resolve(initial_methods)) {
resolved_methods = NULL;
}
}
assert(initialSkip == 0, "There should be no match before initialSkip == 0");
if (HotSpotJVMCI::HotSpotStackFrameReference::objectsMaterialized(JVMCIENV, frame_reference()) == JNI_TRUE) {
// the frame has been deoptimized, we need to re-synchronize the frame and vframe
prev_cvf = NULL;
intptr_t* stack_pointer = (intptr_t*) HotSpotJVMCI::HotSpotStackFrameReference::stackPointer(JVMCIENV, frame_reference());
fst = StackFrameStream(thread, true /* update */, true /* process_frames */);
while (fst.current()->sp() != stack_pointer && !fst.is_done()) {
fst.next();
}
if (fst.current()->sp() != stack_pointer) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "stack frame not found after deopt")
}
vf = vframe::new_vframe(fst, thread);
if (!vf->is_compiled_frame()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "compiled stack frame expected")
}
for (int i = 0; i < frame_number; i++) {
if (vf->is_top()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "vframe not found after deopt")
}
vf = vf->sender();
assert(vf->is_compiled_frame(), "Wrong frame type");
}
resync_vframestream_to_compiled_frame(vfst, stack_pointer, frame_number, thread, CHECK_NULL);
}
frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::klass()->initialize(CHECK_NULL);
}

if (vf->is_top()) {
break;
}
frame_number++;
vf = vf->sender();
} // end of vframe loop

if (fst.is_done()) {
break;
}
fst.next();
vf = vframe::new_vframe(fst, thread);
frame_number = 0;
} // end of frame loop

// the end was reached without finding a matching method
Expand Down Expand Up @@ -1426,10 +1485,10 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
// look for the given stack frame
StackFrameStream fst(thread, false /* update */, true /* process_frames */);
intptr_t* stack_pointer = (intptr_t*) JVMCIENV->get_HotSpotStackFrameReference_stackPointer(hs_frame);
while (fst.current()->sp() != stack_pointer && !fst.is_done()) {
while (fst.current()->id() != stack_pointer && !fst.is_done()) {
fst.next();
}
if (fst.current()->sp() != stack_pointer) {
if (fst.current()->id() != stack_pointer) {
JVMCI_THROW_MSG(IllegalStateException, "stack frame not found");
}

Expand All @@ -1443,10 +1502,10 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
Deoptimization::deoptimize(thread, *fst.current(), Deoptimization::Reason_none);
// look for the frame again as it has been updated by deopt (pc, deopt state...)
StackFrameStream fstAfterDeopt(thread, true /* update */, true /* process_frames */);
while (fstAfterDeopt.current()->sp() != stack_pointer && !fstAfterDeopt.is_done()) {
while (fstAfterDeopt.current()->id() != stack_pointer && !fstAfterDeopt.is_done()) {
fstAfterDeopt.next();
}
if (fstAfterDeopt.current()->sp() != stack_pointer) {
if (fstAfterDeopt.current()->id() != stack_pointer) {
JVMCI_THROW_MSG(IllegalStateException, "stack frame not found after deopt");
}

Expand Down
Loading

0 comments on commit ce28598

Please sign in to comment.