Skip to content

Commit

Permalink
8283726: x86_64 intrinsics for compareUnsigned method in Integer and …
Browse files Browse the repository at this point in the history
…Long

Reviewed-by: kvn, jbhateja
  • Loading branch information
merykitty authored and Jatin Bhateja committed Jun 29, 2022
1 parent b96ba19 commit 108cd69
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 11 deletions.
52 changes: 52 additions & 0 deletions src/hotspot/cpu/x86/x86_64.ad
Original file line number Diff line number Diff line change
Expand Up @@ -13022,6 +13022,32 @@ instruct testL_reg_mem2(rFlagsReg cr, rRegP src, memory mem, immL0 zero)
ins_pipe(ialu_cr_reg_mem);
%}

// Manifest a CmpU result in an integer register. Very painful.
// This is the test to avoid.
instruct cmpU3_reg_reg(rRegI dst, rRegI src1, rRegI src2, rFlagsReg flags)
%{
match(Set dst (CmpU3 src1 src2));
effect(KILL flags);

ins_cost(275); // XXX
format %{ "cmpl $src1, $src2\t# CmpL3\n\t"
"movl $dst, -1\n\t"
"jb,u done\n\t"
"setne $dst\n\t"
"movzbl $dst, $dst\n\t"
"done:" %}
ins_encode %{
Label done;
__ cmpl($src1$$Register, $src2$$Register);
__ movl($dst$$Register, -1);
__ jccb(Assembler::below, done);
__ setne($dst$$Register);
__ movzbl($dst$$Register, $dst$$Register);
__ bind(done);
%}
ins_pipe(pipe_slow);
%}

// Manifest a CmpL result in an integer register. Very painful.
// This is the test to avoid.
instruct cmpL3_reg_reg(rRegI dst, rRegL src1, rRegL src2, rFlagsReg flags)
Expand All @@ -13048,6 +13074,32 @@ instruct cmpL3_reg_reg(rRegI dst, rRegL src1, rRegL src2, rFlagsReg flags)
ins_pipe(pipe_slow);
%}

// Manifest a CmpUL result in an integer register. Very painful.
// This is the test to avoid.
instruct cmpUL3_reg_reg(rRegI dst, rRegL src1, rRegL src2, rFlagsReg flags)
%{
match(Set dst (CmpUL3 src1 src2));
effect(KILL flags);

ins_cost(275); // XXX
format %{ "cmpq $src1, $src2\t# CmpL3\n\t"
"movl $dst, -1\n\t"
"jb,u done\n\t"
"setne $dst\n\t"
"movzbl $dst, $dst\n\t"
"done:" %}
ins_encode %{
Label done;
__ cmpq($src1$$Register, $src2$$Register);
__ movl($dst$$Register, -1);
__ jccb(Assembler::below, done);
__ setne($dst$$Register);
__ movzbl($dst$$Register, $dst$$Register);
__ bind(done);
%}
ins_pipe(pipe_slow);
%}

// Unsigned long compare Instructions; really, same as signed long except they
// produce an rFlagsRegU instead of rFlagsReg.
instruct compUL_rReg(rFlagsRegU cr, rRegL op1, rRegL op2)
Expand Down
26 changes: 16 additions & 10 deletions src/hotspot/share/classfile/vmIntrinsics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class methodHandle;
do_signature(float2_float_signature, "(FF)F") \
do_signature(float3_float_signature, "(FFF)F") \
do_signature(int2_int_signature, "(II)I") \
do_signature(long2_int_signature, "(JJ)I") \
do_signature(long2_long_signature, "(JJ)J") \
\
/* here are the math names, all together: */ \
Expand All @@ -150,9 +151,9 @@ class methodHandle;
do_name(expand_name,"expand") \
\
do_intrinsic(_dabs, java_lang_Math, abs_name, double_double_signature, F_S) \
do_intrinsic(_fabs, java_lang_Math, abs_name, float_float_signature, F_S) \
do_intrinsic(_iabs, java_lang_Math, abs_name, int_int_signature, F_S) \
do_intrinsic(_labs, java_lang_Math, abs_name, long_long_signature, F_S) \
do_intrinsic(_fabs, java_lang_Math, abs_name, float_float_signature, F_S) \
do_intrinsic(_iabs, java_lang_Math, abs_name, int_int_signature, F_S) \
do_intrinsic(_labs, java_lang_Math, abs_name, long_long_signature, F_S) \
do_intrinsic(_dsin, java_lang_Math, sin_name, double_double_signature, F_S) \
do_intrinsic(_floor, java_lang_Math, floor_name, double_double_signature, F_S) \
do_intrinsic(_ceil, java_lang_Math, ceil_name, double_double_signature, F_S) \
Expand Down Expand Up @@ -205,7 +206,7 @@ class methodHandle;
do_intrinsic(_dsqrt_strict, java_lang_StrictMath, sqrt_name, double_double_signature, F_SN) \
\
do_intrinsic(_floatIsInfinite, java_lang_Float, isInfinite_name, float_bool_signature, F_S) \
do_name( isInfinite_name, "isInfinite") \
do_name( isInfinite_name, "isInfinite") \
do_intrinsic(_doubleIsInfinite, java_lang_Double, isInfinite_name, double_bool_signature, F_S) \
\
do_intrinsic(_floatToRawIntBits, java_lang_Float, floatToRawIntBits_name, float_int_signature, F_SN) \
Expand All @@ -221,12 +222,17 @@ class methodHandle;
do_intrinsic(_longBitsToDouble, java_lang_Double, longBitsToDouble_name, long_double_signature, F_SN)\
do_name( longBitsToDouble_name, "longBitsToDouble") \
\
do_intrinsic(_divideUnsigned_i, java_lang_Integer, divideUnsigned_name, int2_int_signature, F_S) \
do_intrinsic(_remainderUnsigned_i, java_lang_Integer, remainderUnsigned_name, int2_int_signature, F_S) \
do_name( divideUnsigned_name, "divideUnsigned") \
do_intrinsic(_divideUnsigned_l, java_lang_Long, divideUnsigned_name, long2_long_signature, F_S) \
do_intrinsic(_remainderUnsigned_l, java_lang_Long, remainderUnsigned_name, long2_long_signature, F_S) \
do_name( remainderUnsigned_name, "remainderUnsigned") \
do_intrinsic(_compareUnsigned_i, java_lang_Integer, compareUnsigned_name, int2_int_signature, F_S) \
do_intrinsic(_compareUnsigned_l, java_lang_Long, compareUnsigned_name, long2_int_signature, F_S) \
do_name( compareUnsigned_name, "compareUnsigned") \
\
do_intrinsic(_divideUnsigned_i, java_lang_Integer, divideUnsigned_name, int2_int_signature, F_S) \
do_intrinsic(_remainderUnsigned_i, java_lang_Integer, remainderUnsigned_name, int2_int_signature, F_S) \
do_name( divideUnsigned_name, "divideUnsigned") \
do_intrinsic(_divideUnsigned_l, java_lang_Long, divideUnsigned_name, long2_long_signature, F_S) \
do_intrinsic(_remainderUnsigned_l, java_lang_Long, remainderUnsigned_name, long2_long_signature, F_S) \
do_name( remainderUnsigned_name, "remainderUnsigned") \
\
do_intrinsic(_numberOfLeadingZeros_i, java_lang_Integer, numberOfLeadingZeros_name,int_int_signature, F_S) \
do_intrinsic(_numberOfLeadingZeros_l, java_lang_Long, numberOfLeadingZeros_name,long_int_signature, F_S) \
\
Expand Down
6 changes: 6 additions & 0 deletions src/hotspot/share/opto/c2compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
case vmIntrinsics::_reverseBytes_l:
if (!Matcher::match_rule_supported(Op_ReverseBytesL)) return false;
break;
case vmIntrinsics::_compareUnsigned_i:
if (!Matcher::match_rule_supported(Op_CmpU3)) return false;
break;
case vmIntrinsics::_compareUnsigned_l:
if (!Matcher::match_rule_supported(Op_CmpUL3)) return false;
break;
case vmIntrinsics::_divideUnsigned_i:
if (!Matcher::match_rule_supported(Op_UDivI)) return false;
break;
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/opto/classes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ macro(CmpL3)
macro(CmpLTMask)
macro(CmpP)
macro(CmpU)
macro(CmpU3)
macro(CmpUL)
macro(CmpUL3)
macro(CompareAndSwapB)
macro(CompareAndSwapS)
macro(CompareAndSwapI)
Expand Down
19 changes: 19 additions & 0 deletions src/hotspot/share/opto/library_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_expand_i:
case vmIntrinsics::_expand_l: return inline_bitshuffle_methods(intrinsic_id());

case vmIntrinsics::_compareUnsigned_i:
case vmIntrinsics::_compareUnsigned_l: return inline_compare_unsigned(intrinsic_id());

case vmIntrinsics::_divideUnsigned_i:
case vmIntrinsics::_divideUnsigned_l:
case vmIntrinsics::_remainderUnsigned_i:
Expand Down Expand Up @@ -2247,6 +2250,22 @@ bool LibraryCallKit::inline_bitshuffle_methods(vmIntrinsics::ID id) {
return true;
}

//--------------------------inline_number_methods-----------------------------
// inline int Integer.compareUnsigned(int, int)
// inline int Long.compareUnsigned(long, long)
bool LibraryCallKit::inline_compare_unsigned(vmIntrinsics::ID id) {
Node* arg1 = argument(0);
Node* arg2 = (id == vmIntrinsics::_compareUnsigned_l) ? argument(2) : argument(1);
Node* n = NULL;
switch (id) {
case vmIntrinsics::_compareUnsigned_i: n = new CmpU3Node(arg1, arg2); break;
case vmIntrinsics::_compareUnsigned_l: n = new CmpUL3Node(arg1, arg2); break;
default: fatal_unexpected_iid(id); break;
}
set_result(_gvn.transform(n));
return true;
}

//--------------------------inline_unsigned_divmod_methods-----------------------------
// inline int Integer.divideUnsigned(int, int)
// inline int Integer.remainderUnsigned(int, int)
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/opto/library_call.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class LibraryCallKit : public GraphKit {
bool inline_fp_range_check(vmIntrinsics::ID id);
bool inline_number_methods(vmIntrinsics::ID id);
bool inline_bitshuffle_methods(vmIntrinsics::ID id);
bool inline_compare_unsigned(vmIntrinsics::ID id);
bool inline_divmod_methods(vmIntrinsics::ID id);
bool inline_reference_get();
bool inline_reference_refersTo0(bool is_phantom);
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/opto/subnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,8 +848,12 @@ bool CmpUNode::is_index_range_check() const {
Node *CmpINode::Ideal( PhaseGVN *phase, bool can_reshape ) {
if (phase->type(in(2))->higher_equal(TypeInt::ZERO)) {
switch (in(1)->Opcode()) {
case Op_CmpU3: // Collapse a CmpU3/CmpI into a CmpU
return new CmpUNode(in(1)->in(1),in(1)->in(2));
case Op_CmpL3: // Collapse a CmpL3/CmpI into a CmpL
return new CmpLNode(in(1)->in(1),in(1)->in(2));
case Op_CmpUL3: // Collapse a CmpUL3/CmpI into a CmpUL
return new CmpULNode(in(1)->in(1),in(1)->in(2));
case Op_CmpF3: // Collapse a CmpF3/CmpI into a CmpF
return new CmpFNode(in(1)->in(1),in(1)->in(2));
case Op_CmpD3: // Collapse a CmpD3/CmpI into a CmpD
Expand Down
26 changes: 25 additions & 1 deletion src/hotspot/share/opto/subnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ class CmpUNode : public CmpNode {
bool is_index_range_check() const;
};

//------------------------------CmpU3Node--------------------------------------
// Compare 2 unsigned values, returning integer value (-1, 0 or 1).
class CmpU3Node : public CmpUNode {
public:
CmpU3Node( Node *in1, Node *in2 ) : CmpUNode(in1,in2) {
// Since it is not consumed by Bools, it is not really a Cmp.
init_class_id(Class_Sub);
}
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegI; }
};

//------------------------------CmpPNode---------------------------------------
// Compare 2 pointer values, returning condition codes (-1, 0 or 1).
class CmpPNode : public CmpNode {
Expand Down Expand Up @@ -220,7 +232,19 @@ class CmpL3Node : public CmpLNode {
// Since it is not consumed by Bools, it is not really a Cmp.
init_class_id(Class_Sub);
}
virtual int Opcode() const;
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegI; }
};

//------------------------------CmpUL3Node-------------------------------------
// Compare 2 unsigned long values, returning integer value (-1, 0 or 1).
class CmpUL3Node : public CmpULNode {
public:
CmpUL3Node( Node *in1, Node *in2 ) : CmpULNode(in1,in2) {
// Since it is not consumed by Bools, it is not really a Cmp.
init_class_id(Class_Sub);
}
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegI; }
};

Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/runtime/vmStructs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1708,11 +1708,13 @@
declare_c2_type(CmpNode, SubNode) \
declare_c2_type(CmpINode, CmpNode) \
declare_c2_type(CmpUNode, CmpNode) \
declare_c2_type(CmpU3Node, CmpUNode) \
declare_c2_type(CmpPNode, CmpNode) \
declare_c2_type(CmpNNode, CmpNode) \
declare_c2_type(CmpLNode, CmpNode) \
declare_c2_type(CmpULNode, CmpNode) \
declare_c2_type(CmpL3Node, CmpLNode) \
declare_c2_type(CmpUL3Node, CmpULNode) \
declare_c2_type(CmpFNode, CmpNode) \
declare_c2_type(CmpF3Node, CmpFNode) \
declare_c2_type(CmpDNode, CmpNode) \
Expand Down
1 change: 1 addition & 0 deletions src/java.base/share/classes/java/lang/Integer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,7 @@ public static int compare(int x, int y) {
* unsigned values
* @since 1.8
*/
@IntrinsicCandidate
public static int compareUnsigned(int x, int y) {
return compare(x + MIN_VALUE, y + MIN_VALUE);
}
Expand Down
1 change: 1 addition & 0 deletions src/java.base/share/classes/java/lang/Long.java
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,7 @@ public static int compare(long x, long y) {
* unsigned values
* @since 1.8
*/
@IntrinsicCandidate
public static int compareUnsigned(long x, long y) {
return compare(x + MIN_VALUE, y + MIN_VALUE);
}
Expand Down
102 changes: 102 additions & 0 deletions test/hotspot/jtreg/compiler/intrinsics/TestCompareUnsigned.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 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.
*/
package compiler.intrinsics;

import compiler.lib.ir_framework.*;
import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;

/*
* @test
* @key randomness
* @bug 8283726
* @requires os.arch=="amd64" | os.arch=="x86_64"
* @summary Test the intrinsics implementation of Integer/Long::compareUnsigned
* @library /test/lib /
* @run driver compiler.intrinsics.TestCompareUnsigned
*/
public class TestCompareUnsigned {
static final int TRUE_VALUE = 10;
static final int FALSE_VALUE = 4;

public static void main(String[] args) {
var test = new TestFramework(TestCompareUnsigned.class);
test.setDefaultWarmup(1);
test.start();
}

static int expectedResult(int x, int y) {
return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
}

static int expectedResult(long x, long y) {
return Long.compare(x + Long.MIN_VALUE, y + Long.MIN_VALUE);
}

@Test
@IR(failOn = {IRNode.CMP_U3})
@IR(counts = {IRNode.CMP_U, "1"})
public int lessThanInt(int x, int y) {
return Integer.compareUnsigned(x, y) < 0 ? TRUE_VALUE : FALSE_VALUE;
}

@Test
@IR(failOn = {IRNode.CMP_UL3})
@IR(counts = {IRNode.CMP_UL, "1"})
public int lessThanLong(long x, long y) {
return Long.compareUnsigned(x, y) < 0 ? TRUE_VALUE : FALSE_VALUE;
}

@Test
@IR(counts = {IRNode.CMP_U3, "1"})
public int compareInt(int x, int y) {
return Integer.compareUnsigned(x, y);
}

@Test
@IR(counts = {IRNode.CMP_UL3, "1"})
public int compareLong(long x, long y) {
return Long.compareUnsigned(x, y);
}

@Run(test = {"lessThanInt", "lessThanLong", "compareInt", "compareLong"})
public void runTests() {
var random = Utils.getRandomInstance();
for (int i = 0; i < 1000; i++) {
int x = random.nextInt();
int y = random.nextInt();
Asserts.assertEquals(lessThanInt(x, x), FALSE_VALUE);
Asserts.assertEquals(compareInt(x, x), 0);
Asserts.assertEquals(lessThanInt(x, y), expectedResult(x, y) < 0 ? TRUE_VALUE : FALSE_VALUE);
Asserts.assertEquals(compareInt(x, y), expectedResult(x, y));
}
for (int i = 0; i < 1000; i++) {
long x = random.nextLong();
long y = random.nextLong();
Asserts.assertEquals(lessThanLong(x, x), FALSE_VALUE);
Asserts.assertEquals(compareLong(x, x), 0);
Asserts.assertEquals(lessThanLong(x, y), expectedResult(x, y) < 0 ? TRUE_VALUE : FALSE_VALUE);
Asserts.assertEquals(compareLong(x, y), expectedResult(x, y));
}
}
}
4 changes: 4 additions & 0 deletions test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ public class IRNode {
public static final String SUB_L = START + "SubL" + MID + END;
public static final String SUB_F = START + "SubF" + MID + END;
public static final String SUB_D = START + "SubD" + MID + END;
public static final String CMP_U = START + "CmpU" + MID + END;
public static final String CMP_UL = START + "CmpUL" + MID + END;
public static final String CMP_U3 = START + "CmpU3" + MID + END;
public static final String CMP_UL3 = START + "CmpUL3" + MID + END;
public static final String MUL = START + "Mul(I|L|F|D)" + MID + END;
public static final String MUL_I = START + "MulI" + MID + END;
public static final String MUL_L = START + "MulL" + MID + END;
Expand Down

1 comment on commit 108cd69

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.