7023639: JSR 292 method handle invocation needs a fast path for compiled code

6984705: JSR 292 method handle creation should not go through JNI

Remove assembly code for JDK 7 chained method handles

Co-authored-by: John Rose <john.r.rose@oracle.com>
Co-authored-by: Michael Haupt <michael.haupt@oracle.com>
Reviewed-by: jrose, twisti, kvn, mhaupt
This commit is contained in:
Christian Thalinger 2012-07-24 10:51:00 -07:00
parent 893817c28d
commit 12901d0e5b
181 changed files with 5760 additions and 14402 deletions

View file

@ -31,7 +31,7 @@
#include "ci/ciCallSite.hpp"
#include "ci/ciField.hpp"
#include "ci/ciKlass.hpp"
#include "ci/ciMethodHandle.hpp"
#include "ci/ciMemberName.hpp"
#include "compiler/compileBroker.hpp"
#include "interpreter/bytecode.hpp"
#include "runtime/sharedRuntime.hpp"
@ -914,11 +914,11 @@ void GraphBuilder::load_local(ValueType* type, int index) {
void GraphBuilder::store_local(ValueType* type, int index) {
Value x = pop(type);
store_local(state(), x, type, index);
store_local(state(), x, index);
}
void GraphBuilder::store_local(ValueStack* state, Value x, ValueType* type, int index) {
void GraphBuilder::store_local(ValueStack* state, Value x, int index) {
if (parsing_jsr()) {
// We need to do additional tracking of the location of the return
// address for jsrs since we don't handle arbitrary jsr/ret
@ -1535,7 +1535,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
case T_ARRAY:
case T_OBJECT:
if (field_val.as_object()->should_be_constant()) {
constant = new Constant(as_ValueType(field_val));
constant = new Constant(as_ValueType(field_val));
}
break;
@ -1562,12 +1562,51 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
append(new StoreField(append(obj), offset, field, val, true, state_before, needs_patching));
}
break;
case Bytecodes::_getfield :
{
case Bytecodes::_getfield: {
// Check for compile-time constants, i.e., trusted final non-static fields.
Instruction* constant = NULL;
obj = apop();
ObjectType* obj_type = obj->type()->as_ObjectType();
if (obj_type->is_constant() && !PatchALot) {
ciObject* const_oop = obj_type->constant_value();
if (field->is_constant()) {
ciConstant field_val = field->constant_value_of(const_oop);
BasicType field_type = field_val.basic_type();
switch (field_type) {
case T_ARRAY:
case T_OBJECT:
if (field_val.as_object()->should_be_constant()) {
constant = new Constant(as_ValueType(field_val));
}
break;
default:
constant = new Constant(as_ValueType(field_val));
}
} else {
// For constant CallSites treat the target field as a compile time constant.
if (const_oop->is_call_site()) {
ciCallSite* call_site = const_oop->as_call_site();
if (field->is_call_site_target()) {
ciMethodHandle* target = call_site->get_target();
if (target != NULL) { // just in case
ciConstant field_val(T_OBJECT, target);
constant = new Constant(as_ValueType(field_val));
// Add a dependence for invalidation of the optimization.
if (!call_site->is_constant_call_site()) {
dependency_recorder()->assert_call_site_target_value(call_site, target);
}
}
}
}
}
}
if (constant != NULL) {
push(type, append(constant));
} else {
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
LoadField* load = new LoadField(apop(), offset, field, false, state_before, needs_patching);
LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
Value replacement = !needs_patching ? _memory->load(load) : load;
if (replacement != load) {
assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked");
@ -1575,22 +1614,23 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
} else {
push(type, append(load));
}
break;
}
case Bytecodes::_putfield :
{ Value val = pop(type);
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
StoreField* store = new StoreField(apop(), offset, field, val, false, state_before, needs_patching);
if (!needs_patching) store = _memory->store(store);
if (store != NULL) {
append(store);
}
}
break;
default :
}
case Bytecodes::_putfield: {
Value val = pop(type);
obj = apop();
if (state_before == NULL) {
state_before = copy_state_for_exception();
}
StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
if (!needs_patching) store = _memory->store(store);
if (store != NULL) {
append(store);
}
break;
}
default:
ShouldNotReachHere();
break;
}
@ -1604,38 +1644,73 @@ Dependencies* GraphBuilder::dependency_recorder() const {
void GraphBuilder::invoke(Bytecodes::Code code) {
const bool has_receiver =
code == Bytecodes::_invokespecial ||
code == Bytecodes::_invokevirtual ||
code == Bytecodes::_invokeinterface;
const bool is_invokedynamic = (code == Bytecodes::_invokedynamic);
bool will_link;
ciMethod* target = stream()->get_method(will_link);
ciMethod* target = stream()->get_method(will_link);
ciKlass* holder = stream()->get_declared_method_holder();
const Bytecodes::Code bc_raw = stream()->cur_bc_raw();
// FIXME bail out for now
if ((bc_raw == Bytecodes::_invokehandle || is_invokedynamic) && !will_link) {
BAILOUT("unlinked call site (FIXME needs patching or recompile support)");
}
// we have to make sure the argument size (incl. the receiver)
// is correct for compilation (the call would fail later during
// linkage anyway) - was bug (gri 7/28/99)
if (target->is_loaded() && target->is_static() != (code == Bytecodes::_invokestatic)) BAILOUT("will cause link error");
{
// Use raw to get rewritten bytecode.
const bool is_invokestatic = bc_raw == Bytecodes::_invokestatic;
const bool allow_static =
is_invokestatic ||
bc_raw == Bytecodes::_invokehandle ||
bc_raw == Bytecodes::_invokedynamic;
if (target->is_loaded()) {
if (( target->is_static() && !allow_static) ||
(!target->is_static() && is_invokestatic)) {
BAILOUT("will cause link error");
}
}
}
ciInstanceKlass* klass = target->holder();
// check if CHA possible: if so, change the code to invoke_special
ciInstanceKlass* calling_klass = method()->holder();
ciKlass* holder = stream()->get_declared_method_holder();
ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder);
ciInstanceKlass* actual_recv = callee_holder;
// some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial.
if (target->is_loaded() && !target->is_abstract() &&
target->can_be_statically_bound() && code == Bytecodes::_invokevirtual) {
code = Bytecodes::_invokespecial;
// Some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial or invokestatic.
if (target->is_loaded() && !target->is_abstract() && target->can_be_statically_bound()) {
switch (bc_raw) {
case Bytecodes::_invokevirtual: code = Bytecodes::_invokespecial; break;
case Bytecodes::_invokehandle: code = Bytecodes::_invokestatic; break;
}
}
bool is_invokedynamic = code == Bytecodes::_invokedynamic;
// Push appendix argument (MethodType, CallSite, etc.), if one.
if (stream()->has_appendix()) {
ciObject* appendix = stream()->get_appendix();
Value arg = append(new Constant(new ObjectConstant(appendix)));
apush(arg);
}
// NEEDS_CLEANUP
// I've added the target-is_loaded() test below but I don't really understand
// I've added the target->is_loaded() test below but I don't really understand
// how klass->is_loaded() can be true and yet target->is_loaded() is false.
// this happened while running the JCK invokevirtual tests under doit. TKR
ciMethod* cha_monomorphic_target = NULL;
ciMethod* exact_target = NULL;
Value better_receiver = NULL;
if (UseCHA && DeoptC1 && klass->is_loaded() && target->is_loaded() &&
!target->is_method_handle_invoke()) {
!(// %%% FIXME: Are both of these relevant?
target->is_method_handle_intrinsic() ||
target->is_compiled_lambda_form())) {
Value receiver = NULL;
ciInstanceKlass* receiver_klass = NULL;
bool type_is_exact = false;
@ -1761,23 +1836,15 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
code == Bytecodes::_invokedynamic) {
ciMethod* inline_target = (cha_monomorphic_target != NULL) ? cha_monomorphic_target : target;
bool success = false;
if (target->is_method_handle_invoke()) {
if (target->is_method_handle_intrinsic()) {
// method handle invokes
success = !is_invokedynamic ? for_method_handle_inline(target) : for_invokedynamic_inline(target);
}
if (!success) {
success = for_method_handle_inline(target);
} else {
// static binding => check if callee is ok
success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), better_receiver);
success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), code, better_receiver);
}
CHECK_BAILOUT();
#ifndef PRODUCT
// printing
if (PrintInlining && !success) {
// if it was successfully inlined, then it was already printed.
print_inline_result(inline_target, success);
}
#endif
clear_inline_bailout();
if (success) {
// Register dependence if JVMTI has either breakpoint
@ -1788,8 +1855,13 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
}
return;
}
} else {
print_inlining(target, "no static binding", /*success*/ false);
}
} else {
print_inlining(target, "not inlineable", /*success*/ false);
}
// 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
@ -1803,10 +1875,6 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
// inlining not successful => standard invoke
bool is_loaded = target->is_loaded();
bool has_receiver =
code == Bytecodes::_invokespecial ||
code == Bytecodes::_invokevirtual ||
code == Bytecodes::_invokeinterface;
ValueType* result_type = as_ValueType(target->return_type());
// We require the debug info to be the "state before" because
@ -1855,7 +1923,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
} else if (exact_target != NULL) {
target_klass = exact_target->holder();
}
profile_call(recv, target_klass);
profile_call(target, recv, target_klass);
}
}
@ -3097,30 +3165,61 @@ int GraphBuilder::recursive_inline_level(ciMethod* cur_callee) const {
}
bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, Value receiver) {
// Clear out any existing inline bailout condition
bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, Bytecodes::Code bc, Value receiver) {
const char* msg = NULL;
// clear out any existing inline bailout condition
clear_inline_bailout();
if (callee->should_exclude()) {
// callee is excluded
INLINE_BAILOUT("excluded by CompilerOracle")
} else if (callee->should_not_inline()) {
// callee is excluded
INLINE_BAILOUT("disallowed by CompilerOracle")
} else if (!callee->can_be_compiled()) {
// callee is not compilable (prob. has breakpoints)
INLINE_BAILOUT("not compilable (disabled)")
} else if (callee->intrinsic_id() != vmIntrinsics::_none && try_inline_intrinsics(callee)) {
// intrinsics can be native or not
return true;
} else if (callee->is_native()) {
// 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, NULL, receiver);
// exclude methods we don't want to inline
msg = should_not_inline(callee);
if (msg != NULL) {
print_inlining(callee, msg, /*success*/ false);
return false;
}
// handle intrinsics
if (callee->intrinsic_id() != vmIntrinsics::_none) {
if (try_inline_intrinsics(callee)) {
print_inlining(callee, "intrinsic");
return true;
}
// try normal inlining
}
// certain methods cannot be parsed at all
msg = check_can_parse(callee);
if (msg != NULL) {
print_inlining(callee, msg, /*success*/ false);
return false;
}
// If bytecode not set use the current one.
if (bc == Bytecodes::_illegal) {
bc = code();
}
if (try_inline_full(callee, holder_known, bc, receiver))
return true;
print_inlining(callee, _inline_bailout_msg, /*success*/ false);
return false;
}
const char* GraphBuilder::check_can_parse(ciMethod* callee) const {
// Certain methods cannot be parsed at all:
if ( callee->is_native()) return "native method";
if ( callee->is_abstract()) return "abstract method";
if (!callee->can_be_compiled()) return "not compilable (disabled)";
return NULL;
}
// negative filter: should callee NOT be inlined? returns NULL, ok to inline, or rejection msg
const char* GraphBuilder::should_not_inline(ciMethod* callee) const {
if ( callee->should_exclude()) return "excluded by CompilerOracle";
if ( callee->should_not_inline()) return "disallowed by CompilerOracle";
if ( callee->dont_inline()) return "don't inline by annotation";
return NULL;
}
@ -3304,7 +3403,7 @@ bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
recv = args->at(0);
null_check(recv);
}
profile_call(recv, NULL);
profile_call(callee, recv, NULL);
}
}
}
@ -3315,13 +3414,6 @@ bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
Value value = append_split(result);
if (result_type != voidType) push(result_type, value);
#ifndef PRODUCT
// printing
if (PrintInlining) {
print_inline_result(callee, true);
}
#endif
// done
return true;
}
@ -3477,7 +3569,7 @@ void GraphBuilder::fill_sync_handler(Value lock, BlockBegin* sync_handler, bool
}
bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBegin* cont_block, Value receiver) {
bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecodes::Code bc, Value receiver) {
assert(!callee->is_native(), "callee must not be native");
if (CompilationPolicy::policy()->should_not_inline(compilation()->env(), callee)) {
INLINE_BAILOUT("inlining prohibited by policy");
@ -3508,10 +3600,10 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
if (callee->force_inline() || callee->should_inline()) {
// ignore heuristic controls on inlining
if (callee->force_inline())
CompileTask::print_inlining(callee, scope()->level(), bci(), "force inline by annotation");
print_inlining(callee, "force inline by annotation");
} else {
if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("too-deep inlining");
if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("too-deep recursive inlining");
if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("inlining too deep");
if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("recursive inlining too deep");
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
@ -3530,28 +3622,25 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
if (compilation()->env()->num_inlined_bytecodes() > DesiredMethodLimit) {
INLINE_BAILOUT("total inlining greater than DesiredMethodLimit");
}
// printing
print_inlining(callee, "");
}
#ifndef PRODUCT
// printing
if (PrintInlining) {
print_inline_result(callee, true);
}
#endif
// NOTE: Bailouts from this point on, which occur at the
// GraphBuilder level, do not cause bailout just of the inlining but
// in fact of the entire compilation.
BlockBegin* orig_block = block();
const bool is_invokedynamic = bc == Bytecodes::_invokedynamic;
const bool has_receiver = (bc != Bytecodes::_invokestatic && !is_invokedynamic);
const int args_base = state()->stack_size() - callee->arg_size();
assert(args_base >= 0, "stack underflow during inlining");
// Insert null check if necessary
Value recv = NULL;
if (code() != Bytecodes::_invokestatic &&
code() != Bytecodes::_invokedynamic) {
if (has_receiver) {
// note: null check must happen even if first instruction of callee does
// an implicit null check since the callee is in a different scope
// and we must make sure exception handling does the right thing
@ -3567,7 +3656,7 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
compilation()->set_would_profile(true);
if (profile_calls()) {
profile_call(recv, holder_known ? callee->holder() : NULL);
profile_call(callee, recv, holder_known ? callee->holder() : NULL);
}
}
@ -3576,7 +3665,7 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
// fall-through of control flow, all return instructions of the
// callee will need to be replaced by Goto's pointing to this
// continuation point.
BlockBegin* cont = cont_block != NULL ? cont_block : block_at(next_bci());
BlockBegin* cont = block_at(next_bci());
bool continuation_existed = true;
if (cont == NULL) {
cont = new BlockBegin(next_bci());
@ -3609,17 +3698,10 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
// 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;
while (i < caller_state->stack_size()) {
const int par_no = i - args_base;
Value arg = caller_state->stack_at_inc(i);
// NOTE: take base() of arg->type() to avoid problems storing
// constants
if (receiver != NULL && par_no == 0) {
arg = receiver;
}
store_local(callee_state, arg, arg->type()->base(), par_no);
}
for (int i = args_base; i < caller_state->stack_size(); ) {
const int arg_no = i - args_base;
Value arg = caller_state->stack_at_inc(i);
store_local(callee_state, arg, arg_no);
}
// Remove args from stack.
@ -3695,29 +3777,27 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
// block merging. This allows load elimination and CSE to take place
// across multiple callee scopes if they are relatively simple, and
// is currently essential to making inlining profitable.
if (cont_block == NULL) {
if (num_returns() == 1
&& block() == orig_block
&& block() == inline_cleanup_block()) {
_last = inline_cleanup_return_prev();
_state = inline_cleanup_state();
} else if (continuation_preds == cont->number_of_preds()) {
// Inlining caused that the instructions after the invoke in the
// caller are not reachable any more. So skip filling this block
// with instructions!
assert(cont == continuation(), "");
if (num_returns() == 1
&& block() == orig_block
&& block() == inline_cleanup_block()) {
_last = inline_cleanup_return_prev();
_state = inline_cleanup_state();
} else if (continuation_preds == cont->number_of_preds()) {
// Inlining caused that the instructions after the invoke in the
// caller are not reachable any more. So skip filling this block
// with instructions!
assert(cont == continuation(), "");
assert(_last && _last->as_BlockEnd(), "");
_skip_block = true;
} else {
// Resume parsing in continuation block unless it was already parsed.
// Note that if we don't change _last here, iteration in
// iterate_bytecodes_for_block will stop when we return.
if (!continuation()->is_set(BlockBegin::was_visited_flag)) {
// add continuation to work list instead of parsing it immediately
assert(_last && _last->as_BlockEnd(), "");
scope_data()->parent()->add_to_work_list(continuation());
_skip_block = true;
} else {
// Resume parsing in continuation block unless it was already parsed.
// Note that if we don't change _last here, iteration in
// iterate_bytecodes_for_block will stop when we return.
if (!continuation()->is_set(BlockBegin::was_visited_flag)) {
// add continuation to work list instead of parsing it immediately
assert(_last && _last->as_BlockEnd(), "");
scope_data()->parent()->add_to_work_list(continuation());
_skip_block = true;
}
}
}
@ -3735,114 +3815,88 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
bool GraphBuilder::for_method_handle_inline(ciMethod* callee) {
assert(!callee->is_static(), "change next line");
int index = state()->stack_size() - (callee->arg_size_no_receiver() + 1);
Value receiver = state()->stack_at(index);
if (receiver->type()->is_constant()) {
ciMethodHandle* method_handle = receiver->type()->as_ObjectType()->constant_value()->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(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();
if (phi != NULL && phi->operand_count() == 2) {
// Get the two MethodHandle inputs from the Phi.
Value op1 = phi->operand_at(0);
Value op2 = phi->operand_at(1);
ObjectType* op1type = op1->type()->as_ObjectType();
ObjectType* op2type = op2->type()->as_ObjectType();
if (op1type->is_constant() && op2type->is_constant()) {
ciMethodHandle* mh1 = op1type->constant_value()->as_method_handle();
ciMethodHandle* mh2 = op2type->constant_value()->as_method_handle();
// Set the callee to have access to the class and signature in
// the MethodHandleCompiler.
mh1->set_callee(callee);
mh1->set_caller(method());
mh2->set_callee(callee);
mh2->set_caller(method());
// Get adapters for the MethodHandles.
ciMethod* mh1_adapter = mh1->get_method_handle_adapter();
ciMethod* mh2_adapter = mh2->get_method_handle_adapter();
if (mh1_adapter != NULL && mh2_adapter != NULL) {
set_inline_cleanup_info();
// Build the If guard
BlockBegin* one = new BlockBegin(next_bci());
BlockBegin* two = new BlockBegin(next_bci());
BlockBegin* end = new BlockBegin(next_bci());
Instruction* iff = append(new If(phi, If::eql, false, op1, one, two, NULL, false));
block()->set_end(iff->as_BlockEnd());
// Connect up the states
one->merge(block()->end()->state());
two->merge(block()->end()->state());
// 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, NULL)) {
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, NULL)) {
restore_inline_cleanup_info();
block()->clear_end(); // remove appended iff
return false;
}
connect_to_end(end);
ValueStack* state_before = state()->copy_for_parsing();
vmIntrinsics::ID iid = callee->intrinsic_id();
switch (iid) {
case vmIntrinsics::_invokeBasic:
{
// get MethodHandle receiver
const int args_base = state()->stack_size() - callee->arg_size();
ValueType* type = state()->stack_at(args_base)->type();
if (type->is_constant()) {
ciMethod* target = type->as_ObjectType()->constant_value()->as_method_handle()->get_vmtarget();
guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove
Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
if (try_inline(target, /*holder_known*/ true, bc)) {
return true;
}
} else {
print_inlining(callee, "receiver not constant", /*success*/ false);
}
}
}
return false;
}
break;
bool GraphBuilder::for_invokedynamic_inline(ciMethod* callee) {
// Get the MethodHandle from the CallSite.
ciCallSite* call_site = stream()->get_call_site();
ciMethodHandle* method_handle = call_site->get_target();
// Set the callee to have access to the class and signature in the
// MethodHandleCompiler.
method_handle->set_callee(callee);
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);
case vmIntrinsics::_linkToVirtual:
case vmIntrinsics::_linkToStatic:
case vmIntrinsics::_linkToSpecial:
case vmIntrinsics::_linkToInterface:
{
// pop MemberName argument
const int args_base = state()->stack_size() - callee->arg_size();
ValueType* type = apop()->type();
if (type->is_constant()) {
ciMethod* target = type->as_ObjectType()->constant_value()->as_member_name()->get_vmtarget();
// If the target is another method handle invoke try recursivly to get
// a better target.
if (target->is_method_handle_intrinsic()) {
if (for_method_handle_inline(target)) {
return true;
}
} else {
ciSignature* signature = target->signature();
const int receiver_skip = target->is_static() ? 0 : 1;
// Cast receiver to its type.
if (!target->is_static()) {
ciKlass* tk = signature->accessing_klass();
Value obj = state()->stack_at(args_base);
if (obj->exact_type() == NULL &&
obj->declared_type() != tk && tk != compilation()->env()->Object_klass()) {
TypeCast* c = new TypeCast(tk, obj, state_before);
append(c);
state()->stack_at_put(args_base, c);
}
}
// Cast reference arguments to its type.
for (int i = 0, j = 0; i < signature->count(); i++) {
ciType* t = signature->type_at(i);
if (t->is_klass()) {
ciKlass* tk = t->as_klass();
Value obj = state()->stack_at(args_base + receiver_skip + j);
if (obj->exact_type() == NULL &&
obj->declared_type() != tk && tk != compilation()->env()->Object_klass()) {
TypeCast* c = new TypeCast(t, obj, state_before);
append(c);
state()->stack_at_put(args_base + receiver_skip + j, c);
}
}
j += t->size(); // long and double take two slots
}
Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
if (try_inline(target, /*holder_known*/ true, bc)) {
return true;
}
}
} else {
print_inlining(callee, "MemberName not constant", /*success*/ false);
}
return true;
}
break;
default:
fatal(err_msg("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid)));
break;
}
set_state(state_before);
return false;
}
@ -4034,22 +4088,24 @@ void GraphBuilder::append_unsafe_CAS(ciMethod* callee) {
}
#ifndef PRODUCT
void GraphBuilder::print_inline_result(ciMethod* callee, bool res) {
CompileTask::print_inlining(callee, scope()->level(), bci(), _inline_bailout_msg);
if (res && CIPrintMethodCodes) {
void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool success) {
if (!PrintInlining) return;
assert(msg != NULL, "must be");
CompileTask::print_inlining(callee, scope()->level(), bci(), msg);
if (success && CIPrintMethodCodes) {
callee->print_codes();
}
}
#ifndef PRODUCT
void GraphBuilder::print_stats() {
vmap()->print();
}
#endif // PRODUCT
void GraphBuilder::profile_call(Value recv, ciKlass* known_holder) {
append(new ProfileCall(method(), bci(), recv, known_holder));
void GraphBuilder::profile_call(ciMethod* callee, Value recv, ciKlass* known_holder) {
append(new ProfileCall(method(), bci(), callee, recv, known_holder));
}
void GraphBuilder::profile_invocation(ciMethod* callee, ValueStack* state) {