Apple Silicon (macOS arm64) dynarec port#632
Open
AurisDSP wants to merge 2 commits into
Open
Conversation
Brings the aarch64 dynarec online on macOS Apple Silicon. The host code ports cleanly with a handful of platform shims: * Build (Makefile): enable WITH_DYNAREC=aarch64 on osx for native arm64; keep -DNO_ASM only when no dynarec backend is in use. * Assembly (linkage_arm64.S): Apple's clang assembler treats ';' as a comment delimiter, so the GAS-style cpp macros that emit multiple directives on one line don't work. Switched GLOBAL_FUNCTION / LOCAL_FUNCTION / GLOBAL_VARIABLE to assembler-side .macro definitions on Apple, with an _-prefixed Mach-O symbol and .private_extern. Added an ADRP_LO12 macro that selects between :lo12: (GAS) and @PAGE/@PAGEOFF (Apple) syntax. Internal references to jump_vaddr changed to use the underscored Mach-O symbol; b.ne/b.eq new_dyna_stop now goes through a local L-label so Apple's assembler doesn't reject the conditional branch's external target. * JIT memory (new_dynarec.c): macOS Hardened Runtime forbids RWX in a single mapping, so on Apple we mmap the cache with MAP_JIT and toggle protection per-thread via pthread_jit_write_protect_np(). To handle set_jump_target() being called from inside new_recompile_block (where the page is already in write mode), the BEGIN/END pair nests via a per-thread depth counter; END clamps to zero so cache_flush() called from invalidation paths still lands the page in execute mode. * W^X helpers (new_dynarec.h): M64P_JIT_WRITE_BEGIN / M64P_JIT_WRITE_END macros wrap pthread_jit_write_protect_np with the nesting counter on Apple, no-ops elsewhere. * Cache flush (assem_arm64.c): on Apple, replaced the manual dc civac / ic ivau loop with sys_icache_invalidate() and a paired M64P_JIT_WRITE_END so freshly-emitted code is visible and executable before it's invoked. arch_init() and set_jump_target() now wrap their writes with M64P_JIT_WRITE_BEGIN / M64P_JIT_WRITE_END. * Portability (custom/dependencies): pngpriv.h's TARGET_OS_MAC branch pulls in <fp.h> (a Mac OS 9 header), so it's been excluded on modern macOS. zutil.h's MACOS branch redefines fdopen, which collides with Apple's stdio.h __memcpy_chk-style macros; skip the redefinition on __APPLE__. Mario Kart 64 boots and runs with audio + video on M-series hardware (cached_interpreter and dynamic_recompiler both work; angrylion + HLE RSP avoids paraLLEl-RDP's macOS mutex thread bug). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These don't address any specific reported bug, just hardening that fell out of an audit while looking at the Apple AudioConverter path. Same fixes apply to the non-Apple sinc-resampler branch. * malloc() return values are checked at init time; on failure the buffers are freed and reset to NULL so subsequent push paths can bail instead of dereferencing garbage. * aiDacrateChanged() returns early when the N64 reports a DAC rate of zero. The non-Apple resampler branch additionally guards the 44100.0 / GameFreq division with the same check, since GameFreq could still be left at zero by a concurrent reset. * aiLenChanged() truncates an oversized incoming chunk to fit MAX_AUDIO_FRAMES * 2 stereo frames before appending to the accumulation buffer. The previous clamp only handled the buffer fullness side, so a single push larger than the buffer capacity could memcpy past the end. * The audio_batch_cb drain loops now break on a zero return rather than spinning forever. RetroArch returns 0 when the host can't accept samples (paused, fast-forward window full); the previous code would hang the emulation thread. * Dropped a stale '#if 0' debug printf and fixed a 'vlaues' typo in a comment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
|
This will take some time to review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the aarch64 dynarec online on macOS Apple Silicon (M1/M2/M3/M4). Resolves #561.
The host code ports cleanly with a handful of platform shims:
Build (Makefile): enable
WITH_DYNAREC=aarch64onosxfor native arm64; keep-DNO_ASMonly when no dynarec backend is in use.Assembly (linkage_arm64.S): Apple's clang assembler treats
;as a comment delimiter, so the GAS-style cpp macros that emit multiple directives on one line don't work. SwitchedGLOBAL_FUNCTION/LOCAL_FUNCTION/GLOBAL_VARIABLEto assembler-side.macrodefinitions on Apple, with an_-prefixed Mach-O symbol and.private_extern. Added anADRP_LO12macro that selects between:lo12:(GAS) and@PAGE/@PAGEOFF(Apple) syntax. Internalb.ne new_dyna_stopwas rerouted through a localL-label so Apple's assembler doesn't reject the conditional branch's external target.JIT memory (new_dynarec.c): macOS Hardened Runtime forbids RWX in a single mapping, so on Apple we mmap the cache with
MAP_JITand toggle protection per-thread viapthread_jit_write_protect_np(). To handleset_jump_target()being called from insidenew_recompile_block(where the page is already in write mode), the BEGIN/END pair nests via a per-thread depth counter (`_Thread_local`); END clamps to zero socache_flush()called from invalidation paths still lands the page in execute mode.W^X helpers (new_dynarec.h): `M64P_JIT_WRITE_BEGIN` / `M64P_JIT_WRITE_END` macros wrap `pthread_jit_write_protect_np` with the nesting counter on Apple, no-ops elsewhere. The counter is `_Thread_local` since the underlying syscall is per-thread.
Cache flush (assem_arm64.c): on Apple, replaced the manual `dc civac` / `ic ivau` loop with `sys_icache_invalidate()` and a paired `M64P_JIT_WRITE_END`. `arch_init()` and `set_jump_target()` now wrap their writes with BEGIN/END.
Portability (custom/dependencies):
The second commit is hardening that fell out of an audit while looking at the Apple AudioConverter path — same fixes apply to the non-Apple sinc-resampler branch (NULL checks after malloc, zero-frequency guard, oversized-input truncation, drain-loop break on `audio_batch_cb` returning 0).
What works
What doesn't work yet (out of scope)
Some homebrew ROMs (e.g. BikeRace '98, Wet Dreams POM '99) hit a SIGSEGV inside generated code with `LR=1`-shaped corruption. The crash isn't macOS-specific — it reproduces on this branch's tree on Linux with the same recompiler — so it's a pre-existing aarch64 codegen edge case rather than something the macOS port introduced. Worth filing separately.
Tested on
Test plan