diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 7334ec675e31f..31713d7362a18 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -3107,6 +3107,38 @@ enum Nf { #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); + } + // -------------- ZCB Instruction Definitions -------------- // Zcb additional C instructions private: diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index eb715227f7e4e..b7edd3d231f81 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -870,6 +870,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 bf553b3577038..987ac72473245 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1935,6 +1935,29 @@ static conditional_branch_insn conditional_branches[] = (conditional_branch_insn)&MacroAssembler::bgeu }; +static const char* conditional_branch_name[] = +{ + /* SHORT branches */ + "beq", + "bgt", + "nullptr", + "blt", + "bne", + "ble", + "nullptr", + "bge", + + /* UNSIGNED branches */ + "beq", + "bgtu", + "nullptr", + "bltu", + "bne", + "bleu", + "nullptr", + "bgeu" +}; + static float_conditional_branch_insn float_conditional_branches[] = { /* FLOAT SHORT branches */ @@ -2004,9 +2027,58 @@ 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 ? true : false; + int op_select = cmpFlag & (~unsigned_branch_mask); + + char buf[1024]; + os::snprintf_checked(buf, 1024, "CMP flag: (%s) (%s) %d , op1:%s, op2:%s, dst:%s, src:%s | name:%s value:%x bef:%x", + op_select ? "unsigned" : "signed", conditional_branch_name[op_select], + op_select, op1->name(), op2->name(), dst->name(), src->name(), + conditional_branch_name[cmpFlag ^ (1 << neg_cond_bits)], cmpFlag ^ (1 << neg_cond_bits), cmpFlag + ); + BLOCK_COMMENT(buf); + + switch (op_select) { + case BoolTest::eq: + cmov_eq(op1, op2, dst, src); + return; + break; + case BoolTest::ne: + cmov_ne(op1, op2, dst, src); + return; + 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(); + } } // Set dst to NaN if any NaN input. diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index ffbb7c589112a..6ceb90689bf3d 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -110,6 +110,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, EXPERIMENTAL, "Use Zicond instructions") \ product(bool, UseZihintpause, false, EXPERIMENTAL, \ "Use Zihintpause instructions") \ product(bool, UseZtso, false, EXPERIMENTAL, "Assume Ztso memory model") \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 81d3338638d8d..462da6c1522fd 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1128,6 +1128,147 @@ 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); +} + // 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 568df0562225c..6fad1168e8a2c 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -626,6 +626,17 @@ class MacroAssembler: public Assembler { void bltz(Register Rs, const address dest); void bgtz(Register Rs, const address dest); + 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); + 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/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 8fdde0094f40d..e08838c3a6fe4 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -116,6 +116,8 @@ class VM_Version : public Abstract_VM_Version { // // Zfh Half-Precision Floating-Point 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. @@ -164,6 +166,7 @@ class VM_Version : public Abstract_VM_Version { decl(ext_Zvbb , "Zvbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvbb)) \ decl(ext_Zvfh , "Zvfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvfh)) \ 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) \ @@ -223,6 +226,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 2020e2fdb2429..f785d93539348 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -181,6 +181,9 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { VM_Version::ext_Zvfh.enable_feature(); } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { + VM_Version::ext_Zicond.enable_feature(); + } 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);