Repository navigation
Conversation
…inked `cargo run -- --hosted-clang` builds clang and LLD for x86_64-unknown-toyos (src/hostedclang.rs) from the LLVM commit the host's LLVM is, with the toolchain's clang against its C sysroot, into the host store under a key of their own: the host LLVM's key, the sysroot's, the recipe and the CMake options. The sources are the commit's, written from the fork's LLVM repository through an index of their own, never a checkout's files. The binaries say the version, revision and repository the host's clang says, `22.1.8-rust-dev` with bootstrap's suffix among them, since every object a clang compiles carries that in its `.comment`. Each is refused unless it is an x86-64 ELF naming no library it needs. Made only when asked for: its build is LLVM's, and its key moves with every sysroot's. On this host it took 23 minutes and gave a 147,533,968-byte clang and an 85,568,488-byte ld.lld, static PIEs; stripped, 121,926,824 and 70,926,168. What the build for a ToyOS host stopped on, measured with the tree's own toolchain on the clang and lld targets alone: Support's `alarm`, `wait` and `wait4` (Watchdog.inc, Program.inc), and clang's `std::ifstream`, which libc++ has only with `std::filesystem`; at the link, clangBasic's `lround`. No archive of either tool names ORC's `shm_open`, the interpreter's `scanf` or llvm-objdump's `ctime`, so libc gains none of them. Neither tool starts a child for `clang -c` or `ld.lld`: clang runs cc1 in its own process (`CLANG_SPAWN_CC1` off), and LLD starts one only for `--error-handling-script`. libc gains: - `wait` and `wait4`, answering ECHILD as `waitpid` does: libc starts no child. - `alarm`, kept by a thread of libc's that ends the process with SIGALRM's default action, exit 142, once it is due. A handler never runs, since `sigaction` keeps none: that is stage 3 of the child-process track. The seconds left, rounded up, are `alarmreq.rs`'s rule, held on the host. - `lround`, and `round` on the same rule, a half away from zero, held to the host's C library. `round` was `floor(x + 0.5)`, which answered -2 for -2.5 and 1 for 0.49999999999999994. - what libc++'s `src/filesystem` and `<fstream>` call: `setbuf`, `fseeko`, `ftello`, `truncate`, `pathconf`'s `_PC_PATH_MAX`, and `utimes`, refused ENOSYS: no call sets a file's times. libc++ is built with `std::filesystem`. `open` refuses every directory, so no descriptor names one for `openat`: `remove_all` walks a directory iterator, as libc++'s Windows does. `issues/bootstrap-cannot-build-llvm-clang-and-lld-for-a-toyos-host.md` is closed: ToyOS's build, with nothing supplied by hand, makes a clang and an `ld.lld` for x86_64-unknown-toyos, by CMake and not bootstrap. What bootstrap still owes is M3's (`issues/rustc-llvm-cannot-build-for-a-toyos-host.md`). The alarm and libc++ issues are renamed to what stays true of them, and the track records the owner's ruling of 2026-10-09. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Measurement: LLVM's clang and lld for a ToyOS hostAgainst sysroot
|
Measurement: libc++ with std::filesystem
|
Hosted build: cargo run -- --hosted-clangRun on the tree this branch's commit holds. The tree had
|
Mutations of libc's rulesEach patch was made against
|
Gates at the head's tree (f91864ee73a9)
|
Gate at the head's tree: cargo run -- --ci hostExit 0, "Host: 78 step(s), all green". The loom |
Gate at the head's tree: cargo run -- --ci host, part 1 of 3
|
Gate at the head's tree: cargo run -- --ci host, part 2 of 3
|
Gate at the head's tree: cargo run -- --ci host, part 3 of 3
|
|
Review of #809 at Net lines: Answers to the brief's questions:
BLOCKER
NOTE
SEND BACK |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…es back, and the bootstrap defect stays open Answers the review of b9f562d on #809. libc: `signal` and `sigaction` keep SIGALRM's disposition, and a due alarm under SIG_IGN is dropped instead of ending the process (`alarmreq::ends`, held on the host). `signal` takes and answers a pointer, as signal.h declares it: SIG_DFL is null, which no Rust fn pointer may be. A SIGALRM blocked in every thread still ends the process, since libc keeps each thread's mask in that thread and std's threads are none of libc's; the issue records it with its exit extended. The corpus: 206_libc_refusals reads utimes' ENOSYS, wait's and wait4's ECHILD and pathconf's EINVAL; 207_libc_names reads truncate through stat, setbuf's buffer and none, an fseeko/ftello round trip, lround's halves and its EDOM, pathconf's _PC_PATH_MAX, SIGALRM's disposition through signal and sigaction, and alarm(5), alarm(0), alarm(0). The owed lines go from the issues that carried them. Build: hostedclang's export is llvm::check_out_committed, which now takes the git directory it reads; static_elf goes, no test having shown it refuse anything; release.rs's layers are a Part of their own, so the hosted clang is no layer by type and NOT_A_LAYER goes. Issues: the bootstrap defect is open again, re-scoped: the CMake build in src/hostedclang.rs goes once bootstrap's ToyOS-host LLVM makes clang and lld, owner M3. remove_all's race is a defect of its own with an owner and an exit that removes REMOVE_ALL_USE_DIRECTORY_ITERATOR. The host-tools rows name src/hostedclang.rs; the track no longer reads as if M2 waits on HTTPS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Round 2, head ea95e75: mutations of the libc rulesEach patch applied with ignored-ends: exit 101diff --git a/userland/libc/src/alarmreq.rs b/userland/libc/src/alarmreq.rs
index d32a42302..ab79a8041 100644
--- a/userland/libc/src/alarmreq.rs
+++ b/userland/libc/src/alarmreq.rs
@@ -12,7 +12,8 @@ pub(crate) const SIG_IGN: usize = 1;
/// process: it does unless `SIGALRM` is ignored, since no handler runs
/// (`issues/an-alarm-reaches-no-sigalrm-handler.md`).
pub(crate) fn ends(handler: usize) -> bool {
- handler != SIG_IGN
+ let _ = handler;
+ true
}
/// When an alarm of `seconds` armed at `now` is due; none for 0, whichround-floor-half: exit 101diff --git a/userland/libc/src/fparts.rs b/userland/libc/src/fparts.rs
index 2531f2ae8..9f86a56a9 100644
--- a/userland/libc/src/fparts.rs
+++ b/userland/libc/src/fparts.rs
@@ -41,6 +41,8 @@ pub(crate) fn modf(x: f64) -> (f64, f64) {
/// `round`: `x` to the nearest integer, a half away from zero, keeping its
/// sign; an integer, an infinity or a NaN is itself.
pub(crate) fn round(x: f64) -> f64 {
+ #[allow(unreachable_code)]
+ return (x + 0.5).floor();
let (int, fraction) = modf(x);
if fraction.abs() < 0.5 {
return int;lround-saturates: exit 101diff --git a/userland/libc/src/fparts.rs b/userland/libc/src/fparts.rs
index 2531f2ae8..008cf828a 100644
--- a/userland/libc/src/fparts.rs
+++ b/userland/libc/src/fparts.rs
@@ -60,7 +60,7 @@ pub(crate) fn lround(x: f64) -> Result<i64, Domain> {
// `long`'s range as doubles, `[-2^63, 2^63)`; a NaN is in neither half.
const LIMIT: f64 = 9_223_372_036_854_775_808.0;
let rounded = round(x);
- if (-LIMIT..LIMIT).contains(&rounded) { Ok(rounded as i64) } else { Err(Domain) }
+ if rounded.is_nan() { Err(Domain) } else { Ok(rounded as i64) }
}
/// `logb` at a zero: POSIX's pole error, `-inf` with `errno` `ERANGE`.left-rounds-down: exit 101diff --git a/userland/libc/src/alarmreq.rs b/userland/libc/src/alarmreq.rs
index d32a42302..2b253cc2c 100644
--- a/userland/libc/src/alarmreq.rs
+++ b/userland/libc/src/alarmreq.rs
@@ -28,5 +28,5 @@ pub(crate) fn due(now: u64, seconds: u32) -> Option<u64> {
pub(crate) fn left(due: Option<u64>, now: u64) -> u32 {
let Some(due) = due.filter(|&due| due > now) else { return 0 };
// At most the `u32` seconds `due` was armed with.
- (due - now).div_ceil(NANOS_PER_SEC) as u32
+ ((due - now) / NANOS_PER_SEC) as u32
}Unmutated: |
Round 2, head ea95e75: gates
The host clang of this tree's LLVM says
request.txt tail: |
|
Review, round 2, of #809 at Net lines: Round-1 BLOCKERs
Round-1 NOTEs 1–6: all answered. The title now says "built for", the BLOCKER
NOTE
SEND BACK |
|
T14 at |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…, and the hosted clang's export folds into place Round 2's review of #809 asked three things. SIGALRM's number was written twice in libc, as 128 + 14 in alarm.rs and as a const in misc.rs. It is now alarmreq's SIGALRM, beside SIG_DFL and SIG_IGN, read by both; the host test compares it with signal.h's, which also gives the const a reader in toyos-libc-copies. sigaction's oldact for SIGALRM answered sa_flags and sa_mask 0 whatever the last act set. POSIX answers the previous action whole, so libc keeps the whole struct sigaction under a lock in place of the handler's atomic, and signal installs an action of the handler alone. 207_libc_names sets SA_RESTART and SIGUSR1's bit and reads both back; the C corpus runs only on metal, so the line is read by the T14's boot:testcases. hostedclang's export had one caller and three lines; place does it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Round 2, head ea95e75: logs behind the gates (review BLOCKER 1)The round-2 gates comment's measurements, in full, scrubbed of home-directory paths (
|
Round 2, head ea95e75: logs behind the gates (review BLOCKER 1), part 1 of 1
|
Round 2, head ea95e75: logs behind the gates (review BLOCKER 1), part 1 of 3
|
Round 2, head ea95e75: logs behind the gates (review BLOCKER 1), part 2 of 3
|
Round 2, head ea95e75: logs behind the gates (review BLOCKER 1), part 3 of 3
|
Round 3, head a5d7972: gatesMerge of
|
Round 3, head a5d7972: gates: QEMU suite log
|
Round 3, head a5d7972: gates: cargo run -- --ci host, part 1 of 3
|
Round 3, head a5d7972: gates: cargo run -- --ci host, part 2 of 3
|
…in the store `--hosted-clang` read the LLVM commit from the primary checkout's `.git/modules/rust/modules/src/llvm-project`, a repository no build fetches into: after #790 moved the gitlink to ToyOSOrg/llvm-project b7420fe it held no such commit (its origin is rust-lang's), and the build died with `unable to read tree`. The normal build obtains the commit one way: bootstrap's `Llvm` step checks the gitlink's commit out in the fork checkout and fetches it there, and `llvm::place` writes what builds read of it into `llvm/<key>/src` from that checkout; the C++ runtime is built from there. The LLVM now keeps the hosted clang's pathspecs there too, and the hosted clang builds from that directory of the LLVM it holds in use: the commit is the one the host LLVM's key names on any host, whether the LLVM was built here or found in the store, and no checkout is read. The pathspecs an LLVM keeps are now part of its key, so a change to either list moves it; the recipe moves to 5, and every LLVM key with it. The hosted clang's key names the LLVM's key, which now names its sources, so it drops its own copy of them; its recipe moves to 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
Round 4, head 4ce7ce7:
|
|
Review, round 3, of #809 at Earlier BLOCKERs
BLOCKER
NOTE
SEND BACK |
…clusively
`--hosted-clang` resolved the host's LLVM holding its worktree's build lock
only shared. When the store lacked it, bootstrap's `Llvm` step then ran in
the fork checkout, updating `src/llvm-project` and writing
`build/toyos-llvm`, beside every other shared holder of the same worktree,
which `buildlock.rs` names as the exclusive mode's job; the compiler and
sysroot builders reach `llvm::resolve` only inside exclusive phases.
`llvm::resolve_held` asks under the shared lock whether the store holds the
LLVM the fork names, and makes one only through `Held::act_if`, with the lock
exclusive; one the store holds is found with the lock shared, at no cost of
serialisation. `an_llvm_is_made_only_with_the_worktree_lock_exclusive`
holds both halves: a shared acquirer is kept out while the LLVM is made, and
a second worktree held shared by another process finds it without waiting.
The store design stays, on two measurements. A linked worktree whose LLVM
came from the store holds no LLVM commit in its fork's `src/llvm-project`:
of the 16 on this host with a fork checkout, none did (`git cat-file -e
<gitlink>^{tree}` exits 128 in each, its directory empty), and only the one
whose bootstrap built the LLVM held it. A fresh depth-1 fetch of the commit
from the fork's remote took 23 s and 278 MiB per worktree. Keeping the
hosted clang's sources in the stored LLVM costs 61.6 MB compressed per
`llvm` entry, packed as actions/cache packs it (posix tar, zstd -T0
--long=30): 102,023,921 bytes at recipe 4 against 163,600,770 at recipe 5,
of one LLVM with only its `src/` differing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Round 5, measurement (a): does a store-sourced worktree's fork hold the LLVM commit?For every worktree on this host with a fork checkout: its gitlink, the entries in
|
Round 5, measurement (b): the compressed
|
Round 5, BLOCKER 2: the lock fix's test and its mutations
|
Round 5, gates at 85d3eb4
|
Round 5, gates at 85d3eb4: --ci host
|
Round 5, gates at 85d3eb4: --ci host, part 1 of 3
|
Round 5, gates at 85d3eb4: --ci host, part 2 of 3
|
Round 5, gates at 85d3eb4: --ci host, part 3 of 3
|
|
Review, round 4, of #809 at Earlier BLOCKERs
BLOCKER
NOTENone. SEND BACK |
Main brings the app grants (#807) and the C cases linked without DWARF (#806); neither touches what this branch changes, and the merge is clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
…nd makes it if a sweep took it `choose_held` read `defect` with no lock on the LLVM's key, and only then took the key's use inside `keyed_made`. A placement elsewhere could sweep an LLVM unused for the keep time in that gap, since nobody held its key, and `keyed_made` then found a defect and ran `unbuilt`, which panicked where the right outcome is to make the LLVM. It now takes `buildlock::keyed_using` first and reads the defect under it, which no sweep takes from under it. A whole LLVM is returned held; one that is not has its use dropped, since the worktree lock orders before the key's, and goes through `act_if`, whose exclusive branch makes it; if the second look finds it whole, the loop takes the use again. `unbuilt` and its panic go. `choose_held` takes the whole-check as a parameter, as it takes the build, so `a_sweep_after_the_llvm_is_found_whole_fails_no_build` can run a real `keystore::sweep` right after the check answers: it panicked before this change and passes after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
Round 6, BLOCKER 1 at 1bfc3e9: the sweep in the gap, red before and green afterThe negative control is the old decision restored (
|
Round 6, gates at 1bfc3e9: --build-only
|
Round 6, gates at 1bfc3e9: the QEMU suite
|
Round 6, gates at 1bfc3e9: ci-host, part 1 of 3
|
Round 6, gates at 1bfc3e9: ci-host, part 2 of 3
|
Round 6, gates at 1bfc3e9: ci-host, part 3 of 3
|
Round 6, gates at 1bfc3e9: hosted-clang, part 1 of 1
|
|
Review, round 5, of #809 at Net lines: The merge: the branch's own diff under Earlier BLOCKERs
BLOCKERNone. NOTE
LAND |
Stage S1 of the self-hosting track (
issues/toyos-builds-itself.md): the toolchain build makes a clang and anld.lldfor anx86_64-unknown-toyoshost, statically linked, with nothing supplied by hand. No guest has run either: running them is M2a's work, which needs S2'spkgor a data disk that holds 148 MB. Nothing ships them in an image yet.Head
1bfc3e9d2: the first round's commitb9f562d9d, a merge ofmainatd6298c83e, round 2'sea95e75ac, a merge ofmainat198a9d38e(#790's LLVM pin), round 3'sa5d797234, round 4's4ce7ce79a, round 5's85d3eb452, a merge ofmainat2b1a0e746(#806, #807), and round 6's commit. Every gate below ran at that head's tree unless its row says otherwise.What changed, per decision
Measured first. LLVM at the commit the host LLVM is built from (
ceaf0fbb8440) was configured with CMake forx86_64-unknown-toyosand built with the tree's own toolchain (sysrootbc7e876cc60df9b3, before this branch) on theclangandlldtargets alone,n2 -k 100000:LLVM host triple: x86_64-unknown-toyos.Watchdog.cppneedsalarm.Program.cppneedswait4,alarmandwait. Everything else depends on Support.-include) and their definitions in a scratch object on the link line, a build log of 3,474 lines showed three compile failures, all clang'sstd::ifstream, which libc++ has only withstd::filesystem(CrossTranslationUnit.cpp,LayoutOverrideSource.cpp,CompilerInvocation.cpp), and two link failures onalarmalone,llvm-min-tblgenandclang-tblgenbuilt for ToyOS, from before the scratch object went on the link line.bin/lldlinked and named nothing else undefined.nm -uover every archive the run built named five undefined calls: clangBasic'sSanitizers.cpp.ocallslround; Support'sProgram.cpp.ocallsalarm,waitandwait4, and itsWatchdog.cpp.ocallsalarm.shm_open,shm_unlink,scanforctime. They belong to ORC, the interpreter andllvm-objdump, which neither tool links, so libc gains none of them (issues/libc-lacks-names-llvm-for-a-toyos-host-calls.mdkeeps them).wait/wait4: no CMake option takesProgram.cppout of Support (llvm/lib/Support/CMakeLists.txtlists it unconditionally), so the names must exist. Neither tool starts a child for this milestone: the build's cache readsCLANG_SPAWN_CC1:BOOL=OFF, soclang -cruns cc1 in its own process, and LLD callsExecuteAndWaitonly for--error-handling-script(lld/Common/ErrorHandler.cpp:305).wait/wait4answer what is true today,ECHILD.src/libcxx.rsdoes but withstd::filesystemon stopped onfseeko,ftello,setbuf,pathconf/_PC_PATH_MAX,truncate,utimes, and onremove_all'sopenat,fdopendir,unlinkat,O_DIRECTORY,O_NOFOLLOWandAT_REMOVEDIR. With the first six declared and-DREMOVE_ALL_USE_DIRECTORY_ITERATOR, it built with exit 0.libc (
userland/libc), each needed by the measurement:waitandwait4answerECHILDaswaitpiddoes, because libc starts no child.alarm(alarm.rs, rules inalarmreq.rs) arms one alarm per process and answers what the replaced one had left, rounded up. When it is due, a thread of libc's drops it ifSIGALRMis ignored, and otherwise ends the process with exit 142,SIGALRM's default action.signalandsigactionnow keepSIGALRM's action and answer the one they replace; every other signal is as before. libc keeps the wholestruct sigactionunder a lock, sosigaction'soldactis the previous action whole, flags and mask included, as POSIX says;signalinstalls an action of the handler alone.SIGALRM's number is declared once, inalarmreq.rs, and a host test compares it withsignal.h's.signaltakes and answers a pointer, assignal.hdeclares it:SIG_DFLis null, which no Rust fn pointer may be (the prototype gate intests/libc-archrefused theusizeI first gave it).issues/an-alarm-reaches-no-sigalrm-handler.mdwith its exit extended: a handler never runs, and aSIGALRMblocked in every thread ends the process at once instead of pending. libc keeps each thread's mask in that thread alone (pthread.rs,MASK), and a thread std starts is none of libc's, so nothing can read "every thread"; honouring it waits on stage 3's signals with the handler.issues/libc-has-no-alarm.mddesigned it: that design runs a handler and makeswait4answerEINTR, which needs the signals stage 3 builds.lround, androundon the same rule infparts.rs, rounding a half away from zero.roundwasfloor(x + 0.5), which answers -2 for -2.5 and 1 for 0.49999999999999994. A domain error setsEDOMand answersLONG_MIN.src/filesystemand<fstream>:setbuf,fseeko,ftello,truncate;pathconf, which answers_PC_PATH_MAXastoyos::fs::MAX_PATHand refuses any other name withEINVAL;utimes, refused withENOSYSbecause no call sets a file's times (issues/libc-refuses-what-toyos-cannot-yet-answer.md).The corpus reads each back (
tests/testcases/tinycc, the T14's testcases boot):206_libc_refusals:utimes-1ENOSYS;waitandwait4-1ECHILD;pathconfof an unknown name -1EINVAL.207_libc_names:truncateto 2 andstatreads 2, and of a missing file -1ENOENT;setbufon a caller's buffer holds ten bytes there with the file still empty,ftello10,fseeko3,ftello3,fgetc3;setbuf(f, NULL)puts three bytes in the file at once;signal(SIGALRM, …)answersSIG_DFLthenSIG_IGN, andsigactionreads backSIG_IGNwith theSA_RESTARTflag (0x10000000) and theSIGUSR1mask bit (0x200) it was given;alarm(5)0,alarm(0)from 1 to 5,alarm(0)0;lroundof 2.5, -2.5 and 0.49999999999999994 through volatile operands, 3 -3 0, and of 1e19LONG_MINwithEDOM;pathconf(_PC_PATH_MAX)4096..expectlines are written from the code, not from a run: the corpus runs only on the T14, and no QEMU path runs a C case here (--debug's GDB stub binds a port this host already has in use). The T14 reading atea95e75acpassed them; thesigactionflags-and-mask line is new at this head and has not run yet.libc++ (
src/libcxx.rs) is built withstd::filesystem. Becauseopenrefuses every directory, no descriptor names one foropenatandunlinkatto resolve against, soremove_allwalks a directory iterator (-DREMOVE_ALL_USE_DIRECTORY_ITERATOR), as libc++'s Windows does. That walk is the race libc++ names (D118134): a directory swapped for a link mid-walk sends the deletes outside the tree. It isissues/remove-all-follows-a-link-swapped-in-mid-walk.md, owner M2, whose exit is a directory handle the kernel resolves names against,openat/fdopendir/unlinkatover it, and the macro gone. Fakingopenatby joining paths would have hidden the race that function exists to close.The ToyOS-hosted clang and LLD (
src/hostedclang.rs,Keyed::HostedClang, store directoryhosted-clang/):Llvmstep. It builds only what clang and lld link, the waysrc/libcxx.rsbuilds the runtimes, with the toolchain's clang against the C sysroot'stoolchain.cmake. That is a second build of one LLVM for one host, soissues/bootstrap-cannot-build-llvm-clang-and-lld-for-a-toyos-host.mdstays open, re-scoped as the review's first option (orchestrator's ruling): owner M3, exit bootstrap installing clang andld.lldfor a ToyOS host andsrc/hostedclang.rs's CMake build gone. The module header names it.llvm/<key>/src, the way the C++ runtime's are (src/sysroot.rsbuilds libc++ fromllvm.dir.join("src")). The normal build obtains the commit one way: bootstrap'sLlvmstep checks the gitlink's commit out in the fork checkout, fetching from the URL.gitmodulesnames, andllvm::placewrites the kept pathspecs from that checkout into the store (check_out_committed, as onmain).llvm.rsnow keepslibcxx::SOURCESandhostedclang::SOURCESthere (llvm::sources), names both lists in its key (llvm::recipe, recipe 5), and holds an LLVM missing one as not whole (llvm::defect).--hosted-clangholds that LLVM in use and points CMake at itssrc, so it builds from the commit the LLVM key names on any host, whether the LLVM was built there or found in the store, and reads no checkout.rust/src/llvm-projectat the gitlink, asllvm::placedoes for the runtimes. That checkout holds the commit only in a worktree whose bootstrap built the LLVM: of the 16 linked worktrees on this host with a fork checkout, none whose LLVM came from the store held it (git -C rust/src/llvm-project cat-file -e <gitlink>^{tree}exit 128 in each, the directory empty, so git resolves to the fork worktree's own repository); only this branch's did. Putting it there is a fetch: depth 1 from the fork's remote, 23 s and 278 MiB, per worktree, since each fork worktree has its own submodule repository. Keeping the sources in the store instead costs 61.6 MB per compressedllvmentry, packed as actions/cache packs it (posix tar,zstd -T0 --long=30): 102,023,921 bytes at recipe 4 and 163,600,770 at recipe 5, of this host's one LLVM with onlysrc/differing. CI's recipe-4 entries (Linux host) are 163,571,967 and 163,562,484 bytes; no recipe-5 entry exists in CI, so CI's growth is the local delta, not a measurement of CI's.llvm::resolve_held, round 5).--hosted-clangholds its worktree's lock shared;resolve_heldasks under it whether the store holds the LLVM the fork names, makes one only throughHeld::act_ifwith the lock exclusive, asbuildlock.rsrequires of a build in the fork, and finds one the store holds with the lock still shared. Round 4 calledllvm::resolveunder the shared lock, so bootstrap could have run in the fork beside other shared holders.llvm::choose_held, round 6). It takesbuildlock::keyed_usingfor the LLVM's key and readsdefectunder it, so no sweep can take the LLVM between the answer and its use: a whole one is returned held. One that is not has its use dropped, since the worktree lock orders before the LLVM key's, and goes throughHeld::act_if, whose exclusive branch makes it; if the second look there finds it whole, the loop takes the use again. Round 5 readdefectwith no lock on the key and took the use only after, insidekeyed_made, so a placement elsewhere sweeping an LLVM unused for the keep time in that gap madekeyed_madecallunbuilt, which panicked where the LLVM should have been made.unbuiltand its panic are gone.choose_heldtakes the whole-check as a parameter, as it takes the build, so the test can run a realkeystore::sweepright after the check answers..git/modules/rust/modules/src/llvm-projectinstead. No build fetches into that repository, itsoriginis rust-lang's, and it did not hold The fork's LLVM gives a loop counter's recurrence only the wrap flags proven for it, a sysroot whose compilers lose a loop's last exit is refused, and the ScalarEvolution issue is closed #790'sb7420fe(unable to read tree).git ls-remoteofToyOSOrg/llvm-projectlistsb7420fe534bf…asrefs/heads/toyos-rustc-22.1-2026-05-19, the branch.gitmodulesnames, and round 4's run took that path in this worktree:Updating submodule src/llvm-project, then LLVMf8ab0dcf9b6f24f8built in 15:30.llvm/<key>/grows from 501M to 819M (du -sh), andsrc/from 137M to 455M.75420f854e592eadbuilt ata5d797234(same tree witness, LLVM90d48821e80c5da1),libc++.aandlibc++experimental.afor both targets are byte-identical once the LLVM key, embedded six times in eachlibc++.apath, is renamed (libcxx-compare.log).NATIVEbuild, with the C and C++ compilers the LLVM key names and every host library off.22.1.8-rust-devincluded:llvm-stringsmeasurement below. Every object a clang compiles carries this in.comment, and M2a compares objects byte for byte.cargo run -- --hosted-clang(src/flags.rs,src/main.rs). Its key moves with every sysroot key, so--build-onlyand CI never make it. The flag,src/lib.rs,src/buildlock.rs(the kind and its place in the lock order) andsrc/release.rsare outside the brief's literal fence: the entry point and what the new kind forces.release.rsnames a toolchain's layers by aPartof its own, four kinds, each mapped to itsKeyed; the hosted clang is no layer by type, where the first round had twounreachable!arms.static_elf: no test showed it refuse anything, and the property it read is measured below withreadelfinstead.hosted-clang/6079d1a299fd8083, from LLVMf8ab0dcf9b6f24f8(b7420fe534bf) and sysroot0e3936d1ead20157(the merge ofmainmoved the sysroot key). Its version strings were not read again this round. At round 4,hosted-clang/3745748acb2b4584, from the same LLVM and sysroot24bd8a26da529cbb: both binaries saidclang version 22.1.8-rust-dev (https://github.com/ToyOSOrg/llvm-project.git b7420fe534bf063b78a61c18fe6fb2bbcf4340e0)andLLD 22.1.8with the same repository and commit (version.log).Gates
Round 6, at
1bfc3e9d2; each log is on the pull request in full (comments "Round 6, BLOCKER 1 at 1bfc3e9", "Round 6, gates at 1bfc3e9" and its parts), paths scrubbed.cargo run -- --build-only0e3936d1ead20157, the merge ofmainhaving moved its keybuild-only.logcargo run -- --hosted-clangf8ab0dcf9b6f24f8in the store throughresolve_held(no "Building LLVM" line) and builthosted-clang/6079d1a299fd8083,bin/clang147,531,920 bytes,bin/ld.lld85,566,352 byteshosted-clang.logcargo run -- --ci hosta_sweep_after_the_llvm_is_found_whole_fails_no_buildandan_llvm_is_made_only_with_the_worktree_lock_exclusiveamong themci-host.logcargo test --test toyos-build(the QEMU suite, run because the sysroot key moved)suite.log,suite.uptimecargo test --lib -- llvm::tests buildlock::tests hostedclangunit.logsh mutate.sh(the negative control below)mutation-old-decision.loggit diff a5d797234 1bfc3e9d2 -- userland/ kernel/ tests/main's #806 and #807 through the merge. The branch's own delta there is byte-identical:git diff 198a9d38e a5d797234 -- userland/ kernel/ tests/andgit diff origin/main 1bfc3e9d2 -- userland/ kernel/ tests/compare equal (cmpexit 0)Round 5, at
85d3eb452:--hosted-clang0 (found3745748acb2b4584),--build-only0,--ci host0 (78 steps), the QEMU suite 0 (41 of 41), the unit tests 0; logs in round 5's comments.Round 4, at
4ce7ce79a:--hosted-clang0 (built3745748acb2b4584), an unfilteredreadelf -l -dof both binaries (DYN (Position-Independent Executable file),FLAGS_1 PIE, noINTERPprogram header, noNEEDEDentry),--ci host0,--build-only0; logs in round 4's comments. The binaries round 5 found are those bytes.Round 3, at
a5d797234:--ci host0 (78 steps), the QEMU suite 0 ("41 passed, 41 total"),--build-only0,cargo test -p toyos-libc-copies0 (52 passed), and--hosted-clang101 (unable to read tree, which round 4 fixes); logs in round 3's comments.Round 2, at
ea95e75ac, logs posted in full in round 3:--ci host0, the QEMU suite 0 (41 of 41),--build-only0,--hosted-clang0 (f335fdb3731f3fdd,bin/clang147,531,384 bytes,bin/ld.lld85,565,776 bytes), and an unfilteredreadelf -l -dof both binaries: static-pie, noINTERPprogram header, noNEEDEDentry. Both sayclang version 22.1.8-rust-dev,https://github.com/ToyOSOrg/llvm-project.gitandceaf0fbb8440c733a98691a88e39b3ce74443677, as the host clang of that tree's LLVM does (llvm-strings, round 2's gates comment). The measurement configure and builds of round 1 (sysrootbc7e876cc60df9b3): 0 / 1, the failures above.Negative control, round 6: the old decision restored as a checked patch (
lackingread with no lock on the key, thenchoose(store, fork, unbuilt), with thewholeparameter kept so the test reaches it), applied, built,a_sweep_after_the_llvm_is_found_whole_fails_no_buildandan_llvm_is_made_only_with_the_worktree_lock_exclusiverun, reversed,git status --porcelainempty, run again. Mutant exit 101: the new test panics withunbuilt's "left the store while this build held its worktree lock shared", the review's failure, and the round-5 test stays ok. Restored exit 0. Patch, script and log in the "BLOCKER 1" comment.Mutations, round 5, each a checked patch applied,
an_llvm_is_made_only_with_the_worktree_lock_exclusiverun, reversed and run again, the tree left clean; patches, script and logs in round 5's "BLOCKER 2" comment.choose_heldmaking the LLVM under the shared lock (the whole fix reverted in behaviour): exit 101, "an LLVM was built … beside shared holders"; restored, exit 0.choose_heldtaking the lock exclusive even for an LLVM the store holds: exit 101, the other worktree's shared holder never released ("the holder failed"); restored, exit 0.Mutation, round 4:
llvm::placekeepinglibcxx::SOURCESalone, a checked patch applied,cargo test --lib -- llvm::run, reversed, the tree left clean: mutated exit 101 (the_kept_sources_are_the_commit_sand nine others, whose LLVMdefectnow refuses), unmutated exit 0; patch, script and logs in round 4's comment.Mutations, round 3, each a checked patch applied, run with
cargo test -p toyos-libc-copies, then reversed, the tree left clean; patches and logs in round 3's mutations comment.alarmreq::endsanswering true forSIG_IGN: exit 101,a_due_alarm_ends_the_process_unless_sigalrm_is_ignoredred.roundasfloor(x + 0.5): exit 101,round_is_the_host_librariesandlround_is_the_host_libraries_…red.lroundsaturating out of range: exit 101,lround_is_the_host_libraries_…red.alarm's seconds left rounded down: exit 101,the_seconds_left_round_up_and_end_at_zerored.SIGALRMdeclared as 15: exit 101,sigalrm_is_the_number_signal_h_gives_itred.sa_flagsandsa_mask0, which is not the new207line.Oracle.
roundandlroundare compared against the host C library's, bit for bit, over every exponent, the halves, and the edges oflong.T14 (the orchestrator's). At
ea95e75ac:boot:testcases, both images' sha256 checked againstrequest.txt, judge exit 0,249 passed, 0 failed, 2 boot(s), 137 C cases eachccheck: 0, among them24_math_library,206_libc_refusalsand207_libc_names. Ata5d797234:boot:testcases, two boots, each image's sha256 checked againstrequest.txt(testcases3774f16b…f6201433,testcases-watchdog7f7a404f…c3c39ad0), judge exit 0,249 passed, 0 failed, 2 boot(s), 137 C cases eachccheck: 0, so207_libc_names'sigactionline matched on hardware. What it read of this branch stands at1bfc3e9d2in source: the branch's own delta underuserland/,kernel/andtests/is byte-identical toa5d797234's (Gates). It does not stand in bytes: the images it read were built ata5d797234's LLVM key (recipe 4) and withoutmain's #806 and #807, and at this head every guest binary is rebuilt against recipe 5's LLVM and sysroot0e3936d1ead20157. The QEMU suite above is what reads them, not the T14.What the reviewer is owed
static_elfis gone. New tests:an_llvm_is_made_only_with_the_worktree_lock_exclusive, a host test across two processes, because whether a lock keeps another process out is runtime behaviour reading cannot see; both its halves go red under their mutation.a_sweep_after_the_llvm_is_found_whole_fails_no_build, a host test that runs a realkeystore::sweepbetween the whole-check and what follows, because whether a sweep can land in that window depends on whichflockis held when, which reading the call order alone got wrong in round 5; red under the old decision, green under the new.llvm::choose_held. The negative controls are round 6's (the old decision restored) and round 5's mutation 1 (the LLVM made under the shared lock). I know of no independent oracle for it: the tests read the lock throughflock, from a fresh descriptor and from another process, the same primitive the lock is.src/+548 −48 (hostedclang.rs,llvm.rsandbuildlock.rs+497 −30 with their tests) anduserland/libc+292 −11; tests +190 −8 (tests/libc-archand the two corpus cases); issues +166 −107.What I am unsure of
REMOVE_ALL_USE_DIRECTORY_ITERATORis a macro internal to libc++, not a documented knob, and libc++ could rename it without notice; nothing pins it. Its issue's exit removes it.alarm's thread is a precursor of stage 3's "libc's own thread". Stage 3 should fold it rather than add a second one.SIG_IGNbeing dropped is held on the host as a rule (alarmreq::ends); no guest case lets an alarm come due, which only a wait on time can show, and its issue's exit carries it.LLVM_ENABLE_PLUGINSis on by default, so clang exports its symbols:.dynstris 5.7 MB. Turning it off would cost a rebuild and change no behaviour here, so it is left for S2's size work.libtoyos_c.a(std's panic locations). That isissues/two-checkouts-of-one-tree-build-different-guest-bytes.md, not this branch's.toolchain.yml'sllvmcache entry carry LLVM's sources that only--hosted-clangreads: 318M more per stored LLVM (du), 61.6 MB more per compressed entry. The release does not (release.rspacksstage2and the laid-out sysroot, nollvm/<key>/src). The alternative's cost is an on-demand fetch, 23 s and 278 MiB in each worktree that runs--hosted-clangwithout having built its LLVM, and a fetch of its own beside bootstrap's. The store design was kept because no checkout holds the commit without that fetch; on the numbers alone the trade is close, and an on-demand fetch would cost less in total on a host that rarely runs--hosted-clang. A host whose LLVM was swept while its sysroot stayed rebuilds the whole LLVM to run--hosted-clang(15:30 in round 4).--hosted-clang's gate runs found the LLVM in the store, soresolve_held's exclusive branch ran only in the host tests, against a stand-in for bootstrap. The loop's second pass (a sweep before the use is taken, thenact_iffinding the LLVM whole) runs in no test.act_ifis covered byan_llvm_is_made_only_with_the_worktree_lock_exclusive, not by a sweep in that test.🤖 Generated with Claude Code
https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C