uae-wii/src/compemu_support.c

6080 lines
128 KiB
C

#define writemem_special writemem
#define readmem_special readmem
#define USE_MATCHSTATE 0
#define setzflg_uses_bsf 0
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "events.h"
#include "include/memory.h"
#include "custom.h"
#include "newcpu.h"
#include "comptbl.h"
#include "compemu.h"
#include "uae_endian.h"
// %%% BRIAN KING WAS HERE %%%
extern int canbang;
compop_func *compfunctbl[65536];
compop_func *nfcompfunctbl[65536];
#ifdef NOFLAGS_SUPPORT
cpuop_func *nfcpufunctbl[65536];
#endif
uae_u8* comp_pc_p;
uae_u8* start_pc_p;
uae_u32 start_pc;
uae_u32 current_block_pc_p;
uae_u32 current_block_start_target;
uae_u32 needed_flags;
static uae_u32 next_pc_p;
static uae_u32 taken_pc_p;
static int branch_cc;
int segvcount=0;
int soft_flush_count=0;
int hard_flush_count=0;
int compile_count=0;
int checksum_count=0;
static uae_u8* current_compile_p=NULL;
static uae_u8* max_compile_start;
uae_u8* compiled_code=NULL;
static uae_s32 reg_alloc_run;
static int align_loops = 32;// Align the start of loops
static int align_jumps = 32;// Align the start of jumps
void *pushall_call_handler;
static void *popall_do_nothing;
static void *popall_exec_nostats;
static void *popall_execute_normal;
static void *popall_cache_miss;
static void *popall_recompile_block;
static void *popall_check_checksum;
static uae_u8 *popallspace;
/* The 68k only ever executes from even addresses. So right now, we
waste half the entries in this array
UPDATE: We now use those entries to store the start of the linked
lists that we maintain for each hash result. */
cacheline cache_tags[TAGSIZE];
static int letit;
blockinfo* hold_bi[MAX_HOLD_BI];
blockinfo* active;
blockinfo* dormant;
op_properties prop[65536];
#ifdef NOFLAGS_SUPPORT
/* 68040 */
extern const struct cputbl op_smalltbl_0_nf[];
#endif
extern const struct comptbl op_smalltbl_0_comp_nf[];
extern const struct comptbl op_smalltbl_0_comp_ff[];
#ifdef NOFLAGS_SUPPORT
/* 68020 + 68881 */
extern const struct cputbl op_smalltbl_1_nf[];
/* 68020 */
extern const struct cputbl op_smalltbl_2_nf[];
/* 68010 */
extern const struct cputbl op_smalltbl_3_nf[];
/* 68000 */
extern const struct cputbl op_smalltbl_4_nf[];
#ifdef CPUEMU_5
/* 68000 slow but compatible. */
extern const struct cputbl op_smalltbl_5_nf[];
#endif
#endif
static void flush_icache_hard(int n);
bigstate live;
smallstate empty_ss;
smallstate default_ss;
static int optlev;
static int writereg(int r, int size);
static void unlock(int r);
static void setlock(int r);
static int readreg_specific(int r, int size, int spec);
static int writereg_specific(int r, int size, int spec);
static void prepare_for_call_1(void);
static void prepare_for_call_2(void);
static void align_target(uae_u32 a);
#ifdef NATMEM_OFFSET
# ifndef WIN32
struct sigaction *saved_handler;
# endif
#endif
uae_u32 m68k_pc_offset;
/* Some arithmetic ooperations can be optimized away if the operands
are known to be constant. But that's only a good idea when the
side effects they would have on the flags are not important. This
variable indicates whether we need the side effects or not
*/
uae_u32 needflags=0;
/* Flag handling is complicated.
x86 instructions create flags, which quite often are exactly what we
want. So at times, the "68k" flags are actually in the x86 flags.
Then again, sometimes we do x86 instructions that clobber the x86
flags, but don't represent a corresponding m68k instruction. In that
case, we have to save them.
We used to save them to the stack, but now store them back directly
into the regflags.cznv of the traditional emulation. Thus some odd
names.
So flags can be in either of two places (used to be three; boy were
things complicated back then!); And either place can contain either
valid flags or invalid trash (and on the stack, there was also the
option of "nothing at all", now gone). A couple of variables keep
track of the respective states.
To make things worse, we might or might not be interested in the flags.
by default, we are, but a call to dont_care_flags can change that
until the next call to live_flags. If we are not, pretty much whatever
is in the register and/or the native flags is seen as valid.
*/
STATIC_INLINE blockinfo* get_blockinfo(uae_u32 cl)
{
return cache_tags[cl+1].bi;
}
STATIC_INLINE blockinfo* get_blockinfo_addr(void* addr)
{
blockinfo* bi=get_blockinfo(cacheline(addr));
while (bi) {
if (bi->pc_p==addr)
return bi;
bi=bi->next_same_cl;
}
return NULL;
}
/*******************************************************************
* All sorts of list related functions for all of the lists *
*******************************************************************/
STATIC_INLINE void remove_from_cl_list(blockinfo* bi)
{
uae_u32 cl=cacheline(bi->pc_p);
if (bi->prev_same_cl_p)
*(bi->prev_same_cl_p)=bi->next_same_cl;
if (bi->next_same_cl)
bi->next_same_cl->prev_same_cl_p=bi->prev_same_cl_p;
if (cache_tags[cl+1].bi)
cache_tags[cl].handler=cache_tags[cl+1].bi->handler_to_use;
else
cache_tags[cl].handler=popall_execute_normal;
}
STATIC_INLINE void remove_from_list(blockinfo* bi)
{
if (bi->prev_p)
*(bi->prev_p)=bi->next;
if (bi->next)
bi->next->prev_p=bi->prev_p;
}
STATIC_INLINE void remove_from_lists(blockinfo* bi)
{
remove_from_list(bi);
remove_from_cl_list(bi);
}
STATIC_INLINE void add_to_cl_list(blockinfo* bi)
{
uae_u32 cl=cacheline(bi->pc_p);
if (cache_tags[cl+1].bi)
cache_tags[cl+1].bi->prev_same_cl_p=&(bi->next_same_cl);
bi->next_same_cl=cache_tags[cl+1].bi;
cache_tags[cl+1].bi=bi;
bi->prev_same_cl_p=&(cache_tags[cl+1].bi);
cache_tags[cl].handler=bi->handler_to_use;
}
STATIC_INLINE void raise_in_cl_list(blockinfo* bi)
{
remove_from_cl_list(bi);
add_to_cl_list(bi);
}
STATIC_INLINE void add_to_active(blockinfo* bi)
{
if (active)
active->prev_p=&(bi->next);
bi->next=active;
active=bi;
bi->prev_p=&active;
}
STATIC_INLINE void add_to_dormant(blockinfo* bi)
{
if (dormant)
dormant->prev_p=&(bi->next);
bi->next=dormant;
dormant=bi;
bi->prev_p=&dormant;
}
STATIC_INLINE void remove_dep(dependency* d)
{
if (d->prev_p)
*(d->prev_p)=d->next;
if (d->next)
d->next->prev_p=d->prev_p;
d->prev_p=NULL;
d->next=NULL;
}
/* This block's code is about to be thrown away, so it no longer
depends on anything else */
STATIC_INLINE void remove_deps(blockinfo* bi)
{
remove_dep(&(bi->dep[0]));
remove_dep(&(bi->dep[1]));
}
STATIC_INLINE void adjust_jmpdep(dependency* d, void* a)
{
*(d->jmp_off) = (uae_uintptr)a - ((uae_uintptr)d->jmp_off+4);
}
/********************************************************************
* Soft flush handling support functions *
********************************************************************/
STATIC_INLINE void set_dhtu(blockinfo* bi, void* dh)
{
//printf("bi is %p\n",bi);
if (dh!=bi->direct_handler_to_use) {
dependency* x=bi->deplist;
//printf("bi->deplist=%p\n",bi->deplist);
while (x) {
//printf("x is %p\n",x);
//printf("x->next is %p\n",x->next);
//printf("x->prev_p is %p\n",x->prev_p);
if (x->jmp_off) {
adjust_jmpdep(x,dh);
}
x=x->next;
}
bi->direct_handler_to_use=dh;
}
}
STATIC_INLINE void invalidate_block(blockinfo* bi)
{
int i;
bi->optlevel=0;
bi->count=currprefs.optcount[0]-1;
bi->handler=NULL;
bi->handler_to_use=popall_execute_normal;
bi->direct_handler=NULL;
set_dhtu(bi,bi->direct_pen);
bi->needed_flags=0xff;
for (i=0;i<2;i++) {
bi->dep[i].jmp_off=NULL;
bi->dep[i].target=NULL;
}
remove_deps(bi);
}
STATIC_INLINE void create_jmpdep(blockinfo* bi, int i, uae_u32* jmpaddr, uae_uintptr target)
{
blockinfo* tbi=get_blockinfo_addr((void*)target);
Dif(!tbi) {
write_log ("JIT: Could not create jmpdep!\n");
abort();
}
bi->dep[i].jmp_off=jmpaddr;
bi->dep[i].target=tbi;
bi->dep[i].next=tbi->deplist;
if (bi->dep[i].next)
bi->dep[i].next->prev_p=&(bi->dep[i].next);
bi->dep[i].prev_p=&(tbi->deplist);
tbi->deplist=&(bi->dep[i]);
}
STATIC_INLINE void big_to_small_state(bigstate* b, smallstate* s)
{
int i;
int count=0;
for (i=0;i<N_REGS;i++) {
s->nat[i].validsize=0;
s->nat[i].dirtysize=0;
if (b->nat[i].nholds) {
int index=b->nat[i].nholds-1;
int r=b->nat[i].holds[index];
s->nat[i].holds=r;
s->nat[i].validsize=b->state[r].validsize;
s->nat[i].dirtysize=b->state[r].dirtysize;
count++;
}
}
write_log ("JIT: count=%d\n", count);
for (i=0;i<N_REGS;i++) { // FIXME --- don't do dirty yet
s->nat[i].dirtysize=0;
}
}
STATIC_INLINE void attached_state(blockinfo* bi)
{
bi->havestate=1;
if (bi->direct_handler_to_use==bi->direct_handler)
set_dhtu(bi,bi->direct_pen);
bi->direct_handler=bi->direct_pen;
bi->status=BI_TARGETTED;
}
STATIC_INLINE blockinfo* get_blockinfo_addr_new(void* addr, int setstate)
{
blockinfo* bi=get_blockinfo_addr(addr);
int i;
if (!bi) {
for (i=0;i<MAX_HOLD_BI && !bi;i++) {
if (hold_bi[i]) {
bi=hold_bi[i];
hold_bi[i]=NULL;
bi->pc_p=addr;
invalidate_block(bi);
add_to_active(bi);
add_to_cl_list(bi);
}
}
}
if (!bi) {
write_log ("JIT: Looking for blockinfo, can't find free one\n");
abort();
}
#if USE_MATCHSTATE
if (setstate &&
!bi->havestate) {
big_to_small_state(&live,&(bi->env));
attached_state(bi);
}
#endif
return bi;
}
static void prepare_block(blockinfo* bi);
STATIC_INLINE void alloc_blockinfos(void)
{
int i;
blockinfo* bi;
for (i=0;i<MAX_HOLD_BI;i++) {
if (hold_bi[i])
return;
bi=hold_bi[i]=(blockinfo*)current_compile_p;
current_compile_p+=sizeof(blockinfo);
prepare_block(bi);
}
}
/*
* Translation cache management
*/
static void free_cache (void)
{
if (compiled_code) {
flush_icache_hard (6);
cache_free (compiled_code);
compiled_code = 0;
write_log ("JIT: Deallocated translation cache.\n");
}
}
static void alloc_cache (void)
{
if (currprefs.cachesize == 0)
return;
while (!compiled_code && currprefs.cachesize) {
compiled_code = cache_alloc (currprefs.cachesize * 1024);
if (!compiled_code)
currprefs.cachesize /= 2;
}
if (compiled_code) {
max_compile_start = compiled_code + currprefs.cachesize * 1024 - BYTES_PER_INST;
current_compile_p = compiled_code;
}
if (currprefs.cachesize != changed_prefs.cachesize)
changed_prefs.cachesize = currprefs.cachesize;
write_log ("JIT: Allocated %d KB translation cache.\n", currprefs.cachesize);
}
void set_cache_state (int enabled)
{
if (enabled != letit)
flush_icache_hard (77);
letit = enabled;
}
/********************************************************************
* Preferences handling. This is just a convenient place to put it *
********************************************************************/
extern int have_done_picasso;
void check_prefs_changed_comp (void)
{
currprefs.comptrustbyte = changed_prefs.comptrustbyte;
currprefs.comptrustword = changed_prefs.comptrustword;
currprefs.comptrustlong = changed_prefs.comptrustlong;
currprefs.comptrustnaddr = changed_prefs.comptrustnaddr;
currprefs.compnf = changed_prefs.compnf;
currprefs.comp_hardflush = changed_prefs.comp_hardflush;
currprefs.comp_constjump = changed_prefs.comp_constjump;
currprefs.comp_oldsegv = changed_prefs.comp_oldsegv;
currprefs.compfpu = changed_prefs.compfpu;
if (currprefs.cachesize != changed_prefs.cachesize) {
currprefs.cachesize = changed_prefs.cachesize;
free_cache ();
alloc_cache ();
}
if ((!canbang || !currprefs.cachesize) && currprefs.comptrustbyte != 1) {
// Set all of these to indirect when canbang == 0
// Basically, set the compforcesettings option...
currprefs.comptrustbyte = 1;
currprefs.comptrustword = 1;
currprefs.comptrustlong = 1;
currprefs.comptrustnaddr= 1;
currprefs.compforcesettings = 1;
changed_prefs.comptrustbyte = 1;
changed_prefs.comptrustword = 1;
changed_prefs.comptrustlong = 1;
changed_prefs.comptrustnaddr= 1;
changed_prefs.compforcesettings = 1;
if (currprefs.cachesize)
write_log ("JIT: Reverting to \"indirect\" access, because canbang is zero!\n");
}
}
/********************************************************************
* Functions to emit data into memory, and other general support *
********************************************************************/
static uae_u8* target;
STATIC_INLINE void emit_byte(uae_u8 x)
{
*target++=x;
}
STATIC_INLINE void emit_word(uae_u16 x)
{
*((uae_u16*)target)=x;
target+=2;
}
STATIC_INLINE void emit_long(uae_u32 x)
{
*((uae_u32*)target)=x;
target+=4;
}
STATIC_INLINE void emit_block (const uae_u8 *block, uae_u32 blocklen)
{
memcpy ((uae_u8 *) target, block, blocklen);
target += blocklen;
}
STATIC_INLINE uae_u32 reverse32 (uae_u32 oldv)
{
return bswap_32 (oldv);
}
void set_target(uae_u8* t)
{
target=t;
}
STATIC_INLINE uae_u8* get_target_noopt(void)
{
return target;
}
STATIC_INLINE uae_u8* get_target(void)
{
return get_target_noopt();
}
/********************************************************************
* Getting the information about the target CPU *
********************************************************************/
#include "compemu_raw_x86.c"
/********************************************************************
* Flags status handling. EMIT TIME! *
********************************************************************/
static void bt_l_ri_noclobber(R4 r, IMM i);
static void make_flags_live_internal(void)
{
if (live.flags_in_flags==VALID)
return;
Dif (live.flags_on_stack==TRASH) {
write_log ("JIT: Want flags, got something on stack, but it is TRASH\n");
abort();
}
if (live.flags_on_stack==VALID) {
int tmp;
tmp=readreg_specific(FLAGTMP,4,FLAG_NREG2);
raw_reg_to_flags(tmp);
unlock(tmp);
live.flags_in_flags=VALID;
return;
}
write_log ("JIT: Huh? live.flags_in_flags=%d, live.flags_on_stack=%d, but need to make live\n",
live.flags_in_flags, live.flags_on_stack);
abort();
}
static void flags_to_stack(void)
{
if (live.flags_on_stack==VALID)
return;
if (!live.flags_are_important) {
live.flags_on_stack=VALID;
return;
}
Dif (live.flags_in_flags!=VALID)
abort();
else {
int tmp;
tmp=writereg_specific(FLAGTMP,4,FLAG_NREG1);
raw_flags_to_reg(tmp);
unlock(tmp);
}
live.flags_on_stack=VALID;
}
STATIC_INLINE void clobber_flags(void)
{
if (live.flags_in_flags==VALID && live.flags_on_stack!=VALID)
flags_to_stack();
live.flags_in_flags=TRASH;
}
/* Prepare for leaving the compiled stuff */
STATIC_INLINE void flush_flags(void)
{
flags_to_stack();
return;
}
static int touchcnt;
/********************************************************************
* register allocation per block logging *
********************************************************************/
static uae_s8 vstate[VREGS];
static uae_s8 nstate[N_REGS];
#define L_UNKNOWN -127
#define L_UNAVAIL -1
#define L_NEEDED -2
#define L_UNNEEDED -3
STATIC_INLINE void log_startblock(void)
{
int i;
for (i=0;i<VREGS;i++)
vstate[i]=L_UNKNOWN;
for (i=0;i<N_REGS;i++)
nstate[i]=L_UNKNOWN;
}
STATIC_INLINE void log_isused(int n)
{
if (nstate[n]==L_UNKNOWN)
nstate[n]=L_UNAVAIL;
}
STATIC_INLINE void log_isreg(int n, int r)
{
if (nstate[n]==L_UNKNOWN)
nstate[n]=r;
if (vstate[r]==L_UNKNOWN)
vstate[r]=L_NEEDED;
}
STATIC_INLINE void log_clobberreg(int r)
{
if (vstate[r]==L_UNKNOWN)
vstate[r]=L_UNNEEDED;
}
/* This ends all possibility of clever register allocation */
STATIC_INLINE void log_flush(void)
{
int i;
for (i=0;i<VREGS;i++)
if (vstate[i]==L_UNKNOWN)
vstate[i]=L_NEEDED;
for (i=0;i<N_REGS;i++)
if (nstate[i]==L_UNKNOWN)
nstate[i]=L_UNAVAIL;
}
STATIC_INLINE void log_dump(void)
{
int i;
return;
write_log("----------------------\n");
for (i=0;i<N_REGS;i++) {
switch(nstate[i]) {
case L_UNKNOWN: write_log("Nat %d : UNKNOWN\n",i); break;
case L_UNAVAIL: write_log("Nat %d : UNAVAIL\n",i); break;
default: write_log("Nat %d : %d\n",i,nstate[i]); break;
}
}
for (i=0;i<VREGS;i++) {
if (vstate[i]==L_UNNEEDED)
write_log("Virt %d: UNNEEDED\n",i);
}
}
/********************************************************************
* register status handling. EMIT TIME! *
********************************************************************/
STATIC_INLINE void set_status(int r, int status)
{
if (status==ISCONST)
log_clobberreg(r);
live.state[r].status=status;
}
STATIC_INLINE int isinreg(int r)
{
return live.state[r].status==CLEAN || live.state[r].status==DIRTY;
}
STATIC_INLINE void adjust_nreg(int r, uae_u32 val)
{
if (!val)
return;
raw_lea_l_brr(r,r,val);
}
static void tomem(int r)
{
int rr=live.state[r].realreg;
if (isinreg(r)) {
if (live.state[r].val &&
live.nat[rr].nholds==1 &&
!live.nat[rr].locked) {
// printf("RemovingA offset %x from reg %d (%d) at %p\n",
// live.state[r].val,r,rr,target);
adjust_nreg(rr,live.state[r].val);
live.state[r].val=0;
live.state[r].dirtysize=4;
set_status(r,DIRTY);
}
}
if (live.state[r].status==DIRTY) {
switch (live.state[r].dirtysize) {
case 1: raw_mov_b_mr((uae_uintptr)live.state[r].mem,rr); break;
case 2: raw_mov_w_mr((uae_uintptr)live.state[r].mem,rr); break;
case 4: raw_mov_l_mr((uae_uintptr)live.state[r].mem,rr); break;
default: abort();
}
set_status(r,CLEAN);
live.state[r].dirtysize=0;
}
}
STATIC_INLINE int isconst(int r)
{
return live.state[r].status==ISCONST;
}
int is_const(int r)
{
return isconst(r);
}
STATIC_INLINE void writeback_const(int r)
{
if (!isconst(r))
return;
Dif (live.state[r].needflush==NF_HANDLER) {
write_log ("JIT: Trying to write back constant NF_HANDLER!\n");
abort();
}
raw_mov_l_mi((uae_uintptr)live.state[r].mem,live.state[r].val);
live.state[r].val=0;
set_status(r,INMEM);
}
STATIC_INLINE void tomem_c(int r)
{
if (isconst(r)) {
writeback_const(r);
}
else
tomem(r);
}
static void evict(int r)
{
int rr;
if (!isinreg(r))
return;
tomem(r);
rr=live.state[r].realreg;
Dif (live.nat[rr].locked &&
live.nat[rr].nholds==1) {
write_log ("JIT: register %d in nreg %d is locked!\n", r, live.state[r].realreg);
abort();
}
live.nat[rr].nholds--;
if (live.nat[rr].nholds!=live.state[r].realind) { /* Was not last */
int topreg=live.nat[rr].holds[live.nat[rr].nholds];
int thisind=live.state[r].realind;
live.nat[rr].holds[thisind]=topreg;
live.state[topreg].realind=thisind;
}
live.state[r].realreg=-1;
set_status(r,INMEM);
}
STATIC_INLINE void free_nreg(int r)
{
int i=live.nat[r].nholds;
while (i) {
int vr;
--i;
vr=live.nat[r].holds[i];
evict(vr);
}
Dif (live.nat[r].nholds!=0) {
write_log ("JIT: Failed to free nreg %d, nholds is %d\n", r, live.nat[r].nholds);
abort();
}
}
/* Use with care! */
STATIC_INLINE void isclean(int r)
{
if (!isinreg(r))
return;
live.state[r].validsize=4;
live.state[r].dirtysize=0;
live.state[r].val=0;
set_status(r,CLEAN);
}
STATIC_INLINE void disassociate(int r)
{
isclean(r);
evict(r);
}
STATIC_INLINE void set_const(int r, uae_u32 val)
{
disassociate(r);
live.state[r].val=val;
set_status(r,ISCONST);
}
STATIC_INLINE uae_u32 get_offset(int r)
{
return live.state[r].val;
}
static int alloc_reg_hinted(int r, int size, int willclobber, int hint)
{
int bestreg;
uae_s32 when;
int i;
uae_s32 badness=0; /* to shut up gcc */
bestreg=-1;
when=2000000000;
for (i=N_REGS;i--;) {
badness=live.nat[i].touched;
if (live.nat[i].nholds==0)
badness=0;
if (i==hint)
badness-=200000000;
if (!live.nat[i].locked && badness<when) {
if ((size==1 && live.nat[i].canbyte) ||
(size==2 && live.nat[i].canword) ||
(size==4)) {
bestreg=i;
when=badness;
if (live.nat[i].nholds==0 && hint<0)
break;
if (i==hint)
break;
}
}
}
Dif (bestreg==-1)
abort();
if (live.nat[bestreg].nholds>0) {
free_nreg(bestreg);
}
if (isinreg(r)) {
int rr=live.state[r].realreg;
/* This will happen if we read a partially dirty register at a
bigger size */
Dif (willclobber || live.state[r].validsize>=size)
abort();
Dif (live.nat[rr].nholds!=1)
abort();
if (size==4 && live.state[r].validsize==2) {
log_isused(bestreg);
raw_mov_l_rm(bestreg,(uae_uintptr)live.state[r].mem);
raw_bswap_32(bestreg);
raw_zero_extend_16_rr(rr,rr);
raw_zero_extend_16_rr(bestreg,bestreg);
raw_bswap_32(bestreg);
raw_lea_l_brr_indexed(rr,rr,bestreg,1,0);
live.state[r].validsize=4;
live.nat[rr].touched=touchcnt++;
return rr;
}
if (live.state[r].validsize==1) {
/* Nothing yet */
}
evict(r);
}
if (!willclobber) {
if (live.state[r].status!=UNDEF) {
if (isconst(r)) {
raw_mov_l_ri(bestreg,live.state[r].val);
live.state[r].val=0;
live.state[r].dirtysize=4;
set_status(r,DIRTY);
log_isused(bestreg);
}
else {
if (r==FLAGTMP)
raw_load_flagreg(bestreg,r);
else if (r==FLAGX)
raw_load_flagx(bestreg,r);
else {
raw_mov_l_rm(bestreg,(uae_uintptr)live.state[r].mem);
}
live.state[r].dirtysize=0;
set_status(r,CLEAN);
log_isreg(bestreg,r);
}
}
else {
live.state[r].val=0;
live.state[r].dirtysize=0;
set_status(r,CLEAN);
log_isused(bestreg);
}
live.state[r].validsize=4;
}
else { /* this is the easiest way, but not optimal. FIXME! */
/* Now it's trickier, but hopefully still OK */
if (!isconst(r) || size==4) {
live.state[r].validsize=size;
live.state[r].dirtysize=size;
live.state[r].val=0;
set_status(r,DIRTY);
if (size==4)
log_isused(bestreg);
else
log_isreg(bestreg,r);
}
else {
if (live.state[r].status!=UNDEF)
raw_mov_l_ri(bestreg,live.state[r].val);
live.state[r].val=0;
live.state[r].validsize=4;
live.state[r].dirtysize=4;
set_status(r,DIRTY);
log_isused(bestreg);
}
}
live.state[r].realreg=bestreg;
live.state[r].realind=live.nat[bestreg].nholds;
live.nat[bestreg].touched=touchcnt++;
live.nat[bestreg].holds[live.nat[bestreg].nholds]=r;
live.nat[bestreg].nholds++;
return bestreg;
}
static int alloc_reg(int r, int size, int willclobber)
{
return alloc_reg_hinted(r,size,willclobber,-1);
}
static void unlock(int r)
{
Dif (!live.nat[r].locked)
abort();
live.nat[r].locked--;
}
static void setlock(int r)
{
live.nat[r].locked++;
}
static void mov_nregs(int d, int s)
{
int nd=live.nat[d].nholds;
int i;
if (s==d)
return;
if (nd>0)
free_nreg(d);
raw_mov_l_rr(d,s);
log_isused(d);
for (i=0;i<live.nat[s].nholds;i++) {
int vs=live.nat[s].holds[i];
live.state[vs].realreg=d;
live.state[vs].realind=i;
live.nat[d].holds[i]=vs;
}
live.nat[d].nholds=live.nat[s].nholds;
live.nat[s].nholds=0;
}
STATIC_INLINE void make_exclusive(int r, int size, int spec)
{
reg_status oldstate;
int rr=live.state[r].realreg;
int nr;
int nind;
int ndirt=0;
int i;
if (!isinreg(r))
return;
if (live.nat[rr].nholds==1)
return;
for (i=0;i<live.nat[rr].nholds;i++) {
int vr=live.nat[rr].holds[i];
if (vr!=r &&
(live.state[vr].status==DIRTY || live.state[vr].val))
ndirt++;
}
if (!ndirt && size<live.state[r].validsize && !live.nat[rr].locked) {
/* Everything else is clean, so let's keep this register */
for (i=0;i<live.nat[rr].nholds;i++) {
int vr=live.nat[rr].holds[i];
if (vr!=r) {
evict(vr);
i--; /* Try that index again! */
}
}
Dif (live.nat[rr].nholds!=1) {
write_log ("JIT: natreg %d holds %d vregs, %d not exclusive\n",
rr, live.nat[rr].nholds, r);
abort();
}
return;
}
/* We have to split the register */
oldstate=live.state[r];
setlock(rr); /* Make sure this doesn't go away */
/* Forget about r being in the register rr */
disassociate(r);
/* Get a new register, that we will clobber completely */
if (oldstate.status==DIRTY) {
/* If dirtysize is <4, we need a register that can handle the
eventual smaller memory store! Thanks to Quake68k for exposing
this detail ;-) */
nr=alloc_reg_hinted(r,oldstate.dirtysize,1,spec);
}
else {
nr=alloc_reg_hinted(r,4,1,spec);
}
nind=live.state[r].realind;
live.state[r]=oldstate; /* Keep all the old state info */
live.state[r].realreg=nr;
live.state[r].realind=nind;
if (size<live.state[r].validsize) {
if (live.state[r].val) {
/* Might as well compensate for the offset now */
raw_lea_l_brr(nr,rr,oldstate.val);
live.state[r].val=0;
live.state[r].dirtysize=4;
set_status(r,DIRTY);
}
else
raw_mov_l_rr(nr,rr); /* Make another copy */
}
unlock(rr);
}
STATIC_INLINE void add_offset(int r, uae_u32 off)
{
live.state[r].val+=off;
}
STATIC_INLINE void remove_offset(int r, int spec)
{
int rr;
if (isconst(r))
return;
if (live.state[r].val==0)
return;
if (isinreg(r) && live.state[r].validsize<4)
evict(r);
if (!isinreg(r))
alloc_reg_hinted(r,4,0,spec);
Dif (live.state[r].validsize!=4) {
write_log ("JIT: Validsize=%d in remove_offset\n", live.state[r].validsize);
abort();
}
make_exclusive(r,0,-1);
/* make_exclusive might have done the job already */
if (live.state[r].val==0)
return;
rr=live.state[r].realreg;
if (live.nat[rr].nholds==1) {
//printf("RemovingB offset %x from reg %d (%d) at %p\n",
// live.state[r].val,r,rr,target);
adjust_nreg(rr,live.state[r].val);
live.state[r].dirtysize=4;
live.state[r].val=0;
set_status(r,DIRTY);
return;
}
write_log ("JIT: Failed in remove_offset\n");
abort();
}
STATIC_INLINE void remove_all_offsets(void)
{
int i;
for (i=0;i<VREGS;i++)
remove_offset(i,-1);
}
STATIC_INLINE int readreg_general(int r, int size, int spec, int can_offset)
{
int n;
int answer=-1;
if (live.state[r].status==UNDEF) {
write_log ("JIT: WARNING: Unexpected read of undefined register %d\n", r);
}
if (!can_offset)
remove_offset(r,spec);
if (isinreg(r) && live.state[r].validsize>=size) {
n=live.state[r].realreg;
switch(size) {
case 1:
if (live.nat[n].canbyte || spec>=0) {
answer=n;
}
break;
case 2:
if (live.nat[n].canword || spec>=0) {
answer=n;
}
break;
case 4:
answer=n;
break;
default: abort();
}
if (answer<0)
evict(r);
}
/* either the value was in memory to start with, or it was evicted and
is in memory now */
if (answer<0) {
answer=alloc_reg_hinted(r,spec>=0?4:size,0,spec);
}
if (spec>=0 && spec!=answer) {
/* Too bad */
mov_nregs(spec,answer);
answer=spec;
}
live.nat[answer].locked++;
live.nat[answer].touched=touchcnt++;
return answer;
}
static int readreg(int r, int size)
{
return readreg_general(r,size,-1,0);
}
static int readreg_specific(int r, int size, int spec)
{
return readreg_general(r,size,spec,0);
}
static int readreg_offset(int r, int size)
{
return readreg_general(r,size,-1,1);
}
STATIC_INLINE int writereg_general(int r, int size, int spec)
{
int n;
int answer=-1;
if (size<4) {
remove_offset(r,spec);
}
make_exclusive(r,size,spec);
if (isinreg(r)) {
int nvsize=size>live.state[r].validsize?size:live.state[r].validsize;
int ndsize=size>live.state[r].dirtysize?size:live.state[r].dirtysize;
n=live.state[r].realreg;
Dif (live.nat[n].nholds!=1)
abort();
switch(size) {
case 1:
if (live.nat[n].canbyte || spec>=0) {
live.state[r].dirtysize=ndsize;
live.state[r].validsize=nvsize;
answer=n;
}
break;
case 2:
if (live.nat[n].canword || spec>=0) {
live.state[r].dirtysize=ndsize;
live.state[r].validsize=nvsize;
answer=n;
}
break;
case 4:
live.state[r].dirtysize=ndsize;
live.state[r].validsize=nvsize;
answer=n;
break;
default: abort();
}
if (answer<0)
evict(r);
}
/* either the value was in memory to start with, or it was evicted and
is in memory now */
if (answer<0) {
answer=alloc_reg_hinted(r,size,1,spec);
}
if (spec>=0 && spec!=answer) {
mov_nregs(spec,answer);
answer=spec;
}
if (live.state[r].status==UNDEF)
live.state[r].validsize=4;
live.state[r].dirtysize=size>live.state[r].dirtysize?size:live.state[r].dirtysize;
live.state[r].validsize=size>live.state[r].validsize?size:live.state[r].validsize;
live.nat[answer].locked++;
live.nat[answer].touched=touchcnt++;
if (size==4) {
live.state[r].val=0;
}
else {
Dif (live.state[r].val) {
write_log ("JIT: Problem with val\n");
abort();
}
}
set_status(r,DIRTY);
return answer;
}
static int writereg(int r, int size)
{
return writereg_general(r,size,-1);
}
static int writereg_specific(int r, int size, int spec)
{
return writereg_general(r,size,spec);
}
STATIC_INLINE int rmw_general(int r, int wsize, int rsize, int spec)
{
int n;
int answer=-1;
if (live.state[r].status==UNDEF) {
write_log ("JIT: WARNING: Unexpected read of undefined register %d\n", r);
}
remove_offset(r,spec);
make_exclusive(r,0,spec);
Dif (wsize<rsize) {
write_log ("JIT: Cannot handle wsize<rsize in rmw_general()\n");
abort();
}
if (isinreg(r) && live.state[r].validsize>=rsize) {
n=live.state[r].realreg;
Dif (live.nat[n].nholds!=1)
abort();
switch(rsize) {
case 1:
if (live.nat[n].canbyte || spec>=0) {
answer=n;
}
break;
case 2:
if (live.nat[n].canword || spec>=0) {
answer=n;
}
break;
case 4:
answer=n;
break;
default: abort();
}
if (answer<0)
evict(r);
}
/* either the value was in memory to start with, or it was evicted and
is in memory now */
if (answer<0) {
answer=alloc_reg_hinted(r,spec>=0?4:rsize,0,spec);
}
if (spec>=0 && spec!=answer) {
/* Too bad */
mov_nregs(spec,answer);
answer=spec;
}
if (wsize>live.state[r].dirtysize)
live.state[r].dirtysize=wsize;
if (wsize>live.state[r].validsize)
live.state[r].validsize=wsize;
set_status(r,DIRTY);
live.nat[answer].locked++;
live.nat[answer].touched=touchcnt++;
Dif (live.state[r].val) {
write_log ("JIT: Problem with val(rmw)\n");
abort();
}
return answer;
}
static int rmw(int r, int wsize, int rsize)
{
return rmw_general(r,wsize,rsize,-1);
}
static int rmw_specific(int r, int wsize, int rsize, int spec)
{
return rmw_general(r,wsize,rsize,spec);
}
/* needed for restoring the carry flag on non-P6 cores */
static void bt_l_ri_noclobber(R4 r, IMM i)
{
int size=4;
if (i<16)
size=2;
r=readreg(r,size);
raw_bt_l_ri(r,i);
unlock(r);
}
/********************************************************************
* FPU register status handling. EMIT TIME! *
********************************************************************/
static void f_tomem(int r)
{
if (live.fate[r].status==DIRTY) {
#if USE_LONG_DOUBLE
raw_fmov_ext_mr((uae_uintptr)live.fate[r].mem,live.fate[r].realreg);
#else
raw_fmov_mr((uae_uintptr)live.fate[r].mem,live.fate[r].realreg);
#endif
live.fate[r].status=CLEAN;
}
}
static void f_tomem_drop(int r)
{
if (live.fate[r].status==DIRTY) {
#if USE_LONG_DOUBLE
raw_fmov_ext_mr_drop((uae_uintptr)live.fate[r].mem,live.fate[r].realreg);
#else
raw_fmov_mr_drop((uae_uintptr)live.fate[r].mem,live.fate[r].realreg);
#endif
live.fate[r].status=INMEM;
}
}
STATIC_INLINE int f_isinreg(int r)
{
return live.fate[r].status==CLEAN || live.fate[r].status==DIRTY;
}
static void f_evict(int r)
{
int rr;
if (!f_isinreg(r))
return;
rr=live.fate[r].realreg;
if (live.fat[rr].nholds==1)
f_tomem_drop(r);
else
f_tomem(r);
Dif (live.fat[rr].locked &&
live.fat[rr].nholds==1) {
write_log ("JIT: FPU register %d in nreg %d is locked!\n", r, live.fate[r].realreg);
abort();
}
live.fat[rr].nholds--;
if (live.fat[rr].nholds!=live.fate[r].realind) { /* Was not last */
int topreg=live.fat[rr].holds[live.fat[rr].nholds];
int thisind=live.fate[r].realind;
live.fat[rr].holds[thisind]=topreg;
live.fate[topreg].realind=thisind;
}
live.fate[r].status=INMEM;
live.fate[r].realreg=-1;
}
STATIC_INLINE void f_free_nreg(int r)
{
int i=live.fat[r].nholds;
while (i) {
int vr;
--i;
vr=live.fat[r].holds[i];
f_evict(vr);
}
Dif (live.fat[r].nholds!=0) {
write_log ("JIT: Failed to free nreg %d, nholds is %d\n", r, live.fat[r].nholds);
abort();
}
}
/* Use with care! */
STATIC_INLINE void f_isclean(int r)
{
if (!f_isinreg(r))
return;
live.fate[r].status=CLEAN;
}
STATIC_INLINE void f_disassociate(int r)
{
f_isclean(r);
f_evict(r);
}
static int f_alloc_reg(int r, int willclobber)
{
int bestreg;
uae_s32 when;
int i;
uae_s32 badness;
bestreg=-1;
when=2000000000;
for (i=N_FREGS;i--;) {
badness=live.fat[i].touched;
if (live.fat[i].nholds==0)
badness=0;
if (!live.fat[i].locked && badness<when) {
bestreg=i;
when=badness;
if (live.fat[i].nholds==0)
break;
}
}
Dif (bestreg==-1)
abort();
if (live.fat[bestreg].nholds>0) {
f_free_nreg(bestreg);
}
if (f_isinreg(r)) {
f_evict(r);
}
if (!willclobber) {
if (live.fate[r].status!=UNDEF) {
#if USE_LONG_DOUBLE
raw_fmov_ext_rm(bestreg,(uae_uintptr)live.fate[r].mem);
#else
raw_fmov_rm(bestreg,(uae_uintptr)live.fate[r].mem);
#endif
}
live.fate[r].status=CLEAN;
}
else {
live.fate[r].status=DIRTY;
}
live.fate[r].realreg=bestreg;
live.fate[r].realind=live.fat[bestreg].nholds;
live.fat[bestreg].touched=touchcnt++;
live.fat[bestreg].holds[live.fat[bestreg].nholds]=r;
live.fat[bestreg].nholds++;
return bestreg;
}
static void f_unlock(int r)
{
Dif (!live.fat[r].locked)
abort();
live.fat[r].locked--;
}
static void f_setlock(int r)
{
live.fat[r].locked++;
}
static __inline__ int f_readreg(int r)
{
int n;
int answer=-1;
if (f_isinreg(r)) {
n=live.fate[r].realreg;
answer=n;
}
/* either the value was in memory to start with, or it was evicted and
is in memory now */
if (answer<0)
answer=f_alloc_reg(r,0);
live.fat[answer].locked++;
live.fat[answer].touched=touchcnt++;
return answer;
}
static __inline__ void f_make_exclusive(int r, int clobber)
{
freg_status oldstate;
int rr=live.fate[r].realreg;
int nr;
int nind;
int ndirt=0;
int i;
if (!f_isinreg(r))
return;
if (live.fat[rr].nholds==1)
return;
for (i=0;i<live.fat[rr].nholds;i++) {
int vr=live.fat[rr].holds[i];
if (vr!=r && live.fate[vr].status==DIRTY)
ndirt++;
}
if (!ndirt && !live.fat[rr].locked) {
/* Everything else is clean, so let's keep this register */
for (i=0;i<live.fat[rr].nholds;i++) {
int vr=live.fat[rr].holds[i];
if (vr!=r) {
f_evict(vr);
i--; /* Try that index again! */
}
}
Dif (live.fat[rr].nholds!=1) {
write_log ("JIT: realreg %d holds %d (", rr, live.fat[rr].nholds);
for (i=0;i<live.fat[rr].nholds;i++) {
write_log (" %d(%d,%d)", live.fat[rr].holds[i],
live.fate[live.fat[rr].holds[i]].realreg,
live.fate[live.fat[rr].holds[i]].realind);
}
write_log ("\n");
abort();
}
return;
}
/* We have to split the register */
oldstate=live.fate[r];
f_setlock(rr); /* Make sure this doesn't go away */
/* Forget about r being in the register rr */
f_disassociate(r);
/* Get a new register, that we will clobber completely */
nr=f_alloc_reg(r,1);
nind=live.fate[r].realind;
if (!clobber)
raw_fmov_rr(nr,rr); /* Make another copy */
live.fate[r]=oldstate; /* Keep all the old state info */
live.fate[r].realreg=nr;
live.fate[r].realind=nind;
f_unlock(rr);
}
static __inline__ int f_writereg(int r)
{
int n;
int answer=-1;
f_make_exclusive(r,1);
if (f_isinreg(r)) {
n=live.fate[r].realreg;
answer=n;
}
if (answer<0) {
answer=f_alloc_reg(r,1);
}
live.fate[r].status=DIRTY;
live.fat[answer].locked++;
live.fat[answer].touched=touchcnt++;
return answer;
}
static int f_rmw(int r)
{
int n;
f_make_exclusive(r,0);
if (f_isinreg(r)) {
n=live.fate[r].realreg;
}
else
n=f_alloc_reg(r,0);
live.fate[r].status=DIRTY;
live.fat[n].locked++;
live.fat[n].touched=touchcnt++;
return n;
}
static void fflags_into_flags_internal(uae_u32 tmp)
{
int r;
clobber_flags();
r=f_readreg(FP_RESULT);
raw_fflags_into_flags(r);
f_unlock(r);
}
/********************************************************************
* CPU functions exposed to gencomp. Both CREATE and EMIT time *
********************************************************************/
/*
* RULES FOR HANDLING REGISTERS:
*
* * In the function headers, order the parameters
* - 1st registers written to
* - 2nd read/modify/write registers
* - 3rd registers read from
* * Before calling raw_*, you must call readreg, writereg or rmw for
* each register
* * The order for this is
* - 1st call remove_offset for all registers written to with size<4
* - 2nd call readreg for all registers read without offset
* - 3rd call rmw for all rmw registers
* - 4th call readreg_offset for all registers that can handle offsets
* - 5th call get_offset for all the registers from the previous step
* - 6th call writereg for all written-to registers
* - 7th call raw_*
* - 8th unlock all registers that were locked
*/
MIDFUNC(0,live_flags,(void))
{
live.flags_on_stack=TRASH;
live.flags_in_flags=VALID;
live.flags_are_important=1;
}
MENDFUNC(0,live_flags,(void))
MIDFUNC(0,dont_care_flags,(void))
{
live.flags_are_important=0;
}
MENDFUNC(0,dont_care_flags,(void))
/*
* Copy m68k C flag into m68k X flag
*
* FIXME: This needs to be moved into the machdep
* part of the source because it depends on what bit
* is used to hold X.
*/
MIDFUNC(0,duplicate_carry,(void))
{
evict(FLAGX);
make_flags_live_internal();
COMPCALL(setcc_m)((uae_uintptr)live.state[FLAGX].mem + 1, 2);
}
MENDFUNC(0,duplicate_carry,(void))
/*
* Set host C flag from m68k X flag.
*
* FIXME: This needs to be moved into the machdep
* part of the source because it depends on what bit
* is used to hold X.
*/
MIDFUNC(0,restore_carry,(void))
{
if (!have_rat_stall) { /* Not a P6 core, i.e. no partial stalls */
bt_l_ri_noclobber(FLAGX, 8);
}
else { /* Avoid the stall the above creates.
This is slow on non-P6, though.
*/
COMPCALL(rol_w_ri(FLAGX, 8));
isclean(FLAGX);
/* Why is the above faster than the below? */
//raw_rol_b_mi((uae_uintptr)live.state[FLAGX].mem,8);
}
}
MENDFUNC(0,restore_carry,(void))
MIDFUNC(0,start_needflags,(void))
{
needflags=1;
}
MENDFUNC(0,start_needflags,(void))
MIDFUNC(0,end_needflags,(void))
{
needflags=0;
}
MENDFUNC(0,end_needflags,(void))
MIDFUNC(0,make_flags_live,(void))
{
make_flags_live_internal();
}
MENDFUNC(0,make_flags_live,(void))
MIDFUNC(1,fflags_into_flags,(W2 tmp))
{
clobber_flags();
fflags_into_flags_internal(tmp);
}
MENDFUNC(1,fflags_into_flags,(W2 tmp))
MIDFUNC(2,bt_l_ri,(R4 r, IMM i)) /* This is defined as only affecting C */
{
int size=4;
if (i<16)
size=2;
CLOBBER_BT;
r=readreg(r,size);
raw_bt_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,bt_l_ri,(R4 r, IMM i)) /* This is defined as only affecting C */
MIDFUNC(2,bt_l_rr,(R4 r, R4 b)) /* This is defined as only affecting C */
{
CLOBBER_BT;
r=readreg(r,4);
b=readreg(b,4);
raw_bt_l_rr(r,b);
unlock(r);
unlock(b);
}
MENDFUNC(2,bt_l_rr,(R4 r, R4 b)) /* This is defined as only affecting C */
MIDFUNC(2,btc_l_ri,(RW4 r, IMM i))
{
int size=4;
if (i<16)
size=2;
CLOBBER_BT;
r=rmw(r,size,size);
raw_btc_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,btc_l_ri,(RW4 r, IMM i))
MIDFUNC(2,btc_l_rr,(RW4 r, R4 b))
{
CLOBBER_BT;
b=readreg(b,4);
r=rmw(r,4,4);
raw_btc_l_rr(r,b);
unlock(r);
unlock(b);
}
MENDFUNC(2,btc_l_rr,(RW4 r, R4 b))
MIDFUNC(2,btr_l_ri,(RW4 r, IMM i))
{
int size=4;
if (i<16)
size=2;
CLOBBER_BT;
r=rmw(r,size,size);
raw_btr_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,btr_l_ri,(RW4 r, IMM i))
MIDFUNC(2,btr_l_rr,(RW4 r, R4 b))
{
CLOBBER_BT;
b=readreg(b,4);
r=rmw(r,4,4);
raw_btr_l_rr(r,b);
unlock(r);
unlock(b);
}
MENDFUNC(2,btr_l_rr,(RW4 r, R4 b))
MIDFUNC(2,bts_l_ri,(RW4 r, IMM i))
{
int size=4;
if (i<16)
size=2;
CLOBBER_BT;
r=rmw(r,size,size);
raw_bts_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,bts_l_ri,(RW4 r, IMM i))
MIDFUNC(2,bts_l_rr,(RW4 r, R4 b))
{
CLOBBER_BT;
b=readreg(b,4);
r=rmw(r,4,4);
raw_bts_l_rr(r,b);
unlock(r);
unlock(b);
}
MENDFUNC(2,bts_l_rr,(RW4 r, R4 b))
MIDFUNC(2,mov_l_rm,(W4 d, IMM s))
{
CLOBBER_MOV;
d=writereg(d,4);
raw_mov_l_rm(d,s);
unlock(d);
}
MENDFUNC(2,mov_l_rm,(W4 d, IMM s))
MIDFUNC(1,call_r,(R4 r)) /* Clobbering is implicit */
{
r=readreg(r,4);
raw_call_r(r);
unlock(r);
}
MENDFUNC(1,call_r,(R4 r)) /* Clobbering is implicit */
MIDFUNC(2,sub_l_mi,(IMM d, IMM s))
{
CLOBBER_SUB;
raw_sub_l_mi(d,s) ;
}
MENDFUNC(2,sub_l_mi,(IMM d, IMM s))
MIDFUNC(2,mov_l_mi,(IMM d, IMM s))
{
CLOBBER_MOV;
raw_mov_l_mi(d,s) ;
}
MENDFUNC(2,mov_l_mi,(IMM d, IMM s))
MIDFUNC(2,mov_w_mi,(IMM d, IMM s))
{
CLOBBER_MOV;
raw_mov_w_mi(d,s) ;
}
MENDFUNC(2,mov_w_mi,(IMM d, IMM s))
MIDFUNC(2,mov_b_mi,(IMM d, IMM s))
{
CLOBBER_MOV;
raw_mov_b_mi(d,s) ;
}
MENDFUNC(2,mov_b_mi,(IMM d, IMM s))
MIDFUNC(2,rol_b_ri,(RW1 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROL;
r=rmw(r,1,1);
raw_rol_b_ri(r,i);
unlock(r);
}
MENDFUNC(2,rol_b_ri,(RW1 r, IMM i))
MIDFUNC(2,rol_w_ri,(RW2 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROL;
r=rmw(r,2,2);
raw_rol_w_ri(r,i);
unlock(r);
}
MENDFUNC(2,rol_w_ri,(RW2 r, IMM i))
MIDFUNC(2,rol_l_ri,(RW4 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROL;
r=rmw(r,4,4);
raw_rol_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,rol_l_ri,(RW4 r, IMM i))
MIDFUNC(2,rol_l_rr,(RW4 d, R1 r))
{
if (isconst(r)) {
COMPCALL(rol_l_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,4,4);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_rol_l_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,rol_l_rr,(RW4 d, R1 r))
MIDFUNC(2,rol_w_rr,(RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(rol_w_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,2,2);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_rol_w_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,rol_w_rr,(RW2 d, R1 r))
MIDFUNC(2,rol_b_rr,(RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(rol_b_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,1,1);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_rol_b_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,rol_b_rr,(RW1 d, R1 r))
MIDFUNC(2,shll_l_rr,(RW4 d, R1 r))
{
if (isconst(r)) {
COMPCALL(shll_l_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHLL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,4,4);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_shll_l_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shll_l_rr,(RW4 d, R1 r))
MIDFUNC(2,shll_w_rr,(RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shll_w_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHLL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,2,2);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shll_b\n", r);
abort();
}
raw_shll_w_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shll_w_rr,(RW2 d, R1 r))
MIDFUNC(2,shll_b_rr,(RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shll_b_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHLL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,1,1);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shll_b\n", r);
abort();
}
raw_shll_b_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shll_b_rr,(RW1 d, R1 r))
MIDFUNC(2,ror_b_ri,(R1 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROR;
r=rmw(r,1,1);
raw_ror_b_ri(r,i);
unlock(r);
}
MENDFUNC(2,ror_b_ri,(R1 r, IMM i))
MIDFUNC(2,ror_w_ri,(R2 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROR;
r=rmw(r,2,2);
raw_ror_w_ri(r,i);
unlock(r);
}
MENDFUNC(2,ror_w_ri,(R2 r, IMM i))
MIDFUNC(2,ror_l_ri,(R4 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ROR;
r=rmw(r,4,4);
raw_ror_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,ror_l_ri,(R4 r, IMM i))
MIDFUNC(2,ror_l_rr,(R4 d, R1 r))
{
if (isconst(r)) {
COMPCALL(ror_l_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROR;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,4,4);
raw_ror_l_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,ror_l_rr,(R4 d, R1 r))
MIDFUNC(2,ror_w_rr,(R2 d, R1 r))
{
if (isconst(r)) {
COMPCALL(ror_w_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROR;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,2,2);
raw_ror_w_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,ror_w_rr,(R2 d, R1 r))
MIDFUNC(2,ror_b_rr,(R1 d, R1 r))
{
if (isconst(r)) {
COMPCALL(ror_b_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_ROR;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,1,1);
raw_ror_b_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,ror_b_rr,(R1 d, R1 r))
MIDFUNC(2,shrl_l_rr,(RW4 d, R1 r))
{
if (isconst(r)) {
COMPCALL(shrl_l_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,4,4);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_shrl_l_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shrl_l_rr,(RW4 d, R1 r))
MIDFUNC(2,shrl_w_rr,(RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shrl_w_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,2,2);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shrl_b\n", r);
abort();
}
raw_shrl_w_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shrl_w_rr,(RW2 d, R1 r))
MIDFUNC(2,shrl_b_rr,(RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shrl_b_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRL;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,1,1);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shrl_b\n", r);
abort();
}
raw_shrl_b_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shrl_b_rr,(RW1 d, R1 r))
MIDFUNC(2,shll_l_ri,(RW4 r, IMM i))
{
if (!i && !needflags)
return;
if (isconst(r) && !needflags) {
live.state[r].val<<=i;
return;
}
CLOBBER_SHLL;
r=rmw(r,4,4);
raw_shll_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,shll_l_ri,(RW4 r, IMM i))
MIDFUNC(2,shll_w_ri,(RW2 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHLL;
r=rmw(r,2,2);
raw_shll_w_ri(r,i);
unlock(r);
}
MENDFUNC(2,shll_w_ri,(RW2 r, IMM i))
MIDFUNC(2,shll_b_ri,(RW1 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHLL;
r=rmw(r,1,1);
raw_shll_b_ri(r,i);
unlock(r);
}
MENDFUNC(2,shll_b_ri,(RW1 r, IMM i))
MIDFUNC(2,shrl_l_ri,(RW4 r, IMM i))
{
if (!i && !needflags)
return;
if (isconst(r) && !needflags) {
live.state[r].val>>=i;
return;
}
CLOBBER_SHRL;
r=rmw(r,4,4);
raw_shrl_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,shrl_l_ri,(RW4 r, IMM i))
MIDFUNC(2,shrl_w_ri,(RW2 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHRL;
r=rmw(r,2,2);
raw_shrl_w_ri(r,i);
unlock(r);
}
MENDFUNC(2,shrl_w_ri,(RW2 r, IMM i))
MIDFUNC(2,shrl_b_ri,(RW1 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHRL;
r=rmw(r,1,1);
raw_shrl_b_ri(r,i);
unlock(r);
}
MENDFUNC(2,shrl_b_ri,(RW1 r, IMM i))
MIDFUNC(2,shra_l_ri,(RW4 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHRA;
r=rmw(r,4,4);
raw_shra_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,shra_l_ri,(RW4 r, IMM i))
MIDFUNC(2,shra_w_ri,(RW2 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHRA;
r=rmw(r,2,2);
raw_shra_w_ri(r,i);
unlock(r);
}
MENDFUNC(2,shra_w_ri,(RW2 r, IMM i))
MIDFUNC(2,shra_b_ri,(RW1 r, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SHRA;
r=rmw(r,1,1);
raw_shra_b_ri(r,i);
unlock(r);
}
MENDFUNC(2,shra_b_ri,(RW1 r, IMM i))
MIDFUNC(2,shra_l_rr,(RW4 d, R1 r))
{
if (isconst(r)) {
COMPCALL(shra_l_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRA;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,4,4);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_rol_b\n", r);
abort();
}
raw_shra_l_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shra_l_rr,(RW4 d, R1 r))
MIDFUNC(2,shra_w_rr,(RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shra_w_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRA;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,2,2);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shra_b\n", r);
abort();
}
raw_shra_w_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shra_w_rr,(RW2 d, R1 r))
MIDFUNC(2,shra_b_rr,(RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
if (isconst(r)) {
COMPCALL(shra_b_ri)(d,(uae_u8)live.state[r].val);
return;
}
CLOBBER_SHRA;
r=readreg_specific(r,1,SHIFTCOUNT_NREG);
d=rmw(d,1,1);
Dif (r!=1) {
write_log ("JIT: Illegal register %d in raw_shra_b\n", r);
abort();
}
raw_shra_b_rr(d,r) ;
unlock(r);
unlock(d);
}
MENDFUNC(2,shra_b_rr,(RW1 d, R1 r))
MIDFUNC(2,setcc,(W1 d, IMM cc))
{
CLOBBER_SETCC;
d=writereg(d,1);
raw_setcc(d,cc);
unlock(d);
}
MENDFUNC(2,setcc,(W1 d, IMM cc))
MIDFUNC(2,setcc_m,(IMM d, IMM cc))
{
CLOBBER_SETCC;
raw_setcc_m(d,cc);
}
MENDFUNC(2,setcc_m,(IMM d, IMM cc))
MIDFUNC(3,cmov_l_rr,(RW4 d, R4 s, IMM cc))
{
if (d==s)
return;
CLOBBER_CMOV;
s=readreg(s,4);
d=rmw(d,4,4);
raw_cmov_l_rr(d,s,cc);
unlock(s);
unlock(d);
}
MENDFUNC(3,cmov_l_rr,(RW4 d, R4 s, IMM cc))
MIDFUNC(1,setzflg_l,(RW4 r))
{
if (setzflg_uses_bsf) {
CLOBBER_BSF;
r=rmw(r,4,4);
raw_bsf_l_rr(r,r);
unlock(r);
}
else {
Dif (live.flags_in_flags!=VALID) {
write_log ("JIT: setzflg() wanted flags in native flags, they are %d\n",
live.flags_in_flags);
abort();
}
r=readreg(r,4);
{
int f=writereg(S11,4);
int t=writereg(S12,4);
raw_flags_set_zero(f,r,t);
unlock(f);
unlock(r);
unlock(t);
}
}
}
MENDFUNC(1,setzflg_l,(RW4 r))
MIDFUNC(3,cmov_l_rm,(RW4 d, IMM s, IMM cc))
{
CLOBBER_CMOV;
d=rmw(d,4,4);
raw_cmov_l_rm(d,s,cc);
unlock(d);
}
MENDFUNC(3,cmov_l_rm,(RW4 d, IMM s, IMM cc))
MIDFUNC(2,bsf_l_rr,(W4 d, R4 s))
{
CLOBBER_BSF;
s=readreg(s,4);
d=writereg(d,4);
raw_bsf_l_rr(d,s);
unlock(s);
unlock(d);
}
MENDFUNC(2,bsf_l_rr,(W4 d, R4 s))
MIDFUNC(2,imul_32_32,(RW4 d, R4 s))
{
CLOBBER_MUL;
s=readreg(s,4);
d=rmw(d,4,4);
raw_imul_32_32(d,s);
unlock(s);
unlock(d);
}
MENDFUNC(2,imul_32_32,(RW4 d, R4 s))
MIDFUNC(2,imul_64_32,(RW4 d, RW4 s))
{
CLOBBER_MUL;
s=rmw_specific(s,4,4,MUL_NREG2);
d=rmw_specific(d,4,4,MUL_NREG1);
raw_imul_64_32(d,s);
unlock(s);
unlock(d);
}
MENDFUNC(2,imul_64_32,(RW4 d, RW4 s))
MIDFUNC(2,mul_64_32,(RW4 d, RW4 s))
{
CLOBBER_MUL;
s=rmw_specific(s,4,4,MUL_NREG2);
d=rmw_specific(d,4,4,MUL_NREG1);
raw_mul_64_32(d,s);
unlock(s);
unlock(d);
}
MENDFUNC(2,mul_64_32,(RW4 d, RW4 s))
MIDFUNC(2,mul_32_32,(RW4 d, R4 s))
{
CLOBBER_MUL;
s=readreg(s,4);
d=rmw(d,4,4);
raw_mul_32_32(d,s);
unlock(s);
unlock(d);
}
MENDFUNC(2,mul_32_32,(RW4 d, R4 s))
MIDFUNC(2,sign_extend_16_rr,(W4 d, R2 s))
{
int isrmw;
if (isconst(s)) {
set_const(d,(uae_s32)(uae_s16)live.state[s].val);
return;
}
CLOBBER_SE16;
isrmw=(s==d);
if (!isrmw) {
s=readreg(s,2);
d=writereg(d,4);
}
else { /* If we try to lock this twice, with different sizes, we
are int trouble! */
s=d=rmw(s,4,2);
}
raw_sign_extend_16_rr(d,s);
if (!isrmw) {
unlock(d);
unlock(s);
}
else {
unlock(s);
}
}
MENDFUNC(2,sign_extend_16_rr,(W4 d, R2 s))
MIDFUNC(2,sign_extend_8_rr,(W4 d, R1 s))
{
int isrmw;
if (isconst(s)) {
set_const(d,(uae_s32)(uae_s8)live.state[s].val);
return;
}
isrmw=(s==d);
CLOBBER_SE8;
if (!isrmw) {
s=readreg(s,1);
d=writereg(d,4);
}
else { /* If we try to lock this twice, with different sizes, we
are int trouble! */
s=d=rmw(s,4,1);
}
raw_sign_extend_8_rr(d,s);
if (!isrmw) {
unlock(d);
unlock(s);
}
else {
unlock(s);
}
}
MENDFUNC(2,sign_extend_8_rr,(W4 d, R1 s))
MIDFUNC(2,zero_extend_16_rr,(W4 d, R2 s))
{
int isrmw;
if (isconst(s)) {
set_const(d,(uae_u32)(uae_u16)live.state[s].val);
return;
}
isrmw=(s==d);
CLOBBER_ZE16;
if (!isrmw) {
s=readreg(s,2);
d=writereg(d,4);
}
else { /* If we try to lock this twice, with different sizes, we
are int trouble! */
s=d=rmw(s,4,2);
}
raw_zero_extend_16_rr(d,s);
if (!isrmw) {
unlock(d);
unlock(s);
}
else {
unlock(s);
}
}
MENDFUNC(2,zero_extend_16_rr,(W4 d, R2 s))
MIDFUNC(2,zero_extend_8_rr,(W4 d, R1 s))
{
int isrmw;
if (isconst(s)) {
set_const(d,(uae_u32)(uae_u8)live.state[s].val);
return;
}
isrmw=(s==d);
CLOBBER_ZE8;
if (!isrmw) {
s=readreg(s,1);
d=writereg(d,4);
}
else { /* If we try to lock this twice, with different sizes, we
are int trouble! */
s=d=rmw(s,4,1);
}
raw_zero_extend_8_rr(d,s);
if (!isrmw) {
unlock(d);
unlock(s);
}
else {
unlock(s);
}
}
MENDFUNC(2,zero_extend_8_rr,(W4 d, R1 s))
MIDFUNC(2,mov_b_rr,(W1 d, R1 s))
{
if (d==s)
return;
if (isconst(s)) {
COMPCALL(mov_b_ri)(d,(uae_u8)live.state[s].val);
return;
}
CLOBBER_MOV;
s=readreg(s,1);
d=writereg(d,1);
raw_mov_b_rr(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,mov_b_rr,(W1 d, R1 s))
MIDFUNC(2,mov_w_rr,(W2 d, R2 s))
{
if (d==s)
return;
if (isconst(s)) {
COMPCALL(mov_w_ri)(d,(uae_u16)live.state[s].val);
return;
}
CLOBBER_MOV;
s=readreg(s,2);
d=writereg(d,2);
raw_mov_w_rr(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,mov_w_rr,(W2 d, R2 s))
MIDFUNC(4,mov_l_rrm_indexed,(W4 d,R4 baser, R4 index, IMM factor))
{
CLOBBER_MOV;
baser=readreg(baser,4);
index=readreg(index,4);
d=writereg(d,4);
raw_mov_l_rrm_indexed(d,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_l_rrm_indexed,(W4 d,R4 baser, R4 index, IMM factor))
MIDFUNC(4,mov_w_rrm_indexed,(W2 d, R4 baser, R4 index, IMM factor))
{
CLOBBER_MOV;
baser=readreg(baser,4);
index=readreg(index,4);
d=writereg(d,2);
raw_mov_w_rrm_indexed(d,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_w_rrm_indexed,(W2 d, R4 baser, R4 index, IMM factor))
MIDFUNC(4,mov_b_rrm_indexed,(W1 d, R4 baser, R4 index, IMM factor))
{
CLOBBER_MOV;
baser=readreg(baser,4);
index=readreg(index,4);
d=writereg(d,1);
raw_mov_b_rrm_indexed(d,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_b_rrm_indexed,(W1 d, R4 baser, R4 index, IMM factor))
MIDFUNC(4,mov_l_mrr_indexed,(R4 baser, R4 index, IMM factor, R4 s))
{
CLOBBER_MOV;
baser=readreg(baser,4);
index=readreg(index,4);
s=readreg(s,4);
Dif (baser==s || index==s)
abort();
raw_mov_l_mrr_indexed(baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_l_mrr_indexed,(R4 baser, R4 index, IMM factor, R4 s))
MIDFUNC(4,mov_w_mrr_indexed,(R4 baser, R4 index, IMM factor, R2 s))
{
CLOBBER_MOV;
baser=readreg(baser,4);
index=readreg(index,4);
s=readreg(s,2);
raw_mov_w_mrr_indexed(baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_w_mrr_indexed,(R4 baser, R4 index, IMM factor, R2 s))
MIDFUNC(4,mov_b_mrr_indexed,(R4 baser, R4 index, IMM factor, R1 s))
{
CLOBBER_MOV;
s=readreg(s,1);
baser=readreg(baser,4);
index=readreg(index,4);
raw_mov_b_mrr_indexed(baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(4,mov_b_mrr_indexed,(R4 baser, R4 index, IMM factor, R1 s))
MIDFUNC(5,mov_l_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R4 s))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
s=readreg(s,4);
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
raw_mov_l_bmrr_indexed(base,baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_l_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R4 s))
MIDFUNC(5,mov_w_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R2 s))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
s=readreg(s,2);
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
raw_mov_w_bmrr_indexed(base,baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_w_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R2 s))
MIDFUNC(5,mov_b_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R1 s))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
s=readreg(s,1);
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
raw_mov_b_bmrr_indexed(base,baser,index,factor,s);
unlock(s);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_b_bmrr_indexed,(IMM base, R4 baser, R4 index, IMM factor, R1 s))
/* Read a long from base+baser+factor*index */
MIDFUNC(5,mov_l_brrm_indexed,(W4 d, IMM base, R4 baser, R4 index, IMM factor))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
d=writereg(d,4);
raw_mov_l_brrm_indexed(d,base,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_l_brrm_indexed,(W4 d, IMM base, R4 baser, R4 index, IMM factor))
MIDFUNC(5,mov_w_brrm_indexed,(W2 d, IMM base, R4 baser, R4 index, IMM factor))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
remove_offset(d,-1);
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
d=writereg(d,2);
raw_mov_w_brrm_indexed(d,base,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_w_brrm_indexed,(W2 d, IMM base, R4 baser, R4 index, IMM factor))
MIDFUNC(5,mov_b_brrm_indexed,(W1 d, IMM base, R4 baser, R4 index, IMM factor))
{
int basereg=baser;
int indexreg=index;
CLOBBER_MOV;
remove_offset(d,-1);
baser=readreg_offset(baser,4);
index=readreg_offset(index,4);
base+=get_offset(basereg);
base+=factor*get_offset(indexreg);
d=writereg(d,1);
raw_mov_b_brrm_indexed(d,base,baser,index,factor);
unlock(d);
unlock(baser);
unlock(index);
}
MENDFUNC(5,mov_b_brrm_indexed,(W1 d, IMM base, R4 baser, R4 index, IMM factor))
/* Read a long from base+factor*index */
MIDFUNC(4,mov_l_rm_indexed,(W4 d, IMM base, R4 index, IMM factor))
{
int indexreg=index;
if (isconst(index)) {
COMPCALL(mov_l_rm)(d,base+factor*live.state[index].val);
return;
}
CLOBBER_MOV;
index=readreg_offset(index,4);
base+=get_offset(indexreg)*factor;
d=writereg(d,4);
raw_mov_l_rm_indexed(d,base,index,factor);
unlock(index);
unlock(d);
}
MENDFUNC(4,mov_l_rm_indexed,(W4 d, IMM base, R4 index, IMM factor))
/* read the long at the address contained in s+offset and store in d */
MIDFUNC(3,mov_l_rR,(W4 d, R4 s, IMM offset))
{
if (isconst(s)) {
COMPCALL(mov_l_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
d=writereg(d,4);
raw_mov_l_rR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_l_rR,(W4 d, R4 s, IMM offset))
/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3,mov_w_rR,(W2 d, R4 s, IMM offset))
{
if (isconst(s)) {
COMPCALL(mov_w_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
d=writereg(d,2);
raw_mov_w_rR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_w_rR,(W2 d, R4 s, IMM offset))
/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3,mov_b_rR,(W1 d, R4 s, IMM offset))
{
if (isconst(s)) {
COMPCALL(mov_b_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
d=writereg(d,1);
raw_mov_b_rR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_b_rR,(W1 d, R4 s, IMM offset))
/* read the long at the address contained in s+offset and store in d */
MIDFUNC(3,mov_l_brR,(W4 d, R4 s, IMM offset))
{
int sreg=s;
if (isconst(s)) {
COMPCALL(mov_l_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
s=readreg_offset(s,4);
offset+=get_offset(sreg);
d=writereg(d,4);
raw_mov_l_brR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_l_brR,(W4 d, R4 s, IMM offset))
/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3,mov_w_brR,(W2 d, R4 s, IMM offset))
{
int sreg=s;
if (isconst(s)) {
COMPCALL(mov_w_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
remove_offset(d,-1);
s=readreg_offset(s,4);
offset+=get_offset(sreg);
d=writereg(d,2);
raw_mov_w_brR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_w_brR,(W2 d, R4 s, IMM offset))
/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3,mov_b_brR,(W1 d, R4 s, IMM offset))
{
int sreg=s;
if (isconst(s)) {
COMPCALL(mov_b_rm)(d,live.state[s].val+offset);
return;
}
CLOBBER_MOV;
remove_offset(d,-1);
s=readreg_offset(s,4);
offset+=get_offset(sreg);
d=writereg(d,1);
raw_mov_b_brR(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_b_brR,(W1 d, R4 s, IMM offset))
MIDFUNC(3,mov_l_Ri,(R4 d, IMM i, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_l_mi)(live.state[d].val+offset,i);
return;
}
CLOBBER_MOV;
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_l_Ri(d,i,offset);
unlock(d);
}
MENDFUNC(3,mov_l_Ri,(R4 d, IMM i, IMM offset))
MIDFUNC(3,mov_w_Ri,(R4 d, IMM i, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_w_mi)(live.state[d].val+offset,i);
return;
}
CLOBBER_MOV;
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_w_Ri(d,i,offset);
unlock(d);
}
MENDFUNC(3,mov_w_Ri,(R4 d, IMM i, IMM offset))
MIDFUNC(3,mov_b_Ri,(R4 d, IMM i, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_b_mi)(live.state[d].val+offset,i);
return;
}
CLOBBER_MOV;
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_b_Ri(d,i,offset);
unlock(d);
}
MENDFUNC(3,mov_b_Ri,(R4 d, IMM i, IMM offset))
/* Warning! OFFSET is byte sized only! */
MIDFUNC(3,mov_l_Rr,(R4 d, R4 s, IMM offset))
{
if (isconst(d)) {
COMPCALL(mov_l_mr)(live.state[d].val+offset,s);
return;
}
if (isconst(s)) {
COMPCALL(mov_l_Ri)(d,live.state[s].val,offset);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
d=readreg(d,4);
raw_mov_l_Rr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_l_Rr,(R4 d, R4 s, IMM offset))
MIDFUNC(3,mov_w_Rr,(R4 d, R2 s, IMM offset))
{
if (isconst(d)) {
COMPCALL(mov_w_mr)(live.state[d].val+offset,s);
return;
}
if (isconst(s)) {
COMPCALL(mov_w_Ri)(d,(uae_u16)live.state[s].val,offset);
return;
}
CLOBBER_MOV;
s=readreg(s,2);
d=readreg(d,4);
raw_mov_w_Rr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_w_Rr,(R4 d, R2 s, IMM offset))
MIDFUNC(3,mov_b_Rr,(R4 d, R1 s, IMM offset))
{
if (isconst(d)) {
COMPCALL(mov_b_mr)(live.state[d].val+offset,s);
return;
}
if (isconst(s)) {
COMPCALL(mov_b_Ri)(d,(uae_u8)live.state[s].val,offset);
return;
}
CLOBBER_MOV;
s=readreg(s,1);
d=readreg(d,4);
raw_mov_b_Rr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_b_Rr,(R4 d, R1 s, IMM offset))
MIDFUNC(3,lea_l_brr,(W4 d, R4 s, IMM offset))
{
if (isconst(s)) {
COMPCALL(mov_l_ri)(d,live.state[s].val+offset);
return;
}
#if USE_OFFSET
if (d==s) {
add_offset(d,offset);
return;
}
#endif
CLOBBER_LEA;
s=readreg(s,4);
d=writereg(d,4);
raw_lea_l_brr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,lea_l_brr,(W4 d, R4 s, IMM offset))
MIDFUNC(5,lea_l_brr_indexed,(W4 d, R4 s, R4 index, IMM factor, IMM offset))
{
if (!offset) {
COMPCALL(lea_l_rr_indexed)(d,s,index,factor);
return;
}
CLOBBER_LEA;
s=readreg(s,4);
index=readreg(index,4);
d=writereg(d,4);
raw_lea_l_brr_indexed(d,s,index,factor,offset);
unlock(d);
unlock(index);
unlock(s);
}
MENDFUNC(5,lea_l_brr_indexed,(W4 d, R4 s, R4 index, IMM factor, IMM offset))
MIDFUNC(4,lea_l_rr_indexed,(W4 d, R4 s, R4 index, IMM factor))
{
CLOBBER_LEA;
s=readreg(s,4);
index=readreg(index,4);
d=writereg(d,4);
raw_lea_l_rr_indexed(d,s,index,factor);
unlock(d);
unlock(index);
unlock(s);
}
MENDFUNC(4,lea_l_rr_indexed,(W4 d, R4 s, R4 index, IMM factor))
/* write d to the long at the address contained in s+offset */
MIDFUNC(3,mov_l_bRr,(R4 d, R4 s, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_l_mr)(live.state[d].val+offset,s);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_l_bRr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_l_bRr,(R4 d, R4 s, IMM offset))
/* write the word at the address contained in s+offset and store in d */
MIDFUNC(3,mov_w_bRr,(R4 d, R2 s, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_w_mr)(live.state[d].val+offset,s);
return;
}
CLOBBER_MOV;
s=readreg(s,2);
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_w_bRr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_w_bRr,(R4 d, R2 s, IMM offset))
MIDFUNC(3,mov_b_bRr,(R4 d, R1 s, IMM offset))
{
int dreg=d;
if (isconst(d)) {
COMPCALL(mov_b_mr)(live.state[d].val+offset,s);
return;
}
CLOBBER_MOV;
s=readreg(s,1);
d=readreg_offset(d,4);
offset+=get_offset(dreg);
raw_mov_b_bRr(d,s,offset);
unlock(d);
unlock(s);
}
MENDFUNC(3,mov_b_bRr,(R4 d, R1 s, IMM offset))
MIDFUNC(1,gen_bswap_32,(RW4 r))
{
if (isconst(r)) {
uae_u32 oldv=live.state[r].val;
live.state[r].val=reverse32(oldv);
return;
}
CLOBBER_SW32;
r=rmw(r,4,4);
raw_bswap_32(r);
unlock(r);
}
MENDFUNC(1,gen_bswap_32,(RW4 r))
MIDFUNC(1,gen_bswap_16,(RW2 r))
{
if (isconst(r)) {
uae_u32 oldv=live.state[r].val;
live.state[r].val=((oldv>>8)&0xff) | ((oldv<<8)&0xff00) |
(oldv&0xffff0000);
return;
}
CLOBBER_SW16;
r=rmw(r,2,2);
raw_bswap_16(r);
unlock(r);
}
MENDFUNC(1,gen_bswap_16,(RW2 r))
MIDFUNC(2,mov_l_rr,(W4 d, R4 s))
{
int olds;
if (d==s) { /* How pointless! */
return;
}
if (isconst(s)) {
COMPCALL(mov_l_ri)(d,live.state[s].val);
return;
}
#if USE_ALIAS
olds=s;
disassociate(d);
s=readreg_offset(s,4);
live.state[d].realreg=s;
live.state[d].realind=live.nat[s].nholds;
live.state[d].val=live.state[olds].val;
live.state[d].validsize=4;
live.state[d].dirtysize=4;
set_status(d,DIRTY);
live.nat[s].holds[live.nat[s].nholds]=d;
live.nat[s].nholds++;
log_clobberreg(d);
/* printf("Added %d to nreg %d(%d), now holds %d regs\n",
d,s,live.state[d].realind,live.nat[s].nholds); */
unlock(s);
#else
CLOBBER_MOV;
s=readreg(s,4);
d=writereg(d,4);
raw_mov_l_rr(d,s);
unlock(d);
unlock(s);
#endif
}
MENDFUNC(2,mov_l_rr,(W4 d, R4 s))
MIDFUNC(2,mov_l_mr,(IMM d, R4 s))
{
if (isconst(s)) {
COMPCALL(mov_l_mi)(d,live.state[s].val);
return;
}
CLOBBER_MOV;
s=readreg(s,4);
raw_mov_l_mr(d,s);
unlock(s);
}
MENDFUNC(2,mov_l_mr,(IMM d, R4 s))
MIDFUNC(2,mov_w_mr,(IMM d, R2 s))
{
if (isconst(s)) {
COMPCALL(mov_w_mi)(d,(uae_u16)live.state[s].val);
return;
}
CLOBBER_MOV;
s=readreg(s,2);
raw_mov_w_mr(d,s);
unlock(s);
}
MENDFUNC(2,mov_w_mr,(IMM d, R2 s))
MIDFUNC(2,mov_w_rm,(W2 d, IMM s))
{
CLOBBER_MOV;
d=writereg(d,2);
raw_mov_w_rm(d,s);
unlock(d);
}
MENDFUNC(2,mov_w_rm,(W2 d, IMM s))
MIDFUNC(2,mov_b_mr,(IMM d, R1 s))
{
if (isconst(s)) {
COMPCALL(mov_b_mi)(d,(uae_u8)live.state[s].val);
return;
}
CLOBBER_MOV;
s=readreg(s,1);
raw_mov_b_mr(d,s);
unlock(s);
}
MENDFUNC(2,mov_b_mr,(IMM d, R1 s))
MIDFUNC(2,mov_b_rm,(W1 d, IMM s))
{
CLOBBER_MOV;
d=writereg(d,1);
raw_mov_b_rm(d,s);
unlock(d);
}
MENDFUNC(2,mov_b_rm,(W1 d, IMM s))
MIDFUNC(2,mov_l_ri,(W4 d, IMM s))
{
set_const(d,s);
return;
}
MENDFUNC(2,mov_l_ri,(W4 d, IMM s))
MIDFUNC(2,mov_w_ri,(W2 d, IMM s))
{
CLOBBER_MOV;
d=writereg(d,2);
raw_mov_w_ri(d,s);
unlock(d);
}
MENDFUNC(2,mov_w_ri,(W2 d, IMM s))
MIDFUNC(2,mov_b_ri,(W1 d, IMM s))
{
CLOBBER_MOV;
d=writereg(d,1);
raw_mov_b_ri(d,s);
unlock(d);
}
MENDFUNC(2,mov_b_ri,(W1 d, IMM s))
MIDFUNC(2,add_l_mi,(IMM d, IMM s))
{
CLOBBER_ADD;
raw_add_l_mi(d,s) ;
}
MENDFUNC(2,add_l_mi,(IMM d, IMM s))
MIDFUNC(2,add_w_mi,(IMM d, IMM s))
{
CLOBBER_ADD;
raw_add_w_mi(d,s) ;
}
MENDFUNC(2,add_w_mi,(IMM d, IMM s))
MIDFUNC(2,add_b_mi,(IMM d, IMM s))
{
CLOBBER_ADD;
raw_add_b_mi(d,s) ;
}
MENDFUNC(2,add_b_mi,(IMM d, IMM s))
MIDFUNC(2,test_l_ri,(R4 d, IMM i))
{
CLOBBER_TEST;
d=readreg(d,4);
raw_test_l_ri(d,i);
unlock(d);
}
MENDFUNC(2,test_l_ri,(R4 d, IMM i))
MIDFUNC(2,test_l_rr,(R4 d, R4 s))
{
CLOBBER_TEST;
d=readreg(d,4);
s=readreg(s,4);
raw_test_l_rr(d,s);;
unlock(d);
unlock(s);
}
MENDFUNC(2,test_l_rr,(R4 d, R4 s))
MIDFUNC(2,test_w_rr,(R2 d, R2 s))
{
CLOBBER_TEST;
d=readreg(d,2);
s=readreg(s,2);
raw_test_w_rr(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,test_w_rr,(R2 d, R2 s))
MIDFUNC(2,test_b_rr,(R1 d, R1 s))
{
CLOBBER_TEST;
d=readreg(d,1);
s=readreg(s,1);
raw_test_b_rr(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,test_b_rr,(R1 d, R1 s))
MIDFUNC(2,and_l_ri,(RW4 d, IMM i))
{
if (isconst (d) && ! needflags) {
live.state[d].val &= i;
return;
}
CLOBBER_AND;
d=rmw(d,4,4);
raw_and_l_ri(d,i);
unlock(d);
}
MENDFUNC(2,and_l_ri,(RW4 d, IMM i))
MIDFUNC(2,and_l,(RW4 d, R4 s))
{
CLOBBER_AND;
s=readreg(s,4);
d=rmw(d,4,4);
raw_and_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,and_l,(RW4 d, R4 s))
MIDFUNC(2,and_w,(RW2 d, R2 s))
{
CLOBBER_AND;
s=readreg(s,2);
d=rmw(d,2,2);
raw_and_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,and_w,(RW2 d, R2 s))
MIDFUNC(2,and_b,(RW1 d, R1 s))
{
CLOBBER_AND;
s=readreg(s,1);
d=rmw(d,1,1);
raw_and_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,and_b,(RW1 d, R1 s))
MIDFUNC(2,or_l_ri,(RW4 d, IMM i))
{
if (isconst(d) && !needflags) {
live.state[d].val|=i;
return;
}
CLOBBER_OR;
d=rmw(d,4,4);
raw_or_l_ri(d,i);
unlock(d);
}
MENDFUNC(2,or_l_ri,(RW4 d, IMM i))
MIDFUNC(2,or_l,(RW4 d, R4 s))
{
if (isconst(d) && isconst(s) && !needflags) {
live.state[d].val|=live.state[s].val;
return;
}
CLOBBER_OR;
s=readreg(s,4);
d=rmw(d,4,4);
raw_or_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,or_l,(RW4 d, R4 s))
MIDFUNC(2,or_w,(RW2 d, R2 s))
{
CLOBBER_OR;
s=readreg(s,2);
d=rmw(d,2,2);
raw_or_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,or_w,(RW2 d, R2 s))
MIDFUNC(2,or_b,(RW1 d, R1 s))
{
CLOBBER_OR;
s=readreg(s,1);
d=rmw(d,1,1);
raw_or_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,or_b,(RW1 d, R1 s))
MIDFUNC(2,adc_l,(RW4 d, R4 s))
{
CLOBBER_ADC;
s=readreg(s,4);
d=rmw(d,4,4);
raw_adc_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,adc_l,(RW4 d, R4 s))
MIDFUNC(2,adc_w,(RW2 d, R2 s))
{
CLOBBER_ADC;
s=readreg(s,2);
d=rmw(d,2,2);
raw_adc_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,adc_w,(RW2 d, R2 s))
MIDFUNC(2,adc_b,(RW1 d, R1 s))
{
CLOBBER_ADC;
s=readreg(s,1);
d=rmw(d,1,1);
raw_adc_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,adc_b,(RW1 d, R1 s))
MIDFUNC(2,add_l,(RW4 d, R4 s))
{
if (isconst(s)) {
COMPCALL(add_l_ri)(d,live.state[s].val);
return;
}
CLOBBER_ADD;
s=readreg(s,4);
d=rmw(d,4,4);
raw_add_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,add_l,(RW4 d, R4 s))
MIDFUNC(2,add_w,(RW2 d, R2 s))
{
if (isconst(s)) {
COMPCALL(add_w_ri)(d,(uae_u16)live.state[s].val);
return;
}
CLOBBER_ADD;
s=readreg(s,2);
d=rmw(d,2,2);
raw_add_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,add_w,(RW2 d, R2 s))
MIDFUNC(2,add_b,(RW1 d, R1 s))
{
if (isconst(s)) {
COMPCALL(add_b_ri)(d,(uae_u8)live.state[s].val);
return;
}
CLOBBER_ADD;
s=readreg(s,1);
d=rmw(d,1,1);
raw_add_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,add_b,(RW1 d, R1 s))
MIDFUNC(2,sub_l_ri,(RW4 d, IMM i))
{
if (!i && !needflags)
return;
if (isconst(d) && !needflags) {
live.state[d].val-=i;
return;
}
#if USE_OFFSET
if (!needflags) {
add_offset(d,-(signed)i);
return;
}
#endif
CLOBBER_SUB;
d=rmw(d,4,4);
raw_sub_l_ri(d,i);
unlock(d);
}
MENDFUNC(2,sub_l_ri,(RW4 d, IMM i))
MIDFUNC(2,sub_w_ri,(RW2 d, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SUB;
d=rmw(d,2,2);
raw_sub_w_ri(d,i);
unlock(d);
}
MENDFUNC(2,sub_w_ri,(RW2 d, IMM i))
MIDFUNC(2,sub_b_ri,(RW1 d, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_SUB;
d=rmw(d,1,1);
raw_sub_b_ri(d,i);
unlock(d);
}
MENDFUNC(2,sub_b_ri,(RW1 d, IMM i))
MIDFUNC(2,add_l_ri,(RW4 d, IMM i))
{
if (!i && !needflags)
return;
if (isconst(d) && !needflags) {
live.state[d].val+=i;
return;
}
#if USE_OFFSET
if (!needflags) {
add_offset(d,i);
return;
}
#endif
CLOBBER_ADD;
d=rmw(d,4,4);
raw_add_l_ri(d,i);
unlock(d);
}
MENDFUNC(2,add_l_ri,(RW4 d, IMM i))
MIDFUNC(2,add_w_ri,(RW2 d, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ADD;
d=rmw(d,2,2);
raw_add_w_ri(d,i);
unlock(d);
}
MENDFUNC(2,add_w_ri,(RW2 d, IMM i))
MIDFUNC(2,add_b_ri,(RW1 d, IMM i))
{
if (!i && !needflags)
return;
CLOBBER_ADD;
d=rmw(d,1,1);
raw_add_b_ri(d,i);
unlock(d);
}
MENDFUNC(2,add_b_ri,(RW1 d, IMM i))
MIDFUNC(2,sbb_l,(RW4 d, R4 s))
{
CLOBBER_SBB;
s=readreg(s,4);
d=rmw(d,4,4);
raw_sbb_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sbb_l,(RW4 d, R4 s))
MIDFUNC(2,sbb_w,(RW2 d, R2 s))
{
CLOBBER_SBB;
s=readreg(s,2);
d=rmw(d,2,2);
raw_sbb_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sbb_w,(RW2 d, R2 s))
MIDFUNC(2,sbb_b,(RW1 d, R1 s))
{
CLOBBER_SBB;
s=readreg(s,1);
d=rmw(d,1,1);
raw_sbb_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sbb_b,(RW1 d, R1 s))
MIDFUNC(2,sub_l,(RW4 d, R4 s))
{
if (isconst(s)) {
COMPCALL(sub_l_ri)(d,live.state[s].val);
return;
}
CLOBBER_SUB;
s=readreg(s,4);
d=rmw(d,4,4);
raw_sub_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sub_l,(RW4 d, R4 s))
MIDFUNC(2,sub_w,(RW2 d, R2 s))
{
if (isconst(s)) {
COMPCALL(sub_w_ri)(d,(uae_u16)live.state[s].val);
return;
}
CLOBBER_SUB;
s=readreg(s,2);
d=rmw(d,2,2);
raw_sub_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sub_w,(RW2 d, R2 s))
MIDFUNC(2,sub_b,(RW1 d, R1 s))
{
if (isconst(s)) {
COMPCALL(sub_b_ri)(d,(uae_u8)live.state[s].val);
return;
}
CLOBBER_SUB;
s=readreg(s,1);
d=rmw(d,1,1);
raw_sub_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,sub_b,(RW1 d, R1 s))
MIDFUNC(2,cmp_l,(R4 d, R4 s))
{
CLOBBER_CMP;
s=readreg(s,4);
d=readreg(d,4);
raw_cmp_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,cmp_l,(R4 d, R4 s))
MIDFUNC(2,cmp_l_ri,(R4 r, IMM i))
{
CLOBBER_CMP;
r=readreg(r,4);
raw_cmp_l_ri(r,i);
unlock(r);
}
MENDFUNC(2,cmp_l_ri,(R4 r, IMM i))
MIDFUNC(2,cmp_w,(R2 d, R2 s))
{
CLOBBER_CMP;
s=readreg(s,2);
d=readreg(d,2);
raw_cmp_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,cmp_w,(R2 d, R2 s))
MIDFUNC(2,cmp_b,(R1 d, R1 s))
{
CLOBBER_CMP;
s=readreg(s,1);
d=readreg(d,1);
raw_cmp_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,cmp_b,(R1 d, R1 s))
MIDFUNC(2,xor_l,(RW4 d, R4 s))
{
CLOBBER_XOR;
s=readreg(s,4);
d=rmw(d,4,4);
raw_xor_l(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,xor_l,(RW4 d, R4 s))
MIDFUNC(2,xor_w,(RW2 d, R2 s))
{
CLOBBER_XOR;
s=readreg(s,2);
d=rmw(d,2,2);
raw_xor_w(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,xor_w,(RW2 d, R2 s))
MIDFUNC(2,xor_b,(RW1 d, R1 s))
{
CLOBBER_XOR;
s=readreg(s,1);
d=rmw(d,1,1);
raw_xor_b(d,s);
unlock(d);
unlock(s);
}
MENDFUNC(2,xor_b,(RW1 d, R1 s))
MIDFUNC(5,call_r_11,(W4 out1, R4 r, R4 in1, IMM osize, IMM isize))
{
clobber_flags();
remove_all_offsets();
if (osize==4) {
if (out1!=in1 && out1!=r) {
COMPCALL(forget_about)(out1);
}
}
else {
tomem_c(out1);
}
in1=readreg_specific(in1,isize,REG_PAR1);
r=readreg(r,4);
prepare_for_call_1(); /* This should ensure that there won't be
any need for swapping nregs in prepare_for_call_2
*/
#if USE_NORMAL_CALLING_CONVENTION
raw_push_l_r(in1);
#endif
unlock(in1);
unlock(r);
prepare_for_call_2();
raw_call_r(r);
#if USE_NORMAL_CALLING_CONVENTION
raw_inc_sp(4);
#endif
live.nat[REG_RESULT].holds[0]=out1;
live.nat[REG_RESULT].nholds=1;
live.nat[REG_RESULT].touched=touchcnt++;
live.state[out1].realreg=REG_RESULT;
live.state[out1].realind=0;
live.state[out1].val=0;
live.state[out1].validsize=osize;
live.state[out1].dirtysize=osize;
set_status(out1,DIRTY);
}
MENDFUNC(5,call_r_11,(W4 out1, R4 r, R4 in1, IMM osize, IMM isize))
MIDFUNC(5,call_r_02,(R4 r, R4 in1, R4 in2, IMM isize1, IMM isize2))
{
clobber_flags();
remove_all_offsets();
in1=readreg_specific(in1,isize1,REG_PAR1);
in2=readreg_specific(in2,isize2,REG_PAR2);
r=readreg(r,4);
prepare_for_call_1(); /* This should ensure that there won't be
any need for swapping nregs in prepare_for_call_2
*/
#if USE_NORMAL_CALLING_CONVENTION
raw_push_l_r(in2);
raw_push_l_r(in1);
#endif
unlock(r);
unlock(in1);
unlock(in2);
prepare_for_call_2();
raw_call_r(r);
#if USE_NORMAL_CALLING_CONVENTION
raw_inc_sp(8);
#endif
}
MENDFUNC(5,call_r_02,(R4 r, R4 in1, R4 in2, IMM isize1, IMM isize2))
MIDFUNC(1,forget_about,(W4 r))
{
if (isinreg(r))
disassociate(r);
live.state[r].val=0;
set_status(r,UNDEF);
}
MENDFUNC(1,forget_about,(W4 r))
MIDFUNC(0,nop,(void))
{
raw_nop();
}
MENDFUNC(0,nop,(void))
MIDFUNC(1,f_forget_about,(FW r))
{
if (f_isinreg(r))
f_disassociate(r);
live.fate[r].status=UNDEF;
}
MENDFUNC(1,f_forget_about,(FW r))
MIDFUNC(1,fmov_pi,(FW r))
{
r=f_writereg(r);
raw_fmov_pi(r);
f_unlock(r);
}
MENDFUNC(1,fmov_pi,(FW r))
MIDFUNC(1,fmov_log10_2,(FW r))
{
r=f_writereg(r);
raw_fmov_log10_2(r);
f_unlock(r);
}
MENDFUNC(1,fmov_log10_2,(FW r))
MIDFUNC(1,fmov_log2_e,(FW r))
{
r=f_writereg(r);
raw_fmov_log2_e(r);
f_unlock(r);
}
MENDFUNC(1,fmov_log2_e,(FW r))
MIDFUNC(1,fmov_loge_2,(FW r))
{
r=f_writereg(r);
raw_fmov_loge_2(r);
f_unlock(r);
}
MENDFUNC(1,fmov_loge_2,(FW r))
MIDFUNC(1,fmov_1,(FW r))
{
r=f_writereg(r);
raw_fmov_1(r);
f_unlock(r);
}
MENDFUNC(1,fmov_1,(FW r))
MIDFUNC(1,fmov_0,(FW r))
{
r=f_writereg(r);
raw_fmov_0(r);
f_unlock(r);
}
MENDFUNC(1,fmov_0,(FW r))
MIDFUNC(2,fmov_rm,(FW r, MEMR m))
{
r=f_writereg(r);
raw_fmov_rm(r,m);
f_unlock(r);
}
MENDFUNC(2,fmov_rm,(FW r, MEMR m))
MIDFUNC(2,fmovi_rm,(FW r, MEMR m))
{
r=f_writereg(r);
raw_fmovi_rm(r,m);
f_unlock(r);
}
MENDFUNC(2,fmovi_rm,(FW r, MEMR m))
MIDFUNC(2,fmovi_mr,(MEMW m, FR r))
{
r=f_readreg(r);
raw_fmovi_mr(m,r);
f_unlock(r);
}
MENDFUNC(2,fmovi_mr,(MEMW m, FR r))
MIDFUNC(2,fmovs_rm,(FW r, MEMR m))
{
r=f_writereg(r);
raw_fmovs_rm(r,m);
f_unlock(r);
}
MENDFUNC(2,fmovs_rm,(FW r, MEMR m))
MIDFUNC(2,fmovs_mr,(MEMW m, FR r))
{
r=f_readreg(r);
raw_fmovs_mr(m,r);
f_unlock(r);
}
MENDFUNC(2,fmovs_mr,(MEMW m, FR r))
MIDFUNC(2,fmov_ext_mr,(MEMW m, FR r))
{
r=f_readreg(r);
raw_fmov_ext_mr(m,r);
f_unlock(r);
}
MENDFUNC(2,fmov_ext_mr,(MEMW m, FR r))
MIDFUNC(2,fmov_mr,(MEMW m, FR r))
{
r=f_readreg(r);
raw_fmov_mr(m,r);
f_unlock(r);
}
MENDFUNC(2,fmov_mr,(MEMW m, FR r))
MIDFUNC(2,fmov_ext_rm,(FW r, MEMR m))
{
r=f_writereg(r);
raw_fmov_ext_rm(r,m);
f_unlock(r);
}
MENDFUNC(2,fmov_ext_rm,(FW r, MEMR m))
MIDFUNC(2,fmov_rr,(FW d, FR s))
{
if (d==s) { /* How pointless! */
return;
}
#if USE_F_ALIAS
f_disassociate(d);
s=f_readreg(s);
live.fate[d].realreg=s;
live.fate[d].realind=live.fat[s].nholds;
live.fate[d].status=DIRTY;
live.fat[s].holds[live.fat[s].nholds]=d;
live.fat[s].nholds++;
f_unlock(s);
#else
s=f_readreg(s);
d=f_writereg(d);
raw_fmov_rr(d,s);
f_unlock(s);
f_unlock(d);
#endif
}
MENDFUNC(2,fmov_rr,(FW d, FR s))
MIDFUNC(2,fldcw_m_indexed,(R4 index, IMM base))
{
index=readreg(index,4);
raw_fldcw_m_indexed(index,base);
unlock(index);
}
MENDFUNC(2,fldcw_m_indexed,(R4 index, IMM base))
MIDFUNC(1,ftst_r,(FR r))
{
r=f_readreg(r);
raw_ftst_r(r);
f_unlock(r);
}
MENDFUNC(1,ftst_r,(FR r))
MIDFUNC(0,dont_care_fflags,(void))
{
f_disassociate(FP_RESULT);
}
MENDFUNC(0,dont_care_fflags,(void))
MIDFUNC(2,fsqrt_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fsqrt_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fsqrt_rr,(FW d, FR s))
MIDFUNC(2,fabs_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fabs_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fabs_rr,(FW d, FR s))
MIDFUNC(2,fsin_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fsin_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fsin_rr,(FW d, FR s))
MIDFUNC(2,fcos_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fcos_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fcos_rr,(FW d, FR s))
MIDFUNC(2,ftwotox_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_ftwotox_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,ftwotox_rr,(FW d, FR s))
MIDFUNC(2,fetox_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fetox_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fetox_rr,(FW d, FR s))
MIDFUNC(2,frndint_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_frndint_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,frndint_rr,(FW d, FR s))
MIDFUNC(2,flog2_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_flog2_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,flog2_rr,(FW d, FR s))
MIDFUNC(2,fneg_rr,(FW d, FR s))
{
s=f_readreg(s);
d=f_writereg(d);
raw_fneg_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fneg_rr,(FW d, FR s))
MIDFUNC(2,fadd_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_fadd_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fadd_rr,(FRW d, FR s))
MIDFUNC(2,fsub_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_fsub_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fsub_rr,(FRW d, FR s))
MIDFUNC(2,fcmp_rr,(FR d, FR s))
{
d=f_readreg(d);
s=f_readreg(s);
raw_fcmp_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fcmp_rr,(FR d, FR s))
MIDFUNC(2,fdiv_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_fdiv_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fdiv_rr,(FRW d, FR s))
MIDFUNC(2,frem_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_frem_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,frem_rr,(FRW d, FR s))
MIDFUNC(2,frem1_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_frem1_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,frem1_rr,(FRW d, FR s))
MIDFUNC(2,fmul_rr,(FRW d, FR s))
{
s=f_readreg(s);
d=f_rmw(d);
raw_fmul_rr(d,s);
f_unlock(s);
f_unlock(d);
}
MENDFUNC(2,fmul_rr,(FRW d, FR s))
/********************************************************************
* Support functions exposed to gencomp. CREATE time *
********************************************************************/
int kill_rodent(int r)
{
return KILLTHERAT &&
have_rat_stall &&
(live.state[r].status==INMEM ||
live.state[r].status==CLEAN ||
live.state[r].status==ISCONST ||
live.state[r].dirtysize==4);
}
uae_u32 get_const(int r)
{
Dif (!isconst(r)) {
write_log ("JIT: Register %d should be constant, but isn't\n", r);
abort();
}
return live.state[r].val;
}
void sync_m68k_pc(void)
{
if (m68k_pc_offset) {
add_l_ri(PC_P,m68k_pc_offset);
comp_pc_p+=m68k_pc_offset;
m68k_pc_offset=0;
}
}
/********************************************************************
* Support functions exposed to newcpu *
********************************************************************/
uae_u32 scratch[VREGS];
fptype fscratch[VFREGS];
void init_comp(void)
{
int i;
uae_u8* cb = (uae_u8 *)can_byte;
uae_u8* cw = (uae_u8 *)can_word;
uae_u8* au = (uae_u8 *)always_used;
for (i=0;i<VREGS;i++) {
live.state[i].realreg=-1;
live.state[i].needflush=NF_SCRATCH;
live.state[i].val=0;
set_status(i,UNDEF);
}
for (i=0;i<VFREGS;i++) {
live.fate[i].status=UNDEF;
live.fate[i].realreg=-1;
live.fate[i].needflush=NF_SCRATCH;
}
for (i=0;i<VREGS;i++) {
if (i<16) { /* First 16 registers map to 68k registers */
live.state[i].mem=((uae_u32*)&regs)+i;
live.state[i].needflush=NF_TOMEM;
set_status(i,INMEM);
}
else
live.state[i].mem=scratch+i;
}
live.state[PC_P].mem=(uae_u32*)&(regs.pc_p);
live.state[PC_P].needflush=NF_TOMEM;
set_const(PC_P,(uae_uintptr)comp_pc_p);
live.state[FLAGX].mem=&(regs.ccrflags.x);
live.state[FLAGX].needflush=NF_TOMEM;
set_status(FLAGX,INMEM);
live.state[FLAGTMP].mem=&(regs.ccrflags.cznv);
live.state[FLAGTMP].needflush=NF_TOMEM;
set_status(FLAGTMP,INMEM);
live.state[NEXT_HANDLER].needflush=NF_HANDLER;
set_status(NEXT_HANDLER,UNDEF);
for (i=0;i<VFREGS;i++) {
if (i<8) { /* First 8 registers map to 68k FPU registers */
live.fate[i].mem=(uae_u32*)(((fptype*)regs.fp)+i);
live.fate[i].needflush=NF_TOMEM;
live.fate[i].status=INMEM;
}
else if (i==FP_RESULT) {
live.fate[i].mem=(uae_u32*)(&regs.fp_result);
live.fate[i].needflush=NF_TOMEM;
live.fate[i].status=INMEM;
}
else
live.fate[i].mem=(uae_u32*)(fscratch+i);
}
for (i=0;i<N_REGS;i++) {
live.nat[i].touched=0;
live.nat[i].nholds=0;
live.nat[i].locked=0;
if (*cb==i) {
live.nat[i].canbyte=1; cb++;
} else live.nat[i].canbyte=0;
if (*cw==i) {
live.nat[i].canword=1; cw++;
} else live.nat[i].canword=0;
if (*au==i) {
live.nat[i].locked=1; au++;
}
}
for (i=0;i<N_FREGS;i++) {
live.fat[i].touched=0;
live.fat[i].nholds=0;
live.fat[i].locked=0;
}
touchcnt=1;
m68k_pc_offset=0;
live.flags_in_flags=TRASH;
live.flags_on_stack=VALID;
live.flags_are_important=1;
raw_fp_init();
}
static void vinton(int i, uae_s8* vton, int depth)
{
int n;
int rr;
Dif (vton[i]==-1) {
write_log ("JIT: Asked to load register %d, but nowhere to go\n", i);
abort();
}
n=vton[i];
Dif (live.nat[n].nholds>1)
abort();
if (live.nat[n].nholds && depth<N_REGS) {
vinton(live.nat[n].holds[0],vton,depth+1);
}
if (!isinreg(i))
return; /* Oops --- got rid of that one in the recursive calls */
rr=live.state[i].realreg;
if (rr!=n)
mov_nregs(n,rr);
}
#if USE_MATCHSTATE
/* This is going to be, amongst other things, a more elaborate version of
flush() */
static __inline__ void match_states(smallstate* s)
{
uae_s8 vton[VREGS];
uae_s8 ndone[N_REGS];
int i;
int again=0;
for (i=0;i<VREGS;i++)
vton[i]=-1;
for (i=0;i<N_REGS;i++)
if (s->nat[i].validsize)
vton[s->nat[i].holds]=i;
flush_flags(); /* low level */
sync_m68k_pc(); /* mid level */
/* We don't do FREGS yet, so this is raw flush() code */
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_SCRATCH ||
live.fate[i].status==CLEAN) {
f_disassociate(i);
}
}
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_TOMEM &&
live.fate[i].status==DIRTY) {
f_evict(i);
}
}
raw_fp_cleanup_drop();
/* Now comes the fun part. First, we need to remove all offsets */
for (i=0;i<VREGS;i++)
if (!isconst(i) && live.state[i].val)
remove_offset(i,-1);
/* Next, we evict everything that does not end up in registers,
write back overly dirty registers, and write back constants */
for (i=0;i<VREGS;i++) {
switch (live.state[i].status) {
case ISCONST:
if (i!=PC_P)
writeback_const(i);
break;
case DIRTY:
if (vton[i]==-1) {
evict(i);
break;
}
if (live.state[i].dirtysize>s->nat[vton[i]].dirtysize)
tomem(i);
/* Fall-through! */
case CLEAN:
if (vton[i]==-1 ||
live.state[i].validsize<s->nat[vton[i]].validsize)
evict(i);
else
make_exclusive(i,0,-1);
break;
case INMEM:
break;
case UNDEF:
break;
default:
write_log ("JIT: Weird status: %d\n", live.state[i].status);
abort();
}
}
/* Quick consistency check */
for (i=0;i<VREGS;i++) {
if (isinreg(i)) {
int n=live.state[i].realreg;
if (live.nat[n].nholds!=1) {
write_log ("JIT: Register %d isn't alone in nreg %d\n", i, n);
abort();
}
if (vton[i]==-1) {
write_log ("JIT: Register %d is still in register, shouldn't be\n", i);
abort();
}
}
}
/* Now we need to shuffle things around so the VREGs are in the
right N_REGs. */
for (i=0;i<VREGS;i++) {
if (isinreg(i) && vton[i]!=live.state[i].realreg)
vinton(i,vton,0);
}
/* And now we may need to load some registers from memory */
for (i=0;i<VREGS;i++) {
int n=vton[i];
if (n==-1) {
Dif (isinreg(i)) {
write_log ("JIT: Register %d unexpectedly in nreg %d\n",
i, live.state[i].realreg);
abort();
}
}
else {
switch(live.state[i].status) {
case CLEAN:
case DIRTY:
Dif (n!=live.state[i].realreg)
abort();
break;
case INMEM:
Dif (live.nat[n].nholds) {
write_log ("JIT: natreg %d holds %d vregs, should be empty\n",
n, live.nat[n].nholds);
}
raw_mov_l_rm(n,(uae_uintptr)live.state[i].mem);
live.state[i].validsize=4;
live.state[i].dirtysize=0;
live.state[i].realreg=n;
live.state[i].realind=0;
live.state[i].val=0;
live.state[i].is_swapped=0;
live.nat[n].nholds=1;
live.nat[n].holds[0]=i;
set_status(i,CLEAN);
break;
case ISCONST:
if (i!=PC_P) {
write_log ("JIT: Got constant in matchstate for reg %d. Bad!\n", i);
abort();
}
break;
case UNDEF:
break;
}
}
}
/* One last consistency check, and adjusting the states in live
to those in s */
for (i=0;i<VREGS;i++) {
int n=vton[i];
switch(live.state[i].status) {
case INMEM:
if (n!=-1)
abort();
break;
case ISCONST:
if (i!=PC_P)
abort();
break;
case CLEAN:
case DIRTY:
if (n==-1)
abort();
if (live.state[i].dirtysize>s->nat[n].dirtysize)
abort;
if (live.state[i].validsize<s->nat[n].validsize)
abort;
live.state[i].dirtysize=s->nat[n].dirtysize;
live.state[i].validsize=s->nat[n].validsize;
if (live.state[i].dirtysize)
set_status(i,DIRTY);
break;
case UNDEF:
break;
}
if (n!=-1)
live.nat[n].touched=touchcnt++;
}
}
#else
static __inline__ void match_states(smallstate* s)
{
flush(1);
}
#endif
/* Only do this if you really mean it! The next call should be to init!*/
void flush(int save_regs)
{
int i;
log_flush();
flush_flags(); /* low level */
sync_m68k_pc(); /* mid level */
if (save_regs) {
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_SCRATCH ||
live.fate[i].status==CLEAN) {
f_disassociate(i);
}
}
for (i=0;i<VREGS;i++) {
if (live.state[i].needflush==NF_TOMEM) {
switch(live.state[i].status) {
case INMEM:
if (live.state[i].val) {
raw_add_l_mi((uae_uintptr)live.state[i].mem,live.state[i].val);
live.state[i].val=0;
}
break;
case CLEAN:
case DIRTY:
remove_offset(i,-1); tomem(i); break;
case ISCONST:
if (i!=PC_P)
writeback_const(i);
break;
default: break;
}
Dif (live.state[i].val && i!=PC_P) {
write_log ("JIT: Register %d still has val %x\n",
i, live.state[i].val);
}
}
}
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_TOMEM &&
live.fate[i].status==DIRTY) {
f_evict(i);
}
}
raw_fp_cleanup_drop();
}
if (needflags) {
write_log ("JIT: Warning! flush with needflags=1!\n");
}
}
static void flush_keepflags(void)
{
int i;
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_SCRATCH ||
live.fate[i].status==CLEAN) {
f_disassociate(i);
}
}
for (i=0;i<VREGS;i++) {
if (live.state[i].needflush==NF_TOMEM) {
switch(live.state[i].status) {
case INMEM:
/* Can't adjust the offset here --- that needs "add" */
break;
case CLEAN:
case DIRTY:
remove_offset(i,-1); tomem(i); break;
case ISCONST:
if (i!=PC_P)
writeback_const(i);
break;
default: break;
}
}
}
for (i=0;i<VFREGS;i++) {
if (live.fate[i].needflush==NF_TOMEM &&
live.fate[i].status==DIRTY) {
f_evict(i);
}
}
raw_fp_cleanup_drop();
}
void freescratch(void)
{
int i;
for (i=0;i<N_REGS;i++)
if (live.nat[i].locked && i!=4)
write_log ("JIT: Warning! %d is locked\n", i);
for (i=0;i<VREGS;i++)
if (live.state[i].needflush==NF_SCRATCH) {
forget_about(i);
}
for (i=0;i<VFREGS;i++)
if (live.fate[i].needflush==NF_SCRATCH) {
f_forget_about(i);
}
}
/********************************************************************
* Support functions, internal *
********************************************************************/
static void align_target (uae_u32 a)
{
if (!a)
return;
if (1)
raw_emit_nop_filler (a - (((uae_uintptr) target) & (a - 1)));
else {
/* Fill with NOPs --- makes debugging with gdb easier */
while ((uae_uintptr) target & (a - 1))
*target++=0x90;
}
}
extern uae_u8* kickmemory;
STATIC_INLINE int isinrom (uae_uintptr addr)
{
return (addr >= (uae_uintptr) kickmemory &&
addr < (uae_uintptr) kickmemory + 8 * 65536);
}
static void flush_all(void)
{
int i;
log_flush();
for (i=0;i<VREGS;i++)
if (live.state[i].status==DIRTY) {
if (!call_saved[live.state[i].realreg]) {
tomem(i);
}
}
for (i=0;i<VFREGS;i++)
if (f_isinreg(i))
f_evict(i);
raw_fp_cleanup_drop();
}
/* Make sure all registers that will get clobbered by a call are
save and sound in memory */
static void prepare_for_call_1(void)
{
flush_all(); /* If there are registers that don't get clobbered,
* we should be a bit more selective here */
}
/* We will call a C routine in a moment. That will clobber all registers,
so we need to disassociate everything */
static void prepare_for_call_2(void)
{
int i;
for (i=0;i<N_REGS;i++)
if (!call_saved[i] && live.nat[i].nholds>0)
free_nreg(i);
for (i=0;i<N_FREGS;i++)
if (live.fat[i].nholds>0)
f_free_nreg(i);
live.flags_in_flags=TRASH; /* Note: We assume we already rescued the
flags at the very start of the call_r
functions! */
}
/********************************************************************
* Memory access and related functions, CREATE time *
********************************************************************/
void register_branch (uae_u32 not_taken, uae_u32 taken, uae_u8 cond)
{
next_pc_p = not_taken;
taken_pc_p = taken;
branch_cc = cond;
}
static uae_uintptr get_handler_address (uae_uintptr addr)
{
blockinfo *bi = get_blockinfo_addr_new ((void *) addr, 0);
return (uae_uintptr) &(bi->direct_handler_to_use);
}
static uae_uintptr get_handler (uae_uintptr addr)
{
blockinfo *bi = get_blockinfo_addr_new ((void *) addr, 0);
return (uae_uintptr) bi->direct_handler_to_use;
}
static void load_handler (int reg, uae_uintptr addr)
{
mov_l_rm (reg, get_handler_address (addr));
}
/* This version assumes that it is writing *real* memory, and *will* fail
* if that assumption is wrong! No branches, no second chances, just
* straight go-for-it attitude */
STATIC_INLINE void writemem_real (int address, int source, int offset, int size, int tmp, int clobber)
{
int f = tmp;
#ifdef NATMEM_OFFSET
if (canbang) { /* Woohoo! go directly at the memory! */
if (clobber)
f = source;
switch (size) {
case 1: mov_b_bRr (address, source, NATMEM_OFFSET); break;
case 2: mov_w_rr (f, source); gen_bswap_16 (f); mov_w_bRr (address, f, NATMEM_OFFSET); break;
case 4: mov_l_rr (f, source); gen_bswap_32 (f); mov_l_bRr (address, f, NATMEM_OFFSET); break;
}
forget_about (tmp);
forget_about (f);
return;
}
#endif
mov_l_rr (f, address);
shrl_l_ri (f, 16); /* The index into the baseaddr table */
mov_l_rm_indexed (f, (uae_uintptr) baseaddr, f, 4);
if (address == source && size > 1) { /* IBrowse does this! */
add_l (f, address); /* f now has the final address */
switch (size) {
case 2: gen_bswap_16 (source); mov_w_Rr (f, source, 0); gen_bswap_16 (source); break;
case 4: gen_bswap_32 (source); mov_l_Rr (f, source, 0); gen_bswap_32 (source); break;
}
} else {
/* f now holds the offset */
switch (size) {
case 1: mov_b_mrr_indexed (address,f,1,source); break;
case 2: gen_bswap_16 (source); mov_w_mrr_indexed (address, f, 1, source); gen_bswap_16 (source); break;
case 4: gen_bswap_32 (source); mov_l_mrr_indexed (address, f, 1, source); gen_bswap_32 (source); break;
}
}
}
STATIC_INLINE void writemem (int address, int source, int offset, int size, int tmp)
{
int f = tmp;
mov_l_rr (f, address);
shrl_l_ri (f, 16); /* The index into the mem bank table */
mov_l_rm_indexed (f, (uae_uintptr) mem_banks, f, 4);
/* Now f holds a pointer to the actual membank */
mov_l_rR (f, f, offset);
/* Now f holds the address of the b/w/lput function */
call_r_02 (f, address, source, 4, size);
forget_about (tmp);
}
void writebyte (int address, int source, int tmp)
{
int distrust;
switch (currprefs.comptrustbyte) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_WRITE) || distrust)
writemem_special (address, source, 20, 1, tmp);
else
writemem_real (address, source, 20, 1, tmp, 0);
}
void writeword_general (int address, int source, int tmp, int clobber)
{
int distrust;
switch (currprefs.comptrustword) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_WRITE) || distrust)
writemem_special (address, source, 16, 2, tmp);
else
writemem_real (address, source, 16, 2, tmp, clobber);
}
void writelong_general (int address, int source, int tmp, int clobber)
{
int distrust;
switch (currprefs.comptrustlong) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_WRITE) || distrust)
writemem_special (address, source, 12, 4, tmp);
else
writemem_real (address, source, 12, 4, tmp, clobber);
}
/* This version assumes that it is reading *real* memory, and *will* fail
* if that assumption is wrong! No branches, no second chances, just
* straight go-for-it attitude */
STATIC_INLINE void readmem_real (int address, int dest, int offset, int size, int tmp)
{
int f = tmp;
if (size == 4 && address != dest)
f = dest;
#ifdef NATMEM_OFFSET
if (canbang) { /* Woohoo! go directly at the memory! */
switch (size) {
case 1: mov_b_brR (dest, address, NATMEM_OFFSET); break;
case 2: mov_w_brR (dest, address, NATMEM_OFFSET); gen_bswap_16 (dest); break;
case 4: mov_l_brR (dest, address, NATMEM_OFFSET); gen_bswap_32 (dest); break;
}
forget_about (tmp);
return;
}
#endif
mov_l_rr (f, address);
shrl_l_ri (f, 16); /* The index into the baseaddr table */
mov_l_rm_indexed (f, (uae_uintptr) baseaddr, f, 4);
/* f now holds the offset */
switch(size) {
case 1: mov_b_rrm_indexed (dest, address, f, 1); break;
case 2: mov_w_rrm_indexed (dest, address, f, 1); gen_bswap_16 (dest); break;
case 4: mov_l_rrm_indexed (dest, address, f, 1); gen_bswap_32 (dest); break;
}
forget_about (tmp);
}
STATIC_INLINE void readmem (int address, int dest, int offset, int size, int tmp)
{
int f = tmp;
mov_l_rr (f,address);
shrl_l_ri (f,16); /* The index into the mem bank table */
mov_l_rm_indexed (f, (uae_uintptr) mem_banks, f, 4);
/* Now f holds a pointer to the actual membank */
mov_l_rR (f, f, offset);
/* Now f holds the address of the b/w/lget function */
call_r_11 (dest, f, address, size, 4);
forget_about (tmp);
}
void readbyte (int address, int dest, int tmp)
{
int distrust;
switch (currprefs.comptrustbyte) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_READ) || distrust)
readmem_special (address, dest, 8, 1,tmp);
else
readmem_real (address, dest, 8, 1, tmp);
}
void readword (int address, int dest, int tmp)
{
int distrust;
switch (currprefs.comptrustword) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_READ) || distrust)
readmem_special (address, dest, 4, 2, tmp);
else
readmem_real (address, dest, 4, 2, tmp);
}
void readlong (int address, int dest, int tmp)
{
int distrust;
switch (currprefs.comptrustlong) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if ((special_mem & SPECIAL_MEM_READ) || distrust)
readmem_special (address, dest, 0, 4,tmp);
else
readmem_real (address, dest, 0, 4, tmp);
}
/* This one might appear a bit odd... */
STATIC_INLINE void get_n_addr_old (int address, int dest, int tmp)
{
readmem (address, dest, 24, 4, tmp);
}
STATIC_INLINE void get_n_addr_real (int address, int dest, int tmp)
{
int f = tmp;
if (address != dest)
f = dest;
#ifdef NATMEM_OFFSET
if (canbang) {
lea_l_brr (dest, address, NATMEM_OFFSET);
forget_about (tmp);
return;
}
#endif
mov_l_rr (f, address);
mov_l_rr (dest, address); // gb-- nop if dest==address
shrl_l_ri (f, 16);
mov_l_rm_indexed (f, (uae_uintptr) baseaddr, f, 4);
add_l (dest,f);
forget_about (tmp);
}
void get_n_addr (int address, int dest, int tmp)
{
int distrust;
switch (currprefs.comptrustnaddr) {
default:
case 0: distrust = 0; break;
case 1: distrust = 1; break;
case 2: distrust = ((start_pc & 0xF80000) == 0xF80000); break;
case 3: distrust = !have_done_picasso; break;
}
if (special_mem || distrust)
get_n_addr_old (address,dest,tmp);
else
get_n_addr_real (address,dest,tmp);
}
void get_n_addr_jmp (int address, int dest, int tmp)
{
#if 0 /* For this, we need to get the same address as the rest of UAE
would --- otherwise we end up translating everything twice */
get_n_addr (address, dest, tmp);
#else
int f = tmp;
if (address != dest)
f = dest;
mov_l_rr (f, address);
shrl_l_ri (f, 16); /* The index into the baseaddr bank table */
mov_l_rm_indexed (dest, (uae_uintptr) baseaddr, f, 4);
add_l (dest, address);
and_l_ri (dest, ~1);
forget_about (tmp);
#endif
}
/* base is a register, but dp is an actual value.
target is a register, as is tmp */
void calc_disp_ea_020(int base, uae_u32 dp, int target, int tmp)
{
int reg = (dp >> 12) & 15;
int regd_shift=(dp >> 9) & 3;
if (dp & 0x100) {
int ignorebase=(dp&0x80);
int ignorereg=(dp&0x40);
int addbase=0;
int outer=0;
if ((dp & 0x30) == 0x20) addbase = (uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2);
if ((dp & 0x30) == 0x30) addbase = comp_get_ilong((m68k_pc_offset+=4)-4);
if ((dp & 0x3) == 0x2) outer = (uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2);
if ((dp & 0x3) == 0x3) outer = comp_get_ilong((m68k_pc_offset+=4)-4);
if ((dp & 0x4) == 0) { /* add regd *before* the get_long */
if (!ignorereg) {
if ((dp & 0x800) == 0)
sign_extend_16_rr(target,reg);
else
mov_l_rr(target,reg);
shll_l_ri(target,regd_shift);
}
else
mov_l_ri(target,0);
/* target is now regd */
if (!ignorebase)
add_l(target,base);
add_l_ri(target,addbase);
if (dp&0x03) readlong(target,target,tmp);
} else { /* do the getlong first, then add regd */
if (!ignorebase) {
mov_l_rr(target,base);
add_l_ri(target,addbase);
}
else
mov_l_ri(target,addbase);
if (dp&0x03) readlong(target,target,tmp);
if (!ignorereg) {
if ((dp & 0x800) == 0)
sign_extend_16_rr(tmp,reg);
else
mov_l_rr(tmp,reg);
shll_l_ri(tmp,regd_shift);
/* tmp is now regd */
add_l(target,tmp);
}
}
add_l_ri(target,outer);
}
else { /* 68000 version */
if ((dp & 0x800) == 0) { /* Sign extend */
sign_extend_16_rr(target,reg);
lea_l_brr_indexed(target,base,target,1<<regd_shift,(uae_s32)((uae_s8)dp));
}
else {
lea_l_brr_indexed(target,base,reg,1<<regd_shift,(uae_s32)((uae_s8)dp));
}
}
forget_about(tmp);
}
STATIC_INLINE unsigned int cft_map (unsigned int f)
{
return ((f >> 8) & 255) | ((f & 255) << 8);
}
extern unsigned long op_illg (uae_u32 opcode, struct regstruct *regs) REGPARAM;
static void calc_checksum(blockinfo* bi, uae_u32* c1, uae_u32* c2)
{
uae_u32 k1=0;
uae_u32 k2=0;
uae_s32 len=bi->len;
uae_uintptr tmp=bi->min_pcp;
uae_u32* pos;
len+=(tmp&3);
tmp&=(~3);
pos=(uae_u32*)tmp;
if (len<0 || len>MAX_CHECKSUM_LEN) {
*c1=0;
*c2=0;
}
else {
while (len>0) {
k1+=*pos;
k2^=*pos;
pos++;
len-=4;
}
*c1=k1;
*c2=k2;
}
}
int check_for_cache_miss(void)
{
blockinfo* bi=get_blockinfo_addr(regs.pc_p);
if (bi) {
int cl=cacheline(regs.pc_p);
if (bi!=cache_tags[cl+1].bi) {
raise_in_cl_list(bi);
return 1;
}
}
return 0;
}
static void recompile_block(void)
{
/* An existing block's countdown code has expired. We need to make
sure that execute_normal doesn't refuse to recompile due to a
perceived cache miss... */
blockinfo* bi=get_blockinfo_addr(regs.pc_p);
Dif (!bi)
abort();
raise_in_cl_list(bi);
execute_normal();
return;
}
static void cache_miss(void)
{
blockinfo* bi=get_blockinfo_addr(regs.pc_p);
uae_u32 cl=cacheline(regs.pc_p);
blockinfo* bi2=get_blockinfo(cl);
if (!bi) {
execute_normal(); /* Compile this block now */
return;
}
Dif (!bi2 || bi==bi2) {
write_log ("JIT: Unexplained cache miss %p %p\n", bi, bi2);
abort();
}
raise_in_cl_list(bi);
return;
}
static void check_checksum(void)
{
blockinfo* bi=get_blockinfo_addr(regs.pc_p);
uae_u32 cl=cacheline(regs.pc_p);
blockinfo* bi2=get_blockinfo(cl);
uae_u32 c1,c2;
checksum_count++;
/* These are not the droids you are looking for... */
if (!bi) {
/* Whoever is the primary target is in a dormant state, but
calling it was accidental, and we should just compile this
new block */
execute_normal();
return;
}
if (bi!=bi2) {
/* The block was hit accidentally, but it does exist. Cache miss */
cache_miss();
return;
}
if (bi->c1 || bi->c2)
calc_checksum(bi,&c1,&c2);
else {
c1=c2=1; /* Make sure it doesn't match */
}
if (c1==bi->c1 && c2==bi->c2) {
/* This block is still OK. So we reactivate. Of course, that
means we have to move it into the needs-to-be-flushed list */
bi->handler_to_use=bi->handler;
set_dhtu(bi,bi->direct_handler);
/* printf("reactivate %p/%p (%x %x/%x %x)\n",bi,bi->pc_p,
c1,c2,bi->c1,bi->c2);*/
remove_from_list(bi);
add_to_active(bi);
raise_in_cl_list(bi);
}
else {
/* This block actually changed. We need to invalidate it,
and set it up to be recompiled */
/* printf("discard %p/%p (%x %x/%x %x)\n",bi,bi->pc_p,
c1,c2,bi->c1,bi->c2); */
invalidate_block(bi);
raise_in_cl_list(bi);
execute_normal();
}
}
static void free_popalls (void)
{
if (popallspace) {
cache_free (popallspace);
popallspace = 0;
}
popall_exec_nostats = 0;
popall_execute_normal = 0;
popall_cache_miss = 0;
popall_recompile_block = 0;
popall_do_nothing = 0;
popall_check_checksum = 0;
pushall_call_handler = 0;
}
static void create_popalls (void)
{
popallspace = cache_alloc (1024);
if (popallspace) {
int i, r;
current_compile_p = popallspace;
set_target (current_compile_p);
#if USE_PUSH_POP
/* If we can't use gcc inline assembly, we need to pop some
* registers before jumping back to the various get-out routines.
* This generates the code for it.
*/
popall_do_nothing = current_compile_p;
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) do_nothing);
align_target (align_jumps);
popall_execute_normal = get_target ();
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) execute_normal);
align_target (align_jumps);
popall_cache_miss = get_target ();
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) cache_miss);
align_target (align_jumps);
popall_recompile_block = get_target ();
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) recompile_block);
align_target (align_jumps);
popall_exec_nostats = get_target ();
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) exec_nostats);
align_target (align_jumps);
popall_check_checksum = get_target ();
for (i = 0; i < N_REGS; i++) {
if (need_to_preserve[i])
raw_pop_l_r (i);
}
raw_jmp ((uae_uintptr) check_checksum);
align_target (align_jumps);
current_compile_p=get_target();
#else
popall_exec_nostats = exec_nostats;
popall_execute_normal = execute_normal;
popall_cache_miss = cache_miss;
popall_recompile_block = recompile_block;
popall_do_nothing = do_nothing;
popall_check_checksum = check_checksum;
#endif
/* And now, the code to do the matching pushes and then jump
* into a handler routine.
*/
pushall_call_handler = get_target ();
#if USE_PUSH_POP
for (i = N_REGS; i--;) {
if (need_to_preserve[i])
raw_push_l_r (i);
}
#endif
r = REG_PC_TMP;
raw_mov_l_rm (r, (uae_uintptr) &regs.pc_p);
raw_and_l_ri (r, TAGMASK);
raw_jmp_m_indexed ((uae_uintptr) cache_tags, r, 4);
}
}
STATIC_INLINE void reset_lists (void)
{
int i;
for (i = 0; i < MAX_HOLD_BI; i++)
hold_bi[i] = NULL;
active = NULL;
dormant = NULL;
}
static void prepare_block(blockinfo* bi)
{
int i;
set_target(current_compile_p);
align_target (align_jumps);
bi->direct_pen=(void*)get_target();
raw_mov_l_rm(0,(uae_uintptr)&(bi->pc_p));
raw_mov_l_mr((uae_uintptr)&regs.pc_p, 0);
raw_jmp((uae_uintptr)popall_execute_normal);
align_target (align_jumps);
bi->direct_pcc=(void*)get_target();
raw_mov_l_rm(0,(uae_uintptr)&(bi->pc_p));
raw_mov_l_mr((uae_uintptr)&regs.pc_p, 0);
raw_jmp((uae_uintptr)popall_check_checksum);
current_compile_p=get_target();
bi->deplist=NULL;
for (i=0;i<2;i++) {
bi->dep[i].prev_p=NULL;
bi->dep[i].next=NULL;
}
bi->env=default_ss;
bi->status=BI_NEW;
bi->havestate=0;
//bi->env=empty_ss;
}
void compemu_reset(void)
{
set_cache_state(0);
}
void build_comp (void)
{
int i;
unsigned long opcode;
const struct comptbl *tbl = op_smalltbl_0_comp_ff;
const struct comptbl *nftbl = op_smalltbl_0_comp_nf;
int count;
#ifdef NOFLAGS_SUPPORT
struct cputbl *nfctbl = (currprefs.cpu_level >= 4 ? op_smalltbl_0_nf
: currprefs.cpu_level == 3 ? op_smalltbl_1_nf
: currprefs.cpu_level == 2 ? op_smalltbl_2_nf
: currprefs.cpu_level == 1 ? op_smalltbl_3_nf
# ifdef CPUEMU_5
: currprefs.cpu_compatible ? op_smalltbl_5_nf
# endif
: op_smalltbl_4_nf);
#endif
raw_init_cpu ();
write_log ("JIT: Building compiler function table.\n");
for (opcode = 0; opcode < 65536; opcode++) {
#ifdef NOFLAGS_SUPPORT
nfcpufunctbl[opcode] = op_illg;
#endif
compfunctbl[opcode] = NULL;
nfcompfunctbl[opcode] = NULL;
prop[opcode].use_flags = 0x1f;
prop[opcode].set_flags = 0x1f;
prop[opcode].is_jump=1;
}
for (i = 0; tbl[i].opcode < 65536; i++) {
int isjmp=(tbl[i].specific&1);
int isaddx=(tbl[i].specific&8);
int iscjmp=(tbl[i].specific&16);
prop[tbl[i].opcode].is_jump=isjmp;
prop[tbl[i].opcode].is_const_jump=iscjmp;
prop[tbl[i].opcode].is_addx=isaddx;
compfunctbl[tbl[i].opcode] = tbl[i].handler;
}
for (i = 0; nftbl[i].opcode < 65536; i++) {
nfcompfunctbl[nftbl[i].opcode] = nftbl[i].handler;
#ifdef NOFLAGS_SUPPORT
nfcpufunctbl[nftbl[i].opcode] = nfctbl[i].handler;
#endif
}
#ifdef NOFLAGS_SUPPORT
for (i = 0; nfctbl[i].handler; i++) {
nfcpufunctbl[nfctbl[i].opcode] = nfctbl[i].handler;
}
#endif
for (opcode = 0; opcode < 65536; opcode++) {
compop_func *f;
compop_func *nff;
#ifdef NOFLAGS_SUPPORT
cpuop_func *nfcf;
#endif
int isjmp,isaddx,iscjmp;
if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > currprefs.cpu_level)
continue;
if (table68k[opcode].handler != -1) {
f = compfunctbl[table68k[opcode].handler];
nff = nfcompfunctbl[table68k[opcode].handler];
#ifdef NOFLAGS_SUPPORT
nfcf = nfcpufunctbl[table68k[opcode].handler];
#endif
isjmp=prop[table68k[opcode].handler].is_jump;
iscjmp=prop[table68k[opcode].handler].is_const_jump;
isaddx=prop[table68k[opcode].handler].is_addx;
prop[opcode].is_jump=isjmp;
prop[opcode].is_const_jump=iscjmp;
prop[opcode].is_addx=isaddx;
compfunctbl[opcode] = f;
nfcompfunctbl[opcode] = nff;
#ifdef NOFLAGS_SUPPORT
Dif (nfcf == op_illg)
abort();
nfcpufunctbl[opcode] = nfcf;
#endif
}
prop[opcode].set_flags =table68k[opcode].flagdead;
prop[opcode].use_flags =table68k[opcode].flaglive;
/* Unconditional jumps don't evaluate condition codes, so they
don't actually use any flags themselves */
if (prop[opcode].is_const_jump)
prop[opcode].use_flags=0;
}
#ifdef NOFLAGS_SUPPORT
for (i = 0; nfctbl[i].handler != NULL; i++) {
if (nfctbl[i].specific)
nfcpufunctbl[tbl[i].opcode] = nfctbl[i].handler;
}
#endif
count=0;
for (opcode = 0; opcode < 65536; opcode++) {
if (compfunctbl[opcode])
count++;
}
write_log ("JIT: Supposedly %d compileable opcodes.\n", count);
#ifdef NATMEM_OFFSET
if (canbang) {
# ifndef _WIN32
/* Set up signal handler to catch illegal natmem accesses */
struct sigaction act;
act.sa_sigaction = vec;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction (SIGSEGV, &act, NULL);
# endif
/* Cache for generating illegal natmem access handler. */
veccode = cache_alloc (256);
if (!veccode) {
canbang = 0;
sigaction (SIGSEGV, saved_handler, 0);
} else
write_log ("JIT: Enabled direct memory access.\n");
}
#endif
/* Initialise state */
alloc_cache ();
create_popalls ();
reset_lists ();
for (i=0;i<TAGSIZE;i+=2) {
cache_tags[i].handler=(void*)popall_execute_normal;
cache_tags[i+1].bi=NULL;
}
compemu_reset ();
for (i=0;i<N_REGS;i++) {
empty_ss.nat[i].holds=-1;
empty_ss.nat[i].validsize=0;
empty_ss.nat[i].dirtysize=0;
}
default_ss=empty_ss;
}
static void flush_icache_hard(int n)
{
uae_u32 i;
blockinfo* bi;
hard_flush_count++;
#if 0
printf("Flush Icache_hard(%d/%x/%p), %u instruction bytes\n",
n,regs.pc,regs.pc_p,current_compile_p-compiled_code);
#endif
bi=active;
while(bi) {
cache_tags[cacheline(bi->pc_p)].handler=(void*)popall_execute_normal;
cache_tags[cacheline(bi->pc_p)+1].bi=NULL;
bi=bi->next;
}
bi=dormant;
while(bi) {
cache_tags[cacheline(bi->pc_p)].handler=(void*)popall_execute_normal;
cache_tags[cacheline(bi->pc_p)+1].bi=NULL;
bi=bi->next;
}
reset_lists();
if (!compiled_code)
return;
current_compile_p=compiled_code;
set_special (&regs,0); /* To get out of compiled code */
}
/* "Soft flushing" --- instead of actually throwing everything away,
we simply mark everything as "needs to be checked".
*/
void flush_icache(int n)
{
uae_u32 i;
blockinfo* bi;
blockinfo* bi2;
if (currprefs.comp_hardflush) {
flush_icache_hard(n);
return;
}
soft_flush_count++;
if (!active)
return;
bi=active;
while (bi) {
uae_u32 cl=cacheline(bi->pc_p);
if (!bi->handler) {
/* invalidated block */
if (bi==cache_tags[cl+1].bi)
cache_tags[cl].handler=popall_execute_normal;
bi->handler_to_use=popall_execute_normal;
set_dhtu(bi,bi->direct_pen);
}
else {
if (bi==cache_tags[cl+1].bi)
cache_tags[cl].handler=popall_check_checksum;
bi->handler_to_use=popall_check_checksum;
set_dhtu(bi,bi->direct_pcc);
}
bi2=bi;
bi=bi->next;
}
/* bi2 is now the last entry in the active list */
bi2->next=dormant;
if (dormant)
dormant->prev_p=&(bi2->next);
dormant=active;
active->prev_p=&dormant;
active=NULL;
}
int failure;
void compile_block (const cpu_history *pc_hist, int blocklen, int totcycles)
{
if (letit && compiled_code && currprefs.cpu_level>=2) {
/* OK, here we need to 'compile' a block */
int i;
int r;
int was_comp=0;
uae_u8 liveflags[MAXRUN+1];
uae_uintptr max_pcp=(uae_uintptr)pc_hist[0].location;
uae_uintptr min_pcp=max_pcp;
uae_u32 cl=cacheline(pc_hist[0].location);
void* specflags=(void*)&regs.spcflags;
blockinfo* bi=NULL;
blockinfo* bi2;
int extra_len=0;
compile_count++;
if (current_compile_p>=max_compile_start)
flush_icache_hard(7);
alloc_blockinfos();
bi=get_blockinfo_addr_new(pc_hist[0].location,0);
bi2=get_blockinfo(cl);
optlev=bi->optlevel;
if (bi->handler) {
Dif (bi!=bi2) {
/* I don't think it can happen anymore. Shouldn't, in
any case. So let's make sure... */
write_log ("WOOOWOO count=%d, ol=%d %p %p\n",
bi->count, bi->optlevel, bi->handler_to_use,
cache_tags[cl].handler);
abort();
}
Dif (bi->count!=-1 && bi->status!=BI_TARGETTED) {
/* What the heck? We are not supposed to be here! */
abort();
}
}
if (bi->count==-1) {
optlev++;
while (!currprefs.optcount[optlev])
optlev++;
bi->count=currprefs.optcount[optlev]-1;
}
current_block_pc_p=(uae_uintptr)pc_hist[0].location;
remove_deps(bi); /* We are about to create new code */
bi->optlevel=optlev;
bi->pc_p=(uae_u8*)pc_hist[0].location;
liveflags[blocklen]=0x1f; /* All flags needed afterwards */
i=blocklen;
while (i--) {
uae_u16* currpcp=pc_hist[i].location;
int op=cft_map(*currpcp);
if ((uae_uintptr) currpcp < min_pcp)
min_pcp = (uae_uintptr) currpcp;
if ((uae_uintptr) currpcp > max_pcp)
max_pcp = (uae_uintptr) currpcp;
if (currprefs.compnf) {
liveflags[i]=((liveflags[i+1]&
(~prop[op].set_flags))|
prop[op].use_flags);
if (prop[op].is_addx && (liveflags[i+1]&FLAG_Z)==0)
liveflags[i]&= ~FLAG_Z;
}
else {
liveflags[i]=0x1f;
}
}
bi->needed_flags=liveflags[0];
#if 1
/* This is the non-direct handler */
align_target(32);
set_target(get_target()+1);
align_target(16);
/* Now aligned at n*32+16 */
#else
align_target (align_loops);
#endif
bi->handler=
bi->handler_to_use=(void*)get_target();
raw_cmp_l_mi((uae_uintptr)&regs.pc_p,(uae_uintptr)pc_hist[0].location);
raw_jnz((uae_uintptr)popall_cache_miss);
/* This was 16 bytes on the x86, so now aligned on (n+1)*32 */
was_comp=0;
#if USE_MATCHSTATE
comp_pc_p=(uae_u8*)pc_hist[0].location;
init_comp();
match_states(&(bi->env));
was_comp=1;
#endif
bi->direct_handler=(void*)get_target();
set_dhtu(bi,bi->direct_handler);
current_block_start_target=(uae_uintptr)get_target();
if (bi->count>=0) { /* Need to generate countdown code */
raw_mov_l_mi((uae_uintptr)&regs.pc_p,(uae_uintptr)pc_hist[0].location);
raw_sub_l_mi((uae_uintptr)&(bi->count),1);
raw_jl((uae_uintptr)popall_recompile_block);
}
if (optlev==0) { /* No need to actually translate */
/* Execute normally without keeping stats */
raw_mov_l_mi((uae_uintptr)&regs.pc_p,(uae_uintptr)pc_hist[0].location);
raw_jmp((uae_uintptr)popall_exec_nostats);
}
else {
reg_alloc_run=0;
next_pc_p=0;
taken_pc_p=0;
branch_cc=0;
log_startblock();
for (i=0;i<blocklen &&
get_target_noopt()<max_compile_start;i++) {
cpuop_func **cputbl;
compop_func **comptbl;
uae_u16 opcode;
opcode=cft_map((uae_u16)*pc_hist[i].location);
special_mem=pc_hist[i].specmem;
needed_flags=(liveflags[i+1] & prop[opcode].set_flags);
if (!needed_flags && currprefs.compnf) {
#ifdef NOFLAGS_SUPPORT
cputbl=nfcpufunctbl;
#else
cputbl=cpufunctbl;
#endif
comptbl=nfcompfunctbl;
}
else {
cputbl=cpufunctbl;
comptbl=compfunctbl;
}
if (comptbl[opcode] && optlev>1) {
failure=0;
if (!was_comp) {
comp_pc_p=(uae_u8*)pc_hist[i].location;
init_comp();
}
was_comp++;
comptbl[opcode](opcode);
freescratch();
if (!(liveflags[i+1] & FLAG_CZNV)) {
/* We can forget about flags */
dont_care_flags();
}
#if INDIVIDUAL_INST
flush(1);
nop();
flush(1);
was_comp=0;
#endif
}
else
failure=1;
if (failure) {
if (was_comp) {
flush(1);
was_comp=0;
}
raw_mov_l_ri(REG_PAR1,(uae_u32)opcode);
raw_mov_l_ri(REG_PAR2,(uae_uintptr)&regs);
#if USE_NORMAL_CALLING_CONVENTION
raw_push_l_r(REG_PAR2);
raw_push_l_r(REG_PAR1);
#endif
raw_mov_l_mi((uae_uintptr)&regs.pc_p,
(uae_uintptr)pc_hist[i].location);
raw_call((uae_uintptr)cputbl[opcode]);
#if USE_NORMAL_CALLING_CONVENTION
raw_inc_sp(8);
#endif
if (i<blocklen-1) {
uae_s8* branchadd;
raw_mov_l_rm(0,(uae_uintptr)specflags);
raw_test_l_rr(0,0);
raw_jz_b_oponly();
branchadd = (uae_s8 *)get_target();
emit_byte(0);
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_jmp((uae_uintptr)popall_do_nothing);
*branchadd=(uae_uintptr)get_target()-(uae_uintptr)branchadd-1;
}
}
}
#if 0 /* This isn't completely kosher yet; It really needs to be
be integrated into a general inter-block-dependency scheme */
if (next_pc_p && taken_pc_p &&
was_comp && taken_pc_p==current_block_pc_p) {
blockinfo* bi1=get_blockinfo_addr_new((void*)next_pc_p,0);
blockinfo* bi2=get_blockinfo_addr_new((void*)taken_pc_p,0);
uae_u8 x=bi1->needed_flags;
if (x==0xff || 1) { /* To be on the safe side */
uae_u16* next=(uae_u16*)next_pc_p;
uae_u16 op=cft_map(*next);
x=0x1f;
x&=(~prop[op].set_flags);
x|=prop[op].use_flags;
}
x|=bi2->needed_flags;
if (!(x & FLAG_CZNV)) {
/* We can forget about flags */
dont_care_flags();
extra_len+=2; /* The next instruction now is part of this
block */
}
}
#endif
if (next_pc_p) { /* A branch was registered */
uae_uintptr t1=next_pc_p;
uae_uintptr t2=taken_pc_p;
int cc=branch_cc;
uae_u32* branchadd;
uae_u32* tba;
bigstate tmp;
blockinfo* tbi;
if (taken_pc_p<next_pc_p) {
/* backward branch. Optimize for the "taken" case ---
which means the raw_jcc should fall through when
the 68k branch is taken. */
t1=taken_pc_p;
t2=next_pc_p;
cc=branch_cc^1;
}
#if !USE_MATCHSTATE
flush_keepflags();
#endif
tmp=live; /* ouch! This is big... */
raw_jcc_l_oponly(cc);
branchadd=(uae_u32*)get_target();
emit_long(0);
/* predicted outcome */
tbi=get_blockinfo_addr_new((void*)t1,1);
match_states(&(tbi->env));
//flush(1); /* Can only get here if was_comp==1 */
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_jcc_l_oponly(9);
tba=(uae_u32*)get_target();
emit_long(get_handler(t1)-((uae_uintptr)tba+4));
raw_mov_l_mi((uae_uintptr)&regs.pc_p,t1);
raw_jmp((uae_uintptr)popall_do_nothing);
create_jmpdep(bi,0,tba,t1);
align_target (align_jumps);
/* not-predicted outcome */
*branchadd=(uae_uintptr)get_target()-((uae_uintptr)branchadd+4);
live=tmp; /* Ouch again */
tbi=get_blockinfo_addr_new((void*)t2,1);
match_states(&(tbi->env));
//flush(1); /* Can only get here if was_comp==1 */
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_jcc_l_oponly(9);
tba=(uae_u32*)get_target();
emit_long(get_handler(t2)-((uae_uintptr)tba+4));
raw_mov_l_mi((uae_uintptr)&regs.pc_p,t2);
raw_jmp((uae_uintptr)popall_do_nothing);
create_jmpdep(bi,1,tba,t2);
}
else
{
if (was_comp) {
flush(1);
}
/* Let's find out where next_handler is... */
if (was_comp && isinreg(PC_P)) {
int r2;
r=live.state[PC_P].realreg;
if (r==0)
r2=1;
else
r2=0;
raw_and_l_ri(r,TAGMASK);
raw_mov_l_ri(r2,(uae_uintptr)popall_do_nothing);
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_cmov_l_rm_indexed(r2,(uae_uintptr)cache_tags,r,4,9);
raw_jmp_r(r2);
}
else if (was_comp && isconst(PC_P)) {
uae_u32 v=live.state[PC_P].val;
uae_u32* tba;
blockinfo* tbi;
tbi=get_blockinfo_addr_new((void*)(uae_uintptr)v,1);
match_states(&(tbi->env));
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_jcc_l_oponly(9);
tba=(uae_u32*)get_target();
emit_long(get_handler(v)-((uae_uintptr)tba+4));
raw_mov_l_mi((uae_uintptr)&regs.pc_p,v);
raw_jmp((uae_uintptr)popall_do_nothing);
create_jmpdep(bi,0,tba,v);
}
else {
int r2;
r=REG_PC_TMP;
raw_mov_l_rm(r,(uae_uintptr)&regs.pc_p);
if (r==0)
r2=1;
else
r2=0;
raw_and_l_ri(r,TAGMASK);
raw_mov_l_ri(r2,(uae_uintptr)popall_do_nothing);
raw_sub_l_mi((uae_uintptr)&countdown,scaled_cycles(totcycles));
raw_cmov_l_rm_indexed(r2,(uae_uintptr)cache_tags,r,4,9);
raw_jmp_r(r2);
}
}
}
if (next_pc_p+extra_len>=max_pcp &&
next_pc_p+extra_len<max_pcp+LONGEST_68K_INST)
max_pcp=next_pc_p+extra_len; /* extra_len covers flags magic */
else
max_pcp+=LONGEST_68K_INST;
bi->len=max_pcp-min_pcp;
bi->min_pcp=min_pcp;
remove_from_list(bi);
if (isinrom(min_pcp) && isinrom(max_pcp))
add_to_dormant(bi); /* No need to checksum it on cache flush.
Please don't start changing ROMs in
flight! */
else {
calc_checksum(bi,&(bi->c1),&(bi->c2));
add_to_active(bi);
}
log_dump();
align_target (align_jumps);
current_compile_p=get_target();
raise_in_cl_list(bi);
bi->nexthandler=current_compile_p;
/* We will flush soon, anyway, so let's do it now */
if (current_compile_p>=max_compile_start)
flush_icache_hard(7);
do_extra_cycles(totcycles); /* for the compilation time */
}
}