[riscv64] Add RVC Instr CB and fix some RVC Instr CA

Adds the following CB type RISC-V instructions to the assembler:
c.beqz, c.bnez, c.andi, c.srai, c.srli. Also removes sext_xlen
from RVC instructions c.xor, c.or, c.and.

Change-Id: I96ce4693019c28235ccd4f85d0a68ca89a3f4096
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2912922
Reviewed-by: Brice Dobry <brice.dobry@futurewei.com>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Brice Dobry <brice.dobry@futurewei.com>
Cr-Commit-Position: refs/heads/master@{#74801}
This commit is contained in:
Derek Tu 2021-05-24 00:25:34 +00:00 committed by V8 LUCI CQ
parent 7c30ae29c0
commit 115db49c25
8 changed files with 297 additions and 4 deletions

View File

@ -302,6 +302,11 @@ bool Assembler::IsBranch(Instr instr) {
return (instr & kBaseOpcodeMask) == BRANCH;
}
bool Assembler::IsCBranch(Instr instr) {
int Op = instr & kRvcOpcodeMask;
return Op == RO_C_BNEZ || Op == RO_C_BEQZ;
}
bool Assembler::IsJump(Instr instr) {
int Op = instr & kBaseOpcodeMask;
return Op == JAL || Op == JALR;
@ -413,6 +418,12 @@ int Assembler::target_at(int pos, bool is_internal) {
if (offset == kEndOfJumpChain) return kEndOfChain;
return offset + pos;
} break;
case RO_C_BNEZ:
case RO_C_BEQZ: {
int32_t offset = instruction->RvcImm8BValue();
if (offset == kEndOfJumpChain) return kEndOfChain;
return pos + offset;
} break;
default: {
if (instr == kEndOfJumpChain) {
return kEndOfChain;
@ -496,6 +507,22 @@ static inline ShortInstr SetCJalOffset(int32_t pos, int32_t target_pos,
return instr | (imm11 & kImm11Mask);
}
static inline Instr SetCBranchOffset(int32_t pos, int32_t target_pos,
Instr instr) {
DCHECK(Assembler::IsCBranch(instr));
int32_t imm = target_pos - pos;
DCHECK_EQ(imm & 1, 0);
DCHECK(is_intn(imm, Assembler::kCBranchOffsetBits));
instr &= ~kRvcBImm8Mask;
int32_t imm8 = ((imm & 0x20) >> 5) | ((imm & 0x6)) | ((imm & 0xc0) >> 3) |
((imm & 0x18) << 2) | ((imm & 0x100) >> 1);
imm8 = ((imm8 & 0x1f) << 2) | ((imm8 & 0xe0) << 5);
DCHECK(Assembler::IsCBranch(instr | imm8 & kRvcBImm8Mask));
return instr | (imm8 & kRvcBImm8Mask);
}
void Assembler::target_at_put(int pos, int target_pos, bool is_internal) {
if (is_internal) {
uint64_t imm = reinterpret_cast<uint64_t>(buffer_start_) + target_pos;
@ -547,6 +574,11 @@ void Assembler::target_at_put(int pos, int target_pos, bool is_internal) {
ShortInstr short_instr = SetCJalOffset(pos, target_pos, instr);
instr_at_put(pos, short_instr);
} break;
case RO_C_BNEZ:
case RO_C_BEQZ: {
instr = SetCBranchOffset(pos, target_pos, instr);
instr_at_put(pos, instr);
} break;
default: {
// Emitted label constant, not part of a branch.
// Make label relative to Code pointer of generated Code object.
@ -1068,6 +1100,24 @@ void Assembler::GenInstrCS(uint8_t funct3, Opcode opcode, FPURegister rs2,
emit(instr);
}
void Assembler::GenInstrCB(uint8_t funct3, Opcode opcode, Register rs1,
uint8_t uimm8) {
DCHECK(is_uint3(funct3) && is_uint8(uimm8));
ShortInstr instr = opcode | ((uimm8 & 0x1f) << 2) | ((uimm8 & 0xe0) << 5) |
((rs1.code() & 0x7) << kRvcRs1sShift) |
(funct3 << kRvcFunct3Shift);
emit(instr);
}
void Assembler::GenInstrCBA(uint8_t funct3, uint8_t funct2, Opcode opcode,
Register rs1, uint8_t uimm6) {
DCHECK(is_uint3(funct3) && is_uint2(funct2) && is_uint6(uimm6));
ShortInstr instr = opcode | ((uimm6 & 0x1f) << 2) | ((uimm6 & 0x20) << 7) |
((rs1.code() & 0x7) << kRvcRs1sShift) |
(funct3 << kRvcFunct3Shift) | (funct2 << 10);
emit(instr);
}
// ----- Instruction class templates match those in the compiler
void Assembler::GenInstrBranchCC_rri(uint8_t funct3, Register rs1, Register rs2,
@ -2200,6 +2250,37 @@ void Assembler::c_j(int16_t imm12) {
BlockTrampolinePoolFor(1);
}
// CB Instructions
void Assembler::c_bnez(Register rs1, int16_t imm9) {
DCHECK(((rs1.code() & 0b11000) == 0b01000) && is_int9(imm9));
uint8_t uimm8 = ((imm9 & 0x20) >> 5) | ((imm9 & 0x6)) | ((imm9 & 0xc0) >> 3) |
((imm9 & 0x18) << 2) | ((imm9 & 0x100) >> 1);
GenInstrCB(0b111, C1, rs1, uimm8);
}
void Assembler::c_beqz(Register rs1, int16_t imm9) {
DCHECK(((rs1.code() & 0b11000) == 0b01000) && is_int9(imm9));
uint8_t uimm8 = ((imm9 & 0x20) >> 5) | ((imm9 & 0x6)) | ((imm9 & 0xc0) >> 3) |
((imm9 & 0x18) << 2) | ((imm9 & 0x100) >> 1);
GenInstrCB(0b110, C1, rs1, uimm8);
}
void Assembler::c_srli(Register rs1, uint8_t uimm6) {
DCHECK(((rs1.code() & 0b11000) == 0b01000) && is_uint6(uimm6));
GenInstrCBA(0b100, 0b00, C1, rs1, uimm6);
}
void Assembler::c_srai(Register rs1, uint8_t uimm6) {
DCHECK(((rs1.code() & 0b11000) == 0b01000) && is_uint6(uimm6));
GenInstrCBA(0b100, 0b01, C1, rs1, uimm6);
}
void Assembler::c_andi(Register rs1, uint8_t uimm6) {
DCHECK(((rs1.code() & 0b11000) == 0b01000) && is_uint6(uimm6));
GenInstrCBA(0b100, 0b10, C1, rs1, uimm6);
}
// Privileged
void Assembler::uret() {

View File

@ -198,7 +198,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
kOffset20 = 20, // RISCV imm20
kOffset13 = 13, // RISCV branch
kOffset32 = 32, // RISCV auipc + instr_I
kOffset11 = 11 // RISCV C_J
kOffset11 = 11, // RISCV C_J
kOffset8 = 8 // RISCV compressed branch
};
// Determines if Label is bound and near enough so that branch instruction
@ -214,6 +215,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
int32_t offset);
int JumpOffset(Instr instr);
int CJumpOffset(Instr instr);
int CBranchOffset(Instr instr);
static int LdOffset(Instr instr);
static int AuipcOffset(Instr instr);
static int JalrOffset(Instr instr);
@ -231,6 +233,9 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
inline int16_t cjump_offset(Label* L) {
return (int16_t)branch_offset_helper(L, OffsetSize::kOffset11);
}
inline int32_t cbranch_offset(Label* L) {
return branch_offset_helper(L, OffsetSize::kOffset8);
}
uint64_t jump_address(Label* L);
uint64_t branch_long_offset(Label* L);
@ -322,6 +327,9 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Bits available for offset field in compresed jump
static constexpr int kCJalOffsetBits = 12;
// Bits available for offset field in compressed branch
static constexpr int kCBranchOffsetBits = 9;
// Max offset for b instructions with 12-bit offset field (multiple of 2)
static constexpr int kMaxBranchOffset = (1 << (13 - 1)) - 1;
@ -627,6 +635,13 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
void c_sw(Register rs2, Register rs1, uint16_t uimm7);
void c_sd(Register rs2, Register rs1, uint16_t uimm8);
void c_fsd(FPURegister rs2, Register rs1, uint16_t uimm8);
void c_bnez(Register rs1, int16_t imm9);
inline void c_bnez(Register rs1, Label* L) { c_bnez(rs1, branch_offset(L)); }
void c_beqz(Register rs1, int16_t imm9);
inline void c_beqz(Register rs1, Label* L) { c_beqz(rs1, branch_offset(L)); }
void c_srli(Register rs1, uint8_t uimm6);
void c_srai(Register rs1, uint8_t uimm6);
void c_andi(Register rs1, uint8_t uimm6);
// Privileged
void uret();
@ -855,6 +870,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Check if an instruction is a branch of some kind.
static bool IsBranch(Instr instr);
static bool IsCBranch(Instr instr);
static bool IsJump(Instr instr);
static bool IsJal(Instr instr);
static bool IsCJal(Instr instr);
@ -1103,6 +1119,9 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
void GenInstrCS(uint8_t funct3, Opcode opcode, FPURegister rs2, Register rs1,
uint8_t uimm5);
void GenInstrCJ(uint8_t funct3, Opcode opcode, uint16_t uint11);
void GenInstrCB(uint8_t funct3, Opcode opcode, Register rs1, uint8_t uimm8);
void GenInstrCBA(uint8_t funct3, uint8_t funct2, Opcode opcode, Register rs1,
uint8_t uimm6);
// ----- Instruction class templates match those in LLVM's RISCVInstrInfo.td
void GenInstrBranchCC_rri(uint8_t funct3, Register rs1, Register rs2,

View File

@ -210,6 +210,7 @@ const int kRvcRs1sBits = 3;
const int kRvcRs2sShift = 2;
const int kRvcRs2sBits = 3;
const int kRvcFunct2Shift = 5;
const int kRvcFunct2BShift = 10;
const int kRvcFunct2Bits = 2;
const int kRvcFunct6Shift = 10;
const int kRvcFunct6Bits = 6;
@ -245,9 +246,11 @@ const int kRvcFunct3Mask = (((1 << kRvcFunct3Bits) - 1) << kRvcFunct3Shift);
const int kRvcFunct4Mask = (((1 << kRvcFunct4Bits) - 1) << kRvcFunct4Shift);
const int kRvcFunct6Mask = (((1 << kRvcFunct6Bits) - 1) << kRvcFunct6Shift);
const int kRvcFunct2Mask = (((1 << kRvcFunct2Bits) - 1) << kRvcFunct2Shift);
const int kRvcFunct2BMask = (((1 << kRvcFunct2Bits) - 1) << kRvcFunct2BShift);
const int kCRTypeMask = kRvcOpcodeMask | kRvcFunct4Mask;
const int kCSTypeMask = kRvcOpcodeMask | kRvcFunct6Mask;
const int kCATypeMask = kRvcOpcodeMask | kRvcFunct6Mask | kRvcFunct2Mask;
const int kRvcBImm8Mask = (((1 << 5) - 1) << 2) | (((1 << 3) - 1) << 10);
// RISCV CSR related bit mask and shift
const int kFcsrFlagsBits = 5;
@ -917,6 +920,11 @@ class InstructionGetters : public T {
return this->Bits(kRvcFunct2Shift + kRvcFunct2Bits - 1, kRvcFunct2Shift);
}
inline int RvcFunct2BValue() const {
DCHECK(this->IsShortInstruction());
return this->Bits(kRvcFunct2BShift + kRvcFunct2Bits - 1, kRvcFunct2BShift);
}
inline int CsrValue() const {
DCHECK(this->InstructionType() == InstructionBase::kIType &&
this->BaseOpcode() == SYSTEM);
@ -1125,6 +1133,17 @@ class InstructionGetters : public T {
return imm12 << 20 >> 20;
}
inline int RvcImm8BValue() const {
DCHECK(this->IsShortInstruction());
// | funct3 | imm[8|4:3] | rs1` | imm[7:6|2:1|5] | opcode |
// 15 12 10 7 2
uint32_t Bits = this->InstructionBits();
int32_t imm9 = ((Bits & 0x4) << 3) | ((Bits & 0x18) >> 2) |
((Bits & 0x60) << 1) | ((Bits & 0xc00) >> 7) |
((Bits & 0x1000) >> 4);
return imm9 << 23 >> 23;
}
inline bool AqValue() const { return this->Bits(kAqShift, kAqShift); }
inline bool RlValue() const { return this->Bits(kRlShift, kRlShift); }

View File

@ -93,6 +93,7 @@ class Decoder {
void PrintRvcImm5D(Instruction* instr);
void PrintRvcImm8Addi4spn(Instruction* instr);
void PrintRvcImm11CJ(Instruction* instr);
void PrintRvcImm8B(Instruction* instr);
void PrintAcquireRelease(Instruction* instr);
void PrintBranchOffset(Instruction* instr);
void PrintStoreOffset(Instruction* instr);
@ -118,6 +119,7 @@ class Decoder {
void DecodeCLType(Instruction* instr);
void DecodeCSType(Instruction* instr);
void DecodeCJType(Instruction* instr);
void DecodeCBType(Instruction* instr);
// Printing of instruction name.
void PrintInstructionName(Instruction* instr);
@ -326,6 +328,11 @@ void Decoder::PrintRvcImm11CJ(Instruction* instr) {
out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}
void Decoder::PrintRvcImm8B(Instruction* instr) {
int32_t imm = instr->RvcImm8BValue();
out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}
void Decoder::PrintAcquireRelease(Instruction* instr) {
bool aq = instr->AqValue();
bool rl = instr->RlValue();
@ -599,6 +606,10 @@ int Decoder::FormatRvcImm(Instruction* instr, const char* format) {
DCHECK(STRING_STARTS_WITH(format, "Cimm8Addi4spn"));
PrintRvcImm8Addi4spn(instr);
return 13;
} else if (format[5] == 'B') {
DCHECK(STRING_STARTS_WITH(format, "Cimm8B"));
PrintRvcImm8B(instr);
return 6;
}
UNREACHABLE();
} else if (format[4] == '1') {
@ -1752,6 +1763,29 @@ void Decoder::DecodeCJType(Instruction* instr) {
}
}
void Decoder::DecodeCBType(Instruction* instr) {
switch (instr->RvcOpcode()) {
case RO_C_BNEZ:
Format(instr, "bnez 'Crs1s, x0, 'Cimm8B");
break;
case RO_C_BEQZ:
Format(instr, "beqz 'Crs1s, x0, 'Cimm8B");
break;
case RO_C_MISC_ALU:
if (instr->RvcFunct2BValue() == 0b00)
Format(instr, "srli 'Crs1s, 'Crs1s, 'Cshamt");
else if (instr->RvcFunct2BValue() == 0b01)
Format(instr, "srai 'Crs1s, 'Crs1s, 'Cshamt");
else if (instr->RvcFunct2BValue() == 0b10)
Format(instr, "andi 'Crs1s, 'Crs1s, 'Cimm6");
else
UNSUPPORTED_RISCV();
break;
default:
UNSUPPORTED_RISCV();
}
}
// Disassemble the instruction at *instr_ptr into the output buffer.
// All instructions are one word long, except for the simulator
// pseudo-instruction stop(msg). For that one special case, we return
@ -1807,6 +1841,9 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
case Instruction::kCSType:
DecodeCSType(instr);
break;
case Instruction::kCBType:
DecodeCBType(instr);
break;
default:
Format(instr, "UNSUPPORTED");
UNSUPPORTED_RISCV();

View File

@ -3182,13 +3182,13 @@ void Simulator::DecodeCAType() {
set_rvc_rs1s(sext_xlen(rvc_rs1s() - rvc_rs2s()));
break;
case RO_C_XOR:
set_rvc_rs1s(sext_xlen(rvc_rs1s() ^ rvc_rs2s()));
set_rvc_rs1s(rvc_rs1s() ^ rvc_rs2s());
break;
case RO_C_OR:
set_rvc_rs1s(sext_xlen(rvc_rs1s() | rvc_rs2s()));
set_rvc_rs1s(rvc_rs1s() | rvc_rs2s());
break;
case RO_C_AND:
set_rvc_rs1s(sext_xlen(rvc_rs1s() & rvc_rs2s()));
set_rvc_rs1s(rvc_rs1s() & rvc_rs2s());
break;
case RO_C_SUBW:
set_rvc_rs1s(sext32(rvc_rs1s() - rvc_rs2s()));
@ -3347,6 +3347,37 @@ void Simulator::DecodeCJType() {
}
}
void Simulator::DecodeCBType() {
switch (instr_.RvcOpcode()) {
case RO_C_BNEZ:
if (rvc_rs1() != 0) {
int64_t next_pc = get_pc() + rvc_imm8_b();
set_pc(next_pc);
}
break;
case RO_C_BEQZ:
if (rvc_rs1() == 0) {
int64_t next_pc = get_pc() + rvc_imm8_b();
set_pc(next_pc);
}
break;
case RO_C_MISC_ALU:
if (instr_.RvcFunct2BValue() == 0b00) { // c.srli
set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6()));
} else if (instr_.RvcFunct2BValue() == 0b01) { // c.srai
require(rvc_shamt6() < xlen);
set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6()));
} else if (instr_.RvcFunct2BValue() == 0b10) { // c.andi
set_rvc_rs1s(rvc_imm6() & rvc_rs1s());
} else {
UNSUPPORTED();
}
break;
default:
UNSUPPORTED();
}
}
// Executes the current instruction.
void Simulator::InstructionDecode(Instruction* instr) {
if (v8::internal::FLAG_check_icache) {
@ -3399,6 +3430,9 @@ void Simulator::InstructionDecode(Instruction* instr) {
case Instruction::kCJType:
DecodeCJType();
break;
case Instruction::kCBType:
DecodeCBType();
break;
case Instruction::kCIType:
DecodeCIType();
break;

View File

@ -500,6 +500,7 @@ class Simulator : public SimulatorBase {
inline int16_t rvc_imm6_sdsp() const { return instr_.RvcImm6SdspValue(); }
inline int16_t rvc_imm5_w() const { return instr_.RvcImm5WValue(); }
inline int16_t rvc_imm5_d() const { return instr_.RvcImm5DValue(); }
inline int16_t rvc_imm8_b() const { return instr_.RvcImm8BValue(); }
inline void set_rd(int64_t value, bool trace = true) {
set_register(rd_reg(), value);
@ -627,6 +628,7 @@ class Simulator : public SimulatorBase {
void DecodeCLType();
void DecodeCSType();
void DecodeCJType();
void DecodeCBType();
// Used for breakpoints and traps.
void SoftwareInterrupt();

View File

@ -1503,6 +1503,100 @@ TEST(RVC_JUMP) {
CHECK_EQ(expected_res, res);
}
TEST(RVC_CB) {
// Test RV64C extension CI type instructions.
FLAG_riscv_c_extension = true;
CcTest::InitializeVM();
// Test c.srai
{
auto fn = [](MacroAssembler& assm) { __ c_srai(a0, 13); };
auto res = GenAndRunTest<int64_t>(0x1234'5678ULL, fn);
CHECK_EQ(0x1234'5678ULL >> 13, res);
}
// Test c.srli
{
auto fn = [](MacroAssembler& assm) { __ c_srli(a0, 13); };
auto res = GenAndRunTest<int64_t>(0x1234'5678ULL, fn);
CHECK_EQ(0x1234'5678ULL >> 13, res);
}
// Test c.andi
{
auto fn = [](MacroAssembler& assm) { __ c_andi(a0, 13); };
auto res = GenAndRunTest<int64_t>(LARGE_INT_EXCEED_32_BIT, fn);
CHECK_EQ(LARGE_INT_EXCEED_32_BIT & 13, res);
}
}
TEST(RVC_CB_BRANCH) {
FLAG_riscv_c_extension = true;
// Test floating point compare and
// branch instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
struct T {
double a;
double b;
double c;
double d;
double e;
double f;
int32_t result;
} t;
// Create a function that accepts &t,
// and loads, manipulates, and stores
// the doubles t.a ... t.f.
Label neither_is_nan, less_than, outa_here;
auto fn = [&neither_is_nan, &less_than, &outa_here](MacroAssembler& assm) {
__ fld(ft0, a0, offsetof(T, a));
__ fld(ft1, a0, offsetof(T, b));
__ fclass_d(t5, ft0);
__ fclass_d(t6, ft1);
__ or_(a1, t5, t6);
__ andi(a1, a1, kSignalingNaN | kQuietNaN);
__ c_beqz(a1, &neither_is_nan);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&neither_is_nan);
__ flt_d(a1, ft1, ft0);
__ c_bnez(a1, &less_than);
__ sw(zero_reg, a0, offsetof(T, result));
__ j(&outa_here);
__ bind(&less_than);
__ RV_li(a4, 1);
__ sw(a4, a0, offsetof(T, result)); // Set true.
// This test-case should have additional
// tests.
__ bind(&outa_here);
};
auto f = AssembleCode<F3>(fn);
t.a = 1.5e14;
t.b = 2.75e11;
t.c = 2.0;
t.d = -4.0;
t.e = 0.0;
t.f = 0.0;
t.result = 0;
f.Call(&t, 0, 0, 0, 0);
CHECK_EQ(1.5e14, t.a);
CHECK_EQ(2.75e11, t.b);
CHECK_EQ(1, t.result);
}
TEST(TARGET_ADDR) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();

View File

@ -505,6 +505,13 @@ TEST(RV64C) {
COMPARE(c_fsd(fa1, s1, 24), "0000ac8c fsd fa1, 24(s1)");
COMPARE(c_j(-12), "0000bfd5 j -12");
COMPARE(c_beqz(a0, -12), "0000d975 beqz a0, x0, -12");
COMPARE(c_bnez(a0, -12), "0000f975 bnez a0, x0, -12");
COMPARE(c_srli(a1, 24), "000081e1 srli a1, a1, 24");
COMPARE(c_andi(a1, 24), "000089e1 andi a1, a1, 24");
COMPARE(c_srai(a1, 24), "000085e1 srai a1, a1, 24");
VERIFY_RUN();
}