| /* ********************************************************** |
| * Copyright (c) 2022 Rivos, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of Rivos, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL RIVOS, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #include <stdint.h> |
| |
| #include "../globals.h" |
| #include "../isa_regdeps/decode.h" |
| #include "codec.h" |
| #include "encode_api.h" |
| #include "trie.h" |
| |
| /* RISC-V extended instruction information structure. |
| * |
| * Holds extra elements required for encoding/decoding. Since instr_info_t is |
| * 48 bytes large, there are 16 bytes available to a single cache-line (assuming |
| * 64 byte lines). |
| */ |
| typedef struct { |
| /* The instruction information contains: |
| * - OP_* opcode -> type |
| * - N(dst) - there can either by 0 or 1 destination -> opcode[31] |
| * - N(src) - there can be up to 4 sources -> opcode[30:28] |
| * - Operands - Current instruction set allows maximum of 5 operands |
| * (including semantically divided immediate parts). At most one |
| * of those can be a destination register and if there are 5 |
| * operands, there is always a destination register. Therefore: |
| * - Destination type (riscv64_fld_t) -> dst1_type |
| * - 1st source operand (riscv64_fld_t) -> src1_type |
| * - 2nd source operand (riscv64_fld_t) -> src2_type |
| * - 3rd source operand (riscv64_fld_t) -> src3_type |
| * - 4th source operand (riscv64_fld_t) -> dst2_type |
| * - Match - fixed bits of the instruction -> code[63:32] |
| * - Mask - fixed bits mask for encoding validation -> code[31:0] |
| */ |
| instr_info_t info; |
| riscv64_isa_ext_t ext; /* ISA or extension of this instruction. */ |
| } rv_instr_info_t; |
| |
| #if !defined(X64) |
| # error RISC-V codec only supports 64-bit architectures mask+match -> code. |
| #endif |
| |
| /* Instruction operand decoder function. |
| * |
| * Decodes an operand from a given instruction into the instr_t structure |
| * provided by the caller. |
| * |
| * @param[in] dc DynamoRIO context. |
| * @param[in] inst instruction bytes. |
| * @param[in] op_sz Operand size (OP_* enum value). |
| * @param[in] pc Program Counter. Effectively pointer to the bytestream. |
| * @param[in] orig_pc If the code was translated, the original program counter. |
| * @param[in] info Instruction info for decoder use. |
| * @param[in] idx Source/destination index. |
| * @param[inout] out The instruction structure to decode the operand to. |
| * |
| * @return True if decoding succeeded, false otherwise. This function will log |
| * the error. |
| */ |
| typedef bool (*opnd_dec_func_t)(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, |
| byte *orig_pc, int idx, instr_t *out); |
| |
| /********************************************************** |
| * Helper functions. |
| */ |
| |
| #define INFO_NDST(opcode) GET_FIELD((opcode), 31, 30); |
| #define INFO_NSRC(opcode) GET_FIELD((opcode), 29, 27); |
| |
| /* |
| * End of helper functions. |
| **********************************************************/ |
| |
| /* Include a generated list of rv_instr_info_t and an accompanying trie |
| * structure for fast lookup: |
| * static rv_instr_info_t instr_infos[]; |
| * static trie_node_t instr_infos_trie[]; |
| */ |
| #include "instr_info_trie.h" |
| |
| /********************************************************** |
| * Format decoding functions. |
| */ |
| |
| /* Dummy function for catching invalid operand values. Should never be called. |
| */ |
| static bool |
| decode_none_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| |
| /* Decode the destination fixed-point register field: |
| * |31 12|11 7|6 0| |
| * | ... | rd | opcode | |
| * ^----^ |
| * Applies to R, R4, I, U and J uncompressed formats. |
| */ |
| static bool |
| decode_rd_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| reg_t rd = DR_REG_X0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(rd); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the destination floating-point register field: |
| * |31 12|11 7|6 0| |
| * | ... | rd | opcode | |
| * ^----^ |
| * Applies to R, R4, I, U and J uncompressed formats. |
| */ |
| static bool |
| decode_rdfp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the destination vector register field: |
| * |31 12|11 7|6 0| |
| * | ... | vd | opcode | |
| * ^----^ |
| */ |
| static bool |
| decode_vd_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| reg_t reg = DR_REG_VR0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 1st source fixed-point register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | rs1 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, I, S and B uncompressed formats. |
| */ |
| static bool |
| decode_rs1_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 1st source floating-point register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | rs1 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, I, S and B uncompressed formats. |
| */ |
| static bool |
| decode_rs1fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 1st source vector register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | vs1 | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| decode_vs1_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_VR0 + GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the rs1 field as a base register: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | base | ... | opcode | |
| * ^------^ |
| * Applies to instructions of the Zicbom and Zicbop extensions. |
| */ |
| static bool |
| decode_base_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_base_disp(reg, DR_REG_NULL, 0, 0, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 2nd source fixed-point register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | rs2 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, S and B uncompressed formats. |
| */ |
| static bool |
| decode_rs2_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 24, 20); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 2nd source floating-point register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | rs2 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, S and B uncompressed formats. |
| */ |
| static bool |
| decode_rs2fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 24, 20); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 2nd source vector register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | vs2 | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| decode_vs2_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_VR0 + GET_FIELD(inst, 24, 20); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 3rd source fixed-point register field: |
| * |31 27|26 7|6 0| |
| * | rs3 | ... | opcode | |
| * ^---^ |
| * Applies to the R4 uncompressed format. |
| */ |
| static bool |
| decode_rs3fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 31, 27); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 3rd source vector register field: |
| * |31 12|11 7|6 0| |
| * | ... | vs3 | opcode | |
| * ^----^ |
| */ |
| static bool |
| decode_vs3_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_VR0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the fence mode field of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^----^ |
| */ |
| static bool |
| decode_fm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 31, 28); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode all predecessor bits of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^-----------------^ |
| */ |
| static bool |
| decode_pred_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 27, 24); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode all successor bits of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^-----------------^ |
| */ |
| static bool |
| decode_succ_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 23, 20); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode acquire-release semantics of an atomic instruction (A extension): |
| * |31 27| 26 | 25 |24 7|6 0| |
| * | ... | aq | rl | ... | opcode | |
| * ^-------^ |
| */ |
| static bool |
| decode_aqrl_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 26, 25); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the CSR number in instructions from the Zicsr extension: |
| * |31 20|19 7|6 0| |
| * | csr | ... | opcode | |
| * ^---^ |
| */ |
| static bool |
| decode_csr_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| /* FIXME i#3544: Should CSRs be as DR_REG_* or rather as hex defines? Their |
| * set is extensible by platform implementers and various extensions, so |
| * for now let's leave it as an int. |
| */ |
| int32_t imm = GET_FIELD(inst, 31, 20); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the rounding mode in floating-point instructions: |
| * |31 15|14 12|11 7|6 0| |
| * | ... | rm | ... | opcode | |
| * ^----^ |
| * The valid values can be found in Table 11.1 in the RISC-V |
| * Instruction Set Manual Volume I: Unprivileged ISA (ver. 20191213). |
| */ |
| static bool |
| decode_rm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 14, 12); |
| /* Invalid. Reserved for future use. */ |
| ASSERT(imm != 0b101 && imm != 0b110); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 6-bit (6th bit always 0 in rv32) shift amount: |
| * |31 26|25 20|19 7|6 0| |
| * | ... | shamt | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| decode_shamt_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 25, 20); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 5-bit shift amount in rv64: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | shamt5 | ... | opcode | |
| * ^------^ |
| */ |
| static bool |
| decode_shamt5_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 24, 20); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 7-bit (7th bit always 0 in rv64) shift amount in rv64: |
| * |31 27|26 20|19 7|6 0| |
| * | ... | shamt6 | ... | opcode | |
| * ^------^ |
| */ |
| static bool |
| decode_shamt6_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| /* shamt6 >= 64 only makes sense on RV128 but let user take care of it. */ |
| int32_t imm = GET_FIELD(inst, 26, 20); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the I-type format: |
| * |31 20|19 15|14 12|11 7|6 0| |
| * | imm[11:0] | rs1 | funct3 | rd | opcode | |
| * ^---------^ |
| * Into: |
| * |31 11|10 0| |
| * | imm[11] | imm[10:0] | |
| */ |
| static bool |
| decode_i_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = SIGN_EXTEND(GET_FIELD(inst, 31, 20), 12); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the S-type format: |
| * |31 25|24 20|19 15|14 12|11 7|6 0| |
| * | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | |
| * ^---------^ ^--------^ |
| * Into: |
| * |31 11|10 5|4 0| |
| * | imm[11] | imm[10:5] | imm[4:0] | |
| */ |
| static bool |
| decode_s_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = (GET_FIELD(inst, 31, 25) << 5) | (GET_FIELD(inst, 11, 7)); |
| imm = SIGN_EXTEND(imm, 12); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the B-type format as a pc-relative offset: |
| * | 31 |30 25|24 20|19 15|14 12|11 8| 7 |6 0| |
| * |imm[12]|imm[10:5]| rs2 | rs1 | funct3 |imm[4:1]|imm[11]| opcode | |
| * ^---------------^ ^--------------^ |
| * Into: |
| * |31 12| 11 |10 5|4 1| 0 | |
| * | imm[12] |imm[11]| imm[10:5] | imm[4:1] | 0 | |
| */ |
| static bool |
| decode_b_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = BIT(inst, 31) << 12; |
| imm |= BIT(inst, 7) << 11; |
| imm |= GET_FIELD(inst, 30, 25) << 5; |
| imm |= GET_FIELD(inst, 11, 8) << 1; |
| imm = SIGN_EXTEND(imm, 13); |
| |
| opnd_t opnd = opnd_create_pc(orig_pc + imm); |
| instr_set_target(out, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the U-type format: |
| * |31 12|11 7|6 0| |
| * | imm[31:12] | rd | opcode | |
| * ^----------^ |
| * Into: |
| * |31 12|11 0| |
| * | imm[31:12] | 0 | |
| */ |
| static bool |
| decode_u_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| uint uimm = GET_FIELD(inst, 31, 12); |
| opnd_t opnd = opnd_create_immed_int(uimm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the U-type format (PC-relative): |
| * |31 12|11 7|6 0| |
| * | imm[31:12] | rd | opcode | |
| * ^----------^ |
| * Into: |
| * |31 12|11 0| |
| * | imm[31:12] | 0 | |
| */ |
| static bool |
| decode_u_immpc_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t uimm = GET_FIELD(inst, 31, 12); |
| /* OPSZ_0 is used here to indicate that this is not a real memory access instruction. |
| */ |
| opnd_t opnd = opnd_create_rel_addr(orig_pc + (uimm << 12), OPSZ_0); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the J-type format as a pc-relative offset: |
| * | 31 |30 21| 20 |19 12|11 7|6 0| |
| * | imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode | |
| * ^------------------------------------------^ |
| * Into: |
| * |31 20|19 12| 11 |10 1| 0 | |
| * | imm[20] | imm[19:12] | imm[11] | imm[10:1] | 0 | |
| */ |
| static bool |
| decode_j_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = BIT(inst, 31) << 20; |
| imm |= GET_FIELD(inst, 19, 12) << 12; |
| imm |= BIT(inst, 20) << 11; |
| imm |= GET_FIELD(inst, 30, 21) << 1; |
| imm = SIGN_EXTEND(imm, 21); |
| |
| opnd_t opnd = opnd_create_pc(orig_pc + imm); |
| instr_set_target(out, opnd); |
| return true; |
| } |
| |
| /* Decode the destination fixed-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| decode_crd_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the destination floating-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| decode_crdfp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 1st source fixed-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| decode_crs1_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 2nd source fixed-point register field: |
| * |31 7|6 2|1 0| |
| * | ... | rs2 | opcode | |
| * ^---^ |
| * Applies to CR and CSS compressed formats. |
| */ |
| static bool |
| decode_crs2_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 6, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 2nd source floating-point register field: |
| * |31 7|6 2|1 0| |
| * | ... | rs2 | opcode | |
| * ^---^ |
| * Applies to CR and CSS compressed formats. |
| */ |
| static bool |
| decode_crs2fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F0 + GET_FIELD(inst, 6, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) destination fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to CIW and CL compressed formats. |
| */ |
| static bool |
| decode_crd__opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 4, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) destination floating-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to CIW and CL compressed formats. |
| */ |
| static bool |
| decode_crd_fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F8 + GET_FIELD(inst, 4, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) 1st source fixed-point register field: |
| * |31 10|9 7|6 2|1 0| |
| * | ... | rs1' | ... | opcode | |
| * ^---^ |
| * Applies to CL, CS, CA and CB compressed formats. |
| */ |
| static bool |
| decode_crs1__opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) 2nd source fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rs2' | opcode | |
| * ^---^ |
| * Applies to CS and CA compressed formats. |
| */ |
| static bool |
| decode_crs2__opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 4, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) 2nd source floating-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rs2' | opcode | |
| * ^---^ |
| * Applies to CS and CA compressed formats. |
| */ |
| static bool |
| decode_crs2_fp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_F8 + GET_FIELD(inst, 4, 2); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the limited range (x8-x15) destination fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to the CA compressed format. |
| */ |
| static bool |
| decode_crd___opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the 6-bit (6th bit always 0 in rv32) shift amount: |
| * |15 13| 12 |11 10|9 7|6 2|1 0| |
| * | funct3 | imm[5] | funct2 | rs1' | imm[4:0] | opcode | |
| * ^------^ ^--------^ |
| */ |
| static bool |
| decode_cshamt_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = (BIT(inst, 12) << 5) | GET_FIELD(inst, 6, 2); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the CSR immediate in instructions from the Zicsr extension: |
| * |31 20|19 15|14 7|6 0| |
| * | csr | imm[4:0] | ... | opcode | |
| * ^--------^ |
| * Into: |
| * |31 5|4 0| |
| * | 0 | imm[4:0] | |
| */ |
| static bool |
| decode_csr_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 19, 15); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate of the caddi16sp instruction: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[9] | ... | imm[4|6|8:7|5] | opcode | |
| * ^------^ ^--------------^ |
| * Into: |
| * |31 9|8 4|3 0| |
| * | imm[9] | imm[8:4] | 0 | |
| */ |
| static bool |
| decode_caddi16sp_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, |
| byte *orig_pc, int idx, instr_t *out) |
| { |
| int32_t imm = (BIT(inst, 12) << 9); |
| imm |= (GET_FIELD(inst, 4, 3) << 7); |
| imm |= (BIT(inst, 5) << 6); |
| imm |= (BIT(inst, 2) << 5); |
| imm |= (BIT(inst, 6) << 4); |
| imm = SIGN_EXTEND(imm, 10); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the SP-based immediate offset of c.lwsp and c.flwsp instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:2|7:6] | opcode | |
| * ^------^ ^------------^ |
| * Into: |
| * |31 8|7 2|3 0| |
| * sp + | 0 | imm[7:2] | 0 | |
| */ |
| static bool |
| decode_clwsp_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 3, 2) << 6; |
| imm |= BIT(inst, 12) << 5; |
| imm |= GET_FIELD(inst, 6, 4) << 2; |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_base_disp(DR_REG_SP, DR_REG_NULL, 0, imm, OPSZ_4), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the SP-based immediate offset of c.ldsp and c.fldsp instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:3|8:6] | opcode | |
| * ^------^ ^------------^ |
| * Into: |
| * |31 9|8 2|3 0| |
| * sp + | 0 | imm[8:3] | 0 | |
| */ |
| static bool |
| decode_cldsp_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 4, 2) << 6; |
| imm |= BIT(inst, 12) << 5; |
| imm |= GET_FIELD(inst, 6, 5) << 3; |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_base_disp(DR_REG_SP, DR_REG_NULL, 0, imm, OPSZ_8), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate of the c.lui instruction: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[17] | ... | imm[16:12] | opcode | |
| * ^-------^ ^----------^ |
| * Into: |
| * |31 17|16 12|11 0| |
| * | imm[17] | imm[16:12] | 0 | |
| */ |
| static bool |
| decode_clui_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = (BIT(inst, 12) << 5) | GET_FIELD(inst, 6, 2); |
| opnd_t opnd = opnd_create_immed_int(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the SP-based offset immediate of c.swsp and c.fswsp instructions: |
| * |15 13|12 7|6 2|1 0| |
| * | ... | imm[5:2|7:6] | ... | opcode | |
| * ^------------^ |
| * Into: |
| * |31 8|7 2|1 0| |
| * sp + | 0 | imm[7:2] | 0 | |
| */ |
| static bool |
| decode_cswsp_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = (GET_FIELD(inst, 8, 7) << 6) | (GET_FIELD(inst, 12, 9) << 2); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_base_disp(DR_REG_SP, DR_REG_NULL, 0, imm, OPSZ_4), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the SP-based offset immediate of c.sdsp and c.fsdsp instructions: |
| * |15 13|12 7|6 2|1 0| |
| * | ... | imm[5:3|8:6] | ... | opcode | |
| * ^------------^ |
| * Into: |
| * |31 9|8 3|2 0| |
| * sp + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| decode_csdsp_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = (GET_FIELD(inst, 9, 7) << 6) | (GET_FIELD(inst, 12, 10) << 3); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_base_disp(DR_REG_SP, DR_REG_NULL, 0, imm, OPSZ_8), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate of the c.addi4spn instruction: |
| * |15 13|12 5|4 2|1 0| |
| * | ... | imm[5:4|9:6|2|3] | ... | opcode | |
| * ^----------------^ |
| * Into: |
| * |31 10|9 2|1 0| |
| * | 0 | imm[9:2] | 0 | |
| */ |
| static bool |
| decode_ciw_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = GET_FIELD(inst, 10, 7) << 6; |
| imm |= GET_FIELD(inst, 12, 11) << 4; |
| imm |= BIT(inst, 5) << 3; |
| imm |= BIT(inst, 6) << 2; |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and offset immediate of c.lw and c.flw |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[2|6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * Into: |
| * |31 7|6 2|1 0| |
| * rs1' + | 0 | imm[6:2] | 0 | |
| */ |
| static bool |
| decode_clw_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| int32_t imm = BIT(inst, 5) << 6; |
| imm |= GET_FIELD(inst, 12, 10) << 3; |
| imm |= BIT(inst, 6) << 2; |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, OPSZ_4), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and offset immediate of c.ld and c.fld |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[7:6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * Into: |
| * |31 8|7 3|2 0| |
| * rs1' + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| decode_cld_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| int32_t imm = (GET_FIELD(inst, 6, 5) << 6) | (GET_FIELD(inst, 12, 10) << 3); |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, OPSZ_8), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and offset immediate of c.sw and c.fsw |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[2|6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * Into: |
| * |31 7|6 2|1 0| |
| * rs1' + | 0 | imm[6:2] | 0 | |
| */ |
| static bool |
| decode_csw_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| int32_t imm = BIT(inst, 5) << 6; |
| imm |= GET_FIELD(inst, 12, 10) << 3; |
| imm |= BIT(inst, 6) << 2; |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, OPSZ_4), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and offset immediate of c.sd and c.fsd |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[7:6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * Into: |
| * |31 8|7 3|2 0| |
| * rs1' + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| decode_csd_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| int32_t imm = (GET_FIELD(inst, 6, 5) << 6) | (GET_FIELD(inst, 12, 10) << 3); |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, OPSZ_8), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base immediate of c.addi, c.addiw, c.li, c.andi instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:0] | opcode | |
| * ^------^ ^--------^ |
| * Into: |
| * |31 5|4 0| |
| * | imm[5] | imm[4:0] | |
| */ |
| static bool |
| decode_cimm5_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = SIGN_EXTEND((BIT(inst, 12) << 5) | GET_FIELD(inst, 6, 2), 6); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(imm, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the CB-type format as a pc-relative offset: |
| * |15 13|12 10|9 7|6 2|1 0| |
| * | ... | imm[8|4:3] | ... | imm[7:6|2:1|5] | opcode | |
| * ^----------^ ^--------------^ |
| * Into: |
| * |31 8|7 1| 0 | |
| * | imm[8] | imm[7:1] | 0 | |
| */ |
| static bool |
| decode_cb_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = BIT(inst, 12) << 8; |
| imm |= GET_FIELD(inst, 6, 5) << 6; |
| imm |= BIT(inst, 2) << 5; |
| imm |= GET_FIELD(inst, 11, 10) << 3; |
| imm |= GET_FIELD(inst, 4, 3) << 1; |
| imm = SIGN_EXTEND(imm, 9); |
| |
| opnd_t opnd = opnd_create_pc(orig_pc + imm); |
| instr_set_target(out, opnd); |
| return true; |
| } |
| |
| /* Decode the immediate field of the CJ-type format as a pc-relative offset: |
| * |15 13|12 2|1 0| |
| * | ... | [11|4|9:8|10|6|7|3:1|5] | opcode | |
| * ^-----------------------^ |
| * Into: |
| * |31 11|10 1| 0 | |
| * | imm[11] | imm[10:1] | 0 | |
| */ |
| static bool |
| decode_cj_imm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| int32_t imm = BIT(inst, 12) << 11; |
| imm |= BIT(inst, 8) << 10; |
| imm |= GET_FIELD(inst, 10, 9) << 8; |
| imm |= BIT(inst, 6) << 7; |
| imm |= BIT(inst, 7) << 6; |
| imm |= BIT(inst, 2) << 5; |
| imm |= BIT(inst, 11) << 4; |
| imm |= GET_FIELD(inst, 5, 3) << 1; |
| imm = SIGN_EXTEND(imm, 12); |
| |
| opnd_t opnd = opnd_create_pc(orig_pc + imm); |
| instr_set_target(out, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and immediate offset of a virtual load-like field: |
| * |31 20|19 15|14 7|6 0| |
| * | imm[11:0] | rs1 | ... | opcode | |
| * ^---------^ ^-----^ |
| * Into: |
| * |31 11|7 0| |
| * rs1 + | imm[11] | imm[10:0] | |
| * |
| * Note that this is a virtual field injected by codec.py into instructions |
| * which share the immediate field type with other non-base+disp instructions. |
| */ |
| static bool |
| decode_v_l_rs1_disp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, |
| byte *orig_pc, int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 19, 15); |
| /* Immediate part of LR.W/D or vector load is always 0. */ |
| bool is_vector_load = GET_FIELD(inst, 6, 0) == 0b0000111 && |
| (GET_FIELD(inst, 14, 12) == 0 || GET_FIELD(inst, 14, 12) > 0b100); |
| bool is_lr = GET_FIELD(inst, 6, 0) == 0b0101111; |
| int32_t imm = is_vector_load || is_lr ? 0 : SIGN_EXTEND(GET_FIELD(inst, 31, 20), 12); |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, op_sz), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the base register and immediate offset of a virtual store-like field: |
| * |31 25|24 20|19 15|14 12|11 7|6 0| |
| * | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | |
| * ^---------^ ^-----^ ^--------^ |
| * Into: |
| * |31 11|7 0| |
| * rs1 + | imm[11] | imm[10:0] | |
| * |
| * Note that this is a virtual field injected by codec.py into instructions |
| * which share the immediate field type with other non-base+disp instructions. |
| */ |
| static bool |
| decode_v_s_rs1_disp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, |
| byte *orig_pc, int idx, instr_t *out) |
| { |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 19, 15); |
| /* Immediate part of SC.W/D or vector store is always 0. */ |
| bool is_vector_store = GET_FIELD(inst, 6, 0) == 0b0100111 && |
| (GET_FIELD(inst, 14, 12) == 0 || GET_FIELD(inst, 14, 12) > 0b100); |
| bool is_sc = GET_FIELD(inst, 6, 0) == 0b0101111; |
| int32_t imm = is_vector_store || is_sc |
| ? 0 |
| : (GET_FIELD(inst, 31, 25) << 5) | GET_FIELD(inst, 11, 7); |
| imm = SIGN_EXTEND(imm, 12); |
| opnd_t opnd = opnd_add_flags(opnd_create_base_disp(reg, DR_REG_NULL, 0, imm, op_sz), |
| DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rs1 field which is always sp. |
| */ |
| static bool |
| decode_irs1_sp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| opnd_t opnd = opnd_create_reg(DR_REG_SP); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rs1 field which is always zero. |
| */ |
| static bool |
| decode_irs1_zero_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| opnd_t opnd = opnd_create_reg(DR_REG_ZERO); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rs2 field which is always zero. |
| */ |
| static bool |
| decode_irs2_zero_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 1); |
| opnd_t opnd = opnd_create_reg(DR_REG_ZERO); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rd field which is always zero. |
| */ |
| static bool |
| decode_ird_zero_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| opnd_t opnd = opnd_create_reg(DR_REG_ZERO); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rd field which is always ra. |
| */ |
| static bool |
| decode_ird_ra_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| opnd_t opnd = opnd_create_reg(DR_REG_RA); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rd field which is always sp. |
| */ |
| static bool |
| decode_ird_sp_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| opnd_t opnd = opnd_create_reg(DR_REG_SP); |
| instr_set_dst(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit immediate field which is always 0. |
| */ |
| static bool |
| decode_iimm_0_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 1); |
| opnd_t opnd = |
| opnd_add_flags(opnd_create_immed_int(0, op_sz), DR_OPND_IMM_PRINT_DECIMAL); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rd field which is same as crd. |
| */ |
| static bool |
| decode_icrs1_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| reg_t reg = DR_REG_X0 + GET_FIELD(inst, 11, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the implicit rd field which is same as crd__. |
| */ |
| static bool |
| decode_icrs1___opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| ASSERT(idx == 0); |
| reg_t reg = DR_REG_X8 + GET_FIELD(inst, 9, 7); |
| opnd_t opnd = opnd_create_reg(reg); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the zimm immediate field in vsetivli instruction (V extension): |
| * |31 30|29 20|19 15|14 12|11 7|6 0| |
| * | ... | zimm10 | zimm | ... | rd | opcode | |
| * ^------^ |
| */ |
| static bool |
| decode_zimm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the zimm10 immediate field in vsetivli instruction (V extension): |
| * |31 30|29 20|19 15|14 12|11 7|6 0| |
| * | ... | zimm10 | zimm | ... | rd | opcode | |
| * ^--------^ |
| */ |
| static bool |
| decode_zimm10_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 29, 20); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the zimm11 immediate field in vsetvli instruction (V extension): |
| * |31|30 20|19 15|14 12|11 7|6 0| |
| * | | zimm11 | rs1 | ... | rd | opcode | |
| * ^--------^ |
| */ |
| static bool |
| decode_zimm11_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 30, 20); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the vm (vector mask) immediate field in vector instructions (V extension): |
| * |31 26| 25 |24 0| |
| * | ... | vm | ... | |
| * ^----^ |
| */ |
| static bool |
| decode_vm_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 25, 25); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the nf (nfields) immediate field in vector instructions (V extension): |
| * |31 29|28 0| |
| * | nf | ... | |
| * ^------^ |
| */ |
| static bool |
| decode_nf_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, int idx, |
| instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 31, 29); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Decode the simm5 immediate field in vector instructions (V extension): |
| * |31 26| 25 |24 20|19 15|14 12|11 7|6 0| |
| * | funct6 | vm | vs2 | simm5 | ... | vd | opcode | |
| * ^-------^ |
| */ |
| static bool |
| decode_simm5_opnd(dcontext_t *dc, uint32_t inst, int op_sz, byte *pc, byte *orig_pc, |
| int idx, instr_t *out) |
| { |
| uint32_t imm = GET_FIELD(inst, 19, 15); |
| opnd_t opnd = opnd_create_immed_uint(imm, op_sz); |
| instr_set_src(out, idx, opnd); |
| return true; |
| } |
| |
| /* Array of operand decode functions indexed by riscv64_fld_t. |
| * |
| * NOTE: After benchmarking, perhaps this could be placed in the same section as |
| * instr_infos and trie? |
| */ |
| opnd_dec_func_t opnd_decoders[] = { |
| [RISCV64_FLD_NONE] = decode_none_opnd, |
| [RISCV64_FLD_RD] = decode_rd_opnd, |
| [RISCV64_FLD_RDFP] = decode_rdfp_opnd, |
| [RISCV64_FLD_RS1] = decode_rs1_opnd, |
| [RISCV64_FLD_RS1FP] = decode_rs1fp_opnd, |
| [RISCV64_FLD_BASE] = decode_base_opnd, |
| [RISCV64_FLD_RS2] = decode_rs2_opnd, |
| [RISCV64_FLD_RS2FP] = decode_rs2fp_opnd, |
| [RISCV64_FLD_RS3FP] = decode_rs3fp_opnd, |
| [RISCV64_FLD_FM] = decode_fm_opnd, |
| [RISCV64_FLD_PRED] = decode_pred_opnd, |
| [RISCV64_FLD_SUCC] = decode_succ_opnd, |
| [RISCV64_FLD_AQRL] = decode_aqrl_opnd, |
| [RISCV64_FLD_CSR] = decode_csr_opnd, |
| [RISCV64_FLD_RM] = decode_rm_opnd, |
| [RISCV64_FLD_SHAMT] = decode_shamt_opnd, |
| [RISCV64_FLD_SHAMT5] = decode_shamt5_opnd, |
| [RISCV64_FLD_SHAMT6] = decode_shamt6_opnd, |
| [RISCV64_FLD_I_IMM] = decode_i_imm_opnd, |
| [RISCV64_FLD_S_IMM] = decode_s_imm_opnd, |
| [RISCV64_FLD_B_IMM] = decode_b_imm_opnd, |
| [RISCV64_FLD_U_IMM] = decode_u_imm_opnd, |
| [RISCV64_FLD_U_IMMPC] = decode_u_immpc_opnd, |
| [RISCV64_FLD_J_IMM] = decode_j_imm_opnd, |
| [RISCV64_FLD_CRD] = decode_crd_opnd, |
| [RISCV64_FLD_CRDFP] = decode_crdfp_opnd, |
| [RISCV64_FLD_CRS1] = decode_crs1_opnd, |
| [RISCV64_FLD_CRS2] = decode_crs2_opnd, |
| [RISCV64_FLD_CRS2FP] = decode_crs2fp_opnd, |
| [RISCV64_FLD_CRD_] = decode_crd__opnd, |
| [RISCV64_FLD_CRD_FP] = decode_crd_fp_opnd, |
| [RISCV64_FLD_CRS1_] = decode_crs1__opnd, |
| [RISCV64_FLD_CRS2_] = decode_crs2__opnd, |
| [RISCV64_FLD_CRS2_FP] = decode_crs2_fp_opnd, |
| [RISCV64_FLD_CRD__] = decode_crd___opnd, |
| [RISCV64_FLD_CSHAMT] = decode_cshamt_opnd, |
| [RISCV64_FLD_CSR_IMM] = decode_csr_imm_opnd, |
| [RISCV64_FLD_CADDI16SP_IMM] = decode_caddi16sp_imm_opnd, |
| [RISCV64_FLD_CLWSP_IMM] = decode_clwsp_imm_opnd, |
| [RISCV64_FLD_CLDSP_IMM] = decode_cldsp_imm_opnd, |
| [RISCV64_FLD_CLUI_IMM] = decode_clui_imm_opnd, |
| [RISCV64_FLD_CSWSP_IMM] = decode_cswsp_imm_opnd, |
| [RISCV64_FLD_CSDSP_IMM] = decode_csdsp_imm_opnd, |
| [RISCV64_FLD_CIW_IMM] = decode_ciw_imm_opnd, |
| [RISCV64_FLD_CLW_IMM] = decode_clw_imm_opnd, |
| [RISCV64_FLD_CLD_IMM] = decode_cld_imm_opnd, |
| [RISCV64_FLD_CSW_IMM] = decode_csw_imm_opnd, |
| [RISCV64_FLD_CSD_IMM] = decode_csd_imm_opnd, |
| [RISCV64_FLD_CIMM5] = decode_cimm5_opnd, |
| [RISCV64_FLD_CB_IMM] = decode_cb_imm_opnd, |
| [RISCV64_FLD_CJ_IMM] = decode_cj_imm_opnd, |
| [RISCV64_FLD_V_L_RS1_DISP] = decode_v_l_rs1_disp_opnd, |
| [RISCV64_FLD_V_S_RS1_DISP] = decode_v_s_rs1_disp_opnd, |
| [RISCV64_FLD_IRS1_SP] = decode_irs1_sp_opnd, |
| [RISCV64_FLD_IRS1_ZERO] = decode_irs1_zero_opnd, |
| [RISCV64_FLD_IRS2_ZERO] = decode_irs2_zero_opnd, |
| [RISCV64_FLD_IRD_ZERO] = decode_ird_zero_opnd, |
| [RISCV64_FLD_IRD_RA] = decode_ird_ra_opnd, |
| [RISCV64_FLD_IRD_SP] = decode_ird_sp_opnd, |
| [RISCV64_FLD_IIMM_0] = decode_iimm_0_opnd, |
| [RISCV64_FLD_ICRS1] = decode_icrs1_opnd, |
| [RISCV64_FLD_ICRS1__] = decode_icrs1___opnd, |
| [RISCV64_FLD_ZIMM] = decode_zimm_opnd, |
| [RISCV64_FLD_ZIMM10] = decode_zimm10_opnd, |
| [RISCV64_FLD_ZIMM11] = decode_zimm11_opnd, |
| [RISCV64_FLD_VM] = decode_vm_opnd, |
| [RISCV64_FLD_NF] = decode_nf_opnd, |
| [RISCV64_FLD_SIMM5] = decode_simm5_opnd, |
| [RISCV64_FLD_VD] = decode_vd_opnd, |
| [RISCV64_FLD_VS1] = decode_vs1_opnd, |
| [RISCV64_FLD_VS2] = decode_vs2_opnd, |
| [RISCV64_FLD_VS3] = decode_vs3_opnd, |
| [RISCV64_FLD_I_S_RS1_DISP] = decode_v_s_rs1_disp_opnd, |
| }; |
| |
| /* Decode RVC quadrant 0. |
| * |
| * The values are derived from table 16.5 in the RISC-V Instruction Set Manual |
| * Volume I: Unprivileged ISA (ver. 20191213). |
| */ |
| static inline rv_instr_info_t * |
| match_op_0(int funct, bool rv32, bool rv64) |
| { |
| switch (funct) { |
| case 0: return &instr_infos[OP_c_addi4spn]; |
| case 1: return &instr_infos[OP_c_fld]; |
| case 2: return &instr_infos[OP_c_lw]; |
| case 3: |
| if (rv32) |
| return &instr_infos[OP_c_flw]; |
| if (rv64) |
| return &instr_infos[OP_c_ld]; |
| return NULL; |
| // 4 is reserved. |
| case 5: return &instr_infos[OP_c_fsd]; |
| case 6: return &instr_infos[OP_c_sw]; |
| case 7: |
| if (rv32) |
| return &instr_infos[OP_c_fsw]; |
| else if (rv64) |
| return &instr_infos[OP_c_sd]; |
| else |
| return NULL; |
| default: return NULL; |
| } |
| } |
| |
| /* Decode RVC quadrant 1. |
| * |
| * The values are derived from table 16.6 in the RISC-V Instruction Set Manual |
| * Volume I: Unprivileged ISA (ver. 20191213). |
| */ |
| static inline rv_instr_info_t * |
| match_op_1(int funct, int funct2, int funct3, int bit11to7, int bit12, bool rv32, |
| bool rv64) |
| { |
| switch (funct) { |
| case 0: |
| if (bit11to7 == 0) |
| return &instr_infos[OP_c_nop]; |
| else |
| return &instr_infos[OP_c_addi]; |
| case 1: |
| if (rv32) |
| return &instr_infos[OP_c_jal]; |
| else if (rv64) |
| return &instr_infos[OP_c_addiw]; |
| else |
| return NULL; |
| case 2: return &instr_infos[OP_c_li]; |
| case 3: |
| if (bit11to7 == 2) |
| return &instr_infos[OP_c_addi16sp]; |
| else |
| return &instr_infos[OP_c_lui]; |
| case 4: |
| switch (funct2) { |
| case 0: return &instr_infos[OP_c_srli]; |
| case 1: return &instr_infos[OP_c_srai]; |
| case 2: return &instr_infos[OP_c_andi]; |
| case 3: |
| switch (bit12) { |
| case 0: |
| switch (funct3) { |
| case 0: return &instr_infos[OP_c_sub]; |
| case 1: return &instr_infos[OP_c_xor]; |
| case 2: return &instr_infos[OP_c_or]; |
| case 3: return &instr_infos[OP_c_and]; |
| default: return NULL; |
| } |
| case 1: |
| switch (funct3) { |
| case 0: return &instr_infos[OP_c_subw]; |
| case 1: return &instr_infos[OP_c_addw]; |
| // 2 and 3 are reserved. |
| default: return NULL; |
| } |
| default: return NULL; |
| } |
| default: return NULL; |
| } |
| case 5: return &instr_infos[OP_c_j]; |
| case 6: return &instr_infos[OP_c_beqz]; |
| case 7: return &instr_infos[OP_c_bnez]; |
| default: return NULL; |
| }; |
| } |
| |
| /* Decode RVC quadrant 2 |
| * |
| * The values are derived from table 16.7 in the RISC-V Instruction Set Manual |
| * Volume I: Unprivileged ISA (ver. 20191213). |
| */ |
| static inline rv_instr_info_t * |
| match_op_2(int funct, int bit11to7, int bit6to2, int bit12, bool rv32, bool rv64) |
| { |
| switch (funct) { |
| case 0: return &instr_infos[OP_c_slli]; |
| case 1: return &instr_infos[OP_c_fldsp]; |
| case 2: return &instr_infos[OP_c_lwsp]; |
| case 3: |
| if (rv32) |
| return &instr_infos[OP_c_flwsp]; |
| else if (rv64) |
| return &instr_infos[OP_c_ldsp]; |
| else |
| return NULL; |
| case 4: |
| switch (bit12) { |
| case 0: |
| if (bit6to2 == 0) |
| return &instr_infos[OP_c_jr]; |
| else |
| return &instr_infos[OP_c_mv]; |
| case 1: |
| if ((bit11to7 == 0) && (bit6to2 == 0)) |
| return &instr_infos[OP_c_ebreak]; |
| else if (bit6to2 == 0) |
| return &instr_infos[OP_c_jalr]; |
| else |
| return &instr_infos[OP_c_add]; |
| default: return NULL; |
| } |
| case 5: return &instr_infos[OP_c_fsdsp]; |
| case 6: return &instr_infos[OP_c_swsp]; |
| case 7: |
| if (rv32) |
| return &instr_infos[OP_c_fswsp]; |
| else if (rv64) |
| return &instr_infos[OP_c_sdsp]; |
| else |
| return NULL; |
| default: return NULL; |
| } |
| } |
| |
| static rv_instr_info_t * |
| get_rvc_instr_info(uint32_t inst, int xlen) |
| { |
| /* 0 is an illegal instruction which is often used as a canary. */ |
| if (inst == 0) |
| return &instr_infos[OP_unimp]; |
| |
| int op = GET_FIELD(inst, 1, 0); |
| int funct = GET_FIELD(inst, 15, 13); |
| int bit11to7 = GET_FIELD(inst, 11, 7); |
| int funct2 = GET_FIELD(inst, 11, 10); |
| int bit12 = BIT(inst, 12); |
| int funct3 = GET_FIELD(inst, 6, 5); |
| int bit6to2 = GET_FIELD(inst, 6, 2); |
| bool rv32 = xlen == 32; |
| bool rv64 = xlen == 64; |
| rv_instr_info_t *info = NULL; |
| uint32_t mask, match; |
| |
| switch (op) { |
| case 0: info = match_op_0(funct, rv32, rv64); break; |
| case 1: info = match_op_1(funct, funct2, funct3, bit11to7, bit12, rv32, rv64); break; |
| case 2: info = match_op_2(funct, bit11to7, bit6to2, bit12, rv32, rv64); break; |
| } |
| |
| if (info == NULL) |
| return NULL; |
| mask = GET_FIELD(info->info.code, 31, 0); |
| match = GET_FIELD(info->info.code, 63, 32); |
| |
| ASSERT_MESSAGE(CHKLVL_DEFAULT, "Malformed matching in RVC", (inst & mask) == match); |
| return info; |
| } |
| |
| #define OPCODE_FLD_MASK 0x7f |
| |
| static rv_instr_info_t * |
| get_rv_instr_info(uint32_t inst, trie_node_t trie[]) |
| { |
| rv_instr_info_t *info; |
| uint32_t mask, match; |
| size_t index; |
| |
| /* The initial lookup loop will always index with the OPCODE field so just skip this |
| * for faster lookup. |
| */ |
| index = (inst & OPCODE_FLD_MASK) + 1; |
| index = trie_lookup(trie, inst, index); |
| |
| if (index == TRIE_NODE_EMPTY) |
| return NULL; |
| info = &instr_infos[index]; |
| mask = GET_FIELD(info->info.code, 31, 0); |
| match = GET_FIELD(info->info.code, 63, 32); |
| /* Don't assert, rather allow for an unknown instruction. */ |
| if ((inst & mask) != match) |
| return NULL; |
| return info; |
| } |
| |
| /* |
| * End of format decoding functions. |
| **********************************************************/ |
| |
| instr_info_t * |
| get_instruction_info(uint opc) |
| { |
| if (opc >= BUFFER_SIZE_ELEMENTS(instr_infos)) |
| return NULL; |
| return &instr_infos[opc].info; |
| } |
| |
| byte * |
| decode_common(dcontext_t *dcontext, byte *pc, byte *orig_pc, instr_t *instr) |
| { |
| /* #DR_ISA_REGDEPS synthetic ISA has its own decoder. |
| * XXX i#1684: when DR can be built with full dynamic architecture selection we won't |
| * need to pollute the decoding of other architectures with this synthetic ISA special |
| * case. |
| */ |
| if (dr_get_isa_mode(dcontext) == DR_ISA_REGDEPS) |
| return decode_isa_regdeps(dcontext, pc, instr); |
| |
| /* Decode instruction width from the opcode. */ |
| int width = instruction_width(*(uint16_t *)pc); |
| /* Start assuming a compressed instruction. Code memory should be 2b aligned. */ |
| uint32_t inst = *(uint16_t *)pc; |
| rv_instr_info_t *info = NULL; |
| int nsrc = 0, ndst = 0; |
| byte *next_pc; |
| |
| CLIENT_ASSERT(instr->opcode == OP_INVALID || instr->opcode == OP_UNDECODED, |
| "decode: instr is already decoded, may need to call instr_reset()"); |
| |
| switch (width) { |
| case 4: |
| inst |= *((uint16_t *)pc + 1) << 16; |
| info = get_rv_instr_info(inst, instr_infos_trie); |
| break; |
| case 2: info = get_rvc_instr_info(inst, 64); break; |
| default: |
| LOG(THREAD, LOG_INTERP, 3, "decode: unhandled instruction width %d at " PFX "\n", |
| width, pc); |
| return NULL; |
| } |
| next_pc = pc + width; |
| |
| if (info == NULL) { |
| LOG(THREAD, LOG_INTERP, 3, "decode: unknown instruction 0x%08x at " PFX "\n", |
| inst, pc); |
| return NULL; |
| } |
| |
| nsrc = INFO_NSRC(info->info.opcode); |
| ndst = INFO_NDST(info->info.opcode); |
| CLIENT_ASSERT(ndst >= 0 || ndst <= 1, "Invalid number of destination operands."); |
| CLIENT_ASSERT(nsrc >= 0 || nsrc <= 4, "Invalid number of source operands."); |
| |
| instr_set_opcode(instr, info->info.type); |
| instr_set_num_opnds(dcontext, instr, ndst, nsrc); |
| |
| switch (ndst) { |
| case 2: |
| CLIENT_ASSERT(info->info.dst2_type < RISCV64_FLD_CNT, "Invalid dst2_type."); |
| if (!opnd_decoders[info->info.dst2_type](dcontext, inst, info->info.dst2_size, pc, |
| orig_pc, 1, instr)) |
| goto decode_failure; |
| case 1: |
| CLIENT_ASSERT(info->info.dst1_type < RISCV64_FLD_CNT, "Invalid dst1_type."); |
| if (!opnd_decoders[info->info.dst1_type](dcontext, inst, info->info.dst1_size, pc, |
| orig_pc, 0, instr)) |
| goto decode_failure; |
| case 0: break; |
| default: ASSERT_NOT_REACHED(); |
| } |
| switch (nsrc) { |
| case 4: |
| CLIENT_ASSERT(info->info.dst2_type < RISCV64_FLD_CNT, "Invalid dst2_type."); |
| if (!opnd_decoders[info->info.dst2_type](dcontext, inst, info->info.dst2_size, pc, |
| orig_pc, 3, instr)) |
| goto decode_failure; |
| case 3: |
| CLIENT_ASSERT(info->info.src3_type < RISCV64_FLD_CNT, "Invalid src3_type."); |
| if (!opnd_decoders[info->info.src3_type](dcontext, inst, info->info.src3_size, pc, |
| orig_pc, 2, instr)) |
| goto decode_failure; |
| case 2: |
| CLIENT_ASSERT(info->info.src2_type < RISCV64_FLD_CNT, "Invalid src2_type."); |
| if (!opnd_decoders[info->info.src2_type](dcontext, inst, info->info.src2_size, pc, |
| orig_pc, 1, instr)) |
| goto decode_failure; |
| case 1: |
| CLIENT_ASSERT(info->info.src1_type < RISCV64_FLD_CNT, "Invalid src1_type."); |
| if (!opnd_decoders[info->info.src1_type](dcontext, inst, info->info.src1_size, pc, |
| orig_pc, 0, instr)) |
| goto decode_failure; |
| case 0: break; |
| default: ASSERT_NOT_REACHED(); |
| } |
| |
| if (orig_pc != pc) { |
| /* We do not want to copy when encoding and condone an invalid |
| * relative target. |
| * FIXME i#3544: Add re-relativization support without having to re-encode. |
| */ |
| instr_set_raw_bits_valid(instr, false); |
| instr_set_translation(instr, orig_pc); |
| } else { |
| /* We set raw bits AFTER setting all srcs and dsts because setting |
| * a src or dst marks instr as having invalid raw bits. |
| */ |
| ASSERT(CHECK_TRUNCATE_TYPE_uint(next_pc - pc)); |
| instr_set_raw_bits(instr, pc, (uint)(next_pc - pc)); |
| } |
| |
| return next_pc; |
| |
| decode_failure: |
| instr_set_operands_valid(instr, false); |
| instr_set_opcode(instr, OP_INVALID); |
| return NULL; |
| } |
| |
| /* Instruction operand encoder function. |
| * |
| * Encodes an operand from a given instr_t into the instruction. |
| */ |
| typedef bool (*opnd_enc_func_t)(instr_t *instr, byte *pc, int idx, uint32_t *out, |
| decode_info_t *di); |
| |
| /********************************************************** |
| * Format encoding functions. |
| */ |
| |
| /* Dummy function for catching invalid operand values. Should never be called. |
| */ |
| static bool |
| encode_none_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| |
| /* Encodes an implicit opnd, no need to do anything. |
| */ |
| static bool |
| encode_implicit_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| return true; |
| } |
| |
| /* Encode the destination fixed-point register field: |
| * |31 12|11 7|6 0| |
| * | ... | rd | opcode | |
| * ^----^ |
| * Applies to R, R4, I, U and J uncompressed formats. |
| */ |
| static bool |
| encode_rd_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 11, 7); |
| return true; |
| } |
| |
| /* Encode the destination floating-point register field: |
| * |31 12|11 7|6 0| |
| * | ... | rd | opcode | |
| * ^----^ |
| * Applies to R, R4, I, U and J uncompressed formats. |
| */ |
| static bool |
| encode_rdfp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| ASSERT(opnd_get_reg(opnd) >= DR_REG_F0); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 11, 7); |
| return true; |
| } |
| |
| /* Encode the destination vector register field: |
| * |31 12|11 7|6 0| |
| * | ... | vd | opcode | |
| * ^----^ |
| */ |
| static bool |
| encode_vd_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t reg = opnd_get_reg(opnd) - DR_REG_VR0; |
| *out |= SET_FIELD(reg, 11, 7); |
| return true; |
| } |
| |
| /* Encode the 1st source fixed-point register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | rs1 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, I, S and B uncompressed formats. |
| */ |
| static bool |
| encode_rs1_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 19, 15); |
| return true; |
| } |
| |
| /* Encode the 1st source floating-point register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | rs1 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, I, S and B uncompressed formats. |
| */ |
| static bool |
| encode_rs1fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| ASSERT(opnd_get_reg(opnd) >= DR_REG_F0); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 19, 15); |
| return true; |
| } |
| |
| /* Encode the 1st source vector register field: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | vs1 | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| encode_vs1_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_reg(opnd) - DR_REG_VR0; |
| *out |= SET_FIELD(reg, 19, 15); |
| return true; |
| } |
| |
| /* Encode the rs1 field as a base register: |
| * |31 20|19 15|14 7|6 0| |
| * | ... | base | ... | opcode | |
| * ^------^ |
| * Applies to instructions of the Zicbom and Zicbop extensions. |
| */ |
| static bool |
| encode_base_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_base(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 19, 15); |
| return true; |
| } |
| |
| /* Encode the 2nd source fixed-point register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | rs2 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, S and B uncompressed formats. |
| */ |
| static bool |
| encode_rs2_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 24, 20); |
| return true; |
| } |
| |
| /* Encode the 2nd source floating-point register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | rs2 | ... | opcode | |
| * ^-----^ |
| * Applies to R, R4, S and B uncompressed formats. |
| */ |
| static bool |
| encode_rs2fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| ASSERT(opnd_get_reg(opnd) >= DR_REG_F0); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 24, 20); |
| return true; |
| } |
| |
| /* Encode the 2nd source vector register field: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | vs2 | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| encode_vs2_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_reg(opnd) - DR_REG_VR0; |
| *out |= SET_FIELD(reg, 24, 20); |
| return true; |
| } |
| |
| /* Encode the 3rd source fixed-point register field: |
| * |31 27|26 7|6 0| |
| * | rs3 | ... | opcode | |
| * ^---^ |
| * Applies to the R4 uncompressed format. |
| */ |
| static bool |
| encode_rs3fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 31, 27); |
| return true; |
| } |
| |
| /* Encode the 3rd source vector register field: |
| * |31 12|11 7|6 0| |
| * | ... | vs3 | opcode | |
| * ^----^ |
| */ |
| static bool |
| encode_vs3_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_reg(opnd) - DR_REG_VR0; |
| *out |= SET_FIELD(reg, 11, 7); |
| return true; |
| } |
| |
| /* Encode the fence mode field of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^----^ |
| */ |
| static bool |
| encode_fm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 31, 28); |
| return true; |
| } |
| |
| /* Encode all predecessor bits of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^-----------------^ |
| */ |
| static bool |
| encode_pred_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 27, 24); |
| return true; |
| } |
| |
| /* Encode all successor bits of the "fence" instruction: |
| * |31 28| 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 |19 15|14 12|11 7|6 0| |
| * | fm | PI | PO | PR | PW | SI | SO | SR | SW | rs1 | funct3 | rd | 0xF | |
| * ^-----------------^ |
| */ |
| static bool |
| encode_succ_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 23, 20); |
| return true; |
| } |
| |
| /* Encode acquire-release semantics of an atomic instruction (A extension): |
| * |31 27| 26 | 25 |24 7|6 0| |
| * | ... | aq | rl | ... | opcode | |
| * ^-------^ |
| */ |
| static bool |
| encode_aqrl_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 26, 25); |
| return true; |
| } |
| |
| /* Encode the CSR number in instructions from the Zicsr extension: |
| * |31 20|19 7|6 0| |
| * | csr | ... | opcode | |
| * ^---^ |
| */ |
| static bool |
| encode_csr_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 31, 20); |
| return true; |
| } |
| |
| /* Encode the rounding mode in floating-point instructions: |
| * |31 15|14 12|11 7|6 0| |
| * | ... | rm | ... | opcode | |
| * ^----^ |
| * The valid values can be found in Table 11.1 in the RISC-V |
| * Instruction Set Manual Volume I: Unprivileged ISA (ver. 20191213). |
| */ |
| static bool |
| encode_rm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| /* Invalid. Reserved for future use. */ |
| ASSERT(imm != 0b101 && imm != 0b110); |
| *out |= SET_FIELD(imm, 14, 12); |
| return true; |
| } |
| |
| /* Encode the 6-bit (6th bit always 0 in rv32) shift amount: |
| * |31 26|25 20|19 7|6 0| |
| * | ... | shamt | ... | opcode | |
| * ^-----^ |
| */ |
| static bool |
| encode_shamt_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 25, 20); |
| return true; |
| } |
| |
| /* Encode the 5-bit shift amount in rv64: |
| * |31 25|24 20|19 7|6 0| |
| * | ... | shamt5 | ... | opcode | |
| * ^------^ |
| */ |
| static bool |
| encode_shamt5_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 24, 20); |
| return true; |
| } |
| |
| /* Encode the 7-bit (7th bit always 0 in rv64) shift amount in rv64: |
| * |31 27|26 20|19 7|6 0| |
| * | ... | shamt6 | ... | opcode | |
| * ^------^ |
| */ |
| static bool |
| encode_shamt6_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| /* shamt6 >= 64 only makes sense on RV128 but let user take care of it. */ |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 26, 20); |
| return true; |
| } |
| |
| /* Encode the immediate field of the I-type format: |
| * |31 20|19 15|14 12|11 7|6 0| |
| * | imm[11:0] | rs1 | funct3 | rd | opcode | |
| * ^---------^ |
| * From: |
| * |31 11|10 0| |
| * | imm[11] | imm[10:0] | |
| */ |
| static bool |
| encode_i_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 31, 20); |
| return true; |
| } |
| |
| /* Encode the immediate field of the S-type format: |
| * |31 25|24 20|19 15|14 12|11 7|6 0| |
| * | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | |
| * ^---------^ ^--------^ |
| * From: |
| * |31 11|10 5|4 0| |
| * | imm[11] | imm[10:5] | imm[4:0] | |
| */ |
| static bool |
| encode_s_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm >> 5, 31, 25) | SET_FIELD(imm, 11, 7); |
| return true; |
| } |
| |
| /* Encode the immediate field of the B-type format as a pc-relative offset: |
| * | 31 |30 25|24 20|19 15|14 12|11 8| 7 |6 0| |
| * |imm[12]|imm[10:5]| rs2 | rs1 | funct3 |imm[4:1]|imm[11]| opcode | |
| * ^---------------^ ^--------------^ |
| * From: |
| * |31 12| 11 |10 5|4 1| 0 | |
| * | imm[12] |imm[11]| imm[10:5] | imm[4:1] | 0 | |
| */ |
| static bool |
| encode_b_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_target(instr); |
| int32_t imm; |
| if (opnd.kind == PC_kind) |
| imm = opnd_get_pc(opnd) - pc; |
| else if (opnd.kind == INSTR_kind) |
| imm = (byte *)opnd_get_instr(opnd)->offset - (byte *)instr->offset; |
| else |
| return false; |
| |
| *out |= SET_FIELD(imm >> 11, 7, 7) | SET_FIELD(imm >> 1, 11, 8) | |
| SET_FIELD(imm >> 5, 30, 25) | SET_FIELD(imm >> 12, 31, 31); |
| return true; |
| } |
| |
| /* Encode the immediate field of the U-type format: |
| * |31 12|11 7|6 0| |
| * | imm[31:12] | rd | opcode | |
| * ^----------^ |
| * From: |
| * |31 12|11 0| |
| * | imm[31:12] | 0 | |
| */ |
| static bool |
| encode_u_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 31, 12); |
| return true; |
| } |
| |
| /* Encode the immediate field of the U-type format (PC-relative): |
| * |31 12|11 7|6 0| |
| * | imm[31:12] | rd | opcode | |
| * ^----------^ |
| * From: |
| * |31 12|11 0| |
| * | imm[31:12] | 0 | |
| */ |
| static bool |
| encode_u_immpc_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm; |
| if (opnd.kind == REL_ADDR_kind) |
| imm = (app_pc)opnd_get_addr(opnd) - pc; |
| else if (opnd.kind == INSTR_kind) |
| imm = (byte *)opnd_get_instr(opnd)->offset - (byte *)instr->offset; |
| else |
| return false; |
| if (!di->check_reachable || ((imm >> 12) << 12) == imm) { |
| *out |= SET_FIELD(imm >> 12, 31, 12); |
| return true; |
| } else |
| return false; |
| } |
| |
| /* Encode the immediate field of the J-type format as a pc-relative offset: |
| * | 31 |30 21| 20 |19 12|11 7|6 0| |
| * | imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode | |
| * ^------------------------------------------^ |
| * From: |
| * |31 20|19 12| 11 |10 1| 0 | |
| * | imm[20] | imm[19:12] | imm[11] | imm[10:1] | 0 | |
| */ |
| static bool |
| encode_j_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_target(instr); |
| int32_t imm; |
| if (opnd.kind == PC_kind) |
| imm = opnd_get_pc(opnd) - pc; |
| else if (opnd.kind == INSTR_kind) |
| imm = (byte *)opnd_get_instr(opnd)->offset - (byte *)instr->offset; |
| else |
| return false; |
| |
| *out |= SET_FIELD(imm >> 1, 31, 21) | SET_FIELD(imm >> 11, 20, 20) | |
| SET_FIELD(imm >> 12, 19, 12) | SET_FIELD(imm >> 20, 31, 31); |
| return true; |
| } |
| |
| /* Encode the destination fixed-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| encode_crd_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 11, 7); |
| return true; |
| } |
| |
| /* Encode the destination floating-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| encode_crdfp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 11, 7); |
| return true; |
| } |
| |
| /* Encode the 1st source fixed-point register field: |
| * |31 12|11 7|6 2|1 0| |
| * | ... | rd | ... | opcode | |
| * ^----^ |
| * Applies to CR and CI compressed formats. |
| */ |
| static bool |
| encode_crs1_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 11, 7); |
| return true; |
| } |
| |
| /* Encode the 2nd source fixed-point register field: |
| * |31 7|6 2|1 0| |
| * | ... | rs2 | opcode | |
| * ^---^ |
| * Applies to CR and CSS compressed formats. |
| */ |
| static bool |
| encode_crs2_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(rd, 6, 2); |
| return true; |
| } |
| |
| /* Encode the 2nd source floating-point register field: |
| * |31 7|6 2|1 0| |
| * | ... | rs2 | opcode | |
| * ^---^ |
| * Applies to CR and CSS compressed formats. |
| */ |
| static bool |
| encode_crs2fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F0; |
| *out |= SET_FIELD(rd, 6, 2); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) destination fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to CIW and CL compressed formats. |
| */ |
| static bool |
| encode_crd__opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(rd, 4, 2); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) destination floating-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to CIW and CL compressed formats. |
| */ |
| static bool |
| encode_crd_fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F8; |
| *out |= SET_FIELD(rd, 4, 2); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) 1st source fixed-point register field: |
| * |31 10|9 7|6 2|1 0| |
| * | ... | rs1' | ... | opcode | |
| * ^---^ |
| * Applies to CL, CS, CA and CB compressed formats. |
| */ |
| static bool |
| encode_crs1__opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(rd, 9, 7); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) 2nd source fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rs2' | opcode | |
| * ^---^ |
| * Applies to CS and CA compressed formats. |
| */ |
| static bool |
| encode_crs2__opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(rd, 4, 2); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) 2nd source floating-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rs2' | opcode | |
| * ^---^ |
| * Applies to CS and CA compressed formats. |
| */ |
| static bool |
| encode_crs2_fp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_F8; |
| *out |= SET_FIELD(rd, 4, 2); |
| return true; |
| } |
| |
| /* Encode the limited range (x8-x15) destination fixed-point register field: |
| * |31 5|4 2|1 0| |
| * | ... | rd' | opcode | |
| * ^---^ |
| * Applies to the CA compressed format. |
| */ |
| static bool |
| encode_crd___opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t rd = opnd_get_reg(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(rd, 9, 7); |
| return true; |
| } |
| |
| /* Encode the 6-bit (6th bit always 0 in rv32) shift amount: |
| * |15 13| 12 |11 10|9 7|6 2|1 0| |
| * | funct3 | imm[5] | funct2 | rs1' | imm[4:0] | opcode | |
| * ^------^ ^--------^ |
| */ |
| static bool |
| encode_cshamt_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 6, 2) | SET_FIELD(imm >> 5, 12, 12); |
| return true; |
| } |
| |
| /* Encode the CSR immediate in instructions from the Zicsr extension: |
| * |31 20|19 15|14 7|6 0| |
| * | csr | imm[4:0] | ... | opcode | |
| * ^--------^ |
| * From: |
| * |31 5|4 0| |
| * | 0 | imm[4:0] | |
| */ |
| static bool |
| encode_csr_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 19, 15); |
| return true; |
| } |
| |
| /* Encode the immediate of the caddi16sp instruction: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[9] | ... | imm[4|6|8:7|5] | opcode | |
| * ^------^ ^--------------^ |
| * From: |
| * |31 9|8 4|3 0| |
| * | imm[9] | imm[8:4] | 0 | |
| */ |
| static bool |
| encode_caddi16sp_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, |
| decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm >> 5, 2, 2) | SET_FIELD(imm >> 7, 4, 3) | |
| SET_FIELD(imm >> 6, 5, 5) | SET_FIELD(imm >> 4, 6, 6) | |
| SET_FIELD(imm >> 9, 12, 12); |
| return true; |
| } |
| |
| /* Encode the SP-based immediate offset of c.lwsp and c.flwsp instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:2|7:6] | opcode | |
| * ^------^ ^------------^ |
| * From: |
| * |31 8|7 2|3 0| |
| * sp + | 0 | imm[7:2] | 0 | |
| */ |
| static bool |
| encode_clwsp_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_disp(opnd); |
| ASSERT(opnd_get_base(opnd) == DR_REG_SP); |
| *out |= SET_FIELD(imm >> 6, 3, 2) | SET_FIELD(imm >> 2, 6, 4) | |
| SET_FIELD(imm >> 5, 12, 12); |
| return true; |
| } |
| |
| /* Encode the SP-based immediate offset of c.ldsp and c.fldsp instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:3|8:6] | opcode | |
| * ^------^ ^------------^ |
| * From: |
| * |31 9|8 2|3 0| |
| * sp + | 0 | imm[8:3] | 0 | |
| */ |
| static bool |
| encode_cldsp_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_disp(opnd); |
| ASSERT(opnd_get_base(opnd) == DR_REG_SP); |
| *out |= SET_FIELD(imm >> 6, 4, 2) | SET_FIELD(imm >> 3, 6, 5) | |
| SET_FIELD(imm >> 5, 12, 12); |
| return true; |
| } |
| |
| /* Encode the immediate of the c.lui instruction: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[17] | ... | imm[16:12] | opcode | |
| * ^-------^ ^----------^ |
| * From: |
| * |31 17|16 12|11 0| |
| * | imm[17] | imm[16:12] | 0 | |
| */ |
| static bool |
| encode_clui_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 6, 2) | SET_FIELD(imm >> 5, 12, 12); |
| return true; |
| } |
| |
| /* Encode the SP-based offset immediate of c.swsp and c.fswsp instructions: |
| * |15 13|12 7|6 2|1 0| |
| * | ... | imm[5:2|7:6] | ... | opcode | |
| * ^------------^ |
| * From: |
| * |31 8|7 2|1 0| |
| * sp + | 0 | imm[7:2] | 0 | |
| */ |
| static bool |
| encode_cswsp_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| int32_t imm = opnd_get_disp(opnd); |
| ASSERT(opnd_get_base(opnd) == DR_REG_SP); |
| *out |= SET_FIELD(imm >> 6, 8, 7) | SET_FIELD(imm >> 2, 12, 9); |
| return true; |
| } |
| |
| /* Encode the SP-based offset immediate of c.sdsp and c.fsdsp instructions: |
| * |15 13|12 7|6 2|1 0| |
| * | ... | imm[5:3|8:6] | ... | opcode | |
| * ^------------^ |
| * From: |
| * |31 9|8 3|2 0| |
| * sp + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| encode_csdsp_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| int32_t imm = opnd_get_disp(opnd); |
| ASSERT(opnd_get_base(opnd) == DR_REG_SP); |
| *out |= SET_FIELD(imm >> 6, 9, 7) | SET_FIELD(imm >> 3, 12, 10); |
| return true; |
| } |
| |
| /* Encode the immediate of the c.addi4spn instruction: |
| * |15 13|12 5|4 2|1 0| |
| * | ... | imm[5:4|9:6|2|3] | ... | opcode | |
| * ^----------------^ |
| * From: |
| * |31 10|9 2|1 0| |
| * | 0 | imm[9:2] | 0 | |
| */ |
| static bool |
| encode_ciw_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm >> 3, 5, 5) | SET_FIELD(imm >> 2, 6, 6) | |
| SET_FIELD(imm >> 6, 10, 7) | SET_FIELD(imm >> 4, 12, 11); |
| return true; |
| } |
| |
| /* Encode the base register and offset immediate of c.lw and c.flw |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[2|6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * From: |
| * |31 7|6 2|1 0| |
| * rs1' + | 0 | imm[6:2] | 0 | |
| */ |
| static bool |
| encode_clw_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(reg, 9, 7); |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm >> 6, 5, 5) | SET_FIELD(imm >> 2, 6, 6) | |
| SET_FIELD(imm >> 3, 12, 10); |
| return true; |
| } |
| |
| /* Encode the base register and offset immediate of c.ld and c.fld |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[7:6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * From: |
| * |31 8|7 3|2 0| |
| * rs1' + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| encode_cld_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(reg, 9, 7); |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm >> 6, 6, 5) | SET_FIELD(imm >> 3, 12, 10); |
| return true; |
| } |
| |
| /* Encode the base register and offset immediate of c.sw and c.fsw |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[2|6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * From: |
| * |31 7|6 2|1 0| |
| * rs1' + | 0 | imm[6:2] | 0 | |
| */ |
| static bool |
| encode_csw_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(reg, 9, 7); |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm >> 6, 5, 5) | SET_FIELD(imm >> 2, 6, 6) | |
| SET_FIELD(imm >> 3, 12, 10); |
| return true; |
| } |
| |
| /* Encode the base register and offset immediate of c.sd and c.fsd |
| * instructions: |
| * |15 13|12 10|9 7|6 5|4 2|1 0| |
| * | ... | imm[5:3] | rs1' | imm[7:6] | ... | opcode | |
| * ^--------^ ^----^ ^--------^ |
| * From: |
| * |31 8|7 3|2 0| |
| * rs1' + | 0 | imm[7:3] | 0 | |
| */ |
| static bool |
| encode_csd_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X8; |
| *out |= SET_FIELD(reg, 9, 7); |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm >> 6, 6, 5) | SET_FIELD(imm >> 3, 12, 10); |
| return true; |
| } |
| |
| /* Encode the base immediate of c.addi, c.addiw, c.li, c.andi instructions: |
| * |15 13| 12 |11 7|6 2|1 0| |
| * | ... | imm[5] | ... | imm[4:0] | opcode | |
| * ^------^ ^--------^ |
| * From: |
| * |31 5|4 0| |
| * | imm[5] | imm[4:0] | |
| */ |
| static bool |
| encode_cimm5_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 6, 2) | SET_FIELD(imm >> 5, 12, 12); |
| return true; |
| } |
| |
| /* Encode the immediate field of the CB-type format as a pc-relative offset: |
| * |15 13|12 10|9 7|6 2|1 0| |
| * | ... | imm[8|4:3] | ... | imm[7:6|2:1|5] | opcode | |
| * ^----------^ ^--------------^ |
| * From: |
| * |31 8|7 1| 0 | |
| * | imm[8] | imm[7:1] | 0 | |
| */ |
| static bool |
| encode_cb_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_target(instr); |
| |
| int32_t imm; |
| if (opnd.kind == PC_kind) |
| imm = opnd_get_pc(opnd) - pc; |
| else if (opnd.kind == INSTR_kind) |
| imm = (byte *)opnd_get_instr(opnd)->offset - (byte *)instr->offset; |
| else |
| return false; |
| |
| *out |= SET_FIELD(imm >> 5, 2, 2) | SET_FIELD(imm >> 1, 4, 3) | |
| SET_FIELD(imm >> 6, 6, 5) | SET_FIELD(imm >> 3, 11, 10) | |
| SET_FIELD(imm >> 8, 12, 12); |
| return true; |
| } |
| |
| /* Encode the immediate field of the CJ-type format as a pc-relative offset: |
| * |15 13|12 2|1 0| |
| * | ... | [11|4|9:8|10|6|7|3:1|5] | opcode | |
| * ^-----------------------^ |
| * From: |
| * |31 11|10 1| 0 | |
| * | imm[11] | imm[10:1] | 0 | |
| */ |
| static bool |
| encode_cj_imm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_target(instr); |
| |
| int32_t imm; |
| if (opnd.kind == PC_kind) |
| imm = opnd_get_pc(opnd) - pc; |
| else if (opnd.kind == INSTR_kind) |
| imm = (byte *)opnd_get_instr(opnd)->offset - (byte *)instr->offset; |
| else |
| return false; |
| |
| *out |= SET_FIELD(imm >> 5, 2, 2) | SET_FIELD(imm >> 1, 5, 3) | |
| SET_FIELD(imm >> 7, 6, 6) | SET_FIELD(imm >> 6, 7, 7) | |
| SET_FIELD(imm >> 10, 8, 8) | SET_FIELD(imm >> 8, 10, 9) | |
| SET_FIELD(imm >> 4, 11, 11) | SET_FIELD(imm >> 11, 12, 12); |
| return true; |
| } |
| |
| /* Encode the base register and immediate offset of a virtual load-like field: |
| * |31 20|19 15|14 7|6 0| |
| * | imm[11:0] | rs1 | ... | opcode | |
| * ^---------^ ^-----^ |
| * From: |
| * |31 11|7 0| |
| * rs1 + | imm[11] | imm[10:0] | |
| * |
| * Note that this is a virtual field injected by codec.py into instructions |
| * which share the immediate field type with other non-base+disp instructions. |
| */ |
| static bool |
| encode_v_l_rs1_disp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, |
| decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(reg, 19, 15); |
| if (instr->opcode != OP_lr_w && instr->opcode != OP_lr_d) { |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm, 31, 20); |
| } |
| return true; |
| } |
| |
| /* Encode the base register and immediate offset of a virtual store-like field: |
| * |31 25|24 20|19 15|14 12|11 7|6 0| |
| * | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | |
| * ^---------^ ^-----^ ^--------^ |
| * From: |
| * |31 11|7 0| |
| * rs1 + | imm[11] | imm[10:0] | |
| * |
| * Note that this is a virtual field injected by codec.py into instructions |
| * which share the immediate field type with other non-base+disp instructions. |
| */ |
| static bool |
| encode_v_s_rs1_disp_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, |
| decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_dst(instr, idx); |
| uint32_t reg = opnd_get_base(opnd) - DR_REG_X0; |
| *out |= SET_FIELD(reg, 19, 15); |
| if (instr->opcode != OP_sc_w && instr->opcode != OP_sc_d) { |
| int32_t imm = opnd_get_disp(opnd); |
| *out |= SET_FIELD(imm, 11, 7) | SET_FIELD(imm >> 5, 31, 25); |
| } |
| return true; |
| } |
| |
| /* Encode the zimm immediate field in vsetivli instruction (V extension): |
| * |31 30|29 20|19 15|14 12|11 7|6 0| |
| * | ... | zimm10 | zimm | ... | rd | opcode | |
| * ^------^ |
| */ |
| static bool |
| encode_zimm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 19, 15); |
| return true; |
| } |
| |
| /* Encode the zimm10 immediate field in vsetivli instruction (V extension): |
| * |31 30|29 20|19 15|14 12|11 7|6 0| |
| * | ... | zimm10 | zimm | ... | rd | opcode | |
| * ^--------^ |
| */ |
| static bool |
| encode_zimm10_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 29, 20); |
| return true; |
| } |
| |
| /* Encode the zimm11 immediate field in vsetvli instruction (V extension): |
| * |31|30 20|19 15|14 12|11 7|6 0| |
| * | | zimm11 | rs1 | ... | rd | opcode | |
| * ^--------^ |
| */ |
| static bool |
| encode_zimm11_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 30, 20); |
| return true; |
| } |
| |
| /* Encode the vm (vector mask) immediate field in vector instructions (V extension): |
| * |31 26| 25 |24 0| |
| * | ... | vm | ... | |
| * ^----^ |
| */ |
| static bool |
| encode_vm_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 25, 25); |
| return true; |
| } |
| |
| /* Encode the nf (nfields) immediate field in vector instructions (V extension): |
| * |31 29|28 0| |
| * | nf | ... | |
| * ^------^ |
| */ |
| static bool |
| encode_nf_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| uint32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 31, 29); |
| return true; |
| } |
| |
| /* Encode the simm5 immediate field in vector instructions (V extension): |
| * |31 26| 25 |24 20|19 15|14 12|11 7|6 0| |
| * | funct6 | vm | vs2 | simm5 | ... | vd | opcode | |
| * ^-------^ |
| */ |
| static bool |
| encode_simm5_opnd(instr_t *instr, byte *pc, int idx, uint32_t *out, decode_info_t *di) |
| { |
| opnd_t opnd = instr_get_src(instr, idx); |
| int32_t imm = opnd_get_immed_int(opnd); |
| *out |= SET_FIELD(imm, 19, 15); |
| return true; |
| } |
| |
| /* Array of operand encode functions indexed by riscv64_fld_t. */ |
| opnd_enc_func_t opnd_encoders[] = { |
| [RISCV64_FLD_NONE] = encode_none_opnd, |
| [RISCV64_FLD_RD] = encode_rd_opnd, |
| [RISCV64_FLD_RDFP] = encode_rdfp_opnd, |
| [RISCV64_FLD_RS1] = encode_rs1_opnd, |
| [RISCV64_FLD_RS1FP] = encode_rs1fp_opnd, |
| [RISCV64_FLD_BASE] = encode_base_opnd, |
| [RISCV64_FLD_RS2] = encode_rs2_opnd, |
| [RISCV64_FLD_RS2FP] = encode_rs2fp_opnd, |
| [RISCV64_FLD_RS3FP] = encode_rs3fp_opnd, |
| [RISCV64_FLD_FM] = encode_fm_opnd, |
| [RISCV64_FLD_PRED] = encode_pred_opnd, |
| [RISCV64_FLD_SUCC] = encode_succ_opnd, |
| [RISCV64_FLD_AQRL] = encode_aqrl_opnd, |
| [RISCV64_FLD_CSR] = encode_csr_opnd, |
| [RISCV64_FLD_RM] = encode_rm_opnd, |
| [RISCV64_FLD_SHAMT] = encode_shamt_opnd, |
| [RISCV64_FLD_SHAMT5] = encode_shamt5_opnd, |
| [RISCV64_FLD_SHAMT6] = encode_shamt6_opnd, |
| [RISCV64_FLD_I_IMM] = encode_i_imm_opnd, |
| [RISCV64_FLD_S_IMM] = encode_s_imm_opnd, |
| [RISCV64_FLD_B_IMM] = encode_b_imm_opnd, |
| [RISCV64_FLD_U_IMM] = encode_u_imm_opnd, |
| [RISCV64_FLD_U_IMMPC] = encode_u_immpc_opnd, |
| [RISCV64_FLD_J_IMM] = encode_j_imm_opnd, |
| [RISCV64_FLD_CRD] = encode_crd_opnd, |
| [RISCV64_FLD_CRDFP] = encode_crdfp_opnd, |
| [RISCV64_FLD_CRS1] = encode_crs1_opnd, |
| [RISCV64_FLD_CRS2] = encode_crs2_opnd, |
| [RISCV64_FLD_CRS2FP] = encode_crs2fp_opnd, |
| [RISCV64_FLD_CRD_] = encode_crd__opnd, |
| [RISCV64_FLD_CRD_FP] = encode_crd_fp_opnd, |
| [RISCV64_FLD_CRS1_] = encode_crs1__opnd, |
| [RISCV64_FLD_CRS2_] = encode_crs2__opnd, |
| [RISCV64_FLD_CRS2_FP] = encode_crs2_fp_opnd, |
| [RISCV64_FLD_CRD__] = encode_crd___opnd, |
| [RISCV64_FLD_CSHAMT] = encode_cshamt_opnd, |
| [RISCV64_FLD_CSR_IMM] = encode_csr_imm_opnd, |
| [RISCV64_FLD_CADDI16SP_IMM] = encode_caddi16sp_imm_opnd, |
| [RISCV64_FLD_CLWSP_IMM] = encode_clwsp_imm_opnd, |
| [RISCV64_FLD_CLDSP_IMM] = encode_cldsp_imm_opnd, |
| [RISCV64_FLD_CLUI_IMM] = encode_clui_imm_opnd, |
| [RISCV64_FLD_CSWSP_IMM] = encode_cswsp_imm_opnd, |
| [RISCV64_FLD_CSDSP_IMM] = encode_csdsp_imm_opnd, |
| [RISCV64_FLD_CIW_IMM] = encode_ciw_imm_opnd, |
| [RISCV64_FLD_CLW_IMM] = encode_clw_imm_opnd, |
| [RISCV64_FLD_CLD_IMM] = encode_cld_imm_opnd, |
| [RISCV64_FLD_CSW_IMM] = encode_csw_imm_opnd, |
| [RISCV64_FLD_CSD_IMM] = encode_csd_imm_opnd, |
| [RISCV64_FLD_CIMM5] = encode_cimm5_opnd, |
| [RISCV64_FLD_CB_IMM] = encode_cb_imm_opnd, |
| [RISCV64_FLD_CJ_IMM] = encode_cj_imm_opnd, |
| [RISCV64_FLD_V_L_RS1_DISP] = encode_v_l_rs1_disp_opnd, |
| [RISCV64_FLD_V_S_RS1_DISP] = encode_v_s_rs1_disp_opnd, |
| [RISCV64_FLD_IRS1_SP] = encode_implicit_opnd, |
| [RISCV64_FLD_IRS1_ZERO] = encode_implicit_opnd, |
| [RISCV64_FLD_IRS2_ZERO] = encode_implicit_opnd, |
| [RISCV64_FLD_IRD_ZERO] = encode_implicit_opnd, |
| [RISCV64_FLD_IRD_RA] = encode_implicit_opnd, |
| [RISCV64_FLD_IRD_SP] = encode_implicit_opnd, |
| [RISCV64_FLD_IIMM_0] = encode_implicit_opnd, |
| [RISCV64_FLD_ICRS1] = encode_implicit_opnd, |
| [RISCV64_FLD_ICRS1__] = encode_implicit_opnd, |
| [RISCV64_FLD_ZIMM] = encode_zimm_opnd, |
| [RISCV64_FLD_ZIMM10] = encode_zimm10_opnd, |
| [RISCV64_FLD_ZIMM11] = encode_zimm11_opnd, |
| [RISCV64_FLD_VM] = encode_vm_opnd, |
| [RISCV64_FLD_NF] = encode_nf_opnd, |
| [RISCV64_FLD_SIMM5] = encode_simm5_opnd, |
| [RISCV64_FLD_VD] = encode_vd_opnd, |
| [RISCV64_FLD_VS1] = encode_vs1_opnd, |
| [RISCV64_FLD_VS2] = encode_vs2_opnd, |
| [RISCV64_FLD_VS3] = encode_vs3_opnd, |
| [RISCV64_FLD_I_S_RS1_DISP] = encode_implicit_opnd, |
| }; |
| |
| uint |
| encode_common(byte *pc, instr_t *instr, decode_info_t *di) |
| { |
| ASSERT(((ptr_int_t)pc & 1) == 0); |
| ASSERT(instr->opcode < sizeof(instr_infos) / sizeof(rv_instr_info_t)); |
| |
| rv_instr_info_t *info = &instr_infos[instr->opcode]; |
| int ndst = INFO_NDST(info->info.opcode); |
| int nsrc = INFO_NSRC(info->info.opcode); |
| uint inst = info->info.code >> 32; |
| |
| CLIENT_ASSERT(ndst >= 0 || ndst <= 1, "Invalid number of destination operands."); |
| CLIENT_ASSERT(nsrc >= 0 || nsrc <= 4, "Invalid number of source operands."); |
| |
| switch (ndst) { |
| case 2: |
| CLIENT_ASSERT(info->info.dst2_type < RISCV64_FLD_CNT, "Invalid dst2_type."); |
| if (!opnd_encoders[info->info.dst2_type](instr, pc, 1, &inst, di)) |
| goto encode_failure; |
| case 1: |
| CLIENT_ASSERT(info->info.dst1_type < RISCV64_FLD_CNT, "Invalid dst1_type."); |
| if (!opnd_encoders[info->info.dst1_type](instr, pc, 0, &inst, di)) |
| goto encode_failure; |
| case 0: break; |
| default: ASSERT_NOT_REACHED(); |
| } |
| switch (nsrc) { |
| case 4: |
| CLIENT_ASSERT(info->info.dst2_type < RISCV64_FLD_CNT, "Invalid dst2_type."); |
| if (!opnd_encoders[info->info.dst2_type](instr, pc, 3, &inst, di)) |
| goto encode_failure; |
| case 3: |
| CLIENT_ASSERT(info->info.src3_type < RISCV64_FLD_CNT, "Invalid src3_type."); |
| if (!opnd_encoders[info->info.src3_type](instr, pc, 2, &inst, di)) |
| goto encode_failure; |
| case 2: |
| CLIENT_ASSERT(info->info.src2_type < RISCV64_FLD_CNT, "Invalid src2_type."); |
| if (!opnd_encoders[info->info.src2_type](instr, pc, 1, &inst, di)) |
| goto encode_failure; |
| case 1: |
| CLIENT_ASSERT(info->info.src1_type < RISCV64_FLD_CNT, "Invalid src1_type."); |
| if (!opnd_encoders[info->info.src1_type](instr, pc, 0, &inst, di)) |
| goto encode_failure; |
| case 0: break; |
| default: ASSERT_NOT_REACHED(); |
| } |
| |
| if (info->ext == RISCV64_ISA_EXT_RVC) { |
| inst &= 0xFFFF; |
| } |
| return inst; |
| |
| encode_failure: |
| return ENCFAIL; |
| } |