[Bug 60453] New: ARM64: user-mode callbacks overwrite FPCR with FPSR (rounding mode lost, FIZ/AH set on FEAT_AFP CPUs)
http://bugs.winehq.org/show_bug.cgi?id=60453 Bug ID: 60453 Summary: ARM64: user-mode callbacks overwrite FPCR with FPSR (rounding mode lost, FIZ/AH set on FEAT_AFP CPUs) Product: Wine Version: 11.19 Hardware: aarch64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: ntdll Assignee: wine-bugs@list.winehq.org Reporter: micronenator@gmail.com Target Milestone: --- Distribution: --- Created attachment 82273 --> http://bugs.winehq.org/attachment.cgi?id=82273 reproducer and speculative patch On ARM64, user-mode callbacks (e.g. window procedures called from win32u during CreateWindow, SetWindowPos, DestroyWindow) appear to corrupt the thread's floating-point state: after the callback returns, FPCR contains the old FPSR value and FPSR is 0. Cause, as far as I can tell, dlls/ntdll/unix/signal_arm64.c, call_user_mode_callback(): "mrs x1, fpcr\n\t" "mrs x2, fpsr\n\t" "bfi x1, x2, #0, #32\n\t" ... "stp x1, x2, [x29, #0xb0]\n\t" user_mode_callback_return() restores FPCR from the low half and FPSR from the high half: "ldp x5, x6, [x29, #0xb0]\n\t" "msr fpcr, x5\n\t" "lsr x5, x5, #32\n\t" "msr fpsr, x5\n\t" The bfi in call_user_mode_callback() inserts FPSR into bits 0-31, overwriting FPCR, instead of into bits 32-63. Apparent fix (makes both test cases below pass): - "bfi x1, x2, #0, #32\n\t" + "bfi x1, x2, #32, #32\n\t" Effects: 1. Seen on ARM64 with and without FEAT_AFP: FPCR settings made before CreateWindow (rounding mode, FZ, DN) are reset, and the FPSR exception flags are cleared. 2. Seen on a CPU with FEAT_AFP (ARMv8.7): the FPSR flags land on the FEAT_AFP control bits. IOC (bit 0) becomes FPCR.FIZ (flush denormal inputs to zero), DZC (bit 1) becomes FPCR.AH. After a callback that started with IOC set, the thread runs with FIZ on and denormal inputs read as zero. Whether that happens depends on which flags happen to be set, so it varies between runs. Real-world impact: a Windows ARM64 Unity game on a FEAT_AFP CPU shows wrong behaviour in some launches, because float comparisons that rely on denormals fail while FIZ is on. Test cases (attached), build with llvm-mingw: aarch64-w64-mingw32-clang -O0 -o X.exe X.c -luser32 fpstate_repro.c: set FPCR to DN | FZ | round toward +inf (0x03400000) and FPSR to IOC, call CreateWindow, read both back. Not FEAT_AFP specific. fiz_repro.c: set FPSR to IOC, call CreateWindow, check in the window procedure whether a denormal input reads as zero. Only meaningful on FEAT_AFP CPUs; on others it prints SKIP. Results (fiz_repro FAIL: denormal flushed in 1 of 5 window procedure calls): fpstate_repro fiz_repro Wine master eba8937, Cortex-A76 FAIL SKIP + fix OK SKIP Wine master eba8937, FEAT_AFP CPU FAIL FAIL + fix OK OK Proton 11.0 ARM64, FEAT_AFP CPU FAIL FAIL + fix (binary-patched ntdll.so) OK OK (Cortex-A76 has no FEAT_AFP.) fpstate_repro output on master: fpcr: expected 03400000, got 00000000 fpsr: expected IOC (bit 0) still set, got 00000000 FAIL Not tested: Wine's own test suite with the change, callback paths other than the window messages above, and ARM64 machines other than the two above. The same bfi is present in wine-9.0 and wine-10.0. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla