Phase 3 of the native-default drive — the locally-verifiable items (CI and
rolling-bootstrap changes are deferred to Phase 4, where they become correct
once v0.12.0 is the rolling-bootstrap anchor):
- Help text now marks the backends explicitly: "qbe (deprecated) | native
(default)". QBE is retained as the e2e parity oracle and QBE-fixpoint guard;
removal is a later release.
- fixpoint-native-internal.sh now drives the FULL internal pipeline (internal
assembler + internal linker) against an all-external (gcc assemble + gcc link)
reference, instead of only differing on the assembler. Both internal tools are
now the default toolchain, so the conformance guard must exercise both. Header
comments updated to reflect that it guards the internal linker too and why it
stays a differential probe (the internal assembler buffers the whole object in
memory and OOMs on the compiler's own ~631k-line .s).
- language-rationale.adoc records the decision: native backend + internal
assembler + internal linker as the default toolchain (no external tools beyond
the C runtime startup objects), why QBE is kept-but-deprecated, and the
per-invocation escape hatches (--assembler external, --linker external,
--backend qbe, --incremental).
Verified: FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK; full suite
3541 tests / 0 failures on both the QBE-built and native-built test runners.
Neither fixpoint exercises the in-process internal assembler (--assembler
internal): fixpoint.sh assembles via qbe+gcc, fixpoint-native.sh assembles
the native .s with gcc. A miscompilation that only corrupts the internal
assembler's object output therefore passes both fixpoints cleanly — which is
exactly how the sret-Result field-read bug (fixed in the previous commit)
escaped the fixpoint gate.
scripts/fixpoint-native-internal.sh closes that gap. It compiles a small
representative program (record-returning function with sret-Result field
reads, plus immutable string literals) with BOTH --assembler internal and
--assembler external, then asserts the two binaries behave identically
(stdout + exit code). Behavioural equivalence is the sound invariant: the
two assemblers may emit different-but-valid encodings, so a byte-level
section compare would false-positive.
A true self-hosting internal-assembler fixpoint (compile the compiler with
itself via --assembler internal) is deferred: the internal assembler buffers
the whole object in memory and needs ~53 GB for the compiler's own 631k-line
.s, OOM-killing. That scalability limit is tracked in bugs.txt; until the
assembler streams its output, this differential conformance check plus
TInternalAsmE2ETests are the internal-assembler guards.
Verified: prints NATIVE_INTERNAL_OK on a fixed compiler, exits non-zero with
EXIT_MISMATCH on a compiler carrying the sret-Result bug.