7050554: JSR 292 - need optimization for selectAlternative
Reviewed-by: kvn, jrose
--- a/hotspot/src/share/vm/ci/ciCallProfile.hpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciCallProfile.hpp Thu Jun 02 13:36:11 2011 -0700
@@ -36,6 +36,7 @@
private:
// Fields are initialized directly by ciMethod::call_profile_at_bci.
friend class ciMethod;
+ friend class ciMethodHandle;
enum { MorphismLimit = 2 }; // Max call site's morphism we care about
int _limit; // number of receivers have been determined
@@ -58,10 +59,10 @@
public:
// Note: The following predicates return false for invalid profiles:
- bool has_receiver(int i) { return _limit > i; }
- int morphism() { return _morphism; }
+ bool has_receiver(int i) const { return _limit > i; }
+ int morphism() const { return _morphism; }
- int count() { return _count; }
+ int count() const { return _count; }
int receiver_count(int i) {
assert(i < _limit, "out of Call Profile MorphismLimit");
return _receiver_count[i];
--- a/hotspot/src/share/vm/ci/ciMethodHandle.cpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.cpp Thu Jun 02 13:36:11 2011 -0700
@@ -43,7 +43,7 @@
methodHandle callee(_callee->get_methodOop());
// We catch all exceptions here that could happen in the method
// handle compiler and stop the VM.
- MethodHandleCompiler mhc(h, callee->name(), callee->signature(), _profile->count(), is_invokedynamic, THREAD);
+ MethodHandleCompiler mhc(h, callee->name(), callee->signature(), _profile.count(), is_invokedynamic, THREAD);
if (!HAS_PENDING_EXCEPTION) {
methodHandle m = mhc.compile(THREAD);
if (!HAS_PENDING_EXCEPTION) {
--- a/hotspot/src/share/vm/ci/ciMethodHandle.hpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.hpp Thu Jun 02 13:36:11 2011 -0700
@@ -36,7 +36,7 @@
private:
ciMethod* _callee;
ciMethod* _caller;
- ciCallProfile* _profile;
+ ciCallProfile _profile;
// Return an adapter for this MethodHandle.
ciMethod* get_adapter_impl(bool is_invokedynamic) const;
@@ -49,8 +49,7 @@
ciMethodHandle(instanceHandle h_i) :
ciInstance(h_i),
_callee(NULL),
- _caller(NULL),
- _profile(NULL)
+ _caller(NULL)
{}
// What kind of ciObject is this?
@@ -58,7 +57,7 @@
void set_callee(ciMethod* m) { _callee = m; }
void set_caller(ciMethod* m) { _caller = m; }
- void set_call_profile(ciCallProfile* profile) { _profile = profile; }
+ 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); }
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Jun 02 13:36:11 2011 -0700
@@ -300,12 +300,23 @@
st->print("%7d ", (int) st->time_stamp().milliseconds()); // print timestamp
st->print("%4d ", compile_id); // print compilation number
+ // For unloaded methods the transition to zombie occurs after the
+ // method is cleared so it's impossible to report accurate
+ // information for that case.
+ bool is_synchronized = false;
+ bool has_exception_handler = false;
+ bool is_native = false;
+ if (method != NULL) {
+ is_synchronized = method->is_synchronized();
+ has_exception_handler = method->has_exception_handler();
+ is_native = method->is_native();
+ }
// method attributes
const char compile_type = is_osr_method ? '%' : ' ';
- const char sync_char = method->is_synchronized() ? 's' : ' ';
- const char exception_char = method->has_exception_handler() ? '!' : ' ';
+ const char sync_char = is_synchronized ? 's' : ' ';
+ const char exception_char = has_exception_handler ? '!' : ' ';
const char blocking_char = is_blocking ? 'b' : ' ';
- const char native_char = method->is_native() ? 'n' : ' ';
+ const char native_char = is_native ? 'n' : ' ';
// print method attributes
st->print("%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, native_char);
@@ -316,11 +327,15 @@
}
st->print(" "); // more indent
- method->print_short_name(st);
- if (is_osr_method) {
- st->print(" @ %d", osr_bci);
+ if (method == NULL) {
+ st->print("(method)");
+ } else {
+ method->print_short_name(st);
+ if (is_osr_method) {
+ st->print(" @ %d", osr_bci);
+ }
+ st->print(" (%d bytes)", method->code_size());
}
- st->print(" (%d bytes)", method->code_size());
if (msg != NULL) {
st->print(" %s", msg);
--- a/hotspot/src/share/vm/opto/callGenerator.cpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/opto/callGenerator.cpp Thu Jun 02 13:36:11 2011 -0700
@@ -698,6 +698,46 @@
}
+CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMState* jvms,
+ ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
+ if (method_handle->Opcode() == Op_ConP) {
+ const TypeOopPtr* oop_ptr = method_handle->bottom_type()->is_oopptr();
+ ciObject* const_oop = oop_ptr->const_oop();
+ ciMethodHandle* method_handle = const_oop->as_method_handle();
+
+ // Set the callee to have access to the class and signature in
+ // the MethodHandleCompiler.
+ method_handle->set_callee(callee);
+ method_handle->set_caller(caller);
+ method_handle->set_call_profile(profile);
+
+ // Get an adapter for the MethodHandle.
+ ciMethod* target_method = method_handle->get_method_handle_adapter();
+ if (target_method != NULL) {
+ CallGenerator* hit_cg = Compile::current()->call_generator(target_method, -1, false, jvms, true, 1);
+ if (hit_cg != NULL && hit_cg->is_inline())
+ return hit_cg;
+ }
+ } 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) {
+ // 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);
+ if (cg1 != NULL && cg2 != NULL) {
+ return new PredictedDynamicCallGenerator(mh, cg2, cg1, PROB_FAIR);
+ }
+ }
+ return NULL;
+}
+
+
JVMState* PredictedDynamicCallGenerator::generate(JVMState* jvms) {
GraphKit kit(jvms);
PhaseGVN& gvn = kit.gvn();
@@ -707,33 +747,45 @@
log->elem("predicted_dynamic_call bci='%d'", jvms->bci());
}
- // Get the constant pool cache from the caller class.
- ciMethod* caller_method = jvms->method();
- ciBytecodeStream str(caller_method);
- str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
- ciCPCache* cpcache = str.get_cpcache();
-
- // Get the offset of the CallSite from the constant pool cache
- // pointer.
- int index = str.get_method_index();
- size_t call_site_offset = cpcache->get_f1_offset(index);
-
- // Load the CallSite object from the constant pool cache.
- const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache);
- Node* cpcache_adr = kit.makecon(cpcache_ptr);
- Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset);
- Node* call_site = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw);
-
- // Load the target MethodHandle from the CallSite object.
- Node* target_adr = kit.basic_plus_adr(call_site, call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
- Node* target_mh = kit.make_load(kit.control(), target_adr, TypeInstPtr::BOTTOM, T_OBJECT);
-
- // Check if the MethodHandle is still the same.
const TypeOopPtr* predicted_mh_ptr = TypeOopPtr::make_from_constant(_predicted_method_handle, true);
Node* predicted_mh = kit.makecon(predicted_mh_ptr);
- Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(target_mh, predicted_mh));
- Node* bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) );
+ Node* bol = NULL;
+ int bc = jvms->method()->java_code_at_bci(jvms->bci());
+ if (bc == Bytecodes::_invokespecial) {
+ // This is the selectAlternative idiom for guardWithTest
+ Node* receiver = kit.argument(0);
+
+ // Check if the MethodHandle is the expected one
+ Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(receiver, predicted_mh));
+ bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) );
+ } else {
+ assert(bc == Bytecodes::_invokedynamic, "must be");
+ // Get the constant pool cache from the caller class.
+ ciMethod* caller_method = jvms->method();
+ ciBytecodeStream str(caller_method);
+ str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
+ ciCPCache* cpcache = str.get_cpcache();
+
+ // Get the offset of the CallSite from the constant pool cache
+ // pointer.
+ int index = str.get_method_index();
+ size_t call_site_offset = cpcache->get_f1_offset(index);
+
+ // Load the CallSite object from the constant pool cache.
+ const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache);
+ Node* cpcache_adr = kit.makecon(cpcache_ptr);
+ Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset);
+ Node* call_site = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw);
+
+ // Load the target MethodHandle from the CallSite object.
+ Node* target_adr = kit.basic_plus_adr(call_site, call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
+ Node* target_mh = kit.make_load(kit.control(), target_adr, TypeInstPtr::BOTTOM, T_OBJECT);
+
+ // Check if the MethodHandle is still the same.
+ Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(target_mh, predicted_mh));
+ bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) );
+ }
IfNode* iff = kit.create_and_xform_if(kit.control(), bol, _hit_prob, COUNT_UNKNOWN);
kit.set_control( gvn.transform(new(kit.C, 1) IfTrueNode (iff)));
Node* slow_ctl = gvn.transform(new(kit.C, 1) IfFalseNode(iff));
--- a/hotspot/src/share/vm/opto/callGenerator.hpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/opto/callGenerator.hpp Thu Jun 02 13:36:11 2011 -0700
@@ -111,6 +111,8 @@
static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic
static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface
+ static CallGenerator* for_method_handle_inline(Node* method_handle, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
+
// How to generate a replace a direct call with an inline version
static CallGenerator* for_late_inline(ciMethod* m, CallGenerator* inline_cg);
--- a/hotspot/src/share/vm/opto/doCall.cpp Wed Jun 01 23:25:31 2011 -0700
+++ b/hotspot/src/share/vm/opto/doCall.cpp Thu Jun 02 13:36:11 2011 -0700
@@ -123,24 +123,9 @@
GraphKit kit(jvms);
Node* n = kit.argument(0);
- if (n->Opcode() == Op_ConP) {
- const TypeOopPtr* oop_ptr = n->bottom_type()->is_oopptr();
- ciObject* const_oop = oop_ptr->const_oop();
- ciMethodHandle* method_handle = const_oop->as_method_handle();
-
- // Set the callee to have access to the class and signature in
- // the MethodHandleCompiler.
- method_handle->set_callee(call_method);
- method_handle->set_caller(caller);
- method_handle->set_call_profile(&profile);
-
- // Get an adapter for the MethodHandle.
- ciMethod* target_method = method_handle->get_method_handle_adapter();
- if (target_method != NULL) {
- CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
- if (hit_cg != NULL && hit_cg->is_inline())
- return hit_cg;
- }
+ CallGenerator* cg = CallGenerator::for_method_handle_inline(n, jvms, caller, call_method, profile);
+ if (cg != NULL) {
+ return cg;
}
return CallGenerator::for_direct_call(call_method);
@@ -157,7 +142,7 @@
// the MethodHandleCompiler.
method_handle->set_callee(call_method);
method_handle->set_caller(caller);
- method_handle->set_call_profile(&profile);
+ method_handle->set_call_profile(profile);
// Get an adapter for the MethodHandle.
ciMethod* target_method = method_handle->get_invokedynamic_adapter();