blob: baece8a4adf633ef1cd0248c003af328b0b3fe80 [file]
/* **********************************************************
* 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 "../globals.h"
#include "instr.h"
#include "encode_api.h"
#include "codec.h"
/* XXX i#6690: currently only RISCV64 is supported for instruction encoding.
* We want to add support for RISCV64 decoding and synthetic ISA encoding as well.
* XXX i#1684: move this function to core/ir/instr_shared.c once we can support
* all architectures in the same build of DR.
*/
bool
instr_set_isa_mode(instr_t *instr, dr_isa_mode_t mode)
{
if (mode != DR_ISA_RV64 && mode != DR_ISA_REGDEPS)
return false;
instr->isa_mode = mode;
return true;
}
int
instr_length_arch(dcontext_t *dcontext, instr_t *instr)
{
int opcode = instr_get_opcode(instr);
switch (opcode) {
case OP_LABEL: return 0;
case OP_c_flwsp:
case OP_c_fswsp:
case OP_c_flw:
case OP_c_fsw:
case OP_c_jal:
case OP_c_ldsp:
case OP_c_sdsp:
case OP_c_ld:
case OP_c_sd:
case OP_c_addiw:
case OP_c_addw:
case OP_c_subw:
case OP_c_lwsp:
case OP_c_fldsp:
case OP_c_swsp:
case OP_c_fsdsp:
case OP_c_lw:
case OP_c_fld:
case OP_c_sw:
case OP_c_fsd:
case OP_c_j:
case OP_c_jr:
case OP_c_jalr:
case OP_c_beqz:
case OP_c_bnez:
case OP_c_li:
case OP_c_lui:
case OP_c_addi:
case OP_c_addi16sp:
case OP_c_addi4spn:
case OP_c_slli:
case OP_c_srli:
case OP_c_srai:
case OP_c_andi:
case OP_c_mv:
case OP_c_add:
case OP_c_and:
case OP_c_or:
case OP_c_xor:
case OP_c_sub:
case OP_c_nop:
case OP_c_ebreak: return RISCV64_INSTR_COMPRESSED_SIZE;
default: return RISCV64_INSTR_SIZE;
}
}
bool
opc_is_not_a_real_memory_load(int opc)
{
return opc == OP_auipc;
}
bool
opc_is_not_a_real_memory_store(int opc)
{
return false;
}
uint
instr_branch_type(instr_t *cti_instr)
{
int opcode = instr_get_opcode(cti_instr);
switch (opcode) {
case OP_jal:
case OP_c_jal: /* C.JAL expands to JAL ra, offset, which is direct call. */
case OP_c_j: /* C.J expands to JAL zero, offset, which is a direct jump. */
/* JAL non-zero, offset is a dierct call. */
if (opnd_get_reg(instr_get_dst(cti_instr, 0)) != DR_REG_ZERO)
return LINK_DIRECT | LINK_CALL;
else
return LINK_DIRECT | LINK_JMP;
case OP_jalr:
case OP_c_jr: /* C.JR expands to JALR zero, 0(rs1). */
case OP_c_jalr: /* C.JALR expands to JALR ra, 0(rs1) */
/* JALR zero, 0(ra) is a return. */
if (opnd_get_reg(instr_get_dst(cti_instr, 0)) == DR_REG_ZERO &&
opnd_get_reg(instr_get_src(cti_instr, 0)) == DR_REG_RA &&
opnd_get_immed_int(instr_get_src(cti_instr, 1)) == 0)
return LINK_INDIRECT | LINK_RETURN;
/* JALR non-zero, offset(rs1) is an indirect call. */
else if (opnd_get_reg(instr_get_dst(cti_instr, 0)) != DR_REG_ZERO)
return LINK_INDIRECT | LINK_CALL;
else
return LINK_INDIRECT | LINK_JMP;
case OP_beq:
case OP_bne:
case OP_blt:
case OP_bltu:
case OP_bge:
case OP_bgeu:
case OP_c_beqz:
case OP_c_bnez: return LINK_DIRECT | LINK_JMP;
}
CLIENT_ASSERT(false, "instr_branch_type: unknown opcode");
return LINK_INDIRECT;
}
const char *
get_opcode_name(int opc)
{
return get_instruction_info(opc)->name;
}
bool
instr_is_mov(instr_t *instr)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
instr_is_call_arch(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return ((opc == OP_jal || opc == OP_jalr || opc == OP_c_jal || opc == OP_c_jalr) &&
opnd_get_reg(instr_get_dst(instr, 0)) != DR_REG_ZERO);
}
bool
instr_is_call_direct(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return ((opc == OP_jal || opc == OP_c_jal) &&
opnd_get_reg(instr_get_dst(instr, 0)) != DR_REG_ZERO);
}
bool
instr_is_near_call_direct(instr_t *instr)
{
return instr_is_call_direct(instr);
}
bool
instr_is_call_indirect(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return ((opc == OP_jalr || opc == OP_c_jalr) &&
opnd_get_reg(instr_get_dst(instr, 0)) != DR_REG_ZERO);
}
bool
instr_is_return(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return ((opc == OP_c_jr || opc == OP_jalr) &&
opnd_get_reg(instr_get_dst(instr, 0)) == DR_REG_X0 &&
opnd_get_reg(instr_get_src(instr, 0)) == DR_REG_RA &&
opnd_get_immed_int(instr_get_src(instr, 1)) == 0);
}
bool
instr_is_cbr_arch(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return opc == OP_beq || opc == OP_bne || opc == OP_blt || opc == OP_bltu ||
opc == OP_bge || opc == OP_bgeu || opc == OP_c_beqz || opc == OP_c_bnez;
}
bool
instr_is_mbr_arch(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return (opc == OP_jalr || opc == OP_c_jr || opc == OP_c_jalr);
}
bool
instr_is_far_cti(instr_t *instr)
{
return false;
}
bool
instr_is_ubr_arch(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return ((opc == OP_jal || opc == OP_c_j) &&
opnd_get_reg(instr_get_dst(instr, 0)) == DR_REG_ZERO);
}
bool
instr_is_near_ubr(instr_t *instr)
{
return instr_is_ubr(instr);
}
bool
instr_is_cti_short(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return (opc == OP_c_beqz || opc == OP_c_bnez);
}
bool
instr_is_cti_loop(instr_t *instr)
{
return false;
}
bool
instr_is_cti_short_rewrite(instr_t *instr, byte *pc)
{
return false;
}
bool
instr_is_interrupt(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return (opc == OP_ecall);
}
bool
instr_is_syscall(instr_t *instr)
{
int opc = instr_get_opcode(instr);
return (opc == OP_ecall);
}
bool
instr_is_mov_constant(instr_t *instr, ptr_int_t *value)
{
uint opc = instr_get_opcode(instr);
if (opc == OP_addi || opc == OP_addiw || opc == OP_c_addi || opc == OP_c_addiw ||
opc == OP_c_addi4spn || opc == OP_c_addi16sp || opc == OP_c_li) {
opnd_t op1 = instr_get_src(instr, 0);
opnd_t op2 = instr_get_src(instr, 1);
if (opnd_is_reg(op1) && opnd_get_reg(op1) == DR_REG_X0) {
*value = opnd_get_immed_int(op2);
return true;
}
}
return false;
}
bool
instr_is_prefetch(instr_t *instr)
{
switch (instr_get_opcode(instr)) {
case OP_prefetch_i:
case OP_prefetch_r:
case OP_prefetch_w: return true;
default: return false;
}
}
bool
instr_is_string_op(instr_t *instr)
{
return false;
}
bool
instr_is_rep_string_op(instr_t *instr)
{
return false;
}
bool
instr_saves_float_pc(instr_t *instr)
{
return false;
}
bool
instr_is_undefined(instr_t *instr)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
void
instr_invert_cbr(instr_t *instr)
{
int opc = instr_get_opcode(instr);
CLIENT_ASSERT(instr_is_cbr(instr), "instr_invert_cbr: instr not a cbr");
switch (opc) {
case OP_beq: instr_set_opcode(instr, OP_bne); break;
case OP_bne: instr_set_opcode(instr, OP_beq); break;
case OP_blt: instr_set_opcode(instr, OP_bge); break;
case OP_bltu: instr_set_opcode(instr, OP_bgeu); break;
case OP_bge: instr_set_opcode(instr, OP_blt); break;
case OP_bgeu: instr_set_opcode(instr, OP_bltu); break;
case OP_c_beqz: instr_set_opcode(instr, OP_c_bnez); break;
case OP_c_bnez: instr_set_opcode(instr, OP_c_beqz); break;
default: CLIENT_ASSERT(false, "instr_invert_cbr: unknown opcode");
}
}
bool
instr_cbr_taken(instr_t *instr, priv_mcontext_t *mc, bool pre)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
instr_predicate_reads_srcs(dr_pred_type_t pred)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
instr_predicate_writes_eflags(dr_pred_type_t pred)
{
return false;
}
bool
instr_predicate_is_cond(dr_pred_type_t pred)
{
return false;
}
bool
reg_is_gpr(reg_id_t reg)
{
return (reg >= DR_REG_START_GPR && reg <= DR_REG_STOP_GPR);
}
bool
reg_is_simd(reg_id_t reg)
{
return false;
}
bool
reg_is_vector_simd(reg_id_t reg)
{
return false;
}
bool
reg_is_opmask(reg_id_t reg)
{
return false;
}
bool
reg_is_bnd(reg_id_t reg)
{
return false;
}
bool
reg_is_strictly_zmm(reg_id_t reg)
{
return false;
}
bool
reg_is_ymm(reg_id_t reg)
{
return false;
}
bool
reg_is_strictly_ymm(reg_id_t reg)
{
return false;
}
bool
reg_is_xmm(reg_id_t reg)
{
return false;
}
bool
reg_is_strictly_xmm(reg_id_t reg)
{
return false;
}
bool
reg_is_mmx(reg_id_t reg)
{
return false;
}
bool
instr_is_opmask(instr_t *instr)
{
return false;
}
bool
reg_is_fp(reg_id_t reg)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
instr_is_nop(instr_t *instr)
{
uint opc = instr_get_opcode(instr);
if (opc == OP_c_nop)
return true;
if (instr_num_srcs(instr) < 2 || instr_num_dsts(instr) < 1)
return false;
opnd_t rd = instr_get_dst(instr, 0);
opnd_t rs = instr_get_src(instr, 0);
opnd_t i = instr_get_src(instr, 1);
return (opc == OP_addi && opnd_get_reg(rd) == DR_REG_X0 &&
opnd_get_reg(rs) == DR_REG_X0 && opnd_get_immed_int(i) == 0);
}
bool
opnd_same_sizes_ok(opnd_size_t s1, opnd_size_t s2, bool is_reg)
{
return (s1 == s2);
}
instr_t *
instr_create_nbyte_nop(dcontext_t *dcontext, uint num_bytes, bool raw)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
DR_API
bool
instr_is_exclusive_load(instr_t *instr)
{
switch (instr_get_opcode(instr)) {
case OP_lr_w:
case OP_lr_d: return true;
}
return false;
}
DR_API
bool
instr_is_exclusive_store(instr_t *instr)
{
switch (instr_get_opcode(instr)) {
case OP_sc_w:
case OP_sc_d: return true;
}
return false;
}
DR_API
bool
instr_is_scatter(instr_t *instr)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
DR_API
bool
instr_is_gather(instr_t *instr)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}
bool
instr_compute_vector_address(instr_t *instr, priv_mcontext_t *mc, size_t mc_size,
dr_mcontext_flags_t mc_flags, opnd_t curop, uint addr_index,
DR_PARAM_OUT bool *have_addr, DR_PARAM_OUT app_pc *addr,
DR_PARAM_OUT bool *write)
{
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
return false;
}