// 08 - runtime block entry log
// 10 - smc self-check
// 20 - runtime block entry counter
+// 40 - rcache checking
// 80 - branch cache statistics
// 100 - write trace
// 200 - compare trace
// 800 - state dump on exit
// {
#ifndef DRC_DEBUG
-#define DRC_DEBUG 0x0
+#define DRC_DEBUG 0//x8e7
#endif
#if DRC_DEBUG
OP_UNDEFINED,
};
-#define OP_ISBRANCH(op) (BITRANGE(OP_BRANCH, OP_BRANCH_RF) & BITMASK1(op))
+// XXX consider trap insns: OP_TRAPA, OP_UNDEFINED?
+#define OP_ISBRANCH(op) ((BITRANGE(OP_BRANCH, OP_BRANCH_RF)| BITMASK1(OP_RTE)) \
+ & BITMASK1(op))
#define OP_ISBRAUC(op) (BITMASK4(OP_BRANCH, OP_BRANCH_R, OP_BRANCH_RF, OP_RTE) \
& BITMASK1(op))
-#define OP_ISBRACND(op) (BITMASK2(OP_BRANCH_CT, OP_BRANCH_CF) & BITMASK1(op))
+#define OP_ISBRACND(op) (BITMASK3(OP_BRANCH_CT, OP_BRANCH_CF, OP_BRANCH_N) \
+ & BITMASK1(op))
#define OP_ISBRAIMM(op) (BITMASK3(OP_BRANCH, OP_BRANCH_CT, OP_BRANCH_CF) \
- & BITMASK1(op))
-#define OP_ISBRAIND(op) (BITMASK2(OP_BRANCH_R, OP_BRANCH_RF) & BITMASK1(op))
+ & BITMASK1(op))
+#define OP_ISBRAIND(op) (BITMASK3(OP_BRANCH_R, OP_BRANCH_RF, OP_RTE) \
+ & BITMASK1(op))
#ifdef DRC_SH2
}
#if (DRC_DEBUG & (8|256|512|1024)) || defined(PDB)
+#if (DRC_DEBUG & (256|512|1024))
static SH2 csh2[2][8];
+#endif
static void REGPARM(3) *sh2_drc_log_entry(void *block, SH2 *sh2, u32 sr)
{
if (block != NULL) {
typedef struct {
u8 hreg; // "host" reg
u8 flags:4; // TEMP or REG?
- u8 type:4;
+ u8 type:2; // CACHED or TEMP?
+ u8 ref:2; // ref counter
u16 stamp; // kind of a timestamp
u32 gregs; // "guest" reg mask
} cache_reg_t;
// used by handlers in worst case (currently 4).
// Register assignment goes by ABI convention. Caller save registers are TEMP,
// the others are either static or REG. SR must be static, R0 very recommended.
+// VBR, PC, PR must not be static (read from context in utils).
// TEMP registers first, REG last. alloc/evict algorithm depends on this.
-// The 1st TEMP must not be RET_REG on x86 (it uses temps for some insns).
+// The 1st TEMP must not be RET_REG on platforms using temps in insns (eg. x86).
// XXX shouldn't this be somehow defined in the code emitters?
#ifdef __arm__
#include "../drc/emit_arm.c"
};
// OABI/EABI: params: r0-r3, return: r0-r1, temp: r12,r14, saved: r4-r8,r10,r11
-// SP,PC: r13,r15 must not be used. saved: r9 (for platform use, e.g. on OSx)
+// SP,PC: r13,r15 must not be used. saved: r9 (for platform use, e.g. on ios)
static cache_reg_t cache_regs[] = {
{ 12, HRF_TEMP }, // temps
{ 14, HRF_TEMP },
gconst_t gconsts[ARRAY_SIZE(guest_regs)];
static int rcache_get_reg_(sh2_reg_e r, rc_gr_mode mode, int do_locking, int *hr);
+static inline int rcache_is_cached(sh2_reg_e r);
static void rcache_add_vreg_alias(int x, sh2_reg_e r);
static void rcache_remove_vreg_alias(int x, sh2_reg_e r);
+static void rcache_evict_vreg(int x);
+static void rcache_remap_vreg(int x);
#define RCACHE_DUMP(msg) { \
cache_reg_t *cp; \
guest_reg_t *gp; \
int i; \
printf("cache dump %s:\n",msg); \
- printf("cache_regs:\n"); \
+ printf(" cache_regs:\n"); \
for (i = 0; i < ARRAY_SIZE(cache_regs); i++) { \
cp = &cache_regs[i]; \
- if (cp->type != HR_FREE || cp->gregs) \
- printf("%d: hr=%d t=%d f=%x m=%x\n", i, cp->hreg, cp->type, cp->flags, cp->gregs); \
+ if (cp->type != HR_FREE || cp->gregs || (cp->flags & ~(HRF_REG|HRF_TEMP))) \
+ printf(" %d: hr=%d t=%d f=%x c=%d m=%x\n", i, cp->hreg, cp->type, cp->flags, cp->ref, cp->gregs); \
} \
- printf("guest_regs:\n"); \
+ printf(" guest_regs:\n"); \
for (i = 0; i < ARRAY_SIZE(guest_regs); i++) { \
gp = &guest_regs[i]; \
- if (gp->vreg != -1 || gp->sreg >= 0) \
- printf("%d: v=%d f=%x s=%d\n", i, gp->vreg, gp->flags, gp->sreg); \
+ if (gp->vreg != -1 || gp->sreg >= 0 || gp->flags) \
+ printf(" %d: v=%d f=%x s=%d c=%d\n", i, gp->vreg, gp->flags, gp->sreg, gp->cnst); \
+ } \
+ printf(" gconsts:\n"); \
+ for (i = 0; i < ARRAY_SIZE(gconsts); i++) { \
+ if (gconsts[i].gregs) \
+ printf(" %d: m=%x v=%x\n", i, gconsts[i].gregs, gconsts[i].val); \
} \
}
+#define RCACHE_CHECK(msg) { \
+ cache_reg_t *cp; \
+ guest_reg_t *gp; \
+ int i, x, d = 0; \
+ for (i = 0; i < ARRAY_SIZE(cache_regs); i++) { \
+ cp = &cache_regs[i]; \
+ if (cp->type == HR_FREE || cp->type == HR_TEMP) continue; \
+ /* check connectivity greg->vreg */ \
+ FOR_ALL_BITS_SET_DO(cp->gregs, x, \
+ if (guest_regs[x].vreg != i) \
+ { d = 1; printf("cache check v=%d r=%d not connected?\n",i,x); } \
+ ) \
+ } \
+ for (i = 0; i < ARRAY_SIZE(guest_regs); i++) { \
+ gp = &guest_regs[i]; \
+ if (gp->vreg != -1 && !(cache_regs[gp->vreg].gregs & (1 << i))) \
+ { d = 1; printf("cache check r=%d v=%d not connected?\n", i, gp->vreg); }\
+ if (gp->vreg != -1 && cache_regs[gp->vreg].type != HR_STATIC && cache_regs[gp->vreg].type != HR_CACHED) \
+ { d = 1; printf("cache check r=%d v=%d wrong type?\n", i, gp->vreg); }\
+ if ((gp->flags & GRF_CONST) && !(gconsts[gp->cnst].gregs & (1 << i))) \
+ { d = 1; printf("cache check r=%d c=%d not connected?\n", i, gp->cnst); }\
+ if ((gp->flags & GRF_CDIRTY) && (gp->vreg != -1 || !(gp->flags & GRF_CONST)) )\
+ { d = 1; printf("cache check r=%d CDIRTY?\n", i); } \
+ } \
+ for (i = 0; i < ARRAY_SIZE(gconsts); i++) { \
+ FOR_ALL_BITS_SET_DO(gconsts[i].gregs, x, \
+ if (guest_regs[x].cnst != i || !(guest_regs[x].flags & GRF_CONST)) \
+ { d = 1; printf("cache check c=%d v=%d not connected?\n",i,x); } \
+ ) \
+ } \
+ if (d) RCACHE_DUMP(msg) \
+/* else { \
+ printf("locked regs %s:\n",msg); \
+ for (i = 0; i < ARRAY_SIZE(cache_regs); i++) { \
+ cp = &cache_regs[i]; \
+ if (cp->flags & HRF_LOCKED) \
+ printf(" %d: hr=%d t=%d f=%x c=%d m=%x\n", i, cp->hreg, cp->type, cp->flags, cp->ref, cp->gregs); \
+ } \
+ } */ \
+}
+
#if PROPAGATE_CONSTANTS
static inline int gconst_alloc(sh2_reg_e r)
{
int i, n = -1;
for (i = 0; i < ARRAY_SIZE(gconsts); i++) {
- if (gconsts[i].gregs & (1 << r))
- gconsts[i].gregs &= ~(1 << r);
+ gconsts[i].gregs &= ~(1 << r);
if (gconsts[i].gregs == 0 && n < 0)
n = i;
}
if (n >= 0)
gconsts[n].gregs = (1 << r);
- else
+ else {
+ printf("all gconst buffers in use, aborting\n");
exit(1); // cannot happen - more constants than guest regs?
+ }
return n;
}
if (guest_regs[r].vreg >= 0)
rcache_remove_vreg_alias(guest_regs[r].vreg, r);
}
-
-static void gconst_copy(sh2_reg_e rd, sh2_reg_e rs)
-{
- if (guest_regs[rd].flags & GRF_CONST) {
- guest_regs[rd].flags &= ~(GRF_CONST|GRF_CDIRTY);
- gconsts[guest_regs[rd].cnst].gregs &= ~(1 << rd);
- }
- if (guest_regs[rs].flags & GRF_CONST) {
- guest_regs[rd].flags |= GRF_CONST;
- guest_regs[rd].cnst = guest_regs[rs].cnst;
- gconsts[guest_regs[rd].cnst].gregs |= (1 << rd);
- }
-}
#endif
static int gconst_get(sh2_reg_e r, u32 *val)
static int gconst_try_read(int vreg, sh2_reg_e r)
{
int i, x;
+
if (guest_regs[r].flags & GRF_CDIRTY) {
x = guest_regs[r].cnst;
emith_move_r_imm(cache_regs[vreg].hreg, gconsts[x].val);
FOR_ALL_BITS_SET_DO(gconsts[x].gregs, i,
{
- if (guest_regs[i].vreg >= 0 && i != r)
+ if (guest_regs[i].vreg >= 0 && guest_regs[i].vreg != vreg)
rcache_remove_vreg_alias(guest_regs[i].vreg, i);
- rcache_add_vreg_alias(vreg, i);
+ if (guest_regs[i].vreg < 0)
+ rcache_add_vreg_alias(vreg, i);
guest_regs[i].flags &= ~GRF_CDIRTY;
guest_regs[i].flags |= GRF_DIRTY;
});
+ if (cache_regs[vreg].type != HR_STATIC)
+ cache_regs[vreg].type = HR_CACHED;
+ cache_regs[vreg].flags |= HRF_DIRTY;
return 1;
}
return 0;
static void gconst_kill(sh2_reg_e r)
{
- if (guest_regs[r].flags &= ~(GRF_CONST|GRF_CDIRTY))
+ if (guest_regs[r].flags & (GRF_CONST|GRF_CDIRTY))
gconsts[guest_regs[r].cnst].gregs &= ~(1 << r);
guest_regs[r].flags &= ~(GRF_CONST|GRF_CDIRTY);
}
+static void gconst_copy(sh2_reg_e rd, sh2_reg_e rs)
+{
+ gconst_kill(rd);
+ if (guest_regs[rs].flags & GRF_CONST) {
+ guest_regs[rd].flags |= GRF_CONST;
+ if (guest_regs[rd].vreg < 0)
+ guest_regs[rd].flags |= GRF_CDIRTY;
+ guest_regs[rd].cnst = guest_regs[rs].cnst;
+ gconsts[guest_regs[rd].cnst].gregs |= (1 << rd);
+ }
+}
+
static void gconst_clean(void)
{
int i;
}
}
+
static u16 rcache_counter;
-static u32 rcache_static;
-static u32 rcache_locked;
-static u32 rcache_hint_soon;
-static u32 rcache_hint_late;
-static u32 rcache_hint_write;
-static u32 rcache_hint_clean;
-#define rcache_hint (rcache_hint_soon|rcache_hint_late)
+// SH2 register usage bitmasks
+static u32 rcache_regs_static; // statically allocated regs
+static u32 rcache_regs_now; // regs used in current insn
+static u32 rcache_regs_soon; // regs used in the next few insns
+static u32 rcache_regs_late; // regs used in later insns
+static u32 rcache_regs_discard; // regs overwritten without being used
+static u32 rcache_regs_clean; // regs needing cleaning
+// combination masks XXX this seems obscure
+#define rcache_regs_used (rcache_regs_soon|rcache_regs_late|rcache_regs_clean)
+#define rcache_regs_nowused (rcache_regs_now|rcache_regs_used)
+#define rcache_regs_nowsoon (rcache_regs_now|rcache_regs_soon)
+#define rcache_regs_soonclean (rcache_regs_soon|rcache_regs_clean)
+
+static void rcache_ref_vreg(int x)
+{
+ if (x >= 0) {
+ cache_regs[x].ref ++;
+ cache_regs[x].flags |= HRF_LOCKED;
+ }
+}
+
+static void rcache_unref_vreg(int x)
+{
+ if (x >= 0 && -- cache_regs[x].ref == 0) {
+ cache_regs[x].flags &= ~HRF_LOCKED;
+ }
+}
+
+static void rcache_free_vreg(int x)
+{
+ if (cache_regs[x].type != HR_STATIC)
+ cache_regs[x].type = HR_FREE;
+ cache_regs[x].flags &= (HRF_REG|HRF_TEMP);
+ cache_regs[x].gregs = 0;
+ cache_regs[x].ref = 0;
+}
static void rcache_unmap_vreg(int x)
{
int i;
FOR_ALL_BITS_SET_DO(cache_regs[x].gregs, i,
+ if (guest_regs[i].flags & GRF_DIRTY) {
+ // if a dirty reg is unmapped save its value to context
+ if (~rcache_regs_discard & (1 << i))
+ emith_ctx_write(cache_regs[x].hreg, i * 4);
+ guest_regs[i].flags &= ~GRF_DIRTY;
+ }
guest_regs[i].vreg = -1);
- if (cache_regs[x].type != HR_STATIC)
- cache_regs[x].type = HR_FREE;
- cache_regs[x].gregs = 0;
- cache_regs[x].flags &= (HRF_REG|HRF_TEMP);
+ rcache_free_vreg(x);
+}
+
+static void rcache_move_vreg(int d, int x)
+{
+ int i;
+
+ if (cache_regs[d].type != HR_STATIC)
+ cache_regs[d].type = HR_CACHED;
+ cache_regs[d].gregs = cache_regs[x].gregs;
+ cache_regs[d].flags &= (HRF_TEMP|HRF_REG);
+ cache_regs[d].flags |= cache_regs[x].flags & ~(HRF_TEMP|HRF_REG);
+ cache_regs[d].ref = 0;
+ cache_regs[d].stamp = cache_regs[x].stamp;
+ emith_move_r_r(cache_regs[d].hreg, cache_regs[x].hreg);
+ for (i = 0; i < ARRAY_SIZE(guest_regs); i++)
+ if (guest_regs[i].vreg == x)
+ guest_regs[i].vreg = d;
+ rcache_free_vreg(x);
}
static void rcache_clean_vreg(int x)
if (cache_regs[x].flags & HRF_DIRTY) { // writeback
cache_regs[x].flags &= ~HRF_DIRTY;
+ rcache_ref_vreg(x);
FOR_ALL_BITS_SET_DO(cache_regs[x].gregs, r,
if (guest_regs[r].flags & GRF_DIRTY) {
if (guest_regs[r].flags & GRF_STATIC) {
if (guest_regs[r].vreg != guest_regs[r].sreg) {
if (!(cache_regs[guest_regs[r].sreg].flags & HRF_LOCKED)) {
// statically mapped reg not in its sreg. move back to sreg
- rcache_clean_vreg(guest_regs[r].sreg);
- rcache_unmap_vreg(guest_regs[r].sreg);
- emith_move_r_r(cache_regs[guest_regs[r].sreg].hreg, cache_regs[guest_regs[r].vreg].hreg);
+ rcache_evict_vreg(guest_regs[r].sreg);
+ emith_move_r_r(cache_regs[guest_regs[r].sreg].hreg,
+ cache_regs[guest_regs[r].vreg].hreg);
rcache_remove_vreg_alias(x, r);
rcache_add_vreg_alias(guest_regs[r].sreg, r);
cache_regs[guest_regs[r].sreg].flags |= HRF_DIRTY;
} else {
// must evict since sreg is locked
- emith_ctx_write(cache_regs[x].hreg, r * 4);
+ if (~rcache_regs_discard & (1 << r))
+ emith_ctx_write(cache_regs[x].hreg, r * 4);
guest_regs[r].flags &= ~GRF_DIRTY;
- guest_regs[r].vreg = -1;
+ rcache_remove_vreg_alias(x, r);
}
- }
- } else if (~rcache_hint_write & (1 << r)) {
- emith_ctx_write(cache_regs[x].hreg, r * 4);
+ } else
+ cache_regs[x].flags |= HRF_DIRTY;
+ } else {
+ if (~rcache_regs_discard & (1 << r))
+ emith_ctx_write(cache_regs[x].hreg, r * 4);
guest_regs[r].flags &= ~GRF_DIRTY;
}
+ rcache_regs_clean &= ~(1 << r);
})
+ rcache_unref_vreg(x);
}
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after clean");
+#endif
}
static void rcache_add_vreg_alias(int x, sh2_reg_e r)
{
cache_regs[x].gregs |= (1 << r);
guest_regs[r].vreg = x;
+ if (cache_regs[x].type != HR_STATIC)
+ cache_regs[x].type = HR_CACHED;
}
static void rcache_remove_vreg_alias(int x, sh2_reg_e r)
{
cache_regs[x].gregs &= ~(1 << r);
- if (!cache_regs[x].gregs) {
+ if (!cache_regs[x].gregs)
// no reg mapped -> free vreg
- if (cache_regs[x].type != HR_STATIC)
- cache_regs[x].type = HR_FREE;
- cache_regs[x].flags &= (HRF_REG|HRF_TEMP);
- }
+ rcache_free_vreg(x);
guest_regs[r].vreg = -1;
}
static void rcache_evict_vreg(int x)
{
+#if REMAP_REGISTER
+ rcache_remap_vreg(x);
+#else
rcache_clean_vreg(x);
+#endif
rcache_unmap_vreg(x);
}
static void rcache_evict_vreg_aliases(int x, sh2_reg_e r)
{
- cache_regs[x].gregs &= ~(1 << r);
+ rcache_remove_vreg_alias(x, r);
rcache_evict_vreg(x);
- cache_regs[x].gregs = (1 << r);
- if (cache_regs[x].type != HR_STATIC)
- cache_regs[x].type = HR_CACHED;
- if (guest_regs[r].flags & GRF_DIRTY)
- cache_regs[x].flags |= HRF_DIRTY;
+ rcache_add_vreg_alias(x, r);
}
-static cache_reg_t *rcache_evict(void)
+static int rcache_allocate(int what, int minprio)
{
// evict reg with oldest stamp (only for HRF_REG, no temps)
int i, i_prio, oldest = -1, prio = 0;
u16 min_stamp = (u16)-1;
for (i = 0; i < ARRAY_SIZE(cache_regs); i++) {
- // consider only unlocked REG
- if (!(cache_regs[i].flags & HRF_REG) || (cache_regs[i].flags & HRF_LOCKED))
+ // consider only unlocked REG or non-TEMP
+ if (cache_regs[i].flags == 0 || (cache_regs[i].flags & HRF_LOCKED))
+ continue;
+ if ((what > 0 && !(cache_regs[i].flags & HRF_REG)) ||
+ (what == 0 && (cache_regs[i].flags & HRF_TEMP)) ||
+ (what < 0 && !(cache_regs[i].flags & HRF_TEMP)))
continue;
- if (cache_regs[i].type == HR_FREE || (cache_regs[i].type == HR_TEMP)) {
+ if (cache_regs[i].type == HR_FREE || cache_regs[i].type == HR_TEMP) {
+ // REG is free
+ prio = 6;
oldest = i;
break;
}
if (cache_regs[i].type == HR_CACHED) {
- if (rcache_locked & cache_regs[i].gregs)
+ if (rcache_regs_now & cache_regs[i].gregs)
// REGs needed for the current insn
i_prio = 1;
- else if (rcache_hint_soon & cache_regs[i].gregs)
- // REGs needed in some future insn
+ else if (rcache_regs_soon & cache_regs[i].gregs)
+ // REGs needed in the next insns
i_prio = 2;
- else if (rcache_hint_late & cache_regs[i].gregs)
+ else if (rcache_regs_late & cache_regs[i].gregs)
// REGs needed in some future insn
i_prio = 3;
- else if ((rcache_hint_write & cache_regs[i].gregs) != cache_regs[i].gregs)
- // REGs not needed soon
+ else if (!(~rcache_regs_discard & cache_regs[i].gregs))
+ // REGs not needed in the foreseeable future
i_prio = 4;
else
// REGs soon overwritten anyway
i_prio = 5;
-
if (prio < i_prio || (prio == i_prio && cache_regs[i].stamp < min_stamp)) {
min_stamp = cache_regs[i].stamp;
oldest = i;
}
}
- if (oldest == -1) {
- printf("no registers to evict, aborting\n");
- exit(1);
- }
+
+ if (prio < minprio || oldest == -1)
+ return -1;
if (cache_regs[oldest].type == HR_CACHED)
rcache_evict_vreg(oldest);
- cache_regs[oldest].type = HR_FREE;
- cache_regs[oldest].flags &= (HRF_TEMP|HRF_REG);
- cache_regs[oldest].gregs = 0;
+ else
+ rcache_free_vreg(oldest);
+
+ return oldest;
+}
+
+static int rcache_allocate_vreg(int needed)
+{
+ int x;
+
+ // get a free reg, but use temps only if r is not needed soon
+ for (x = ARRAY_SIZE(cache_regs) - 1; x >= 0; x--) {
+ if (cache_regs[x].flags && (cache_regs[x].type == HR_FREE ||
+ (cache_regs[x].type == HR_TEMP && !(cache_regs[x].flags & HRF_LOCKED))) &&
+ (!needed || (cache_regs[x].flags & HRF_REG)))
+ break;
+ }
+
+ if (x < 0)
+ x = rcache_allocate(1, 0);
+ return x;
+}
- return &cache_regs[oldest];
+static int rcache_allocate_nontemp(void)
+{
+ int x = rcache_allocate(0, 3);
+ return x;
+}
+
+static int rcache_allocate_temp(void)
+{
+ int x;
+
+ // use any free reg, but prefer TEMP regs
+ for (x = 0; x < ARRAY_SIZE(cache_regs); x++) {
+ if (cache_regs[x].flags && (cache_regs[x].type == HR_FREE ||
+ (cache_regs[x].type == HR_TEMP && !(cache_regs[x].flags & HRF_LOCKED))))
+ break;
+ }
+
+ if (x >= ARRAY_SIZE(cache_regs))
+ x = rcache_allocate(-1, 1);
+ if (x < 0) {
+ printf("no temp register available, aborting\n");
+ exit(1);
+ }
+ return x;
}
#if REMAP_REGISTER
// maps a host register to a REG
static int rcache_map_reg(sh2_reg_e r, int hr, int mode)
{
- int i;
+ int x, i;
gconst_kill(r);
// deal with statically mapped regs
if (mode == RC_GR_RMW && (guest_regs[r].flags & GRF_STATIC)) {
- if (guest_regs[r].vreg == guest_regs[r].sreg) {
+ x = guest_regs[r].sreg;
+ if (guest_regs[r].vreg == x) {
// STATIC in its sreg with no aliases, and some processing pending
- if (cache_regs[guest_regs[r].vreg].gregs == 1 << r)
- return cache_regs[guest_regs[r].vreg].hreg;
- } else if (!cache_regs[guest_regs[r].sreg].gregs)
+ if (cache_regs[x].gregs == 1 << r)
+ return cache_regs[x].hreg;
+ } else if (cache_regs[x].type == HR_FREE ||
+ (cache_regs[x].type == HR_TEMP && !(cache_regs[x].flags & HRF_LOCKED)))
// STATIC not in its sreg, with sreg available -> move it
i = guest_regs[r].sreg;
}
if (guest_regs[r].vreg >= 0)
rcache_remove_vreg_alias(guest_regs[r].vreg, r);
if (cache_regs[i].type == HR_CACHED)
- rcache_unmap_vreg(i);
+ rcache_evict_vreg(i);
// set new mappping
if (cache_regs[i].type != HR_STATIC)
cache_regs[i].type = HR_CACHED;
cache_regs[i].gregs = 1 << r;
cache_regs[i].flags &= (HRF_TEMP|HRF_REG);
+ cache_regs[i].ref = 0;
cache_regs[i].stamp = ++rcache_counter;
- cache_regs[i].flags |= HRF_DIRTY|HRF_LOCKED;
+ cache_regs[i].flags |= HRF_DIRTY;
+ rcache_ref_vreg(i);
guest_regs[r].flags |= GRF_DIRTY;
guest_regs[r].vreg = i;
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after map");
+#endif
return cache_regs[i].hreg;
}
-// remap vreg from a TEMP to a REG if it is hinted (upcoming TEMP invalidation)
-static void rcache_remap_vreg(int r)
+// remap vreg from a TEMP to a REG if it will be used (upcoming TEMP invalidation)
+static void rcache_remap_vreg(int x)
{
- int i, j, free = -1, cached = -1, hinted = -1;
- u16 min_stamp_cached = (u16)-1, min_stamp_hinted = -1;
+ int d;
- // r must be a vreg
- if (cache_regs[r].type != HR_CACHED)
+ // x must be a cached vreg
+ if (cache_regs[x].type != HR_CACHED && cache_regs[x].type != HR_STATIC)
return;
- // if r is already a REG or isn't used, clean here to avoid data loss on inval
- if ((cache_regs[r].flags & HRF_REG) || !(rcache_hint & cache_regs[r].gregs)) {
- rcache_clean_vreg(r);
+ // don't do it if x is already a REG or isn't used or to be cleaned anyway
+ if ((cache_regs[x].flags & HRF_REG) ||
+ !(rcache_regs_used & ~rcache_regs_clean & cache_regs[x].gregs)) {
+ // clean here to avoid data loss on invalidation
+ rcache_clean_vreg(x);
return;
}
- // find REG, either free or unused temp or oldest cached
- for (i = 0; i < ARRAY_SIZE(cache_regs) && free < 0; i++) {
- if ((cache_regs[i].flags & HRF_TEMP) || (cache_regs[i].flags & HRF_LOCKED))
- continue;
- if (cache_regs[i].type == HR_FREE || cache_regs[i].type == HR_TEMP)
- free = i;
- if (cache_regs[i].type == HR_CACHED && !(rcache_hint & cache_regs[i].gregs)) {
- if (cache_regs[i].stamp < min_stamp_cached) {
- min_stamp_cached = cache_regs[i].stamp;
- cached = i;
- }
- }
- if (cache_regs[i].type == HR_CACHED && !(rcache_hint_soon & cache_regs[i].gregs)
- && (rcache_hint_soon & cache_regs[r].gregs))
- if (cache_regs[i].stamp < min_stamp_hinted) {
- min_stamp_hinted = cache_regs[i].stamp;
- hinted = i;
- }
+ if (cache_regs[x].flags & HRF_LOCKED) {
+ printf("remap vreg %d is locked\n", x);
+ exit(1);
}
- if (free >= 0) {
- i = free;
- } else if (cached >= 0 && cached != r) {
- i = cached;
- rcache_evict_vreg(i);
- } else if (hinted >= 0 && hinted != r) {
- i = hinted;
- rcache_evict_vreg(i);
- } else {
- rcache_clean_vreg(r);
+ // allocate a non-TEMP vreg
+ rcache_ref_vreg(x); // lock to avoid evicting x
+ d = rcache_allocate_nontemp();
+ rcache_unref_vreg(x);
+ if (d < 0) {
+ rcache_clean_vreg(x);
return;
}
- // set new mapping and remove old one
- cache_regs[i].type = HR_CACHED;
- cache_regs[i].gregs = cache_regs[r].gregs;
- cache_regs[i].flags &= (HRF_TEMP|HRF_REG);
- cache_regs[i].flags |= cache_regs[r].flags & ~(HRF_TEMP|HRF_REG);
- cache_regs[i].stamp = cache_regs[r].stamp;
- emith_move_r_r(cache_regs[i].hreg, cache_regs[r].hreg);
- for (j = 0; j < ARRAY_SIZE(guest_regs); j++)
- if (guest_regs[j].vreg == r)
- guest_regs[j].vreg = i;
- cache_regs[r].type = HR_FREE;
- cache_regs[r].flags &= (HRF_TEMP|HRF_REG);
- cache_regs[r].gregs = 0;
+ // move vreg to new location
+ rcache_move_vreg(d, x);
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after remap");
+#endif
+}
+#endif
+
+#if ALIAS_REGISTERS
+static void rcache_alias_vreg(sh2_reg_e rd, sh2_reg_e rs)
+{
+ int x;
+
+ // if s isn't constant, it must be in cache for aliasing
+ if (!gconst_check(rs))
+ rcache_get_reg_(rs, RC_GR_READ, 0, NULL);
+
+ // if d and s are not already aliased
+ x = guest_regs[rs].vreg;
+ if (guest_regs[rd].vreg != x) {
+ // remove possible old mapping of dst
+ if (guest_regs[rd].vreg >= 0)
+ rcache_remove_vreg_alias(guest_regs[rd].vreg, rd);
+ // make dst an alias of src
+ if (x >= 0)
+ rcache_add_vreg_alias(x, rd);
+ // if d is now in cache, it must be dirty
+ if (guest_regs[rd].vreg >= 0) {
+ x = guest_regs[rd].vreg;
+ cache_regs[x].flags |= HRF_DIRTY;
+ guest_regs[rd].flags |= GRF_DIRTY;
+ }
+ }
+
+ gconst_copy(rd, rs);
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after alias");
+#endif
}
#endif
// note: must not be called when doing conditional code
static int rcache_get_reg_(sh2_reg_e r, rc_gr_mode mode, int do_locking, int *hr)
{
- cache_reg_t *tr = NULL;
- int i, h, split = -1;
+ int src, dst, ali;
+ cache_reg_t *tr;
- rcache_counter++;
+ dst = src = guest_regs[r].vreg;
- // maybe already cached?
- // if so, prefer against gconst (they must be in sync)
- i = guest_regs[r].vreg;
- if ((guest_regs[r].flags & GRF_STATIC) && i != guest_regs[r].sreg &&
+ rcache_ref_vreg(src); // lock to avoid evicting src
+ // good opportunity to relocate a remapped STATIC?
+ if ((guest_regs[r].flags & GRF_STATIC) && src != guest_regs[r].sreg &&
!(cache_regs[guest_regs[r].sreg].flags & HRF_LOCKED) &&
- (i < 0 || mode != RC_GR_READ) &&
- !((rcache_hint_soon|rcache_locked) & cache_regs[guest_regs[r].sreg].gregs)) {
- // good opportunity to relocate a remapped STATIC
- h = guest_regs[r].sreg;
- rcache_evict_vreg(h);
- tr = &cache_regs[h];
- tr->gregs = 1 << r;
- if (i >= 0) {
- if (mode != RC_GR_WRITE) {
- if (hr)
- *hr = cache_regs[i].hreg;
- else
- emith_move_r_r(cache_regs[h].hreg, cache_regs[i].hreg);
- hr = NULL;
- }
- rcache_remove_vreg_alias(guest_regs[r].vreg, r);
- } else if (mode != RC_GR_WRITE) {
- if (gconst_try_read(h, r)) {
- tr->flags |= HRF_DIRTY;
- guest_regs[r].flags |= GRF_DIRTY;
- } else
- emith_ctx_read(tr->hreg, r * 4);
- }
- guest_regs[r].vreg = guest_regs[r].sreg;
- goto end;
- } else if (i >= 0) {
- if (mode == RC_GR_READ || !(cache_regs[i].gregs & ~(1 << r))) {
- // either only reading, or no multiple mapping
- tr = &cache_regs[i];
- goto end;
- }
- // split if aliases needed rsn, or already locked, or r is STATIC in sreg
- if (((rcache_hint|rcache_locked) & cache_regs[i].gregs & ~(1 << r)) ||
- (cache_regs[i].flags & HRF_LOCKED) ||
- (cache_regs[i].type == HR_STATIC && !(guest_regs[r].flags & GRF_STATIC))) {
- // need to split up. take reg out here to avoid unnecessary writebacks
- rcache_remove_vreg_alias(i, r);
- split = i;
- } else {
- // aliases not needed anytime soon, remove them
- // XXX split aliases away if writing and static and not locked and hinted?
- rcache_evict_vreg_aliases(i, r);
- tr = &cache_regs[i];
- goto end;
+ (src < 0 || mode != RC_GR_READ) &&
+ !(rcache_regs_nowsoon & cache_regs[guest_regs[r].sreg].gregs)) {
+ dst = guest_regs[r].sreg;
+ rcache_evict_vreg(dst);
+ } else if (dst < 0) {
+ // allocate a cache register
+ if ((dst = rcache_allocate_vreg(rcache_regs_nowsoon & (1 << r))) < 0) {
+ printf("no registers to evict, aborting\n");
+ exit(1);
}
}
-
- // get a free reg, but use temps only if r is not needed soon
- for (i = ARRAY_SIZE(cache_regs) - 1; i >= 0; i--) {
- if ((cache_regs[i].type == HR_FREE ||
- (cache_regs[i].type == HR_TEMP && !(cache_regs[i].flags & HRF_LOCKED))) &&
- (!(rcache_hint & (1 << r)) || (cache_regs[i].flags & HRF_REG))) {
- tr = &cache_regs[i];
- break;
+ tr = &cache_regs[dst];
+ tr->stamp = rcache_counter;
+ rcache_unref_vreg(src);
+ // remove r from src
+ if (src >= 0 && src != dst)
+ rcache_remove_vreg_alias(src, r);
+
+ // if r has a constant it may have aliases
+ if (mode != RC_GR_WRITE && gconst_try_read(dst, r))
+ src = dst;
+
+ // if r will be modified, check for aliases being needed rsn
+ ali = tr->gregs & ~(1 << r);
+ if (mode != RC_GR_READ && src == dst && ali) {
+ int x = -1;
+ if (rcache_regs_nowsoon & ali) {
+ if (tr->type == HR_STATIC && guest_regs[r].sreg == dst &&
+ !(tr->flags & HRF_LOCKED)) {
+ // split aliases if r is STATIC in sreg and dst isn't already locked
+ rcache_ref_vreg(dst); // lock to avoid evicting dst
+ if ((x = rcache_allocate_vreg(rcache_regs_nowsoon & ali)) >= 0) {
+ src = x;
+ rcache_move_vreg(src, dst);
+ }
+ rcache_unref_vreg(dst);
+ } else {
+ // split r
+ rcache_ref_vreg(src); // lock to avoid evicting src
+ if ((x = rcache_allocate_vreg(rcache_regs_nowsoon & (1 << r))) >= 0) {
+ dst = x;
+ tr = &cache_regs[dst];
+ tr->stamp = rcache_counter;
+ }
+ rcache_unref_vreg(src);
+ }
}
+ if (x < 0)
+ // aliases not needed or no vreg available, remove them
+ rcache_evict_vreg_aliases(dst, r);
+ else if (src != dst)
+ rcache_remove_vreg_alias(src, r);
}
- if (!tr)
- tr = rcache_evict();
-
- tr->type = HR_CACHED;
- tr->gregs = 1 << r;
- guest_regs[r].vreg = tr - cache_regs;
-
- if (mode != RC_GR_WRITE) {
- if (gconst_try_read(guest_regs[r].vreg, r)) {
- tr->flags |= HRF_DIRTY;
- guest_regs[r].flags |= GRF_DIRTY;
- } else if (split >= 0) {
- if (hr) {
- cache_regs[split].flags |= HRF_LOCKED;
- *hr = cache_regs[split].hreg;
- hr = NULL;
- } else if (tr->hreg != cache_regs[split].hreg)
- emith_move_r_r(tr->hreg, cache_regs[split].hreg);
- } else
- emith_ctx_read(tr->hreg, r * 4);
- }
+ // assign r to dst
+ rcache_add_vreg_alias(dst, r);
-end:
- if (hr)
- *hr = tr->hreg;
+ // handle dst register transfer
+ if (src < 0 && mode != RC_GR_WRITE)
+ emith_ctx_read(tr->hreg, r * 4);
+ if (hr) {
+ *hr = (src >= 0 ? cache_regs[src].hreg : tr->hreg);
+ rcache_ref_vreg(reg_map_host[*hr]);
+ } else if (src >= 0 && cache_regs[src].hreg != tr->hreg)
+ emith_move_r_r(tr->hreg, cache_regs[src].hreg);
+
+ // housekeeping
if (do_locking)
- tr->flags |= HRF_LOCKED;
- tr->stamp = rcache_counter;
+ rcache_ref_vreg(dst);
if (mode != RC_GR_READ) {
tr->flags |= HRF_DIRTY;
guest_regs[r].flags |= GRF_DIRTY;
gconst_kill(r);
}
-
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after getreg");
+#endif
return tr->hreg;
}
static int rcache_get_tmp(void)
{
- cache_reg_t *tr = NULL;
int i;
- // use any free reg, but prefer TEMP regs
- for (i = 0; i < ARRAY_SIZE(cache_regs); i++) {
- if (cache_regs[i].type == HR_FREE ||
- (cache_regs[i].type == HR_TEMP && !(cache_regs[i].flags & HRF_LOCKED))) {
- tr = &cache_regs[i];
- break;
- }
- }
-
- if (!tr)
- tr = rcache_evict();
+ i = rcache_allocate_temp();
+ rcache_ref_vreg(i);
- tr->type = HR_TEMP;
- tr->flags |= HRF_LOCKED;
- return tr->hreg;
+ cache_regs[i].type = HR_TEMP;
+ return cache_regs[i].hreg;
}
-static int rcache_get_hr_id(int hr)
+static int rcache_get_vreg_hr(int hr)
{
int i;
i = reg_map_host[hr];
- if (i < 0) // can't happen
+ if (i < 0 || (cache_regs[i].flags & HRF_LOCKED)) {
+ printf("host register %d is locked\n", hr);
exit(1);
+ }
-#if REMAP_REGISTER
- if (cache_regs[i].type == HR_CACHED)
- rcache_remap_vreg(i);
-#endif
if (cache_regs[i].type == HR_CACHED)
rcache_evict_vreg(i);
else if (cache_regs[i].type == HR_TEMP && (cache_regs[i].flags & HRF_LOCKED)) {
return i;
}
-static int rcache_get_arg_id(int arg)
+static int rcache_get_vreg_arg(int arg)
{
int hr = 0;
host_arg2reg(hr, arg);
- return rcache_get_hr_id(hr);
+ return rcache_get_vreg_hr(hr);
}
// get a reg to be used as function arg
static int rcache_get_tmp_arg(int arg)
{
- int id = rcache_get_arg_id(arg);
- cache_regs[id].type = HR_TEMP;
- cache_regs[id].flags |= HRF_LOCKED;
+ int x = rcache_get_vreg_arg(arg);
+ cache_regs[x].type = HR_TEMP;
+ rcache_ref_vreg(x);
- return cache_regs[id].hreg;
+ return cache_regs[x].hreg;
}
// ... as return value after a call
static int rcache_get_tmp_ret(void)
{
- int id = rcache_get_hr_id(RET_REG);
- cache_regs[id].type = HR_TEMP;
- cache_regs[id].flags |= HRF_LOCKED;
+ int x = rcache_get_vreg_hr(RET_REG);
+ cache_regs[x].type = HR_TEMP;
+ rcache_ref_vreg(x);
- return cache_regs[id].hreg;
+ return cache_regs[x].hreg;
}
// same but caches a reg if access is readonly (announced by hr being NULL)
static int rcache_get_reg_arg(int arg, sh2_reg_e r, int *hr)
{
- int i, srcr, dstr, dstid;
- int dirty = 0, src_dirty = 0, is_const = 0, is_cached = 0;
+ int i, srcr, dstr, dstid, keep;
u32 val;
host_arg2reg(dstr, arg);
i = guest_regs[r].vreg;
if (i >= 0 && cache_regs[i].type == HR_CACHED && cache_regs[i].hreg == dstr)
- // r is already in arg
+ // r is already in arg, avoid evicting
dstid = i;
else
- dstid = rcache_get_arg_id(arg);
+ dstid = rcache_get_vreg_arg(arg);
dstr = cache_regs[dstid].hreg;
- if (rcache_hint & (1 << r)) {
+ if (rcache_is_cached(r)) {
// r is needed later on anyway
srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL);
- is_cached = (cache_regs[reg_map_host[srcr]].type == HR_CACHED);
- } else if (!(rcache_hint_clean & (1 << r)) &&
- (guest_regs[r].flags & GRF_CDIRTY) && gconst_get(r, &val)) {
+ keep = 1;
+ } else if ((guest_regs[r].flags & GRF_CDIRTY) && gconst_get(r, &val)) {
// r has an uncomitted const - load into arg, but keep constant uncomitted
srcr = dstr;
- is_const = 1;
- } else if ((i = guest_regs[r].vreg) >= 0) {
- // maybe already cached?
- srcr = cache_regs[i].hreg;
- is_cached = (cache_regs[reg_map_host[srcr]].type == HR_CACHED);
+ emith_move_r_imm(srcr, val);
+ keep = 0;
} else {
- // must read either const or from ctx
+ // must read from ctx
srcr = dstr;
- if (rcache_static & (1 << r))
- srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL);
- else if (gconst_try_read(dstid, r))
- dirty = 1;
- else
- emith_ctx_read(srcr, r * 4);
+ emith_ctx_read(srcr, r * 4);
+ keep = 1;
}
- if (is_cached) {
- i = reg_map_host[srcr];
- if (srcr == dstr) { // evict aliases here since it is reallocated below
- if (guest_regs[r].flags & GRF_STATIC) // move STATIC back to its sreg
- rcache_clean_vreg(guest_regs[r].vreg);
-#if REMAP_REGISTER
- rcache_remap_vreg(i);
-#endif
- if (cache_regs[i].type == HR_CACHED)
- rcache_evict_vreg(i);
- }
- else if (hr != NULL) // must lock srcr if not copied here
- cache_regs[i].flags |= HRF_LOCKED;
- if (guest_regs[r].flags & GRF_DIRTY)
- src_dirty = 1;
- }
+ if (cache_regs[dstid].type == HR_CACHED)
+ rcache_evict_vreg(dstid);
cache_regs[dstid].type = HR_TEMP;
- if (is_const) {
- // uncomitted constant
- emith_move_r_imm(srcr, val);
- } else if (dstr != srcr) {
- // arg is a copy of cached r
- if (hr == NULL)
+ if (hr == NULL) {
+ if (dstr != srcr)
+ // arg is a copy of cached r
emith_move_r_r(dstr, srcr);
- } else if (hr != NULL) {
- // caller will modify arg, so it will soon be out of sync with r
- if (dirty || src_dirty) {
- if (~rcache_hint_write & (1 << r)) {
- emith_ctx_write(dstr, r * 4); // must clean since arg will be modified
- guest_regs[r].flags &= ~GRF_DIRTY;
- }
- }
+ else if (keep && guest_regs[r].vreg < 0)
+ // keep arg as vreg for r
+ rcache_add_vreg_alias(dstid, r);
} else {
- // keep arg as vreg for r
- cache_regs[dstid].type = HR_CACHED;
- if (guest_regs[r].vreg < 0) {
- cache_regs[dstid].gregs = 1 << r;
- guest_regs[r].vreg = dstid;
- }
- if (dirty || src_dirty) { // mark as modifed for cleaning later on
- cache_regs[dstid].flags |= HRF_DIRTY;
- guest_regs[r].flags |= GRF_DIRTY;
- }
- }
-
- if (hr)
*hr = srcr;
+ if (dstr != srcr) // must lock srcr if not copied here
+ rcache_ref_vreg(reg_map_host[srcr]);
+ }
cache_regs[dstid].stamp = ++rcache_counter;
- cache_regs[dstid].flags |= HRF_LOCKED;
+ rcache_ref_vreg(dstid);
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after getarg");
+#endif
return dstr;
}
static void rcache_free_tmp(int hr)
{
int i = reg_map_host[hr];
+
if (i < 0 || cache_regs[i].type != HR_TEMP) {
printf("rcache_free_tmp fail: #%i hr %d, type %d\n", i, hr, cache_regs[i].type);
- return;
+ exit(1);
}
- cache_regs[i].type = HR_FREE;
- cache_regs[i].flags &= (HRF_REG|HRF_TEMP);
+ rcache_free_vreg(i);
}
// saves temporary result either in REG or in drctmp
static int rcache_save_tmp(int hr)
{
- int i, free = -1, cached = -1;
- u16 min_stamp = (u16)-1;
+ int i;
// find REG, either free or unlocked temp or oldest non-hinted cached
- for (i = 0; i < ARRAY_SIZE(cache_regs) && free < 0; i++) {
- if ((cache_regs[i].flags & HRF_TEMP) || (cache_regs[i].flags & HRF_LOCKED))
- continue;
- if (cache_regs[i].type == HR_FREE || cache_regs[i].type == HR_TEMP)
- free = i;
- if (cache_regs[i].type == HR_CACHED &&
- !((rcache_hint | rcache_locked) & cache_regs[i].gregs)) {
- if (cache_regs[i].stamp < min_stamp) {
- min_stamp = cache_regs[i].stamp;
- cached = i;
- }
- }
- }
-
- if (free >= 0)
- i = free;
- else if (cached >= 0) {
- i = cached;
- rcache_evict_vreg(i);
- } else {
+ i = rcache_allocate_nontemp();
+ if (i < 0) {
// if none is available, store in drctmp
emith_ctx_write(hr, offsetof(SH2, drc_tmp));
rcache_free_tmp(hr);
cache_regs[i].type = HR_CACHED;
cache_regs[i].gregs = 0; // not storing any guest register
cache_regs[i].flags &= (HRF_TEMP|HRF_REG);
- cache_regs[i].flags |= HRF_LOCKED;
+ cache_regs[i].ref = 0;
cache_regs[i].stamp = ++rcache_counter;
+ rcache_ref_vreg(i);
emith_move_r_r(cache_regs[i].hreg, hr);
rcache_free_tmp(hr);
return i;
}
-static int rcache_restore_tmp(int r)
+static int rcache_restore_tmp(int x)
{
int hr;
// find REG with tmp store: cached but with no gregs
- if (r >= 0) {
- if (cache_regs[r].type != HR_CACHED || cache_regs[r].gregs) {
- printf("invalid tmp storage %d\n", r);
+ if (x >= 0) {
+ if (cache_regs[x].type != HR_CACHED || cache_regs[x].gregs) {
+ printf("invalid tmp storage %d\n", x);
exit(1);
}
// found, transform to a TEMP
- cache_regs[r].type = HR_TEMP;
- cache_regs[r].flags |= HRF_LOCKED;
- return cache_regs[r].hreg;
+ cache_regs[x].type = HR_TEMP;
+ return cache_regs[x].hreg;
}
// if not available, create a TEMP store and fetch from drctmp
return hr;
}
-static void rcache_unlock(int hr)
+static void rcache_free(int hr)
{
- if (hr >= 0) {
- cache_regs[hr].flags &= ~HRF_LOCKED;
- rcache_locked &= ~cache_regs[hr].gregs;
+ int x = reg_map_host[hr];
+ if (cache_regs[x].type == HR_TEMP)
+ rcache_free_tmp(hr);
+ else
+ rcache_unref_vreg(x);
+}
+
+static void rcache_unlock(int x)
+{
+ if (x >= 0) {
+ cache_regs[x].flags &= ~HRF_LOCKED;
+ cache_regs[x].ref = 0;
+// rcache_regs_now &= ~cache_regs[x].gregs;
}
}
static void rcache_unlock_all(void)
{
int i;
- for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
+ for (i = 0; i < ARRAY_SIZE(cache_regs); i++) {
cache_regs[i].flags &= ~HRF_LOCKED;
+ cache_regs[i].ref = 0;
+ }
}
-static inline void rcache_set_locked(u32 mask)
-{
- rcache_locked = mask & ~rcache_static;
-}
-
-static inline void rcache_set_hint_soon(u32 mask)
+static inline void rcache_set_usage_now(u32 mask)
{
- rcache_hint_soon = mask & ~rcache_static;
+ rcache_regs_now = mask;
}
-static inline void rcache_set_hint_late(u32 mask)
+static inline void rcache_set_usage_soon(u32 mask)
{
- rcache_hint_late = mask & ~rcache_static;
+ rcache_regs_soon = mask;
}
-static inline void rcache_set_hint_write(u32 mask)
+static inline void rcache_set_usage_late(u32 mask)
{
- rcache_hint_write = mask & ~rcache_static;
+ rcache_regs_late = mask;
}
-static inline int rcache_is_hinted(sh2_reg_e r)
+static inline void rcache_set_usage_discard(u32 mask)
{
- // consider static REGs as always hinted, since they are always there
- return ((rcache_hint | rcache_static) & (1 << r));
+ rcache_regs_discard = mask;
}
static inline int rcache_is_cached(sh2_reg_e r)
{
- // consider static REGs as always hinted, since they are always there
- return (guest_regs[r].vreg >= 0);
+ // is r in cache or needed RSN?
+ return (guest_regs[r].vreg >= 0 || (rcache_regs_soonclean & (1 << r)));
}
static inline int rcache_is_hreg_used(int hr)
(cache_regs[x].type != HR_TEMP || (cache_regs[x].flags & HRF_LOCKED));
}
-static inline u32 rcache_used_hreg_mask(void)
+static inline u32 rcache_used_hregs_mask(void)
{
u32 mask = 0;
int i;
(cache_regs[i].type != HR_TEMP || (cache_regs[i].flags & HRF_LOCKED)))
mask |= 1 << cache_regs[i].hreg;
- return mask & ~rcache_static;
+ return mask;
}
static inline u32 rcache_dirty_mask(void)
return mask;
}
-static inline u32 rcache_reg_mask(void)
+static inline u32 rcache_cached_mask(void)
{
u32 mask = 0;
int i;
for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
- if (cache_regs[i].type == HR_CACHED)
+ if (cache_regs[i].type == HR_CACHED || cache_regs[i].type == HR_STATIC)
mask |= cache_regs[i].gregs;
return mask;
{
int i;
+ rcache_regs_clean = (1 << ARRAY_SIZE(guest_regs)) - 1;
for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
- if (cache_regs[i].type == HR_CACHED && (cache_regs[i].flags & HRF_TEMP))
+ if (cache_regs[i].type == HR_CACHED && (cache_regs[i].flags & HRF_TEMP)) {
+ rcache_unlock(i);
#if REMAP_REGISTER
rcache_remap_vreg(i);
#else
rcache_clean_vreg(i);
#endif
+ }
+ rcache_regs_clean = 0;
}
-static void rcache_clean_mask(u32 mask)
+static void rcache_clean_masked(u32 mask)
{
- int i;
+ int i, r, hr;
- if (!(mask &= ~rcache_static))
+ if (!(mask &= ~rcache_regs_static))
return;
- rcache_hint_clean |= mask;
-
- // clean only vregs where all aliases are covered by the mask
+ rcache_regs_clean |= mask;
+
+ // clean constants where all aliases are covered by the mask
+ for (i = 0; i < ARRAY_SIZE(gconsts); i++)
+ if ((gconsts[i].gregs & mask) && !(gconsts[i].gregs & ~mask)) {
+ FOR_ALL_BITS_SET_DO(gconsts[i].gregs, r,
+ if (guest_regs[r].flags & GRF_CDIRTY) {
+ hr = rcache_get_reg_(r, RC_GR_READ, 0, NULL);
+ rcache_clean_vreg(reg_map_host[hr]);
+ break;
+ });
+ }
+ // clean vregs where all aliases are covered by the mask
for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
- if (cache_regs[i].type == HR_CACHED &&
+ if ((cache_regs[i].type == HR_CACHED || cache_regs[i].type == HR_STATIC) &&
(cache_regs[i].gregs & mask) && !(cache_regs[i].gregs & ~mask))
rcache_clean_vreg(i);
}
int i;
gconst_clean();
+ rcache_regs_clean = (1 << ARRAY_SIZE(guest_regs)) - 1;
for (i = ARRAY_SIZE(cache_regs)-1; i >= 0; i--)
if (cache_regs[i].type == HR_CACHED || cache_regs[i].type == HR_STATIC)
rcache_clean_vreg(i);
+
+ // relocate statics to their sregs (necessary before conditional jumps)
+ for (i = 0; i < ARRAY_SIZE(guest_regs); i++) {
+ if ((guest_regs[i].flags & GRF_STATIC) &&
+ guest_regs[i].vreg != guest_regs[i].sreg) {
+ rcache_ref_vreg(guest_regs[i].vreg);
+ rcache_evict_vreg(guest_regs[i].sreg);
+ rcache_unref_vreg(guest_regs[i].vreg);
+ if (guest_regs[i].vreg < 0)
+ emith_ctx_read(cache_regs[guest_regs[i].sreg].hreg, i*4);
+ else
+ emith_move_r_r(cache_regs[guest_regs[i].sreg].hreg,
+ cache_regs[guest_regs[i].vreg].hreg);
+ cache_regs[guest_regs[i].sreg].gregs = 1 << i;
+ cache_regs[guest_regs[i].sreg].flags |= HRF_DIRTY;
+ guest_regs[i].flags |= GRF_DIRTY;
+ guest_regs[i].vreg = guest_regs[i].sreg;
+ }
+ }
+ rcache_regs_clean = 0;
}
static void rcache_invalidate_tmp(void)
for (i = 0; i < ARRAY_SIZE(cache_regs); i++) {
if (cache_regs[i].flags & HRF_TEMP) {
+ rcache_unlock(i);
if (cache_regs[i].type == HR_CACHED)
- rcache_unmap_vreg(i);
- cache_regs[i].type = HR_FREE;
- cache_regs[i].flags &= (HRF_TEMP|HRF_REG);
- cache_regs[i].gregs = 0;
+ rcache_evict_vreg(i);
+ else
+ rcache_free_vreg(i);
}
}
}
static void rcache_invalidate(void)
{
int i;
-
gconst_invalidate();
- for (i = 0; i < ARRAY_SIZE(cache_regs); i++) {
- cache_regs[i].flags &= (HRF_TEMP|HRF_REG);
- if (cache_regs[i].type != HR_STATIC)
- cache_regs[i].type = HR_FREE;
- cache_regs[i].gregs = 0;
- }
+ for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
+ rcache_free_vreg(i);
for (i = 0; i < ARRAY_SIZE(guest_regs); i++) {
guest_regs[i].flags &= GRF_STATIC;
if (!(guest_regs[i].flags & GRF_STATIC))
guest_regs[i].vreg = -1;
else {
- if (guest_regs[i].vreg < 0)
- emith_ctx_read(cache_regs[guest_regs[i].sreg].hreg, i*4);
- else if (guest_regs[i].vreg != guest_regs[i].sreg)
- emith_move_r_r(cache_regs[guest_regs[i].sreg].hreg,
- cache_regs[guest_regs[i].vreg].hreg);
cache_regs[guest_regs[i].sreg].gregs = 1 << i;
+ cache_regs[guest_regs[i].sreg].flags |= HRF_DIRTY;
+ guest_regs[i].flags |= GRF_DIRTY;
guest_regs[i].vreg = guest_regs[i].sreg;
}
}
rcache_counter = 0;
- rcache_hint_soon = rcache_hint_late = rcache_hint_write = rcache_hint_clean = 0;
+ rcache_regs_now = rcache_regs_soon = rcache_regs_late = 0;
+ rcache_regs_discard = rcache_regs_clean = 0;
}
static void rcache_flush(void)
for (i = 0; i < ARRAY_SIZE(guest_regs); i++)
if (guest_regs[i].flags & GRF_STATIC) {
- rcache_static |= (1 << i);
+ rcache_regs_static |= (1 << i);
guest_regs[i].sreg = reg_map_host[guest_regs[i].sreg];
cache_regs[guest_regs[i].sreg].type = HR_STATIC;
} else
// ---------------------------------------------------------------
// NB may return either REG or TEMP
-static int emit_get_rbase_and_offs(SH2 *sh2, sh2_reg_e r, int rmod, u32 *offs)
+static int emit_get_rbase_and_offs(SH2 *sh2, sh2_reg_e r, int rmode, u32 *offs)
{
uptr omask = 0xff; // offset mask, XXX: ARM oriented..
u32 mask = 0;
return hr;
}
+ // ROM, SDRAM. Host address should be mmapped to be equal to SH2 address.
la = (uptr)*(void **)((char *)sh2 + poffs);
- // accessing ROM or SDRAM, code location doesn't matter. The host address
- // for these should be mmapped to be equal to the SH2 address.
- // if r is in rcache or needed soon anyway, and offs is relative to region
- // use rcached const to avoid loading a literal on ARM
- if ((guest_regs[r].vreg >= 0 || ((guest_regs[r].flags & GRF_CDIRTY) &&
- ((rcache_hint_soon|rcache_hint_clean) & (1 << r)))) && !(*offs & ~mask)) {
+
+ // if r is in rcache or needed soon anyway, and offs is relative to region,
+ // and address translation fits in add_ptr_imm (s32), then use rcached const
+ if (la == (s32)la && !(*offs & ~mask) && rcache_is_cached(r)) {
u32 odd = a & 1; // need to fix odd address for correct byte addressing
la -= (s32)((a & ~mask) - *offs - odd); // diff between reg and memory
- // if reg is modified later on, allocate it RMW to remove aliases here
- // else the aliases vreg stays locked and a vreg shortage may occur.
- hr = hr2 = rcache_get_reg(r, rmod ? RC_GR_RMW : RC_GR_READ, NULL);
+ hr = hr2 = rcache_get_reg(r, rmode, NULL);
if ((la & ~omask) - odd) {
hr = rcache_get_tmp();
emith_add_r_r_ptr_imm(hr, hr2, (la & ~omask) - odd);
+ rcache_free(hr2);
}
*offs = (la & omask);
} else {
static void emit_move_r_r(sh2_reg_e dst, sh2_reg_e src)
{
- int hr_d, hr_s;
-
- if (guest_regs[src].vreg >= 0 || gconst_check(src) || rcache_is_hinted(src)) {
- hr_s = rcache_get_reg(src, RC_GR_READ, NULL);
+ if (gconst_check(src) || rcache_is_cached(src)) {
#if ALIAS_REGISTERS
- // check for aliasing
- int i = guest_regs[src].vreg;
- if (guest_regs[dst].vreg != i) {
- // remove possible old mapping of dst
- if (guest_regs[dst].vreg >= 0)
- rcache_remove_vreg_alias(guest_regs[dst].vreg, dst);
- // make dst an alias of src
- rcache_add_vreg_alias(i, dst);
- cache_regs[i].flags |= HRF_DIRTY;
- guest_regs[dst].flags |= GRF_DIRTY;
- gconst_kill(dst);
-#if PROPAGATE_CONSTANTS
- gconst_copy(dst, src);
-#endif
- return;
- }
-#endif
- hr_d = rcache_get_reg(dst, RC_GR_WRITE, NULL);
+ rcache_alias_vreg(dst, src);
+#else
+ int hr_s = rcache_get_reg(src, RC_GR_READ, NULL);
+ int hr_d = rcache_get_reg(dst, RC_GR_WRITE, NULL);
emith_move_r_r(hr_d, hr_s);
-#if PROPAGATE_CONSTANTS
gconst_copy(dst, src);
#endif
} else {
- hr_d = rcache_get_reg(dst, RC_GR_WRITE, NULL);
+ int hr_d = rcache_get_reg(dst, RC_GR_WRITE, NULL);
emith_ctx_read(hr_d, src * 4);
}
}
+static void emit_add_r_imm(sh2_reg_e r, u32 imm)
+{
+ u32 val;
+ int isgc = gconst_get(r, &val);
+ int hr, hr2;
+
+ if (!isgc || rcache_is_cached(r)) {
+ // not constant, or r is already in cache
+ hr = rcache_get_reg(r, RC_GR_RMW, &hr2);
+ emith_add_r_r_imm(hr, hr2, imm);
+ rcache_free(hr2);
+ if (isgc)
+ gconst_set(r, val + imm);
+ } else
+ gconst_new(r, val + imm);
+}
+
+static void emit_sub_r_imm(sh2_reg_e r, u32 imm)
+{
+ u32 val;
+ int isgc = gconst_get(r, &val);
+ int hr, hr2;
+
+ if (!isgc || rcache_is_cached(r)) {
+ // not constant, or r is already in cache
+ hr = rcache_get_reg(r, RC_GR_RMW, &hr2);
+ emith_sub_r_r_imm(hr, hr2, imm);
+ rcache_free(hr2);
+ if (isgc)
+ gconst_set(r, val - imm);
+ } else
+ gconst_new(r, val - imm);
+}
+
static void emit_sync_t_to_sr(void)
{
// avoid reloading SR from context if there's nothing to do
#ifndef DRC_SR_REG
// must writeback cycles for poll detection stuff
if (guest_regs[SHR_SR].vreg != -1)
- rcache_evict_vreg(guest_regs[SHR_SR].vreg);
+ rcache_unmap_vreg(guest_regs[SHR_SR].vreg);
#endif
+ rcache_invalidate_tmp();
if (size & MF_POLLING)
switch (size & MF_SIZEMASK) {
case 2: emith_call(sh2_drc_read32); break; // 32
}
- rcache_invalidate_tmp();
return rcache_get_tmp_ret();
}
rcache_clean_tmp();
#ifndef DRC_SR_REG
if (guest_regs[SHR_SR].vreg != -1)
- rcache_evict_vreg(guest_regs[SHR_SR].vreg);
+ rcache_unmap_vreg(guest_regs[SHR_SR].vreg);
#endif
+ rcache_invalidate_tmp();
switch (size & MF_SIZEMASK) {
case 0: emith_call(sh2_drc_write8); break; // 8
case 1: emith_call(sh2_drc_write16); break; // 16
case 2: emith_call(sh2_drc_write32); break; // 32
}
-
- rcache_invalidate_tmp();
}
// rd = @(Rs,#offs); rd < 0 -> return a temp
emit_move_r_imm32(rd, val);
hr2 = rcache_get_reg(rd, RC_GR_RMW, NULL);
}
- if ((size & MF_POSTINCR) && gconst_get(rs, &val))
- gconst_new(rs, val + (1 << (size & MF_SIZEMASK)));
+ if (size & MF_POSTINCR)
+ emit_add_r_imm(rs, 1 << (size & MF_SIZEMASK));
return hr2;
}
- hr = emit_get_rbase_and_offs(sh2, rs, size & MF_POSTINCR, &offs);
+ val = size & MF_POSTINCR;
+ hr = emit_get_rbase_and_offs(sh2, rs, val ? RC_GR_RMW : RC_GR_READ, &offs);
if (hr != -1) {
if (rd == SHR_TMP)
hr2 = rcache_get_tmp();
case 1: emith_read16s_r_r_offs(hr2, hr, offs); break; // 16
case 2: emith_read_r_r_offs(hr2, hr, offs); emith_ror(hr2, hr2, 16); break;
}
- if (cache_regs[reg_map_host[hr]].type == HR_TEMP) // may also return REG
- rcache_free_tmp(hr);
- if (size & MF_POSTINCR) {
- int isgc = gconst_get(rs, &val);
- if (!isgc || guest_regs[rs].vreg >= 0) {
- // already loaded
- hr = rcache_get_reg(rs, RC_GR_RMW, NULL);
- emith_add_r_r_imm(hr, hr, 1 << (size & MF_SIZEMASK));
- if (isgc)
- gconst_set(rs, val + (1 << (size & MF_SIZEMASK)));
- } else
- gconst_new(rs, val + (1 << (size & MF_SIZEMASK)));
- }
+ rcache_free(hr);
+ if (size & MF_POSTINCR)
+ emit_add_r_imm(rs, 1 << (size & MF_SIZEMASK));
return hr2;
}
#endif
- if (gconst_get(rs, &val) && guest_regs[rs].vreg < 0 && !(rcache_hint_soon & (1 << rs))) {
+ if (gconst_get(rs, &val) && !rcache_is_cached(rs)) {
hr = rcache_get_tmp_arg(0);
emith_move_r_imm(hr, val + offs);
if (size & MF_POSTINCR)
hr2 = rcache_get_reg(rs, RC_GR_RMW, NULL);
emith_add_r_r_imm(hr, hr2, offs);
emith_add_r_imm(hr2, 1 << (size & MF_SIZEMASK));
+ if (gconst_get(rs, &val))
+ gconst_set(rs, val + (1 << (size & MF_SIZEMASK)));
} else {
hr = rcache_get_reg_arg(0, rs, &hr2);
if (offs || hr != hr2)
u32 val;
if (rd == SHR_TMP) {
- host_arg2reg(hr2, 1);
+ host_arg2reg(hr2, 1); // already locked and prepared by caller
} else if ((size & MF_PREDECR) && rd == rs) { // must avoid caching rd in arg1
hr2 = rcache_get_reg_arg(1, rd, &hr);
- if (hr != hr2) emith_move_r_r(hr2, hr);
+ if (hr != hr2) {
+ emith_move_r_r(hr2, hr);
+ rcache_free(hr2);
+ }
} else
hr2 = rcache_get_reg_arg(1, rd, NULL);
+ if (rd != SHR_TMP)
+ rcache_unlock(guest_regs[rd].vreg); // unlock in case rd is in arg0
- if (gconst_get(rs, &val) && guest_regs[rs].vreg < 0 && !(rcache_hint_soon & (1 << rs))) {
+ if (gconst_get(rs, &val) && !rcache_is_cached(rs)) {
+ hr = rcache_get_tmp_arg(0);
if (size & MF_PREDECR) {
val -= 1 << (size & MF_SIZEMASK);
gconst_new(rs, val);
}
- hr = rcache_get_tmp_arg(0);
emith_move_r_imm(hr, val + offs);
} else if (offs || (size & MF_PREDECR)) {
- if (size & MF_PREDECR) {
- hr = rcache_get_reg(rs, RC_GR_RMW, &hr2);
- emith_sub_r_r_imm(hr, hr2, 1 << (size & MF_SIZEMASK));
- }
+ if (size & MF_PREDECR)
+ emit_sub_r_imm(rs, 1 << (size & MF_SIZEMASK));
+ rcache_unlock(guest_regs[rs].vreg); // unlock in case rs is in arg0
hr = rcache_get_reg_arg(0, rs, &hr2);
if (offs || hr != hr2)
emith_add_r_r_imm(hr, hr2, offs);
} else
- rcache_get_reg_arg(0, rs, NULL);
+ hr = rcache_get_reg_arg(0, rs, NULL);
emit_memhandler_write(size);
}
if (op_flags[i] & OF_BTARGET)
ADD_TO_ARRAY(branch_target_pc, branch_target_count, pc, );
if (ops[i].op == OP_LDC && (ops[i].dest & BITMASK1(SHR_SR)) && pc+2 < end_pc)
- op_flags[i+1] |= OF_BTARGET; // RTE entrypoint in case of SR(IMASK) change
+ op_flags[i+1] |= OF_BTARGET; // RTE entrypoint in case of SR.IMASK change
#if LOOP_DETECTION
// loop types detected:
// 1. target: ... BRA target -> idle loop
drcf.polling = (drcf.loop_type == OF_POLL_LOOP ? MF_POLLING : 0);
#endif
-#if (DRC_DEBUG & ~7)
- // must update PC
- emit_move_r_imm32(SHR_PC, pc);
-#endif
rcache_clean();
#if (DRC_DEBUG & 0x10)
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
emith_sync_t(sr);
rcache_clean();
- tmp = rcache_used_hreg_mask();
+ tmp = rcache_used_hregs_mask();
emith_save_caller_regs(tmp);
emit_do_static_regs(1, 0);
rcache_get_reg_arg(2, SHR_SR, NULL);
tmp2 = rcache_get_tmp_arg(0);
tmp3 = rcache_get_tmp_arg(1);
+ tmp4 = rcache_get_tmp_arg(3);
emith_move_r_ptr_imm(tmp2, tcache_ptr);
- emith_move_r_r_ptr(tmp3,CONTEXT_REG);
+ emith_move_r_r_ptr(tmp3, CONTEXT_REG);
+ emith_move_r_imm(tmp4, pc);
+ emith_ctx_write(tmp4, SHR_PC * 4);
+ rcache_invalidate_tmp();
emith_call(sh2_drc_log_entry);
emith_restore_caller_regs(tmp);
- rcache_invalidate_tmp();
#endif
do_host_disasm(tcache_id);
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
FLUSH_CYCLES(sr);
emith_sync_t(sr);
+ emit_move_r_imm32(SHR_PC, pc);
rcache_clean();
- tmp = rcache_used_hreg_mask();
+ tmp = rcache_used_hregs_mask();
emith_save_caller_regs(tmp);
emit_do_static_regs(1, 0);
emith_pass_arg_r(0, CONTEXT_REG);
// dbg(1, "unhandled delay_dep_fw: %x", delay_dep_fw & ~BITMASK1(SHR_T));
if (delay_dep_bk & ~BITMASK2(SHR_PC, SHR_PR))
dbg(1, "unhandled delay_dep_bk: %x", delay_dep_bk);
- rcache_set_hint_soon(0);
- rcache_set_hint_late(0);
- rcache_set_hint_write(0);
}
- else
- {
- // inform cache about future register usage
- u32 late = 0; // regs read by future ops
- u32 write = 0; // regs written to (to detect write before read)
- u32 soon = 0; // regs read soon
- tmp = (OP_ISBRANCH(opd[0].op) || opd[0].op == OP_RTE || // branching insns
- opd[0].op == OP_TRAPA || opd[0].op == OP_UNDEFINED);
- for (v = 1; v <= 9; v++) {
- // no sense in looking any further than the next rcache flush
- if (pc + 2*v < end_pc && !(op_flags[i+v] & OF_BTARGET) &&
- (!tmp || (op_flags[i+v] & OF_DELAY_OP))) {
- late |= opd[v].source & ~write;
- // ignore source regs after they have been written to
- write |= opd[v].dest;
- } else {
- // upcoming rcache_flush, start writing back unused dirty stuff
- tmp2 = write|opd[0].source|opd[0].dest; // insn may change reg aliases
- rcache_clean_mask(rcache_dirty_mask() & ~tmp2);
- break;
- }
- tmp |= (OP_ISBRANCH(opd[v].op) || opd[v].op == OP_RTE ||
- opd[v].op == OP_TRAPA || opd[v].op == OP_UNDEFINED);
+
+ // inform cache about future register usage
+ u32 late = 0; // regs read by future ops
+ u32 write = 0; // regs written to (to detect write before read)
+ u32 soon = 0; // regs read soon
+ for (v = 1; v <= 9; v++) {
+ // no sense in looking any further than the next rcache flush
+ tmp = ((op_flags[i+v] & OF_BTARGET) || (op_flags[i+v-1] & OF_DELAY_OP) ||
+ (OP_ISBRACND(opd[v-1].op) && !(op_flags[i+v] & OF_DELAY_OP)));
+ if (pc + 2*v <= end_pc && !tmp) { // (pc already incremented above)
+ late |= opd[v].source & ~write;
+ // ignore source regs after they have been written to
+ write |= opd[v].dest;
// regs needed in the next few instructions
if (v <= 4)
soon = late;
+ } else {
+ // upcoming rcache_flush, start writing back unused dirty stuff
+ rcache_clean_masked(rcache_dirty_mask() & ~(write|opd[0].dest));
+ break;
}
- rcache_set_hint_soon(late); // insns 1-3
- rcache_set_hint_late(late & ~soon); // insns 4-9
- rcache_set_hint_write(write & ~(late|soon) & ~opd[0].source);
- // overwritten without being used
}
- rcache_set_locked(opd[0].source); // try not to evict src regs for this op
+ rcache_set_usage_now(opd[0].source); // current insn
+ rcache_set_usage_soon(late); // insns 1-3
+ rcache_set_usage_late(late & ~soon); // insns 4-9
+ rcache_set_usage_discard(write & ~(late|soon) & ~opd[0].source);
switch (opd->op)
{
case OP_RTE: // RTE 0000000000101011
emith_invalidate_t();
// pop PC
- emit_memhandler_read_rr(sh2, SHR_PC, SHR_SP, 0, 2 | MF_POSTINCR);
+ tmp = emit_memhandler_read_rr(sh2, SHR_PC, SHR_SP, 0, 2 | MF_POSTINCR);
+ rcache_free(tmp);
// pop SR
tmp = emit_memhandler_read_rr(sh2, SHR_TMP, SHR_SP, 0, 2 | MF_POSTINCR);
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
/////////////////////////////////////////////
case 0x07: // ADD #imm,Rn 0111nnnniiiiiiii
- tmp = rcache_get_reg(GET_Rn(), RC_GR_RMW, &tmp2);
- if (op & 0x80) { // adding negative
- emith_sub_r_r_imm(tmp, tmp2, -op & 0xff);
- } else
- emith_add_r_r_imm(tmp, tmp2, op & 0xff);
+ if (op & 0x80) // adding negative
+ emit_sub_r_imm(GET_Rn(), (u8)-op);
+ else
+ emit_add_r_imm(GET_Rn(), (u8)op);
goto end_op;
/////////////////////////////////////////////
end_op:
rcache_unlock_all();
+#if DRC_DEBUG & 64
+ RCACHE_CHECK("after insn");
+#endif
cycles += opd->cycles;
// idle or delay loop
emit_sync_t_to_sr();
emith_sh2_delay_loop(cycles, drcf.delay_reg);
+ rcache_unlock_all(); // may lock delay_reg
drcf.polling = drcf.loop_type = 0;
}
#endif
emith_jump_cond_patchable(cond, target);
}
else if (target != NULL) {
- emith_jump_patchable(target);
rcache_invalidate();
+ emith_jump_patchable(target);
}
// branch not taken, correct cycle count
emith_sync_t(sr);
rcache_clean();
tmp = rcache_get_reg_arg(0, SHR_PC, NULL);
+ rcache_invalidate();
#if CALL_STACK
struct op_data *opd_b = (op_flags[i] & OF_DELAY_OP) ? opd-1 : opd;
if (opd_b->rm == SHR_PR) {
// JSR/BSRF
tmp = rcache_get_tmp_arg(1);
emith_call_link(tmp, sh2_drc_dispatcher_call);
+ rcache_free(tmp);
} else
#endif
if (gconst_get(SHR_PC, &target_pc)) {
// JMP
emith_jump(sh2_drc_dispatcher);
}
- rcache_invalidate();
drcf.pending_branch_indirect = 0;
drcf.polling = drcf.loop_type = 0;
}
target = dr_prepare_ext_branch(block->entryp, pc, sh2->is_slave, tcache_id);
if (target == NULL)
return NULL;
- emith_jump_patchable(target);
rcache_invalidate();
+ emith_jump_patchable(target);
} else
rcache_flush();
emith_flush();
tmp = rcache_get_reg_arg(1, SHR_SR, NULL);
emith_clear_msb(tmp, tmp, 22);
emith_move_r_r_ptr(arg2, CONTEXT_REG);
- emith_call(p32x_sh2_write32); // XXX: use sh2_drc_write32?
rcache_invalidate();
+ emith_call(p32x_sh2_write32); // XXX: use sh2_drc_write32?
// push PC
rcache_get_reg_arg(0, SHR_SP, NULL);
emith_ctx_read(arg1, SHR_PC * 4);
emith_move_r_r_ptr(arg2, CONTEXT_REG);
- emith_call(p32x_sh2_write32);
rcache_invalidate();
+ emith_call(p32x_sh2_write32);
// update I, cycles, do callback
emith_ctx_read(arg1, offsetof(SH2, pending_level));
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
if (arg0 != RET_REG)
emith_move_r_r(arg0, RET_REG);
emith_call_cleanup();
- emith_jump(sh2_drc_dispatcher);
rcache_invalidate();
+ emith_jump(sh2_drc_dispatcher);
emith_flush();
// sh2_drc_entry(SH2 *sh2)