blob: e4f7a663acdf14da86e3df070d72b31050c5ce80 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012-2014 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 _INSTR_INLINE_H_
#define _INSTR_INLINE_H_ 1
/* Use different names to avoid conflicting with an including project */
#define DR_IF_DEBUG IF_DEBUG
#define DR_IF_DEBUG_ IF_DEBUG_
/* DR_API EXPORT TOFILE dr_ir_instr.h */
/* DR_API EXPORT BEGIN */
#ifdef DR_FAST_IR
#ifdef AVOID_API_EXPORT
# define MAKE_OPNDS_VALID(instr) \
(void)(TEST(INSTR_OPERANDS_VALID, (instr)->flags) ? \
(instr) : instr_decode_with_current_dcontext(instr))
#endif
/* CLIENT_ASSERT with a trailing comma in a debug build, otherwise nothing. */
#define CLIENT_ASSERT_(cond, msg) DR_IF_DEBUG_(CLIENT_ASSERT(cond, msg))
#ifdef API_EXPORT_ONLY
/* 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 DR_IF_DEBUG(stmt)
#define DR_IF_DEBUG_(stmt)
#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_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_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;
}
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);
}
#ifdef X64
# 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);
}
#endif /* X64 */
/* 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 = r;
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((r >= DR_REG_MM0 && r <= DR_REG_XMM15) ||
(r >= DR_REG_YMM0 && r <= DR_REG_YMM15),
"opnd_create_reg_partial: non-multimedia register");
#endif
opnd.kind = REG_kind;
opnd.value.reg = r;
opnd.size = subsize;
opnd.aux.flags = 0;
return opnd;
}
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 = 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)
#define opnd_get_reg OPND_GET_REG
#define OPND_GET_FLAGS(opnd) \
(CLIENT_ASSERT_(opnd_is_reg(opnd) || opnd_is_base_disp(opnd), \
"opnd_get_flags called on non-reg non-base-disp opnd") \
(opnd).aux.flags)
#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)
#define OPND_GET_INDEX(opnd) (GET_BASE_DISP(opnd).index_reg)
#ifdef X86
# define OPND_GET_SCALE(opnd) (GET_BASE_DISP(opnd).scale)
#else
# define OPND_GET_SCALE(opnd) \
(CLIENT_ASSERT_(false, "opnd_get_scale not supported on ARM") 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(ARM)
# 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 AVOID_API_EXPORT
/* 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
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;
}
#endif /* DR_FAST_IR */
/* DR_API EXPORT END */
#endif /* _INSTR_INLINE_H_ */