Skip to content
155 changes: 85 additions & 70 deletions src/hotspot/share/opto/graphKit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,71 +527,29 @@ void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptR

//------------------------------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_exception_handler()) {
treat_throw_as_hot = true;
}
}
builtin_throw(reason, builtin_throw_exception(reason), /*allow_too_many_traps*/ true);
}

void GraphKit::builtin_throw(Deoptimization::DeoptReason reason,
ciInstance* ex_obj,
bool allow_too_many_traps) {
// 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 = nullptr;
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 we have a preconstructed exception object, use it.
if (ex_obj != nullptr) {
if (is_builtin_throw_hot(reason)) {
if (method()->can_omit_stack_trace() && ex_obj != nullptr) {
// 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.
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);
uncommon_trap_if_should_post_on_exceptions(reason, true /*must_throw*/);
// here if should_post_on_exceptions is false
// continue on with the normal codegen
}
Expand Down Expand Up @@ -622,6 +580,18 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {

add_exception_state(make_exception_state(ex_node));
return;
} else if (builtin_throw_too_many_traps(reason, ex_obj)) {
// We cannot afford to take too many traps here. Suffer in the interpreter instead.
assert(allow_too_many_traps, "not allowed");
if (C->log() != nullptr) {
C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'",
Deoptimization::trap_reason_name(reason),
C->trap_count(reason));
}
uncommon_trap(reason, Deoptimization::Action_none,
(ciKlass*) nullptr, (char*) nullptr,
true /*must_throw*/);
return;
}
}

Expand All @@ -633,27 +603,72 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {
// 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() : nullptr;
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() != nullptr)
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, Deoptimization::Action_maybe_recompile,
(ciKlass*) nullptr, (char*) nullptr,
true /*must_throw*/);
}

bool GraphKit::is_builtin_throw_hot(Deoptimization::DeoptReason reason) {
// 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.
if (ProfileTraps) {
if (too_many_traps(reason)) {
return 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_exception_handler()) {
return true;
}
}
return false;
}

uncommon_trap(reason, action, (ciKlass*)nullptr, (char*)nullptr, must_throw);
bool GraphKit::builtin_throw_too_many_traps(Deoptimization::DeoptReason reason,
ciInstance* ex_obj) {
if (is_builtin_throw_hot(reason)) {
if (method()->can_omit_stack_trace() && ex_obj != nullptr) {
return false; // no traps; throws preallocated exception instead
}
ciMethod* m = Deoptimization::reason_is_speculate(reason) ? C->method() : nullptr;
if (method()->method_data()->trap_recompiled_at(bci(), m) ||
C->too_many_traps(reason)) {
return true;
}
}
return false;
}

ciInstance* GraphKit::builtin_throw_exception(Deoptimization::DeoptReason reason) const {
// Preallocated exception objects to use when we don't need the backtrace.
switch (reason) {
case Deoptimization::Reason_null_check:
return env()->NullPointerException_instance();
case Deoptimization::Reason_div0_check:
return env()->ArithmeticException_instance();
case Deoptimization::Reason_range_check:
return env()->ArrayIndexOutOfBoundsException_instance();
case Deoptimization::Reason_class_check:
return env()->ClassCastException_instance();
case Deoptimization::Reason_array_check:
return env()->ArrayStoreException_instance();
default:
return nullptr;
}
}

//----------------------------PreserveJVMState---------------------------------
PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) {
Expand Down
10 changes: 10 additions & 0 deletions src/hotspot/share/opto/graphKit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ class GraphKit : public Phase {
// Helper to throw a built-in exception.
// The JVMS must allow the bytecode to be re-executed via an uncommon trap.
void builtin_throw(Deoptimization::DeoptReason reason);
void builtin_throw(Deoptimization::DeoptReason reason,
ciInstance* exception_object,
bool allow_too_many_traps);
bool builtin_throw_too_many_traps(Deoptimization::DeoptReason reason,
ciInstance* exception_object);
private:
bool is_builtin_throw_hot(Deoptimization::DeoptReason reason);
ciInstance* builtin_throw_exception(Deoptimization::DeoptReason reason) const;

public:

// Helper to check the JavaThread::_should_post_on_exceptions flag
// and branch to an uncommon_trap if it is true (with the specified reason and must_throw)
Expand Down
18 changes: 13 additions & 5 deletions src/hotspot/share/opto/library_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2003,7 +2003,14 @@ bool LibraryCallKit::inline_min_max(vmIntrinsics::ID id) {
return true;
}

void LibraryCallKit::inline_math_mathExact(Node* math, Node *test) {
bool LibraryCallKit::inline_math_mathExact(Node* math, Node* test) {
if (builtin_throw_too_many_traps(Deoptimization::Reason_intrinsic,
env()->ArithmeticException_instance())) {
// It has been already too many times, but we cannot use builtin_throw (e.g. we care about backtraces),
// so let's bail out intrinsic rather than risking deopting again.
return false;
}

Node* bol = _gvn.transform( new BoolNode(test, BoolTest::overflow) );
IfNode* check = create_and_map_if(control(), bol, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN);
Node* fast_path = _gvn.transform( new IfFalseNode(check));
Expand All @@ -2017,12 +2024,14 @@ void LibraryCallKit::inline_math_mathExact(Node* math, Node *test) {
set_control(slow_path);
set_i_o(i_o());

uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_none);
builtin_throw(Deoptimization::Reason_intrinsic,
env()->ArithmeticException_instance(),
/*allow_too_many_traps*/ false);
}

set_control(fast_path);
set_result(math);
return true;
}

template <typename OverflowOp>
Expand All @@ -2032,8 +2041,7 @@ bool LibraryCallKit::inline_math_overflow(Node* arg1, Node* arg2) {
MathOp* mathOp = new MathOp(arg1, arg2);
Node* operation = _gvn.transform( mathOp );
Node* ofcheck = _gvn.transform( new OverflowOp(arg1, arg2) );
inline_math_mathExact(operation, ofcheck);
return true;
return inline_math_mathExact(operation, ofcheck);
}

bool LibraryCallKit::inline_math_addExactI(bool is_increment) {
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/opto/library_call.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class LibraryCallKit : public GraphKit {
bool inline_math_pow();
template <typename OverflowOp>
bool inline_math_overflow(Node* arg1, Node* arg2);
void inline_math_mathExact(Node* math, Node* test);
bool inline_math_mathExact(Node* math, Node* test);
bool inline_math_addExactI(bool is_increment);
bool inline_math_addExactL(bool is_increment);
bool inline_math_multiplyExactI();
Expand Down
Loading