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:
vamsi-parasa 2022-04-10 03:47:18 +00:00 committed by Jatin Bhateja
parent 0b867b5e73
commit 37e28aea27
20 changed files with 1155 additions and 2 deletions

View File

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

View File

@ -1368,6 +1368,7 @@ private:
#ifdef _LP64
void idivq(Register src);
void divq(Register src); // Unsigned division
#endif
void imull(Register src);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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