| /* ********************************************************** |
| * 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_ */ |