/* * Copyright (c) 2001, 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. *
*/
//---------------------------clean_stack--------------------------------------- // Clear away rubbish from the stack area of the JVM state. // This destroys any arguments that may be waiting on the stack. void GraphKit::clean_stack(int from_sp) {
SafePointNode* map = this->map();
JVMState* jvms = this->jvms(); int stk_size = jvms->stk_size(); int stkoff = jvms->stkoff();
Node* top = this->top(); for (int i = from_sp; i < stk_size; i++) { if (map->in(stkoff + i) != top) {
map->set_req(stkoff + i, top);
}
}
}
//--------------------------------sync_jvms----------------------------------- // Make sure our current jvms agrees with our parse state.
JVMState* GraphKit::sync_jvms() const {
JVMState* jvms = this->jvms();
jvms->set_bci(bci()); // Record the new bci in the JVMState
jvms->set_sp(sp()); // Record the new sp in the JVMState
assert(jvms_in_sync(), "jvms is now in sync"); return jvms;
}
//--------------------------------sync_jvms_for_reexecute--------------------- // Make sure our current jvms agrees with our parse state. This version // uses the reexecute_sp for reexecuting bytecodes.
JVMState* GraphKit::sync_jvms_for_reexecute() {
JVMState* jvms = this->jvms();
jvms->set_bci(bci()); // Record the new bci in the JVMState
jvms->set_sp(reexecute_sp()); // Record the new sp in the JVMState return jvms;
}
#ifdef ASSERT bool GraphKit::jvms_in_sync() const {
Parse* parse = is_Parse(); if (parse == NULL) { if (bci() != jvms()->bci()) returnfalse; if (sp() != (int)jvms()->sp()) returnfalse; returntrue;
} if (jvms()->method() != parse->method()) returnfalse; if (jvms()->bci() != parse->bci()) returnfalse; int jvms_sp = jvms()->sp(); if (jvms_sp != parse->sp()) returnfalse; int jvms_depth = jvms()->depth(); if (jvms_depth != parse->depth()) returnfalse; returntrue;
}
// Local helper checks for special internal merge points // used to accumulate and merge exception states. // They are marked by the region's in(0) edge being the map itself. // Such merge points must never "escape" into the parser at large, // until they have been handed to gvn.transform. staticbool is_hidden_merge(Node* reg) { if (reg == NULL) returnfalse; if (reg->is_Phi()) {
reg = reg->in(0); if (reg == NULL) returnfalse;
} return reg->is_Region() && reg->in(0) != NULL && reg->in(0)->is_Root();
}
void GraphKit::verify_map() const { if (map() == NULL) return; // null map is OK
assert(map()->req() <= jvms()->endoff(), "no extra garbage on map");
assert(!map()->has_exceptions(), "call add_exception_states_from 1st");
assert(!is_hidden_merge(control()), "call use_exception_state, not set_map");
}
void GraphKit::verify_exception_state(SafePointNode* ex_map) {
assert(ex_map->next_exception() == NULL, "not already part of a chain");
assert(has_saved_ex_oop(ex_map), "every exception state has an ex_oop");
} #endif
//---------------------------stop_and_kill_map--------------------------------- // Set _map to NULL, signalling a stop to further bytecode execution. // First smash the current map's control to a constant, to mark it dead. void GraphKit::stop_and_kill_map() {
SafePointNode* dead_map = stop(); if (dead_map != NULL) {
dead_map->disconnect_inputs(C); // Mark the map as killed.
assert(dead_map->is_killed(), "must be so marked");
}
}
//--------------------------------stopped-------------------------------------- // Tell if _map is NULL, or control is top. bool GraphKit::stopped() { if (map() == NULL) returntrue; elseif (control() == top()) returntrue; elsereturnfalse;
}
//-----------------------------has_ex_handler---------------------------------- // Tell if this method or any caller method has exception handlers. bool GraphKit::has_ex_handler() { for (JVMState* jvmsp = jvms(); jvmsp != NULL; jvmsp = jvmsp->caller()) { if (jvmsp->has_method() && jvmsp->method()->has_exception_handlers()) { returntrue;
}
} returnfalse;
}
//------------------------------save_ex_oop------------------------------------ // Save an exception without blowing stack contents or other JVM state. void GraphKit::set_saved_ex_oop(SafePointNode* ex_map, Node* ex_oop) {
assert(!has_saved_ex_oop(ex_map), "clear ex-oop before setting again");
ex_map->add_req(ex_oop);
debug_only(verify_exception_state(ex_map));
}
inlinestatic Node* common_saved_ex_oop(SafePointNode* ex_map, bool clear_it) {
assert(GraphKit::has_saved_ex_oop(ex_map), "ex_oop must be there");
Node* ex_oop = ex_map->in(ex_map->req()-1); if (clear_it) ex_map->del_req(ex_map->req()-1); return ex_oop;
}
//-----------------------------saved_ex_oop------------------------------------ // Recover a saved exception from its map.
Node* GraphKit::saved_ex_oop(SafePointNode* ex_map) { return common_saved_ex_oop(ex_map, false);
}
//--------------------------clear_saved_ex_oop--------------------------------- // Erase a previously saved exception from its map.
Node* GraphKit::clear_saved_ex_oop(SafePointNode* ex_map) { return common_saved_ex_oop(ex_map, true);
}
#ifdef ASSERT //---------------------------has_saved_ex_oop---------------------------------- // Erase a previously saved exception from its map. bool GraphKit::has_saved_ex_oop(SafePointNode* ex_map) { return ex_map->req() == ex_map->jvms()->endoff()+1;
} #endif
//-------------------------make_exception_state-------------------------------- // Turn the current JVM state into an exception state, appending the ex_oop.
SafePointNode* GraphKit::make_exception_state(Node* ex_oop) {
sync_jvms();
SafePointNode* ex_map = stop(); // do not manipulate this map any more
set_saved_ex_oop(ex_map, ex_oop); return ex_map;
}
//--------------------------add_exception_state-------------------------------- // Add an exception to my list of exceptions. void GraphKit::add_exception_state(SafePointNode* ex_map) { if (ex_map == NULL || ex_map->control() == top()) { return;
} #ifdef ASSERT
verify_exception_state(ex_map); if (has_exceptions()) {
assert(ex_map->jvms()->same_calls_as(_exceptions->jvms()), "all collected exceptions must come from the same place");
} #endif
// If there is already an exception of exactly this type, merge with it. // In particular, null-checks and other low-level exceptions common up here.
Node* ex_oop = saved_ex_oop(ex_map); const Type* ex_type = _gvn.type(ex_oop); if (ex_oop == top()) { // No action needed. return;
}
assert(ex_type->isa_instptr(), "exception must be an instance"); for (SafePointNode* e2 = _exceptions; e2 != NULL; e2 = e2->next_exception()) { const Type* ex_type2 = _gvn.type(saved_ex_oop(e2)); // We check sp also because call bytecodes can generate exceptions // both before and after arguments are popped! if (ex_type2 == ex_type
&& e2->_jvms->sp() == ex_map->_jvms->sp()) {
combine_exception_states(ex_map, e2); return;
}
}
// No pre-existing exception of the same type. Chain it on the list.
push_exception_state(ex_map);
}
//-----------------------transfer_exceptions_into_jvms-------------------------
JVMState* GraphKit::transfer_exceptions_into_jvms() { if (map() == NULL) { // We need a JVMS to carry the exceptions, but the map has gone away. // Create a scratch JVMS, cloned from any of the exception states... if (has_exceptions()) {
_map = _exceptions;
_map = clone_map();
_map->set_next_exception(NULL);
clear_saved_ex_oop(_map);
debug_only(verify_map());
} else { // ...or created from scratch
JVMState* jvms = new (C) JVMState(_method, NULL);
jvms->set_bci(_bci);
jvms->set_sp(_sp);
jvms->set_map(new SafePointNode(TypeFunc::Parms, jvms));
set_jvms(jvms); for (uint i = 0; i < map()->req(); i++) map()->init_req(i, top());
set_all_memory(top()); while (map()->req() < jvms->endoff()) map()->add_req(top());
} // (This is a kludge, in case you didn't notice.)
set_control(top());
}
JVMState* jvms = sync_jvms();
assert(!jvms->map()->has_exceptions(), "no exceptions on this map yet");
jvms->map()->set_next_exception(_exceptions);
_exceptions = NULL; // done with this set of exceptions return jvms;
}
staticinlinevoid add_n_reqs(Node* dstphi, Node* srcphi) {
assert(is_hidden_merge(dstphi), "must be a special merge node");
assert(is_hidden_merge(srcphi), "must be a special merge node");
uint limit = srcphi->req(); for (uint i = PhiNode::Input; i < limit; i++) {
dstphi->add_req(srcphi->in(i));
}
} staticinlinevoid add_one_req(Node* dstphi, Node* src) {
assert(is_hidden_merge(dstphi), "must be a special merge node");
assert(!is_hidden_merge(src), "must not be a special merge node");
dstphi->add_req(src);
}
//-----------------------combine_exception_states------------------------------ // This helper function combines exception states by building phis on a // specially marked state-merging region. These regions and phis are // untransformed, and can build up gradually. The region is marked by // having a control input of its exception map, rather than NULL. Such // regions do not appear except in this function, and in use_exception_state. void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map) { if (failing()) return; // dying anyway...
JVMState* ex_jvms = ex_map->_jvms;
assert(ex_jvms->same_calls_as(phi_map->_jvms), "consistent call chains");
assert(ex_jvms->stkoff() == phi_map->_jvms->stkoff(), "matching locals");
assert(ex_jvms->sp() == phi_map->_jvms->sp(), "matching stack sizes");
assert(ex_jvms->monoff() == phi_map->_jvms->monoff(), "matching JVMS");
assert(ex_jvms->scloff() == phi_map->_jvms->scloff(), "matching scalar replaced objects");
assert(ex_map->req() == phi_map->req(), "matching maps");
uint tos = ex_jvms->stkoff() + ex_jvms->sp();
Node* hidden_merge_mark = root();
Node* region = phi_map->control();
MergeMemNode* phi_mem = phi_map->merged_memory();
MergeMemNode* ex_mem = ex_map->merged_memory(); if (region->in(0) != hidden_merge_mark) { // The control input is not (yet) a specially-marked region in phi_map. // Make it so, and build some phis.
region = new RegionNode(2);
_gvn.set_type(region, Type::CONTROL);
region->set_req(0, hidden_merge_mark); // marks an internal ex-state
region->init_req(1, phi_map->control());
phi_map->set_control(region);
Node* io_phi = PhiNode::make(region, phi_map->i_o(), Type::ABIO);
record_for_igvn(io_phi);
_gvn.set_type(io_phi, Type::ABIO);
phi_map->set_i_o(io_phi); for (MergeMemStream mms(phi_mem); mms.next_non_empty(); ) {
Node* m = mms.memory();
Node* m_phi = PhiNode::make(region, m, Type::MEMORY, mms.adr_type(C));
record_for_igvn(m_phi);
_gvn.set_type(m_phi, Type::MEMORY);
mms.set_memory(m_phi);
}
}
// Either or both of phi_map and ex_map might already be converted into phis.
Node* ex_control = ex_map->control(); // if there is special marking on ex_map also, we add multiple edges from src bool add_multiple = (ex_control->in(0) == hidden_merge_mark); // how wide was the destination phi_map, originally?
uint orig_width = region->req();
if (add_multiple) {
add_n_reqs(region, ex_control);
add_n_reqs(phi_map->i_o(), ex_map->i_o());
} else { // ex_map has no merges, so we just add single edges everywhere
add_one_req(region, ex_control);
add_one_req(phi_map->i_o(), ex_map->i_o());
} for (MergeMemStream mms(phi_mem, ex_mem); mms.next_non_empty2(); ) { if (mms.is_empty()) { // get a copy of the base memory, and patch some inputs into it const TypePtr* adr_type = mms.adr_type(C);
Node* phi = mms.force_memory()->as_Phi()->slice_memory(adr_type);
assert(phi->as_Phi()->region() == mms.base_memory()->in(0), "");
mms.set_memory(phi); // Prepare to append interesting stuff onto the newly sliced phi: while (phi->req() > orig_width) phi->del_req(phi->req()-1);
} // Append stuff from ex_map: if (add_multiple) {
add_n_reqs(mms.memory(), mms.memory2());
} else {
add_one_req(mms.memory(), mms.memory2());
}
}
uint limit = ex_map->req(); for (uint i = TypeFunc::Parms; i < limit; i++) { // Skip everything in the JVMS after tos. (The ex_oop follows.) if (i == tos) i = ex_jvms->monoff();
Node* src = ex_map->in(i);
Node* dst = phi_map->in(i); if (src != dst) {
PhiNode* phi; if (dst->in(0) != region) {
dst = phi = PhiNode::make(region, dst, _gvn.type(dst));
record_for_igvn(phi);
_gvn.set_type(phi, phi->type());
phi_map->set_req(i, dst); // Prepare to append interesting stuff onto the new phi: while (dst->req() > orig_width) dst->del_req(dst->req()-1);
} else {
assert(dst->is_Phi(), "nobody else uses a hidden region");
phi = dst->as_Phi();
} if (add_multiple && src->in(0) == ex_control) { // Both are phis.
add_n_reqs(dst, src);
} else { while (dst->req() < region->req()) add_one_req(dst, src);
} const Type* srctype = _gvn.type(src); if (phi->type() != srctype) { const Type* dsttype = phi->type()->meet_speculative(srctype); if (phi->type() != dsttype) {
phi->set_type(dsttype);
_gvn.set_type(phi, dsttype);
}
}
}
}
phi_map->merge_replaced_nodes_with(ex_map);
}
//--------------------------use_exception_state--------------------------------
Node* GraphKit::use_exception_state(SafePointNode* phi_map) { if (failing()) { stop(); return top(); }
Node* region = phi_map->control();
Node* hidden_merge_mark = root();
assert(phi_map->jvms()->map() == phi_map, "sanity: 1-1 relation");
Node* ex_oop = clear_saved_ex_oop(phi_map); if (region->in(0) == hidden_merge_mark) { // Special marking for internal ex-states. Process the phis now.
region->set_req(0, region); // now it's an ordinary region
set_jvms(phi_map->jvms()); // ...so now we can use it as a map // Note: Setting the jvms also sets the bci and sp.
set_control(_gvn.transform(region));
uint tos = jvms()->stkoff() + sp(); for (uint i = 1; i < tos; i++) {
Node* x = phi_map->in(i); if (x->in(0) == region) {
assert(x->is_Phi(), "expected a special phi");
phi_map->set_req(i, _gvn.transform(x));
}
} for (MergeMemStream mms(merged_memory()); mms.next_non_empty(); ) {
Node* x = mms.memory(); if (x->in(0) == region) {
assert(x->is_Phi(), "nobody else uses a hidden region");
mms.set_memory(_gvn.transform(x));
}
} if (ex_oop->in(0) == region) {
assert(ex_oop->is_Phi(), "expected a special phi");
ex_oop = _gvn.transform(ex_oop);
}
} else {
set_jvms(phi_map->jvms());
}
assert(!is_hidden_merge(phi_map->control()), "hidden ex. states cleared");
assert(!is_hidden_merge(phi_map->i_o()), "hidden ex. states cleared"); return ex_oop;
}
void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptReason reason, bool must_throw) { // if the exception capability is set, then we will generate code // to check the JavaThread.should_post_on_exceptions flag to see // if we actually need to report exception events (for this // thread). If we don't need to report exception events, we will // take the normal fast path provided by add_exception_events. If // exception event reporting is enabled for this thread, we will // take the uncommon_trap in the BuildCutout below.
// first must access the should_post_on_exceptions_flag in this thread's JavaThread
Node* jthread = _gvn.transform(new ThreadLocalNode());
Node* adr = basic_plus_adr(top(), jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset()));
Node* should_post_flag = make_load(control(), adr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, MemNode::unordered);
// Test the should_post_on_exceptions_flag vs. 0
Node* chk = _gvn.transform( new CmpINode(should_post_flag, intcon(0)) );
Node* tst = _gvn.transform( new BoolNode(chk, BoolTest::eq) );
// Branch to slow_path if should_post_on_exceptions_flag was true
{ BuildCutout unless(this, tst, PROB_MAX); // Do not try anything fancy if we're notifying the VM on every throw. // Cf. case Bytecodes::_athrow in parse2.cpp.
uncommon_trap(reason, Deoptimization::Action_none,
(ciKlass*)NULL, (char*)NULL, must_throw);
}
// If this particular condition has not yet happened at this // bytecode, then use the uncommon trap mechanism, and allow for // a future recompilation if several traps occur here. // If the throw is hot, try to use a more complicated inline mechanism // which keeps execution inside the compiled code. bool treat_throw_as_hot = false;
ciMethodData* md = method()->method_data();
if (ProfileTraps) { if (too_many_traps(reason)) {
treat_throw_as_hot = true;
} // (If there is no MDO at all, assume it is early in // execution, and that any deopts are part of the // startup transient, and don't need to be remembered.)
// Also, if there is a local exception handler, treat all throws // as hot if there has been at least one in this method. if (C->trap_count(reason) != 0
&& method()->method_data()->trap_count(reason) != 0
&& has_ex_handler()) {
treat_throw_as_hot = true;
}
}
// If this throw happens frequently, an uncommon trap might cause // a performance pothole. If there is a local exception handler, // and if this particular bytecode appears to be deoptimizing often, // let us handle the throw inline, with a preconstructed instance. // Note: If the deopt count has blown up, the uncommon trap // runtime is going to flush this nmethod, not matter what. if (treat_throw_as_hot && method()->can_omit_stack_trace()) { // If the throw is local, we use a pre-existing instance and // punt on the backtrace. This would lead to a missing backtrace // (a repeat of 4292742) if the backtrace object is ever asked // for its backtrace. // Fixing this remaining case of 4292742 requires some flavor of // escape analysis. Leave that for the future.
ciInstance* ex_obj = NULL; switch (reason) { case Deoptimization::Reason_null_check:
ex_obj = env()->NullPointerException_instance(); break; case Deoptimization::Reason_div0_check:
ex_obj = env()->ArithmeticException_instance(); break; case Deoptimization::Reason_range_check:
ex_obj = env()->ArrayIndexOutOfBoundsException_instance(); break; case Deoptimization::Reason_class_check:
ex_obj = env()->ClassCastException_instance(); break; case Deoptimization::Reason_array_check:
ex_obj = env()->ArrayStoreException_instance(); break; default: break;
} if (failing()) { stop(); return; } // exception allocation might fail if (ex_obj != NULL) { if (env()->jvmti_can_post_on_exceptions()) { // check if we must post exception events, take uncommon trap if so
uncommon_trap_if_should_post_on_exceptions(reason, must_throw); // here if should_post_on_exceptions is false // continue on with the normal codegen
}
// Cheat with a preallocated exception object. if (C->log() != NULL)
C->log()->elem("hot_throw preallocated='1' reason='%s'",
Deoptimization::trap_reason_name(reason)); const TypeInstPtr* ex_con = TypeInstPtr::make(ex_obj);
Node* ex_node = _gvn.transform(ConNode::make(ex_con));
// Clear the detail message of the preallocated exception object. // Weblogic sometimes mutates the detail message of exceptions // using reflection. int offset = java_lang_Throwable::get_detailMessage_offset(); const TypePtr* adr_typ = ex_con->add_offset(offset);
if (!method()->has_exception_handlers()) { // We don't need to preserve the stack if there's no handler as the entire frame is going to be popped anyway. // This prevents issues with exception handling and late inlining.
set_sp(0);
clean_stack(0);
}
// %%% Maybe add entry to OptoRuntime which directly throws the exc.? // It won't be much cheaper than bailing to the interp., since we'll // have to pass up all the debug-info, and the runtime will have to // create the stack trace.
// Usual case: Bail to interpreter. // Reserve the right to recompile if we haven't seen anything yet.
ciMethod* m = Deoptimization::reason_is_speculate(reason) ? C->method() : NULL;
Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile; if (treat_throw_as_hot
&& (method()->method_data()->trap_recompiled_at(bci(), m)
|| C->too_many_traps(reason))) { // We cannot afford to take more traps here. Suffer in the interpreter. if (C->log() != NULL)
C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'",
Deoptimization::trap_reason_name(reason),
C->trap_count(reason));
action = Deoptimization::Action_none;
}
// "must_throw" prunes the JVM state to include only the stack, if there // are no local exception handlers. This should cut down on register // allocation time and code size, by drastically reducing the number // of in-edges on the call to the uncommon trap.
//------------------------------clone_map-------------------------------------- // Implementation of PreserveJVMState // // Only clone_map(...) here. If this function is only used in the // PreserveJVMState class we may want to get rid of this extra // function eventually and do it all there.
SafePointNode* GraphKit::clone_map() { if (map() == NULL) return NULL;
// Clone the memory edge first
Node* mem = MergeMemNode::make(map()->memory());
gvn().set_type_bottom(mem);
//-----------------------------set_map_clone----------------------------------- void GraphKit::set_map_clone(SafePointNode* m) {
_map = m;
_map = clone_map();
_map->set_next_exception(NULL);
debug_only(verify_map());
}
//----------------------------kill_dead_locals--------------------------------- // Detect any locals which are known to be dead, and force them to top. void GraphKit::kill_dead_locals() { // Consult the liveness information for the locals. If any // of them are unused, then they can be replaced by top(). This // should help register allocation time and cut down on the size // of the deoptimization information.
// This call is made from many of the bytecode handling // subroutines called from the Big Switch in do_one_bytecode. // Every bytecode which might include a slow path is responsible // for killing its dead locals. The more consistent we // are about killing deads, the fewer useless phis will be // constructed for them at various merge points.
// bci can be -1 (InvocationEntryBci). We return the entry // liveness for the method.
if (method() == NULL || method()->code_size() == 0) { // We are building a graph for a call to a native method. // All locals are live. return;
}
ResourceMark rm;
// Consult the liveness information for the locals. If any // of them are unused, then they can be replaced by top(). This // should help register allocation time and cut down on the size // of the deoptimization information.
MethodLivenessResult live_locals = method()->liveness_at_bci(bci());
int len = (int)live_locals.size();
assert(len <= jvms()->loc_size(), "too many live locals"); for (int local = 0; local < len; local++) { if (!live_locals.at(local)) {
set_local(local, top());
}
}
}
#ifdef ASSERT //-------------------------dead_locals_are_killed------------------------------ // Return true if all dead locals are set to top in the map. // Used to assert "clean" debug info at various points. bool GraphKit::dead_locals_are_killed() { if (method() == NULL || method()->code_size() == 0) { // No locals need to be dead, so all is as it should be. returntrue;
}
// Make sure somebody called kill_dead_locals upstream.
ResourceMark rm; for (JVMState* jvms = this->jvms(); jvms != NULL; jvms = jvms->caller()) { if (jvms->loc_size() == 0) continue; // no locals to consult
SafePointNode* map = jvms->map();
ciMethod* method = jvms->method(); int bci = jvms->bci(); if (jvms == this->jvms()) {
bci = this->bci(); // it might not yet be synched
}
MethodLivenessResult live_locals = method->liveness_at_bci(bci); int len = (int)live_locals.size(); if (!live_locals.is_valid() || len == 0) // This method is trivial, or is poisoned by a breakpoint. returntrue;
assert(len == jvms->loc_size(), "live map consistent with locals map"); for (int local = 0; local < len; local++) { if (!live_locals.at(local) && map->local(jvms, local) != top()) { if (PrintMiscellaneous && (Verbose || WizardMode)) {
tty->print_cr("Zombie local %d: ", local);
jvms->dump();
} returnfalse;
}
}
} returntrue;
}
#endif//ASSERT
// Helper function for enforcing certain bytecodes to reexecute if deoptimization happens. staticbool should_reexecute_implied_by_bytecode(JVMState *jvms, bool is_anewarray) {
ciMethod* cur_method = jvms->method(); int cur_bci = jvms->bci(); if (cur_method != NULL && cur_bci != InvocationEntryBci) {
Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci); return Interpreter::bytecode_should_reexecute(code) ||
(is_anewarray && code == Bytecodes::_multianewarray); // Reexecute _multianewarray bytecode which was replaced with // sequence of [a]newarray. See Parse::do_multianewarray(). // // Note: interpreter should not have it set since this optimization // is limited by dimensions and guarded by flag so in some cases // multianewarray() runtime calls will be generated and // the bytecode should not be reexecutes (stack will not be reset).
} else { returnfalse;
}
}
// Helper function for adding JVMState and debug information to node void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { // Add the safepoint edges to the call (or other safepoint).
// Make sure dead locals are set to top. This // should help register allocation time and cut down on the size // of the deoptimization information.
assert(dead_locals_are_killed(), "garbage in debug info before safepoint");
// Walk the inline list to fill in the correct set of JVMState's // Also fill in the associated edges for each JVMState.
// If the bytecode needs to be reexecuted we need to put // the arguments back on the stack. constbool should_reexecute = jvms()->should_reexecute();
JVMState* youngest_jvms = should_reexecute ? sync_jvms_for_reexecute() : sync_jvms();
// NOTE: set_bci (called from sync_jvms) might reset the reexecute bit to // undefined if the bci is different. This is normal for Parse but it // should not happen for LibraryCallKit because only one bci is processed.
assert(!is_LibraryCallKit() || (jvms()->should_reexecute() == should_reexecute), "in LibraryCallKit the reexecute bit should not change");
// If we are guaranteed to throw, we can prune everything but the // input to the current bytecode. bool can_prune_locals = false;
uint stack_slots_not_pruned = 0; int inputs = 0, depth = 0; if (must_throw) {
assert(method() == youngest_jvms->method(), "sanity"); if (compute_stack_effects(inputs, depth)) {
can_prune_locals = true;
stack_slots_not_pruned = inputs;
}
}
if (env()->should_retain_local_variables()) { // At any safepoint, this method can get breakpointed, which would // then require an immediate deoptimization.
can_prune_locals = false; // do not prune locals
stack_slots_not_pruned = 0;
}
// do not scribble on the input jvms
JVMState* out_jvms = youngest_jvms->clone_deep(C);
call->set_jvms(out_jvms); // Start jvms list for call node
// For a known set of bytecodes, the interpreter should reexecute them if // deoptimization happens. We set the reexecute state for them here if (out_jvms->is_reexecute_undefined() && //don't change if already specified
should_reexecute_implied_by_bytecode(out_jvms, call->is_AllocateArray())) { #ifdef ASSERT int inputs = 0, not_used; // initialized by GraphKit::compute_stack_effects()
assert(method() == youngest_jvms->method(), "sanity");
assert(compute_stack_effects(inputs, not_used), "unknown bytecode: %s", Bytecodes::name(java_bc()));
assert(out_jvms->sp() >= (uint)inputs, "not enough operands for reexecution"); #endif// ASSERT
out_jvms->set_should_reexecute(true); //NOTE: youngest_jvms not changed
}
// Set up edges so that the call looks like this: // Call [state:] ctl io mem fptr retadr // [parms:] parm0 ... parmN // [root:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN // [...mid:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN [...] // [young:] loc0 ... locN stk0 ... stkSP mon0 obj0 ... monN objN // Note that caller debug info precedes callee debug info.
// Fill pointer walks backwards from "young:" to "root:" in the diagram above:
uint debug_ptr = call->req();
// Loop over the map input edges associated with jvms, add them // to the call node, & reset all offsets to match call node array. for (JVMState* in_jvms = youngest_jvms; in_jvms != NULL; ) {
uint debug_end = debug_ptr;
uint debug_start = debug_ptr - in_jvms->debug_size();
debug_ptr = debug_start; // back up the ptr
uint p = debug_start; // walks forward in [debug_start, debug_end)
uint j, k, l;
SafePointNode* in_map = in_jvms->map();
out_jvms->set_map(call);
if (can_prune_locals) {
assert(in_jvms->method() == out_jvms->method(), "sanity"); // If the current throw can reach an exception handler in this JVMS, // then we must keep everything live that can reach that handler. // As a quick and dirty approximation, we look for any handlers at all. if (in_jvms->method()->has_exception_handlers()) {
can_prune_locals = false;
}
}
// Add the Locals
k = in_jvms->locoff();
l = in_jvms->loc_size();
out_jvms->set_locoff(p); if (!can_prune_locals) { for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
} else {
p += l; // already set to top above by add_req_batch
}
// Add the Expression Stack
k = in_jvms->stkoff();
l = in_jvms->sp();
out_jvms->set_stkoff(p); if (!can_prune_locals) { for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
} elseif (can_prune_locals && stack_slots_not_pruned != 0) { // Divide stack into {S0,...,S1}, where S0 is set to top.
uint s1 = stack_slots_not_pruned;
stack_slots_not_pruned = 0; // for next iteration if (s1 > l) s1 = l;
uint s0 = l - s1;
p += s0; // skip the tops preinstalled by add_req_batch for (j = s0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
} else {
p += l; // already set to top above by add_req_batch
}
// Add the Monitors
k = in_jvms->monoff();
l = in_jvms->mon_size();
out_jvms->set_monoff(p); for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
// Copy any scalar object fields.
k = in_jvms->scloff();
l = in_jvms->scl_size();
out_jvms->set_scloff(p); for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
// Finish the new jvms.
out_jvms->set_endoff(p);
assert(out_jvms->endoff() == debug_end, "fill ptr must match");
assert(out_jvms->depth() == in_jvms->depth(), "depth must match");
assert(out_jvms->loc_size() == in_jvms->loc_size(), "size must match");
assert(out_jvms->mon_size() == in_jvms->mon_size(), "size must match");
assert(out_jvms->scl_size() == in_jvms->scl_size(), "size must match");
assert(out_jvms->debug_size() == in_jvms->debug_size(), "size must match");
// Update the two tail pointers in parallel.
out_jvms = out_jvms->caller();
in_jvms = in_jvms->caller();
}
assert(debug_ptr == non_debug_edges, "debug info must fit exactly");
// Test the correctness of JVMState::debug_xxx accessors:
assert(call->jvms()->debug_start() == non_debug_edges, "");
assert(call->jvms()->debug_end() == call->req(), "");
assert(call->jvms()->debug_depth() == call->req() - non_debug_edges, "");
}
case Bytecodes::_ireturn: case Bytecodes::_lreturn: case Bytecodes::_freturn: case Bytecodes::_dreturn: case Bytecodes::_areturn:
assert(rsize == -depth, "");
inputs = rsize; break;
case Bytecodes::_jsr: case Bytecodes::_jsr_w:
inputs = 0;
depth = 1; // S.B. depth=1, not zero break;
default: // bytecode produces a typed result
inputs = rsize - depth;
assert(inputs >= 0, ""); break;
}
Node* GraphKit::array_ideal_length(AllocateArrayNode* alloc, const TypeOopPtr* oop_type, bool replace_length_in_map) {
Node* length = alloc->Ideal_length(); if (replace_length_in_map == false || map()->find_edge(length) >= 0) {
Node* ccast = alloc->make_ideal_length(oop_type, &_gvn); if (ccast != length) { // do not transform ccast here, it might convert to top node for // negative array length and break assumptions in parsing stage.
_gvn.set_type_bottom(ccast);
record_for_igvn(ccast); if (replace_length_in_map) {
replace_in_map(length, ccast);
} return ccast;
}
} return length;
}
//------------------------------do_null_check---------------------------------- // Helper function to do a NULL pointer check. Returned value is // the incoming address with NULL casted away. You are allowed to use the // not-null value only if you are control dependent on the test. #ifndef PRODUCT externint explicit_null_checks_inserted,
explicit_null_checks_elided; #endif
Node* GraphKit::null_check_common(Node* value, BasicType type, // optional arguments for variations: bool assert_null,
Node* *null_control, bool speculative) {
assert(!assert_null || null_control == NULL, "not both at once"); if (stopped()) return top();
NOT_PRODUCT(explicit_null_checks_inserted++);
// Construct NULL check
Node *chk = NULL; switch(type) { case T_LONG : chk = new CmpLNode(value, _gvn.zerocon(T_LONG)); break; case T_INT : chk = new CmpINode(value, _gvn.intcon(0)); break; case T_ARRAY : // fall through
type = T_OBJECT; // simplify further tests case T_OBJECT : { const Type *t = _gvn.type( value );
const TypeOopPtr* tp = t->isa_oopptr(); if (tp != NULL && !tp->is_loaded() // Only for do_null_check, not any of its siblings:
&& !assert_null && null_control == NULL) { // Usually, any field access or invocation on an unloaded oop type // will simply fail to link, since the statically linked class is // likely also to be unloaded. However, in -Xcomp mode, sometimes // the static class is loaded but the sharper oop type is not. // Rather than checking for this obscure case in lots of places, // we simply observe that a null check on an unloaded class // will always be followed by a nonsense operation, so we // can just issue the uncommon trap here. // Our access to the unloaded class will only be correct // after it has been loaded and initialized, which requires // a trip through the interpreter.
ciKlass* klass = tp->unloaded_klass(); #ifndef PRODUCT if (WizardMode) { tty->print("Null check of unloaded "); klass->print(); tty->cr(); } #endif
uncommon_trap(Deoptimization::Reason_unloaded,
Deoptimization::Action_reinterpret,
klass, "!loaded"); return top();
}
if (assert_null) { // See if the type is contained in NULL_PTR. // If so, then the value is already null. if (t->higher_equal(TypePtr::NULL_PTR)) {
NOT_PRODUCT(explicit_null_checks_elided++); return value; // Elided null assert quickly!
}
} else { // See if mixing in the NULL pointer changes type. // If so, then the NULL pointer was not allowed in the original // type. In other words, "value" was not-null. if (t->meet(TypePtr::NULL_PTR) != t->remove_speculative()) { // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ...
NOT_PRODUCT(explicit_null_checks_elided++); return value; // Elided null check quickly!
}
}
chk = new CmpPNode( value, null() ); break;
}
//----------- // if peephole optimizations occurred, a prior test existed. // If a prior test existed, maybe it dominates as we can avoid this test. if (tst != btst && type == T_OBJECT) { // At this point we want to scan up the CFG to see if we can // find an identical test (and so avoid this test altogether).
Node *cfg = control(); int depth = 0; while( depth < 16 ) { // Limit search depth for speed if( cfg->Opcode() == Op_IfTrue &&
cfg->in(0)->in(1) == tst ) { // Found prior test. Use "cast_not_null" to construct an identical // CastPP (and hence hash to) as already exists for the prior test. // Return that casted value. if (assert_null) {
replace_in_map(value, null()); return null(); // do not issue the redundant test
}
Node *oldcontrol = control();
set_control(cfg);
Node *res = cast_not_null(value);
set_control(oldcontrol);
NOT_PRODUCT(explicit_null_checks_elided++); return res;
}
cfg = IfNode::up_one_dom(cfg, /*linear_only=*/ true); if (cfg == NULL) break; // Quit at region nodes
depth++;
}
}
//----------- // Branch to failure if null float ok_prob = PROB_MAX; // a priori estimate: nulls never happen
Deoptimization::DeoptReason reason; if (assert_null) {
reason = Deoptimization::reason_null_assert(speculative);
} elseif (type == T_OBJECT) {
reason = Deoptimization::reason_null_check(speculative);
} else {
reason = Deoptimization::Reason_div0_check;
} // %%% Since Reason_unhandled is not recorded on a per-bytecode basis, // ciMethodData::has_trap_at will return a conservative -1 if any // must-be-null assertion has failed. This could cause performance // problems for a method after its first do_null_assert failure. // Consider using 'Reason_class_check' instead?
// To cause an implicit null check, we set the not-null probability // to the maximum (PROB_MAX). For an explicit check the probability // is set to a smaller value. if (null_control != NULL || too_many_traps(reason)) { // probability is less likely
ok_prob = PROB_LIKELY_MAG(3);
} elseif (!assert_null &&
(ImplicitNullCheckThreshold > 0) &&
method() != NULL &&
(method()->method_data()->trap_count(reason)
>= (uint)ImplicitNullCheckThreshold)) {
ok_prob = PROB_LIKELY_MAG(3);
}
if (null_control != NULL) {
IfNode* iff = create_and_map_if(control(), tst, ok_prob, COUNT_UNKNOWN);
Node* null_true = _gvn.transform( new IfFalseNode(iff));
set_control( _gvn.transform( new IfTrueNode(iff))); #ifndef PRODUCT if (null_true == top()) {
explicit_null_checks_elided++;
} #endif
(*null_control) = null_true;
} else {
BuildCutout unless(this, tst, ok_prob); // Check for optimizer eliding test at parse time if (stopped()) { // Failure not possible; do not bother making uncommon trap.
NOT_PRODUCT(explicit_null_checks_elided++);
} elseif (assert_null) {
uncommon_trap(reason,
Deoptimization::Action_make_not_entrant,
NULL, "assert_null");
} else {
replace_in_map(value, zerocon(type));
builtin_throw(reason);
}
}
// Must throw exception, fall-thru not possible? if (stopped()) { return top(); // No result
}
if (assert_null) { // Cast obj to null on this path.
replace_in_map(value, zerocon(type)); return zerocon(type);
}
// Cast obj to not-null on this path, if there is no null_control. // (If there is a null_control, a non-null value may come back to haunt us.) if (type == T_OBJECT) {
Node* cast = cast_not_null(value, false); if (null_control == NULL || (*null_control) == top())
replace_in_map(value, cast);
value = cast;
}
return value;
}
//------------------------------cast_not_null---------------------------------- // Cast obj to not-null on this path
Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { const Type *t = _gvn.type(obj); const Type *t_not_null = t->join_speculative(TypePtr::NOTNULL); // Object is already not-null? if( t == t_not_null ) return obj;
// Scan for instances of 'obj' in the current JVM mapping. // These instances are known to be not-null after the test. if (do_replace_in_map)
replace_in_map(obj, cast);
return cast; // Return casted value
}
// Sometimes in intrinsics, we implicitly know an object is not null // (there's no actual null check) so we can cast it to not null. In // the course of optimizations, the input to the cast can become null. // In that case that data path will die and we need the control path // to become dead as well to keep the graph consistent. So we have to // add a check for null for which one branch can't be taken. It uses // an Opaque4 node that will cause the check to be removed after loop // opts so the test goes away and the compiled code doesn't execute a // useless check.
Node* GraphKit::must_be_not_null(Node* value, bool do_replace_in_map) { if (!TypePtr::NULL_PTR->higher_equal(_gvn.type(value))) { return value;
}
Node* chk = _gvn.transform(new CmpPNode(value, null()));
Node *tst = _gvn.transform(new BoolNode(chk, BoolTest::ne));
Node* opaq = _gvn.transform(new Opaque4Node(C, tst, intcon(1)));
IfNode *iff = new IfNode(control(), opaq, PROB_MAX, COUNT_UNKNOWN);
_gvn.set_type(iff, iff->Value(&_gvn));
Node *if_f = _gvn.transform(new IfFalseNode(iff));
Node *frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr));
Node* halt = _gvn.transform(new HaltNode(if_f, frame, "unexpected null in intrinsic"));
C->root()->add_req(halt);
Node *if_t = _gvn.transform(new IfTrueNode(iff));
set_control(if_t); return cast_not_null(value, do_replace_in_map);
}
// Note: This operation potentially replaces any edge // on the map. This includes locals, stack, and monitors // of the current (innermost) JVM state.
// don't let inconsistent types from profiling escape this // method
//============================================================================= //--------------------------------memory---------------------------------------
Node* GraphKit::memory(uint alias_idx) {
MergeMemNode* mem = merged_memory();
Node* p = mem->memory_at(alias_idx);
assert(p != mem->empty_memory(), "empty");
_gvn.set_type(p, Type::MEMORY); // must be mapped return p;
}
//-----------------------------reset_memory------------------------------------
Node* GraphKit::reset_memory() {
Node* mem = map()->memory(); // do not use this node for any more parsing!
debug_only( map()->set_memory((Node*)NULL) ); return _gvn.transform( mem );
}
//============================================================================= // // parser factory methods for MemNodes // // These are layered on top of the factory methods in LoadNode and StoreNode, // and integrate with the parser's memory state and _gvn engine. //
Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx,
MemNode::MemOrd mo, bool require_atomic_access, bool unaligned, bool mismatched, bool unsafe) {
assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); const TypePtr* adr_type = NULL;
debug_only(adr_type = C->get_adr_type(adr_idx));
Node *mem = memory(adr_idx);
Node* st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo, require_atomic_access); if (unaligned) {
st->as_Store()->set_unaligned_access();
} if (mismatched) {
st->as_Store()->set_mismatched_access();
} if (unsafe) {
st->as_Store()->set_unsafe_access();
}
st = _gvn.transform(st);
set_memory(st, adr_idx); // Back-to-back stores can only remove intermediate store with DU info // so push on worklist for optimizer. if (mem->req() > MemNode::Address && adr == mem->in(MemNode::Address))
record_for_igvn(st);
return st;
}
Node* GraphKit::access_store_at(Node* obj,
Node* adr, const TypePtr* adr_type,
Node* val, const Type* val_type,
BasicType bt,
DecoratorSet decorators) { // Transformation of a value which could be NULL pointer (CastPP #NULL) // could be delayed during Parse (for example, in adjust_map_after_if()). // Execute transformation here to avoid barrier generation in such case. if (_gvn.type(val) == TypePtr::NULL_PTR) {
val = _gvn.makecon(TypePtr::NULL_PTR);
}
Node* GraphKit::access_load(Node* adr, // actual address to load val at const Type* val_type,
BasicType bt,
DecoratorSet decorators) { if (stopped()) { return top(); // Dead path ?
}
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.