Skip to main content

rucc_codegen/
capability.rs

1//! What this target can be asked to do, and what happens when it cannot.
2//!
3//! Design: `spec/optimizer/36-lowering-and-isel.md` section 36.4.
4//!
5//! GCC asks one question of a target, which is whether it has an instruction for this operation at
6//! this mode, and the whole of `gcc/optabs.cc` is built on the answer. rucc asks the same question
7//! and used to give three separate answers in three separate places: a list in [`crate::coverage`]
8//! saying an opcode has no rule and that is on purpose, a set of `match` arms in
9//! [`crate::quad`] and [`crate::wide`] turning an operation into a call to the compiler runtime,
10//! and the pre-selection group in [`crate::lowering`] rewriting an operation into ones the machine
11//! does have.
12//!
13//! Those are not three questions. They are one question with three possible answers, and three
14//! places that answer it can disagree without anything noticing. An opcode named on the exception
15//! list and also lowered before selection is a stale line in the list; an opcode named there that
16//! has grown a libcall is the same staleness the other way round. This module is the one table, and
17//! [`Row`] is the three columns.
18//!
19//! # A row is a name
20//!
21//! Section 36.4 says one row per operation and mode. A name is exactly that here, and it is already
22//! how the rest of the back end talks: `add.i64` is the addition opcode at sixty four bits, and
23//! [`crate::coverage`] has always said that a name is an opcode and a width together. So the rows
24//! are the names, which come from three places.
25//!
26//! [`rucc_ir::term::heads`] gives every name the rule language can spell, which is the universe the
27//! rule column is about. [`LIBCALLS`] gives the names at the two modes the rule language cannot
28//! spell, which is the whole reason those calls exist. And an opcode with no mode at all, a `call`
29//! or a `jump` or a `trap`, gets one row under its own name, because the question is still asked
30//! about it and the answer is still one of the three.
31//!
32//! # Why more than one column can be filled
33//!
34//! An operation is not obliged to have exactly one answer, and reading it that way is the mistake
35//! the old list made. `sitofp.i64.f64` has a rule, because this machine has `cvtsi2sd`, and it is
36//! also named by [`crate::lowering::Step::Floats`], because the same pass handles the widths the
37//! machine has no instruction for and walks away from the ones it does. Both columns are true and
38//! neither is stale. What would be a contradiction is a rule at a name something rewrites by hand
39//! before selection ever runs, since that rule could never fire, and that is the one overlap the
40//! tests below refuse.
41//!
42//! # Which way the arrow points
43//!
44//! The lowering column is not written down here. It is read out of [`crate::lowering::Step`], which
45//! is where a lowering says which opcodes it is about, so there is no second copy of the group
46//! membership to go stale. That is the only direction worth having: the group is the authority on
47//! what the group rewrites, and a table that repeated it would be the third mechanism again under a
48//! new name.
49//!
50//! What is written down here is the half no pass can be asked for. [`HAND`] is the opcodes lowered
51//! somewhere a rule cannot reach and a pass cannot be asked about either, because the answer is a
52//! `match` arm in [`crate::lower`] or in `rucc_safety`, and [`LIBCALLS`] is the runtime function an
53//! operation becomes, which was three sets of `match` arms and is now one list they read.
54
55use rucc_ir::Opcode;
56
57use crate::coverage::{GAPS, NAMES};
58use crate::lowering::Step;
59use crate::select::Table;
60
61/// What rewrites an operation before the selector sees it.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Lowering {
64    /// A member of the pre-selection group, which says for itself which opcodes it is about.
65    Group(Step),
66    /// A place a rule cannot reach and a pass cannot be asked about, and what it does there.
67    Hand(&'static str),
68}
69
70impl Lowering {
71    /// Where the rewrite happens, for a report somebody reads.
72    #[must_use]
73    pub const fn where_(self) -> &'static str {
74        match self {
75            Lowering::Group(step) => step.name(),
76            Lowering::Hand(where_) => where_,
77        }
78    }
79}
80
81/// One operation at one mode, and the three answers.
82#[derive(Debug, Clone)]
83pub struct Row {
84    /// The opcode the row is about.
85    pub opcode: Opcode,
86    /// What the operation at this mode is called, which is `add.i64` or `sitofp.i64.f64` or, for an
87    /// operation with no mode, the opcode's own name.
88    pub name: &'static str,
89    /// Whether a rule in this target's table is written at that name.
90    pub rule: bool,
91    /// What rewrites it before selection, if anything does.
92    pub lowering: Option<Lowering>,
93    /// The runtime function it becomes, if that is what happens to it.
94    pub libcall: Option<&'static str>,
95    /// Why nothing does any of the three, and the issue that closes it.
96    pub nothing: Option<(&'static str, &'static str)>,
97}
98
99impl Row {
100    /// Whether this target can be asked to do this operation at this mode at all.
101    ///
102    /// The question the whole table is for. A row with no answer is an operation that reaches the
103    /// selector and is refused there, which turns a hole in the back end into a user's problem.
104    #[must_use]
105    pub const fn answered(&self) -> bool {
106        self.rule || self.lowering.is_some() || self.libcall.is_some()
107    }
108}
109
110/// An opcode lowered somewhere a rule cannot reach, and what happens to it there.
111///
112/// Not one of these is a gap. Each is an opcode whose lowering depends on something no pattern can
113/// see, so the answer lives where that something is known: where a call's operands go depends on
114/// the signature, where a local lives depends on the frame, an unconditional jump is an edge and
115/// edges live on the block.
116///
117/// This used to be twice as long, and the half that went is the half the pre-selection group can be
118/// asked about directly. An entry here is one nothing can be asked about, because what does the
119/// work is a `match` arm rather than a pass with a name.
120pub static HAND: &[(Opcode, &str)] = &[
121    // The convention. What a call's operands are is whatever the signature made them, and which
122    // register each one arrives in depends on the classification of every argument before it.
123    (Opcode::Call, "`crate::abi`, which builds a call out of the convention"),
124    (Opcode::CallIndirect, "`crate::abi`, the same instruction with the callee in a register"),
125    // The frame, which is not known until the allocator has finished running out of registers.
126    (Opcode::Alloca, "`crate::lower`, as an address into a frame `crate::frame` lays out later"),
127    // The stack pointer, which is not a value the program computed and so is not a value a rule
128    // could bind. A scope holding a variable length array reads it as it opens and writes it back
129    // as it closes, which is how the bytes are given back.
130    (Opcode::StackSave, "`crate::lower`, as a move out of the stack pointer"),
131    (Opcode::StackRestore, "`crate::lower`, the same move the other way round"),
132    // A relocation, which is right because of what the linker does rather than because of what
133    // any bitvector equals.
134    (Opcode::GlobalAddr, "`crate::lower`, a `lea` off the instruction pointer with a name on it"),
135    // The same instruction against a place in this function rather than a name outside it. What
136    // it addresses is a block, and a block is not a value a pattern can bind.
137    (Opcode::BlockAddr, "`crate::lower`, the same `lea` against a label of this function"),
138    // The one thing on this machine that no ordinary instruction can work out, which is why it
139    // is built here rather than matched: `%fs` is not a register a rule could name.
140    (Opcode::ThreadPointer, "`crate::lower`, as the load through `%fs` at zero that reads it"),
141    (
142        Opcode::ApplyArgs,
143        "`crate::lower`, as the block the prologue saved the argument registers in",
144    ),
145    (Opcode::Apply, "`crate::lower`, as a call built out of that block"),
146    // What a named register holds, built there for a reason of the same shape written about any
147    // register rather than about one: which register it is is a string beside the instruction and
148    // a pattern matches on an opcode and a type, so no rule could name it.
149    (Opcode::RegisterValue, "`crate::lower`, as one move out of the register the program named"),
150    // A hint, which is built here for a reason of the same shape and one step stronger: which of
151    // the four instructions it is comes out of a number in the builtin's arguments, and a pattern
152    // matches on an opcode and a type and could not see it.
153    (Opcode::Prefetch, "`crate::lower`, as one of the four `prefetch` instructions"),
154    // Stopping, which is built here because it computes nothing for a rule to have a pattern for
155    // and because what makes it right is the operating system rather than any bitvector.
156    (Opcode::Trap, "`crate::lower`, as the `ud2` the program stops on"),
157    // The two that walk the frames, built here because how long the walk is comes out of a number
158    // beside the instruction and a pattern matches on an opcode and a type. What they start from is
159    // the frame pointer, which is not a register a rule could name either, and asking for one is
160    // part of building them.
161    (Opcode::FrameAddress, "`crate::lower`, as the walk up the saved frame pointers"),
162    (Opcode::ReturnAddress, "`crate::lower`, as the same walk with one load at the end of it"),
163    // The pair that saves a place in a function and comes back to it, built here because what the
164    // first of them writes down is where control comes back to, which is a place in this function
165    // and not a value a pattern can bind. Each is a group of instructions rather than one, and the
166    // first of them ends the block it was written in, which no rule can do.
167    (
168        Opcode::SetjmpMarker,
169        "`crate::lower`, as the four words it writes and the block the restore comes back to",
170    ),
171    (
172        Opcode::LongjmpMarker,
173        "`crate::lower`, as the four words read back, the frame put back and the jump",
174    ),
175    // No instruction at all. The IR keeps the width the same and the machine has one register
176    // file for both, so the value is already where it needs to be.
177    (Opcode::PtrToInt, "`crate::lower`, which renames the value rather than computing anything"),
178    (Opcode::IntToPtr, "`crate::lower`, the same rename the other way round"),
179    // Memory SSA, which is built at -O2, read by the passes that need it, and taken back off
180    // before selection. Nothing in the back end has ever seen a value of type `mem`.
181    (Opcode::MemEntry, "nothing at all, since memory SSA comes off before the back end runs"),
182    // An object size question, which `rucc_opt::objsize` answers with a constant before any other
183    // pass runs and at every level, so the back end never sees one.
184    (Opcode::ObjectSize, "nothing at all, since `rucc_opt::objsize` answers it first"),
185    (Opcode::IsConstant, "nothing at all, since `rucc_opt::constant_p` answers it first"),
186    // The anonymous arguments of a call that was inlined, which the inliner puts in its place, and
187    // a body still holding one is a body that is not emitted.
188    (Opcode::VaArgPack, "nothing at all, since `rucc_opt::inline` replaces it first"),
189    (Opcode::VaArgPackLen, "nothing at all, since `rucc_opt::inline` replaces it first"),
190    // The edges and the two ways of writing down that control does not arrive.
191    (Opcode::Jump, "`crate::layout`, since an edge is on the block and not in the block"),
192    // The one terminator selection does write, because what it reads is a value. How many arms it
193    // has is not fixed, and a rule says what an instruction reads rather than where a block goes.
194    (Opcode::IndirectBr, "`crate::lower`, as the jump through the register that holds the address"),
195    (Opcode::Unreachable, "nothing at all, which is the answer for a place control does not reach"),
196    (Opcode::UnreachableHint, "nothing at all, for the same reason"),
197    // The template, which is a string and not a term. A rule set cannot be written over a string,
198    // so the instructions a template names are looked up in the machine description rather than
199    // matched, which is `rucc_target::x86_64::read`.
200    (
201        Opcode::InlineAsm,
202        "`crate::lower`, as the places its operands share and the instructions its template names",
203    ),
204    // The barrier itself, which is one instruction or none and neither is a rewrite of anything.
205    (
206        Opcode::Fence,
207        "`crate::lower`, as an `mfence` at the strongest ordering on x86 and a `dmb` above relaxed on \
208         AArch64",
209    ),
210    // The compare and exchange, which is one instruction and produces two values, and a rule
211    // replaces a term with an instruction producing one.
212    (
213        Opcode::Cmpxchg,
214        "`crate::lower`, as a locked compare and exchange on x86 and an exclusive loop on AArch64",
215    ),
216    // The read modify write, which produces one value a rule could have named and whose operation
217    // is carried beside it rather than in the head a rule matches on, so one pattern would be all
218    // thirteen of them. `crate::lowering::Step::Retries` is the half of this the group does and it
219    // names no opcode, because the half it does not do is lowered here.
220    (
221        Opcode::AtomicRmw,
222        "`crate::lower`, as an exchange, a locked add or an exclusive loop, and `crate::retry` for the \
223         eight with no instruction, with the two on floating values refused",
224    ),
225    // The one of the five variable argument opcodes the group does not name, because what it writes
226    // is the register save area and where that is comes out of the convention rather than the term.
227    (Opcode::VaStart, "`crate::varargs`, which writes the register save area the ABI describes"),
228    // Memory safety. A check is a call to the runtime, and the rewrite happens after the optimizer
229    // has run so that the descriptor table only has rows for checks that survived it.
230    (Opcode::CheckBounds, "`rucc_safety::lower`, into a call carrying the row that describes it"),
231    (Opcode::CheckLive, "`rucc_safety::lower`, the same call over the lifetime plane"),
232    (Opcode::CheckDeriv, "`rucc_safety::lower`, the same call where the pointer is computed"),
233    (Opcode::CheckType, "`rucc_safety::lower`, the same call, carrying the type asked about"),
234    (
235        Opcode::CheckInit,
236        "`rucc_safety::lower`, the same call over the init plane, carrying no type",
237    ),
238    (Opcode::CheckRace, "`rucc_safety::lower`, the same call over the epoch plane"),
239    (
240        Opcode::CheckFree,
241        "`rucc_safety::lower`, the same call in front of the free rather than the access",
242    ),
243    // The five plane writes the same pass emits, which become calls the same way. A judgement
244    // decides nothing, so none of the calls carries a descriptor row, and neither do the two
245    // edges below them.
246    (Opcode::MetaType, "`rucc_safety::lower`, into the call that records what a store stored"),
247    (Opcode::MetaTypeCopy, "`rucc_safety::lower`, the same call over the range a copy read"),
248    (Opcode::MetaInit, "`rucc_safety::lower`, into the call that says a store wrote a range"),
249    (Opcode::MetaInitCopy, "`rucc_safety::lower`, the same call over the range a copy read"),
250    // The aux's own copy, which the same pass emits beside those two and which is not a plane write
251    // in the sense they are: what it moves is the capability beside every pointer a copy carried.
252    (Opcode::CapCopy, "`rucc_safety::lower`, the same call over the slots a copy moved"),
253    (Opcode::MetaEpoch, "`rucc_safety::lower`, into the call that says which thread stored"),
254    // The two halves of a synchronization edge, which are the same shape of call and are not a
255    // plane write at all: what they move is a thread's own clock, which lives beside the thread.
256    (
257        Opcode::MetaRelease,
258        "`rucc_safety::lower`, into the call that publishes this thread's clock at an atomic",
259    ),
260    (Opcode::MetaAcquire, "`rucc_safety::lower`, into the call that takes the other end of it"),
261    // The same pair for a fence, which are the same calls with no key, since a fence orders
262    // against every thread rather than against an object.
263    (
264        Opcode::MetaFenceRelease,
265        "`rucc_safety::lower`, into the call that publishes this thread's clock to everyone",
266    ),
267    (
268        Opcode::MetaFenceAcquire,
269        "`rucc_safety::lower`, into the call that takes what any release fence published",
270    ),
271    // The `restrict` contract, which is judgement J8 and is the one check that records as well as
272    // asks. What it records goes in a slot the block owns, and the two markers are what open and
273    // close that slot, so all four are calls to the runtime the same way.
274    (
275        Opcode::CheckRestrictRead,
276        "`rucc_safety::lower`, into the call that asks what the block has already reached",
277    ),
278    (Opcode::CheckRestrictWrite, "`rucc_safety::lower`, the same call, saying it wrote"),
279    (Opcode::RestrictEnter, "`rucc_safety::lower`, into the call that opens the block's record"),
280    (Opcode::RestrictLeave, "`rucc_safety::lower`, into the call that closes it again"),
281    // The two markers, and the only pair on this list that is lowered into nothing. A declared
282    // region is not code, it is the reason some code carries no checks, so by the time the back end
283    // sees it the whole of its effect has already happened. What it costs is the count document 10
284    // section 10.2 asks for, and `rucc_safety::summary` takes that before the back end runs.
285    (Opcode::SafeRegionBegin, "`rucc_safety::lower`, into nothing, once the count has been taken"),
286    (Opcode::SafeRegionEnd, "`rucc_safety::lower`, the same, which is to say nothing"),
287    (Opcode::CapExtent, "`rucc_safety::lower`, into a call that asks rather than one that judges"),
288    (Opcode::CapExtentBack, "`rucc_safety::lower`, the same call about the bytes below an address"),
289    // The capability the checks were reading, which the same pass takes out once they are calls,
290    // because a call to the runtime is handed an address and finds the rest for itself. One that
291    // something does read is a slot, and the only one of those the pass can fill so far is a
292    // capability for a pointer an allocator just returned, which is a load out of that instance's
293    // own header rather than anything worked out from the address.
294    (Opcode::CapOf, "`rucc_safety::slot`, into the header read at an allocation site or the walk"),
295    // The two ends of a capability that something does read. A capability is four words of frame
296    // and the value that stands for one is the slot's address, so the pair below is an `alloca`
297    // with four zero words written into it and a call handed the addresses of two slots.
298    (Opcode::CapNull, "`rucc_safety::slot`, into a frame slot with the bottom capability in it"),
299    (Opcode::CapStore, "`rucc_safety::slot`, into the call that writes one into the aux plane"),
300    // The other end of that write, which is the one capability nothing has to work out, because the
301    // store that put it beside the pointer already did. So this is a call too, and it is the only
302    // instruction the pass rewrites that reads a slot and fills one.
303    (Opcode::CapLoad, "`rucc_safety::slot`, into the call that reads one back out again"),
304    // The sub-object tier's whole mechanism, which is arithmetic on the range a capability holds
305    // and is a call for the same reason the rest are: where the four words sit is the runtime's to
306    // know, and a second place that agreed about it would be a second place that could stop.
307    (Opcode::CapNarrow, "`rucc_safety::slot`, into the call that moves the range in"),
308    // The expensive producer and the only one that always has an answer, which is why it is what a
309    // pointer from outside the instrumented world falls back to. Same two arguments as the fresh
310    // allocation above, since the runtime declares the pair as one shape.
311    (Opcode::CapRecover, "`rucc_safety::slot`, into the call that walks the planes for one"),
312    // The two ends of a call, which is where a capability stops being this function's business.
313    // Neither of them is a capability instruction in the sense the five above are: one copies a
314    // call's worth of them into a frame in thread local storage and publishes it, and the other
315    // says there is no frame at all, which is what a callee nobody can vouch for gets.
316    (Opcode::CapPublish, "`rucc_safety::frame`, into the frame a call hands its callee"),
317    (Opcode::CapClear, "`rucc_safety::frame`, into the call that says there is no frame"),
318    // And the reading end of the first of those two, which is the one of the three that does make a
319    // capability. It is in the callee rather than in the caller and it answers whether or not there
320    // was a frame, because a pointer nobody described is one to be recovered from the planes.
321    (Opcode::CapArg, "`rucc_safety::frame`, into the read of the frame the caller published"),
322    // And the same pair for the pointer a call gives back, which is the one value crossing a call in
323    // the other direction. The writing end is in the callee and is the only thing here that writes
324    // into a frame it did not make, which it may because the frame is the caller's stack and the
325    // caller is waiting for it.
326    (
327        Opcode::CapYield,
328        "`rucc_safety::frame`, into the write of the frame the caller is waiting on",
329    ),
330    (Opcode::CapResult, "`rucc_safety::frame`, into the read of what the callee left behind"),
331    // What `__builtin_expect` said, which the pass writes onto the arms of the branch it was said
332    // about before taking the instruction out, so that a hint and a profile are the same thing to
333    // everything downstream of the optimizer.
334    (Opcode::Expect, "`rucc_opt::expect`, which moves the hint onto the branch and removes it"),
335];
336
337/// The runtime function an operation becomes, by opcode and by mode.
338///
339/// The third answer, and the one GCC's fallback ladder ends at. An operation with no instruction
340/// and no way of being built out of instructions is a call to the compiler runtime, and which call
341/// it is is a fact about the operation and the mode and nothing else. It used to be three sets of
342/// `match` arms, in [`crate::quad`], in [`crate::wide`] and in [`crate::expand`], and it is one
343/// list they read.
344///
345/// The mode is spelled the way the rule language spells one, which is the width for an operation on
346/// one type and the two widths for a conversion. Every mode here is a mode the rule language cannot
347/// spell, since an operation the machine has does not become a call, which is why these names are
348/// not in [`rucc_ir::term::heads`] and are rows of their own.
349///
350/// The bulk operations are the exception that proves it. A copy becomes a run of moves at a size
351/// the lowering will take on and a call to the C library above it, so the mode is the size rather
352/// than a width, and both answers are true of the same row.
353pub static LIBCALLS: &[(Opcode, &str, &str)] = &[
354    // The quad format, which no x86-64 instruction touches. `crate::quad` is the pass.
355    (Opcode::FAdd, "f128", "__addtf3"),
356    (Opcode::FSub, "f128", "__subtf3"),
357    (Opcode::FMul, "f128", "__multf3"),
358    (Opcode::FDiv, "f128", "__divtf3"),
359    (Opcode::FNeg, "f128", "__negtf2"),
360    // A comparison is one call per predicate and a test of the integer it gives back, which is why
361    // the pass keeps the integer predicate beside the name and this list holds only the name.
362    (Opcode::FCmp, "oeq.f128", "__eqtf2"),
363    (Opcode::FCmp, "une.f128", "__netf2"),
364    (Opcode::FCmp, "olt.f128", "__lttf2"),
365    (Opcode::FCmp, "ole.f128", "__letf2"),
366    (Opcode::FCmp, "ogt.f128", "__gttf2"),
367    (Opcode::FCmp, "oge.f128", "__getf2"),
368    (Opcode::FCmp, "uno.f128", "__unordtf2"),
369    (Opcode::FPExt, "f32.f128", "__extendsftf2"),
370    (Opcode::FPExt, "f64.f128", "__extenddftf2"),
371    (Opcode::FPTrunc, "f128.f32", "__trunctfsf2"),
372    (Opcode::FPTrunc, "f128.f64", "__trunctfdf2"),
373    (Opcode::SIToFP, "i32.f128", "__floatsitf"),
374    (Opcode::SIToFP, "i64.f128", "__floatditf"),
375    (Opcode::UIToFP, "i32.f128", "__floatunsitf"),
376    (Opcode::UIToFP, "i64.f128", "__floatunditf"),
377    (Opcode::FPToSI, "f128.i32", "__fixtfsi"),
378    (Opcode::FPToSI, "f128.i64", "__fixtfdi"),
379    (Opcode::FPToUI, "f128.i32", "__fixunstfsi"),
380    (Opcode::FPToUI, "f128.i64", "__fixunstfdi"),
381    // The half format, which no x86-64 instruction computes in either. `crate::half` is the pass,
382    // and it needs fewer rows than the quad does because it has somewhere to go: a half widens to
383    // a `float` exactly, so every operation is the `float` one with a widening in front of it and
384    // a narrowing behind it, and only the widening and the narrowings are calls.
385    //
386    // The three narrowings are three rows and not one, and that is the part worth reading twice.
387    // Each of them rounds once, and rounding twice is a different answer: a `double` that sits
388    // just above the halfway point between two halves rounds down to that halfway point in a
389    // `float` and then to even from there, which is the wrong neighbour. libgcc has a routine per
390    // source width for exactly this reason and gcc calls the one that matches, so this table has a
391    // row per source width too.
392    (Opcode::FPExt, "f16.f32", "__extendhfsf2"),
393    (Opcode::FPTrunc, "f32.f16", "__truncsfhf2"),
394    (Opcode::FPTrunc, "f64.f16", "__truncdfhf2"),
395    (Opcode::FPTrunc, "f128.f16", "__trunctfhf2"),
396    // An integer wider than a register. `crate::wide` splits what it can into halves and calls for
397    // what it cannot, which is the four that need the whole value at once and the conversions.
398    (Opcode::UDiv, "i128", "__udivti3"),
399    (Opcode::SDiv, "i128", "__divti3"),
400    (Opcode::URem, "i128", "__umodti3"),
401    (Opcode::SRem, "i128", "__modti3"),
402    (Opcode::SIToFP, "i128.f32", "__floattisf"),
403    (Opcode::SIToFP, "i128.f64", "__floattidf"),
404    (Opcode::SIToFP, "i128.f128", "__floattitf"),
405    (Opcode::UIToFP, "i128.f32", "__floatuntisf"),
406    (Opcode::UIToFP, "i128.f64", "__floatuntidf"),
407    (Opcode::UIToFP, "i128.f128", "__floatuntitf"),
408    (Opcode::FPToSI, "f32.i128", "__fixsfti"),
409    (Opcode::FPToSI, "f64.i128", "__fixdfti"),
410    (Opcode::FPToSI, "f128.i128", "__fixtfti"),
411    (Opcode::FPToUI, "f32.i128", "__fixunssfti"),
412    (Opcode::FPToUI, "f64.i128", "__fixunsdfti"),
413    (Opcode::FPToUI, "f128.i128", "__fixunstfti"),
414    // The bulk operations, which are the C library rather than the compiler runtime. A copy the
415    // lowering will not take on is one whose size is not a constant or is above the threshold, and
416    // a move is always a call because the two regions may overlap.
417    (Opcode::Memcpy, "big", "memcpy"),
418    (Opcode::Memset, "big", "memset"),
419    (Opcode::Memmove, "any", "memmove"),
420];
421
422/// The runtime function this operation at this mode becomes, or nothing where it is not a call.
423///
424/// What the passes that emit one read, so that the name a call is made under and the name the table
425/// reports are the same string rather than two strings somebody has to keep equal.
426#[must_use]
427pub fn libcall(opcode: Opcode, mode: &str) -> Option<&'static str> {
428    LIBCALLS
429        .iter()
430        .find(|&&(at, spelled, _)| at == opcode && spelled == mode)
431        .map(|&(_, _, name)| name)
432}
433
434/// What rewrites this opcode before selection, if anything does.
435///
436/// The group is asked first and answers for itself, so the membership is not written down twice.
437/// [`HAND`] is what is left, which is the opcodes no pass can be asked about.
438#[must_use]
439pub fn lowering(opcode: Opcode) -> Option<Lowering> {
440    for &step in Step::GROUP {
441        if step.opcodes().contains(&opcode) {
442            return Some(Lowering::Group(step));
443        }
444    }
445    HAND.iter().find(|&&(at, _)| at == opcode).map(|&(_, where_)| Lowering::Hand(where_))
446}
447
448/// The whole table for one target's rules.
449///
450/// Nothing is compiled and nothing is run. Every column is data: the rule set is a table, the group
451/// says which opcodes it is about, and the two lists above are lists.
452#[must_use]
453pub fn rows(table: &Table) -> Vec<Row> {
454    let patterns = pattern_heads(table);
455    let named = rucc_ir::term::heads();
456    let mut out = Vec::with_capacity(named.len() + LIBCALLS.len() + 64);
457
458    // The names the rule language can spell, which is the universe the rule column is about.
459    for &(opcode, name) in &named {
460        out.push(Row {
461            opcode,
462            name,
463            rule: patterns.contains(&name),
464            lowering: lowering(opcode),
465            libcall: None,
466            nothing: NAMES
467                .iter()
468                .find(|&&(at, ..)| at == name)
469                .map(|&(_, why, issue)| (why, issue)),
470        });
471    }
472
473    // The modes the rule language cannot spell, which is why the calls exist.
474    for &(opcode, mode, call) in LIBCALLS {
475        out.push(Row {
476            opcode,
477            name: mode,
478            rule: false,
479            lowering: lowering(opcode),
480            libcall: Some(call),
481            nothing: None,
482        });
483    }
484
485    // And the operations with no mode at all, which still have the question asked about them.
486    for opcode in Opcode::all() {
487        if named.iter().any(|&(at, _)| at == opcode) {
488            continue;
489        }
490        if LIBCALLS.iter().any(|&(at, ..)| at == opcode) {
491            continue;
492        }
493        out.push(Row {
494            opcode,
495            name: opcode.name(),
496            rule: false,
497            lowering: lowering(opcode),
498            libcall: None,
499            nothing: GAPS
500                .iter()
501                .find(|&&(at, ..)| at == opcode)
502                .map(|&(_, why, issue)| (why, issue)),
503        });
504    }
505    out
506}
507
508/// Every name a rule in a table is written about, which is the first question the trie asks.
509///
510/// Node zero is the root of the trie over the patterns and the first thing any walk asks is what
511/// the term in hand is called, so the branches on the head there are exactly the set of pattern
512/// heads. Nothing else can be at the root: a pattern is a term with a head, so the first step of
513/// every one of them is a head, there is no constant to compare and nothing bound yet to be the
514/// same as. There is no wildcard there to worry about either, since a rule matching any term at
515/// all is one nobody has written and one that would be an error to write, because a lowering has
516/// to know what it is lowering.
517///
518/// The names come out sorted and without repeats because the root is sorted, which is what the
519/// walk needs it to be, so there is nothing to do here but read it.
520pub(crate) fn pattern_heads(table: &Table) -> Vec<&'static str> {
521    let Some(root) = table.nodes.first() else { return Vec::new() };
522    let mut found: Vec<&'static str> = root.heads.iter().map(|&(head, ..)| head).collect();
523    found.dedup();
524    found
525}
526
527#[cfg(test)]
528mod tests {
529    use super::*;
530    use crate::select::x86_64::TABLE;
531
532    /// The claim the table is for, which is section 36.4's: an operation this target cannot do, that
533    /// nothing rewrites and that has no runtime function, is a build failure here rather than a
534    /// selection failure on somebody's program.
535    #[test]
536    fn every_row_has_at_least_one_answer_or_says_why_it_has_none() {
537        let mut unanswered = Vec::new();
538        for row in rows(&TABLE) {
539            if row.answered() || row.nothing.is_some() {
540                continue;
541            }
542            unanswered.push(row.name);
543        }
544        assert!(
545            unanswered.is_empty(),
546            "no rule lowers these, nothing rewrites them, no runtime function stands for them and \
547             nothing says why: {unanswered:?}"
548        );
549    }
550
551    /// Every row that has no answer names the issue that gives it one, since a hole with no issue
552    /// behind it is a hole nobody has decided anything about.
553    #[test]
554    fn a_row_with_no_answer_names_the_issue_that_gives_it_one() {
555        for row in rows(&TABLE) {
556            let Some((why, issue)) = row.nothing else { continue };
557            assert!(
558                !row.answered(),
559                "`{}` is {why} and is also answered, so the entry is stale and {issue} may be \
560                 closed",
561                row.name
562            );
563            let number = issue
564                .strip_prefix("tamnd/rucc#")
565                .unwrap_or_else(|| panic!("{issue} is not an issue in this project's tracker"));
566            assert!(number.parse::<u32>().is_ok(), "{issue} does not name an issue number");
567        }
568    }
569
570    /// The one overlap that is a contradiction. A rule at a name something rewrites by hand before
571    /// selection runs is a rule that can never fire, because the instruction is gone by then. The
572    /// group is not this, which is the next test.
573    #[test]
574    fn a_rule_at_a_name_something_rewrites_by_hand_could_never_fire() {
575        for row in rows(&TABLE) {
576            let Some(Lowering::Hand(where_)) = row.lowering else { continue };
577            assert!(
578                !row.rule,
579                "`{}` is rewritten by {where_} before selection, so the rule written at it can \
580                 never fire",
581                row.name
582            );
583        }
584    }
585
586    /// And the overlap that is not a contradiction, which is the thing the old single list could
587    /// not say. A member of the group is allowed to leave a construct alone, and the two it leaves
588    /// alone most often are the conversions this machine has an instruction for, so those names
589    /// have a rule and a lowering at once and both are true.
590    #[test]
591    fn an_operation_the_machine_has_and_a_lowering_names_is_allowed_both() {
592        let both: Vec<&str> = rows(&TABLE)
593            .iter()
594            .filter(|row| row.rule && matches!(row.lowering, Some(Lowering::Group(_))))
595            .map(|row| row.name)
596            .collect();
597        assert!(
598            both.iter().any(|name| name.starts_with("sitofp.")),
599            "a signed conversion is what `crate::expand` walks away from when the machine has the \
600             instruction, and the table should show both answers: {both:?}"
601        );
602    }
603
604    /// The group membership is read rather than repeated, which is what stops the two going out of
605    /// step. Asking for a step's opcodes and asking the table for the same opcode give the same
606    /// step, because there is only the one list.
607    #[test]
608    fn the_lowering_column_is_the_group_saying_what_it_is_about() {
609        for &step in Step::GROUP {
610            for &opcode in step.opcodes() {
611                assert_eq!(
612                    lowering(opcode),
613                    Some(Lowering::Group(step)),
614                    "`{}` is named by `{}` and the table says otherwise",
615                    opcode.name(),
616                    step.name()
617                );
618            }
619        }
620    }
621
622    /// An opcode is answered for in one place. A member of the group that is also on the hand
623    /// written list is the three mechanisms back again, with the list and the group each thinking
624    /// it owns the opcode.
625    #[test]
626    fn nothing_the_group_names_is_also_written_down_by_hand() {
627        for &(opcode, where_) in HAND {
628            for &step in Step::GROUP {
629                assert!(
630                    !step.opcodes().contains(&opcode),
631                    "`{}` is named by `{}` and the hand written list says it is lowered by {where_}",
632                    opcode.name(),
633                    step.name()
634                );
635            }
636        }
637    }
638
639    /// A runtime function is named once. Two rows naming the same call at the same mode would be
640    /// the same duplication in the column the passes read.
641    #[test]
642    fn no_two_rows_answer_for_the_same_operation_at_the_same_mode() {
643        let mut seen: Vec<(Opcode, &str)> = Vec::new();
644        for &(opcode, mode, call) in LIBCALLS {
645            assert!(
646                !seen.contains(&(opcode, mode)),
647                "`{}` at `{mode}` is answered twice, and the second answer is {call}",
648                opcode.name()
649            );
650            seen.push((opcode, mode));
651        }
652    }
653
654    /// The lookup the passes make, which is the whole reason the list is data rather than `match`
655    /// arms. A pass asks for the operation and the mode and gets the one string.
656    #[test]
657    fn the_passes_ask_for_a_call_by_the_operation_and_the_mode() {
658        assert_eq!(libcall(Opcode::SDiv, "i128"), Some("__divti3"));
659        assert_eq!(libcall(Opcode::FAdd, "f128"), Some("__addtf3"));
660        assert_eq!(libcall(Opcode::SIToFP, "i128.f64"), Some("__floattidf"));
661        assert_eq!(libcall(Opcode::Memmove, "any"), Some("memmove"));
662        assert_eq!(libcall(Opcode::SDiv, "i64"), None, "a divide the machine has is not a call");
663        assert_eq!(libcall(Opcode::Add, "i128"), None, "a wide add is two adds and not a call");
664    }
665
666    /// Every runtime function is one somebody can link against, which for the compiler runtime
667    /// means the name gcc's own runtime uses. A misspelled one would build and fail at the link,
668    /// which is the furthest away this mistake can be found.
669    #[test]
670    fn a_runtime_function_is_spelled_the_way_the_runtime_spells_it() {
671        for &(opcode, mode, call) in LIBCALLS {
672            let library = matches!(opcode, Opcode::Memcpy | Opcode::Memset | Opcode::Memmove);
673            assert_eq!(
674                call.starts_with("__"),
675                !library,
676                "`{call}` is a {} function and is not spelled like one",
677                if library { "C library" } else { "compiler runtime" }
678            );
679            assert!(
680                !mode.is_empty() && mode.is_ascii(),
681                "`{call}` answers for a mode with no name"
682            );
683        }
684    }
685
686    /// One row per operation and mode, which is what section 36.4 asks for, and enough of them that
687    /// the table is about the whole back end rather than a corner of it.
688    #[test]
689    fn the_table_is_one_row_per_operation_and_mode() {
690        let rows = rows(&TABLE);
691        assert!(
692            rows.len() > Opcode::all().count(),
693            "an operation with more than one mode is more than one row, so there are more rows \
694             than there are opcodes: {} rows and {} opcodes",
695            rows.len(),
696            Opcode::all().count()
697        );
698        let with_rule = rows.iter().filter(|row| row.rule).count();
699        let with_lowering = rows.iter().filter(|row| row.lowering.is_some()).count();
700        let with_libcall = rows.iter().filter(|row| row.libcall.is_some()).count();
701        assert!(with_rule > 0 && with_lowering > 0 && with_libcall > 0);
702        assert_eq!(with_libcall, LIBCALLS.len());
703        println!(
704            "rucc-codegen: {} rows, {with_rule} by rule, {with_lowering} by lowering, \
705             {with_libcall} by a call to the runtime",
706            rows.len()
707        );
708    }
709
710    /// Every opcode is in the table exactly once under its own name or once per mode it has, and
711    /// none is left out. A new opcode with no row is the thing this whole module exists to stop.
712    #[test]
713    fn every_opcode_the_ir_has_is_in_the_table() {
714        let rows = rows(&TABLE);
715        for opcode in Opcode::all() {
716            assert!(
717                rows.iter().any(|row| row.opcode == opcode),
718                "`{}` has no row, so nothing says what this target does about it",
719                opcode.name()
720            );
721        }
722    }
723
724    /// What a hand written entry says, which is a place somebody can open. An entry naming nothing
725    /// is an entry that excuses an opcode without saying where the answer is.
726    #[test]
727    fn a_hand_written_entry_names_where_the_answer_is() {
728        for &(opcode, where_) in HAND {
729            assert!(
730                where_.contains('`') || where_.starts_with("nothing"),
731                "the entry for `{}` says {where_}, which names no module",
732                opcode.name()
733            );
734        }
735        assert_eq!(Lowering::Hand("`crate::abi`, and so on").where_(), "`crate::abi`, and so on");
736        assert_eq!(Lowering::Group(Step::Bytes).where_(), "bytes");
737    }
738}