From 114e0d90f76ade29145aaeb6ccc1e24e63073027 Mon Sep 17 00:00:00 2001 From: Julio Restrepo Date: Sun, 3 May 2026 12:55:44 -0400 Subject: [PATCH 1/3] fix(macos-arm64): restore known-good dynarec MAP_JIT lane --- Makefile | 9 +- custom/dependencies/libpng/pngpriv.h | 4 +- .../r4300/new_dynarec/arm64/assem_arm64.c | 33 ++- .../r4300/new_dynarec/arm64/assem_arm64.h | 8 + .../r4300/new_dynarec/arm64/linkage_arm64.S | 226 ++++++++++-------- .../device/r4300/new_dynarec/new_dynarec.c | 149 ++++++++---- 6 files changed, 266 insertions(+), 163 deletions(-) diff --git a/Makefile b/Makefile index 97c16094..1e464992 100644 --- a/Makefile +++ b/Makefile @@ -399,17 +399,22 @@ else ifneq (,$(findstring RK,$(platform))) else ifneq (,$(findstring osx,$(platform))) TARGET := $(TARGET_NAME)_libretro.dylib LDFLAGS += -dynamiclib + SYSTEM_ZLIB = 1 OSXVER = `sw_vers -productVersion | cut -d. -f 2` OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9)) && echo "YES"` LDFLAGS += -mmacosx-version-min=10.7 LDFLAGS += -stdlib=libc++ - PLATCFLAGS += -D__MACOSX__ -DOSX -DOS_MAC_OS_X -DHAVE_UNISTD_H=1 -DHAVE_POSIX_MEMALIGN -DNO_ASM -DGL_SILENCE_DEPRECATION=1 + PLATCFLAGS += -D__MACOSX__ -DOSX -DOS_MAC_OS_X -DHAVE_UNISTD_H=1 -DHAVE_POSIX_MEMALIGN -DGL_SILENCE_DEPRECATION=1 GL_LIB := -framework OpenGL LDFLAGS += -framework AudioToolbox # Target Dynarec - WITH_DYNAREC = + ifeq ($(ARCH), $(filter $(ARCH), arm64 aarch64)) + WITH_DYNAREC = aarch64 + else + WITH_DYNAREC = + endif HAVE_PARALLEL_RSP = 1 HAVE_PARALLEL_RDP = 1 diff --git a/custom/dependencies/libpng/pngpriv.h b/custom/dependencies/libpng/pngpriv.h index 7216050d..1b6360eb 100644 --- a/custom/dependencies/libpng/pngpriv.h +++ b/custom/dependencies/libpng/pngpriv.h @@ -508,7 +508,9 @@ */ # include -# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ +# if defined(__APPLE__) +# include +# elif (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) /* We need to check that hasn't already been included earlier * as it seems it doesn't agree with , yet we should really use diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c index 46243eca..c95f22e4 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c @@ -263,7 +263,17 @@ static uintptr_t jump_table_symbols[] = { static void cache_flush(char* start, char* end) { -#ifndef WIN32 +#if defined(__APPLE__) && defined(__aarch64__) + // Apple-documented MAP_JIT pattern: switch to executable, then invalidate icache. + jit_write_disable(); + sys_icache_invalidate(start, end - start); + jit_write_enable(); +#elif defined(__APPLE__) + __builtin___clear_cache(start, end); +#elif defined(__APPLE__) + sys_dcache_flush(start, end - start); + sys_icache_invalidate(start, end - start); +#elif !defined(WIN32) // 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. @@ -356,7 +366,7 @@ static void *kill_pointer(void *stub) int *i_ptr=(int*)((intptr_t)ptr+offset); assert((*i_ptr&0xfc000000)==0x14000000); //b set_jump_target((intptr_t)i_ptr,(intptr_t)stub); - + intptr_t ptr_rx=((intptr_t)i_ptr-(intptr_t)base_addr)+(intptr_t)base_addr_rx; cache_flush((void*)ptr_rx, (void*)(ptr_rx+4)); return i_ptr; @@ -439,7 +449,7 @@ static void alloc_reg(struct regstat *cur,int i,signed char tr) // registers that have not been used recently. if(i>0) { for(hr=0;hrregmap[hr]==-1) { + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==-1) { if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) { cur->regmap[hr]=tr; cur->dirty&=~(1<regmap[hr]==-1) { + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==-1) { cur->regmap[hr]=tr; cur->dirty&=~(1<isconst&=~(1<0) { for(hr=0;hrregmap[hr]==-1) { + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==-1) { if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) { cur->regmap[hr]=tr|64; cur->dirty&=~(1<regmap[hr]==-1) { + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==-1) { cur->regmap[hr]=tr|64; cur->dirty&=~(1<isconst&=~(1<regmap[hr]==tr) return; + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==tr) return; } // Try to allocate any available register for(hr=HOST_REGS-1;hr>=0;hr--) { - if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) { + if(!IS_REG_EXCLUDED(hr)&&cur->regmap[hr]==-1) { cur->regmap[hr]=tr; cur->dirty&=~(1<isconst&=~(1<regmap[n]==tr) { + if(!IS_REG_EXCLUDED(n)&&cur->regmap[n]==tr) { dirty=(cur->dirty>>n)&1; cur->regmap[n]=-1; } @@ -4608,6 +4618,7 @@ static void arch_init(void) { ptr=(intptr_t *)jump_table_symbols; ptr2=(intptr_t *)((char *)base_addr+(1<> 16) & 0xFFFF, lsl 16 @@ -56,133 +86,133 @@ fp_stop = offsetof_struct_new_dynarec_hot_state_stop TEXT_SECTION -GLOBAL_FUNCTION(jump_vaddr_x0): - b jump_vaddr +GLOBAL_FUNCTION jump_vaddr_x0 + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x1): +GLOBAL_FUNCTION jump_vaddr_x1 mov w0, w1 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x2): +GLOBAL_FUNCTION jump_vaddr_x2 mov w0, w2 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x3): +GLOBAL_FUNCTION jump_vaddr_x3 mov w0, w3 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x4): +GLOBAL_FUNCTION jump_vaddr_x4 mov w0, w4 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x5): +GLOBAL_FUNCTION jump_vaddr_x5 mov w0, w5 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x6): +GLOBAL_FUNCTION jump_vaddr_x6 mov w0, w6 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x8): +GLOBAL_FUNCTION jump_vaddr_x8 mov w0, w8 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x9): +GLOBAL_FUNCTION jump_vaddr_x9 mov w0, w9 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x10): +GLOBAL_FUNCTION jump_vaddr_x10 mov w0, w10 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x11): +GLOBAL_FUNCTION jump_vaddr_x11 mov w0, w11 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x12): +GLOBAL_FUNCTION jump_vaddr_x12 mov w0, w12 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x13): +GLOBAL_FUNCTION jump_vaddr_x13 mov w0, w13 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x14): +GLOBAL_FUNCTION jump_vaddr_x14 mov w0, w14 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x15): +GLOBAL_FUNCTION jump_vaddr_x15 mov w0, w15 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x16): +GLOBAL_FUNCTION jump_vaddr_x16 mov w0, w16 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x17): +GLOBAL_FUNCTION jump_vaddr_x17 mov w0, w17 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x19): +GLOBAL_FUNCTION jump_vaddr_x19 mov w0, w19 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x21): +GLOBAL_FUNCTION jump_vaddr_x21 mov w0, w21 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x22): +GLOBAL_FUNCTION jump_vaddr_x22 mov w0, w22 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x23): +GLOBAL_FUNCTION jump_vaddr_x23 mov w0, w23 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x24): +GLOBAL_FUNCTION jump_vaddr_x24 mov w0, w24 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x25): +GLOBAL_FUNCTION jump_vaddr_x25 mov w0, w25 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x26): +GLOBAL_FUNCTION jump_vaddr_x26 mov w0, w26 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x27): +GLOBAL_FUNCTION jump_vaddr_x27 mov w0, w27 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x28): +GLOBAL_FUNCTION jump_vaddr_x28 mov w0, w28 - b jump_vaddr + b GLABEL(jump_vaddr) -GLOBAL_FUNCTION(jump_vaddr_x7): +GLOBAL_FUNCTION jump_vaddr_x7 mov w0, w7 -GLOBAL_FUNCTION(jump_vaddr): - bl get_addr_ht +GLOBAL_FUNCTION jump_vaddr + bl GLABEL(get_addr_ht) br x0 -GLOBAL_FUNCTION(verify_code): +GLOBAL_FUNCTION verify_code /* x0 = head */ mov x21, x30 /* Save link register */ - bl verify_dirty + bl GLABEL(verify_dirty) tst x0, x0 b.ne .D1 mov x30, x21 /* Restore link register */ ret .D1: - bl get_addr + bl GLABEL(get_addr) br x0 -GLOBAL_FUNCTION(cc_interrupt): +GLOBAL_FUNCTION cc_interrupt str w20, [x29, #fp_cycle_count] str wzr, [x29, #fp_pending_exception] mov x20, x30 /* Save link register */ - bl dynarec_gen_interrupt + bl GLABEL(dynarec_gen_interrupt) mov x30, x20 /* Restore link register */ ldr w20, [x29, #fp_cycle_count] ldr w1, [x29, #fp_pending_exception] @@ -194,10 +224,10 @@ GLOBAL_FUNCTION(cc_interrupt): ret .E1: ldr w0, [x29, #fp_pcaddr] - bl get_addr_ht + bl GLABEL(get_addr_ht) br x0 -LOCAL_FUNCTION(new_dyna_stop): +LOCAL_FUNCTION new_dyna_stop add x16, x29, #saved_context ldp x19,x20,[x16,#0] ldp x21,x22,[x16,#16] @@ -207,48 +237,48 @@ LOCAL_FUNCTION(new_dyna_stop): ldp x29,x30,[x16,#80] ret -GLOBAL_FUNCTION(do_interrupt): +GLOBAL_FUNCTION do_interrupt ldr w2, [x29, #fp_stop] tst w2, w2 b.ne new_dyna_stop ldr w0, [x29, #fp_pcaddr] - bl get_addr_ht + bl GLABEL(get_addr_ht) ldr w20, [x29, #fp_cycle_count] br x0 -GLOBAL_FUNCTION(fp_exception): +GLOBAL_FUNCTION fp_exception str w0, [x29, #fp_pcaddr] - bl cop1_unusable + bl GLABEL(cop1_unusable) br x0 -GLOBAL_FUNCTION(jump_syscall): +GLOBAL_FUNCTION jump_syscall str w0, [x29, #fp_pcaddr] - bl SYSCALL_new + bl GLABEL(SYSCALL_new) br x0 -GLOBAL_FUNCTION(jump_eret): +GLOBAL_FUNCTION jump_eret str w20, [x29, #fp_cycle_count] - bl ERET_new + bl GLABEL(ERET_new) ldr w20, [x29, #fp_cycle_count] tst w0, w0 b.eq new_dyna_stop br x0 -GLOBAL_FUNCTION(dyna_linker): - bl dynamic_linker +GLOBAL_FUNCTION dyna_linker + bl GLABEL(dynamic_linker) br x0 -GLOBAL_FUNCTION(dyna_linker_ds): - bl dynamic_linker_ds +GLOBAL_FUNCTION dyna_linker_ds + bl GLABEL(dynamic_linker_ds) br x0 -GLOBAL_FUNCTION(new_dyna_start): - adrp x16, g_dev - add x16, x16, :lo12:g_dev +GLOBAL_FUNCTION new_dyna_start + adrp x16, PAGE_ADDR(g_dev) + add x16, x16, PAGE_OFF(g_dev) movl x1, (device_r4300_new_dynarec_hot_state_dynarec_local + saved_context) add x16, x16, x1 - adrp x1, base_addr_rx - add x1, x1, :lo12:base_addr_rx + adrp x1, PAGE_ADDR(base_addr_rx) + add x1, x1, PAGE_OFF(base_addr_rx) mov w0, #0xa4000000 stp x19,x20,[x16,#0] stp x21,x22,[x16,#16] @@ -259,12 +289,12 @@ GLOBAL_FUNCTION(new_dyna_start): sub x29, x16, #saved_context ldr x19, [x1] add w0, w0, #0x40 - bl new_recompile_block + bl GLABEL(new_recompile_block) ldr w20, [x29, #fp_cycle_count] br x19 -GLOBAL_FUNCTION(breakpoint): +GLOBAL_FUNCTION breakpoint brk 0 ret -END_SECTION \ No newline at end of file +END_SECTION diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c index 8b0df163..501079e1 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c @@ -28,6 +28,21 @@ #if defined(__APPLE__) #define MAP_ANONYMOUS MAP_ANON +#if defined(__aarch64__) +#include +#include +#include +#include +#include +static inline void jit_write_enable(void) { pthread_jit_write_protect_np(0); } +static inline void jit_write_disable(void) { pthread_jit_write_protect_np(1); } +#else +static inline void jit_write_enable(void) { } +static inline void jit_write_disable(void) { } +#endif +#else +static inline void jit_write_enable(void) { } +static inline void jit_write_disable(void) { } #endif #include "new_dynarec.h" @@ -321,7 +336,7 @@ static void clear_all_regs(signed char regmap[]) static signed char get_reg(signed char regmap[],int r) { int hr; - for (hr=0;hrstart+i*4) { // Disable recursion (for debugging) for(r=0;r>2].regmap_entry[r]) { will_dirty_i|=will_dirty[(ba[i]-start)>>2]&(1<>2]&(1<start+i*4) { // Disable recursion (for debugging) for(r=0;r>2].regmap_entry[r]) { will_dirty_i&=will_dirty[(ba[i]-start)>>2]&(1<33) will_dirty_i&=~(1<>16)!=0x1000) { for(r=0;r>r)&1));*/} @@ -1597,7 +1612,7 @@ static void clean_registers(int istart,int iend,int wr) { if(i>r)&1));*/} @@ -1612,7 +1627,7 @@ static void clean_registers(int istart,int iend,int wr) temp_will_dirty=will_dirty_i; temp_wont_dirty=wont_dirty_i; for(r=0;r=0) { @@ -1788,7 +1803,7 @@ static void alloc_all(struct regstat *cur,int i) int hr; for(hr=0;hrregmap[hr]&63)!=rs1[i])&&((cur->regmap[hr]&63)!=rs2[i])&& ((cur->regmap[hr]&63)!=rt1[i])&&((cur->regmap[hr]&63)!=rt2[i])) { @@ -2552,10 +2567,14 @@ void *dynamic_linker(void * src, u_int vaddr) //TODO: Avoid disabling link between blocks for conditional branches int *ptr=(int*)src_rw; if((*ptr&0xfc000000)==0x14000000) { //b + jit_write_enable(); add_link(vaddr, add_pointer(src_rw,head->addr)); + jit_write_disable(); } #else + jit_write_enable(); add_link(vaddr, add_pointer(src_rw,head->addr)); + jit_write_disable(); #endif return (void*)(((intptr_t)head->addr-(intptr_t)base_addr)+(intptr_t)base_addr_rx); } @@ -2610,10 +2629,14 @@ void *dynamic_linker_ds(void * src, u_int vaddr) //TODO: Avoid disabling link between blocks for conditional branches int *ptr=(int*)src_rw; if((*ptr&0xfc000000)==0x14000000) { //b + jit_write_enable(); add_link(vaddr, add_pointer(src_rw,head->addr)); + jit_write_disable(); } #else + jit_write_enable(); add_link(vaddr, add_pointer(src_rw,head->addr)); + jit_write_disable(); #endif return (void*)(((intptr_t)head->addr-(intptr_t)base_addr)+(intptr_t)base_addr_rx); } @@ -2803,6 +2826,7 @@ static void invalidate_page(u_int page) } head=jump_out[page]; jump_out[page]=0; + if(head!=NULL) jit_write_enable(); while(head!=NULL) { inv_debug("INVALIDATE: kill pointer to %x (%x)\n",head->vaddr,(intptr_t)head->addr); uintptr_t host_addr=(intptr_t)kill_pointer(head->addr); @@ -2816,6 +2840,7 @@ static void invalidate_page(u_int page) free(head); head=next; } + jit_write_disable(); } void invalidate_block(u_int block) @@ -3732,7 +3757,7 @@ static void wb_invalidate(signed char pre[],signed char entry[],uint64_t dirty,u { int hr; for(hr=0;hr=0) { if((dirty>>hr)&1) { @@ -3758,7 +3783,7 @@ static void wb_invalidate(signed char pre[],signed char entry[],uint64_t dirty,u } // Move from one register to another (no writeback) for(hr=0;hr=0&&(pre[hr]&63)0)&&((i_regmap[hr]&63)>hr)&1) { if(i_regmap[hr]<64) { @@ -3812,7 +3837,7 @@ static void wb_needed_dirtys(signed char i_regmap[],uint64_t i_is32,uint64_t i_d int hr; int t=(addr-start)>>2; for(hr=0;hr0)&&((i_regmap[hr]&63)>hr)&1) && !(((i_is32&~regs[t].was32&~unneeded_reg_upper[t])>>(i_regmap[hr]&63))&1)) { if((i_dirty>>hr)&1) { @@ -3849,7 +3874,7 @@ static void wb_sx(signed char pre[],signed char entry[],uint64_t dirty,uint64_t if(is32_pre==is32) return; int hr,reg; for(hr=0;hr=0) { if((dirty>>hr)&1) { @@ -3869,7 +3894,7 @@ static void wb_valid(signed char pre[],signed char entry[],u_int dirty_pre,u_int //if(dirty_pre==dirty) return; int hr,reg; for(hr=0;hr>(reg&63))&1) { if(((reg&63)>0)&&((reg&63)>hr)&1) { if(regmap[hr]<64) { @@ -3922,7 +3947,7 @@ static void store_regs_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirt int t=(addr-start)>>2; int hr; for(hr=0;hr0)&&((i_regmap[hr]&63)>hr)&1) || (((i_is32&~regs[t].was32&~unneeded_reg_upper[t])>>(i_regmap[hr]&63))&1)) { if((i_dirty>>hr)&1) { @@ -3974,7 +3999,7 @@ static void load_regs_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirty } // Load 32-bit regs for(hr=0;hr=0&®s[t].regmap_entry[hr]=0&®s[t].regmap_entry[hr]>hr)&1) && ((i_dirty>>hr)&1) && (((i_is32&~unneeded_reg_upper[t])>>i_regmap[hr])&1) ) || (((i_is32&~regs[t].was32&~unneeded_reg_upper[t])>>(i_regmap[hr]&63))&1)) { #else @@ -3992,7 +4017,7 @@ static void load_regs_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirty } //Load 64-bit regs for(hr=0;hr=64&®s[t].regmap_entry[hr]=64&®s[t].regmap_entry[hr]>(regs[t].regmap_entry[hr]&63))&1) { @@ -4033,7 +4058,7 @@ static void load_regs(signed char entry[],signed char regmap[],int is32,int rs1, int hr; // Load 32-bit regs for(hr=0;hr=0) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0) { if(entry[hr]!=regmap[hr]) { if(regmap[hr]==rs1||regmap[hr]==rs2) { @@ -4050,7 +4075,7 @@ static void load_regs(signed char entry[],signed char regmap[],int is32,int rs1, } //Load 64-bit regs for(hr=0;hr=0) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0) { if(entry[hr]!=regmap[hr]) { if(regmap[hr]-64==rs1||regmap[hr]-64==rs2) { @@ -4078,7 +4103,7 @@ static void load_consts(signed char pre[],signed char regmap[],int is32,int i) int hr; // Load 32-bit regs for(hr=0;hr=0) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0) { //if(entry[hr]!=regmap[hr]) { if(i==0||!((regs[i-1].isconst>>hr)&1)||pre[hr]!=regmap[hr]||bt[i]) { if(((regs[i].isconst>>hr)&1)&®map[hr]<64&®map[hr]>0) { @@ -4097,7 +4122,7 @@ static void load_consts(signed char pre[],signed char regmap[],int is32,int i) } // Load 64-bit regs for(hr=0;hr=0) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0) { //if(entry[hr]!=regmap[hr]) { if(i==0||!((regs[i-1].isconst>>hr)&1)||pre[hr]!=regmap[hr]||bt[i]) { if(((regs[i].isconst>>hr)&1)&®map[hr]>64) { @@ -4129,7 +4154,7 @@ static void load_all_consts(signed char regmap[],int is32,u_int dirty,u_int isco int hr; // Load 32-bit regs for(hr=0;hr=0&&((dirty>>hr)&1)) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0&&((dirty>>hr)&1)) { if(((isconst>>hr)&1)&®map[hr]<64&®map[hr]>0) { int value=constmap[i][hr]; if(value==0) { @@ -4143,7 +4168,7 @@ static void load_all_consts(signed char regmap[],int is32,u_int dirty,u_int isco } // Load 64-bit regs for(hr=0;hr=0&&((dirty>>hr)&1)) { + if(!IS_REG_EXCLUDED(hr)&®map[hr]>=0&&((dirty>>hr)&1)) { if(((isconst>>hr)&1)&®map[hr]>64) { if((is32>>(regmap[hr]&63))&1) { int lr=get_reg(regmap,regmap[hr]-64); @@ -4170,7 +4195,7 @@ static void load_all_regs(signed char i_regmap[]) { int hr; for(hr=0;hr=0) { if(i_regmap[hr]==0) { emit_zeroreg(hr); @@ -4252,7 +4277,7 @@ static void loop_preload(signed char pre[],signed char entry[]) { int hr; for(hr=0;hr=0) { if(get_reg(pre,entry[hr])<0) { @@ -4574,7 +4599,7 @@ static void do_ccstub(int n) int addr,alt,ntaddr; while(hrregmap[hr]&63)!=rs1[i] && (i_regs->regmap[hr]&63)!=rs2[i] ) { @@ -8223,7 +8248,7 @@ static void pagespan_assemble(int i,struct regstat *i_regs) } while(hrregmap[hr]&63)!=rs1[i] && (i_regs->regmap[hr]&63)!=rs2[i] ) { @@ -8235,7 +8260,7 @@ static void pagespan_assemble(int i,struct regstat *i_regs) { while(hrregmap[hr]&63)!=rs1[i] && (i_regs->regmap[hr]&63)!=rs2[i] ) { @@ -8656,7 +8681,18 @@ void new_dynarec_init(void) #define CACHE_ADDR FIXED_CACHE_ADDR #endif -#if CACHE_ADDR==DOUBLE_CACHE_ADDR +#if defined(__APPLE__) && defined(__aarch64__) + { + // macOS arm64: MAP_JIT single mapping with W^X toggling. + base_addr = mmap(NULL, 1<=0) { + if(!IS_REG_EXCLUDED(hr)&®s[i].regmap[hr]>=0) { if(regmap_pre[i][hr]!=regs[i].regmap[hr]) { regs[i].wasconst&=~(1<64) { if(!((regs[i].dirty>>hr)&1)) f_regmap[hr]=regs[i].regmap[hr]; @@ -11083,7 +11127,7 @@ int new_recompile_block(int addr) if(itype[i]==LOAD||itype[i]==LOADLR|| itype[i]==STORE||itype[i]==STORELR||itype[i]==C1LS) { for(hr=0;hr=0) break; @@ -11137,7 +11181,7 @@ int new_recompile_block(int addr) // Find highest score and allocate that register int maxscore=0; for(hr=0;hrscore[maxscore]) { maxscore=hr; //DebugMessage(M64MSG_VERBOSE, "highest score: %d %d (%x->%x)",score[hr],hr,start+i*4,start+end[hr]*4); @@ -11909,5 +11953,6 @@ int new_recompile_block(int addr) } expirep=(expirep+1)&65535; } + jit_write_disable(); return 0; } From ca0d717ebbb5eef27cc7b81b9d4c32c00393aebb Mon Sep 17 00:00:00 2001 From: Julio Restrepo Date: Sun, 3 May 2026 12:55:44 -0400 Subject: [PATCH 2/3] arm64 dynarec: fix trampoline register crash on macOS --- .../device/r4300/new_dynarec/arm64/assem_arm64.c | 13 +++++++++---- .../src/device/r4300/new_dynarec/new_dynarec.c | 10 ++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c index c95f22e4..a2477cb2 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/arm64/assem_arm64.c @@ -102,7 +102,7 @@ static const uintptr_t jump_vaddr_reg[32] = { (intptr_t)jump_vaddr_x15, (intptr_t)jump_vaddr_x16, (intptr_t)jump_vaddr_x17, - (intptr_t)breakpoint, /*trampoline jumps uses x18*/ + (intptr_t)breakpoint, /*x18 is platform-reserved (no jump_vaddr_x18 thunk)*/ (intptr_t)jump_vaddr_x19, (intptr_t)breakpoint, /*cycle count*/ (intptr_t)jump_vaddr_x21, @@ -4532,7 +4532,7 @@ static void do_miniht_jump(int rs,int rh,int ht) { intptr_t jaddr=(intptr_t)out; emit_jeq(0); if(rs==18) { - // x18 is used for trampoline jumps, move it to another register (x0) + // x18 has no jump_vaddr_x18 thunk; move it to x0 for dispatch. emit_mov(rs,0); rs=0; } @@ -4622,12 +4622,17 @@ static void arch_init(void) { while((char *)ptr<(char *)jump_table_symbols+sizeof(jump_table_symbols)) { int *ptr4=(int*)ptr2; + intptr_t target = *ptr; + /* x18 is platform-reserved on Apple arm64. + Default to x17 for trampolines, but preserve x17 when the target + itself is jump_vaddr_x17 (it consumes w17 as input). */ + int tramp_reg = (target == (intptr_t)jump_vaddr_x17) ? 16 : 17; intptr_t offset=*ptr-(intptr_t)ptr3; if(offset>=-134217728LL&&offset<134217728LL) { *ptr4=0x14000000|((offset>>2)&0x3ffffff); // direct branch }else{ - *ptr4=0x58000000|((8>>2)<<5)|18; // ldr x18,[pc,#8] - *(ptr4+1)=0xd61f0000|(18<<5); + *ptr4=0x58000000|((8>>2)<<5)|tramp_reg; // ldr xN,[pc,#8] + *(ptr4+1)=0xd61f0000|(tramp_reg<<5); // br xN } ptr2++; *ptr2=*ptr; diff --git a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c index 501079e1..f0e74820 100644 --- a/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c +++ b/mupen64plus-core/src/device/r4300/new_dynarec/new_dynarec.c @@ -4767,7 +4767,7 @@ static void do_ccstub(int n) } #if NEW_DYNAREC==NEW_DYNAREC_ARM64 if(r==18) { - // x18 is used for trampoline jumps, move it to another register (x0) + // x18 has no jump_vaddr_x18 thunk on arm64, move it to x0. emit_mov(r,0); r=0; stubs[n][2]=jump_vaddr_reg[0]; @@ -7283,7 +7283,7 @@ static void rjump_assemble(int i,struct regstat *i_regs) { #if NEW_DYNAREC==NEW_DYNAREC_ARM64 if(rs==18) { - // x18 is used for trampoline jumps, move it to another register (x0) + // x18 has no jump_vaddr_x18 thunk on arm64, move it to x0. emit_mov(rs,0); rs=0; } @@ -8644,7 +8644,7 @@ static void pagespan_ds(void) store_regs_bt(regs[0].regmap,regs[0].is32,regs[0].dirty,-1); #if NEW_DYNAREC==NEW_DYNAREC_ARM64 if(btaddr==18) { - // x18 is used for trampoline jumps, move it to another register (x0) + // x18 has no jump_vaddr_x18 thunk on arm64, move it to x0. emit_mov(btaddr,0); btaddr=0; } @@ -8869,6 +8869,7 @@ int new_recompile_block(int addr) else { assem_debug("Compile at unmapped memory address: %x ", (int)addr); //assem_debug("start: %x next: %x",g_dev.r4300.new_dynarec_hot_state.memory_map[start>>12],g_dev.r4300.new_dynarec_hot_state.memory_map[(start+4096)>>12]); + jit_write_disable(); return 1; // Caller will invoke exception handler } //DebugMessage(M64MSG_VERBOSE, "source= %x",(intptr_t)source); @@ -8876,7 +8877,8 @@ int new_recompile_block(int addr) else { //DebugMessage(M64MSG_VERBOSE, "Compile at bogus memory address: %x ", (int)addr); DebugMessage(M64MSG_ERROR, "Compile at bogus memory address: %x", (int)addr); - exit(1); + jit_write_disable(); + return 1; // Caller will invoke exception handler } /* Pass 1: disassemble */ From 87aa93712c725f7533418e5161bed56bd7325ec9 Mon Sep 17 00:00:00 2001 From: Julio Restrepo Date: Sun, 3 May 2026 12:55:44 -0400 Subject: [PATCH 3/3] fix(core): tighten register address masks and guard RDRAM bounds Narrow RI and RSP register index masks from 0xffff to the correct bit widths (0x1f / 0x7), preventing out-of-range array indexing. Add explicit bounds checks in rdram read/write paths so out-of-range register accesses emit a warning and return safely instead of crashing. --- mupen64plus-core/src/device/rcp/ri/ri_controller.h | 2 +- mupen64plus-core/src/device/rcp/rsp/rsp_core.h | 4 ++-- mupen64plus-core/src/device/rdram/rdram.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mupen64plus-core/src/device/rcp/ri/ri_controller.h b/mupen64plus-core/src/device/rcp/ri/ri_controller.h index 940fa80e..7cf2eac9 100644 --- a/mupen64plus-core/src/device/rcp/ri/ri_controller.h +++ b/mupen64plus-core/src/device/rcp/ri/ri_controller.h @@ -51,7 +51,7 @@ struct ri_controller static osal_inline uint32_t ri_reg(uint32_t address) { - return (address & 0xffff) >> 2; + return (address & 0x1f) >> 2; } static osal_inline uint16_t ri_address_to_id_field(uint32_t address) diff --git a/mupen64plus-core/src/device/rcp/rsp/rsp_core.h b/mupen64plus-core/src/device/rcp/rsp/rsp_core.h index 2d067cfb..ca48553e 100644 --- a/mupen64plus-core/src/device/rcp/rsp/rsp_core.h +++ b/mupen64plus-core/src/device/rcp/rsp/rsp_core.h @@ -111,12 +111,12 @@ static osal_inline uint32_t rsp_mem_address(uint32_t address) static osal_inline uint32_t rsp_reg(uint32_t address) { - return (address & 0xffff) >> 2; + return (address & 0x1f) >> 2; } static osal_inline uint32_t rsp_reg2(uint32_t address) { - return (address & 0xffff) >> 2; + return (address & 0x7) >> 2; } void init_rsp(struct rsp_core* sp, diff --git a/mupen64plus-core/src/device/rdram/rdram.c b/mupen64plus-core/src/device/rdram/rdram.c index 28065eaf..550ac084 100644 --- a/mupen64plus-core/src/device/rdram/rdram.c +++ b/mupen64plus-core/src/device/rdram/rdram.c @@ -162,6 +162,12 @@ void read_rdram_regs(void* opaque, uint32_t address, uint32_t* value) uint32_t reg = rdram_reg(address); size_t module; + if (reg >= RDRAM_REGS_COUNT) { + DebugMessage(M64MSG_WARNING, "Reading from unsupported RDRAM reg %08x", address); + *value = 0; + return; + } + if (address & RDRAM_BCAST_ADDRESS_MASK) { DebugMessage(M64MSG_WARNING, "Reading from broadcast address is unsupported %08x", address); return; @@ -188,6 +194,11 @@ void write_rdram_regs(void* opaque, uint32_t address, uint32_t value, uint32_t m size_t module; size_t modules = get_modules_count(rdram); + if (reg >= RDRAM_REGS_COUNT) { + DebugMessage(M64MSG_WARNING, "Ignoring write to unsupported RDRAM reg %08x", address); + return; + } + /* HACK: Detect when current Control calibration is about to start, * so we can set corrupted rdram_dram handler */