/* * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
//-----------------------------ParseGenerator--------------------------------- // Internal class which handles all direct bytecode traversal. class ParseGenerator : public InlineCallGenerator { private: bool _is_osr; float _expected_uses;
// Simply return the exit state of the parser, // augmented by any exceptional states. return exits.transfer_exceptions_into_jvms();
}
//---------------------------DirectCallGenerator------------------------------ // Internal class which handles all out-of-line calls w/o receiver type checks. class DirectCallGenerator : public CallGenerator { private:
CallStaticJavaNode* _call_node; // Force separate memory and I/O projections for the exceptional // paths to facilitate late inlinig. bool _separate_io_proj;
if (kit.C->log() != NULL) {
kit.C->log()->elem("direct_call bci='%d'", jvms->bci());
}
CallStaticJavaNode* call = new CallStaticJavaNode(kit.C, tf(), target, method()); if (is_inlined_method_handle_intrinsic(jvms, method())) { // To be able to issue a direct call and skip a call to MH.linkTo*/invokeBasic adapter, // additional information about the method being invoked should be attached // to the call site to make resolution logic work // (see SharedRuntime::resolve_static_call_C).
call->set_override_symbolic_info(true);
}
_call_node = call; // Save the call node in case we need it later if (!is_static) { // Make an explicit receiver null_check as part of this call. // Since we share a map with the caller, his JVMS gets adjusted.
kit.null_check_receiver_before_call(method()); if (kit.stopped()) { // And dump it back to the caller, decorated with any exceptions: return kit.transfer_exceptions_into_jvms();
} // Mark the call node as virtual, sort of:
call->set_optimized_virtual(true); if (method()->is_method_handle_intrinsic() ||
method()->is_compiled_lambda_form()) {
call->set_method_handle_invoke(true);
}
}
kit.set_arguments_for_java_call(call);
kit.set_edges_for_java_call(call, false, _separate_io_proj);
Node* ret = kit.set_results_for_java_call(call, _separate_io_proj);
kit.push_node(method()->return_type()->basic_type(), ret); return kit.transfer_exceptions_into_jvms();
}
//--------------------------VirtualCallGenerator------------------------------ // Internal class which handles all out-of-line calls checking receiver type. class VirtualCallGenerator : public CallGenerator { private: int _vtable_index; bool _separate_io_proj;
CallDynamicJavaNode* _call_node;
if (kit.C->log() != NULL) {
kit.C->log()->elem("virtual_call bci='%d'", jvms->bci());
}
// If the receiver is a constant null, do not torture the system // by attempting to call through it. The compile will proceed // correctly, but may bail out in final_graph_reshaping, because // the call instruction will have a seemingly deficient out-count. // (The bailout says something misleading about an "infinite loop".) if (kit.gvn().type(receiver)->higher_equal(TypePtr::NULL_PTR)) {
assert(Bytecodes::is_invoke(kit.java_bc()), "%d: %s", kit.java_bc(), Bytecodes::name(kit.java_bc()));
ciMethod* declared_method = kit.method()->get_method_at_bci(kit.bci()); int arg_size = declared_method->signature()->arg_size_for_bc(kit.java_bc());
kit.inc_sp(arg_size); // restore arguments
kit.uncommon_trap(Deoptimization::Reason_null_check,
Deoptimization::Action_none,
NULL, "null receiver"); return kit.transfer_exceptions_into_jvms();
}
// Ideally we would unconditionally do a null check here and let it // be converted to an implicit check based on profile information. // However currently the conversion to implicit null checks in // Block::implicit_null_check() only looks for loads and stores, not calls.
ciMethod *caller = kit.method();
ciMethodData *caller_md = (caller == NULL) ? NULL : caller->method_data(); if (!UseInlineCaches || !ImplicitNullChecks || !os::zero_page_read_protected() ||
((ImplicitNullCheckThreshold > 0) && caller_md &&
(caller_md->trap_count(Deoptimization::Reason_null_check)
>= (uint)ImplicitNullCheckThreshold))) { // Make an explicit receiver null_check as part of this call. // Since we share a map with the caller, his JVMS gets adjusted.
receiver = kit.null_check_receiver_before_call(method()); if (kit.stopped()) { // And dump it back to the caller, decorated with any exceptions: return kit.transfer_exceptions_into_jvms();
}
}
assert(!method()->is_static(), "virtual call must not be to static");
assert(!method()->is_final(), "virtual call should not be to final");
assert(!method()->is_private(), "virtual call should not be to private");
assert(_vtable_index == Method::invalid_vtable_index || !UseInlineCaches, "no vtable calls if +UseInlineCaches ");
address target = SharedRuntime::get_resolve_virtual_call_stub(); // Normal inline cache used for call
CallDynamicJavaNode* call = new CallDynamicJavaNode(tf(), target, method(), _vtable_index); if (is_inlined_method_handle_intrinsic(jvms, method())) { // To be able to issue a direct call (optimized virtual or virtual) // and skip a call to MH.linkTo*/invokeBasic adapter, additional information // about the method being invoked should be attached to the call site to // make resolution logic work (see SharedRuntime::resolve_{virtual,opt_virtual}_call_C).
call->set_override_symbolic_info(true);
}
_call_node = call; // Save the call node in case we need it later
// Represent the effect of an implicit receiver null_check // as part of this call. Since we share a map with the caller, // his JVMS gets adjusted.
kit.cast_not_null(receiver); return kit.transfer_exceptions_into_jvms();
}
CallGenerator* CallGenerator::for_inline(ciMethod* m, float expected_uses) { if (InlineTree::check_can_parse(m) != NULL) return NULL; returnnew ParseGenerator(m, expected_uses);
}
// As a special case, the JVMS passed to this CallGenerator is // for the method execution already in progress, not just the JVMS // of the caller. Thus, this CallGenerator cannot be mixed with others!
CallGenerator* CallGenerator::for_osr(ciMethod* m, int osr_bci) { if (InlineTree::check_can_parse(m) != NULL) return NULL; float past_uses = m->interpreter_invocation_count(); float expected_uses = past_uses; returnnew ParseGenerator(m, expected_uses, true);
}
CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) {
assert(!m->is_static(), "for_virtual_call mismatch");
assert(!m->is_method_handle_intrinsic(), "should be a direct call"); returnnew VirtualCallGenerator(m, vtable_index, false/*separate_io_projs*/);
}
// Allow inlining decisions to be delayed class LateInlineCallGenerator : public DirectCallGenerator { private:
jlong _unique_id; // unique id for log compilation bool _is_pure_call; // a hint that the call doesn't have important side effects to care about
// Record that this call site should be revisited once the main // parse is finished. if (!is_mh_late_inline()) {
C->add_late_inline(this);
}
// Emit the CallStaticJava and request separate projections so // that the late inlining logic can distinguish between fall // through and exceptional uses of the memory and io projections // as is done for allocations and macro expansion. return DirectCallGenerator::generate(jvms);
}
Compile* C = Compile::current(); if (_input_not_const) { // inlining won't be possible so no need to enqueue right now.
call_node()->set_generator(this);
} else {
C->add_late_inline(this);
} return new_jvms;
}
bool LateInlineMHCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms) { // When inlining a virtual call, the null check at the call and the call itself can throw. These 2 paths have different // expression stacks which causes late inlining to break. The MH invoker is not expected to be called from a method with // exception handlers. When there is no exception handler, GraphKit::builtin_throw() pops the stack which solves the issue // of late inlining with exceptions.
assert(!jvms->method()->has_exception_handlers() ||
(method()->intrinsic_id() != vmIntrinsics::_linkToVirtual &&
method()->intrinsic_id() != vmIntrinsics::_linkToInterface), "no exception handler expected"); // Even if inlining is not allowed, a virtual call can be strength-reduced to a direct call. bool allow_inline = C->inlining_incrementally(); bool input_not_const = true;
CallGenerator* cg = for_method_handle_inline(jvms, _caller, method(), allow_inline, input_not_const);
assert(!input_not_const, "sanity"); // shouldn't have been scheduled for inlining in the first place
if (cg != NULL) {
assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline, "we're doing late inlining");
_inline_cg = cg;
C->dec_number_of_mh_late_inlines(); returntrue;
} else { // Method handle call which has a constant appendix argument should be either inlined or replaced with a direct call // unless there's a signature mismatch between caller and callee. If the failure occurs, there's not much to be improved later, // so don't reinstall the generator to avoid pushing the generator between IGVN and incremental inlining indefinitely. returnfalse;
}
}
// Allow inlining decisions to be delayed class LateInlineVirtualCallGenerator : public VirtualCallGenerator { private:
jlong _unique_id; // unique id for log compilation
CallGenerator* _inline_cg;
ciMethod* _callee; bool _is_pure_call; float _prof_factor;
// Convert the CallDynamicJava into an inline virtualvoid do_late_inline();
virtualvoid set_callee_method(ciMethod* m) {
assert(_callee == NULL, "repeated inlining attempt");
_callee = m;
}
virtual JVMState* generate(JVMState* jvms) { // Emit the CallDynamicJava and request separate projections so // that the late inlining logic can distinguish between fall // through and exceptional uses of the memory and io projections // as is done for allocations and macro expansion.
JVMState* new_jvms = VirtualCallGenerator::generate(jvms); if (call_node() != NULL) {
call_node()->set_generator(this);
} return new_jvms;
}
bool LateInlineVirtualCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms) { // Method handle linker case is handled in CallDynamicJavaNode::Ideal(). // Unless inlining is performed, _override_symbolic_info bit will be set in DirectCallGenerator::generate().
// Implicit receiver null checks introduce problems when exception states are combined.
Node* receiver = jvms->map()->argument(jvms, 0); const Type* recv_type = C->initial_gvn()->type(receiver); if (recv_type->maybe_null()) { if (C->print_inlining() || C->print_intrinsics()) {
C->print_inlining(method(), jvms->depth()-1, call_node()->jvms()->bci(), "late call devirtualization failed (receiver may be null)");
} returnfalse;
} // Even if inlining is not allowed, a virtual call can be strength-reduced to a direct call. bool allow_inline = C->inlining_incrementally(); if (!allow_inline && _callee->holder()->is_interface()) { // Don't convert the interface call to a direct call guarded by an interface subtype check. if (C->print_inlining() || C->print_intrinsics()) {
C->print_inlining(method(), jvms->depth()-1, call_node()->jvms()->bci(), "late call devirtualization failed (interface call)");
} returnfalse;
}
CallGenerator* cg = C->call_generator(_callee,
vtable_index(), false/*call_does_dispatch*/,
jvms,
allow_inline,
_prof_factor,
NULL /*speculative_receiver_type*/, true/*allow_intrinsics*/);
if (cg != NULL) {
assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline, "we're doing late inlining");
_inline_cg = cg; returntrue;
} else { // Virtual call which provably doesn't dispatch should be either inlined or replaced with a direct call.
assert(false, "no progress"); returnfalse;
}
}
CallGenerator* CallGenerator::for_late_inline_virtual(ciMethod* m, int vtable_index, float prof_factor) {
assert(IncrementalInlineVirtual, "required");
assert(!m->is_static(), "for_virtual_call mismatch");
assert(!m->is_method_handle_intrinsic(), "should be a direct call"); returnnew LateInlineVirtualCallGenerator(m, vtable_index, prof_factor);
}
void LateInlineVirtualCallGenerator::do_late_inline() {
assert(_callee != NULL, "required"); // set up in CallDynamicJavaNode::Ideal
CallGenerator::do_late_inline_helper();
}
void CallGenerator::do_late_inline_helper() {
assert(is_late_inline(), "only late inline allowed");
Compile* C = Compile::current(); // Remove inlined methods from Compiler's lists. if (call->is_macro()) {
C->remove_macro_node(call);
}
// The call is marked as pure (no important side effects), but result isn't used. // It's safe to remove the call. bool result_not_used = (callprojs.resproj == NULL || callprojs.resproj->outcnt() == 0);
if (is_pure_call() && result_not_used) {
GraphKit kit(call->jvms());
kit.replace_call(call, C->top(), true);
} else { // Make a clone of the JVMState that appropriate to use for driving a parse
JVMState* old_jvms = call->jvms();
JVMState* jvms = old_jvms->clone_shallow(C);
uint size = call->req();
SafePointNode* map = new SafePointNode(size, jvms); for (uint i1 = 0; i1 < size; i1++) {
map->init_req(i1, call->in(i1));
}
// Make sure the state is a MergeMem for parsing. if (!map->in(TypeFunc::Memory)->is_MergeMem()) {
Node* mem = MergeMemNode::make(map->in(TypeFunc::Memory));
C->initial_gvn()->set_type_bottom(mem);
map->set_req(TypeFunc::Memory, mem);
}
uint nargs = method()->arg_size(); // blow away old call arguments
Node* top = C->top(); for (uint i1 = 0; i1 < nargs; i1++) {
map->set_req(TypeFunc::Parms + i1, top);
}
jvms->set_map(map);
// Make enough space in the expression stack to transfer // the incoming arguments and return value.
map->ensure_stack(jvms, jvms->method()->max_stack()); for (uint i1 = 0; i1 < nargs; i1++) {
map->set_argument(jvms, i1, call->in(TypeFunc::Parms + i1));
}
C->print_inlining_assert_ready();
C->print_inlining_move_to(this);
C->log_late_inline(this);
// JVMState is ready, so time to perform some checks and prepare for inlining attempt. if (!do_late_inline_check(C, jvms)) {
map->disconnect_inputs(C);
C->print_inlining_update_delayed(this); return;
}
// Setup default node notes to be picked up by the inlining
Node_Notes* old_nn = C->node_notes_at(call->_idx); if (old_nn != NULL) {
Node_Notes* entry_nn = old_nn->clone(C);
entry_nn->set_jvms(jvms);
C->set_default_node_notes(entry_nn);
}
// Now perform the inlining using the synthesized JVMState
JVMState* new_jvms = inline_cg()->generate(jvms); if (new_jvms == NULL) return; // no change if (C->failing()) return;
// Capture any exceptional control flow
GraphKit kit(new_jvms);
// Find the result object
Node* result = C->top(); int result_size = method()->return_type()->size(); if (result_size != 0 && !kit.stopped()) {
result = (result_size == 1) ? kit.pop() : kit.pop_pair();
}
//------------------------PredictedCallGenerator------------------------------ // Internal class which handles all out-of-line calls checking receiver type. class PredictedCallGenerator : public CallGenerator {
ciKlass* _predicted_receiver;
CallGenerator* _if_missed;
CallGenerator* _if_hit; float _hit_prob; bool _exact_check;
public:
PredictedCallGenerator(ciKlass* predicted_receiver,
CallGenerator* if_missed,
CallGenerator* if_hit, bool exact_check, float hit_prob)
: CallGenerator(if_missed->method())
{ // The call profile data may predict the hit_prob as extreme as 0 or 1. // Remove the extremes values from the range. if (hit_prob > PROB_MAX) hit_prob = PROB_MAX; if (hit_prob < PROB_MIN) hit_prob = PROB_MIN;
JVMState* PredictedCallGenerator::generate(JVMState* jvms) {
GraphKit kit(jvms);
kit.C->print_inlining_update(this);
PhaseGVN& gvn = kit.gvn(); // We need an explicit receiver null_check before checking its type. // We share a map with the caller, so his JVMS gets adjusted.
Node* receiver = kit.argument(0);
CompileLog* log = kit.C->log(); if (log != NULL) {
log->elem("predicted_call bci='%d' exact='%d' klass='%d'",
jvms->bci(), (_exact_check ? 1 : 0), log->identify(_predicted_receiver));
}
receiver = kit.null_check_receiver_before_call(method()); if (kit.stopped()) { return kit.transfer_exceptions_into_jvms();
}
// Make a copy of the replaced nodes in case we need to restore them
ReplacedNodes replaced_nodes = kit.map()->replaced_nodes();
replaced_nodes.clone();
Node* casted_receiver = receiver; // will get updated in place...
Node* slow_ctl = NULL; if (_exact_check) {
slow_ctl = kit.type_check_receiver(receiver, _predicted_receiver, _hit_prob,
&casted_receiver);
} else {
slow_ctl = kit.subtype_check_receiver(receiver, _predicted_receiver,
&casted_receiver);
}
SafePointNode* slow_map = NULL;
JVMState* slow_jvms = NULL;
{ PreserveJVMState pjvms(&kit);
kit.set_control(slow_ctl); if (!kit.stopped()) {
slow_jvms = _if_missed->generate(kit.sync_jvms()); if (kit.failing()) return NULL; // might happen because of NodeCountInliningCutoff
assert(slow_jvms != NULL, "must be");
kit.add_exception_states_from(slow_jvms);
kit.set_map(slow_jvms->map()); if (!kit.stopped())
slow_map = kit.stop();
}
}
if (kit.stopped()) { // Instance does not match the predicted type.
kit.set_jvms(slow_jvms); return kit.transfer_exceptions_into_jvms();
}
// Fall through if the instance matches the desired type.
kit.replace_in_map(receiver, casted_receiver);
// Make the hot call:
JVMState* new_jvms = _if_hit->generate(kit.sync_jvms()); if (new_jvms == NULL) { // Inline failed, so make a direct call.
assert(_if_hit->is_inline(), "must have been a failed inline");
CallGenerator* cg = CallGenerator::for_direct_call(_if_hit->method());
new_jvms = cg->generate(kit.sync_jvms());
}
kit.add_exception_states_from(new_jvms);
kit.set_jvms(new_jvms);
// Need to merge slow and fast? if (slow_map == NULL) { // The fast path is the only path remaining. return kit.transfer_exceptions_into_jvms();
}
if (kit.stopped()) { // Inlined method threw an exception, so it's just the slow path after all.
kit.set_jvms(slow_jvms); return kit.transfer_exceptions_into_jvms();
}
// There are 2 branches and the replaced nodes are only valid on // one: restore the replaced nodes to what they were before the // branch.
kit.map()->set_replaced_nodes(replaced_nodes);
// Finish the diamond.
kit.C->set_has_split_ifs(true); // Has chance for split-if optimization
RegionNode* region = new RegionNode(3);
region->init_req(1, kit.control());
region->init_req(2, slow_map->control());
kit.set_control(gvn.transform(region));
Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO);
iophi->set_req(2, slow_map->i_o());
kit.set_i_o(gvn.transform(iophi)); // Merge memory
kit.merge_memory(slow_map->merged_memory(), region, 2); // Transform new memory Phis. for (MergeMemStream mms(kit.merged_memory()); mms.next_non_empty();) {
Node* phi = mms.memory(); if (phi->is_Phi() && phi->in(0) == region) {
mms.set_memory(gvn.transform(phi));
}
}
uint tos = kit.jvms()->stkoff() + kit.sp();
uint limit = slow_map->req(); for (uint i = TypeFunc::Parms; i < limit; i++) { // Skip unused stack slots; fast forward to monoff(); if (i == tos) {
i = kit.jvms()->monoff(); if( i >= limit ) break;
}
Node* m = kit.map()->in(i);
Node* n = slow_map->in(i); if (m != n) { const Type* t = gvn.type(m)->meet_speculative(gvn.type(n));
Node* phi = PhiNode::make(region, m, t);
phi->set_req(2, n);
kit.map()->set_req(i, gvn.transform(phi));
}
} return kit.transfer_exceptions_into_jvms();
}
// In lambda forms we erase signature types to avoid resolving issues // involving class loaders. When we optimize a method handle invoke // to a direct call we must cast the receiver and arguments to its // actual types.
ciSignature* signature = target->signature(); constint receiver_skip = target->is_static() ? 0 : 1; // Cast receiver to its type. if (!target->is_static()) {
Node* arg = kit.argument(0); const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr(); const Type* sig_type = TypeOopPtr::make_from_klass(signature->accessing_klass()); if (arg_type != NULL && !arg_type->higher_equal(sig_type)) { const Type* recv_type = arg_type->filter_speculative(sig_type); // keep speculative part
Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, recv_type));
kit.set_argument(0, cast_obj);
}
} // 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()) {
Node* arg = kit.argument(receiver_skip + j); const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr(); const Type* sig_type = TypeOopPtr::make_from_klass(t->as_klass()); if (arg_type != NULL && !arg_type->higher_equal(sig_type)) { const Type* narrowed_arg_type = arg_type->filter_speculative(sig_type); // keep speculative part
Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, narrowed_arg_type));
kit.set_argument(receiver_skip + j, cast_obj);
}
}
j += t->size(); // long and double take two slots
}
// Try to get the most accurate receiver type constbool is_virtual = (iid == vmIntrinsics::_linkToVirtual); constbool is_virtual_or_interface = (is_virtual || iid == vmIntrinsics::_linkToInterface); int vtable_index = Method::invalid_vtable_index; bool call_does_dispatch = false;
ciKlass* speculative_receiver_type = NULL; if (is_virtual_or_interface) {
ciInstanceKlass* klass = target->holder();
Node* receiver_node = kit.argument(0); const TypeOopPtr* receiver_type = gvn.type(receiver_node)->isa_oopptr(); // call_does_dispatch and vtable_index are out-parameters. They might be changed. // optimize_virtual_call() takes 2 different holder // arguments for a corner case that doesn't apply here (see // Parse::do_call())
target = C->optimize_virtual_call(caller, klass, klass,
target, receiver_type, is_virtual,
call_does_dispatch, vtable_index, // out-parameters false/* check_access */); // We lack profiling at this call but type speculation may // provide us with a type
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
}
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
allow_inline,
PROB_ALWAYS,
speculative_receiver_type); return cg;
} else {
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(), "member_name not constant");
}
} break;
//------------------------PredicatedIntrinsicGenerator------------------------------ // Internal class which handles all predicated Intrinsic calls. class PredicatedIntrinsicGenerator : public CallGenerator {
CallGenerator* _intrinsic;
CallGenerator* _cg;
JVMState* PredicatedIntrinsicGenerator::generate(JVMState* jvms) { // The code we want to generate here is: // if (receiver == NULL) // uncommon_Trap // if (predicate(0)) // do_intrinsic(0) // else // if (predicate(1)) // do_intrinsic(1) // ... // else // do_java_comp
if (!method()->is_static()) { // We need an explicit receiver null_check before checking its type in predicate. // We share a map with the caller, so his JVMS gets adjusted.
Node* receiver = kit.null_check_receiver_before_call(method()); if (kit.stopped()) { return kit.transfer_exceptions_into_jvms();
}
}
int n_predicates = _intrinsic->predicates_count();
assert(n_predicates > 0, "sanity");
// Region for normal compilation code if intrinsic failed.
Node* slow_region = new RegionNode(1);
int results = 0; for (int predicate = 0; (predicate < n_predicates) && !kit.stopped(); predicate++) { #ifdef ASSERT
JVMState* old_jvms = kit.jvms();
SafePointNode* old_map = kit.map();
Node* old_io = old_map->i_o();
Node* old_mem = old_map->memory();
Node* old_exc = old_map->next_exception(); #endif
Node* else_ctrl = _intrinsic->generate_predicate(kit.sync_jvms(), predicate); #ifdef ASSERT // Assert(no_new_memory && no_new_io && no_new_exceptions) after generate_predicate.
assert(old_jvms == kit.jvms(), "generate_predicate should not change jvm state");
SafePointNode* new_map = kit.map();
assert(old_io == new_map->i_o(), "generate_predicate should not change i_o");
assert(old_mem == new_map->memory(), "generate_predicate should not change memory");
assert(old_exc == new_map->next_exception(), "generate_predicate should not add exceptions"); #endif if (!kit.stopped()) {
PreserveJVMState pjvms(&kit); // Generate intrinsic code:
JVMState* new_jvms = _intrinsic->generate(kit.sync_jvms()); if (new_jvms == NULL) { // Intrinsic failed, use normal compilation path for this predicate.
slow_region->add_req(kit.control());
} else {
kit.add_exception_states_from(new_jvms);
kit.set_jvms(new_jvms); if (!kit.stopped()) {
result_jvms[results++] = kit.jvms();
}
}
} if (else_ctrl == NULL) {
else_ctrl = kit.C->top();
}
kit.set_control(else_ctrl);
} if (!kit.stopped()) { // Final 'else' after predicates.
slow_region->add_req(kit.control());
} if (slow_region->req() > 1) {
PreserveJVMState pjvms(&kit); // Generate normal compilation code:
kit.set_control(gvn.transform(slow_region));
JVMState* new_jvms = _cg->generate(kit.sync_jvms()); if (kit.failing()) return NULL; // might happen because of NodeCountInliningCutoff
assert(new_jvms != NULL, "must be");
kit.add_exception_states_from(new_jvms);
kit.set_jvms(new_jvms); if (!kit.stopped()) {
result_jvms[results++] = kit.jvms();
}
}
if (results == 0) { // All paths ended in uncommon traps.
(void) kit.stop(); return kit.transfer_exceptions_into_jvms();
}
if (results == 1) { // Only one path
kit.set_jvms(result_jvms[0]); return kit.transfer_exceptions_into_jvms();
}
// Merge all paths.
kit.C->set_has_split_ifs(true); // Has chance for split-if optimization
RegionNode* region = new RegionNode(results + 1);
Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO); for (int i = 0; i < results; i++) {
JVMState* jvms = result_jvms[i]; int path = i + 1;
SafePointNode* map = jvms->map();
region->init_req(path, map->control());
iophi->set_req(path, map->i_o()); if (i == 0) {
kit.set_jvms(jvms);
} else {
kit.merge_memory(map->merged_memory(), region, path);
}
}
kit.set_control(gvn.transform(region));
kit.set_i_o(gvn.transform(iophi)); // Transform new memory Phis. for (MergeMemStream mms(kit.merged_memory()); mms.next_non_empty();) {
Node* phi = mms.memory(); if (phi->is_Phi() && phi->in(0) == region) {
mms.set_memory(gvn.transform(phi));
}
}
// Merge debug info.
Node** ins = NEW_RESOURCE_ARRAY(Node*, results);
uint tos = kit.jvms()->stkoff() + kit.sp();
Node* map = kit.map();
uint limit = map->req(); for (uint i = TypeFunc::Parms; i < limit; i++) { // Skip unused stack slots; fast forward to monoff(); if (i == tos) {
i = kit.jvms()->monoff(); if( i >= limit ) break;
}
Node* n = map->in(i);
ins[0] = n; const Type* t = gvn.type(n); bool needs_phi = false; for (int j = 1; j < results; j++) {
JVMState* jvms = result_jvms[j];
Node* jmap = jvms->map();
Node* m = NULL; if (jmap->req() > i) {
m = jmap->in(i); if (m != n) {
needs_phi = true;
t = t->meet_speculative(gvn.type(m));
}
}
ins[j] = m;
} if (needs_phi) {
Node* phi = PhiNode::make(region, n, t); for (int j = 1; j < results; j++) {
phi->set_req(j + 1, ins[j]);
}
map->set_req(i, gvn.transform(phi));
}
}
return kit.transfer_exceptions_into_jvms();
}
//-------------------------UncommonTrapCallGenerator----------------------------- // Internal class which handles all out-of-line calls checking receiver type. class UncommonTrapCallGenerator : public CallGenerator {
Deoptimization::DeoptReason _reason;
Deoptimization::DeoptAction _action;
JVMState* UncommonTrapCallGenerator::generate(JVMState* jvms) {
GraphKit kit(jvms);
kit.C->print_inlining_update(this); // Take the trap with arguments pushed on the stack. (Cf. null_check_receiver). // Callsite signature can be different from actual method being called (i.e _linkTo* sites). // Use callsite signature always.
ciMethod* declared_method = kit.method()->get_method_at_bci(kit.bci()); int nargs = declared_method->arg_size();
kit.inc_sp(nargs);
assert(nargs <= kit.sp() && kit.sp() <= jvms->stk_size(), "sane sp w/ args pushed"); if (_reason == Deoptimization::Reason_class_check &&
_action == Deoptimization::Action_maybe_recompile) { // Temp fix for 6529811 // Don't allow uncommon_trap to override our decision to recompile in the event // of a class cast failure for a monomorphic call as it will never let us convert // the call to either bi-morphic or megamorphic and can lead to unc-trap loops bool keep_exact_action = true;
kit.uncommon_trap(_reason, _action, NULL, "monomorphic vcall checkcast", false, keep_exact_action);
} else {
kit.uncommon_trap(_reason, _action);
} return kit.transfer_exceptions_into_jvms();
}
// (Note: Moved hook_up_call to GraphKit::set_edges_for_java_call.)
// (Node: Merged hook_up_exits into ParseGenerator::generate.)
Messung V0.5
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.