8284742: x86: Handle integral division overflow during parsing

Reviewed-by: kvn, mdoerr
This commit is contained in:
Quan Anh Mai 2022-04-20 16:17:01 +00:00 committed by Vladimir Kozlov
parent 5291ec8d56
commit b4a85cdae1
24 changed files with 648 additions and 378 deletions

@ -2796,6 +2796,10 @@ bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack,
return false;
}
bool Parse::do_one_bytecode_targeted() {
return false;
}
#define MOV_VOLATILE(REG, BASE, INDEX, SCALE, DISP, SCRATCH, INSN) \
C2_MacroAssembler _masm(&cbuf); \
{ \

@ -1159,6 +1159,10 @@ bool maybe_far_call(const MachCallNode *n) {
return !MacroAssembler::_reachable_from_cache(n->as_MachCall()->entry_point());
}
bool Parse::do_one_bytecode_targeted() {
return false;
}
%}
//----------ENCODING BLOCK-----------------------------------------------------

@ -2407,6 +2407,10 @@ const RegMask Matcher::method_handle_invoke_SP_save_mask() {
return RegMask();
}
bool Parse::do_one_bytecode_targeted() {
return false;
}
%}
//----------ENCODING BLOCK-----------------------------------------------------

@ -2044,6 +2044,10 @@ bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack,
return clone_base_plus_offset_address(m, mstack, address_visited);
}
bool Parse::do_one_bytecode_targeted() {
return false;
}
%}

@ -1698,6 +1698,10 @@ bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack,
return clone_base_plus_offset_address(m, mstack, address_visited);
}
bool Parse::do_one_bytecode_targeted() {
return false;
}
%} // source
//----------ENCODING BLOCK-----------------------------------------------------

@ -2235,6 +2235,13 @@ void Assembler::idivl(Register src) {
emit_int16((unsigned char)0xF7, (0xF8 | encode));
}
void Assembler::idivl(Address src) {
InstructionMark im(this);
prefix(src);
emit_int8((unsigned char)0xF7);
emit_operand(as_Register(7), src);
}
void Assembler::divl(Register src) { // Unsigned
int encode = prefix_and_encode(src->encoding());
emit_int16((unsigned char)0xF7, (0xF0 | encode));
@ -12366,6 +12373,13 @@ void Assembler::idivq(Register src) {
emit_int16((unsigned char)0xF7, (0xF8 | encode));
}
void Assembler::idivq(Address src) {
InstructionMark im(this);
prefixq(src);
emit_int8((unsigned char)0xF7);
emit_operand(as_Register(7), src);
}
void Assembler::divq(Register src) {
int encode = prefixq_and_encode(src->encoding());
emit_int16((unsigned char)0xF7, (0xF0 | encode));

@ -1196,6 +1196,9 @@ private:
void vpabsd(XMMRegister dst, XMMRegister src, int vector_len);
void evpabsq(XMMRegister dst, XMMRegister src, int vector_len);
void divl(Register src);
void divq(Register src);
// Divide Scalar Double-Precision Floating-Point Values
void divsd(XMMRegister dst, Address src);
void divsd(XMMRegister dst, XMMRegister src);
@ -1364,12 +1367,9 @@ private:
void hlt();
void idivl(Register src);
void divl(Register src); // Unsigned division
#ifdef _LP64
void idivl(Address src);
void idivq(Register src);
void divq(Register src); // Unsigned division
#endif
void idivq(Address src);
void imull(Register src);
void imull(Register dst, Register src);

@ -0,0 +1,47 @@
/*
* 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.
*
*/
#include "precompiled.hpp"
#ifdef COMPILER2
#include "opto/parse.hpp"
#include "interpreter/bytecodes.hpp"
bool Parse::do_one_bytecode_targeted() {
switch (bc()) {
case Bytecodes::_idiv: // fallthrough
case Bytecodes::_irem: // fallthrough
#ifdef _LP64
case Bytecodes::_ldiv: // fallthrough
case Bytecodes::_lrem:
#endif
do_divmod_fixup();
return true;
default:
return false;
}
}
#endif // COMPILER2

@ -1556,47 +1556,6 @@ encode %{
emit_d32 ( cbuf, 0x0 ); // imm32==0x0
%}
enc_class cdq_enc %{
// Full implementation of Java idiv and irem; checks for
// special case as described in JVM spec., p.243 & p.271.
//
// normal case special case
//
// input : rax,: dividend min_int
// reg: divisor -1
//
// output: rax,: quotient (= rax, idiv reg) min_int
// rdx: remainder (= rax, irem reg) 0
//
// Code sequnce:
//
// 81 F8 00 00 00 80 cmp rax,80000000h
// 0F 85 0B 00 00 00 jne normal_case
// 33 D2 xor rdx,edx
// 83 F9 FF cmp rcx,0FFh
// 0F 84 03 00 00 00 je done
// normal_case:
// 99 cdq
// F7 F9 idiv rax,ecx
// done:
//
emit_opcode(cbuf,0x81); emit_d8(cbuf,0xF8);
emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00);
emit_opcode(cbuf,0x00); emit_d8(cbuf,0x80); // cmp rax,80000000h
emit_opcode(cbuf,0x0F); emit_d8(cbuf,0x85);
emit_opcode(cbuf,0x0B); emit_d8(cbuf,0x00);
emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00); // jne normal_case
emit_opcode(cbuf,0x33); emit_d8(cbuf,0xD2); // xor rdx,edx
emit_opcode(cbuf,0x83); emit_d8(cbuf,0xF9); emit_d8(cbuf,0xFF); // cmp rcx,0FFh
emit_opcode(cbuf,0x0F); emit_d8(cbuf,0x84);
emit_opcode(cbuf,0x03); emit_d8(cbuf,0x00);
emit_opcode(cbuf,0x00); emit_d8(cbuf,0x00); // je done
// normal_case:
emit_opcode(cbuf,0x99); // cdq
// idiv (note: must be emitted by the user of this rule)
// normal:
%}
// Dense encoding for older common ops
enc_class Opc_plus(immI opcode, rRegI reg) %{
emit_opcode(cbuf, $opcode$$constant + $reg$$reg);
@ -7797,21 +7756,17 @@ instruct mulL_eReg_con(eADXRegL dst, immL_127 src, rRegI tmp, eFlagsReg cr) %{
// Integer DIV with Register
instruct divI_eReg(eAXRegI rax, eDXRegI rdx, eCXRegI div, eFlagsReg cr) %{
match(Set rax (DivI rax div));
match(Set rax (NoOvfDivI rax div));
effect(KILL rdx, KILL cr);
size(26);
ins_cost(30*100+10*100);
format %{ "CMP EAX,0x80000000\n\t"
"JNE,s normal\n\t"
"XOR EDX,EDX\n\t"
"CMP ECX,-1\n\t"
"JE,s done\n"
"normal: CDQ\n\t"
"IDIV $div\n\t"
"done:" %}
opcode(0xF7, 0x7); /* Opcode F7 /7 */
ins_encode( cdq_enc, OpcP, RegOpc(div) );
ins_pipe( ialu_reg_reg_alu0 );
size(3);
ins_cost(500);
format %{ "CDQ\n\t"
"IDIV $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe( pipe_slow );
%}
// Divide Register Long
@ -7831,35 +7786,33 @@ instruct divL_eReg(eADXRegL dst, eRegL src1, eRegL src2) %{
// Integer DIVMOD with Register, both quotient and mod results
instruct divModI_eReg_divmod(eAXRegI rax, eDXRegI rdx, eCXRegI div, eFlagsReg cr) %{
match(DivModI rax div);
match(NoOvfDivModI rax div);
effect(KILL cr);
size(26);
ins_cost(30*100+10*100);
format %{ "CMP EAX,0x80000000\n\t"
"JNE,s normal\n\t"
"XOR EDX,EDX\n\t"
"CMP ECX,-1\n\t"
"JE,s done\n"
"normal: CDQ\n\t"
"IDIV $div\n\t"
"done:" %}
opcode(0xF7, 0x7); /* Opcode F7 /7 */
ins_encode( cdq_enc, OpcP, RegOpc(div) );
size(3);
ins_cost(500);
format %{ "CDQ\n\t"
"IDIV $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe( pipe_slow );
%}
// Integer MOD with Register
instruct modI_eReg(eDXRegI rdx, eAXRegI rax, eCXRegI div, eFlagsReg cr) %{
match(Set rdx (ModI rax div));
match(Set rdx (NoOvfModI rax div));
effect(KILL rax, KILL cr);
size(26);
ins_cost(300);
size(3);
ins_cost(500);
format %{ "CDQ\n\t"
"IDIV $div" %}
opcode(0xF7, 0x7); /* Opcode F7 /7 */
ins_encode( cdq_enc, OpcP, RegOpc(div) );
ins_pipe( ialu_reg_reg_alu0 );
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe( pipe_slow );
%}
// Remainder Register Long

@ -338,11 +338,19 @@ extern RegMask _FLOAT_REG_mask;
extern RegMask _STACK_OR_PTR_REG_mask;
extern RegMask _STACK_OR_LONG_REG_mask;
extern RegMask _STACK_OR_LONG_NO_RAX_RDX_REG_mask;
extern RegMask _STACK_OR_INT_REG_mask;
extern RegMask _STACK_OR_INT_NO_RAX_RDX_REG_mask;
inline const RegMask& STACK_OR_PTR_REG_mask() { return _STACK_OR_PTR_REG_mask; }
inline const RegMask& STACK_OR_LONG_REG_mask() { return _STACK_OR_LONG_REG_mask; }
inline const RegMask& STACK_OR_LONG_NO_RAX_RDX_REG_mask() {
return _STACK_OR_LONG_NO_RAX_RDX_REG_mask;
}
inline const RegMask& STACK_OR_INT_REG_mask() { return _STACK_OR_INT_REG_mask; }
inline const RegMask& STACK_OR_INT_NO_RAX_RDX_REG_mask() {
return _STACK_OR_INT_NO_RAX_RDX_REG_mask;
}
%}
@ -368,7 +376,9 @@ RegMask _INT_NO_RBP_R13_REG_mask;
RegMask _FLOAT_REG_mask;
RegMask _STACK_OR_PTR_REG_mask;
RegMask _STACK_OR_LONG_REG_mask;
RegMask _STACK_OR_LONG_NO_RAX_RDX_REG_mask;
RegMask _STACK_OR_INT_REG_mask;
RegMask _STACK_OR_INT_NO_RAX_RDX_REG_mask;
static bool need_r12_heapbase() {
return UseCompressedOops;
@ -429,6 +439,9 @@ void reg_mask_init() {
_LONG_NO_RBP_R13_REG_mask.Remove(OptoReg::as_OptoReg(r13->as_VMReg()));
_LONG_NO_RBP_R13_REG_mask.Remove(OptoReg::as_OptoReg(r13->as_VMReg()->next()));
_STACK_OR_LONG_NO_RAX_RDX_REG_mask = _LONG_NO_RAX_RDX_REG_mask;
_STACK_OR_LONG_NO_RAX_RDX_REG_mask.OR(STACK_OR_STACK_SLOTS_mask());
_INT_REG_mask = _ALL_INT_REG_mask;
if (PreserveFramePointer) {
_INT_REG_mask.Remove(OptoReg::as_OptoReg(rbp->as_VMReg()));
@ -451,6 +464,9 @@ void reg_mask_init() {
_INT_NO_RBP_R13_REG_mask.Remove(OptoReg::as_OptoReg(rbp->as_VMReg()));
_INT_NO_RBP_R13_REG_mask.Remove(OptoReg::as_OptoReg(r13->as_VMReg()));
_STACK_OR_INT_NO_RAX_RDX_REG_mask = _INT_NO_RAX_RDX_REG_mask;
_STACK_OR_INT_NO_RAX_RDX_REG_mask.OR(STACK_OR_STACK_SLOTS_mask());
// _FLOAT_REG_LEGACY_mask/_FLOAT_REG_EVEX_mask is generated by adlc
// from the float_reg_legacy/float_reg_evex register class.
_FLOAT_REG_mask = VM_Version::supports_evex() ? _FLOAT_REG_EVEX_mask : _FLOAT_REG_LEGACY_mask;
@ -1907,123 +1923,6 @@ encode %{
emit_rm(cbuf, 0x3, $dst$$reg & 7, $src$$reg & 7);
%}
enc_class cdql_enc(no_rax_rdx_RegI div)
%{
// Full implementation of Java idiv and irem; checks for
// special case as described in JVM spec., p.243 & p.271.
//
// normal case special case
//
// input : rax: dividend min_int
// reg: divisor -1
//
// output: rax: quotient (= rax idiv reg) min_int
// rdx: remainder (= rax irem reg) 0
//
// Code sequnce:
//
// 0: 3d 00 00 00 80 cmp $0x80000000,%eax
// 5: 75 07/08 jne e <normal>
// 7: 33 d2 xor %edx,%edx
// [div >= 8 -> offset + 1]
// [REX_B]
// 9: 83 f9 ff cmp $0xffffffffffffffff,$div
// c: 74 03/04 je 11 <done>
// 000000000000000e <normal>:
// e: 99 cltd
// [div >= 8 -> offset + 1]
// [REX_B]
// f: f7 f9 idiv $div
// 0000000000000011 <done>:
MacroAssembler _masm(&cbuf);
Label normal;
Label done;
// cmp $0x80000000,%eax
__ cmpl(as_Register(RAX_enc), 0x80000000);
// jne e <normal>
__ jccb(Assembler::notEqual, normal);
// xor %edx,%edx
__ xorl(as_Register(RDX_enc), as_Register(RDX_enc));
// cmp $0xffffffffffffffff,%ecx
__ cmpl($div$$Register, -1);
// je 11 <done>
__ jccb(Assembler::equal, done);
// <normal>
// cltd
__ bind(normal);
__ cdql();
// idivl
// <done>
__ idivl($div$$Register);
__ bind(done);
%}
enc_class cdqq_enc(no_rax_rdx_RegL div)
%{
// Full implementation of Java ldiv and lrem; checks for
// special case as described in JVM spec., p.243 & p.271.
//
// normal case special case
//
// input : rax: dividend min_long
// reg: divisor -1
//
// output: rax: quotient (= rax idiv reg) min_long
// rdx: remainder (= rax irem reg) 0
//
// Code sequnce:
//
// 0: 48 ba 00 00 00 00 00 mov $0x8000000000000000,%rdx
// 7: 00 00 80
// a: 48 39 d0 cmp %rdx,%rax
// d: 75 08 jne 17 <normal>
// f: 33 d2 xor %edx,%edx
// 11: 48 83 f9 ff cmp $0xffffffffffffffff,$div
// 15: 74 05 je 1c <done>
// 0000000000000017 <normal>:
// 17: 48 99 cqto
// 19: 48 f7 f9 idiv $div
// 000000000000001c <done>:
MacroAssembler _masm(&cbuf);
Label normal;
Label done;
// mov $0x8000000000000000,%rdx
__ mov64(as_Register(RDX_enc), 0x8000000000000000);
// cmp %rdx,%rax
__ cmpq(as_Register(RAX_enc), as_Register(RDX_enc));
// jne 17 <normal>
__ jccb(Assembler::notEqual, normal);
// xor %edx,%edx
__ xorl(as_Register(RDX_enc), as_Register(RDX_enc));
// cmp $0xffffffffffffffff,$div
__ cmpq($div$$Register, -1);
// je 1e <done>
__ jccb(Assembler::equal, done);
// <normal>
// cqto
__ bind(normal);
__ cdqq();
// idivq (note: must be emitted by the user of this rule)
// <done>
__ idivq($div$$Register);
__ bind(done);
%}
// Opcde enc_class for 8/32 bit immediate instructions with sign-extension
enc_class OpcSE(immI imm)
%{
@ -8652,40 +8551,65 @@ instruct umulHiL_rReg(rdx_RegL dst, no_rax_RegL src, rax_RegL rax, rFlagsReg cr)
instruct divI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div,
rFlagsReg cr)
%{
match(Set rax (DivI rax div));
match(Set rax (NoOvfDivI rax div));
effect(KILL rdx, KILL cr);
ins_cost(30*100+10*100); // XXX
format %{ "cmpl rax, 0x80000000\t# idiv\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpl $div, -1\n\t"
"je,s done\n"
"normal: cdql\n\t"
"idivl $div\n"
"done:" %}
ins_encode(cdql_enc(div));
ins_pipe(ialu_reg_reg_alu0);
ins_cost(500);
format %{ "cdql\n\t"
"idivl $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe(pipe_slow);
%}
instruct divI_mem(rax_RegI rax, rdx_RegI rdx, memory div,
rFlagsReg cr)
%{
match(Set rax (NoOvfDivI rax (LoadI div)));
effect(KILL rdx, KILL cr);
ins_cost(575);
format %{ "cdql\n\t"
"idivl $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Address);
%}
ins_pipe(pipe_slow);
%}
instruct divL_rReg(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div,
rFlagsReg cr)
%{
match(Set rax (DivL rax div));
match(Set rax (NoOvfDivL rax div));
effect(KILL rdx, KILL cr);
ins_cost(30*100+10*100); // XXX
format %{ "movq rdx, 0x8000000000000000\t# ldiv\n\t"
"cmpq rax, rdx\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpq $div, -1\n\t"
"je,s done\n"
"normal: cdqq\n\t"
"idivq $div\n"
"done:" %}
ins_encode(cdqq_enc(div));
ins_pipe(ialu_reg_reg_alu0);
ins_cost(500);
format %{ "cdqq\n\t"
"idivq $div" %}
ins_encode %{
__ cdqq();
__ idivq($div$$Register);
%}
ins_pipe(pipe_slow);
%}
instruct divL_mem(rax_RegL rax, rdx_RegL rdx, memory div,
rFlagsReg cr)
%{
match(Set rax (NoOvfDivL rax (LoadL div)));
effect(KILL rdx, KILL cr);
ins_cost(575);
format %{ "cdqq\n\t"
"idivq $div" %}
ins_encode %{
__ cdqq();
__ idivq($div$$Address);
%}
ins_pipe(pipe_slow);
%}
instruct udivI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, rFlagsReg cr)
@ -8718,19 +8642,16 @@ instruct udivL_rReg(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div, rFlagsReg c
instruct divModI_rReg_divmod(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div,
rFlagsReg cr)
%{
match(DivModI rax div);
match(NoOvfDivModI rax div);
effect(KILL cr);
ins_cost(30*100+10*100); // XXX
format %{ "cmpl rax, 0x80000000\t# idiv\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpl $div, -1\n\t"
"je,s done\n"
"normal: cdql\n\t"
"idivl $div\n"
"done:" %}
ins_encode(cdql_enc(div));
ins_cost(500);
format %{ "cdql\n\t"
"idivl $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe(pipe_slow);
%}
@ -8738,20 +8659,16 @@ instruct divModI_rReg_divmod(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div,
instruct divModL_rReg_divmod(rax_RegL rax, rdx_RegL rdx, no_rax_rdx_RegL div,
rFlagsReg cr)
%{
match(DivModL rax div);
match(NoOvfDivModL rax div);
effect(KILL cr);
ins_cost(30*100+10*100); // XXX
format %{ "movq rdx, 0x8000000000000000\t# ldiv\n\t"
"cmpq rax, rdx\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpq $div, -1\n\t"
"je,s done\n"
"normal: cdqq\n\t"
"idivq $div\n"
"done:" %}
ins_encode(cdqq_enc(div));
ins_cost(500);
format %{ "cdqq\n\t"
"idivq $div" %}
ins_encode %{
__ cdqq();
__ idivq($div$$Register);
%}
ins_pipe(pipe_slow);
%}
@ -8789,108 +8706,68 @@ instruct udivModL_rReg_divmod(rax_RegL rax, no_rax_rdx_RegL tmp, rdx_RegL rdx,
ins_pipe(pipe_slow);
%}
//----------- DivL-By-Constant-Expansions--------------------------------------
// DivI cases are handled by the compiler
// Magic constant, reciprocal of 10
instruct loadConL_0x6666666666666667(rRegL dst)
%{
effect(DEF dst);
format %{ "movq $dst, #0x666666666666667\t# Used in div-by-10" %}
ins_encode(load_immL(dst, 0x6666666666666667));
ins_pipe(ialu_reg);
%}
instruct mul_hi(rdx_RegL dst, no_rax_RegL src, rax_RegL rax, rFlagsReg cr)
%{
effect(DEF dst, USE src, USE_KILL rax, KILL cr);
format %{ "imulq rdx:rax, rax, $src\t# Used in div-by-10" %}
ins_encode %{
__ imulq($src$$Register);
%}
ins_pipe(ialu_reg_reg_alu0);
%}
instruct sarL_rReg_63(rRegL dst, rFlagsReg cr)
%{
effect(USE_DEF dst, KILL cr);
format %{ "sarq $dst, #63\t# Used in div-by-10" %}
ins_encode %{
__ sarq($dst$$Register, 63);
%}
ins_pipe(ialu_reg);
%}
instruct sarL_rReg_2(rRegL dst, rFlagsReg cr)
%{
effect(USE_DEF dst, KILL cr);
format %{ "sarq $dst, #2\t# Used in div-by-10" %}
ins_encode %{
__ sarq($dst$$Register, 2);
%}
ins_pipe(ialu_reg);
%}
instruct divL_10(rdx_RegL dst, no_rax_RegL src, immL10 div)
%{
match(Set dst (DivL src div));
ins_cost((5+8)*100);
expand %{
rax_RegL rax; // Killed temp
rFlagsReg cr; // Killed
loadConL_0x6666666666666667(rax); // movq rax, 0x6666666666666667
mul_hi(dst, src, rax, cr); // mulq rdx:rax <= rax * $src
sarL_rReg_63(src, cr); // sarq src, 63
sarL_rReg_2(dst, cr); // sarq rdx, 2
subL_rReg(dst, src, cr); // subl rdx, src
%}
%}
//-----------------------------------------------------------------------------
instruct modI_rReg(rdx_RegI rdx, rax_RegI rax, no_rax_rdx_RegI div,
rFlagsReg cr)
%{
match(Set rdx (ModI rax div));
match(Set rdx (NoOvfModI rax div));
effect(KILL rax, KILL cr);
ins_cost(300); // XXX
format %{ "cmpl rax, 0x80000000\t# irem\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpl $div, -1\n\t"
"je,s done\n"
"normal: cdql\n\t"
"idivl $div\n"
"done:" %}
ins_encode(cdql_enc(div));
ins_pipe(ialu_reg_reg_alu0);
ins_cost(500);
format %{ "cdql\n\t"
"idivl $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Register);
%}
ins_pipe(pipe_slow);
%}
instruct modI_mem(rdx_RegI rdx, rax_RegI rax, memory div,
rFlagsReg cr)
%{
match(Set rdx (NoOvfModI rax (LoadI div)));
effect(KILL rax, KILL cr);
ins_cost(575);
format %{ "cdql\n\t"
"idivl $div" %}
ins_encode %{
__ cdql();
__ idivl($div$$Address);
%}
ins_pipe(pipe_slow);
%}
instruct modL_rReg(rdx_RegL rdx, rax_RegL rax, no_rax_rdx_RegL div,
rFlagsReg cr)
%{
match(Set rdx (ModL rax div));
match(Set rdx (NoOvfModL rax div));
effect(KILL rax, KILL cr);
ins_cost(300); // XXX
format %{ "movq rdx, 0x8000000000000000\t# lrem\n\t"
"cmpq rax, rdx\n\t"
"jne,s normal\n\t"
"xorl rdx, rdx\n\t"
"cmpq $div, -1\n\t"
"je,s done\n"
"normal: cdqq\n\t"
"idivq $div\n"
"done:" %}
ins_encode(cdqq_enc(div));
ins_pipe(ialu_reg_reg_alu0);
ins_cost(500);
format %{ "cdqq\n\t"
"idivq $div" %}
ins_encode %{
__ cdqq();
__ idivq($div$$Register);
%}
ins_pipe(pipe_slow);
%}
instruct modL_mem(rdx_RegL rdx, rax_RegL rax, memory div,
rFlagsReg cr)
%{
match(Set rdx (NoOvfModL rax (LoadL div)));
effect(KILL rax, KILL cr);
ins_cost(575);
format %{ "cdqq\n\t"
"idivq $div" %}
ins_encode %{
__ cdqq();
__ idivq($div$$Address);
%}
ins_pipe(pipe_slow);
%}
instruct umodI_rReg(rdx_RegI rdx, rax_RegI rax, no_rax_rdx_RegI div, rFlagsReg cr)

@ -226,6 +226,7 @@ int main(int argc, char *argv[])
AD.addInclude(AD._CPP_file, "opto/intrinsicnode.hpp");
AD.addInclude(AD._CPP_file, "opto/locknode.hpp");
AD.addInclude(AD._CPP_file, "opto/opcodes.hpp");
AD.addInclude(AD._CPP_file, "opto/parse.hpp");
AD.addInclude(AD._CPP_file, "opto/regalloc.hpp");
AD.addInclude(AD._CPP_file, "opto/regmask.hpp");
AD.addInclude(AD._CPP_file, "opto/runtime.hpp");

@ -162,11 +162,15 @@ macro(DivD)
macro(DivF)
macro(DivI)
macro(DivL)
macro(NoOvfDivI)
macro(NoOvfDivL)
macro(UDivI)
macro(UDivL)
macro(DivMod)
macro(DivModI)
macro(DivModL)
macro(NoOvfDivModI)
macro(NoOvfDivModL)
macro(UDivModI)
macro(UDivModL)
macro(EncodeISOArray)
@ -235,6 +239,8 @@ macro(ModD)
macro(ModF)
macro(ModI)
macro(ModL)
macro(NoOvfModI)
macro(NoOvfModL)
macro(UModI)
macro(UModL)
macro(MoveI2F)

@ -3529,6 +3529,46 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
}
break;
case Op_NoOvfModI:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_NoOvfDivI);
if (d) {
// Replace them with a fused divmod if supported
if (Matcher::has_match_rule(Op_NoOvfDivModI)) {
NoOvfDivModINode* divmod = NoOvfDivModINode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulINode(d, d->in(2));
Node* sub = new SubINode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
break;
case Op_NoOvfModL:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_NoOvfDivL);
if (d) {
// Replace them with a fused divmod if supported
if (Matcher::has_match_rule(Op_NoOvfDivModL)) {
NoOvfDivModLNode* divmod = NoOvfDivModLNode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulLNode(d, d->in(2));
Node* sub = new SubLNode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
break;
case Op_LoadVector:
case Op_StoreVector:
case Op_LoadVectorGather:

@ -1466,3 +1466,27 @@ Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) {
}
return new MachProjNode(this, proj->_con, rm, ideal_reg);
}
//------------------------------make------------------------------------------
NoOvfDivModINode* NoOvfDivModINode::make(Node* div_or_mod) {
Node* n = div_or_mod;
assert(n->Opcode() == Op_NoOvfDivI || n->Opcode() == Op_NoOvfModI,
"only div or mod input pattern accepted");
NoOvfDivModINode* divmod = new NoOvfDivModINode(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------------------------------------------
NoOvfDivModLNode* NoOvfDivModLNode::make(Node* div_or_mod) {
Node* n = div_or_mod;
assert(n->Opcode() == Op_NoOvfDivL || n->Opcode() == Op_NoOvfModL,
"only div or mod input pattern accepted");
NoOvfDivModLNode* divmod = new NoOvfDivModLNode(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;
}

@ -103,7 +103,7 @@ public:
virtual uint ideal_reg() const { return Op_RegI; }
};
//------------------------------UDivLNode---------------------------------------
//------------------------------UDivLNode--------------------------------------
// Unsigned long division
class UDivLNode : public Node {
public:
@ -116,6 +116,24 @@ public:
virtual uint ideal_reg() const { return Op_RegL; }
};
//---------------------------NoOvfDivINode-------------------------------------
// Non-overflow integer division, UB when dividend == min_jint and divisor == -1
// so user has to ensure this combination does not appear
class NoOvfDivINode : public DivINode {
public:
NoOvfDivINode( Node *c, Node *dividend, Node *divisor ) : DivINode(c, dividend, divisor ) {}
virtual int Opcode() const;
};
//---------------------------NoOvfDivLNode-------------------------------------
// Non-overflow long division, UB when dividend == min_jlong and divisor == -1
// so user has to ensure this combination does not appear
class NoOvfDivLNode : public DivLNode {
public:
NoOvfDivLNode( Node *c, Node *dividend, Node *divisor ) : DivLNode(c, dividend, divisor ) {}
virtual int Opcode() const;
};
//------------------------------ModINode---------------------------------------
// Integer modulus
class ModINode : public Node {
@ -162,7 +180,7 @@ public:
virtual uint ideal_reg() const { return Op_RegD; }
};
//------------------------------UModINode---------------------------------------
//------------------------------UModINode--------------------------------------
// Unsigned integer modulus
class UModINode : public Node {
public:
@ -173,7 +191,7 @@ public:
virtual uint ideal_reg() const { return Op_RegI; }
};
//------------------------------UModLNode---------------------------------------
//------------------------------UModLNode--------------------------------------
// Unsigned long modulus
class UModLNode : public Node {
public:
@ -184,7 +202,25 @@ public:
virtual uint ideal_reg() const { return Op_RegL; }
};
//------------------------------DivModNode---------------------------------------
//---------------------------NoOvfModINode-------------------------------------
// Non-overflow integer modulus, UB when dividend == min_jint and divisor == -1
// so user has to ensure this combination does not appear
class NoOvfModINode : public ModINode {
public:
NoOvfModINode( Node *c, Node *dividend, Node *divisor ) : ModINode(c, dividend, divisor ) {}
virtual int Opcode() const;
};
//---------------------------NoOvfModLNode-------------------------------------
// Non-overflow long modulus, UB when dividend == min_jlong and divisor == -1
// so user has to ensure this combination does not appear
class NoOvfModLNode : public ModLNode {
public:
NoOvfModLNode( Node *c, Node *dividend, Node *divisor ) : ModLNode(c, dividend, divisor ) {}
virtual int Opcode() const;
};
//-----------------------------DivModNode--------------------------------------
// Division with remainder result.
class DivModNode : public MultiNode {
protected:
@ -206,7 +242,7 @@ public:
ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); }
};
//------------------------------DivModINode---------------------------------------
//-----------------------------DivModINode-------------------------------------
// Integer division with remainder result.
class DivModINode : public DivModNode {
public:
@ -219,7 +255,7 @@ public:
static DivModINode* make(Node* div_or_mod);
};
//------------------------------DivModLNode---------------------------------------
//-----------------------------DivModLNode-------------------------------------
// Long division with remainder result.
class DivModLNode : public DivModNode {
public:
@ -233,7 +269,7 @@ public:
};
//------------------------------UDivModINode---------------------------------------
//----------------------------UDivModINode-------------------------------------
// Unsigend integer division with remainder result.
class UDivModINode : public DivModNode {
public:
@ -246,7 +282,7 @@ public:
static UDivModINode* make(Node* div_or_mod);
};
//------------------------------UDivModLNode---------------------------------------
//----------------------------UDivModLNode-------------------------------------
// Unsigned long division with remainder result.
class UDivModLNode : public DivModNode {
public:
@ -259,4 +295,28 @@ public:
static UDivModLNode* make(Node* div_or_mod);
};
//---------------------------NoOvfDivModINode----------------------------------
// Non-overflow integer division with remainder result, UB when dividend == min_jint
// and divisor == -1 so user has to ensure this combination does not appear
class NoOvfDivModINode : public DivModINode {
public:
NoOvfDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModINode(c, dividend, divisor) {}
virtual int Opcode() const;
// Make a divmod and associated projections from a div or mod.
static NoOvfDivModINode* make(Node* div_or_mod);
};
//---------------------------NoOvfDivModLNode----------------------------------
// Non-overflow long division with remainder result, UB when dividend == min_jlong
// and divisor == -1 so user has to ensure this combination does not appear
class NoOvfDivModLNode : public DivModLNode {
public:
NoOvfDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModLNode(c, dividend, divisor) {}
virtual int Opcode() const;
// Make a divmod and associated projections from a div or mod.
static NoOvfDivModLNode* make(Node* div_or_mod);
};
#endif // SHARE_OPTO_DIVNODE_HPP

@ -462,7 +462,8 @@ class Parse : public GraphKit {
void merge_memory_edges(MergeMemNode* n, int pnum, bool nophi);
// Parse this bytecode, and alter the Parsers JVM->Node mapping
void do_one_bytecode();
void do_one_bytecode_common();
bool do_one_bytecode_targeted();
// helper function to generate array store check
void array_store_check();
@ -534,6 +535,10 @@ class Parse : public GraphKit {
void do_jsr();
void do_ret();
// implementation of div/rem bytecodes for handling of special case
// min_jint / -1
void do_divmod_fixup();
float dynamic_branch_prediction(float &cnt, BoolTest::mask btest, Node* test);
float branch_prediction(float &cnt, BoolTest::mask btest, int target_bci, Node* test);
bool seems_never_taken(float prob) const;

@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
#include "jvm_io.h"
#include "compiler/compileLog.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/resourceArea.hpp"
@ -1552,7 +1553,22 @@ void Parse::do_one_block() {
assert(!have_se || pre_bc_sp >= inputs, "have enough stack to execute this BC: pre_bc_sp=%d, inputs=%d", pre_bc_sp, inputs);
#endif //ASSERT
do_one_bytecode();
// Try parsing machine-dependently, then if it is not needed then parse
// the bytecode in a machine independent manner
if (!do_one_bytecode_targeted()) {
do_one_bytecode_common();
}
#ifndef PRODUCT
if (C->should_print_igv(1)) {
IdealGraphPrinter* printer = C->igv_printer();
char buffer[256];
jio_snprintf(buffer, sizeof(buffer), "Bytecode %d: %s", bci(), Bytecodes::name(bc()));
bool old = printer->traverse_outs();
printer->set_traverse_outs(true);
printer->print_method(buffer, 4);
printer->set_traverse_outs(old);
}
#endif
assert(!have_se || stopped() || failing() || (sp() - pre_bc_sp) == depth,
"incorrect depth prediction: sp=%d, pre_bc_sp=%d, depth=%d", sp(), pre_bc_sp, depth);

@ -1802,7 +1802,7 @@ Node* Parse::optimize_cmp_with_klass(Node* c) {
//------------------------------do_one_bytecode--------------------------------
// Parse this bytecode, and alter the Parsers JVM->Node mapping
void Parse::do_one_bytecode() {
void Parse::do_one_bytecode_common() {
Node *a, *b, *c, *d; // Handy temps
BoolTest::mask btest;
int i;
@ -2750,16 +2750,4 @@ void Parse::do_one_bytecode() {
tty->print("\nUnhandled bytecode %s\n", Bytecodes::name(bc()) );
ShouldNotReachHere();
}
#ifndef PRODUCT
if (C->should_print_igv(1)) {
IdealGraphPrinter* printer = C->igv_printer();
char buffer[256];
jio_snprintf(buffer, sizeof(buffer), "Bytecode %d: %s", bci(), Bytecodes::name(bc()));
bool old = printer->traverse_outs();
printer->set_traverse_outs(true);
printer->print_method(buffer, 4);
printer->set_traverse_outs(old);
}
#endif
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 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
@ -433,3 +433,77 @@ void Parse::do_multianewarray() {
// - Make a fast path for small multi-arrays. (W/ implicit init. loops.)
// - Issue CastII against length[*] values, to TypeInt::POS.
}
// On some architectures, a division cannot be done immediately due to
// the special case with min_jint / -1. As a result, we need to have
// special handling for this case
void Parse::do_divmod_fixup() {
Bytecodes::Code bc = this->bc();
BasicType bt = (bc == Bytecodes::_idiv || bc == Bytecodes::_irem) ? T_INT : T_LONG;
// Operands need to stay in the stack during zero check
if (bt == T_INT) {
zero_check_int(peek(0));
} else {
zero_check_long(peek(1));
}
// Compile-time detection of arithmetic exception
if (stopped()) {
return;
}
Node* in2 = (bt == T_INT) ? pop() : pop_pair();
Node* in1 = (bt == T_INT) ? pop() : pop_pair();
auto generate_division = [](PhaseGVN& gvn, Node* control, Node* in1, Node* in2,
Bytecodes::Code bc) {
switch (bc) {
case Bytecodes::_idiv: return gvn.transform(new NoOvfDivINode(control, in1, in2));
case Bytecodes::_ldiv: return gvn.transform(new NoOvfDivLNode(control, in1, in2));
case Bytecodes::_irem: return gvn.transform(new NoOvfModINode(control, in1, in2));
case Bytecodes::_lrem: return gvn.transform(new NoOvfModLNode(control, in1, in2));
default:
ShouldNotReachHere();
return static_cast<Node*>(nullptr);
}
};
auto push_result = [](Parse& parser, Node* res, BasicType bt) {
if (bt == T_INT) {
parser.push(res);
} else {
parser.push_pair(res);
}
};
// No overflow possibility here
if ((in1 == in2) ||
(bt == T_INT && !TypeInt::MIN->higher_equal(_gvn.type(in1))) ||
(bt == T_LONG && !TypeLong::MIN->higher_equal(_gvn.type(in1)))) {
Node* res = generate_division(_gvn, control(), in1, in2, bc);
push_result(*this, res, bt);
return;
}
// The generated graph is equivalent to (in2 == -1) ? -in1 : (in1 / in2)
// we need to have a separate branch for in2 == -1 due to the special
// case of min_jint / -1
Node* cmp = _gvn.transform(CmpNode::make(in2, _gvn.integercon(-1, bt), bt));
Node* bol = Bool(cmp, BoolTest::eq);
IfNode* iff = create_and_map_if(control(), bol, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN);
Node* iff_true = IfTrue(iff);
Node* iff_false = IfFalse(iff);
Node* res_fast = (bc == Bytecodes::_idiv || bc == Bytecodes::_ldiv)
? _gvn.transform(SubNode::make(_gvn.zerocon(bt), in1, bt))
: _gvn.zerocon(bt);
Node* res_slow = generate_division(_gvn, iff_false, in1, in2, bc);
Node* merge = new RegionNode(3);
merge->init_req(1, iff_true);
merge->init_req(2, iff_false);
record_for_igvn(merge);
set_control(_gvn.transform(merge));
Node* res = new PhiNode(merge, Type::get_const_basic_type(bt));
res->init_req(1, res_fast);
res->init_req(2, res_slow);
res = _gvn.transform(res);
push_result(*this, res, bt);
}

@ -1581,17 +1581,23 @@
declare_c2_type(DivDNode, Node) \
declare_c2_type(UDivINode, Node) \
declare_c2_type(UDivLNode, Node) \
declare_c2_type(NoOvfDivINode, DivINode) \
declare_c2_type(NoOvfDivLNode, DivLNode) \
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(NoOvfModINode, ModINode) \
declare_c2_type(NoOvfModLNode, ModLNode) \
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(NoOvfDivModINode, DivModINode) \
declare_c2_type(NoOvfDivModLNode, DivModLNode) \
declare_c2_type(BoxLockNode, Node) \
declare_c2_type(LoopNode, RegionNode) \
declare_c2_type(CountedLoopNode, LoopNode) \

@ -0,0 +1,99 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.integerArithmetic;
import jdk.test.lib.Asserts;
/*
* @test TestDivision
* @bug 8284742
* @summary Tests to verify compiled code correctly handles integral divisions.
* @library /test/lib
*
* @run main/othervm -Xcomp -XX:-TieredCompilation
* -XX:CompileCommand=compileonly,*.TestDivision::divide
* -XX:CompileCommand=compileonly,*.TestDivision::remainder
* compiler.integerArithmetic.TestDivision
*/
public class TestDivision {
public static void main(String[] args) {
Asserts.assertEquals(divide(19, 7), 19 / 7);
Asserts.assertEquals(remainder(19, 7), 19 % 7);
Asserts.assertEquals(divide(19L, 7L), 19L / 7L);
Asserts.assertEquals(remainder(19L, 7L), 19L % 7L);
Asserts.assertEquals(divide(19, -7), 19 / -7);
Asserts.assertEquals(remainder(19, -7), 19 % -7);
Asserts.assertEquals(divide(19L, -7L), 19L / -7L);
Asserts.assertEquals(remainder(19L, -7L), 19L % -7L);
Asserts.assertEquals(divide(-19, 7), -19 / 7);
Asserts.assertEquals(remainder(-19, 7), -19 % 7);
Asserts.assertEquals(divide(-19L, 7L), -19L / 7L);
Asserts.assertEquals(remainder(-19L, 7L), -19L % 7L);
Asserts.assertEquals(divide(-19, -7), -19 / -7);
Asserts.assertEquals(remainder(-19, -7), -19 % -7);
Asserts.assertEquals(divide(-19L, -7L), -19L / -7L);
Asserts.assertEquals(remainder(-19L, -7L), -19L % -7L);
Asserts.assertEquals(divide(Integer.MIN_VALUE, -1), Integer.MIN_VALUE / -1);
Asserts.assertEquals(remainder(Integer.MIN_VALUE, -1), Integer.MIN_VALUE % -1);
Asserts.assertEquals(divide(Long.MIN_VALUE, -1), Long.MIN_VALUE / -1L);
Asserts.assertEquals(remainder(Long.MIN_VALUE, -1), Long.MIN_VALUE % -1L);
try {
divide(19, 0);
Asserts.fail();
} catch (ArithmeticException e) {}
try {
remainder(19, 0);
Asserts.fail();
} catch (ArithmeticException e) {}
try {
divide(19L, 0L);
Asserts.fail();
} catch (ArithmeticException e) {}
try {
remainder(19L, 0L);
Asserts.fail();
} catch (ArithmeticException e) {}
}
static int divide(int x, int y) {
return x / y;
}
static int remainder(int x, int y) {
return x % y;
}
static long divide(long x, long y) {
return x / y;
}
static long remainder(long x, long y) {
return x % y;
}
}

@ -169,8 +169,8 @@ public class IRNode {
public static final String MUL = START + "Mul(I|L|F|D)" + MID + END;
public static final String MUL_I = START + "MulI" + MID + END;
public static final String MUL_L = START + "MulL" + MID + END;
public static final String DIV = START + "Div(I|L|F|D)" + MID + END;
public static final String DIV_L = START + "DivL" + MID + END;
public static final String DIV = START + "(NoOvf)?Div(I|L|F|D)" + MID + END;
public static final String DIV_L = START + "(NoOvf)?DivL" + MID + END;
public static final String CON_I = START + "ConI" + MID + END;
public static final String CON_L = START + "ConL" + MID + END;
public static final String CONV_I2L = START + "ConvI2L" + MID + END;

@ -24,6 +24,7 @@ package org.openjdk.bench.java.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
@ -34,12 +35,10 @@ 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)
@Fork(1)
public class IntegerDivMod {
RandomGenerator randomGenerator;
@ -60,12 +59,37 @@ public class IntegerDivMod {
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);
if (divisorType.equals("positive")) {
divisor = Math.abs(divisor);
} else if (divisorType.equals("negative")) {
divisor = -Math.abs(divisor);
}
divisors[i] = divisor;
}
}
@Benchmark
public void testDivide() {
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / divisors[i];
}
}
@Benchmark
public void testDivideKnownPositive() {
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / Math.max(1, divisors[i]);
}
}
@Benchmark
public void testDivideHoistedDivisor() {
int x = divisors[0];
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / x;
}
}
@Benchmark
public void testDivideUnsigned() {
for (int i = 0; i < BUFFER_SIZE; i++) {
@ -91,8 +115,4 @@ public class IntegerDivMod {
quotients[i] = Integer.divideUnsigned(dividend, divisor);
remainders[i] = Integer.remainderUnsigned(dividend, divisor);
}
}

@ -24,6 +24,7 @@ package org.openjdk.bench.java.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
@ -34,12 +35,10 @@ 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)
@Fork(1)
public class LongDivMod {
RandomGenerator randomGenerator;
@ -60,12 +59,37 @@ public class LongDivMod {
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);
if (divisorType.equals("positive")) {
divisor = Math.abs(divisor);
} else if (divisorType.equals("negative")) {
divisor = -Math.abs(divisor);
}
divisors[i] = divisor;
}
}
@Benchmark
public void testDivide() {
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / divisors[i];
}
}
@Benchmark
public void testDivideKnownPositive() {
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / Math.max(1, divisors[i]);
}
}
@Benchmark
public void testDivideHoistedDivisor() {
long x = divisors[0];
for (int i = 0; i < BUFFER_SIZE; i++) {
quotients[i] = dividends[i] / x;
}
}
@Benchmark
public void testDivideUnsigned() {
for (int i = 0; i < BUFFER_SIZE; i++) {
@ -91,8 +115,4 @@ public class LongDivMod {
quotients[i] = Long.divideUnsigned(dividend, divisor);
remainders[i] = Long.remainderUnsigned(dividend, divisor);
}
}