| /* ********************************************************** |
| * Copyright (c) 2010-2021 Google, Inc. All rights reserved. |
| * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* Dr. Memory: the memory debugger |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; |
| * version 2.1 of the License, and no later version. |
| |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| /*************************************************************************** |
| * spill.c: Dr. Memory scratch register spilling |
| */ |
| |
| #include "dr_api.h" |
| #include "drreg.h" |
| #include "utils.h" |
| #include "client_per_thread.h" |
| #include "options.h" |
| #include "spill.h" |
| #include "instru.h" |
| #include "slowpath.h" |
| #include "replace.h" |
| #include "shadow.h" |
| #include "fastpath.h" |
| |
| #include <stddef.h> /* offsetof */ |
| |
| /*************************************************************************** |
| * We allocate our own register spill slots for faster access than |
| * the non-directly-addressable DR slots (only 3 are direct). |
| */ |
| |
| /* Indirection to allow us to switch which TLS slots we use for spill slots */ |
| #define MAX_FAST_DR_SPILL_SLOT SPILL_SLOT_3 |
| |
| /* We used to store segment bases in some TLS slots that were followed by the |
| * reg spill slots, but now that DR has API support for bases we don't need them |
| * anymore: |
| */ |
| #define NUM_INSTRU_TLS_SLOTS 0 |
| |
| /* drreg allocates our reg spill slots in pattern mode */ |
| #define NUM_TLS_SLOTS \ |
| (NUM_INSTRU_TLS_SLOTS + (options.pattern == 0 ? options.num_spill_slots : 0)) |
| |
| reg_id_t seg_tls; |
| |
| /* the offset of our tls_instr_t + reg spill tls slots */ |
| static uint tls_instru_base; |
| |
| /* we store a pointer in regular tls for access to other threads' TLS */ |
| static int tls_idx_instru = -1; |
| |
| byte * |
| get_own_seg_base(void) |
| { |
| #ifdef WINDOWS |
| return (byte *) get_TEB(); |
| #else |
| return dr_get_dr_segment_base(seg_tls); |
| #endif |
| } |
| |
| static bool |
| handle_drreg_error(drreg_status_t status) |
| { |
| ASSERT(false, "drreg bb event failure"); |
| /* XXX: can we share the tool crash message? */ |
| NOTIFY_ERROR("FATAL ERROR: internal register failure %d: please report this", status); |
| dr_abort(); |
| return true; /* we handled it */ |
| } |
| |
| void |
| instru_tls_init(void) |
| { |
| if (options.pattern != 0) { |
| drreg_options_t ops = { |
| sizeof(ops), options.num_spill_slots, options.conservative, |
| handle_drreg_error, |
| }; |
| IF_DEBUG(drreg_status_t res =) |
| drreg_init(&ops); |
| ASSERT(res == DRREG_SUCCESS, "fatal error: failed to initialize drreg"); |
| } |
| if (NUM_TLS_SLOTS > 0) { |
| IF_DEBUG(bool ok =) |
| dr_raw_tls_calloc(&seg_tls, &tls_instru_base, NUM_TLS_SLOTS, 0); |
| ASSERT(ok, "fatal error: unable to reserve tls slots"); |
| LOG(2, "TLS spill base: "PIFX"\n", tls_instru_base); |
| tls_idx_instru = drmgr_register_tls_field(); |
| ASSERT(tls_idx_instru > -1, "failed to reserve TLS slot"); |
| ASSERT(seg_tls == EXPECTED_SEG_TLS, "unexpected tls segment"); |
| } else { |
| /* We still need the seg base. |
| * XXX: DR should provide an API to retrieve it w/o allocating slots! |
| */ |
| if (dr_raw_tls_calloc(&seg_tls, &tls_instru_base, 1, 0)) { |
| dr_raw_tls_cfree(tls_instru_base, 1); |
| } else { |
| /* Fall back to hardcoded defaults */ |
| #ifdef X86 |
| # ifdef X64 |
| seg_tls = DR_SEG_GS; |
| # else |
| seg_tls = DR_SEG_FS; |
| # endif |
| #elif defined(ARM) |
| # ifdef X64 |
| seg_tls = DR_REG_TPIDRURO; |
| # else |
| seg_tls = DR_REG_TPIDRURW; |
| # endif |
| #endif |
| } |
| } |
| } |
| |
| void |
| instru_tls_exit(void) |
| { |
| if (NUM_TLS_SLOTS > 0) { |
| IF_DEBUG(bool ok =) |
| dr_raw_tls_cfree(tls_instru_base, NUM_TLS_SLOTS); |
| ASSERT(ok, "WARNING: unable to free tls slots"); |
| drmgr_unregister_tls_field(tls_idx_instru); |
| } |
| if (options.pattern != 0) { |
| IF_DEBUG(drreg_status_t res =) |
| drreg_exit(); |
| ASSERT(res == DRREG_SUCCESS, "WARNING: drreg failed on exit"); |
| } |
| } |
| |
| void |
| instru_tls_thread_init(void *drcontext) |
| { |
| #ifdef UNIX |
| /* We used to acquire the app's fs and gs bases (via opnd_compute address on |
| * a synthetic far-base-disp opnd) but with early injection they aren't set |
| * up yet, and we'd need to do work to handle changes: instead, now that |
| * DR supplies dr_insert_get_seg_base(), we just use that. |
| */ |
| LOG(1, "dr: TLS base="PFX"\n", dr_get_dr_segment_base(seg_tls)); |
| #endif |
| if (NUM_TLS_SLOTS > 0) { |
| /* store in per-thread data struct so we can access from another thread */ |
| drmgr_set_tls_field(drcontext, tls_idx_instru, (void *) |
| (get_own_seg_base() + tls_instru_base)); |
| } |
| } |
| |
| void |
| instru_tls_thread_exit(void *drcontext) |
| { |
| if (NUM_TLS_SLOTS > 0) |
| drmgr_set_tls_field(drcontext, tls_idx_instru, NULL); |
| } |
| |
| uint |
| num_own_spill_slots(void) |
| { |
| return options.num_spill_slots; |
| } |
| |
| static opnd_t |
| opnd_create_own_spill_slot(uint index) |
| { |
| ASSERT(index < options.num_spill_slots, "spill slot index overflow"); |
| ASSERT(INSTRUMENT_MEMREFS(), "incorrectly called"); |
| return opnd_create_far_base_disp_ex |
| /* must use 0 scale to match what DR decodes for opnd_same */ |
| (seg_tls, REG_NULL, REG_NULL, 0, |
| tls_instru_base + (NUM_INSTRU_TLS_SLOTS + index)*sizeof(ptr_uint_t), OPSZ_PTR, |
| /* we do NOT want an addr16 prefix since most likely going to run on |
| * Core or Core2, and P4 doesn't care that much */ |
| false, true, false); |
| } |
| |
| ptr_uint_t |
| get_own_tls_value(uint index) |
| { |
| ASSERT(NUM_TLS_SLOTS > 0, "should not get here if we have no slots"); |
| if (index < options.num_spill_slots) { |
| byte *seg_base = get_own_seg_base(); |
| return *(ptr_uint_t *) (seg_base + tls_instru_base + |
| (NUM_INSTRU_TLS_SLOTS + index)*sizeof(ptr_uint_t)); |
| } else { |
| dr_spill_slot_t DR_slot = index - options.num_spill_slots; |
| return dr_read_saved_reg(dr_get_current_drcontext(), DR_slot); |
| } |
| } |
| |
| void |
| set_own_tls_value(uint index, ptr_uint_t val) |
| { |
| ASSERT(NUM_TLS_SLOTS > 0, "should not get here if we have no slots"); |
| if (index < options.num_spill_slots) { |
| byte *seg_base = get_own_seg_base(); |
| *(ptr_uint_t *)(seg_base + tls_instru_base + |
| (NUM_INSTRU_TLS_SLOTS + index)*sizeof(ptr_uint_t)) = val; |
| } else { |
| dr_spill_slot_t DR_slot = index - options.num_spill_slots; |
| dr_write_saved_reg(dr_get_current_drcontext(), DR_slot, val); |
| } |
| } |
| |
| ptr_uint_t |
| get_thread_tls_value(void *drcontext, uint index) |
| { |
| ASSERT(NUM_TLS_SLOTS > 0, "should not get here if we have no slots"); |
| if (index < options.num_spill_slots) { |
| byte *tls = (byte *) drmgr_get_tls_field(drcontext, tls_idx_instru); |
| return *(ptr_uint_t *) |
| (tls + (NUM_INSTRU_TLS_SLOTS + index)*sizeof(ptr_uint_t)); |
| } else { |
| dr_spill_slot_t DR_slot = index - options.num_spill_slots; |
| return dr_read_saved_reg(drcontext, DR_slot); |
| } |
| } |
| |
| void |
| set_thread_tls_value(void *drcontext, uint index, ptr_uint_t val) |
| { |
| ASSERT(NUM_TLS_SLOTS > 0, "should not get here if we have no slots"); |
| if (index < options.num_spill_slots) { |
| byte *tls = (byte *) drmgr_get_tls_field(drcontext, tls_idx_instru); |
| *(ptr_uint_t *) |
| (tls + (NUM_INSTRU_TLS_SLOTS + index)*sizeof(ptr_uint_t)) = val; |
| } else { |
| dr_spill_slot_t DR_slot = index - options.num_spill_slots; |
| dr_write_saved_reg(drcontext, DR_slot, val); |
| } |
| } |
| |
| ptr_uint_t |
| get_raw_tls_value(uint offset) |
| { |
| #ifdef X86 |
| ptr_uint_t val; |
| # ifdef WINDOWS |
| val = *(ptr_uint_t *)(((byte *)get_TEB()) + offset); |
| # else |
| # ifdef X64 |
| asm("movzbq %0, %%"ASM_XAX : : "m"(offset) : ASM_XAX); |
| # else |
| asm("movzbl %0, %%"ASM_XAX : : "m"(offset) : ASM_XAX); |
| # endif |
| asm("mov %%"ASM_SEG":(%%"ASM_XAX"), %%"ASM_XAX : : : ASM_XAX); |
| asm("mov %%"ASM_XAX", %0" : "=m"(val) : : ASM_XAX); |
| # endif |
| return val; |
| #else |
| /* FIXME i#1726: port to ARM */ |
| ASSERT_NOT_REACHED(); |
| return 0; |
| #endif |
| } |
| |
| int |
| spill_reg3_slot(bool eflags_dead, bool eax_dead, bool r1_dead, bool r2_dead) |
| { |
| if (whole_bb_spills_enabled()) |
| return SPILL_SLOT_4; |
| if (eflags_dead || eax_dead) |
| return SPILL_SLOT_EFLAGS_EAX; |
| /* we can only use slots 1 and 2 if no spill or xchg, since for xchg |
| * we restore and then use the tls slot for slowpath param, so truly |
| * if r1/r2 is dead. even then we must restore reg3 first in slowpath prefix |
| * before we move param into tls slot. |
| */ |
| if (r1_dead) |
| return SPILL_SLOT_1; |
| if (r2_dead) |
| return SPILL_SLOT_2; |
| return SPILL_SLOT_4; |
| } |
| |
| void |
| spill_reg(void *drcontext, instrlist_t *ilist, instr_t *where, reg_id_t reg, |
| dr_spill_slot_t slot) |
| { |
| ASSERT(options.pattern == 0, "not converted to drreg yet"); |
| if (slot < options.num_spill_slots) { |
| STATS_INC(reg_spill_own); |
| PRE(ilist, where, |
| XINST_CREATE_store(drcontext, opnd_create_own_spill_slot(slot), |
| opnd_create_reg(reg))); |
| } else { |
| dr_spill_slot_t DR_slot = slot - options.num_spill_slots; |
| #ifdef STATISTICS |
| if (DR_slot > MAX_FAST_DR_SPILL_SLOT) |
| STATS_INC(reg_spill_slow); |
| #endif |
| dr_save_reg(drcontext, ilist, where, reg, DR_slot); |
| } |
| } |
| |
| void |
| restore_reg(void *drcontext, instrlist_t *ilist, instr_t *where, reg_id_t reg, |
| dr_spill_slot_t slot) |
| { |
| ASSERT(options.pattern == 0, "not converted to drreg yet"); |
| if (slot < options.num_spill_slots) { |
| PRE(ilist, where, |
| XINST_CREATE_load(drcontext, opnd_create_reg(reg), |
| opnd_create_own_spill_slot(slot))); |
| } else { |
| dr_spill_slot_t DR_slot = slot - options.num_spill_slots; |
| dr_restore_reg(drcontext, ilist, where, reg, DR_slot); |
| } |
| } |
| |
| opnd_t |
| spill_slot_opnd(void *drcontext, dr_spill_slot_t slot) |
| { |
| ASSERT(options.pattern == 0, "not converted to drreg yet"); |
| if (slot < options.num_spill_slots) { |
| return opnd_create_own_spill_slot(slot); |
| } else { |
| dr_spill_slot_t DR_slot = slot - options.num_spill_slots; |
| return dr_reg_spill_slot_opnd(drcontext, DR_slot); |
| } |
| } |
| |
| static bool |
| is_spill_slot_opnd(void *drcontext, opnd_t op) |
| { |
| static uint offs_min_own, offs_max_own, offs_min_DR, offs_max_DR; |
| if (offs_max_DR == 0) { |
| offs_min_own = opnd_get_disp(opnd_create_own_spill_slot(0)); |
| offs_max_own = opnd_get_disp(opnd_create_own_spill_slot |
| (options.num_spill_slots - 1)); |
| offs_min_DR = opnd_get_disp(dr_reg_spill_slot_opnd(drcontext, SPILL_SLOT_1)); |
| offs_max_DR = opnd_get_disp(dr_reg_spill_slot_opnd |
| (drcontext, dr_max_opnd_accessible_spill_slot())); |
| } |
| if (opnd_is_far_base_disp(op) && |
| opnd_get_index(op) == REG_NULL && |
| opnd_get_segment(op) == seg_tls) { |
| uint offs = opnd_get_disp(op); |
| if (offs >= offs_min_own && offs <= offs_max_own) |
| return true; |
| if (offs >= offs_min_DR && offs <= offs_max_DR) |
| return true; |
| } |
| return false; |
| } |
| |
| /*************************************************************************** |
| * STATE RESTORATION |
| */ |
| |
| /* Our state restoration model for non-pattern, until we transition |
| * them to use drreg as well (i#1795): only whole-bb scratch regs and aflags |
| * need to be restored (i.e., all local scratch regs are restored |
| * before any app instr). For each such reg or aflags, we guarantee |
| * that either the app value is in TLS at each app instr (where fault |
| * might happen) or the app value is dead and it's ok to have garbage |
| * in TLS b/c the app will write it before reading (this is all modulo |
| * the app's own fault handler going off on a different path (xref |
| * DRi#400): so we're slightly risky here). |
| * |
| * We also perform a few other shadow-related restorations at the end |
| * of this routine. |
| */ |
| bool |
| event_restore_state(void *drcontext, bool restore_memory, dr_restore_state_info_t *info) |
| { |
| bool shadow_write = false; |
| instr_t inst; |
| uint memopidx; |
| bool write; |
| byte *addr; |
| /* If always app_code_consistent could just re-analyze aflags, but not |
| * the case so we have to store whether we've done eflags-at-top |
| */ |
| #ifdef X86 |
| /* XXX PR 485216: we should restore dead registers on a fault, but it |
| * would be a perf hit to save them: for now we don't do anything. |
| * It's doubtful an app will ever have a problem with that. |
| */ |
| bb_saved_info_t *save; |
| #endif |
| |
| #ifdef TOOL_DR_MEMORY |
| cls_drmem_t *cpt = (cls_drmem_t *) drmgr_get_cls_field(drcontext, cls_idx_drmem); |
| ASSERT(cpt != NULL, "cpt shouldn't be null"); |
| |
| if (!INSTRUMENT_MEMREFS()) |
| return true; |
| |
| if (options.pattern != 0) /* pattern is using drreg */ |
| return true; |
| |
| /* Are we asking DR to translate just pc? Then return true and ignore regs */ |
| if (cpt->self_translating) { |
| ASSERT(options.verbose >= 3, "only used for -verbose 3+"); |
| return true; |
| } |
| #endif |
| |
| #ifdef X86 /* ARM uses drreg's state restoration */ |
| hashtable_lock(&bb_table); |
| save = (bb_saved_info_t *) hashtable_lookup(&bb_table, info->fragment_info.tag); |
| # ifdef TOOL_DR_MEMORY |
| LOG(2, "%s: raw pc="PFX", xl8 pc="PFX", tag="PFX"\n", |
| __FUNCTION__, info->raw_mcontext->pc, info->mcontext->pc, |
| info->fragment_info.tag); |
| DOLOG(2, { |
| /* We leave the translation as being in our own library, since no |
| * other good alternative. We document this to users. |
| */ |
| if (in_replace_routine(info->mcontext->pc)) |
| LOG(2, "fault in replace_ routine "PFX"\n", info->mcontext->pc); |
| }); |
| # endif |
| if (save != NULL) { |
| /* We save first thing and restore prior to last instr. |
| * Our restore clobbers the eflags value in our tls slot, so |
| * on a fault in the last instr we should do nothing. |
| * FIXME: NOT ENOUGH TESTED: |
| * need carefully constructed tests like tests/state.c |
| */ |
| if (info->mcontext->pc != save->last_instr && |
| /* i#1466: only restore after first_restore_pc */ |
| info->mcontext->pc >= save->first_restore_pc) { |
| /* Use drcontext's shadow, not executing thread's shadow! (PR 475211) */ |
| ptr_uint_t regval; |
| if (save->eflags_saved) { |
| IF_DEBUG(ptr_uint_t orig_flags = info->mcontext->xflags;) |
| uint sahf; |
| regval = save->aflags_in_eax ? info->raw_mcontext->xax : |
| get_thread_tls_value(drcontext, SPILL_SLOT_EFLAGS_EAX); |
| sahf = (regval & 0xff00) >> 8; |
| info->mcontext->xflags &= ~0xff; |
| info->mcontext->xflags |= sahf; |
| if (TEST(1, regval)) /* from "seto al" */ |
| info->mcontext->xflags |= EFLAGS_OF; |
| LOG(2, "translated eflags from "PFX" to "PFX"\n", |
| orig_flags, info->mcontext->xflags); |
| } |
| /* Restore whole-bb spilled registers (PR 489221). Note that |
| * now we're closer to being able to restore app state at any |
| * point: but we still don't have any local 3rd scratch reg |
| * recorded. |
| */ |
| if (save->scratch1 != REG_NULL) { |
| regval = get_thread_tls_value(drcontext, SPILL_SLOT_1); |
| LOG(2, "restoring per-bb %s to "PFX"\n", |
| get_register_name(save->scratch1), regval); |
| reg_set_value(save->scratch1, info->mcontext, regval); |
| } |
| if (save->scratch2 != REG_NULL) { |
| regval = get_thread_tls_value(drcontext, SPILL_SLOT_2); |
| LOG(2, "restoring per-bb %s to "PFX"\n", |
| get_register_name(save->scratch2), regval); |
| reg_set_value(save->scratch2, info->mcontext, regval); |
| } |
| if (save->aflags_in_eax) { |
| regval = get_thread_tls_value(drcontext, SPILL_SLOT_5); |
| LOG(2, "restoring %s to "PFX"\n", |
| get_register_name(DR_REG_XAX), regval); |
| reg_set_value(DR_REG_XAX, info->mcontext, regval); |
| } |
| } |
| } |
| hashtable_unlock(&bb_table); |
| #endif /* X86 */ |
| |
| #ifndef TOOL_DR_MEMORY |
| /* Dr. Heapstat has no fault paths */ |
| return true; |
| #endif |
| |
| /* Note that although we now have non-meta instrumentation that |
| * comes through here for PR 448701 we do not restore registers |
| * here since no such fault will make it to the app. |
| * PR 480208: we do need to restore for a thread relocation. For now we |
| * return false to force thread suspension elsewhere (b/c we can't relocate |
| * for the 1st of our two-part sub-dword dst write), if the translation is |
| * targeting our own meta-may-fault instr, which will always be a write to |
| * shadow memory == DR memory. |
| */ |
| if (!info->raw_mcontext_valid) { |
| ASSERT(false, "should always have raw_mcontext"); |
| return true; |
| } |
| instr_init(drcontext, &inst); |
| decode(drcontext, info->raw_mcontext->pc, &inst); |
| ASSERT(instr_valid(&inst), "unknown translation instr"); |
| DOLOG(3, { |
| LOG(3, "considering to-be-translated instr: "); |
| instr_disassemble(drcontext, &inst, LOGFILE_GET(drcontext)); |
| LOG(3, "\n"); |
| }); |
| for (memopidx = 0; |
| instr_compute_address_ex(&inst, info->raw_mcontext, memopidx, &addr, &write); |
| memopidx++) { |
| if (write && dr_memory_is_dr_internal(addr)) { |
| shadow_write = true; |
| break; |
| } |
| } |
| instr_free(drcontext, &inst); |
| if (shadow_write) { |
| /* An app write to a random address could fool us, but shouldn't be |
| * a problem: DR will just suspend somewhere else. |
| */ |
| if (!restore_memory) { |
| /* We could move the state restoration code from |
| * handle_special_shadow_fault() to here and return true, except |
| * we'd also have to change our two-part sub-dword dst write to get |
| * the final value in a reg before committing. So, we return false, |
| * and only restore the state when we need to determine the app's |
| * mem address for a real fault. |
| */ |
| LOG(1, "failing non-fault translation\n"); |
| return false; |
| } |
| /* else, it must be our own special-shadow fault, handled by our |
| * signal/exception events below |
| */ |
| } |
| #ifdef TOOL_DR_MEMORY |
| else if (restore_memory && cpt->mem2fpmm_source != NULL) { |
| /* Our i#471 heuristic could end up with a thread relocation in between |
| * the fld and the fstp. In such a case we restore the shadow. |
| */ |
| umbra_shadow_memory_info_t info; |
| shadow_set_byte(&info, cpt->mem2fpmm_dest, cpt->mem2fpmm_prev_shadow); |
| cpt->mem2fpmm_source = NULL; |
| } |
| #endif |
| |
| return true; |
| } |
| |
| /*************************************************************************** |
| * SCRATCH REGISTER SELECTION |
| */ |
| |
| #ifdef X86 /* ARM uses drreg's state restoration */ |
| |
| # ifdef DEBUG |
| static void |
| print_scratch_reg(void *drcontext, scratch_reg_info_t *si, int num, file_t file) |
| { |
| dr_fprintf(file, "r%d=", num); |
| opnd_disassemble(drcontext, opnd_create_reg(si->reg), file); |
| if (si->xchg != REG_NULL) { |
| dr_fprintf(file, " xchg "); |
| opnd_disassemble(drcontext, opnd_create_reg(si->xchg), file); |
| } else if (si->dead) { |
| dr_fprintf(file, " dead"); |
| } else { |
| dr_fprintf(file, "spill#%d", si->slot); |
| } |
| } |
| |
| static void |
| check_scratch_reg_no_overlap(scratch_reg_info_t *s1, scratch_reg_info_t *s2) |
| { |
| ASSERT(s1->reg != s2->reg, "scratch reg error"); |
| ASSERT(s1->xchg != s2->reg, "scratch reg error"); |
| ASSERT(s2->xchg != s1->reg, "scratch reg error"); |
| /* if both are using slots (no xchg, not dead) then make sure they differ */ |
| ASSERT(s1->xchg != REG_NULL || s2->xchg != REG_NULL || |
| s1->dead || s2->dead || |
| s1->slot != s2->slot, "scratch reg error"); |
| } |
| # endif /* DEBUG */ |
| |
| /* It's up to the caller to ensure that fixed was not already chosen |
| * for an earlier scratch reg |
| */ |
| static void |
| pick_scratch_reg_helper(fastpath_info_t *mi, scratch_reg_info_t *si, |
| int live[NUM_LIVENESS_REGS], |
| bool only_abcd, reg_id_t fixed, int slot, |
| opnd_t no_overlap1, opnd_t no_overlap2) |
| { |
| int nxt_dead; |
| for (nxt_dead = 0; nxt_dead < NUM_LIVENESS_REGS; nxt_dead++) { |
| if (live[nxt_dead] == LIVE_DEAD) |
| break; |
| } |
| si->global = false; |
| /* FIXME PR 463053: we're technically being a little unsafe here: |
| * even if a register is dead on the normal path, if a fault |
| * can occur before the register is written to, we should |
| * restore its value in the fault handler. Pretty pathological |
| * so for now we ignore it, given that we have larger problems |
| * and we need the speed. |
| */ |
| if (nxt_dead < NUM_LIVENESS_REGS && |
| (!only_abcd || nxt_dead <= DR_REG_XBX - REG_START) && |
| !opnd_uses_reg(no_overlap1, REG_START + nxt_dead) && |
| !opnd_uses_reg(no_overlap2, REG_START + nxt_dead) && |
| /* do not pick local reg that overlaps w/ whole-bb reg */ |
| REG_START + nxt_dead != mi->bb->reg1.reg && |
| REG_START + nxt_dead != mi->bb->reg2.reg) { |
| /* we can use it directly */ |
| si->reg = REG_START + nxt_dead; |
| si->xchg = REG_NULL; |
| si->dead = true; |
| STATS_INC(reg_dead); |
| live[nxt_dead] = LIVE_LIVE; |
| } else if (nxt_dead < NUM_LIVENESS_REGS && |
| /* FIXME OPT: if we split the spills up we could use xchg for |
| * reg2 or reg3 even if not for reg1 |
| */ |
| !opnd_uses_reg(no_overlap1, REG_START + nxt_dead) && |
| !opnd_uses_reg(no_overlap2, REG_START + nxt_dead) && |
| !opnd_uses_reg(no_overlap1, fixed) && |
| !opnd_uses_reg(no_overlap2, fixed) && |
| /* do not pick local reg that overlaps w/ whole-bb reg */ |
| REG_START + nxt_dead != mi->bb->reg1.reg && |
| REG_START + nxt_dead != mi->bb->reg2.reg) { |
| /* pick fixed reg and xchg for it */ |
| si->reg = fixed; |
| si->xchg = REG_START + nxt_dead; |
| si->dead = false; |
| STATS_INC(reg_xchg); |
| live[nxt_dead] = LIVE_LIVE; |
| } else { |
| /* pick fixed reg and spill it */ |
| si->reg = fixed; |
| si->xchg = REG_NULL; |
| si->dead = false; |
| si->slot = slot; |
| STATS_INC(reg_spill); |
| } |
| } |
| |
| /* Chooses scratch registers and sets liveness and eflags info |
| * If mi->eax.used is set, does NOT determine how to spill eax based |
| * on eflags and does NOT use the SPILL_SLOT_EFLAGS_EAX unless eflags |
| * is dead. |
| */ |
| void |
| pick_scratch_regs(instr_t *inst, fastpath_info_t *mi, bool only_abcd, bool need3, |
| bool reg3_must_be_ecx, opnd_t no_overlap1, opnd_t no_overlap2) |
| { |
| int nxt_dead; |
| int live[NUM_LIVENESS_REGS]; |
| /* Slots for additional local regs, when using whole-bb slots 1 and 2 |
| * for regs and slot 3 (SPILL_SLOT_EFLAGS_EAX) for eflags |
| */ |
| int local_slot[] = {SPILL_SLOT_4, SPILL_SLOT_5}; |
| int local_idx = 0; |
| IF_DEBUG(const int local_idx_max = sizeof(local_slot)/sizeof(local_slot[0]);) |
| mi->aflags = get_aflags_and_reg_liveness(inst, live, false/* regs too */); |
| |
| ASSERT((whole_bb_spills_enabled() && mi->bb->reg1.reg != REG_NULL) || |
| (!whole_bb_spills_enabled() && mi->bb->reg1.reg == REG_NULL), |
| "whole_bb_spills_enabled() should correspond to reg1,reg2 being set"); |
| |
| /* don't pick esp since it can't be an index register */ |
| live[DR_REG_XSP - REG_START] = LIVE_LIVE; |
| |
| if (mi->eax.used) { |
| /* caller wants us to ignore eax and eflags */ |
| } else if (!whole_bb_spills_enabled() && mi->aflags != EFLAGS_WRITE_ARITH) { |
| mi->eax.reg = DR_REG_XAX; |
| mi->eax.used = true; |
| mi->eax.dead = (live[DR_REG_XAX - REG_START] == LIVE_DEAD); |
| /* Ensure we don't use eax for another scratch reg */ |
| if (mi->eax.dead) { |
| live[DR_REG_XAX - REG_START] = LIVE_LIVE; |
| STATS_INC(reg_dead); |
| } else |
| STATS_INC(reg_spill); |
| /* we do not try to xchg instead of spilling as that would complicate |
| * shared_slowpath, spill_reg3_slot, etc. |
| */ |
| mi->eax.xchg = REG_NULL; |
| mi->eax.slot = SPILL_SLOT_EFLAGS_EAX; |
| mi->eax.global = false; |
| } else |
| mi->eax.used = false; |
| |
| /* Up to 3 scratch regs (beyond eax for eflags) need to be from |
| * ecx, edx, ebx so we can reference low and high 8-bit sub-regs |
| */ |
| mi->reg1.used = false; /* set later */ |
| mi->reg2.used = false; /* set later */ |
| mi->reg3.used = false; /* set later */ |
| |
| if (need3) { |
| if (reg3_must_be_ecx && mi->bb->reg1.reg == DR_REG_XCX) { |
| mi->reg3 = mi->bb->reg1; |
| mi->reg3.dead = (live[mi->reg3.reg - REG_START] == LIVE_DEAD); |
| } else if (reg3_must_be_ecx && mi->bb->reg2.reg == DR_REG_XCX) { |
| mi->reg3 = mi->bb->reg2; |
| mi->reg3.dead = (live[mi->reg3.reg - REG_START] == LIVE_DEAD); |
| } else if (reg3_must_be_ecx && only_abcd) { |
| /* instrument_fastpath requires reg3 to be ecx since we have |
| * to use cl for OP_shl var op. |
| */ |
| mi->reg3.reg = DR_REG_XCX; |
| mi->reg3.dead = (live[mi->reg3.reg - REG_START] == LIVE_DEAD); |
| mi->reg3.global = false; |
| /* Ensure we don't use for another scratch reg */ |
| live[mi->reg3.reg - REG_START] = LIVE_LIVE; |
| if (!mi->reg3.dead) { |
| for (nxt_dead = 0; nxt_dead < NUM_LIVENESS_REGS; nxt_dead++) { |
| if (live[nxt_dead] == LIVE_DEAD) |
| break; |
| } |
| if (nxt_dead < NUM_LIVENESS_REGS && |
| !opnd_uses_reg(no_overlap1, REG_START + nxt_dead) && |
| !opnd_uses_reg(no_overlap2, REG_START + nxt_dead) && |
| !opnd_uses_reg(no_overlap1, mi->reg3.reg) && |
| !opnd_uses_reg(no_overlap2, mi->reg3.reg) && |
| /* we can't xchg with what we'll use for reg1 or reg2 */ |
| REG_START + nxt_dead != DR_REG_XDX && |
| REG_START + nxt_dead != DR_REG_XBX && |
| /* do not pick local reg that overlaps w/ whole-bb reg */ |
| REG_START + nxt_dead != mi->bb->reg1.reg && |
| REG_START + nxt_dead != mi->bb->reg2.reg) { |
| mi->reg3.xchg = REG_START + nxt_dead; |
| live[nxt_dead] = LIVE_LIVE; |
| STATS_INC(reg_xchg); |
| } else { |
| mi->reg3.slot = spill_reg3_slot(mi->aflags == EFLAGS_WRITE_ARITH, |
| !mi->eax.used || mi->eax.dead, |
| /* later we'll update these */ |
| false, false); |
| STATS_INC(reg_spill); |
| } |
| } else |
| STATS_INC(reg_dead); |
| } else { |
| pick_scratch_reg_helper(mi, &mi->reg3, live, only_abcd, |
| (mi->bb->reg1.reg == DR_REG_XCX ? |
| ((mi->bb->reg2.reg == DR_REG_XDX) ? |
| DR_REG_XBX : DR_REG_XDX) : |
| ((mi->bb->reg2.reg == DR_REG_XCX) ? |
| ((mi->bb->reg1.reg == DR_REG_XDX) ? |
| DR_REG_XBX : DR_REG_XDX) |
| : DR_REG_XCX)), |
| spill_reg3_slot(mi->aflags == EFLAGS_WRITE_6, |
| !mi->eax.used || mi->eax.dead, |
| /* later we'll update these */ |
| false, false), |
| no_overlap1, no_overlap2); |
| } |
| if (mi->bb->reg1.reg != REG_NULL && !mi->reg3.dead && mi->reg3.xchg == REG_NULL) { |
| if (!mi->reg3.global) { |
| /* spill_reg3_slot() should return SPILL_SLOT_4 for us */ |
| ASSERT(mi->reg3.slot == local_slot[local_idx], "reg3: wrong slot!"); |
| local_idx++; |
| } else |
| ASSERT(mi->reg3.slot < SPILL_SLOT_EFLAGS_EAX, "reg3 global: 0 or 1!"); |
| } |
| } else |
| mi->reg3.reg = REG_NULL; |
| |
| if (mi->bb->reg1.reg != REG_NULL && |
| (!need3 || !reg3_must_be_ecx || mi->bb->reg1.reg != DR_REG_XCX) && |
| /* we only need to check for overlap for xchg (since messes up |
| * app values) so we ignore no_overlap* |
| */ |
| (!mi->eax.used || mi->bb->reg1.reg != DR_REG_XAX)) { |
| /* Use whole-bb spilled reg (PR 489221) */ |
| mi->reg1 = mi->bb->reg1; |
| mi->reg1.dead = (live[mi->reg1.reg - REG_START] == LIVE_DEAD); |
| } else { |
| /* Pick primary scratch reg */ |
| ASSERT(local_idx < local_idx_max, "local slot overflow"); |
| pick_scratch_reg_helper(mi, &mi->reg1, live, only_abcd, |
| (need3 && mi->reg3.reg == DR_REG_XDX) ? |
| DR_REG_XCX : |
| ((mi->bb->reg2.reg == DR_REG_XDX) ? |
| DR_REG_XBX : DR_REG_XDX), |
| /* if whole-bb ecx is in slot 1 or 2, use 3rd slot */ |
| (mi->bb->reg1.reg == REG_NULL) ? SPILL_SLOT_1 : |
| local_slot[local_idx++], no_overlap1, no_overlap2); |
| } |
| |
| |
| if (mi->bb->reg2.reg != REG_NULL && |
| (!need3 || !reg3_must_be_ecx || mi->bb->reg2.reg != DR_REG_XCX) && |
| (!mi->eax.used || mi->bb->reg2.reg != DR_REG_XAX)) { |
| /* Use whole-bb spilled reg (PR 489221) */ |
| mi->reg2 = mi->bb->reg2; |
| mi->reg2.dead = (live[mi->reg2.reg - REG_START] == LIVE_DEAD); |
| } else { |
| /* Pick secondary scratch reg */ |
| ASSERT(local_idx < local_idx_max, "local slot overflow"); |
| pick_scratch_reg_helper(mi, &mi->reg2, live, only_abcd, |
| mi->reg1.reg == DR_REG_XBX ? |
| ((need3 && mi->reg3.reg == DR_REG_XDX) ? |
| DR_REG_XCX : DR_REG_XDX) : |
| ((need3 && mi->reg3.reg == DR_REG_XBX) ? |
| DR_REG_XCX : DR_REG_XBX), |
| /* if whole-bb ecx is in slot 1 or 2, use 3rd slot */ |
| (mi->bb->reg2.reg == REG_NULL) ? SPILL_SLOT_2 : |
| local_slot[local_idx++], no_overlap1, no_overlap2); |
| } |
| |
| if (mi->bb->reg1.reg == REG_NULL && |
| need3 && !mi->reg3.dead && mi->reg3.xchg == REG_NULL) { |
| /* See if we can use slots 1 or 2 instead: matters when using DR slots */ |
| mi->reg3.slot = spill_reg3_slot(mi->aflags == EFLAGS_WRITE_ARITH, |
| !mi->eax.used || mi->eax.dead, |
| mi->reg1.dead, mi->reg2.dead); |
| } |
| |
| DOLOG(4, { |
| void *drcontext = dr_get_current_drcontext(); |
| tls_util_t *pt = PT_GET(drcontext); |
| ASSERT(pt != NULL, "should always have dcontext in cur DR"); |
| LOG(3, "scratch: "); |
| instr_disassemble(drcontext, inst, LOGFILE(pt)); |
| LOG(3, "| "); |
| print_scratch_reg(drcontext, &mi->reg1, 1, LOGFILE(pt)); |
| LOG(3, ", "); |
| print_scratch_reg(drcontext, &mi->reg2, 2, LOGFILE(pt)); |
| if (need3) { |
| LOG(3, ", "); |
| print_scratch_reg(drcontext, &mi->reg3, 3, LOGFILE(pt)); |
| } |
| LOG(3, "\n"); |
| }); |
| |
| # ifdef DEBUG |
| check_scratch_reg_no_overlap(&mi->reg1, &mi->reg2); |
| if (need3) { |
| check_scratch_reg_no_overlap(&mi->reg1, &mi->reg3); |
| check_scratch_reg_no_overlap(&mi->reg2, &mi->reg3); |
| } |
| # endif |
| } |
| #endif /* X86 */ |
| |
| static bool |
| insert_spill_common(void *drcontext, instrlist_t *bb, instr_t *inst, |
| scratch_reg_info_t *si, bool spill, |
| bool just_xchg, bool do_global) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| /* No need to do a local spill/restore if globally spilled (PR 489221) */ |
| if (si->used && !si->dead && (!si->global || do_global)) { |
| if (si->xchg != REG_NULL) { |
| /* spill/restore are identical */ |
| PRE(bb, inst, INSTR_CREATE_xchg |
| (drcontext, opnd_create_reg(si->reg), opnd_create_reg(si->xchg))); |
| } else if (!just_xchg) { |
| if (spill) |
| spill_reg(drcontext, bb, inst, si->reg, si->slot); |
| else |
| restore_reg(drcontext, bb, inst, si->reg, si->slot); |
| } |
| if (!spill && do_global) { |
| /* FIXME PR 553724: avoid later redundant restore at |
| * bottom of bb by setting used to false here. |
| * However that's not working for reasons I haven't yet determined. |
| */ |
| } |
| return true; |
| } |
| return false; |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| return false; |
| #endif |
| } |
| |
| bool |
| insert_spill_global(void *drcontext, instrlist_t *bb, instr_t *inst, |
| scratch_reg_info_t *si, bool spill) |
| { |
| return insert_spill_common(drcontext, bb, inst, si, spill, false, true); |
| } |
| |
| void |
| insert_spill_or_restore(void *drcontext, instrlist_t *bb, instr_t *inst, |
| scratch_reg_info_t *si, bool spill, bool just_xchg) |
| { |
| insert_spill_common(drcontext, bb, inst, si, spill, just_xchg, false); |
| } |
| |
| /* insert aflags save code sequence w/o spill: lahf; seto %al; */ |
| void |
| insert_save_aflags_nospill(void *drcontext, instrlist_t *ilist, |
| instr_t *inst, bool save_oflag) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| PRE(ilist, inst, INSTR_CREATE_lahf(drcontext)); |
| if (save_oflag) { |
| PRE(ilist, inst, |
| INSTR_CREATE_setcc(drcontext, OP_seto, opnd_create_reg(DR_REG_AL))); |
| } |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| /* insert aflags restore code sequence w/o spill: add %al, 0x7f; sahf; */ |
| void |
| insert_restore_aflags_nospill(void *drcontext, instrlist_t *ilist, |
| instr_t *inst, bool restore_oflag) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| if (restore_oflag) { |
| PRE(ilist, inst, INSTR_CREATE_add |
| (drcontext, opnd_create_reg(REG_AL), OPND_CREATE_INT8(0x7f))); |
| } |
| PRE(ilist, inst, INSTR_CREATE_sahf(drcontext)); |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| void |
| insert_save_aflags(void *drcontext, instrlist_t *bb, instr_t *inst, |
| scratch_reg_info_t *si, int aflags) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| if (si->reg != REG_NULL) { |
| ASSERT(si->reg == DR_REG_XAX, "must use eax for aflags"); |
| insert_spill_or_restore(drcontext, bb, inst, si, true/*save*/, false); |
| } |
| insert_save_aflags_nospill(drcontext, bb, inst, aflags != EFLAGS_WRITE_OF); |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| void |
| insert_restore_aflags(void *drcontext, instrlist_t *bb, instr_t *inst, |
| scratch_reg_info_t *si, int aflags) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| insert_restore_aflags_nospill(drcontext, bb, inst, |
| aflags != EFLAGS_WRITE_OF); |
| if (si->reg != REG_NULL) { |
| ASSERT(si->reg == DR_REG_XAX, "must use eax for aflags"); |
| insert_spill_or_restore(drcontext, bb, inst, si, false/*restore*/, false); |
| } |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| static inline bool |
| scratch_reg1_is_avail(instr_t *inst, fastpath_info_t *mi, bb_info_t *bi) |
| { |
| int opc = instr_get_opcode(inst); |
| return (bi->reg1.reg != DR_REG_NULL && |
| bi->reg1.used && |
| /* ensure neither sharing w/ next nor sharing w/ prev */ |
| !SHARING_XL8_ADDR_BI(bi) && mi != NULL && !mi->use_shared && |
| /* cmovcc does an aflags restore after the lea, so reg1 needs |
| * to stay untouched |
| */ |
| !opc_is_cmovcc(opc) && !opc_is_fcmovcc(opc)); |
| } |
| |
| static inline bool |
| scratch_reg2_is_avail(instr_t *inst, fastpath_info_t *mi, bb_info_t *bi) |
| { |
| int opc = instr_get_opcode(inst); |
| return (bi->reg2.reg != DR_REG_NULL && |
| bi->reg2.used && |
| /* we use reg2 for cmovcc */ |
| !opc_is_cmovcc(opc) && !opc_is_fcmovcc(opc)); |
| } |
| |
| /* single eflags save per bb */ |
| void |
| save_aflags_if_live(void *drcontext, instrlist_t *bb, instr_t *inst, |
| fastpath_info_t *mi, bb_info_t *bi) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| scratch_reg_info_t si; |
| /* We save aflags unless there's no read prior to the 1st write. |
| * We clobber eax while doing so if eax is dead. |
| * Technically both are unsafe: should restore on a fault (PR |
| * 463053) but we consider that too pathological to bother. |
| */ |
| bool eax_dead = bi->eax_dead || |
| (bi->reg1.reg == DR_REG_XAX && scratch_reg1_is_avail(inst, mi, bi)) || |
| (bi->reg2.reg == DR_REG_XAX && scratch_reg2_is_avail(inst, mi, bi)); |
| ASSERT(options.pattern == 0, "pattern is using drreg"); |
| if (!bi->eflags_used) |
| return; |
| if (bi->aflags != EFLAGS_WRITE_ARITH) { |
| /* slot 5 won't be used for 3rd reg (that's 4) and is ok for temp use */ |
| si.slot = SPILL_SLOT_5; |
| if (eax_dead || bi->aflags_where == AFLAGS_IN_EAX) { |
| si.reg = REG_NULL; |
| } else { |
| si.reg = DR_REG_XAX; |
| /* reg1 is used for xl8 sharing so check whether shared_memop is set |
| * == share w/ next. it's cleared prior to post-app-write aflags save |
| * so we use mi->use_shared to detect share w/ prev. |
| * for aflags restore code added later it's ok to be too conservative: |
| * these fields should all be cleared anyway, and won't matter for |
| * top-of-bb save. |
| * we can assume that reg2 is dead. |
| */ |
| if (scratch_reg1_is_avail(inst, mi, bi)) |
| si.xchg = bi->reg1.reg; |
| else if (scratch_reg2_is_avail(inst, mi, bi)) |
| si.xchg = bi->reg2.reg; |
| else |
| si.xchg = REG_NULL; |
| ASSERT(si.xchg != DR_REG_XAX, "xchg w/ self is not a save"); |
| si.used = true; |
| si.dead = false; |
| si.global = false; /* to enable the save */ |
| } |
| insert_save_aflags(drcontext, bb, inst, &si, bi->aflags); |
| /* optimization: |
| * We keep the flags in eax if eax is not used in any app instrs later. |
| * XXX: we can be more aggressive to keep the flags in eax if it is |
| * not used in app instr between here and the next eflags restore. |
| * Since a lahf followed by a read of eax causes a partial-reg |
| * stall that could improve perf noticeably. |
| * However, this would make the state restore more complex. |
| */ |
| if (bi->aflags_where == AFLAGS_UNKNOWN) { |
| # ifdef TOOL_DR_MEMORY |
| /* i#1466: remember where to start restore state for pattern mode. |
| * In pattern mode, we only save eax if we save aflags. i#1466 is a bug |
| * that tries to restore eax on a fault before we saving eax, so we use |
| * first_restore_pc to remember where eax is saved and only restore eax |
| * for fault happening after that pc. |
| * For shadow mode, it should always start from the first pc, and we |
| * set first_restore_pc to be NULL. |
| */ |
| if (options.pattern != 0) { |
| ASSERT(bi->first_restore_pc == NULL, |
| "first_restore_pc must be NULL if aflags_where is not set"); |
| bi->first_restore_pc = (mi == NULL ? instr_get_app_pc(inst) : mi->xl8); |
| ASSERT(bi->first_restore_pc != NULL, "instr app_pc must not be NULL"); |
| } |
| /* We can avoid saving eflags to TLS and restoring app %eax if we know that |
| * %eax is not used later in the bb. This might be called in the middle of |
| * a bb, so we need use first_restore_pc to remember where we start stealing |
| * %eax (#i1466). |
| */ |
| if (!xax_is_used_subsequently(inst) && options.pattern != 0) { |
| /* To keep aflags in %eax, we need a permanent TLS store for |
| * storing app's %eax value. Current implementation uses SLOT 5, |
| * which is used for third register spill and temporary eax |
| * spill on aflags saving. So this optimization is only applied |
| * in pattern mode. |
| * XXX: to apply this optimization in shadow mode, we should use |
| * a permanent TLS slot, and make sure neither spill reg1 nor |
| * reg2 uses %eax. |
| */ |
| /* - eax is not used by app later in bb |
| * - eax is holding aflags |
| * - app's eax is in tls slot from now on |
| */ |
| bi->aflags_where = AFLAGS_IN_EAX; |
| } else |
| # endif |
| bi->aflags_where = AFLAGS_IN_TLS; |
| } |
| if (bi->aflags_where == AFLAGS_IN_EAX) |
| return; |
| /* save aflags into tls */ |
| PRE(bb, inst, INSTR_CREATE_mov_st |
| (drcontext, spill_slot_opnd(drcontext, SPILL_SLOT_EFLAGS_EAX), |
| opnd_create_reg(DR_REG_XAX))); |
| if (!eax_dead) { /* restore eax */ |
| /* I used to use xchg to avoid needing two instrs, but xchg w/ mem's |
| * lock of the bus shows up as a measurable perf hit (PR 553724) |
| */ |
| insert_spill_or_restore(drcontext, bb, inst, &si, false/*restore*/, false); |
| } |
| } |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| /* Single eflags save per bb |
| * N.B.: the sequence added here is matched in restore_mcontext_on_shadow_fault() |
| */ |
| void |
| restore_aflags_if_live(void *drcontext, instrlist_t *bb, instr_t *inst, |
| fastpath_info_t *mi, bb_info_t *bi) |
| { |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| scratch_reg_info_t si; |
| if (!bi->eflags_used) |
| return; |
| ASSERT(bi->aflags_where == AFLAGS_IN_TLS || |
| bi->aflags_where == AFLAGS_IN_EAX, |
| "bi->aflags_where is not set"); |
| ASSERT(options.pattern == 0, "pattern is using drreg"); |
| si.reg = DR_REG_XAX; |
| si.xchg = REG_NULL; |
| si.slot = SPILL_SLOT_EFLAGS_EAX; |
| si.used = true; |
| si.dead = false; |
| si.global = false; /* to enable the restore */ |
| if (bi->aflags_where == AFLAGS_IN_TLS) { |
| if (bi->eax_dead || |
| (bi->reg1.reg == DR_REG_XAX && scratch_reg1_is_avail(inst, mi, bi)) || |
| (bi->reg2.reg == DR_REG_XAX && scratch_reg2_is_avail(inst, mi, bi))) { |
| insert_spill_or_restore(drcontext, bb, inst, &si, false/*restore*/, false); |
| /* we do NOT want the eax-restore at the end of insert_restore_aflags() */ |
| si.reg = DR_REG_NULL; |
| } else { |
| si.slot = SPILL_SLOT_5; |
| /* See notes in save_aflags_if_live on sharing impacting reg1 being scratch */ |
| if (scratch_reg1_is_avail(inst, mi, bi)) |
| si.xchg = bi->reg1.reg; |
| else if (scratch_reg2_is_avail(inst, mi, bi)) |
| si.xchg = bi->reg2.reg; |
| ASSERT(si.xchg != DR_REG_XAX, "xchg w/ self is not a save"); |
| /* I used to use xchg to avoid needing two instrs, but xchg w/ mem's |
| * lock of the bus shows up as a measurable perf hit (PR 553724) |
| */ |
| insert_spill_or_restore(drcontext, bb, inst, &si, true/*save*/, false); |
| PRE(bb, inst, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_XAX), |
| spill_slot_opnd(drcontext, SPILL_SLOT_EFLAGS_EAX))); |
| /* we DO want the eax-restore at the end of insert_restore_aflags() */ |
| } |
| } else { /* AFLAGS_IN_EAX */ |
| si.slot = SPILL_SLOT_5; |
| } |
| insert_restore_aflags(drcontext, bb, inst, &si, bi->aflags); |
| /* avoid re-restoring. FIXME: do this for insert_spill_global too? */ |
| bi->eflags_used = false; |
| #else |
| /* FIXME i#1795/i#1726: port shadow modes to use drreg */ |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| /*************************************************************************** |
| * Whole-bb spilling (PR 489221) |
| */ |
| |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| static void |
| pick_bb_scratch_regs_helper(opnd_t opnd, int uses[NUM_LIVENESS_REGS]) |
| { |
| int j; |
| for (j = 0; j < opnd_num_regs_used(opnd); j++) { |
| reg_id_t reg = opnd_get_reg_used(opnd, j); |
| if (reg_is_gpr(reg)) { |
| int idx = reg_to_pointer_sized(reg) - REG_START; |
| ASSERT(idx >= 0 && idx < NUM_LIVENESS_REGS, "reg enum error"); |
| uses[idx]++; |
| if (opnd_is_memory_reference(opnd)) |
| uses[idx]++; |
| } |
| } |
| } |
| |
| static void |
| pick_bb_scratch_regs(instr_t *inst, bb_info_t *bi) |
| { |
| /* Pick the best regs to use as scratch. We want the fewest uses, |
| * since we have to restore on each use (twice for a memref: once |
| * for lea, once for app instr). |
| * Having varying scratch regs per bb forces us to store which |
| * ones so we can restore on slowpath/shadow fault/app fault but |
| * it is worth it to shrink fastpath instru. |
| * |
| * Plan: pick just 2 scratches, and then those that need 3 |
| * (mem2mem or sub-dword) have to grab a local 3rd but adjust so |
| * that ecx is considered #3. |
| * Going to always put whole-bb eflags into tls slot, so not |
| * considering leaving it in eax and getting 2 more scratch regs, for |
| * simplicity: though that could be more efficient for some bbs. |
| * |
| * Future work PR 492073: if bb has high scores for all regs, try to |
| * split into 2: maybe 1st half diff from 2nd half. Or, fall back to |
| * per-instr, can be less expensive than restoring regs before each lea |
| * and app instr. |
| */ |
| int uses[NUM_LIVENESS_REGS] = {0,}; |
| int i, uses_least = INT_MAX, uses_second = INT_MAX; |
| |
| while (inst != NULL) { |
| if (instr_is_app(inst)) { |
| for (i = 0; i < instr_num_dsts(inst); i++) |
| pick_bb_scratch_regs_helper(instr_get_dst(inst, i), uses); |
| for (i = 0; i < instr_num_srcs(inst); i++) |
| pick_bb_scratch_regs_helper(instr_get_src(inst, i), uses); |
| if (instr_is_cti(inst)) |
| break; |
| } |
| inst = instr_get_next(inst); |
| } |
| /* Too risky to use esp: if no alt sig stk (ESXi) or on Windows can't |
| * handle fault |
| */ |
| uses[DR_REG_XSP - REG_START] = INT_MAX; |
| #ifdef X86 |
| /* Future work PR 492073: If esi/edi/ebp are among the least-used, xchg |
| * w/ cx/dx/bx and swap in each app instr. Have to ensure results in |
| * legal instrs (if app uses sub-dword, or certain addressing modes). |
| */ |
| uses[DR_REG_XBP - REG_START] = INT_MAX; |
| uses[DR_REG_XSI - REG_START] = INT_MAX; |
| uses[DR_REG_XDI - REG_START] = INT_MAX; |
| #elif defined(ARM) |
| /* stolen reg must not be used */ |
| uses[dr_get_stolen_reg() - REG_START] = INT_MAX; |
| uses[DR_REG_LR - REG_START] = INT_MAX; |
| uses[DR_REG_PC - REG_START] = INT_MAX; |
| #endif /* X86/ARM */ |
| #ifdef X64 |
| /* XXX i#1632: once we have byte-to-byte shadowing we shouldn't need |
| * just a/b/c/d regs and should be able to use these. For now with |
| * 1B-to-2b we need %ah, etc. |
| */ |
| for (i = DR_REG_R8; i <= DR_REG_R15; i++) { |
| ASSERT(i - REG_START < NUM_LIVENESS_REGS, "overflow"); |
| uses[i - REG_START] = INT_MAX; |
| } |
| #endif |
| for (i = 0; i < NUM_LIVENESS_REGS; i++) { |
| if (uses[i] < uses_least) { |
| uses_second = uses_least; |
| bi->reg2.reg = bi->reg1.reg; |
| uses_least = uses[i]; |
| bi->reg1.reg = REG_START + i; |
| } else if (uses[i] < uses_second) { |
| uses_second = uses[i]; |
| bi->reg2.reg = REG_START + i; |
| } |
| } |
| /* For PR 493257 (share shadow translations) we do NOT want reg1 to be |
| * eax, so we can save eflags w/o clobbering shared shadow addr in reg1 |
| */ |
| if (bi->reg1.reg == DR_REG_XAX) { |
| scratch_reg_info_t tmp = bi->reg1; |
| ASSERT(bi->reg2.reg != DR_REG_XAX, "reg2 shouldn't be eax"); |
| bi->reg1 = bi->reg2; |
| bi->reg2 = tmp; |
| } |
| /* We don't want ecx in reg1, for sharing. Even though we swap |
| * when we need ecx as a 3rd reg, sharing really wants reg1==whole-bb reg1. |
| */ |
| else if (bi->reg1.reg == DR_REG_XCX && bi->reg2.reg != DR_REG_XAX) { |
| scratch_reg_info_t tmp = bi->reg1; |
| ASSERT(bi->reg2.reg != DR_REG_XCX, "reg2 shouldn't equal reg1"); |
| DOLOG(3, { |
| void *drcontext = dr_get_current_drcontext(); |
| LOG(3, "swapping reg1 "); |
| opnd_disassemble(drcontext, opnd_create_reg(bi->reg1.reg), |
| LOGFILE(PT_GET(drcontext))); |
| LOG(3, " and reg2 "); |
| opnd_disassemble(drcontext, opnd_create_reg(bi->reg2.reg), |
| LOGFILE(PT_GET(drcontext))); |
| LOG(3, "\n"); |
| }); |
| bi->reg1 = bi->reg2; |
| bi->reg2 = tmp; |
| } |
| ASSERT(bi->reg1.reg <= DR_REG_XBX, "NYI non-a/b/c/d reg"); |
| bi->reg1.slot = SPILL_SLOT_1; |
| /* Dead-across-whole-bb is rare so we don't bother to support xchg */ |
| bi->reg1.xchg = REG_NULL; |
| /* The dead fields will be computed in fastpath_pre_app_instr */ |
| bi->reg1.dead = false; |
| bi->reg1.used = false; /* will be set once used */ |
| bi->reg1.global = true; |
| ASSERT(bi->reg2.reg <= DR_REG_XBX, "NYI non-a/b/c/d reg"); |
| ASSERT(bi->reg1.reg != bi->reg2.reg, "reg conflict"); |
| bi->reg2.slot = SPILL_SLOT_2; |
| bi->reg2.xchg = REG_NULL; |
| bi->reg2.dead = false; |
| bi->reg2.used = false; /* will be set once used */ |
| bi->reg2.global = true; |
| |
| #ifdef STATISTICS |
| if (uses_least > 0) { |
| STATS_INC(reg_spill_used_in_bb); |
| } else { |
| STATS_INC(reg_spill_unused_in_bb); |
| } if (uses_second > 0) { |
| STATS_INC(reg_spill_used_in_bb); |
| } else { |
| STATS_INC(reg_spill_unused_in_bb); |
| } |
| #endif |
| DOLOG(3, { |
| void *drcontext = dr_get_current_drcontext(); |
| tls_util_t *pt = PT_GET(drcontext); |
| ASSERT(pt != NULL, "should always have dcontext in cur DR"); |
| LOG(3, "whole-bb scratch: "); |
| print_scratch_reg(drcontext, &bi->reg1, 1, LOGFILE(pt)); |
| LOG(3, " x%d, ", uses_least); |
| print_scratch_reg(drcontext, &bi->reg2, 2, LOGFILE(pt)); |
| LOG(3, " x%d\n", uses_second); |
| }); |
| } |
| #endif /* X86 */ |
| |
| bool |
| whole_bb_spills_enabled(void) |
| { |
| /* Whole-bb eflags and register saving requires spill slots that |
| * can be live across instructions == our own spill slots. |
| * We use SPILL_SLOT_1, SPILL_SLOT_2, and SPILL_SLOT_EFLAGS_EAX |
| * for 2 registers and eflags. |
| */ |
| return (options.num_spill_slots >= SPILL_SLOT_EFLAGS_EAX && |
| #ifdef TOOL_DR_HEAPSTAT |
| options.staleness && |
| #endif |
| /* should we enable whole-bb for -leaks_only? |
| * we'd need to enable bb table and state restore on fault. |
| * since it's rare to have more than one stack adjust in a |
| * single bb, I don't think we'd gain enough perf to be worth |
| * the complexity. |
| */ |
| INSTRUMENT_MEMREFS()); |
| } |
| |
| void |
| fastpath_top_of_bb(void *drcontext, void *tag, instrlist_t *bb, bb_info_t *bi) |
| { |
| instr_t *inst = instrlist_first(bb); |
| #ifdef DEBUG |
| /* We look at instr pc, not the tag, to handle displaced code such |
| * as for the vsyscall hook. |
| */ |
| app_pc prev_pc = instr_get_app_pc(instrlist_first_app(bb)); |
| ASSERT(prev_pc != NULL, "bb first app pc must not be NULL"); |
| /* i#260 and i#1466: bbs must be contiguous */ |
| if (inst != NULL && whole_bb_spills_enabled() && |
| /* bi->is_repstr_to_loop is set in app2app and may mess up the instr pc */ |
| !bi->is_repstr_to_loop) { |
| for (; inst != NULL; inst = instr_get_next_app(inst)) { |
| app_pc cur_pc = instr_get_app_pc(inst); |
| if (cur_pc == NULL) |
| continue; |
| /* relax the check here instead of "cur_pc == prev_pc + instr_length" |
| * to allow client adding fake app instr |
| */ |
| ASSERT(cur_pc >= prev_pc, "bb is not contiguous"); |
| prev_pc = cur_pc; |
| } |
| inst = instrlist_first(bb); |
| } |
| #endif |
| if (inst == NULL || !whole_bb_spills_enabled() || |
| /* pattern is using drreg */ |
| options.pattern != 0) { |
| bi->eflags_used = false; |
| bi->reg1.reg = REG_NULL; |
| bi->reg1.used = false; |
| bi->reg2.reg = REG_NULL; |
| bi->reg2.used = false; |
| return; |
| } |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| /* analyze bb and pick which scratch regs to use. don't actually do |
| * the spills until we know there's actual instrumentation in this bb. |
| * we don't also delay the analysis b/c it's simpler to analyze the |
| * unmodified instrlist: we do add clean calls that don't count as |
| * instru we need to spill for, etc. we could put in checks for meta |
| * but will wait until analysis shows up as perf bottleneck. |
| */ |
| pick_bb_scratch_regs(inst, bi); |
| #endif |
| } |
| |
| /* Invoked before the regular pre-app instrumentation */ |
| void |
| fastpath_pre_instrument(void *drcontext, instrlist_t *bb, instr_t *inst, bb_info_t *bi) |
| { |
| #ifdef X86 |
| int live[NUM_LIVENESS_REGS]; |
| #endif |
| app_pc pc = instr_get_app_pc(inst); |
| if (pc != NULL) { |
| if (bi->first_app_pc == NULL) |
| bi->first_app_pc = pc; |
| bi->last_app_pc = pc; |
| } |
| |
| if (!whole_bb_spills_enabled()) |
| return; |
| if (options.pattern != 0) /* pattern is using drreg */ |
| return; |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| /* Haven't instrumented below here yet, so forward analysis should |
| * see only app instrs |
| * XXX i#777: should do reverse walk during analysis phase and store results |
| * in bit array in new note fields |
| */ |
| bi->aflags = get_aflags_and_reg_liveness(inst, live, options.pattern != 0); |
| /* Update the dead fields */ |
| if (bi->reg1.reg != DR_REG_NULL) |
| bi->reg1.dead = (live[bi->reg1.reg - REG_START] == LIVE_DEAD); |
| if (bi->reg2.reg != DR_REG_NULL) |
| bi->reg2.dead = (live[bi->reg2.reg - REG_START] == LIVE_DEAD); |
| |
| bi->eax_dead = (live[DR_REG_XAX - REG_START] == LIVE_DEAD); |
| #endif |
| } |
| |
| bool |
| instr_is_spill(instr_t *inst) |
| { |
| return (instr_get_opcode(inst) == IF_X86_ELSE(OP_mov_st, OP_str) && |
| is_spill_slot_opnd(dr_get_current_drcontext(), instr_get_dst(inst, 0)) && |
| opnd_is_reg(instr_get_src(inst, 0))); |
| } |
| |
| bool |
| instr_is_restore(instr_t *inst) |
| { |
| return (instr_get_opcode(inst) == IF_X86_ELSE(OP_mov_ld, OP_ldr) && |
| is_spill_slot_opnd(dr_get_current_drcontext(), instr_get_src(inst, 0)) && |
| opnd_is_reg(instr_get_dst(inst, 0))); |
| } |
| |
| bool |
| instr_at_pc_is_restore(void *drcontext, byte *pc) |
| { |
| instr_t inst; |
| bool res; |
| instr_init(drcontext, &inst); |
| res = (decode(drcontext, pc, &inst) != NULL && |
| instr_is_restore(&inst)); |
| instr_free(drcontext, &inst); |
| return res; |
| } |
| |
| void |
| mark_eflags_used(void *drcontext, instrlist_t *bb, bb_info_t *bi) |
| { |
| instr_t *where_spill = (bi == NULL || bi->spill_after == NULL) ? |
| instrlist_first(bb) : instr_get_next(bi->spill_after); |
| if (!whole_bb_spills_enabled() || bi == NULL/*gencode*/ || bi->eflags_used) |
| return; |
| /* optimization: if flags are dead then ignore our use. |
| * technically unsafe (PR 463053). |
| * FIXME: for use in fastpath_pre_instrument() for post-instr |
| * save are we using the pre-instr analysis? |
| */ |
| if (bi->aflags == EFLAGS_WRITE_ARITH) { |
| LOG(4, "eflags are dead so not saving\n"); |
| return; |
| } |
| /* To use global-eax for eflags we must spill regs before flags */ |
| while (instr_is_meta(where_spill) && |
| instr_is_spill(where_spill) && |
| instr_get_next(where_spill) != NULL) |
| where_spill = instr_get_next(where_spill); |
| LOG(4, "marking eflags used => spilling if live\n"); |
| bi->eflags_used = true; |
| save_aflags_if_live(drcontext, bb, where_spill, NULL, bi); |
| #ifdef STATISTICS |
| if (bi->aflags != EFLAGS_WRITE_ARITH) |
| STATS_INC(aflags_saved_at_top); |
| #endif |
| } |
| |
| void |
| mark_scratch_reg_used(void *drcontext, instrlist_t *bb, |
| bb_info_t *bi, scratch_reg_info_t *si) |
| { |
| instr_t *where_spill = (bi == NULL || bi->spill_after == NULL) ? |
| instrlist_first(bb) : instr_get_next(bi->spill_after); |
| /* Update global used values, and if global save on first use in bb */ |
| if (si->used) |
| return; |
| if (si->global) { |
| ASSERT(bi != NULL, "should only use global in bb, not gencode"); |
| /* To use global-eax for eflags we must spill regs before flags */ |
| while (instr_get_prev(where_spill) != NULL && |
| instr_is_meta(instr_get_prev(where_spill)) && |
| /* We want to NOT walk back into clean call: clean call |
| * ends in restore, not spill |
| */ |
| instr_is_spill(instr_get_prev(where_spill))) |
| where_spill = instr_get_prev(where_spill); |
| if (si->reg == bi->reg1.reg) { |
| bi->reg1.used = true; |
| insert_spill_global(drcontext, bb, where_spill, &bi->reg1, true/*save*/); |
| } else { |
| ASSERT(si->reg == bi->reg2.reg, "global vs local mismatch"); |
| bi->reg2.used = true; |
| insert_spill_global(drcontext, bb, where_spill, &bi->reg2, true/*save*/); |
| } |
| } |
| /* Even if global we have to mark the si copy as used too */ |
| si->used = true; |
| } |
| |
| bool |
| instr_needs_eflags_restore(instr_t *inst, uint aflags_liveness) |
| { |
| return (TESTANY(EFLAGS_READ_ARITH, |
| instr_get_eflags(inst, DR_QUERY_DEFAULT)) || |
| /* If the app instr writes some subset of eflags we need to restore |
| * rest so they're combined properly |
| */ |
| (TESTANY(EFLAGS_WRITE_ARITH, |
| instr_get_eflags(inst, DR_QUERY_INCLUDE_ALL)) && |
| aflags_liveness != EFLAGS_WRITE_ARITH)); |
| } |
| |
| /* Invoked after the regular pre-app instrumentation */ |
| void |
| fastpath_pre_app_instr(void *drcontext, instrlist_t *bb, instr_t *inst, |
| bb_info_t *bi, fastpath_info_t *mi) |
| { |
| /* Preserve app semantics wrt global spilled registers */ |
| /* XXX i#777: gets next instr, and below does liveness analysis w/ forward scan. |
| * should use stored info from reverse scan done during analysis phase. |
| */ |
| #ifdef X86 |
| instr_t *next = instr_get_next(inst); |
| int live[NUM_LIVENESS_REGS]; |
| bool restored_for_read = false; |
| #endif |
| |
| if (!whole_bb_spills_enabled()) |
| return; |
| if (options.pattern != 0) /* pattern is using drreg */ |
| return; |
| |
| #ifdef X86 /* XXX i#1795: eliminate this and port to drreg */ |
| /* If this is the last instr, the end-of-bb restore will restore for any read, |
| * and everything is dead so we can ignore writes |
| */ |
| if (next == NULL) |
| return; |
| |
| /* Before each read, restore global spilled registers */ |
| if (instr_needs_eflags_restore(inst, bi->aflags)) |
| restore_aflags_if_live(drcontext, bb, inst, mi, bi); |
| /* Optimization: don't bother to restore if this is not a meaningful read |
| * (e.g., xor with self) |
| */ |
| if ((bi->reg1.reg != DR_REG_NULL || bi->reg2.reg != DR_REG_NULL) && |
| (!result_is_always_defined(inst, true/*natively*/) || |
| /* if sub-dword then we have to restore for rest of bits */ |
| opnd_size_in_bytes(opnd_get_size(instr_get_src(inst, 0))) < 4)) { |
| /* we don't mark as used: if unused so far, no reason to restore */ |
| if (instr_reads_from_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL) || |
| /* Restore all app values prior to an annotation to ensure the |
| * arguments passed to the handler are correct. |
| */ |
| (instr_is_label(inst) && |
| (ptr_uint_t)instr_get_note(inst) == DR_NOTE_ANNOTATION) || |
| /* if sub-reg is written we need to restore rest */ |
| (instr_writes_to_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL) && |
| !instr_writes_to_exact_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL)) || |
| /* for conditional write, simplest to restore before and save after, b/c |
| * if cond fails we have to avoid saving bogus value |
| */ |
| (instr_writes_to_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL) && |
| !instr_writes_to_reg(inst, bi->reg1.reg, DR_QUERY_DEFAULT))) { |
| restored_for_read = true; |
| /* If reg1 holds a shared shadow addr, better to preserve it than |
| * to have to re-translate |
| */ |
| if (!opnd_is_null(bi->shared_memop)) { |
| if (instr_writes_to_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL) || |
| instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) || |
| /* must consider reading the other reg (PR 494169) */ |
| instr_reads_from_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL)) { |
| /* give up: not worth complexity (i#165 covers handling) */ |
| STATS_INC(xl8_not_shared_reg_conflict); |
| bi->shared_memop = opnd_create_null(); |
| } else { |
| PRE(bb, inst, |
| XINST_CREATE_store(drcontext, opnd_create_reg(bi->reg2.reg), |
| opnd_create_reg(bi->reg1.reg))); |
| PRE(bb, next, |
| XINST_CREATE_store(drcontext, opnd_create_reg(bi->reg1.reg), |
| opnd_create_reg(bi->reg2.reg))); |
| } |
| } |
| insert_spill_global(drcontext, bb, inst, &bi->reg1, false/*restore*/); |
| } |
| if (instr_reads_from_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) || |
| /* Restore all app values prior to an annotation to ensure the |
| * arguments passed to the handler are correct. |
| */ |
| (instr_is_label(inst) && |
| (ptr_uint_t)instr_get_note(inst) == DR_NOTE_ANNOTATION) || |
| /* if sub-reg is written we need to restore rest */ |
| (instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) && |
| !instr_writes_to_exact_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL)) || |
| /* for conditional write, simplest to restore before and save after, b/c |
| * if cond fails we have to avoid saving bogus value |
| */ |
| (instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) && |
| !instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_DEFAULT))) { |
| restored_for_read = true; |
| insert_spill_global(drcontext, bb, inst, &bi->reg2, false/*restore*/); |
| } |
| } |
| |
| /* After each write, update global spilled values, unless that reg is |
| * dead (xref PR 463053 for safety) |
| */ |
| /* Haven't instrumented below here yet, so forward analysis should |
| * see only app instrs |
| */ |
| /* We updated reg*.dead in fastpath_pre_instrument() but here we want |
| * liveness post-app-instr |
| */ |
| bi->aflags = get_aflags_and_reg_liveness(next, live, options.pattern != 0); |
| /* Update the dead fields */ |
| if (bi->reg1.reg != DR_REG_NULL) |
| bi->reg1.dead = (live[bi->reg1.reg - REG_START] == LIVE_DEAD); |
| if (bi->reg2.reg != DR_REG_NULL) |
| bi->reg2.dead = (live[bi->reg2.reg - REG_START] == LIVE_DEAD); |
| bi->eax_dead = (live[DR_REG_XAX - REG_START] == LIVE_DEAD); |
| |
| if (bi->reg1.reg != DR_REG_NULL && |
| instr_writes_to_reg(inst, bi->reg1.reg, DR_QUERY_INCLUDE_ALL)) { |
| if (!bi->reg1.dead) { |
| bi->reg1.used = true; |
| insert_spill_global(drcontext, bb, next, &bi->reg1, true/*save*/); |
| } |
| /* If reg1 holds a shared shadow addr, better to preserve it than |
| * to have to re-translate. We must do this even if reg1 is dead. |
| */ |
| if (!opnd_is_null(bi->shared_memop)) { |
| if (restored_for_read || |
| (instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) && |
| !bi->reg2.dead)) { |
| /* give up: not worth complexity for now (i#165 covers handling) */ |
| STATS_INC(xl8_not_shared_reg_conflict); |
| bi->shared_memop = opnd_create_null(); |
| } else { |
| bi->reg1.used = true; |
| PRE(bb, inst, |
| XINST_CREATE_store(drcontext, opnd_create_reg(bi->reg2.reg), |
| opnd_create_reg(bi->reg1.reg))); |
| PRE(bb, next, |
| XINST_CREATE_store(drcontext, opnd_create_reg(bi->reg1.reg), |
| opnd_create_reg(bi->reg2.reg))); |
| } |
| } |
| } |
| if (bi->reg2.reg != DR_REG_NULL && |
| instr_writes_to_reg(inst, bi->reg2.reg, DR_QUERY_INCLUDE_ALL) && |
| !bi->reg2.dead) { |
| bi->reg2.used = true; |
| insert_spill_global(drcontext, bb, next, &bi->reg2, true/*save*/); |
| } |
| if (TESTANY(EFLAGS_WRITE_ARITH, |
| instr_get_eflags(inst, DR_QUERY_INCLUDE_ALL)) && |
| bi->aflags != EFLAGS_WRITE_ARITH) { |
| /* Optimization: no need if next is jcc and we just checked definedness */ |
| if (IF_DRMEM(bi->eflags_defined && ) instr_is_jcc(next)) { |
| /* We just wrote to real eflags register, so don't restore at end */ |
| LOG(4, "next instr is jcc so not saving eflags\n"); |
| bi->eflags_used = false; |
| } else { |
| save_aflags_if_live(drcontext, bb, next, mi, bi); |
| } |
| } |
| #endif /* X86 */ |
| } |
| |
| void |
| fastpath_bottom_of_bb(void *drcontext, void *tag, instrlist_t *bb, |
| bb_info_t *bi, bool added_instru, bool translating, |
| bool check_ignore_unaddr) |
| { |
| instr_t *last = instrlist_last(bb); |
| bb_saved_info_t *save; |
| if (!whole_bb_spills_enabled()) |
| return; |
| ASSERT(!added_instru || instrlist_first(bb) != NULL, "can't add instru w/o instrs"); |
| |
| /* the .used field controls whether we actually saved, and thus restore */ |
| LOG(3, "whole-bb scratch: r1=%s, r2=%s, efl=%s\n", |
| bi->reg1.used ? "used" : "unused", |
| bi->reg2.used ? "used" : "unused", |
| bi->eflags_used ? "used" : "unused"); |
| |
| if (!translating) { |
| /* Add to table so we can restore on slowpath or a fault */ |
| save = (bb_saved_info_t *) global_alloc(sizeof(*save), HEAPSTAT_PERBB); |
| memset(save, 0, sizeof(*save)); |
| /* If dead initially and only used later, fine to have fault path |
| * restore from TLS early since dead. But if never used and thus never |
| * spilled then we have to tell the fault path to not restore from TLS. |
| */ |
| if (bi->reg1.used) |
| save->scratch1 = bi->reg1.reg; |
| else |
| save->scratch1 = REG_NULL; |
| if (bi->reg2.used) |
| save->scratch2 = bi->reg2.reg; |
| else |
| save->scratch2 = REG_NULL; |
| save->eflags_saved = bi->eflags_used; |
| save->aflags_in_eax = (bi->aflags_where == AFLAGS_IN_EAX); |
| /* We store the pc of the last instr, since everything is restored |
| * already (and NOT present in our tls slots) if have a fault in that |
| * instr: unless it's a transformed repstr, in which case the final |
| * OP_loop won't fault, so a fault will be before the restores (i#532). |
| */ |
| if (bi->is_repstr_to_loop) |
| save->last_instr = NULL; |
| else |
| save->last_instr = bi->last_app_pc; |
| /* i#1466: remember the first_restore_pc for restore state in pattern mode */ |
| save->first_restore_pc = bi->first_restore_pc; |
| save->check_ignore_unaddr = check_ignore_unaddr; |
| /* i#826: share_xl8_max_diff can change, save it. */ |
| save->share_xl8_max_diff = bi->share_xl8_max_diff; |
| /* store style of instru rather than ask DR to store xl8. |
| * XXX DRi#772: could add flush callback and avoid this save |
| */ |
| save->pattern_4byte_check_only = bi->pattern_4byte_check_only; |
| |
| /* we store the size and assume bbs are contiguous so we can free (i#260) */ |
| ASSERT(bi->first_app_pc != NULL, "first instr should have app pc"); |
| ASSERT(bi->last_app_pc != NULL, "last instr should have app pc"); |
| if (bi->is_repstr_to_loop) /* first is +1 hack */ |
| bi->first_app_pc = bi->last_app_pc; |
| else { |
| ASSERT(bi->last_app_pc >= bi->first_app_pc, |
| "bb should be contiguous w/ increasing pcs"); |
| } |
| save->bb_size = decode_next_pc(drcontext, bi->last_app_pc) - bi->first_app_pc; |
| |
| /* PR 495787: Due to non-precise flushing we can have a flushed bb |
| * removed from the htables and then a new bb created before we received |
| * the deletion event. We can't tell this apart from duplication due to |
| * thread-private copies: but this mechanism should handle that as well, |
| * since our saved info should be deterministic and identical for each |
| * copy. Note that we do not want a new "unreachable event" b/c we need |
| * to keep our bb info around in case the semi-flushed bb hits a fault. |
| */ |
| hashtable_lock(&bb_table); |
| bb_save_add_entry(tag, save); |
| hashtable_unlock(&bb_table); |
| } |
| if (options.pattern != 0) /* pattern is using drreg */ |
| return; |
| |
| /* We do this *after* recording what to restore, b/c this can change the used |
| * fields (i#1458). |
| */ |
| if (added_instru) { |
| restore_aflags_if_live(drcontext, bb, last, NULL, bi); |
| if (bi->reg1.reg != DR_REG_NULL) |
| insert_spill_global(drcontext, bb, last, &bi->reg1, false/*restore*/); |
| if (bi->reg2.reg != DR_REG_NULL) |
| insert_spill_global(drcontext, bb, last, &bi->reg2, false/*restore*/); |
| } |
| } |