Merge pull request #7 from akihikodaki:template PiperOrigin-RevId: 718087600 Change-Id: I935e8de167bb5118acffc0b5818b80b595dbe66a
diff --git a/repos.bzl b/repos.bzl index 0ec585f..b413290 100644 --- a/repos.bzl +++ b/repos.bzl
@@ -22,7 +22,7 @@ if not native.existing_rule("com_google_mpact-sim"): http_archive( name = "com_google_mpact-sim", - sha256 = "99d29bebd3ebccdec699ae3448a03f1245778e3f7a9abae9dcc9770aa51da287", - strip_prefix = "mpact-sim-cd146315f0214f1ecaeadeaecadb12c6f9557cb6", - url = "https://github.com/google/mpact-sim/archive/cd146315f0214f1ecaeadeaecadb12c6f9557cb6.tar.gz", + sha256 = "10bd6f43f6d340535f0eef15bb946c8332e62148458a2dc3b6a412739b4cf253", + strip_prefix = "mpact-sim-d958141f3e6f8dfbdc79e35a9beabaea490e479c", + url = "https://github.com/google/mpact-sim/archive/d958141f3e6f8dfbdc79e35a9beabaea490e479c.tar.gz", )
diff --git a/riscv/BUILD b/riscv/BUILD index fd00819..65faa21 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -268,6 +268,7 @@ "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", "@com_google_mpact-sim//mpact/sim/generic:arch_state", "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", @@ -307,6 +308,30 @@ ], ) +# TODO(julianmb): Remove this target once there is a rva23_instructions target. +cc_library( + name = "riscv_vector_basic_bit_manipulation_instructions", + srcs = [ + "riscv_vector_basic_bit_manipulation_instructions.cc", + ], + hdrs = [ + "riscv_vector_basic_bit_manipulation_instructions.h", + "riscv_vector_instruction_helpers.h", + ], + copts = [ + "-O3", + "-ffp-model=strict", + ], + deps = [ + ":riscv_state", + "@com_google_absl//absl/log", + "@com_google_absl//absl/numeric:bits", + "@com_google_absl//absl/types:span", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + ], +) + mpact_isa_decoder( name = "riscv32g_isa", src = "riscv32g.isa", @@ -605,6 +630,35 @@ ], ) +mpact_isa_decoder( + name = "zvbb_isa", + src = "riscv_zvbb.isa", + includes = [ + "riscv_vector.isa", + ], + isa_name = "ZVBB", + prefix = "zvbb", + deps = [ + ":riscv_v", + ":riscv_vector_basic_bit_manipulation_instructions", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "zvbb_bin_fmt", + src = "riscv_zvbb.bin_fmt", + decoder_name = "ZVBB", + includes = [ + "riscv32g.bin_fmt", + "riscv_vector.bin_fmt", + ], + prefix = "zvbb", + deps = [ + ":zvbb_isa", + ], +) + cc_library( name = "riscv32g_decoder", srcs = [ @@ -643,6 +697,7 @@ "riscv_getters_zba.h", "riscv_getters_zbb32.h", "riscv_getters_zbb64.h", + "riscv_getters_zvbb.h", ], deps = [ ":riscv_encoding_common", @@ -807,6 +862,56 @@ ) cc_library( + name = "riscv64g_encoder", + srcs = [ + "riscv64g_bin_encoder_interface.cc", + "riscv_bin_setters.cc", + ], + hdrs = [ + "riscv64g_bin_encoder_interface.h", + "riscv_bin_setters.h", + ], + deps = [ + ":riscv64g_bin_fmt", + ":riscv64g_isa", + ":riscv_getters", + ":riscv_state", + "@com_google_absl//absl/base:no_destructor", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/asm", + "@com_googlesource_code_re2//:re2", + ], +) + +cc_binary( + name = "riscv64g_as", + srcs = ["riscv64g_as_main.cc"], + copts = ["-O3"], + deps = [ + ":riscv64g_bin_fmt", + ":riscv64g_encoder", + ":riscv64g_isa", + "@com_github_serge1_elfio//:elfio", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/flags:parse", + "@com_google_absl//absl/log", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/asm", + "@com_google_mpact-sim//mpact/sim/util/asm:simple_assembler", + "@com_googlesource_code_re2//:re2", + ], +) + +cc_library( name = "riscv64g_vec_decoder", srcs = [ "riscv64g_vec_decoder.cc", @@ -871,6 +976,41 @@ ) cc_library( + name = "zvbb_decoder", + srcs = [ + "zvbb_decoder.cc", + "zvbb_encoding.cc", + ], + hdrs = [ + "zvbb_decoder.h", + "zvbb_encoding.h", + ], + copts = ["-O3"], + deps = [ + ":riscv_encoding_common", + ":riscv_getters", + ":riscv_state", + ":riscv_vector_basic_bit_manipulation_instructions", + ":zvbb_bin_fmt", + ":zvbb_isa", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/log", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + "@com_google_mpact-sim//mpact/sim/generic:arch_state", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:program_error", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + +cc_library( name = "riscv_top", srcs = [ "riscv_top.cc", @@ -1208,6 +1348,27 @@ ], ) +cc_library( + name = "riscv_plic", + srcs = [ + "riscv_plic.cc", + ], + hdrs = [ + "riscv_plic.h", + ], + deps = [ + "@com_google_absl//absl/container:btree", + "@com_google_absl//absl/log", + "@com_google_absl//absl/numeric:bits", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/util/memory", + "@com_googlesource_code_re2//:re2", + ], +) + cc_binary( name = "rv32g_test_sim", srcs = [
diff --git a/riscv/riscv64g.isa b/riscv/riscv64g.isa index 7fcbc7f..467e57c 100644 --- a/riscv/riscv64g.isa +++ b/riscv/riscv64g.isa
@@ -43,7 +43,7 @@ slot riscv64g : riscv64i, riscv64c, riscv64m, riscv64_amo_arithmetic, riscv64f, riscv64d, zicsr, zfencei, privileged { default size = 4; default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", + disasm: "Illegal instruction at %(@:08x)", semfunc: "&RiscVIllegalInstruction"; } @@ -57,11 +57,11 @@ resources TwoOp = { next_pc, rs1 : rd[..rd]}; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { - addi{: rs1, I_imm12 : rd}, + addi{: rs1, %reloc(I_imm12) : rd}, resources: TwoOp, disasm: "addi", "%rd, %rs1, %I_imm12", semfunc: "&RV64::RiscVIAdd"; - addiw{: rs1, I_imm12 : rd}, + addiw{: rs1, %reloc(I_imm12) : rd}, resources: TwoOp, disasm: "addiw", "%rd, %rs1, %I_imm12", semfunc: "&RV64::RiscVIAddw"; @@ -87,42 +87,42 @@ semfunc: "&RV64::RiscVIXor"; slli{: rs1, I_uimm6 : rd}, resources: TwoOp, - disasm: "slli", "%rd, %rs1, 0x%(I_uimm6:x)", + disasm: "slli", "%rd, %rs1, %(I_uimm6:x)", semfunc: "&RV64::RiscVISll"; srli{: rs1, I_uimm6 : rd}, resources: TwoOp, - disasm: "srli", "%rd %rs1, 0x%(I_uimm6:x)", + disasm: "srli", "%rd %rs1, %(I_uimm6:x)", semfunc: "&RV64::RiscVISrl"; srai{: rs1, I_uimm6 : rd}, resources: TwoOp, - disasm: "srai", "%rd, %rs1, 0x%(I_uimm6:x)", + disasm: "srai", "%rd, %rs1, %(I_uimm6:x)", semfunc: "&RV64::RiscVISra"; slliw{: rs1, I_uimm5 : rd}, resources: TwoOp, - disasm: "slliw", "%rd, %rs1, 0x%(I_uimm5:x)", + disasm: "slliw", "%rd, %rs1, %(I_uimm5:x)", semfunc: "&RV64::RiscVISllw"; srliw{: rs1, I_uimm5 : rd}, resources: TwoOp, - disasm: "srliw", "%rd %rs1, 0x%(I_uimm5:x)", + disasm: "srliw", "%rd %rs1, %(I_uimm5:x)", semfunc: "&RV64::RiscVISrlw"; sraiw{: rs1, I_uimm5 : rd}, resources: TwoOp, - disasm: "sraiw", "%rd, %rs1, 0x%(I_uimm5:x)", + disasm: "sraiw", "%rd, %rs1, %(I_uimm5:x)", semfunc: "&RV64::RiscVISraw"; - lui{: U_imm20 : rd}, + lui{: %reloc(U_imm20) : rd}, resources: { next_pc : rd[0..]}, - disasm: "lui", "%rd, 0x%(U_imm20:08x)", + disasm: "lui", "%rd, %(U_imm20:08x)", semfunc: "&RV64::RiscVILui"; - auipc{: U_imm20 : rd}, + auipc{: %reloc(U_imm20) : rd}, resources: { next_pc : rd[0..]}, - disasm: "auipc", "%rd, 0x%(U_imm20:08x)", + disasm: "auipc", "%rd, %(U_imm20:08x)", semfunc: "&RV64::RiscVIAuipc"; add{: rs1, rs2 : rd}, resources: ThreeOp, disasm: "add", "%rd, %rs1, %rs2", semfunc: "&RV64::RiscVIAdd"; addw{: rs1, rs2 : rd}, - disasm: "add", "%rd, %rs1, %rs2", + disasm: "addw", "%rd, %rs1, %rs2", semfunc: "&RV64::RiscVIAddw"; slt{: rs1, rs2 : rd}, resources: ThreeOp, @@ -170,7 +170,7 @@ semfunc: "&RV64::RiscVISrlw"; subw{: rs1, rs2 : rd}, resources: ThreeOp, - disasm: "sub", "%rd, %rs1, %rs2", + disasm: "subw", "%rd, %rs1, %rs2", semfunc: "&RV64::RiscVISubw"; sraw{: rs1, rs2 : rd}, resources: ThreeOp, @@ -182,87 +182,87 @@ hint{}, disasm: "hint", semfunc: "&RiscVINop"; - jal{: J_imm20 : next_pc, rd}, + jal{: %reloc(J_imm20) : next_pc, rd}, resources: { next_pc : next_pc[0..], rd[0..]}, disasm: "jal", "%rd, %(@+J_imm20:08x)", semfunc: "&RV64::RiscVIJal"; - jalr{: rs1, J_imm12 : next_pc, rd}, + jalr{: rs1, %reloc(J_imm12) : next_pc, rd}, resources: { next_pc, rs1 : next_pc[0..], rd[0..]}, disasm: "jalr", "%rd, %rs1, %J_imm12", semfunc: "&RV64::RiscVIJalr"; - j{: J_imm20 : next_pc, rd}, + j{: %reloc(J_imm20) : next_pc, rd}, resources: { next_pc : next_pc[0..], rd[0..]}, disasm: "j", "%(@+J_imm20:08x)", semfunc: "&RV64::RiscVIJal"; - jr{: rs1, J_imm12 : next_pc, rd}, + jr{: rs1, %reloc(J_imm12) : next_pc, rd}, resources: { next_pc, rs1 : next_pc[0..], rd[0..]}, - disasm: "jr", "%rs1, %J_imm12", + disasm: "jr", "%rs1, %(J_imm12:08x)", semfunc: "&RV64::RiscVIJalr"; - beq{: rs1, rs2, B_imm12 : next_pc}, + beq{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "beq", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBeq"; - bne{: rs1, rs2, B_imm12 : next_pc}, + bne{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "bne", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBne"; - blt{: rs1, rs2, B_imm12 : next_pc}, + blt{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "blt", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBlt"; - bltu{: rs1, rs2, B_imm12 : next_pc}, + bltu{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "bltu", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBltu"; - bge{: rs1, rs2, B_imm12 : next_pc}, + bge{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "bge", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBge"; - bgeu{: rs1, rs2, B_imm12 : next_pc}, + bgeu{: rs1, rs2, %reloc(B_imm12) : next_pc}, resources: { next_pc, rs1, rs2 : next_pc[0..]}, disasm: "bgeu", "%rs1, %rs2, %(@+B_imm12:08x)", semfunc: "&RV64::RiscVIBgeu"; - ld{(: rs1, I_imm12), (: : rd)}, + ld{(: rs1, %reloc(I_imm12)), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "ld", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILd", "&RV64::RiscVILdChild"; - lw{(: rs1, I_imm12), (: : rd)}, + lw{(: rs1, %reloc(I_imm12)), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lw", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILw", "&RV64::RiscVILwChild"; - lwu{(: rs1, I_imm12), (: : rd)}, + lwu{(: rs1, %reloc(I_imm12)), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lwu", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILw", "&RV64::RiscVILwuChild"; - lh{(: rs1, I_imm12 :), (: : rd)}, + lh{(: rs1, %reloc(I_imm12) :), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lh", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILh", "&RV64::RiscVILhChild"; - lhu{(: rs1, I_imm12 :), (: : rd)}, + lhu{(: rs1, %reloc(I_imm12) :), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lhu", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILhu", "&RV64::RiscVILhuChild"; - lb{(: rs1, I_imm12 :), (: : rd)}, + lb{(: rs1, %reloc(I_imm12) :), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lb", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILb", "&RV64::RiscVILbChild"; - lbu{(: rs1, I_imm12 :), (: : rd)}, + lbu{(: rs1, %reloc(I_imm12) :), (: : rd)}, resources: { next_pc, rs1 : rd[0..]}, disasm: "lbu", "%rd, %I_imm12(%rs1)", semfunc: "&RV64::RiscVILbu", "&RV64::RiscVILbuChild"; - sd{: rs1, S_imm12, rs2 : }, + sd{: rs1, %reloc(S_imm12), rs2 : }, resources: { next_pc, rs1, rs2 : }, disasm: "sd", "%rs2, %S_imm12(%rs1)", semfunc: "&RV64::RiscVISd"; - sw{: rs1, S_imm12, rs2 : }, + sw{: rs1, %reloc(S_imm12), rs2 : }, resources: { next_pc, rs1, rs2 : }, disasm: "sw", "%rs2, %S_imm12(%rs1)", semfunc: "&RV64::RiscVISw"; - sh{: rs1, S_imm12, rs2 : }, + sh{: rs1, %reloc(S_imm12), rs2 : }, resources: { next_pc, rs1, rs2 : }, disasm: "sh", "%rs2, %S_imm12(%rs1)", semfunc: "&RV64::RiscVISh"; - sb{: rs1, S_imm12, rs2 : }, + sb{: rs1, %reloc(S_imm12), rs2 : }, resources: { next_pc, rs1, rs2 : }, disasm: "sb", "%rs2, %S_imm12(%rs1)", semfunc: "&RV64::RiscVISb"; @@ -442,11 +442,11 @@ opcodes { amoswapw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoswap.w", "%rd, rs2, (%rs1)", + disasm: "amoswap.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmoswapw", "&RV64::RiscVILwChild"; amoswapd{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoswap.d", "%rd, rs2, (%rs1)", + disasm: "amoswap.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmoswapd", "&RV64::RiscVILdChild"; } } @@ -460,27 +460,27 @@ opcodes { amoandw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoand.w", "%rd, rs2, (%rs1)", + disasm: "amoand.w%A_aq%A_rl", "%rd, %rs2, (%rs1)", semfunc: "&AAmoandw", "&RV64::RiscVILwChild"; amoandd{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoand.d", "%rd, rs2, (%rs1)", + disasm: "amoand.d%A_aq%A_rl", "%rd, %rs2, (%rs1)", semfunc: "&AAmoandd", "&RV64::RiscVILdChild"; amoorw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoor.w", "%rd, rs2, (%rs1)", + disasm: "amoor.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmoorw", "&RV64::RiscVILwChild"; amoord{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoor.d", "%rd, rs2, (%rs1)", + disasm: "amoor.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmoord", "&RV64::RiscVILdChild"; amoxorw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoxor.w", "%rd, rs2, (%rs1)", + disasm: "amoxor.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmoxorw", "&RV64::RiscVILwChild"; amoxord{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoxor.d", "%rd, rs2, (%rs1)", + disasm: "amoxor.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmoxord", "&RV64::RiscVILdChild"; } } @@ -494,43 +494,43 @@ opcodes { amoaddw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoadd.w", "%rd, rs2, (%rs1)", + disasm: "amoadd.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmoaddw", "&RV64::RiscVILwChild"; amoaddd{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amoadd.d", "%rd, rs2, (%rs1)", + disasm: "amoadd.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmoaddd", "&RV64::RiscVILdChild"; amomaxw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomax.w", "%rd, rs2, (%rs1)", + disasm: "amomax.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmomaxw", "&RV64::RiscVILwChild"; amomaxd{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomax.d", "%rd, rs2, (%rs1)", + disasm: "amomax.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmomaxd", "&RV64::RiscVILdChild"; amomaxuw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomaxu.w", "%rd, rs2, (%rs1)", + disasm: "amomaxu.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmomaxuw", "&RV64::RiscVILwChild"; amomaxud{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomaxu.d", "%rd, rs2, (%rs1)", + disasm: "amomaxu.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmomaxud", "&RV64::RiscVILdChild"; amominw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomin.w", "%rd, rs2, (%rs1)", + disasm: "amomin.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmominw", "&RV64::RiscVILwChild"; amomind{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amomin.d", "%rd, rs2, (%rs1)", + disasm: "amomin.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmomind", "&RV64::RiscVILdChild"; amominuw{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amominu.w", "%rd, rs2, (%rs1)", + disasm: "amominu.w", "%rd, %rs2, (%rs1)", semfunc: "&AAmominuw", "&RV64::RiscVILwChild"; amominud{(: rs1, rs2, A_aq, A_rl), (: : rd)}, resources: ThreeOp, - disasm: "amominu.d", "%rd, rs2, (%rs1)", + disasm: "amominu.d", "%rd, %rs2, (%rs1)", semfunc: "&AAmominud", "&RV64::RiscVILdChild"; } } @@ -621,11 +621,11 @@ resources ThreeOp = { next_pc, frs1, frs2 : frd[0..]}; resources FourOp = { next_pc, frs1, frs2, frs3 : frd[0..]}; opcodes { - flw{(: rs1, I_imm12 : ), (: : frd)}, + flw{(: rs1, %reloc(I_imm12) : ), (: : frd)}, resources: { next_pc, rs1 : frd[0..]}, semfunc: "&RV64::RiscVILw", "&RiscVIFlwChild", disasm: "flw", "%frd, %I_imm12(%rs1)"; - fsw{: rs1, S_imm12, frs2}, + fsw{: rs1, %reloc(S_imm12), frs2}, resources: { next_pc, rs1, frs2}, semfunc: "&RV64::RiscVFSw", disasm: "fsw", "%frs2, %S_imm12(%rs1)"; @@ -752,11 +752,11 @@ default size = 4; default latency = global_latency; opcodes { - fld{(: rs1, I_imm12 : ), (: : drd)}, + fld{(: rs1, %reloc(I_imm12) : ), (: : drd)}, resources: {next_pc, rs1 : drd[0..]}, semfunc: "&RV64::RiscVILd", "&RV64::RiscVILdChild", disasm: "fld", "%drd, %I_imm12(%rs1)"; - fsd{: rs1, S_imm12, drs2}, + fsd{: rs1, %reloc(S_imm12), drs2}, resources: {next_pc, rs1, drs2}, semfunc: "&RV64::RiscVDSd", disasm: "fsd", "%drs2, %S_imm12(%rs1)"; @@ -936,7 +936,7 @@ resources: {next_pc,c3rs1, c3drs2}, disasm: "c.fsd", "%c3drs2, %I_cl_uimm5x8(%c3rs1)", semfunc: "&RV64::RiscVISd"; - cj{: I_cj_imm11, x0 : next_pc, x0}, + cj{: %reloc(I_cj_imm11), x0 : next_pc, x0}, resources: {next_pc,x0 : next_pc[0..], x0[0..]}, disasm: "c.j", "%(@+I_cj_imm11:08x)", semfunc: "&RV64::RiscVIJal"; @@ -948,11 +948,11 @@ resources: {next_pc,crs1, x0 : next_pc[0..], x1[0..]}, disasm: "c.jalr", "%crs1", semfunc: "&RV64::RiscVIJalr"; - cbeqz{: c3rs1, x0, I_cb_imm8 : next_pc}, + cbeqz{: c3rs1, x0, %reloc(I_cb_imm8) : next_pc}, resources: {next_pc,c3rs1, x0 : next_pc[0..]}, disasm: "c.beqz", "%c3rs1, %(@+I_cb_imm8:08x)", semfunc: "&RV64::RiscVIBeq"; - cbnez{: c3rs1, x0, I_cb_imm8 : next_pc}, + cbnez{: c3rs1, x0, %reloc(I_cb_imm8) : next_pc}, resources: {next_pc,c3rs1, x0 : next_pc[0..]}, disasm: "c.bnez", "%c3rs1, %(@+I_cb_imm8:08x)", semfunc: "&RV64::RiscVIBne"; @@ -962,7 +962,7 @@ semfunc: "&RV64::RiscVIAdd"; clui{: I_ci_imm6_12 : rd}, resources: {next_pc : rd[0..]}, - disasm: "c.lui", "%rd, 0x%(I_ci_imm6_12:x)", + disasm: "c.lui", "%rd, %(I_ci_imm6_12:x)", semfunc: "&RV64::RiscVILui"; caddi{: rd, I_ci_imm6 : rd}, resources: {next_pc, rd : rd[0..]}, @@ -982,15 +982,15 @@ semfunc: "&RV64::RiscVIAdd"; cslli{: rd, I_ci_uimm6 : rd}, resources: {next_pc, rd : rd[0..]}, - disasm: "c.slli", "%rd, %rd, 0x%(I_ci_uimm6:x)", + disasm: "c.slli", "%rd, %rd, %(I_ci_uimm6:x)", semfunc: "&RV64::RiscVISll"; csrli{: c3rs1, I_ci_uimm6 : c3rs1}, resources: {next_pc, c3rs1 : c3rs1[0..]}, - disasm: "c.srli", "%c3rs1, %c3rs1, 0x%(I_ci_uimm6:x)", + disasm: "c.srli", "%c3rs1, %c3rs1, %(I_ci_uimm6:x)", semfunc: "&RV64::RiscVISrl"; csrai{: c3rs1, I_ci_uimm6 : c3rs1}, resources: {next_pc, c3rs1 : c3rs1[0..]}, - disasm: "c.srai", "%c3rs1, %c3rs1, 0x%(I_ci_uimm6:x)", + disasm: "c.srai", "%c3rs1, %c3rs1, %(I_ci_uimm6:x)", semfunc: "&RV64::RiscVISra"; candi{: c3rs1, I_ci_imm6 : c3rs1}, resources: {next_pc, c3rs1 : c3rs1[0..]},
diff --git a/riscv/riscv64g_as_main.cc b/riscv/riscv64g_as_main.cc new file mode 100644 index 0000000..3893308 --- /dev/null +++ b/riscv/riscv64g_as_main.cc
@@ -0,0 +1,205 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstdint> +#include <fstream> +#include <iostream> +#include <optional> +#include <string> +#include <vector> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/log/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "elfio/elf_types.hpp" +#include "elfio/elfio.hpp" +#include "elfio/elfio_dump.hpp" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/asm/opcode_assembler_interface.h" +#include "mpact/sim/util/asm/resolver_interface.h" +#include "mpact/sim/util/asm/simple_assembler.h" +#include "re2/re2.h" +#include "riscv/riscv64g_bin_encoder_interface.h" +#include "riscv/riscv64g_encoder.h" + +using ::mpact::sim::riscv::isa64::RiscV64GBinEncoderInterface; +using ::mpact::sim::riscv::isa64::Riscv64gSlotMatcher; +using ::mpact::sim::util::assembler::SimpleAssembler; + +// This file implements the main function for the generated assembler for +// RiscV64G. + +namespace { + +using ::mpact::sim::generic::operator*; // NOLINT(misc-unused-using-decls) +using ::mpact::sim::util::assembler::OpcodeAssemblerInterface; +using ::mpact::sim::util::assembler::RelocationInfo; +using ::mpact::sim::util::assembler::ResolverInterface; +using AddSymbolCallback = + ::mpact::sim::util::assembler::OpcodeAssemblerInterface::AddSymbolCallback; + +// This class implements the byte oriented OpcodeAssemblerInterface, converting +// from the bit interface provided by the SlotMatcher interface. Since there is +// only one slot in the RiscV64G ISA, only one slot matcher is needed. +class RiscV64GAssembler : public OpcodeAssemblerInterface { + public: + RiscV64GAssembler(Riscv64gSlotMatcher* matcher) + : label_re_("^(\\S+)\\s*:"), matcher_(matcher) {}; + ~RiscV64GAssembler() override = default; + absl::Status Encode(uint64_t address, absl::string_view text, + AddSymbolCallback add_symbol_callback, + ResolverInterface* resolver, std::vector<uint8_t>& bytes, + std::vector<RelocationInfo>& relocations) override { + // First check to see if there is a label, if so, add it to the symbol table + // with the current address. + std::string label; + if (RE2::Consume(&text, label_re_, &label)) { + auto status = add_symbol_callback(label, address, 0, STT_NOTYPE, 0, 0); + if (!status.ok()) return status; + } + auto res = matcher_->Encode(address, text, 0, resolver, relocations); + if (!res.status().ok()) return res.status(); + auto [value, size] = res.value(); + union { + uint64_t i; + uint8_t b[sizeof(uint64_t)]; + } u; + u.i = value; + for (int i = 0; i < size / 8; ++i) { + bytes.push_back(u.b[i]); + } + return absl::OkStatus(); + } + + private: + RE2 label_re_; + Riscv64gSlotMatcher* matcher_; +}; + +} // namespace + +// This flag dumps the info for the generated ELF file. +ABSL_FLAG(bool, dump_elf, false, "Dump the ELF file"); +// Produce a relocatable file as opposed to an executable. +ABSL_FLAG(bool, c, false, "Produce a relocatable file"); +// Specify the output file name. +ABSL_FLAG(std::optional<std::string>, o, std::nullopt, "Output file name"); + +// Supported RiscV ELF flags (TSO memory ordering and compact encodings). +enum class RiscVElfFlags { + kNone = 0, + kRiscvTso = 0x0001, + kRiscvRvc = 0x0010, +}; + +int main(int argc, char* argv[]) { + auto arg_vec = absl::ParseCommandLine(argc, argv); + + if (arg_vec.size() > 2) { + std::cout << "Too many arguments\n"; + return 1; + } + + std::istream* is; + + if (arg_vec.size() == 1) { + is = &std::cin; + } else { + is = new std::fstream(arg_vec[1], std::fstream::in); + } + + RiscV64GBinEncoderInterface bin_encoder_interface; + Riscv64gSlotMatcher matcher(&bin_encoder_interface); + RiscV64GAssembler riscv_64g_assembler(&matcher); + CHECK_OK(matcher.Initialize()); + // Instantiate the assembler. + SimpleAssembler assembler("#", ELFCLASS64, &riscv_64g_assembler); + // Set up the abi and the machine type. + assembler.writer().set_os_abi(ELFOSABI_LINUX); + assembler.writer().set_machine(EM_RISCV); + // Set the appropriate ELF header flags. + assembler.writer().set_flags(*RiscVElfFlags::kRiscvTso | + *RiscVElfFlags::kRiscvRvc); + // Perform the first pass of parsing the assembly code. + auto status = assembler.Parse(*is); + if (!status.ok()) { + std::cout << "Failed to parse assembly: " << status.message() << "\n"; + return 1; + } + // Perform the second pass of parsing the assembly code. This pass will either + // generate an executable or a relocatable file. + std::string output_file_name; + if (absl::GetFlag(FLAGS_c)) { + status = assembler.CreateRelocatable(); + if (arg_vec.size() == 1) { + output_file_name = "stdin.o"; + } else { + std::string input_file_name = arg_vec[1]; + auto dot_pos = input_file_name.find_last_of('.'); + if (dot_pos == std::string::npos) { + output_file_name = absl::StrCat(input_file_name, ".o"); + } else { + output_file_name = + absl::StrCat(input_file_name.substr(0, dot_pos), ".o"); + } + } + } else { + status = assembler.CreateExecutable(0x1000, "main"); + output_file_name = "a.out"; + } + if (!status.ok()) { + std::cout << "Assembly failure: " << status.message() << "\n"; + return 1; + } + // Write out the output file. + if (absl::GetFlag(FLAGS_o).has_value()) { + output_file_name = absl::GetFlag(FLAGS_o).value(); + } + std::ofstream output_file(output_file_name); + if (!output_file.is_open()) { + std::cout << "Failed to open output file: " << output_file_name << "\n"; + return 1; + } + status = assembler.Write(output_file); + if (!status.ok()) { + std::cout << "Failed to write output file: " << status.message() << "\n"; + return 1; + } + output_file.close(); + if (is != &std::cin) delete is; + + // Dump the ELF info if requested. + if (absl::GetFlag(FLAGS_dump_elf)) { + ELFIO::elfio reader; + if (!reader.load(output_file_name)) { + std::cout << "Failed to load output file: " << output_file_name << "\n"; + return 1; + } + + ELFIO::dump::header(std::cout, reader); + ELFIO::dump::section_headers(std::cout, reader); + ELFIO::dump::segment_headers(std::cout, reader); + ELFIO::dump::symbol_tables(std::cout, reader); + ELFIO::dump::notes(std::cout, reader); + ELFIO::dump::modinfo(std::cout, reader); + ELFIO::dump::dynamic_tags(std::cout, reader); + ELFIO::dump::section_datas(std::cout, reader); + ELFIO::dump::segment_datas(std::cout, reader); + } + return 0; +}
diff --git a/riscv/riscv64g_bin_encoder_interface.cc b/riscv/riscv64g_bin_encoder_interface.cc new file mode 100644 index 0000000..7e02c18 --- /dev/null +++ b/riscv/riscv64g_bin_encoder_interface.cc
@@ -0,0 +1,135 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv64g_bin_encoder_interface.h" + +#include <cstdint> +#include <tuple> +#include <vector> + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/asm/resolver_interface.h" +#include "riscv/riscv64g_bin_encoder.h" +#include "riscv/riscv64g_encoder.h" +#include "riscv/riscv64g_enums.h" +#include "riscv/riscv_bin_setters.h" + +namespace mpact { +namespace sim { +namespace riscv { +namespace isa64 { + +using ::mpact::sim::generic::operator*; // NOLINT(misc-unused-using-decls) +using ::mpact::sim::util::assembler::ResolverInterface; + +RiscV64GBinEncoderInterface::RiscV64GBinEncoderInterface() { + AddRiscvSourceOpBinSetters<SourceOpEnum, OpMap, encoding64::Encoder>( + source_op_map_); + AddRiscvDestOpBinSetters<DestOpEnum, OpMap, encoding64::Encoder>( + dest_op_map_); + AddRiscvSourceOpRelocationSetters<OpcodeEnum, SourceOpEnum, RelocationMap>( + relocation_source_op_map_); +} + +absl::StatusOr<std::tuple<uint64_t, int>> +RiscV64GBinEncoderInterface::GetOpcodeEncoding(SlotEnum slot, int entry, + OpcodeEnum opcode, + ResolverInterface *resolver) { + return encoding64::kOpcodeEncodings->at(opcode); +} + +absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetSrcOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, SourceOpEnum source_op, int source_num, + ResolverInterface *resolver) { + auto iter = source_op_map_.find(*source_op); + if (iter == source_op_map_.end()) { + return absl::NotFoundError(absl::StrCat( + "Source operand not found for op enum value ", *source_op)); + } + return iter->second(address, text, resolver); +} + +absl::Status RiscV64GBinEncoderInterface::AppendSrcOpRelocation( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, SourceOpEnum source_op, int source_num, + ResolverInterface *resolver, std::vector<RelocationInfo> &relocations) { + auto iter = relocation_source_op_map_.find(std::tie(opcode, source_op)); + if (iter == relocation_source_op_map_.end()) return absl::OkStatus(); + return iter->second(address, text, resolver, relocations); +} + +absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetDestOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, DestOpEnum dest_op, int dest_num, + ResolverInterface *resolver) { + auto iter = dest_op_map_.find(*dest_op); + if (iter == dest_op_map_.end()) { + return absl::NotFoundError( + absl::StrCat("Dest operand not found for op enum value ", *dest_op)); + } + return iter->second(address, text, resolver); +} + +absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetListDestOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, ListDestOpEnum dest_op, int dest_num, + ResolverInterface *resolver) { + auto iter = list_dest_op_map_.find(*dest_op); + if (iter == list_dest_op_map_.end()) { + return absl::NotFoundError(absl::StrCat( + "List dest operand not found for op enum value ", *dest_op)); + } + return iter->second(address, text, resolver); +} + +absl::Status RiscV64GBinEncoderInterface::AppendDestOpRelocation( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, DestOpEnum dest_op, int dest_num, + ResolverInterface *resolver, std::vector<RelocationInfo> &relocations) { + // There are no destination operands that require relocation. + return absl::OkStatus(); +} + +absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetListSrcOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, ListSourceOpEnum source_op, int source_num, + ResolverInterface *resolver) { + auto iter = list_source_op_map_.find(*source_op); + if (iter == list_source_op_map_.end()) { + return absl::NotFoundError(absl::StrCat( + "List source operand not found for op enum value ", *source_op)); + } + return iter->second(address, text, resolver); +} + +absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetPredOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, PredOpEnum pred_op, ResolverInterface *resolver) { + auto iter = pred_op_map_.find(*pred_op); + if (iter == pred_op_map_.end()) { + return absl::NotFoundError(absl::StrCat( + "Predicate operand not found for op enum value ", *pred_op)); + } + return iter->second(address, text, resolver); +} + +} // namespace isa64 +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv64g_bin_encoder_interface.h b/riscv/riscv64g_bin_encoder_interface.h new file mode 100644 index 0000000..f25fe1d --- /dev/null +++ b/riscv/riscv64g_bin_encoder_interface.h
@@ -0,0 +1,110 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_ + +#include <cstdint> +#include <functional> +#include <tuple> +#include <vector> + +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "mpact/sim/util/asm/opcode_assembler_interface.h" +#include "mpact/sim/util/asm/resolver_interface.h" +#include "riscv/riscv64g_encoder.h" +#include "riscv/riscv64g_enums.h" + +// This file defines the class that implements the RiscV64GEncoderInterfaceBase. +// This class is used to convert from a text representation to the binary +// encoding of opcodes, source and destination operands. + +namespace mpact { +namespace sim { +namespace riscv { +namespace isa64 { + +using ::mpact::sim::util::assembler::RelocationInfo; +using ::mpact::sim::util::assembler::ResolverInterface; + +class RiscV64GBinEncoderInterface : public RiscV64GEncoderInterfaceBase { + public: + RiscV64GBinEncoderInterface(); + RiscV64GBinEncoderInterface(const RiscV64GBinEncoderInterface &) = delete; + RiscV64GBinEncoderInterface &operator=(const RiscV64GBinEncoderInterface &) = + delete; + ~RiscV64GBinEncoderInterface() override = default; + + absl::StatusOr<std::tuple<uint64_t, int>> GetOpcodeEncoding( + SlotEnum slot, int entry, OpcodeEnum opcode, + ResolverInterface *resolver) override; + absl::StatusOr<uint64_t> GetSrcOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, SourceOpEnum source_op, int source_num, + ResolverInterface *resolver) override; + absl::Status AppendSrcOpRelocation( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, SourceOpEnum source_op, int source_num, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) override; + absl::StatusOr<uint64_t> GetDestOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, DestOpEnum dest_op, int dest_num, + ResolverInterface *resolver) override; + absl::Status AppendDestOpRelocation( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, DestOpEnum dest_op, int dest_num, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) override; + absl::StatusOr<uint64_t> GetListSrcOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, ListSourceOpEnum source_op, int source_num, + ResolverInterface *resolver) override; + absl::StatusOr<uint64_t> GetListDestOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, ListDestOpEnum dest_op, int dest_num, + ResolverInterface *resolver) override; + absl::StatusOr<uint64_t> GetPredOpEncoding( + uint64_t address, absl::string_view text, SlotEnum slot, int entry, + OpcodeEnum opcode, PredOpEnum pred_op, + ResolverInterface *resolver) override; + + private: + using OpMap = absl::flat_hash_map< + int, std::function<absl::StatusOr<uint64_t>(uint64_t, absl::string_view, + ResolverInterface *)>>; + + using RelocationMap = + absl::flat_hash_map<std::tuple<OpcodeEnum, SourceOpEnum>, + std::function<absl::Status( + uint64_t, absl::string_view, ResolverInterface *, + std::vector<RelocationInfo> &)>>; + + OpMap source_op_map_; + RelocationMap relocation_source_op_map_; + OpMap dest_op_map_; + OpMap list_dest_op_map_; + OpMap list_source_op_map_; + OpMap pred_op_map_; +}; + +} // namespace isa64 +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_
diff --git a/riscv/riscv_arm_semihost.cc b/riscv/riscv_arm_semihost.cc index 5a79e1e..b3ba038 100644 --- a/riscv/riscv_arm_semihost.cc +++ b/riscv/riscv_arm_semihost.cc
@@ -238,7 +238,9 @@ LOG(ERROR) << "Program exit not caught in ARM semihosting - no callback"; return absl::NotFoundError("Exit callback not valid in ARM semihosting"); } - LOG(ERROR) << "Exceptions other than ApplicationExit are not implemented"; + LOG(ERROR) << absl::StrCat( + "Exception ", absl::Hex(param_value), " other than ApplicationExit (", + absl::Hex(kAdpStoppedApplicationExit), ") are not implemented"); return absl::UnimplementedError( "Exceptions other than ApplicationExit are not implemented"); }
diff --git a/riscv/riscv_arm_semihost.h b/riscv/riscv_arm_semihost.h index 82ee35a..19d89bd 100644 --- a/riscv/riscv_arm_semihost.h +++ b/riscv/riscv_arm_semihost.h
@@ -15,6 +15,7 @@ #ifndef MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_ #define MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_ +#include <cstdint> #include <functional> #include "absl/container/flat_hash_map.h"
diff --git a/riscv/riscv_bin_setters.cc b/riscv/riscv_bin_setters.cc new file mode 100644 index 0000000..6d6684d --- /dev/null +++ b/riscv/riscv_bin_setters.cc
@@ -0,0 +1,145 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv_bin_setters.h" + +#include <cstdint> +#include <string> +#include <vector> + +#include "absl/base/no_destructor.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "mpact/sim/generic/type_helpers.h" +#include "re2/re2.h" + +namespace mpact { +namespace sim { +namespace riscv { + +namespace internal { + +// Current set of relocation types supported by the assembler. +enum class RelocType { + kNone = 0, + kBranch = 16, + kJal = 17, + kPcrelHi20 = 23, + kPcrelLo12I = 24, + kPcrelLo12S = 25, + kHi20 = 26, + kLo12I = 27, + kLo12S = 28, +}; + +using ::mpact::sim::generic::operator*; // NOLINT(misc-unused-using-decls) + +// This regular expression matches the text for a symbol. The first capture +// group is the relocation type (if present), the second capture group is the +// symbol name. +absl::NoDestructor<RE2> kSymRe( + "^\\s*(%[a-zA-Z0-9_]+)?\\s*\\(?([a-zA-Z_][^)]+)\\)?\\s*$"); + +absl::Status RelocateAddiIImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + if (relo == "%lo") { + relocations.emplace_back(0, sym, *RelocType::kLo12I, 0, 0); + return absl::OkStatus(); + } + if (relo == "%pcrel_lo") { + relocations.emplace_back(0, sym, *RelocType::kPcrelLo12I, 0, 0); + return absl::OkStatus(); + } + if (!relo.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid relocation: '", relo, "'")); + } + return absl::OkStatus(); +} + +absl::Status RelocateJJImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + relocations.emplace_back(0, sym, *RelocType::kJal, 0, 0); + return absl::OkStatus(); +} + +absl::Status RelocateJrJImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + if (relo == "%pcrel_lo") { + relocations.emplace_back(0, sym, *RelocType::kPcrelLo12I, 0, 0); + } + return absl::OkStatus(); +} + +absl::Status RelocateLuiUImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + relocations.emplace_back(0, sym, *RelocType::kHi20, 0, 0); + return absl::OkStatus(); +} + +absl::Status RelocateSdSImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + if (relo == "%lo") { + relocations.emplace_back(0, sym, *RelocType::kLo12S, 0, 0); + return absl::OkStatus(); + } + if (relo == "%pcrel_lo") { + relocations.emplace_back(0, sym, *RelocType::kPcrelLo12S, 0, 0); + return absl::OkStatus(); + } + if (!relo.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid relocation: '", relo, "'")); + } + return absl::OkStatus(); +} + +absl::Status RelocateAuipcUImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations) { + std::string relo; + std::string sym; + if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus(); + if (relo == "%pcrel_hi") { + relocations.emplace_back(0, sym, *RelocType::kPcrelHi20, 0, 0); + } + return absl::OkStatus(); +} + +} // namespace internal + +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv_bin_setters.h b/riscv/riscv_bin_setters.h new file mode 100644 index 0000000..4eb312c --- /dev/null +++ b/riscv/riscv_bin_setters.h
@@ -0,0 +1,1040 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_ + +#include <cstdint> +#include <initializer_list> +#include <string> +#include <utility> +#include <vector> + +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "mpact/sim/util/asm/opcode_assembler_interface.h" +#include "mpact/sim/util/asm/resolver_interface.h" +#include "re2/re2.h" +#include "riscv/riscv_getter_helpers.h" + +// This file contains various setters for the RiscV binary encoder that is used +// by the assembler to map from operand text strings to integer values. + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::util::assembler::RelocationInfo; +using ::mpact::sim::util::assembler::ResolverInterface; + +// Initializer lists for maps for different subsets of registers, mapping from +// the register name to the register number. +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kRegisterList = { + {"x0", 0}, {"x1", 1}, {"x2", 2}, {"x3", 3}, {"x4", 4}, + {"x5", 5}, {"x6", 6}, {"x7", 7}, {"x8", 8}, {"x9", 9}, + {"x10", 10}, {"x11", 11}, {"x12", 12}, {"x13", 13}, {"x14", 14}, + {"x15", 15}, {"x16", 16}, {"x17", 17}, {"x18", 18}, {"x19", 19}, + {"x20", 20}, {"x21", 21}, {"x22", 22}, {"x23", 23}, {"x24", 24}, + {"x25", 25}, {"x26", 26}, {"x27", 27}, {"x28", 28}, {"x29", 29}, + {"x30", 30}, {"x31", 31}, {"zero", 0}, {"ra", 1}, {"sp", 2}, + {"gp", 3}, {"tp", 4}, {"t0", 5}, {"t1", 6}, {"t2", 7}, + {"s0", 8}, {"s1", 9}, {"a0", 10}, {"a1", 11}, {"a2", 12}, + {"a3", 13}, {"a4", 14}, {"a5", 15}, {"a6", 16}, {"a7", 17}, + {"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, + {"s7", 23}, {"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, + {"t3", 28}, {"t4", 29}, {"t5", 30}, {"t6", 31}}; + +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kCRegisterList = { + {"x8", 8}, {"x9", 9}, {"x10", 10}, {"x11", 11}, + {"x12", 12}, {"x13", 13}, {"x14", 14}, {"x15", 15}, + {"s0", 8}, {"s1", 9}, {"a0", 10}, {"a1", 11}, + {"a2", 12}, {"a3", 13}, {"a4", 14}, {"a5", 15}, +}; + +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kFRegisterList = { + {"f0", 0}, {"f1", 1}, {"f2", 2}, {"f3", 3}, {"f4", 4}, + {"f5", 5}, {"f6", 6}, {"f7", 7}, {"f8", 8}, {"f9", 9}, + {"f10", 10}, {"f11", 11}, {"f12", 12}, {"f13", 13}, {"f14", 14}, + {"f15", 15}, {"f16", 16}, {"f17", 17}, {"f18", 18}, {"f19", 19}, + {"f20", 20}, {"f21", 21}, {"f22", 22}, {"f23", 23}, {"f24", 24}, + {"f25", 25}, {"f26", 26}, {"f27", 27}, {"f28", 28}, {"f29", 29}, + {"f30", 30}, {"f31", 31}, {"ft0", 0}, {"ft1", 1}, {"ft2", 2}, + {"ft3", 3}, {"ft4", 4}, {"ft5", 5}, {"ft6", 6}, {"ft7", 7}, + {"fs0", 8}, {"fs1", 9}, {"fa0", 10}, {"fa1", 11}, {"fa2", 12}, + {"fa3", 13}, {"fa4", 14}, {"fa5", 15}, {"fa6", 16}, {"fa7", 17}, + {"fs2", 18}, {"fs3", 19}, {"fs4", 20}, {"fs5", 21}, {"fs6", 22}, + {"fs7", 23}, {"fs8", 24}, {"fs9", 25}, {"fs10", 26}, {"fs11", 27}, + {"ft8", 28}, {"ft9", 29}, {"ft10", 30}, {"ft11", 31}}; + +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kDRegisterList = { + {"d0", 0}, {"d1", 1}, {"d2", 2}, {"d3", 3}, {"d4", 4}, + {"d5", 5}, {"d6", 6}, {"d7", 7}, {"d8", 8}, {"d9", 9}, + {"d10", 10}, {"d11", 11}, {"d12", 12}, {"d13", 13}, {"d14", 14}, + {"d15", 15}, {"d16", 16}, {"d17", 17}, {"d18", 18}, {"d19", 19}, + {"d20", 20}, {"d21", 21}, {"d22", 22}, {"d23", 23}, {"d24", 24}, + {"d25", 25}, {"d26", 26}, {"d27", 27}, {"d28", 28}, {"d29", 29}, + {"d30", 30}, {"d31", 31}, {"dt0", 0}, {"dt1", 1}, {"dt2", 2}, + {"dt3", 3}, {"dt4", 4}, {"dt5", 5}, {"dt6", 6}, {"dt7", 7}, + {"ds0", 8}, {"ds1", 9}, {"da0", 10}, {"da1", 11}, {"da2", 12}, + {"da3", 13}, {"da4", 14}, {"da5", 15}, {"da6", 16}, {"da7", 17}, + {"ds2", 18}, {"ds3", 19}, {"ds4", 20}, {"ds5", 21}, {"ds6", 22}, + {"ds7", 23}, {"ds8", 24}, {"ds9", 25}, {"ds10", 26}, {"ds11", 27}, + {"dt8", 28}, {"dt9", 29}, {"dt10", 30}, {"dt11", 31}}; + +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kDCRegisterList = { + {"d8", 8}, {"d9", 9}, {"d10", 10}, {"d11", 11}, + {"d12", 12}, {"d13", 13}, {"d14", 14}, {"d15", 15}, + {"ds0", 8}, {"ds1", 9}, {"da0", 10}, {"da1", 11}, + {"da2", 12}, {"da3", 13}, {"da4", 14}, {"da5", 15}, +}; + +// This is the initializer list for the map from CSR register names to register +// numbers. +constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>> + kCsrRegisterList = { + {"fflags", 0x001}, + {"frm", 0x002}, + {"fcsr", 0x003}, + {"cycle", 0xc00}, + {"time", 0xc01}, + {"instret", 0xc02}, + {"hpmcounter3", 0xc03}, + {"hpmcounter4", 0xc04}, + {"hpmcounter5", 0xc05}, + {"hpmcounter6", 0xc06}, + {"hpmcounter7", 0xc07}, + {"hpmcounter8", 0xc08}, + {"hpmcounter9", 0xc09}, + {"hpmcounter10", 0xc0a}, + {"hpmcounter11", 0xc0b}, + {"hpmcounter12", 0xc0c}, + {"hpmcounter13", 0xc0d}, + {"hpmcounter14", 0xc0e}, + {"hpmcounter15", 0xc0f}, + {"hpmcounter16", 0xc10}, + {"hpmcounter17", 0xc11}, + {"hpmcounter18", 0xc12}, + {"hpmcounter19", 0xc13}, + {"hpmcounter20", 0xc14}, + {"hpmcounter21", 0xc15}, + {"hpmcounter22", 0xc16}, + {"hpmcounter23", 0xc17}, + {"hpmcounter24", 0xc18}, + {"hpmcounter25", 0xc19}, + {"hpmcounter26", 0xc1a}, + {"hpmcounter27", 0xc1b}, + {"hpmcounter28", 0xc1c}, + {"hpmcounter29", 0xc1d}, + {"hpmcounter30", 0xc1e}, + {"hpmcounter31", 0xc1f}, + {"cycleh", 0xc80}, + {"timeh", 0xc81}, + {"instreth", 0xc82}, + {"hpmcounter3h", 0xc83}, + {"hpmcounter4h", 0xc84}, + {"hpmcounter5h", 0xc85}, + {"hpmcounter6h", 0xc86}, + {"hpmcounter7h", 0xc87}, + {"hpmcounter8h", 0xc88}, + {"hpmcounter9h", 0xc89}, + {"hpmcounter10h", 0xc8a}, + {"hpmcounter11h", 0xc8b}, + {"hpmcounter12h", 0xc8c}, + {"hpmcounter13h", 0xc8d}, + {"hpmcounter14h", 0xc8e}, + {"hpmcounter15h", 0xc8f}, + {"hpmcounter16h", 0xc90}, + {"hpmcounter17h", 0xc91}, + {"hpmcounter18h", 0xc92}, + {"hpmcounter19h", 0xc93}, + {"hpmcounter20h", 0xc94}, + {"hpmcounter2h1", 0xc95}, + {"hpmcounter22h", 0xc96}, + {"hpmcounter23h", 0xc97}, + {"hpmcounter24h", 0xc98}, + {"hpmcounter25h", 0xc99}, + {"hpmcounter26h", 0xc9a}, + {"hpmcounter27h", 0xc9b}, + {"hpmcounter28h", 0xc9c}, + {"hpmcounter29h", 0xc9d}, + {"hpmcounter30h", 0xc9e}, + {"hpmcounter3h1", 0xc9f}, + {"sstatus", 0x100}, + {"sie", 0x104}, + {"stvec", 0x105}, + {"stcounteren", 0x106}, + {"senvcfg", 0x10a}, + {"scountinhibit", 0x120}, + {"sscratch", 0x140}, + {"sepc", 0x141}, + {"scause", 0x142}, + {"stval", 0x143}, + {"sip", 0x144}, + {"scountovf", 0xda0}, + {"satp", 0x180}, + {"scontext", 0x5a8}, + {"sstateen0", 0x10c}, + {"sstateen1", 0x10d}, + {"sstateen2", 0x10e}, + {"sstateen3", 0x10f}, + {"hstatus", 0x600}, + {"hedeleg", 0x602}, + {"hideleg", 0x603}, + {"hie", 0x604}, + {"hcounteren", 0x606}, + {"hgeie", 0x607}, + {"hedelegh", 0x612}, + {"htval", 0x643}, + {"hip", 0x644}, + {"hvip", 0x645}, + {"htinst", 0x64a}, + {"hgeip", 0xe12}, + {"henvcfg", 0x60a}, + {"henvcfgh", 0x61a}, + {"hgatp", 0x680}, + {"hcontext", 0x6a8}, + {"htimedelta", 0x605}, + {"htimedeltah", 0x615}, + {"hstateen0", 0x60c}, + {"hstateen1", 0x60d}, + {"hstateen2", 0x60e}, + {"hstateen3", 0x60f}, + {"hstateen0h", 0x61c}, + {"hstateen1h", 0x61d}, + {"hstateen2h", 0x61e}, + {"hstateen3h", 0x61f}, + {"vsstatus", 0x200}, + {"vsie", 0x204}, + {"vstvec", 0x205}, + {"vsscratch", 0x240}, + {"vsepc", 0x241}, + {"vscause", 0x242}, + {"vstval", 0x243}, + {"vsip", 0x244}, + {"vsatp", 0x280}, + {"mvendorid", 0xf11}, + {"marchid", 0xf12}, + {"mimpid", 0xf13}, + {"mhartid", 0xf14}, + {"mconfigptr", 0xf15}, + {"mstatus", 0x300}, + {"misa", 0x301}, + {"medeleg", 0x302}, + {"mideleg", 0x303}, + {"mie", 0x304}, + {"mcounteren", 0x306}, + {"mstatush", 0x310}, + {"medelegh", 0x312}, + {"mscratch", 0x340}, + {"mepc", 0x341}, + {"mcause", 0x342}, + {"mtval", 0x343}, + {"mip", 0x344}, + {"mtinst", 0x34a}, + {"mtval2", 0x34b}, + {"menvcfg", 0x30a}, + {"menvcfgh", 0x31a}, + {"mseccfg", 0x747}, + {"mseccfgh", 0x757}, + {"pmpcfg0", 0x3a0}, + {"pmpcfg1", 0x3a1}, + {"pmpcfg2", 0x3a2}, + {"pmpcfg3", 0x3a3}, + {"pmpcfg4", 0x3a4}, + {"pmpcfg5", 0x3a5}, + {"pmpcfg6", 0x3a6}, + {"pmpcfg7", 0x3a7}, + {"pmpcfg8", 0x3a8}, + {"pmpcfg9", 0x3a9}, + {"pmpcfg10", 0x3aa}, + {"pmpcfg11", 0x3ab}, + {"pmpcfg12", 0x3ac}, + {"pmpcfg13", 0x3ad}, + {"pmpcfg14", 0x3ae}, + {"pmpcfg15", 0x3af}, + {"pmpaddr0", 0x3b0}, + {"pmpaddr1", 0x3b1}, + {"pmpaddr2", 0x3b2}, + {"pmpaddr3", 0x3b3}, + {"pmpaddr4", 0x3b4}, + {"pmpaddr5", 0x3b5}, + {"pmpaddr6", 0x3b6}, + {"pmpaddr7", 0x3b7}, + {"pmpaddr8", 0x3b8}, + {"pmpaddr9", 0x3b9}, + {"pmpaddr10", 0x3ba}, + {"pmpaddr11", 0x3bb}, + {"pmpaddr12", 0x3bc}, + {"pmpaddr13", 0x3bd}, + {"pmpaddr14", 0x3be}, + {"pmpaddr15", 0x3bf}, + {"pmpaddr16", 0x3c0}, + {"pmpaddr17", 0x3c1}, + {"pmpaddr18", 0x3c2}, + {"pmpaddr19", 0x3c3}, + {"pmpaddr20", 0x3c4}, + {"pmpaddr21", 0x3c5}, + {"pmpaddr22", 0x3c6}, + {"pmpaddr23", 0x3c7}, + {"pmpaddr24", 0x3c8}, + {"pmpaddr25", 0x3c9}, + {"pmpaddr26", 0x3ca}, + {"pmpaddr27", 0x3cb}, + {"pmpaddr28", 0x3cc}, + {"pmpaddr29", 0x3cd}, + {"pmpaddr30", 0x3ce}, + {"pmpaddr31", 0x3cf}, + {"pmpaddr32", 0x3d0}, + {"pmpaddr33", 0x3d1}, + {"pmpaddr34", 0x3d2}, + {"pmpaddr35", 0x3d3}, + {"pmpaddr36", 0x3d4}, + {"pmpaddr37", 0x3d5}, + {"pmpaddr38", 0x3d6}, + {"pmpaddr39", 0x3d7}, + {"pmpaddr40", 0x3d8}, + {"pmpaddr41", 0x3d9}, + {"pmpaddr42", 0x3da}, + {"pmpaddr43", 0x3db}, + {"pmpaddr44", 0x3dc}, + {"pmpaddr45", 0x3dd}, + {"pmpaddr46", 0x3de}, + {"pmpaddr47", 0x3df}, + {"pmpaddr48", 0x3e0}, + {"pmpaddr49", 0x3e1}, + {"pmpaddr50", 0x3e2}, + {"pmpaddr51", 0x3e3}, + {"pmpaddr52", 0x3e4}, + {"pmpaddr53", 0x3e5}, + {"pmpaddr54", 0x3e6}, + {"pmpaddr55", 0x3e7}, + {"pmpaddr56", 0x3e8}, + {"pmpaddr57", 0x3e9}, + {"pmpaddr58", 0x3ea}, + {"pmpaddr59", 0x3eb}, + {"pmpaddr60", 0x3ec}, + {"pmpaddr61", 0x3ed}, + {"pmpaddr62", 0x3ee}, + {"pmpaddr63", 0x3ef}, + {"mstateen0", 0x30c}, + {"mstateen1", 0x30d}, + {"mstateen2", 0x30e}, + {"mstateen3", 0x30f}, + {"mstateen0h", 0x31c}, + {"mstateen1h", 0x31d}, + {"mstateen2h", 0x31e}, + {"mstateen3h", 0x31f}, + {"mnscratch", 0x740}, + {"mnepc", 0x741}, + {"mncause", 0x742}, + {"mnstatus", 0x743}, + {"mcycle", 0xb00}, + {"minstret", 0xb02}, + {"mhpmcounter3", 0xb03}, + {"mhpmcounter4", 0xb04}, + {"mhpmcounter5", 0xb05}, + {"mhpmcounter6", 0xb06}, + {"mhpmcounter7", 0xb07}, + {"mhpmcounter8", 0xb08}, + {"mhpmcounter9", 0xb09}, + {"mhpmcounter10", 0xb0a}, + {"mhpmcounter11", 0xb0b}, + {"mhpmcounter12", 0xb0c}, + {"mhpmcounter13", 0xb0d}, + {"mhpmcounter14", 0xb0e}, + {"mhpmcounter15", 0xb0f}, + {"mhpmcounter16", 0xb10}, + {"mhpmcounter17", 0xb11}, + {"mhpmcounter18", 0xb12}, + {"mhpmcounter19", 0xb13}, + {"mhpmcounter20", 0xb14}, + {"mhpmcounter21", 0xb15}, + {"mhpmcounter22", 0xb16}, + {"mhpmcounter23", 0xb17}, + {"mhpmcounter24", 0xb18}, + {"mhpmcounter25", 0xb19}, + {"mhpmcounter26", 0xb1a}, + {"mhpmcounter27", 0xb1b}, + {"mhpmcounter28", 0xb1c}, + {"mhpmcounter29", 0xb1d}, + {"mhpmcounter30", 0xb1e}, + {"mhpmcounter31", 0xb1f}, + {"mcycleh", 0xb80}, + {"minstreth", 0xb82}, + {"mhpmcounter3h", 0xb83}, + {"mhpmcounter4h", 0xb84}, + {"mhpmcounter5h", 0xb85}, + {"mhpmcounter6h", 0xb86}, + {"mhpmcounter7h", 0xb87}, + {"mhpmcounter8h", 0xb88}, + {"mhpmcounter9h", 0xb89}, + {"mhpmcounter10h", 0xb8a}, + {"mhpmcounter11h", 0xb8b}, + {"mhpmcounter12h", 0xb8c}, + {"mhpmcounter13h", 0xb8d}, + {"mhpmcounter14h", 0xb8e}, + {"mhpmcounter15h", 0xb8f}, + {"mhpmcounter16h", 0xb90}, + {"mhpmcounter17h", 0xb91}, + {"mhpmcounter18h", 0xb92}, + {"mhpmcounter19h", 0xb93}, + {"mhpmcounter20h", 0xb94}, + {"mhpmcounter21h", 0xb95}, + {"mhpmcounter22h", 0xb96}, + {"mhpmcounter23h", 0xb97}, + {"mhpmcounter24h", 0xb98}, + {"mhpmcounter25h", 0xb99}, + {"mhpmcounter26h", 0xb9a}, + {"mhpmcounter27h", 0xb9b}, + {"mhpmcounter28h", 0xb9c}, + {"mhpmcounter29h", 0xb9d}, + {"mhpmcounter30h", 0xb9e}, + {"mhpmcounter31h", 0xb9f}, + {"mcountinhibit", 0x320}, + {"mhpmevent3", 0x323}, + {"mhpmevent4", 0x324}, + {"mhpmevent5", 0x325}, + {"mhpmevent6", 0x326}, + {"mhpmevent7", 0x327}, + {"mhpmevent8", 0x328}, + {"mhpmevent9", 0x329}, + {"mhpmevent10", 0x32a}, + {"mhpmevent11", 0x32b}, + {"mhpmevent12", 0x32c}, + {"mhpmevent13", 0x32d}, + {"mhpmevent14", 0x32e}, + {"mhpmevent15", 0x32f}, + {"mhpmevent16", 0x330}, + {"mhpmevent17", 0x331}, + {"mhpmevent18", 0x332}, + {"mhpmevent19", 0x333}, + {"mhpmevent20", 0x334}, + {"mhpmevent21", 0x335}, + {"mhpmevent22", 0x336}, + {"mhpmevent23", 0x337}, + {"mhpmevent24", 0x338}, + {"mhpmevent25", 0x339}, + {"mhpmevent26", 0x33a}, + {"mhpmevent27", 0x33b}, + {"mhpmevent28", 0x33c}, + {"mhpmevent29", 0x33d}, + {"mhpmevent30", 0x33e}, + {"mhpmevent31", 0x33f}, + {"mhpmevent3h", 0x723}, + {"mhpmevent4h", 0x724}, + {"mhpmevent5h", 0x725}, + {"mhpmevent6h", 0x726}, + {"mhpmevent7h", 0x727}, + {"mhpmevent8h", 0x728}, + {"mhpmevent9h", 0x729}, + {"mhpmevent10h", 0x72a}, + {"mhpmevent11h", 0x72b}, + {"mhpmevent12h", 0x72c}, + {"mhpmevent13h", 0x72d}, + {"mhpmevent14h", 0x72e}, + {"mhpmevent15h", 0x72f}, + {"mhpmevent16h", 0x730}, + {"mhpmevent17h", 0x731}, + {"mhpmevent18h", 0x732}, + {"mhpmevent19h", 0x733}, + {"mhpmevent20h", 0x734}, + {"mhpmevent21h", 0x735}, + {"mhpmevent22h", 0x736}, + {"mhpmevent23h", 0x737}, + {"mhpmevent24h", 0x738}, + {"mhpmevent25h", 0x739}, + {"mhpmevent26h", 0x73a}, + {"mhpmevent27h", 0x73b}, + {"mhpmevent28h", 0x73c}, + {"mhpmevent29h", 0x73d}, + {"mhpmevent30h", 0x73e}, + {"mhpmevent31h", 0x73f}, +}; + +// A helper function to convert a text string to an integer. The function takes +// either numeric literals (hexadecimal or decimal), symbol names, or relocation +// functions, e.g., %hi(<symbol name>). +template <typename T> +absl::StatusOr<T> SimpleTextToInt(absl::string_view op_text, + ResolverInterface *resolver) { + T value; + static RE2 hex_re("^\\s*0x([0-9a-fA-F]+)\\s*$"); + static RE2 dec_re("^\\s*(-?[0-9]+)\\s*$"); + static RE2 relo_re("^\\s*\\%[a-zA-Z0-9_]+\\s*\\(([a-zA-Z0-9_]+)\\s*\\)\\s*$"); + static RE2 symbol_re("^\\s*([a-zA-Z0-9_]+)\\s*$"); + std::string str; + std::string text(op_text); + // First see if the operand is a relocation function, and extract the text + // argument. A relocation function is on the form of %name(arg). + if (RE2::FullMatch(op_text, relo_re, &str)) { + text = str; + } + // Extract the hex immediate. + if (RE2::FullMatch(text, hex_re, &str)) { + if (absl::SimpleHexAtoi(str, &value)) return value; + return absl::InvalidArgumentError( + absl::StrCat("Invalid hexadecimal immediate: ", text)); + } + // Extract the decimal immediate. + if (RE2::FullMatch(text, dec_re, &str)) { + if (absl::SimpleAtoi(str, &value)) return value; + return absl::InvalidArgumentError( + absl::StrCat("Invalid decimal immediate: ", text)); + } + // Extract the symbol. + if (RE2::FullMatch(text, symbol_re, &str)) { + if (resolver != nullptr) { + auto res = resolver->Resolve(str); + if (!res.ok()) { + return res.status(); + } + return static_cast<T>(res.value()); + } + } + return absl::InvalidArgumentError(absl::StrCat("Invalid argument: ", text)); +} + +using ValueMap = absl::flat_hash_map<absl::string_view, uint64_t>; + +// This function adds the bin setters for the source operands to the given map. +template <typename Enum, typename Map, typename Encoder> +void AddRiscvSourceOpBinSetters(Map &map) { + Insert(map, *Enum::kAAq, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map = {{"", 0}, {".aq", 1}}; + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::AType::InsertAq(iter->second, 0ULL); + }); + Insert(map, *Enum::kARl, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map = {{"", 0}, {".rl", 1}}; + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::AType::InsertRl(iter->second, 0ULL); + }); + Insert(map, *Enum::kBImm12, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + uint32_t delta = res.value() - address; + return Encoder::BType::InsertBImm(delta, 0ULL); + }); + Insert(map, *Enum::kC3drs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CS::InsertCsRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kC3rs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CL::InsertClRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kC3rs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CS::InsertCsRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kCSRUimm5, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::IType::InsertIUimm5(res.value(), 0ULL); + }); + Insert(map, *Enum::kCdrs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CR::InsertRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kCrs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CR::InsertRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kCrs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::CR::InsertRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kCsr, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCsrRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::IType::InsertUImm12(iter->second, 0ULL); + }); + Insert(map, *Enum::kDrs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kDrs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kDrs3, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::R4Type::InsertRs3(iter->second, 0ULL); + }); + Insert(map, *Enum::kFrs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kFRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kFrs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kFRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kFrs3, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kFRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::R4Type::InsertRs3(iter->second, 0ULL); + }); + Insert(map, *Enum::kICbImm8, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + uint32_t delta = res.value() - address; + return Encoder::CB::InsertBimm(delta, 0ULL); + }); + Insert(map, *Enum::kICiImm6, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertImm6(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiImm612, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertImm18(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiImm6x16, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertCiImm10(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiUimm6, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertUimm6(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiUimm6x4, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertCiImmW(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiUimm6x8, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CI::InsertCiImmD(res.value(), 0ULL); + }); + Insert(map, *Enum::kICiwUimm8x4, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CIW::InsertCiwImm10(res.value(), 0ULL); + }); + Insert(map, *Enum::kICjImm11, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + auto delta = res.value() - address; + return Encoder::CJ::InsertJimm(delta, 0ULL); + }); + Insert(map, *Enum::kIClUimm5x4, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CL::InsertClImmW(res.value(), 0ULL); + }); + Insert(map, *Enum::kIClUimm5x8, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CL::InsertClImmD(res.value(), 0ULL); + }); + Insert(map, *Enum::kICssUimm6x4, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CS::InsertCsImmW(res.value(), 0ULL); + }); + Insert(map, *Enum::kICssUimm6x8, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::CS::InsertCsImmD(res.value(), 0ULL); + }); + Insert(map, *Enum::kIImm12, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::IType::InsertImm12(res.value(), 0ULL); + }); + Insert(map, *Enum::kIUimm5, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::RType::InsertRUimm5(res.value(), 0ULL); + }); + Insert(map, *Enum::kIUimm6, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::RSType::InsertRUimm6(res.value(), 0ULL); + }); + Insert(map, *Enum::kJImm12, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::IType::InsertImm12(res.value(), 0ULL); + }); + Insert(map, *Enum::kJImm20, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<int32_t>(text, resolver); + if (!res.ok()) return res.status(); + uint32_t delta = res.value() - address; + auto value = Encoder::JType::InsertJImm(delta, 0ULL); + return value; + }); + Insert(map, *Enum::kPred, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::Fence::InsertPred(res.value(), 0ULL); + }); + Insert(map, *Enum::kRd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRd(iter->second, 0ULL); + }); + Insert(map, *Enum::kRm, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::R4Type::InsertRs3(iter->second, 0ULL); + }); + Insert(map, *Enum::kRs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kRs2, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::RType::InsertRs2(iter->second, 0ULL); + }); + Insert(map, *Enum::kSImm12, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::SType::InsertSImm(res.value(), 0ULL); + }); + Insert(map, *Enum::kSucc, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::Fence::InsertSucc(res.value(), 0ULL); + }); + Insert(map, *Enum::kUImm20, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + auto res = SimpleTextToInt<uint32_t>(text, resolver); + if (!res.ok()) return res.status(); + return Encoder::UType::InsertUImm(res.value(), 0ULL); + }); +} + +// This function adds the destination operand setters for the RiscV ISA to the +// given map. +template <typename Enum, typename Map, typename Encoder> +void AddRiscvDestOpBinSetters(Map &map) { + Insert(map, *Enum::kC3drd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::CL::InsertClRd(iter->second, 0ULL); + }); + Insert(map, *Enum::kC3rd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::CL::InsertClRd(iter->second, 0ULL); + }); + Insert(map, *Enum::kC3rs1, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::CS::InsertCsRs1(iter->second, 0ULL); + }); + Insert(map, *Enum::kCsr, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kCsrRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source operand: ", text)); + } + return Encoder::IType::InsertUImm12(iter->second, 0ULL); + }); + Insert(map, *Enum::kDrd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kDRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::RType::InsertRd(iter->second, 0ULL); + }); + Insert(map, *Enum::kFrd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kFRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::RType::InsertRd(iter->second, 0ULL); + }); + Insert(map, *Enum::kRd, + [](uint64_t address, absl::string_view text, + ResolverInterface *resolver) -> absl::StatusOr<uint64_t> { + static ValueMap map(kRegisterList); + auto iter = map.find(text); + if (iter == map.end()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid destination operand: ", text)); + } + return Encoder::RType::InsertRd(iter->second, 0ULL); + }); +} + +// These functions add the appropriate relocation entry to the relocations +// vector if the operand (in text) requires it. +namespace internal { + +absl::Status RelocateAddiIImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); +absl::Status RelocateJJImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); +absl::Status RelocateJrJImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); +absl::Status RelocateLuiUImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); +absl::Status RelocateSdSImm12(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); +absl::Status RelocateAuipcUImm20(uint64_t address, absl::string_view text, + ResolverInterface *resolver, + std::vector<RelocationInfo> &relocations); + +} // namespace internal + +// This function adds the source operand relocation setters for the RiscV ISA to +// the given map. Notice that the key in the map is the tuple consisting of the +// opcode and the source operand enum values. +template <typename OpcodeEnum, typename SourceOpEnum, typename Map> +void AddRiscvSourceOpRelocationSetters(Map &map) { + Insert(map, OpcodeEnum::kAddi, SourceOpEnum::kIImm12, + internal::RelocateAddiIImm12); + Insert(map, OpcodeEnum::kJal, SourceOpEnum::kJImm20, + internal::RelocateJJImm20); + Insert(map, OpcodeEnum::kJ, SourceOpEnum::kJImm20, internal::RelocateJJImm20); + Insert(map, OpcodeEnum::kJr, SourceOpEnum::kJImm12, + internal::RelocateJrJImm12); + Insert(map, OpcodeEnum::kLui, SourceOpEnum::kUImm20, + internal::RelocateLuiUImm20); + Insert(map, OpcodeEnum::kSd, SourceOpEnum::kSImm12, + internal::RelocateSdSImm12); + Insert(map, OpcodeEnum::kJalr, SourceOpEnum::kJImm12, + internal::RelocateJrJImm12); + Insert(map, OpcodeEnum::kAuipc, SourceOpEnum::kUImm20, + internal::RelocateAuipcUImm20); +} + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_
diff --git a/riscv/riscv_clint.h b/riscv/riscv_clint.h index 468b8e6..9226c0b 100644 --- a/riscv/riscv_clint.h +++ b/riscv/riscv_clint.h
@@ -18,6 +18,7 @@ #include <cstdint> #include "mpact/sim/generic/counters.h" +#include "mpact/sim/generic/counters_base.h" #include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/instruction.h" #include "mpact/sim/generic/ref_count.h" @@ -117,4 +118,4 @@ } // namespace sim } // namespace mpact -#endif // THIRD_PARTY_MPACT_RISCV_RISCV_CLINT_H_ \ No newline at end of file +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_CLINT_H_
diff --git a/riscv/riscv_getter_helpers.h b/riscv/riscv_getter_helpers.h index dc3f91d..408da03 100644 --- a/riscv/riscv_getter_helpers.h +++ b/riscv/riscv_getter_helpers.h
@@ -63,6 +63,16 @@ } } +template <typename M, typename E1, typename E2, typename G> +inline void Insert(M &map, E1 entry1, E2 entry2, G getter) { + auto key = std::tie(entry1, entry2); + if (!map.contains(key)) { + map.insert(std::make_pair(key, getter)); + } else { + map.at(key) = getter; + } +} + // Generic helper functions to create register operands. template <typename RegType> inline DestinationOperandInterface *GetRegisterDestinationOp(RiscVState *state,
diff --git a/riscv/riscv_getters_zvbb.h b/riscv/riscv_getters_zvbb.h new file mode 100644 index 0000000..7484890 --- /dev/null +++ b/riscv/riscv_getters_zvbb.h
@@ -0,0 +1,104 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_ + +#include <cstdint> +#include <new> + +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/immediate_operand.h" +#include "mpact/sim/generic/literal_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_getters_vector.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_register_aliases.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +// The following function adds source operand getters to the given getter map. +// The function uses the template parameters to get the correct enum type +// for the instruction set being decoded. The Extractors parameter is used to +// get the correct instruction format extractor for the instruction set. +template <typename Enum, typename Extractors, typename VectorRegister> +void AddRiscVZvbbSourceVectorGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kVmask, [common]() -> SourceOperandInterface * { + auto vm = Extractors::VArith::ExtractVm(common->inst_word()); + if (vm == 1) { + // Unmasked, return the True mask. + return new RV32VectorTrueOperand(common->state()); + } + // Masked. Return the mask register. + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), 0); + }); + Insert(getter_map, *Enum::kVs1, [common]() -> SourceOperandInterface * { + auto num = Extractors::VArith::ExtractVs1(common->inst_word()); + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), num); + }); + Insert(getter_map, *Enum::kVs2, [common]() -> SourceOperandInterface * { + auto num = Extractors::VArith::ExtractVs2(common->inst_word()); + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), num); + }); +} + +template <typename Enum, typename Extractors, typename IntegerRegister> +void AddRiscvZvbbSourceScalarGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kRs1, [common]() -> SourceOperandInterface * { + int num = Extractors::VArith::ExtractRs1(common->inst_word()); + if (num == 0) return new generic::IntLiteralOperand<0>({1}); + return GetRegisterSourceOp<IntegerRegister>( + common->state(), absl::StrCat(RiscVState::kXregPrefix, num), + kXRegisterAliases[num]); + }); + Insert(getter_map, *Enum::kUimm5, [common]() -> SourceOperandInterface * { + const auto num = Extractors::VArith::ExtractUimm5(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); + Insert(getter_map, *Enum::kUimm6, [common]() -> SourceOperandInterface * { + const auto num = Extractors::VArith::ExtractUimm6(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); +} + +template <typename Enum, typename Extractors, typename VectorRegister> +void AddRiscVZvbbDestGetters(DestOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Destination operand getters. + Insert(getter_map, *Enum::kVd, + [common](int latency) -> DestinationOperandInterface * { + auto num = Extractors::VArith::ExtractVd(common->inst_word()); + return mpact::sim::riscv::GetVectorRegisterDestinationOp< + VectorRegister>(common->state(), latency, num); + }); +} + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_
diff --git a/riscv/riscv_plic.cc b/riscv/riscv_plic.cc new file mode 100644 index 0000000..10fb89c --- /dev/null +++ b/riscv/riscv_plic.cc
@@ -0,0 +1,587 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv_plic.h" + +#include <cstdint> +#include <cstring> + +#include "absl/log/log.h" +#include "absl/numeric/bits.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "re2/re2.h" + +namespace mpact { +namespace sim { +namespace riscv { + +RiscVPlic::RiscVPlic(int num_sources, int num_contexts) + : num_sources_(num_sources), num_contexts_(num_contexts) { + // Initialize the gateway info. + gateway_info_ = new GatewayInfo[num_sources_]; + // Initialize the context interface. + context_if_ = new RiscVPlicIrqInterface *[num_contexts_]; + context_irq_ = new bool[num_contexts_]; + for (int i = 0; i < num_contexts_; ++i) { + context_if_[i] = nullptr; + context_irq_[i] = false; + } + // Initialize the interrupt priority. + interrupt_priority_ = new uint32_t[num_sources_]; + std::memset(interrupt_priority_, 0, sizeof(uint32_t) * num_sources_); + // Initialize the interrupt pending bits. + interrupt_pending_ = new uint32_t[num_sources_ / 32 + 1]; + std::memset(interrupt_pending_, 0, + sizeof(uint32_t) * (num_sources_ / 32 + 1)); + // Initialize the interrupt enabled bits. + interrupt_enabled_ = new uint32_t *[num_contexts_]; + for (int i = 0; i < num_contexts_; ++i) { + interrupt_enabled_[i] = new uint32_t[num_sources_ / 32 + 1]; + std::memset(interrupt_enabled_[i], 0, + sizeof(uint32_t) * (num_sources_ / 32 + 1)); + } + // Initialize the priority threshold. + priority_threshold_ = new uint32_t[num_contexts_]; + std::memset(priority_threshold_, 0, sizeof(uint32_t) * num_contexts_); + // Initialize the interrupt claim/complete bits. + interrupt_claim_complete_ = new uint32_t[num_contexts_]; + std::memset(interrupt_claim_complete_, 0, sizeof(uint32_t) * num_contexts_); +} + +RiscVPlic::~RiscVPlic() { + // Clean up all the allocated memory. + delete[] gateway_info_; + gateway_info_ = nullptr; + delete[] context_if_; + context_if_ = nullptr; + delete[] context_irq_; + context_irq_ = nullptr; + delete[] interrupt_priority_; + interrupt_priority_ = nullptr; + delete[] interrupt_pending_; + interrupt_pending_ = nullptr; + for (int i = 0; i < num_contexts_; ++i) { + delete[] interrupt_enabled_[i]; + } + delete[] interrupt_enabled_; + interrupt_enabled_ = nullptr; + delete[] priority_threshold_; + priority_threshold_ = nullptr; + delete[] interrupt_claim_complete_; + interrupt_claim_complete_ = nullptr; +} + +absl::Status RiscVPlic::Configure(absl::string_view source_cfg, + absl::string_view context_cfg) { + // List of "<source>=<priority>;" items. + RE2 re_source("^(\\d+)\\s*=\\s*(\\d+)\\s*(;|$)"); + int source; + int priority; + absl::string_view cfg = source_cfg; + while (RE2::Consume(&cfg, re_source, &source, &priority)) { + if (source >= num_sources_) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source number: ", source)); + } + interrupt_priority_[source] = priority; + } + if (!cfg.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source configuration: ", cfg)); + } + // List of "<context>=<source>,<threshold>,<enable>;" items. + RE2 re_context("^(\\d+)\\s*=\\s*(\\d+)\\s*,\\s*"); + RE2 re_context_source("(\\d+)\\s*,\\s*(\\d)\\s*(,|;)\\s*"); + int context; + int threshold; + int enable; + char terminator; + cfg = context_cfg; + while (RE2::Consume(&cfg, re_context, &context, &threshold)) { + if (context >= num_contexts_) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid context number: ", context)); + } + priority_threshold_[context] = threshold; + while ( + RE2::Consume(&cfg, re_context_source, &source, &enable, &terminator)) { + if (source >= num_sources_) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid source number: ", source)); + } + source_to_context_.insert({source, context}); + context_to_source_.insert({context, source}); + int bit = source & 0x1f; + int word = source / 32; + uint32_t mask = ~(1 << bit); + uint32_t value = interrupt_enabled_[context][word]; + value = (value & mask) | (enable << bit); + interrupt_enabled_[context][word] = value; + if (terminator == ';') break; + } + } + if (!cfg.empty()) { + return absl::InvalidArgumentError( + absl::StrCat("Invalid context configuration: ", cfg)); + } + return absl::OkStatus(); +} + +void RiscVPlic::SetInterrupt(int source, bool value, bool is_level) { + // Make sure the source is in range. + if ((source < 0) || (source >= num_sources_)) { + LOG(WARNING) << "Invalid interrupt source: " << source; + return; + } + // No action for clearing a non-level based interrupt. + if (!value && !is_level) return; + + auto &info = gateway_info_[source]; + if (!info.ready || !value) { + if (is_level) info.pending = value; + return; + } + + // If it is level based, latch the pending bit in the gateway. + if (is_level) info.pending = true; + + // If the priority is 0, the interrupt is disabled. + if (interrupt_priority_[source] == 0) return; + + // Accept the request, set ready to false to prevent any other requests + // until this has been processed. + info.ready = false; + // Set the plic pending bit. + SetPlicPendingInterrupt(source); +} + +void RiscVPlic::SetPlicPendingInterrupt(int source) { + if ((source <= 0) || (source >= num_sources_)) { + LOG(ERROR) << "Invalid interrupt source: " << source; + return; + } + // Return if source is already pending. + if (IsPending(source)) return; + // Get interrupt priority. + auto priority = interrupt_priority_[source]; + // Return if source has priority 0. + if (priority == 0) return; + // Set source to pending. + SetPending(source, true); + // Iterate over all contexts that have this source enabled. + auto [begin, end] = source_to_context_.equal_range(source); + for (auto it = begin; it != end; ++it) { + auto context = it->second; + // If the priority is less or equal to the threshold, do not trigger the + // interrupt. + if (priority <= priority_threshold_[context]) continue; + if (context_if_[context] == nullptr) continue; + // Trigger the interrupt. + context_if_[context]->SetIrq(true); + context_irq_[context] = true; + } +} + +// Implementation of the memory load interface for reading memory mapped +// registers. +void RiscVPlic::Load(uint64_t address, DataBuffer *db, Instruction *inst, + ReferenceCount *context) { + uint32_t offset = address & 0xff'ffff; + switch (db->size<uint8_t>()) { + case 1: + db->Set<uint8_t>(0, static_cast<uint8_t>(Read(offset))); + break; + case 2: + db->Set<uint16_t>(0, static_cast<uint16_t>(Read(offset))); + break; + case 4: + db->Set<uint32_t>(0, static_cast<uint32_t>(Read(offset))); + break; + case 8: + db->Set<uint32_t>(0, static_cast<uint32_t>(Read(offset))); + db->Set<uint32_t>(1, static_cast<uint32_t>(Read(offset + 4))); + break; + default: + ::memset(db->raw_ptr(), 0, sizeof(db->size<uint8_t>())); + break; + } + // Execute the instruction to process and write back the load data. + if (nullptr != inst) { + if (db->latency() > 0) { + inst->IncRef(); + if (context != nullptr) context->IncRef(); + inst->state()->function_delay_line()->Add(db->latency(), + [inst, context]() { + inst->Execute(context); + if (context != nullptr) + context->DecRef(); + inst->DecRef(); + }); + } else { + inst->Execute(context); + } + } +} + +// No support for vector loads. +void RiscVPlic::Load(DataBuffer *address_db, DataBuffer *mask_db, int el_size, + DataBuffer *db, Instruction *inst, + ReferenceCount *context) { + LOG(FATAL) << "RiscVPlic does not support vector loads"; +} + +// Implementation of memory store interface to support writes to memory mapped +// registers. +void RiscVPlic::Store(uint64_t address, DataBuffer *db) { + uint32_t offset = address & 0xff'ffff; + switch (db->size<uint8_t>()) { + case 1: + return Write(offset, static_cast<uint32_t>(db->Get<uint8_t>(0))); + case 2: + return Write(offset, static_cast<uint32_t>(db->Get<uint16_t>(0))); + case 4: + return Write(offset, static_cast<uint32_t>(db->Get<uint32_t>(0))); + case 8: + return Write(offset, static_cast<uint32_t>(db->Get<uint32_t>(0))); + return Write(offset + 4, static_cast<uint32_t>(db->Get<uint32_t>(1))); + default: + return; + } +} + +void RiscVPlic::SetContext(int context_no, RiscVPlicIrqInterface *context_if) { + context_if_[context_no] = context_if; +} + +// No support for vector stores. +void RiscVPlic::Store(DataBuffer *address, DataBuffer *mask, int el_size, + DataBuffer *db) { + LOG(FATAL) << "RiscVPlic does not support vector stores"; +} + +uint32_t RiscVPlic::Read(uint32_t offset) { + uint32_t value = 0; + // Interrupt priority bit by source. + if (offset < 0x00'1000) { + value = interrupt_priority_[offset >> 2]; + return value; + } + + // Interrupt pending bits by source/ + if (offset < 0x00'2000) { + offset -= 0x00'1000; + if (offset > (num_sources_ / 32)) { + LOG(WARNING) << "Invalid offset: " << offset; + return 0; + } + value = interrupt_pending_[offset]; + return value; + } + + // Interrupt enable bits for sources by context. + if (offset < 0x20'0000) { + offset -= 0x00'2000; + int context = offset >> 7; + // Get the context id. + if (context >= num_contexts_) { + LOG(ERROR) << "Invalid context number: " << context; + return 0; + } + // Get the word index in the source dimension. + int word = offset & 0x7f; + if (word * 32 >= num_sources_ + 32) { + LOG(ERROR) << "Invalid source number"; + return 0; + } + value = interrupt_enabled_[context][word]; + return value; + } + + // Interrupt priority threshold and claim/complete. + // Get context id. + offset -= 0x20'0000; + int context = offset >> 12; + if (context >= num_contexts_) { + LOG(ERROR) << "Invalid context number: " << context; + return 0; + } + auto reg_id = offset & 0xfff; + switch (reg_id) { + case 0x0: + // Priority threshold. + value = priority_threshold_[context]; + return value; + case 0x4: + // Claim/complete interrupt. + value = ClaimInterrupt(context); + return value; + default: + LOG(ERROR) << "Invalid offset: " << offset; + break; + } + return value; +} + +void RiscVPlic::Write(uint32_t offset, uint32_t value) { + // Interrupt priority bit by source. + if (offset < 0x00'1000) { + int source = offset >> 3; + if (source >= num_sources_) { + LOG(ERROR) << "Invalid source number: " << source; + return; + } + uint32_t prev = interrupt_priority_[source]; + interrupt_priority_[source] = value; + // If the priority is being changed from 0 to non-zero, see if there is a + // pending level based interrupt, and if so, set the plic pending bit. + if (prev == 0 && value != 0) { + auto &info = gateway_info_[source]; + if (info.ready && info.pending) { + SetPlicPendingInterrupt(source); + } + } + return; + } + + // Interrupt pending bits. + if (offset < 0x00'2000) { + offset -= 0x00'1000; + if (offset > (num_sources_ / 32)) { + LOG(WARNING) << "Invalid offset: " << offset; + return; + } + uint32_t prev = interrupt_pending_[offset]; + interrupt_pending_[offset] = value; + // Determine which bits are being set. + uint32_t bits_set = (value ^ prev) & value; + // Trigger interrupts for any of the newly set pending bits. + while (bits_set != 0) { + int bit = absl::countr_zero(bits_set); + int source = offset * 32 + bit; + bits_set &= ~(1 << bit); + auto [begin, end] = source_to_context_.equal_range(source); + for (auto it = begin; it != end; ++it) { + auto context = it->second; + // If the context IRQ line is not already set, see if the priority is + // above the threshold, and if so, set the IRQ line. + if (!context_irq_[context]) { + // If the priority is less or equal to the threshold, do not trigger + // the interrupt. + if (interrupt_priority_[source] <= priority_threshold_[context]) + continue; + if (context_if_[context] == nullptr) { + LOG(ERROR) << "No context interface for context " << context; + continue; + } + // Trigger the interrupt. + context_if_[context]->SetIrq(true); + context_irq_[context] = true; + } + } + } + return; + } + + // Interrupt enable bits for sources by context. + if (offset < 0x20'0000) { + offset -= 0x00'2000; + // Convert from word address to index. + offset >>= 2; + // Get the word index in the source dimension. + int word = (offset >> 2) & 0x1f; + // Get the context id. + int context = offset >> 7; + uint32_t prev = interrupt_enabled_[context][word]; + // Determine which bits are being set. + uint32_t bits_set = (value ^ prev) & value; + uint32_t bits_cleared = (value ^ prev) & ~value; + // Update the enable bits. + interrupt_enabled_[context][word] = value; + // Iterate over the set bits and add them to the source/context maps. + while (bits_set != 0) { + int bit = absl::countr_zero(bits_set); + int source = word * 32 + bit; + source_to_context_.insert({source, context}); + context_to_source_.insert({context, source}); + bits_set &= ~(1 << bit); + if (context_if_[context] == nullptr) { + LOG(ERROR) << "No context interface for context " << context; + continue; + } + // If there is no pending IRQ for this context, and the priority is + // above the threshold, set the IRQ. + if (!context_irq_[context]) { + if (interrupt_priority_[source] > priority_threshold_[context]) { + context_irq_[context] = true; + context_if_[context]->SetIrq(true); + } + } + } + // Iterate over the cleared bits and erase them from the source/context + // maps. + while (bits_cleared != 0) { + int bit = absl::countr_zero(bits_cleared); + int source = word * 32 + bit; + auto [s2c_begin, s2c_end] = source_to_context_.equal_range(source); + for (auto s2c_it = s2c_begin; s2c_it != s2c_end; ++s2c_it) { + if (s2c_it->second == context) { + source_to_context_.erase(s2c_it); + break; + } + } + auto [c2s_begin, c2s_end] = context_to_source_.equal_range(context); + for (auto c2s_it = c2s_begin; c2s_it != c2s_end; ++c2s_it) { + if (c2s_it->second == source) { + context_to_source_.erase(c2s_it); + break; + } + } + bits_cleared &= ~(1 << bit); + } + return; + } + + offset -= 0x20'0000; + // Interrupt priority threshold and claim/complete. + // Get context id. + int context = offset >> 12; + if (context >= num_contexts_) { + LOG(ERROR) << "Invalid context number: " << context; + return; + } + auto reg_id = offset & 0xfff; + switch (reg_id) { + case 0x0: { + // Priority threshold. + uint32_t prev = priority_threshold_[context]; + priority_threshold_[context] = value; + if (value < prev) { + // If the priority threshold is being lowered and there is no active + // interrupt, see if there is a pending interrupt for this context, + // and if so, set the IRQ. + if (!context_irq_[context]) { + if (context_if_[context] == nullptr) { + LOG(ERROR) << "No context interface for context " << context; + return; + } + auto [begin, end] = context_to_source_.equal_range(context); + for (auto it = begin; it != end; ++it) { + if (interrupt_priority_[it->second] > value) { + context_irq_[context] = true; + context_if_[context]->SetIrq(true); + break; + } + } + } + } + return; + } + case 0x4: + // Claim/complete interrupt. + CompleteInterrupt(context, value); + return; + default: + LOG(ERROR) << "Invalid offset: " << offset; + break; + } +} + +uint32_t RiscVPlic::ClaimInterrupt(int context) { + if (context < 0 || context >= num_contexts_) return 0; + uint32_t id = 0; + int priority = 0; + // Find the id of the highest-priority pending interrupt for this context. + auto [begin, end] = context_to_source_.equal_range(context); + int count = 0; + int source = 0; + for (auto it = begin; it != end; ++it) { + source = it->second; + // If the source is not pending, skip it. + if (!IsPending(source)) continue; + // If the priority is less or equal to the current priority, skip it. + if (priority > interrupt_priority_[source]) continue; + // If the priority is the same, the lower id is chosen, so if the new + // source is greater than the current, go to the next. + if ((priority == interrupt_priority_[source]) && (id < source)) continue; + id = source; + priority = interrupt_priority_[source]; + count++; + } + if (id != 0) { + SetPending(id, false); + interrupt_claim_complete_[context] = id; + count--; + } + if (count == 0) { + // If there are zero remaining pending interrupts, clear the IRQ line. + context_if_[context]->SetIrq(false); + context_irq_[context] = false; + } + return id; +} + +void RiscVPlic::CompleteInterrupt(int context, uint32_t id) { + if (context < 0 || context >= num_contexts_) return; + // Check if id is in the set of enabled sources for context. + auto [begin, end] = context_to_source_.equal_range(context); + bool found = false; + for (auto it = begin; it != end; ++it) { + if (it->second == id) { + found = true; + break; + } + } + if (!found) return; + // The PLIC spec only requires that the id be valid for the set of + // interrupts enabled for the context, not that it matches the + // interrupt_claim_complete_ value. + auto source = interrupt_claim_complete_[context]; + interrupt_claim_complete_[context] = 0; + auto &info = gateway_info_[source]; + // Check to see if there's a pending level based interrupt w priority > 0. + if (info.pending && (interrupt_priority_[source] > 0)) { + // Set the plic pending bit but no need to set the ready bit as this will + // be forwarded to the plic core right away. + SetPlicPendingInterrupt(source); + return; + } + // Set the gateway ready bit to true. + info.ready = true; +} + +void RiscVPlic::SetPending(int source, bool value) { + int word = source >> 5; + int bit = source & 0x1f; + if (value) { + interrupt_pending_[word] |= 1 << bit; + } else { + interrupt_pending_[word] &= ~(1 << bit); + } +} + +bool RiscVPlic::IsPending(int source) { + int word = source >> 5; + int bit = source & 0x1f; + return (interrupt_pending_[word] & (1 << bit)) != 0; +} + +RiscVPlicSourceInterface::RiscVPlicSourceInterface(RiscVPlic *plic, int source, + bool is_level) + : plic_(plic), source_(source), is_level_(is_level) {} + +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv_plic.h b/riscv/riscv_plic.h new file mode 100644 index 0000000..8d8745f --- /dev/null +++ b/riscv/riscv_plic.h
@@ -0,0 +1,173 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_PLIC_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_PLIC_H_ + +#include <cstdint> + +#include "absl/container/btree_map.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/ref_count.h" +#include "mpact/sim/util/memory/memory_interface.h" + +// This file implements the RiscV PLIC (Platform Level Interrupt Controller). +// It has a memory mapped register interface that is used to control the +// interrupts to one or more contexts across one or more RiscV cores. +// +// The interrupt targets (contexts) are notified using the +// RiscVPlicIrqInterface interface. Therefore each target much register its +// own instance of this interface with the plic using the SetContext method. +// +// Interrupt sources communicate with the PLIC using the SetInterrupt method. +// +// The PLIC supports both level and edge triggered interrupts. + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::DataBuffer; +using ::mpact::sim::generic::Instruction; +using ::mpact::sim::generic::ReferenceCount; + +using ::mpact::sim::util::MemoryInterface; + +class RiscVPlicIrqInterface { + public: + virtual ~RiscVPlicIrqInterface() = default; + virtual void SetIrq(bool irq_value) = 0; +}; + +class RiscVPlic : public MemoryInterface { + public: + // The constructor takes the number of interrupt sources and the number of + // contexts that the PLIC can send interrupts to. A core usually has multiple + // contexts, one for each privilege level (machine, supervisor, etc.) that is + // capable of receiving and handling interrupts. + RiscVPlic(int num_sources, int num_contexts); + RiscVPlic() = delete; + RiscVPlic(const RiscVPlic &) = delete; + RiscVPlic &operator=(const RiscVPlic &) = delete; + ~RiscVPlic() override; + + // Configure the PLIC state according to the source and context configuration + // strings. The source configuration string is a semicolon separated list of + // <source>=<priority> items, where <source> is the interrupt source number + // and <priority> is an non-negative integer priority value. Higher values + // have higher priorities. A value of zero disables the source. Any source + // that is not configured is disabled by default. However, MMR writes can + // change the configured values. + // + // The context configuration string is a semicolon separated list of + // <context>=<threshold>,(<source>,<enable>)+ items, where <context> is the + // context number, <source> is the source number, <threshold> is a non + // negative integer that is the priority threshold for that context, and + // <enable> is a 0 or 1 value that indicates whether the interrupt source is + // enabled for that context. Multiple lines for the same context with + // different sources is allowed. Any context not configured is assumed to + // disable all sources with a zero priority threshold. + absl::Status Configure(absl::string_view source_cfg, + absl::string_view context_cfg); + // Interrupt request from the given interrupt source. + void SetInterrupt(int source, bool value, bool is_level); + + // MemoryInterface overrides. + // Non-vector load method. + void Load(uint64_t address, DataBuffer *db, Instruction *inst, + ReferenceCount *context) override; + // Vector load method - this is stubbed out. + void Load(DataBuffer *address_db, DataBuffer *mask_db, int el_size, + DataBuffer *db, Instruction *inst, + ReferenceCount *context) override; + // Non-vector store method. + void Store(uint64_t address, DataBuffer *db) override; + // Vector store method - this is stubbed out. + void Store(DataBuffer *address, DataBuffer *mask, int el_size, + DataBuffer *db) override; + + void SetContext(int context_no, RiscVPlicIrqInterface *context_if); + + private: + struct GatewayInfo { + // The gateway is able to accept the interrupt and send it to the plic core. + bool ready; + // Pending bit in gateway. Only gets set if the interrupt is level based. + bool pending; + GatewayInfo() : ready(true), pending(false) {} + }; + + // MMR read/write methods. + uint32_t Read(uint32_t offset); + void Write(uint32_t offset, uint32_t value); + // Interrupt claim. + uint32_t ClaimInterrupt(int context); + // Signal interrupt completion for the given context and interrupt id. + void CompleteInterrupt(int context, uint32_t id); + // Set plic core pending interrupt bit and trigger interrupt to context as + // needed. + void SetPlicPendingInterrupt(int source); + // Handling pending bits. + void SetPending(int source, bool value); + bool IsPending(int source); + + int num_sources_; + int num_contexts_; + // Interface to call to write the IRQ line for a context. + RiscVPlicIrqInterface **context_if_; + // Last value written to the IRQ line for a context. + bool *context_irq_ = nullptr; + // Source gateway info. + GatewayInfo *gateway_info_ = nullptr; + // Interrupt priorities by source. + uint32_t *interrupt_priority_ = nullptr; + // Pending interrupts by source - 32 bits per word. + uint32_t *interrupt_pending_ = nullptr; + // Enable bits per context per source - 32 bits per word. + // Array is organized as interrupt_enabled_[context][source / 32]. + uint32_t **interrupt_enabled_ = nullptr; + // Priority threshold by context. + uint32_t *priority_threshold_ = nullptr; + // Interrupt claim/complete register by context. + uint32_t *interrupt_claim_complete_ = nullptr; + // Map from source to context that has the source enabled. This must be + // updated whenever an an enable bit is changed for a context. + absl::btree_multimap<int, int> source_to_context_; + // Map from context to source for which the context has the source enabled. + absl::btree_multimap<int, int> context_to_source_; +}; + +class RiscVPlicSourceInterface : public RiscVPlicIrqInterface { + public: + RiscVPlicSourceInterface(RiscVPlic *plic, int source, bool is_level); + RiscVPlicSourceInterface() = delete; + ~RiscVPlicSourceInterface() override = default; + void SetIrq(bool irq_value) override { + if (plic_ != nullptr) plic_->SetInterrupt(source_, irq_value, is_level_); + }; + + private: + RiscVPlic *plic_ = nullptr; + int source_ = 0; + bool is_level_ = false; +}; + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_PLIC_H_
diff --git a/riscv/riscv_state.h b/riscv/riscv_state.h index 1342a5c..a09c8d6 100644 --- a/riscv/riscv_state.h +++ b/riscv/riscv_state.h
@@ -435,17 +435,16 @@ // Specialization for RiscV vector registers. template <> -inline std::pair<RVVectorRegister *, bool> RiscVState::GetRegister<RVVectorRegister>( - absl::string_view name) { +inline std::pair<RVVectorRegister *, bool> +RiscVState::GetRegister<RVVectorRegister>(absl::string_view name) { int vector_byte_width = vector_register_width(); if (vector_byte_width == 0) return std::make_pair(nullptr, false); auto ptr = registers()->find(std::string(name)); if (ptr != registers()->end()) - return std::make_pair(static_cast<RVVectorRegister *>(ptr->second), - false); + return std::make_pair(static_cast<RVVectorRegister *>(ptr->second), false); // Create a new register and return a pointer to the object. - return std::make_pair( - AddRegister<RVVectorRegister>(name, vector_byte_width), true); + return std::make_pair(AddRegister<RVVectorRegister>(name, vector_byte_width), + true); } } // namespace riscv
diff --git a/riscv/riscv_vector.bin_fmt b/riscv/riscv_vector.bin_fmt index cad9aea..11a1db0 100644 --- a/riscv/riscv_vector.bin_fmt +++ b/riscv/riscv_vector.bin_fmt
@@ -43,6 +43,8 @@ unsigned opcode[7]; overlays: unsigned uimm5[5] = vs1; + unsigned uimm6[6] = func6[0], vs1; + unsigned func5[5] = func6[5..1]; signed simm5[5] = vs1; unsigned rd[5] = vd; unsigned rs1[5] = vs1;
diff --git a/riscv/riscv_vector_basic_bit_manipulation_instructions.cc b/riscv/riscv_vector_basic_bit_manipulation_instructions.cc new file mode 100644 index 0000000..d6d8273 --- /dev/null +++ b/riscv/riscv_vector_basic_bit_manipulation_instructions.cc
@@ -0,0 +1,370 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/numeric/bits.h" +#include "absl/types/span.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/riscv_vector_instruction_helpers.h" +#include "riscv/riscv_vector_state.h" + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +namespace mpact { +namespace sim { +namespace riscv { + +void RV32VUnimplementedInstruction(const Instruction *inst) { + auto *state = static_cast<RiscVState *>(inst->state()); + state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0, + *ExceptionCode::kIllegalInstruction, + /*epc*/ inst->address(), inst); +} + +namespace { +template <typename T> +T BitReverse(T input) { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result <<= 1; + result |= (input & 1); + input >>= 1; + } + return result; +} + +template <class T> +constexpr T ByteSwap(T input) { + // TODO(julianmb): Once c++23 is supported, use std::byteswap. + T result = 0; + for (int i = 0; i < sizeof(T); ++i) { + result |= ((input >> (i * 8)) & 0xFF) << ((sizeof(T) - 1 - i) * 8); + } + return result; +} +} // namespace + +void Vandn(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 & ~vs1; }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 & ~vs1; }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 & ~vs1; }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 & ~vs1; }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vbrev8(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return BitReverse(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2) -> uint16_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2) -> uint32_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2) -> uint64_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vrev8(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2) -> uint8_t { return vs2; }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return ByteSwap(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return ByteSwap(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return ByteSwap(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vrol(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t { + uint8_t rotate_amount = vs1 & 0b0000'0111; + return absl::rotl(vs2, rotate_amount); + }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t { + uint8_t rotate_amount = vs1 & 0b0000'1111; + return absl::rotl(vs2, rotate_amount); + }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t { + uint8_t rotate_amount = vs1 & 0b0001'1111; + return absl::rotl(vs2, rotate_amount); + }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t { + uint8_t rotate_amount = vs1 & 0b0011'1111; + return absl::rotl(vs2, rotate_amount); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vror(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t { + uint8_t rotate_amount = vs1 & 0b0000'0111; + return absl::rotr(vs2, rotate_amount); + }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t { + uint8_t rotate_amount = vs1 & 0b0000'1111; + return absl::rotr(vs2, rotate_amount); + }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t { + uint8_t rotate_amount = vs1 & 0b0001'1111; + return absl::rotr(vs2, rotate_amount); + }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t { + uint8_t rotate_amount = vs1 & 0b0011'1111; + return absl::rotr(vs2, rotate_amount); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +// Instructions that are only in Zvbb + +void Vbrev(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return BitReverse(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return BitReverse(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return BitReverse(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return BitReverse(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vclz(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::countl_zero(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::countl_zero(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::countl_zero(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::countl_zero(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vctz(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::countr_zero(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::countr_zero(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::countr_zero(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::countr_zero(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void VectorVcpop(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::popcount(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::popcount(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::popcount(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::popcount(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vwsll(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint16_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint16_t { + return static_cast<uint16_t>(vs2) << (vs1 & 0x0F); + }); + case 2: + return RiscVBinaryVectorOp<uint32_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint32_t { + return static_cast<uint32_t>(vs2) << (vs1 & 0x1F); + }); + case 4: + return RiscVBinaryVectorOp<uint64_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint64_t { + return static_cast<uint64_t>(vs2) << (vs1 & 0x3F); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv_vector_basic_bit_manipulation_instructions.h b/riscv/riscv_vector_basic_bit_manipulation_instructions.h new file mode 100644 index 0000000..9c4f1c6 --- /dev/null +++ b/riscv/riscv_vector_basic_bit_manipulation_instructions.h
@@ -0,0 +1,51 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_ + +#include "mpact/sim/generic/instruction.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using Instruction = ::mpact::sim::generic::Instruction; + +void RV32VUnimplementedInstruction(const Instruction *inst); + +// Vector bit manipulation instructions. + +// Zvkb subset of instructions +void Vandn(Instruction *); +void Vbrev8(Instruction *); +void Vrev8(Instruction *); +void Vrol(Instruction *); +void Vror(Instruction *); + +// Zvbb instructions +void Vbrev(Instruction *); +void Vclz(Instruction *); +void Vctz(Instruction *); +// There is a name collision with an existing Vcpop instruction that stores the +// result in a scalar register. This implementation stores the result in a +// vector register. +void VectorVcpop(Instruction *); +void Vwsll(Instruction *); + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_
diff --git a/riscv/riscv_vector_permute_instructions.cc b/riscv/riscv_vector_permute_instructions.cc index 9192c88..bf993d7 100644 --- a/riscv/riscv_vector_permute_instructions.cc +++ b/riscv/riscv_vector_permute_instructions.cc
@@ -15,8 +15,11 @@ #include "riscv/riscv_vector_permute_instructions.h" #include <algorithm> +#include <cstdint> #include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" #include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/instruction.h" #include "riscv/riscv_register.h"
diff --git a/riscv/riscv_vector_unary_instructions.cc b/riscv/riscv_vector_unary_instructions.cc index c6fc407..318ad24 100644 --- a/riscv/riscv_vector_unary_instructions.cc +++ b/riscv/riscv_vector_unary_instructions.cc
@@ -15,10 +15,7 @@ #include "riscv/riscv_vector_unary_instructions.h" #include <cstdint> -#include <cstring> #include <functional> -#include <optional> -#include <type_traits> #include "absl/log/log.h" #include "absl/strings/str_cat.h" @@ -77,8 +74,7 @@ if (rv_vector->vstart()) return; if (rv_vector->vector_length() == 0) return; int sew = rv_vector->selected_element_width(); - auto *dest_db = inst->Destination(0)->AllocateDataBuffer(); - std::memset(dest_db->raw_ptr(), 0, dest_db->size<uint8_t>()); + auto *dest_db = inst->Destination(0)->CopyDataBuffer(); switch (sew) { case 1: dest_db->Set<int8_t>(0, generic::GetInstructionSource<int8_t>(inst, 0));
diff --git a/riscv/riscv_zvbb.bin_fmt b/riscv/riscv_zvbb.bin_fmt new file mode 100644 index 0000000..a9fb0f7 --- /dev/null +++ b/riscv/riscv_zvbb.bin_fmt
@@ -0,0 +1,47 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv32g.bin_fmt" +#include "riscv/riscv_vector.bin_fmt" + +decoder ZVBB { + namespace mpact::sim::riscv::zvbb; + opcode_enum = "OpcodeEnum"; + includes { + #include "riscv/zvbb_decoder.h" + } + RiscVZvbbInst32 = { RiscVZvkbInst32, RiscVBasicBitInst32 }; +} + +instruction group RiscVZvkbInst32[32] : VArith { + vandn_vv : VArith : func6 == 0b000'001, func3 == 0b000, opcode == 0b101'0111; + vandn_vx : VArith : func6 == 0b000'001, func3 == 0b100, opcode == 0b101'0111; + vbrev8_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'000; + vrev8_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'001; + vrol_vv : VArith : func6 == 0b010'101, func3 == 0b000, opcode == 0b101'0111; + vrol_vx : VArith : func6 == 0b010'101, func3 == 0b100, opcode == 0b101'0111; + vror_vv : VArith : func6 == 0b010'100, func3 == 0b000, opcode == 0b101'0111; + vror_vx : VArith : func6 == 0b010'100, func3 == 0b100, opcode == 0b101'0111; + vror_vi : VArith : func5 == 0b01010, func3 == 0b011, opcode == 0b101'0111; +} + +instruction group RiscVBasicBitInst32[32] : VArith { + vbrev_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'010; + vclz_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'100; + vctz_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'101; + vcpop_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'110; + vwsll_vv : VArith : func6 == 0b110'101, func3 == 0b000, opcode == 0b101'0111; + vwsll_vx : VArith : func6 == 0b110'101, func3 == 0b100, opcode == 0b101'0111; + vwsll_vi : VArith : func6 == 0b110'101, func3 == 0b011, opcode == 0b101'0111; +}
diff --git a/riscv/riscv_zvbb.isa b/riscv/riscv_zvbb.isa new file mode 100644 index 0000000..f6f5926 --- /dev/null +++ b/riscv/riscv_zvbb.isa
@@ -0,0 +1,103 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the ISA description of the RiscV zvbb extention +// instructions. + +isa ZVBB { + namespace mpact::sim::riscv::zvbb; + slots { + riscv_zvbb; + } +} + +// First disasm field is 18 char wide and left justified. +disasm widths = {-18}; + +#include "riscv/riscv_vector.isa" + +slot riscv_zvkb { + includes { + #include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + vandn_vv{: vs2, vs1, vmask : vd}, + disasm: "vandn.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vandn"; + vandn_vx{: vs2, rs1, vmask : vd}, + disasm: "vandn.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vandn"; + vbrev8_v{: vs2, vmask : vd}, + disasm: "vbrev8.v", "%vd, %vs2, %vmask", + semfunc: "&Vbrev8"; + vrev8_v{: vs2, vmask : vd}, + disasm: "vrev8.v", "%vd, %vs2, %vmask", + semfunc: "&Vrev8"; + vrol_vv{: vs2, vs1, vmask : vd}, + disasm: "vrol.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vrol"; + vrol_vx{: vs2, rs1, vmask : vd}, + disasm: "vrol.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vrol"; + vror_vv{: vs2, vs1, vmask : vd}, + disasm: "vror.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vror"; + vror_vx{: vs2, rs1, vmask : vd}, + disasm: "vror.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vror"; + vror_vi{: vs2, uimm6, vmask : vd}, + disasm: "vror.vi", "%vd, %vs2, %uimm6, %vmask", + semfunc: "&Vror"; + } +} + +slot riscv_zvbb : riscv_zvkb { + includes { + #include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + vbrev_v{: vs2, vmask : vd}, + disasm: "vbrev.v", "%vd, %vs2, %vmask", + semfunc: "&Vbrev"; + vclz_v{: vs2, vmask : vd}, + disasm: "vclz.v", "%vd, %vs2, %vmask", + semfunc: "&Vclz"; + vctz_v{: vs2, vmask : vd}, + disasm: "vctz.v", "%vd, %vs2, %vmask", + semfunc: "&Vctz"; + vcpop_v{: vs2, vmask : vd}, + disasm: "vcpop.v", "%vd, %vs2, %vmask", + semfunc: "&VectorVcpop"; + vwsll_vv{: vs2, vs1, vmask : vd}, + disasm: "vwsll.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vwsll"; + vwsll_vx{: vs2, rs1, vmask : vd}, + disasm: "vwsll.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vwsll"; + vwsll_vi{: vs2, uimm5, vmask : vd}, + disasm: "vwsll.vi", "%vd, %vs2, %uimm5, %vmask", + semfunc: "&Vwsll"; + } +} +
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index 82910b6..4ea63fe 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -334,6 +334,23 @@ ) cc_test( + name = "zvbb_encoding_test", + size = "small", + srcs = [ + "zvbb_encoding_test.cc", + ], + deps = [ + "//riscv:riscv32g_bitmanip_decoder", + "//riscv:riscv_state", + "//riscv:zvbb_decoder", + "//riscv:zvbb_isa", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + +cc_test( name = "riscv32_htif_semihost_test", size = "small", srcs = [ @@ -727,6 +744,21 @@ ) cc_test( + name = "riscv_plic_test", + size = "small", + srcs = [ + "riscv_plic_test.cc", + ], + deps = [ + "//riscv:riscv_plic", + "@com_google_absl//absl/log:check", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_googlesource_code_re2//:re2", + ], +) + +cc_test( name = "librenode_mpact_riscv32.so_test", size = "small", srcs = ["librenode_mpact_riscv32_so_test.cc"], @@ -876,6 +908,24 @@ ], ) +cc_test( + name = "riscv_vector_basic_bit_manipulation_instructions_test", + size = "small", + srcs = [ + "riscv_vector_basic_bit_manipulation_test.cc", + ], + deps = [ + ":riscv_vector_instructions_test_base", + "//riscv:riscv_state", + "//riscv:riscv_v", + "//riscv:riscv_vector_basic_bit_manipulation_instructions", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + ], +) + config_setting( name = "arm_cpu", values = {"cpu": "arm"},
diff --git a/riscv/test/riscv_fp_test_base.h b/riscv/test/riscv_fp_test_base.h index fc653da..f427a52 100644 --- a/riscv/test/riscv_fp_test_base.h +++ b/riscv/test/riscv_fp_test_base.h
@@ -332,24 +332,26 @@ // Creates source and destination scalar register operands for the registers // named in the two vectors and append them to the given instruction. + template <typename T> void AppendRegisterOperands(Instruction *inst, const std::vector<std::string> &sources, const std::vector<std::string> &destinations) { for (auto ®_name : sources) { - auto *reg = state_->GetRegister<RV32Register>(reg_name).first; + auto *reg = state_->GetRegister<T>(reg_name).first; inst->AppendSource(reg->CreateSourceOperand()); } for (auto ®_name : destinations) { - auto *reg = state_->GetRegister<RV32Register>(reg_name).first; + auto *reg = state_->GetRegister<T>(reg_name).first; inst->AppendDestination(reg->CreateDestinationOperand(0)); } } // Creates source and destination scalar register operands for the registers // named in the two vectors and append them to the default instruction. + template <typename T> void AppendRegisterOperands(const std::vector<std::string> &sources, const std::vector<std::string> &destinations) { - AppendRegisterOperands(instruction_, sources, destinations); + AppendRegisterOperands<T>(instruction_, sources, destinations); } // named register and sets it to the corresponding value. @@ -437,7 +439,8 @@ const std::string kRdName = absl::StrCat(reg_prefixes[1], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name}, {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); FillArrayWithRandomFPValues<LHS>(lhs_span); using LhsInt = typename FPTypeInfo<LHS>::IntType; *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; @@ -488,7 +491,8 @@ const std::string kRdName = absl::StrCat(reg_prefixes[1], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name, kRmName}, {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); instruction_->AppendDestination(flag_op); FillArrayWithRandomFPValues<LHS>(lhs_span); @@ -553,7 +557,8 @@ const std::string kRdName = absl::StrCat(reg_prefixes[2], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name, kR2Name}, {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); instruction_->AppendDestination(flag_op); FillArrayWithRandomFPValues<LHS>(lhs_span); @@ -615,7 +620,8 @@ const std::string kRdName = absl::StrCat(reg_prefixes[2], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name, kR2Name}, {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); instruction_->AppendDestination(flag_op); FillArrayWithRandomFPValues<LHS>(lhs_span); @@ -684,7 +690,9 @@ const std::string kRdName = absl::StrCat(reg_prefixes[3], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kR3Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name, kR2Name, kR3Name}, + {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); FillArrayWithRandomFPValues<LHS>(lhs_span); FillArrayWithRandomFPValues<MHS>(mhs_span); FillArrayWithRandomFPValues<RHS>(rhs_span); @@ -745,7 +753,9 @@ const std::string kRdName = absl::StrCat(reg_prefixes[3], 5); // This is used for the rounding mode operand. const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kR3Name, kRmName}, {kRdName}); + AppendRegisterOperands<RVFpRegister>({kR1Name, kR2Name, kR3Name}, + {kRdName}); + AppendRegisterOperands<RV32Register>({kRmName}, {}); auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); instruction_->AppendDestination(flag_op); FillArrayWithRandomFPValues<LHS>(lhs_span);
diff --git a/riscv/test/riscv_plic_test.cc b/riscv/test/riscv_plic_test.cc new file mode 100644 index 0000000..62c150c --- /dev/null +++ b/riscv/test/riscv_plic_test.cc
@@ -0,0 +1,315 @@ + +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv_plic.h" + +#include <cstdint> + +#include "absl/log/check.h" +#include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/data_buffer.h" + +// This file contains unit tests for the RiscV PLIC model. + +namespace { + +using ::mpact::sim::generic::DataBuffer; +using ::mpact::sim::generic::DataBufferFactory; +using ::mpact::sim::riscv::RiscVPlic; +using ::mpact::sim::riscv::RiscVPlicIrqInterface; + +constexpr int kNumSources = 32; +constexpr int kNumContexts = 3; + +// Mock interrupt target for a context. +class MockRiscVInterruptTarget : public RiscVPlicIrqInterface { + public: + void SetIrq(bool irq_value) override { irq_value_ = irq_value; } + + bool irq_value() const { return irq_value_; } + void set_irq_value(bool value) { irq_value_ = value; } + + private: + bool irq_value_ = false; +}; + +// Test fixture. +class RiscVPlicTest : public ::testing::Test { + protected: + RiscVPlicTest() { + plic_ = new RiscVPlic(kNumSources, kNumContexts); + for (int i = 0; i < kNumContexts; ++i) { + target_[i] = new MockRiscVInterruptTarget(); + plic_->SetContext(i, target_[i]); + } + db_ = db_factory_.Allocate<uint32_t>(1); + db_->set_latency(0); + } + + ~RiscVPlicTest() override { + delete plic_; + for (int i = 0; i < kNumContexts; ++i) { + delete target_[i]; + } + db_->DecRef(); + } + + // Convenience methods to read/write the PLIC state using its memory + // interface. + bool GetEnable(int source, int context) { + uint32_t word = 0x2000 + (context * 0x80) + (source >> 5); + int bit = source & 0x1f; + plic()->Load(word, db_, nullptr, nullptr); + return (db_->Get<uint32_t>(0) & (1 << bit)) != 0; + } + + void SetEnable(int source, int context, bool value) { + uint32_t word = 0x2000 + (context * 0x80) + (source >> 5); + int bit = source & 0x1f; + plic()->Load(word, db_, nullptr, nullptr); + auto span = db_->Get<uint32_t>(); + uint32_t mask = ~(1 << bit); + uint32_t u_val = static_cast<uint32_t>(value); + span[0] = (span[0] & mask) | (u_val << bit); + plic()->Store(word, db_); + } + + bool GetPending(int source) { + uint32_t word = 0x1000 + ((source >> 5) << 2); + int bit = source & 0x1f; + plic()->Load(word, db_, nullptr, nullptr); + return (db_->Get<uint32_t>(0) & (1 << bit)) != 0; + } + + void SetPending(int source, bool value) { + int word = 0x1000 + ((source >> 5) << 2); + int bit = source & 0x1f; + plic()->Load(word, db_, nullptr, nullptr); + auto span = db_->Get<uint32_t>(); + uint32_t mask = ~(1 << bit); + span[0] = (span[0] & mask) | (static_cast<uint32_t>(value) << bit); + plic()->Store(word, db_); + } + + uint32_t GetPriority(int source) { + plic()->Load(source << 2, db_, nullptr, nullptr); + return db_->Get<uint32_t>(0); + } + + void SetPriority(int source, uint32_t priority) { + db_->Set<uint32_t>(0, priority); + plic()->Store(source << 2, db_); + } + + int GetPriorityThreshold(int context) { + plic()->Load(0x20'0000 + context * 0x1000, db_, nullptr, nullptr); + return db_->Get<uint32_t>(0); + } + + void SetPriorityThreshold(int context, uint32_t threshold) { + db_->Set<uint32_t>(0, threshold); + plic()->Store(0x20'0000 + context * 0x1000, db_); + } + + uint32_t GetInterruptClaim(int context) { + plic()->Load(0x20'0000 + context * 0x1000 + 4, db_, nullptr, nullptr); + return db_->Get<uint32_t>(0); + } + + void SetInterruptClaim(int context, uint32_t claim) { + db_->Set<uint32_t>(0, claim); + plic()->Store(0x20'0000 + context * 0x1000 + 4, db_); + } + + void SetDefaultConfig() { + // Set priorities to increasing values for all sources. + // Thresholds are set 8, 16 and 24 for the three contexts respectively. + // Source 30 is enabled for all contexts. + auto status = plic()->Configure( + "0=0;1=1;2=2;3=3;4=4;5=5;6=6;7=7;8=8;9=9;10=10;11=11;" + "12=12;13=13;14=14;15=15;16=16;17=17;18=18;19=19;20=20;" + "21=21;22=22;23=23;24=24;25=25;26=26;27=27;28=28;29=29;" + "30=30;31=31;", + "0=8,1,1,2,1,3,1,4,1,5,1,6,1,7,1,8,1,9,1,10,1,30,1;" + "1=16,11,1,12,1,13,1,14,1,15,1,16,1,17,1,18,1,19,1,20,1,30,1;" + "2=24,19,1,20,1,21,1,22,1,23,1,24,1,25,1,26,1,27,1,28,1,29,1,30,1,31," + "1;"); + CHECK_OK(status); + } + + // Accessors. + RiscVPlic *plic() { return plic_; } + DataBufferFactory &db_factory() { return db_factory_; } + MockRiscVInterruptTarget *target(int i) { return target_[i]; } + + private: + DataBuffer *db_; + DataBufferFactory db_factory_; + RiscVPlic *plic_ = nullptr; + MockRiscVInterruptTarget *target_[kNumContexts]; +}; + +// Test that the initial state of the PLIC is as expected. Nothing enabled, +// priorities at zero, priority thresholds at zero, no pending interrupts, +// no claimed interrupts, no IRQ lines set. +TEST_F(RiscVPlicTest, InitialState) { + for (int s = 0; s < kNumSources; ++s) { + for (int c = 0; c < kNumContexts; ++c) { + EXPECT_FALSE(GetEnable(s, c)); + EXPECT_FALSE(GetPending(s)); + EXPECT_EQ(GetPriority(s), 0) << "Source " << s; + EXPECT_EQ(GetPriorityThreshold(c), 0); + EXPECT_EQ(GetInterruptClaim(c), 0); + EXPECT_FALSE(target(c)->irq_value()); + } + } +} + +// Verify that the PLIC is configured as expected with the default configuration +// strings. +TEST_F(RiscVPlicTest, DefaultConfiguration) { + SetDefaultConfig(); + for (int s = 0; s < kNumSources; ++s) { + for (int c = 0; c < kNumContexts; ++c) { + switch (c) { + case 0: + EXPECT_EQ(GetEnable(s, c), ((s >= 1) && (s <= 10)) || (s == 30)) + << "Source " << s << " context " << c; + break; + case 1: + EXPECT_EQ(GetEnable(s, c), ((s >= 11) && (s <= 20)) || (s == 30)) + << "Source " << s << " context " << c; + break; + case 2: + EXPECT_EQ(GetEnable(s, c), (s >= 19) && (s <= 31)) + << "Source " << s << " context " << c; + break; + } + EXPECT_FALSE(GetPending(s)); + EXPECT_EQ(GetPriority(s), s) << "Source " << s; + EXPECT_EQ(GetPriorityThreshold(c), (c + 1) * 8) << "Context " << c; + EXPECT_EQ(GetInterruptClaim(c), 0) << "Context " << c; + EXPECT_FALSE(target(c)->irq_value()) << "Context " << c; + } + } +} + +// Test operation of level triggered interrupts. +TEST_F(RiscVPlicTest, LevelTriggeredInterrupt) { + SetDefaultConfig(); + // Set irq for source 10 level sensitive. + plic()->SetInterrupt(10, /*value=*/true, /*is_level=*/true); + // Expect pending to be set. + EXPECT_TRUE(GetPending(10)); + // Clear the irq. + plic()->SetInterrupt(10, /*value=*/false, /*is_level=*/true); + // Expect pending to still be set. + EXPECT_TRUE(GetPending(10)); + // Expect irq to be set for context 0; + EXPECT_TRUE(target(0)->irq_value()); + // Clear the irq. + target(0)->set_irq_value(false); + // Now raise the irq again. + plic()->SetInterrupt(10, /*value=*/true, /*is_level=*/true); + // Expect pending to be set. + EXPECT_TRUE(GetPending(10)); + // Claim the interrupt. + uint32_t id = GetInterruptClaim(0); + EXPECT_EQ(id, 10); + // A second claim should return 0. + EXPECT_EQ(GetInterruptClaim(0), 0); + // Interrupt is no longer pending. + EXPECT_FALSE(GetPending(10)); + // Complete the interrupt. + SetInterruptClaim(0, id); + // Expect pending to be set, as the level is still high. + EXPECT_TRUE(GetPending(10)); + // Lower the irq. + plic()->SetInterrupt(10, /*value=*/false, /*is_level=*/true); + // Expect pending to still be set. + EXPECT_TRUE(GetPending(10)); +} + +// Test operation of edge triggered interrupts. +TEST_F(RiscVPlicTest, EdgeTriggeredInterrupt) { + SetDefaultConfig(); + // Set irq for source 10 level sensitive. + plic()->SetInterrupt(10, /*value=*/true, /*is_level=*/false); + // Expect pending to be set. + EXPECT_TRUE(GetPending(10)); + // Expect irq to be set for context 0; + EXPECT_TRUE(target(0)->irq_value()); + // Claim the interrupt. + uint32_t id = GetInterruptClaim(0); + EXPECT_EQ(id, 10); + // A second claim should return 0. + EXPECT_EQ(GetInterruptClaim(0), 0); + // Interrupt is no longer pending. + EXPECT_FALSE(GetPending(10)); + // Complete the interrupt. + SetInterruptClaim(0, id); + // Expect pending to be cleared. + EXPECT_FALSE(GetPending(10)); +} + +TEST_F(RiscVPlicTest, PriorityThreshold) { + SetDefaultConfig(); + // Signal interrupts for sources 5-10. + for (int i = 5; i <= 10; ++i) { + plic()->SetInterrupt(i, /*value=*/true, /*is_level=*/false); + // Only sources 9 and 10 should trigger interrupts. + EXPECT_EQ(target(0)->irq_value(), i > 8); + } + // Now claim the interrupts, all should be claimed in order of priority,' + // even those below the threshold. The IRQ line should remain high until + // there are no more pending interrupts. + for (int i = 10; i >= 5; --i) { + EXPECT_TRUE(target(0)->irq_value()); + EXPECT_TRUE(GetPending(i)); + uint32_t id = GetInterruptClaim(0); + EXPECT_EQ(id, i); + SetInterruptClaim(0, id); + EXPECT_FALSE(GetPending(i)); + // IRQ remains high until the last interrupt is completed. + EXPECT_EQ(target(0)->irq_value(), i != 5); + } +} + +TEST_F(RiscVPlicTest, MultipleTargets) { + SetDefaultConfig(); + // Signal interrupt for source 30. + plic()->SetInterrupt(30, /*value=*/true, /*is_level=*/false); + EXPECT_TRUE(GetPending(30)); + // Expect irq to be set for all contexts. + for (int c = 0; c < kNumContexts; ++c) { + EXPECT_TRUE(target(c)->irq_value()); + } + // Try claiming the interrupt for each context, only the first will succeed. + for (int c = 0; c < kNumContexts; ++c) { + uint32_t id = GetInterruptClaim(c); + // Expect pending to be cleared. + EXPECT_FALSE(GetPending(30)); + if (c == 0) { + EXPECT_EQ(id, 30); + } else { + EXPECT_EQ(id, 0); + } + // The target should be cleared. + EXPECT_EQ(target(c)->irq_value(), 0); + } + // Complete the interrupt for context 0. + SetInterruptClaim(0, 30); +} +} // namespace
diff --git a/riscv/test/riscv_vector_basic_bit_manipulation_test.cc b/riscv/test/riscv_vector_basic_bit_manipulation_test.cc new file mode 100644 index 0000000..7ddd73d --- /dev/null +++ b/riscv/test/riscv_vector_basic_bit_manipulation_test.cc
@@ -0,0 +1,463 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <sys/types.h> + +#include <cstdint> + +#include "absl/strings/str_cat.h" +#include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" +#include "riscv/test/riscv_vector_instructions_test_base.h" + +// This file contains tests for the RiscV vector basic bit manipulations. + +namespace { + +using ::mpact::sim::generic::WideType; +using ::mpact::sim::riscv::RV32Register; +using ::mpact::sim::riscv::RVVectorRegister; +using ::mpact::sim::riscv::Vandn; +using ::mpact::sim::riscv::Vbrev; +using ::mpact::sim::riscv::Vbrev8; +using ::mpact::sim::riscv::Vclz; +using ::mpact::sim::riscv::Vctz; +using ::mpact::sim::riscv::VectorVcpop; +using ::mpact::sim::riscv::Vrev8; +using ::mpact::sim::riscv::Vrol; +using ::mpact::sim::riscv::Vror; +using ::mpact::sim::riscv::Vwsll; +using ::mpact::sim::riscv::test::RiscVVectorInstructionsTestBase; + +class RiscVVectorBasicBitManipulationTest + : public RiscVVectorInstructionsTestBase {}; + +// Helper function for testing the vandn_vv instruction. Generate the expected +// result using the bitwise operator. +template <typename T> +inline void VandnVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vandn); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vandn", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { return ~vs1 & vs2; }); +} + +// Helper function for testing the vandn_vx instruction. Generate the expected +// result using the bitwise operator. +template <typename T> +inline void VandnVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vandn); + tester->BinaryOpTestHelperVX<T, T, T>( + absl::StrCat("Vandn", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { return ~rs1 & vs2; }); +} + +// Helper function for testing the vbrev_v instruction. Generate the expected +// result by reversing the input bits. +template <typename T> +inline void VbrevVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vbrev); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("Vbrev", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result = (result << 1) | (vs2 & 1); + vs2 >>= 1; + } + return result; + }); +} + +// Helper function for testing the vbrev8_v instruction. Generate the expected +// result by reversing the bits in each of the input bytes. +template <typename T> +inline void Vbrev8VHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vbrev8); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("Vbrev8", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T result = 0; + for (int offset = 0; offset < sizeof(T) * 8; offset += 8) { + uint8_t byte = (vs2 >> offset) & 0xFF; + T reversed_byte = 0; + for (int j = 0; j < 8; ++j) { + reversed_byte = (reversed_byte << 1) | (byte & 1); + byte >>= 1; + } + result |= reversed_byte << offset; + } + return result; + }); +} + +// Helper function for testing the vbrev_v instruction. Generate the expected +// result by reversing the bytes of the input. +template <typename T> +inline void Vrev8VHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrev8); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("Vrev8", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T result = 0; + for (int offset = 0; offset < sizeof(T) * 8; offset += 8) { + uint8_t byte = (vs2 >> offset) & 0xff; + result = (result << 8) | byte; + } + return result; + }); +} + +// Helper function for testing the vrol_vv instruction. Generate the expected +// result by rotating the input bits left. +template <typename T> +inline void VrolVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrol); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vrol", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { + T bitsize = sizeof(T) * 8; + T shift_mask = bitsize - 1; + uint8_t shiftl_amount = vs1 & shift_mask; + uint8_t shiftr_amount = (bitsize - shiftl_amount) & shift_mask; + return (vs2 << shiftl_amount) | (vs2 >> shiftr_amount); + }); +} + +// Helper function for testing the vrol_vx instruction. Generate the expected +// result by rotating the input bits left. +template <typename T> +inline void VrolVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrol); + tester->BinaryOpTestHelperVX<T, T, T>( + absl::StrCat("Vrol", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { + T bitsize = sizeof(T) * 8; + T shift_mask = bitsize - 1; + uint8_t shiftl_amount = rs1 & shift_mask; + uint8_t shiftr_amount = (bitsize - shiftl_amount) & shift_mask; + return (vs2 << shiftl_amount) | (vs2 >> shiftr_amount); + }); +} + +// Helper function for testing the vror_vv instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { + T bitsize = sizeof(T) * 8; + T shift_mask = bitsize - 1; + uint8_t shiftr_amount = vs1 & shift_mask; + uint8_t shiftl_amount = (bitsize - shiftr_amount) & shift_mask; + return (vs2 << shiftl_amount) | (vs2 >> shiftr_amount); + }); +} + +// Helper function for testing the vror_vx instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { + T bitsize = sizeof(T) * 8; + T shift_mask = bitsize - 1; + uint8_t shiftr_amount = rs1 & shift_mask; + uint8_t shiftl_amount = (bitsize - shiftr_amount) & shift_mask; + return (vs2 << shiftl_amount) | (vs2 >> shiftr_amount); + }); +} + +// Helper function for testing the vror_vi instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVIHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vi"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T imm) -> T { + T bitsize = sizeof(T) * 8; + T shift_mask = bitsize - 1; + uint8_t shiftr_amount = imm & shift_mask; + uint8_t shiftl_amount = (bitsize - shiftr_amount) & shift_mask; + return (vs2 << shiftl_amount) | (vs2 >> shiftr_amount); + }); +} + +// Helper function for testing the vclz_v instruction. Generate the expected +// result by counting the number of leading zeros in the input. +template <typename T> +inline void VclzVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vclz); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("vclz", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T mask = static_cast<T>(1) << (sizeof(T) * 8 - 1); + for (int i = 0; i < sizeof(T) * 8; ++i) { + if ((vs2 & mask) != 0) { + return i; + } + mask >>= 1; + } + return static_cast<T>(sizeof(T) * 8); + }); +} + +// Helper function for testing the vctz_v instruction. Generate the expected +// result by counting the number of trailing zeros in the input. +template <typename T> +inline void VctzVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vctz); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("vctz", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T mask = static_cast<T>(1); + for (int i = 0; i < sizeof(T) * 8; ++i) { + if ((vs2 & mask) != 0) { + return i; + } + mask <<= 1; + } + return static_cast<T>(sizeof(T) * 8); + }); +} + +// Helper function for testing the vcpop_v instruction. Generate the expected +// result by counting the number of bits set in the input. +template <typename T> +inline void VcpopVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&VectorVcpop); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("vcpop", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result += (vs2 & 1) ? 1 : 0; + vs2 >>= 1; + } + return result; + }); +} + +// Helper function for testing the vwsll_vv instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = vs1 & shift_mask; + return static_cast<WT>(vs2) << shift_amount; + }); +} + +// Helper function for testing the vwsll_vx instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = rs1 & shift_mask; + return static_cast<WT>(vs2) << shift_amount; + }); +} + +// Helper function for testing the vwsll_vi instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVIHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vi"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T imm) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = imm & shift_mask; + return static_cast<WT>(vs2) << shift_amount; + }); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vandn) { + VandnVVHelper<uint8_t>(this); + ResetInstruction(); + VandnVVHelper<uint16_t>(this); + ResetInstruction(); + VandnVVHelper<uint32_t>(this); + ResetInstruction(); + VandnVVHelper<uint64_t>(this); + ResetInstruction(); + + VandnVXHelper<uint8_t>(this); + ResetInstruction(); + VandnVXHelper<uint16_t>(this); + ResetInstruction(); + VandnVXHelper<uint32_t>(this); + ResetInstruction(); + VandnVXHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vbrev8) { + Vbrev8VHelper<uint8_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint16_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint32_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vrev8) { + Vrev8VHelper<uint8_t>(this); + ResetInstruction(); + Vrev8VHelper<uint16_t>(this); + ResetInstruction(); + Vrev8VHelper<uint32_t>(this); + ResetInstruction(); + Vrev8VHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vrol) { + VrolVVHelper<uint8_t>(this); + ResetInstruction(); + VrolVVHelper<uint16_t>(this); + ResetInstruction(); + VrolVVHelper<uint32_t>(this); + ResetInstruction(); + VrolVVHelper<uint64_t>(this); + ResetInstruction(); + + VrolVXHelper<uint8_t>(this); + ResetInstruction(); + VrolVXHelper<uint16_t>(this); + ResetInstruction(); + VrolVXHelper<uint32_t>(this); + ResetInstruction(); + VrolVXHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vror) { + VrorVVHelper<uint8_t>(this); + ResetInstruction(); + VrorVVHelper<uint16_t>(this); + ResetInstruction(); + VrorVVHelper<uint32_t>(this); + ResetInstruction(); + VrorVVHelper<uint64_t>(this); + ResetInstruction(); + + VrorVXHelper<uint8_t>(this); + ResetInstruction(); + VrorVXHelper<uint16_t>(this); + ResetInstruction(); + VrorVXHelper<uint32_t>(this); + ResetInstruction(); + VrorVXHelper<uint64_t>(this); + ResetInstruction(); + + VrorVIHelper<uint8_t>(this); + ResetInstruction(); + VrorVIHelper<uint16_t>(this); + ResetInstruction(); + VrorVIHelper<uint32_t>(this); + ResetInstruction(); + VrorVIHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vbrev) { + VbrevVHelper<uint8_t>(this); + ResetInstruction(); + VbrevVHelper<uint16_t>(this); + ResetInstruction(); + VbrevVHelper<uint32_t>(this); + ResetInstruction(); + VbrevVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vclzv) { + VclzVHelper<uint8_t>(this); + ResetInstruction(); + VclzVHelper<uint16_t>(this); + ResetInstruction(); + VclzVHelper<uint32_t>(this); + ResetInstruction(); + VclzVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vctz) { + VctzVHelper<uint8_t>(this); + ResetInstruction(); + VctzVHelper<uint16_t>(this); + ResetInstruction(); + VctzVHelper<uint32_t>(this); + ResetInstruction(); + VctzVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vcpop) { + VcpopVHelper<uint8_t>(this); + ResetInstruction(); + VcpopVHelper<uint16_t>(this); + ResetInstruction(); + VcpopVHelper<uint32_t>(this); + ResetInstruction(); + VcpopVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vwsll) { + VwsllVVHelper<uint8_t>(this); + ResetInstruction(); + VwsllVVHelper<uint16_t>(this); + ResetInstruction(); + VwsllVVHelper<uint32_t>(this); + ResetInstruction(); + + VwsllVXHelper<uint8_t>(this); + ResetInstruction(); + VwsllVXHelper<uint16_t>(this); + ResetInstruction(); + VwsllVXHelper<uint32_t>(this); + ResetInstruction(); + + VwsllVIHelper<uint8_t>(this); + ResetInstruction(); + VwsllVIHelper<uint16_t>(this); + ResetInstruction(); + VwsllVIHelper<uint32_t>(this); + ResetInstruction(); +} + +} // namespace
diff --git a/riscv/test/riscv_vector_fp_compare_instructions_test.cc b/riscv/test/riscv_vector_fp_compare_instructions_test.cc index 87648ef..7ef13c2 100644 --- a/riscv/test/riscv_vector_fp_compare_instructions_test.cc +++ b/riscv/test/riscv_vector_fp_compare_instructions_test.cc
@@ -228,7 +228,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kFs1Name}, {}); + AppendRegisterOperands<Fs1>({kFs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); // Initialize input values. FillArrayWithRandomValues<Vs2>(vs2_span);
diff --git a/riscv/test/riscv_vector_fp_instructions_test.cc b/riscv/test/riscv_vector_fp_instructions_test.cc index 0aa2d50..c23519c 100644 --- a/riscv/test/riscv_vector_fp_instructions_test.cc +++ b/riscv/test/riscv_vector_fp_instructions_test.cc
@@ -330,7 +330,7 @@ auto vs2_span = Span<Vs2>(vs2_value); auto vd_span = Span<Vd>(vd_value); AppendVectorRegisterOperands({kVs2}, {kVd}); - AppendRegisterOperands({kFs1Name}, {}); + AppendRegisterOperands<RVFpRegister>({kFs1Name}, {}); AppendVectorRegisterOperands({kVd, kVmask}, {kVd}); SetVectorRegisterValues<uint8_t>( {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
diff --git a/riscv/test/riscv_vector_fp_test_utilities.h b/riscv/test/riscv_vector_fp_test_utilities.h index 96c8120..9ab9d3a 100644 --- a/riscv/test/riscv_vector_fp_test_utilities.h +++ b/riscv/test/riscv_vector_fp_test_utilities.h
@@ -628,7 +628,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {kVd}); - AppendRegisterOperands({kFs1Name}, {}); + AppendRegisterOperands<Fs1>({kFs1Name}, {}); auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); instruction_->AppendDestination(flag_op); AppendVectorRegisterOperands({kVmask}, {}); @@ -797,7 +797,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {kVd}); - AppendRegisterOperands({kFs1Name}, {}); + AppendRegisterOperands<Fs1>({kFs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {}); SetVectorRegisterValues<uint8_t>( {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
diff --git a/riscv/test/riscv_vector_fp_unary_instructions_test.cc b/riscv/test/riscv_vector_fp_unary_instructions_test.cc index 6fb2b30..e3ee470 100644 --- a/riscv/test/riscv_vector_fp_unary_instructions_test.cc +++ b/riscv/test/riscv_vector_fp_unary_instructions_test.cc
@@ -610,7 +610,7 @@ // Test vfmv.f.s instruction - move element 0 to scalar fp register. TEST_F(RiscVFPUnaryInstructionsTest, VfmvToScalar) { SetSemanticFunction(&Vfmvfs); - AppendRegisterOperands({}, {kFs1Name}); + AppendRegisterOperands<RVFpRegister>({}, {kFs1Name}); AppendVectorRegisterOperands({kVs2}, {}); for (int byte_sew : {1, 2, 4, 8}) { int vlen = kVectorLengthInBytes / byte_sew; @@ -650,7 +650,7 @@ // Test vfmv.f.s instruction - move scalar fp register to element 0. TEST_F(RiscVFPUnaryInstructionsTest, VfmvFromScalar) { SetSemanticFunction(&Vfmvsf); - AppendRegisterOperands({kFs1Name}, {}); + AppendRegisterOperands<RVFpRegister>({kFs1Name}, {}); AppendVectorRegisterOperands({}, {kVd}); for (int byte_sew : {1, 2, 4, 8}) { int vlen = kVectorLengthInBytes / byte_sew;
diff --git a/riscv/test/riscv_vector_instructions_test_base.h b/riscv/test/riscv_vector_instructions_test_base.h index a243929..7ba43ce 100644 --- a/riscv/test/riscv_vector_instructions_test_base.h +++ b/riscv/test/riscv_vector_instructions_test_base.h
@@ -180,6 +180,7 @@ // Creates source and destination scalar register operands for the registers // named in the two vectors and append them to the given instruction. + template <typename T> void AppendRegisterOperands(Instruction *inst, const std::vector<std::string> &sources, const std::vector<std::string> &destinations) { @@ -195,9 +196,10 @@ // Creates source and destination scalar register operands for the registers // named in the two vectors and append them to the default instruction. + template <typename T> void AppendRegisterOperands(const std::vector<std::string> &sources, const std::vector<std::string> &destinations) { - AppendRegisterOperands(instruction_, sources, destinations); + AppendRegisterOperands<T>(instruction_, sources, destinations); } // Returns the value of the named vector register. @@ -564,7 +566,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<Rs1>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); // Initialize input values. FillArrayWithRandomValues<Vs2>(vs2_span); @@ -833,7 +835,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<Rs1>({kRs1Name}, {}); AppendVectorRegisterOperands({kVd, kVmask}, {kVd}); // Initialize input values. FillArrayWithRandomValues<Vd>(vd_span); @@ -1090,7 +1092,7 @@ Vs2 vs2_value[vs2_size * 8]; auto vs2_span = Span<Vs2>(vs2_value); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<Rs1>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); // Initialize input values. FillArrayWithRandomValues<Vs2>(vs2_span);
diff --git a/riscv/test/riscv_vector_permute_instructions_test.cc b/riscv/test/riscv_vector_permute_instructions_test.cc index 5cebdab..d649d59 100644 --- a/riscv/test/riscv_vector_permute_instructions_test.cc +++ b/riscv/test/riscv_vector_permute_instructions_test.cc
@@ -14,6 +14,8 @@ #include "riscv/riscv_vector_permute_instructions.h" +#include <cstdint> + #include "absl/random/random.h" #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/generic/instruction.h" @@ -208,7 +210,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, VrgatherVS8) { SetSemanticFunction(&Vrgather); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); VrgatherVSHelper<uint8_t>(this, instruction_); } @@ -216,7 +218,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, VrgatherVS16) { SetSemanticFunction(&Vrgather); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); VrgatherVSHelper<uint16_t>(this, instruction_); } @@ -224,7 +226,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, VrgatherVS32) { SetSemanticFunction(&Vrgather); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); VrgatherVSHelper<uint32_t>(this, instruction_); } @@ -232,7 +234,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, VrgatherVS64) { SetSemanticFunction(&Vrgather); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); VrgatherVSHelper<uint64_t>(this, instruction_); } @@ -347,7 +349,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslideup8) { SetSemanticFunction(&Vslideup); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ true); } @@ -355,7 +357,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslideup16) { SetSemanticFunction(&Vslideup); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ true); } @@ -363,7 +365,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslideup32) { SetSemanticFunction(&Vslideup); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ true); } @@ -371,7 +373,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslideup64) { SetSemanticFunction(&Vslideup); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ true); } @@ -380,7 +382,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslidedown8) { SetSemanticFunction(&Vslidedown); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ false); } @@ -388,7 +390,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslidedown16) { SetSemanticFunction(&Vslidedown); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ false); } @@ -396,7 +398,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslidedown32) { SetSemanticFunction(&Vslidedown); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ false); } @@ -404,7 +406,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslidedown64) { SetSemanticFunction(&Vslidedown); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ false); } @@ -486,7 +488,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1up8) { SetSemanticFunction(&Vslide1up); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ true); } @@ -494,7 +496,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1up16) { SetSemanticFunction(&Vslide1up); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ true); } @@ -502,7 +504,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1up32) { SetSemanticFunction(&Vslide1up); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ true); } @@ -510,7 +512,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1up64) { SetSemanticFunction(&Vslide1up); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ true); } @@ -518,7 +520,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1down8) { SetSemanticFunction(&Vslide1down); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ false); } @@ -526,7 +528,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1down16) { SetSemanticFunction(&Vslide1down); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ false); } @@ -534,7 +536,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1down32) { SetSemanticFunction(&Vslide1down); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ false); } @@ -542,7 +544,7 @@ TEST_F(RiscVVectorPermuteInstructionsTest, Vslide1down64) { SetSemanticFunction(&Vslide1down); AppendVectorRegisterOperands({kVs2}, {}); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({kVmask}, {kVd}); Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ false); }
diff --git a/riscv/test/riscv_vector_unary_instructions_test.cc b/riscv/test/riscv_vector_unary_instructions_test.cc index db3e21e..84953ea 100644 --- a/riscv/test/riscv_vector_unary_instructions_test.cc +++ b/riscv/test/riscv_vector_unary_instructions_test.cc
@@ -101,7 +101,7 @@ // Test move vector element 0 to scalar register. TEST_F(RiscVVectorUnaryInstructionsTest, VmvToScalar) { SetSemanticFunction(&VmvToScalar); - AppendRegisterOperands({}, {kRs1Name}); + AppendRegisterOperands<RV32Register>({}, {kRs1Name}); AppendVectorRegisterOperands({kVs2}, {}); for (int byte_sew : {1, 2, 4, 8}) { int vlen = kVectorLengthInBytes / byte_sew; @@ -151,7 +151,7 @@ // Test move scalar to vector element 0. TEST_F(RiscVVectorUnaryInstructionsTest, VmvFromScalar) { SetSemanticFunction(&VmvFromScalar); - AppendRegisterOperands({kRs1Name}, {}); + AppendRegisterOperands<RV32Register>({kRs1Name}, {}); AppendVectorRegisterOperands({}, {kVs2}); for (int byte_sew : {1, 2, 4, 8}) { int vlen = kVectorLengthInBytes / byte_sew; @@ -160,6 +160,23 @@ ConfigureVectorUnit(vtype, vlen); // Test 10 different values. for (int i = 0; i < 10; i++) { + // Set the vector register to known values. + for (int v = 0; v < vlen; v++) { + switch (byte_sew) { + case 1: + vreg_[kVs2]->data_buffer()->Set<int8_t>(v, v); + break; + case 2: + vreg_[kVs2]->data_buffer()->Set<int16_t>(v, v); + break; + case 4: + vreg_[kVs2]->data_buffer()->Set<int32_t>(v, v); + break; + case 8: + vreg_[kVs2]->data_buffer()->Set<int64_t>(v, v); + break; + } + } auto value = RandomValue<SignedXregType>(); SetRegisterValues<SignedXregType>({{kRs1Name, value}}); instruction_->Execute(); @@ -167,18 +184,30 @@ case 1: EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int8_t>(0), static_cast<int8_t>(value)); + for (int v = 1; v < vlen; v++) { + EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int8_t>(v), v); + } break; case 2: EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int16_t>(0), static_cast<int16_t>(value)); + for (int v = 1; v < vlen; v++) { + EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int16_t>(v), v); + } break; case 4: EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int32_t>(0), static_cast<int32_t>(value)); + for (int v = 1; v < vlen; v++) { + EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int32_t>(v), v); + } break; case 8: EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int64_t>(0), static_cast<int64_t>(value)); + for (int v = 1; v < vlen; v++) { + EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int64_t>(v), v); + } break; } } @@ -190,7 +219,7 @@ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; SetSemanticFunction(&Vcpop); AppendVectorRegisterOperands({kVs2, kVmask}, {}); - AppendRegisterOperands({}, {kRdName}); + AppendRegisterOperands<RV32Register>({}, {kRdName}); for (int vlen : {1, 8, 32, 48, 127, 200}) { ConfigureVectorUnit(vtype, vlen); // All 1s for mask and vector. @@ -211,7 +240,7 @@ TEST_F(RiscVVectorUnaryInstructionsTest, Vfirst) { SetSemanticFunction(&Vfirst); AppendVectorRegisterOperands({kVs2, kVmask}, {}); - AppendRegisterOperands({}, {kRdName}); + AppendRegisterOperands<RV32Register>({}, {kRdName}); uint8_t reg_value[kVectorLengthInBytes]; // Set vtype to byte vector, and vector lmul to 8. uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
diff --git a/riscv/test/zvbb_encoding_test.cc b/riscv/test/zvbb_encoding_test.cc new file mode 100644 index 0000000..64f7be6 --- /dev/null +++ b/riscv/test/zvbb_encoding_test.cc
@@ -0,0 +1,203 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/zvbb_encoding.h" + +#include <cstdint> + +#include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/flat_demand_memory.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_enums.h" + +// This file contains tests for the RiscV32GZBEncoding class to ensure that +// the instruction decoding is correct. + +namespace { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +using mpact::sim::riscv::RiscVState; +using mpact::sim::riscv::RiscVXlen; +using mpact::sim::util::FlatDemandMemory; + +using mpact::sim::riscv::zvbb::kComplexResourceNames; +using mpact::sim::riscv::zvbb::kDestOpNames; +using mpact::sim::riscv::zvbb::kSimpleResourceNames; +using mpact::sim::riscv::zvbb::kSourceOpNames; + +using SlotEnum = mpact::sim::riscv::zvbb::SlotEnum; +using OpcodeEnum = mpact::sim::riscv::zvbb::OpcodeEnum; +using SourceOpEnum = mpact::sim::riscv::zvbb::SourceOpEnum; +using DestOpEnum = mpact::sim::riscv::zvbb::DestOpEnum; +using SimpleResourceEnum = mpact::sim::riscv::zvbb::SimpleResourceEnum; +using ComplexResourceEnum = mpact::sim::riscv::zvbb::ComplexResourceEnum; + +using mpact::sim::riscv::zvbb::ZVBBEncoding; + +// Constexpr for opcodes for vector basic bit manipulation instructions. +constexpr uint32_t kVandnVv = 0b000001'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVandnVx = 0b000001'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVbrev8V = 0b010010'0'00000'01000'010'00000'1010111; +constexpr uint32_t kVrev8V = 0b010010'0'00000'01001'010'00000'1010111; +constexpr uint32_t kVrolVv = 0b010101'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVrolVx = 0b010101'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVrorVv = 0b010100'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVrorVx = 0b010100'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVrorVi_i5_0 = 0b01010'0'0'00000'00000'011'00000'1010111; +constexpr uint32_t kVrorVi_i5_1 = 0b01010'1'0'00000'00000'011'00000'1010111; + +constexpr uint32_t kVbrevV = 0b010010'0'00000'01010'010'00000'1010111; +constexpr uint32_t kVclzV = 0b010010'0'00000'01100'010'00000'1010111; +constexpr uint32_t kVctzV = 0b010010'0'00000'01101'010'00000'1010111; +constexpr uint32_t kVcpopV = 0b010010'0'00000'01110'010'00000'1010111; +constexpr uint32_t kVwsllVv = 0b110101'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVwsllVx = 0b110101'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVwsllVi = 0b110101'0'00000'00000'011'00000'1010111; + +class ZVBBEncodingTest : public testing::Test { + protected: + ZVBBEncodingTest() { + state_ = new RiscVState("test", RiscVXlen::RV32, &memory_); + enc_ = new ZVBBEncoding(state_); + } + + ~ZVBBEncodingTest() override { + delete enc_; + delete state_; + } + + FlatDemandMemory memory_; + RiscVState *state_; + ZVBBEncoding *enc_; +}; + +TEST_F(ZVBBEncodingTest, SourceOperands) { + auto &getters = enc_->source_op_getters(); + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kSourceOpNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, DestOperands) { + auto &getters = enc_->dest_op_getters(); + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No dest operand for enum value " << i + << " (" << kDestOpNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, SimpleResources) { + auto &getters = enc_->simple_resource_getters(); + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kSimpleResourceNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, ComplexResources) { + auto &getters = enc_->source_op_getters(); + for (int i = *ComplexResourceEnum::kNone; + i < *ComplexResourceEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kComplexResourceNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, VandnVv) { + enc_->ParseInstruction(kVandnVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVandnVv); +} + +TEST_F(ZVBBEncodingTest, VandnVx) { + enc_->ParseInstruction(kVandnVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVandnVx); +} + +TEST_F(ZVBBEncodingTest, Vbrev8V) { + enc_->ParseInstruction(kVbrev8V); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVbrev8V); +} + +TEST_F(ZVBBEncodingTest, Vrev8V) { + enc_->ParseInstruction(kVrev8V); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrev8V); +} + +TEST_F(ZVBBEncodingTest, VrolVv) { + enc_->ParseInstruction(kVrolVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrolVv); +} + +TEST_F(ZVBBEncodingTest, VrolVx) { + enc_->ParseInstruction(kVrolVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrolVx); +} + +TEST_F(ZVBBEncodingTest, VrorVv) { + enc_->ParseInstruction(kVrorVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVv); +} + +TEST_F(ZVBBEncodingTest, VrorVx) { + enc_->ParseInstruction(kVrorVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVx); +} + +TEST_F(ZVBBEncodingTest, VrorVi) { + enc_->ParseInstruction(kVrorVi_i5_0); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVi); + enc_->ParseInstruction(kVrorVi_i5_1); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVi); +} + +TEST_F(ZVBBEncodingTest, VbrevV) { + enc_->ParseInstruction(kVbrevV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVbrevV); +} + +TEST_F(ZVBBEncodingTest, VclzV) { + enc_->ParseInstruction(kVclzV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVclzV); +} + +TEST_F(ZVBBEncodingTest, VctzV) { + enc_->ParseInstruction(kVctzV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVctzV); +} + +TEST_F(ZVBBEncodingTest, VcpopV) { + enc_->ParseInstruction(kVcpopV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVcpopV); +} + +TEST_F(ZVBBEncodingTest, VwsllVv) { + enc_->ParseInstruction(kVwsllVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVv); +} + +TEST_F(ZVBBEncodingTest, VwsllVx) { + enc_->ParseInstruction(kVwsllVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVx); +} + +TEST_F(ZVBBEncodingTest, VwsllVi) { + enc_->ParseInstruction(kVwsllVi); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVi); +} + +} // namespace
diff --git a/riscv/zvbb_encoding.cc b/riscv/zvbb_encoding.cc new file mode 100644 index 0000000..3ac6038 --- /dev/null +++ b/riscv/zvbb_encoding.cc
@@ -0,0 +1,130 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/zvbb_encoding.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_getters_zvbb.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_bin_decoder.h" +#include "riscv/zvbb_decoder.h" +#include "riscv/zvbb_enums.h" + +namespace mpact::sim::riscv::zvbb { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +ZVBBEncoding::ZVBBEncoding(RiscVState *state) + : state_(state), + inst_word_(0), + opcode_(OpcodeEnum::kNone), + format_(FormatEnum::kNone) { + resource_delay_line_ = + state_->CreateAndAddDelayLine<generic::SimpleResourceDelayLine>(8); + // Initialize getters. + source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; }); + dest_op_getters_.emplace(*DestOpEnum::kNone, + [](int latency) { return nullptr; }); + simple_resource_getters_.emplace(*SimpleResourceEnum::kNone, + []() { return nullptr; }); + complex_resource_getters_.emplace( + *ComplexResourceEnum::kNone, + [](int latency, int end) { return nullptr; }); + + AddRiscVZvbbSourceVectorGetters<SourceOpEnum, Extractors, RVVectorRegister>( + source_op_getters_, this); + AddRiscvZvbbSourceScalarGetters<SourceOpEnum, Extractors, RV32Register>( + source_op_getters_, this); + + AddRiscVZvbbDestGetters<DestOpEnum, Extractors, RVVectorRegister>( + dest_op_getters_, this); + + // Verify that there are getters for each enum value. + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + if (source_op_getters_.find(i) == source_op_getters_.end()) { + LOG(ERROR) << "No getter for source op enum value " << i; + } + } + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + if (dest_op_getters_.find(i) == dest_op_getters_.end()) { + LOG(ERROR) << "No getter for destination op enum value " << i; + } + } + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + if (simple_resource_getters_.find(i) == simple_resource_getters_.end()) { + LOG(ERROR) << "No getter for simple resource enum value " << i; + } + } +} + +ZVBBEncoding::~ZVBBEncoding() { delete resource_pool_; } + +void ZVBBEncoding::ParseInstruction(uint32_t inst_word) { + inst_word_ = inst_word; + auto [opcode, format] = DecodeRiscVZvbbInst32WithFormat(inst_word_); + opcode_ = opcode; + format_ = format; +} + +ResourceOperandInterface *ZVBBEncoding::GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) { + return nullptr; +} + +ResourceOperandInterface *ZVBBEncoding::GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, int end) { + return nullptr; +} + +DestinationOperandInterface *ZVBBEncoding::GetDestination(SlotEnum, int, + OpcodeEnum opcode, + DestOpEnum dest_op, + int dest_no, + int latency) { + int index = static_cast<int>(dest_op); + auto iter = dest_op_getters_.find(index); + if (iter == dest_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for destination op enum value ", + index, "for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(latency); +} + +SourceOperandInterface *ZVBBEncoding::GetSource(SlotEnum, int, + OpcodeEnum opcode, + SourceOpEnum source_op, + int source_no) { + int index = static_cast<int>(source_op); + auto iter = source_op_getters_.find(index); + if (iter == source_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index, + " for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(); +} + +} // namespace mpact::sim::riscv::zvbb
diff --git a/riscv/zvbb_encoding.h b/riscv/zvbb_encoding.h new file mode 100644 index 0000000..bc84053 --- /dev/null +++ b/riscv/zvbb_encoding.h
@@ -0,0 +1,103 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_ +#define THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_ + +#include <cstdint> +#include <string> + +#include "mpact/sim/generic/simple_resource.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_bin_decoder.h" +#include "riscv/zvbb_decoder.h" +#include "riscv/zvbb_enums.h" + +namespace mpact::sim::riscv::zvbb { + +// This class provides the interface between the generated instruction decoder +// framework (which is agnostic of the actual bit representation of +// instructions) and the instruction representation. This class provides methods +// to return the opcode, source operands, and destination operands for +// instructions according to the operand fields in the encoding. +class ZVBBEncoding : public ZVBBEncodingBase, public RiscVEncodingCommon { + public: + explicit ZVBBEncoding(RiscVState *state); + ~ZVBBEncoding() override; + + void ParseInstruction(uint32_t inst_word); + OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; } + FormatEnum GetFormat(SlotEnum, int) { return format_; } + + PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum, + PredOpEnum) override { + return nullptr; + } + + ResourceOperandInterface *GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, + int end) override; + + ResourceOperandInterface *GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) override; + + SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op, + int source_no) override; + + DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum, + DestOpEnum op, int dest_no, + int latency) override; + + int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override { + return 0; + } + + // Methods inherited from RiscVEncodingCommon. + RiscVState *state() const override { return state_; } + generic::SimpleResourcePool *resource_pool() override { + return resource_pool_; + } + uint32_t inst_word() const override { return inst_word_; } + + const SourceOpGetterMap &source_op_getters() { return source_op_getters_; } + const DestOpGetterMap &dest_op_getters() { return dest_op_getters_; } + const SimpleResourceGetterMap &simple_resource_getters() { + return simple_resource_getters_; + } + const ComplexResourceGetterMap &complex_resource_getters() { + return complex_resource_getters_; + } + + private: + std::string GetSimpleResourceName(SimpleResourceEnum resource_enum); + + RiscVState *state_; + uint32_t inst_word_; + OpcodeEnum opcode_; + FormatEnum format_; + SourceOpGetterMap source_op_getters_; + DestOpGetterMap dest_op_getters_; + SimpleResourceGetterMap simple_resource_getters_; + ComplexResourceGetterMap complex_resource_getters_; + generic::SimpleResourceDelayLine *resource_delay_line_ = nullptr; + generic::SimpleResourcePool *resource_pool_ = nullptr; +}; + +} // namespace mpact::sim::riscv::zvbb + +#endif // THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_