Linux Bootstrap Walk — 780a0079 → HEAD
State as of Apr 11 2026 (session 3): The walk is complete. HEAD Linux binary is fixpoint-stable AND three real compiler bugs have been fixed on top. 16/16 example programs run, 18/18 async QSpec files pass (140 tests), 117 closure-related QSpec tests pass, plus broad regression coverage across arithmetic/strings/traits/etc. (466+ tests total).
Session-3 fixes (Apr 11, evening):
-
3440903f— Async cross-suspend spill/reload (mir_lower_expr_handlers.qz) SSA values computed in a binary-expression LHS leaked into post-resume blocks when the RHS contained an await or suspending intrinsic call (recv, sched_sleep, etc). LLVM flagged “Instruction does not dominate all uses” in concurrency.qz. Fix:mir_expr_contains_await()walker +mir_async_spill_if_await()helper that stores the LHS to a generator-frame dynamic local before RHS lowering. -
e2f829fd— MIR_AWAIT double-use UAF (codegen_instr.qz) Non-async await loweredawait hto pthread_join + load result + free(handle). Inside a loop condition (while count < await h), the second iteration read from the freed memory → segfault. Fix: after pthread_join, write state=-1 to handle slot 0 and drop the free, so subsequent awaits dispatch through the state-cached future-path. Small bounded leak for idempotent semantics. -
73a14d56— Closure capture walker async cases (mir.qz)mir_collect_captures_walkhad no case branches for NODE_AWAIT / NODE_GO / NODE_ASYNC_CALL, sox -> x + await hdidn’t detecthas a free variable and emittedload ptr %hagainst an undefined alloca. Fix: push left child onto the walker worklist for all three async nodes.
Plus brainfuck.qz (user example) got two .unwrap() additions where v5.26’s
Option-returning stack.pop() and map_get() were being assigned directly to Int
vars. All 4 BF programs (Hello World, Print ‘A’, 3+5, digits 0-9) now run.
Goal is to walk forward to HEAD (7ba5fa12, Apr 8).
This handoff is self-contained — a new session (or later-you) can execute from cold. Read this file first, then run the Quick Resume block, then follow the Walk Plan.
Session-2 Findings (READ BEFORE PLANNING)
The session-1 wall analysis was partially wrong in important ways. Key corrections:
-
Wall 2 hypothesis was wrong. The handoff originally claimed: “the binary’s compiled-in knowledge is irrelevant — it uses the source’s registration.” This is false. The binary’s
tc_register_builtinsruns in compiled code, not driven by source. Newtc_register_builtincalls in source are dead code from the binary’s perspective. The binary at rev N can only typecheck calls to intrinsics that were registered in the source it was compiled FROM. This means each new intrinsic (map_new,sb_append_byte, etc.) requires walking exactly to the commit that introduced it — no leapfrogging. -
TYPE_MAP fossil starts at 97d088ce, not 0c73a2fc. The “API cleanup Phase 1” commit added
TYPE_MAPreferences intypecheck_builtins.qz(lines 511, 655, 667) but never defined the constant intype_constants.qz. This propagates through every subsequent commit including HEAD. Same fix as before: addconst TYPE_MAP = 25toself-hosted/shared/type_constants.qz(it’s a fossil alias for TYPE_HASHMAP). -
Bold jump 780a0079 → 54eb4965 fails on multiple compounding walls, not just one. The cascade observed: cconv fossils → @cfg imports → TYPE_MAP fossil → “Map” annotation unknown → map_new/map_set/__map_get_raw call sites unknown → sb_append_byte call sites unknown. Each must be addressed individually.
-
The g_intrinsic_registry wall. At 97d088ce,
codegen_intrinsics.qz:63declaresvar g_intrinsic_registry = map_new(). This is a hard wall: you can’t compile 97d088ce with a binary that lacksmap_new. To get a binary withmap_new, you have to walk to the registration commit (97d088ce itself or earlier). Chicken and egg. -
Strategic implication: there is no shortcut. The walk needs a per-milestone plan that walks through the introduction commit of each new intrinsic in dependency order. Big strides only work between milestones; at each milestone the previous binary is insufficient.
Session-2 Progress (extended walk)
5 new Linux goldens built this session:
quartz-linux-x64-015b0305-golden— Apr 3, P30 (cconv stubbed)quartz-linux-x64-32fa81b3-golden— Apr 4, end of dogfooding (78-commit jump from 015b0305)quartz-linux-x64-97d088ce-golden— Apr 6, API cleanup Phase 1 (registers map_new/map_get/__map_get_raw)quartz-linux-x64-aef2610b-golden— Apr 6, Open UFCSquartz-linux-x64-0c73a2fc-golden— Apr 6, Self type alias / Map rename (rebuilt with bilingual annotation handler)
Permanent fossils discovered (NEVER defined in git history, even at HEAD):
ast_func_is_cconv_c— referenced in mir_lower.qz and resolver.qz at every commit, never definedmir_register_poll_callee/mir_is_poll_callee— only defined at 54eb4965 (mir.qz)mir_func_set_cconv_c/mir_func_is_cconv_c— only defined at 54eb4965 (mir.qz)mir_block_set_switch/mir_block_get_switch_values/mir_block_get_switch_targets— only at 54eb4965cg_emit_struct_hash_eq_functions/cg_emit_custom_map_functions— only at 54eb4965tc_type_is_hashable/tc_struct_is_hashable/tc_hashable_rejection_reason— never defined in source at any tested commitTYPE_MAPconstant — referenced from 97d088ce onward, never defined intype_constants.qz(must alias to TYPE_HASHMAP=25)NODE_IS_CHECKconstant — referenced in mir_lower files, never defined (stub as -1)
Standard patch set applied at every milestone commit (97d088ce and later):
- Strip cconv_c if-blocks from
mir_lower.qz(2-4 sites) - Strip cconv_c if-block from
resolver.qz:851(rewrite toif false) - Strip poll_callee if-block from
mir_lower_expr_handlers.qz - Add
const TYPE_MAP = 25totype_constants.qz(alias for TYPE_HASHMAP) - Add
const NODE_IS_CHECK = -1tonode_constants.qz(stub) - Stub the hashable check block in
typecheck_expr_handlers.qzANDtypecheck.qz(both have similar blocks) - Substitute
Map<→HashMap<andmap_new<→hashmap_new<in all source files - (97d088ce only) Substitute
__map_get_raw(→__hashmap_get_raw(in typecheck_builtins.qz; rewritevar g_intrinsic_registry = map_new()→hashmap_new()in codegen_intrinsics.qz - (0c73a2fc only) Add
return type_constants::TYPE_HASHMAP if annotation == "HashMap"line to typecheck.qztc_parse_typeto make the rebuilt binary bilingual; fix slice length bug introduced by Map<→HashMap< substitution (change0, 4to0, 8after"HashMap<") - (54eb4965 only) Strip 3
@cfglines fromquartz.qz(and the imports they gate); substitutesb_append_byte(→sb_append_char(everywhere
Crucial correction to wall model from the previous handoff:
The binary’s compiled-in tc_register_builtins and tc_parse_type contain the
intrinsic table and type-annotation handlers that the binary uses at runtime. These
are FROZEN at compile time. New tc_register_builtin calls or new annotation == "X"
lines in source are dead code from the binary’s perspective until a fresh binary is
built FROM that source. Implication: there is no leapfrogging over commits that introduce
new intrinsics or type annotations — each must be walked through individually.
Session-2 Final Status (extended — HEAD FIXPOINT ACHIEVED)
🎯🎯🎯 LINUX QUARTZ COMPILER AT HEAD (7ba5fa12) IS FIXPOINT-STABLE.
gen1 → gen2 → gen3 all build successfully. Saved as quartz-linux-x64-7ba5fa12-golden.
The final breakthrough came from identifying a latent codegen bug in 54eb4965 that
was exposed by the new mangle/suffix caching patterns at afad28c0 and 77d968d5. Both
commits use an as_int(result) / as_string(cached) round-trip through an intmap to
deduplicate strings. The 54eb4965 binary miscompiles this pattern when the cache grows
large (symptom: cross-module struct type lookups mysteriously fail at typecheck time,
after resolve_pass1 reports ~9.7GB memory use). Reverting those cache calls back to
raw "#{prefix}$#{suffix}" string interpolation restores correctness.
Reverts needed (in addition to standard patch set):
self-hosted/resolver.qz:string_intern::mangle(a, b)→"#{a}$#{b}"(2 call sites)self-hosted/middle/typecheck_registry.qz:_cached_suffix(tc, name)→"$#{name}"(8 call sites; the_cached_suffixfunction definition itself can be left as dead code)
These two reverts plus the standard patch set produce a fixpoint-clean HEAD binary.
Final golden chain (12 Linux x64 goldens, 7 new this session):
quartz-linux-x64-bce5e646-golden— Mar 25, original cross-compilequartz-linux-x64-bbded285-golden— Mar 26quartz-linux-x64-27b133a1-golden— Mar 27quartz-linux-x64-ac0ef57c-golden— Mar 29quartz-linux-x64-d12ea4b6-golden— Mar 30quartz-linux-x64-780a0079-golden— Apr 1quartz-linux-x64-015b0305-golden— Apr 3 ✨ session 2 (cconv stubbed)quartz-linux-x64-32fa81b3-golden— Apr 4 ✨ session 2 (78-commit jump)quartz-linux-x64-97d088ce-golden— Apr 6 ✨ session 2 (registers map_new)quartz-linux-x64-aef2610b-golden— Apr 6 ✨ session 2 (Open UFCS)quartz-linux-x64-0c73a2fc-golden— Apr 6 ✨ session 2 (Map rename, bilingual)quartz-linux-x64-54eb4965-golden— Apr 8 ✨ session 2 (corridor exit, cconv defs land)quartz-linux-x64-7ba5fa12-golden— Apr 8 ✨ session 2 (HEAD — defective but linked)
The Map struct dispatch wall at 54eb4965 was resolved by stubbing
tc_all_builtin_names in typecheck_registry.qz to return vec_new() instead of
builtins.keys. The 0c73a2fc binary’s UFCS dispatcher looks up a struct named
“Map” because of the HashMap→Map rename, and that struct doesn’t exist. The stub
loses error-message suggestions but compiles.
Why HEAD binary is defective and how to fix it
The HEAD binary I built crashes on its own source. Likely causes:
- Stubbed hashable check — the binary’s compiled
tc_parse_typefor HashMap types skips hashable validation. May leave types in an invalid state for downstream passes. - Stubbed NODE_IS_CHECK = -1 — the binary’s compiled code path for is-binding
never fires (since no real node has kind -1). User code with
if val is Some(x)silently misbehaves. - Stubbed mir_register_poll_callee — the binary’s compiled code doesn’t track
poll callees, so functions called from
$polldon’t get the alwaysinline-disable annotation. May cause mimalloc integration issues if poll-callees use stack alloc. - The .keys stub —
tc_all_builtin_namesreturns empty, breaking error suggestion logic but more importantly anything that iterates builtin names. - Map<K,V> annotation handler — the binary only recognizes
HashMap<...>prefix (length 8), notMap<...>prefix (length 4). It does recognize bare “Map” and bare “HashMap”. If the binary’s own source usesMap<...>anywhere, it crashes when re-compiling itself.
Path to fixpoint-clean HEAD binary:
- Add a real
Map<...>prefix branch alongsideHashMap<...>intc_parse_type(don’t just substitute — keep BOTH prefixes working) - Define
NODE_IS_CHECKto a real unused value (e.g., 89) instead of -1, AND ensure the parser sets that node kind forispatterns - Add a real
tc_type_is_hashablefunction in typecheck_helpers.qz that returns true for primitives and false otherwise (a “good enough” hashable check) - Rewrite
tc_all_builtin_namesto iterate via a different API instead of.keys, or add ahashmap_keysUFCS dispatch override - Rebuild from 54eb4965 → … → HEAD with these cleaner patches
The cleaner-patch approach should produce a binary that fixpoints. Estimate: 1-2 hours in a fresh session armed with this handoff.
Worktrees to resume from:
quartz-54eb4965— fully patched, builds cleanly. Best resume point.quartz-head— patched HEAD source, has a defective HEAD binaryquartz-0c73a2fc,quartz-aef2610b,quartz-97d088ce,quartz-32fa81b3,quartz-015b0305— intermediate stable points
System-level changes from session 2:
- mimalloc installed via linuxbrew (
brew install mimalloc) - The clang shim at
tools/llc-shim/clang(in quartz-head worktree only) has been updated to add-L/home/linuxbrew/.linuxbrew/lib -Wl,-rpath,/home/linuxbrew/.linuxbrew/libfor mimalloc linking. Other worktrees still use the original shim.
Quick Resume (do this first each session)
# 1. Restore the most advanced golden into a worktree at its native commit.
# If a quartz-780a0079 worktree already exists from a prior session, skip the add.
cd /home/mathisto/projects/quartz-git
git worktree list | grep -q quartz-780a0079 || \
git worktree add /home/mathisto/projects/quartz-780a0079 780a0079
cp self-hosted/bin/backups/quartz-linux-x64-780a0079-golden \
/home/mathisto/projects/quartz-780a0079/self-hosted/bin/quartz
cp self-hosted/bin/backups/quake-linux-x64-780a0079-golden \
/home/mathisto/projects/quartz-780a0079/self-hosted/bin/quake
# 2. Shim directory — needed for every worktree. The bce5e646 worktree has the
# canonical copy. If it was cleaned up, the scripts are reproduced inline below
# (see "Shim scripts" section).
mkdir -p /home/mathisto/projects/quartz-780a0079/tools/llc-shim
cp /home/mathisto/projects/quartz-bce5e646/tools/llc-shim/* \
/home/mathisto/projects/quartz-780a0079/tools/llc-shim/
# 3. Sanity check — binary runs, version string matches source VERSION
/home/mathisto/projects/quartz-780a0079/self-hosted/bin/quartz --version
# Expect: "quartz 5.12.21-alpha" (or similar — the VERSION const from 780a0079 source)
# 4. Confirm it can still self-build (catches any regression since last session)
cd /home/mathisto/projects/quartz-780a0079 && \
PATH=/home/mathisto/projects/quartz-780a0079/tools/llc-shim:$PATH \
./self-hosted/bin/quake build 2>&1 | tail -5
# Expect: "Built: self-hosted/bin/quartz (debug mode, ...)"
If Quick Resume fails, the Linux binary at 780a0079 is broken. Fall back:
- Try the next-oldest golden:
d12ea4b6,ac0ef57c,27b133a1,bbded285,bce5e646. - If all fail, restart the full bootstrap from Mac cross-compile per
memory/cross_compile_recipe.md.
Walk Plan
Four walls remain between 780a0079 and HEAD. They must be crossed in this order:
- cconv_c fossil corridor (
015b0305→54eb4965^, ~15 commits) - sb_append_byte + map_new + other intrinsics (first hard-wall commit to identify)
- @cfg(feature) imports (
5d1aaa23, Apr 7) - mimalloc linkage (
afad28c0, Apr 8) and everything up to HEAD
Each wall has a concrete strategy below. After each wall is crossed, save the new
binary as a golden at self-hosted/bin/backups/quartz-linux-x64-<commit>-golden and
matching quake-linux-x64-<commit>-golden.
Wall 1 — cconv_c fossil corridor
Symptom: Any commit from 015b0305 (Apr 3) through 54eb4965^ (Apr 7) fails with:
Module 'ast' has no function 'ast_func_is_cconv_c'
Module 'mir' has no function 'mir_func_set_cconv_c'
Module 'mir' has no function 'mir_register_poll_callee'
Root cause: These functions are called in self-hosted/backend/mir_lower.qz but
never defined in the source tree during this range. They were only added to source at
54eb4965 Memory optimization Phase 1 (Apr 8). Earlier commits compiled because the
committed binary at each commit had the functions baked in from pre-commit builds.
Source-only walking hits the dangling references.
Strategy: Patch mir_lower.qz to remove the 4 call sites. The code pattern is:
if ast::ast_func_is_cconv_c(s, node)
mir::mir_func_set_cconv_c(func)
end
Appearing at roughly lines 2446-2447 and 2824-2825 in 780a0079-era layout. The entire
if-block can be deleted — cconv_c is a C-calling-convention marker that only
matters for extern-C functions, which the self-hosted compiler source doesn’t use.
mir_register_poll_callee has similar treatment — find the call site and either comment
out or delete.
Strategy A — single-file patch at the LAST corridor commit:
Rather than patching every commit in the corridor, jump directly to the commit right
before 54eb4965 (which is d9a4230c, also the @cfg(feature)-imports wall — see
Wall 3). If that’s the only commit you touch, you patch once and cross the whole corridor.
BUT you still have to cross @cfg(feature) imports to reach d9a4230c, so this
approach couples walls 1 and 3.
Strategy B — jump to 54eb4965 directly:
54eb4965 is where cconv_c functions actually get defined. Any later commit has them.
Try jumping directly with the 780a0079 binary. This bypasses the entire corridor.
HOWEVER, 54eb4965 is also past the @cfg(feature) imports wall, the map_new wall,
and the sb_append_byte wall, so it will fail with those errors instead of cconv.
Worth a single attempt for the error log.
Strategy C (recommended) — walk-and-patch:
# Create worktree at first corridor commit
git worktree add /home/mathisto/projects/quartz-015b0305 015b0305
cd /home/mathisto/projects/quartz-015b0305
# Install binary + shims
cp ../quartz-780a0079/self-hosted/bin/quartz self-hosted/bin/quartz
cp ../quartz-780a0079/self-hosted/bin/quake self-hosted/bin/quake
mkdir -p tools/llc-shim
cp ../quartz-bce5e646/tools/llc-shim/* tools/llc-shim/
# Apply cconv patch — delete the if-blocks in mir_lower.qz
# Find exact line numbers first:
grep -n 'ast_func_is_cconv_c\|mir_func_set_cconv_c\|mir_register_poll_callee' \
self-hosted/backend/mir_lower.qz
# Then use Edit tool (or sed) to remove each if-block.
# Build
PATH=$PWD/tools/llc-shim:$PATH ./self-hosted/bin/quake build 2>&1 | tail -10
# If success, save golden
cp self-hosted/bin/quartz ../quartz-git/self-hosted/bin/backups/quartz-linux-x64-015b0305-golden
cp self-hosted/bin/quake ../quartz-git/self-hosted/bin/backups/quake-linux-x64-015b0305-golden
# The 015b0305 binary has the cconv stubs compiled in. Use it to compile the NEXT
# corridor commit, which will have the same cconv references but will compile because
# the compiler binary accepts/ignores them now — WAIT NO, that's wrong. The binary's
# behavior doesn't affect what source it can compile, only how it typechecks.
# The NEXT commit's source ALSO has the cconv references, so the patch must be
# re-applied at each commit in the corridor until 54eb4965 lands the real definitions.
Patch template (apply at every corridor commit):
# In the worktree's self-hosted/backend/mir_lower.qz:
# 1. Find all ast_func_is_cconv_c call sites (should be 2)
# 2. Delete the entire if-block wrapping each one (3 lines: if/call/end)
# 3. Find mir_register_poll_callee call sites (if any) and delete
Better: write a small Python or sed script that applies the patch, and run it at each worktree as you create it. Template:
cat > /tmp/cconv_patch.sh <<'EOF'
#!/bin/bash
# Strip cconv_c and poll_callee call sites from mir_lower.qz.
# Safe because these functions are C-calling-convention markers that
# the self-hosted compiler doesn't need.
F=self-hosted/backend/mir_lower.qz
python3 <<PYEOF
import re
with open("$F") as f: src = f.read()
# Delete blocks: if ast::ast_func_is_cconv_c(...)\n mir::mir_func_set_cconv_c(...)\n end
src = re.sub(
r' if ast::ast_func_is_cconv_c\([^)]*\)\n\s*mir::mir_func_set_cconv_c\([^)]*\)\n\s*end\n',
'', src)
# Delete any lingering poll_callee calls (adjust regex if real form differs)
src = re.sub(r'.*mir_register_poll_callee.*\n', '', src)
with open("$F", "w") as f: f.write(src)
PYEOF
EOF
chmod +x /tmp/cconv_patch.sh
Run /tmp/cconv_patch.sh in each corridor worktree before quake build. VERIFY the
patch matches the actual source at each commit — the regex may need tightening if the
formatting drifts. After patching, grep -c 'cconv_c\|poll_callee' self-hosted/backend/mir_lower.qz
should return 0 (or very close).
Commits in the corridor (in forward order): Get the list with:
git log --reverse --oneline 780a0079..54eb4965 --format="%h %s"
Walk them one at a time: worktree, install binary, patch, build, save golden, advance. The binary from commit N is used to compile commit N+1 (with fresh patches).
Success criterion: A golden binary at 54eb4965 that was built from unpatched source
(since 54eb4965 defines the functions for real, no patch needed at that specific commit).
From there, the corridor is crossed and walls 2-4 become the remaining work.
Wall 2 — new intrinsics (map_new, sb_append_byte, etc.)
Symptoms (seen in prior session):
Unknown type 'Map<String, Int>'
Undefined function: map_new
Undefined function: map_set
Undefined function: sb_append_byte
Known introduction points:
map_newregistered intypecheck_builtins.qzat97d088ce(Apr 6, “API cleanup Phase 1”)sb_append_byteintrinsic added at5f9448b1(Apr 7, “Fix UTF-8 double-encoding”)Map<K,V>type usage in self-hosted source starts around the same era
Strategy: These are genuine feature additions, not fossils. The walker must cross the exact commits that register them. The pattern is:
- Binary at rev N doesn’t know the new intrinsic.
- Source at rev N+1 registers the intrinsic in
typecheck_builtins.qz(in the new source). - The N binary reads N+1 source, parses
typecheck_builtins.qz, and now knows about the new intrinsic. It can then typecheck code that uses it elsewhere in N+1 source.
This works because typecheck_builtins.qz is parsed before other modules. The binary’s
compiled-in knowledge is irrelevant — it uses the source’s registration.
So the walk SHOULD just work — each commit’s typecheck_builtins.qz registers what
its own source needs. The wall we hit previously was because we were jumping OVER the
registration commits. The corridor walk (Wall 1) will naturally traverse these commits
in order, so Wall 2 may resolve itself as a side effect of Wall 1.
If it doesn’t auto-resolve: Look at the build error for the specific undefined symbol,
git log -S 'tc_register_builtin(tc, "<symbol>"' to find the registration commit, and
either jump directly to that commit or step through the intervening ones.
Wall 3 — @cfg(feature) imports
Symptom:
error[QZ0101]: @cfg must precede a definition (def, struct, enum, type, newtype, extern)
--> self-hosted/quartz.qz:28:1
| import codegen_wasm
Root cause: Parser support for @cfg on import statements was added at
5d1aaa23 (Apr 7, “CLI style library … @cfg(feature) imports”). Usage in
self-hosted/quartz.qz to gate LSP/REPL/WASM imports landed at d9a4230c
(Apr 8, “Gate LSP and REPL behind @cfg(feature)”). Any binary built from source
before 5d1aaa23 lacks the parser support.
Strategy: Walk through 5d1aaa23 using a binary that predates it (any walled
binary). The 5d1aaa23 commit adds parser support AND may or may not use it in
self-hosted/quartz.qz. Check:
git show 5d1aaa23:self-hosted/quartz.qz | grep -B1 '^@cfg'
If 5d1aaa23’s own quartz.qz uses @cfg on imports, you can’t compile 5d1aaa23 with a
pre-5d1aaa23 binary. In that case, temporary source patch: remove the @cfg lines
from 5d1aaa23’s quartz.qz (3 sites), build, save golden. The resulting binary has the
parser support compiled in, so it can compile the UNPATCHED d9a4230c source that
actually uses the feature.
Template patch (adjust for 5d1aaa23’s exact line layout):
# Find and delete these 3 lines (keep the `import` line that follows each):
@cfg(feature: "wasm")
@cfg(feature: "lsp")
@cfg(feature: "repl")
Wall 4 — mimalloc
Symptom: Linker error cannot find -lmimalloc at afad28c0 (Apr 8, “Phase 2
memory optimization: mimalloc …”) and later.
Fix: Install mimalloc on Linux before crossing afad28c0.
brew install mimalloc # linuxbrew — matches the existing llvm/clang install
# OR
sudo apt install libmimalloc-dev
After install, ldconfig -p | grep mimalloc should show the library. The afad28c0
source’s build commands already include -lmimalloc when target is Linux, so no patch
needed — just the system dependency.
Shim scripts (reproduce if tools/llc-shim/ is missing)
Every walked worktree needs tools/llc-shim/llc and tools/llc-shim/clang on PATH.
If the bce5e646 worktree has been cleaned up, reproduce:
tools/llc-shim/llc
#!/usr/bin/env bash
# Strip duplicate `declare` lines, rewrite @__stderrp → @stderr before passing to real llc.
# Bce5e646-era codegen emits both bugs for Linux cross-compile.
set -e
REAL_LLC=/home/linuxbrew/.linuxbrew/bin/llc
input=""
new_args=()
for arg in "$@"; do
if [[ -z "$input" && -f "$arg" && "$arg" == *.ll ]]; then
input="$arg"
new_args+=("__PATCHED__")
else
new_args+=("$arg")
fi
done
if [[ -z "$input" ]]; then exec "$REAL_LLC" "$@"; fi
patched=$(mktemp --suffix=.ll)
trap 'rm -f "$patched"' EXIT
awk '/^declare / { if (seen[$0]++) next } { print }' "$input" \
| sed -E 's/@__stderrp$/@stderr/' > "$patched"
final_args=()
for arg in "${new_args[@]}"; do
if [[ "$arg" == "__PATCHED__" ]]; then final_args+=("$patched"); else final_args+=("$arg"); fi
done
exec "$REAL_LLC" "${final_args[@]}"
tools/llc-shim/clang
#!/usr/bin/env bash
# Inject -no-pie when linking. Bce5e646-era cross-compiled IR is not PIC but modern
# Linux clang defaults to PIE. Only affects link invocations (no -c flag, has .o input).
set -e
REAL_CLANG=/home/linuxbrew/.linuxbrew/bin/clang
has_c_flag=0
has_o_input=0
for arg in "$@"; do
[[ "$arg" == "-c" ]] && has_c_flag=1
[[ "$arg" == *.o && ! "$arg" == *-o ]] && has_o_input=1
done
if [[ $has_c_flag -eq 0 && $has_o_input -eq 1 ]]; then
exec "$REAL_CLANG" -no-pie "$@"
else
exec "$REAL_CLANG" "$@"
fi
Both scripts need chmod +x. Scope to the worktree only — do NOT install globally.
When to retire the shims: After each new golden, test whether bare llc/clang work. Cross-compile fixes landed incrementally in the forward history, so at some point both shims become unnecessary. Suggested test after each golden:
PATH=/home/linuxbrew/.linuxbrew/bin:$PATH ./self-hosted/bin/quake build 2>&1 | tail -5
If this succeeds without the shim PATH prefix, delete tools/llc-shim/ and proceed
without it. If it fails, keep the shim.
Success criterion
Reached: Golden binary at 7ba5fa12 (HEAD) that passes quake build and
quake fixpoint. Save as quartz-linux-x64-7ba5fa12-golden and quake-linux-x64-7ba5fa12-golden.
Optionally copy into self-hosted/bin/ of the main trunk checkout (do NOT git add —
the committed binary must remain macOS arm64 for Mac users).
At that point, the walk is complete and the walker scaffolding is retired:
- Delete
~/projects/quartz/bs-walk2/,/tmp/overlay_527/,fix_*.pyrewriters - Delete intermediate worktrees:
git worktree listthengit worktree remove <path> - Keep the golden backups — they’re cheap insurance
- Update
memory/linux_bootstrap.mdto reflect HEAD-reached state
Resume points if this document gets lost
self-hosted/bin/backups/has 7 Linux goldens from this session (bce5e646 through 780a0079)memory/linux_bootstrap.mdhas the full state recordmemory/cross_compile_recipe.mdhas the Mac-side recipe if you need to regenerate from scratchmemory/bce5e646_codegen_quirks.mddocuments the llc/clang shim rationalememory/macos_mimalloc_ioaccelerator_bug.mdexplains why the Mac bootstrap is broken
One more thing
CLAUDE.md prime directive #2 (“no holes left behind”) obligates logging this as real
work owed: The fossil commits in git history (10bc4539, 015b0305→54eb4965, etc.)
are a repo hygiene problem. CI never verified source-only builds at these commits, so
the broken state went unnoticed. Once HEAD is reached and the walk is complete, consider
filing a ROADMAP item for source-only CI — something like “CI job that rebuilds from a
known-good binary + each commit in forward order, catching dangling references when they
land, not months later during bootstrap recovery.”