Expand description
Which IR opcodes have somewhere to go, and which do not.
Design: spec/10-backend.md section 10.2, under Coverage.
Every opcode has to be lowered by something or be a hole somebody wrote down. Without this the way a hole is found is that somebody compiles a program containing one and the selector reports that it cannot lower an instruction, which is a fine diagnostic and a bad discovery mechanism: it turns a gap in the rule set into a user’s problem rather than a failing build.
§The three answers
An opcode is lowered by a rule, or somewhere a rule cannot reach, or nowhere.
The first is the ordinary answer and the one this can check by itself. crate::term says
every name a rule could be written at, the table says every name one is written at, and an
opcode is covered when each of its names is in both. That is what makes this a check about
widths rather than about opcodes: an add with a rule at four widths and no rule at the fifth
is not covered, and would be reported here as the missing name rather than as a covered opcode.
The second is ELSEWHERE, which is not a gap. spec/10-backend.md names five of them and
there are more now, and they are all the same kind of thing: an opcode whose lowering depends on
something no pattern can see. Where a call’s arguments go depends on the signature, where a
local lives depends on the frame, an unconditional jump is an edge and edges live on the block,
and a memcpy is a run of moves whose length is a constant the pattern would have to count. A
rule matches one term and can say none of that.
The third is GAPS, which is the number spec/15-testing.md section 15.8 says we keep. Each
entry names why it is there and the issue that closes it, so that an opcode nobody has written a
rule for is a decision somebody wrote down rather than a surprise.
§What makes the lists honest
An entry that stops being true fails. An opcode on either list that a rule starts covering is a stale entry and the tests below say so by name, which is the same rule the exclusion lists in the compatibility harness are kept under: a list nothing checks is a list that only grows.
The direction this cannot check is an opcode moving from GAPS to ELSEWHERE without the
list following it, because where an opcode is lowered by name is a match arm and there is
nothing to ask about a match arm from here. What that costs is one line of a list going out of
date; what it does not cost is a gap going unnoticed, since the opcode is still on a list and
still counted.
§The other question
All of the above is about the rule set as it is written. Fired is about the rule set as it
is used: which rules a compilation actually reached. A rule nothing reaches is proved and dead
weight, or it is a construct the corpus does not contain and somebody should know which. The
selector marks a rule as it fires it, the driver writes the marks out under
-Zrule-coverage=FILE, and the harness in tamnd/rucc-compat unions those files over a corpus,
which is what turns coverage of the rule set into a number. spec/20-execution-testing.md
section 20.9 is the design and tamnd/rucc#261 is the work.
Structs§
- Fired
- Which rules fired, over one function or over a whole compilation.
- Report
- What a target’s rules cover, and what they do not.
Statics§
- ELSEWHERE
- An opcode no rule is written about, and the place that lowers it instead.
- GAPS
- An opcode nothing lowers, why it is here, and the issue that closes it.
- WIDTHS
- A width no rule is written at, why, and the issue that closes it.