From bcc33d5ef3bdbfaee51c45014851c54028da03f1 Mon Sep 17 00:00:00 2001 From: Hamlin Li Date: Tue, 22 Apr 2025 08:32:03 +0000 Subject: [PATCH] 8352504: RISC-V: implement and enable CMoveI/L 8346786: RISC-V: Reconsider ConditionalMoveLimit when adding conditional move Reviewed-by: fyang, fjiang --- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 30 ++++ .../cpu/riscv/c2_MacroAssembler_riscv.hpp | 4 + src/hotspot/cpu/riscv/c2_globals_riscv.hpp | 2 +- .../cpu/riscv/macroAssembler_riscv.cpp | 124 ++++++++++++++ .../cpu/riscv/macroAssembler_riscv.hpp | 5 + src/hotspot/cpu/riscv/riscv.ad | 103 ++++++++++-- src/hotspot/cpu/riscv/vm_version_riscv.cpp | 8 - .../os_cpu/linux_riscv/riscv_hwprobe.cpp | 5 +- .../c2/irTests/ModINodeIdealizationTests.java | 6 +- .../c2/irTests/ModLNodeIdealizationTests.java | 6 +- .../c2/irTests/TestConv2BExpansion.java | 8 + .../compiler/c2/irTests/TestFPComparison.java | 1 - .../compiler/c2/irTests/TestIfMinMax.java | 17 +- .../lib/ir_framework/TestFramework.java | 1 + .../compiler/vectorapi/TestVectorTest.java | 3 +- .../bench/java/lang/ClassComparison.java | 101 +++++++++++ .../openjdk/bench/java/lang/FPComparison.java | 159 +++++++++++++++++- .../bench/java/lang/IntegerComparison.java | 153 +++++++++++++++++ .../bench/java/lang/LongComparison.java | 152 +++++++++++++++++ .../bench/java/lang/PointerComparison.java | 101 +++++++++++ 20 files changed, 940 insertions(+), 49 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/ClassComparison.java create mode 100644 test/micro/org/openjdk/bench/java/lang/IntegerComparison.java create mode 100644 test/micro/org/openjdk/bench/java/lang/LongComparison.java create mode 100644 test/micro/org/openjdk/bench/java/lang/PointerComparison.java diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 39a49f8f1eb..0dc39744741 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2156,6 +2156,36 @@ void C2_MacroAssembler::enc_cmove(int cmpFlag, Register op1, Register op2, Regis } } +void C2_MacroAssembler::enc_cmove_cmp_fp(int cmpFlag, FloatRegister op1, FloatRegister op2, Register dst, Register src, bool is_single) { + int op_select = cmpFlag & (~unsigned_branch_mask); + + switch (op_select) { + case BoolTest::eq: + cmov_cmp_fp_eq(op1, op2, dst, src, is_single); + break; + case BoolTest::ne: + cmov_cmp_fp_ne(op1, op2, dst, src, is_single); + break; + case BoolTest::le: + cmov_cmp_fp_le(op1, op2, dst, src, is_single); + break; + case BoolTest::ge: + assert(false, "Should go to BoolTest::le case"); + ShouldNotReachHere(); + break; + case BoolTest::lt: + cmov_cmp_fp_lt(op1, op2, dst, src, is_single); + break; + case BoolTest::gt: + assert(false, "Should go to BoolTest::lt case"); + ShouldNotReachHere(); + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } +} + // Set dst to NaN if any NaN input. void C2_MacroAssembler::minmax_fp(FloatRegister dst, FloatRegister src1, FloatRegister src2, FLOAT_TYPE ft, bool is_min) { diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index a650174d90f..73fceea3805 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -129,6 +129,10 @@ Register op1, Register op2, Register dst, Register src); + void enc_cmove_cmp_fp(int cmpFlag, + FloatRegister op1, FloatRegister op2, + Register dst, Register src, bool is_single); + void spill(Register r, bool is64, int offset) { is64 ? sd(r, Address(sp, offset)) : sw(r, Address(sp, offset)); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index de3c1b17c8e..79bdc4917c9 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -43,7 +43,7 @@ define_pd_global(bool, TieredCompilation, COMPILER1_PRESENT(true) NOT define_pd_global(intx, CompileThreshold, 10000); define_pd_global(intx, OnStackReplacePercentage, 140); -define_pd_global(intx, ConditionalMoveLimit, 0); +define_pd_global(intx, ConditionalMoveLimit, 3); define_pd_global(intx, FreqInlineSize, 325); define_pd_global(intx, MinJumpTableSize, 10); define_pd_global(intx, InteriorEntryAlignment, 16); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index c6393be0714..218da7cde03 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1267,6 +1267,130 @@ void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Regist bind(no_set); } +// ----------- cmove, compare float ----------- + +// Move src to dst only if cmp1 == cmp2, +// otherwise leave dst unchanged, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 != cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 eq cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + feq_s(t0, cmp1, cmp2); + } else { + feq_d(t0, cmp1, cmp2); + } + czero_nez(dst, dst, t0); + czero_eqz(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 != cmp2, including the case of NaN + // not jump (i.e. move src to dst) if cmp1 == cmp2 + float_bne(cmp1, cmp2, no_set); + } else { + double_bne(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// Keep dst unchanged only if cmp1 == cmp2, +// otherwise move src to dst, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 == cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 ne cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + feq_s(t0, cmp1, cmp2); + } else { + feq_d(t0, cmp1, cmp2); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 == cmp2 + // not jump (i.e. move src to dst) if cmp1 != cmp2, including the case of NaN + float_beq(cmp1, cmp2, no_set); + } else { + double_beq(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// When cmp1 <= cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 < cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +// scenario 2: +// java code : cmp1 > cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + flt_s(t0, cmp2, cmp1); + } else { + flt_d(t0, cmp2, cmp1); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 > cmp2 + // not jump (i.e. move src to dst) if cmp1 <= cmp2 or either is NaN + float_bgt(cmp1, cmp2, no_set); + } else { + double_bgt(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// When cmp1 < cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 <= cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +// scenario 2: +// java code : cmp1 >= cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + fle_s(t0, cmp2, cmp1); + } else { + fle_d(t0, cmp2, cmp1); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 >= cmp2 + // not jump (i.e. move src to dst) if cmp1 < cmp2 or either is NaN + float_bge(cmp1, cmp2, no_set); + } else { + double_bge(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + // Float compare branch instructions #define INSN(NAME, FLOATCMP, BRANCH) \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index b390fb236c2..c47200579c7 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -657,6 +657,11 @@ class MacroAssembler: public Assembler { void cmov_gt(Register cmp1, Register cmp2, Register dst, Register src); void cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + public: // We try to follow risc-v asm menomics. // But as we don't layout a reachable GOT, diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 1eb1464e7d9..2e9d25c8156 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1933,6 +1933,12 @@ bool Matcher::match_rule_supported(int opcode) { case Op_SubHF: case Op_SqrtHF: return UseZfh; + + case Op_CMoveF: + case Op_CMoveD: + case Op_CMoveP: + case Op_CMoveN: + return false; } return true; // Per default match rules are supported. @@ -9938,12 +9944,15 @@ instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, la // ============================================================================ // Conditional Move Instructions + +// --------- CMoveI --------- + instruct cmovI_cmpI(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOp cop) %{ match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" %} ins_encode %{ @@ -9960,7 +9969,7 @@ instruct cmovI_cmpU(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" %} ins_encode %{ @@ -9977,7 +9986,7 @@ instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) % ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" %} ins_encode %{ @@ -9994,7 +10003,7 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" %} ins_encode %{ @@ -10006,12 +10015,46 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +instruct cmovI_cmpF(iRegINoSp dst, iRegI src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveI (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovI_cmpD(iRegINoSp dst, iRegI src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveI (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + instruct cmovI_cmpN(iRegINoSp dst, iRegI src, iRegN op1, iRegN op2, cmpOpU cop) %{ match(Set dst (CMoveI (Binary cop (CmpN op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" %} ins_encode %{ @@ -10028,7 +10071,7 @@ instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" %} ins_encode %{ @@ -10040,12 +10083,14 @@ instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +// --------- CMoveL --------- + instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) %{ match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" %} ins_encode %{ @@ -10062,7 +10107,7 @@ instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" %} ins_encode %{ @@ -10079,7 +10124,7 @@ instruct cmovL_cmpI(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOp cop) % ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" %} ins_encode %{ @@ -10096,7 +10141,7 @@ instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" %} ins_encode %{ @@ -10108,12 +10153,46 @@ instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +instruct cmovL_cmpF(iRegLNoSp dst, iRegL src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovL_cmpD(iRegLNoSp dst, iRegL src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + instruct cmovL_cmpN(iRegLNoSp dst, iRegL src, iRegN op1, iRegN op2, cmpOpU cop) %{ match(Set dst (CMoveL (Binary cop (CmpN op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" %} ins_encode %{ @@ -10130,7 +10209,7 @@ instruct cmovL_cmpP(iRegLNoSp dst, iRegL src, iRegP op1, iRegP op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpP\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpP\n\t" %} ins_encode %{ diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index a0de9d767bf..8dcffc9c646 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -248,14 +248,6 @@ void VM_Version::common_initialize() { #ifdef COMPILER2 void VM_Version::c2_initialize() { - if (UseCMoveUnconditionally) { - FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); - } - - if (ConditionalMoveLimit > 0) { - FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); - } - if (!UseRVV) { FLAG_SET_DEFAULT(MaxVectorSize, 0); } else { diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 06d6aaf109f..d19128cafc2 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -233,10 +233,13 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZACAS)) { VM_Version::ext_Zacas.enable_feature(); } -#endif + // Currently tests shows that cmove using Zicond instructions will bring + // performance regression, but to get a test coverage all the time, will + // still prefer to enabling it in debug version. if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { VM_Version::ext_Zicond.enable_feature(); } +#endif if (is_valid(RISCV_HWPROBE_KEY_CPUPERF_0)) { VM_Version::unaligned_access.enable_feature( query[RISCV_HWPROBE_KEY_CPUPERF_0].value & RISCV_HWPROBE_MISALIGNED_MASK); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java index dffafea7ea8..d19e2562779 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java @@ -121,10 +121,8 @@ public class ModINodeIdealizationTests { } @Test - @IR(applyIfPlatform = {"riscv64", "false"}, - failOn = {IRNode.MOD_I}) - @IR(applyIfPlatform = {"riscv64", "false"}, - counts = {IRNode.AND_I, ">=1", IRNode.RSHIFT, ">=1", IRNode.CMP_I, "2"}) + @IR(failOn = {IRNode.MOD_I}) + @IR(counts = {IRNode.AND_I, ">=1", IRNode.RSHIFT, ">=1", IRNode.CMP_I, "2"}) // Special optimization for the case 2^k-1 for bigger k public int powerOf2Minus1(int x) { return x % 127; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java index e931ff87c09..fc08ef60603 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java @@ -105,10 +105,8 @@ public class ModLNodeIdealizationTests { } @Test - @IR(applyIfPlatform = {"riscv64", "false"}, - failOn = {IRNode.MOD_L}) - @IR(applyIfPlatform = {"riscv64", "false"}, - counts = {IRNode.AND_L, ">=1", IRNode.RSHIFT, ">=1", IRNode.CMP_L, "2"}) + @IR(failOn = {IRNode.MOD_L}) + @IR(counts = {IRNode.AND_L, ">=1", IRNode.RSHIFT, ">=1", IRNode.CMP_L, "2"}) // Special optimization for the case 2^k-1 for bigger k public long powerOf2Minus1(long x) { return x % ((1L << 33) - 1); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java b/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java index de32b7b21f6..35d676b6ddb 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java @@ -42,6 +42,14 @@ public class TestConv2BExpansion { TestFramework.run(); } + // These IR checks do not apply on riscv64, as riscv64 supports Conv2B, e.g. for `return x == 0`, + // the graph looks like: + // Return (XorI (Conv2B ConI(#int: 1))) + // On other platforms, e.g. x86_64 which does not supports Conv2B, the graph looks like: + // Return (CMoveI (Bool (CompI (Param1 ConI(#int: 0))) ConI(#int: 1) ConI(#int: 0))) + // On riscv64, current graph is more efficient than `CMoveI`, as it + // 1. generates less code + // 2. even when zicond is not supported, it does not introduce branches. @Test @IR(counts = {IRNode.CMOVE_I, "1"}, failOn = {IRNode.XOR}) public boolean testIntEquals0(int x) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java b/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java index 8445c856b4e..a06cb45d59e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java @@ -31,7 +31,6 @@ import jdk.test.lib.Asserts; * @summary Test that code generation for fp comparison works as intended * @library /test/lib / * @run driver compiler.c2.irTests.TestFPComparison - * @requires os.arch != "riscv64" */ public class TestFPComparison { static final double[] DOUBLES = new double[] { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java index e232895257a..bb0a1200a7f 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java @@ -33,7 +33,6 @@ import jdk.test.lib.Utils; * @bug 8324655 8329797 8331090 * @key randomness * @summary Test that if expressions are properly folded into min/max nodes - * @requires os.arch != "riscv64" * @library /test/lib / * @run driver compiler.c2.irTests.TestIfMinMax */ @@ -228,7 +227,7 @@ public class TestIfMinMax { @Test @IR(applyIf = { "SuperWordReductions", "true" }, - applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MAX_REDUCTION_V, "> 0" }) @Arguments(setup = "setupIntArrays") public Object[] testMaxIntReduction(int[] a, int[] b) { @@ -262,7 +261,7 @@ public class TestIfMinMax { @Test @IR(applyIf = { "SuperWordReductions", "true" }, - applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MIN_REDUCTION_V, "> 0" }) @Arguments(setup = "setupIntArrays") public Object[] testMinIntReduction(int[] a, int[] b) { @@ -297,7 +296,7 @@ public class TestIfMinMax { @Test @IR(applyIf = { "SuperWordReductions", "true" }, - applyIfCPUFeatureOr = { "avx512", "true" }, + applyIfCPUFeatureOr = { "avx512", "true", "rvv", "true" }, counts = { IRNode.MAX_REDUCTION_V, "> 0" }) @Arguments(setup = "setupLongArrays") public Object[] testMaxLongReduction(long[] a, long[] b) { @@ -332,7 +331,7 @@ public class TestIfMinMax { @Test @IR(applyIf = { "SuperWordReductions", "true" }, - applyIfCPUFeatureOr = { "avx512", "true" }, + applyIfCPUFeatureOr = { "avx512", "true", "rvv", "true" }, counts = { IRNode.MIN_REDUCTION_V, "> 0" }) @Arguments(setup = "setupLongArrays") public Object[] testMinLongReduction(long[] a, long[] b) { @@ -366,7 +365,7 @@ public class TestIfMinMax { } @Test - @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MAX_VI, "> 0" }) @Arguments(setup = "setupIntArrays") public Object[] testMaxIntVector(int[] a, int[] b) { @@ -401,7 +400,7 @@ public class TestIfMinMax { } @Test - @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MIN_VI, "> 0" }) @Arguments(setup = "setupIntArrays") public Object[] testMinIntVector(int[] a, int[] b) { @@ -436,7 +435,7 @@ public class TestIfMinMax { } @Test - @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MAX_VL, "> 0" }) @Arguments(setup = "setupLongArrays") public Object[] testMaxLongVector(long[] a, long[] b) { @@ -471,7 +470,7 @@ public class TestIfMinMax { } @Test - @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true", "rvv", "true"}, counts = { IRNode.MIN_VL, "> 0" }) @Arguments(setup = "setupLongArrays") public Object[] testMinLongVector(long[] a, long[] b) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 8f70ad9b7d0..a6d738b3a09 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -146,6 +146,7 @@ public class TestFramework { "UseRVV", "UseZbb", "UseZfh", + "UseZicond", "UseZvbb" ) ); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java index 35f357c22f2..c6329c70f65 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -35,6 +35,7 @@ import jdk.incubator.vector.VectorMask; * @library /test/lib / * @requires (os.simpleArch == "x64" & vm.cpu.features ~= ".*sse4.*" & (vm.opt.UseSSE == "null" | vm.opt.UseSSE > 3)) * | os.arch == "aarch64" + * | (os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*") * @run driver compiler.vectorapi.TestVectorTest */ public class TestVectorTest { diff --git a/test/micro/org/openjdk/bench/java/lang/ClassComparison.java b/test/micro/org/openjdk/bench/java/lang/ClassComparison.java new file mode 100644 index 00000000000..2a768f243e2 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ClassComparison.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025, Rivos Inc. 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.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.random.RandomGenerator; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class ClassComparison { + static final int INVOCATIONS = 1024; + + Class[] c1; + Class[] c2; + int[] res; + long[] resLong; + Object[] resObject; + Object ro1; + Object ro2; + Object[] resClass; + Class rc1; + Class rc2; + + @Setup + public void setup() { + var random = RandomGenerator.getDefault(); + c1 = new Class[INVOCATIONS]; + c2 = new Class[INVOCATIONS]; + res = new int[INVOCATIONS]; + resLong = new long[INVOCATIONS]; + resObject = new Object[INVOCATIONS]; + ro1 = new Object(); + ro2 = new Object(); + resClass = new Class[INVOCATIONS]; + rc1 = Float.class; + rc2 = Double.class; + for (int i = 0; i < INVOCATIONS; i++) { + c1[i] = random.nextBoolean() ? Float.class : Double.class; + } + List list = Arrays.asList(c1); + Collections.shuffle(list); + list.toArray(c2); + } + + @Benchmark + public void equalClass() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (c1[i] == c2[i]) ? 1 : 2; + } + } + + @Benchmark + public void notEqualClass() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (c1[i] != c2[i]) ? 1 : 2; + } + } + + public void equalClassResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (c1[i] == c2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualClassResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (c1[i] != c2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/FPComparison.java b/test/micro/org/openjdk/bench/java/lang/FPComparison.java index 9fe88b934f8..8074ada3257 100644 --- a/test/micro/org/openjdk/bench/java/lang/FPComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/FPComparison.java @@ -23,6 +23,7 @@ package org.openjdk.bench.java.lang; import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; import java.util.concurrent.TimeUnit; import java.util.random.RandomGenerator; @@ -41,6 +42,13 @@ public class FPComparison { float[] f2; double[] d2; int[] res; + long[] resLong; + Object[] resObject; + Object ro1; + Object ro2; + Class[] resClass; + Class rc1; + Class rc2; @Setup public void setup() { @@ -50,6 +58,13 @@ public class FPComparison { f2 = new float[INVOCATIONS]; d2 = new double[INVOCATIONS]; res = new int[INVOCATIONS]; + resLong = new long[INVOCATIONS]; + resObject = new Object[INVOCATIONS]; + ro1 = new Object(); + ro2 = new Object(); + resClass = new Class[INVOCATIONS]; + rc1 = Float.class; + rc2 = Double.class; for (int i = 0; i < INVOCATIONS; i++) { int type = random.nextInt(5); if (type == 1) { @@ -79,56 +94,184 @@ public class FPComparison { @Benchmark public void isNanFloat() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Float.isNaN(f1[i]) ? 1 : 0; + res[i] = Float.isNaN(f1[i]) ? 1 : 2; } } @Benchmark public void isNanDouble() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Double.isNaN(d1[i]) ? 1 : 0; + res[i] = Double.isNaN(d1[i]) ? 1 : 2; } } @Benchmark public void isInfiniteFloat() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Float.isInfinite(f1[i]) ? 1 : 0; + res[i] = Float.isInfinite(f1[i]) ? 1 : 2; } } @Benchmark public void isInfiniteDouble() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Double.isInfinite(d1[i]) ? 1 : 0; + res[i] = Double.isInfinite(d1[i]) ? 1 : 2; } } @Benchmark public void isFiniteFloat() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Float.isFinite(f1[i]) ? 1 : 0; + res[i] = Float.isFinite(f1[i]) ? 1 : 2; } } @Benchmark public void isFiniteDouble() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = Double.isFinite(d1[i]) ? 1 : 0; + res[i] = Double.isFinite(d1[i]) ? 1 : 2; } } @Benchmark public void equalFloat() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = (f1[i] == f2[i]) ? 1 : 0; + res[i] = (f1[i] == f2[i]) ? 1 : 2; } } @Benchmark public void equalDouble() { for (int i = 0; i < INVOCATIONS; i++) { - res[i] = (d1[i] == d2[i]) ? 1 : 0; + res[i] = (d1[i] == d2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (f1[i] < f2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (d1[i] < d2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessEqualFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (f1[i] <= f2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessEqualDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (d1[i] <= d2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (f1[i] > f2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (d1[i] > d2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (f1[i] >= f2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (d1[i] >= d2[i]) ? 1 : 2; + } + } + + // --------- result: long --------- + + @Benchmark + public void equalFloatResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (f1[i] == f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void equalDoubleResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (d1[i] == d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessFloatResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (f1[i] < f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessDoubleResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (d1[i] < d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualFloatResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (f1[i] <= f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualDoubleResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (d1[i] <= d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterFloatResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (f1[i] > f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterDoubleResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (d1[i] > d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualFloatResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (f1[i] >= f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualDoubleResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (d1[i] >= d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } } diff --git a/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java b/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java new file mode 100644 index 00000000000..1853be8497d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025, Rivos Inc. 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.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.concurrent.TimeUnit; +import java.util.random.RandomGenerator; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class IntegerComparison { + static final int INVOCATIONS = 1024; + + int[] i1; + int[] i2; + int[] res; + long[] resLong; + Object[] resObject; + Object ro1; + Object ro2; + Object[] resClass; + Class rc1; + Class rc2; + + @Setup + public void setup() { + var random = RandomGenerator.getDefault(); + i1 = new int[INVOCATIONS]; + i2 = new int[INVOCATIONS]; + res = new int[INVOCATIONS]; + resLong = new long[INVOCATIONS]; + resObject = new Object[INVOCATIONS]; + ro1 = new Object(); + ro2 = new Object(); + resClass = new Class[INVOCATIONS]; + rc1 = Float.class; + rc2 = Double.class; + for (int i = 0; i < INVOCATIONS; i++) { + i1[i] = random.nextInt(INVOCATIONS); + i2[i] = random.nextInt(INVOCATIONS); + } + } + + @Benchmark + public void equalInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] == i2[i]) ? 1 : 2; + } + } + + @Benchmark + public void notEqualInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] != i2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] < i2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessEqualInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] <= i2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] > i2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualInteger() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (i1[i] >= i2[i]) ? 1 : 2; + } + } + + // --------- result: long --------- + + public void equalIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] == i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] != i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + public void lessIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] < i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] <= i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + public void greaterIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] > i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualIntegerResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (i1[i] >= i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/LongComparison.java b/test/micro/org/openjdk/bench/java/lang/LongComparison.java new file mode 100644 index 00000000000..bed5ee245b2 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/LongComparison.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025, Rivos Inc. 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.*; + +import java.util.concurrent.TimeUnit; +import java.util.random.RandomGenerator; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class LongComparison { + static final int INVOCATIONS = 1024; + + long[] l1; + long[] l2; + int[] res; + long[] resLong; + Object[] resObject; + Object ro1; + Object ro2; + Object[] resClass; + Class rc1; + Class rc2; + + @Setup + public void setup() { + var random = RandomGenerator.getDefault(); + l1 = new long[INVOCATIONS]; + l2 = new long[INVOCATIONS]; + res = new int[INVOCATIONS]; + resLong = new long[INVOCATIONS]; + resObject = new Object[INVOCATIONS]; + ro1 = new Object(); + ro2 = new Object(); + resClass = new Class[INVOCATIONS]; + rc1 = Float.class; + rc2 = Double.class; + for (int i = 0; i < INVOCATIONS; i++) { + l1[i] = random.nextLong(INVOCATIONS); + l2[i] = random.nextLong(INVOCATIONS); + } + } + + @Benchmark + public void equalLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] == l2[i]) ? 1 : 2; + } + } + + @Benchmark + public void notEqualLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] != l2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] < l2[i]) ? 1 : 2; + } + } + + @Benchmark + public void lessEqualLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] <= l2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] > l2[i]) ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualLong() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (l1[i] >= l2[i]) ? 1 : 2; + } + } + + // --------- result: long --------- + + public void equalLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] == l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] != l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + public void lessLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] < l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] <= l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + public void greaterLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] > l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualLongResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (l1[i] >= l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/PointerComparison.java b/test/micro/org/openjdk/bench/java/lang/PointerComparison.java new file mode 100644 index 00000000000..b6bcf008619 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/PointerComparison.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025, Rivos Inc. 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.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.random.RandomGenerator; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class PointerComparison { + static final int INVOCATIONS = 1024; + + Object[] o1; + Object[] o2; + int[] res; + long[] resLong; + Object[] resObject; + Object ro1; + Object ro2; + Object[] resClass; + Class rc1; + Class rc2; + + @Setup + public void setup() { + var random = RandomGenerator.getDefault(); + o1 = new Object[INVOCATIONS]; + o2 = new Object[INVOCATIONS]; + res = new int[INVOCATIONS]; + resLong = new long[INVOCATIONS]; + resObject = new Object[INVOCATIONS]; + ro1 = new Object(); + ro2 = new Object(); + resClass = new Class[INVOCATIONS]; + rc1 = Float.class; + rc2 = Double.class; + for (int i = 0; i < INVOCATIONS; i++) { + o1[i] = new Object(); + } + List list = Arrays.asList(o1); + Collections.shuffle(list); + list.toArray(o2); + } + + @Benchmark + public void equalObject() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (o1[i] == o2[i]) ? 1 : 2; + } + } + + @Benchmark + public void notEqualObject() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = (o1[i] != o2[i]) ? 1 : 2; + } + } + + public void equalObjectResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (o1[i] == o2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualObjectResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = (o1[i] != o2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } +}