blob: f96d47c62494d71098aa9b632b833446ebad431d [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012-2021 Google, 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 Google, 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 GOOGLE, 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.
*/
#ifndef _DR_IR_INSTR_INLINE_H_
#define _DR_IR_INSTR_INLINE_H_ 1
/**************************************************
* INSTRUCTION ROUTINE INLINING SUPPORT
*/
/**
* @file dr_ir_instr_inline.h
* @brief Instruction routine inlining support.
*/
#ifdef DR_FAST_IR /* Around whole file. */
# ifdef DYNAMORIO_INTERNAL
/* Use different names to avoid conflicting with an including project */
# define DR_IF_DEBUG IF_DEBUG
# define DR_IF_DEBUG_ IF_DEBUG_
# define MAKE_OPNDS_VALID(instr) \
(void)(TEST(INSTR_OPERANDS_VALID, (instr)->flags) \
? (instr) \
: instr_decode_with_current_dcontext(instr))
/* CLIENT_ASSERT with a trailing comma in a debug build, otherwise nothing. */
# define CLIENT_ASSERT_(cond, msg) DR_IF_DEBUG_(CLIENT_ASSERT(cond, msg))
# else
# define DR_IF_DEBUG(stmt)
# define DR_IF_DEBUG_(stmt)
/* Internally DR has multiple levels of IR, but once it gets to a client, we
* assume it's already level 3 or higher, and we don't need to do any checks.
* Furthermore, instr_decode() and get_thread_private_dcontext() are not
* exported.
*/
# define MAKE_OPNDS_VALID(instr) ((void)0)
/* Turn off checks if a client includes us with DR_FAST_IR. We can't call the
* internal routines we'd use for these checks anyway.
*/
# define CLIENT_ASSERT(cond, msg)
# define CLIENT_ASSERT_(cond, msg)
# endif
/* Any function that takes or returns an opnd_t by value should be a macro,
* *not* an inline function. Most widely available versions of gcc have trouble
* optimizing structs that have been passed by value, even after inlining.
*/
/* opnd_t predicates */
/* Simple predicates */
# define OPND_IS_NULL(op) ((op).kind == NULL_kind)
# define OPND_IS_IMMED_INT(op) ((op).kind == IMMED_INTEGER_kind)
# define OPND_IS_IMMED_FLOAT(op) ((op).kind == IMMED_FLOAT_kind)
# define OPND_IS_IMMED_DOUBLE(op) ((op).kind == IMMED_DOUBLE_kind)
# define OPND_IS_NEAR_PC(op) ((op).kind == PC_kind)
# define OPND_IS_NEAR_INSTR(op) ((op).kind == INSTR_kind)
# define OPND_IS_REG(op) ((op).kind == REG_kind)
# define OPND_IS_BASE_DISP(op) ((op).kind == BASE_DISP_kind)
# define OPND_IS_FAR_PC(op) ((op).kind == FAR_PC_kind)
# define OPND_IS_FAR_INSTR(op) ((op).kind == FAR_INSTR_kind)
# define OPND_IS_MEM_INSTR(op) ((op).kind == MEM_INSTR_kind)
# define OPND_IS_VALID(op) ((op).kind < LAST_kind)
# define opnd_is_null OPND_IS_NULL
# define opnd_is_immed_int OPND_IS_IMMED_INT
# define opnd_is_immed_float OPND_IS_IMMED_FLOAT
# define opnd_is_immed_double OPND_IS_IMMED_DOUBLE
# define opnd_is_near_pc OPND_IS_NEAR_PC
# define opnd_is_near_instr OPND_IS_NEAR_INSTR
# define opnd_is_reg OPND_IS_REG
# define opnd_is_base_disp OPND_IS_BASE_DISP
# define opnd_is_far_pc OPND_IS_FAR_PC
# define opnd_is_far_instr OPND_IS_FAR_INSTR
# define opnd_is_mem_instr OPND_IS_MEM_INSTR
# define opnd_is_valid OPND_IS_VALID
/* Compound predicates */
INSTR_INLINE
bool
opnd_is_immed(opnd_t op)
{
return op.kind == IMMED_INTEGER_kind || op.kind == IMMED_FLOAT_kind ||
op.kind == IMMED_DOUBLE_kind;
}
INSTR_INLINE
bool
opnd_is_pc(opnd_t op)
{
return op.kind == PC_kind || op.kind == FAR_PC_kind;
}
INSTR_INLINE
bool
opnd_is_instr(opnd_t op)
{
return op.kind == INSTR_kind || op.kind == FAR_INSTR_kind;
}
INSTR_INLINE
bool
opnd_is_near_base_disp(opnd_t op)
{
return op.kind == BASE_DISP_kind IF_X86(&&op.aux.segment == DR_REG_NULL);
}
INSTR_INLINE
bool
opnd_is_far_base_disp(opnd_t op)
{
return IF_X86_ELSE(op.kind == BASE_DISP_kind && op.aux.segment != DR_REG_NULL, false);
}
INSTR_INLINE
bool
opnd_is_element_vector_reg(opnd_t op)
{
return op.kind == REG_kind && ((op.aux.flags & DR_OPND_IS_VECTOR) != 0);
}
INSTR_INLINE
bool
opnd_is_predicate_reg(opnd_t op)
{
/* TODO i#5488: support for x86 AVC512 mask registers */
return IF_AARCH64_ELSE(op.kind == REG_kind &&
op.value.reg_and_element_size.reg >= DR_REG_P0 &&
op.value.reg_and_element_size.reg <= DR_REG_P15,
/* Silence an x86 only warning about an unused `op`.*/
false && op.kind == REG_kind);
}
INSTR_INLINE
bool
opnd_is_predicate_merge(opnd_t op)
{
return opnd_is_predicate_reg(op) &&
((op.aux.flags & DR_OPND_IS_MERGE_PREDICATE) != 0);
}
INSTR_INLINE
bool
opnd_is_predicate_zero(opnd_t op)
{
return opnd_is_predicate_reg(op) && ((op.aux.flags & DR_OPND_IS_ZERO_PREDICATE) != 0);
}
INSTR_INLINE
bool
opnd_is_governing(opnd_t op)
{
return opnd_is_predicate_reg(op) && ((op.aux.flags & DR_OPND_IS_GOVERNING) != 0);
}
# if defined(X64) || defined(ARM)
# ifdef X86
# define OPND_IS_REL_ADDR(op) ((op).kind == REL_ADDR_kind)
# define opnd_is_rel_addr OPND_IS_REL_ADDR
INSTR_INLINE
bool
opnd_is_near_rel_addr(opnd_t opnd)
{
return opnd.kind == REL_ADDR_kind IF_X86(&&opnd.aux.segment == DR_REG_NULL);
}
INSTR_INLINE
bool
opnd_is_far_rel_addr(opnd_t opnd)
{
return IF_X86_ELSE(opnd.kind == REL_ADDR_kind && opnd.aux.segment != DR_REG_NULL,
false);
}
# elif defined(AARCHXX)
# ifdef ARM
# define OPND_IS_REL_ADDR(op) \
((op).kind == REL_ADDR_kind || \
(opnd_is_base_disp(op) && opnd_get_base(op) == DR_REG_PC))
# else
# define OPND_IS_REL_ADDR(op) ((op).kind == REL_ADDR_kind)
# endif
# define opnd_is_rel_addr OPND_IS_REL_ADDR
INSTR_INLINE
bool
opnd_is_near_rel_addr(opnd_t opnd)
{
return opnd_is_rel_addr(opnd);
}
INSTR_INLINE
bool
opnd_is_far_rel_addr(opnd_t opnd)
{
return false;
}
# elif defined(RISCV64)
# define OPND_IS_REL_ADDR(op) ((op).kind == REL_ADDR_kind)
# define opnd_is_rel_addr OPND_IS_REL_ADDR
INSTR_INLINE
bool
opnd_is_near_rel_addr(opnd_t opnd)
{
return opnd_is_rel_addr(opnd);
}
INSTR_INLINE
bool
opnd_is_far_rel_addr(opnd_t opnd)
{
/* FIXME i#3544: Decide if this should differentiate between JAL and AUIPC+JALR. */
return false;
}
# endif /* RISCV64 */
# endif /* X64 || ARM */
/* opnd_t constructors */
/* XXX: How can we macro-ify these? We can use C99 initializers or a copy from
* a constant, but that implies a full initialization, when we could otherwise
* partially intialize. Do we care?
*/
INSTR_INLINE
opnd_t
opnd_create_null(void)
{
opnd_t opnd;
opnd.kind = NULL_kind;
return opnd;
}
INSTR_INLINE
opnd_t
opnd_create_reg(reg_id_t r)
{
opnd_t opnd DR_IF_DEBUG(= { 0 }); /* FIXME: Needed until i#417 is fixed. */
CLIENT_ASSERT(r <= DR_REG_LAST_ENUM && r != DR_REG_INVALID,
"opnd_create_reg: invalid register");
opnd.kind = REG_kind;
opnd.value.reg_and_element_size.reg = r;
opnd.value.reg_and_element_size.element_size = OPSZ_NA;
opnd.size = 0; /* indicates full size of reg */
opnd.aux.flags = 0;
return opnd;
}
INSTR_INLINE
opnd_t
opnd_create_reg_partial(reg_id_t r, opnd_size_t subsize)
{
opnd_t opnd DR_IF_DEBUG(= { 0 }); /* FIXME: Needed until i#417 is fixed. */
# ifdef X86
CLIENT_ASSERT(subsize == 0 || (r >= DR_REG_MM0 && r <= DR_REG_XMM31) ||
(r >= DR_REG_YMM0 && r <= DR_REG_ZMM31),
"opnd_create_reg_partial: non-multimedia register");
# endif
opnd.kind = REG_kind;
opnd.value.reg_and_element_size.reg = r;
opnd.value.reg_and_element_size.element_size = OPSZ_NA;
opnd.size = subsize;
opnd.aux.flags = 0;
return opnd;
}
INSTR_INLINE
opnd_t
opnd_create_reg_element_vector(reg_id_t r, opnd_size_t element_size)
{
opnd_t opnd DR_IF_DEBUG(= { 0 }); /* FIXME: Needed until i#417 is fixed. */
CLIENT_ASSERT(element_size != OPSZ_NA &&
(r <= DR_REG_LAST_ENUM && r != DR_REG_INVALID),
"opnd_create_reg_element_vector: invalid register or no size");
opnd.kind = REG_kind;
opnd.size = 0; /* indicates full size of reg */
opnd.value.reg_and_element_size.reg = r;
opnd.value.reg_and_element_size.element_size = element_size;
opnd.aux.flags = DR_OPND_IS_VECTOR;
return opnd;
}
# ifdef AARCH64
INSTR_INLINE
opnd_t
opnd_create_predicate_reg(reg_id_t r, bool is_merge)
{
opnd_t opnd DR_IF_DEBUG(= { 0 }); /* FIXME: Needed until i#417 is fixed. */
CLIENT_ASSERT(r >= DR_REG_P0 && r <= DR_REG_P15,
"opnd_create_predicate_reg: invalid predicate register");
opnd.kind = REG_kind;
opnd.size = 0; /* indicates full size of reg */
opnd.value.reg_and_element_size.reg = r;
opnd.value.reg_and_element_size.element_size = OPSZ_NA;
opnd.aux.flags =
(ushort)(is_merge ? DR_OPND_IS_MERGE_PREDICATE : DR_OPND_IS_ZERO_PREDICATE);
return opnd;
}
# endif
INSTR_INLINE
opnd_t
opnd_create_reg_ex(reg_id_t r, opnd_size_t subsize, dr_opnd_flags_t flags)
{
opnd_t opnd = opnd_create_reg_partial(r, subsize);
opnd.aux.flags = (ushort)flags;
return opnd;
}
INSTR_INLINE
opnd_t
opnd_create_pc(app_pc pc)
{
opnd_t opnd;
opnd.kind = PC_kind;
opnd.value.pc = pc;
return opnd;
}
/* opnd_t accessors */
# define OPND_GET_REG(opnd) \
(CLIENT_ASSERT_(opnd_is_reg(opnd), "opnd_get_reg called on non-reg opnd")(opnd) \
.value.reg_and_element_size.reg)
# define opnd_get_reg OPND_GET_REG
# if defined(X86)
# define OPND_GET_FLAGS(opnd) \
(CLIENT_ASSERT_( \
opnd_is_reg(opnd) || opnd_is_base_disp(opnd) || opnd_is_immed_int(opnd), \
"opnd_get_flags called on non-reg non-base-disp non-immed-int opnd") 0)
# elif defined(AARCHXX) || defined(RISCV64)
# define OPND_GET_FLAGS(opnd) \
(CLIENT_ASSERT_( \
opnd_is_reg(opnd) || opnd_is_base_disp(opnd) || \
opnd_is_immed_int(opnd), \
"opnd_get_flags called on non-reg non-base-disp non-immed-int opnd")( \
opnd) \
.aux.flags)
# endif
# define opnd_get_flags OPND_GET_FLAGS
# define GET_BASE_DISP(opnd) \
(CLIENT_ASSERT_(opnd_is_base_disp(opnd), \
"opnd_get_base_disp called on invalid opnd type")(opnd) \
.value.base_disp)
# define OPND_GET_BASE(opnd) (GET_BASE_DISP(opnd).base_reg)
# define OPND_GET_DISP(opnd) (GET_BASE_DISP(opnd).disp)
# ifdef X86
# define OPND_GET_INDEX(opnd) \
(GET_BASE_DISP(opnd).index_reg_is_zmm \
? DR_REG_START_ZMM + GET_BASE_DISP(opnd).index_reg \
: GET_BASE_DISP(opnd).index_reg)
# define OPND_GET_SCALE(opnd) (GET_BASE_DISP(opnd).scale)
# else
# define OPND_GET_INDEX(opnd) (GET_BASE_DISP(opnd).index_reg)
# define OPND_GET_SCALE(opnd) 0
# endif
# define opnd_get_base OPND_GET_BASE
# define opnd_get_disp OPND_GET_DISP
# define opnd_get_index OPND_GET_INDEX
# define opnd_get_scale OPND_GET_SCALE
# ifdef X86
# define OPND_GET_SEGMENT(opnd) \
(CLIENT_ASSERT_(opnd_is_base_disp(opnd) IF_X64(|| opnd_is_abs_addr(opnd) || \
opnd_is_rel_addr(opnd)), \
"opnd_get_segment called on invalid opnd type")(opnd) \
.aux.segment)
# elif defined(AARCHXX) || defined(RISCV64)
# define OPND_GET_SEGMENT(opnd) DR_REG_NULL
# endif
# define opnd_get_segment OPND_GET_SEGMENT
/* instr_t accessors */
INSTR_INLINE
bool
instr_is_app(instr_t *instr)
{
CLIENT_ASSERT(instr != NULL, "instr_is_app: passed NULL");
return ((instr->flags & INSTR_DO_NOT_MANGLE) == 0);
}
INSTR_INLINE
bool
instr_ok_to_mangle(instr_t *instr)
{
return instr_is_app(instr);
}
INSTR_INLINE
bool
instr_is_meta(instr_t *instr)
{
CLIENT_ASSERT(instr != NULL, "instr_is_meta: passed NULL");
return ((instr->flags & INSTR_DO_NOT_MANGLE) != 0);
}
# ifdef DYNAMORIO_INTERNAL
/* These are hot internally, but unlikely to be used by clients. */
INSTR_INLINE
bool
instr_operands_valid(instr_t *instr)
{
return TEST(INSTR_OPERANDS_VALID, instr->flags);
}
INSTR_INLINE
bool
instr_raw_bits_valid(instr_t *instr)
{
return TEST(INSTR_RAW_BITS_VALID, instr->flags);
}
INSTR_INLINE
bool
instr_has_allocated_bits(instr_t *instr)
{
return TEST(INSTR_RAW_BITS_ALLOCATED, instr->flags);
}
INSTR_INLINE
bool
instr_needs_encoding(instr_t *instr)
{
return !TEST(INSTR_RAW_BITS_VALID, instr->flags);
}
INSTR_INLINE
bool
instr_ok_to_emit(instr_t *instr)
{
CLIENT_ASSERT(instr != NULL, "instr_ok_to_emit: passed NULL");
return ((instr->flags & INSTR_DO_NOT_EMIT) == 0);
}
# endif
INSTR_INLINE
int
instr_num_srcs(instr_t *instr)
{
MAKE_OPNDS_VALID(instr);
return instr->num_srcs;
}
INSTR_INLINE
int
instr_num_dsts(instr_t *instr)
{
MAKE_OPNDS_VALID(instr);
return instr->num_dsts;
}
/* src0 is static, rest are dynamic. */
/* FIXME: Double evaluation. */
# define INSTR_GET_SRC(instr, pos) \
(MAKE_OPNDS_VALID(instr), \
CLIENT_ASSERT_(pos >= 0 && pos < (instr)->num_srcs, \
"instr_get_src: ordinal invalid")( \
(pos) == 0 ? (instr)->src0 : (instr)->srcs[(pos)-1]))
# define INSTR_GET_DST(instr, pos) \
(MAKE_OPNDS_VALID(instr), \
CLIENT_ASSERT_(pos >= 0 && pos < (instr)->num_dsts, \
"instr_get_dst: ordinal invalid")(instr) \
->dsts[pos])
/* Assumes that if an instr has a jump target, it's stored in the 0th src
* location.
*/
# define INSTR_GET_TARGET(instr) \
(MAKE_OPNDS_VALID(instr), \
CLIENT_ASSERT_(instr_is_cti(instr), "instr_get_target: called on non-cti") \
CLIENT_ASSERT_((instr)->num_srcs >= 1, \
"instr_get_target: instr has no sources")(instr) \
->src0)
# define instr_get_src INSTR_GET_SRC
# define instr_get_dst INSTR_GET_DST
# define instr_get_target INSTR_GET_TARGET
INSTR_INLINE
void
instr_set_note(instr_t *instr, void *value)
{
instr->note = value;
}
INSTR_INLINE
void *
instr_get_note(instr_t *instr)
{
return instr->note;
}
INSTR_INLINE
instr_t *
instr_get_next(instr_t *instr)
{
return instr->next;
}
INSTR_INLINE
instr_t *
instr_get_next_app(instr_t *instr)
{
CLIENT_ASSERT(instr != NULL, "instr_get_next_app: passed NULL");
for (instr = instr->next; instr != NULL; instr = instr->next) {
if (instr_is_app(instr))
return instr;
}
return NULL;
}
INSTR_INLINE
instr_t *
instr_get_prev(instr_t *instr)
{
return instr->prev;
}
INSTR_INLINE
instr_t *
instr_get_prev_app(instr_t *instr)
{
CLIENT_ASSERT(instr != NULL, "instr_get_prev_app: passed NULL");
for (instr = instr->prev; instr != NULL; instr = instr->prev) {
if (instr_is_app(instr))
return instr;
}
return NULL;
}
INSTR_INLINE
void
instr_set_next(instr_t *instr, instr_t *next)
{
instr->next = next;
}
INSTR_INLINE
void
instr_set_prev(instr_t *instr, instr_t *prev)
{
instr->prev = prev;
}
INSTR_INLINE
instr_t *
instr_from_noalloc(instr_noalloc_t *noalloc)
{
return &noalloc->instr;
}
#endif /* DR_FAST_IR */
#endif /* _DR_IR_INSTR_INLINE_H_ */