From f71bda9637366c2bb06612cf03ed126c628df678 Mon Sep 17 00:00:00 2001 From: Gianluca Guida Date: Tue, 23 May 2023 13:47:07 +0100 Subject: [PATCH] Implement Zacas extension. --- disasm/disasm.cc | 6 ++++++ riscv/insns/amocas_d.h | 37 +++++++++++++++++++++++++++++++++++++ riscv/insns/amocas_q.h | 34 ++++++++++++++++++++++++++++++++++ riscv/insns/amocas_w.h | 2 ++ riscv/isa_parser.cc | 6 ++++++ riscv/isa_parser.h | 1 + riscv/mmu.h | 11 +++++++++++ riscv/processor.cc | 5 +++++ riscv/riscv.mk.in | 6 ++++++ 9 files changed, 108 insertions(+) create mode 100644 riscv/insns/amocas_d.h create mode 100644 riscv/insns/amocas_q.h create mode 100644 riscv/insns/amocas_w.h diff --git a/disasm/disasm.cc b/disasm/disasm.cc index 8722cdb977..940fa66c16 100644 --- a/disasm/disasm.cc +++ b/disasm/disasm.cc @@ -815,6 +815,12 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_XAMO(sc_d) } + if (isa->extension_enabled(EXT_ZACAS)) { + DEFINE_XAMO(amocas_w) + DEFINE_XAMO(amocas_d) + DEFINE_XAMO(amocas_q) + } + add_insn(new disasm_insn_t("j", match_jal, mask_jal | mask_rd, {&jump_target})); add_insn(new disasm_insn_t("jal", match_jal | match_rd_ra, mask_jal | mask_rd, {&jump_target})); add_insn(new disasm_insn_t("jal", match_jal, mask_jal, {&xrd, &jump_target})); diff --git a/riscv/insns/amocas_d.h b/riscv/insns/amocas_d.h new file mode 100644 index 0000000000..e002e6ab75 --- /dev/null +++ b/riscv/insns/amocas_d.h @@ -0,0 +1,37 @@ +require_extension(EXT_ZACAS); + +if (xlen == 32) { + // RV32: the spec defines two 32-bit comparisons. Since we're + // loading 64-bit for memory we have to adjust for endianness. + uint64_t comp, swap, res; + + require_align(insn.rd(), 2); + require_align(insn.rs2(), 2); + if (insn.rd() == 0) { + comp = 0; + } else if (MMU.is_target_big_endian()) { + comp = (uint32_t)READ_REG(insn.rd() + 1) | (RD << 32); + } else { + comp = (uint32_t)RD | (READ_REG(insn.rd() + 1) << 32); + } + if (insn.rs2() == 0) { + swap = 0; + } else if (MMU.is_target_big_endian()) { + swap = (uint32_t)READ_REG(insn.rs2() + 1) | (RS2 << 32); + } else { + swap = (uint32_t)RS2 | (READ_REG(insn.rs2() + 1) << 32); + } + res = MMU.amo_compare_and_swap(RS1, comp, swap); + if (insn.rd() != 0) { + if (MMU.is_target_big_endian()) { + WRITE_REG(insn.rd() + 1, sext32((uint32_t)res)); + WRITE_REG(insn.rd(), sext32(res >> 32)); + } else { + WRITE_REG(insn.rd(), sext32((uint32_t)res)); + WRITE_REG(insn.rd() + 1, sext32(res >> 32)); + } + } + } else { + // RV64 + WRITE_RD(MMU.amo_compare_and_swap(RS1, RD, RS2)); +} diff --git a/riscv/insns/amocas_q.h b/riscv/insns/amocas_q.h new file mode 100644 index 0000000000..0b7593b3dc --- /dev/null +++ b/riscv/insns/amocas_q.h @@ -0,0 +1,34 @@ +require_extension(EXT_ZACAS); +require_rv64; +require_align(insn.rd(), 2); +require_align(insn.rs2(), 2); + +// The spec defines two 64-bit comparisons. Since we're loading +// 128-bit for memory we have to adjust for endianness. + +uint128_t comp, swap, res; + +if (insn.rd() == 0) { + comp = 0; +} else if (MMU.is_target_big_endian()) { + comp = READ_REG(insn.rd() + 1) | ((uint128_t)RD << 64); +} else { + comp = RD | ((uint128_t)READ_REG(insn.rd() + 1) << 64); +} +if (insn.rs2() == 0) { + swap = 0; +} else if (MMU.is_target_big_endian()) { + swap = READ_REG(insn.rs2() + 1) | ((uint128_t)RS2 << 64); +} else { + swap = RS2 | ((uint128_t)READ_REG(insn.rs2() + 1) << 64); +} +res = MMU.amo_compare_and_swap(RS1, comp, swap); +if (insn.rd() != 0) { + if (MMU.is_target_big_endian()) { + WRITE_REG(insn.rd(), res >> 64); + WRITE_REG(insn.rd() + 1, res); + } else { + WRITE_REG(insn.rd(), res); + WRITE_REG(insn.rd() + 1, res >> 64); + } +} diff --git a/riscv/insns/amocas_w.h b/riscv/insns/amocas_w.h new file mode 100644 index 0000000000..a78c21cb73 --- /dev/null +++ b/riscv/insns/amocas_w.h @@ -0,0 +1,2 @@ +require_extension(EXT_ZACAS); +WRITE_RD(sext32(MMU.amo_compare_and_swap(RS1, RD, RS2))); diff --git a/riscv/isa_parser.cc b/riscv/isa_parser.cc index 8bb8c495b3..1c4300c958 100644 --- a/riscv/isa_parser.cc +++ b/riscv/isa_parser.cc @@ -120,6 +120,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) // HINTs encoded in base-ISA instructions are always present. } else if (ext_str == "zihintntl") { // HINTs encoded in base-ISA instructions are always present. + } else if (ext_str == "zacas") { + extension_table[EXT_ZACAS] = true; } else if (ext_str == "zmmul") { extension_table[EXT_ZMMUL] = true; } else if (ext_str == "zba") { @@ -301,6 +303,10 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) bad_isa_string(str, "'Zcf/Zcd/Zcb/Zcmp/Zcmt' extensions require 'Zca' extension"); } + if (extension_table[EXT_ZACAS] && !extension_table['A']) { + bad_isa_string(str, "'Zacas' extension requires 'A' extension"); + } + std::string lowercase = strtolower(priv); bool user = false, supervisor = false; diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 4e6856195c..3cbee7dea0 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -61,6 +61,7 @@ typedef enum { EXT_ZVFBFMIN, EXT_ZVFBFWMA, EXT_SSTC, + EXT_ZACAS, EXT_INTERNAL_ZFH_MOVE, NUM_ISA_EXTENSIONS } isa_extension_t; diff --git a/riscv/mmu.h b/riscv/mmu.h index efc6e9de14..46c54ce88a 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -187,6 +187,17 @@ class mmu_t }) } + template + T amo_compare_and_swap(reg_t addr, T comp, T swap) { + convert_load_traps_to_store_traps({ + store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true); + auto lhs = load(addr); + if (lhs == comp) + store(addr, swap); + return lhs; + }) + } + void store_float128(reg_t addr, float128_t val) { if (unlikely(addr & (sizeof(float128_t)-1)) && !is_misaligned_enabled()) { diff --git a/riscv/processor.cc b/riscv/processor.cc index a75b0ff6f1..1d5675a51a 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -47,6 +47,11 @@ processor_t::processor_t(const isa_parser_t *isa, const cfg_t *cfg, fprintf(stderr, "V extension is not supported on platforms without __int128 type\n"); abort(); } + + if (isa->extension_enabled(EXT_ZACAS) && isa->get_max_xlen() == 64) { + fprintf(stderr, "Zacas extension is not supported on 64-bit platforms without __int128 type\n"); + abort(); + } #endif parse_varch_string(cfg->varch()); diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index db63290205..6472982ed5 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -1335,6 +1335,11 @@ riscv_insn_ext_bf16 = \ $(riscv_insn_ext_zvfbfmin) \ $(riscv_insn_ext_zvfbfwma) \ +riscv_insn_ext_zacas = \ + amocas_w \ + amocas_d \ + $(if $(HAVE_INT128),amocas_q) + riscv_insn_list = \ $(riscv_insn_ext_a) \ $(riscv_insn_ext_c) \ @@ -1360,6 +1365,7 @@ riscv_insn_list = \ $(riscv_insn_ext_cmo) \ $(riscv_insn_ext_zicond) \ $(riscv_insn_ext_bf16) \ + $(riscv_insn_ext_zacas) \ riscv_gen_srcs = $(addsuffix .cc,$(riscv_insn_list))