X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=pcsx_rearmed.git;a=blobdiff_plain;f=libpcsxcore%2Fnew_dynarec%2Fnew_dynarec.c;h=cd63d2bf218c52bd1a6544f81e6a0b2c9f43c061;hp=c36021c23187c1a8a973d833acd1242891aaa390;hb=HEAD;hpb=277718fa66c96f64360b2c97a5dfa3ef3e6f1711 diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index c36021c2..19df23a5 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef __MACH__ #include #endif @@ -37,6 +38,7 @@ static Jit g_jit; #include "new_dynarec_config.h" #include "../psxhle.h" #include "../psxinterpreter.h" +#include "../psxcounters.h" #include "../gte.h" #include "emu_if.h" // emulator interface #include "linkage_offsets.h" @@ -55,6 +57,7 @@ static Jit g_jit; //#define DISASM //#define ASSEM_PRINT +//#define ASSEM_PRINT_ADDRS //#define REGMAP_PRINT // with DISASM only //#define INV_DEBUG_W //#define STAT_PRINT @@ -64,9 +67,30 @@ static Jit g_jit; #else #define assem_debug(...) #endif +#ifdef ASSEM_PRINT_ADDRS +#define log_addr(a) (a) +#else +// for diff-able output +#define log_addr(a) ((u_long)(a) <= 1024u ? (void *)(a) : (void *)0xadd0l) +#endif //#define inv_debug printf #define inv_debug(...) +#define SysPrintf_lim(...) do { \ + if (err_print_count++ < 64u) \ + SysPrintf(__VA_ARGS__); \ +} while (0) + +// from linkage_* +extern int cycle_count; // ... until end of the timeslice, counts -N -> 0 (CCREG) +extern int last_count; // last absolute target, often = next_interupt + +extern int reg_cop2d[], reg_cop2c[]; + +extern void *hash_table_ptr; +extern uintptr_t ram_offset; +extern uintptr_t mini_ht[32][2]; + #ifdef __i386__ #include "assem_x86.h" #endif @@ -81,7 +105,6 @@ static Jit g_jit; #endif #define RAM_SIZE 0x200000 -#define MAXBLOCK 4096 #define MAX_OUTPUT_BLOCK_SIZE 262144 #define EXPIRITY_OFFSET (MAX_OUTPUT_BLOCK_SIZE * 2) #define PAGE_COUNT 1024 @@ -98,6 +121,8 @@ static Jit g_jit; #define TC_REDUCE_BYTES 0 #endif +struct ndrc_globals ndrc_g; + struct ndrc_tramp { struct tramp_insns ops[2048 / sizeof(struct tramp_insns)]; @@ -110,18 +135,16 @@ struct ndrc_mem struct ndrc_tramp tramp; }; -#ifdef BASE_ADDR_DYNAMIC static struct ndrc_mem *ndrc; -#else -static struct ndrc_mem ndrc_ __attribute__((aligned(4096))); -static struct ndrc_mem *ndrc = &ndrc_; +#ifndef BASE_ADDR_DYNAMIC +// reserve .bss space with upto 64k page size in mind +static char ndrc_bss[((sizeof(*ndrc) + 65535) & ~65535) + 65536]; #endif #ifdef TC_WRITE_OFFSET # ifdef __GLIBC__ # include # include # include -# include # endif static long ndrc_write_ofs; #define NDRC_WRITE_OFFSET(x) (void *)((char *)(x) + ndrc_write_ofs) @@ -158,13 +181,14 @@ struct regstat { signed char regmap_entry[HOST_REGS]; signed char regmap[HOST_REGS]; - uint64_t wasdirty; - uint64_t dirty; - uint64_t u; + u_int wasdirty; + u_int dirty; u_int wasconst; // before; for example 'lw r2, (r2)' wasconst is true u_int isconst; // ... but isconst is false when r2 is known (hr) u_int loadedconst; // host regs that have constants loaded + u_int noevict; // can't evict this hr (alloced by current op) //u_int waswritten; // MIPS regs that were used as store base before + uint64_t u; }; struct ht_entry @@ -241,8 +265,13 @@ static struct decoded_insn u_char is_delay_load:1; // is_load + MFC/CFC u_char is_exception:1; // unconditional, also interp. fallback u_char may_except:1; // might generate an exception + u_char ls_type:2; // load/store type (ls_width_type LS_*) } dops[MAXBLOCK]; +enum ls_width_type { + LS_8 = 0, LS_16, LS_32, LS_LR +}; + static struct compile_info { int imm; @@ -263,7 +292,7 @@ static struct compile_info static uint64_t gte_rs[MAXBLOCK]; // gte: 32 data and 32 ctl regs static uint64_t gte_rt[MAXBLOCK]; static uint64_t gte_unneeded[MAXBLOCK]; - static u_int smrv[32]; // speculated MIPS register values + unsigned int ndrc_smrv_regs[32]; // speculated MIPS register values static u_int smrv_strong; // mask or regs that are likely to have correct values static u_int smrv_weak; // same, but somewhat less likely static u_int smrv_strong_next; // same, but after current insn executes @@ -291,7 +320,10 @@ static struct compile_info static void *copy; static u_int expirep; static u_int stop_after_jal; + static u_int ni_count; + static u_int err_print_count; static u_int f1_hack; + static u_int vsync_hack; #ifdef STAT_PRINT static int stat_bc_direct; static int stat_bc_pre; @@ -313,33 +345,20 @@ static struct compile_info #define stat_clear(s) #endif - int new_dynarec_hacks; - int new_dynarec_hacks_pergame; - int new_dynarec_hacks_old; - int new_dynarec_did_compile; - - #define HACK_ENABLED(x) ((new_dynarec_hacks | new_dynarec_hacks_pergame) & (x)) - - extern int cycle_count; // ... until end of the timeslice, counts -N -> 0 - extern int last_count; // last absolute target, often = next_interupt - extern int pcaddr; - extern int pending_exception; - extern int branch_target; - extern uintptr_t ram_offset; - extern uintptr_t mini_ht[32][2]; + #define HACK_ENABLED(x) ((ndrc_g.hacks | ndrc_g.hacks_pergame) & (x)) /* registers that may be allocated */ /* 1-31 gpr */ #define LOREG 32 // lo #define HIREG 33 // hi //#define FSREG 34 // FPU status (FCSR) -#define CSREG 35 // Coprocessor status +//#define CSREG 35 // Coprocessor status #define CCREG 36 // Cycle count #define INVCP 37 // Pointer to invalid_code //#define MMREG 38 // Pointer to memory_map -#define ROREG 39 // ram offset (if rdram!=0x80000000) +#define ROREG 39 // ram offset (if psxM != 0x80000000) #define TEMPREG 40 -#define FTEMP 40 // FPU temporary register +#define FTEMP 40 // Load/store temporary register (was fpu) #define PTEMP 41 // Prefetch temporary register //#define TLREG 42 // TLB mapping offset #define RHASH 43 // Return address hash @@ -348,7 +367,6 @@ static struct compile_info #define MAXREG 45 #define AGEN1 46 // Address generation temporary register (pass5b_preallocate2) //#define AGEN2 47 // Address generation temporary register -#define BTREG 50 // Branch target temporary register /* instruction types */ #define NOP 0 // No operation @@ -379,7 +397,6 @@ static struct compile_info /* branch codes */ #define TAKEN 1 #define NOTTAKEN 2 -#define NULLDS 3 #define DJT_1 (void *)1l // no function, just a label in assem_debug log #define DJT_2 (void *)2l @@ -396,12 +413,9 @@ void jump_overflow_ds(u_int u0, u_int u1, u_int pc); void jump_addrerror (u_int cause, u_int addr, u_int pc); void jump_addrerror_ds(u_int cause, u_int addr, u_int pc); void jump_to_new_pc(); -void call_gteStall(); void new_dyna_leave(); -void *ndrc_get_addr_ht_param(u_int vaddr, int can_compile); -void *ndrc_get_addr_ht(u_int vaddr); -void ndrc_add_jump_out(u_int vaddr, void *src); +void *ndrc_get_addr_ht(u_int vaddr, struct ht_entry *ht); void ndrc_write_invalidate_one(u_int addr); static void ndrc_write_invalidate_many(u_int addr, u_int end); @@ -410,16 +424,16 @@ static void invalidate_block(struct block_info *block); static void exception_assemble(int i, const struct regstat *i_regs, int ccadj_); // Needed by assembler -static void wb_register(signed char r, const signed char regmap[], uint64_t dirty); -static void wb_dirtys(const signed char i_regmap[], uint64_t i_dirty); -static void wb_needed_dirtys(const signed char i_regmap[], uint64_t i_dirty, int addr); +static void wb_register(signed char r, const signed char regmap[], u_int dirty); +static void wb_dirtys(const signed char i_regmap[], u_int i_dirty); +static void wb_needed_dirtys(const signed char i_regmap[], u_int i_dirty, int addr); static void load_all_regs(const signed char i_regmap[]); static void load_needed_regs(const signed char i_regmap[], const signed char next_regmap[]); static void load_regs_entry(int t); static void load_all_consts(const signed char regmap[], u_int dirty, int i); static u_int get_host_reglist(const signed char *regmap); -static int get_final_value(int hr, int i, int *value); +static int get_final_value(int hr, int i, u_int *value); static void add_stub(enum stub_type type, void *addr, void *retaddr, u_int a, uintptr_t b, uintptr_t c, u_int d, u_int e); static void add_stub_r(enum stub_type type, void *addr, void *retaddr, @@ -473,12 +487,7 @@ static void mprotect_w_x(void *start, void *end, int is_x) #endif } -static void start_tcache_write(void *start, void *end) -{ - mprotect_w_x(start, end, 0); -} - -static void end_tcache_write(void *start, void *end) +void new_dyna_clear_cache(void *start, void *end) { #if defined(__arm__) || defined(__aarch64__) size_t len = (char *)end - (char *)start; @@ -489,7 +498,11 @@ static void end_tcache_write(void *start, void *end) #elif defined(VITA) sceKernelSyncVMDomain(sceBlock, start, len); #elif defined(_3DS) - ctr_flush_invalidate_cache(); + // tuned for old3ds' 16k:16k cache (in it's mostly clean state...) + if ((char *)end - (char *)start <= 2*1024) + ctr_clear_cache_range(start, end); + else + ctr_clear_cache(); #elif defined(HAVE_LIBNX) if (g_jit.type == JitType_CodeMemory) { armDCacheClean(start, len); @@ -498,14 +511,30 @@ static void end_tcache_write(void *start, void *end) __asm__ volatile("isb" ::: "memory"); } #elif defined(__aarch64__) - // as of 2021, __clear_cache() is still broken on arm64 - // so here is a custom one :( + // __clear_cache() doesn't handle differing cacheline sizes on big.LITTLE and + // leaves it to the kernel to virtualize ctr_el0, which some old kernels don't do clear_cache_arm64(start, end); #else __clear_cache(start, end); #endif (void)len; #endif +} + +static void start_tcache_write(void *start, void *end) +{ + mprotect_w_x(start, end, 0); +} + +static void end_tcache_write(void *start, void *end) +{ +#ifdef NDRC_THREAD + if (!ndrc_g.thread.dirty_start || (size_t)ndrc_g.thread.dirty_start > (size_t)start) + ndrc_g.thread.dirty_start = start; + if ((size_t)ndrc_g.thread.dirty_end < (size_t)end) + ndrc_g.thread.dirty_end = end; +#endif + new_dyna_clear_cache(start, end); mprotect_w_x(start, end, 1); } @@ -593,7 +622,6 @@ static void do_clear_cache(void) #define NO_CYCLE_PENALTY_THR 12 -int cycle_multiplier_old; static int cycle_multiplier_active; static int CLOCK_ADJUST(int x) @@ -605,7 +633,9 @@ static int CLOCK_ADJUST(int x) static int ds_writes_rjump_rs(int i) { - return dops[i].rs1 != 0 && (dops[i].rs1 == dops[i+1].rt1 || dops[i].rs1 == dops[i+1].rt2); + return dops[i].rs1 != 0 + && (dops[i].rs1 == dops[i+1].rt1 || dops[i].rs1 == dops[i+1].rt2 + || dops[i].rs1 == dops[i].rt1); // overwrites itself - same effect } // psx addr mirror masking (for invalidation) @@ -636,9 +666,32 @@ static u_int get_page_prev(u_int vaddr) return page; } +static struct ht_entry *hash_table_get_p(struct ht_entry *ht, u_int vaddr) +{ + return &ht[((vaddr >> 16) ^ vaddr) & 0xFFFF]; +} + static struct ht_entry *hash_table_get(u_int vaddr) { - return &hash_table[((vaddr>>16)^vaddr)&0xFFFF]; + return hash_table_get_p(hash_table, vaddr); +} + +#define HASH_TABLE_BAD 0xbac + +static void hash_table_clear(void) +{ + struct ht_entry *ht_bin; + int i, j; + for (i = 0; i < ARRAY_SIZE(hash_table); i++) { + for (j = 0; j < ARRAY_SIZE(hash_table[i].vaddr); j++) { + hash_table[i].vaddr[j] = ~0; + hash_table[i].tcaddr[j] = (void *)(uintptr_t)HASH_TABLE_BAD; + } + } + // don't allow ~0 to hit + ht_bin = hash_table_get(~0); + for (j = 0; j < ARRAY_SIZE(ht_bin->vaddr); j++) + ht_bin->vaddr[j] = 1; } static void hash_table_add(u_int vaddr, void *tcaddr) @@ -656,17 +709,30 @@ static void hash_table_remove(int vaddr) //printf("remove hash: %x\n",vaddr); struct ht_entry *ht_bin = hash_table_get(vaddr); if (ht_bin->vaddr[1] == vaddr) { - ht_bin->vaddr[1] = -1; - ht_bin->tcaddr[1] = NULL; + ht_bin->vaddr[1] = ~0; + ht_bin->tcaddr[1] = (void *)(uintptr_t)HASH_TABLE_BAD; } if (ht_bin->vaddr[0] == vaddr) { ht_bin->vaddr[0] = ht_bin->vaddr[1]; ht_bin->tcaddr[0] = ht_bin->tcaddr[1]; - ht_bin->vaddr[1] = -1; - ht_bin->tcaddr[1] = NULL; + ht_bin->vaddr[1] = ~0; + ht_bin->tcaddr[1] = (void *)(uintptr_t)HASH_TABLE_BAD; } } +static void mini_ht_clear(void) +{ +#ifdef USE_MINI_HT + int i; + for (i = 0; i < ARRAY_SIZE(mini_ht) - 1; i++) { + mini_ht[i][0] = ~0; + mini_ht[i][1] = HASH_TABLE_BAD; + } + mini_ht[i][0] = 1; + mini_ht[i][1] = HASH_TABLE_BAD; +#endif +} + static void mark_invalid_code(u_int vaddr, u_int len, char invalid) { u_int vaddr_m = vaddr & 0x1fffffff; @@ -689,7 +755,7 @@ static int doesnt_expire_soon(u_char *tcaddr) return diff > EXPIRITY_OFFSET + MAX_OUTPUT_BLOCK_SIZE; } -static unused void check_for_block_changes(u_int start, u_int end) +static attr_unused void check_for_block_changes(u_int start, u_int end) { u_int start_page = get_page_prev(start); u_int end_page = get_page(end - 1); @@ -746,9 +812,30 @@ static void *try_restore_block(u_int vaddr, u_int start_page, u_int end_page) return NULL; } +// this doesn't normally happen +static noinline u_int generate_exception(u_int pc) +{ + //if (execBreakCheck(&psxRegs, pc)) + // return psxRegs.pc; + + // generate an address or bus error + psxRegs.CP0.n.Cause &= 0x300; + psxRegs.CP0.n.EPC = pc; + if (pc & 3) { + psxRegs.CP0.n.Cause |= R3000E_AdEL << 2; + psxRegs.CP0.n.BadVAddr = pc; +#ifdef DRC_DBG + last_count -= 2; +#endif + } else + psxRegs.CP0.n.Cause |= R3000E_IBE << 2; + return (psxRegs.pc = 0x80000080); +} + // Get address from virtual address // This is called from the recompiled JR/JALR instructions -static void noinline *get_addr(u_int vaddr, int can_compile) +static void noinline *get_addr(struct ht_entry *ht, const u_int vaddr, + enum ndrc_compile_mode compile_mode) { u_int start_page = get_page_prev(vaddr); u_int i, page, end_page = get_page(vaddr); @@ -776,36 +863,45 @@ static void noinline *get_addr(u_int vaddr, int can_compile) if (found_clean) return found_clean; - if (!can_compile) + if (compile_mode == ndrc_cm_no_compile) return NULL; +#ifdef NDRC_THREAD + if (ndrc_g.thread.handle && compile_mode == ndrc_cm_compile_live) { + psxRegs.pc = vaddr; + return new_dyna_leave; + } + if (!ndrc_g.thread.handle) +#endif + memcpy(ndrc_smrv_regs, psxRegs.GPR.r, sizeof(ndrc_smrv_regs)); int r = new_recompile_block(vaddr); - if (r == 0) - return ndrc_get_addr_ht(vaddr); + if (likely(r == 0)) + return ndrc_get_addr_ht(vaddr, ht); - // generate an address error - psxRegs.CP0.n.Cause &= 0x300; - psxRegs.CP0.n.Cause |= R3000E_AdEL << 2; - psxRegs.CP0.n.EPC = vaddr; - psxRegs.pc = 0x80000080; - return ndrc_get_addr_ht(0x80000080); + if (compile_mode == ndrc_cm_compile_live) + return ndrc_get_addr_ht(generate_exception(vaddr), ht); + + return NULL; } // Look up address in hash table first -void *ndrc_get_addr_ht_param(u_int vaddr, int can_compile) +void *ndrc_get_addr_ht_param(struct ht_entry *ht, unsigned int vaddr, + enum ndrc_compile_mode compile_mode) { //check_for_block_changes(vaddr, vaddr + MAXBLOCK); - const struct ht_entry *ht_bin = hash_table_get(vaddr); + const struct ht_entry *ht_bin = hash_table_get_p(ht, vaddr); u_int vaddr_a = vaddr & ~3; stat_inc(stat_ht_lookups); if (ht_bin->vaddr[0] == vaddr_a) return ht_bin->tcaddr[0]; if (ht_bin->vaddr[1] == vaddr_a) return ht_bin->tcaddr[1]; - return get_addr(vaddr, can_compile); + return get_addr(ht, vaddr, compile_mode); } -void *ndrc_get_addr_ht(u_int vaddr) +// "usual" addr lookup for indirect branches, etc +// to be used by currently running code only +void *ndrc_get_addr_ht(u_int vaddr, struct ht_entry *ht) { - return ndrc_get_addr_ht_param(vaddr, 1); + return ndrc_get_addr_ht_param(ht, vaddr, ndrc_cm_compile_live); } static void clear_all_regs(signed char regmap[]) @@ -965,7 +1061,7 @@ static uint32_t get_const(const struct regstat *cur, signed char reg) // Least soon needed registers // Look at the next ten instructions and see which registers // will be used. Try not to reallocate these. -static void lsn(u_char hsn[], int i, int *preferred_reg) +static void lsn(u_char hsn[], int i) { int j; int b=-1; @@ -1036,12 +1132,8 @@ static void lsn(u_char hsn[], int i, int *preferred_reg) if(dops[i].itype==C2LS) { hsn[FTEMP]=0; } - // Load L/R also uses FTEMP as a temporary register - if(dops[i].itype==LOADLR) { - hsn[FTEMP]=0; - } - // Also SWL/SWR/SDL/SDR - if(dops[i].opcode==0x2a||dops[i].opcode==0x2e||dops[i].opcode==0x2c||dops[i].opcode==0x2d) { + // Load/store L/R also uses FTEMP as a temporary register + if (dops[i].itype == LOADLR || dops[i].itype == STORELR) { hsn[FTEMP]=0; } // Don't remove the miniht registers @@ -1191,6 +1283,7 @@ static const struct { FUNCNAME(cc_interrupt), FUNCNAME(gen_interupt), FUNCNAME(ndrc_get_addr_ht), + FUNCNAME(ndrc_get_addr_ht_param), FUNCNAME(jump_handler_read8), FUNCNAME(jump_handler_read16), FUNCNAME(jump_handler_read32), @@ -1208,7 +1301,6 @@ static const struct { FUNCNAME(jump_overflow_ds), FUNCNAME(jump_addrerror), FUNCNAME(jump_addrerror_ds), - FUNCNAME(call_gteStall), FUNCNAME(new_dyna_leave), FUNCNAME(pcsx_mtc0), FUNCNAME(pcsx_mtc0_ds), @@ -1218,7 +1310,11 @@ static const struct { FUNCNAME(do_memhandler_post), #endif #ifdef DRC_DBG +# ifdef __aarch64__ + FUNCNAME(do_insn_cmp_arm64), +# else FUNCNAME(do_insn_cmp), +# endif #endif }; @@ -1238,8 +1334,8 @@ static const char *fpofs_name(u_int ofs) switch (ofs) { #define ofscase(x) case LO_##x: return " ; " #x ofscase(next_interupt); + ofscase(cycle_count); ofscase(last_count); - ofscase(pending_exception); ofscase(stop); ofscase(address); ofscase(lo); @@ -1251,6 +1347,7 @@ static const char *fpofs_name(u_int ofs) ofscase(psxH_ptr); ofscase(invc_ptr); ofscase(ram_offset); + ofscase(hash_table_ptr); #undef ofscase } buf[0] = 0; @@ -1545,9 +1642,7 @@ static int invalidate_range(u_int start, u_int end, } if (hit) { do_clear_cache(); -#ifdef USE_MINI_HT - memset(mini_ht, -1, sizeof(mini_ht)); -#endif + mini_ht_clear(); } if (inv_start <= (start_m & ~0xfff) && inv_end >= (start_m | 0xfff)) @@ -1564,6 +1659,24 @@ void new_dynarec_invalidate_range(unsigned int start, unsigned int end) invalidate_range(start, end, NULL, NULL); } +// check if the range may need invalidation (must be thread-safe) +int new_dynarec_quick_check_range(unsigned int start, unsigned int end) +{ + u_int start_page = get_page_prev(start); + u_int end_page = get_page(end - 1); + u_int page; + + if (inv_code_start <= start && end <= inv_code_end) + return 0; + for (page = start_page; page <= end_page; page++) { + if (blocks[page]) { + //SysPrintf("quick hit %x-%x\n", start, end); + return 1; + } + } + return 0; +} + static void ndrc_write_invalidate_many(u_int start, u_int end) { // this check is done by the caller @@ -1604,22 +1717,20 @@ void new_dynarec_invalidate_all_pages(void) } } - #ifdef USE_MINI_HT - memset(mini_ht, -1, sizeof(mini_ht)); - #endif do_clear_cache(); + mini_ht_clear(); } // Add an entry to jump_out after making a link -// src should point to code by emit_extjump() -void ndrc_add_jump_out(u_int vaddr, void *src) +// stub should point to stub code by emit_extjump() +static void ndrc_add_jump_out(u_int vaddr, void *stub) { - inv_debug("ndrc_add_jump_out: %p -> %x\n", src, vaddr); + inv_debug("ndrc_add_jump_out: %p -> %x\n", stub, vaddr); u_int page = get_page(vaddr); struct jump_info *ji; stat_inc(stat_links); - check_extjump2(src); + check_extjump2(stub); ji = jumps[page]; if (ji == NULL) { ji = malloc(sizeof(*ji) + sizeof(ji->e[0]) * 16); @@ -1632,12 +1743,98 @@ void ndrc_add_jump_out(u_int vaddr, void *src) } jumps[page] = ji; ji->e[ji->count].target_vaddr = vaddr; - ji->e[ji->count].stub = src; + ji->e[ji->count].stub = stub; ji->count++; } +void ndrc_patch_link(u_int vaddr, void *insn, void *stub, void *target) +{ + void *insn_end = (char *)insn + 4; + + //start_tcache_write(insn, insn_end); + mprotect_w_x(insn, insn_end, 0); + + assert(target != stub); + set_jump_target_far1(insn, target); + ndrc_add_jump_out(vaddr, stub); + +#if defined(__aarch64__) || defined(NO_WRITE_EXEC) + // arm64: no syscall concerns, dyna_linker lacks stale detection + // w^x: have to do costly permission switching anyway + new_dyna_clear_cache(NDRC_WRITE_OFFSET(insn), NDRC_WRITE_OFFSET(insn_end)); +#endif + //end_tcache_write(insn, insn_end); + mprotect_w_x(insn, insn_end, 1); +} + /* Register allocation */ +static void alloc_set(struct regstat *cur, int reg, int hr) +{ + cur->regmap[hr] = reg; + cur->dirty &= ~(1u << hr); + cur->isconst &= ~(1u << hr); + cur->noevict |= 1u << hr; +} + +static void evict_alloc_reg(struct regstat *cur, int i, int reg, int preferred_hr) +{ + u_char hsn[MAXREG+1]; + int j, r, hr; + memset(hsn, 10, sizeof(hsn)); + lsn(hsn, i); + //printf("hsn(%x): %d %d %d %d %d %d %d\n",start+i*4,hsn[cur->regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); + if(i>0) { + // Don't evict the cycle count at entry points, otherwise the entry + // stub will have to write it. + if(dops[i].bt&&hsn[CCREG]>2) hsn[CCREG]=2; + if (i>1 && hsn[CCREG] > 2 && dops[i-2].is_jump) hsn[CCREG]=2; + for(j=10;j>=3;j--) + { + // Alloc preferred register if available + if (!((cur->noevict >> preferred_hr) & 1) + && hsn[cur->regmap[preferred_hr]] == j) + { + alloc_set(cur, reg, preferred_hr); + return; + } + for(r=1;r<=MAXREG;r++) + { + if(hsn[r]==j&&r!=dops[i-1].rs1&&r!=dops[i-1].rs2&&r!=dops[i-1].rt1&&r!=dops[i-1].rt2) { + for(hr=0;hrnoevict >> hr) & 1)) + continue; + if(hr!=HOST_CCREG||jregmap[hr]==r) { + alloc_set(cur, reg, hr); + return; + } + } + } + } + } + } + } + for(j=10;j>=0;j--) + { + for(r=1;r<=MAXREG;r++) + { + if(hsn[r]==j) { + for(hr=0;hrnoevict >> hr) & 1)) + continue; + if(cur->regmap[hr]==r) { + alloc_set(cur, reg, hr); + return; + } + } + } + } + } + SysPrintf("This shouldn't happen (evict_alloc_reg)\n"); + abort(); +} + // Note: registers are allocated clean (unmodified state) // if you intend to modify the register, you must call dirty_reg(). static void alloc_reg(struct regstat *cur,int i,signed char reg) @@ -1654,25 +1851,23 @@ static void alloc_reg(struct regstat *cur,int i,signed char reg) if((cur->u>>reg)&1) return; // see if it's already allocated - if (get_reg(cur->regmap, reg) >= 0) + if ((hr = get_reg(cur->regmap, reg)) >= 0) { + cur->noevict |= 1u << hr; return; + } // Keep the same mapping if the register was already allocated in a loop preferred_reg = loop_reg(i,reg,preferred_reg); // Try to allocate the preferred register - if(cur->regmap[preferred_reg]==-1) { - cur->regmap[preferred_reg]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[preferred_reg] == -1) { + alloc_set(cur, reg, preferred_reg); return; } r=cur->regmap[preferred_reg]; assert(r < 64); if((cur->u>>r)&1) { - cur->regmap[preferred_reg]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[hr] < 0) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[0],cur->regmap[1],cur->regmap[2],cur->regmap[3],cur->regmap[5],cur->regmap[6],cur->regmap[7]); - //printf("hsn(%x): %d %d %d %d %d %d %d\n",start+i*4,hsn[cur->regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); - if(i>0) { - // Don't evict the cycle count at entry points, otherwise the entry - // stub will have to write it. - if(dops[i].bt&&hsn[CCREG]>2) hsn[CCREG]=2; - if (i>1 && hsn[CCREG] > 2 && dops[i-2].is_jump) hsn[CCREG]=2; - for(j=10;j>=3;j--) - { - // Alloc preferred register if available - if(hsn[r=cur->regmap[preferred_reg]&63]==j) { - for(hr=0;hrregmap[hr]==r) { - cur->regmap[hr]=-1; - cur->dirty&=~(1<isconst&=~(1<regmap[preferred_reg]=reg; - return; - } - for(r=1;r<=MAXREG;r++) - { - if(hsn[r]==j&&r!=dops[i-1].rs1&&r!=dops[i-1].rs2&&r!=dops[i-1].rt1&&r!=dops[i-1].rt2) { - for(hr=0;hrregmap[hr]==r) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<=0;j--) - { - for(r=1;r<=MAXREG;r++) - { - if(hsn[r]==j) { - for(hr=0;hrregmap[hr]==r) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[hr]==reg) return; + if (hr != EXCLUDE_REG && cur->regmap[hr] == reg) { + cur->noevict |= 1u << hr; + return; + } } // Try to allocate any available register for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<u>>r)&1) { if(i==0||((unneeded_reg[i-1]>>r)&1)) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); - if(i>0) { - // Don't evict the cycle count at entry points, otherwise the entry - // stub will have to write it. - if(dops[i].bt&&hsn[CCREG]>2) hsn[CCREG]=2; - if (i>1 && hsn[CCREG] > 2 && dops[i-2].is_jump) hsn[CCREG]=2; - for(j=10;j>=3;j--) - { - for(r=1;r<=MAXREG;r++) - { - if(hsn[r]==j&&r!=dops[i-1].rs1&&r!=dops[i-1].rs2&&r!=dops[i-1].rt1&&r!=dops[i-1].rt2) { - for(hr=0;hr2) { - if(cur->regmap[hr]==r) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<=0;j--) - { - for(r=1;r<=MAXREG;r++) - { - if(hsn[r]==j) { - for(hr=0;hrregmap[hr]==r) { - cur->regmap[hr]=reg; - cur->dirty&=~(1<isconst&=~(1<u>>dops[i].rt1)&1)) { @@ -2093,7 +2170,8 @@ static void load_alloc(struct regstat *current,int i) } } -static void store_alloc(struct regstat *current,int i) +// this may eat up to 7 registers +static void store_alloc(struct regstat *current, int i) { clear_const(current,dops[i].rs2); if(!(dops[i].rs2)) current->u&=~1LL; // Allow allocating r0 if necessary @@ -2108,16 +2186,14 @@ static void store_alloc(struct regstat *current,int i) if (dops[i].opcode == 0x2a || dops[i].opcode == 0x2e) { // SWL/SWL alloc_reg(current,i,FTEMP); } - if (dops[i].may_except) { - alloc_cc(current, i); // for exceptions - dirty_reg(current, CCREG); - } + if (dops[i].may_except) + alloc_cc_optional(current, i); // for exceptions // We need a temporary register for address generation alloc_reg_temp(current,i,-1); cinfo[i].min_free_regs=1; } -static void c2ls_alloc(struct regstat *current,int i) +static void c2ls_alloc(struct regstat *current, int i) { clear_const(current,dops[i].rt1); if(needed_again(dops[i].rs1,i)) alloc_reg(current,i,dops[i].rs1); @@ -2129,10 +2205,8 @@ static void c2ls_alloc(struct regstat *current,int i) if (dops[i].opcode == 0x3a) // SWC2 alloc_reg(current,i,INVCP); #endif - if (dops[i].may_except) { - alloc_cc(current, i); // for exceptions - dirty_reg(current, CCREG); - } + if (dops[i].may_except) + alloc_cc_optional(current, i); // for exceptions // We need a temporary register for address generation alloc_reg_temp(current,i,-1); cinfo[i].min_free_regs=1; @@ -2145,41 +2219,22 @@ static void multdiv_alloc(struct regstat *current,int i) // case 0x19: MULTU // case 0x1A: DIV // case 0x1B: DIVU - // case 0x1C: DMULT - // case 0x1D: DMULTU - // case 0x1E: DDIV - // case 0x1F: DDIVU clear_const(current,dops[i].rs1); clear_const(current,dops[i].rs2); alloc_cc(current,i); // for stalls - if(dops[i].rs1&&dops[i].rs2) - { - if((dops[i].opcode2&4)==0) // 32-bit - { - current->u&=~(1LL<u&=~(1LL<u &= ~(1ull << HIREG); + current->u &= ~(1ull << LOREG); + alloc_reg(current, i, HIREG); + alloc_reg(current, i, LOREG); + dirty_reg(current, HIREG); + dirty_reg(current, LOREG); + if ((dops[i].opcode2 & 0x3e) == 0x1a || (dops[i].rs1 && dops[i].rs2)) // div(u) { - // Multiply by zero is zero. - // MIPS does not have a divide by zero exception. - // The result is undefined, we return zero. - alloc_reg(current,i,HIREG); - alloc_reg(current,i,LOREG); - dirty_reg(current,HIREG); - dirty_reg(current,LOREG); + alloc_reg(current, i, dops[i].rs1); + alloc_reg(current, i, dops[i].rs2); } + // else multiply by zero is zero } #endif @@ -2195,6 +2250,10 @@ static void cop0_alloc(struct regstat *current,int i) } else if(dops[i].opcode2==4) // MTC0 { + if (((source[i]>>11)&0x1e) == 12) { + alloc_cc(current, i); + dirty_reg(current, CCREG); + } if(dops[i].rs1){ clear_const(current,dops[i].rs1); alloc_reg(current,i,dops[i].rs1); @@ -2333,7 +2392,7 @@ static void add_stub_r(enum stub_type type, void *addr, void *retaddr, } // Write out a single register -static void wb_register(signed char r, const signed char regmap[], uint64_t dirty) +static void wb_register(signed char r, const signed char regmap[], u_int dirty) { int hr; for(hr=0;hrregmap); if (do_oflow) assert(tmp >= 0); - //if (t < 0 && do_oflow) // broken s2 - // t = tmp; + if (t < 0 && do_oflow) + t = tmp; if (t >= 0) { s1 = get_reg(i_regs->regmap, dops[i].rs1); s2 = get_reg(i_regs->regmap, dops[i].rs2); @@ -2867,8 +2927,8 @@ static void *emit_fastpath_cmp_jump(int i, const struct regstat *i_regs, assert(addr >= 0); *offset_reg = -1; if(((smrv_strong|smrv_weak)>>mr)&1) { - type=get_ptr_mem_type(smrv[mr]); - //printf("set %08x @%08x r%d %d\n", smrv[mr], start+i*4, mr, type); + type=get_ptr_mem_type(ndrc_smrv_regs[mr]); + //printf("set %08x @%08x r%d %d\n", ndrc_smrv_regs[mr], start+i*4, mr, type); } else { // use the mirror we are running on @@ -2880,11 +2940,11 @@ static void *emit_fastpath_cmp_jump(int i, const struct regstat *i_regs, // alignment check u_int op = dops[i].opcode; int mask = ((op & 0x37) == 0x21 || op == 0x25) ? 1 : 3; // LH/SH/LHU - void *jaddr; + void *jaddr2; emit_testimm(addr, mask); - jaddr = out; + jaddr2 = out; emit_jne(0); - add_stub_r(ALIGNMENT_STUB, jaddr, out, i, addr, i_regs, ccadj_, 0); + add_stub_r(ALIGNMENT_STUB, jaddr2, out, i, addr, i_regs, ccadj_, 0); } if(type==MTYPE_8020) { // RAM 80200000+ mirror @@ -3262,7 +3322,7 @@ static void loadlr_assemble(int i, const struct regstat *i_regs, int ccadj_) static void do_invstub(int n) { literal_pool(20); - assem_debug("do_invstub\n"); + assem_debug("do_invstub %x\n", start + stubs[n].e*4); u_int reglist = stubs[n].a; u_int addrr = stubs[n].b; int ofs_start = stubs[n].c; @@ -3326,6 +3386,7 @@ static void do_store_smc_check(int i, const struct regstat *i_regs, u_int reglis emit_cmpmem_indexedsr12_imm(invalid_code, addr, 1); #error not handled #endif + (void)count; #ifdef INVALIDATE_USE_COND_CALL if (count == 1) { emit_cmpimm(HOST_TEMPREG, 1); @@ -3339,9 +3400,13 @@ static void do_store_smc_check(int i, const struct regstat *i_regs, u_int reglis imm_min -= cinfo[i].imm; imm_max -= cinfo[i].imm; add_stub(INVCODE_STUB, jaddr, out, reglist|(1<regmap); tl=get_reg(i_regs->regmap,dops[i].rs2); s=get_reg(i_regs->regmap,dops[i].rs1); offset=cinfo[i].imm; if(s>=0) { c=(i_regs->wasconst>>s)&1; - if(c) { - memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE; + if (c) { + addr_const = constmap[i][s] + offset; + memtarget = ((signed int)addr_const) < (signed int)(0x80000000 + RAM_SIZE); } } assert(tl>=0); assert(addr >= 0); if(i_regs->regmap[HOST_CCREG]==CCREG) reglist&=~(1<regmap,dops[i].rs2,ccadj_,reglist); } + if (!c || is_ram_addr(addr_const)) + do_store_smc_check(i, i_regs, reglist, addr); + if (c && !memtarget) + inline_writestub(type, i, addr_const, i_regs->regmap, dops[i].rs2, ccadj_, reglist); // basic current block modification detection.. // not looking back as that should be in mips cache already // (see Spyro2 title->attract mode) - if(c&&start+i*4regmap==regs[i].regmap); // not delay slot if(i_regs->regmap==regs[i].regmap) { load_all_consts(regs[i].regmap_entry,regs[i].wasdirty,i); wb_dirtys(regs[i].regmap_entry,regs[i].wasdirty); - emit_movimm(start+i*4+4,0); - emit_writeword(0,&pcaddr); - emit_addimm(HOST_CCREG,2,HOST_CCREG); + emit_readptr(&hash_table_ptr, 1); + emit_movimm(start+i*4+4, 0); + emit_writeword(0, &psxRegs.pc); + emit_addimm(HOST_CCREG, 2, HOST_CCREG); emit_far_call(ndrc_get_addr_ht); emit_jmpreg(0); } @@ -3451,21 +3512,23 @@ static void storelr_assemble(int i, const struct regstat *i_regs, int ccadj_) void *done0, *done1, *done2; int memtarget=0,c=0; int offset_reg = -1; - u_int reglist=get_host_reglist(i_regs->regmap); + u_int addr_const = ~0; + u_int reglist = get_host_reglist(i_regs->regmap); tl=get_reg(i_regs->regmap,dops[i].rs2); s=get_reg(i_regs->regmap,dops[i].rs1); offset=cinfo[i].imm; if(s>=0) { - c=(i_regs->isconst>>s)&1; - if(c) { - memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE; + c = (i_regs->isconst >> s) & 1; + if (c) { + addr_const = constmap[i][s] + offset; + memtarget = ((signed int)addr_const) < (signed int)(0x80000000 + RAM_SIZE); } } assert(tl>=0); assert(addr >= 0); + reglist |= 1u << addr; if(!c) { emit_cmpimm(addr, RAM_SIZE); - if (!offset && s != addr) emit_mov(s, addr); jaddr=out; emit_jno(0); } @@ -3479,10 +3542,6 @@ static void storelr_assemble(int i, const struct regstat *i_regs, int ccadj_) if (ram_offset) offset_reg = get_ro_reg(i_regs, 0); - if (dops[i].opcode==0x2C||dops[i].opcode==0x2D) { // SDL/SDR - assert(0); - } - emit_testimm(addr,2); case23=out; emit_jne(0); @@ -3507,14 +3566,14 @@ static void storelr_assemble(int i, const struct regstat *i_regs, int ccadj_) if (dops[i].opcode == 0x2A) { // SWL // Write two msb into two least significant bytes if (dops[i].rs2) emit_rorimm(tl, 16, tl); - do_store_hword(addr, -1, tl, offset_reg, 0); + do_store_hword(addr, -1, tl, offset_reg, 1); if (dops[i].rs2) emit_rorimm(tl, 16, tl); } else if (dops[i].opcode == 0x2E) { // SWR // Write 3 lsb into three most significant bytes do_store_byte(addr, tl, offset_reg); if (dops[i].rs2) emit_rorimm(tl, 8, tl); - do_store_hword(addr, 1, tl, offset_reg, 0); + do_store_hword(addr, 1, tl, offset_reg, 1); if (dops[i].rs2) emit_rorimm(tl, 24, tl); } done1=out; @@ -3542,7 +3601,7 @@ static void storelr_assemble(int i, const struct regstat *i_regs, int ccadj_) // 3 set_jump_target(case3, out); if (dops[i].opcode == 0x2A) { // SWL - do_store_word(addr, -3, tl, offset_reg, 0); + do_store_word(addr, -3, tl, offset_reg, 1); } else if (dops[i].opcode == 0x2E) { // SWR do_store_byte(addr, tl, offset_reg); @@ -3552,9 +3611,10 @@ static void storelr_assemble(int i, const struct regstat *i_regs, int ccadj_) set_jump_target(done2, out); if (offset_reg == HOST_TEMPREG) host_tempreg_release(); - if(!c||!memtarget) + if (!c || !memtarget) add_stub_r(STORELR_STUB,jaddr,out,i,addr,i_regs,ccadj_,reglist); - do_store_smc_check(i, i_regs, reglist, addr); + if (!c || is_ram_addr(addr_const)) + do_store_smc_check(i, i_regs, reglist, addr); } static void cop0_assemble(int i, const struct regstat *i_regs, int ccadj_) @@ -3564,27 +3624,23 @@ static void cop0_assemble(int i, const struct regstat *i_regs, int ccadj_) signed char t=get_reg_w(i_regs->regmap, dops[i].rt1); u_int copr=(source[i]>>11)&0x1f; if(t>=0&&dops[i].rt1!=0) { - emit_readword(®_cop0[copr],t); + emit_readword(&psxRegs.CP0.r[copr],t); } } else if(dops[i].opcode2==4) // MTC0 { - signed char s=get_reg(i_regs->regmap,dops[i].rs1); + int s = get_reg(i_regs->regmap, dops[i].rs1); + int cc = get_reg(i_regs->regmap, CCREG); char copr=(source[i]>>11)&0x1f; assert(s>=0); wb_register(dops[i].rs1,i_regs->regmap,i_regs->dirty); - if(copr==9||copr==11||copr==12||copr==13) { + if (copr == 12 || copr == 13) { emit_readword(&last_count,HOST_TEMPREG); - emit_loadreg(CCREG,HOST_CCREG); // TODO: do proper reg alloc - emit_add(HOST_CCREG,HOST_TEMPREG,HOST_CCREG); - emit_addimm(HOST_CCREG,ccadj_,HOST_CCREG); - emit_writeword(HOST_CCREG,&psxRegs.cycle); - } - // What a mess. The status register (12) can enable interrupts, - // so needs a special case to handle a pending interrupt. - // The interrupt must be taken immediately, because a subsequent - // instruction might disable interrupts again. - if(copr==12||copr==13) { + if (cc != HOST_CCREG) + emit_loadreg(CCREG, HOST_CCREG); + emit_add(HOST_CCREG, HOST_TEMPREG, HOST_CCREG); + emit_addimm(HOST_CCREG, ccadj_ + 2, HOST_CCREG); + emit_writeword(HOST_CCREG, &psxRegs.cycle); if (is_delayslot) { // burn cycles to cause cc_interrupt, which will // reschedule next_interupt. Relies on CCREG from above. @@ -3592,42 +3648,39 @@ static void cop0_assemble(int i, const struct regstat *i_regs, int ccadj_) emit_writeword(HOST_CCREG,&last_count); emit_movimm(0,HOST_CCREG); emit_storereg(CCREG,HOST_CCREG); - emit_loadreg(dops[i].rs1,1); - emit_movimm(copr,0); + emit_loadreg(dops[i].rs1, 2); + emit_movimm(copr, 1); + emit_addimm_ptr(FP, (u_char *)&psxRegs - (u_char *)&dynarec_local, 0); emit_far_call(pcsx_mtc0_ds); emit_loadreg(dops[i].rs1,s); return; } emit_movimm(start+i*4+4,HOST_TEMPREG); - emit_writeword(HOST_TEMPREG,&pcaddr); - emit_movimm(0,HOST_TEMPREG); - emit_writeword(HOST_TEMPREG,&pending_exception); - } - if(s==HOST_CCREG) - emit_loadreg(dops[i].rs1,1); - else if(s!=1) - emit_mov(s,1); - emit_movimm(copr,0); + emit_writeword(HOST_TEMPREG,&psxRegs.pc); + } + if (s != 2) + emit_mov(s, 2); + emit_movimm(copr, 1); + emit_addimm_ptr(FP, (u_char *)&psxRegs - (u_char *)&dynarec_local, 0); emit_far_call(pcsx_mtc0); - if(copr==9||copr==11||copr==12||copr==13) { + if (copr == 12 || copr == 13) { emit_readword(&psxRegs.cycle,HOST_CCREG); - emit_readword(&next_interupt,HOST_TEMPREG); - emit_addimm(HOST_CCREG,-ccadj_,HOST_CCREG); + emit_readword(&last_count,HOST_TEMPREG); emit_sub(HOST_CCREG,HOST_TEMPREG,HOST_CCREG); - emit_writeword(HOST_TEMPREG,&last_count); - emit_storereg(CCREG,HOST_CCREG); - } - if(copr==12||copr==13) { + //emit_writeword(HOST_TEMPREG,&last_count); assert(!is_delayslot); - emit_readword(&pending_exception,HOST_TEMPREG); - emit_test(HOST_TEMPREG,HOST_TEMPREG); + emit_readword(&psxRegs.pc, 0); + emit_movimm(start+i*4+4, HOST_TEMPREG); + emit_cmp(HOST_TEMPREG, 0); void *jaddr = out; emit_jeq(0); - emit_readword(&pcaddr, 0); - emit_addimm(HOST_CCREG,2,HOST_CCREG); + emit_readptr(&hash_table_ptr, 1); emit_far_call(ndrc_get_addr_ht); emit_jmpreg(0); set_jump_target(jaddr, out); + emit_addimm(HOST_CCREG, -ccadj_ - 2, HOST_CCREG); + if (cc != HOST_CCREG) + emit_storereg(CCREG, HOST_CCREG); } emit_loadreg(dops[i].rs1,s); } @@ -3644,11 +3697,7 @@ static void rfe_assemble(int i, const struct regstat *i_regs) static int cop2_is_stalling_op(int i, int *cycles) { - if (dops[i].opcode == 0x3a) { // SWC2 - *cycles = 0; - return 1; - } - if (dops[i].itype == COP2 && (dops[i].opcode2 == 0 || dops[i].opcode2 == 2)) { // MFC2/CFC2 + if (dops[i].itype == COP2 || dops[i].itype == C2LS) { *cycles = 0; return 1; } @@ -3682,7 +3731,7 @@ static void emit_log_gte_stall(int i, int stall, u_int reglist) static void cop2_do_stall_check(u_int op, int i, const struct regstat *i_regs, u_int reglist) { - int j = i, other_gte_op_cycles = -1, stall = -MAXBLOCK, cycles_passed; + int j = i, cycles, other_gte_op_cycles = -1, stall = -MAXBLOCK, cycles_passed; int rtmp = reglist_find_free(reglist); if (HACK_ENABLED(NDHACK_NO_STALLS)) @@ -3706,17 +3755,11 @@ static void cop2_do_stall_check(u_int op, int i, const struct regstat *i_regs, u if (other_gte_op_cycles >= 0) stall = other_gte_op_cycles - cycles_passed; else if (cycles_passed >= 44) - stall = 0; // can't stall + stall = 0; // can't possibly stall if (stall == -MAXBLOCK && rtmp >= 0) { // unknown stall, do the expensive runtime check assem_debug("; cop2_do_stall_check\n"); -#if 0 // too slow - save_regs(reglist); - emit_movimm(gte_cycletab[op], 0); - emit_addimm(HOST_CCREG, cinfo[i].ccadj, 1); - emit_far_call(call_gteStall); - restore_regs(reglist); -#else + // busy - (cc + adj) -> busy - adj - cc host_tempreg_acquire(); emit_readword(&psxRegs.gteBusyCycle, rtmp); emit_addimm(rtmp, -cinfo[i].ccadj, rtmp); @@ -3725,7 +3768,6 @@ static void cop2_do_stall_check(u_int op, int i, const struct regstat *i_regs, u emit_cmovb_reg(rtmp, HOST_CCREG); //emit_log_gte_stall(i, 0, reglist); host_tempreg_release(); -#endif } else if (stall > 0) { //emit_log_gte_stall(i, stall, reglist); @@ -3733,7 +3775,8 @@ static void cop2_do_stall_check(u_int op, int i, const struct regstat *i_regs, u } // save gteBusyCycle, if needed - if (gte_cycletab[op] == 0) + cycles = gte_cycletab[op]; + if (cycles == 0) return; other_gte_op_cycles = -1; for (j = i + 1; j < slen; j++) { @@ -3750,20 +3793,12 @@ static void cop2_do_stall_check(u_int op, int i, const struct regstat *i_regs, u // will handle stall when assembling that op return; cycles_passed = cinfo[min(j, slen -1)].ccadj - cinfo[i].ccadj; - if (cycles_passed >= 44) + if (cycles_passed >= cycles) return; assem_debug("; save gteBusyCycle\n"); host_tempreg_acquire(); -#if 0 - emit_readword(&last_count, HOST_TEMPREG); - emit_add(HOST_TEMPREG, HOST_CCREG, HOST_TEMPREG); - emit_addimm(HOST_TEMPREG, cinfo[i].ccadj, HOST_TEMPREG); - emit_addimm(HOST_TEMPREG, gte_cycletab[op]), HOST_TEMPREG); + emit_addimm(HOST_CCREG, cinfo[i].ccadj + cycles, HOST_TEMPREG); emit_writeword(HOST_TEMPREG, &psxRegs.gteBusyCycle); -#else - emit_addimm(HOST_CCREG, cinfo[i].ccadj + gte_cycletab[op], HOST_TEMPREG); - emit_writeword(HOST_TEMPREG, &psxRegs.gteBusyCycle); -#endif host_tempreg_release(); } @@ -3954,12 +3989,12 @@ static void c2ls_assemble(int i, const struct regstat *i_regs, int ccadj_) enum stub_type type; int offset_reg = -1; int fastio_reg_override = -1; + u_int addr_const = ~0; u_int reglist=get_host_reglist(i_regs->regmap); u_int copr=(source[i]>>16)&0x1f; s=get_reg(i_regs->regmap,dops[i].rs1); tl=get_reg(i_regs->regmap,FTEMP); offset=cinfo[i].imm; - assert(dops[i].rs1>0); assert(tl>=0); if(i_regs->regmap[HOST_CCREG]==CCREG) @@ -3971,8 +4006,13 @@ static void c2ls_assemble(int i, const struct regstat *i_regs, int ccadj_) if (dops[i].opcode==0x3a) { // SWC2 reglist |= 1<=0) c=(i_regs->wasconst>>s)&1; - memtarget=c&&(((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE); + if (s >= 0) { + c = (i_regs->isconst >> s) & 1; + if (c) { + addr_const = constmap[i][s] + offset; + memtarget = ((signed int)addr_const) < (signed int)(0x80000000 + RAM_SIZE); + } + } cop2_do_stall_check(0, i, i_regs, reglist); @@ -4021,9 +4061,9 @@ static void c2ls_assemble(int i, const struct regstat *i_regs, int ccadj_) host_tempreg_release(); if(jaddr2) add_stub_r(type,jaddr2,out,i,ar,i_regs,ccadj_,reglist); - if(dops[i].opcode==0x3a) // SWC2 + if (dops[i].opcode == 0x3a && (!c || is_ram_addr(addr_const))) // SWC2 do_store_smc_check(i, i_regs, reglist, ar); - if (dops[i].opcode==0x32) { // LWC2 + if (dops[i].opcode == 0x32) { // LWC2 host_tempreg_acquire(); cop2_put_dreg(copr,tl,HOST_TEMPREG); host_tempreg_release(); @@ -4110,6 +4150,8 @@ static void do_unalignedwritestub(int n) if(cc<0) emit_loadreg(CCREG,2); emit_addimm(cc<0?2:cc,(int)stubs[n].d+1,2); + emit_movimm(start + i*4,3); + emit_writeword(3,&psxRegs.pc); emit_far_call((dops[i].opcode==0x2a?jump_handle_swl:jump_handle_swr)); emit_addimm(0,-((int)stubs[n].d+1),cc<0?2:cc); if(cc<0) @@ -4235,28 +4277,27 @@ static void intcall_assemble(int i, const struct regstat *i_regs, int ccadj_) static void speculate_mov(int rs,int rt) { - if(rt!=0) { - smrv_strong_next|=1<=0) { if(get_final_value(hr,i,&value)) - smrv[dops[i].rt1]=value; - else smrv[dops[i].rt1]=constmap[i][hr]; + ndrc_smrv_regs[dops[i].rt1]=value; + else ndrc_smrv_regs[dops[i].rt1]=constmap[i][hr]; smrv_strong_next|=1<>24)==0xa0)) { + if(start<0x2000&&(dops[i].rt1==26||(ndrc_smrv_regs[dops[i].rt1]>>24)==0xa0)) { // special case for BIOS - smrv[dops[i].rt1]=0xa0000000; + ndrc_smrv_regs[dops[i].rt1]=0xa0000000; smrv_strong_next|=1<>r)&1),(smrv_weak>>r)&1,regs[i].isconst,regs[i].wasconst); #endif } @@ -4545,7 +4587,7 @@ static void address_generation(int i, const struct regstat *i_regs, signed char { int offset = cinfo[i].imm; int add_offset = offset != 0; - int c=(i_regs->wasconst>>rs)&1; + int c = rs >= 0 && ((i_regs->wasconst >> rs) & 1); if(dops[i].rs1==0) { // Using r0 as a base address assert(ra >= 0); @@ -4584,12 +4626,6 @@ static void address_generation(int i, const struct regstat *i_regs, signed char cinfo[i].addr = rs; add_offset = 0; } - else if (dops[i].itype == STORELR) { // overwrites addr - assert(ra >= 0); - assert(rs != ra); - emit_mov(rs, ra); - cinfo[i].addr = ra; - } else cinfo[i].addr = rs; if (add_offset) { @@ -4638,7 +4674,7 @@ static void address_generation(int i, const struct regstat *i_regs, signed char } } -static int get_final_value(int hr, int i, int *value) +static int get_final_value(int hr, int i, u_int *value) { int reg=regs[i].regmap[hr]; while(i=0&&((regs[i-1].isconst>>hr)&1)&&pre[hr]==regmap[hr] - &®map[hr]==regs[i-1].regmap[hr]&&((regs[i-1].loadedconst>>hr)&1)) + for (hr = 0; hr < HOST_REGS; hr++) { + if (hr == EXCLUDE_REG || regmap[hr] < 0 || pre[hr] != regmap[hr]) + continue; + if ((((regs[i-1].isconst & regs[i-1].loadedconst) >> hr) & 1) + && regmap[hr] == regs[i-1].regmap[hr]) { - regs[i].loadedconst|=1<>hr)&1)) { assert(regmap[hr]<64); if(((regs[i].isconst>>hr)&1)&®map[hr]>0) { - int value,similar=0; + u_int value, similar=0; if(get_final_value(hr,i,&value)) { // see if some other register has similar value for(hr2=0;hr2>2; @@ -4794,6 +4834,7 @@ static void wb_needed_dirtys(const signed char i_regmap[], uint64_t i_dirty, int } // Load all registers (except cycle count) +#ifndef load_all_regs static void load_all_regs(const signed char i_regmap[]) { int hr; @@ -4810,48 +4851,31 @@ static void load_all_regs(const signed char i_regmap[]) } } } +#endif // Load all current registers also needed by next instruction static void load_needed_regs(const signed char i_regmap[], const signed char next_regmap[]) { + signed char regmap_sel[HOST_REGS]; int hr; - for(hr=0;hr=0) { - if(i_regmap[hr]==0) { - emit_zeroreg(hr); - } - else - if(i_regmap[hr]>0 && i_regmap[hr]= 0) + regmap_sel[hr] = i_regmap[hr]; } + load_all_regs(regmap_sel); } // Load all regs, storing cycle count if necessary static void load_regs_entry(int t) { - int hr; if(dops[t].is_ds) emit_addimm(HOST_CCREG,CLOCK_ADJUST(1),HOST_CCREG); else if(cinfo[t].ccadj) emit_addimm(HOST_CCREG,-cinfo[t].ccadj,HOST_CCREG); if(regs[t].regmap_entry[HOST_CCREG]!=CCREG) { emit_storereg(CCREG,HOST_CCREG); } - // Load 32-bit regs - for(hr=0;hr=0&®s[t].regmap_entry[hr]dirty & ~i_regs->loadedconst) { + assem_debug("/ drc_dbg_wb\n"); + wb_dirtys(i_regs->regmap, i_regs->dirty & ~i_regs->loadedconst); + assem_debug("\\ drc_dbg_wb\n"); + } +} #else #define drc_dbg_emit_do_cmp(x,y) +#define drc_dbg_emit_wb_dirtys(x,y) #endif // Used when a branch jumps into the delay slot of another branch @@ -5143,12 +5183,9 @@ static void do_ccstub(int n) literal_pool(256); assem_debug("do_ccstub %x\n",start+(u_int)stubs[n].b*4); set_jump_target(stubs[n].addr, out); - int i=stubs[n].b; - if(stubs[n].d==NULLDS) { - // Delay slot instruction is nullified ("likely" branch) - wb_dirtys(regs[i].regmap,regs[i].dirty); - } - else if(stubs[n].d!=TAKEN) { + int i = stubs[n].b; + int r_pc = -1; + if (stubs[n].d != TAKEN) { wb_dirtys(branch_regs[i].regmap,branch_regs[i].dirty); } else { @@ -5158,8 +5195,7 @@ static void do_ccstub(int n) if(stubs[n].c!=-1) { // Save PC as return address - emit_movimm(stubs[n].c,0); - emit_writeword(0,&pcaddr); + emit_movimm(stubs[n].c, (r_pc = 0)); } else { @@ -5213,7 +5249,7 @@ static void do_ccstub(int n) } hr++; } - if((dops[i].opcode&0x2E)==6) // BLEZ/BGTZ needs another register + if ((dops[i].opcode & 0x3e) == 6) // BLEZ/BGTZ needs another register { while(hr=0) emit_cmp(s1l,s2l); @@ -5240,7 +5276,7 @@ static void do_ccstub(int n) emit_cmovne_reg(alt,addr); #endif } - if((dops[i].opcode&0x2f)==5) // BNE + else if (dops[i].opcode == 5) // BNE { #ifdef HAVE_CMOV_IMM if(s2l>=0) emit_cmp(s1l,s2l); @@ -5253,7 +5289,7 @@ static void do_ccstub(int n) emit_cmovne_reg(alt,addr); #endif } - if((dops[i].opcode&0x2f)==6) // BLEZ + else if (dops[i].opcode == 6) // BLEZ { //emit_movimm(cinfo[i].ba,alt); //emit_movimm(start+i*4+8,addr); @@ -5261,7 +5297,7 @@ static void do_ccstub(int n) emit_cmpimm(s1l,1); emit_cmovl_reg(alt,addr); } - if((dops[i].opcode&0x2f)==7) // BGTZ + else if (dops[i].opcode == 7) // BGTZ { //emit_movimm(cinfo[i].ba,addr); //emit_movimm(start+i*4+8,ntaddr); @@ -5269,53 +5305,33 @@ static void do_ccstub(int n) emit_cmpimm(s1l,1); emit_cmovl_reg(ntaddr,addr); } - if((dops[i].opcode==1)&&(dops[i].opcode2&0x2D)==0) // BLTZ + else if (dops[i].itype == SJUMP) // BLTZ/BGEZ { //emit_movimm(cinfo[i].ba,alt); //emit_movimm(start+i*4+8,addr); - emit_mov2imm_compact(cinfo[i].ba,alt,start+i*4+8,addr); - emit_test(s1l,s1l); - emit_cmovs_reg(alt,addr); - } - if((dops[i].opcode==1)&&(dops[i].opcode2&0x2D)==1) // BGEZ - { - //emit_movimm(cinfo[i].ba,addr); - //emit_movimm(start+i*4+8,alt); - emit_mov2imm_compact(cinfo[i].ba,addr,start+i*4+8,alt); - emit_test(s1l,s1l); - emit_cmovs_reg(alt,addr); - } - if(dops[i].opcode==0x11 && dops[i].opcode2==0x08 ) { - if(source[i]&0x10000) // BC1T - { - //emit_movimm(cinfo[i].ba,alt); - //emit_movimm(start+i*4+8,addr); - emit_mov2imm_compact(cinfo[i].ba,alt,start+i*4+8,addr); - emit_testimm(s1l,0x800000); - emit_cmovne_reg(alt,addr); - } - else // BC1F - { - //emit_movimm(cinfo[i].ba,addr); - //emit_movimm(start+i*4+8,alt); - emit_mov2imm_compact(cinfo[i].ba,addr,start+i*4+8,alt); - emit_testimm(s1l,0x800000); - emit_cmovne_reg(alt,addr); + if (dops[i].rs1) { + emit_mov2imm_compact(cinfo[i].ba, + (dops[i].opcode2 & 1) ? addr : alt, start + i*4 + 8, + (dops[i].opcode2 & 1) ? alt : addr); + emit_test(s1l,s1l); + emit_cmovs_reg(alt,addr); } + else + emit_movimm((dops[i].opcode2 & 1) ? cinfo[i].ba : start + i*4 + 8, addr); } - emit_writeword(addr,&pcaddr); + r_pc = addr; } else if(dops[i].itype==RJUMP) { - int r=get_reg(branch_regs[i].regmap,dops[i].rs1); + r_pc = get_reg(branch_regs[i].regmap, dops[i].rs1); if (ds_writes_rjump_rs(i)) { - r=get_reg(branch_regs[i].regmap,RTEMP); + r_pc = get_reg(branch_regs[i].regmap, RTEMP); } - emit_writeword(r,&pcaddr); } else {SysPrintf("Unknown branch type in do_ccstub\n");abort();} } + emit_writeword(r_pc, &psxRegs.pc); // Update cycle count assert(branch_regs[i].regmap[HOST_CCREG]==CCREG||branch_regs[i].regmap[HOST_CCREG]==-1); if(stubs[n].a) emit_addimm(HOST_CCREG,(int)stubs[n].a,HOST_CCREG); @@ -5326,17 +5342,13 @@ static void do_ccstub(int n) load_needed_regs(branch_regs[i].regmap,regs[(cinfo[i].ba-start)>>2].regmap_entry); else if(dops[i].itype==RJUMP) { if(get_reg(branch_regs[i].regmap,RTEMP)>=0) - emit_readword(&pcaddr,get_reg(branch_regs[i].regmap,RTEMP)); + emit_readword(&psxRegs.pc,get_reg(branch_regs[i].regmap,RTEMP)); else emit_loadreg(dops[i].rs1,get_reg(branch_regs[i].regmap,dops[i].rs1)); } }else if(stubs[n].d==NOTTAKEN) { if(i=0); return_address=start+i*4+8; if(rt>=0) { @@ -5382,7 +5394,8 @@ static void ujump_assemble_write_ra(int i) if(i_regmap[temp]!=PTEMP) emit_movimm((uintptr_t)hash_table_get(return_address),temp); } #endif - emit_movimm(return_address,rt); // PC into link register + if (!((regs[i].loadedconst >> rt) & 1)) + emit_movimm(return_address, rt); // PC into link register #ifdef IMM_PREFETCH emit_prefetch(hash_table_get(return_address)); #endif @@ -5392,7 +5405,6 @@ static void ujump_assemble_write_ra(int i) static void ujump_assemble(int i, const struct regstat *i_regs) { - int ra_done=0; if(i==(cinfo[i].ba-start)>>2) assem_debug("idle loop\n"); address_generation(i+1,i_regs,regs[i].regmap_entry); #ifdef REG_PREFETCH @@ -5405,17 +5417,13 @@ static void ujump_assemble(int i, const struct regstat *i_regs) if(i_regmap[temp]==PTEMP) emit_movimm((uintptr_t)hash_table_get(return_address),temp); } #endif - if(dops[i].rt1==31&&(dops[i].rt1==dops[i+1].rs1||dops[i].rt1==dops[i+1].rs2)) { + if (dops[i].rt1 == 31) ujump_assemble_write_ra(i); // writeback ra for DS - ra_done=1; - } ds_assemble(i+1,i_regs); uint64_t bc_unneeded=branch_regs[i].u; bc_unneeded|=1|(1LL<=0); return_address=start+i*4+8; #ifdef REG_PREFETCH @@ -5454,7 +5460,8 @@ static void rjump_assemble_write_ra(int i) if(i_regmap[temp]!=PTEMP) emit_movimm((uintptr_t)hash_table_get(return_address),temp); } #endif - emit_movimm(return_address,rt); // PC into link register + if (!((regs[i].loadedconst >> rt) & 1)) + emit_movimm(return_address, rt); // PC into link register #ifdef IMM_PREFETCH emit_prefetch(hash_table_get(return_address)); #endif @@ -5464,7 +5471,6 @@ static void rjump_assemble(int i, const struct regstat *i_regs) { int temp; int rs,cc; - int ra_done=0; rs=get_reg(branch_regs[i].regmap,dops[i].rs1); assert(rs>=0); if (ds_writes_rjump_rs(i)) { @@ -5492,18 +5498,14 @@ static void rjump_assemble(int i, const struct regstat *i_regs) if(rh>=0) do_preload_rhash(rh); } #endif - if(dops[i].rt1!=0&&(dops[i].rt1==dops[i+1].rs1||dops[i].rt1==dops[i+1].rs2)) { + if (dops[i].rt1 != 0) rjump_assemble_write_ra(i); - ra_done=1; - } ds_assemble(i+1,i_regs); uint64_t bc_unneeded=branch_regs[i].u; bc_unneeded|=1|(1LL<= 0) { + emit_addimm(sp, ld_ofs, HOST_TEMPREG); + emit_ldr_dualindexed(ro, HOST_TEMPREG, HOST_TEMPREG); + } + else + emit_readword_indexed(ld_ofs, sp, HOST_TEMPREG); + emit_cmpimm(HOST_TEMPREG, 17); + t_exit[2] = out; + emit_jl(0); + + assem_debug("1:\n"); + loop_target = out; + emit_addimm(HOST_TEMPREG, -16, HOST_TEMPREG); + emit_addimm(cc, cycles, cc); + emit_cmpimm(HOST_TEMPREG, 17); + t_loop_break = out; + emit_jl(DJT_2); + emit_cmpimm(cc, -cycles); + emit_jl(loop_target); + + assem_debug("2:\n"); + set_jump_target(t_loop_break, out); + do_store_word(sp, ld_ofs, HOST_TEMPREG, ro, 1); + + for (j = 0; j < ARRAY_SIZE(t_exit); j++) + set_jump_target(t_exit[j], out); + host_tempreg_release(); +} + static void cjump_assemble(int i, const struct regstat *i_regs) { const signed char *i_regmap = i_regs->regmap; @@ -5570,6 +5618,7 @@ static void cjump_assemble(int i, const struct regstat *i_regs) int internal=internal_branch(cinfo[i].ba); if(i==(cinfo[i].ba-start)>>2) assem_debug("idle loop\n"); if(!match) invert=1; + if (vsync_hack && (vsync_hack >> 16) == i) invert=1; #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(i>(cinfo[i].ba-start)>>2) invert=1; #endif @@ -5704,6 +5753,8 @@ static void cjump_assemble(int i, const struct regstat *i_regs) } if(invert) { if(taken) set_jump_target(taken, out); + if (vsync_hack && (vsync_hack >> 16) == i) + vsync_hack_assemble(i, vsync_hack & 0xffff, cc); #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if (match && (!internal || !dops[(cinfo[i].ba-start)>>2].is_ds)) { if(adj) { @@ -5791,6 +5842,7 @@ static void cjump_assemble(int i, const struct regstat *i_regs) load_reg(regs[i].regmap,branch_regs[i].regmap,ROREG); load_regs(regs[i].regmap,branch_regs[i].regmap,CCREG,INVCP); ds_assemble(i+1,&branch_regs[i]); + drc_dbg_emit_wb_dirtys(i+1, &branch_regs[i]); cc=get_reg(branch_regs[i].regmap,CCREG); if(cc==-1) { emit_loadreg(CCREG,cc=HOST_CCREG); @@ -5904,7 +5956,7 @@ static void sjump_assemble(int i, const struct regstat *i_regs) if(dops[i].rt1==31) { int rt,return_address; rt=get_reg(branch_regs[i].regmap,31); - assem_debug("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); + //assem_debug("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); if(rt>=0) { // Save the PC even if the branch is not taken return_address=start+i*4+8; @@ -5953,7 +6005,7 @@ static void sjump_assemble(int i, const struct regstat *i_regs) if(adj&&!invert) emit_addimm(cc, cinfo[i].ccadj + CLOCK_ADJUST(2) - adj, cc); { assert(s1l>=0); - if((dops[i].opcode2&0xf)==0) // BLTZ/BLTZAL + if ((dops[i].opcode2 & 1) == 0) // BLTZ/BLTZAL { emit_test(s1l,s1l); if(invert){ @@ -5964,7 +6016,7 @@ static void sjump_assemble(int i, const struct regstat *i_regs) emit_js(0); } } - if((dops[i].opcode2&0xf)==1) // BGEZ/BLTZAL + else // BGEZ/BGEZAL { emit_test(s1l,s1l); if(invert){ @@ -6019,34 +6071,29 @@ static void sjump_assemble(int i, const struct regstat *i_regs) // In-order execution (branch first) //printf("IOE\n"); void *nottaken = NULL; - if(dops[i].rt1==31) { - int rt,return_address; - rt=get_reg(branch_regs[i].regmap,31); - if(rt>=0) { + if (!unconditional && !nevertaken) { + assert(s1l >= 0); + emit_test(s1l, s1l); + } + if (dops[i].rt1 == 31) { + int rt, return_address; + rt = get_reg(branch_regs[i].regmap,31); + if(rt >= 0) { // Save the PC even if the branch is not taken - return_address=start+i*4+8; - emit_movimm(return_address,rt); // PC into link register + return_address = start + i*4+8; + emit_movimm(return_address, rt); // PC into link register #ifdef IMM_PREFETCH emit_prefetch(hash_table_get(return_address)); #endif } } - if(!unconditional) { - //printf("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); - assert(s1l>=0); - if((dops[i].opcode2&0x0d)==0) // BLTZ/BLTZL/BLTZAL/BLTZALL - { - emit_test(s1l,s1l); - nottaken=out; - emit_jns(DJT_1); - } - if((dops[i].opcode2&0x0d)==1) // BGEZ/BGEZL/BGEZAL/BGEZALL - { - emit_test(s1l,s1l); - nottaken=out; - emit_js(DJT_1); - } - } // if(!unconditional) + if (!unconditional && !nevertaken) { + nottaken = out; + if (!(dops[i].opcode2 & 1)) // BLTZ/BLTZAL + emit_jns(DJT_1); + else // BGEZ/BGEZAL + emit_js(DJT_1); + } int adj; uint64_t ds_unneeded=branch_regs[i].u; ds_unneeded&=~((1LL<>14));break; case RJUMP: - if (dops[i].opcode==0x9&&dops[i].rt1!=31) + if (dops[i].opcode2 == 9 && dops[i].rt1 != 31) printf (" %x: %s r%d,r%d\n",start+i*4,insn[i],dops[i].rt1,dops[i].rs1); else printf (" %x: %s r%d\n",start+i*4,insn[i],dops[i].rs1); @@ -6231,7 +6281,7 @@ void disassemble_inst(int i) #ifndef REGMAP_PRINT return; #endif - printf("D: %"PRIx64" WD: %"PRIx64" U: %"PRIx64" hC: %x hWC: %x hLC: %x\n", + printf("D: %x WD: %x U: %"PRIx64" hC: %x hWC: %x hLC: %x\n", regs[i].dirty, regs[i].wasdirty, unneeded_reg[i], regs[i].isconst, regs[i].wasconst, regs[i].loadedconst); print_regmap("pre: ", regmap_pre[i]); @@ -6264,11 +6314,11 @@ static noinline void new_dynarec_test(void) SysPrintf("(%p) testing if we can run recompiled code @%p...\n", new_dynarec_test, out); - ((volatile u_int *)NDRC_WRITE_OFFSET(out))[0]++; // make the cache dirty for (i = 0; i < ARRAY_SIZE(ret); i++) { out = ndrc->translation_cache; beginning = start_block(); + ((volatile u_int *)NDRC_WRITE_OFFSET(out))[0]++; // make the cache dirty emit_movimm(DRC_TEST_VAL + i, 0); // test emit_ret(); literal_pool(0); @@ -6284,6 +6334,12 @@ static noinline void new_dynarec_test(void) out = ndrc->translation_cache; } +static int get_cycle_multiplier(void) +{ + return Config.cycle_multiplier_override && Config.cycle_multiplier == CYCLE_MULT_DEFAULT + ? Config.cycle_multiplier_override : Config.cycle_multiplier; +} + // clear the state completely, instead of just marking // things invalid like invalidate_all_pages() does void new_dynarec_clear_full(void) @@ -6291,14 +6347,15 @@ void new_dynarec_clear_full(void) int n; out = ndrc->translation_cache; memset(invalid_code,1,sizeof(invalid_code)); - memset(hash_table,0xff,sizeof(hash_table)); - memset(mini_ht,-1,sizeof(mini_ht)); memset(shadow,0,sizeof(shadow)); + hash_table_clear(); + mini_ht_clear(); copy=shadow; expirep = EXPIRITY_OFFSET; - pending_exception=0; literalcount=0; stop_after_jal=0; + ni_count=0; + err_print_count=0; inv_code_start=inv_code_end=~0; hack_addr=0; f1_hack=0; @@ -6311,27 +6368,44 @@ void new_dynarec_clear_full(void) stat_clear(stat_blocks); stat_clear(stat_links); - cycle_multiplier_old = Config.cycle_multiplier; - new_dynarec_hacks_old = new_dynarec_hacks; + if (ndrc_g.cycle_multiplier_old != Config.cycle_multiplier + || ndrc_g.hacks_old != (ndrc_g.hacks | ndrc_g.hacks_pergame)) + { + SysPrintf("ndrc config: mul=%d, ha=%x, pex=%d\n", + get_cycle_multiplier(), ndrc_g.hacks, Config.PreciseExceptions); + } + ndrc_g.cycle_multiplier_old = Config.cycle_multiplier; + ndrc_g.hacks_old = ndrc_g.hacks | ndrc_g.hacks_pergame; +} + +static int pgsize(void) +{ + long ret = -1; +#ifdef _SC_PAGESIZE + ret = sysconf(_SC_PAGESIZE); +#endif + if (ret < 1) + ret = 4096; + return ret; } void new_dynarec_init(void) { - SysPrintf("Init new dynarec, ndrc size %x\n", (int)sizeof(*ndrc)); + int align = pgsize() - 1; + SysPrintf("Init new dynarec, ndrc size %x, pgsize %d\n", + (int)sizeof(*ndrc), align + 1); -#ifdef _3DS - check_rosalina(); -#endif #ifdef BASE_ADDR_DYNAMIC #ifdef VITA sceBlock = getVMBlock(); //sceKernelAllocMemBlockForVM("code", sizeof(*ndrc)); if (sceBlock <= 0) - SysPrintf("sceKernelAllocMemBlockForVM failed: %x\n", sceBlock); + SysPrintf("getVMBlock failed: %x\n", sceBlock); int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&ndrc); - if (ret < 0) - SysPrintf("sceKernelGetMemBlockBase failed: %x\n", ret); - sceKernelOpenVMDomain(); - sceClibPrintf("translation_cache = 0x%08lx\n ", (long)ndrc->translation_cache); + if (ret) + SysPrintf("sceKernelGetMemBlockBase: %x\n", ret); + ret = sceKernelOpenVMDomain(); + if (ret) + SysPrintf("sceKernelOpenVMDomain: %x\n", ret); #elif defined(_MSC_VER) ndrc = VirtualAlloc(NULL, sizeof(*ndrc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); @@ -6360,6 +6434,8 @@ void new_dynarec_init(void) void *mw = mmap(NULL, sizeof(*ndrc), PROT_READ | PROT_WRITE, (flags = MAP_SHARED), fd, 0); assert(mw != MAP_FAILED); + #endif + #if defined(NO_WRITE_EXEC) || defined(TC_WRITE_OFFSET) prot = PROT_READ | PROT_EXEC; #endif ndrc = mmap((void *)desired_addr, sizeof(*ndrc), prot, flags, fd, 0); @@ -6372,25 +6448,30 @@ void new_dynarec_init(void) #endif #endif #else + ndrc = (struct ndrc_mem *)((size_t)(ndrc_bss + align) & ~align); #ifndef NO_WRITE_EXEC // not all systems allow execute in data segment by default // size must be 4K aligned for 3DS? if (mprotect(ndrc, sizeof(*ndrc), PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - SysPrintf("mprotect() failed: %s\n", strerror(errno)); + SysPrintf("mprotect(%p) failed: %s\n", ndrc, strerror(errno)); + #endif + #ifdef TC_WRITE_OFFSET + #error "misconfiguration detected" #endif #endif out = ndrc->translation_cache; new_dynarec_clear_full(); + hash_table_ptr = hash_table; #ifdef HOST_IMM8 // Copy this into local area so we don't have to put it in every literal pool invc_ptr=invalid_code; #endif arch_init(); new_dynarec_test(); - ram_offset=(uintptr_t)rdram-0x80000000; - if (ram_offset!=0) - SysPrintf("warning: RAM is not directly mapped, performance will suffer\n"); + ram_offset = (uintptr_t)psxM - 0x80000000; + if (ram_offset != 0) + SysPrintf("RAM is not directly mapped\n"); SysPrintf("Mapped (RAM/scrp/ROM/LUTs/TC):\n"); SysPrintf("%p/%p/%p/%p/%p\n", psxM, psxH, psxR, mem_rtab, out); } @@ -6425,29 +6506,27 @@ void new_dynarec_cleanup(void) static u_int *get_source_start(u_int addr, u_int *limit) { - if (addr < 0x00200000 || - (0xa0000000 <= addr && addr < 0xa0200000)) + if (addr < 0x00800000u + || (0x80000000u <= addr && addr < 0x80800000u) + || (0xa0000000u <= addr && addr < 0xa0800000u)) { // used for BIOS calls mostly? - *limit = (addr&0xa0000000)|0x00200000; - return (u_int *)(rdram + (addr&0x1fffff)); + *limit = (addr & 0xa0600000) + 0x00200000; + return (u_int *)(psxM + (addr & 0x1fffff)); } - else if (!Config.HLE && ( - /* (0x9fc00000 <= addr && addr < 0x9fc80000) ||*/ - (0xbfc00000 <= addr && addr < 0xbfc80000))) + else if ( + (0x9fc00000u <= addr && addr < 0x9fc80000u) || + (0xbfc00000u <= addr && addr < 0xbfc80000u)) { - // BIOS. The multiplier should be much higher as it's uncached 8bit mem, - // but timings in PCSX are too tied to the interpreter's 2-per-insn assumption - if (!HACK_ENABLED(NDHACK_OVERRIDE_CYCLE_M)) - cycle_multiplier_active = 200; + // BIOS. The multiplier should be much higher as it's uncached 8bit mem + // XXX: disabled as this introduces differences from the interpreter + // and lightrec multipliers making emu variations act inconsistently + //if (!HACK_ENABLED(NDHACK_OVERRIDE_CYCLE_M)) + // cycle_multiplier_active = 200; *limit = (addr & 0xfff00000) | 0x80000; return (u_int *)((u_char *)psxR + (addr&0x7ffff)); } - else if (addr >= 0x80000000 && addr < 0x80000000+RAM_SIZE) { - *limit = (addr & 0x80600000) + 0x00200000; - return (u_int *)(rdram + (addr&0x1fffff)); - } return NULL; } @@ -6559,7 +6638,7 @@ void new_dynarec_load_blocks(const void *save, int size) psxRegs.GPR.r[i] = 0x1f800000; } - ndrc_get_addr_ht(sblocks[b].addr); + ndrc_get_addr_ht_param(hash_table, sblocks[b].addr, ndrc_cm_compile_offline); for (f = sblocks[b].regflags, i = 0; f; f >>= 1, i++) { if (f & 1) @@ -6584,9 +6663,64 @@ void new_dynarec_print_stats(void) #endif } +static void force_intcall(int i) +{ + memset(&dops[i], 0, sizeof(dops[i])); + dops[i].itype = INTCALL; + dops[i].rs1 = CCREG; + dops[i].is_exception = dops[i].may_except = 1; + cinfo[i].ba = -1; +} + +static noinline void do_vsync(int i) +{ + // lui a0, x; addiu a0, x; jal puts + u32 addr = (cinfo[i].imm << 16) + (signed short)cinfo[i+1].imm; + char *str = NULL; + int j, t, jals_cnt = 0; + + if (!is_ram_addr(addr)) + return; + str = (char *)psxM + (addr & 0x1fffff); + if (!str || strncmp(str, "VSync: timeout", 14)) + return; + // jal clearPad, jal clearRCnt; j return; nop + for (j = i+2; j < slen; j++) { + if (dops[j].itype == SHIFTIMM || dops[j].itype == IMM16 || dops[j].itype == ALU) + continue; + if (dops[j].opcode == 0x03) { + jals_cnt++; continue; + } + break; + } + if (j >= slen || jals_cnt != 3 || dops[j++].opcode != 0x02) + return; + for (; j < slen; j++) + if (dops[j].itype != SHIFTIMM && dops[j].itype != IMM16) + break; + if (j >= slen || dops[j].opcode != 0x23) // lw x, condition + return; + j += 2; + if (dops[j].opcode != 0 || dops[j].opcode2 != 0x2A) // slt x, y + return; + if (dops[++j].opcode != 0x05) // bnez x, loop + return; + t = (cinfo[j].ba - start) / 4; + if (t < 0 || t >= slen) + return; + // lw x, d(sp) + if (dops[t].opcode != 0x23 || dops[t].rs1 != 29 || (u32)cinfo[t].imm >= 1024) + return; + if (dops[t+2].opcode != 0x09 || cinfo[t+2].imm != -1) // addiu x, -1 + return; + SysPrintf("vsync @%08x\n", start + t*4); + vsync_hack = (j << 16) | (cinfo[t].imm & 0xffff); +} + static int apply_hacks(void) { int i; + vsync_hack = 0; if (HACK_ENABLED(NDHACK_NO_COMPAT_HACKS)) return 0; /* special hack(s) */ @@ -6600,6 +6734,19 @@ static int apply_hacks(void) SysPrintf("PE2 hack @%08x\n", start + (i+3)*4); dops[i + 3].itype = NOP; } + // see also: psxBiosCheckExe() + if (i > 1 && dops[i].opcode == 0x0f && dops[i].rt1 == 4 + && dops[i+1].opcode == 0x09 && dops[i+1].rt1 == 4 && dops[i+1].rs1 == 4 + && dops[i+2].opcode == 0x03) + { + do_vsync(i); + } + } + if (source[0] == 0x3c05edb8 && source[1] == 0x34a58320) + { + // lui a1, 0xEDB8; ori a1, 0x8320 + SysPrintf("F1 2000 hack @%08x\n", start); + cycle_multiplier_active = 100; } i = slen; if (i > 10 && source[i-1] == 0 && source[i-2] == 0x03e00008 @@ -6618,29 +6765,49 @@ static int apply_hacks(void) return 1; } } +#if 0 // alt vsync, not used + if (Config.HLE) + { + if (start <= psxRegs.biosBranchCheck && psxRegs.biosBranchCheck < start + i*4) + { + i = (psxRegs.biosBranchCheck - start) / 4u + 23; + if (dops[i].is_jump && !dops[i+1].bt) + { + force_intcall(i); + dops[i+1].is_ds = 0; + } + } + } +#endif return 0; } -static noinline void pass1_disassemble(u_int pagelimit) +static int is_ld_use_hazard(const struct decoded_insn *op_ld, + const struct decoded_insn *op) { - int i, j, done = 0, ni_count = 0; - unsigned int type,op,op2,op3; + if (op_ld->rt1 == 0 || (op_ld->rt1 != op->rs1 && op_ld->rt1 != op->rs2)) + return 0; + if (op_ld->itype == LOADLR && op->itype == LOADLR) + return op_ld->rt1 == op_ld->rs1; + return op->itype != CJUMP && op->itype != SJUMP; +} - for (i = 0; !done; i++) - { - int force_prev_to_interpreter = 0; +static void disassemble_one(int i, u_int src) +{ + unsigned int type, op, op2, op3; + enum ls_width_type ls_type = LS_32; memset(&dops[i], 0, sizeof(dops[i])); memset(&cinfo[i], 0, sizeof(cinfo[i])); cinfo[i].ba = -1; cinfo[i].addr = -1; - dops[i].opcode = op = source[i] >> 26; + dops[i].opcode = op = src >> 26; op2 = 0; type = INTCALL; set_mnemonic(i, "???"); switch(op) { case 0x00: set_mnemonic(i, "special"); - op2=source[i]&0x3f; + op2 = src & 0x3f; switch(op2) { case 0x00: set_mnemonic(i, "SLL"); type=SHIFTIMM; break; @@ -6653,7 +6820,6 @@ static noinline void pass1_disassemble(u_int pagelimit) case 0x09: set_mnemonic(i, "JALR"); type=RJUMP; break; case 0x0C: set_mnemonic(i, "SYSCALL"); type=SYSCALL; break; case 0x0D: set_mnemonic(i, "BREAK"); type=SYSCALL; break; - case 0x0F: set_mnemonic(i, "SYNC"); type=OTHER; break; case 0x10: set_mnemonic(i, "MFHI"); type=MOV; break; case 0x11: set_mnemonic(i, "MTHI"); type=MOV; break; case 0x12: set_mnemonic(i, "MFLO"); type=MOV; break; @@ -6676,7 +6842,7 @@ static noinline void pass1_disassemble(u_int pagelimit) break; case 0x01: set_mnemonic(i, "regimm"); type = SJUMP; - op2 = (source[i] >> 16) & 0x1f; + op2 = (src >> 16) & 0x1f; switch(op2) { case 0x10: set_mnemonic(i, "BLTZAL"); break; @@ -6703,9 +6869,9 @@ static noinline void pass1_disassemble(u_int pagelimit) case 0x0E: set_mnemonic(i, "XORI"); type=IMM16; break; case 0x0F: set_mnemonic(i, "LUI"); type=IMM16; break; case 0x10: set_mnemonic(i, "COP0"); - op2 = (source[i]>>21) & 0x1f; + op2 = (src >> 21) & 0x1f; if (op2 & 0x10) { - op3 = source[i] & 0x1f; + op3 = src & 0x1f; switch (op3) { case 0x01: case 0x02: case 0x06: case 0x08: type = INTCALL; break; @@ -6719,7 +6885,7 @@ static noinline void pass1_disassemble(u_int pagelimit) u32 rd; case 0x00: set_mnemonic(i, "MFC0"); - rd = (source[i] >> 11) & 0x1F; + rd = (src >> 11) & 0x1F; if (!(0x00000417u & (1u << rd))) type = COP0; break; @@ -6730,18 +6896,18 @@ static noinline void pass1_disassemble(u_int pagelimit) } break; case 0x11: set_mnemonic(i, "COP1"); - op2=(source[i]>>21)&0x1f; + op2 = (src >> 21) & 0x1f; break; case 0x12: set_mnemonic(i, "COP2"); - op2=(source[i]>>21)&0x1f; + op2 = (src >> 21) & 0x1f; if (op2 & 0x10) { type = OTHER; - if (gte_handlers[source[i]&0x3f]!=NULL) { + if (gte_handlers[src & 0x3f] != NULL) { #ifdef DISASM - if (gte_regnames[source[i]&0x3f]!=NULL) - strcpy(insn[i],gte_regnames[source[i]&0x3f]); + if (gte_regnames[src & 0x3f] != NULL) + strcpy(insn[i], gte_regnames[src & 0x3f]); else - snprintf(insn[i], sizeof(insn[i]), "COP2 %x", source[i]&0x3f); + snprintf(insn[i], sizeof(insn[i]), "COP2 %x", src & 0x3f); #endif type = C2OP; } @@ -6755,24 +6921,24 @@ static noinline void pass1_disassemble(u_int pagelimit) } break; case 0x13: set_mnemonic(i, "COP3"); - op2=(source[i]>>21)&0x1f; + op2 = (src >> 21) & 0x1f; break; - case 0x20: set_mnemonic(i, "LB"); type=LOAD; break; - case 0x21: set_mnemonic(i, "LH"); type=LOAD; break; - case 0x22: set_mnemonic(i, "LWL"); type=LOADLR; break; - case 0x23: set_mnemonic(i, "LW"); type=LOAD; break; - case 0x24: set_mnemonic(i, "LBU"); type=LOAD; break; - case 0x25: set_mnemonic(i, "LHU"); type=LOAD; break; - case 0x26: set_mnemonic(i, "LWR"); type=LOADLR; break; - case 0x28: set_mnemonic(i, "SB"); type=STORE; break; - case 0x29: set_mnemonic(i, "SH"); type=STORE; break; - case 0x2A: set_mnemonic(i, "SWL"); type=STORELR; break; - case 0x2B: set_mnemonic(i, "SW"); type=STORE; break; - case 0x2E: set_mnemonic(i, "SWR"); type=STORELR; break; - case 0x32: set_mnemonic(i, "LWC2"); type=C2LS; break; - case 0x3A: set_mnemonic(i, "SWC2"); type=C2LS; break; + case 0x20: set_mnemonic(i, "LB"); type=LOAD; ls_type = LS_8; break; + case 0x21: set_mnemonic(i, "LH"); type=LOAD; ls_type = LS_16; break; + case 0x22: set_mnemonic(i, "LWL"); type=LOADLR; ls_type = LS_LR; break; + case 0x23: set_mnemonic(i, "LW"); type=LOAD; ls_type = LS_32; break; + case 0x24: set_mnemonic(i, "LBU"); type=LOAD; ls_type = LS_8; break; + case 0x25: set_mnemonic(i, "LHU"); type=LOAD; ls_type = LS_16; break; + case 0x26: set_mnemonic(i, "LWR"); type=LOADLR; ls_type = LS_LR; break; + case 0x28: set_mnemonic(i, "SB"); type=STORE; ls_type = LS_8; break; + case 0x29: set_mnemonic(i, "SH"); type=STORE; ls_type = LS_16; break; + case 0x2A: set_mnemonic(i, "SWL"); type=STORELR; ls_type = LS_LR; break; + case 0x2B: set_mnemonic(i, "SW"); type=STORE; ls_type = LS_32; break; + case 0x2E: set_mnemonic(i, "SWR"); type=STORELR; ls_type = LS_LR; break; + case 0x32: set_mnemonic(i, "LWC2"); type=C2LS; ls_type = LS_32; break; + case 0x3A: set_mnemonic(i, "SWC2"); type=C2LS; ls_type = LS_32; break; case 0x3B: - if (Config.HLE && (source[i] & 0x03ffffff) < ARRAY_SIZE(psxHLEt)) { + if (Config.HLE && (src & 0x03ffffff) < ARRAY_SIZE(psxHLEt)) { set_mnemonic(i, "HLECALL"); type = HLECALL; } @@ -6781,9 +6947,10 @@ static noinline void pass1_disassemble(u_int pagelimit) break; } if (type == INTCALL) - SysPrintf("NI %08x @%08x (%08x)\n", source[i], start + i*4, start); - dops[i].itype=type; - dops[i].opcode2=op2; + SysPrintf_lim("NI %08x @%08x (%08x)\n", src, start + i*4, start); + dops[i].itype = type; + dops[i].opcode2 = op2; + dops[i].ls_type = ls_type; /* Get registers/immediates */ dops[i].use_lt1=0; gte_rs[i]=gte_rt[i]=0; @@ -6793,33 +6960,33 @@ static noinline void pass1_disassemble(u_int pagelimit) dops[i].rt2 = 0; switch(type) { case LOAD: - dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rt1=(source[i]>>16)&0x1f; - cinfo[i].imm=(short)source[i]; + dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rt1 = (src >> 16) & 0x1f; + cinfo[i].imm = (short)src; break; case STORE: case STORELR: - dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rs2=(source[i]>>16)&0x1f; - cinfo[i].imm=(short)source[i]; + dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rs2 = (src >> 16) & 0x1f; + cinfo[i].imm = (short)src; break; case LOADLR: // LWL/LWR only load part of the register, // therefore the target register must be treated as a source too - dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rs2=(source[i]>>16)&0x1f; - dops[i].rt1=(source[i]>>16)&0x1f; - cinfo[i].imm=(short)source[i]; + dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rs2 = (src >> 16) & 0x1f; + dops[i].rt1 = (src >> 16) & 0x1f; + cinfo[i].imm = (short)src; break; case IMM16: if (op==0x0f) dops[i].rs1=0; // LUI instruction has no source register - else dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rs2=0; - dops[i].rt1=(source[i]>>16)&0x1f; + else dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rs2 = 0; + dops[i].rt1 = (src >> 16) & 0x1f; if(op>=0x0c&&op<=0x0e) { // ANDI/ORI/XORI - cinfo[i].imm=(unsigned short)source[i]; + cinfo[i].imm = (unsigned short)src; }else{ - cinfo[i].imm=(short)source[i]; + cinfo[i].imm = (short)src; } break; case UJUMP: @@ -6830,36 +6997,36 @@ static noinline void pass1_disassemble(u_int pagelimit) dops[i].rs2=CCREG; break; case RJUMP: - dops[i].rs1=(source[i]>>21)&0x1f; + dops[i].rs1 = (src >> 21) & 0x1f; // The JALR instruction writes to rd. if (op2&1) { - dops[i].rt1=(source[i]>>11)&0x1f; + dops[i].rt1 = (src >> 11) & 0x1f; } dops[i].rs2=CCREG; break; case CJUMP: - dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rs2=(source[i]>>16)&0x1f; + dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rs2 = (src >> 16) & 0x1f; if(op&2) { // BGTZ/BLEZ dops[i].rs2=0; } break; case SJUMP: - dops[i].rs1=(source[i]>>21)&0x1f; - dops[i].rs2=CCREG; + dops[i].rs1 = (src >> 21) & 0x1f; + dops[i].rs2 = CCREG; if (op2 == 0x10 || op2 == 0x11) { // BxxAL dops[i].rt1 = 31; // NOTE: If the branch is not taken, r31 is still overwritten } break; case ALU: - dops[i].rs1=(source[i]>>21)&0x1f; // source - dops[i].rs2=(source[i]>>16)&0x1f; // subtract amount - dops[i].rt1=(source[i]>>11)&0x1f; // destination + dops[i].rs1=(src>>21)&0x1f; // source + dops[i].rs2=(src>>16)&0x1f; // subtract amount + dops[i].rt1=(src>>11)&0x1f; // destination break; case MULTDIV: - dops[i].rs1=(source[i]>>21)&0x1f; // source - dops[i].rs2=(source[i]>>16)&0x1f; // divisor + dops[i].rs1=(src>>21)&0x1f; // source + dops[i].rs2=(src>>16)&0x1f; // divisor dops[i].rt1=HIREG; dops[i].rt2=LOREG; break; @@ -6868,30 +7035,29 @@ static noinline void pass1_disassemble(u_int pagelimit) if(op2==0x11) dops[i].rt1=HIREG; // MTHI if(op2==0x12) dops[i].rs1=LOREG; // MFLO if(op2==0x13) dops[i].rt1=LOREG; // MTLO - if((op2&0x1d)==0x10) dops[i].rt1=(source[i]>>11)&0x1f; // MFxx - if((op2&0x1d)==0x11) dops[i].rs1=(source[i]>>21)&0x1f; // MTxx + if((op2&0x1d)==0x10) dops[i].rt1=(src>>11)&0x1f; // MFxx + if((op2&0x1d)==0x11) dops[i].rs1=(src>>21)&0x1f; // MTxx break; case SHIFT: - dops[i].rs1=(source[i]>>16)&0x1f; // target of shift - dops[i].rs2=(source[i]>>21)&0x1f; // shift amount - dops[i].rt1=(source[i]>>11)&0x1f; // destination + dops[i].rs1=(src>>16)&0x1f; // target of shift + dops[i].rs2=(src>>21)&0x1f; // shift amount + dops[i].rt1=(src>>11)&0x1f; // destination break; case SHIFTIMM: - dops[i].rs1=(source[i]>>16)&0x1f; + dops[i].rs1=(src>>16)&0x1f; dops[i].rs2=0; - dops[i].rt1=(source[i]>>11)&0x1f; - cinfo[i].imm=(source[i]>>6)&0x1f; + dops[i].rt1=(src>>11)&0x1f; + cinfo[i].imm=(src>>6)&0x1f; break; case COP0: - if(op2==0) dops[i].rt1=(source[i]>>16)&0x1F; // MFC0 - if(op2==4) dops[i].rs1=(source[i]>>16)&0x1F; // MTC0 - if(op2==4&&((source[i]>>11)&0x1f)==12) dops[i].rt2=CSREG; // Status + if(op2==0) dops[i].rt1=(src>>16)&0x1F; // MFC0 + if(op2==4) dops[i].rs1=(src>>16)&0x1F; // MTC0 + if(op2==4&&((src>>11)&0x1e)==12) dops[i].rs2=CCREG; break; case COP2: - if(op2<3) dops[i].rt1=(source[i]>>16)&0x1F; // MFC2/CFC2 - if(op2>3) dops[i].rs1=(source[i]>>16)&0x1F; // MTC2/CTC2 - dops[i].rs2=CSREG; - int gr=(source[i]>>11)&0x1F; + if(op2<3) dops[i].rt1=(src>>16)&0x1F; // MFC2/CFC2 + if(op2>3) dops[i].rs1=(src>>16)&0x1F; // MTC2/CTC2 + int gr=(src>>11)&0x1F; switch(op2) { case 0x00: gte_rs[i]=1ll<>21)&0x1F; - cinfo[i].imm=(short)source[i]; - if(op==0x32) gte_rt[i]=1ll<<((source[i]>>16)&0x1F); // LWC2 - else gte_rs[i]=1ll<<((source[i]>>16)&0x1F); // SWC2 + dops[i].rs1=(src>>21)&0x1F; + cinfo[i].imm=(short)src; + if(op==0x32) gte_rt[i]=1ll<<((src>>16)&0x1F); // LWC2 + else gte_rs[i]=1ll<<((src>>16)&0x1F); // SWC2 break; case C2OP: - gte_rs[i]=gte_reg_reads[source[i]&0x3f]; - gte_rt[i]=gte_reg_writes[source[i]&0x3f]; + gte_rs[i]=gte_reg_reads[src&0x3f]; + gte_rt[i]=gte_reg_writes[src&0x3f]; gte_rt[i]|=1ll<<63; // every op changes flags - if((source[i]&0x3f)==GTE_MVMVA) { - int v = (source[i] >> 15) & 3; + if((src&0x3f)==GTE_MVMVA) { + int v = (src >> 15) & 3; gte_rs[i]&=~0xe3fll; if(v==3) gte_rs[i]|=0xe00ll; else gte_rs[i]|=3ll<<(v*2); @@ -6925,6 +7091,24 @@ static noinline void pass1_disassemble(u_int pagelimit) default: break; } +} + +static noinline void pass1a_disassemble(u_int pagelimit) +{ + int i, j, done = 0; + int ds_next = 0; + + for (i = 0; !done; i++) + { + int force_j_to_interpreter = 0; + unsigned int type, op, op2; + + disassemble_one(i, source[i]); + dops[i].is_ds = ds_next; ds_next = 0; + type = dops[i].itype; + op = dops[i].opcode; + op2 = dops[i].opcode2; + /* Calculate branch target addresses */ if(type==UJUMP) cinfo[i].ba=((start+i*4+4)&0xF0000000)|(((unsigned int)source[i]<<6)>>4); @@ -6953,6 +7137,7 @@ static noinline void pass1_disassemble(u_int pagelimit) dops[i].is_store = type == STORE || type == STORELR || op == 0x3a; // SWC2 dops[i].is_exception = type == SYSCALL || type == HLECALL || type == INTCALL; dops[i].may_except = dops[i].is_exception || (type == ALU && (op2 == 0x20 || op2 == 0x22)) || op == 8; + ds_next = dops[i].is_jump; if (((op & 0x37) == 0x21 || op == 0x25) // LH/SH/LHU && ((cinfo[i].imm & 1) || Config.PreciseExceptions)) @@ -6963,46 +7148,87 @@ static noinline void pass1_disassemble(u_int pagelimit) /* rare messy cases to just pass over to the interpreter */ if (i > 0 && dops[i-1].is_jump) { + j = i - 1; // branch in delay slot? if (dops[i].is_jump) { // don't handle first branch and call interpreter if it's hit - SysPrintf("branch in DS @%08x (%08x)\n", start + i*4, start); - force_prev_to_interpreter = 1; + SysPrintf_lim("branch in DS @%08x (%08x)\n", start + i*4, start); + force_j_to_interpreter = 1; } - // basic load delay detection through a branch + // load delay detection through a branch else if (dops[i].is_delay_load && dops[i].rt1 != 0) { - int t=(cinfo[i-1].ba-start)/4; - if(0 <= t && t < i &&(dops[i].rt1==dops[t].rs1||dops[i].rt1==dops[t].rs2)&&dops[t].itype!=CJUMP&&dops[t].itype!=SJUMP) { + const struct decoded_insn *dop = NULL; + int t = -1; + if (cinfo[i-1].ba != -1) { + t = (cinfo[i-1].ba - start) / 4; + if (t < 0 || t > i) { + u_int limit = 0; + u_int *mem = get_source_start(cinfo[i-1].ba, &limit); + if (mem != NULL) { + disassemble_one(MAXBLOCK - 1, mem[0]); + dop = &dops[MAXBLOCK - 1]; + } + } + else + dop = &dops[t]; + } + if ((dop && is_ld_use_hazard(&dops[i], dop)) + || (!dop && Config.PreciseExceptions)) { // jump target wants DS result - potential load delay effect - SysPrintf("load delay in DS @%08x (%08x)\n", start + i*4, start); - force_prev_to_interpreter = 1; - dops[t+1].bt=1; // expected return from interpreter + SysPrintf_lim("load delay in DS @%08x (%08x)\n", start + i*4, start); + force_j_to_interpreter = 1; + if (0 <= t && t < i) + dops[t + 1].bt = 1; // expected return from interpreter } else if(i>=2&&dops[i-2].rt1==2&&dops[i].rt1==2&&dops[i].rs1!=2&&dops[i].rs2!=2&&dops[i-1].rs1!=2&&dops[i-1].rs2!=2&& !(i>=3&&dops[i-3].is_jump)) { // v0 overwrite like this is a sign of trouble, bail out - SysPrintf("v0 overwrite @%08x (%08x)\n", start + i*4, start); - force_prev_to_interpreter = 1; + SysPrintf_lim("v0 overwrite @%08x (%08x)\n", start + i*4, start); + force_j_to_interpreter = 1; } } } - else if (i > 0 && dops[i-1].is_delay_load && dops[i-1].rt1 != 0 - && (dops[i].rs1 == dops[i-1].rt1 || dops[i].rs2 == dops[i-1].rt1)) { - SysPrintf("load delay @%08x (%08x)\n", start + i*4, start); - force_prev_to_interpreter = 1; + else if (i > 0 && dops[i-1].is_delay_load + && is_ld_use_hazard(&dops[i-1], &dops[i]) + && (i < 2 || !dops[i-2].is_ujump)) { + SysPrintf_lim("load delay @%08x (%08x)\n", start + i*4, start); + for (j = i - 1; j > 0 && dops[j-1].is_delay_load; j--) + if (dops[j-1].rt1 != dops[i-1].rt1) + break; + force_j_to_interpreter = 1; } - if (force_prev_to_interpreter) { - memset(&dops[i-1], 0, sizeof(dops[i-1])); - dops[i-1].itype = INTCALL; - dops[i-1].rs1 = CCREG; - cinfo[i-1].ba = -1; + if (force_j_to_interpreter) { + force_intcall(j); done = 2; - i--; // don't compile the DS/problematic load/etc + i = j; // don't compile the problematic branch/load/etc } + if (dops[i].is_exception && i > 0 && dops[i-1].is_jump) { + SysPrintf_lim("exception in DS @%08x (%08x)\n", start + i*4, start); + i--; + force_intcall(i); + done = 2; + } + if (i >= 2) { + if ((source[i-2] & 0xffe0f800) == 0x40806000 // MTC0 $12 + || (dops[i-2].is_jump && dops[i-2].rt1 == 31)) // call + dops[i].bt = 1; + } + if (i >= 1 && (source[i-1] & 0xffe0f800) == 0x40806800) // MTC0 $13 + dops[i].bt = 1; /* Is this the end of the block? */ if (i > 0 && dops[i-1].is_ujump) { - if (dops[i-1].rt1 == 0) { // not jal + // Don't recompile stuff that's already compiled + if (check_addr(start + i*4+4)) { + done = 1; + continue; + } + // Don't get too close to the limit + if (i > MAXBLOCK - 64) + done = 2; + if (dops[i-1].opcode2 == 0x08 || dops[i-1].rs1 == 31) // JR; JALR x, lr + done = 2; + else if (dops[i-1].itype != RJUMP && dops[i-1].rt1 == 0) { // not JAL(R) int found_bbranch = 0, t = (cinfo[i-1].ba - start) / 4; if ((u_int)(t - i) < 64 && start + (t+64)*4 < pagelimit) { // scan for a branch back to i+1 @@ -7022,39 +7248,37 @@ static noinline void pass1_disassemble(u_int pagelimit) done = 2; } else { - if(stop_after_jal) done=1; - // Stop on BREAK - if((source[i+1]&0xfc00003f)==0x0d) done=1; + // jal(r) - continue or perf may suffer for platforms without + // runtime block linking (like in crash3) + if (stop_after_jal) + done = 2; } - // Don't recompile stuff that's already compiled - if(check_addr(start+i*4+4)) done=1; - // Don't get too close to the limit - if(i>MAXBLOCK/2) done=1; } if (dops[i].itype == HLECALL) - stop = 1; - else if (dops[i].itype == INTCALL) - stop = 2; + done = 1; + else if (dops[i].itype == INTCALL) { + ni_count++; + done = 2; + } else if (dops[i].is_exception) - done = stop_after_jal ? 1 : 2; + done = 2; if (done == 2) { // Does the block continue due to a branch? - for(j=i-1;j>=0;j--) - { - if(cinfo[j].ba==start+i*4) done=j=0; // Branch into delay slot - if(cinfo[j].ba==start+i*4+4) done=j=0; - if(cinfo[j].ba==start+i*4+8) done=j=0; + for (j = i-1; j >= 0; j--) { + if (cinfo[j].ba == start+i*4) done=j=0; // Branch into delay slot + if (cinfo[j].ba == start+i*4+4) done=j=0; + if (cinfo[j].ba == start+i*4+8) done=j=0; } } //assert(i 8 || dops[i].opcode == 0x11)) { - done=stop_after_jal=1; - SysPrintf("Disabled speculative precompilation\n"); - } + if (i == MAXBLOCK - 2) + done = 1; + } + if (ni_count > 32 && !stop_after_jal) { + stop_after_jal = 1; + SysPrintf("Disabled speculative precompilation\n"); } while (i > 0 && dops[i-1].is_jump) i--; @@ -7063,8 +7287,17 @@ static noinline void pass1_disassemble(u_int pagelimit) slen = i; } +static noinline void pass1b_bt(void) +{ + int i; + for (i = 0; i < slen; i++) + if (dops[i].is_jump && start <= cinfo[i].ba && cinfo[i].ba < start+slen*4) + // Internal branch, flag target + dops[(cinfo[i].ba - start) >> 2].bt = 1; +} + // Basic liveness analysis for MIPS registers -static noinline void pass2_unneeded_regs(int istart,int iend,int r) +static noinline void pass2b_unneeded_regs(int istart, int iend, int r) { int i; uint64_t u,gte_u,b,gte_b; @@ -7086,9 +7319,6 @@ static noinline void pass2_unneeded_regs(int istart,int iend,int r) //printf("unneeded registers i=%d (%d,%d) r=%d\n",i,istart,iend,r); if(dops[i].is_jump) { - // If subroutine call, flag return address as a possible branch target - if(dops[i].rt1==31 && i=(start+slen*4)) { // Branch out of this block, flush all regs @@ -7104,8 +7334,6 @@ static noinline void pass2_unneeded_regs(int istart,int iend,int r) } else { - // Internal branch, flag target - dops[(cinfo[i].ba-start)>>2].bt=1; if(cinfo[i].ba<=start+i*4) { // Backward branch if(dops[i].is_ujump) @@ -7134,7 +7362,7 @@ static noinline void pass2_unneeded_regs(int istart,int iend,int r) // Only go three levels deep. This recursion can take an // excessive amount of time if there are a lot of nested loops. if(r<2) { - pass2_unneeded_regs((cinfo[i].ba-start)>>2,i-1,r+1); + pass2b_unneeded_regs((cinfo[i].ba-start)>>2, i-1, r+1); }else{ unneeded_reg[(cinfo[i].ba-start)>>2]=1; gte_unneeded[(cinfo[i].ba-start)>>2]=gte_u_unknown; @@ -7174,15 +7402,6 @@ static noinline void pass2_unneeded_regs(int istart,int iend,int r) } } } - else if(dops[i].may_except) - { - // SYSCALL instruction, etc or conditional exception - u=1; - } - else if (dops[i].itype == RFE) - { - u=1; - } //u=1; // DEBUG // Written registers are unneeded u|=1LL< 0 && dops[i].is_load && dops[i].rs1 == 29 && dops[i].ls_type == LS_32 + && dops[i-1].is_store && dops[i-1].rs1 == 29 && dops[i-1].ls_type == LS_32 + && dops[i-1].rs2 == dops[i].rt1 && !dops[i-1].is_ds && i < slen - 1 + && dops[i+1].rs1 != dops[i].rt1 && dops[i+1].rs2 != dops[i].rt1 + && !dops[i].bt && cinfo[i].imm == cinfo[i-1].imm) + { + cinfo[i].imm = 0; + memset(&dops[i], 0, sizeof(dops[i])); + dops[i].itype = NOP; + } + } +} + static noinline void pass3_register_alloc(u_int addr) { struct regstat current; // Current register allocations/status @@ -7228,6 +7488,7 @@ static noinline void pass3_register_alloc(u_int addr) current.wasconst = 0; current.isconst = 0; current.loadedconst = 0; + current.noevict = 0; //current.waswritten = 0; int ds=0; int cc=0; @@ -7240,7 +7501,6 @@ static noinline void pass3_register_alloc(u_int addr) dops[1].bt=1; ds=1; unneeded_reg[0]=1; - current.regmap[HOST_BTREG]=BTREG; } for(i=0;i0) if(regmap_pre[i+1][hr]!=regs[i].regmap[hr]) { - SysPrintf("fail: %x (%d %d!=%d)\n",start+i*4,hr,regmap_pre[i+1][hr],regs[i].regmap[hr]); + SysPrintf_lim("fail: %x (%d %d!=%d)\n", + start+i*4, hr, regmap_pre[i+1][hr], regs[i].regmap[hr]); assert(regmap_pre[i+1][hr]==regs[i].regmap[hr]); } regmap_pre[i+1][hr]=-1; @@ -8346,8 +8575,8 @@ static noinline void pass5a_preallocate1(void) // to use, which can avoid a load-use penalty on certain CPUs. static noinline void pass5b_preallocate2(void) { - int i, hr; - for(i=0;i=0); + #if 0 // what is this for? double allocs $0 in ps1_rom.bin if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0) { regs[i].regmap[hr]=dops[i+1].rs1; @@ -8460,6 +8690,7 @@ static noinline void pass5b_preallocate2(void) regs[i+1].wasdirty&=~(1< %p\n", addr, out); + assem_debug("NOTCOMPILED: addr = %x -> %p\n", addr, log_addr(out)); if (addr & 3) { if (addr != hack_addr) { - SysPrintf("game crash @%08x, ra=%08x\n", addr, psxRegs.GPR.n.ra); + SysPrintf_lim("game crash @%08x, ra=%08x\n", addr, psxRegs.GPR.n.ra); hack_addr = addr; } return -1; @@ -8965,14 +9194,13 @@ static int new_recompile_block(u_int addr) } start = addr; - new_dynarec_did_compile=1; + ndrc_g.did_compile++; if (Config.HLE && start == 0x80001000) // hlecall { - // XXX: is this enough? Maybe check hleSoftCall? void *beginning = start_block(); emit_movimm(start,0); - emit_writeword(0,&pcaddr); + emit_writeword(0,&psxRegs.pc); emit_far_jump(new_dyna_leave); literal_pool(0); end_block(beginning); @@ -8992,6 +9220,7 @@ static int new_recompile_block(u_int addr) emit_addimm(0, 0x18, 0); emit_adds_ptr(1, 1, 1); emit_ldr_dualindexed(1, 0, 0); + emit_readptr(&hash_table_ptr, 1); emit_writeword(0, &psxRegs.GPR.r[26]); // lw k0, 0x18(sp) emit_far_call(ndrc_get_addr_ht); emit_jmpreg(0); // jr k0 @@ -9005,13 +9234,13 @@ static int new_recompile_block(u_int addr) return 0; } - cycle_multiplier_active = Config.cycle_multiplier_override && Config.cycle_multiplier == CYCLE_MULT_DEFAULT - ? Config.cycle_multiplier_override : Config.cycle_multiplier; + cycle_multiplier_active = get_cycle_multiplier(); source = get_source_start(start, &pagelimit); if (source == NULL) { if (addr != hack_addr) { - SysPrintf("Compile at bogus memory address: %08x\n", addr); + SysPrintf_lim("Compile at bogus memory address: %08x, ra=%x\n", + addr, psxRegs.GPR.n.ra); hack_addr = addr; } //abort(); @@ -9031,13 +9260,15 @@ static int new_recompile_block(u_int addr) /* Pass 1 disassembly */ - pass1_disassemble(pagelimit); + pass1a_disassemble(pagelimit); + pass1b_bt(); int clear_hack_addr = apply_hacks(); - /* Pass 2 - Register dependencies and branch targets */ + /* Pass 2 - unneeded, register dependencies */ - pass2_unneeded_regs(0,slen-1,0); + pass2a_unneeded(); + pass2b_unneeded_regs(0, slen-1, 0); /* Pass 3 - Register allocation */ @@ -9055,7 +9286,7 @@ static int new_recompile_block(u_int addr) /* Pass 6 - Optimize clean/dirty state */ pass6_clean_registers(0, slen-1, 1); - /* Pass 7 - Identify 32-bit registers */ + /* Pass 7 */ for (i=slen-1;i>=0;i--) { if(dops[i].itype==CJUMP||dops[i].itype==SJUMP) @@ -9077,17 +9308,22 @@ static int new_recompile_block(u_int addr) void *instr_addr0_override = NULL; int ds = 0; - if (start == 0x80030000) { - // nasty hack for the fastbios thing - // override block entry to this code + if ((Config.HLE && start == 0x80000080) || start == 0x80030000) { instr_addr0_override = out; - emit_movimm(start,0); - // abuse io address var as a flag that we - // have already returned here once - emit_readword(&address,1); - emit_writeword(0,&pcaddr); - emit_writeword(0,&address); - emit_cmp(0,1); + emit_movimm(start, 0); + if (start == 0x80030000) { + // for BiosBootBypass() to work + // io address var abused as a "already been here" flag + emit_readword(&address, 1); + emit_writeword(0, &psxRegs.pc); + emit_writeword(0, &address); + emit_cmp(0, 1); + } + else { + emit_readword(&psxRegs.cpuInRecursion, 1); + emit_writeword(0, &psxRegs.pc); + emit_test(1, 1); + } #ifdef __aarch64__ emit_jeq(out + 4*2); emit_far_jump(new_dyna_leave); @@ -9174,6 +9410,7 @@ static int new_recompile_block(u_int addr) ds = assemble(i, ®s[i], cinfo[i].ccadj); + drc_dbg_emit_wb_dirtys(i, ®s[i]); if (dops[i].is_ujump) literal_pool(1024); else @@ -9265,7 +9502,8 @@ static int new_recompile_block(u_int addr) /* Pass 9 - Linker */ for(i=0;i %8x\n",link_addr[i].addr,link_addr[i].target); + assem_debug("link: %p -> %08x\n", + log_addr(link_addr[i].addr), link_addr[i].target); literal_pool(64); if (!link_addr[i].internal) { @@ -9320,7 +9558,7 @@ static int new_recompile_block(u_int addr) { if ((i == 0 || dops[i].bt) && instr_addr[i]) { - assem_debug("%p (%d) <- %8x\n", instr_addr[i], i, start + i*4); + assem_debug("%p (%d) <- %8x\n", log_addr(instr_addr[i]), i, start + i*4); u_int vaddr = start + i*4; literal_pool(256);