/*
* 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.
*
*/
#include "precompiled.hpp"
#include "ci/ciUtilities.hpp"
#include "classfile/javaClasses.hpp"
#include "ci/ciObjArray.hpp"
#include "asm/register.hpp"
#include "compiler/compileLog.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/c2/barrierSetC2.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/resourceArea.hpp"
#include "opto/addnode.hpp"
#include "opto/castnode.hpp"
#include "opto/convertnode.hpp"
#include "opto/graphKit.hpp"
#include "opto/idealKit.hpp"
#include "opto/intrinsicnode.hpp"
#include "opto/locknode.hpp"
#include "opto/machnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/parse.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "opto/subtypenode.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/powerOfTwo.hpp"
#include "utilities/growableArray.hpp"
//----------------------------GraphKit-----------------------------------------
// Main utility constructor.
GraphKit::GraphKit(JVMState* jvms)
: Phase(Phase::Parser),
_env(C->env()),
_gvn(*C->initial_gvn()),
_barrier_set(BarrierSet::barrier_set()->barrier_set_c2())
{
_exceptions = jvms->map()->next_exception();
if (_exceptions != NULL) jvms->map()->set_next_exception(NULL);
set_jvms(jvms);
}
// Private constructor for parser.
GraphKit::GraphKit()
: Phase(Phase::Parser),
_env(C->env()),
_gvn(*C->initial_gvn()),
_barrier_set(BarrierSet::barrier_set()->barrier_set_c2())
{
_exceptions = NULL;
set_map(NULL);
debug_only(_sp = -99);
debug_only(set_bci(-99));
}
//---------------------------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()) return false;
if (sp() != (int)jvms()->sp()) return false;
return true;
}
if (jvms()->method() != parse->method()) return false;
if (jvms()->bci() != parse->bci()) return false;
int jvms_sp = jvms()->sp();
if (jvms_sp != parse->sp()) return false;
int jvms_depth = jvms()->depth();
if (jvms_depth != parse->depth()) return false;
return true;
}
// 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.
static bool is_hidden_merge(Node* reg) {
if (reg == NULL) return false;
if (reg->is_Phi()) {
reg = reg->in(0);
if (reg == NULL) return false;
}
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) return true;
else if (control() == top()) return true;
else return false;
}
//-----------------------------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()) {
return true;
}
}
return false;
}
//------------------------------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));
}
inline static 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);
}
//-----------------------add_exception_states_from-----------------------------
void GraphKit::add_exception_states_from(JVMState* jvms) {
SafePointNode* ex_map = jvms->map()->next_exception();
if (ex_map != NULL) {
jvms->map()->set_next_exception(NULL);
for (SafePointNode* next_map; ex_map != NULL; ex_map = next_map) {
next_map = ex_map->next_exception();
ex_map->set_next_exception(NULL);
add_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;
}
static inline void 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));
}
}
static inline void 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;
}
//---------------------------------java_bc-------------------------------------
Bytecodes::Code GraphKit::java_bc() const {
ciMethod* method = this->method();
int bci = this->bci();
if (method != NULL && bci != InvocationEntryBci)
return method->java_code_at_bci(bci);
else
return Bytecodes::_illegal;
}
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);
}
}
//------------------------------builtin_throw----------------------------------
void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {
bool must_throw = true;
// 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);
Node *adr = basic_plus_adr(ex_node, ex_node, offset);
const TypeOopPtr* val_type = TypeOopPtr::make_from_klass(env()->String_klass());
Node *store = access_store_at(ex_node, adr, adr_typ, null(), val_type, T_OBJECT, IN_HEAP);
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);
}
add_exception_state(make_exception_state(ex_node));
return;
}
}
// %%% 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.
uncommon_trap(reason, action, (ciKlass*)NULL, (char*)NULL, must_throw);
}
//----------------------------PreserveJVMState---------------------------------
PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) {
debug_only(kit->verify_map());
_kit = kit;
_map = kit->map(); // preserve the map
_sp = kit->sp();
kit->set_map(clone_map ? kit->clone_map() : NULL);
#ifdef ASSERT
_bci = kit->bci();
Parse* parser = kit->is_Parse();
int block = (parser == NULL || parser->block() == NULL) ? -1 : parser->block()->rpo();
_block = block;
#endif
}
PreserveJVMState::~PreserveJVMState() {
GraphKit* kit = _kit;
#ifdef ASSERT
assert(kit->bci() == _bci, "bci must not shift");
Parse* parser = kit->is_Parse();
int block = (parser == NULL || parser->block() == NULL) ? -1 : parser->block()->rpo();
assert(block == _block, "block must not shift");
#endif
kit->set_map(_map);
kit->set_sp(_sp);
}
//-----------------------------BuildCutout-------------------------------------
BuildCutout::BuildCutout(GraphKit* kit, Node* p, float prob, float cnt)
: PreserveJVMState(kit)
{
assert(p->is_Con() || p->is_Bool(), "test must be a bool");
SafePointNode* outer_map = _map; // preserved map is caller's
SafePointNode* inner_map = kit->map();
IfNode* iff = kit->create_and_map_if(outer_map->control(), p, prob, cnt);
outer_map->set_control(kit->gvn().transform( new IfTrueNode(iff) ));
inner_map->set_control(kit->gvn().transform( new IfFalseNode(iff) ));
}
BuildCutout::~BuildCutout() {
GraphKit* kit = _kit;
assert(kit->stopped(), "cutout code must stop, throw, return, etc.");
}
//---------------------------PreserveReexecuteState----------------------------
PreserveReexecuteState::PreserveReexecuteState(GraphKit* kit) {
assert(!kit->stopped(), "must call stopped() before");
_kit = kit;
_sp = kit->sp();
_reexecute = kit->jvms()->_reexecute;
}
PreserveReexecuteState::~PreserveReexecuteState() {
if (_kit->stopped()) return;
_kit->jvms()->_reexecute = _reexecute;
_kit->set_sp(_sp);
}
//------------------------------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);
SafePointNode *clonemap = (SafePointNode*)map()->clone();
JVMState* jvms = this->jvms();
JVMState* clonejvms = jvms->clone_shallow(C);
clonemap->set_memory(mem);
clonemap->set_jvms(clonejvms);
clonejvms->set_map(clonemap);
record_for_igvn(clonemap);
gvn().set_type_bottom(clonemap);
return clonemap;
}
//-----------------------------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.
return true;
}
// 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.
return true;
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();
}
return false;
}
}
}
return true;
}
#endif //ASSERT
// Helper function for enforcing certain bytecodes to reexecute if deoptimization happens.
static bool 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 {
return false;
}
}
// 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.
const bool 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
}
// Presize the call:
DEBUG_ONLY(uint non_debug_edges = call->req());
call->add_req_batch(top(), youngest_jvms->debug_depth());
assert(call->req() == non_debug_edges + youngest_jvms->debug_depth(), "");
// 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));
} else if (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, "");
}
bool GraphKit::compute_stack_effects(int& inputs, int& depth) {
Bytecodes::Code code = java_bc();
if (code == Bytecodes::_wide) {
code = method()->java_code_at_bci(bci() + 1);
}
BasicType rtype = T_ILLEGAL;
int rsize = 0;
if (code != Bytecodes::_illegal) {
depth = Bytecodes::depth(code); // checkcast=0, athrow=-1
rtype = Bytecodes::result_type(code); // checkcast=P, athrow=V
if (rtype < T_CONFLICT)
rsize = type2size[rtype];
}
switch (code) {
case Bytecodes::_illegal:
return false;
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
inputs = 0;
break;
case Bytecodes::_dup: inputs = 1; break;
case Bytecodes::_dup_x1: inputs = 2; break;
case Bytecodes::_dup_x2: inputs = 3; break;
case Bytecodes::_dup2: inputs = 2; break;
case Bytecodes::_dup2_x1: inputs = 3; break;
case Bytecodes::_dup2_x2: inputs = 4; break;
case Bytecodes::_swap: inputs = 2; break;
case Bytecodes::_arraylength: inputs = 1; break;
case Bytecodes::_getstatic:
case Bytecodes::_putstatic:
case Bytecodes::_getfield:
case Bytecodes::_putfield:
{
bool ignored_will_link;
ciField* field = method()->get_field_at_bci(bci(), ignored_will_link);
int size = field->type()->size();
bool is_get = (depth >= 0), is_static = (depth & 1);
inputs = (is_static ? 0 : 1);
if (is_get) {
depth = size - inputs;
} else {
inputs += size; // putxxx pops the value from the stack
depth = - inputs;
}
}
break;
case Bytecodes::_invokevirtual:
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokeinterface:
{
bool ignored_will_link;
ciSignature* declared_signature = NULL;
ciMethod* ignored_callee = method()->get_method_at_bci(bci(), ignored_will_link, &declared_signature);
assert(declared_signature != NULL, "cannot be null");
inputs = declared_signature->arg_size_for_bc(code);
int size = declared_signature->return_type()->size();
depth = size - inputs;
}
break;
case Bytecodes::_multianewarray:
{
ciBytecodeStream iter(method());
iter.reset_to_bci(bci());
iter.next();
inputs = iter.get_dimensions();
assert(rsize == 1, "");
depth = rsize - inputs;
}
break;
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;
}
#ifdef ASSERT
// spot check
int outputs = depth + inputs;
assert(outputs >= 0, "sanity");
switch (code) {
case Bytecodes::_checkcast: assert(inputs == 1 && outputs == 1, ""); break;
case Bytecodes::_athrow: assert(inputs == 1 && outputs == 0, ""); break;
case Bytecodes::_aload_0: assert(inputs == 0 && outputs == 1, ""); break;
case Bytecodes::_return: assert(inputs == 0 && outputs == 0, ""); break;
case Bytecodes::_drem: assert(inputs == 4 && outputs == 2, ""); break;
default: break;
}
#endif //ASSERT
return true;
}
//------------------------------basic_plus_adr---------------------------------
Node* GraphKit::basic_plus_adr(Node* base, Node* ptr, Node* offset) {
// short-circuit a common case
if (offset == intcon(0)) return ptr;
return _gvn.transform( new AddPNode(base, ptr, offset) );
}
Node* GraphKit::ConvI2L(Node* offset) {
// short-circuit a common case
jint offset_con = find_int_con(offset, Type::OffsetBot);
if (offset_con != Type::OffsetBot) {
return longcon((jlong) offset_con);
}
return _gvn.transform( new ConvI2LNode(offset));
}
Node* GraphKit::ConvI2UL(Node* offset) {
juint offset_con = (juint) find_int_con(offset, Type::OffsetBot);
if (offset_con != (juint) Type::OffsetBot) {
return longcon((julong) offset_con);
}
Node* conv = _gvn.transform( new ConvI2LNode(offset));
Node* mask = _gvn.transform(ConLNode::make((julong) max_juint));
return _gvn.transform( new AndLNode(conv, mask) );
}
Node* GraphKit::ConvL2I(Node* offset) {
// short-circuit a common case
jlong offset_con = find_long_con(offset, (jlong)Type::OffsetBot);
if (offset_con != (jlong)Type::OffsetBot) {
return intcon((int) offset_con);
}
return _gvn.transform( new ConvL2INode(offset));
}
//-------------------------load_object_klass-----------------------------------
Node* GraphKit::load_object_klass(Node* obj) {
// Special-case a fresh allocation to avoid building nodes:
Node* akls = AllocateNode::Ideal_klass(obj, &_gvn);
if (akls != NULL) return akls;
Node* k_adr = basic_plus_adr(obj, oopDesc::klass_offset_in_bytes());
return _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS));
}
//-------------------------load_array_length-----------------------------------
Node* GraphKit::load_array_length(Node* array) {
// Special-case a fresh allocation to avoid building nodes:
AllocateArrayNode* alloc = AllocateArrayNode::Ideal_array_allocation(array, &_gvn);
Node *alen;
if (alloc == NULL) {
Node *r_adr = basic_plus_adr(array, arrayOopDesc::length_offset_in_bytes());
alen = _gvn.transform( new LoadRangeNode(0, immutable_memory(), r_adr, TypeInt::POS));
} else {
alen = array_ideal_length(alloc, _gvn.type(array)->is_oopptr(), false);
}
return alen;
}
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
extern int 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;
}
default:
fatal("unexpected type: %s", type2name(type));
}
assert(chk != NULL, "sanity check");
chk = _gvn.transform(chk);
BoolTest::mask btest = assert_null ? BoolTest::eq : BoolTest::ne;
BoolNode *btst = new BoolNode( chk, btest);
Node *tst = _gvn.transform( btst );
//-----------
// 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);
} else if (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);
} else if (!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++);
} else if (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;
Node *cast = new CastPPNode(obj,t_not_null);
cast->init_req(0, control());
cast = _gvn.transform( cast );
// 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);
}
//--------------------------replace_in_map-------------------------------------
void GraphKit::replace_in_map(Node* old, Node* neww) {
if (old == neww) {
return;
}
map()->replace_edge(old, neww);
// 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
const Type* told = _gvn.type(old);
const Type* tnew = _gvn.type(neww);
if (!tnew->higher_equal(told)) {
return;
}
map()->record_replaced_node(old, neww);
}
//=============================================================================
//--------------------------------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 );
}
//------------------------------set_all_memory---------------------------------
void GraphKit::set_all_memory(Node* newmem) {
Node* mergemem = MergeMemNode::make(newmem);
gvn().set_type_bottom(mergemem);
map()->set_memory(mergemem);
}
//------------------------------set_all_memory_call----------------------------
void GraphKit::set_all_memory_call(Node* call, bool separate_io_proj) {
Node* newmem = _gvn.transform( new ProjNode(call, TypeFunc::Memory, separate_io_proj) );
set_all_memory(newmem);
}
//=============================================================================
//
// 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.
//
// factory methods in "int adr_idx"
Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt,
int adr_idx,
MemNode::MemOrd mo,
LoadNode::ControlDependency control_dependency,
bool require_atomic_access,
bool unaligned,
bool mismatched,
bool unsafe,
uint8_t barrier_data) {
assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" );
const TypePtr* adr_type = NULL; // debug-mode-only argument
debug_only(adr_type = C->get_adr_type(adr_idx));
Node* mem = memory(adr_idx);
Node* ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency, require_atomic_access, unaligned, mismatched, unsafe, barrier_data);
ld = _gvn.transform(ld);
if (((bt == T_OBJECT) && C->do_escape_analysis()) || C->eliminate_boxing()) {
// Improve graph before escape analysis and boxing elimination.
record_for_igvn(ld);
}
return ld;
}
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);
}
if (stopped()) {
return top(); // Dead path ?
}
assert(val != NULL, "not dead path");
C2AccessValuePtr addr(adr, adr_type);
C2AccessValue value(val, val_type);
C2ParseAccess access(this, decorators | C2_WRITE_ACCESS, bt, obj, addr);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::store_at(access, value);
} else {
return _barrier_set->store_at(access, value);
}
}
Node* GraphKit::access_load_at(Node* obj, // containing obj
Node* adr, // actual address to store val at
const TypePtr* adr_type,
const Type* val_type,
BasicType bt,
DecoratorSet decorators) {
if (stopped()) {
return top(); // Dead path ?
}
C2AccessValuePtr addr(adr, adr_type);
C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, obj, addr);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::load_at(access, val_type);
} else {
return _barrier_set->load_at(access, val_type);
}
}
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 ?
}
C2AccessValuePtr addr(adr, adr->bottom_type()->is_ptr());
C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, NULL, addr);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::load_at(access, val_type);
} else {
return _barrier_set->load_at(access, val_type);
}
}
Node* GraphKit::access_atomic_cmpxchg_val_at(Node* obj,
Node* adr,
const TypePtr* adr_type,
int alias_idx,
Node* expected_val,
Node* new_val,
const Type* value_type,
BasicType bt,
DecoratorSet decorators) {
C2AccessValuePtr addr(adr, adr_type);
C2AtomicParseAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
bt, obj, addr, alias_idx);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::atomic_cmpxchg_val_at(access, expected_val, new_val, value_type);
} else {
return _barrier_set->atomic_cmpxchg_val_at(access, expected_val, new_val, value_type);
}
}
Node* GraphKit::access_atomic_cmpxchg_bool_at(Node* obj,
Node* adr,
const TypePtr* adr_type,
int alias_idx,
Node* expected_val,
Node* new_val,
const Type* value_type,
BasicType bt,
DecoratorSet decorators) {
C2AccessValuePtr addr(adr, adr_type);
C2AtomicParseAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
bt, obj, addr, alias_idx);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::atomic_cmpxchg_bool_at(access, expected_val, new_val, value_type);
} else {
return _barrier_set->atomic_cmpxchg_bool_at(access, expected_val, new_val, value_type);
}
}
Node* GraphKit::access_atomic_xchg_at(Node* obj,
Node* adr,
const TypePtr* adr_type,
int alias_idx,
Node* new_val,
const Type* value_type,
BasicType bt,
DecoratorSet decorators) {
C2AccessValuePtr addr(adr, adr_type);
C2AtomicParseAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
bt, obj, addr, alias_idx);
if (access.is_raw()) {
return _barrier_set->BarrierSetC2::atomic_xchg_at(access, new_val, value_type);
} else {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.89 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
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 ist noch experimentell.
|