src/share/vm/c1/c1_GraphBuilder.cpp
Print this page
rev 2893 : 7121756: Improve C1 inlining policy by using profiling at call sites
Summary: profile based recompilation of methods with C1 with more inlining.
Reviewed-by:
*** 1689,1699 ****
--- 1689,1750 ----
// falsely lead us to believe that the receiver is final or private.
dependency_recorder()->assert_unique_concrete_method(actual_recv, cha_monomorphic_target);
}
code = Bytecodes::_invokespecial;
}
+
+ Value dynamic_receiver = NULL;
+
+ if (C1TypeProfileInlining &&
+ code != Bytecodes::_invokestatic &&
+ code != Bytecodes::_invokespecial &&
+ code != Bytecodes::_invokedynamic &&
+ (code != Bytecodes::_invokevirtual || !target->is_loaded() || !target->is_final_method())) {
+
+ ciInstanceKlass* k = method()->profile_single_hot_receiver(bci());
+
+ if (k != NULL) {
+ // Profiling reveals a single klass at the call. Attempt inlining (with a guard).
+ ValueStack* state_before = copy_state_before();
+
+ klass = k;
+
+ ciMethod* receiver_method = target->resolve_invoke(calling_klass, k);
+
+ assert(klass->is_loaded() && target->is_loaded(), "should be");
+ assert(!target->is_static(), "should be");
+
+ int index = state()->stack_size() - (target->arg_size_no_receiver() + 1);
+ Value receiver = state()->stack_at(index);
+ // Insert a guard (specialized checkcast instruction)
+ CheckCast* c = new CheckCast(k, receiver, state_before);
+ c->set_profile_inlining();
+ c->set_direct_compare(true);
+ dynamic_receiver = append_split(c);
+
+ cha_monomorphic_target = receiver_method;
+ code = Bytecodes::_invokespecial;
+
+ if (TraceC1ProfileInlining) {
+ ttyLocker ttyl;
+ tty->print("C1ProfileInlining: virtual method inlining ");
+ _compilation->method()->print_short_name(tty);
+ tty->print(" in ");
+ method()->print_short_name(tty);
+ tty->print(" at bci = %d", bci());
+ tty->print(" to ");
+ receiver_method->print_short_name(tty);
+ tty->print(" expects class ");
+ k->print_name();
+ tty->cr();
+ }
+ }
+ }
+
// check if we could do inlining
+ bool do_profiling = false;
+ bool inlining_attempted = false;
if (!PatchALot && Inline && klass->is_loaded() &&
(klass->is_initialized() || klass->is_interface() && target->holder()->is_initialized())
&& target->will_link(klass, callee_holder, code)) {
// callee is known => check if we have static binding
assert(target->is_loaded(), "callee must be known");
*** 1707,1717 ****
// method handle invokes
success = !is_invokedynamic ? for_method_handle_inline(target) : for_invokedynamic_inline(target);
}
if (!success) {
// static binding => check if callee is ok
! success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL));
}
CHECK_BAILOUT();
#ifndef PRODUCT
// printing
--- 1758,1769 ----
// method handle invokes
success = !is_invokedynamic ? for_method_handle_inline(target) : for_invokedynamic_inline(target);
}
if (!success) {
// static binding => check if callee is ok
! inlining_attempted = true;
! success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), C1ProfileInlining ? &do_profiling : NULL, dynamic_receiver);
}
CHECK_BAILOUT();
#ifndef PRODUCT
// printing
*** 1730,1739 ****
--- 1782,1813 ----
}
return;
}
}
}
+
+ if (C1TypeProfileInlining && (code == Bytecodes::_invokevirtual || code == Bytecodes::_invokeinterface) && !inlining_attempted) {
+ if (method()->method_data()) {
+ ciVirtualCallData* call = (ciVirtualCallData*)method()->method_data()->bci_to_data(bci())->as_VirtualCallData();
+
+ if (!call->is_hot() && !call->is_warm() && call->receiver(1) == NULL) {
+ do_profiling = true;
+
+ if (TraceC1ProfileInlining) {
+ ttyLocker ttyl;
+ tty->print("C1ProfileInlining: virtual method profiling ");
+ _compilation->method()->print_short_name(tty);
+ tty->print(" in ");
+ method()->print_short_name(tty);
+ tty->print_cr(" at bci = %d", bci());
+ }
+ }
+ }
+ }
+
+ assert(!do_profiling || C1ProfileInlining, "profiling only if C1InlineFrequent");
+
// If we attempted an inline which did not succeed because of a
// bailout during construction of the callee graph, the entire
// compilation has to be aborted. This is fairly rare and currently
// seems to only occur for jasm-generated classes which contain
// jsr/ret pairs which are not associated with finally clauses and
*** 1799,1809 ****
}
profile_call(recv, target_klass);
}
}
! Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before);
// push result
append_split(result);
if (result_type != voidType) {
if (method()->is_strict()) {
--- 1873,1883 ----
}
profile_call(recv, target_klass);
}
}
! Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before, do_profiling);
// push result
append_split(result);
if (result_type != voidType) {
if (method()->is_strict()) {
*** 3032,3042 ****
}
return recur_level;
}
! bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known) {
// Clear out any existing inline bailout condition
clear_inline_bailout();
if (callee->should_exclude()) {
// callee is excluded
--- 3106,3116 ----
}
return recur_level;
}
! bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, bool* do_profiling, Value dynamic_receiver) {
// Clear out any existing inline bailout condition
clear_inline_bailout();
if (callee->should_exclude()) {
// callee is excluded
*** 3054,3064 ****
// non-intrinsic natives cannot be inlined
INLINE_BAILOUT("non-intrinsic native")
} else if (callee->is_abstract()) {
INLINE_BAILOUT("abstract")
} else {
! return try_inline_full(callee, holder_known);
}
}
bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
--- 3128,3138 ----
// non-intrinsic natives cannot be inlined
INLINE_BAILOUT("non-intrinsic native")
} else if (callee->is_abstract()) {
INLINE_BAILOUT("abstract")
} else {
! return try_inline_full(callee, holder_known, do_profiling, dynamic_receiver);
}
}
bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
*** 3403,3413 ****
_state = orig_state;
_last = orig_last;
}
! bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBegin* cont_block) {
assert(!callee->is_native(), "callee must not be native");
if (CompilationPolicy::policy()->should_not_inline(compilation()->env(), callee)) {
INLINE_BAILOUT("inlining prohibited by policy");
}
// first perform tests of things it's not possible to inline
--- 3477,3487 ----
_state = orig_state;
_last = orig_last;
}
! bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, bool* do_profiling, Value dynamic_receiver, BlockBegin* cont_block) {
assert(!callee->is_native(), "callee must not be native");
if (CompilationPolicy::policy()->should_not_inline(compilation()->env(), callee)) {
INLINE_BAILOUT("inlining prohibited by policy");
}
// first perform tests of things it's not possible to inline
*** 3436,3446 ****
if (callee->should_inline()) {
// ignore heuristic controls on inlining
} else {
if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("too-deep inlining");
if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("too-deep recursive inlining");
! if (callee->code_size_for_inlining() > max_inline_size() ) INLINE_BAILOUT("callee is too large");
// don't inline throwable methods unless the inlining tree is rooted in a throwable class
if (callee->name() == ciSymbol::object_initializer_name() &&
callee->holder()->is_subclass_of(ciEnv::current()->Throwable_klass())) {
// Throwable constructor call
--- 3510,3545 ----
if (callee->should_inline()) {
// ignore heuristic controls on inlining
} else {
if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("too-deep inlining");
if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("too-deep recursive inlining");
!
! if (callee->code_size_for_inlining() > max_inline_size()) {
!
! if (do_profiling != NULL && callee->code_size() <= C1ProfileInlineSize) {
! bool warm = false;
! bool do_inlining = method()->profile_is_hot(bci(), warm);
!
! if (!do_inlining) {
! *do_profiling = !warm;
! INLINE_BAILOUT("callee is too large and too infrequent");
! } else if (TraceC1ProfileInlining) {
! ttyLocker ttyl;
! tty->print("C1ProfileInlining: method inlining of ");
! callee->print_short_name(tty);
! tty->print(" from ");
! method()->print_short_name(tty);
! tty->print(" at bci = %d", bci());
! tty->print(" in ");
! _compilation->method()->print_short_name(tty);
! tty->cr();
! }
!
! } else {
! INLINE_BAILOUT(" callee is too large");
! }
! }
// don't inline throwable methods unless the inlining tree is rooted in a throwable class
if (callee->name() == ciSymbol::object_initializer_name() &&
callee->holder()->is_subclass_of(ciEnv::current()->Throwable_klass())) {
// Throwable constructor call
*** 3534,3546 ****
--- 3633,3654 ----
// Pass parameters into callee state: add assignments
// note: this will also ensure that all arguments are computed before being passed
ValueStack* callee_state = state();
ValueStack* caller_state = state()->caller_state();
{ int i = args_base;
+ bool do_replace = dynamic_receiver != NULL;
while (i < caller_state->stack_size()) {
const int par_no = i - args_base;
Value arg = caller_state->stack_at_inc(i);
+ if (do_replace) {
+ // Profiling data drove us to inline with a single expected
+ // receiver class. Offer more accurate type information in
+ // inlinee by replacing receiver with result of guard
+ // instruction (= expected receiver class).
+ arg = dynamic_receiver;
+ do_replace = false;
+ }
// NOTE: take base() of arg->type() to avoid problems storing
// constants
store_local(callee_state, arg, arg->type()->base(), par_no);
}
}
*** 3671,3681 ****
method_handle->set_caller(method());
// Get an adapter for the MethodHandle.
ciMethod* method_handle_adapter = method_handle->get_method_handle_adapter();
if (method_handle_adapter != NULL) {
! return try_inline(method_handle_adapter, /*holder_known=*/ true);
}
} else if (receiver->as_CheckCast()) {
// Match MethodHandle.selectAlternative idiom
Phi* phi = receiver->as_CheckCast()->obj()->as_Phi();
--- 3779,3789 ----
method_handle->set_caller(method());
// Get an adapter for the MethodHandle.
ciMethod* method_handle_adapter = method_handle->get_method_handle_adapter();
if (method_handle_adapter != NULL) {
! return try_inline(method_handle_adapter, /*holder_known=*/ true, NULL, NULL);
}
} else if (receiver->as_CheckCast()) {
// Match MethodHandle.selectAlternative idiom
Phi* phi = receiver->as_CheckCast()->obj()->as_Phi();
*** 3714,3733 ****
// Save the state for the second inlinee
ValueStack* state_before = copy_state_before();
// Parse first adapter
_last = _block = one;
! if (!try_inline_full(mh1_adapter, /*holder_known=*/ true, end)) {
restore_inline_cleanup_info();
block()->clear_end(); // remove appended iff
return false;
}
// Parse second adapter
_last = _block = two;
_state = state_before;
! if (!try_inline_full(mh2_adapter, /*holder_known=*/ true, end)) {
restore_inline_cleanup_info();
block()->clear_end(); // remove appended iff
return false;
}
--- 3822,3841 ----
// Save the state for the second inlinee
ValueStack* state_before = copy_state_before();
// Parse first adapter
_last = _block = one;
! if (!try_inline_full(mh1_adapter, /*holder_known=*/ true, NULL, NULL, end)) {
restore_inline_cleanup_info();
block()->clear_end(); // remove appended iff
return false;
}
// Parse second adapter
_last = _block = two;
_state = state_before;
! if (!try_inline_full(mh2_adapter, /*holder_known=*/ true, NULL, NULL, end)) {
restore_inline_cleanup_info();
block()->clear_end(); // remove appended iff
return false;
}
*** 3751,3761 ****
method_handle->set_caller(method());
// Get an adapter for the MethodHandle.
ciMethod* method_handle_adapter = method_handle->get_invokedynamic_adapter();
if (method_handle_adapter != NULL) {
! if (try_inline(method_handle_adapter, /*holder_known=*/ true)) {
// Add a dependence for invalidation of the optimization.
if (!call_site->is_constant_call_site()) {
dependency_recorder()->assert_call_site_target_value(call_site, method_handle);
}
return true;
--- 3859,3869 ----
method_handle->set_caller(method());
// Get an adapter for the MethodHandle.
ciMethod* method_handle_adapter = method_handle->get_invokedynamic_adapter();
if (method_handle_adapter != NULL) {
! if (try_inline(method_handle_adapter, /*holder_known=*/ true, NULL, NULL)) {
// Add a dependence for invalidation of the optimization.
if (!call_site->is_constant_call_site()) {
dependency_recorder()->assert_call_site_target_value(call_site, method_handle);
}
return true;