Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
946e4e6
radlink: enable C11 atomics so BLAKE3 skips the lock'd CPU-feature load
honkstar1 Jun 16, 2026
4ed63aa
radlink: release copy-on-write input views in parallel before exit
honkstar1 Jun 16, 2026
4650217
radlink: release the ~1GB image buffer early so reclaim overlaps the run
honkstar1 Jun 17, 2026
99ed2f5
radlink: /OPT:GCTYPES -- garbage-collect unreferenced CodeView types …
honkstar1 Jun 19, 2026
191869b
radlink: frontier worklist for type-GC transitive closure (perf)
honkstar1 Jun 19, 2026
ed2e4df
radlink: skip the GC mark atomic on the already-reachable fast path
honkstar1 Jun 19, 2026
d45ad7e
radlink: header-unit IFC debug-record resolution
honkstar1 Jul 13, 2026
2a4f9a3
linker: parallelize apply_ifc nonblob_complete set merge + ifc parse
honkstar1 Jul 18, 2026
2ab3521
linker: parallelize the serial setup loops in lnk_make_code_view_input
honkstar1 Jun 21, 2026
a4da5be
radlink perf: decommit idle tctx scratch pages before PDB build
honkstar1 Jun 21, 2026
7066161
linker: free leaf bucket_arr probe tables before the merge-types comm…
honkstar1 Jun 22, 2026
fa83336
radlink: env-gated RADLINK_PHASE_LOG raw per-phase micros file (diagn…
honkstar1 Jun 22, 2026
e8489c0
linker: /RAD_SHARED_THREAD_POOL -- cross-process shared thread pool (…
honkstar1 Jun 25, 2026
36d41e2
radlink: sort image relocs by apply_off for monotone-forward patch wr…
honkstar1 Jun 25, 2026
4751260
radlink: non-temporal stores for write-once image fill
honkstar1 Jun 25, 2026
7adc50f
radlink: false-sharing pads on per-worker [worker_count] arrays
honkstar1 Jun 25, 2026
79c0d74
radlink: symbol trie inline-hash fast-reject
honkstar1 Jun 25, 2026
c62b42b
radlink: reap merge-types bucket arena on a background thread
honkstar1 Jul 1, 2026
6e0a8ce
radlink: patch debug-section relocs on private copies (keep CoW input…
honkstar1 Jul 1, 2026
7455386
radlink: /PDBSTRIPPED strips proc itype in the destination buffer, no…
honkstar1 Jul 1, 2026
10f204a
radlink: skip the input-view unmap pass when the process exits right …
honkstar1 Jul 1, 2026
7014fe9
linker: size leaf dedup tables from a deterministic unique-count esti…
honkstar1 Jul 2, 2026
ca73de9
linker: shard GSI/PSI symbol inserts by bucket range
honkstar1 Jul 2, 2026
638c1d4
codeview: replace per-TI linked-list type-index offsets with static d…
honkstar1 Jul 2, 2026
88d075c
linker: parallelize image align-byte fill (first-touch) across the pool
honkstar1 Jul 2, 2026
93128cf
arena: 2MB commit quantum for the link's high-churn arenas
honkstar1 Jul 2, 2026
84ad042
linker: sample the unique-leaf estimator at every 8th leaf position
honkstar1 Jul 2, 2026
48c7c64
linker: size-weighted obj distribution for the PDB barrier passes
honkstar1 Jul 2, 2026
75b2f67
linker: schedule-independent symrec order for same-name global symbols
honkstar1 Jul 2, 2026
7993468
linker: /OPT:REF walk: memoize reloc-symbol resolution, drop per-relo…
honkstar1 Jul 2, 2026
18af789
linker: /OPT:REF: parallelize unreachable-section removal
honkstar1 Jul 2, 2026
39ff004
linker: packed-discriminator pre-filter for archive symbol-dir bsearch
honkstar1 Jul 2, 2026
bc240fd
linker: balance GSI deduper inserts and parallelize bucket compaction
honkstar1 Jul 2, 2026
bd7300c
linker: release scratch free-list blocks off the main thread
honkstar1 Jul 3, 2026
8c851db
linker: warn visibly on unknown /RAD_* command-line switches
honkstar1 Jul 2, 2026
5087c22
base: fail cleanly on arena reserve/commit failure instead of AVing
honkstar1 Jul 2, 2026
7ae549e
linker: opt-in one-line [radlink summary] (v=3) for production triage
honkstar1 Jul 3, 2026
5439a72
linker: batch-prefetch mapped debug input, fanned out over the pool -…
honkstar1 Jul 3, 2026
67d0eca
linker: stop prefaulting every committed page -- commit_memory RIO pr…
honkstar1 Jul 4, 2026
91cb4e4
linker: map input file views read-only -- commit charge per dirtied p…
honkstar1 Jul 6, 2026
3770eeb
linker: fix garbage file/line refs in unresolved-symbol reports -- th…
honkstar1 Jul 10, 2026
b4b63c5
linker: /OPT:ICF -- folded functions keep bindable debug info (Lines …
honkstar1 Jul 9, 2026
8c54530
linker: /OPT:ICF perf -- dirty-skip refinement round keys, /RAD_ICF_H…
honkstar1 Jul 19, 2026
2c76dd9
build: PGO training also links with /OPT:REF /OPT:ICF
honkstar1 Jul 20, 2026
1e93a4b
linker: implement by-name-no-prefix and undecorate import members
honkstar1 Jul 20, 2026
3c25d94
thread_pool: fair-share floor for barrier-pass cohorts
honkstar1 Jul 20, 2026
0aa9675
linker: patch symbol tables in private copies -- keep CoW input pages…
honkstar1 Jul 21, 2026
08bbf0e
linker: fuse leaf TI-fixup into unbucket -- materialize unique leaves…
honkstar1 Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ if "%pgo%"=="1" (
where llvm-profdata /q || echo llvm-profdata is not in the PATH || exit /b 1
if "%clang%"=="1" (
if "%pgo_run%" == "1" (
call llvm-profdata merge %LLVM_PROFILE_FILE% -output=%~dp0build\build.profdata || exit /b 1
call llvm-profdata merge %~dp0build\pgo_raw\*.profraw -output=%~dp0build\build.profdata || exit /b 1
set auto_compile_flags=%auto_compile_flags% -fprofile-use=%~dp0build\build.profdata
set pgo_run=0
) else (
echo [pgo enabled]
set auto_compile_flags=%auto_compile_flags% -fprofile-generate -mllvm -vp-counters-per-site=5
set LLVM_PROFILE_FILE=%~dp0build\build.profraw
if not exist %~dp0build\pgo_raw mkdir %~dp0build\pgo_raw
del /q %~dp0build\pgo_raw\*.profraw 2>nul
set LLVM_PROFILE_FILE=%~dp0build\pgo_raw\build.%%p.profraw
set pgo_run=1
)
) else (
Expand Down Expand Up @@ -142,7 +144,14 @@ pushd build
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg.exe || exit /b 1
if "%raddbg_non_graphical%"=="1" set didbuild=1 && %compile% -DWM_STUB=1 -DR_BACKEND=R_BACKEND_STUB ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg_non_graphical.exe || exit /b 1
if "%com_shim%"=="1" set didbuild=1 && %compile% ..\src\com_shim\com_shim_main.c %compile_link% %out%com_shim.exe || exit /b 1
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %linker% /NOIMPLIB %linker% /NATVIS:"%~dp0\src\linker\linker.natvis" %out%radlink.exe || exit /b 1
:: NOTE: -DBLAKE3_ATOMICS=1 makes BLAKE3 use C11 _Atomic (plain atomic load) for
:: get_cpu_features instead of MSVC's _InterlockedOr `lock or` barrier on every
:: compress dispatch (was a ~5.5s main-thread hot spot). MSVC C11 atomics require
:: /std:c11 /experimental:c11atomics. Kept external so the vendored blake3 source
:: stays pristine.
set radlink_msvc_flags=
if "%msvc%"=="1" set radlink_msvc_flags=/std:c11 /experimental:c11atomics -DBLAKE3_ATOMICS=1
if "%radlink%"=="1" set didbuild=1 && %compile% %radlink_msvc_flags% ..\src\linker\lnk.c %compile_link% %linker% /NOIMPLIB %linker% /NATVIS:"%~dp0\src\linker\linker.natvis" %out%radlink.exe || exit /b 1
if "%radbin%"=="1" set didbuild=1 && %compile% ..\src\radbin\radbin_main.c %compile_link% %out%radbin.exe || exit /b 1
if "%raddump%"=="1" set didbuild=1 && %compile% ..\src\raddump\raddump_main.c %compile_link% %out%raddump.exe || exit /b 1
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
Expand Down Expand Up @@ -183,6 +192,10 @@ if "%pgo_run%"=="1" (
if "%radlink%"=="1" (
pushd local\lyra_pgo
call %~dp0build\radlink @lyra.rsp || exit /b 1
rem lyra.rsp trains with /OPT:NOREF /OPT:NOICF; run a second link with REF+ICF on
rem (later switches win) so the profile also covers the /OPT:REF walk and the ICF
rem refinement -- otherwise PGO marks them cold and the build regresses on real links
call %~dp0build\radlink @lyra.rsp /OPT:REF /OPT:ICF || exit /b 1
popd
)
goto restart
Expand Down
103 changes: 84 additions & 19 deletions src/base/base_arena.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,32 @@ arena_alloc_(ArenaParams *params)
commit_size = AlignPow2(commit_size, get_system_info()->page_size);
}

B32 commit_ok = 0;
if(params->flags & ArenaFlag_LargePages)
{
base = reserve_memory_large(reserve_size);
commit_memory_large(base, commit_size);
commit_ok = (base != 0) && (commit_size == 0 || commit_memory_large(base, commit_size));
}
else
{
base = reserve_memory(reserve_size);
commit_memory(base, commit_size);
commit_ok = (base != 0) && (commit_size == 0 || commit_memory(base, commit_size));
}

// rjf: panic on arena creation failure (previously unchecked in
// non-graphical builds: writing the arena header into an unreserved or
// uncommitted base address turned an out-of-memory condition into an
// access violation)
if(Unlikely(base == 0 || !commit_ok))
{
#if OS_FEATURE_GRAPHICAL
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
#else
fprintf(stderr, "fatal: out of memory: unable to allocate a memory block (reserve %llu bytes, commit %llu bytes)\n", (unsigned long long)reserve_size, (unsigned long long)commit_size);
#endif
abort_self(1);
}

AsanPoisonMemoryRegion(base, commit_size);
// TODO(rjf): we need to reintroduce this later when we have the ability to remove annotations...
// raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line);
Expand All @@ -68,15 +84,6 @@ arena_alloc_(ArenaParams *params)
AsanPoisonMemoryRegion(base, params->reserve_size);
}

// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
abort_self(1);
}
#endif

// rjf: extract arena header & fill
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
Arena *arena = base;
Expand Down Expand Up @@ -263,16 +270,23 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
U64 cmt_size = cmt_pst_clamped - current->cmt;
U8 *cmt_ptr = (U8 *)current + current->cmt;
B32 commit_ok;
if(current->flags & ArenaFlag_LargePages)
{
commit_memory_large(cmt_ptr, cmt_size);
commit_ok = commit_memory_large(cmt_ptr, cmt_size);
}
else
{
commit_memory(cmt_ptr, cmt_size);
commit_ok = commit_memory(cmt_ptr, cmt_size);
}
// rjf: a failed commit must not advance current->cmt: the pages are not
// backed, and pretending they are turns out-of-memory into an access
// violation on first touch (the result == 0 panic below reports it cleanly)
if(Likely(commit_ok))
{
AsanPoisonMemoryRegion(cmt_ptr, cmt_size);
current->cmt = cmt_pst_clamped;
}
AsanPoisonMemoryRegion(cmt_ptr, cmt_size);
current->cmt = cmt_pst_clamped;
}

// rjf: push onto current block
Expand All @@ -294,15 +308,18 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
}
#endif

// rjf: panic on failure
#if OS_FEATURE_GRAPHICAL
// rjf: panic on failure (a failed commit above leaves result == 0; returning
// it would turn out-of-memory into an access violation at the use site)
if(Unlikely(result == 0))
{
#if OS_FEATURE_GRAPHICAL
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
#else
fprintf(stderr, "fatal: out of memory: unable to allocate %llu bytes\n", (unsigned long long)size);
#endif
abort_self(1);
}
#endif


return result;
}

Expand Down Expand Up @@ -353,6 +370,54 @@ arena_pop_to(Arena *arena, U64 pos)

}

//- rjf: arena decommit of unused (rewound/free) pages

internal void
arena_decommit_unused(Arena *arena)
{
// NOTE(perf): decommit committed-but-unused pages so they stop counting against
// working set, while keeping the reservation. Only touches pages strictly above
// the live `pos` high-water of each block in the active chain, and the unused
// bodies of free-list blocks. Live data (<= pos) is never touched. The push path
// re-commits on demand (arena_push grows `cmt`), so reuse is transparent.
if(arena->flags & ArenaFlag_LargePages)
{
// large pages cannot be partially decommitted safely; skip.
return;
}
U64 page_size = get_system_info()->page_size;

// rjf: active chain -- decommit committed region above each block's live pos
for(Arena *n = arena->current; n != 0; n = n->prev)
{
U64 pos_aligned = AlignPow2(n->pos, page_size);
if(pos_aligned < n->cmt)
{
U8 *decommit_ptr = (U8 *)n + pos_aligned;
U64 decommit_size = n->cmt - pos_aligned;
AsanPoisonMemoryRegion(decommit_ptr, decommit_size);
decommit_memory(decommit_ptr, decommit_size);
n->cmt = pos_aligned;
}
}

#if ARENA_FREE_LIST
// rjf: free chain -- decommit everything above the first (header) page
for(Arena *n = arena->free_last; n != 0; n = n->prev)
{
U64 keep = AlignPow2(ARENA_HEADER_SIZE, page_size);
if(keep < n->cmt)
{
U8 *decommit_ptr = (U8 *)n + keep;
U64 decommit_size = n->cmt - keep;
AsanPoisonMemoryRegion(decommit_ptr, decommit_size);
decommit_memory(decommit_ptr, decommit_size);
n->cmt = keep;
}
}
#endif
}

//- rjf: arena push/pop helpers

internal void
Expand Down
3 changes: 3 additions & 0 deletions src/base/base_arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ internal void *arena_push(Arena *arena, U64 size, U64 align, B32 zero);
internal U64 arena_pos(Arena *arena);
internal void arena_pop_to(Arena *arena, U64 pos);

//- rjf: arena decommit of unused (rewound/free) pages
internal void arena_decommit_unused(Arena *arena);

//- rjf: arena push/pop helpers
internal void arena_clear(Arena *arena);
internal void arena_pop(Arena *arena, U64 amt);
Expand Down
29 changes: 25 additions & 4 deletions src/base/base_thread_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ C_LINKAGE thread_static TCTX *tctx_thread_local = 0;
internal TCTX *
tctx_alloc(void)
{
// 2MB commit quantum for scratch arenas (vs the 64KB default): scratch takes
// heavy churn on every thread (the linker pushes tens of GB through these);
// the larger quantum cuts VirtualAlloc(MEM_COMMIT) calls -- all serialized on
// the process address-space lock -- ~32x. Slack is <= 2MB per scratch arena
// past its high-water mark (2 arenas per thread), and arena_decommit_unused
// still trims page-granular, independent of the commit quantum.
#if PROFILE_TELEMETRY
thread_static static char name[2][1024];
raddbg_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", tid());
raddbg_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", tid());
Arena *arena_0 = arena_alloc(.name = name[0]);
Arena *arena_1 = arena_alloc(.name = name[1]);
Arena *arena_0 = arena_alloc(.commit_size = MB(2), .name = name[0]);
Arena *arena_1 = arena_alloc(.commit_size = MB(2), .name = name[1]);
#else
Arena *arena_0 = arena_alloc();
Arena *arena_1 = arena_alloc();
Arena *arena_0 = arena_alloc(.commit_size = MB(2));
Arena *arena_1 = arena_alloc(.commit_size = MB(2));
#endif
TCTX *tctx = push_array(arena_0, TCTX, 1);
tctx->arenas[0] = arena_0;
Expand Down Expand Up @@ -82,6 +88,21 @@ tctx_get_scratch(Arena **conflicts, U64 count)
return result;
}

//- rjf: scratch decommit (release committed-but-unused scratch pages back to OS)

internal void
tctx_scratch_decommit(void)
{
TCTX *tctx = tctx_selected();
for(U64 i = 0; i < ArrayCount(tctx->arenas); i += 1)
{
if(tctx->arenas[i] != 0)
{
arena_decommit_unused(tctx->arenas[i]);
}
}
}

//- rjf: lane metadata

internal LaneCtx
Expand Down
1 change: 1 addition & 0 deletions src/base/base_thread_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ internal TCTX *tctx_selected(void);

//- rjf: scratch arenas
internal Arena *tctx_get_scratch(Arena **conflicts, U64 count);
internal void tctx_scratch_decommit(void);
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)

Expand Down
4 changes: 4 additions & 0 deletions src/base/base_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,12 @@ internal void semaphore_release(Semaphore semaphore);
internal Semaphore semaphore_open(String8 name);
internal void semaphore_close(Semaphore semaphore);
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us);
internal B32 semaphore_take_n(Semaphore semaphore, U32 count, U64 endt_us); // blocking acquire of `count` permits (off hot path)
internal void semaphore_drop(Semaphore semaphore);
internal void semaphore_drop_count(Semaphore semaphore, U64 drop_count);
internal void semaphore_drop_if_room(Semaphore semaphore); // best-effort post; no-op if already at max
internal void semaphore_drop_n(Semaphore semaphore, U32 count); // release `count` permits in one syscall
internal B32 semaphore_drop_prev(Semaphore semaphore, U32 *prev_count_out); // release 1 permit + report the pre-release count (win32: exact, from ReleaseSemaphore; posix: best-effort sem_getvalue)

//- rjf: barriers
internal Barrier barrier_alloc(U64 count);
Expand Down
26 changes: 26 additions & 0 deletions src/codeview/codeview.h
Original file line number Diff line number Diff line change
Expand Up @@ -2701,6 +2701,32 @@ struct CV_TypeIndexInfoList
CV_TypeIndexInfo *last;
};

typedef struct CV_TiOff CV_TiOff;
struct CV_TiOff
{
CV_TypeIndexSource source;
U32 offset;
};

// Flat, allocation-free view over a record's type-index sites. For any record kind at
// most one of the two parts is populated:
// - `arr`: fixed-shape kinds point at a static per-kind table (zero allocation);
// member-walk kinds (FIELDLIST/METHODLIST/inlinee lines) point at an arena-
// materialized array.
// - homogeneous run (count-stride kinds: ARGLIST, SUBSTR_LIST, BUILDINFO, VFTPATH,
// CALLERS/CALLEES/INLINEES): offset(i) = run_base + i*sizeof(CV_TypeIndex).
// Emission order (arr order, then ascending run) matches the legacy
// CV_TypeIndexInfoList push order exactly; hash streams depend on it.
typedef struct CV_TiOffsets CV_TiOffsets;
struct CV_TiOffsets
{
const CV_TiOff *arr;
U32 arr_count;
CV_TypeIndexSource run_source;
U32 run_base;
U32 run_count;
};

typedef struct CV_TypeIndexArray CV_TypeIndexArray;
struct CV_TypeIndexArray
{
Expand Down
Loading
Loading