diff --git a/api/docs/release.dox b/api/docs/release.dox
index a0edc75a787..76378c8c9b4 100644
--- a/api/docs/release.dox
+++ b/api/docs/release.dox
@@ -255,6 +255,7 @@ Further non-compatibility-affecting changes include:
- Added instr_is_sse() and instr_is_sse2().
- Added instr_is_3DNow(), instr_is_sse3(), and instr_is_ssse3().
- Added instr_is_sse41(), instr_is_sse42(), and instr_is_sse4A().
+ - Added instr_is_reg_spill_or_restore().
**************************************************
diff --git a/core/arch/arm/mangle.c b/core/arch/arm/mangle.c
index 29178aa3a80..805f90fe54f 100644
--- a/core/arch/arm/mangle.c
+++ b/core/arch/arm/mangle.c
@@ -438,7 +438,7 @@ find_prior_scratch_reg_restore(dcontext_t *dcontext, instr_t *instr, reg_id_t *p
instr_is_label(prev) && instr_is_our_mangling(prev))
prev = instr_get_prev(prev);
if (prev != NULL &&
- instr_is_reg_spill_or_restore(dcontext, prev, &tls, &spill, prior_reg)) {
+ instr_is_DR_reg_spill_or_restore(dcontext, prev, &tls, &spill, prior_reg)) {
if (tls && !spill &&
*prior_reg >= SCRATCH_REG0 && *prior_reg <= SCRATCH_REG3)
return prev;
@@ -461,8 +461,8 @@ insert_save_to_tls_if_necessary(dcontext_t *dcontext, instrlist_t *ilist,
STATS_INC(non_mbr_spills);
prev = find_prior_scratch_reg_restore(dcontext, where, &prior_reg);
if (INTERNAL_OPTION(opt_mangle) > 0 && prev != NULL && prior_reg == reg) {
- ASSERT(instr_is_reg_spill_or_restore(dcontext, prev, &tls,
- &spill, &prior_reg) &&
+ ASSERT(instr_is_DR_reg_spill_or_restore(dcontext, prev, &tls,
+ &spill, &prior_reg) &&
tls && !spill && prior_reg == reg);
/* remove the redundant restore-spill pair */
instrlist_remove(ilist, prev);
@@ -710,7 +710,7 @@ mangle_syscall_arch(dcontext_t *dcontext, instrlist_t *ilist, uint flags,
* For now we assume that the kernel honors the calling convention
* and won't clobber callee-saved regs.
*/
- /* The instructions inserted here are checked in instr_is_reg_spill_or_restore
+ /* The instructions inserted here are checked in instr_is_DR_reg_spill_or_restore
* and translate_walk_restore, so any update here must be sync-ed there too.
*/
if (dr_reg_stolen != DR_REG_R10 && dr_reg_stolen != DR_REG_R11) {
@@ -809,7 +809,7 @@ mangle_add_predicated_fall_through(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *prev = instr_get_next(mangle_start);
for (; prev != next_instr; prev = instr_get_next(prev)) {
if (instr_is_app(prev) ||
- !instr_is_reg_spill_or_restore(dcontext, prev, NULL, NULL, NULL))
+ !instr_is_DR_reg_spill_or_restore(dcontext, prev, NULL, NULL, NULL))
instr_set_predicate(prev, pred);
}
}
diff --git a/core/arch/instr.h b/core/arch/instr.h
index e5e10a25986..e5acaae78ed 100644
--- a/core/arch/instr.h
+++ b/core/arch/instr.h
@@ -2691,9 +2691,26 @@ bool instr_is_tls_xcx_spill(instr_t *instr);
/* Pass REG_NULL to not care about the reg */
bool
instr_is_tls_restore(instr_t *instr, reg_id_t reg, ushort offs);
+
+DR_API
+/**
+ * Returns whether \p instr is a register spill or restore, whether it was
+ * created by dr_save_reg(), dr_restore_reg(), dr_insert_read_raw_tls(),
+ * dr_insert_write_raw_tls(), routines that call the aforementioned routines
+ * (e.g., dr_save_arith_flags()), or DR's own internal spills and restores.
+ * Returns information about the spill/restore in the OUT parameters.
+ * The returned \p offs is the raw offset in bytes from the TLS segment base,
+ * the stolen register base, or the thread-private context area.
+ */
bool
-instr_is_reg_spill_or_restore(dcontext_t *dcontext, instr_t *instr,
- bool *tls, bool *spill, reg_id_t *reg);
+instr_is_reg_spill_or_restore(void *drcontext, instr_t *instr,
+ bool *tls OUT, bool *spill OUT, reg_id_t *reg OUT,
+ uint *offs OUT);
+
+bool
+instr_is_DR_reg_spill_or_restore(void *drcontext, instr_t *instr,
+ bool *tls OUT, bool *spill OUT, reg_id_t *reg OUT);
+
#ifdef ARM
bool instr_reads_thread_register(instr_t *instr);
bool instr_is_stolen_reg_move(instr_t *instr, bool *save, reg_id_t *reg);
diff --git a/core/arch/instr_shared.c b/core/arch/instr_shared.c
index a3565e1a429..fbf0937dcb7 100644
--- a/core/arch/instr_shared.c
+++ b/core/arch/instr_shared.c
@@ -3240,20 +3240,24 @@ instr_check_mcontext_spill_restore(dcontext_t *dcontext, instr_t *instr,
#endif
}
-bool
-instr_is_reg_spill_or_restore(dcontext_t *dcontext, instr_t *instr,
- bool *tls, bool *spill, reg_id_t *reg)
+static bool
+instr_is_reg_spill_or_restore_ex(void *drcontext, instr_t *instr, bool DR_only,
+ bool *tls, bool *spill, reg_id_t *reg, uint *offs_out)
{
+ dcontext_t *dcontext = (dcontext_t *) drcontext;
int check_disp = 0; /* init to satisfy some compilers */
reg_id_t myreg;
- CLIENT_ASSERT(instr != NULL, "internal error: NULL argument");
+ CLIENT_ASSERT(instr != NULL, "invalid NULL argument");
if (reg == NULL)
reg = &myreg;
if (instr_check_tls_spill_restore(instr, spill, reg, &check_disp)) {
int offs = reg_spill_tls_offs(*reg);
- if (offs != -1 && check_disp == os_tls_offset((ushort)offs)) {
+ if (!DR_only ||
+ (offs != -1 && check_disp == os_tls_offset((ushort)offs))) {
if (tls != NULL)
*tls = true;
+ if (offs_out != NULL)
+ *offs_out = check_disp;
return true;
}
#ifdef ARM
@@ -3271,6 +3275,8 @@ instr_is_reg_spill_or_restore(dcontext_t *dcontext, instr_t *instr,
});
if (tls != NULL)
*tls = true;
+ if (offs_out != NULL)
+ *offs_out = check_disp;
return true;
}
#endif
@@ -3279,15 +3285,35 @@ instr_is_reg_spill_or_restore(dcontext_t *dcontext, instr_t *instr,
instr_check_mcontext_spill_restore(dcontext, instr, spill,
reg, &check_disp)) {
int offs = opnd_get_reg_dcontext_offs(dr_reg_fixer[*reg]);
- if (offs != -1 && check_disp == offs) {
+ if (!DR_only ||
+ (offs != -1 && check_disp == offs)) {
if (tls != NULL)
*tls = false;
+ if (offs_out != NULL)
+ *offs_out = check_disp;
return true;
}
}
return false;
}
+DR_API
+bool
+instr_is_reg_spill_or_restore(void *drcontext, instr_t *instr,
+ bool *tls, bool *spill, reg_id_t *reg, uint *offs)
+{
+ return instr_is_reg_spill_or_restore_ex(drcontext, instr, false,
+ tls, spill, reg, offs);
+}
+
+bool
+instr_is_DR_reg_spill_or_restore(void *drcontext, instr_t *instr,
+ bool *tls, bool *spill, reg_id_t *reg)
+{
+ return instr_is_reg_spill_or_restore_ex(drcontext, instr, true,
+ tls, spill, reg, NULL);
+}
+
/* N.B. : client meta routines (dr_insert_* etc.) should never use anything other
* then TLS_XAX_SLOT unless the client has specified a slot to use as we let the
* client use the rest. */
diff --git a/core/translate.c b/core/translate.c
index 3913a3dcfc1..0baeb71fa34 100644
--- a/core/translate.c
+++ b/core/translate.c
@@ -257,7 +257,7 @@ translate_walk_track(dcontext_t *tdcontext, instr_t *inst, translate_walk_t *wal
for (r = 0; r < REG_SPILL_NUM; r++)
walk->reg_spilled[r] = false;
}
- if (instr_is_reg_spill_or_restore(tdcontext, inst, &spill_tls, &spill, ®)) {
+ if (instr_is_DR_reg_spill_or_restore(tdcontext, inst, &spill_tls, &spill, ®)) {
r = reg - REG_START_SPILL;
ASSERT(r < REG_SPILL_NUM);
IF_ARM({
@@ -1637,7 +1637,7 @@ stress_test_recreate_state(dcontext_t *dcontext, fragment_t *f, instrlist_t *ili
*/
ASSERT(success_so_far /* ok to fail */ ||
(!res &&
- (instr_is_reg_spill_or_restore(dcontext, in, NULL, NULL, NULL) ||
+ (instr_is_DR_reg_spill_or_restore(dcontext, in, NULL, NULL, NULL) ||
(!instr_reads_memory(in) && !instr_writes_memory(in)))));
/* check that xsp and xcx are adjusted properly */
@@ -1651,7 +1651,7 @@ stress_test_recreate_state(dcontext_t *dcontext, fragment_t *f, instrlist_t *ili
instr_check_xsp_mangling(dcontext, in, &xsp_adjust);
if (xsp_adjust != 0)
LOG(THREAD, LOG_INTERP, 3, " xsp_adjust=%d\n", xsp_adjust);
- if (instr_is_reg_spill_or_restore(dcontext, in, NULL, &spill, ®) &&
+ if (instr_is_DR_reg_spill_or_restore(dcontext, in, NULL, &spill, ®) &&
reg == REG_XCX)
spill_xcx_outstanding = spill;
}