Allocate block-exceeding tokens directly in the old generation#1219
Merged
Conversation
a9f1274 to
3b86377
Compare
A token whose payload exceeds a block (`BLOCK_SIZE`) is flagged `NOT_YOUNG` by `init_with_len`, but `kore_alloc_token` was still placing it in the young semispace. The collector never migrates a `NOT_YOUNG` object out of the young from-space, which is reclaimed on every collection, so any such token dangled: a later allocation overwrote its bytes and a subsequent collection crashed walking the corrupted header (we hit this in `kore_collect` -> `arena::evacuate` -> `migrate` on large MegaETH workloads; it reproduces independently of the hyperblock size). We route those allocations to `oldspace`, matching the existing large-buffer handling in `hook_BUFFER_concat_raw`, and add `AGE_MASK` to the `NOT_YOUNG` header so `is_in_old_gen_hdr()` recognises the block as a live old-generation object. The oversized case is kept in a cold, out-of-line helper guarded by `__builtin_expect` so the common small-token path of the `always_inline` `kore_alloc_token` is not bloated at every call site.
`test-gc-large-token` builds a single > 1 MiB `String` token, keeps it live in the `<keep>` cell across collections that reuse the abandoned young region, and reads it back. Without the previous commit the run crashes while the collector walks the dangling token's corrupted header; with it the token lives in the old generation and its length survives intact.
3b86377 to
57a861a
Compare
gtrepta
approved these changes
Jun 19, 2026
Simplified comment regarding allocation size handling in init_with_len function.
Updated comments to clarify allocation behavior for oversized tokens.
gtrepta
approved these changes
Jun 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
While running large MegaETH workloads under the LLVM backend we hit a crash in
kore_collect/arena::evacuatewhile migrating a block with a corrupted header. The culprit is aStringtoken larger than a block:init_with_lencorrectly flags itNOT_YOUNG, butkore_alloc_tokenwas still allocating it in the young semispace. The collector never migrates aNOT_YOUNGobject out of the young from-space, which is reclaimed on every collection, so the token dangled — a later allocation overwrote its bytes and the next collection walked a garbage header. It reproduces independently of the hyperblock size.The fix routes tokens larger than
BLOCK_SIZEtooldspace(matching whathook_BUFFER_concat_rawalready does for large buffers) and setsAGE_MASKon theNOT_YOUNGheader sois_in_old_gen_hdr()recognises the block as a live old-generation object. The oversized branch is kept in a cold, out-of-line helper (__builtin_expect) so the common small-token path of thealways_inlinekore_alloc_tokenisn't bloated at every call site.I also added
test-gc-large-token: it builds a single > 1 MiB token, keeps it live across forced collections that reuse the abandoned young region, then reads it back. The test crashes on the buggy version (the collector walks the dangling token) and passes with the fix; the rest of the GC and unit suites are unaffected.This is safe for the hot path: small-token allocation is unchanged apart from one predicted-not-taken branch, and the oversized path is rare. Instruction counts on an allocation-heavy benchmark are flat versus before, and the full
litsuite stays green.