Skip to content
129 changes: 71 additions & 58 deletions src/hotspot/share/opto/graphKit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,54 +525,31 @@ void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptR

}

Pair<bool, bool> GraphKit::builtin_throw_applies(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.
bool treat_throw_as_hot = false;

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;
}
}
return {treat_throw_as_hot && method()->can_omit_stack_trace(), treat_throw_as_hot};
}

//------------------------------builtin_throw----------------------------------
void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, ciInstance* ex_obj) {
bool must_throw = true;
Pair<bool, bool> applies_and_treat_throw_as_hot = builtin_throw_applies(reason);
bool treat_throw_as_hot = applies_and_treat_throw_as_hot.second;
void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {
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 (applies_and_treat_throw_as_hot.first) {
// 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 (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 @@ -603,6 +580,18 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, ciInstance* ex_

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 @@ -614,29 +603,57 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, ciInstance* ex_
// 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*/);
}

uncommon_trap(reason, action, (ciKlass*)nullptr, (char*)nullptr, 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;
}

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::guess_exception_from_deopt_reason(Deoptimization::DeoptReason reason) const {
// Preconstructed exception objects, to use when we don't need the backtrace.
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();
Expand All @@ -653,10 +670,6 @@ ciInstance* GraphKit::guess_exception_from_deopt_reason(Deoptimization::DeoptRea
}
}

void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {
builtin_throw(reason, guess_exception_from_deopt_reason(reason));
}

//----------------------------PreserveJVMState---------------------------------
PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) {
debug_only(kit->verify_map());
Expand Down
13 changes: 10 additions & 3 deletions src/hotspot/share/opto/graphKit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +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);
Pair<bool, bool> builtin_throw_applies(Deoptimization::DeoptReason reason);
ciInstance* guess_exception_from_deopt_reason(Deoptimization::DeoptReason reason) const;
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
32 changes: 13 additions & 19 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 use_builtin_throw) {
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 care (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,37 +2024,24 @@ void LibraryCallKit::inline_math_mathExact(Node* math, Node* test, bool use_buil
set_control(slow_path);
set_i_o(i_o());

if (use_builtin_throw) {
builtin_throw(Deoptimization::Reason_intrinsic, env()->ArithmeticException_instance());
} else {
uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_maybe_recompile);
}
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>
bool LibraryCallKit::inline_math_overflow(Node* arg1, Node* arg2) {
typedef typename OverflowOp::MathOp MathOp;
bool use_builtin_throw = false;
if (builtin_throw_applies(Deoptimization::Reason_intrinsic).first) {
// If builtin_throw would work (notably, the throw is hot and we don't care about backtraces),
// instead of bailing out on intrinsic or potentially deopting, let's do that!
use_builtin_throw = true;
} else if (too_many_traps(Deoptimization::Reason_intrinsic)) {
// It has been already too many times, but we cannot use builtin_throw care (e.g. we care about backtraces),
// so let's bail out intrinsic rather than risking deopting again.
return false;
}

MathOp* mathOp = new MathOp(arg1, arg2);
Node* operation = _gvn.transform( mathOp );
Node* ofcheck = _gvn.transform( new OverflowOp(arg1, arg2) );
inline_math_mathExact(operation, ofcheck, use_builtin_throw);
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 use_builtin_throw);
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