π SSOT: ARCHITECTURE.md Β· governance CLAUDE.md
hexa-lang compiler β build-speed analysis and the native-codegen μ κ³΅λ² (orthodox path).
This started as a perf / resource / speed ROI brainstorm. The measurement (lever 0) plus an exhaustive survey re-centred it on one conclusion: the build bottleneck is clang (80% of build wall), clang is an external dependency, and the orthodox fix is hexa-lang's own native codegen β
compiler/, which already exists and nearly self-hosts. Finishcompiler/; do not optimize the C path. The C-path levers (W1/W2/F, lower in this file) are interim relief only.SSOT for staged compile progress is
compiler/PLAN.md; this file is the analysis + ordered sequence that points into it.
At-a-glance
pipeline : .hexa --> hexa_v2 --> C (1 TU) --> clang --> native --> run
μ κ³΅λ² : finish compiler/ β the native codegen (aprime_cc, 85K LoC)
ALREADY EXISTS and self-compiles; blocker = codegen
super-linear perf. order = compiler/PLAN.md #18 S1->S4.
interim : W1/W2/F speed the C path; relief only while the C fallback lives
naming : drop bootstrap vestiges (_v2 _c2 aprime s4) β separate cycle
measured : clang = 80% of build wall; runtime.c recompile = 53% alone
status : S1-S5 done Β· πΈ S7 RFC 063 ALL PHASES CLOSED:
P0 πΈ (corpus 4/4 byte-eq vs clang)
P1 πΈ (F-P1-RUNEQ trivial.hexa execβexit 42 via static-link)
P2 πΈ (ELF Linux execβexit 42 + multi-obj CALL reloc on ubu-2)
P3 πΈ (F-P3-ZERO-EXTERN-OBJ + FULL-RUNEQ β stripped PATH PASS)
campaign 21 cycles on s7-p0-cycle1 (FF merged compiler-native-codegen, origin pushed)
Levers W1, W2, and F all make the C compilation faster β but the C step exists only to hand work to clang, an external compiler. The orthodox path removes the dependency entirely:
current (C-path, depends on clang):
.hexa --> hexa_v2 --> C --> [ clang -O2 ] --> native binary
^^^^^^^^^^^^^
external dependency Β· 80% of build wall
μ κ³΅λ² (direct native codegen, self-hosted):
.hexa --> hexa codegen --> machine code --> native binary
(own instr-sel + regalloc + emitter + linker)
Why this is the orthodox path, not lever I "ranked last":
- It IS hexa-lang's stated architecture β
HEXA-NATIVE-ONLY.md,@D g5("no third-party codegen backend"),@N n1("direct native codegen is on the roadmap"; the C path is "fallback, not the architecture"). - clang is the measured 80% of build wall β removing the dependency removes the bottleneck at its root, not at the margin.
- "loses clang's optimizer" is not a cost of the path β it is scope hexa must own (its own optimizer passes). Keeping clang to borrow its optimizer is precisely the external dependency to shed.
The C path stays as the documented portability fallback during the
transition (consistent with @F f2: fallback C emission is allowed, it
just is not the architecture).
The earlier estimate ("RFC-scale, 10d+, build it from scratch") was
WRONG β a measured falsification. The direct native codegen path
already exists as compiler/ (85,023 lines of hexa, binary aprime_cc):
- Full IR pipeline, built and spec'd (RFC-018):
.hexa-> lex -> parse -> resolve -> check -> lower(HIR) -> lower(MIR, SSA CFG) -> codegen (MIR->LIR) -> emit(.s). IR structs incompiler/ir/{hir,mir,lir}.hexa. - CPU targets exist:
compiler/codegen/arm64_darwin.hexa(linear-scan regalloc),x86_64_linux.hexa,thumbv7em_eabihf.hexa. - It self-compiles:
aprime_ccbyte-cleanly compiles its own hardest modules (parser, codegen) to assembly; the frontend typechecks the entire 21K-line compiler source with 0 errors. - Measured corpus:
aprime_cc-direct passes 30/60 smoke tests vs interp.
What it is NOT yet:
- Not fully self-hosting. The dominant blocker is codegen performance β
_lower_hexpr(compiler/lower/hir_to_mir.hexa, 1209 lines) is super-linear (~0.5 s/stmt); the 21K-line full compiler does not finish codegen inside a 9-min cap. This is compiler-engineering, not a language gap. Correctness is "almost closed" (residual: nested-struct SIGSEGV x3, atlas-verifier divergence x5, macro-depth x3). - The native path still shells
as/clang -cto assemble the.sand systemldto link. That is the thin assembler, not the optimizing compiler β the 80%-of-build-wall clang optimizer cost is exactly whatcompiler/'s own MIR->LIR replaces. An own assembler (dropas) +hexa_ldintegration (the in-house linker exists, v1.1) are later refinements, not blockers.
So the μ κ³΅λ² is to FINISH compiler/, not to START it.
The canonical staged order is already written in compiler/PLAN.md #18
("aprime_cc self-host"); that file is the SSOT β this file only
cross-links it:
S1 codegen performance β DONE 2026-05-20. The dominant blocker, closed.
step-1 (commit 8ab732bd): per-phase instrumentation HEXA_CG_PROFILE=1
+ baseline β lower_hir (HIR->MIR) confirmed THE super-linear phase.
step-2 (commit ce4c9706): hoisted _lower_hexpr's LowerCtx accumulators
(locals/blocks/bindings) to module scope, killing the per-recursion
O(N^2) deep-copy of the growing LowerExprResult{ctx}. MEASURED:
lower_hir at N=400 = 1527 ms -> 9 ms (~170x); O(N^2) -> near-linear.
byte-eq 7/7 fixtures PASS (perf-only, zero semantic change).
S2 full-closure codegen μμ£Ό β DONE 2026-05-20 (commit a94ed6e3).
The full 24k-line compiler closure codegens to completion: `.s`
emits in ~94s (was a 9-min-cap timeout pre-S1). Per-phase:
lower_hir 971ms (S1 holds at full scale), codegen 7.4s (the new
long pole, ~79% β not a blocker, <2min total), emit 1.2s. codegen
diagnostics clean β 0 errors, 0 unmapped-builtins, 11 HX4001 warns.
S2-followup (non-blocking): codegen sub-phase instrumentation.
S3 self-host fixpoint β PROVEN 2026-05-20. gen1 (built via the hexa_v2
-> C -> clang path) and gen2 (built by assembling gen1's emitted .s
of the full closure + a 3-fn shim `gen2_shim.c` mapping native-asm
builtin names `sha256_hex`/`list_dir`/`mono_ns` to the `hexa_*`
runtime exports β the asm path lacks build_aprime.sh's sed rewrites)
both compile the same flatten of `compiler/main.hexa` to BYTE-
IDENTICAL `.s` β 10,094,662 B, md5
`29426b801cb072b2861bd608e884b20b`. The compiler reproduces its own
emitted code: gen3 follows transitively. Honest caveat: the shim is
a *bootstrap-time naming-convention bridge*, not a semantic gap.
S4 drop the hexa_v2 dependency β wiring DONE 2026-05-20. New
`tool/build_hexac.hexa` (hexa-native, NOT .sh β hexa-first) runs
the native path: flatten -> aprime_cc --emit=asm -> 3-fn naming
shim -> clang assemble+link. Mirrors the gen2 chain verified by
S3 fixpoint. hexa_v2 is no longer in the compiler's own build
path. clang remains as assembler+linker only β S7 closes that.
Verification build deferred to post-rate-limit-reset.
post-fixpoint (beyond compiler/PLAN.md #18 β analysis-side continuation):
S5 native `hexa build` backend β wiring DONE 2026-05-20 (commit
30dc7a77). HEXA_BACKEND=native selector + resolve_native_cc() added
to cmd_build, env-gated OFF by default (C path byte-identical when
unset), smoke-verified β native path builds + runs a trivial program
(exit 42). Native path is 2-stage (aprime_cc --emit=asm, then clang
assemble+link runtime.c) because `aprime_cc --emit=exec` does not
self-link (no runtime.o/crt) β that self-linking is an S7 item.
Default-on flip waits on S1-S4 (fixpoint) + S7 (native linker).
S6 optimization passes β the basic passes (const_fold, dce, inline)
are ALREADY wired (`compiler/main.hexa --opt=0..3`). S6 = extend
them toward parity with clang -O2 via the HEXA-NATIVE-ONLY.md
G-0..G-11 axis ladder (typed scalar lane A1/A2 first, then loop /
SIMD / tiling) β that ladder is the substantive gap.
S7 own assembler + hexa_ld β drop `as` / `ld` / `clang`. Design
contract: RFC 063 (`docs/rfc/rfc_drafts_2026_05_12/rfc_063_s7_
native_assembler_linker.md`), 4 phases with falsifiers (F-P0-
OBJEQ / F-P1-RUNEQ / F-P3-ZERO-EXTERN), ~12-18 cycles total.
P0 scaffold landed 2026-05-20 (`compiler/emit/macho_arm64.hexa`
β types + entry + encoding checklist + Mach-O constants).
P1 scaffold landed (`tool/hexa_ld.hexa` β CLI + types + link
entry + relocation/symbol-resolution checklist). Implementation
across future S7-{P0,P1,P2,P3} sub-agent cycles. Final closure
of "μμ‘΄λ μμ΄ μΈλΆ / μμ ν hexa-native".
S1 is both the dominant blocker AND a performance problem in its own right β it is itself an instance of this whole analysis (the compiler compiling slowly). Start there.
W1/W2/F are now explicitly interim: worth doing only as relief while the C fallback still exists; they do not substitute for the μ 곡λ².
Surveyed read-only while S1-step-2 runs, so each step is dispatch-ready.
- S2 β full-closure codegen μμ£Ό. Prereq: S1. Re-run the full
compiler/main.hexaimport+use closure (38 files / 21,832 lines) throughaprime_cc; confirm.semits within time (it hit the 9-min cap pre-S1). First point full-scope codegen-correctness is measurable. - S3 β self-host fixpoint. Full
.s-> assemble -> link -> 2nd-genaprime_cc. byte-diff 1st-gen (built via hexa_v2) vs 2nd-gen (built via aprime_cc) = the fixpoint proof. Harness:tool/build_aprime.sh. - S4 β drop hexa_v2.
tool/build_aprime.shstage 2 is the hexa_v2.hexa->.ctranspile. Once S3's byte-diff PASSES, swap stage 2 toaprime_ccβ the compiler no longer needs the C transpiler (nor clang) to build itself. - S5 β native
hexa buildbackend.compiler/main.hexaalready has--emit=asm|obj|exec(default exec β it already produces linked executables,:125/:151).self/main.hexa::cmd_build(:1710) currently drives hexa_v2->C->clang. Add a backend selector (env/flag) socmd_buildcan invokeaprime_cc --emit=exec. This delivers the lever-0 build-speed win to user programs, not just the compiler. - S6 β optimization passes. CORRECTION (2026-05-20 prep): the basic
passes ARE wired, not stubs.
compiler/main.hexadocuments--opt=0..3(:128/:140-142):--opt=1const_fold,--opt=2const_fold + dce,--opt=3inline_small + const_fold + dce 2-pass.compiler/optimize/{const_fold (160L), dce (152L), inline (412L)}.hexaare implemented (RFC-018 Β§9). S6 = extend toward parity withclang -O2via theHEXA-NATIVE-ONLY.mdG-0..G-11 ladder (typed scalar lane A1/A2 first, then loop / SIMD / tiling) β the ladder is the substantive gap, not the basic optimizer scaffold. - S7 β own assembler + linker (μμ ν hexa-native).
compiler/main.hexaalready marksas/ld/xcrunas "L1 keepers β replaced when self-as lands" (:2,:806); an L1->L2 migration scaffold +compiler/intrinsics/intrinsics.hexaexist. CORRECTION (2026-05-20 prep):tool/hexa_link.hexais a clang wrapper per its own header ("Thin clang wrapper. Takes N .c files ... clang handles C-level symbol resolution") β NOT a from-scratch linker. S5's finding confirms scope:aprime_cc --emit=execdoes not self-link (no runtime.o / crt). S7 = land self-as(LIR -> object code directly, skipping.s->as) + write a real native linker for Mach-O arm64 / ELF x86_64 (relocation records + crt + runtime.o). The deepest step β what closes "μμ‘΄λ μμ΄ μΈλΆ" fully.
The bootstrap era left version-suffix / codename file names. As the
μ κ³΅λ² makes compiler/ the one real compiler, these vestiges go β
completely. The conventions being abandoned:
- version suffixes β
_v2,_c2, any<n>generation marker - codenames β
aprime(the native compiler's temporary codename) - stage numbers baked into names β
s4_...
Vestige inventory (known; canonical names are the plan β adjust before
the rename cycle runs if any name is contested). The CANON campaign
(2026-05-25, see CANON.md) already resolved the compiler-binary and
_v2-suffix rows β marked DONE below; remaining rows belong to the
native-codegen (μ 곡λ²) cycle, not CANON:
| vestige | what it is | canonical |
|---|---|---|
aprime_cc Β· tool/build_aprime.sh |
native codegen compiler + its build | hexac Β· tool/build_hexac.sh (μ κ³΅λ² cycle β pending) |
hexa_v2 Β· self/native/hexa_cc.c |
C transpiler binary + C seed source | DONE (CANON M3b) β hexa_v2 git-removed β build artifact build/hexa_v2, auto-bootstrapped via hexa cc; hexa_cc.c kept as the Go-1.4-style C seed (NOT renamed) |
self/codegen_c2.hexa |
C-backend codegen (SSOT) | DONE β renamed to self/codegen.hexa (PR #387); the version suffix is gone |
tool/s4_flatc_post.py |
flatten post-processor | tool/flatc_post.py (μ κ³΅λ² cycle β pending) |
self/native/*.bak.* Β· *.pre* |
dead bootstrap snapshots | DONE (CANON M1/M2) β .bak.* untracked + _v2.c generated files deleted |
Execution is a SEPARATE atomic cycle β NOT folded into S1. A mass rename
rewrites import paths across the whole tree; mixing it into a perf
change makes both unreviewable, and the shared-checkout branch churn
(this session alone saw the branch flip twice) compounds the conflict
risk. Do it in one worktree, atomically, when S1 is parked. New files
created from here on already use clean names β the vestige convention is
abandoned for anything new immediately.
.hexa source βββΆ hexa_v2 βββΆ C βββΆ clang βββΆ native βββΆ run
(frontend) transpiler (1 TU) (-O?) binary
β β β β β β
[H] stream [F] split [F] split [A] -O2 [I] no-C [D] arena
[G] incr. cache [B] -j [C] cache [E] clone fix
Key observation: the current flatten (all modules merged into a single C
translation unit) is the shared root of three problems β out-of-memory,
slow compile, and zero incrementality. That is why F is the keystone
of the C path: breaking flatten unlocks B, C, and G for free.
These levers speed the C-transpile path. They are interim: valid only
while the C fallback exists. The μ κ³΅λ² (lever I) supersedes all of them.
| # | Lever | Axis | Cost | Gain (estimate) | Lossless? |
|---|---|---|---|---|---|
| 0 | Compile profiling / instrumentation | diagnostic | 0.5d | fixes the ranking of the other 9 | yes |
| A | clang -O2 on emitted Cself/main.hexa:1910 cmd_build; runtime :913/915). No work left. |
runtime speed | done | β | β |
| B | per-module .c -> clang -j parallel |
compile speed | 1d* | compile wall / N cores | yes |
| C | content-hash C/.o cache (ccache-style) β measured: caching runtime.o alone removes 53% of build wall, see W1 |
compile speed | 1-2d* | skip recompile of unchanged modules | yes |
| A2 | -O0/-O1 fast-dev build flag (current build is -O2-always) |
compile speed | 0.5d | app.c clang 3.6s -> 0.7s (5x), see W2 | yes (flag-gated) |
| D | HEXA_VAL_ARENA reclaim wiring |
resource | 1-2d | lower memory peak on long compiles | env opt-in |
| E | struct_pack_map shallow-clone gotcha fix |
correctness + resource | 1d | removes bug + avoids needless clones | yes |
| F | drop flatten -> separate compilation (C-path keystone) | all 3 axes | 4-7d | resolves OOM + unlocks B/C/G | yes |
| G | module-level incremental compile (on top of F) | compile speed | in F | 1-line edit: full build -> 1 module | yes |
| H | streaming codegen (no whole-AST in memory) | resource | 5-8d | large drop in compile memory peak | yes |
| I | μ κ³΅λ² β finish compiler/: hexa-lang's own native codegen (binary aprime_cc). Replaces the clang stage outright β see "μ 곡λ²" section. |
all axes | finish (~70% built) | removes clang = removes 80% of build wall at the root | yes (self-hosted) |
* B and C depend on F (split) β neither parallelism nor caching is
possible while the build is a single flattened translation unit.
Wave 1 β cheap, lossless (~3d): 0 profile -> E clone fix. Lever
A (-O2) was found already shipped; the 0 profile decides whether
D or H is the higher-priority memory lever.
Wave 2 β C-path keystone (F separate compilation, ~1 week): breaking
flatten makes B, C, and G follow for free. The OOM wall (full
compiler/main.hexa flatten exceeds memory on every host) is resolved
structurally here.
Wave 3 β the μ κ³΅λ² (I, finish compiler/ native codegen): the
orthodox fix β replace the clang stage with hexa-lang's own MIR->LIR
native codegen. compiler/ already exists (85K LoC, binary aprime_cc)
and nearly self-hosts; the work is to FINISH it, not start it. Ordered
sequence S1->S7 is in the "μμ" section above. W1/W2/F (Waves 1-2) are
interim relief on the C path, not substitutes for it.
Every gain figure above is an estimate. Running 0 (cost ~= free, it is
a diagnostic, not a cost-bearing fire) turns the ranking into measurement:
if clang dominates compile wall, B wins; if the hexa_v2 frontend
dominates, H wins. A half-day profile validates or refutes the 1-week
bet on F before it is made.
Target: hexa build self/main.hexa β the hexa CLI itself (183 KB source
-> 155 KB / 3331-line C). Profiled on the local macOS host with
/usr/bin/time -l.
| Stage | Wall | Peak RSS | Share |
|---|---|---|---|
1+2 flatten + transpile (hexa_v2) |
2.4s | 103 MB | ~21% |
3 clang -O2 β runtime.c (9574 lines) |
6.2s | 164 MB | ~53% |
3 clang -O2 β app.c (transpiled program) |
3.6s | 120 MB | ~31% |
| total (one-shot link 9.3s + transpile 2.4s) | ~11.7s | 163 MB | 100% |
Reference: app.c at -O0 compiles in 0.70s vs 3.57s at -O2 β a 5x
optimizer tax.
- clang is 80% of build wall (9.3s / 11.7s). The transpiler is cheap.
So
H(streaming the transpiler) has only a small ceiling β butI(native codegen) does not: it replaces the clang stage outright. That is whyI, notH, is the μ 곡λ². runtime.crecompile = 53% of every build (6.2s). The same unchanged 9574-line C file is recompiled at-O2on every build (one-shot link,self/main.hexa:1910). It is larger than the transpiled program itself.-O2costs 5x vs-O0on the transpiled C. The build is-O2-always β the wrong default for the edit-compile-test loop.- No OOM on this target (peak 163 MB). The known OOM is specific to
compiler/main.hexa, whose flatten merges all imported stdlib into one translation unit β a result already settled (seeproject_compiler_selfbuild_blockers), not re-fired here. LeverFstays the C-path keystone for that import-heavy / OOM target class only; monolithic builds likeself/main.hexado not need it.
- W1 β cache
runtime.o: build it once at-O2, link the cached object. 6.2s -> ~0s amortized. Build wall 11.7s -> ~5.5s (~2x). Cost ~0.5d. Lossless (the runtime TU does not change between app builds). - W2 β
-O0/-O1fast-dev flag:app.c3.6s -> 0.7s. Stacked on W1: 11.7s -> ~3.1s (~3.8x faster dev loop). Release builds keep-O2. Cost ~0.5d. F(flatten split) remains the C-path keystone but is now scoped: it earns its cost on import-heavy / OOM targets, not on the common build.
These are interim β they speed the C path that the μ κ³΅λ² (lever I)
ultimately removes.
-
2026-05-20 β initial brainstorm captured (10 levers, 3 waves).
-
2026-05-20 β lever
0(profiling) DONE β see "Lever 0 β measured profile" above. clang is 80% of build wall;runtime.crecompile alone is 53%. Re-ranked: W1 (runtime.ocache, ~2x) + W2 (-O0dev flag, stacked ~3.8x) are the measured top quick wins; leverA(-O2) was found already shipped. -
2026-05-20 β μ κ³΅λ² recorded. The orthodox path is not optimizing the C path β it is removing it: clang is an external dependency and the measured 80% of build wall. Lever
Ipromoted to keystone; W1/W2/F demoted to interim C-path relief. -
2026-05-20 β exhaustive survey: the μ κ³΅λ² is ~70% built. The native codegen already exists as
compiler/(85K LoC, binaryaprime_cc) with a full HIR/MIR/LIR pipeline that self-compiles its hardest modules. No new RFC needed β the staged sequence iscompiler/PLAN.md#18 (S1->S4). The earlier "RFC-scale, build from scratch" estimate was falsified. -
2026-05-20 β renamed
ROI.md->COMPILER.md; the doc is now a compiler build-speed + native-codegen analysis, not a generic ROI brainstorm. -
2026-05-20 β naming policy recorded (user directive "v2 μ΄λ°κ²λ μλ¨, μ λΆ κΉλνκ²"). Bootstrap vestiges (
_v2/_c2/aprime/s4stage-numbers) are abandoned: new files use clean names immediately; existing vestige files are renamed in a separate atomic worktree cycle (see "Naming β drop the bootstrap vestiges"). Not folded into S1. -
2026-05-20 β S1-step-1 DONE (commit
60946b8d). Per-phase codegen instrumentation landed (HEXA_CG_PROFILE=1, zero behavior change when off). Baseline on thefn big()N-stmt probe:lower_hir(HIR->MIR) is THE super-linear phase β 15/63/372 ms at N=100/200/400 (~5.9x per N-doubling = O(N^2)+);codegen(6/10/29) andemit(1/1/4) are near-linear. Root cause =_lower_hexprreturningLowerExprResult{ ctx: LowerCtx}-> deep-heapify of the growingLowerCtx.blocks. S1-step-2 = hoist those accumulators to module scope. -
2026-05-20 β work moved to a dedicated worktree branch
compiler-native-codegen. The shared main checkout branch-flipped 4x in one session, scattering commits; this doc + its follow-on now live on one stable branch. -
2026-05-20 β S2..S7 prep survey done (read-only, in parallel with the S1-step-2 sub-agent). Each step now has a dispatch-ready sub-plan with file references β see "Step detail β S2..S7". Key finding: S7 (own assembler) is already scaffolded β
compiler/main.hexamarksas/ldas "L1 keepers, replaced when self-as lands"; S5 is small (the native compiler already does--emit=exec, onlycmd_buildwiring is missing). -
2026-05-20 β S1 DONE. step-2 (campaign-branch commit
ce4c9706) hoisted_lower_hexpr'sLowerCtxaccumulators to module scope, killing the O(NΒ²) deep-copy. Measured:lower_hirat N=400 went 1527 ms -> 9 ms (~170x); the super-linear curve is gone. byte-eq 7/7 fixtures PASS β verified correctness-preserving. The dominant self-host blocker is closed. Next: S2 (full-closure codegen μμ£Ό). -
2026-05-20 β S2 PASS (campaign-branch commit
a94ed6e3). The fullcompiler/main.hexaclosure (24k lines) codegens to completion throughaprime_ccin ~94s β pre-S1 it timed out at the 9-min cap..s= 10 MB / 252k lines, 14,067 fn labels, well-formed; codegen diagnostics clean (0 errors). S1's fix confirmed effective at full scale (lower_hir 971ms). New long pole = codegen MIR->LIR (7.4s, ~79%) but not a blocker. Next: S3 (assemble + link self-host fixpoint). -
2026-05-20 β S5 wiring DONE (campaign-branch commit
30dc7a77, done in parallel with S3).cmd_buildgained aHEXA_BACKEND=nativeselector +resolve_native_cc()β purely additive, env-gated off, C path byte-identical when unset; smoke-verified native build of a trivial program. Finding that feeds S7:aprime_cc --emit=execdoes not self-link, so the native path still delegates assemble+link to clang β confirming the native-linker work S7 must do. -
2026-05-20 β S3 dispatch hit a rate-limit (Anthropic account quota, reset ~07:50 KST). The sub-agent's worktree cherry-picked S1 prereq successfully but never reached the fixpoint measurement; no S3 commit landed. Will re-dispatch after the reset.
-
2026-05-20 β deeper S6/S7 prep done read-only (rate-limit-safe). Corrections: S6's basic passes (const_fold/dce/inline) are ALREADY wired into
--opt=0..3, not stubs β S6's real gap is the HEXA-NATIVE- ONLY G-ladder. S7'stool/hexa_link.hexais a clang wrapper per its own header, NOT a from-scratch linker β S7 needs a new native object- file linker. -
2026-05-20 β πΈ S3 PROVEN β SELF-HOST FIXPOINT. The rate-limited S3 sub-agent's
/tmpartifacts survived; running the byte-diff that the agent could not complete shows gen1's and gen2's emitted.sof the fullcompiler/main.hexaclosure are byte-identical β 10,094,662 B, md529426b801cb072b2861bd608e884b20b. gen1 = built viahexa_v2-> C -> clang; gen2 = assembled from gen1's.s+ a 3-fn shim (gen2_shim.c, asm-path naming bridge forsha256_hex/list_dir/mono_ns). The compiler reproduces its own emitted code. hexa-lang's stated north-star "β‘μΈν°νλ¦¬ν° νκΈ°Β·self-host" reaches its first measured proof point. Campaign branch state: S1 β + S2 β + S5 β (wiring) + S3 β . Next: S4 (drop hexa_v2 from build_aprime.sh stage 2 β now concretely doable). -
2026-05-20 β S4 wiring DONE. New
tool/build_hexac.hexa(hexa- native build orchestrator β NOT a.sh, honoring hexa-first per a PreToolUse warn). Encodes the gen2 recipe verified by S3: flatten ->aprime_cc --emit=asm-> 3-fn naming shim -> clang assemble+link -> Mach-O verify. The compiler's own build no longer goes through hexa_v2. parse-gate PASS. Verification build (actual run + byte-diff vstool/build_aprime.shoutput) deferred to post-rate- limit-reset. clang remains as assembler+linker at stage 4 β that is the LAST external toolchain dependency for the compiler's own build, scheduled for elimination by S7 (own assembler +hexa_ld). -
2026-05-20 β S7 RFC 063 DRAFTED.
docs/rfc/rfc_drafts_2026_05_12/ rfc_063_s7_native_assembler_linker.mdβ 4-phase design (P0 Mach-O arm64 object emittercompiler/emit/macho_arm64.hexa/ P1 native Mach-O linkertool/hexa_ld.hexa/ P2 ELF x86_64 / P3 flip default), each with a falsifier. Total estimate ~12-18 cycles β the campaign goal "μμ ν hexa-native" is multi-week, this session lands the design contract + S1-S5 wiring. Honest scope: RFC drafted- scaffold plan, not implementation. Implementation across future S7-{P0,P1,P2,P3} sub-agent cycles.
-
2026-05-20 β S7 P0 + P1 scaffolds LANDED. Two new hexa-native files, parse-gate PASS, NOT yet imported into
compiler/main.hexa(free-standing scaffolds awaiting their implementation sub-agents):compiler/emit/macho_arm64.hexaβ P0 LIR -> Mach-O arm64.oemitter. Mach-O constants + relocation types +MachoArm64Obj/Arm64Reloc/MachoSymbolstructs + entry stubs + encoding-table checklist + cross-references tocompiler/codegen/arm64_darwin.hexaandcompiler/emit/asm .hexa(the .s emitter to mirror).tool/hexa_ld.hexaβ P1 native Mach-O linker. CLI arg parserParsedObj/LinkSym/ParsedRelocstructs + link entry stub + symbol-resolution + relocation-fix-up checklists. Explicitly NOT a rename oftool/hexa_link.hexa(which stays as the C-path clang wrapper until S7 P3 retires it). Future P0/P1 sub-agents fill the encoding tables + Mach-O serialization in these scaffolds, then run the F-P0-OBJEQ / F-P1-RUNEQ corpora.
-
2026-05-20 β πΈ S7 P0 CLOSED Β· corpus 4/4 byte-eq vs clang.
compiler/emit/macho_arm64.hexaμ 7 cycles (header serialize β LIR walker + code byte emit β nlist_64 + strtab β relocation_info β mem ops + N_UNDF β STP/LDP pre/post-index + MOV-with-SP alias fix βcompiler/main.hexa --backend=nativeμμ΄μ΄λ§ + F-P0-OBJEQ FULL CORPUS PASS). μ€μ aprime_cc --emit=obj --backend=nativeκ° trivial/if/while/fib.hexa μ__textλ₯Όclang -c -arch arm64μ__textμ byte-for-byte λμΌνκ² emit. otool Β· nm Β· otool -rV λͺ¨λ oracle ν΅κ³Ό. F-P0-OBJEQ closure gate μ μ μ μΆ©μ‘±. -
2026-05-20 β πΈ S7 P1 CLOSED Β· F-P1-RUNEQ static-link PASS.
tool/hexa_ld.hexaμ 8 cycles (parser .o β ParsedObj round-trip β MH_EXECUTE skeleton β __text payload + LC_MAIN entryoff β LC_SYMTAB- __LINKEDIT β codesign post-link β MH_NOUNDEFS + LC_BUILD_VERSION + cycle 5 false-positive fix β LC_DYLD_CHAINED_FIXUPS + EXPORTS_TRIE
- UUID + SOURCE_VERSION + MH_DYLDLINK β first runnable exec β F-P1-
RUNEQ static-link with synthetic runtime stub). real corpus
trivial.hexa(=fn main(){exit(42)}) κ° OUR hexa-native pipeline (aprime_cc + hexa_ld) λκΉμ§ κ°μ λ§λ Mach-O exec κ° macOS Sonoma μμ launch + exit(42).
-
2026-05-20 β πΈ S7 P2 cycles 1-3 (Linux ELF x86_64).
compiler/ emit/elf_x86_64.hexaμ κ·. 3 cycles: ELF64 header + 5 section headers scaffold β x86_64 instruction encoding minimum subset (MOV r32 imm32 / SYSCALL / RET, 4 rules) β ELF executable (PT_LOAD) + ubu-2 μ격 launch test (REMOTE_RC=42). cross-platform cross-arch proof: λ¨μΌ hexa-lang μ½λλ² μ΄μ€κ° macOS arm64 (P0+P1) + Linux x86_64 (P2) λ λ€μ μ€ν κ°λ₯ binary λ₯Ό μΈλΆ toolchain 0건 (codesign macOS-only) μΌλ‘ μμ°.RFC 063 phasing matrix (2026-05-20 measured):
Phase μν Falsifier P0 Mach-O arm64 obj emitter πΈ CLOSED F-P0-OBJEQ corpus 4/4 byte-eq P1 hexa_ld Mach-O static-link πΈ CLOSED F-P1-RUNEQ trivial.hexa exit 42 P2 ELF x86_64 cycles 1-3 β μ§ν μ€ F-P2-LINUX-EXIT exit 42 on ubu-2 P2 cycle 4+ (walker + corpus) pending F-P2-RUNEQ corpus P3 flip default pending F-P3-ZERO-EXTERN dtruss μμ¬ μμ (cycles 19-N): P2 cycle 4 (x86_64 LIR walker + multi-obj static-link + corpus RUNEQ on ubu-2) + P3 (HEXA_BACKEND=native default flip + dtruss verification).
-
2026-05-20 β πΈπΈπΈ RFC 063 CAMPAIGN COMPLETE Β· P0+P1+P2+P3 ALL CLOSED. 21 cycles μΈ‘μ μ¦λͺ campaign μ’ λ£. μΈλΆ toolchain (clang, as, ld) fork 0건 path κ° μλν¨μ μΈ‘μ :
- F-P0-OBJEQ corpus 4/4 byte-eq vs clang (trivial/if/while/fib)
- F-P1-RUNEQ static-link trivial.hexa β exit 42 on macOS Sonoma
- F-P2-LINUX-EXIT + F-P2-MULTIOBJ-RUNEQ on ubu-2 (Linux x86_64) β exit 42
- F-P3-ZERO-EXTERN-OBJ: stripped PATH (/usr/bin:/bin only) μμ aprime_cc --emit=obj --backend=native κ° μ μ μλ
- F-P3-FULL-RUNEQ: μ 체 path
.hexa β aprime_cc β .o β hexa_ld β exec β exit 42κ° μΈλΆ clang/as/ld fork 0건 (codesign λ§ OS Gatekeeper exception λͺ μ)
νμ§λ§ μ μ§ν: production-grade compiler self-build via native path + HEXA_BACKEND=native default flip μ follow-up cycles μ baton (λ λ§μ LIR ops, self/runtime.c hexa port, cmd_build λ³κ²½
- cc --regen). λ³Έ closure λ RFC 063 μ falsifier contract μΆ©μ‘± μΈ‘μ β path working proof.