Skip to content

fix(libc): compute _LLog with the bundled libc.prx's Dinkumware code - #11270

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-llog-dinkumware
Oct 30, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
AlexFigas:fix/libc-llog-dinkumware

Conversation

@AlexFigas

@AlexFigas AlexFigas commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

_LLog(x, base) dispatched to the host logl/log10l/log2l. On 4 million random arguments, those differ from the console's result on 869,197, 2,000,696 and 306,649 respectively; glibc's log10l(1000), for example, is not exactly 3 while the console's is. This PR ports the _LLog of the libc.prx that PPSA21564 bundles (0x3fd50, the library's own code). That function is Dinkumware's xlog; the same binary's logl, log10l and log2l are one-call wrappers passing 0, 1 and −1.

What the binary does, and what the port reproduces:

  • Classify with _LDunscale (0x24e0). An all-ones exponent is NaN or ∞. A zero mantissa counts as zero whatever the exponent, so pseudo-zeros do too. Otherwise the mantissa is normalized into [0.5, 1) with exponent max(e, 1) - shift - 0x3ffe, which also covers denormals and unnormals.
  • Special values. A zero gives _Feraise(FE_DIVBYZERO) (ERANGE) and −∞. NaN and +∞ return x. A negative argument, −∞ included, gives _Feraise(FE_INVALID) (EDOM) and the positive quiet NaN 0xc000000000000000:7fff.
  • Reduce. If the mantissa is below √½ (0xb504f333f9de6484:3ffe), double it and decrement the exponent. Then y = x − 1, z = y/(x + 1), w = z², p = _LPoly(w) over the 8 coefficients at 0x15ea90 (Horner from the first), and q = z*(y − p*w).
  • Combine. It uses split constants: an 80-bit low part plus a float high part, exactly as the binary groups them.
    • Base 0: (y − (q + c₁e)) + e·c₂.
    • Base > 0: ((e₂y − (qL + d₁e)) + y·f₁) + e·c₃.
    • Base < 0: ((q·k + g·y) + y·h) + e.

_Feraise (0x11b00) is why log(0) sets ERANGE and negative arguments set EDOM.

x87 precision. The binary's _LLog, and the _LDunscale and _LPoly it calls, have no instruction that reads or sets the x87 control word (no fnstcw, fldcw, fldenv or fninit), so it computes at the caller's precision and leaves the control word as it found it. The port does the same and needed no change for it: its results match the binary's at 64, 53 and 24 bits (below).

The _LLog debt entry now records the evidence and what remains: it runs at the caller's precision, so a guest thread that starts at 53 bits (the thread-start gap the asinl entry records) gets the binary's 53-bit result, not the console's 64-bit one; and logl/log10l/log2l still call the host. Wiring those up would touch the long-double debt line that #11260–#11266 already edit, so it is left for a follow-up.

Tested

On Ubuntu 24.04 under WSL2 (GCC 13.3, Release), head 7b6c2e8 on main 1b580f1:

  • Native comparison, against _LLog_nid_postfix in 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 the .text, read-only, RELRO and .data segments mapped. The __stack_chk_guard and __error GOT entries point at host variables, so the binary's _Feraise path runs for real. The port matched the binary's 80-bit result, errno and FE_INVALID/FE_DIVBYZERO flags on every case, 0 mismatches, at each of 64-, 53- and 24-bit precision (control words 0x37f, 0x27f and 0x07f, set before both are called):
    • 4,000,000 random arguments covering denormals, every exponent and the [0.5, 2) neighbourhood;
    • 80 edge encodings: zeros, pseudo-zeros, ±∞, NaNs, √½ ± 1 ulp, and the extreme exponents;
    • each under bases 0, 1, −1, 7 and −9.
  • guest_dinkumware_internals passes (3 runs on this head, as does guest_math). It gains TestLongLog: 22 known-answer pairs from that binary, including six where the host differs. Examples are log10(1000) = 3 exactly and 0xecad15fd3a0eaf66:3ffd → 0xc589acfa7c6c1534:bffe under base 0, where the host gives …35. It also checks −∞, ERANGE and FE_DIVBYZERO for four zero encodings, and the positive NaN, EDOM and FE_INVALID for −1, −denormal and −∞. TestLongLogAt53Bits runs seven of the pairs at 53 bits under bases 0, 1 and −1: each must give the binary's 53-bit result (for example log(8) is 0x851591f9dd5b9800:4000 instead of 0x851591f9dd5b9b41:4000) and leave the control word unchanged. The existing _LLog checks still pass, and so do guest_math and guest_math_exact.
  • Conventions. python3 tools/check_conventions.py --base origin/main passes.
  • Overlap. python3 tools/pr_overlap.py --branch fix/libc-llog-dinkumware reports fix(libc): parse scaled NaN subjects without numeric conversion #12659, which inserts a new entry directly above the _LLog line this PR rewrites. This PR was approved first, so it lands first, and I asked on fix(libc): parse scaled NaN subjects without numeric conversion #12659 for it to follow (Depends on: #11270). fix(libc): match title libc.prx for hexadecimal strtod underflow #12291, which rewrote the _Stod line above, has merged, and this branch is rebased onto it. fix(libc): parse scaled NaN subjects without numeric conversion #12659 now declares Depends on: #11270. The 25 pull requests the tool could not compare give the same git merge-tree conflicts with this branch as with main. feat(libc): implement _Exp and _FExp #10786 and docs(debt): cut the long-double Dinkumware entry to _LLog with the bundled libc.prx code #11217, which touched the same code earlier, have merged.

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): match title libc.prx for hexadecimal strtod underflow #12291 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.

The port's main value paths are carefully compared against the bundled binary, but the implementation and its regression test do not enforce the x87 precision requirement that the PR itself records in docs/dev/debt/libc.md. _LLog performs its normalization, division and polynomial arithmetic as long double; on x87, a caller's 53-bit precision-control setting can round those intermediates before the final 80-bit result is stored. The reported harness comparison appears to use its default control word, and TestLongLog never changes or verifies the x87 control word. Please add a scoped 64-bit-precision environment around the calculation, restoring the caller's original control word on every return, and add a regression that invokes representative cases under 53-bit precision and checks restoration. If the console contract instead preserves the caller's precision mode, please provide evidence for that and test the expected results under both modes. Policy/convention checks pass; hosted product jobs are still pending or skipped, and I have not independently run the author's local comparison.

@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 5021 docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 7081 docs/dev/debt/libc.md
PR 7386 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 9340 docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9688 docs/dev/debt/libc.md
PR 10226 docs/dev/debt/libc.md
PR 10229 docs/dev/debt/libc.md
PR 10247 docs/dev/debt/libc.md
PR 10317 docs/dev/debt/libc.md
PR 10324 docs/dev/debt/libc.md
PR 10405 docs/dev/debt/libc.md
PR 10620 docs/dev/debt/libc.md
PR 11332 docs/dev/debt/libc.md
PR 11625 docs/dev/debt/libc.md
PR 11724 docs/dev/debt/libc.md
PR 11726 docs/dev/debt/libc.md
PR 12057 docs/dev/debt/libc.md
PR 12292 docs/dev/debt/libc.md
PR 12468 docs/dev/debt/libc.md
PR 12514 docs/dev/debt/libc.md
PR 12560 docs/dev/debt/libc.md
PR 12597 docs/dev/debt/libc.md
PR 12598 docs/dev/debt/libc.md
PR 12627 docs/dev/debt/libc.md
PR 12688 docs/dev/debt/libc.md
PR 12700 docs/dev/debt/libc.md
PR 12965 docs/dev/debt/libc.md

@wachi35676

Copy link
Copy Markdown
Contributor

Windows run of head fa6506405 merged onto main 0f34ebb85, WinLibs GCC 15.2.0, Release. Your runs were under WSL.

  • guest_dinkumware_internals passes 3/3. With main's DinkumwareInternals.cpp it fails at _LLog failed for fa00000000000000:4008 base 1, the log10(1000) case.
  • Because the port depends on 64-bit x87 precision (as the debt entry says), I checked threads on Windows: the x87 control word is 0x037f on the main thread and on a fresh std::thread, and your full TestLongLog passes on both.

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

@github-actions github-actions Bot added libc docs needs rebase Conflicts with main, rebase needed labels Oct 26, 2026
@AlexFigas
AlexFigas force-pushed the fix/libc-llog-dinkumware branch from fa65064 to dcb89d6 Compare October 26, 2026 11:04
@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 The console keeps the caller's precision here too, so I added evidence and tests at both precisions instead of forcing 64 bits:

  • The bundled _LLog (0x3fd50), and the _LDunscale and _LPoly it calls, have no instruction that reads or sets the x87 control word. It computes at the caller's precision and leaves the control word as it was. The same holds for the msun functions; see fix(libc): compute acosl with FreeBSD msun's ld80 code as the bundled libc.prx does #11260 (fix(libc): compute acosl with FreeBSD msun's ld80 code as the bundled libc.prx does #11260 (comment)).
  • The harness now sets the control word before both calls. 4,000,000 random arguments under bases 0, 1, −1, 7 and −9 give the binary's bits, errno and flags at 64-, 53- and 24-bit precision, with no change to the port.
  • TestLongLogAt53Bits runs seven of the known-answer inputs at 53 bits under bases 0, 1 and −1. Each must give the binary's 53-bit result (log(8) is 0x851591f9dd5b9800:4000 there, against …9b41 at 64 bits) and leave the control word unchanged.
  • The debt entry now says _LLog runs at the caller's precision, and what that gives on a 53-bit guest thread.

I also rebased onto main 5b5d24c to clear the conflict with the merged #10786, taking the union of both PRs' headers; nothing else changed. guest_dinkumware_internals, guest_math and guest_math_exact pass, 3 runs each. The head is dcb89d6.

@wachi35676 Thanks for the Windows thread check. AnyPS5 starts guest threads on Windows with _beginthreadex directly (StartNativeThread in libkernel's Thread.cpp), not through std::thread, and the asinl entry reports 53 bits for those. Either way, _LLog now matches the binary at both precisions.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@AlexFigas
AlexFigas requested a review from MarcZX10 October 26, 2026 11:09
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 27, 2026
@adriadam10 adriadam10 self-assigned this Oct 27, 2026
@adriadam10
adriadam10 force-pushed the fix/libc-llog-dinkumware branch from dcb89d6 to ecd40b3 Compare October 27, 2026 07:40
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 27, 2026
@adriadam10
adriadam10 dismissed MarcZX10’s stale review October 27, 2026 09:07

Dismissing a stale review: the requested changes were addressed after it (new head) and the merge queue blocks on it. Please request changes again if anything is still open.

@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 _LLog port has unusually strong reference evidence in the PR description, including results, errno, and exception flags across multiple control words. I am not approving the current submitted ref yet: its merge-base with main is 8df2c3e, while the Tested section says the run was on main 5b5d24c. Current main is 3fea2a9, and docs/dev/debt/libc.md has changed since the submitted base.

Please update the branch and debt entry against current main, and reconcile the test report with the exact submitted head. The implementation and test files have not changed on main since this base, so I found no separate code regression; this is a branch-currency and evidence-to-head issue required before approval.

@AlexFigas
AlexFigas force-pushed the fix/libc-llog-dinkumware branch from ecd40b3 to 4edd28e Compare October 27, 2026 13:44
@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Rebased onto main 1089f2b (4edd28e); the debt entry applies to current libc.md unchanged. The Tested section now describes this exact head:

  • guest_dinkumware_internals passes 3 runs.
  • The reference comparison now calls _LLog_nid_postfix from this head's libc.prx instead of a copy of the port. Against the bundled binary it gives 0 mismatches in result, errno and flags for 4,000,000 random arguments plus 80 edge encodings under bases 0, 1, −1, 7 and −9, at each of 64-, 53- and 24-bit precision.

Depends on now reads none, since #11217 has merged.

@maskym

maskym commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Windows result for head 4edd28ebb4, 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 #11283, #11632, #11634, all merged cleanly, so the run is a joint one; the test of this PR is guest_dinkumware_internals. ctest --test-dir build -R "^(guest_math|guest_math_exact|guest_dinkumware_internals)$": 3/3 passed, guest_dinkumware_internals in 0.05 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 4edd28e. The normalization, zero/negative handling, split constants, and base selection are consistent with the behavior described from the bundled _LLog. I found no separate implementation defect.

Independent focused check: I applied this head's DinkumwareInternals.cpp, GuestDinkumwareInternals.cpp, and debt changes to the clean #11213-based Release checkout, rebuilt guest_dinkumware_internals_tests, and ran guest_dinkumware_internals three times; all passed. The source and test files this PR changes have not changed on current main since the PR's 1089f2b base, and a merge preview with current main is clean. This supports the submitted results, but does not satisfy the repository's current-main branch requirement: the PR branch is still 30 commits behind the current main tip.

Please rebase/update the branch onto current main and let the checks run on that resulting head. The earlier evidence-to-head concern is resolved by the updated report and the focused local run; the remaining request is branch currency before approval.

@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 4edd28e. The _LLog implementation's normalization, polynomial evaluation, exceptional-value behavior, split constants, and base selection match the behavior described from the bundled binary. I found no specific implementation defect.

Independent local check: applied this PR's DinkumwareInternals.cpp, GuestDinkumwareInternals.cpp, and debt changes to a clean #11213-based Release checkout, built guest_dinkumware_internals_tests, and ran guest_dinkumware_internals three times; all passed. The current-main merge preview is clean, and main has not changed the files this PR touches since its 1089f2b base. The additional multi-precision, errno, flags, and native binary comparisons in the PR description are author-reported; I did not rerun that harness.

I approve this implementation and focused test evidence.

@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 PPSA07923 and PPSA05916 natively on Windows 11 and calls their _LLog(x, base) by NID. Neither copy is an FMA build.
  • It compares them with _LLog from the libc.prx DLL built from this PR's exact head 4edd28ebb4 (WinLibs GCC 15.2.0 UCRT r7, Release). guest_math passes on that build.
  • There are 100,000 calls per title: x in (0, 10^4] and base chosen from 0, -1, 1 and -3. Each call compares the full 80-bit result.

Results:

  • Current main differs from the titles on 22,252 of them.
  • This PR differs on none, on both titles.

Only positive finite x in that range was covered: no zero, negative, NaN, infinity or subnormal arguments.

AI-assisted: yes (Claude Code).

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

Re-checked — changes requested

The native _LLog differential is extensive, but the reviewed head is based on main 3a2b919816, 36 commits behind current main 52c09d567d2fa453494dd720fcf53a98149468e4. The repository checklist requires a current-main base. The reported differential and focused test therefore need to be repeated or confirmed on the refreshed integration before approval.

What I ran

  • Reviewed exact head 268b424b3d91f1f95854025f636eb9783897a986 against current main.
  • Ran git merge-tree --write-tree and git diff --check.
  • Checked the native comparison, focused test report, and current hosted checks.

Results

Check Result
Current-main merge preview Clean; current main is not in the branch history
git diff --check Passed
Native comparison Author reports 0 mismatches against bundled libc.prx for the tested arguments and floating-point precisions on the older base
Focused test Author reports guest_dinkumware_internals passes three runs on that head
Current-main run Not reported

Please update onto current main and rerun the focused math tests and native differential on the integrated head. I did not run the test or native comparison locally.

@AlexFigas
AlexFigas force-pushed the fix/libc-llog-dinkumware branch from 268b424 to 7b6c2e8 Compare October 29, 2026 10:17
@AlexFigas

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Rebased onto main 1b580f1: head 7b6c2e8, which merges cleanly with current main 1d8fd64. On this head guest_dinkumware_internals and guest_math pass 3 runs each, and the native comparison with the bundled libc.prx's _LLog matches on all 4,000,000 arguments under each of the 5 bases.

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

Re-checked — changes requested

The update reports exact-head guest_dinkumware_internals and guest_math passes, plus the native _LLog comparison. The branch is based on 1b580f13cf; current main is ba5b5330b3 and has changed the libc debt file this PR edits. Please merge current main and rerun conventions and the focused tests on the resulting exact head. The report is useful evidence for 7b6c2e82ec, but it does not establish the required current-main integration.

@adriadam10
adriadam10 merged commit 76fd1c0 into boykopovar:main Oct 30, 2026
19 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the port against the described _LLog of the bundled libc.prx (classification, reduction, polynomial, split constants for bases 0/>0/<0, zero/negative/infinity handling with errno and flags), the new known-answer and 53-bit tests, the debt entry and the head's CI (conventions, Windows and relinker green). The branch merges cleanly with current main; merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants