Repository navigation
Conversation
…e slot A 64 KiB block keeps the ranges of up to four partial driver stores. When a store does not fit, the oldest is forgotten, and every range of the block reads as stored over before it. A store whose range contains earlier stores' ranges now takes the place of one of them and frees the others, so a range stored again and again keeps one slot. Generations only grow, so the replaced stores answer nothing the new one does not. With slots replaced in place, the round-robin index no longer points at the oldest one, so a store that does not fit now forgets the slot with the lowest generation. A storage image that shares its last block with a buffer that several dispatches write in each frame no longer reads as stored over there, and its write-back stores its results in that block instead of leaving the block to the CPU. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
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.
|
jotitadev
left a comment
There was a problem hiding this comment.
Red/green on lavapipe, the driver the Linux CI job uses, and on a second NVIDIA Linux machine.
Machine: NVIDIA GeForce RTX 3070, driver 595.99.02, Vulkan 1.4.329, plus lavapipe (Mesa 26.0.3, LLVM 21.1.8) on the same machine. Ubuntu 26.04, i7-8700, GCC 15.2, Release, BUILD_TESTING=ON. Head aa8f73490 merged into main 452494439 (clean merge). Device checked in the Vulkan device: lines.
The write-watch probe differs between the two, so the host-import case runs under different rules: the RTX 3070 logs host imports resolve write protection: no ... imported ranges stay watched, lavapipe logs yes ... imported ranges are compared. Both run the watched-memory and the host-import cases.
| Tree | RTX 3070 | lavapipe |
|---|---|---|
| PR | agc_write_tracking and agc_repeated_neighbour_stores pass 3/3 |
pass 3/3 |
PR tests, main's GuestMemory.cpp |
both fail 3/3: repeated stores of the neighbour's bytes count for the first surface's part of the shared block and over a watched host import: 2048 of 524288 texels the GPU wrote were not stored |
both fail 6/6, same messages |
So the host-import case catches the regression with either probe result.
agc_driver_store_over_untracked is not affected by this change here. On the RTX 3070 it fails on every run with and without the change (the result did not reach a block the CPU left alone), as on main. On lavapipe it is intermittent on both trees with a different message (the result was never held pending): 1 of 3 runs with the change, 2 of 6 with main's GuestMemory.cpp.
Not run: Windows, AMD, the full suite, any title.
GPUs tested: NVIDIA GeForce RTX 3070 (595.99.02), lavapipe (Mesa 26.0.3, LLVM 21.1.8), Ubuntu 26.04
Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.
|
Independent interval-history check at exact head Compiled the unchanged A separate scratch oracle keeps an unbounded list of driver-store intervals and their returned generations, then computes overlap and complete coverage by sorting/merging newer intervals. It uses the public Five seeds, each with 128 trials over two 64 KiB blocks:
Discriminating controls: the parent production object fails this oracle with A traced stock pass shows successful |
|
Thanks @jotitadev and @simonbbby: a second NVIDIA machine plus lavapipe for the red/green, and an unbounded-interval oracle with discriminating controls for the bookkeeping. Both are now listed under Tested. AI-assisted: yes (Claude Code) |
|
I opened #7233 from current main to separate complete-write evidence from possible-write ranges in the DriverPieces tracker. It keeps their histories in independent slots and routes BDA page reports plus descriptor-bound direct/copy-back ranges through the possible-write path. The change touches the same tracker code as this PR but does not depend on its interval-retention behavior. If #7233 lands first, please rebase #6868 onto it and keep the containment/replacement rule for both histories; if #6868 lands first, I will rebase #7233 and preserve the separate possible-write ring. |
|
@MarcZX100 agreed. If #7233 lands first, I'll rebase this onto it and keep the containment/replacement rule for both histories. The possible-write ring is where 007 First Light's case ends up. There the four slots fill with one AI-assisted: yes (Claude Code) |
|
I folded the reported retention rule into #7233 at d88fbaa for both the exact-write and possible-write rings, preserving their separate slots. There is also a new assertion for repeated possible writes not invalidating the adjacent range. When convenient, could you try your 007 setup on the combined head and confirm whether the fog-history regression is gone? The PR descriptions now note that these changes need coordination before merge. |
|
@SleglJan: a title-level datapoint for the containment reuse this PR introduced is on #7233 (comment). In Ruffle (PPSA68091), a word the driver rewrites every frame next to a 4x target's DCC fills the block's four pieces, and five of Ruffle's visual tests stop on AI-assisted: yes (Claude Code) |
|
@SleglJan here's a Ruffle case that containment doesn't cover, for when you look at eviction. It's The resident 4x target's DCC is 0x4004606000+0x14000, so it ends at offset 0xa000 of block 0x4004610000. When the check fails, the range's generation is 6801. The block holds four pieces, all at or past 0xa000:
Something that keeps the extent of what was forgotten would help. One option: on overflow, merge the two nearest pieces into one range with the newer generation. That range would count for AI-assisted: yes (Claude Code) |
|
@milescoviello thanks for the clear case. I don't plan to change eviction in this PR or in #6346, so please go ahead with the follow-up. This PR's rule is now part of #7233, which is agreed to land first, so a follow-up on top of #7233 is the natural place. #6346 doesn't change eviction, but it touches the same code. It moves Your shape for it sounds right to me, for both rings: on overflow, merge the two nearest pieces into one range with the newer generation, counted for Your print shows the four kept pieces but not the forgotten one (generation 6812). If that store overlapped the DCC, the stop is real and merging won't remove it. Logging the evicted range would tell you before you build anything. When you have a branch, I can run it against 007 First Light's fog-history case and our AI-assisted: yes (Claude Code) |
|
@SleglJan thanks, I'll do it on top of #7233. You're right about the forgotten piece, so I'll log the evicted range first. If the 6812 store overlapped the DCC, the stop is real and there's nothing to build. I'll add the case you describe to the tests: a resource's own last store and the next allocation's first store, touching at the resource's end, must not merge into a range that reads as stored over the resource. Merging only pieces that leave a gap, or keeping touching pieces apart when another pair can merge, are the two ways I see to avoid it. I'll measure both against the Ruffle case. I'll ping you here with the branch for the 007 fog-history run and the upload counts. Thanks for offering. AI-assisted: yes (Claude Code) |
|
@SleglJan the follow-up is ready, with the evicted-range log first as you suggested. Branch The forgotten piece. I logged each ring's last 8 evicted pieces, on the same Ruffle stack ( Choosing the rule. I traced every driver piece store, whole-block store, block erase,
So nearest-pair merging was as bad as you expected. The extent idea fails exactly where you said: the first piece forgotten is usually the surface's own store, and merging it with any newer neighbour makes the surface read as stored over again. The rule in the branch. When a ring is full, it merges the pair, among the four pieces and the new one, whose union raises the recorded generation of the fewest bytes. For different generations, that's the bytes of the union outside the newer piece. For equal generations, it's the gap. A surface's own store is large, so it stays exact while small stores past it merge with each other. That's your touching case: the next allocation's first store merges with what follows it, not with the surface. The merged piece spans both with the newer generation. For coverage, it keeps the older of the two coverage generations when the pieces touch or overlap, because every byte was then stored since it. It proves nothing across a gap. My first version proved no coverage for any merged piece, and that broke #7233's "out-of-order split driver stores" check. Tests (
For your 007 run, if you still have time: One note for #6346 now on AI-assisted: yes (Claude Code) |
|
@SleglJan a follow-up on the one open point above: AI-assisted: yes (Claude Code) |
|
@milescoviello I ran 007 First Light with your rule and your trace hook, twice. The fog case stays closed under it in both runs. The replay over the two traces splits:
No rule misses a stored-over answer in either run. Setup:
Runs (arm (c), with #7233's rule, for comparison):
Replay (
Per block, from a small script over the same traces with your tool's rules:
I haven't traced which caller makes run 2's most-checked block's checks, so I can't say what a false answer costs there. AI-assisted: yes (Claude Code) |
|
@SleglJan thank you, that's the benchmark I couldn't run. Two runs that split this way settle it against my rule: it fixes the overflow collapse in run 2's hot block, but it costs thousands of false answers in the one block that has them in both runs, where #7233's rule has under 200. I replayed my Ruffle traces through the same tool for the rows your runs favour (
So the forgotten-range rules are the only ones that win in all four traces, and none of them misses a stored-over answer. I'll switch the branch to them:
On memory: a block entry is 112 bytes on #7233 and 136 in the branch. Four forgotten ranges per ring add 96 bytes, and four more ring slots per ring another 128. Entries exist only for blocks with driver stores. Unless you see a reason to prefer 8 slots, I'll start with 4 slots and 4 ranges (168 and 162 in your runs) and report the trace counts again; going to 8 slots is a one-line change if the 007 numbers make it worth it. AI-assisted: yes (Claude Code) |
|
@milescoviello 007 on
Setup:
Replay (
Per block, for the current rule and the branch:
AI-assisted: yes (Claude Code) |
|
@SleglJan thanks for runs 3 and 4, and for catching the hook. Across your four runs, "8 slots, 4 forgotten ranges merging the two oldest" gives 1, 0, 2 and 0 false stored-over answers, against #7233's 374, 690,534, 4,867 and 7,249, and no rule misses one. So I've moved the branch to 8 slots:
On the magenta wash and the green block: no rule misses a stored-over answer, so these rules differ from an unbounded tracker only by extra stored-over answers, never by stale reads. Arm (c) shows the same kind of green block at +11 to +13, so I don't think either comes from the tracker. With 8 slots there are also far fewer extra answers. I haven't attributed either one.
AI-assisted: yes (Claude Code) |
|
@SleglJan both branches are pushed with 8 slots:
Tests (
If you run 007 on it, the trace's "8 slots, 4 forgotten ranges" row should show 0 logged differences directly, without your port's change. AI-assisted: yes (Claude Code) |
|
@milescoviello 007 with 8 slots, runs 5 and 6. Same harness and tree as runs 3 and 4, with the piece rings at 8 as in 4f4729b.
Setup:
The corner measure is new. Where those runs show the block, it gives 0.95 to 1.00 at run 4's +49 to +53 and 0.12 to 0.54 at +54 to +56, and 0.52 to 0.93 at arm (c)'s +11 to +13. It also gives 0.32 at run 4's +6, a green-tinted bloom frame without the block. Replay (
Per block:
On the magenta:
One correction to "only by extra stored-over answers, never by stale reads". In a traced run on runs 3 and 4's build, false answers on that same part in two presents made a 1×1 float image there take its guest bytes again (two more uploads), and the next dispatch that read it got 3.8e-25 where it had read 0. The difference was too small to show on screen, but an extra stored-over answer can bring back stale bytes. That's one more reason to keep them rare, which 8 slots do. AI-assisted: yes (Claude Code) |
|
@SleglJan thank you for runs 5 and 6. With 8 slots, "8 slots, 4 forgotten ranges merging the two oldest" gives 0 and 6 false stored-over answers, against 9,131 and 2,695 for #7233's rule. It differs from the logged answer in 0 checks, so the hook now logs what the build answers. And no rule misses one. That settles the slot count for me. Thanks too for the correction, which I accept: I was wrong that extra stored-over answers can't cause stale reads. Your traced run shows a false answer making the 1×1 float image take its guest bytes again, and the next dispatch read 3.8e-25 where it had read 0. A false stored-over answer can overwrite a GPU result with older guest bytes. That makes keeping them rare part of correctness, not only cost, and I'll say so in the PR description. On the magenta: run 5 shows it with no false answer in its whole trace, and run 6 before its first one. Your forced answers on that part were followed by no artifact. So I agree it doesn't point at the rings. Your lead on the post-processing pass that reads a surface no pass wrote in the frame sounds like the place to look. The follow-up PR still waits on #7233. Its head moved to ac0a545, and in your run that still fails AI-assisted: yes (Claude Code) |
|
Closing in favour of #10333. #7233 (e424d57, @MarcZX10) carries this rule for both rings; #10333 keeps the tests and a rewritten debt line. Thanks @jotitadev and @simonbbby for the red/green runs and the interval oracle, and @milescoviello for the Ruffle case and the eviction follow-up. AI-assisted: yes (Claude Code) |
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e424d57 (MarcZX10, boykopovar#7233) made a driver store whose range contains earlier pieces of its ring take their slot and a full ring forget its oldest piece, for the exact and the possible ring. boykopovar#6868 proposed that rule for the exact ring; this keeps its tests and its debt line and drops the code. The tests cover what main's do not: repeated exact stores of a neighbour's bytes leave the other surface's share of the block unstored and keep their own coverage, a containing store answers for the contained bytes only and frees the slots of the pieces it contains, a full exact ring forgets its oldest piece and not a newer one, the forgotten piece counts for the rest of the block, an overlapping store that contains nothing replaces nothing, and a storage image written back after five such stores in its last 64 KiB block keeps every texel over watched memory and over a watched host import. The debt line records what a forgotten piece costs for both rings. The recorder's --repeated-neighbour-stores-only mode returns 77 when no watched arm ran (no write watching); in the full suite each arm prints its own ok or not-tested line instead of one ok for both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
What
The write tracker keeps the byte ranges of up to four partial driver stores per 64 KiB block (
noteDriverStore). A store that did not fit forgot the oldest and set the block'sdroppedgeneration. From then onStoredOveranswered true andStoredAllSincefalse for every range of the block, for any generation before the forgotten store, whether a store had touched that range or not.A store whose range contains earlier stores' ranges now takes the place of one of them and frees the others, so a range stored again and again keeps one slot. Generations only grow, so the replaced stores answer nothing the new one does not:
StoredOverasks whether a newer store overlaps the range,StoredAllSincewhether newer stores join up over it. With slots replaced in place, the round-robin index would no longer point at the oldest slot, so a store that does not fit now forgets the slot with the lowest generation, as the round-robin did before.Where it shows: a storage image whose last 64 KiB block also holds the start of the next allocation, a buffer that several dispatches write in each frame.
MarkDirectWritesrecords the buffer's written range after each of them, the four slots fill with that one range within a frame, the next store forgets one, and the image's own part of the block reads as stored over although nothing wrote it. Its write-back then leaves that block to the CPU (the shared edge block rule of #441), and the image's texels there are replaced with older contents of that memory. An image uploaded through a CPU copy keeps the uploaded bytes, and #1599's comparison stores its results anyway; an image over a host import keeps no such copy above 1 MiB.In 007 First Light (PPSA11386) the image is the volumetric fog's temporal history (107x60x64, B10G11R11, 1.75 MiB, its end not 64 KiB aligned, no saved copy). The texels that come back hold bytes of another resource, Inf and NaN among them, and the fog pass blends them into the next frames. The screen shows a magenta and white wash over most of it during gameplay.
Several distinct partial stores in one block still fill the four slots and set
dropped; the technical debt entry records it.Related:
UnchangedSinceread the CPU page stamps and these store slots whenever a block's stamp is newer than the query. This change does not depend on it.noteDriverStoreunchanged and merge cleanly with this change; perf(agc): settle Windows imports under 1 MiB without waiting #6678 (open) adds a debt entry at the same place indocs/dev/debt/libSceAgcDriver.md, a placement conflict only.Tested
agc_write_tracking, new checkCheckRepeatedNeighbourStores:StoredAllSince);CheckSharedBlock's check that six distinct small stores, more than the four slots, still count as a store over the rest of the block passes unchanged.agc_repeated_neighbour_stores(new,agc_driver_recorder_tests --repeated-neighbour-stores-only): a 1024x512 RGBA8 storage image 8 KiB into its first block is uploaded and settles, is cleared on the GPU, the 24 KiB after its end take five driver stores, and the image is written back. Every texel the GPU wrote must be stored and the bytes after the image must be unchanged. It runs over watched memory and over a watched host import.main82ddae7's GuestMemory.cpp and the new tests:agc_write_trackingfails at "repeated stores of the neighbour's bytes count for the first surface's part of the shared block", andagc_repeated_neighbour_storesat "over a watched host import: 2048 of 524288 texels the GPU wrote were not stored" (the image's 8 KiB in the shared block).agc_write_trackingfails at "a full block forgets a store newer than its oldest".ctest -R '^agc'on this branch: 617 tests, 1 failure.agc_driver_store_over_untrackedfails the same way onmain(reported on fix(agc): save pending storage results in every 64 KiB block that leaves the write watch #5671).agc_driver_stencil_ref_exportskips itself on this machine.main4186f06 with 007's bring-up stack, among it perf(agc): stores beside a range in its 64 KiB block leave the range unchanged #6346 at b8251dcbb and fix(agc): free retired depth surfaces nobody samples #6465, and local diagnostics; perf(agc): stores beside a range in its 64 KiB block leave the range unchanged #6346 makes the store slots decide whether the history is uploaded again, onmainthey decide whether its write-back stores its last block). Each run: the 59 frames after gameplay starts, every fog volume dumped after each frame's fog pass.MarkDirectWrites, anddroppedwas newer than the image.droppedmade a block read as stored over, over the whole run: 4,724 in a run without this change, 1,550 with it, 1,463 of them with four distinct stores in the block (the case in the debt entry), none of them on the fog volumes.main4524944: both new tests pass 3/3 with the change and fail withmain'sGuestMemory.cpp(3/3 on the RTX 3070, 6/6 on lavapipe), with the same two failure messages as listed above: fix(agc): a driver store over an earlier store's range takes its piece slot #6868 (review)StoredOver/StoredAllSincequeries agree exactly; with further arbitrary partial stores that may exceed the slot capacity, 122,880 more show no missed store and no complete coverage over a gap. The parent fails it (false overlap without history overflow), and a variant that replaces an overlapping, not only a contained, piece fails withlost an actual driver store: fix(agc): a driver store over an earlier store's range takes its piece slot #6868 (comment)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