8344306: RISC-V: Add zicond

Reviewed-by: fyang, luhenry, mli
This commit is contained in:
Robbin Ehn 2024-11-28 12:05:23 +00:00
parent d33ad07c32
commit edfe28541a
10 changed files with 344 additions and 5 deletions

View File

@ -3107,6 +3107,38 @@ public:
#undef INSN
// -------------- Zicond Instruction Definitions --------------
// Zicond conditional operations extension
private:
enum CZERO_OP : unsigned int {
CZERO_NEZ = 0b111,
CZERO_EQZ = 0b101
};
template <CZERO_OP OP_VALUE>
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<CZERO_EQZ>(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<CZERO_NEZ>(rd, rs1_value, rs2_condition);
}
// -------------- ZCB Instruction Definitions --------------
// Zcb additional C instructions
private:

View File

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

View File

@ -2003,10 +2003,48 @@ 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();
}
}
// Set dst to NaN if any NaN input.

View File

@ -98,7 +98,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;

View File

@ -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, DIAGNOSTIC, "Use Zicond instructions") \
product(bool, UseZihintpause, false, EXPERIMENTAL, \
"Use Zihintpause instructions") \
product(bool, UseZtso, false, EXPERIMENTAL, "Assume Ztso memory model") \

View File

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

View File

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

View File

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

View File

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

View File

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