2026.9.21.3 — a token that did nothing, a count that was wrong, and a build that measured the dependencies - #682
Merged
Conversation
THE RE-MEASUREMENT AFTER THE WITHDRAWAL REFUTED A NUMBER THIS REPOSITORY HAD
WRITTEN IN FIVE PLACES.
`cenv.cppm`, `predefines.cppm`, `docs/21`, `docs/22` and the CHANGELOG all
said the borrowed `__CYGWIN__` cost four members: archive, sqlite3, mimalloc,
c-ares. The 30-member run on 2026.9.21.2 says otherwise.
total failures 10 -> 5
newly failing none
the windows.h group 4 -> 2, not 4 -> 0
And the half that did not hold is the useful half, because it says the
DENOMINATOR was wrong:
archive (xz) __CYGWIN__ correct cleared
sqlite3 __CYGWIN__ correct cleared
c-ares __CYGWIN__ WRONG still fails
mimalloc __CYGWIN__ WRONG still fails
c-ares reaches `windows.h` through `#ifdef HAVE_WINDOWS_H`, and that macro is
defined by mcpp-index's own recipe in its Windows branch --- a recipe defect,
the same shape as curl's `HAVE_LINUX_TCP_H`. mimalloc no longer reaches a
header at all.
ALL FOUR WERE GROUPED BY THEIR DIAGNOSTIC. Every one of them stopped at
`windows.h`, so they were recorded as one cause. GROUPING BY DIAGNOSTIC IS NOT
GROUPING BY CAUSE, and a count collected that way overstates what withdrawing
a name can fix.
This is the mirror of a lesson already in this repository: a conclusion gets
re-checked, its REASONS do not. The conclusion --- withdraw the borrowed name
--- was right and was re-verified. The number inside the reason was inherited
from an earlier record three times over, into engine comments, a changelog and
a pull request body, and nothing checked it until the measurement did.
AND mimalloc IS A FINDING OF ITS OWN:
fatal error: error in backend:
Target OS doesn't support __builtin_thread_pointer() yet.
`presents = "posix"` on Windows realises as `--target=x86_64-pc-cygwin`, and
LLVM does not implement that builtin for that OS. Every previous note about
the substitution discussed what the PREPROCESSOR sees --- the ABI unchanged,
the link unchanged, only macro visibility different. This is the first
measured cost that the CODE GENERATOR sees, and it is recorded rather than
fixed: the repair belongs either in mimalloc's recipe or in the choice of
substitute triple, and that is a measurement not yet taken.
A tooling defect surfaced with it. The measurement recorded mimalloc as
`fails: error: build failed`, because `compat.py`'s `first_diagnostic` matched
none of its three patterns --- a backend error carries no `file:line:` and no
`FAIL` --- and its fallback returns the LAST line, which is mcpp's own
summary. A fallback that returns something diagnostic-shaped when the match
failed is worse than returning nothing.
… was unwritten The division of labour said one measures the C surface and the other measures integration. It did not say they ask about different targets: 30-member measurement x86_64-linux-gnu, x86_64-windows-gnu --- no macOS lsp-mcpp-private three targets, including aarch64-macos Measured this round: `archive` is `runs (posix)` on both columns of the 30-member measurement, and the same libarchive fails to link on `lsp-mcpp-private`'s macOS with `memset_pattern16`. Both readings are true. That symbol is an Apple libc function clang emits only for Apple targets; on linux and windows-gnu it cannot appear. So nothing in this ecosystem sweeps macOS at 30-member scale, and E1's criterion --- 10 failures to 5, none newly failing --- holds ON THOSE TWO COLUMNS. A defect of this class is invisible in that number by construction. Adding the column needs a decision first: a Linux host can cross-build aarch64-macos but cannot run it, so that column tops out at `builds`, which RANK places below `runs`. A column that can only reach `builds` makes the regression check permanently looser there than on the other two.
…was the right one
The closure measured zstd and xz --- the two the OLD report named --- and found
them clean. The member that fails is libarchive, and it was never measured.
`lsp-mcpp-private`'s `test_archive` does not link for aarch64-macos:
ld64.lld: error: undefined symbol: memset_pattern16
referenced by `archive_read_support_format_7zip.o`. zstd and xz happen not to
trigger the idiom, so measuring them answered a different question.
THE RECORD LISTED THREE POSSIBILITIES AND PICKED THE FIRST. The third ---
"`-fno-builtin-memset_pattern16` is not enough to turn off LLVM's loop-idiom
pass" --- is the one that holds. A/B on the real compile command from
build.ninja, varying only that flag:
as built (flag present) 1 reference 38200 bytes
flag REMOVED 1 reference
-fno-builtin 0 38888 (+1.8%)
-ffreestanding 0
-mllvm -disable-loop-idiom-memset 0 38152
The flag changes nothing. And it cannot report that it changed nothing: clang
accepts `-fno-builtin-totally_not_a_function` silently, because
`memset_pattern16` is an LLVM TargetLibraryInfo libfunc rather than a clang
builtin. The preprocessed source contains the symbol zero times, which
confirms the call is generated rather than written.
TWO ENGINE-SIDE GAPS, AND THE SECOND IS WHY THE FIRST SURVIVED:
1. the token `builtins = "iso"` emits is ineffective for the one case its
own comment names;
2. that token is not verified. In the same file, `-D` and `-U` are checked
by the probe against `expectDefined`/`expectUndefined`, under a comment
saying a `-D` that did not take effect is a verification failure rather
than a silent one. `builtinsTokens` has no such list.
The criterion that follows: `builtinsTokens` needs the same probe check. A
mechanism that holds only under the assumption that it works needs an
assertion that it works.
The cost of each candidate repair is measured above rather than argued.
…rchitecture choice
`memset_pattern16` is not a function programs call; it is a helper the code
generator emits, in the same family as `memcpy` and `__udivti3`. That family
belongs to the compiler runtime. Measured:
grep -rln memset_pattern16 openkal-llvm-runtime/llvm/ -> 0 hits
LLVM carries no implementation anywhere, so compiler-rt has no fallback: the
call upstream emits is one only Apple's libSystem supplies.
`builtins = "iso"` therefore meets a case it can DECLARE and cannot ENFORCE.
The flag that would enforce it does not work, and LLVM offers no second one.
Four candidates, costs measured:
-fno-builtin works, +1.8% here, disables ISO
functions' optimisation too
-mllvm -disable-loop-idiom-memset works, -0.1%, not a stable interface
supply it in openkal-musl's Apple ~6 lines; port/src/mach/ exists;
port contrary to the declaration's wording
declare the member unbuildable discards a combination that works
The third asks the better question. The declaration is about what the C
library PRESENTS, and a compiler-emitted helper is not an interface the
program requested --- it is closer to ABI. A C library presenting only ISO C
may still owe the code generator the helpers it assumes for that target, the
same way it owes `memcpy`.
This is a decision, not an implementation: it settles whether
`builtins = "iso"` means "turn off what the generator assumes" or "declare
the surface the program can see". Those diverge in other cases too.
`[c-abi] builtins = "iso"` emitted `-fno-builtin-memset_pattern16` from this
mechanism's first revision. A/B on a real compile command, varying only that
flag, reads
as built (flag present) 1 reference to memset_pattern16
flag REMOVED 1 reference
-fno-builtin 0
-mllvm -disable-loop-idiom-memset 0
and clang accepts `-fno-builtin-totally_not_a_function` just as quietly: the
`-fno-builtin-<fn>` family is matched against clang's builtin table, while
`memset_pattern16` is an LLVM TargetLibraryInfo libfunc. The call is emitted by
LoopIdiomRecognize, which consults TLI, and the per-function attribute does not
reach it.
`-mllvm` is not chosen because it passes an internal LLVM option, which can be
renamed or removed between releases; when it is, the mechanism returns to
failing silently, which is the defect being repaired. The cost of the blunt
flag is measured rather than argued: on the translation unit that surfaced this
the object grows 38200 to 38888 bytes, 1.8 per cent.
THE NO-OP SURVIVED BECAUSE IT HAD NO CRITERION. `cenv` verifies its tokens
against a `-dM` dump, and a code-generation property is not visible there. The
criterion now lives in `openkal-cross.yml` and has three legs, on all three
hosts: no flag (the symbol MUST appear, or the probe measures nothing), the
per-function flag (it must still appear, pinning the defect), and an
`aarch64-macos` build over the openkal stack by the mcpp under test (zero
references). Run against the previous binary the step fails, at the link:
ld64.lld: error: undefined symbol: memset_pattern16
`mcpp test` for a target this host can neither execute nor reach through a
runner leaves every test `not run` and exits 2. That is the correct answer to
"do these tests pass" — mcpp did not find out — but 2 is also what a broken
runner returns, so a caller that wanted only the build cannot tell the two
apart and falls back to `mcpp build`.
AND `mcpp build` BUILDS THE PACKAGE. For a package whose only sources are
under `tests/` it compiles nothing of it at all. Measured on mcpp-index's
`archive` member, whose sources are two files under `tests/`:
$ mcpp build --target aarch64-macos # exits 0
Compiling compat.lz4 / compat.xz / compat.zlib / compat.zstd …
$ find target -name '*compression*' -o -name '*versions*'
(only musl's versionsort.o)
The member's dependencies compiled; not one line of the member did. A
compatibility sweep reading that exit code records the member as building on
macOS, which is a reading about the dependencies with the member's name on it.
`--no-run` gives the narrower claim its own answer: every selected test is
compiled and linked for the target, none is executed, and the result says so.
`built` is counted apart from `not_run`, in the human summary and in the
machine interface, because `not_run` means mcpp tried and could not — the
question is open, the exit code is 2 — while `built` means it was told not to,
so the build was the whole question and the exit code is 0.
The criterion is `745_no_run_builds_the_tests_and_says_so.sh`, four legs. Its
runner is a name that is not a program: an unexecutable target would make the
test need a cross toolchain and a host that cannot run it, while a runner that
cannot be found produces the same situation on every host, for the native
target, with nothing installed. Run against a binary without the flag it fails
at leg B.
Leg 1 of the builtins criterion failed on the macOS host with all three readings 0, which is what leg 1 exists to report: the two legs behind it were measuring nothing. The cause is the reader, not the compiler. `grep -ac memset_pattern16` reads 1/0 correctly with GNU grep, and the macOS runner's grep is BSD, where what `-a` promises about a binary file differs. The name being present in an object's string table made a byte match look like it needed no tool; it needed agreement about binary input instead. `llvm-nm -u` is in the payload beside the clang already being used, costs the same lookup, and asks the question the step is actually asking. Measured unchanged on Linux: 1 / 1 / 0, engine 0, and the control against the previous binary still fails at the link.
C5 is the builtins token, whose first leg went red on macOS and reported that the two behind it were measuring nothing. C6 is what `builds` meant on a target with no runner: the member's dependencies, not the member.
…vel up `--workspace --no-run` reported "ok. N member(s); 0 passed; 0 failed", which is what a workspace with no tests at all reports — the false reading `totalNotRun` was added to the same line to prevent, one level down. The fan-out now carries `built` through to the workspace total and to `workspace_summary` as `tests_built`, kept apart from `tests_not_run` for the reason the per-member fields are: one is a question left open, the other is a question that was not asked. Leg E of 745 covers it: two members, one test each, `--workspace --no-run`, exit 0 and "2 built, not run" in the total.
Both version sites in one commit, which `01_help_and_version.sh` cross-checks. The bootstrap pin in .xlings.json stays at 2026.9.21.1: it must name a version that is published, mirrored and in the index, and this one is none of those yet.
`os == "macos" || os == "ios"` is `Triple::is_apple()`. This module takes `os` as a string rather than a `Triple` deliberately — it is pure, and importing the toolchain model to reach one predicate would couple what that choice decoupled — so the spelling stays and the comment names where the canonical list lives and which other sites carry a copy.
G reads an object file, because a code-generation property is in no `-dM` dump — which is why the token it asserts was a silent no-op for the whole of its first life. On 2026.9.21.2 the aarch64-macos link fails outright, so a failed build there is the negative reading rather than an absent one. H asserts `--no-run`; before this release the flag does not exist. Dry-run on the host against both engines: both sections pass on this one and fail on the previous, which is the property a CHANGE section has to have.
aarch64-macos was measurable for the first time once a target with no runner compiled the member's own tests: 20 of 30 build, 10 do not, and nine of the ten are `#ifdef __APPLE__` reaching for the Apple C environment on a target where `__APPLE__` is true and libSystem is not there. It is the Windows problem mirrored, minus the lever: presenting POSIX on Windows is realised as a cygwin triple and `_WIN32` goes away, while on macOS the realisation adds `-D__unix__` and leaves `__APPLE__` standing because it is correct. Nothing in the identity a source file sees there answers which C library is underneath.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 2026.9.21.3. Three things, each found by a measurement that
contradicted something this repository had written down.
1. The count was four and it is two, and the grouping used the wrong key
cenv.cppm,predefines.cppm,docs/21,docs/22and the CHANGELOG all saidthe borrowed
__CYGWIN__cost four members: archive, sqlite3, mimalloc,c-ares. The 30-member run says otherwise.
The half that did not hold is the useful half, because it says the
denominator was wrong:
__CYGWIN____CYGWIN____CYGWIN__#ifdef HAVE_WINDOWS_H, defined by the index's own recipe__CYGWIN__Target OS doesn't support __builtin_thread_pointer()— reaches no header at allThe four were grouped by diagnostic, not by cause: all four stopped at
windows.h, so they were recorded as one class. Grouping by diagnosticoverstates what one withdrawal can fix.
2.
builtins = "iso"emitted a token that did nothing-fno-builtin-memset_pattern16was emitted from this mechanism's firstrevision. A/B on a real compile command, varying only that flag:
And it could not report that it did nothing. clang accepts
-fno-builtin-totally_not_a_functionjust as quietly: the-fno-builtin-<fn>family is matched against clang's builtin table, while
memset_pattern16isan LLVM TargetLibraryInfo libfunc. The call is emitted by LoopIdiomRecognize,
which consults TLI, and the per-function attribute never reaches it.
-mllvmis not chosen because it passes an internal LLVM option, which can berenamed between releases — and when it is, the mechanism returns to failing
silently, which is the defect being repaired. The cost of the blunt flag is
measured: on the translation unit that surfaced this, the object grows 38200
to 38888 bytes, 1.8 per cent.
The no-op survived because it had no criterion.
cenvverifies its tokensagainst a
-dMdump, and a code-generation property is not visible there. Thecriterion now lives in
openkal-cross.yml, three legs on all three hosts:-fno-builtin-memset_pattern16aarch64-macosover openkalLeg 1 then went red on the macOS host with all three readings 0, which is
exactly what it exists to report. The cause was the reader:
grep -acreads1/0 correctly with GNU grep, and the macOS runner's grep is BSD, where what
-apromises about binary input differs.llvm-nm -uis in the payload besidethe clang already in use and asks the question the step is actually asking.
All three hosts now read 1 / 1 / 0 and engine 0.
3.
mcpp test --no-runmcpp testfor a target this host can neither execute nor reach through arunner leaves every test
not runand exits 2 — the correct answer to "dothese tests pass". But 2 is also what a broken runner returns, so a caller
that wanted only the build falls back to
mcpp build.And
mcpp buildbuilds the package. For a package whose only sources areunder
tests/it compiles nothing of it. Measured on mcpp-index'sarchivemember, whose sources are two files under
tests/:A compatibility sweep reading that exit code records the member as building on
macOS, which is a reading about the dependencies with the member's name on it.
--no-rungives the narrower claim its own answer.builtis counted apartfrom
not_run, in the human summary and in--message-format json, becausenot_runmeans mcpp tried and could not — the question is open, exit 2 —while
builtmeans it was told not to, so the build was the whole question andthe exit is 0. The count is carried to the workspace total as well; without
that,
--workspace --no-runreported "0 passed; 0 failed", which is what aworkspace with no tests reports.
Criterion:
745_no_run_builds_the_tests_and_says_so.sh, five legs. Its runneris a name that is not a program — an unexecutable target would make the test
need a cross toolchain and a host that cannot run it, while a runner that
cannot be found produces the same situation on every host, for the native
target, with nothing installed. Run against a binary without the flag it fails
at leg B.
Verification
.agents/docs/2026-09-21-macros-and-withdrawal-verify.shgains sections G andH for the two above. Dry-run on the host against both engines: both pass here
and fail on 2026.9.21.2, which is the property a CHANGE section has to have.
Downstream
mcpplibs/mcpp-index#458 needs
--no-run; itspins.tomland workflowMCPP_VERSIONmove to this release together. Thememset_pattern16fix iswhat
Sunrisepeak/lsp-mcpp-private's macOStest_archivelink has beenfailing on.