Skip to content

fix(libc): classify x87 unnormals as denormal in _LDtest and _LDclass - #11283

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-ldtest-unnormals
Oct 28, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-ldtest-unnormals

Conversation

@AlexFigas

@AlexFigas AlexFigas commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

_LDtest and _LDclass called every 80-bit encoding with a nonzero, non-maximal exponent finite (−1). That includes the unnormals (integer bit clear) and the pseudo-zeros (zero mantissa). The console calls both denormal (−2).

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

_LDtest (A+1YXWOGpuo, 0x108a0) decides from the 16-bit words of the value:

  • If the exponent is 0x7fff, it returns NaN (2) when any of the 63 fraction bits is set, and infinite (1) otherwise.
  • Otherwise, if the integer bit (bit 63) is set, it returns finite (−1), whatever the exponent. That is why a pseudo-denormal is finite, as guest_math_exact already checks.
  • Otherwise it returns zero (0) when the exponent and the whole mantissa are zero, and denormal (−2) for everything else.

_LDclass (hdcGjNpcr4w, 0x54660) copies its argument with fld/fstp and returns _LDtest of the copy, sign-extended.

The console's __fpclassifyl (zwV79ZJ9qAU, 0x54830) is separate code that never reads the integer bit:

  • zero exponent: 0 or −2 by the mantissa;
  • 0x7fff: 1 or 2 by the fraction;
  • anything else: −1.

AnyPS5's __fpclassifyl already returned exactly that, but through TestExtended. Now that TestExtended follows _LDtest, __fpclassifyl classifies by the exponent itself, so its results are unchanged. __isinfl and __isnanl only compare TestExtended with infinite and NaN, which this does not touch.

Tested

On Ubuntu 24.04 under WSL2 (GCC 13.3, Release), head 8130d34 on main 1089f2b unless noted:

  • Native comparison, rerun against this head's libc.prx. 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. It ran the binary's _LDtest, _LDclass, __fpclassifyl, __isinfl and __isnanl next to AnyPS5's on 20,000,000 random 80-bit encodings. Three quarters of them were biased towards zero and all-ones exponents, a clear integer bit, short mantissas, zero mantissas and lone integer bits.
    • On main: _LDtest and _LDclass differ on 11,228,922 of the encodings, and the other three on none.
    • With this PR: all five match on every encoding.
  • guest_math_exact passes. CheckClassification gains three non-canonical encodings: an unnormal 0x4000000000000000:3fff, a negative pseudo-zero 0:8001, and the largest unnormal 0x7fffffffffffffff:7ffe. For each it requires _LDtest and _LDclass to return −2, and __fpclassifyl and __isnormall to return −1 and 1, as the binary does. The test aborts on main.
    • The values come from the same Extended() helper as the existing pseudo-denormal, which main now marks [[gnu::noinline]] (fix(tests): keep guest_math_exact's pseudo-denormal out of constant folding #10888, 38a570a) so AppleClang does not fold its result into a canonical value. This head includes that change, and guest_math_exact passes 3 runs on it. None of the hosted macOS jobs builds guest_math_exact, so I ran it with a fork-only workflow on GitHub's macos-15-intel runner (x86_64, Apple clang 17.0.0, clang-1700.0.13.5, Release):
      • At this head plus only the workflow commit (fd9cad5), guest_math_exact passes 3 runs (run).
      • With this PR's test and main's MathExact.cpp (43de474), it aborts (run), so the three encodings reach the classifier unchanged under AppleClang.
      • guest_math aborts on that runner at main 1089f2b too, with only the workflow added (run), so that abort is not from this PR.
  • guest_math passes (3 runs).
  • Conventions. python3 tools/check_conventions.py --base origin/main passes.
  • Overlap. python3 tools/pr_overlap.py --branch fix/libc-ldtest-unnormals reports no overlap. feat(libc): add Dinkumware float bit helpers from firmware #8397 and feat(libc): implement _LExp #10780 also touch MathExact.cpp or GuestMathExact.cpp, and git merge-tree shows no conflict with either.

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
  • 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 85b823e078ab88e33a31bfec5248df7f7df5ec45. The production split matches the stated behavior: TestExtended now treats noncanonical finite exponent/integer-bit encodings like the console _LDtest, while __fpclassifyl retains its separate exponent-based behavior. The remaining blocker is a test prerequisite: the PR says these noncanonical-value checks need #10888 because AppleClang folds Extended() into a canonical long double; #10888 is still open, and current main’s helper has no noinline attribute. Yet this checklist says Depends on: none. Please list #10888 as a prerequisite or make the test construction self-contained in this PR, and verify the exact resulting head on macOS. The author reports the 20-million-encoding comparison and Linux focused tests; I have not independently rerun that binary harness. Hosted policy/conventions pass, Ubuntu Debug/Release register-context pass, and cross-platform relinker/ASan plus macOS checks are still pending; Linux/Windows full product jobs are skipped.

@wachi35676

Copy link
Copy Markdown
Contributor

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

  • guest_math_exact passes on Windows. With main's MathExact.cpp it aborts (0xc0000409).
  • TestExtended has no other callers besides the five you list, and __fpclassifyl's new exponent-only code gives the same answer as main's for every encoding class, so only _LDtest/_LDclass change.

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

@github-actions github-actions Bot added the libc label Oct 26, 2026
@MarcZX10

Copy link
Copy Markdown
Contributor

The main code change and the Windows/Linux reports still support the _LDtest behavior change. The outstanding prerequisite is unchanged: the added noncanonical-value regression uses Extended() in a way that the PR says AppleClang may constant-fold, and the stated fix is in #10888, which remains open. The checklist still says Depends on: none. Please record #10888 as a dependency (or make this test construction independent of it) and rerun guest_math_exact on macOS after that change; hosted CI does not execute the macOS product test.

@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 #10888 is merged now (bb61ff9), so main has the [[gnu::noinline]] Extended() helper and Depends on: none holds. This PR merges cleanly into main 5b5d24c, and that merge passes guest_math_exact (3 runs) and guest_math on Ubuntu 24.04 under WSL2 (GCC 13.3, Release). I have no Mac, so I can't run it on macOS, and the hosted macOS jobs don't run guest_math_exact. The description is updated.

@AlexFigas
AlexFigas requested a review from MarcZX10 October 26, 2026 11:09
@babyhuey

Copy link
Copy Markdown
Contributor

Windows run of this head 85b823e078a, merged onto main 27ad7244d22 (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 2487 tests: 1786 pass, 0 fail, 701 skip. No failures. 27ad7244d22 itself skips 701 of its selection on this machine too; two of the skips are machine-local: pad_sdl because a controller is plugged in, and guest_fopen_errors because symlinks need Developer Mode.

AI-assisted: yes (Claude Code)

@adriadam10 adriadam10 self-assigned this Oct 27, 2026

@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.

The classification change is narrowly scoped, and the added cases exercise an unnormal, negative pseudo-zero, and largest unnormal against the bundled module's reported results. I am not approving the submitted head yet: its base is acb900c, and current main is 3fea2a9. Main has since changed GuestMathExact.cpp, including the macOS pseudo-denormal fixture adjustment; that is the test file this PR extends.

Please rebase onto current main and rerun guest_math_exact on the rebased tree (including the available macOS coverage). I found no separate implementation defect in MathExact.cpp, which has not changed on main since the PR base; this request is to validate the updated test integration before approval.

@AlexFigas
AlexFigas force-pushed the fix/libc-ldtest-unnormals branch from 85b823e to 8130d34 Compare October 27, 2026 13:40
@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Rebased onto main 1089f2b (8130d34), which includes main's GuestMathExact.cpp change, the [[gnu::noinline]] Extended() from #10888. On that head, guest_math_exact and guest_math pass 3 runs each, and the 20,000,000-encoding comparison against the bundled libc.prx, rerun with this head's build, still matches _LDtest, _LDclass, __fpclassifyl, __isinfl and __isnanl on every encoding.

macOS: I have no Mac, and none of the hosted macOS jobs (relinker, register-context, write-watch) builds guest_math_exact, so it has not run there. The description is updated.

@maskym

maskym commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Windows result for head 8130d344ea, merged into main 75f3702aed (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 open libc PRs #11270, #11632, #11634, all merged cleanly, so the run is a joint one; the test of this PR is guest_math_exact. ctest --test-dir build -R "^(guest_math|guest_math_exact|guest_dinkumware_internals)$": 3/3 passed, guest_math_exact in 0.04 s. An earlier head of this PR passed the same test on main 1089f2bbfd together with #11260, #11262, #11266, #11278 and #10929. Build clean. Not run on a PS5.

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

@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 reviewed the current head 8130d34. The production change preserves the separate exponent-based behavior of __fpclassifyl while making TestExtended match the bundled _LDtest treatment of unnormals and pseudo-zeros. I found no separate logic defect.

Independent focused check: I applied the PR's two changed files to the clean #11213-based Release checkout, rebuilt guest_math_exact_tests, and ran guest_math_exact three times; all passed. This reproduces the Linux test on the PR's 1089f2b base plus unrelated #11213 changes. A current-main merge preview is clean, but the PR branch is still 30 commits behind current main, and the report does not include an AppleClang run of guest_math_exact. The PR itself notes that none of the hosted macOS jobs builds this target; the new noncanonical long double fixtures were added specifically because AppleClang can canonicalize them.

Please update the branch to current main and provide a run of the exact guest_math_exact cases under AppleClang (or wire this target into available macOS CI). The current hosted macOS checks do not establish that these encodings survive the compiler on that platform.

@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 reviewed the current head 8130d34. The classifier split matches the bundled _LDtest behavior while preserving __fpclassifyl's separate exponent-based behavior; I found no logic defect.

Independent Linux check: applied the PR's MathExact.cpp and GuestMathExact.cpp changes to the clean #11213-based Release checkout, rebuilt guest_math_exact_tests, and ran guest_math_exact three times; all passed. I did not test under AppleClang. Current main's merge preview is clean, and the files this PR edits have not changed since its 1089f2b base, so the earlier branch-currency concern is resolved.

The remaining validation gap is specific to this test: the PR says its noncanonical long-double fixtures need the noinline construction fix for AppleClang, but no available macOS CI job builds guest_math_exact. Please run these cases with AppleClang or add this target to macOS CI before approval.

@JJenkx

JJenkx commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

A runtime differential of this head against the libc.prx that titles ship.

Setup:

  • A scratch loader runs the decrypted libc.prx of four titles natively on Windows 11: PPSA07923, PPSA04158, PPSA13934 and PPSA34008, the last two FMA builds.
  • It calls _LDtest (by pointer) and _LDclass (by value) by NID and compares them with the same exports of the libc.prx DLL built from this PR's exact head 8130d344ea (WinLibs GCC 15.2.0 UCRT r7, Release). guest_math passes on that build.
  • There are 200,000 80-bit values per function per title, generated from random mantissas and exponents. One in five has the integer bit cleared (unnormals and pseudo-denormals), one in five has the maximum exponent, one in five a zero exponent, and one in five an exponent near the bias.

Results:

  • Current main differs from PPSA07923 on 80,083 (_LDtest) and 79,551 (_LDclass) of them. The titles return -2 where main returns -1, for values with a nonzero exponent and the integer bit clear, such as exponent 0x3a46 with mantissa 0x0c7f….
  • This PR differs on none, for both functions, on all four titles.

AI-assisted: yes (Claude Code).

@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 AppleClang run of the exact guest_math_exact cases. No hosted macOS job builds that target, so this is a fork-only workflow on GitHub's macos-15-intel runner (x86_64, Apple clang 17.0.0, clang-1700.0.13.5, Release):

  • Head 8130d34 plus only the workflow commit fd9cad5: guest_math_exact passes 3 runs (run).
  • Control, this PR's test with main's MathExact.cpp (43de474): it aborts (run). So the three non-canonical encodings reach _LDtest and _LDclass unchanged under AppleClang.
  • guest_math also aborts on that runner, and so does main 1089f2b with only the workflow added (run). That abort does not come from this PR.

The Tested section now lists these runs.

@AlexFigas
AlexFigas requested a review from MarcZX10 October 27, 2026 18:07

@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.

The earlier AppleClang validation gap is now addressed. The author ran the exact guest_math_exact cases on a macOS 15 Intel runner with Apple clang 17: three runs passed on this head, while the control built with main's MathExact.cpp aborts on the same noncanonical encodings. The unrelated guest_math abort also reproduces on main. This is useful evidence for the compiler-specific fixture concern I raised.

My focused GNU build and repeated guest_math_exact runs passed in the earlier review; the current-main merge preview is clean and hosted checks are green, with the Linux product job skipped. I found no remaining blocker in the x87 classification change. Approved.

@adriadam10
adriadam10 merged commit aa77c9a into boykopovar:main Oct 28, 2026
20 of 21 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the diff against the bundled _LDtest behaviour described in the PR, the new unnormal/pseudo-zero cases in guest_math_exact (they fail on main), __fpclassifyl keeping its exponent-based results, and the Windows/Linux/AppleClang runs reported in the thread; merged.

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.

7 participants