Skip to content

fix(libc): return the sign mask from __signbit, __signbitf and __signbitl - #11278

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-signbit-sign-mask
Oct 29, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-signbit-sign-mask

Conversation

@AlexFigas

@AlexFigas AlexFigas commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

__signbit, __signbitf and __signbitl returned 1 for a negative argument, following FreeBSD's signbit (#3640). The console's versions return the sign bit where it sits in the top 16-bit word of the value: 0x8000 for any argument whose sign bit is set, 0 otherwise. That is also what AnyPS5's _LDsign already returns.

A title that only tests for nonzero is unaffected. A title that compares the result, stores it, or shifts it saw a different value from the console's.

The evidence is the libc.prx bundled with PPSA21564, the library's own code:

Function NID Address Code
__signbit Rw4J-22tu1U 0x54880 vmovmskpd eax, xmm0; shl rax, 63; shr rax, 48
__signbitf CjQROLB88a4 0x54890 vmovmskps eax, xmm0; shl eax, 31; shr eax, 16
__signbitl Cj81LPErPCc 0x548a0 movzx eax, word [rsp+0x10]; and eax, 0x8000

NaNs follow their sign bit like every other value.

Tested

On Ubuntu 24.04 under WSL2 (GCC 13.3, Release), head def9214 (this PR rebased onto main a13ee2d):

  • Native comparison. I used the harness from fix(libc): compute acosl with FreeBSD msun's ld80 code as the bundled libc.prx does #11260, with that libc.prx's segments mapped into a Linux process, and ran the binary's three functions next to AnyPS5's. There are 0 mismatches with this PR:

    • __signbitf on all 4,294,967,296 float bit patterns;
    • __signbit on 20,000,000 random double encodings, half of them biased to zero, subnormal, infinite and NaN exponents;
    • __signbitl on 20,000,000 random 80-bit encodings, including unnormals, pseudo-denormals, pseudo-infinities and pseudo-zeros.

    On main, every negative input differs: 2,147,483,648 float patterns, 10,003,161 doubles and 10,002,538 long doubles.

  • guest_math passes (3 runs). Its __signbitl checks now expect 0x8000. New checks cover __signbit and __signbitf on signed zeros, a subnormal, infinity and NaNs of both signs.

  • Conventions. python3 tools/check_conventions.py --base origin/main passes.

  • Overlap. python3 tools/pr_overlap.py --branch fix/libc-signbit-sign-mask reports no line overlap. It could not compare 29 PRs that conflict with main; each gives the same git merge-tree conflicts with this branch as with main.

  • Stacking. The branch was stacked on fix(libc): return Dinkumware codes from __fpclassifyf #11277, because __signbitf is the line after the __fpclassifyf that PR rewrites. fix(libc): return Dinkumware codes from __fpclassifyf #11277 has merged, so only this PR's commit is left.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none (fix(libc): return Dinkumware codes from __fpclassifyf #11277 has merged)
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head d58ce8353d3a439117811643cc0cfc1fe6c0a3fb. The 0x8000 sign mask matches the bundled libc.prx instruction sequences described in the report, and the new guest cases cover positive/negative zero, finite values, infinities and NaNs across float, double and long double. The PR reports exhaustive comparisons against the bundled binary; I did not independently rerun that harness. Its dependency on #11277 is recorded. No blocking code finding. Policy and conventions checks pass; full product builds are skipped under the PR path policy.

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 6085 core/libs/prx/libc/src/Math.cpp
PR 6295 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 8937 core/libs/prx/libc/src/Math.cpp
PR 10253 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 10345 core/libs/prx/libc/src/Math.cpp
core/libs/tests/GuestMath.cpp
PR 11892 core/libs/tests/GuestMath.cpp

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found an unreported behavior change in the diff beyond the three sign-mask exports. __fpclassifyf_nid_postfix now returns _FDtest_nid_postfix's internal -2/-1/0/1/2 codes, and CheckFloatClassification was changed to expect those codes. Before this PR, the export mapped those categories to the project's FP_SUBNORMAL/FP_NORMAL/FP_ZERO/FP_INFINITE/FP_NAN values (0x08/0x04/0x10/0x01/0x02). That changes an independent public result for every classification and is not supported by the signbit disassembly or the reported differential tests, which compare only __signbit, __signbitf, and __signbitl.

Please restore the existing __fpclassifyf mapping in this signbit PR, or provide direct evidence for the console's __fpclassifyf return codes and update the scope/report and tests to cover that behavior. My earlier approval did not catch this collateral change; I am correcting it now.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to my immediately preceding review: I attributed the __fpclassifyf change to this signbit commit because GitHub shows the full stack diff against main. The exact commit diff confirms that the classification change is entirely in prerequisite #11277, whose PR provides the libc.prx disassembly and exhaustive float differential; this PR's commit only changes the three sign-mask exports and their tests. #11278 correctly records Depends on: #11277. The concern in my prior review was mistaken, and I withdraw it. The existing signbit evidence, test coverage and completed hosted matrix support approval.

@wachi35676

Copy link
Copy Markdown
Contributor

Windows run of head d58ce8353 merged onto main 0f34ebb85, WinLibs GCC 15.2.0, Release. The description's runs are under WSL.

AI-assisted: yes (Claude Code); results above were run on this machine.

@babyhuey

Copy link
Copy Markdown
Contributor

Windows run of this head d58ce8353d3, merged onto main 0a570f617d8 (clean merge, local only), with the selection CI's skipped Windows product job uses: Windows 11, Ryzen 7 5800X, RTX 3080, WinLibs GCC 15.2, Release, BUILD_TESTING=ON. The tree was configured fresh after the merge, then select_tests.py select / ninja -f select.ninja selected / --target libs / select_tests.py ctest --parallel 4 --timeout 120.

  • Selected 2547 tests: 1838 pass, 1 fails, guest_raise_exception, which also fails on 0a570f617d8 itself on this machine (crash or timeout under --parallel 4; it passed on 27ad7244d22, and I'm narrowing that down separately), 708 skip. No failures from this PR.

AI-assisted: yes (Claude Code)

@maskym

maskym commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Windows result for head d58ce8353d, merged into main 1089f2bbfd (the PR's Tested section names no Windows run).

Host: Windows 11 26300, Intel i7-12700K, WinLibs GCC 15.2.0 (x86_64-ucrt-posix-seh, r6), Release, BUILD_TESTING=ON, Ninja. long double is the 80-bit x87 type on this toolchain, as on the console.

The tree also carried the other open libc PRs #11260, #11262, #11266, #11632, #11634, #11270, #11283, #10929, all merged cleanly, so the run is a joint one; the tests of this PR are guest_math. Every test in the set passed: ctest --test-dir build -R "math|libc|...|stdio" 32/32 and ctest -R "guest_dinkumware_internals|guest_standard_streams" 5/5, guest_math in 0.05 s. Build clean. Not run on a PS5.

AI-assisted: yes (Claude Code), the runs and the numbers above are from this machine.

@JJenkx

JJenkx commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

A runtime cross-check on 11 more titles. A scratch loader runs each decrypted libc.prx natively on Windows 11 and calls __signbit (Rw4J-22tu1U), __signbitf (CjQROLB88a4) and __signbitl (Cj81LPErPCc) by NID. The titles are PPSA04158, 05916, 07923, 09614, 13934, 20423, 25636, 26528, 33655, 34008 and 35169.

All 11 export all three, and every one returns:

  • 0x8000 (32768): for -2.0, -0.0 and a negative NaN;
  • 0: for +2.0.

That matches the description's table. On PPSA07923, 200,000 random double and float arguments (a quarter of them infinities, NaNs, zeros or subnormals, with random signs) gave 0 or 0x8000 from the console every time. Current main gave 1 wherever the console gave 0x8000.

AI-assisted: yes (Claude Code).

@AlexFigas
AlexFigas force-pushed the fix/libc-signbit-sign-mask branch from d58ce83 to def9214 Compare October 28, 2026 11:46

@BunnyFl BunnyFl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving head def9214119. I read the diff and ran firmware 13.42's __signbit, __signbitf and __signbitl: the decrypted system libSceLibcInternal.sprx's exports, natively on a PC (not a console). Three identical runs.

For -1, -0, -inf, a negative NaN and the smallest negative subnormal, and for 1, 0, inf, a positive NaN and 1e300, all three return 0x8000 when the sign bit is set and 0 otherwise. That is what this change returns and what the new and updated test rows expect, so the system module agrees with the PPSA21564 libc.prx reading in the description.

@oneandonlydean
oneandonlydean merged commit 444407d into boykopovar:main Oct 29, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants