mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8346989: C2: deoptimization and re-execution cycle with Math.*Exact in case of frequent overflow
Reviewed-by: thartmann, vlivanov
This commit is contained in:
parent
660b17a6b9
commit
97ed536125
@ -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
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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, action, (ciKlass*)nullptr, (char*)nullptr, must_throw);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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));
|
||||
@ -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>
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* The base case
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* With ProfileTraps enabled to allow builtin_throw to work
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:+ProfileTraps -XX:+StackTraceInThrowable -XX:+OmitStackTraceInFastThrow
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* ProfileTraps off => throw will never be hot for builtin_throw
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:-ProfileTraps
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* OmitStackTraceInFastThrow off => can_omit_stack_trace is false => no builtin_throw
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:+ProfileTraps -XX:+StackTraceInThrowable -XX:-OmitStackTraceInFastThrow
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* StackTraceInThrowable off => can_omit_stack_trace is true => yes builtin_throw
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:+ProfileTraps -XX:-StackTraceInThrowable -XX:+OmitStackTraceInFastThrow
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* StackTraceInThrowable off && OmitStackTraceInFastThrow off => can_omit_stack_trace is true => yes builtin_throw
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:+ProfileTraps -XX:-StackTraceInThrowable -XX:-OmitStackTraceInFastThrow
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Math.*Exact intrinsics, especially in case of overflow
|
||||
* Without intrinsics
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm
|
||||
* -XX:+UnlockDiagnosticVMOptions -Xbootclasspath/a:. -XX:+WhiteBoxAPI
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.intrinsics.mathexact.OverflowTest::comp_*
|
||||
* -XX:CompileCommand=dontinline,compiler.intrinsics.mathexact.OverflowTest::*
|
||||
* -XX:DisableIntrinsic=_addExactI,_incrementExactI,_addExactL,_incrementExactL,_subtractExactI,_decrementExactI,_subtractExactL,_decrementExactL,_negateExactI,_negateExactL,_multiplyExactI,_multiplyExactL
|
||||
* compiler.intrinsics.mathexact.OverflowTest
|
||||
*/
|
||||
|
||||
package compiler.intrinsics.mathexact;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import compiler.lib.generators.RestrictableGenerator;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.whitebox.WhiteBox;
|
||||
|
||||
import static compiler.lib.generators.Generators.G;
|
||||
|
||||
public class OverflowTest {
|
||||
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
|
||||
private static final RestrictableGenerator<Integer> int_gen = G.ints();
|
||||
private static final int LIMIT = 10_000;
|
||||
|
||||
public static void main(String... args) throws NoSuchMethodException {
|
||||
OverflowTest t = new OverflowTest();
|
||||
t.run();
|
||||
}
|
||||
|
||||
void run() throws NoSuchMethodException {
|
||||
check_compilation();
|
||||
check_multiplyI();
|
||||
}
|
||||
|
||||
void check_compilation() throws NoSuchMethodException {
|
||||
// Force Math loading
|
||||
Math.min(2, 3);
|
||||
comp_multiplyI(0, 0);
|
||||
comp_multiplyI_no_catch(0, 0);
|
||||
int_multiplyI(0, 0);
|
||||
|
||||
Method comp_multiplyI_meth = OverflowTest.class.getDeclaredMethod("comp_multiplyI", int.class, int.class);
|
||||
Asserts.assertTrue(WHITE_BOX.isMethodCompiled(comp_multiplyI_meth), "comp_multiplyI(int, int) is not compiled");
|
||||
|
||||
Method comp_multiplyI_no_catch_meth = OverflowTest.class.getDeclaredMethod("comp_multiplyI_no_catch", int.class, int.class);
|
||||
Asserts.assertTrue(WHITE_BOX.isMethodCompiled(comp_multiplyI_no_catch_meth), "comp_multiplyI_no_catch(int, int) is not compiled");
|
||||
|
||||
Method int_multiplyI_meth = OverflowTest.class.getDeclaredMethod("int_multiplyI", int.class, int.class);
|
||||
Asserts.assertFalse(WHITE_BOX.isMethodCompiled(int_multiplyI_meth), "int_multiplyI(int, int) is compiled");
|
||||
}
|
||||
|
||||
void assert_consistent(Integer comp_res, Integer int_res) {
|
||||
if (int_res == null) {
|
||||
Asserts.assertNull(comp_res);
|
||||
} else {
|
||||
Asserts.assertNotNull(comp_res);
|
||||
Asserts.assertEquals(comp_res, int_res);
|
||||
}
|
||||
}
|
||||
|
||||
Integer comp_multiplyI(int a, int b) {
|
||||
try {
|
||||
return Math.multiplyExact(a, b);
|
||||
} catch (ArithmeticException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int comp_multiplyI_no_catch(int a, int b) {
|
||||
return Math.multiplyExact(a, b);
|
||||
}
|
||||
|
||||
Integer int_multiplyI(int a, int b) {
|
||||
try {
|
||||
return Math.multiplyExact(a, b);
|
||||
} catch (ArithmeticException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void check_multiplyI() {
|
||||
// 46_340 < 2 ^ 15.5 < 46_341 =>
|
||||
// 46_340^2 < 2 ^ 31 < 46_341^2
|
||||
int limit_square_do_not_overflow = 46_340;
|
||||
|
||||
// In bound cases
|
||||
for (int i = 0; i < LIMIT; i++) {
|
||||
int a = limit_square_do_not_overflow - i;
|
||||
Integer comp_res = comp_multiplyI(a, a);
|
||||
Integer int_res = int_multiplyI(a, a);
|
||||
Asserts.assertNotNull(int_res);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
for (int i = 0; i < LIMIT; i++) {
|
||||
int a = limit_square_do_not_overflow - i;
|
||||
Integer comp_res;
|
||||
try {
|
||||
comp_res = comp_multiplyI_no_catch(a, a);
|
||||
} catch (ArithmeticException _) {
|
||||
comp_res = null;
|
||||
}
|
||||
Integer int_res = int_multiplyI(a, a);
|
||||
Asserts.assertNotNull(int_res);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
|
||||
// Out of bound cases
|
||||
for (int i = 0; i < LIMIT; i++) {
|
||||
int a = limit_square_do_not_overflow + 1 + i;
|
||||
Integer comp_res = comp_multiplyI(a, a);
|
||||
Integer int_res = int_multiplyI(a, a);
|
||||
Asserts.assertNull(int_res);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
for (int i = 0; i < LIMIT; i++) {
|
||||
int a = limit_square_do_not_overflow + 1 + i;
|
||||
Integer comp_res;
|
||||
try {
|
||||
comp_res = comp_multiplyI_no_catch(a, a);
|
||||
} catch (ArithmeticException _) {
|
||||
comp_res = null;
|
||||
}
|
||||
Integer int_res = int_multiplyI(a, a);
|
||||
Asserts.assertNull(int_res);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
|
||||
// Random slice
|
||||
int lhs = int_gen.next();
|
||||
int rhs_start = int_gen.next() & 0xff_ff_00_00;
|
||||
for (int i = 0; i < 0x1_00_00; i++) {
|
||||
int rhs = rhs_start | i;
|
||||
Integer comp_res = comp_multiplyI(lhs, rhs);
|
||||
Integer int_res = int_multiplyI(lhs, rhs);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
for (int i = 0; i < 0x1_00_00; i++) {
|
||||
int rhs = rhs_start | i;
|
||||
Integer comp_res;
|
||||
try {
|
||||
comp_res = comp_multiplyI_no_catch(lhs, rhs);
|
||||
} catch (ArithmeticException _) {
|
||||
comp_res = null;
|
||||
}
|
||||
Integer int_res = int_multiplyI(lhs, rhs);
|
||||
assert_consistent(comp_res, int_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
403
test/micro/org/openjdk/bench/vm/compiler/MathExact.java
Normal file
403
test/micro/org/openjdk/bench/vm/compiler/MathExact.java
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
package org.openjdk.bench.vm.compiler;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.infra.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.MILLISECONDS)
|
||||
public abstract class MathExact {
|
||||
@Param({"1000000"})
|
||||
public int SIZE;
|
||||
|
||||
|
||||
// === multiplyExact(int, int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testMultiplyI(int i) {
|
||||
try {
|
||||
return Math.multiplyExact(i, i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopMultiplyIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testMultiplyI(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopMultiplyIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
// 46_340 < 2 ^ 15.5 (< 46_341, but that's not important)
|
||||
// so
|
||||
// 46_340 ^ 2 < 2 ^ 31
|
||||
testMultiplyI(i % 46_341);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === multiplyExact(long, long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testMultiplyL(long i) {
|
||||
try {
|
||||
return Math.multiplyExact(i, i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopMultiplyLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
// (2 ^ 63 - 1)^0.5 ~= 3_037_000_499.9761
|
||||
// Starting at 3_037_000_000 so that almost all computations overflow
|
||||
testMultiplyL(3_037_000_000L + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopMultiplyLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testMultiplyL(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === negateExact(int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testNegateI(int i) {
|
||||
try {
|
||||
return Math.negateExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopNegateIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testNegateI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testNegateI(Integer.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopNegateIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testNegateI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testNegateI(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === negateExact(long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testNegateL(long i) {
|
||||
try {
|
||||
return Math.negateExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopNegateLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testNegateL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testNegateL(Long.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopNegateLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testNegateL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testNegateL(Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === incrementExact(int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testIncrementI(int i) {
|
||||
try {
|
||||
return Math.incrementExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopIncrementIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testIncrementI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testIncrementI(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopIncrementIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testIncrementI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testIncrementI(Integer.MIN_VALUE + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === incrementExact(long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testIncrementL(long i) {
|
||||
try {
|
||||
return Math.incrementExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopIncrementLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testIncrementL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testIncrementL(Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopIncrementLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testIncrementL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testIncrementL(Long.MIN_VALUE + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === decrementExact(int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testDecrementI(int i) {
|
||||
try {
|
||||
return Math.decrementExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopDecrementIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testDecrementI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testDecrementI(Integer.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopDecrementIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testDecrementI(i);
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testDecrementI(Integer.MAX_VALUE - i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === decrementExact(long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testDecrementL(long i) {
|
||||
try {
|
||||
return Math.decrementExact(i);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopDecrementLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testDecrementL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testDecrementL(Long.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopDecrementLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testDecrementL(i);
|
||||
}
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testDecrementL(Long.MAX_VALUE - i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === addExact(int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testAddI(int l, int r) {
|
||||
try {
|
||||
return Math.addExact(l, r);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopAddIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testAddI(Integer.MAX_VALUE - 1_000, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopAddIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testAddI(i * 5, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === addExact(long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testAddL(long l, long r) {
|
||||
try {
|
||||
return Math.addExact(l, r);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopAddLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testAddL(Long.MAX_VALUE - 1_000L, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopAddLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testAddL(i * 5L, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === subtractExact(int) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public int testSubtractI(int l, int r) {
|
||||
try {
|
||||
return Math.subtractExact(l, r);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopSubtractIOverflow() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testSubtractI(Integer.MIN_VALUE + 1_000, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopSubtractIInBounds() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
testSubtractI(i * 5, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === subtractExact(long) ===
|
||||
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
|
||||
public long testSubtractL(long l, long r) {
|
||||
try {
|
||||
return Math.subtractExact(l, r);
|
||||
} catch (ArithmeticException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopSubtractLOverflow() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testSubtractL(Long.MIN_VALUE + 1_000L, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void loopSubtractLInBounds() {
|
||||
for (long i = 0L; i < (long)SIZE; i++) {
|
||||
testSubtractL(i * 5L, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Fork(value = 1, jvmArgs = {"-XX:TieredStopAtLevel=1"})
|
||||
public static class C1_1 extends MathExact {}
|
||||
|
||||
@Fork(value = 1, jvmArgs = {"-XX:TieredStopAtLevel=2"})
|
||||
public static class C1_2 extends MathExact {}
|
||||
|
||||
@Fork(value = 1, jvmArgs = {"-XX:TieredStopAtLevel=3"})
|
||||
public static class C1_3 extends MathExact {}
|
||||
|
||||
@Fork(value = 1)
|
||||
public static class C2 extends MathExact {}
|
||||
|
||||
@Fork(value = 1,
|
||||
jvmArgs = {
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:DisableIntrinsic=_addExactI,_incrementExactI,_addExactL,_incrementExactL,_subtractExactI,_decrementExactI,_subtractExactL,_decrementExactL,_negateExactI,_negateExactL,_multiplyExactI,_multiplyExactL",
|
||||
})
|
||||
public static class C2_no_intrinsics extends MathExact {}
|
||||
|
||||
@Fork(value = 1, jvmArgs = {"-XX:-OmitStackTraceInFastThrow"})
|
||||
public static class C2_no_builtin_throw extends MathExact {}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user