Skip to content

Commit 4e51a8c

Browse files
committed
8307513: C2: intrinsify Math.max(long,long) and Math.min(long,long)
Reviewed-by: roland, epeter, chagedorn, darcy
1 parent 7e3bc81 commit 4e51a8c

File tree

9 files changed

+737
-119
lines changed

9 files changed

+737
-119
lines changed

src/hotspot/share/classfile/vmIntrinsics.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ class methodHandle;
189189
do_intrinsic(_minF, java_lang_Math, min_name, float2_float_signature, F_S) \
190190
do_intrinsic(_maxD, java_lang_Math, max_name, double2_double_signature, F_S) \
191191
do_intrinsic(_minD, java_lang_Math, min_name, double2_double_signature, F_S) \
192+
do_intrinsic(_maxL, java_lang_Math, max_name, long2_long_signature, F_S) \
193+
do_intrinsic(_minL, java_lang_Math, min_name, long2_long_signature, F_S) \
192194
do_intrinsic(_roundD, java_lang_Math, round_name, double_long_signature, F_S) \
193195
do_intrinsic(_roundF, java_lang_Math, round_name, float_int_signature, F_S) \
194196
do_intrinsic(_dcopySign, java_lang_Math, copySign_name, double2_double_signature, F_S) \

src/hotspot/share/opto/c2compiler.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
633633
case vmIntrinsics::_max:
634634
case vmIntrinsics::_min_strict:
635635
case vmIntrinsics::_max_strict:
636+
case vmIntrinsics::_maxL:
637+
case vmIntrinsics::_minL:
636638
case vmIntrinsics::_arraycopy:
637639
case vmIntrinsics::_arraySort:
638640
case vmIntrinsics::_arrayPartition:

src/hotspot/share/opto/library_call.cpp

Lines changed: 80 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -691,17 +691,17 @@ bool LibraryCallKit::try_to_inline(int predicate) {
691691
case vmIntrinsics::_max:
692692
case vmIntrinsics::_min_strict:
693693
case vmIntrinsics::_max_strict:
694-
return inline_min_max(intrinsic_id());
695-
696-
case vmIntrinsics::_maxF:
694+
case vmIntrinsics::_minL:
695+
case vmIntrinsics::_maxL:
697696
case vmIntrinsics::_minF:
698-
case vmIntrinsics::_maxD:
697+
case vmIntrinsics::_maxF:
699698
case vmIntrinsics::_minD:
700-
case vmIntrinsics::_maxF_strict:
699+
case vmIntrinsics::_maxD:
701700
case vmIntrinsics::_minF_strict:
702-
case vmIntrinsics::_maxD_strict:
701+
case vmIntrinsics::_maxF_strict:
703702
case vmIntrinsics::_minD_strict:
704-
return inline_fp_min_max(intrinsic_id());
703+
case vmIntrinsics::_maxD_strict:
704+
return inline_min_max(intrinsic_id());
705705

706706
case vmIntrinsics::_VectorUnaryOp:
707707
return inline_vector_nary_operation(1);
@@ -1942,7 +1942,78 @@ bool LibraryCallKit::inline_notify(vmIntrinsics::ID id) {
19421942

19431943
//----------------------------inline_min_max-----------------------------------
19441944
bool LibraryCallKit::inline_min_max(vmIntrinsics::ID id) {
1945-
set_result(generate_min_max(id, argument(0), argument(1)));
1945+
Node* a = nullptr;
1946+
Node* b = nullptr;
1947+
Node* n = nullptr;
1948+
switch (id) {
1949+
case vmIntrinsics::_min:
1950+
case vmIntrinsics::_max:
1951+
case vmIntrinsics::_minF:
1952+
case vmIntrinsics::_maxF:
1953+
case vmIntrinsics::_minF_strict:
1954+
case vmIntrinsics::_maxF_strict:
1955+
case vmIntrinsics::_min_strict:
1956+
case vmIntrinsics::_max_strict:
1957+
assert(callee()->signature()->size() == 2, "minF/maxF has 2 parameters of size 1 each.");
1958+
a = argument(0);
1959+
b = argument(1);
1960+
break;
1961+
case vmIntrinsics::_minD:
1962+
case vmIntrinsics::_maxD:
1963+
case vmIntrinsics::_minD_strict:
1964+
case vmIntrinsics::_maxD_strict:
1965+
assert(callee()->signature()->size() == 4, "minD/maxD has 2 parameters of size 2 each.");
1966+
a = round_double_node(argument(0));
1967+
b = round_double_node(argument(2));
1968+
break;
1969+
case vmIntrinsics::_minL:
1970+
case vmIntrinsics::_maxL:
1971+
assert(callee()->signature()->size() == 4, "minL/maxL has 2 parameters of size 2 each.");
1972+
a = argument(0);
1973+
b = argument(2);
1974+
break;
1975+
default:
1976+
fatal_unexpected_iid(id);
1977+
break;
1978+
}
1979+
1980+
switch (id) {
1981+
case vmIntrinsics::_min:
1982+
case vmIntrinsics::_min_strict:
1983+
n = new MinINode(a, b);
1984+
break;
1985+
case vmIntrinsics::_max:
1986+
case vmIntrinsics::_max_strict:
1987+
n = new MaxINode(a, b);
1988+
break;
1989+
case vmIntrinsics::_minF:
1990+
case vmIntrinsics::_minF_strict:
1991+
n = new MinFNode(a, b);
1992+
break;
1993+
case vmIntrinsics::_maxF:
1994+
case vmIntrinsics::_maxF_strict:
1995+
n = new MaxFNode(a, b);
1996+
break;
1997+
case vmIntrinsics::_minD:
1998+
case vmIntrinsics::_minD_strict:
1999+
n = new MinDNode(a, b);
2000+
break;
2001+
case vmIntrinsics::_maxD:
2002+
case vmIntrinsics::_maxD_strict:
2003+
n = new MaxDNode(a, b);
2004+
break;
2005+
case vmIntrinsics::_minL:
2006+
n = new MinLNode(_gvn.C, a, b);
2007+
break;
2008+
case vmIntrinsics::_maxL:
2009+
n = new MaxLNode(_gvn.C, a, b);
2010+
break;
2011+
default:
2012+
fatal_unexpected_iid(id);
2013+
break;
2014+
}
2015+
2016+
set_result(_gvn.transform(n));
19462017
return true;
19472018
}
19482019

@@ -2021,25 +2092,6 @@ bool LibraryCallKit::inline_math_unsignedMultiplyHigh() {
20212092
return true;
20222093
}
20232094

2024-
Node*
2025-
LibraryCallKit::generate_min_max(vmIntrinsics::ID id, Node* x0, Node* y0) {
2026-
Node* result_val = nullptr;
2027-
switch (id) {
2028-
case vmIntrinsics::_min:
2029-
case vmIntrinsics::_min_strict:
2030-
result_val = _gvn.transform(new MinINode(x0, y0));
2031-
break;
2032-
case vmIntrinsics::_max:
2033-
case vmIntrinsics::_max_strict:
2034-
result_val = _gvn.transform(new MaxINode(x0, y0));
2035-
break;
2036-
default:
2037-
fatal_unexpected_iid(id);
2038-
break;
2039-
}
2040-
return result_val;
2041-
}
2042-
20432095
inline int
20442096
LibraryCallKit::classify_unsafe_addr(Node* &base, Node* &offset, BasicType type) {
20452097
const TypePtr* base_type = TypePtr::NULL_PTR;
@@ -4456,7 +4508,7 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) {
44564508
if (!stopped()) {
44574509
// How many elements will we copy from the original?
44584510
// The answer is MinI(orig_tail, length).
4459-
Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length);
4511+
Node* moved = _gvn.transform(new MinINode(orig_tail, length));
44604512

44614513
// Generate a direct call to the right arraycopy function(s).
44624514
// We know the copy is disjoint but we might not know if the
@@ -8477,91 +8529,6 @@ bool LibraryCallKit::inline_character_compare(vmIntrinsics::ID id) {
84778529
return true;
84788530
}
84798531

8480-
//------------------------------inline_fp_min_max------------------------------
8481-
bool LibraryCallKit::inline_fp_min_max(vmIntrinsics::ID id) {
8482-
/* DISABLED BECAUSE METHOD DATA ISN'T COLLECTED PER CALL-SITE, SEE JDK-8015416.
8483-
8484-
// The intrinsic should be used only when the API branches aren't predictable,
8485-
// the last one performing the most important comparison. The following heuristic
8486-
// uses the branch statistics to eventually bail out if necessary.
8487-
8488-
ciMethodData *md = callee()->method_data();
8489-
8490-
if ( md != nullptr && md->is_mature() && md->invocation_count() > 0 ) {
8491-
ciCallProfile cp = caller()->call_profile_at_bci(bci());
8492-
8493-
if ( ((double)cp.count()) / ((double)md->invocation_count()) < 0.8 ) {
8494-
// Bail out if the call-site didn't contribute enough to the statistics.
8495-
return false;
8496-
}
8497-
8498-
uint taken = 0, not_taken = 0;
8499-
8500-
for (ciProfileData *p = md->first_data(); md->is_valid(p); p = md->next_data(p)) {
8501-
if (p->is_BranchData()) {
8502-
taken = ((ciBranchData*)p)->taken();
8503-
not_taken = ((ciBranchData*)p)->not_taken();
8504-
}
8505-
}
8506-
8507-
double balance = (((double)taken) - ((double)not_taken)) / ((double)md->invocation_count());
8508-
balance = balance < 0 ? -balance : balance;
8509-
if ( balance > 0.2 ) {
8510-
// Bail out if the most important branch is predictable enough.
8511-
return false;
8512-
}
8513-
}
8514-
*/
8515-
8516-
Node *a = nullptr;
8517-
Node *b = nullptr;
8518-
Node *n = nullptr;
8519-
switch (id) {
8520-
case vmIntrinsics::_maxF:
8521-
case vmIntrinsics::_minF:
8522-
case vmIntrinsics::_maxF_strict:
8523-
case vmIntrinsics::_minF_strict:
8524-
assert(callee()->signature()->size() == 2, "minF/maxF has 2 parameters of size 1 each.");
8525-
a = argument(0);
8526-
b = argument(1);
8527-
break;
8528-
case vmIntrinsics::_maxD:
8529-
case vmIntrinsics::_minD:
8530-
case vmIntrinsics::_maxD_strict:
8531-
case vmIntrinsics::_minD_strict:
8532-
assert(callee()->signature()->size() == 4, "minD/maxD has 2 parameters of size 2 each.");
8533-
a = round_double_node(argument(0));
8534-
b = round_double_node(argument(2));
8535-
break;
8536-
default:
8537-
fatal_unexpected_iid(id);
8538-
break;
8539-
}
8540-
switch (id) {
8541-
case vmIntrinsics::_maxF:
8542-
case vmIntrinsics::_maxF_strict:
8543-
n = new MaxFNode(a, b);
8544-
break;
8545-
case vmIntrinsics::_minF:
8546-
case vmIntrinsics::_minF_strict:
8547-
n = new MinFNode(a, b);
8548-
break;
8549-
case vmIntrinsics::_maxD:
8550-
case vmIntrinsics::_maxD_strict:
8551-
n = new MaxDNode(a, b);
8552-
break;
8553-
case vmIntrinsics::_minD:
8554-
case vmIntrinsics::_minD_strict:
8555-
n = new MinDNode(a, b);
8556-
break;
8557-
default:
8558-
fatal_unexpected_iid(id);
8559-
break;
8560-
}
8561-
set_result(_gvn.transform(n));
8562-
return true;
8563-
}
8564-
85658532
bool LibraryCallKit::inline_profileBoolean() {
85668533
Node* counts = argument(1);
85678534
const TypeAryPtr* ary = nullptr;

src/hotspot/share/opto/library_call.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ class LibraryCallKit : public GraphKit {
223223
bool inline_math_subtractExactL(bool is_decrement);
224224
bool inline_min_max(vmIntrinsics::ID id);
225225
bool inline_notify(vmIntrinsics::ID id);
226-
Node* generate_min_max(vmIntrinsics::ID id, Node* x, Node* y);
227226
// This returns Type::AnyPtr, RawPtr, or OopPtr.
228227
int classify_unsafe_addr(Node* &base, Node* &offset, BasicType type);
229228
Node* make_unsafe_address(Node*& base, Node* offset, BasicType type = T_ILLEGAL, bool can_cast = false);
@@ -354,7 +353,6 @@ class LibraryCallKit : public GraphKit {
354353
bool inline_vectorizedMismatch();
355354
bool inline_fma(vmIntrinsics::ID id);
356355
bool inline_character_compare(vmIntrinsics::ID id);
357-
bool inline_fp_min_max(vmIntrinsics::ID id);
358356
bool inline_galoisCounterMode_AESCrypt();
359357
Node* inline_galoisCounterMode_AESCrypt_predicate();
360358

src/java.base/share/classes/java/lang/Math.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,7 @@ public static int max(int a, int b) {
20332033
* @param b another argument.
20342034
* @return the larger of {@code a} and {@code b}.
20352035
*/
2036+
@IntrinsicCandidate
20362037
public static long max(long a, long b) {
20372038
return (a >= b) ? a : b;
20382039
}
@@ -2128,6 +2129,7 @@ public static int min(int a, int b) {
21282129
* @param b another argument.
21292130
* @return the smaller of {@code a} and {@code b}.
21302131
*/
2132+
@IntrinsicCandidate
21312133
public static long min(long a, long b) {
21322134
return (a <= b) ? a : b;
21332135
}

test/hotspot/jtreg/compiler/c2/irTests/TestMinMaxIdentities.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ public int intMaxMax(int a, int b) {
112112

113113
// Longs
114114

115-
// As Math.min/max(LL) is not intrinsified, it first needs to be transformed into CMoveL and then MinL/MaxL before
115+
// As Math.min/max(LL) is not intrinsified in the backend, it first needs to be transformed into CMoveL and then MinL/MaxL before
116116
// the identity can be matched. However, the outer min/max is not transformed into CMove because of the CMove cost model.
117-
// As JDK-8307513 adds intrinsics for the methods, the tests will be updated then.
117+
// JDK-8307513 adds intrinsics for the methods such that MinL/MaxL replace the ternary operations,
118+
// and this enables identities to be matched.
119+
// Note that before JDK-8307513 MinL/MaxL nodes were already present before macro expansion.
118120

119121
@Test
120122
@IR(applyIfPlatform = { "riscv64", "false" }, phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, counts = { IRNode.MIN_L, "1" })
@@ -123,13 +125,13 @@ public long longMinMin(long a, long b) {
123125
}
124126

125127
@Test
126-
@IR(applyIfPlatform = { "riscv64", "false" }, phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, counts = { IRNode.MIN_L, "1" })
128+
@IR(failOn = { IRNode.MIN_L, IRNode.MAX_L })
127129
public long longMinMax(long a, long b) {
128130
return Math.min(a, Math.max(a, b));
129131
}
130132

131133
@Test
132-
@IR(applyIfPlatform = { "riscv64", "false" }, phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, counts = { IRNode.MAX_L, "1" })
134+
@IR(failOn = { IRNode.MIN_L, IRNode.MAX_L })
133135
public long longMaxMin(long a, long b) {
134136
return Math.max(a, Math.min(a, b));
135137
}

0 commit comments

Comments
 (0)