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=e0cff62ca622b1b5b04dd942626a76e89d4ab5f0;hp=081532981307c9078f58c38d45c090970582a868;hb=e3c6bdb5e46f72f063bb7f588da6588ac1893b17;hpb=d1e4ebd9988a9a5d9fb38b89f19e24b9ab6029d7 diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 08153298..e0cff62c 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -35,13 +35,18 @@ static int sceBlock; #endif #include "new_dynarec_config.h" -#include "../psxhle.h" //emulator interface -#include "emu_if.h" //emulator interface +#include "../psxhle.h" +#include "../psxinterpreter.h" +#include "../gte.h" +#include "emu_if.h" // emulator interface #define noinline __attribute__((noinline,noclone)) #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif +#ifndef min +#define min(a, b) ((b) < (a) ? (b) : (a)) +#endif //#define DISASM //#define assem_debug printf @@ -62,9 +67,27 @@ static int sceBlock; #include "assem_arm64.h" #endif +#define RAM_SIZE 0x200000 #define MAXBLOCK 4096 #define MAX_OUTPUT_BLOCK_SIZE 262144 +struct ndrc_mem +{ + u_char translation_cache[1 << TARGET_SIZE_2]; + struct + { + struct tramp_insns ops[2048 / sizeof(struct tramp_insns)]; + const void *f[2048 / sizeof(void *)]; + } 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_; +#endif + // stubs enum stub_type { CC_STUB = 1, @@ -166,9 +189,11 @@ struct link_entry static char ooo[MAXBLOCK]; static uint64_t unneeded_reg[MAXBLOCK]; static uint64_t branch_unneeded_reg[MAXBLOCK]; - static signed char regmap_pre[MAXBLOCK][HOST_REGS]; - static uint64_t current_constmap[HOST_REGS]; - static uint64_t constmap[MAXBLOCK][HOST_REGS]; + static signed char regmap_pre[MAXBLOCK][HOST_REGS]; // pre-instruction i? + // contains 'real' consts at [i] insn, but may differ from what's actually + // loaded in host reg as 'final' value is always loaded, see get_final_value() + static uint32_t current_constmap[HOST_REGS]; + static uint32_t constmap[MAXBLOCK][HOST_REGS]; static struct regstat regs[MAXBLOCK]; static struct regstat branch_regs[MAXBLOCK]; static signed char minimum_free_regs[MAXBLOCK]; @@ -196,8 +221,11 @@ struct link_entry #endif int new_dynarec_hacks; + int new_dynarec_hacks_pergame; 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; @@ -272,7 +300,7 @@ struct link_entry #define DJT_2 (void *)2l // asm linkage -int new_recompile_block(int addr); +int new_recompile_block(u_int addr); void *get_addr_ht(u_int vaddr); void invalidate_block(u_int block); void invalidate_addr(u_int addr); @@ -284,9 +312,8 @@ void verify_code_ds(); void cc_interrupt(); void fp_exception(); void fp_exception_ds(); -void jump_syscall_hle(); -void jump_hlecall(); -void jump_intcall(); +void jump_to_new_pc(); +void call_gteStall(); void new_dyna_leave(); // Needed by assembler @@ -297,18 +324,22 @@ static void load_all_regs(signed char i_regmap[]); static void load_needed_regs(signed char i_regmap[],signed char next_regmap[]); static void load_regs_entry(int t); static void load_all_consts(signed char regmap[],u_int dirty,int i); +static u_int get_host_reglist(const signed char *regmap); -static int verify_dirty(u_int *ptr); +static int verify_dirty(const u_int *ptr); static int get_final_value(int hr, int i, 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, - int i, int addr_reg, struct regstat *i_regs, int ccadj, u_int reglist); + int i, int addr_reg, const struct regstat *i_regs, int ccadj, u_int reglist); static void add_to_linker(void *addr, u_int target, int ext); static void *emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override); static void *get_direct_memhandler(void *table, u_int addr, enum stub_type type, uintptr_t *addr_host); +static void cop2_call_stall_check(u_int op, int i, const struct regstat *i_regs, u_int reglist); static void pass_args(int a0, int a1); +static void emit_far_jump(const void *f); +static void emit_far_call(const void *f); static void mprotect_w_x(void *start, void *end, int is_x) { @@ -337,7 +368,7 @@ static void start_tcache_write(void *start, void *end) static void end_tcache_write(void *start, void *end) { -#ifdef __arm__ +#if defined(__arm__) || defined(__aarch64__) size_t len = (char *)end - (char *)start; #if defined(__BLACKBERRY_QNX__) msync(start, len, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE); @@ -347,12 +378,14 @@ static void end_tcache_write(void *start, void *end) sceKernelSyncVMDomain(sceBlock, start, len); #elif defined(_3DS) ctr_flush_invalidate_cache(); + #elif defined(__aarch64__) + // as of 2021, __clear_cache() is still broken on arm64 + // so here is a custom one :( + clear_cache_arm64(start, end); #else __clear_cache(start, end); #endif (void)len; -#else - __clear_cache(start, end); #endif mprotect_w_x(start, end, 1); @@ -361,8 +394,8 @@ static void end_tcache_write(void *start, void *end) static void *start_block(void) { u_char *end = out + MAX_OUTPUT_BLOCK_SIZE; - if (end > translation_cache + (1< ndrc->translation_cache + sizeof(ndrc->translation_cache)) + end = ndrc->translation_cache + sizeof(ndrc->translation_cache); start_tcache_write(out, end); return out; } @@ -372,16 +405,74 @@ static void end_block(void *start) end_tcache_write(start, out); } +// also takes care of w^x mappings when patching code +static u_int needs_clear_cache[1<<(TARGET_SIZE_2-17)]; + +static void mark_clear_cache(void *target) +{ + uintptr_t offset = (u_char *)target - ndrc->translation_cache; + u_int mask = 1u << ((offset >> 12) & 31); + if (!(needs_clear_cache[offset >> 17] & mask)) { + char *start = (char *)((uintptr_t)target & ~4095l); + start_tcache_write(start, start + 4095); + needs_clear_cache[offset >> 17] |= mask; + } +} + +// Clearing the cache is rather slow on ARM Linux, so mark the areas +// that need to be cleared, and then only clear these areas once. +static void do_clear_cache(void) +{ + int i, j; + for (i = 0; i < (1<<(TARGET_SIZE_2-17)); i++) + { + u_int bitmap = needs_clear_cache[i]; + if (!bitmap) + continue; + for (j = 0; j < 32; j++) + { + u_char *start, *end; + if (!(bitmap & (1<translation_cache + i*131072 + j*4096; + end = start + 4095; + for (j++; j < 32; j++) { + if (!(bitmap & (1<>31)|1; - return (x * cycle_multiplier + s * 50) / 100; + return (x * m + s * 50) / 100; +} + +// is the op an unconditional jump? +static int is_ujump(int i) +{ + return itype[i] == UJUMP || itype[i] == RJUMP + || (source[i] >> 16) == 0x1000; // beq r0, r0, offset // b offset +} + +static int is_jump(int i) +{ + return itype[i] == RJUMP || itype[i] == UJUMP || itype[i] == CJUMP || itype[i] == SJUMP; } static u_int get_page(u_int vaddr) @@ -529,7 +620,7 @@ void dirty_reg(struct regstat *cur,signed char reg) } } -void set_const(struct regstat *cur,signed char reg,uint64_t value) +static void set_const(struct regstat *cur, signed char reg, uint32_t value) { int hr; if(!reg) return; @@ -541,7 +632,7 @@ void set_const(struct regstat *cur,signed char reg,uint64_t value) } } -void clear_const(struct regstat *cur,signed char reg) +static void clear_const(struct regstat *cur, signed char reg) { int hr; if(!reg) return; @@ -552,7 +643,7 @@ void clear_const(struct regstat *cur,signed char reg) } } -int is_const(struct regstat *cur,signed char reg) +static int is_const(struct regstat *cur, signed char reg) { int hr; if(reg<0) return 0; @@ -564,7 +655,8 @@ int is_const(struct regstat *cur,signed char reg) } return 0; } -uint64_t get_const(struct regstat *cur,signed char reg) + +static uint32_t get_const(struct regstat *cur, signed char reg) { int hr; if(!reg) return 0; @@ -590,7 +682,7 @@ void lsn(u_char hsn[], int i, int *preferred_reg) j=slen-i-1; break; } - if(itype[i+j]==UJUMP||itype[i+j]==RJUMP||(source[i+j]>>16)==0x1000) + if (is_ujump(i+j)) { // Don't go past an unconditonal jump j++; @@ -638,7 +730,7 @@ void lsn(u_char hsn[], int i, int *preferred_reg) // TODO: preferred register based on backward branch } // Delay slot should preferably not overwrite branch conditions or cycle count - if(i>0&&(itype[i-1]==RJUMP||itype[i-1]==UJUMP||itype[i-1]==CJUMP||itype[i-1]==SJUMP)) { + if (i > 0 && is_jump(i-1)) { if(rs1[i-1]) if(hsn[rs1[i-1]]>1) hsn[rs1[i-1]]=1; if(rs2[i-1]) if(hsn[rs2[i-1]]>1) hsn[rs2[i-1]]=1; hsn[CCREG]=1; @@ -673,7 +765,7 @@ int needed_again(int r, int i) int b=-1; int rn=10; - if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000)) + if (i > 0 && is_ujump(i-1)) { if(ba[i-1]start+slen*4-4) return 0; // Don't need any registers if exiting the block @@ -684,7 +776,7 @@ int needed_again(int r, int i) j=slen-i-1; break; } - if(itype[i+j]==UJUMP||itype[i+j]==RJUMP||(source[i+j]>>16)==0x1000) + if (is_ujump(i+j)) { // Don't go past an unconditonal jump j++; @@ -740,7 +832,7 @@ int loop_reg(int i, int r, int hr) j=slen-i-1; break; } - if(itype[i+j]==UJUMP||itype[i+j]==RJUMP||(source[i+j]>>16)==0x1000) + if (is_ujump(i+j)) { // Don't go past an unconditonal jump j++; @@ -832,13 +924,15 @@ static const struct { FUNCNAME(jump_handler_write16), FUNCNAME(jump_handler_write32), FUNCNAME(invalidate_addr), - FUNCNAME(verify_code), - FUNCNAME(jump_hlecall), - FUNCNAME(jump_syscall_hle), + FUNCNAME(jump_to_new_pc), + FUNCNAME(call_gteStall), FUNCNAME(new_dyna_leave), FUNCNAME(pcsx_mtc0), FUNCNAME(pcsx_mtc0_ds), FUNCNAME(do_insn_cmp), +#ifdef __arm__ + FUNCNAME(verify_code), +#endif }; static const char *func_name(const void *a) @@ -866,6 +960,48 @@ static const char *func_name(const void *a) #include "assem_arm64.c" #endif +static void *get_trampoline(const void *f) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(ndrc->tramp.f); i++) { + if (ndrc->tramp.f[i] == f || ndrc->tramp.f[i] == NULL) + break; + } + if (i == ARRAY_SIZE(ndrc->tramp.f)) { + SysPrintf("trampoline table is full, last func %p\n", f); + abort(); + } + if (ndrc->tramp.f[i] == NULL) { + start_tcache_write(&ndrc->tramp.f[i], &ndrc->tramp.f[i + 1]); + ndrc->tramp.f[i] = f; + end_tcache_write(&ndrc->tramp.f[i], &ndrc->tramp.f[i + 1]); + } + return &ndrc->tramp.ops[i]; +} + +static void emit_far_jump(const void *f) +{ + if (can_jump_or_call(f)) { + emit_jmp(f); + return; + } + + f = get_trampoline(f); + emit_jmp(f); +} + +static void emit_far_call(const void *f) +{ + if (can_jump_or_call(f)) { + emit_call(f); + return; + } + + f = get_trampoline(f); + emit_call(f); +} + // Add virtual address mapping to linked list void ll_add(struct ll_entry **head,int vaddr,void *addr) { @@ -993,9 +1129,7 @@ static void ll_kill_pointers(struct ll_entry *head,uintptr_t addr,int shift) { inv_debug("EXP: Kill pointer at %p (%x)\n",head->addr,head->vaddr); void *host_addr=find_extjump_insn(head->addr); - #if defined(__arm__) || defined(__aarch64__) - mark_clear_cache(host_addr); - #endif + mark_clear_cache(host_addr); set_jump_target(host_addr, head->addr); } head=head->next; @@ -1021,9 +1155,7 @@ static void invalidate_page(u_int page) while(head!=NULL) { inv_debug("INVALIDATE: kill pointer to %x (%p)\n",head->vaddr,head->addr); void *host_addr=find_extjump_insn(head->addr); - #if defined(__arm__) || defined(__aarch64__) - mark_clear_cache(host_addr); - #endif + mark_clear_cache(host_addr); set_jump_target(host_addr, head->addr); next=head->next; free(head); @@ -1046,9 +1178,7 @@ static void invalidate_block_range(u_int block, u_int first, u_int last) for(first=page+1;first3) // MTC1/CTC1 + else if (opcode2[i] > 3) // MTC2/CTC2 { if(rs1[i]){ clear_const(current,rs1[i]); @@ -1818,13 +1949,15 @@ static void cop12_alloc(struct regstat *current,int i) current->u&=~1LL; alloc_reg(current,i,0); } - alloc_reg_temp(current,i,-1); } + alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } void c2op_alloc(struct regstat *current,int i) { + alloc_cc(current,i); // for stalls + dirty_reg(current,CCREG); alloc_reg_temp(current,i,-1); } @@ -1881,8 +2014,9 @@ void delayslot_alloc(struct regstat *current,int i) cop0_alloc(current,i); break; case COP1: + break; case COP2: - cop12_alloc(current,i); + cop2_alloc(current,i); break; case C1LS: c1ls_alloc(current,i); @@ -1948,7 +2082,7 @@ static void add_stub(enum stub_type type, void *addr, void *retaddr, } static void add_stub_r(enum stub_type type, void *addr, void *retaddr, - int i, int addr_reg, struct regstat *i_regs, int ccadj, u_int reglist) + int i, int addr_reg, const struct regstat *i_regs, int ccadj, u_int reglist) { add_stub(type, addr, retaddr, i, addr_reg, (uintptr_t)i_regs, ccadj, reglist); } @@ -2056,10 +2190,11 @@ static void alu_assemble(int i,struct regstat *i_regs) s2l=get_reg(i_regs->regmap,rs2[i]); if(rs2[i]==0) // rx=0); - if(opcode2[i]==0x2a) // SLT + if(opcode2[i]==0x2a&&rs1[i]!=0) { // SLT + assert(s1l>=0); emit_shrimm(s1l,31,t); - else // SLTU (unsigned can not be less than zero) + } + else // SLTU (unsigned can not be less than zero, 0<0) emit_zeroreg(t); } else if(rs1[i]==0) // r0regmap, rt1[i]); + s = get_reg(i_regs->regmap, rs1[i]); + shift = get_reg(i_regs->regmap, rs2[i]); + if (t < 0) + return; + + if(rs1[i]==0) + emit_zeroreg(t); + else if(rs2[i]==0) { + assert(s>=0); + if(s!=t) emit_mov(s,t); + } + else { + host_tempreg_acquire(); + emit_andimm(shift,31,HOST_TEMPREG); + switch(opcode2[i]) { + case 4: // SLLV + emit_shl(s,HOST_TEMPREG,t); + break; + case 6: // SRLV + emit_shr(s,HOST_TEMPREG,t); + break; + case 7: // SRAV + emit_sar(s,HOST_TEMPREG,t); + break; + default: + assert(0); + } + host_tempreg_release(); + } } + #endif enum { @@ -2430,7 +2598,7 @@ static void *emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override) else if(type==MTYPE_1F80) { // scratchpad if (psxH == (void *)0x1f800000) { host_tempreg_acquire(); - emit_addimm(addr,-0x1f800000,HOST_TEMPREG); + emit_xorimm(addr,0x1f800000,HOST_TEMPREG); emit_cmpimm(HOST_TEMPREG,0x1000); host_tempreg_release(); jaddr=out; @@ -2491,20 +2659,45 @@ static void *get_direct_memhandler(void *table, u_int addr, } } -static void load_assemble(int i,struct regstat *i_regs) +static u_int get_host_reglist(const signed char *regmap) +{ + u_int reglist = 0, hr; + for (hr = 0; hr < HOST_REGS; hr++) { + if (hr != EXCLUDE_REG && regmap[hr] >= 0) + reglist |= 1 << hr; + } + return reglist; +} + +static u_int reglist_exclude(u_int reglist, int r1, int r2) +{ + if (r1 >= 0) + reglist &= ~(1u << r1); + if (r2 >= 0) + reglist &= ~(1u << r2); + return reglist; +} + +// find a temp caller-saved register not in reglist (so assumed to be free) +static int reglist_find_free(u_int reglist) +{ + u_int free_regs = ~reglist & CALLER_SAVE_REGS; + if (free_regs == 0) + return -1; + return __builtin_ctz(free_regs); +} + +static void load_assemble(int i, const struct regstat *i_regs) { int s,tl,addr; int offset; void *jaddr=0; int memtarget=0,c=0; int fastio_reg_override=-1; - u_int hr,reglist=0; + u_int reglist=get_host_reglist(i_regs->regmap); tl=get_reg(i_regs->regmap,rt1[i]); s=get_reg(i_regs->regmap,rs1[i]); offset=imm[i]; - for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<=0) { c=(i_regs->wasconst>>s)&1; @@ -2631,14 +2824,87 @@ static void load_assemble(int i,struct regstat *i_regs) } #ifndef loadlr_assemble -void loadlr_assemble(int i,struct regstat *i_regs) +static void loadlr_assemble(int i, const struct regstat *i_regs) { - printf("Need loadlr_assemble for this architecture.\n"); - abort(); + int s,tl,temp,temp2,addr; + int offset; + void *jaddr=0; + int memtarget=0,c=0; + int fastio_reg_override=-1; + u_int reglist=get_host_reglist(i_regs->regmap); + tl=get_reg(i_regs->regmap,rt1[i]); + s=get_reg(i_regs->regmap,rs1[i]); + temp=get_reg(i_regs->regmap,-1); + temp2=get_reg(i_regs->regmap,FTEMP); + addr=get_reg(i_regs->regmap,AGEN1+(i&1)); + assert(addr<0); + offset=imm[i]; + reglist|=1<=0) { + c=(i_regs->wasconst>>s)&1; + if(c) { + memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE; + } + } + if(!c) { + emit_shlimm(addr,3,temp); + if (opcode[i]==0x22||opcode[i]==0x26) { + emit_andimm(addr,0xFFFFFFFC,temp2); // LWL/LWR + }else{ + emit_andimm(addr,0xFFFFFFF8,temp2); // LDL/LDR + } + jaddr=emit_fastpath_cmp_jump(i,temp2,&fastio_reg_override); + } + else { + if(ram_offset&&memtarget) { + host_tempreg_acquire(); + emit_addimm(temp2,ram_offset,HOST_TEMPREG); + fastio_reg_override=HOST_TEMPREG; + } + if (opcode[i]==0x22||opcode[i]==0x26) { + emit_movimm(((constmap[i][s]+offset)<<3)&24,temp); // LWL/LWR + }else{ + emit_movimm(((constmap[i][s]+offset)<<3)&56,temp); // LDL/LDR + } + } + if (opcode[i]==0x22||opcode[i]==0x26) { // LWL/LWR + if(!c||memtarget) { + int a=temp2; + if(fastio_reg_override>=0) a=fastio_reg_override; + emit_readword_indexed(0,a,temp2); + if(fastio_reg_override==HOST_TEMPREG) host_tempreg_release(); + if(jaddr) add_stub_r(LOADW_STUB,jaddr,out,i,temp2,i_regs,ccadj[i],reglist); + } + else + inline_readstub(LOADW_STUB,i,(constmap[i][s]+offset)&0xFFFFFFFC,i_regs->regmap,FTEMP,ccadj[i],reglist); + if(rt1[i]) { + assert(tl>=0); + emit_andimm(temp,24,temp); + if (opcode[i]==0x22) // LWL + emit_xorimm(temp,24,temp); + host_tempreg_acquire(); + emit_movimm(-1,HOST_TEMPREG); + if (opcode[i]==0x26) { + emit_shr(temp2,temp,temp2); + emit_bic_lsr(tl,HOST_TEMPREG,temp,tl); + }else{ + emit_shl(temp2,temp,temp2); + emit_bic_lsl(tl,HOST_TEMPREG,temp,tl); + } + host_tempreg_release(); + emit_or(temp2,tl,tl); + } + //emit_storereg(rt1[i],tl); // DEBUG + } + if (opcode[i]==0x1A||opcode[i]==0x1B) { // LDL/LDR + assert(0); + } } #endif -void store_assemble(int i,struct regstat *i_regs) +void store_assemble(int i, const struct regstat *i_regs) { int s,tl; int addr,temp; @@ -2648,7 +2914,7 @@ void store_assemble(int i,struct regstat *i_regs) int memtarget=0,c=0; int agr=AGEN1+(i&1); int fastio_reg_override=-1; - u_int hr,reglist=0; + u_int reglist=get_host_reglist(i_regs->regmap); tl=get_reg(i_regs->regmap,rs2[i]); s=get_reg(i_regs->regmap,rs1[i]); temp=get_reg(i_regs->regmap,agr); @@ -2662,9 +2928,6 @@ void store_assemble(int i,struct regstat *i_regs) } assert(tl>=0); assert(temp>=0); - for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<waswritten&(1<waswritten&(1<attract mode) if(c&&start+i*4regmap==regs[i].regmap); // not delay slot @@ -2756,13 +3019,13 @@ void store_assemble(int i,struct regstat *i_regs) emit_movimm(start+i*4+4,0); emit_writeword(0,&pcaddr); emit_addimm(HOST_CCREG,2,HOST_CCREG); - emit_call(get_addr_ht); + emit_far_call(get_addr_ht); emit_jmpreg(0); } } } -void storelr_assemble(int i,struct regstat *i_regs) +static void storelr_assemble(int i, const struct regstat *i_regs) { int s,tl; int temp; @@ -2772,7 +3035,7 @@ void storelr_assemble(int i,struct regstat *i_regs) void *done0, *done1, *done2; int memtarget=0,c=0; int agr=AGEN1+(i&1); - u_int hr,reglist=0; + u_int reglist=get_host_reglist(i_regs->regmap); tl=get_reg(i_regs->regmap,rs2[i]); s=get_reg(i_regs->regmap,rs1[i]); temp=get_reg(i_regs->regmap,agr); @@ -2785,9 +3048,6 @@ void storelr_assemble(int i,struct regstat *i_regs) } } assert(tl>=0); - for(hr=0;hrregmap[hr]>=0) reglist|=1<=0); if(!c) { emit_cmpimm(s<0||offset?temp:s,RAM_SIZE); @@ -2802,7 +3062,8 @@ void storelr_assemble(int i,struct regstat *i_regs) emit_jmp(0); } } - emit_addimm_no_flags(ram_offset,temp); + if(ram_offset) + emit_addimm_no_flags(ram_offset,temp); if (opcode[i]==0x2C||opcode[i]==0x2D) { // SDL/SDR assert(0); @@ -2819,15 +3080,11 @@ void storelr_assemble(int i,struct regstat *i_regs) if (opcode[i]==0x2A) { // SWL emit_writeword_indexed(tl,0,temp); } - if (opcode[i]==0x2E) { // SWR + else if (opcode[i]==0x2E) { // SWR emit_writebyte_indexed(tl,3,temp); } - if (opcode[i]==0x2C) { // SDL - assert(0); - } - if (opcode[i]==0x2D) { // SDR + else assert(0); - } done0=out; emit_jmp(0); // 1 @@ -2840,16 +3097,10 @@ void storelr_assemble(int i,struct regstat *i_regs) emit_writebyte_indexed(tl,1,temp); if(rs2[i]) emit_rorimm(tl,8,tl); } - if (opcode[i]==0x2E) { // SWR + else if (opcode[i]==0x2E) { // SWR // Write two lsb into two most significant bytes emit_writehword_indexed(tl,1,temp); } - if (opcode[i]==0x2C) { // SDL - assert(0); - } - if (opcode[i]==0x2D) { // SDR - assert(0); - } done1=out; emit_jmp(0); // 2 @@ -2863,19 +3114,13 @@ void storelr_assemble(int i,struct regstat *i_regs) emit_writehword_indexed(tl,-2,temp); if(rs2[i]) emit_rorimm(tl,16,tl); } - if (opcode[i]==0x2E) { // SWR + else if (opcode[i]==0x2E) { // SWR // Write 3 lsb into three most significant bytes emit_writebyte_indexed(tl,-1,temp); if(rs2[i]) emit_rorimm(tl,8,tl); emit_writehword_indexed(tl,0,temp); if(rs2[i]) emit_rorimm(tl,24,tl); } - if (opcode[i]==0x2C) { // SDL - assert(0); - } - if (opcode[i]==0x2D) { // SDR - assert(0); - } done2=out; emit_jmp(0); // 3 @@ -2886,28 +3131,16 @@ void storelr_assemble(int i,struct regstat *i_regs) emit_writebyte_indexed(tl,-3,temp); if(rs2[i]) emit_rorimm(tl,8,tl); } - if (opcode[i]==0x2E) { // SWR + else if (opcode[i]==0x2E) { // SWR // Write entire word emit_writeword_indexed(tl,-3,temp); } - if (opcode[i]==0x2C) { // SDL - assert(0); - } - if (opcode[i]==0x2D) { // SDR - assert(0); - } set_jump_target(done0, out); set_jump_target(done1, out); set_jump_target(done2, out); - if (opcode[i]==0x2C) { // SDL - assert(0); - } - if (opcode[i]==0x2D) { // SDR - assert(0); - } if(!c||!memtarget) add_stub_r(STORELR_STUB,jaddr,out,i,temp,i_regs,ccadj[i],reglist); - if(!(i_regs->waswritten&(1<waswritten&(1<regmap,INVCP); @@ -2964,7 +3197,7 @@ static void cop0_assemble(int i,struct regstat *i_regs) emit_storereg(CCREG,HOST_CCREG); emit_loadreg(rs1[i],1); emit_movimm(copr,0); - emit_call(pcsx_mtc0_ds); + emit_far_call(pcsx_mtc0_ds); emit_loadreg(rs1[i],s); return; } @@ -2973,14 +3206,12 @@ static void cop0_assemble(int i,struct regstat *i_regs) emit_movimm(0,HOST_TEMPREG); emit_writeword(HOST_TEMPREG,&pending_exception); } - //else if(copr==12&&is_delayslot) emit_call((int)MTC0_R12); - //else if(s==HOST_CCREG) emit_loadreg(rs1[i],1); else if(s!=1) emit_mov(s,1); emit_movimm(copr,0); - emit_call(pcsx_mtc0); + emit_far_call(pcsx_mtc0); if(copr==9||copr==11||copr==12||copr==13) { emit_readword(&Count,HOST_CCREG); emit_readword(&next_interupt,HOST_TEMPREG); @@ -2997,7 +3228,7 @@ static void cop0_assemble(int i,struct regstat *i_regs) emit_jeq(0); emit_readword(&pcaddr, 0); emit_addimm(HOST_CCREG,2,HOST_CCREG); - emit_call(get_addr_ht); + emit_far_call(get_addr_ht); emit_jmpreg(0); set_jump_target(jaddr, out); } @@ -3057,7 +3288,131 @@ static void do_cop1stub(int n) if(regs[i].regmap_entry[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG); emit_movimm(start+(i-ds)*4,EAX); // Get PC emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right? There should probably be an extra cycle... - emit_jmp(ds?fp_exception_ds:fp_exception); + emit_far_jump(ds?fp_exception_ds:fp_exception); +} + +static int cop2_is_stalling_op(int i, int *cycles) +{ + if (opcode[i] == 0x3a) { // SWC2 + *cycles = 0; + return 1; + } + if (itype[i] == COP2 && (opcode2[i] == 0 || opcode2[i] == 2)) { // MFC2/CFC2 + *cycles = 0; + return 1; + } + if (itype[i] == C2OP) { + *cycles = gte_cycletab[source[i] & 0x3f]; + return 1; + } + // ... what about MTC2/CTC2/LWC2? + return 0; +} + +#if 0 +static void log_gte_stall(int stall, u_int cycle) +{ + if ((u_int)stall <= 44) + printf("x stall %2d %u\n", stall, cycle + last_count); + if (cycle + last_count > 1215348544) exit(1); +} + +static void emit_log_gte_stall(int i, int stall, u_int reglist) +{ + save_regs(reglist); + if (stall > 0) + emit_movimm(stall, 0); + else + emit_mov(HOST_TEMPREG, 0); + emit_addimm(HOST_CCREG, CLOCK_ADJUST(ccadj[i]), 1); + emit_far_call(log_gte_stall); + restore_regs(reglist); +} +#endif + +static void cop2_call_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 rtmp = reglist_find_free(reglist); + + if (HACK_ENABLED(NDHACK_GTE_NO_STALL)) + return; + //assert(get_reg(i_regs->regmap, CCREG) == HOST_CCREG); + if (get_reg(i_regs->regmap, CCREG) != HOST_CCREG) { + // happens occasionally... cc evicted? Don't bother then + //printf("no cc %08x\n", start + i*4); + return; + } + if (!bt[i]) { + for (j = i - 1; j >= 0; j--) { + //if (is_ds[j]) break; + if (cop2_is_stalling_op(j, &other_gte_op_cycles) || bt[j]) + break; + } + } + cycles_passed = CLOCK_ADJUST(ccadj[i] - ccadj[j]); + if (other_gte_op_cycles >= 0) + stall = other_gte_op_cycles - cycles_passed; + else if (cycles_passed >= 44) + stall = 0; // can't stall + if (stall == -MAXBLOCK && rtmp >= 0) { + // unknown stall, do the expensive runtime check + assem_debug("; cop2_call_stall_check\n"); +#if 0 // too slow + save_regs(reglist); + emit_movimm(gte_cycletab[op], 0); + emit_addimm(HOST_CCREG, CLOCK_ADJUST(ccadj[i]), 1); + emit_far_call(call_gteStall); + restore_regs(reglist); +#else + host_tempreg_acquire(); + emit_readword(&psxRegs.gteBusyCycle, rtmp); + emit_addimm(rtmp, -CLOCK_ADJUST(ccadj[i]), rtmp); + emit_sub(rtmp, HOST_CCREG, HOST_TEMPREG); + emit_cmpimm(HOST_TEMPREG, 44); + 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); + emit_addimm(HOST_CCREG, stall, HOST_CCREG); + } + + // save gteBusyCycle, if needed + if (gte_cycletab[op] == 0) + return; + other_gte_op_cycles = -1; + for (j = i + 1; j < slen; j++) { + if (cop2_is_stalling_op(j, &other_gte_op_cycles)) + break; + if (is_jump(j)) { + // check ds + if (j + 1 < slen && cop2_is_stalling_op(j + 1, &other_gte_op_cycles)) + j++; + break; + } + } + if (other_gte_op_cycles >= 0) + // will handle stall when assembling that op + return; + cycles_passed = CLOCK_ADJUST(ccadj[min(j, slen -1)] - ccadj[i]); + if (cycles_passed >= 44) + 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, CLOCK_ADJUST(ccadj[i]), HOST_TEMPREG); + emit_addimm(HOST_TEMPREG, gte_cycletab[op]), HOST_TEMPREG); + emit_writeword(HOST_TEMPREG, &psxRegs.gteBusyCycle); +#else + emit_addimm(HOST_CCREG, CLOCK_ADJUST(ccadj[i]) + gte_cycletab[op], HOST_TEMPREG); + emit_writeword(HOST_TEMPREG, &psxRegs.gteBusyCycle); +#endif + host_tempreg_release(); } static void cop2_get_dreg(u_int copr,signed char tl,signed char temp) @@ -3089,22 +3444,7 @@ static void cop2_get_dreg(u_int copr,signed char tl,signed char temp) break; case 28: case 29: - emit_readword(®_cop2d[9],temp); - emit_testimm(temp,0x8000); // do we need this? - emit_andimm(temp,0xf80,temp); - emit_andne_imm(temp,0,temp); - emit_shrimm(temp,7,tl); - emit_readword(®_cop2d[10],temp); - emit_testimm(temp,0x8000); - emit_andimm(temp,0xf80,temp); - emit_andne_imm(temp,0,temp); - emit_orrshr_imm(temp,2,tl); - emit_readword(®_cop2d[11],temp); - emit_testimm(temp,0x8000); - emit_andimm(temp,0xf80,temp); - emit_andne_imm(temp,0,temp); - emit_orrshl_imm(temp,3,tl); - emit_writeword(tl,®_cop2d[copr]); + c2op_mfc2_29_assemble(tl,temp); break; default: emit_readword(®_cop2d[copr],tl); @@ -3136,8 +3476,7 @@ static void cop2_put_dreg(u_int copr,signed char sl,signed char temp) emit_writeword(sl,®_cop2d[28]); break; case 30: - emit_movs(sl,temp); - emit_mvnmi(temp,temp); + emit_xorsar_imm(sl,sl,31,temp); #if defined(HAVE_ARMV5) || defined(__aarch64__) emit_clz(temp,temp); #else @@ -3159,7 +3498,7 @@ static void cop2_put_dreg(u_int copr,signed char sl,signed char temp) } } -static void c2ls_assemble(int i,struct regstat *i_regs) +static void c2ls_assemble(int i, const struct regstat *i_regs) { int s,tl; int ar; @@ -3169,7 +3508,7 @@ static void c2ls_assemble(int i,struct regstat *i_regs) enum stub_type type; int agr=AGEN1+(i&1); int fastio_reg_override=-1; - u_int hr,reglist=0; + u_int reglist=get_host_reglist(i_regs->regmap); u_int copr=(source[i]>>16)&0x1f; s=get_reg(i_regs->regmap,rs1[i]); tl=get_reg(i_regs->regmap,FTEMP); @@ -3177,9 +3516,6 @@ static void c2ls_assemble(int i,struct regstat *i_regs) assert(rs1[i]>0); assert(tl>=0); - for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<=0); if (opcode[i]==0x3a) { // SWC2 - cop2_get_dreg(copr,tl,HOST_TEMPREG); + cop2_call_stall_check(0, i, i_regs, reglist_exclude(reglist, tl, -1)); + cop2_get_dreg(copr,tl,-1); type=STOREW_STUB; } else @@ -3235,7 +3572,7 @@ static void c2ls_assemble(int i,struct regstat *i_regs) if(jaddr2) add_stub_r(type,jaddr2,out,i,ar,i_regs,ccadj[i],reglist); if(opcode[i]==0x3a) // SWC2 - if(!(i_regs->waswritten&(1<waswritten&(1<regmap,INVCP); assert(ir>=0); @@ -3258,10 +3595,18 @@ static void c2ls_assemble(int i,struct regstat *i_regs) } } -static void cop2_assemble(int i,struct regstat *i_regs) +static void cop2_assemble(int i, const struct regstat *i_regs) { - u_int copr=(source[i]>>11)&0x1f; - signed char temp=get_reg(i_regs->regmap,-1); + u_int copr = (source[i]>>11) & 0x1f; + signed char temp = get_reg(i_regs->regmap, -1); + + if (opcode2[i] == 0 || opcode2[i] == 2) { // MFC2/CFC2 + if (!HACK_ENABLED(NDHACK_GTE_NO_STALL)) { + signed char tl = get_reg(i_regs->regmap, rt1[i]); + u_int reglist = reglist_exclude(get_host_reglist(i_regs->regmap), tl, temp); + cop2_call_stall_check(0, i, i_regs, reglist); + } + } if (opcode2[i]==0) { // MFC2 signed char tl=get_reg(i_regs->regmap,rt1[i]); if(tl>=0&&rt1[i]!=0) @@ -3291,14 +3636,7 @@ static void cop2_assemble(int i,struct regstat *i_regs) emit_signextend16(sl,temp); break; case 31: - //value = value & 0x7ffff000; - //if (value & 0x7f87e000) value |= 0x80000000; - emit_shrimm(sl,12,temp); - emit_shlimm(temp,12,temp); - emit_testimm(temp,0x7f000000); - emit_testeqimm(temp,0x00870000); - emit_testeqimm(temp,0x0000e000); - emit_orrne_imm(temp,0x80000000,temp); + c2op_ctc2_31_assemble(sl,temp); break; default: temp=sl; @@ -3309,6 +3647,90 @@ static void cop2_assemble(int i,struct regstat *i_regs) } } +static void do_unalignedwritestub(int n) +{ + assem_debug("do_unalignedwritestub %x\n",start+stubs[n].a*4); + literal_pool(256); + set_jump_target(stubs[n].addr, out); + + int i=stubs[n].a; + struct regstat *i_regs=(struct regstat *)stubs[n].c; + int addr=stubs[n].b; + u_int reglist=stubs[n].e; + signed char *i_regmap=i_regs->regmap; + int temp2=get_reg(i_regmap,FTEMP); + int rt; + rt=get_reg(i_regmap,rs2[i]); + assert(rt>=0); + assert(addr>=0); + assert(opcode[i]==0x2a||opcode[i]==0x2e); // SWL/SWR only implemented + reglist|=(1<regmap,CCREG); assert(ccreg==HOST_CCREG); assert(!is_delayslot); (void)ccreg; - emit_movimm(start+i*4,EAX); // Get PC - emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right? There should probably be an extra cycle... - emit_jmp(jump_syscall_hle); // XXX + + emit_movimm(pc,3); // Get PC + emit_readword(&last_count,2); + emit_writeword(3,&psxRegs.pc); + emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // XXX + emit_add(2,HOST_CCREG,2); + emit_writeword(2,&psxRegs.cycle); + emit_far_call(func); + emit_far_jump(jump_to_new_pc); +} + +static void syscall_assemble(int i,struct regstat *i_regs) +{ + emit_movimm(0x20,0); // cause code + emit_movimm(0,1); // not in delay slot + call_c_cpu_handler(i,i_regs,start+i*4,psxException); } static void hlecall_assemble(int i,struct regstat *i_regs) { - extern void psxNULL(); - signed char ccreg=get_reg(i_regs->regmap,CCREG); - assert(ccreg==HOST_CCREG); - assert(!is_delayslot); - (void)ccreg; - emit_movimm(start+i*4+4,0); // Get PC + void *hlefunc = psxNULL; uint32_t hleCode = source[i] & 0x03ffffff; - if (hleCode >= ARRAY_SIZE(psxHLEt)) - emit_movimm((uintptr_t)psxNULL,1); - else - emit_movimm((uintptr_t)psxHLEt[hleCode],1); - emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // XXX - emit_jmp(jump_hlecall); + if (hleCode < ARRAY_SIZE(psxHLEt)) + hlefunc = psxHLEt[hleCode]; + + call_c_cpu_handler(i,i_regs,start+i*4+4,hlefunc); } static void intcall_assemble(int i,struct regstat *i_regs) { - signed char ccreg=get_reg(i_regs->regmap,CCREG); - assert(ccreg==HOST_CCREG); - assert(!is_delayslot); - (void)ccreg; - emit_movimm(start+i*4,0); // Get PC - emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); - emit_jmp(jump_intcall); + call_c_cpu_handler(i,i_regs,start+i*4,execI); } static void speculate_mov(int rs,int rt) @@ -4075,19 +4498,34 @@ static int match_bt(signed char i_regmap[],uint64_t i_dirty,int addr) static void drc_dbg_emit_do_cmp(int i) { extern void do_insn_cmp(); - extern int cycle; - u_int hr,reglist=0; + //extern int cycle; + u_int hr, reglist = get_host_reglist(regs[i].regmap); - for(hr=0;hr=0) reglist|=1< 0 && !bt[i]) { + for (hr = 0; hr < HOST_REGS; hr++) { + int reg = regs[i-1].regmap[hr]; + if (hr == EXCLUDE_REG || reg < 0) + continue; + if (!((regs[i-1].isconst >> hr) & 1)) + continue; + if (i > 1 && reg == regs[i-2].regmap[hr] && constmap[i-1][hr] == constmap[i-2][hr]) + continue; + emit_movimm(constmap[i-1][hr],0); + emit_storereg(reg, 0); + } + } emit_movimm(start+i*4,0); emit_writeword(0,&pcaddr); - emit_call(do_insn_cmp); + emit_far_call(do_insn_cmp); //emit_readword(&cycle,0); //emit_addimm(0,2,0); //emit_writeword(0,&cycle); + (void)get_reg2; restore_regs(reglist); + assem_debug("\\\\do_insn_cmp\n"); } #else #define drc_dbg_emit_do_cmp(x) @@ -4214,11 +4652,13 @@ void do_cc(int i,signed char i_regmap[],int *adj,int addr,int taken,int invert) else if(*adj==0||invert) { int cycles=CLOCK_ADJUST(count+2); // faster loop HACK +#if 0 if (t&&*adj) { int rel=t-i; if(-NO_CYCLE_PENALTY_THR(ba[i]-start)>>2) invert=1; #endif + #ifdef __aarch64__ + invert=1; // because of near cond. branches + #endif if(ooo[i]) { s1l=get_reg(branch_regs[i].regmap,rs1[i]); @@ -4956,6 +5399,9 @@ static void sjump_assemble(int i,struct regstat *i_regs) #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(i>(ba[i]-start)>>2) invert=1; #endif + #ifdef __aarch64__ + invert=1; // because of near cond. branches + #endif //if(opcode2[i]>=0x10) return; // FIXME (BxxZAL) //assert(opcode2[i]<0x10||rs1[i]==0); // FIXME (BxxZAL) @@ -5519,7 +5965,7 @@ void unneeded_registers(int istart,int iend,int r) uint64_t u,gte_u,b,gte_b; uint64_t temp_u,temp_gte_u=0; uint64_t gte_u_unknown=0; - if(new_dynarec_hacks&NDHACK_GTE_UNNEEDED) + if (HACK_ENABLED(NDHACK_GTE_UNNEEDED)) gte_u_unknown=~0ll; if(iend==slen-1) { u=1; @@ -5570,7 +6016,7 @@ void unneeded_registers(int istart,int iend,int r) bt[(ba[i]-start)>>2]=1; if(ba[i]<=start+i*4) { // Backward branch - if(itype[i]==RJUMP||itype[i]==UJUMP||(source[i]>>16)==0x1000) + if(is_ujump(i)) { // Unconditional branch temp_u=1; @@ -5615,7 +6061,7 @@ void unneeded_registers(int istart,int iend,int r) gte_unneeded[(ba[i]-start)>>2]=gte_u_unknown; } } /*else*/ if(1) { - if(itype[i]==RJUMP||itype[i]==UJUMP||(source[i]>>16)==0x1000) + if (is_ujump(i)) { // Unconditional branch u=unneeded_reg[(ba[i]-start)>>2]; @@ -5725,7 +6171,7 @@ void clean_registers(int istart,int iend,int wr) if(ba[i]=(start+slen*4)) { // Branch out of this block, flush all regs - if(itype[i]==RJUMP||itype[i]==UJUMP||(source[i]>>16)==0x1000) + if (is_ujump(i)) { // Unconditional branch will_dirty_i=0; @@ -5805,7 +6251,7 @@ void clean_registers(int istart,int iend,int wr) // Internal branch if(ba[i]<=start+i*4) { // Backward branch - if(itype[i]==RJUMP||itype[i]==UJUMP||(source[i]>>16)==0x1000) + if (is_ujump(i)) { // Unconditional branch temp_will_dirty=0; @@ -5902,7 +6348,7 @@ void clean_registers(int istart,int iend,int wr) } /*else*/ if(1) { - if(itype[i]==RJUMP||itype[i]==UJUMP||(source[i]>>16)==0x1000) + if (is_ujump(i)) { // Unconditional branch will_dirty_i=0; @@ -6069,7 +6515,7 @@ void clean_registers(int istart,int iend,int wr) regs[i].dirty&=wont_dirty_i; if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==CJUMP||itype[i]==SJUMP) { - if(i>16)!=0x1000) { + if (i < iend-1 && !is_ujump(i)) { for(r=0;rtranslation_cache; beginning = start_block(); emit_movimm(DRC_TEST_VAL + i, 0); // test emit_ret(); @@ -6261,15 +6707,15 @@ static void new_dynarec_test(void) SysPrintf("test passed.\n"); else SysPrintf("test failed, will likely crash soon (r=%08x %08x)\n", ret[0], ret[1]); - out = translation_cache; + out = ndrc->translation_cache; } // clear the state completely, instead of just marking // things invalid like invalidate_all_pages() does -void new_dynarec_clear_full() +void new_dynarec_clear_full(void) { int n; - out = translation_cache; + 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)); @@ -6287,34 +6733,28 @@ void new_dynarec_clear_full() for(n=0;n<4096;n++) ll_clear(jump_dirty+n); } -void new_dynarec_init() +void new_dynarec_init(void) { SysPrintf("Init new dynarec\n"); - // allocate/prepare a buffer for translation cache - // see assem_arm.h for some explanation -#if defined(BASE_ADDR_FIXED) - if (mmap(translation_cache, 1 << TARGET_SIZE_2, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0) != translation_cache) { - SysPrintf("mmap() failed: %s\n", strerror(errno)); - SysPrintf("disable BASE_ADDR_FIXED and recompile\n"); - abort(); - } -#elif defined(BASE_ADDR_DYNAMIC) +#ifdef BASE_ADDR_DYNAMIC #ifdef VITA sceBlock = sceKernelAllocMemBlockForVM("code", 1 << TARGET_SIZE_2); if (sceBlock < 0) SysPrintf("sceKernelAllocMemBlockForVM failed\n"); - int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&translation_cache); + int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&ndrc); if (ret < 0) SysPrintf("sceKernelGetMemBlockBase failed\n"); #else - translation_cache = mmap (NULL, 1 << TARGET_SIZE_2, + uintptr_t desired_addr = 0; + #ifdef __ELF__ + extern char _end; + desired_addr = ((uintptr_t)&_end + 0xffffff) & ~0xffffffl; + #endif + ndrc = mmap((void *)desired_addr, sizeof(*ndrc), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (translation_cache == MAP_FAILED) { + if (ndrc == MAP_FAILED) { SysPrintf("mmap() failed: %s\n", strerror(errno)); abort(); } @@ -6322,11 +6762,12 @@ void new_dynarec_init() #else #ifndef NO_WRITE_EXEC // not all systems allow execute in data segment by default - if (mprotect(translation_cache, 1<translation_cache) + sizeof(ndrc->tramp.ops), + PROT_READ | PROT_WRITE | PROT_EXEC) != 0) SysPrintf("mprotect() failed: %s\n", strerror(errno)); #endif #endif - out = translation_cache; + out = ndrc->translation_cache; cycle_multiplier=200; new_dynarec_clear_full(); #ifdef HOST_IMM8 @@ -6342,15 +6783,15 @@ void new_dynarec_init() SysPrintf("warning: RAM is not directly mapped, performance will suffer\n"); } -void new_dynarec_cleanup() +void new_dynarec_cleanup(void) { int n; -#if defined(BASE_ADDR_FIXED) || defined(BASE_ADDR_DYNAMIC) +#ifdef BASE_ADDR_DYNAMIC #ifdef VITA sceKernelFreeMemBlock(sceBlock); sceBlock = -1; #else - if (munmap(translation_cache, 1<>12]=0; emit_movimm(start,0); emit_writeword(0,&pcaddr); - emit_jmp(new_dyna_leave); + emit_far_jump(new_dyna_leave); literal_pool(0); end_block(beginning); ll_add_flags(jump_in+page,start,state_rflags,(void *)beginning); @@ -6956,7 +7406,7 @@ int new_recompile_block(int addr) else if(type==CJUMP||type==SJUMP) ba[i]=start+i*4+4+((signed int)((unsigned int)source[i]<<16)>>14); else ba[i]=-1; - if(i>0&&(itype[i-1]==RJUMP||itype[i-1]==UJUMP||itype[i-1]==CJUMP||itype[i-1]==SJUMP)) { + if (i > 0 && is_jump(i-1)) { int do_in_intrp=0; // branch in delay slot? if(type==RJUMP||type==UJUMP||type==CJUMP||type==SJUMP) { @@ -6974,7 +7424,7 @@ int new_recompile_block(int addr) bt[t+1]=1; // expected return from interpreter } else if(i>=2&&rt1[i-2]==2&&rt1[i]==2&&rs1[i]!=2&&rs2[i]!=2&&rs1[i-1]!=2&&rs2[i-1]!=2&& - !(i>=3&&(itype[i-3]==RJUMP||itype[i-3]==UJUMP||itype[i-3]==CJUMP||itype[i-3]==SJUMP))) { + !(i>=3&&is_jump(i-3))) { // v0 overwrite like this is a sign of trouble, bail out SysPrintf("v0 overwrite @%08x (%08x)\n", addr + i*4, addr); do_in_intrp=1; @@ -6990,7 +7440,7 @@ int new_recompile_block(int addr) } } /* Is this the end of the block? */ - if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000)) { + if (i > 0 && is_ujump(i-1)) { if(rt1[i-1]==0) { // Continue past subroutine call (JAL) done=2; } @@ -7357,8 +7807,9 @@ int new_recompile_block(int addr) cop0_alloc(¤t,i); break; case COP1: + break; case COP2: - cop12_alloc(¤t,i); + cop2_alloc(¤t,i); break; case C1LS: c1ls_alloc(¤t,i); @@ -7451,7 +7902,7 @@ int new_recompile_block(int addr) dirty_reg(&branch_regs[i-1],31); } memcpy(&branch_regs[i-1].regmap_entry,&branch_regs[i-1].regmap,sizeof(current.regmap)); - memcpy(constmap[i],constmap[i-1],sizeof(current_constmap)); + memcpy(constmap[i],constmap[i-1],sizeof(constmap[i])); break; case RJUMP: memcpy(&branch_regs[i-1],¤t,sizeof(current)); @@ -7472,7 +7923,7 @@ int new_recompile_block(int addr) } #endif memcpy(&branch_regs[i-1].regmap_entry,&branch_regs[i-1].regmap,sizeof(current.regmap)); - memcpy(constmap[i],constmap[i-1],sizeof(current_constmap)); + memcpy(constmap[i],constmap[i-1],sizeof(constmap[i])); break; case CJUMP: if((opcode[i-1]&0x3E)==4) // BEQ/BNE @@ -7499,7 +7950,7 @@ int new_recompile_block(int addr) branch_regs[i-1].isconst=0; branch_regs[i-1].wasconst=0; memcpy(&branch_regs[i-1].regmap_entry,¤t.regmap,sizeof(current.regmap)); - memcpy(constmap[i],constmap[i-1],sizeof(current_constmap)); + memcpy(constmap[i],constmap[i-1],sizeof(constmap[i])); } else if((opcode[i-1]&0x3E)==6) // BLEZ/BGTZ @@ -7524,7 +7975,7 @@ int new_recompile_block(int addr) branch_regs[i-1].isconst=0; branch_regs[i-1].wasconst=0; memcpy(&branch_regs[i-1].regmap_entry,¤t.regmap,sizeof(current.regmap)); - memcpy(constmap[i],constmap[i-1],sizeof(current_constmap)); + memcpy(constmap[i],constmap[i-1],sizeof(constmap[i])); } else // Alloc the delay slot in case the branch is taken @@ -7578,7 +8029,7 @@ int new_recompile_block(int addr) branch_regs[i-1].isconst=0; branch_regs[i-1].wasconst=0; memcpy(&branch_regs[i-1].regmap_entry,¤t.regmap,sizeof(current.regmap)); - memcpy(constmap[i],constmap[i-1],sizeof(current_constmap)); + memcpy(constmap[i],constmap[i-1],sizeof(constmap[i])); } else // Alloc the delay slot in case the branch is taken @@ -7602,7 +8053,7 @@ int new_recompile_block(int addr) break; } - if(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000) + if (is_ujump(i-1)) { if(rt1[i-1]==31) // JAL/JALR { @@ -7651,12 +8102,10 @@ int new_recompile_block(int addr) #if !defined(DRC_DBG) else if(itype[i]==C2OP&>e_cycletab[source[i]&0x3f]>2) { - // GTE runs in parallel until accessed, divide by 2 for a rough guess - cc+=gte_cycletab[source[i]&0x3f]/2; - } - else if(/*itype[i]==LOAD||itype[i]==STORE||*/itype[i]==C1LS) // load,store causes weird timing issues - { - cc+=2; // 2 cycle penalty (after CLOCK_DIVIDER) + // this should really be removed since the real stalls have been implemented, + // but doing so causes sizeable perf regression against the older version + u_int gtec = gte_cycletab[source[i] & 0x3f]; + cc += HACK_ENABLED(NDHACK_GTE_NO_STALL) ? gtec/2 : 2; } else if(i>1&&itype[i]==STORE&&itype[i-1]==STORE&&itype[i-2]==STORE&&!bt[i]) { @@ -7664,7 +8113,8 @@ int new_recompile_block(int addr) } else if(itype[i]==C2LS) { - cc+=4; + // same as with C2OP + cc += HACK_ENABLED(NDHACK_GTE_NO_STALL) ? 4 : 2; } #endif else @@ -7675,7 +8125,7 @@ int new_recompile_block(int addr) if(!is_ds[i]) { regs[i].dirty=current.dirty; regs[i].isconst=current.isconst; - memcpy(constmap[i],current_constmap,sizeof(current_constmap)); + memcpy(constmap[i],current_constmap,sizeof(constmap[i])); } for(hr=0;hr=0) { @@ -7716,7 +8166,7 @@ int new_recompile_block(int addr) } } // Conditional branch may need registers for following instructions - if(itype[i]!=RJUMP&&itype[i]!=UJUMP&&(source[i]>>16)!=0x1000) + if (!is_ujump(i)) { if(i>16)!=0x1000) + if (!is_ujump(i)) { if(likely[i]) { regs[i].regmap[hr]=-1; @@ -7861,7 +8311,7 @@ int new_recompile_block(int addr) { branch_regs[i].regmap[hr]=-1; branch_regs[i].regmap_entry[hr]=-1; - if(itype[i]!=RJUMP&&itype[i]!=UJUMP&&(source[i]>>16)!=0x1000) + if (!is_ujump(i)) { if(!likely[i]&&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]); @@ -7906,8 +8356,8 @@ int new_recompile_block(int addr) } } } - } - } + } // if needed + } // for hr } /* Pass 5 - Pre-allocate registers */ @@ -8052,7 +8502,7 @@ int new_recompile_block(int addr) branch_regs[i].dirty|=(1<>16)!=0x1000) { + if (!is_ujump(i)) { regmap_pre[i+2][hr]=f_regmap[hr]; regs[i+2].wasdirty&=~(1<>16)!=0x1000) { + if (!is_ujump(k)) { regmap_pre[k+2][hr]=f_regmap[hr]; regs[k+2].wasdirty&=~(1<>16)==0x1000) + if (is_ujump(j)) { // Stop on unconditional branch break; @@ -8533,7 +8983,7 @@ int new_recompile_block(int addr) void *instr_addr0_override = NULL; if (start == 0x80030000) { - // nasty hack for fastbios thing + // nasty hack for the fastbios thing // override block entry to this code instr_addr0_override = out; emit_movimm(start,0); @@ -8543,7 +8993,12 @@ int new_recompile_block(int addr) emit_writeword(0,&pcaddr); emit_writeword(0,&address); emit_cmp(0,1); + #ifdef __aarch64__ + emit_jeq(out + 4*2); + emit_far_jump(new_dyna_leave); + #else emit_jne(new_dyna_leave); + #endif } for(i=0;i>16)!=0x1000)) + if (i < 2 || !is_ujump(i-2)) { wb_valid(regmap_pre[i],regs[i].regmap_entry,dirty_pre,regs[i].wasdirty,unneeded_reg[i]); } @@ -8567,7 +9022,7 @@ int new_recompile_block(int addr) } #endif // write back - if(i<2||(itype[i-2]!=UJUMP&&itype[i-2]!=RJUMP&&(source[i-2]>>16)!=0x1000)) + if (i < 2 || !is_ujump(i-2)) { wb_invalidate(regmap_pre[i],regs[i].regmap_entry,regs[i].wasdirty,unneeded_reg[i]); loop_preload(regmap_pre[i],regs[i].regmap_entry); @@ -8659,17 +9114,17 @@ int new_recompile_block(int addr) case SPAN: pagespan_assemble(i,®s[i]);break; } - if(itype[i]==UJUMP||itype[i]==RJUMP||(source[i]>>16)==0x1000) + if (is_ujump(i)) literal_pool(1024); else literal_pool_jumpover(256); } } - //assert(itype[i-2]==UJUMP||itype[i-2]==RJUMP||(source[i-2]>>16)==0x1000); + //assert(is_ujump(i-2)); // If the block did not end with an unconditional branch, // add a jump to the next instruction. if(i>1) { - if(itype[i-2]!=UJUMP&&itype[i-2]!=RJUMP&&(source[i-2]>>16)!=0x1000&&itype[i-1]!=SPAN) { + if(!is_ujump(i-2)&&itype[i-1]!=SPAN) { assert(itype[i-1]!=UJUMP&&itype[i-1]!=CJUMP&&itype[i-1]!=SJUMP&&itype[i-1]!=RJUMP); assert(i==slen); if(itype[i-2]!=CJUMP&&itype[i-2]!=SJUMP) { @@ -8812,8 +9267,8 @@ int new_recompile_block(int addr) // If we're within 256K of the end of the buffer, // start over from the beginning. (Is 256K enough?) - if (out > translation_cache+(1< ndrc->translation_cache + sizeof(ndrc->translation_cache) - MAX_OUTPUT_BLOCK_SIZE) + out = ndrc->translation_cache; // Trap writes to any of the pages we compiled for(i=start>>12;i<=(start+slen*4)>>12;i++) { @@ -8830,11 +9285,11 @@ int new_recompile_block(int addr) /* Pass 10 - Free memory by expiring oldest blocks */ - int end=(((out-translation_cache)>>(TARGET_SIZE_2-16))+16384)&65535; + int end=(((out-ndrc->translation_cache)>>(TARGET_SIZE_2-16))+16384)&65535; while(expirep!=end) { int shift=TARGET_SIZE_2-3; // Divide into 8 blocks - uintptr_t base=(uintptr_t)translation_cache+((expirep>>13)<translation_cache+((expirep>>13)<>11)&3) { @@ -8872,10 +9327,8 @@ int new_recompile_block(int addr) break; case 3: // Clear jump_out - #if defined(__arm__) || defined(__aarch64__) if((expirep&2047)==0) do_clear_cache(); - #endif ll_remove_matching_addrs(jump_out+(expirep&2047),base,shift); ll_remove_matching_addrs(jump_out+2048+(expirep&2047),base,shift); break;