X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libpcsxcore%2Fnew_dynarec%2Fassem_arm64.c;h=63c6866b3dacd93a6726273fc4130a3754ba809b;hb=39b71d9abccb93457f17df887e50507a91390a78;hp=27f9141d3a35eb198e1016d773edc61e20b5c8ab;hpb=3968e69e7fa8f9cb0d44ac79477d5929b9649271;p=pcsx_rearmed.git diff --git a/libpcsxcore/new_dynarec/assem_arm64.c b/libpcsxcore/new_dynarec/assem_arm64.c index 27f9141d..63c6866b 100644 --- a/libpcsxcore/new_dynarec/assem_arm64.c +++ b/libpcsxcore/new_dynarec/assem_arm64.c @@ -23,14 +23,6 @@ #include "pcnt.h" #include "arm_features.h" -#if defined(BASE_ADDR_FIXED) -#elif defined(BASE_ADDR_DYNAMIC) -u_char *translation_cache; -#else -u_char translation_cache[1 << TARGET_SIZE_2] __attribute__((aligned(4096))); -#endif -static u_int needs_clear_cache[1<<(TARGET_SIZE_2-17)]; - #define CALLER_SAVE_REGS 0x0007ffff #define unused __attribute__((unused)) @@ -52,7 +44,7 @@ static void set_jump_target(void *addr, void *target) || (*ptr&0x7e000000) == 0x34000000) { // cbz/cbnz // Conditional branch are limited to +/- 1MB // block max size is 256k so branching beyond the +/- 1MB limit - // should only happen when jumping to an already compiled block (see add_link) + // should only happen when jumping to an already compiled block (see add_jump_out) // a workaround would be to do a trampoline jump via a stub at the end of the block assert(-1048576 <= offset && offset < 1048576); *ptr=(*ptr&0xFF00000F)|(((offset>>2)&0x7ffff)<<5); @@ -338,6 +330,7 @@ static void emit_adds64(u_int rs1, u_int rs2, u_int rt) assem_debug("adds %s,%s,%s\n",regname64[rt],regname64[rs1],regname64[rs2]); output_w32(0xab000000 | rm_rn_rd(rs2, rs1, rt)); } +#define emit_adds_ptr emit_adds64 static void emit_neg(u_int rs, u_int rt) { @@ -442,6 +435,7 @@ static void emit_readdword(void *addr, u_int rt) else abort(); } +#define emit_readptr emit_readdword static void emit_readshword(void *addr, u_int rt) { @@ -821,6 +815,12 @@ static void emit_cmovl_reg(u_int rs,u_int rt) output_w32(0x1a800000 | (COND_LT << 12) | rm_rn_rd(rt, rs, rt)); } +static void emit_cmovb_reg(u_int rs,u_int rt) +{ + assem_debug("csel %s,%s,%s,cc\n",regname[rt],regname[rs],regname[rt]); + output_w32(0x1a800000 | (COND_CC << 12) | rm_rn_rd(rt, rs, rt)); +} + static void emit_cmovs_reg(u_int rs,u_int rt) { assem_debug("csel %s,%s,%s,mi\n",regname[rt],regname[rs],regname[rt]); @@ -889,6 +889,12 @@ static void emit_set_if_carry32(u_int rs1, u_int rs2, u_int rt) emit_cmovb_imm(1,rt); } +static int can_jump_or_call(const void *a) +{ + intptr_t diff = (u_char *)a - out; + return (-134217728 <= diff && diff <= 134217727); +} + static void emit_call(const void *a) { intptr_t diff = (u_char *)a - out; @@ -1043,6 +1049,7 @@ static void emit_readdword_dualindexedx8(u_int rs1, u_int rs2, u_int rt) assem_debug("ldr %s, [%s,%s, uxtw #3]\n",regname64[rt],regname64[rs1],regname[rs2]); output_w32(0xf8605800 | rm_rn_rd(rs2, rs1, rt)); } +#define emit_readptr_dualindexedx_ptrlen emit_readdword_dualindexedx8 static void emit_ldrb_dualindexed(u_int rs1, u_int rs2, u_int rt) { @@ -1295,7 +1302,7 @@ static void emit_extjump2(u_char *addr, u_int target, void *linker) // addr is in the current recompiled block (max 256k) // offset shouldn't exceed +/-1MB emit_adr(addr, 1); - emit_jmp(linker); + emit_far_jump(linker); } static void check_extjump2(void *src) @@ -1376,10 +1383,10 @@ static void do_readstub(int n) u_int reglist = stubs[n].e; const signed char *i_regmap = i_regs->regmap; int rt; - if(itype[i]==C1LS||itype[i]==C2LS||itype[i]==LOADLR) { + if(dops[i].itype==C1LS||dops[i].itype==C2LS||dops[i].itype==LOADLR) { rt=get_reg(i_regmap,FTEMP); }else{ - rt=get_reg(i_regmap,rt1[i]); + rt=get_reg(i_regmap,dops[i].rt1); } assert(rs>=0); int r,temp=-1,temp2=HOST_TEMPREG,regs_saved=0; @@ -1391,7 +1398,7 @@ static void do_readstub(int n) break; } } - if(rt>=0&&rt1[i]!=0) + if(rt>=0&&dops[i].rt1!=0) reglist&=~(1<=0&&rt1[i]!=0)) { + if(dops[i].itype==C1LS||dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) { switch(type) { case LOADB_STUB: emit_ldrsb_dualindexed(temp2,rs,rt); break; case LOADBU_STUB: emit_ldrb_dualindexed(temp2,rs,rt); break; @@ -1438,10 +1445,10 @@ static void do_readstub(int n) int cc=get_reg(i_regmap,CCREG); if(cc<0) emit_loadreg(CCREG,2); - emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n].d+1),2); - emit_call(handler); + emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n].d),2); + emit_far_call(handler); // (no cycle reload after read) - if(itype[i]==C1LS||itype[i]==C2LS||(rt>=0&&rt1[i]!=0)) { + if(dops[i].itype==C1LS||dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) { loadstore_extend(type,0,rt); } if(restore_jump) @@ -1450,7 +1457,8 @@ static void do_readstub(int n) emit_jmp(stubs[n].retaddr); } -static void inline_readstub(enum stub_type type, int i, u_int addr, signed char regmap[], int target, int adj, u_int reglist) +static void inline_readstub(enum stub_type type, int i, u_int addr, + const signed char regmap[], int target, int adj, u_int reglist) { int rs=get_reg(regmap,target); int rt=get_reg(regmap,target); @@ -1460,11 +1468,11 @@ static void inline_readstub(enum stub_type type, int i, u_int addr, signed char uintptr_t host_addr = 0; void *handler; int cc=get_reg(regmap,CCREG); - //if(pcsx_direct_read(type,addr,CLOCK_ADJUST(adj+1),cc,target?rs:-1,rt)) + //if(pcsx_direct_read(type,addr,CLOCK_ADJUST(adj),cc,target?rs:-1,rt)) // return; handler = get_direct_memhandler(mem_rtab, addr, type, &host_addr); if (handler == NULL) { - if(rt<0||rt1[i]==0) + if(rt<0||dops[i].rt1==0) return; if (addr != host_addr) { if (host_addr >= 0x100000000ull) @@ -1492,7 +1500,7 @@ static void inline_readstub(enum stub_type type, int i, u_int addr, signed char } // call a memhandler - if(rt>=0&&rt1[i]!=0) + if(rt>=0&&dops[i].rt1!=0) reglist&=~(1<>12] << 1; emit_adrp((void *)l1, 1); emit_addimm64(1, l1 & 0xfff, 1); } else - emit_call(do_memhandler_pre); + emit_far_call(do_memhandler_pre); - emit_call(handler); + emit_far_call(handler); // (no cycle reload after read) - if(rt>=0&&rt1[i]!=0) + if(rt>=0&&dops[i].rt1!=0) loadstore_extend(type, 0, rt); restore_regs(reglist); } @@ -1529,10 +1537,10 @@ static void do_writestub(int n) u_int reglist=stubs[n].e; signed char *i_regmap=i_regs->regmap; int rt,r; - if(itype[i]==C1LS||itype[i]==C2LS) { + if(dops[i].itype==C1LS||dops[i].itype==C2LS) { rt=get_reg(i_regmap,r=FTEMP); }else{ - rt=get_reg(i_regmap,r=rs2[i]); + rt=get_reg(i_regmap,r=dops[i].rs2); } assert(rs>=0); assert(rt>=0); @@ -1597,10 +1605,10 @@ static void do_writestub(int n) int cc=get_reg(i_regmap,CCREG); if(cc<0) emit_loadreg(CCREG,2); - emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n].d+1),2); + emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n].d),2); // returns new cycle_count - emit_call(handler); - emit_addimm(0,-CLOCK_ADJUST((int)stubs[n].d+1),cc<0?2:cc); + emit_far_call(handler); + emit_addimm(0,-CLOCK_ADJUST((int)stubs[n].d),cc<0?2:cc); if(cc<0) emit_storereg(CCREG,2); if(restore_jump) @@ -1609,7 +1617,8 @@ static void do_writestub(int n) emit_jmp(stubs[n].retaddr); } -static void inline_writestub(enum stub_type type, int i, u_int addr, signed char regmap[], int target, int adj, u_int reglist) +static void inline_writestub(enum stub_type type, int i, u_int addr, + const signed char regmap[], int target, int adj, u_int reglist) { int rs = get_reg(regmap,-1); int rt = get_reg(regmap,target); @@ -1640,12 +1649,12 @@ static void inline_writestub(enum stub_type type, int i, u_int addr, signed char cc = cc_use = get_reg(regmap, CCREG); if (cc < 0) emit_loadreg(CCREG, (cc_use = 2)); - emit_addimm(cc_use, CLOCK_ADJUST(adj+1), 2); + emit_addimm(cc_use, CLOCK_ADJUST(adj), 2); - emit_call(do_memhandler_pre); - emit_call(handler); - emit_call(do_memhandler_post); - emit_addimm(0, -CLOCK_ADJUST(adj+1), cc_use); + emit_far_call(do_memhandler_pre); + emit_far_call(handler); + emit_far_call(do_memhandler_post); + emit_addimm(0, -CLOCK_ADJUST(adj), cc_use); if (cc < 0) emit_storereg(CCREG, cc_use); restore_regs(reglist); @@ -1659,18 +1668,18 @@ static int verify_code_arm64(const void *source, const void *copy, u_int size) } // this output is parsed by verify_dirty, get_bounds, isclean, get_clean_addr -static void do_dirty_stub_base(u_int vaddr) +static void do_dirty_stub_base(u_int vaddr, u_int source_len) { - assert(slen <= MAXBLOCK); + assert(source_len <= MAXBLOCK*4); emit_loadlp_ofs(0, 0); // ldr x1, source emit_loadlp_ofs(0, 1); // ldr x2, copy - emit_movz(slen*4, 2); - emit_call(verify_code_arm64); + emit_movz(source_len, 2); + emit_far_call(verify_code_arm64); void *jmp = out; emit_cbz(0, 0); emit_movz(vaddr & 0xffff, 0); emit_movk_lsl16(vaddr >> 16, 0); - emit_call(get_addr); + emit_far_call(get_addr); emit_jmpreg(0); set_jump_target(jmp, out); } @@ -1679,7 +1688,7 @@ static void assert_dirty_stub(const u_int *ptr) { assert((ptr[0] & 0xff00001f) == 0x58000000); // ldr x0, source assert((ptr[1] & 0xff00001f) == 0x58000001); // ldr x1, copy - assert((ptr[2] & 0xffe0001f) == 0x52800002); // movz w2, #slen*4 + assert((ptr[2] & 0xffe0001f) == 0x52800002); // movz w2, #source_len assert( ptr[8] == 0xd61f0000); // br x0 } @@ -1700,11 +1709,11 @@ static void do_dirty_stub_emit_literals(u_int *loadlps) output_w64((uintptr_t)copy); } -static void *do_dirty_stub(int i) +static void *do_dirty_stub(int i, u_int source_len) { assem_debug("do_dirty_stub %x\n",start+i*4); u_int *loadlps = (void *)out; - do_dirty_stub_base(start + i*4); + do_dirty_stub_base(start + i*4, source_len); void *entry = out; load_regs_entry(i); if (entry == out) @@ -1714,10 +1723,10 @@ static void *do_dirty_stub(int i) return entry; } -static void do_dirty_stub_ds(void) +static void do_dirty_stub_ds(u_int source_len) { u_int *loadlps = (void *)out; - do_dirty_stub_base(start + 1); + do_dirty_stub_base(start + 1, source_len); void *lit_jumpover = out; emit_jmp(out + 8*2); do_dirty_stub_emit_literals(loadlps); @@ -1754,7 +1763,7 @@ static int verify_dirty(const u_int *ptr) assert_dirty_stub(ptr); source = (void *)get_from_ldr_literal(&ptr[0]); // ldr x1, source copy = (void *)get_from_ldr_literal(&ptr[1]); // ldr x1, copy - len = get_from_movz(&ptr[2]); // movz w3, #slen*4 + len = get_from_movz(&ptr[2]); // movz w3, #source_len return !memcmp(source, copy, len); } @@ -1774,17 +1783,18 @@ static void get_bounds(void *addr, u_char **start, u_char **end) const u_int *ptr = addr; assert_dirty_stub(ptr); *start = (u_char *)get_from_ldr_literal(&ptr[0]); // ldr x1, source - *end = *start + get_from_movz(&ptr[2]); // movz w3, #slen*4 + *end = *start + get_from_movz(&ptr[2]); // movz w3, #source_len } /* Special assem */ -static void c2op_prologue(u_int op,u_int reglist) +static void c2op_prologue(u_int op, int i, const struct regstat *i_regs, u_int reglist) { save_load_regs_all(1, reglist); + cop2_do_stall_check(op, i, i_regs, 0); #ifdef PCNT emit_movimm(op, 0); - emit_call(pcnt_gte_start); + emit_far_call(pcnt_gte_start); #endif // pointer to cop2 regs emit_addimm64(FP, (u_char *)&psxRegs.CP2D.r[0] - (u_char *)&dynarec_local, 0); @@ -1794,12 +1804,12 @@ static void c2op_epilogue(u_int op,u_int reglist) { #ifdef PCNT emit_movimm(op, 0); - emit_call(pcnt_gte_end); + emit_far_call(pcnt_gte_end); #endif save_load_regs_all(0, reglist); } -static void c2op_assemble(int i,struct regstat *i_regs) +static void c2op_assemble(int i, const struct regstat *i_regs) { u_int c2op=source[i]&0x3f; u_int hr,reglist_full=0,reglist; @@ -1814,17 +1824,17 @@ static void c2op_assemble(int i,struct regstat *i_regs) need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00; assem_debug("gte op %08x, unneeded %016lx, need_flags %d, need_ir %d\n", source[i],gte_unneeded[i+1],need_flags,need_ir); - if(new_dynarec_hacks&NDHACK_GTE_NO_FLAGS) + if(HACK_ENABLED(NDHACK_GTE_NO_FLAGS)) need_flags=0; //int shift = (source[i] >> 19) & 1; //int lm = (source[i] >> 10) & 1; switch(c2op) { default: (void)need_ir; - c2op_prologue(c2op,reglist); + c2op_prologue(c2op, i, i_regs, reglist); emit_movimm(source[i],1); // opcode emit_writeword(1,&psxRegs.code); - emit_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]); + emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]); break; } c2op_epilogue(c2op,reglist); @@ -1878,15 +1888,15 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) // case 0x19: MULTU // case 0x1A: DIV // case 0x1B: DIVU - if(rs1[i]&&rs2[i]) + if(dops[i].rs1&&dops[i].rs2) { - switch(opcode2[i]) + switch(dops[i].opcode2) { case 0x18: // MULT case 0x19: // MULTU { - signed char m1=get_reg(i_regs->regmap,rs1[i]); - signed char m2=get_reg(i_regs->regmap,rs2[i]); + signed char m1=get_reg(i_regs->regmap,dops[i].rs1); + signed char m2=get_reg(i_regs->regmap,dops[i].rs2); signed char hi=get_reg(i_regs->regmap,HIREG); signed char lo=get_reg(i_regs->regmap,LOREG); assert(m1>=0); @@ -1894,7 +1904,7 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) assert(hi>=0); assert(lo>=0); - if(opcode2[i]==0x18) // MULT + if(dops[i].opcode2==0x18) // MULT emit_smull(m1,m2,hi); else // MULTU emit_umull(m1,m2,hi); @@ -1906,8 +1916,8 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) case 0x1A: // DIV case 0x1B: // DIVU { - signed char numerator=get_reg(i_regs->regmap,rs1[i]); - signed char denominator=get_reg(i_regs->regmap,rs2[i]); + signed char numerator=get_reg(i_regs->regmap,dops[i].rs1); + signed char denominator=get_reg(i_regs->regmap,dops[i].rs2); signed char quotient=get_reg(i_regs->regmap,LOREG); signed char remainder=get_reg(i_regs->regmap,HIREG); assert(numerator>=0); @@ -1915,7 +1925,7 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) assert(quotient>=0); assert(remainder>=0); - if (opcode2[i] == 0x1A) // DIV + if (dops[i].opcode2 == 0x1A) // DIV emit_sdiv(numerator,denominator,quotient); else // DIVU emit_udiv(numerator,denominator,quotient); @@ -1923,7 +1933,7 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) // div 0 quotient (remainder is already correct) host_tempreg_acquire(); - if (opcode2[i] == 0x1A) // DIV + if (dops[i].opcode2 == 0x1A) // DIV emit_sub_asrimm(0,numerator,31,HOST_TEMPREG); else emit_movimm(~0,HOST_TEMPREG); @@ -1940,15 +1950,15 @@ static void multdiv_assemble_arm64(int i,struct regstat *i_regs) { signed char hr=get_reg(i_regs->regmap,HIREG); signed char lr=get_reg(i_regs->regmap,LOREG); - if ((opcode2[i]==0x1A || opcode2[i]==0x1B) && rs2[i]==0) // div 0 + if ((dops[i].opcode2==0x1A || dops[i].opcode2==0x1B) && dops[i].rs2==0) // div 0 { - if (rs1[i]) { - signed char numerator = get_reg(i_regs->regmap, rs1[i]); + if (dops[i].rs1) { + signed char numerator = get_reg(i_regs->regmap, dops[i].rs1); assert(numerator >= 0); if (hr >= 0) emit_mov(numerator,hr); if (lr >= 0) { - if (opcode2[i] == 0x1A) // DIV + if (dops[i].opcode2 == 0x1A) // DIV emit_sub_asrimm(0,numerator,31,lr); else emit_movimm(~0,lr); @@ -1973,7 +1983,7 @@ static void do_jump_vaddr(u_int rs) { if (rs != 0) emit_mov(rs, 0); - emit_call(get_addr_ht); + emit_far_call(get_addr_ht); emit_jmpreg(0); } @@ -2017,51 +2027,60 @@ static void do_miniht_insert(u_int return_address,u_int rt,int temp) { emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]); } -static void mark_clear_cache(void *target) +static void clear_cache_arm64(char *start, char *end) { - u_long offset = (u_char *)target - translation_cache; - u_int mask = 1u << ((offset >> 12) & 31); - if (!(needs_clear_cache[offset >> 17] & mask)) { - char *start = (char *)((u_long)target & ~4095ul); - start_tcache_write(start, start + 4096); - needs_clear_cache[offset >> 17] |= mask; + // Don't rely on GCC's __clear_cache implementation, as it caches + // icache/dcache cache line sizes, that can vary between cores on + // big.LITTLE architectures. + uint64_t addr, ctr_el0; + static size_t icache_line_size = 0xffff, dcache_line_size = 0xffff; + size_t isize, dsize; + + __asm__ volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); + isize = 4 << ((ctr_el0 >> 0) & 0xf); + dsize = 4 << ((ctr_el0 >> 16) & 0xf); + + // use the global minimum cache line size + icache_line_size = isize = icache_line_size < isize ? icache_line_size : isize; + dcache_line_size = dsize = dcache_line_size < dsize ? dcache_line_size : dsize; + + /* If CTR_EL0.IDC is enabled, Data cache clean to the Point of Unification is + not required for instruction to data coherence. */ + if ((ctr_el0 & (1 << 28)) == 0x0) { + addr = (uint64_t)start & ~(uint64_t)(dsize - 1); + for (; addr < (uint64_t)end; addr += dsize) + // use "civac" instead of "cvau", as this is the suggested workaround for + // Cortex-A53 errata 819472, 826319, 827319 and 824069. + __asm__ volatile("dc civac, %0" : : "r"(addr) : "memory"); } -} + __asm__ volatile("dsb ish" : : : "memory"); -// 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() -{ - int i,j; - for (i=0;i<(1<<(TARGET_SIZE_2-17));i++) - { - u_int bitmap=needs_clear_cache[i]; - if(bitmap) { - u_char *start, *end; - for(j=0;j<32;j++) - { - if(bitmap&(1<tramp.f - (u_char *)&ndrc->tramp.ops; + struct tramp_insns *ops = ndrc->tramp.ops; + size_t i; + assert(!(diff & 3)); + start_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops)); + for (i = 0; i < ARRAY_SIZE(ndrc->tramp.ops); i++) { + ops[i].ldr = 0x58000000 | imm19_rt(diff >> 2, 17); // ldr x17, [=val] + ops[i].br = 0xd61f0000 | rm_rn_rd(0, 17, 0); // br x17 + } + end_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops)); } // vim:shiftwidth=2:expandtab