diff --git a/arch/RISCV/RISCVMapping.c b/arch/RISCV/RISCVMapping.c index a49feae08d..a88eaad9e0 100644 --- a/arch/RISCV/RISCVMapping.c +++ b/arch/RISCV/RISCVMapping.c @@ -11,6 +11,7 @@ #include "../../cs_simple_types.h" #include "../../utils.h" +#include "RISCVBaseInfo.h" #include "RISCVMapping.h" #define GET_INSTRINFO_ENUM @@ -53,11 +54,38 @@ void RISCV_add_cs_detail_0(MCInst *MI, riscv_op_group opgroup, unsigned OpNum) { if (!detail_is_set(MI)) return; - // are not "true" arguments and has no Capstone equivalent + // Rounding mode: store in detail, not as a regular operand. if (opgroup == RISCV_OP_GROUP_FRMArg || - opgroup == RISCV_OP_GROUP_FRMArgLegacy) + opgroup == RISCV_OP_GROUP_FRMArgLegacy) { + unsigned frm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal; + riscv_rounding_mode rm; + switch (frm) { + case RISCVFPRndMode_RNE: + rm = RISCV_RM_RNE; + break; + case RISCVFPRndMode_RTZ: + rm = RISCV_RM_RTZ; + break; + case RISCVFPRndMode_RDN: + rm = RISCV_RM_RDN; + break; + case RISCVFPRndMode_RUP: + rm = RISCV_RM_RUP; + break; + case RISCVFPRndMode_RMM: + rm = RISCV_RM_RMM; + break; + case RISCVFPRndMode_DYN: + rm = RISCV_RM_DYN; + break; + default: + rm = RISCV_RM_INVALID; + break; + } + RISCV_get_detail(MI)->rounding_mode = rm; return; - + } + // unmasked instructions, the mask register is not real if (opgroup == RISCV_OP_GROUP_VMaskReg) { MCOperand *mask = MCInst_getOperand(MI, OpNum); diff --git a/bindings/python/capstone/__init__.py b/bindings/python/capstone/__init__.py index 8de6588462..b1c8df3ed9 100755 --- a/bindings/python/capstone/__init__.py +++ b/bindings/python/capstone/__init__.py @@ -1325,9 +1325,11 @@ def __gen_detail(self): elif arch == CS_ARCH_BPF: (self.operands) = bpf.get_arch_info(self._raw.detail.contents.arch.bpf) elif arch == CS_ARCH_RISCV: - (self.need_effective_addr, self.operands) = riscv.get_arch_info( - self._raw.detail.contents.arch.riscv - ) + ( + self.need_effective_addr, + self.operands, + self.rounding_mode, + ) = riscv.get_arch_info(self._raw.detail.contents.arch.riscv) elif arch == CS_ARCH_SH: (self.sh_insn, self.sh_size, self.operands) = sh.get_arch_info( self._raw.detail.contents.arch.sh diff --git a/bindings/python/capstone/riscv.py b/bindings/python/capstone/riscv.py index 47135ab3b3..2784a39206 100644 --- a/bindings/python/capstone/riscv.py +++ b/bindings/python/capstone/riscv.py @@ -57,8 +57,13 @@ class CsRISCV(ctypes.Structure): ("need_effective_addr", ctypes.c_bool), ("op_count", ctypes.c_uint8), ("operands", RISCVOp * 8), + ("rounding_mode", ctypes.c_uint), ) def get_arch_info(a): - return (a.need_effective_addr, copy_ctypes_list(a.operands[: a.op_count])) + return ( + a.need_effective_addr, + copy_ctypes_list(a.operands[: a.op_count]), + a.rounding_mode, + ) diff --git a/bindings/python/capstone/riscv_const.py b/bindings/python/capstone/riscv_const.py index eee1d0253f..5bf5b0d2ca 100644 --- a/bindings/python/capstone/riscv_const.py +++ b/bindings/python/capstone/riscv_const.py @@ -22,6 +22,15 @@ RISCV_OP_FP = CS_OP_FP RISCV_OP_CSR = CS_OP_SPECIAL +# Floating-point rounding mode for RISC-V FP instructions +RISCV_RM_INVALID = 0 +RISCV_RM_RNE = 1 +RISCV_RM_RTZ = 2 +RISCV_RM_RDN = 3 +RISCV_RM_RUP = 4 +RISCV_RM_RMM = 5 +RISCV_RM_DYN = 6 + # RISCV registers RISCV_REG_INVALID = 0 diff --git a/bindings/python/cstest_py/src/cstest_py/details.py b/bindings/python/cstest_py/src/cstest_py/details.py index 1998aafaa7..f6adf60309 100644 --- a/bindings/python/cstest_py/src/cstest_py/details.py +++ b/bindings/python/cstest_py/src/cstest_py/details.py @@ -1329,6 +1329,11 @@ def test_expected_hppa(actual: CsInsn, expected: dict) -> bool: def test_expected_riscv(actual: CsInsn, expected: dict) -> bool: + if "rounding_mode" in expected and not compare_enum( + actual.rounding_mode, expected.get("rounding_mode"), "rounding_mode" + ): + return False + if "operands" not in expected: return True elif not compare_uint32( diff --git a/cstool/cstool_riscv.c b/cstool/cstool_riscv.c index 9fe1ee2a72..6702fe8c59 100644 --- a/cstool/cstool_riscv.c +++ b/cstool/cstool_riscv.c @@ -68,5 +68,14 @@ void print_insn_detail_riscv(csh handle, cs_insn *ins) } } + if (riscv->rounding_mode != RISCV_RM_INVALID) { + static const char *const rm_str[] = { + [RISCV_RM_RNE] = "rne", [RISCV_RM_RTZ] = "rtz", + [RISCV_RM_RDN] = "rdn", [RISCV_RM_RUP] = "rup", + [RISCV_RM_RMM] = "rmm", [RISCV_RM_DYN] = "dyn", + }; + printf("\trounding_mode: %s\n", rm_str[riscv->rounding_mode]); + } + printf("\n"); } diff --git a/docs/cs_v6_release_guide.md b/docs/cs_v6_release_guide.md index 3c09682cf9..f6b0e30f0d 100644 --- a/docs/cs_v6_release_guide.md +++ b/docs/cs_v6_release_guide.md @@ -253,6 +253,7 @@ Nonetheless, we hope this additional information is useful to you. - Added `reg_access` capstone callback to return all read and written registers for the instructions, including registers used as part of memory operands. * Note that `reg_access` does NOT treat CSRs as registers, detailed reasons for why can be found in [the PR implementing the feature](https://github.com/capstone-engine/capstone/pull/2895) * Note that `reg_access` does NOT treat reading the PC's value as reading a register, detailed reasons for why can be found in [the PR implementing the feature](https://github.com/capstone-engine/capstone/pull/2895) +- Added `rounding_mode` field to `cs_riscv` struct inside details struct (`insn->detail->riscv->rounding_mode`) for float and double instructions. > [!NOTE] > All `CS_MODE_RISCV_*` extensions above are disabled by default unless enabled by their option name or the corresponding command line flag in cstool. Any other extension is always enabled and can't be disabled. diff --git a/include/capstone/riscv.h b/include/capstone/riscv.h index 83148303b8..e8b3943691 100644 --- a/include/capstone/riscv.h +++ b/include/capstone/riscv.h @@ -24,6 +24,17 @@ extern "C" { #pragma warning(disable : 4201) #endif +//> Floating-point rounding mode for RISC-V FP instructions +typedef enum riscv_rounding_mode { + RISCV_RM_INVALID = 0, ///< not applicable (no rounding mode) + RISCV_RM_RNE, ///< round to nearest, ties to even + RISCV_RM_RTZ, ///< round towards zero + RISCV_RM_RDN, ///< round down (towards -infinity) + RISCV_RM_RUP, ///< round up (towards +infinity) + RISCV_RM_RMM, ///< round to nearest, ties to max magnitude + RISCV_RM_DYN, ///< dynamic rounding mode (use frm CSR) +} riscv_rounding_mode; + //> Operand type for instruction's operands typedef enum riscv_op_type { RISCV_OP_INVALID = CS_OP_INVALID, ///< = CS_OP_INVALID (Uninitialized). @@ -65,6 +76,8 @@ typedef struct cs_riscv { // or 0 when instruction has no operand. uint8_t op_count; cs_riscv_op operands[NUM_RISCV_OPS]; // operands for this instruction. + // FP rounding mode, or RISCV_RM_INVALID. + riscv_rounding_mode rounding_mode; } cs_riscv; //> RISCV registers diff --git a/suite/cstest/include/test_detail_riscv.h b/suite/cstest/include/test_detail_riscv.h index 491aed51bb..dcc9f00bef 100644 --- a/suite/cstest/include/test_detail_riscv.h +++ b/suite/cstest/include/test_detail_riscv.h @@ -48,6 +48,7 @@ static const cyaml_schema_value_t test_detail_riscv_op_schema = { typedef struct { TestDetailRISCVOp **operands; uint32_t operands_count; + char *rounding_mode; } TestDetailRISCV; static const cyaml_schema_field_t test_detail_riscv_mapping_schema[] = { @@ -55,6 +56,9 @@ static const cyaml_schema_field_t test_detail_riscv_mapping_schema[] = { "operands", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, TestDetailRISCV, operands, &test_detail_riscv_op_schema, 0, CYAML_UNLIMITED), // 0-MAX options + CYAML_FIELD_STRING_PTR( + "rounding_mode", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + TestDetailRISCV, rounding_mode, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; diff --git a/suite/cstest/include/test_mapping.h b/suite/cstest/include/test_mapping.h index c20ee8d8b1..81843f6694 100644 --- a/suite/cstest/include/test_mapping.h +++ b/suite/cstest/include/test_mapping.h @@ -1493,6 +1493,13 @@ static const cs_enum_id_map cs_enum_map[] = { { .str = "RISCV_OP_IMM", .val = RISCV_OP_IMM }, { .str = "RISCV_OP_MEM", .val = RISCV_OP_MEM }, { .str = "RISCV_OP_REG", .val = RISCV_OP_REG }, + { .str = "RISCV_RM_DYN", .val = RISCV_RM_DYN }, + { .str = "RISCV_RM_INVALID", .val = RISCV_RM_INVALID }, + { .str = "RISCV_RM_RDN", .val = RISCV_RM_RDN }, + { .str = "RISCV_RM_RMM", .val = RISCV_RM_RMM }, + { .str = "RISCV_RM_RNE", .val = RISCV_RM_RNE }, + { .str = "RISCV_RM_RTZ", .val = RISCV_RM_RTZ }, + { .str = "RISCV_RM_RUP", .val = RISCV_RM_RUP }, { .str = "SH_GRP_BRANCH_RELATIVE", .val = SH_GRP_BRANCH_RELATIVE }, { .str = "SH_GRP_CALL", .val = SH_GRP_CALL }, { .str = "SH_GRP_INT", .val = SH_GRP_INT }, diff --git a/suite/cstest/src/test_detail_riscv.c b/suite/cstest/src/test_detail_riscv.c index 688ad4afdb..f5d912867a 100644 --- a/suite/cstest/src/test_detail_riscv.c +++ b/suite/cstest/src/test_detail_riscv.c @@ -24,6 +24,7 @@ void test_detail_riscv_free(TestDetailRISCV *detail) test_detail_riscv_op_free(detail->operands[i]); } cs_mem_free(detail->operands); + cs_mem_free(detail->rounding_mode); cs_mem_free(detail); } @@ -40,6 +41,8 @@ TestDetailRISCV *test_detail_riscv_clone(const TestDetailRISCV *detail) clone->operands[i] = test_detail_riscv_op_clone(detail->operands[i]); } + clone->rounding_mode = + detail->rounding_mode ? strdup(detail->rounding_mode) : NULL; return clone; } @@ -115,5 +118,7 @@ bool test_expected_riscv(csh *handle, const cs_riscv *actual, } } + compare_enum_ret(actual->rounding_mode, expected->rounding_mode, false); + return true; } diff --git a/tests/details/riscv.yaml b/tests/details/riscv.yaml index 4cb3a540e9..220179129f 100644 --- a/tests/details/riscv.yaml +++ b/tests/details/riscv.yaml @@ -907,6 +907,7 @@ test_cases: - asm_text: "fadd.s ft5, ft6, ft7, dyn" details: riscv: + rounding_mode: RISCV_RM_DYN operands: - type: RISCV_OP_REG reg: ft5 @@ -932,6 +933,7 @@ test_cases: - asm_text: "fmin.s fa0, fa1, fa2" details: riscv: + rounding_mode: RISCV_RM_INVALID operands: - type: RISCV_OP_REG reg: fa0