7071307: MethodHandle bimorphic inlining should consider the frequency
Reviewed-by: twisti, roland, kvn, iveresov
This commit is contained in:
parent
1038fed51d
commit
32fd1b087d
@ -1262,6 +1262,15 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||
}
|
||||
break;
|
||||
|
||||
case _adapter_opt_profiling:
|
||||
if (java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes() != 0) {
|
||||
Address G3_mh_vmcount(G3_method_handle, java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes());
|
||||
__ ld(G3_mh_vmcount, O1_scratch);
|
||||
__ add(O1_scratch, 1, O1_scratch);
|
||||
__ st(O1_scratch, G3_mh_vmcount);
|
||||
}
|
||||
// fall through
|
||||
|
||||
case _adapter_retype_only:
|
||||
case _adapter_retype_raw:
|
||||
// Immediately jump to the next MH layer:
|
||||
|
@ -1343,6 +1343,13 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||
}
|
||||
break;
|
||||
|
||||
case _adapter_opt_profiling:
|
||||
if (java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes() != 0) {
|
||||
Address rcx_mh_vmcount(rcx_recv, java_lang_invoke_CountingMethodHandle::vmcount_offset_in_bytes());
|
||||
__ incrementl(rcx_mh_vmcount);
|
||||
}
|
||||
// fall through
|
||||
|
||||
case _adapter_retype_only:
|
||||
case _adapter_retype_raw:
|
||||
// immediately jump to the next MH layer:
|
||||
|
@ -79,6 +79,17 @@ public:
|
||||
assert(i < _limit, "out of Call Profile MorphismLimit");
|
||||
return _receiver[i];
|
||||
}
|
||||
|
||||
// Rescale the current profile based on the incoming scale
|
||||
ciCallProfile rescale(double scale) {
|
||||
assert(scale >= 0 && scale <= 1.0, "out of range");
|
||||
ciCallProfile call = *this;
|
||||
call._count = (int)(call._count * scale);
|
||||
for (int i = 0; i < _morphism; i++) {
|
||||
call._receiver_count[i] = (int)(call._receiver_count[i] * scale);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CI_CICALLPROFILE_HPP
|
||||
|
@ -37,7 +37,7 @@
|
||||
// ciMethodHandle::get_adapter
|
||||
//
|
||||
// Return an adapter for this MethodHandle.
|
||||
ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const {
|
||||
ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) {
|
||||
VM_ENTRY_MARK;
|
||||
Handle h(get_oop());
|
||||
methodHandle callee(_callee->get_methodOop());
|
||||
@ -73,7 +73,7 @@ ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const {
|
||||
// ciMethodHandle::get_adapter
|
||||
//
|
||||
// Return an adapter for this MethodHandle.
|
||||
ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
|
||||
ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) {
|
||||
ciMethod* result = get_adapter_impl(is_invokedynamic);
|
||||
if (result) {
|
||||
// Fake up the MDO maturity.
|
||||
@ -86,11 +86,22 @@ ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
// ------------------------------------------------------------------
|
||||
// ciMethodHandle::print_impl
|
||||
// ciMethodHandle::print_chain_impl
|
||||
//
|
||||
// Implementation of the print method.
|
||||
void ciMethodHandle::print_impl(outputStream* st) {
|
||||
st->print(" type=");
|
||||
get_oop()->print();
|
||||
void ciMethodHandle::print_chain_impl(outputStream* st) {
|
||||
ASSERT_IN_VM;
|
||||
MethodHandleChain::print(get_oop());
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciMethodHandle::print_chain
|
||||
//
|
||||
// Implementation of the print_chain method.
|
||||
void ciMethodHandle::print_chain(outputStream* st) {
|
||||
GUARDED_VM_ENTRY(print_chain_impl(st););
|
||||
}
|
||||
#endif
|
||||
|
@ -37,19 +37,23 @@ private:
|
||||
ciMethod* _callee;
|
||||
ciMethod* _caller;
|
||||
ciCallProfile _profile;
|
||||
ciMethod* _method_handle_adapter;
|
||||
ciMethod* _invokedynamic_adapter;
|
||||
|
||||
// Return an adapter for this MethodHandle.
|
||||
ciMethod* get_adapter_impl(bool is_invokedynamic) const;
|
||||
ciMethod* get_adapter( bool is_invokedynamic) const;
|
||||
ciMethod* get_adapter_impl(bool is_invokedynamic);
|
||||
ciMethod* get_adapter( bool is_invokedynamic);
|
||||
|
||||
protected:
|
||||
void print_impl(outputStream* st);
|
||||
void print_chain_impl(outputStream* st) PRODUCT_RETURN;
|
||||
|
||||
public:
|
||||
ciMethodHandle(instanceHandle h_i) :
|
||||
ciInstance(h_i),
|
||||
_callee(NULL),
|
||||
_caller(NULL)
|
||||
_caller(NULL),
|
||||
_method_handle_adapter(NULL),
|
||||
_invokedynamic_adapter(NULL)
|
||||
{}
|
||||
|
||||
// What kind of ciObject is this?
|
||||
@ -60,10 +64,22 @@ public:
|
||||
void set_call_profile(ciCallProfile profile) { _profile = profile; }
|
||||
|
||||
// Return an adapter for a MethodHandle call.
|
||||
ciMethod* get_method_handle_adapter() const { return get_adapter(false); }
|
||||
ciMethod* get_method_handle_adapter() {
|
||||
if (_method_handle_adapter == NULL) {
|
||||
_method_handle_adapter = get_adapter(false);
|
||||
}
|
||||
return _method_handle_adapter;
|
||||
}
|
||||
|
||||
// Return an adapter for an invokedynamic call.
|
||||
ciMethod* get_invokedynamic_adapter() const { return get_adapter(true); }
|
||||
ciMethod* get_invokedynamic_adapter() {
|
||||
if (_invokedynamic_adapter == NULL) {
|
||||
_invokedynamic_adapter = get_adapter(true);
|
||||
}
|
||||
return _invokedynamic_adapter;
|
||||
}
|
||||
|
||||
void print_chain(outputStream* st = tty) PRODUCT_RETURN;
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
|
||||
|
@ -194,16 +194,26 @@ bool ciObject::can_be_constant() {
|
||||
// ciObject::should_be_constant()
|
||||
bool ciObject::should_be_constant() {
|
||||
if (ScavengeRootsInCode >= 2) return true; // force everybody to be a constant
|
||||
if (!JavaObjectsInPerm && !is_null_object()) {
|
||||
if (is_null_object()) return true;
|
||||
|
||||
ciEnv* env = CURRENT_ENV;
|
||||
if (!JavaObjectsInPerm) {
|
||||
// We want Strings and Classes to be embeddable by default since
|
||||
// they used to be in the perm world. Not all Strings used to be
|
||||
// embeddable but there's no easy way to distinguish the interned
|
||||
// from the regulars ones so just treat them all that way.
|
||||
ciEnv* env = CURRENT_ENV;
|
||||
if (klass() == env->String_klass() || klass() == env->Class_klass()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (EnableInvokeDynamic &&
|
||||
(klass()->is_subclass_of(env->MethodHandle_klass()) ||
|
||||
klass()->is_subclass_of(env->CallSite_klass()))) {
|
||||
assert(ScavengeRootsInCode >= 1, "must be");
|
||||
// We want to treat these aggressively.
|
||||
return true;
|
||||
}
|
||||
|
||||
return handle() == NULL || is_perm();
|
||||
}
|
||||
|
||||
|
@ -2324,6 +2324,8 @@ int java_lang_invoke_BoundMethodHandle::_vmargslot_offset;
|
||||
|
||||
int java_lang_invoke_AdapterMethodHandle::_conversion_offset;
|
||||
|
||||
int java_lang_invoke_CountingMethodHandle::_vmcount_offset;
|
||||
|
||||
void java_lang_invoke_MethodHandle::compute_offsets() {
|
||||
klassOop k = SystemDictionary::MethodHandle_klass();
|
||||
if (k != NULL && EnableInvokeDynamic) {
|
||||
@ -2372,6 +2374,23 @@ void java_lang_invoke_AdapterMethodHandle::compute_offsets() {
|
||||
}
|
||||
}
|
||||
|
||||
void java_lang_invoke_CountingMethodHandle::compute_offsets() {
|
||||
klassOop k = SystemDictionary::CountingMethodHandle_klass();
|
||||
if (k != NULL && EnableInvokeDynamic) {
|
||||
compute_offset(_vmcount_offset, k, vmSymbols::vmcount_name(), vmSymbols::int_signature(), true);
|
||||
}
|
||||
}
|
||||
|
||||
int java_lang_invoke_CountingMethodHandle::vmcount(oop mh) {
|
||||
assert(is_instance(mh), "CMH only");
|
||||
return mh->int_field(_vmcount_offset);
|
||||
}
|
||||
|
||||
void java_lang_invoke_CountingMethodHandle::set_vmcount(oop mh, int count) {
|
||||
assert(is_instance(mh), "CMH only");
|
||||
mh->int_field_put(_vmcount_offset, count);
|
||||
}
|
||||
|
||||
oop java_lang_invoke_MethodHandle::type(oop mh) {
|
||||
return mh->obj_field(_type_offset);
|
||||
}
|
||||
@ -3043,6 +3062,7 @@ void JavaClasses::compute_offsets() {
|
||||
java_lang_invoke_MethodType::compute_offsets();
|
||||
java_lang_invoke_MethodTypeForm::compute_offsets();
|
||||
java_lang_invoke_CallSite::compute_offsets();
|
||||
java_lang_invoke_CountingMethodHandle::compute_offsets();
|
||||
}
|
||||
java_security_AccessControlContext::compute_offsets();
|
||||
// Initialize reflection classes. The layouts of these classes
|
||||
|
@ -981,6 +981,34 @@ class java_lang_invoke_AdapterMethodHandle: public java_lang_invoke_BoundMethodH
|
||||
};
|
||||
|
||||
|
||||
// A simple class that maintains an invocation count
|
||||
class java_lang_invoke_CountingMethodHandle: public java_lang_invoke_MethodHandle {
|
||||
friend class JavaClasses;
|
||||
|
||||
private:
|
||||
static int _vmcount_offset;
|
||||
static void compute_offsets();
|
||||
|
||||
public:
|
||||
// Accessors
|
||||
static int vmcount(oop mh);
|
||||
static void set_vmcount(oop mh, int count);
|
||||
|
||||
// Testers
|
||||
static bool is_subclass(klassOop klass) {
|
||||
return SystemDictionary::CountingMethodHandle_klass() != NULL &&
|
||||
Klass::cast(klass)->is_subclass_of(SystemDictionary::CountingMethodHandle_klass());
|
||||
}
|
||||
static bool is_instance(oop obj) {
|
||||
return obj != NULL && is_subclass(obj->klass());
|
||||
}
|
||||
|
||||
// Accessors for code generation:
|
||||
static int vmcount_offset_in_bytes() { return _vmcount_offset; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Interface to java.lang.invoke.MemberName objects
|
||||
// (These are a private interface for Java code to query the class hierarchy.)
|
||||
|
||||
|
@ -133,14 +133,14 @@ class SymbolPropertyTable;
|
||||
template(reflect_Method_klass, java_lang_reflect_Method, Pre) \
|
||||
template(reflect_Constructor_klass, java_lang_reflect_Constructor, Pre) \
|
||||
\
|
||||
/* NOTE: needed too early in bootstrapping process to have checks based on JDK version */ \
|
||||
/* Universe::is_gte_jdk14x_version() is not set up by this point. */ \
|
||||
/* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \
|
||||
template(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt) \
|
||||
template(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \
|
||||
template(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \
|
||||
template(reflect_DelegatingClassLoader_klass, sun_reflect_DelegatingClassLoader, Opt) \
|
||||
template(reflect_ConstantPool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \
|
||||
/* NOTE: needed too early in bootstrapping process to have checks based on JDK version */ \
|
||||
/* Universe::is_gte_jdk14x_version() is not set up by this point. */ \
|
||||
/* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \
|
||||
template(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt) \
|
||||
template(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \
|
||||
template(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \
|
||||
template(reflect_DelegatingClassLoader_klass, sun_reflect_DelegatingClassLoader, Opt) \
|
||||
template(reflect_ConstantPool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \
|
||||
template(reflect_UnsafeStaticFieldAccessorImpl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \
|
||||
\
|
||||
/* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \
|
||||
@ -155,6 +155,7 @@ class SymbolPropertyTable;
|
||||
template(BootstrapMethodError_klass, java_lang_BootstrapMethodError, Pre_JSR292) \
|
||||
template(WrongMethodTypeException_klass, java_lang_invoke_WrongMethodTypeException, Pre_JSR292) \
|
||||
template(CallSite_klass, java_lang_invoke_CallSite, Pre_JSR292) \
|
||||
template(CountingMethodHandle_klass, java_lang_invoke_CountingMethodHandle, Opt) \
|
||||
template(ConstantCallSite_klass, java_lang_invoke_ConstantCallSite, Pre_JSR292) \
|
||||
template(MutableCallSite_klass, java_lang_invoke_MutableCallSite, Pre_JSR292) \
|
||||
template(VolatileCallSite_klass, java_lang_invoke_VolatileCallSite, Pre_JSR292) \
|
||||
|
@ -218,6 +218,7 @@
|
||||
template(returnType_name, "returnType") \
|
||||
template(signature_name, "signature") \
|
||||
template(slot_name, "slot") \
|
||||
template(selectAlternative_name, "selectAlternative") \
|
||||
\
|
||||
/* Support for annotations (JDK 1.5 and above) */ \
|
||||
\
|
||||
@ -246,9 +247,11 @@
|
||||
template(java_lang_invoke_MethodTypeForm_signature, "Ljava/lang/invoke/MethodTypeForm;") \
|
||||
template(java_lang_invoke_MemberName, "java/lang/invoke/MemberName") \
|
||||
template(java_lang_invoke_MethodHandleNatives, "java/lang/invoke/MethodHandleNatives") \
|
||||
template(java_lang_invoke_MethodHandleImpl, "java/lang/invoke/MethodHandleImpl") \
|
||||
template(java_lang_invoke_AdapterMethodHandle, "java/lang/invoke/AdapterMethodHandle") \
|
||||
template(java_lang_invoke_BoundMethodHandle, "java/lang/invoke/BoundMethodHandle") \
|
||||
template(java_lang_invoke_DirectMethodHandle, "java/lang/invoke/DirectMethodHandle") \
|
||||
template(java_lang_invoke_CountingMethodHandle, "java/lang/invoke/CountingMethodHandle") \
|
||||
/* internal up-calls made only by the JVM, via class sun.invoke.MethodHandleNatives: */ \
|
||||
template(findMethodHandleType_name, "findMethodHandleType") \
|
||||
template(findMethodHandleType_signature, "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;") \
|
||||
@ -263,6 +266,7 @@
|
||||
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
|
||||
NOT_LP64( do_alias(machine_word_signature, int_signature) ) \
|
||||
LP64_ONLY( do_alias(machine_word_signature, long_signature) ) \
|
||||
template(selectAlternative_signature, "(ZLjava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;") \
|
||||
\
|
||||
/* common method and field names */ \
|
||||
template(object_initializer_name, "<init>") \
|
||||
@ -347,6 +351,7 @@
|
||||
template(vmmethod_name, "vmmethod") \
|
||||
template(vmtarget_name, "vmtarget") \
|
||||
template(vmentry_name, "vmentry") \
|
||||
template(vmcount_name, "vmcount") \
|
||||
template(vmslots_name, "vmslots") \
|
||||
template(vmlayout_name, "vmlayout") \
|
||||
template(vmindex_name, "vmindex") \
|
||||
@ -910,6 +915,8 @@
|
||||
do_intrinsic(_invokeVarargs, java_lang_invoke_MethodHandle, invokeVarargs_name, object_array_object_signature, F_R) \
|
||||
do_intrinsic(_invokeDynamic, java_lang_invoke_InvokeDynamic, star_name, object_array_object_signature, F_SN) \
|
||||
\
|
||||
do_intrinsic(_selectAlternative, java_lang_invoke_MethodHandleImpl, selectAlternative_name, selectAlternative_signature, F_S) \
|
||||
\
|
||||
/* unboxing methods: */ \
|
||||
do_intrinsic(_booleanValue, java_lang_Boolean, booleanValue_name, void_boolean_signature, F_R) \
|
||||
do_name( booleanValue_name, "booleanValue") \
|
||||
|
@ -600,6 +600,11 @@ public:
|
||||
uint taken() {
|
||||
return uint_at(taken_off_set);
|
||||
}
|
||||
|
||||
void set_taken(uint cnt) {
|
||||
set_uint_at(taken_off_set, cnt);
|
||||
}
|
||||
|
||||
// Saturating counter
|
||||
uint inc_taken() {
|
||||
uint cnt = taken() + 1;
|
||||
@ -926,6 +931,10 @@ public:
|
||||
return uint_at(not_taken_off_set);
|
||||
}
|
||||
|
||||
void set_not_taken(uint cnt) {
|
||||
set_uint_at(not_taken_off_set, cnt);
|
||||
}
|
||||
|
||||
uint inc_not_taken() {
|
||||
uint cnt = not_taken() + 1;
|
||||
// Did we wrap? Will compiler screw us??
|
||||
|
@ -141,7 +141,21 @@ const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_
|
||||
assert(mha_profile, "must exist");
|
||||
CounterData* cd = mha_profile->as_CounterData();
|
||||
invoke_count = cd->count();
|
||||
call_site_count = invoke_count; // use the same value
|
||||
if (invoke_count == 0) {
|
||||
return "method handle not reached";
|
||||
}
|
||||
|
||||
if (_caller_jvms != NULL && _caller_jvms->method() != NULL &&
|
||||
_caller_jvms->method()->method_data() != NULL &&
|
||||
!_caller_jvms->method()->method_data()->is_empty()) {
|
||||
ciMethodData* mdo = _caller_jvms->method()->method_data();
|
||||
ciProfileData* mha_profile = mdo->bci_to_data(_caller_jvms->bci());
|
||||
assert(mha_profile, "must exist");
|
||||
CounterData* cd = mha_profile->as_CounterData();
|
||||
call_site_count = cd->count();
|
||||
} else {
|
||||
call_site_count = invoke_count; // use the same value
|
||||
}
|
||||
}
|
||||
|
||||
assert(invoke_count != 0, "require invocation count greater than zero");
|
||||
|
@ -149,7 +149,6 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) {
|
||||
call->set_optimized_virtual(true);
|
||||
if (method()->is_method_handle_invoke()) {
|
||||
call->set_method_handle_invoke(true);
|
||||
kit.C->set_has_method_handle_invokes(true);
|
||||
}
|
||||
}
|
||||
kit.set_arguments_for_java_call(call);
|
||||
@ -207,7 +206,6 @@ JVMState* DynamicCallGenerator::generate(JVMState* jvms) {
|
||||
call->set_optimized_virtual(true);
|
||||
// Take extra care (in the presence of argument motion) not to trash the SP:
|
||||
call->set_method_handle_invoke(true);
|
||||
kit.C->set_has_method_handle_invokes(true);
|
||||
|
||||
// Pass the target MethodHandle as first argument and shift the
|
||||
// other arguments.
|
||||
@ -706,18 +704,30 @@ CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMS
|
||||
}
|
||||
} else if (method_handle->Opcode() == Op_Phi && method_handle->req() == 3 &&
|
||||
method_handle->in(1)->Opcode() == Op_ConP && method_handle->in(2)->Opcode() == Op_ConP) {
|
||||
float prob = PROB_FAIR;
|
||||
Node* meth_region = method_handle->in(0);
|
||||
if (meth_region->is_Region() &&
|
||||
meth_region->in(1)->is_Proj() && meth_region->in(2)->is_Proj() &&
|
||||
meth_region->in(1)->in(0) == meth_region->in(2)->in(0) &&
|
||||
meth_region->in(1)->in(0)->is_If()) {
|
||||
// If diamond, so grab the probability of the test to drive the inlining below
|
||||
prob = meth_region->in(1)->in(0)->as_If()->_prob;
|
||||
if (meth_region->in(1)->is_IfTrue()) {
|
||||
prob = 1 - prob;
|
||||
}
|
||||
}
|
||||
|
||||
// selectAlternative idiom merging two constant MethodHandles.
|
||||
// Generate a guard so that each can be inlined. We might want to
|
||||
// do more inputs at later point but this gets the most common
|
||||
// case.
|
||||
const TypeOopPtr* oop_ptr = method_handle->in(1)->bottom_type()->is_oopptr();
|
||||
ciObject* const_oop = oop_ptr->const_oop();
|
||||
ciMethodHandle* mh = const_oop->as_method_handle();
|
||||
|
||||
CallGenerator* cg1 = for_method_handle_inline(method_handle->in(1), jvms, caller, callee, profile);
|
||||
CallGenerator* cg2 = for_method_handle_inline(method_handle->in(2), jvms, caller, callee, profile);
|
||||
CallGenerator* cg1 = for_method_handle_inline(method_handle->in(1), jvms, caller, callee, profile.rescale(1.0 - prob));
|
||||
CallGenerator* cg2 = for_method_handle_inline(method_handle->in(2), jvms, caller, callee, profile.rescale(prob));
|
||||
if (cg1 != NULL && cg2 != NULL) {
|
||||
return new PredictedDynamicCallGenerator(mh, cg2, cg1, PROB_FAIR);
|
||||
const TypeOopPtr* oop_ptr = method_handle->in(1)->bottom_type()->is_oopptr();
|
||||
ciObject* const_oop = oop_ptr->const_oop();
|
||||
ciMethodHandle* mh = const_oop->as_method_handle();
|
||||
return new PredictedDynamicCallGenerator(mh, cg2, cg1, prob);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
@ -375,9 +375,9 @@ intptr_t IdealGraphPrinter::get_node_id(Node *n) {
|
||||
return (intptr_t)(n);
|
||||
}
|
||||
|
||||
void IdealGraphPrinter::visit_node(Node *n, void *param) {
|
||||
void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) {
|
||||
|
||||
if(param) {
|
||||
if (edges) {
|
||||
|
||||
// Output edge
|
||||
intptr_t dest_id = get_node_id(n);
|
||||
@ -599,16 +599,11 @@ void IdealGraphPrinter::visit_node(Node *n, void *param) {
|
||||
|
||||
#ifdef ASSERT
|
||||
if (node->debug_orig() != NULL) {
|
||||
temp_set->Clear();
|
||||
stringStream dorigStream;
|
||||
Node* dorig = node->debug_orig();
|
||||
if (dorig) {
|
||||
while (dorig && temp_set->test_set(dorig->_idx)) {
|
||||
dorigStream.print("%d ", dorig->_idx);
|
||||
Node* first = dorig;
|
||||
dorig = first->debug_orig();
|
||||
while (dorig && dorig != first) {
|
||||
dorigStream.print("%d ", dorig->_idx);
|
||||
dorig = dorig->debug_orig();
|
||||
}
|
||||
}
|
||||
print_prop("debug_orig", dorigStream.as_string());
|
||||
}
|
||||
@ -629,7 +624,7 @@ void IdealGraphPrinter::visit_node(Node *n, void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
void IdealGraphPrinter::walk_nodes(Node *start, void *param) {
|
||||
void IdealGraphPrinter::walk_nodes(Node *start, bool edges, VectorSet* temp_set) {
|
||||
|
||||
|
||||
VectorSet visited(Thread::current()->resource_area());
|
||||
@ -650,7 +645,7 @@ void IdealGraphPrinter::walk_nodes(Node *start, void *param) {
|
||||
while(nodeStack.length() > 0) {
|
||||
|
||||
Node *n = nodeStack.pop();
|
||||
visit_node(n, param);
|
||||
visit_node(n, edges, temp_set);
|
||||
|
||||
if (_traverse_outs) {
|
||||
for (DUIterator i = n->outs(); n->has_out(i); i++) {
|
||||
@ -689,12 +684,14 @@ void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, in
|
||||
print_attr(GRAPH_NAME_PROPERTY, (const char *)name);
|
||||
end_head();
|
||||
|
||||
VectorSet temp_set(Thread::current()->resource_area());
|
||||
|
||||
head(NODES_ELEMENT);
|
||||
walk_nodes(node, NULL);
|
||||
walk_nodes(node, false, &temp_set);
|
||||
tail(NODES_ELEMENT);
|
||||
|
||||
head(EDGES_ELEMENT);
|
||||
walk_nodes(node, (void *)1);
|
||||
walk_nodes(node, true, &temp_set);
|
||||
tail(EDGES_ELEMENT);
|
||||
if (C->cfg() != NULL) {
|
||||
head(CONTROL_FLOW_ELEMENT);
|
||||
|
@ -104,8 +104,8 @@ private:
|
||||
void print_indent();
|
||||
void print_method(ciMethod *method, int bci, InlineTree *tree);
|
||||
void print_inline_tree(InlineTree *tree);
|
||||
void visit_node(Node *n, void *param);
|
||||
void walk_nodes(Node *start, void *param);
|
||||
void visit_node(Node *n, bool edges, VectorSet* temp_set);
|
||||
void walk_nodes(Node *start, bool edges, VectorSet* temp_set);
|
||||
void begin_elem(const char *s);
|
||||
void end_elem();
|
||||
void begin_head(const char *s);
|
||||
|
@ -1106,6 +1106,9 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) {
|
||||
mcall_java->_optimized_virtual = call_java->is_optimized_virtual();
|
||||
is_method_handle_invoke = call_java->is_method_handle_invoke();
|
||||
mcall_java->_method_handle_invoke = is_method_handle_invoke;
|
||||
if (is_method_handle_invoke) {
|
||||
C->set_has_method_handle_invokes(true);
|
||||
}
|
||||
if( mcall_java->is_MachCallStaticJava() )
|
||||
mcall_java->as_MachCallStaticJava()->_name =
|
||||
call_java->as_CallStaticJava()->_name;
|
||||
|
@ -182,10 +182,6 @@ void MethodHandleChain::print(oopDesc* m) {
|
||||
HandleMark hm;
|
||||
ResourceMark rm;
|
||||
Handle mh(m);
|
||||
print(mh);
|
||||
}
|
||||
|
||||
void MethodHandleChain::print(Handle mh) {
|
||||
EXCEPTION_MARK;
|
||||
MethodHandleChain mhc(mh, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
@ -222,16 +218,33 @@ void MethodHandleChain::print_impl(TRAPS) {
|
||||
if (o != NULL) {
|
||||
if (o->is_instance()) {
|
||||
tty->print(" instance %s", o->klass()->klass_part()->internal_name());
|
||||
if (java_lang_invoke_CountingMethodHandle::is_instance(o)) {
|
||||
tty->print(" vmcount: %d", java_lang_invoke_CountingMethodHandle::vmcount(o));
|
||||
}
|
||||
} else {
|
||||
o->print();
|
||||
}
|
||||
}
|
||||
oop vmt = chain.vmtarget_oop();
|
||||
if (vmt != NULL) {
|
||||
if (vmt->is_method()) {
|
||||
tty->print(" ");
|
||||
methodOop(vmt)->print_short_name(tty);
|
||||
} else if (java_lang_invoke_MethodHandle::is_instance(vmt)) {
|
||||
tty->print(" method handle " INTPTR_FORMAT, vmt);
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
} else if (chain.is_adapter()) {
|
||||
tty->print("adapter: arg_slot %d conversion op %s",
|
||||
chain.adapter_arg_slot(),
|
||||
adapter_op_to_string(chain.adapter_conversion_op()));
|
||||
switch (chain.adapter_conversion_op()) {
|
||||
case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
|
||||
if (java_lang_invoke_CountingMethodHandle::is_instance(chain.method_handle_oop())) {
|
||||
tty->print(" vmcount: %d", java_lang_invoke_CountingMethodHandle::vmcount(chain.method_handle_oop()));
|
||||
}
|
||||
case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_RAW:
|
||||
case java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST:
|
||||
case java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM:
|
||||
@ -907,7 +920,10 @@ MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* si
|
||||
_non_bcp_klasses(THREAD, 5),
|
||||
_cur_stack(0),
|
||||
_max_stack(0),
|
||||
_rtype(T_ILLEGAL)
|
||||
_rtype(T_ILLEGAL),
|
||||
_selectAlternative_bci(-1),
|
||||
_taken_count(0),
|
||||
_not_taken_count(0)
|
||||
{
|
||||
|
||||
// Element zero is always the null constant.
|
||||
@ -1115,11 +1131,50 @@ void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index, int args_size)
|
||||
_bytecode.push(0);
|
||||
break;
|
||||
|
||||
case Bytecodes::_ifeq:
|
||||
assert((unsigned short) index == index, "index does not fit in 16-bit");
|
||||
_bytecode.push(op);
|
||||
_bytecode.push(index >> 8);
|
||||
_bytecode.push(index);
|
||||
break;
|
||||
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
void MethodHandleCompiler::update_branch_dest(int src, int dst) {
|
||||
switch (_bytecode.at(src)) {
|
||||
case Bytecodes::_ifeq:
|
||||
dst -= src; // compute the offset
|
||||
assert((unsigned short) dst == dst, "index does not fit in 16-bit");
|
||||
_bytecode.at_put(src + 1, dst >> 8);
|
||||
_bytecode.at_put(src + 2, dst);
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
void MethodHandleCompiler::emit_load(ArgToken arg) {
|
||||
TokenType tt = arg.token_type();
|
||||
BasicType bt = arg.basic_type();
|
||||
|
||||
switch (tt) {
|
||||
case tt_parameter:
|
||||
case tt_temporary:
|
||||
emit_load(bt, arg.index());
|
||||
break;
|
||||
case tt_constant:
|
||||
emit_load_constant(arg);
|
||||
break;
|
||||
case tt_illegal:
|
||||
case tt_void:
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MethodHandleCompiler::emit_load(BasicType bt, int index) {
|
||||
if (index <= 3) {
|
||||
@ -1318,6 +1373,29 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co
|
||||
jvalue MethodHandleCompiler::zero_jvalue = { 0 };
|
||||
jvalue MethodHandleCompiler::one_jvalue = { 1 };
|
||||
|
||||
// Fetch any values from CountingMethodHandles and capture them for profiles
|
||||
bool MethodHandleCompiler::fetch_counts(ArgToken arg1, ArgToken arg2) {
|
||||
int count1 = -1, count2 = -1;
|
||||
if (arg1.token_type() == tt_constant && arg1.basic_type() == T_OBJECT &&
|
||||
java_lang_invoke_CountingMethodHandle::is_instance(arg1.object()())) {
|
||||
count1 = java_lang_invoke_CountingMethodHandle::vmcount(arg1.object()());
|
||||
}
|
||||
if (arg2.token_type() == tt_constant && arg2.basic_type() == T_OBJECT &&
|
||||
java_lang_invoke_CountingMethodHandle::is_instance(arg2.object()())) {
|
||||
count2 = java_lang_invoke_CountingMethodHandle::vmcount(arg2.object()());
|
||||
}
|
||||
int total = count1 + count2;
|
||||
if (count1 != -1 && count2 != -1 && total != 0) {
|
||||
// Normalize the collect counts to the invoke_count
|
||||
tty->print("counts %d %d scaled by %d = ", count2, count1, _invoke_count);
|
||||
if (count1 != 0) _not_taken_count = (int)(_invoke_count * count1 / (double)total);
|
||||
if (count2 != 0) _taken_count = (int)(_invoke_count * count2 / (double)total);
|
||||
tty->print_cr("%d %d", _taken_count, _not_taken_count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emit bytecodes for the given invoke instruction.
|
||||
MethodHandleWalker::ArgToken
|
||||
MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
|
||||
@ -1367,6 +1445,29 @@ MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
|
||||
}
|
||||
}
|
||||
|
||||
if (m->intrinsic_id() == vmIntrinsics::_selectAlternative &&
|
||||
fetch_counts(argv[1], argv[2])) {
|
||||
assert(argc == 3, "three arguments");
|
||||
assert(tailcall, "only");
|
||||
|
||||
// do inline bytecodes so we can drop profile data into it,
|
||||
// 0: iload_0
|
||||
emit_load(argv[0]);
|
||||
// 1: ifeq 8
|
||||
_selectAlternative_bci = _bytecode.length();
|
||||
emit_bc(Bytecodes::_ifeq, 0); // emit placeholder offset
|
||||
// 4: aload_1
|
||||
emit_load(argv[1]);
|
||||
// 5: areturn;
|
||||
emit_bc(Bytecodes::_areturn);
|
||||
// 8: aload_2
|
||||
update_branch_dest(_selectAlternative_bci, cur_bci());
|
||||
emit_load(argv[2]);
|
||||
// 9: areturn
|
||||
emit_bc(Bytecodes::_areturn);
|
||||
return ArgToken(); // Dummy return value.
|
||||
}
|
||||
|
||||
check_non_bcp_klass(klass, CHECK_(zero));
|
||||
if (m->is_method_handle_invoke()) {
|
||||
check_non_bcp_klasses(m->method_handle_type(), CHECK_(zero));
|
||||
@ -1377,10 +1478,6 @@ MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
|
||||
assert(argc == asc.size() + ((op == Bytecodes::_invokestatic || op == Bytecodes::_invokedynamic) ? 0 : 1),
|
||||
"argc mismatch");
|
||||
|
||||
// Inline the method.
|
||||
InvocationCounter* ic = m->invocation_counter();
|
||||
ic->set_carry_flag();
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
ArgToken arg = argv[i];
|
||||
TokenType tt = arg.token_type();
|
||||
@ -1686,7 +1783,7 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const {
|
||||
}
|
||||
|
||||
|
||||
methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||
methodHandle MethodHandleCompiler::get_method_oop(TRAPS) {
|
||||
methodHandle empty;
|
||||
// Create a method that holds the generated bytecode. invokedynamic
|
||||
// has no receiver, normal MH calls do.
|
||||
@ -1765,6 +1862,7 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||
assert(m->method_data() == NULL, "there should not be an MDO yet");
|
||||
m->set_method_data(mdo);
|
||||
|
||||
bool found_selectAlternative = false;
|
||||
// Iterate over all profile data and set the count of the counter
|
||||
// data entries to the original call site counter.
|
||||
for (ProfileData* profile_data = mdo->first_data();
|
||||
@ -1774,7 +1872,15 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||
CounterData* counter_data = profile_data->as_CounterData();
|
||||
counter_data->set_count(_invoke_count);
|
||||
}
|
||||
if (profile_data->is_BranchData() &&
|
||||
profile_data->bci() == _selectAlternative_bci) {
|
||||
BranchData* bd = profile_data->as_BranchData();
|
||||
bd->set_taken(_taken_count);
|
||||
bd->set_not_taken(_not_taken_count);
|
||||
found_selectAlternative = true;
|
||||
}
|
||||
}
|
||||
assert(_selectAlternative_bci == -1 || found_selectAlternative, "must have found profile entry");
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
|
@ -74,6 +74,7 @@ public:
|
||||
set_method_handle(MethodHandle_vmtarget_oop(), THREAD);
|
||||
}
|
||||
|
||||
Handle root() { return _root; }
|
||||
Handle method_handle() { return _method_handle; }
|
||||
oop method_handle_oop() { return _method_handle(); }
|
||||
oop method_type_oop() { return MethodHandle_type_oop(); }
|
||||
@ -110,7 +111,6 @@ public:
|
||||
// the signature for each method. The signatures are printed in
|
||||
// slot order to make it easier to understand.
|
||||
void print();
|
||||
static void print(Handle mh);
|
||||
static void print(oopDesc* mh);
|
||||
#endif
|
||||
};
|
||||
@ -277,6 +277,10 @@ private:
|
||||
KlassHandle _target_klass;
|
||||
Thread* _thread;
|
||||
|
||||
int _selectAlternative_bci; // These are used for capturing profiles from GWTs
|
||||
int _taken_count;
|
||||
int _not_taken_count;
|
||||
|
||||
// Values used by the compiler.
|
||||
static jvalue zero_jvalue;
|
||||
static jvalue one_jvalue;
|
||||
@ -372,6 +376,7 @@ private:
|
||||
|
||||
unsigned char* bytecode() const { return _bytecode.adr_at(0); }
|
||||
int bytecode_length() const { return _bytecode.length(); }
|
||||
int cur_bci() const { return _bytecode.length(); }
|
||||
|
||||
// Fake constant pool.
|
||||
int cpool_oop_put(int tag, Handle con) {
|
||||
@ -436,6 +441,8 @@ private:
|
||||
}
|
||||
|
||||
void emit_bc(Bytecodes::Code op, int index = 0, int args_size = -1);
|
||||
void update_branch_dest(int src, int dst);
|
||||
void emit_load(ArgToken arg);
|
||||
void emit_load(BasicType bt, int index);
|
||||
void emit_store(BasicType bt, int index);
|
||||
void emit_load_constant(ArgToken arg);
|
||||
@ -455,11 +462,14 @@ private:
|
||||
virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS);
|
||||
virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS);
|
||||
|
||||
// Check for profiling information on a GWT and return true if it's found
|
||||
bool fetch_counts(ArgToken a1, ArgToken a2);
|
||||
|
||||
// Get a real constant pool.
|
||||
constantPoolHandle get_constant_pool(TRAPS) const;
|
||||
|
||||
// Get a real methodOop.
|
||||
methodHandle get_method_oop(TRAPS) const;
|
||||
methodHandle get_method_oop(TRAPS);
|
||||
|
||||
public:
|
||||
MethodHandleCompiler(Handle root, Symbol* name, Symbol* signature, int invoke_count, bool for_invokedynamic, TRAPS);
|
||||
|
@ -158,6 +158,8 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
|
||||
"adapter_fold/4/ref",
|
||||
"adapter_fold/5/ref",
|
||||
|
||||
"adapter_opt_profiling",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -2653,6 +2655,11 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||
// Finalize the conversion field. (Note that it is final to Java code.)
|
||||
java_lang_invoke_AdapterMethodHandle::set_conversion(mh(), new_conversion);
|
||||
|
||||
if (java_lang_invoke_CountingMethodHandle::is_instance(mh())) {
|
||||
assert(ek_orig == _adapter_retype_only, "only one handled");
|
||||
ek_opt = _adapter_opt_profiling;
|
||||
}
|
||||
|
||||
// Done!
|
||||
java_lang_invoke_MethodHandle::set_vmentry(mh(), entry(ek_opt));
|
||||
|
||||
@ -2905,8 +2912,12 @@ JVM_ENTRY(jint, MHN_getConstant(JNIEnv *env, jobject igcls, jint which)) {
|
||||
return MethodHandles::stack_move_unit();
|
||||
case MethodHandles::GC_CONV_OP_IMPLEMENTED_MASK:
|
||||
return MethodHandles::adapter_conversion_ops_supported_mask();
|
||||
case MethodHandles::GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS:
|
||||
return MethodHandles::OP_ROT_ARGS_DOWN_LIMIT_BIAS;
|
||||
case MethodHandles::GC_COUNT_GWT:
|
||||
#ifdef COMPILER2
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -187,6 +187,8 @@ class MethodHandles: AllStatic {
|
||||
_adapter_opt_fold_FIRST = _adapter_opt_fold_ref,
|
||||
_adapter_opt_fold_LAST = _adapter_opt_fold_5_ref,
|
||||
|
||||
_adapter_opt_profiling,
|
||||
|
||||
_EK_LIMIT,
|
||||
_EK_FIRST = 0
|
||||
};
|
||||
@ -266,6 +268,8 @@ class MethodHandles: AllStatic {
|
||||
return _adapter_fold_args;
|
||||
if (ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST)
|
||||
return _adapter_opt_return_any;
|
||||
if (ek == _adapter_opt_profiling)
|
||||
return _adapter_retype_only;
|
||||
assert(false, "oob");
|
||||
return _EK_LIMIT;
|
||||
}
|
||||
@ -582,6 +586,7 @@ class MethodHandles: AllStatic {
|
||||
GC_JVM_STACK_MOVE_UNIT = 1,
|
||||
GC_CONV_OP_IMPLEMENTED_MASK = 2,
|
||||
GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS = 3,
|
||||
GC_COUNT_GWT = 4,
|
||||
|
||||
// format of result from getTarget / encode_target:
|
||||
ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
|
||||
|
Loading…
x
Reference in New Issue
Block a user