Tiny Pool redesign: P0.1, P0.3, P1.1, P1.2 - Out-of-band class_idx lookup
This commit implements the first phase of Tiny Pool redesign based on
ChatGPT architecture review. The goal is to eliminate Header/Next pointer
conflicts by moving class_idx lookup out-of-band (to SuperSlab metadata).
## P0.1: C0(8B) class upgraded to 16B
- Size table changed: {16,32,64,128,256,512,1024,2048} (8 classes)
- LUT updated: 1..16 → class 0, 17..32 → class 1, etc.
- tiny_next_off: C0 now uses offset 1 (header preserved)
- Eliminates edge cases for 8B allocations
## P0.3: Slab reuse guard Box (tls_slab_reuse_guard_box.h)
- New Box for draining TLS SLL before slab reuse
- ENV gate: HAKMEM_TINY_SLAB_REUSE_GUARD=1
- Prevents stale pointers when slabs are recycled
- Follows Box theory: single responsibility, minimal API
## P1.1: SuperSlab class_map addition
- Added uint8_t class_map[SLABS_PER_SUPERSLAB_MAX] to SuperSlab
- Maps slab_idx → class_idx for out-of-band lookup
- Initialized to 255 (UNASSIGNED) on SuperSlab creation
- Set correctly on slab initialization in all backends
## P1.2: Free fast path uses class_map
- ENV gate: HAKMEM_TINY_USE_CLASS_MAP=1
- Free path can now get class_idx from class_map instead of Header
- Falls back to Header read if class_map returns invalid value
- Fixed Legacy Backend dynamic slab initialization bug
## Documentation added
- HAKMEM_ARCHITECTURE_OVERVIEW.md: 4-layer architecture analysis
- TLS_SLL_ARCHITECTURE_INVESTIGATION.md: Root cause analysis
- PTR_LIFECYCLE_TRACE_AND_ROOT_CAUSE_ANALYSIS.md: Pointer tracking
- TINY_REDESIGN_CHECKLIST.md: Implementation roadmap (P0-P3)
## Test results
- Baseline: 70% success rate (30% crash - pre-existing issue)
- class_map enabled: 70% success rate (same as baseline)
- Performance: ~30.5M ops/s (unchanged)
## Next steps (P1.3, P2, P3)
- P1.3: Add meta->active for accurate TLS/freelist sync
- P2: TLS SLL redesign with Box-based counting
- P3: Complete Header out-of-band migration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,14 +1,14 @@
|
||||
// tiny_nextptr.h - Authoritative next-pointer offset/load/store for tiny boxes
|
||||
//
|
||||
// Finalized Phase E1-CORRECT spec (物理制約込み):
|
||||
// P0.1: C7 uses offset 0 (overwrites header), C0-C6 use offset 1 (header preserved)
|
||||
//
|
||||
// HAKMEM_TINY_HEADER_CLASSIDX != 0 のとき:
|
||||
//
|
||||
// Class 0:
|
||||
// [1B header][7B payload] (total 8B)
|
||||
// → offset 1 に 8B ポインタは入らないため不可能
|
||||
// → freelist中は header を潰して next を base+0 に格納
|
||||
// → next_off = 0
|
||||
// [1B header][15B payload] (total 16B)
|
||||
// → headerは保持し、next は header直後 base+1 に格納
|
||||
// → next_off = 1
|
||||
//
|
||||
// Class 1〜6:
|
||||
// [1B header][payload >= 8B]
|
||||
@ -17,8 +17,8 @@
|
||||
//
|
||||
// Class 7:
|
||||
// [1B header][payload 2047B]
|
||||
// → C7アップグレード後も header保持、next は base+1 に格納
|
||||
// → next_off = 1
|
||||
// → headerは上書きし、next は base+0 に格納(最大サイズなので許容)
|
||||
// → next_off = 0
|
||||
//
|
||||
// HAKMEM_TINY_HEADER_CLASSIDX == 0 のとき:
|
||||
//
|
||||
@ -44,14 +44,12 @@
|
||||
#include <execinfo.h> // backtrace for rare misalign diagnostics
|
||||
|
||||
// Compute freelist next-pointer offset within a block for the given class.
|
||||
// P0.1: C7 uses offset 0 (overwrites header), C0-C6 use offset 1 (header preserved)
|
||||
static inline __attribute__((always_inline)) size_t tiny_next_off(int class_idx) {
|
||||
#if HAKMEM_TINY_HEADER_CLASSIDX
|
||||
// Phase E1-CORRECT FINAL (C7 user data corruption fix):
|
||||
// Class 0, 7 → offset 0 (freelist中はheader潰す - next pointerをuser dataから保護)
|
||||
// - C0: 8B block, header後に8Bポインタ入らない (物理制約)
|
||||
// - C7: 2048B block, nextを base[0] に格納してuser accessible領域から隔離 (設計選択)
|
||||
// Class 1-6 → offset 1 (header保持 - 十分なpayloadあり、user dataと干渉しない)
|
||||
return (class_idx == 0 || class_idx == 7) ? 0u : 1u;
|
||||
// C7 (2048B): offset 0 (overwrites header in freelist - largest class can tolerate)
|
||||
// C0-C6: offset 1 (header preserved - user data is not disturbed)
|
||||
return (class_idx == 7) ? 0u : 1u;
|
||||
#else
|
||||
(void)class_idx;
|
||||
return 0u;
|
||||
@ -63,11 +61,12 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
|
||||
size_t off = tiny_next_off(class_idx);
|
||||
|
||||
if (off == 0) {
|
||||
// Aligned access at base (header無し or C0/C7 freelist時)
|
||||
// Aligned access at base (header無し or C7 freelist時)
|
||||
return *(void* const*)base;
|
||||
}
|
||||
|
||||
// off != 0: use memcpy to avoid UB on architectures that forbid unaligned loads.
|
||||
// C0-C6: offset 1 (header preserved)
|
||||
void* next = NULL;
|
||||
const uint8_t* p = (const uint8_t*)base + off;
|
||||
memcpy(&next, p, sizeof(void*));
|
||||
@ -75,36 +74,25 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
|
||||
}
|
||||
|
||||
// Safe store of next pointer into a block base.
|
||||
// DESIGN RULE: "Header is written by BOTH Alloc and Free/Drain"
|
||||
// - Free/Drain paths: This function restores header for C0-C6 (offset 1), then writes Next pointer
|
||||
// - Alloc paths: Write header before returning block to user (HAK_RET_ALLOC)
|
||||
// - C7 (offset 0): Header is overwritten by next pointer, so no restoration needed
|
||||
// P0.1: C7 uses offset 0 (overwrites header), C0-C6 use offset 1 (header preserved)
|
||||
static inline __attribute__((always_inline)) void tiny_next_store(void* base, int class_idx, void* next) {
|
||||
size_t off = tiny_next_off(class_idx);
|
||||
|
||||
#if HAKMEM_TINY_HEADER_CLASSIDX
|
||||
// Only restore header for C1-C6 (offset=1 classes)
|
||||
// C0, C7 use offset=0, so header will be overwritten by next pointer
|
||||
if (class_idx != 0 && class_idx != 7) {
|
||||
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
||||
uint8_t got = *(uint8_t*)base;
|
||||
if (__builtin_expect(got != expected, 0)) {
|
||||
static _Atomic uint32_t g_next_hdr_diag = 0;
|
||||
uint32_t n = atomic_fetch_add_explicit(&g_next_hdr_diag, 1, memory_order_relaxed);
|
||||
if (n < 16) {
|
||||
fprintf(stderr, "[NXT_HDR_MISMATCH] cls=%d base=%p got=0x%02x expect=0x%02x\n",
|
||||
class_idx, base, got, expected);
|
||||
}
|
||||
}
|
||||
*(uint8_t*)base = expected; // Always restore header before writing next
|
||||
// For C0-C6 (offset 1): Restore header before writing next pointer
|
||||
// For C7 (offset 0): Header is overwritten, so no restoration needed
|
||||
if (off != 0) {
|
||||
// Restore header for classes that preserve it (C0-C6)
|
||||
*(uint8_t*)base = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
|
||||
}
|
||||
#endif
|
||||
|
||||
// DISABLED: Misalignment detector produces false positives
|
||||
// Reason: Slab base offsets (2048, 65536) are not stride-aligned,
|
||||
// causing all blocks in a slab to appear "misaligned"
|
||||
// TODO: Reimplement to check stride DISTANCE between consecutive blocks
|
||||
// instead of absolute alignment to stride boundaries
|
||||
// NOTE: Disabled alignment check removed (was 47 LOC of #if 0 code)
|
||||
|
||||
if (off == 0) {
|
||||
// Aligned access at base.
|
||||
// Aligned access at base (overwrites header for C7).
|
||||
*(void**)base = next;
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user