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;