8283726: x86_64 intrinsics for compareUnsigned method in Integer and Long

Reviewed-by: kvn, jbhateja
This commit is contained in:
Quan Anh Mai 2022-06-29 10:34:05 +00:00 committed by Jatin Bhateja
parent b96ba19807
commit 108cd69516
15 changed files with 271 additions and 11 deletions

View File

@ -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)
@ -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)

View File

@ -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: */ \
@ -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) \
@ -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) \
@ -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) \
\

View File

@ -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;

View File

@ -97,7 +97,9 @@ macro(CmpL3)
macro(CmpLTMask)
macro(CmpP)
macro(CmpU)
macro(CmpU3)
macro(CmpUL)
macro(CmpUL3)
macro(CompareAndSwapB)
macro(CompareAndSwapS)
macro(CompareAndSwapI)

View File

@ -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:
@ -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)

View File

@ -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);

View File

@ -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

View File

@ -173,6 +173,18 @@ public:
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 {
@ -220,7 +232,19 @@ public:
// 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; }
};

View File

@ -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) \

View File

@ -1500,6 +1500,7 @@ public final class Integer extends Number
* unsigned values
* @since 1.8
*/
@IntrinsicCandidate
public static int compareUnsigned(int x, int y) {
return compare(x + MIN_VALUE, y + MIN_VALUE);
}

View File

@ -1641,6 +1641,7 @@ public final class Long extends Number
* unsigned values
* @since 1.8
*/
@IntrinsicCandidate
public static int compareUnsigned(long x, long y) {
return compare(x + MIN_VALUE, y + MIN_VALUE);
}

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -52,6 +52,7 @@ public class Integers {
@Param("500")
private int size;
private int bound;
private String[] strings;
private int[] intsTiny;
private int[] intsSmall;
@ -60,6 +61,7 @@ public class Integers {
@Setup
public void setup() {
Random r = new Random(0);
bound = 50;
strings = new String[size];
intsTiny = new int[size];
intsSmall = new int[size];
@ -146,4 +148,20 @@ public class Integers {
bh.consume(intsBig[i] << intsSmall[i]);
}
}
@Benchmark
public void compareUnsignedIndirect(Blackhole bh) {
for (int i = 0; i < size; i++) {
int r = (Integer.compareUnsigned(intsSmall[i], bound - 16) < 0) ? 1 : 0;
bh.consume(r);
}
}
@Benchmark
public void compareUnsignedDirect(Blackhole bh) {
for (int i = 0; i < size; i++) {
int r = Integer.compareUnsigned(intsSmall[i], bound - 16);
bh.consume(r);
}
}
}

View File

@ -49,6 +49,7 @@ public class Longs {
@Param("500")
private int size;
private long bound;
private String[] strings;
private long[] longArraySmall;
private long[] longArrayBig;
@ -56,6 +57,7 @@ public class Longs {
@Setup
public void setup() {
var random = ThreadLocalRandom.current();
bound = 20000L;
strings = new String[size];
longArraySmall = new long[size];
longArrayBig = new long[size];
@ -141,4 +143,20 @@ public class Longs {
bh.consume(longArrayBig[i] << longArraySmall[i]);
}
}
@Benchmark
public void compareUnsignedIndirect(Blackhole bh) {
for (int i = 0; i < size; i++) {
int r = (Long.compareUnsigned(longArraySmall[i], bound - 16) < 0) ? 1 : 0;
bh.consume(r);
}
}
@Benchmark
public void compareUnsignedDirect(Blackhole bh) {
for (int i = 0; i < size; i++) {
int r = Long.compareUnsigned(longArraySmall[i], bound - 16);
bh.consume(r);
}
}
}