8282221: x86 intrinsics for divideUnsigned and remainderUnsigned methods in java.lang.Integer and java.lang.Long
Reviewed-by: sviswanathan, kvn, jbhateja
This commit is contained in:
parent
0b867b5e73
commit
37e28aea27
@ -12366,6 +12366,11 @@ void Assembler::idivq(Register src) {
|
||||
emit_int16((unsigned char)0xF7, (0xF8 | encode));
|
||||
}
|
||||
|
||||
void Assembler::divq(Register src) {
|
||||
int encode = prefixq_and_encode(src->encoding());
|
||||
emit_int16((unsigned char)0xF7, (0xF0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::imulq(Register dst, Register src) {
|
||||
int encode = prefixq_and_encode(dst->encoding(), src->encoding());
|
||||
emit_int24(0x0F, (unsigned char)0xAF, (0xC0 | encode));
|
||||
|
@ -1368,6 +1368,7 @@ private:
|
||||
|
||||
#ifdef _LP64
|
||||
void idivq(Register src);
|
||||
void divq(Register src); // Unsigned division
|
||||
#endif
|
||||
|
||||
void imull(Register src);
|
||||
|
@ -4534,3 +4534,167 @@ void C2_MacroAssembler::vector_maskall_operation32(KRegister dst, Register src,
|
||||
kunpckdql(dst, tmp, tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
void C2_MacroAssembler::udivI(Register rax, Register divisor, Register rdx) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
cmpl(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorl(rdx, rdx);
|
||||
divl(divisor);
|
||||
jmpb(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath for divisor < 0:
|
||||
// quotient = (dividend & ~(dividend - divisor)) >>> (Integer.SIZE - 1)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in java.lang.Long.divideUnsigned()
|
||||
movl(rdx, rax);
|
||||
subl(rdx, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnl(rax, rdx, rax);
|
||||
} else {
|
||||
notl(rdx);
|
||||
andl(rax, rdx);
|
||||
}
|
||||
shrl(rax, 31);
|
||||
bind(done);
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::umodI(Register rax, Register divisor, Register rdx) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
cmpl(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorl(rdx, rdx);
|
||||
divl(divisor);
|
||||
jmpb(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath when divisor < 0:
|
||||
// remainder = dividend - (((dividend & ~(dividend - divisor)) >> (Integer.SIZE - 1)) & divisor)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in java.lang.Long.remainderUnsigned()
|
||||
movl(rdx, rax);
|
||||
subl(rax, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnl(rax, rax, rdx);
|
||||
} else {
|
||||
notl(rax);
|
||||
andl(rax, rdx);
|
||||
}
|
||||
sarl(rax, 31);
|
||||
andl(rax, divisor);
|
||||
subl(rdx, rax);
|
||||
bind(done);
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::udivmodI(Register rax, Register divisor, Register rdx, Register tmp) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
|
||||
cmpl(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorl(rdx, rdx);
|
||||
divl(divisor);
|
||||
jmpb(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath for divisor < 0:
|
||||
// quotient = (dividend & ~(dividend - divisor)) >>> (Integer.SIZE - 1)
|
||||
// remainder = dividend - (((dividend & ~(dividend - divisor)) >> (Integer.SIZE - 1)) & divisor)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in
|
||||
// java.lang.Long.divideUnsigned() and java.lang.Long.remainderUnsigned()
|
||||
movl(rdx, rax);
|
||||
subl(rax, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnl(rax, rax, rdx);
|
||||
} else {
|
||||
notl(rax);
|
||||
andl(rax, rdx);
|
||||
}
|
||||
movl(tmp, rax);
|
||||
shrl(rax, 31); // quotient
|
||||
sarl(tmp, 31);
|
||||
andl(tmp, divisor);
|
||||
subl(rdx, tmp); // remainder
|
||||
bind(done);
|
||||
}
|
||||
|
||||
#ifdef _LP64
|
||||
void C2_MacroAssembler::udivL(Register rax, Register divisor, Register rdx) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
cmpq(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorl(rdx, rdx);
|
||||
divq(divisor);
|
||||
jmpb(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath for divisor < 0:
|
||||
// quotient = (dividend & ~(dividend - divisor)) >>> (Long.SIZE - 1)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in java.lang.Long.divideUnsigned()
|
||||
movq(rdx, rax);
|
||||
subq(rdx, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnq(rax, rdx, rax);
|
||||
} else {
|
||||
notq(rdx);
|
||||
andq(rax, rdx);
|
||||
}
|
||||
shrq(rax, 63);
|
||||
bind(done);
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::umodL(Register rax, Register divisor, Register rdx) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
cmpq(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorq(rdx, rdx);
|
||||
divq(divisor);
|
||||
jmp(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath when divisor < 0:
|
||||
// remainder = dividend - (((dividend & ~(dividend - divisor)) >> (Long.SIZE - 1)) & divisor)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in java.lang.Long.remainderUnsigned()
|
||||
movq(rdx, rax);
|
||||
subq(rax, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnq(rax, rax, rdx);
|
||||
} else {
|
||||
notq(rax);
|
||||
andq(rax, rdx);
|
||||
}
|
||||
sarq(rax, 63);
|
||||
andq(rax, divisor);
|
||||
subq(rdx, rax);
|
||||
bind(done);
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::udivmodL(Register rax, Register divisor, Register rdx, Register tmp) {
|
||||
Label done;
|
||||
Label neg_divisor_fastpath;
|
||||
cmpq(divisor, 0);
|
||||
jccb(Assembler::less, neg_divisor_fastpath);
|
||||
xorq(rdx, rdx);
|
||||
divq(divisor);
|
||||
jmp(done);
|
||||
bind(neg_divisor_fastpath);
|
||||
// Fastpath for divisor < 0:
|
||||
// quotient = (dividend & ~(dividend - divisor)) >>> (Long.SIZE - 1)
|
||||
// remainder = dividend - (((dividend & ~(dividend - divisor)) >> (Long.SIZE - 1)) & divisor)
|
||||
// See Hacker's Delight (2nd ed), section 9.3 which is implemented in
|
||||
// java.lang.Long.divideUnsigned() and java.lang.Long.remainderUnsigned()
|
||||
movq(rdx, rax);
|
||||
subq(rax, divisor);
|
||||
if (VM_Version::supports_bmi1()) {
|
||||
andnq(rax, rax, rdx);
|
||||
} else {
|
||||
notq(rax);
|
||||
andq(rax, rdx);
|
||||
}
|
||||
movq(tmp, rax);
|
||||
shrq(rax, 63); // quotient
|
||||
sarq(tmp, 63);
|
||||
andq(tmp, divisor);
|
||||
subq(rdx, tmp); // remainder
|
||||
bind(done);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -340,6 +340,15 @@ public:
|
||||
void evpternlog(XMMRegister dst, int func, KRegister mask, XMMRegister src2, Address src3,
|
||||
bool merge, BasicType bt, int vlen_enc);
|
||||
|
||||
void udivI(Register rax, Register divisor, Register rdx);
|
||||
void umodI(Register rax, Register divisor, Register rdx);
|
||||
void udivmodI(Register rax, Register divisor, Register rdx, Register tmp);
|
||||
|
||||
#ifdef _LP64
|
||||
void udivL(Register rax, Register divisor, Register rdx);
|
||||
void umodL(Register rax, Register divisor, Register rdx);
|
||||
void udivmodL(Register rax, Register divisor, Register rdx, Register tmp);
|
||||
#endif
|
||||
void vector_popcount_int(XMMRegister dst, XMMRegister src, XMMRegister xtmp1,
|
||||
XMMRegister xtmp2, XMMRegister xtmp3, Register rtmp,
|
||||
int vec_enc);
|
||||
|
@ -8688,6 +8688,32 @@ instruct divL_rReg(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div,
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
instruct udivI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, rFlagsReg cr)
|
||||
%{
|
||||
match(Set rax (UDivI rax div));
|
||||
effect(KILL rdx, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "udivl $rax,$rax,$div\t# UDivI\n" %}
|
||||
ins_encode %{
|
||||
__ udivI($rax$$Register, $div$$Register, $rdx$$Register);
|
||||
%}
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
instruct udivL_rReg(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div, rFlagsReg cr)
|
||||
%{
|
||||
match(Set rax (UDivL rax div));
|
||||
effect(KILL rdx, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "udivq $rax,$rax,$div\t# UDivL\n" %}
|
||||
ins_encode %{
|
||||
__ udivL($rax$$Register, $div$$Register, $rdx$$Register);
|
||||
%}
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
// Integer DIVMOD with Register, both quotient and mod results
|
||||
instruct divModI_rReg_divmod(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div,
|
||||
rFlagsReg cr)
|
||||
@ -8729,6 +8755,41 @@ instruct divModL_rReg_divmod(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div,
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// Unsigned integer DIVMOD with Register, both quotient and mod results
|
||||
instruct udivModI_rReg_divmod(rax_RegI rax, no_rax_rdx_RegI tmp, rdx_RegI rdx,
|
||||
no_rax_rdx_RegI div, rFlagsReg cr)
|
||||
%{
|
||||
match(UDivModI rax div);
|
||||
effect(TEMP tmp, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "udivl $rax,$rax,$div\t# begin UDivModI\n\t"
|
||||
"umodl $rdx,$rax,$div\t! using $tmp as TEMP # end UDivModI\n"
|
||||
%}
|
||||
ins_encode %{
|
||||
__ udivmodI($rax$$Register, $div$$Register, $rdx$$Register, $tmp$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// Unsigned long DIVMOD with Register, both quotient and mod results
|
||||
instruct udivModL_rReg_divmod(rax_RegL rax, no_rax_rdx_RegL tmp, rdx_RegL rdx,
|
||||
no_rax_rdx_RegL div, rFlagsReg cr)
|
||||
%{
|
||||
match(UDivModL rax div);
|
||||
effect(TEMP tmp, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "udivq $rax,$rax,$div\t# begin UDivModL\n\t"
|
||||
"umodq $rdx,$rax,$div\t! using $tmp as TEMP # end UDivModL\n"
|
||||
%}
|
||||
ins_encode %{
|
||||
__ udivmodL($rax$$Register, $div$$Register, $rdx$$Register, $tmp$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
|
||||
//----------- DivL-By-Constant-Expansions--------------------------------------
|
||||
// DivI cases are handled by the compiler
|
||||
|
||||
@ -8832,6 +8893,32 @@ instruct modL_rReg(rdx_RegL rdx, rax_RegL rax, no_rax_rdx_RegL div,
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
instruct umodI_rReg(rdx_RegI rdx, rax_RegI rax, no_rax_rdx_RegI div, rFlagsReg cr)
|
||||
%{
|
||||
match(Set rdx (UModI rax div));
|
||||
effect(KILL rax, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "umodl $rdx,$rax,$div\t# UModI\n" %}
|
||||
ins_encode %{
|
||||
__ umodI($rax$$Register, $div$$Register, $rdx$$Register);
|
||||
%}
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
instruct umodL_rReg(rdx_RegL rdx, rax_RegL rax, no_rax_rdx_RegL div, rFlagsReg cr)
|
||||
%{
|
||||
match(Set rdx (UModL rax div));
|
||||
effect(KILL rax, KILL cr);
|
||||
|
||||
ins_cost(300);
|
||||
format %{ "umodq $rdx,$rax,$div\t# UModL\n" %}
|
||||
ins_encode %{
|
||||
__ umodL($rax$$Register, $div$$Register, $rdx$$Register);
|
||||
%}
|
||||
ins_pipe(ialu_reg_reg_alu0);
|
||||
%}
|
||||
|
||||
// Integer Shift Instructions
|
||||
// Shift Left by one
|
||||
instruct salI_rReg_1(rRegI dst, immI_1 shift, rFlagsReg cr)
|
||||
|
@ -216,6 +216,12 @@ 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(_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) \
|
||||
\
|
||||
|
@ -267,6 +267,18 @@ 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::_divideUnsigned_i:
|
||||
if (!Matcher::match_rule_supported(Op_UDivI)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_remainderUnsigned_i:
|
||||
if (!Matcher::match_rule_supported(Op_UModI)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_divideUnsigned_l:
|
||||
if (!Matcher::match_rule_supported(Op_UDivL)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_remainderUnsigned_l:
|
||||
if (!Matcher::match_rule_supported(Op_UModL)) return false;
|
||||
break;
|
||||
|
||||
/* CompareAndSet, Object: */
|
||||
case vmIntrinsics::_compareAndSetReference:
|
||||
|
@ -162,9 +162,13 @@ macro(DivD)
|
||||
macro(DivF)
|
||||
macro(DivI)
|
||||
macro(DivL)
|
||||
macro(UDivI)
|
||||
macro(UDivL)
|
||||
macro(DivMod)
|
||||
macro(DivModI)
|
||||
macro(DivModL)
|
||||
macro(UDivModI)
|
||||
macro(UDivModL)
|
||||
macro(EncodeISOArray)
|
||||
macro(EncodeP)
|
||||
macro(EncodePKlass)
|
||||
@ -231,6 +235,8 @@ macro(ModD)
|
||||
macro(ModF)
|
||||
macro(ModI)
|
||||
macro(ModL)
|
||||
macro(UModI)
|
||||
macro(UModL)
|
||||
macro(MoveI2F)
|
||||
macro(MoveF2I)
|
||||
macro(MoveL2D)
|
||||
|
@ -21,7 +21,6 @@
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "jvm_io.h"
|
||||
#include "asm/macroAssembler.hpp"
|
||||
@ -3500,6 +3499,36 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
|
||||
}
|
||||
break;
|
||||
|
||||
case Op_UModI:
|
||||
if (UseDivMod) {
|
||||
// Check if a%b and a/b both exist
|
||||
Node* d = n->find_similar(Op_UDivI);
|
||||
if (d) {
|
||||
// Replace them with a fused unsigned divmod if supported
|
||||
if (Matcher::has_match_rule(Op_UDivModI)) {
|
||||
UDivModINode* divmod = UDivModINode::make(n);
|
||||
d->subsume_by(divmod->div_proj(), this);
|
||||
n->subsume_by(divmod->mod_proj(), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Op_UModL:
|
||||
if (UseDivMod) {
|
||||
// Check if a%b and a/b both exist
|
||||
Node* d = n->find_similar(Op_UDivL);
|
||||
if (d) {
|
||||
// Replace them with a fused unsigned divmod if supported
|
||||
if (Matcher::has_match_rule(Op_UDivModL)) {
|
||||
UDivModLNode* divmod = UDivModLNode::make(n);
|
||||
d->subsume_by(divmod->div_proj(), this);
|
||||
n->subsume_by(divmod->mod_proj(), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Op_LoadVector:
|
||||
case Op_StoreVector:
|
||||
case Op_LoadVectorGather:
|
||||
|
@ -841,6 +841,84 @@ Node *DivDNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
return (new MulDNode(in(1), phase->makecon(TypeD::make(reciprocal))));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Identity---------------------------------------
|
||||
// If the divisor is 1, we are an identity on the dividend.
|
||||
Node* UDivINode::Identity(PhaseGVN* phase) {
|
||||
return (phase->type( in(2) )->higher_equal(TypeInt::ONE)) ? in(1) : this;
|
||||
}
|
||||
//------------------------------Value------------------------------------------
|
||||
// A UDivINode divides its inputs. The third input is a Control input, used to
|
||||
// prevent hoisting the divide above an unsafe test.
|
||||
const Type* UDivINode::Value(PhaseGVN* phase) const {
|
||||
// Either input is TOP ==> the result is TOP
|
||||
const Type *t1 = phase->type( in(1) );
|
||||
const Type *t2 = phase->type( in(2) );
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
|
||||
// x/x == 1 since we always generate the dynamic divisor check for 0.
|
||||
if (in(1) == in(2)) {
|
||||
return TypeInt::ONE;
|
||||
}
|
||||
|
||||
// Either input is BOTTOM ==> the result is the local BOTTOM
|
||||
const Type *bot = bottom_type();
|
||||
if( (t1 == bot) || (t2 == bot) ||
|
||||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
|
||||
return bot;
|
||||
|
||||
// Otherwise we give up all hope
|
||||
return TypeInt::INT;
|
||||
}
|
||||
|
||||
//------------------------------Idealize---------------------------------------
|
||||
Node *UDivINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Check for dead control input
|
||||
if (in(0) && remove_dead_region(phase, can_reshape)) return this;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Identity---------------------------------------
|
||||
// If the divisor is 1, we are an identity on the dividend.
|
||||
Node* UDivLNode::Identity(PhaseGVN* phase) {
|
||||
return (phase->type( in(2) )->higher_equal(TypeLong::ONE)) ? in(1) : this;
|
||||
}
|
||||
//------------------------------Value------------------------------------------
|
||||
// A UDivLNode divides its inputs. The third input is a Control input, used to
|
||||
// prevent hoisting the divide above an unsafe test.
|
||||
const Type* UDivLNode::Value(PhaseGVN* phase) const {
|
||||
// Either input is TOP ==> the result is TOP
|
||||
const Type *t1 = phase->type( in(1) );
|
||||
const Type *t2 = phase->type( in(2) );
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
|
||||
// x/x == 1 since we always generate the dynamic divisor check for 0.
|
||||
if (in(1) == in(2)) {
|
||||
return TypeLong::ONE;
|
||||
}
|
||||
|
||||
// Either input is BOTTOM ==> the result is the local BOTTOM
|
||||
const Type *bot = bottom_type();
|
||||
if( (t1 == bot) || (t2 == bot) ||
|
||||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
|
||||
return bot;
|
||||
|
||||
// Otherwise we give up all hope
|
||||
return TypeLong::LONG;
|
||||
}
|
||||
|
||||
//------------------------------Idealize---------------------------------------
|
||||
Node *UDivLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Check for dead control input
|
||||
if (in(0) && remove_dead_region(phase, can_reshape)) return this;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Idealize---------------------------------------
|
||||
Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
@ -1005,6 +1083,13 @@ const Type* ModINode::Value(PhaseGVN* phase) const {
|
||||
return TypeInt::make( i1->get_con() % i2->get_con() );
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Idealize---------------------------------------
|
||||
Node *UModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Check for dead control input
|
||||
if( in(0) && remove_dead_region(phase, can_reshape) ) return this;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Idealize---------------------------------------
|
||||
@ -1216,6 +1301,14 @@ const Type* ModFNode::Value(PhaseGVN* phase) const {
|
||||
return TypeF::make(jfloat_cast(xr));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Idealize---------------------------------------
|
||||
Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Check for dead control input
|
||||
if( in(0) && remove_dead_region(phase, can_reshape) ) return this;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//------------------------------Value------------------------------------------
|
||||
@ -1320,3 +1413,56 @@ Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) {
|
||||
}
|
||||
return new MachProjNode(this, proj->_con, rm, ideal_reg);
|
||||
}
|
||||
|
||||
//------------------------------make------------------------------------------
|
||||
UDivModINode* UDivModINode::make(Node* div_or_mod) {
|
||||
Node* n = div_or_mod;
|
||||
assert(n->Opcode() == Op_UDivI || n->Opcode() == Op_UModI,
|
||||
"only div or mod input pattern accepted");
|
||||
|
||||
UDivModINode* divmod = new UDivModINode(n->in(0), n->in(1), n->in(2));
|
||||
Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num);
|
||||
Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num);
|
||||
return divmod;
|
||||
}
|
||||
|
||||
//------------------------------make------------------------------------------
|
||||
UDivModLNode* UDivModLNode::make(Node* div_or_mod) {
|
||||
Node* n = div_or_mod;
|
||||
assert(n->Opcode() == Op_UDivL || n->Opcode() == Op_UModL,
|
||||
"only div or mod input pattern accepted");
|
||||
|
||||
UDivModLNode* divmod = new UDivModLNode(n->in(0), n->in(1), n->in(2));
|
||||
Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num);
|
||||
Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num);
|
||||
return divmod;
|
||||
}
|
||||
|
||||
//------------------------------match------------------------------------------
|
||||
// return result(s) along with their RegMask info
|
||||
Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) {
|
||||
uint ideal_reg = proj->ideal_reg();
|
||||
RegMask rm;
|
||||
if (proj->_con == div_proj_num) {
|
||||
rm = match->divI_proj_mask();
|
||||
} else {
|
||||
assert(proj->_con == mod_proj_num, "must be div or mod projection");
|
||||
rm = match->modI_proj_mask();
|
||||
}
|
||||
return new MachProjNode(this, proj->_con, rm, ideal_reg);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------match------------------------------------------
|
||||
// return result(s) along with their RegMask info
|
||||
Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) {
|
||||
uint ideal_reg = proj->ideal_reg();
|
||||
RegMask rm;
|
||||
if (proj->_con == div_proj_num) {
|
||||
rm = match->divL_proj_mask();
|
||||
} else {
|
||||
assert(proj->_con == mod_proj_num, "must be div or mod projection");
|
||||
rm = match->modL_proj_mask();
|
||||
}
|
||||
return new MachProjNode(this, proj->_con, rm, ideal_reg);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 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
|
||||
@ -90,6 +90,32 @@ public:
|
||||
virtual uint ideal_reg() const { return Op_RegD; }
|
||||
};
|
||||
|
||||
//------------------------------UDivINode---------------------------------------
|
||||
// Unsigned integer division
|
||||
class UDivINode : public Node {
|
||||
public:
|
||||
UDivINode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeInt::INT; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
//------------------------------UDivLNode---------------------------------------
|
||||
// Unsigned long division
|
||||
class UDivLNode : public Node {
|
||||
public:
|
||||
UDivLNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeLong::LONG; }
|
||||
virtual uint ideal_reg() const { return Op_RegL; }
|
||||
};
|
||||
|
||||
//------------------------------ModINode---------------------------------------
|
||||
// Integer modulus
|
||||
class ModINode : public Node {
|
||||
@ -136,6 +162,28 @@ public:
|
||||
virtual uint ideal_reg() const { return Op_RegD; }
|
||||
};
|
||||
|
||||
//------------------------------UModINode---------------------------------------
|
||||
// Unsigned integer modulus
|
||||
class UModINode : public Node {
|
||||
public:
|
||||
UModINode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeInt::INT; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
//------------------------------UModLNode---------------------------------------
|
||||
// Unsigned long modulus
|
||||
class UModLNode : public Node {
|
||||
public:
|
||||
UModLNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeLong::LONG; }
|
||||
virtual uint ideal_reg() const { return Op_RegL; }
|
||||
};
|
||||
|
||||
//------------------------------DivModNode---------------------------------------
|
||||
// Division with remainder result.
|
||||
class DivModNode : public MultiNode {
|
||||
@ -184,4 +232,31 @@ public:
|
||||
static DivModLNode* make(Node* div_or_mod);
|
||||
};
|
||||
|
||||
|
||||
//------------------------------UDivModINode---------------------------------------
|
||||
// Unsigend integer division with remainder result.
|
||||
class UDivModINode : public DivModNode {
|
||||
public:
|
||||
UDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; }
|
||||
virtual Node *match( const ProjNode *proj, const Matcher *m );
|
||||
|
||||
// Make a divmod and associated projections from a div or mod.
|
||||
static UDivModINode* make(Node* div_or_mod);
|
||||
};
|
||||
|
||||
//------------------------------UDivModLNode---------------------------------------
|
||||
// Unsigned long division with remainder result.
|
||||
class UDivModLNode : public DivModNode {
|
||||
public:
|
||||
UDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; }
|
||||
virtual Node *match( const ProjNode *proj, const Matcher *m );
|
||||
|
||||
// Make a divmod and associated projections from a div or mod.
|
||||
static UDivModLNode* make(Node* div_or_mod);
|
||||
};
|
||||
|
||||
#endif // SHARE_OPTO_DIVNODE_HPP
|
||||
|
@ -527,6 +527,11 @@ bool LibraryCallKit::try_to_inline(int predicate) {
|
||||
case vmIntrinsics::_reverseBytes_s:
|
||||
case vmIntrinsics::_reverseBytes_c: return inline_number_methods(intrinsic_id());
|
||||
|
||||
case vmIntrinsics::_divideUnsigned_i:
|
||||
case vmIntrinsics::_divideUnsigned_l:
|
||||
case vmIntrinsics::_remainderUnsigned_i:
|
||||
case vmIntrinsics::_remainderUnsigned_l: return inline_divmod_methods(intrinsic_id());
|
||||
|
||||
case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass();
|
||||
|
||||
case vmIntrinsics::_Reference_get: return inline_reference_get();
|
||||
@ -2188,6 +2193,39 @@ bool LibraryCallKit::inline_number_methods(vmIntrinsics::ID id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------inline_unsigned_divmod_methods-----------------------------
|
||||
// inline int Integer.divideUnsigned(init, int)
|
||||
// inline int Integer.remainderUnsigned(int, int)
|
||||
bool LibraryCallKit::inline_divmod_methods(vmIntrinsics::ID id) {
|
||||
Node* n = NULL;
|
||||
switch(id) {
|
||||
case vmIntrinsics::_divideUnsigned_i:
|
||||
zero_check_int(argument(1));
|
||||
// Compile-time detect of null-exception?
|
||||
if (stopped()) return false;
|
||||
n = new UDivINode(control(), argument(0), argument(1));
|
||||
break;
|
||||
case vmIntrinsics::_divideUnsigned_l:
|
||||
zero_check_long(argument(2));
|
||||
// Compile-time detect of null-exception?
|
||||
if (stopped()) return false;
|
||||
n = new UDivLNode(control(), argument(0), argument(2)); break;
|
||||
case vmIntrinsics::_remainderUnsigned_i:
|
||||
zero_check_int(argument(1));
|
||||
// Compile-time detect of null-exception?
|
||||
if (stopped()) return false;
|
||||
n = new UModINode(control(), argument(0), argument(1)); break;
|
||||
case vmIntrinsics::_remainderUnsigned_l:
|
||||
zero_check_long(argument(2));
|
||||
// Compile-time detect of null-exception?
|
||||
if (stopped()) return false;
|
||||
n = new UModLNode(control(), argument(0), argument(2)); break;
|
||||
default: fatal_unexpected_iid(id); break;
|
||||
}
|
||||
set_result(_gvn.transform(n));
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------inline_unsafe_access----------------------------
|
||||
|
||||
const TypeOopPtr* LibraryCallKit::sharpen_unsafe_type(Compile::AliasType* alias_type, const TypePtr *adr_type) {
|
||||
|
@ -264,6 +264,7 @@ class LibraryCallKit : public GraphKit {
|
||||
bool inline_onspinwait();
|
||||
bool inline_fp_conversions(vmIntrinsics::ID id);
|
||||
bool inline_number_methods(vmIntrinsics::ID id);
|
||||
bool inline_divmod_methods(vmIntrinsics::ID id);
|
||||
bool inline_reference_get();
|
||||
bool inline_reference_refersTo0(bool is_phantom);
|
||||
bool inline_Class_cast();
|
||||
|
@ -1579,13 +1579,19 @@
|
||||
declare_c2_type(DivLNode, Node) \
|
||||
declare_c2_type(DivFNode, Node) \
|
||||
declare_c2_type(DivDNode, Node) \
|
||||
declare_c2_type(UDivINode, Node) \
|
||||
declare_c2_type(UDivLNode, Node) \
|
||||
declare_c2_type(ModINode, Node) \
|
||||
declare_c2_type(ModLNode, Node) \
|
||||
declare_c2_type(ModFNode, Node) \
|
||||
declare_c2_type(ModDNode, Node) \
|
||||
declare_c2_type(UModINode, Node) \
|
||||
declare_c2_type(UModLNode, Node) \
|
||||
declare_c2_type(DivModNode, MultiNode) \
|
||||
declare_c2_type(DivModINode, DivModNode) \
|
||||
declare_c2_type(DivModLNode, DivModNode) \
|
||||
declare_c2_type(UDivModINode, DivModNode) \
|
||||
declare_c2_type(UDivModLNode, DivModNode) \
|
||||
declare_c2_type(BoxLockNode, Node) \
|
||||
declare_c2_type(LoopNode, RegionNode) \
|
||||
declare_c2_type(CountedLoopNode, LoopNode) \
|
||||
|
@ -1542,6 +1542,7 @@ public final class Integer extends Number
|
||||
* @see #remainderUnsigned
|
||||
* @since 1.8
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static int divideUnsigned(int dividend, int divisor) {
|
||||
// In lieu of tricky code, for now just use long arithmetic.
|
||||
return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));
|
||||
@ -1559,6 +1560,7 @@ public final class Integer extends Number
|
||||
* @see #divideUnsigned
|
||||
* @since 1.8
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static int remainderUnsigned(int dividend, int divisor) {
|
||||
// In lieu of tricky code, for now just use long arithmetic.
|
||||
return (int)(toUnsignedLong(dividend) % toUnsignedLong(divisor));
|
||||
|
@ -1663,6 +1663,7 @@ public final class Long extends Number
|
||||
* @see #remainderUnsigned
|
||||
* @since 1.8
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static long divideUnsigned(long dividend, long divisor) {
|
||||
/* See Hacker's Delight (2nd ed), section 9.3 */
|
||||
if (divisor >= 0) {
|
||||
@ -1685,6 +1686,7 @@ public final class Long extends Number
|
||||
* @see #divideUnsigned
|
||||
* @since 1.8
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static long remainderUnsigned(long dividend, long divisor) {
|
||||
/* See Hacker's Delight (2nd ed), section 9.3 */
|
||||
if (divisor >= 0) {
|
||||
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Test x86_64 intrinsic for divideUnsigned() and remainderUnsigned() methods for Integer
|
||||
* @requires os.arch=="amd64" | os.arch=="x86_64"
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.intrinsics.TestIntegerUnsignedDivMod
|
||||
*/
|
||||
|
||||
package compiler.intrinsics;
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
public class TestIntegerUnsignedDivMod {
|
||||
private int BUFFER_SIZE;
|
||||
private int[] dividends;
|
||||
private int[] divisors;
|
||||
private int[] quotients;
|
||||
private int[] remainders;
|
||||
final long MAX_UNSIGNED_INT = Integer.toUnsignedLong(0xffff_ffff);
|
||||
long[] inRange = {
|
||||
0L,
|
||||
1L,
|
||||
2L,
|
||||
2147483646L, // MAX_VALUE - 1
|
||||
2147483647L, // MAX_VALUE
|
||||
2147483648L, // MAX_VALUE + 1
|
||||
MAX_UNSIGNED_INT - 1L,
|
||||
MAX_UNSIGNED_INT,
|
||||
};
|
||||
|
||||
public static void main(String args[]) {
|
||||
TestFramework.run(TestIntegerUnsignedDivMod.class);
|
||||
}
|
||||
|
||||
public TestIntegerUnsignedDivMod() {
|
||||
BUFFER_SIZE = inRange.length * inRange.length;
|
||||
dividends = new int[BUFFER_SIZE];
|
||||
divisors = new int[BUFFER_SIZE];
|
||||
quotients = new int[BUFFER_SIZE];
|
||||
remainders = new int[BUFFER_SIZE];
|
||||
|
||||
int idx = 0;
|
||||
for (int i = 0; i < inRange.length; i++) {
|
||||
for (int j = 0; j < inRange.length; j++){
|
||||
dividends[idx] = (int) inRange[i];
|
||||
divisors[idx] = (int) inRange[j];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UDivI", ">= 1"}) // Atleast one UDivI node is generated if intrinsic is used
|
||||
public void testDivideUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
quotients[i] = Integer.divideUnsigned(dividends[i], divisors[i]);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("divideUnsigned");
|
||||
}
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UModI", ">= 1"}) // Atleast one UModI node is generated if intrinsic is used
|
||||
public void testRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
remainders[i] = Integer.remainderUnsigned(dividends[i], divisors[i]);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("remainderUnsigned");
|
||||
}
|
||||
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UDivModI", ">= 1"}) // Atleast one UDivModI node is generated if intrinsic is used
|
||||
public void testDivModUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
divmod(dividends[i], divisors[i], i);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("divmodUnsigned");
|
||||
}
|
||||
|
||||
private void divmod(int dividend, int divisor, int i) {
|
||||
quotients[i] = Integer.divideUnsigned(dividend, divisor);
|
||||
remainders[i] = Integer.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public void checkResult(String mode) {
|
||||
for (int i=0; i < BUFFER_SIZE; i++) {
|
||||
if (divisors[i] == 0) continue;
|
||||
long dividend = Integer.toUnsignedLong(dividends[i]);
|
||||
long divisor = Integer.toUnsignedLong(divisors[i]);
|
||||
int quo = (int) (dividend / divisor);
|
||||
int rem = (int) (dividend % divisor);
|
||||
boolean mismatch;
|
||||
switch (mode) {
|
||||
case "divideUnsigned": mismatch = (quotients[i] != quo); break;
|
||||
case "remainderUnsigned": mismatch = (remainders[i] != rem); break;
|
||||
case "divmodUnsigned": mismatch = (quotients[i] != quo || remainders[i] != rem); break;
|
||||
default: throw new IllegalArgumentException("incorrect mode");
|
||||
}
|
||||
if (mismatch) {
|
||||
throw new RuntimeException(errorMessage(mode, i, quo, rem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String errorMessage(String mode, int i, int quo, int rem) {
|
||||
StringBuilder sb = new StringBuilder(mode);
|
||||
sb = sb.append(" test error at index=").append(i);
|
||||
sb = sb.append(": dividend=").append(dividends[i]);
|
||||
sb = sb.append("; divisor= ").append(divisors[i]);
|
||||
if (!mode.equals("remainderUnsigned")) {
|
||||
sb = sb.append("; quotient (expected)= ").append(quo);
|
||||
sb = sb.append("; quotient (actual)= ").append(quotients[i]);
|
||||
}
|
||||
if (!mode.equals("divideUnsigned")) {
|
||||
sb = sb.append("; remainder (expected)= ").append(rem);
|
||||
sb = sb.append("; remainder (actual)= ").append(remainders[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Test x86_64 intrinsic for divideUnsigned() and remainderUnsigned() methods for Long
|
||||
* @requires os.arch=="amd64" | os.arch=="x86_64"
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.intrinsics.TestLongUnsignedDivMod
|
||||
*/
|
||||
|
||||
package compiler.intrinsics;
|
||||
import compiler.lib.ir_framework.*;
|
||||
import java.math.*;
|
||||
|
||||
public class TestLongUnsignedDivMod {
|
||||
private int BUFFER_SIZE;
|
||||
private long[] dividends;
|
||||
private long[] divisors;
|
||||
private long[] quotients;
|
||||
private long[] remainders;
|
||||
long TWO_31 = 1L << Integer.SIZE - 1;
|
||||
long TWO_32 = 1L << Integer.SIZE;
|
||||
long TWO_33 = 1L << Integer.SIZE + 1;
|
||||
BigInteger NINETEEN = BigInteger.valueOf(19L);
|
||||
BigInteger TWO_63 = BigInteger.ONE.shiftLeft(Long.SIZE - 1);
|
||||
BigInteger TWO_64 = BigInteger.ONE.shiftLeft(Long.SIZE);
|
||||
BigInteger[] inRange = {
|
||||
BigInteger.ZERO,
|
||||
BigInteger.ONE,
|
||||
BigInteger.TEN,
|
||||
NINETEEN,
|
||||
|
||||
BigInteger.valueOf(TWO_31 - 19L),
|
||||
BigInteger.valueOf(TWO_31 - 10L),
|
||||
BigInteger.valueOf(TWO_31 - 1L),
|
||||
BigInteger.valueOf(TWO_31),
|
||||
BigInteger.valueOf(TWO_31 + 1L),
|
||||
BigInteger.valueOf(TWO_31 + 10L),
|
||||
BigInteger.valueOf(TWO_31 + 19L),
|
||||
|
||||
BigInteger.valueOf(TWO_32 - 19L),
|
||||
BigInteger.valueOf(TWO_32 - 10L),
|
||||
BigInteger.valueOf(TWO_32 - 1L),
|
||||
BigInteger.valueOf(TWO_32),
|
||||
BigInteger.valueOf(TWO_32 + 1L),
|
||||
BigInteger.valueOf(TWO_32 + 10L),
|
||||
BigInteger.valueOf(TWO_32 - 19L),
|
||||
|
||||
BigInteger.valueOf(TWO_33 - 19L),
|
||||
BigInteger.valueOf(TWO_33 - 10L),
|
||||
BigInteger.valueOf(TWO_33 - 1L),
|
||||
BigInteger.valueOf(TWO_33),
|
||||
BigInteger.valueOf(TWO_33 + 1L),
|
||||
BigInteger.valueOf(TWO_33 + 10L),
|
||||
BigInteger.valueOf(TWO_33 + 19L),
|
||||
|
||||
TWO_63.subtract(NINETEEN),
|
||||
TWO_63.subtract(BigInteger.TEN),
|
||||
TWO_63.subtract(BigInteger.ONE),
|
||||
TWO_63,
|
||||
TWO_63.add(BigInteger.ONE),
|
||||
TWO_63.add(BigInteger.TEN),
|
||||
TWO_63.add(NINETEEN),
|
||||
|
||||
TWO_64.subtract(NINETEEN),
|
||||
TWO_64.subtract(BigInteger.TEN),
|
||||
TWO_64.subtract(BigInteger.ONE),
|
||||
};
|
||||
|
||||
public static void main(String args[]) {
|
||||
TestFramework.run(TestLongUnsignedDivMod.class);
|
||||
}
|
||||
|
||||
public TestLongUnsignedDivMod() {
|
||||
BUFFER_SIZE = inRange.length * inRange.length;
|
||||
dividends = new long[BUFFER_SIZE];
|
||||
divisors = new long[BUFFER_SIZE];
|
||||
quotients = new long[BUFFER_SIZE];
|
||||
remainders = new long[BUFFER_SIZE];
|
||||
|
||||
int idx = 0;
|
||||
for (int i = 0; i < inRange.length; i++) {
|
||||
for (int j = 0; j < inRange.length; j++){
|
||||
dividends[idx] = inRange[i].longValue();
|
||||
divisors[idx] = inRange[j].longValue();
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UDivL", ">= 1"}) // Atleast one UDivL node is generated if intrinsic is used
|
||||
public void testDivideUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
quotients[i] = Long.divideUnsigned(dividends[i], divisors[i]);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("divideUnsigned");
|
||||
}
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UModL", ">= 1"}) // Atleast one UModL node is generated if intrinsic is used
|
||||
public void testRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
remainders[i] = Long.remainderUnsigned(dividends[i], divisors[i]);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("remainderUnsigned");
|
||||
}
|
||||
|
||||
|
||||
@Test // needs to be run in (fast) debug mode
|
||||
@Warmup(10000)
|
||||
@IR(counts = {"UDivModL", ">= 1"}) // Atleast one UDivModL node is generated if intrinsic is used
|
||||
public void testDivModUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
try {
|
||||
divmod(dividends[i], divisors[i], i);
|
||||
} catch(ArithmeticException ea) {
|
||||
; // expected
|
||||
}
|
||||
}
|
||||
checkResult("divmodUnsigned");
|
||||
}
|
||||
|
||||
private void divmod(long dividend, long divisor, int i) {
|
||||
quotients[i] = Long.divideUnsigned(dividend, divisor);
|
||||
remainders[i] = Long.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public void checkResult(String mode) {
|
||||
for (int i=0; i < BUFFER_SIZE; i++) {
|
||||
if (divisors[i] == 0) continue;
|
||||
BigInteger dividend = toUnsignedBigInteger(dividends[i]);
|
||||
BigInteger divisor = toUnsignedBigInteger(divisors[i]);
|
||||
|
||||
long quo = dividend.divide(divisor).longValue();
|
||||
long rem = dividend.remainder(divisor).longValue();
|
||||
boolean mismatch;
|
||||
switch (mode) {
|
||||
case "divideUnsigned": mismatch = (quotients[i] != quo); break;
|
||||
case "remainderUnsigned": mismatch = (remainders[i] != rem); break;
|
||||
case "divmodUnsigned": mismatch = (quotients[i] != quo || remainders[i] != rem); break;
|
||||
default: throw new IllegalArgumentException("incorrect mode");
|
||||
}
|
||||
if (mismatch) {
|
||||
throw new RuntimeException(errorMessage(mode, i, quo, rem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String errorMessage(String mode, int i, long quo, long rem) {
|
||||
StringBuilder sb = new StringBuilder(mode);
|
||||
sb = sb.append(" test error at index=").append(i);
|
||||
sb = sb.append(": dividend=").append(dividends[i]);
|
||||
sb = sb.append("; divisor= ").append(divisors[i]);
|
||||
if (!mode.equals("remainderUnsigned")) {
|
||||
sb = sb.append("; quotient (expected)= ").append(quo);
|
||||
sb = sb.append("; quotient (actual)= ").append(quotients[i]);
|
||||
}
|
||||
if (!mode.equals("divideUnsigned")) {
|
||||
sb = sb.append("; remainder (expected)= ").append(rem);
|
||||
sb = sb.append("; remainder (actual)= ").append(remainders[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private BigInteger toUnsignedBigInteger(long i) {
|
||||
if (i >= 0L)
|
||||
return BigInteger.valueOf(i);
|
||||
else {
|
||||
int upper = (int) (i >>> 32);
|
||||
int lower = (int) i;
|
||||
|
||||
// return (upper << 32) + lower
|
||||
return (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft(32).
|
||||
add(BigInteger.valueOf(Integer.toUnsignedLong(lower)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
98
test/micro/org/openjdk/bench/java/lang/IntegerDivMod.java
Normal file
98
test/micro/org/openjdk/bench/java/lang/IntegerDivMod.java
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 org.openjdk.bench.java.lang;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import java.util.random.RandomGenerator;
|
||||
import java.util.random.RandomGeneratorFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Tests unsigned division and modulus methods in java.lang.Integer
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
public class IntegerDivMod {
|
||||
|
||||
RandomGenerator randomGenerator;
|
||||
|
||||
@Param({"mixed", "positive", "negative"})
|
||||
String divisorType;
|
||||
@Param({"1024"})
|
||||
int BUFFER_SIZE;
|
||||
int[] dividends, divisors, quotients, remainders;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
dividends = new int[BUFFER_SIZE];
|
||||
divisors = new int[BUFFER_SIZE];
|
||||
quotients = new int[BUFFER_SIZE];
|
||||
remainders = new int[BUFFER_SIZE];
|
||||
RandomGenerator rng = RandomGeneratorFactory.getDefault().create(0);
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
dividends[i] = rng.nextInt();
|
||||
int divisor = rng.nextInt();
|
||||
if (divisorType.equals("positive")) divisor = Math.abs(divisor);
|
||||
else if (divisorType.equals("negative")) divisor = -Math.abs(divisor);
|
||||
divisors[i] = divisor;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testDivideUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
quotients[i] = Integer.divideUnsigned(dividends[i], divisors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
remainders[i] = Integer.remainderUnsigned(dividends[i], divisors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testDivideRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
divmod(dividends[i], divisors[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
public void divmod(int dividend, int divisor, int i) {
|
||||
quotients[i] = Integer.divideUnsigned(dividend, divisor);
|
||||
remainders[i] = Integer.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
98
test/micro/org/openjdk/bench/java/lang/LongDivMod.java
Normal file
98
test/micro/org/openjdk/bench/java/lang/LongDivMod.java
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 org.openjdk.bench.java.lang;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import java.util.random.RandomGenerator;
|
||||
import java.util.random.RandomGeneratorFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Tests unsigned division and modulus methods in java.lang.Long
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
public class LongDivMod {
|
||||
|
||||
RandomGenerator randomGenerator;
|
||||
|
||||
@Param({"mixed", "positive", "negative"})
|
||||
String divisorType;
|
||||
@Param({"1024"})
|
||||
int BUFFER_SIZE;
|
||||
long[] dividends, divisors, quotients, remainders;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
dividends = new long[BUFFER_SIZE];
|
||||
divisors = new long[BUFFER_SIZE];
|
||||
quotients = new long[BUFFER_SIZE];
|
||||
remainders = new long[BUFFER_SIZE];
|
||||
RandomGenerator rng = RandomGeneratorFactory.getDefault().create(0);
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
dividends[i] = rng.nextLong();
|
||||
long divisor = rng.nextLong();
|
||||
if (divisorType.equals("positive")) divisor = Math.abs(divisor);
|
||||
else if (divisorType.equals("negative")) divisor = -Math.abs(divisor);
|
||||
divisors[i] = divisor;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testDivideUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
quotients[i] = Long.divideUnsigned(dividends[i], divisors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
remainders[i] = Long.remainderUnsigned(dividends[i], divisors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testDivideRemainderUnsigned() {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
divmod(dividends[i], divisors[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
public void divmod(long dividend, long divisor, int i) {
|
||||
quotients[i] = Long.divideUnsigned(dividend, divisor);
|
||||
remainders[i] = Long.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user