WIP: fix(runtime): compile host jumps and pause hints on macOS arm64 #477
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!477
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:mac/arm64-host"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
On a Mac with Apple silicon, the host's jump back to a waiting caller and its spin-wait pause do not compile, because
both are written for x86 only.
host/hos_setjmp.c'srecomp_sj_setjmp/recomp_sj_longjmpare x86-64 assembly, andhos_thread.cand the POSIXwindows.huse x86'spause(_mm_pause,__builtin_ia32_pause). A native arm64 macOSbuild stops there. The x86-64 route (Windows, Linux, and macOS under Rosetta) is fine and must stay as it is.
What changed
runtime:
host/posix/darwin_cpu.c(new). A Darwin arm64recomp_sj_setjmp/recomp_sj_longjmpin assembly.and the low 64 bits of v8–v15 (d8–d15). It also restores FPCR and FPSR, as the x86 jump restores MXCSR and the x87
control word.
SjSlot.buf): 0 the first time, then thelongjmpvalue (1 for 0).#error. It needs authenticated saved pointers, and nothing here strips or skips them.runtime:
host/posix/darwin_cpu.h(new). The two declarations (returns_twice,noreturn) anddarwin_cpu_pause():yieldon arm64, x86'spausebuiltin on x86-64.runtime:
host/hos_setjmp.c. Its two x86 bodies (Win64 and System V) are left as they are, inside#if !defined(__APPLE__) || !defined(__aarch64__), so Darwin arm64 takesdarwin_cpu.c's.runtime:
host/hos_thread.cincludes<emmintrin.h>only on x86-64, andpresent_pollspins withYieldProcessor()on macOS.
host/posix/windows.hmapsYieldProcessor()todarwin_cpu_pause()on macOS.Windows and Linux: unchanged. The new lines are under
__APPLE__, and their x86-64 objects are byte-identical tomain's (Measurements).tests:
tests/test_arm64_host.pywithtests/arm64_host_test.candtests/arm64_host_registers.S(new). Game-free:hos_setjmp.c,hos_thread.cand aYieldProcessor()caller with the real runtime headers (generatedas the build does);
return, with SP, the frame chain, LR, x18 and the floating-point state;
It runs on the Mac's native architecture by default;
--arch arm64 --arch x86_64runs both (x86_64 under Rosetta).--source <tree>tests another checkout;--outkeeps the objects.docs:
docs/development.md, "Darwin host CPU checks".Flags and settings
None in the game. The test takes
--arch,--source,--out,--opt,--ccand--cxx.Measurements
main:
5e072fc8c2adadba5ea610148be1c47800ba0cca(2026-11-14)this PR:
mac/arm64-host@56b5ed6b7ea93aefb8b454ba25e4c177f0dbda96Hardware: Apple M4 Max (16 CPUs, 64 GiB), macOS 26.7.1 (25G241), Apple clang 21.0.0,
-O2,-mmacosx-version-min=14.4.No GPU, no game
Scene: none (a portability fix; its proof is the test, as CONTRIBUTING's
fixrow allows)Method:
tests/test_arm64_host.py --arch arm64 --arch x86_64on this PR and againstmain's source (git archive 5e072fc8,--source), in two interleaved rounds (PR, main, PR, main). The first round kept its objects (--out). The x86-64objects of both sides were then compared byte for byte, and with
otool -tvVandotool -rv. Then the full game-freesuite on this PR, compared by test name with
main's on the same Mac.How to reproduce:
python -I -B tests/test_arm64_host.py --arch arm64 --arch x86_64, then the same with--source <a tree of main>.5e072fc856b5ed6bhos_setjmp.c,hos_thread.c, aYieldProcessor()caller#errormain'sTesting
Tested:
On an Apple M4 Max with macOS 26.7.1, game-free only, natively and under Rosetta, as above.
The test without
--arch, as the suite runs it: native arm64 only, exit 0.The full suite,
tests/run_all.py, 6 at once, on this PR, compared by test name withmainon the same Mac (therunner labels macOS
linux):tests/test_arm64_host.py(pass): no other check changed status,and the same 17 are skipped.
main's own on a Mac, which the macOS port's other pull requestsaddress:
test_appimage_package_retry,test_collect_wide,test_control,test_framegen_resources,test_game_layout,test_low_memory_notice,test_mem_gate,test_mem_gate_children,test_mirror_trim,test_module_ids,test_oot_state_tap,test_pad_layout,test_pcache_header_read,test_pipeline_cache_file,test_pipeline_cache_save,test_pipeline_play_cache,test_prof_symbols,test_program_cache_cleanup,test_program_cache_one_variant.Not tested:
main's, and the x86-64 objects builthere are byte-identical to
main's; that is Apple clang on macOS, not a Windows or Linux build.maindoes not build for macOS yet; the other macOS pull requests (#420) do the rest. Inparticular, the macOS build links
host/posix/darwin_cpu.cthrough the build-scripts pull request of the same wave(
build(macos): discover dual-architecture toolchains and form Mach-O commands), not through this one.to a caller that is still live, as on x86.
Needs other testers:
tests/run_all.py, expecting the same results as onmain(request to open).
python -I -B tests/test_arm64_host.py --arch arm64 --arch x86_64, expecting exit 0(request to open).
Screenshots
Nothing on screen.
Risk and rollback
register fixture sets and checks each one, rather than trusting the compiler's register choice.
main(byte-identical on macOS).LONGSHOT_SHADER_STAMP(nx2/build.py:1176-1182) changes.Reviewer notes
darwin_cpu.candtests/arm64_host_registers.S: which registers are saved, where in the slot, and howthe fixture proves each one comes back.
recomp_sj_setjmpmust stay declaredreturns_twicewhere it is called (darwin_cpu.h).host/(hos_svc.c's andnn_os_sync.c's spin loops,nn_video.c's SSE copy) are alreadyunder x86 guards on
main, so they are left alone.56b5ed6b(one commit). It ismacos/arm64-host'se5c4f8c9cherry-picked ontomainwithout a conflict(the same patch id), with the
Assisted-byandCo-Authored-Bytrailers of this rebase added.notes; nothing copied.
Issues
See #420 (the macOS port).
Checklist
For everyone
type(scope): description).main. Branched from5e072fc8; main has moved since, and it is rebased in one sweep before ready.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above). On macOS:526 passed, 17 skipped, and the same 19 failures as
mainon this Mac, named under Testing.requests are still to open.
the median reported. For a
fix: the test fails onmainand passes here, 2 interleaved rounds, both routes.third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.mainand passing here.Needs other testers.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.