Skip to content

2026.9.21.3 — a token that did nothing, a count that was wrong, and a build that measured the dependencies - #682

Merged
Sunrisepeak merged 13 commits into
mainfrom
the-count-was-four-and-it-is-two
Sep 21, 2026
Merged

Sunrisepeak merged 13 commits into
mainfrom
the-count-was-four-and-it-is-two

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Sep 20, 2026

Copy link
Copy Markdown
Member

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/22 and the CHANGELOG all said
the borrowed __CYGWIN__ cost four members: archive, sqlite3, mimalloc,
c-ares. The 30-member run says otherwise.

total failures        10 -> 5
newly failing         none
the windows.h group   4 -> 2, not 4 -> 0

The half that did not hold is the useful half, because it says the
denominator was wrong:

member recorded cause measured cause after the withdrawal
archive (via xz) __CYGWIN__ correct cleared
sqlite3 __CYGWIN__ correct cleared
c-ares __CYGWIN__ #ifdef HAVE_WINDOWS_H, defined by the index's own recipe still red
mimalloc __CYGWIN__ Target OS doesn't support __builtin_thread_pointer() — reaches no header at all still red

The four were grouped by diagnostic, not by cause: all four stopped at
windows.h, so they were recorded as one class. Grouping by diagnostic
overstates what one withdrawal can fix.

2. builtins = "iso" emitted a token that did nothing

-fno-builtin-memset_pattern16 was emitted from this mechanism's first
revision. A/B on a real compile command, varying only that flag:

as built (flag present)              1 reference to memset_pattern16
flag REMOVED                         1 reference
-fno-builtin                         0
-mllvm -disable-loop-idiom-memset    0

And it could not report that it did nothing. 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 never reaches it.

-mllvm is not chosen because it passes an internal LLVM option, which can be
renamed 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. 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, three legs on all three hosts:

leg flag required
1 none the symbol must appear, or legs 2 and 3 measure nothing
2 -fno-builtin-memset_pattern16 must still appear — this pins the defect
3 the engine, aarch64-macos over openkal zero references

Leg 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 -ac reads
1/0 correctly with GNU grep, and the macOS runner's grep is BSD, where what
-a promises about binary input differs. llvm-nm -u is in the payload beside
the 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-run

mcpp test for a target this host can neither execute nor reach through a
runner leaves every test not run and exits 2 — the correct answer to "do
these 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 build builds the package. For a package whose only sources are
under tests/ it compiles nothing of it. 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)

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. built is counted apart
from not_run, in the human summary and in --message-format json, because
not_run means mcpp tried and could not — the question is open, exit 2 —
while built means it was told not to, so the build was the whole question and
the exit is 0. The count is carried to the workspace total as well; without
that, --workspace --no-run reported "0 passed; 0 failed", which is what a
workspace with no tests reports.

Criterion: 745_no_run_builds_the_tests_and_says_so.sh, five 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.

Verification

.agents/docs/2026-09-21-macros-and-withdrawal-verify.sh gains sections G and
H 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; its pins.toml and workflow
MCPP_VERSION move to this release together. The memset_pattern16 fix is
what Sunrisepeak/lsp-mcpp-private's macOS test_archive link has been
failing on.

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.
@Sunrisepeak Sunrisepeak changed the title the count was four and it is two, and the grouping used the wrong key 2026.9.21.3 — a token that did nothing, a count that was wrong, and a build that measured the dependencies Sep 21, 2026
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.
@Sunrisepeak
Sunrisepeak merged commit 476c366 into main Sep 21, 2026
40 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants