diff --git a/Backport-JDK-8344306-8352607-8330213-8352504-8352248-RISC-V-Add-zicond.patch b/Backport-JDK-8344306-8352607-8330213-8352504-8352248-RISC-V-Add-zicond.patch new file mode 100644 index 0000000000000000000000000000000000000000..ba43c5efb0b4d9898cd389b1053ac126069baa23 --- /dev/null +++ b/Backport-JDK-8344306-8352607-8330213-8352504-8352248-RISC-V-Add-zicond.patch @@ -0,0 +1,1944 @@ +diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp +index 4c167073a..183ef6017 100644 +--- a/src/hotspot/cpu/riscv/assembler_riscv.hpp ++++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp +@@ -3255,6 +3255,38 @@ public: + + #undef INSN + ++// -------------- Zicond Instruction Definitions -------------- ++// Zicond conditional operations extension ++private: ++enum CZERO_OP : unsigned int { ++ CZERO_NEZ = 0b111, ++ CZERO_EQZ = 0b101 ++}; ++ ++template ++void czero(Register Rd, Register Rs1, Register Rs2) { ++ assert_cond(UseZicond); ++ uint32_t insn = 0; ++ patch ((address)&insn, 6, 0, 0b0110011); // bits: 7, name: 0x33, attr: ['OP'] ++ patch_reg((address)&insn, 7, Rd); // bits: 5, name: 'rd' ++ patch ((address)&insn, 14, 12, OP_VALUE); // bits: 3, name: 0x7, attr: ['CZERO.NEZ'] / 0x5, attr: ['CZERO.EQZ']} ++ patch_reg((address)&insn, 15, Rs1); // bits: 5, name: 'rs1', attr: ['value'] ++ patch_reg((address)&insn, 20, Rs2); // bits: 5, name: 'rs2', attr: ['condition'] ++ patch ((address)&insn, 31, 25, 0b0000111); // bits: 7, name: 0x7, attr: ['CZERO'] ++ emit_int32(insn); ++} ++ ++public: ++// Moves zero to a register rd, if the condition rs2 is equal to zero, otherwise moves rs1 to rd. ++void czero_eqz(Register rd, Register rs1_value, Register rs2_condition) { ++ czero(rd, rs1_value, rs2_condition); ++} ++ ++// Moves zero to a register rd, if the condition rs2 is nonzero, otherwise moves rs1 to rd. ++void czero_nez(Register rd, Register rs1_value, Register rs2_condition) { ++ czero(rd, rs1_value, rs2_condition); ++} ++ + // --------------------------------------------------------------------------------------- + + #define INSN(NAME, REGISTER) \ +diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +index c2a250966..221c16aaa 100644 +--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp ++++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +@@ -905,6 +905,7 @@ void LIR_Assembler::emit_op3(LIR_Op3* op) { + } + } + ++// Consider using cmov (Zicond) + void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, BasicType type, + LIR_Opr cmp_opr1, LIR_Opr cmp_opr2) { + Label label; +diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +index 8fa218ae9..0f138e219 100644 +--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp ++++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +@@ -2031,10 +2031,78 @@ void C2_MacroAssembler::enc_cmpEqNe_imm0_branch(int cmpFlag, Register op1, Label + } + + void C2_MacroAssembler::enc_cmove(int cmpFlag, Register op1, Register op2, Register dst, Register src) { +- Label L; +- cmp_branch(cmpFlag ^ (1 << neg_cond_bits), op1, op2, L); +- mv(dst, src); +- bind(L); ++ bool is_unsigned = (cmpFlag & unsigned_branch_mask) == unsigned_branch_mask; ++ int op_select = cmpFlag & (~unsigned_branch_mask); ++ ++ switch (op_select) { ++ case BoolTest::eq: ++ cmov_eq(op1, op2, dst, src); ++ break; ++ case BoolTest::ne: ++ cmov_ne(op1, op2, dst, src); ++ break; ++ case BoolTest::le: ++ if (is_unsigned) { ++ cmov_leu(op1, op2, dst, src); ++ } else { ++ cmov_le(op1, op2, dst, src); ++ } ++ break; ++ case BoolTest::ge: ++ if (is_unsigned) { ++ cmov_geu(op1, op2, dst, src); ++ } else { ++ cmov_ge(op1, op2, dst, src); ++ } ++ break; ++ case BoolTest::lt: ++ if (is_unsigned) { ++ cmov_ltu(op1, op2, dst, src); ++ } else { ++ cmov_lt(op1, op2, dst, src); ++ } ++ break; ++ case BoolTest::gt: ++ if (is_unsigned) { ++ cmov_gtu(op1, op2, dst, src); ++ } else { ++ cmov_gt(op1, op2, dst, src); ++ } ++ break; ++ default: ++ assert(false, "unsupported compare condition"); ++ ShouldNotReachHere(); ++ } ++} ++ ++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. +diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +index c40b96998..662464098 100644 +--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp ++++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +@@ -102,7 +102,6 @@ + + // refer to conditional_branches and float_conditional_branches + static const int bool_test_bits = 3; +- static const int neg_cond_bits = 2; + static const int unsigned_branch_mask = 1 << bool_test_bits; + static const int double_branch_mask = 1 << bool_test_bits; + +@@ -125,6 +124,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 53a41665f..59af30921 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/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp +index 824d36f0f..c67879c56 100644 +--- a/src/hotspot/cpu/riscv/globals_riscv.hpp ++++ b/src/hotspot/cpu/riscv/globals_riscv.hpp +@@ -113,6 +113,7 @@ define_pd_global(intx, InlineSmallCode, 1000); + product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ + product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \ + product(bool, UseZicboz, false, EXPERIMENTAL, "Use Zicboz instructions") \ ++ product(bool, UseZicond, false, DIAGNOSTIC, "Use Zicond instructions") \ + product(bool, UseZihintpause, false, EXPERIMENTAL, \ + "Use Zihintpause instructions") \ + product(bool, UseZvbc, false, EXPERIMENTAL, "Use Zvbc instructions") \ +diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +index e2cc6cd92..4cc72703c 100644 +--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp ++++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +@@ -971,6 +971,271 @@ void MacroAssembler::wrap_label(Register r1, Register r2, Label &L, + + #undef INSN + ++// cmov ++void MacroAssembler::cmov_eq(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ xorr(t0, cmp1, cmp2); ++ czero_eqz(dst, dst, t0); ++ czero_nez(t0 , src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bne(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_ne(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ xorr(t0, cmp1, cmp2); ++ czero_nez(dst, dst, t0); ++ czero_eqz(t0 , src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ beq(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_le(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ slt(t0, cmp2, cmp1); ++ czero_eqz(dst, dst, t0); ++ czero_nez(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bgt(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_leu(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ sltu(t0, cmp2, cmp1); ++ czero_eqz(dst, dst, t0); ++ czero_nez(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bgtu(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_ge(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ slt(t0, cmp1, cmp2); ++ czero_eqz(dst, dst, t0); ++ czero_nez(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ blt(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_geu(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ sltu(t0, cmp1, cmp2); ++ czero_eqz(dst, dst, t0); ++ czero_nez(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bltu(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_lt(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ slt(t0, cmp1, cmp2); ++ czero_nez(dst, dst, t0); ++ czero_eqz(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bge(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_ltu(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ sltu(t0, cmp1, cmp2); ++ czero_nez(dst, dst, t0); ++ czero_eqz(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bgeu(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_gt(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ slt(t0, cmp2, cmp1); ++ czero_nez(dst, dst, t0); ++ czero_eqz(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ ble(cmp1, cmp2, no_set); ++ mv(dst, src); ++ bind(no_set); ++} ++ ++void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src) { ++ if (UseZicond) { ++ sltu(t0, cmp2, cmp1); ++ czero_nez(dst, dst, t0); ++ czero_eqz(t0, src, t0); ++ orr(dst, dst, t0); ++ return; ++ } ++ Label no_set; ++ bleu(cmp1, cmp2, no_set); ++ mv(dst, src); ++ 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 0be049b1b..7906e8254 100644 +--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp ++++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +@@ -439,6 +439,22 @@ class MacroAssembler: public Assembler { + void load_int_misaligned(Register dst, Address src, Register tmp, bool is_signed, int granularity = 1); + void load_long_misaligned(Register dst, Address src, Register tmp, int granularity = 1); + ++ void cmov_eq(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_ne(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_le(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_leu(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_ge(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_geu(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_lt(Register cmp1, Register cmp2, Register dst, Register src); ++ void cmov_ltu(Register cmp1, Register cmp2, Register dst, Register src); ++ 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: + // Standard pseudo instructions + inline void nop() { +diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad +index 6e381d3f0..e33c82820 100644 +--- a/src/hotspot/cpu/riscv/riscv.ad ++++ b/src/hotspot/cpu/riscv/riscv.ad +@@ -1923,6 +1923,12 @@ const bool Matcher::match_rule_supported(int opcode) { + case Op_CountTrailingZerosI: + case Op_CountTrailingZerosL: + return UseZbb; ++ ++ case Op_CMoveF: ++ case Op_CMoveD: ++ case Op_CMoveP: ++ case Op_CMoveN: ++ return false; + } + + return true; // Per default match rules are supported. +@@ -8942,17 +8948,11 @@ instruct minI_reg_reg(iRegINoSp dst, iRegI src) + match(Set dst (MinI dst src)); + + ins_cost(BRANCH_COST + ALU_COST); +- format %{ +- "ble $dst, $src, skip\t#@minI_reg_reg\n\t" +- "mv $dst, $src\n\t" +- "skip:" +- %} ++ format %{"minI_reg_reg $dst, $dst, $src\t#@minI_reg_reg\n\t"%} + + ins_encode %{ +- Label Lskip; +- __ ble(as_Register($dst$$reg), as_Register($src$$reg), Lskip); +- __ mv(as_Register($dst$$reg), as_Register($src$$reg)); +- __ bind(Lskip); ++ __ cmov_gt(as_Register($dst$$reg), as_Register($src$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); + %} + + ins_pipe(pipe_class_compare); +@@ -8963,17 +8963,11 @@ instruct maxI_reg_reg(iRegINoSp dst, iRegI src) + match(Set dst (MaxI dst src)); + + ins_cost(BRANCH_COST + ALU_COST); +- format %{ +- "bge $dst, $src, skip\t#@maxI_reg_reg\n\t" +- "mv $dst, $src\n\t" +- "skip:" +- %} ++ format %{"maxI_reg_reg $dst, $dst, $src\t#@maxI_reg_reg\n\t"%} + + ins_encode %{ +- Label Lskip; +- __ bge(as_Register($dst$$reg), as_Register($src$$reg), Lskip); +- __ mv(as_Register($dst$$reg), as_Register($src$$reg)); +- __ bind(Lskip); ++ __ cmov_lt(as_Register($dst$$reg), as_Register($src$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); + %} + + ins_pipe(pipe_class_compare); +@@ -8989,17 +8983,11 @@ instruct minI_reg_zero(iRegINoSp dst, immI0 zero) + match(Set dst (MinI zero dst)); + + ins_cost(BRANCH_COST + ALU_COST); +- format %{ +- "blez $dst, skip\t#@minI_reg_zero\n\t" +- "mv $dst, zr\n\t" +- "skip:" +- %} ++ format %{"minI_reg_zero $dst, $dst, zr\t#@minI_reg_zero\n\t"%} + + ins_encode %{ +- Label Lskip; +- __ blez(as_Register($dst$$reg), Lskip); +- __ mv(as_Register($dst$$reg), zr); +- __ bind(Lskip); ++ __ cmov_gt(as_Register($dst$$reg), zr, ++ as_Register($dst$$reg), zr); + %} + + ins_pipe(pipe_class_compare); +@@ -9011,17 +8999,11 @@ instruct maxI_reg_zero(iRegINoSp dst, immI0 zero) + match(Set dst (MaxI zero dst)); + + ins_cost(BRANCH_COST + ALU_COST); +- format %{ +- "bgez $dst, skip\t#@maxI_reg_zero\n\t" +- "mv $dst, zr\n\t" +- "skip:" +- %} ++ format %{"maxI_reg_zero $dst, $dst, zr\t#@maxI_reg_zero\n\t"%} + + ins_encode %{ +- Label Lskip; +- __ bgez(as_Register($dst$$reg), Lskip); +- __ mv(as_Register($dst$$reg), zr); +- __ bind(Lskip); ++ __ cmov_lt(as_Register($dst$$reg), zr, ++ as_Register($dst$$reg), zr); + %} + + ins_pipe(pipe_class_compare); +@@ -9034,23 +9016,12 @@ instruct minI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) + effect(DEF dst, USE src1, USE src2); + + ins_cost(BRANCH_COST + ALU_COST * 2); +- format %{ +- "ble $src1, $src2, Lsrc1.\t#@minI_rReg\n\t" +- "mv $dst, $src2\n\t" +- "j Ldone\n\t" +- "bind Lsrc1\n\t" +- "mv $dst, $src1\n\t" +- "bind\t#@minI_rReg" +- %} ++ format %{"minI_rReg $dst, $src1, $src2\t#@minI_rReg\n\t"%} + + ins_encode %{ +- Label Lsrc1, Ldone; +- __ ble(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1); +- __ mv(as_Register($dst$$reg), as_Register($src2$$reg)); +- __ j(Ldone); +- __ bind(Lsrc1); + __ mv(as_Register($dst$$reg), as_Register($src1$$reg)); +- __ bind(Ldone); ++ __ cmov_gt(as_Register($src1$$reg), as_Register($src2$$reg), ++ as_Register($dst$$reg), as_Register($src2$$reg)); + %} + + ins_pipe(pipe_class_compare); +@@ -9063,24 +9034,12 @@ instruct maxI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) + effect(DEF dst, USE src1, USE src2); + + ins_cost(BRANCH_COST + ALU_COST * 2); +- format %{ +- "bge $src1, $src2, Lsrc1\t#@maxI_rReg\n\t" +- "mv $dst, $src2\n\t" +- "j Ldone\n\t" +- "bind Lsrc1\n\t" +- "mv $dst, $src1\n\t" +- "bind\t#@maxI_rReg" +- %} ++ format %{"maxI_rReg $dst, $src1, $src2\t#@maxI_rReg\n\t"%} + + ins_encode %{ +- Label Lsrc1, Ldone; +- __ bge(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1); +- __ mv(as_Register($dst$$reg), as_Register($src2$$reg)); +- __ j(Ldone); +- __ bind(Lsrc1); + __ mv(as_Register($dst$$reg), as_Register($src1$$reg)); +- __ bind(Ldone); +- ++ __ cmov_lt(as_Register($src1$$reg), as_Register($src2$$reg), ++ as_Register($dst$$reg), as_Register($src2$$reg)); + %} + + ins_pipe(pipe_class_compare); +@@ -9847,12 +9806,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 %{ +@@ -9869,7 +9831,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 %{ +@@ -9886,7 +9848,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 %{ +@@ -9903,7 +9865,75 @@ 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 %{ ++ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, ++ as_Register($op1$$reg), as_Register($op2$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); ++ %} ++ ++ 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 %{ ++ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" ++ %} ++ ++ ins_encode %{ ++ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, ++ as_Register($op1$$reg), as_Register($op2$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); ++ %} ++ ++ ins_pipe(pipe_class_compare); ++%} ++ ++instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) %{ ++ match(Set dst (CMoveI (Binary cop (CmpP op1 op2)) (Binary dst src))); ++ ins_cost(ALU_COST + BRANCH_COST); ++ ++ format %{ ++ "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" + %} + + ins_encode %{ +@@ -9915,12 +9945,14 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL 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 %{ +@@ -9937,7 +9969,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 %{ +@@ -9954,7 +9986,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 %{ +@@ -9971,7 +10003,75 @@ 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 %{ ++ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, ++ as_Register($op1$$reg), as_Register($op2$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); ++ %} ++ ++ 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 %{ ++ "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" ++ %} ++ ++ ins_encode %{ ++ __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, ++ as_Register($op1$$reg), as_Register($op2$$reg), ++ as_Register($dst$$reg), as_Register($src$$reg)); ++ %} ++ ++ ins_pipe(pipe_class_compare); ++%} ++ ++instruct cmovL_cmpP(iRegLNoSp dst, iRegL src, iRegP op1, iRegP op2, cmpOpU cop) %{ ++ match(Set dst (CMoveL (Binary cop (CmpP op1 op2)) (Binary dst src))); ++ ins_cost(ALU_COST + BRANCH_COST); ++ ++ format %{ ++ "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 d3505dbc7..5f8d4f7e0 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp +@@ -299,14 +299,6 @@ void VM_Version::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(SpecialEncodeISOArray, false); + } +diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp +index 99a4c3dd4..83f481d43 100644 +--- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp ++++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp +@@ -108,6 +108,8 @@ class VM_Version : public Abstract_VM_Version { + // Zbc Carry-less multiplication + // Zbs Single-bit instructions + // ++ // Zicond Conditional operations ++ // + // Zicsr Control and Status Register (CSR) Instructions + // Zifencei Instruction-Fetch Fence + // Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. +@@ -148,6 +150,7 @@ class VM_Version : public Abstract_VM_Version { + decl(ext_Zacas , "Zacas" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \ + decl(ext_Zvbc , "Zvbc" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvbc)) \ + decl(ext_Zvkn , "Zvkn" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvkn)) \ ++ decl(ext_Zicond , "Zicond" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ + decl(mvendorid , "VendorId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + decl(marchid , "ArchId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ + decl(mimpid , "ImpId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ +@@ -207,6 +210,7 @@ class VM_Version : public Abstract_VM_Version { + RV_ENABLE_EXTENSION(UseZicbom) \ + RV_ENABLE_EXTENSION(UseZicbop) \ + RV_ENABLE_EXTENSION(UseZicboz) \ ++ RV_ENABLE_EXTENSION(UseZicond) \ + RV_ENABLE_EXTENSION(UseZihintpause) \ + + static void useRVA23U64Profile(); +diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +index eae996696..513f274f0 100644 +--- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp ++++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +@@ -191,6 +191,14 @@ void RiscvHwprobe::add_features_from_query_result() { + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVBC)) { + VM_Version::ext_Zvbc.enable_feature(); + } ++#ifndef PRODUCT ++ // 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/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp +index f0fc24d97..3bf999cd8 100644 +--- a/src/hotspot/share/opto/loopopts.cpp ++++ b/src/hotspot/share/opto/loopopts.cpp +@@ -774,7 +774,7 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) { + break; + } + } +- if (phi == nullptr || _igvn.type(phi) == Type::TOP) { ++ if (phi == nullptr || _igvn.type(phi) == Type::TOP || !CMoveNode::supported(_igvn.type(phi))) { + break; + } + if (PrintOpto && VerifyLoopOptimizations) { tty->print_cr("CMOV"); } +diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp +index bfa30f02a..491dc3b5c 100644 +--- a/src/hotspot/share/opto/movenode.cpp ++++ b/src/hotspot/share/opto/movenode.cpp +@@ -171,7 +171,7 @@ const Type* CMoveNode::Value(PhaseGVN* phase) const { + // Make a correctly-flavored CMove. Since _type is directly determined + // from the inputs we do not need to specify it here. + CMoveNode *CMoveNode::make(Node *c, Node *bol, Node *left, Node *right, const Type *t) { +- switch( t->basic_type() ) { ++ switch(t->basic_type()) { + case T_INT: return new CMoveINode( bol, left, right, t->is_int() ); + case T_FLOAT: return new CMoveFNode( bol, left, right, t ); + case T_DOUBLE: return new CMoveDNode( bol, left, right, t ); +@@ -180,8 +180,23 @@ CMoveNode *CMoveNode::make(Node *c, Node *bol, Node *left, Node *right, const Ty + case T_ADDRESS: return new CMovePNode( c, bol, left, right, t->is_ptr() ); + case T_NARROWOOP: return new CMoveNNode( c, bol, left, right, t ); + default: +- ShouldNotReachHere(); +- return nullptr; ++ ShouldNotReachHere(); ++ return nullptr; ++ } ++} ++ ++bool CMoveNode::supported(const Type* t) { ++ switch (t->basic_type()) { ++ case T_INT: return Matcher::match_rule_supported(Op_CMoveI); ++ case T_FLOAT: return Matcher::match_rule_supported(Op_CMoveF); ++ case T_DOUBLE: return Matcher::match_rule_supported(Op_CMoveD); ++ case T_LONG: return Matcher::match_rule_supported(Op_CMoveL); ++ case T_OBJECT: return Matcher::match_rule_supported(Op_CMoveP); ++ case T_ADDRESS: return Matcher::match_rule_supported(Op_CMoveP); ++ case T_NARROWOOP: return Matcher::match_rule_supported(Op_CMoveN); ++ default: ++ ShouldNotReachHere(); ++ return false; + } + } + +diff --git a/src/hotspot/share/opto/movenode.hpp b/src/hotspot/share/opto/movenode.hpp +index 961896546..62ed2381e 100644 +--- a/src/hotspot/share/opto/movenode.hpp ++++ b/src/hotspot/share/opto/movenode.hpp +@@ -48,6 +48,7 @@ class CMoveNode : public TypeNode { + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); + static CMoveNode *make(Node *c, Node *bol, Node *left, Node *right, const Type *t); ++ static bool supported(const Type* t); + // Helper function to spot cmove graph shapes + static Node *is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ); + }; +diff --git a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp +new file mode 100644 +index 000000000..152b997b3 +--- /dev/null ++++ b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2024, 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. ++ */ ++ ++#include "precompiled.hpp" ++ ++#if (defined(RISCV) || defined(RISCV64)) && !defined(ZERO) ++ ++#include "asm/assembler.inline.hpp" ++#include "asm/macroAssembler.hpp" ++#include "memory/resourceArea.hpp" ++#include "runtime/orderAccess.hpp" ++#include "unittest.hpp" ++ ++typedef int64_t (*zicond_func)(int64_t cmp1, int64_t cmp2, int64_t dst, int64_t src); ++typedef void (MacroAssembler::*cmov_func)(Register cmp1, Register cmp2, Register dst, Register src); ++ ++class CmovTester { ++ public: ++ static void test(cmov_func func, int64_t a0, int64_t a1, int64_t a2, int64_t a3, int64_t result) { ++ BufferBlob* bb = BufferBlob::create("riscvTest", 128); ++ CodeBuffer code(bb); ++ MacroAssembler _masm(&code); ++ address entry = _masm.pc(); ++ { ++ ((&_masm)->*func)(c_rarg0, c_rarg1, c_rarg2, c_rarg3); ++ _masm.mv(c_rarg0, c_rarg2); ++ _masm.ret(); ++ } ++ _masm.flush(); ++ OrderAccess::cross_modify_fence(); ++ int64_t ret = ((zicond_func)entry)(a0, a1, a2, a3); ++ ASSERT_EQ(ret, result); ++ BufferBlob::free(bb); ++ } ++}; ++ ++void run_cmov_tests() { ++ // If 42(a0) eq 42(a1): assign dest(a2/66) the src(a3/77), expect result: 77 ++ CmovTester::test(&MacroAssembler::cmov_eq, 42, 42, 66, 77, 77); ++ // If 41(a0) eq 42(a1): assign dest(a2/66) the src(a3/77), expect result: 66 ++ CmovTester::test(&MacroAssembler::cmov_eq, 41, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_ne, 41, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_ne, 42, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_le, 41, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_le, 42, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_le, 42, -1, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_leu, 41, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_leu, 42, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_leu, -1, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_ge, 43, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_ge, 42, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_ge, -1, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_geu, 43, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_geu, 42, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_geu, 42, -1, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_lt, 41, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_lt, 42, 42, 66, 77, 66); ++ CmovTester::test(&MacroAssembler::cmov_lt, 42, -1, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_ltu, 41, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_ltu, 42, 42, 66, 77, 66); ++ CmovTester::test(&MacroAssembler::cmov_ltu, -1, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_gt, 43, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_gt, 42, 42, 66, 77, 66); ++ CmovTester::test(&MacroAssembler::cmov_gt, -1, 42, 66, 77, 66); ++ ++ CmovTester::test(&MacroAssembler::cmov_gtu, 43, 42, 66, 77, 77); ++ CmovTester::test(&MacroAssembler::cmov_gtu, 42, 42, 66, 77, 66); ++ CmovTester::test(&MacroAssembler::cmov_gtu, 42, -1, 66, 77, 66); ++} ++ ++TEST_VM(RiscV, cmov) { ++ run_cmov_tests(); ++ if (UseZicond) { ++ UseZicond = false; ++ run_cmov_tests(); ++ UseZicond = true; ++ } ++} ++ ++#endif // RISCV +diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java b/test/hotspot/jtreg/compiler/c2/irTests/TestConv2BExpansion.java +index de32b7b21..35d676b6d 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 8445c856b..a06cb45d5 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/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +index 8ce751f39..2beba7705 100644 +--- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java ++++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +@@ -140,6 +140,7 @@ public class TestFramework { + "UseSVE", + "UseZbb", + "UseRVV", ++ "UseZicond", + "Xlog", + "LogCompilation" + ) +diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorTest.java +index 35f357c22..c6329c70f 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 000000000..2a768f243 +--- /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 9fe88b934..8074ada32 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 000000000..1853be849 +--- /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 000000000..bed5ee245 +--- /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 000000000..b6bcf0086 +--- /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; ++ } ++ } ++} diff --git a/openjdk-21.spec b/openjdk-21.spec index 53c88563df80da88513f1f6f2fc261958b09ef4f..c0ed2a6f9ca66741d748b6fa11457884b8541c02 100644 --- a/openjdk-21.spec +++ b/openjdk-21.spec @@ -905,7 +905,7 @@ Name: java-21-%{origin} Version: %{newjavaver}.%{buildver} # This package needs `.rolling` as part of Release so as to not conflict on install with # java-X-openjdk. I.e. when latest rolling release is also an LTS release packaged as -Release: 4 +Release: 5 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons @@ -1077,6 +1077,7 @@ Patch3020: Backport-JDK-8327964-8360179-RISC-V-Only-enable-BigInteger-intrinsics Patch3021: Backport-JDK-8315743-8315856-8344010-8344382-RISC-V-Use-Zacas-extension-for-cmpxchg.patch Patch3022: Backport-JDK-8319778-8324881-8319797-8319900-Recursive-lightweight-locking-riscv64-implementation.patch Patch3023: Backport-JDK-8345351-8356159-RISC-V-Add-Zabha.patch +Patch3024: Backport-JDK-8344306-8352607-8330213-8352504-8352248-RISC-V-Add-zicond.patch BuildRequires: autoconf BuildRequires: automake @@ -1394,6 +1395,7 @@ pushd %{top_level_dir_name} %patch3021 -p1 %patch3022 -p1 %patch3023 -p1 +%patch3024 -p1 popd %endif @@ -1951,6 +1953,9 @@ cjc.mainProgram(args) -- the returns from copy_jdk_configs.lua should not affect %changelog +* Thu Nov 20 2025 zhangshihui - 1:21.0.9.10-5 +- RISC-V add Zicond + * Fri Nov 14 2025 zhangshihui - 1:21.0.9.10-4 - RISC-V add Zacas and Zabha and implemente lightweight locking