Skip to content

B05, sixty lines of C++ and you are inside the compiler - #74

Merged
tamnd merged 1 commit into
mainfrom
b05-the-plugin
Sep 6, 2026
Merged

tamnd merged 1 commit into
mainfrom
b05-the-plugin

Conversation

@tamnd

@tamnd tamnd commented Sep 6, 2026

Copy link
Copy Markdown
Owner

The last lesson of M2. Closes the M2 lesson list; the milestone's second gate (issue #21) is the four green boot runs, which is separate.

What is here

A plugin cannot be built in a notebook: it is C++ compiled against GCC's private headers by the compiler that will load it, and Colab has neither. So B05 is three layers.

  1. Five example plugins under gxplug/examples/, in the repository, so a --depth 1 clone has them and every line can be read.
  2. Eleven real compilations by a real GCC 16.2.0 in corpora/plug/b05.json: argv, return code, both streams, and the assembly of every run that produced any.
  3. Ten spans of GCC's own plugin machinery in corpora/source/b05.json, from plugin.cc, plugin.def, tree-pass.h and passes.cc.
Plugin What it is for
hello.cc the contract, in the smallest form that still works
countpass.cc a GIMPLE pass of your own, inserted after ssa, with its own dump file
gate.cc switching one of GCC's own passes off, which does change the generated code
nolicence.cc hello.cc without the GPL symbol, refused at load time
wrongver.cc a staged version mismatch, refused by its own check

The check

make -C gxplug examples-check builds all five, loads each, and asserts that hello and countpass leave the assembly byte identical, that gate does not, and that the two broken ones fail the compilation rather than being skipped. The plugin workflow now runs it on all four channels, so the lesson cannot describe plugins that only work on the machine they were written on.

Its program is an array traversal rather than the accumulator make check uses, because the pass being switched off is ivopts and a loop with no memory reference gives ivopts nothing to rewrite. The two halves of the gate result are checked apart: how many times the pass was refused, which catches a pass name that matched nothing, and whether the assembly moved, which catches a pass whose absence makes no difference. They fail for different reasons and the messages say which.

Two traps the lesson is built around

Three of the twenty six names in plugin.def are pseudo-events, handled inside register_callback and never fired. So registering a pass is spelled exactly like registering a callback and is a function call, and passing a callback alongside the pass info trips a gcc_assert rather than producing a diagnostic.

The digit in -fdump-tree-cddce1 is an instance number and not part of the pass name. Gating cddce1 matches nothing, reports nothing, and looks like a successful experiment. There is a recorded run of exactly that in the corpus.

The more useful of the two gate results is the one that changes nothing: einline is refused twice and the assembly is byte for byte what it was, because the IPA inliner later on does the work.

Also

  • Six glossary terms under a new "Extending the compiler" group, GLOSSARY.md now 89 terms
  • Tier 0 offline experiment b05-plugin, 30 registered
  • just corpus-b05, with --check re-asserting every fact without compiling
  • gxray.plug's recorded half: example, body, Invocation, Session, load_session
  • Eleven new tests, including one that runs examples-check behind @needs_plugin_gcc, and three over the grader's computed answers

Gates

pytest -q 1450 passed. ruff check, ruff format --check, prosecheck, bpc check, bpc pages --check, refcheck check (492 citations), dumpparse check, matrix table --check, tier0 check (30 passed), nbbuild check, nbbuild verify, python -m gxray.glossary all clean. matrix digests --check still reports the two known-missing boot images.

The notebook was executed in a real kernel and every cell read; the grader scores 8 of 8 on the answers the notebook leads you to.

The last lesson of M2, and the one that turns a reader from somebody who
watches GCC into somebody who runs code inside it.

A plugin cannot be built in a notebook. It is C++ compiled against GCC's
private headers by the compiler that will load it, and Colab has neither.
So the lesson is in three layers. The five example plugins live in the
repository, where a --depth 1 clone still gets them and every line can be
read. Eleven real compilations by a real GCC 16.2.0 are recorded in
corpora/plug/b05.json, with argv, return code, both streams and the
assembly of every run that produced any. Ten spans of GCC's own plugin
machinery are cut into corpora/source/b05.json.

The five examples, all under gxplug/examples/:

  hello        the contract, in the smallest form that still works
  countpass    a GIMPLE pass of your own, inserted after ssa
  gate         switching one of GCC's own passes off from outside
  nolicence    hello without the GPL symbol, refused at load time
  wrongver     a staged version mismatch, refused by its own check

make examples-check builds all five, loads each, and asserts that hello
and countpass leave the assembly byte identical, that gate does not, and
that the two broken ones fail the compilation rather than being skipped.
The plugin workflow now runs it on Debian, Fedora, Homebrew and the GCC
this project builds itself, so the lesson cannot describe plugins that
only work where they were written. Its program is an array traversal
rather than the accumulator `make check` uses, because the pass being
switched off is ivopts and a loop with no memory reference gives ivopts
nothing to rewrite, and the two halves of the result are checked apart:
how many times the pass was refused, which catches a name that matched
nothing, and whether the assembly moved, which catches a pass whose
absence makes no difference.

Two things the lesson goes out of its way to say, because both cost an
afternoon and neither is written down anywhere obvious. Three of the
twenty six names in plugin.def are pseudo-events, handled inside
register_callback and never fired, so registering a pass looks exactly
like registering a callback and is a function call. And the digit in
-fdump-tree-cddce1 is an instance number and not part of the pass name,
so gating "cddce1" matches nothing, reports nothing, and looks like a
successful experiment.

The more useful of the two gate results is the one that changes nothing:
einline is refused twice and the assembly is byte for byte what it was,
because the IPA inliner later on does the work. A pass switched off with
no visible effect is the normal case.

Also here: six glossary terms under a new "Extending the compiler" group,
a tier 0 offline experiment, a corpus-b05 recipe, gxray.plug's recorded
half, and eleven tests including one that runs examples-check.
@tamnd
tamnd merged commit f344c8d into main Sep 6, 2026
16 checks passed
@tamnd
tamnd deleted the b05-the-plugin branch September 6, 2026 00:20
@tamnd tamnd mentioned this pull request Sep 6, 2026
6 of 8 tasks
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.

1 participant