Repository navigation
feat(libc): export the std::__sce_v2 _Locimp_ctor and _Locimp_dtor with facet reference counting - #16165
Conversation
…nd _Locinfo category calls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th facet reference counting The versioned _Locimp family now owns its facets the way the console's libc.prx does: _Locimp_ctor retains every copied facet, _Locimp_dtor releases them (deleting the ones that reach zero) and frees the vector, _Locimp_Addfac retains the new facet and releases the one it replaces, and _New_Locimp(const&) copies the fields and calls _Locimp_ctor. The _Locimp objects built by the versioned calls get a vtable whose last release runs _Locimp_dtor and frees the object. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Windows check of head Setup: Windows 11 Enterprise 22621, WinLibs GCC 15.2 UCRT, Release. The hosted Windows check does not run these tests on pull requests.
AI-assisted: yes. |
Progress report (151a2cb → 8c982c6)📈 System libraries: 27.09% (+0.02%, +10 implemented functions) ✅ 10 implemented
Changed libraries (implemented exports)
|
The progress report counted DestroySceV2Locimp and DeleteSceV2Locimp as implemented functions; the _nid_no_patch suffix excludes them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last two checks compared against copy->base.vtable after deleteObject had freed the copy, so they read freed memory and failed once the allocator reused the first bytes. Co-authored-by: Danigamer05 <96824289+Danigamer05@users.noreply.github.com>
❌ AMD GPU test: failedPR head
Failing testsThis PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.
guest_sce_v2_locimp_references: Failed (also failed when re-run alone), assertion and diagnosticsUnstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (14, not counted against this PR)Validated but not compared (1, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 808 tests)Skipped (71)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
Hi @Danigamer05, the new locimp-references test failed on Linux: the last two checks ( |
|
Thanks for the fix-up, I also carried it into #16178. |
What
Builds on #16113: this branch contains its commit, so until it merges only the two commits after it are this PR's change.
Export the versioned
std::__sce_v2locale::_Locimp::_Locimp_ctorand_Locimp_dtorfrom libc, and make the versioned_Locimpfamily count facet references the way the console does._ZNSt8__sce_v26locale7_Locimp12_Locimp_ctorEPS1_RKS1_XBQPgR5196I0xF25E0_ZNSt8__sce_v26locale7_Locimp12_Locimp_dtorEPS1_L2pyahLSGFk0xD1030Changed exports:
_ZNSt8__sce_v26locale7_Locimp11_New_LocimpERKS1_(ynwqogpIbL0,0xD0F60) and_ZNSt8__sce_v26locale7_Locimp14_Locimp_AddfacEPS1_PNS0_5facetEm(7KPYZ-jSAto,0xF2680).The behavior comes from the
libc.prxbundled with PPSA03671 v01.001.005 (J03848509, SHA-256483961e086b8a1fffa8138b3c0badf798987fb5f35ec908d828946408be2d8a6). As in the module:_Locimp_ctor(self, source)builds only the facet vector. The caller has already copied the count. A zero count leaves the vector alone. Otherwise it callsmalloc(count * 8), throwsbad_allocon null, and copies the facets from the last to the first, retaining each non-null one._Locimp_dtor(self)releases each non-null facet from the last to the first, callsdeleteObjecton the ones that reach zero, and frees the vector unless it is the classic one. It does not free the_Locimp._New_Locimp(const _Locimp&)allocates0x30bytes and sets the_Locimpvtable, one reference and a null vector. It copies the count, category, transparency and name pointer, then calls_Locimp_ctor. If that throws, it frees the object._Locimp_Addfacgrows the vector withrealloctomax(id + 1, 40)slots and clears the new ones. It retains the new facet, releases the one it replaces (deleting it at zero) and stores the new one._Locimpobjects built by_New_Locimp(const&),_New_Locimp(bool)andempty()get the module's_Locimpvtable. The last release returns the object, anddeleteObjectruns_Locimp_dtorand frees it. Before this PR they used the copy vtable and were never freed.Before this PR, the versioned
_New_Locimp(const&)and_Locimp_Addfaccalled the unversioned copy and insertion, which neither retain nor release. The unversioned functions are unchanged.guest_sce_v2_localepassed facets with a null vtable to_Locimp_Addfacand read the copy after its last release. It now uses a counting facet and checks that the last release deletes the copy and its facet.A new debt entry lists what still differs: the lock, null arguments, size overflow, the one-slot classic vector, and copies of locales built by unversioned calls. The versioned locale entry drops its sentence about the reused unversioned copy and insertion, and #16113's entry drops the one about the copy vtable.
Tested
Windows x86-64, WinLibs GCC 15.2.0, Release,
BUILD_TESTING=ON, on top of #16113 (d18e1c71325, maind893997f1a6); the branch merges cleanly with mainc4469b60daf. Exact tested commit:b24626696c2. It adds one commit to4e7016e6926that renames two internal helpers. The build, the tests, the side-by-side run and the conventions check ran on it; the negative control and the overlap checks below ran on4e7016e6926.Built libc, libkernel, their NID-patched production libraries and the related tests. The new
guest_sce_v2_locimp_referencespasses, along withguest_sce_v2_locale,guest_sce_v2_locale_basics,guest_localeandguest_sce_v2_exceptions;public_headerspassed on4e7016e6926.The NID-patched libc exports both new NIDs.
The new test uses facets that count retains, releases and deletes. It checks:
_New_Locimp(const&)copies, and that the copy owns its vector;id + 1, the cleared slots, replacing a facet and re-adding the same one;_Locimp_ctorleaves the vector alone for a zero count and copies onlycountfacets, and that_Locimp_dtorfrees the vector and deletes facets at zero;_Locimpsharing the classic vector keeps it on destruction and gets a copy of it on growth;bad_alloc, retains nothing, keeps the vector and frees the half-built_New_Locimpobject, and that another thread can still take the lock afterwards;_New_Locimp(bool)andempty()objects are freed with their facets on the last release.The original functions were loaded from the bundled module with their relocations applied. Their imports were bound by NID to this branch's libc and libkernel. The module's heap is not set up outside a console process, so its allocation calls were redirected to the host's. Both sides'
_Xbad_allocwere redirected to one recorder. The recorder exits bylongjmp, so after a failed_New_Locimp(const&)the harness frees the object as the module's landing pad does.Side by side with the patched production library, over two seeds with 200,000 random steps each: about 64,000
_New_Locimp(const&), 16,000_New_Locimp(bool), 88,000_Locimp_Addfac, 100,000_Locimp_ctor/_Locimp_dtorpairs on caller-owned objects, and 131,000 retains, releases anddestroycalls through the vtable. One step in six made its allocation fail (about 12,000 throws per seed), and about 13,000_Locimps per seed reached zero and were deleted. Each step compared:_Locimp's references, count, category, transparency, name and slots;malloc,reallocandfreecalls.The only difference is that the module also calls
free(NULL), which the harness ignores. No comparison differed. feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113's side-by-side run also still passes with two seeds on4e7016e6926.Negative control: five lines were altered one at a time:
_Locimp_ctordid not retain;_Locimp_dtordid not free the vector;_Locimp_Addfackept the replaced facet;_New_Locimp(const&)did not copy the count;_New_Locimp(bool)kept the copy vtable.CTest failed on each, and the side-by-side run reported differences each time. The source was then restored byte for byte (SHA-256 checked) and rebuilt, and the tests and side-by-side run passed again.
Code conventions and
git diff --checkpass.tools/pr_overlap.pyread 1,871 open PRs at4e7016e6926and reports no overlap. It keeps exit 2 for 36 branches that already conflict with main. Merging each of the 36 with this branch and with main usinggit merge-treegives the same conflict blocks, apart from main's own movement since feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113's base, except indocs/dev/debt/libc.md: for feat(aot): consolidate native ARM64 compiler, runtime and consumers #5021, fix(libc): follow a Windows symbolic link before .. in a guest path #9371, feat(libc): add the std::__sce_v2 thread start, timed wait, clock and processor count calls #9447 and feat(libc): add std::__sce_v2::_Random_device, _Fiopen and setw #9526 the edited_Locimpline and the new entry land inside the conflict block they already have there, as feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113's lines do, and docs(debt): separate entries with blank lines and drop derived export totals #14212 gets no conflict block beyond the ones it has with feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113. The debt edit adds no conflict over feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113 with any of the 142 open PRs that touch that file. No open PR adds these names or NIDs. The branch also merges cleanly with fix(libc): preserve Dinkumware zero signs when no conversion occurs #14595, feat(libc): export the std::__sce_v2 extended-precision helpers #15336, feat(libc): export the std::__sce_v2 multiprecision helpers #15471, feat(libc): export the std::__sce_v2 _Poly and _XLgamma helpers #15484, feat(libc): export the std::__sce_v2 _Xtime_diff_to_millis2 helper #15490, feat(libc): export the std::__sce_v2 _Exp and _FExp helpers #15501, feat(libc): export the std::__sce_v2 _Pmsw, _FPmsw and _LPmsw helpers #15519, feat(libc): throw std::bad_cast and the std::__sce_v2 bad_any_cast, format_error and future_error #15552, feat(libc): forward the std::atomic wait and notify helpers to libkernel #15561, feat(libc): count the std::__sce_v2 _Winit and ios_base::Init initializations #15602, feat(libc): export the std::__sce_v2 floating-point constants #15614, fix(libc): store the std::__sce_v2 _Throw_Cpp_error value in the first code word #15667, feat(libc): export the std::__sce_v2 _Stollx, _Stoullx, _Stolx and _Stoulx parsers #15683, feat(libc): export std::_Throw_bad_any_cast and std::_Throw_bad_node_access #15791, feat(libc): export the std::error_category complete and deleting destructors #15796, feat(libc): export the std::__sce_v2 resetiosflags, setiosflags, setbase and setprecision manipulators #15809, feat(libc): export the std::__sce_v2 _Timespec64_get_sys and _Timespec64_diff_to_ts clock helpers #15834, feat(libc): export the std::__sce_v2 at-thread-exit registry calls #15879, feat(libc): export the remaining std::__sce_v2 facet ids and ios_base _Sync and _Index #15923, feat(libc): export the std::__sce_v2 clog, wclog and standard stream pointers #16083 and feat(libc): export the std::__sce_v2 locale::global, empty, classic and _Locinfo category calls #16113.Not tested: Linux, macOS, another SDK, a game, GPU or console. No game compatibility claim. No original module, extracted bytes, harness or log is committed or uploaded.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code