Skip to main content

rucc_codegen/
combine.rs

1//! Putting a run of machine instructions together into the shorter run the machine has for it.
2//!
3//! Design: `spec/10-backend.md` section 10.9, and `spec/optimizer/37-machine-level-optimization.md`
4//! sections 37.3 and 37.4.
5//!
6//! Section 37.4 names this pass first of the ten it says are genuinely machine level, and says what
7//! shape it should be: a match over machine instructions in SSA form, inside one block, over a
8//! window of a few instructions, which is `gcc/late-combine.cc` rather than `gcc/combine.cc`. The
9//! reason for the smaller of the two is in the same section. Combine is fifteen thousand lines
10//! because it was written without def-use chains and had to find them again each time, and every
11//! RTL pass GCC has written since is on the SSA form it added later for exactly that.
12//!
13//! Section 37.3 says what the pass does once it has found a run: substitute the earlier instruction
14//! into the later one, and ask the machine description whether what came out is an instruction this
15//! target has. That is [`crate::changes`] and this pass does not repeat any of it.
16//!
17//! # The runs it puts together
18//!
19//! Two of them. A value read out of memory and then used once, by arithmetic that this machine
20//! could have read it out of memory itself, which is [`loads`]:
21//!
22//! ```text
23//!   movq 16(%rax), %rcx
24//!   addq %rcx, %rdx        ->    addq 16(%rax), %rdx
25//! ```
26//!
27//! Two instructions become one. The register the load wrote is not written at all, which is one
28//! fewer value for the allocator to find a place for, and the bytes come down because an addressing
29//! mode costs what it costs whichever instruction carries it and the load's own opcode byte goes.
30//!
31//! It is the commonest pair in the machine IR this compiler writes. Counting adjacent instructions
32//! over the corpus at `-O2`, where the first writes what the second reads, the largest family by a
33//! long way is a move into arithmetic, and an addition at eight bytes is the largest single entry
34//! in it. What the pass gets over that corpus is 865 of these at `-O2` and 845 fewer instructions
35//! once the allocator has had its say, with the difference between the two explained below.
36//!
37//! And the same value written back where it came from, which is [`stores`]:
38//!
39//! ```text
40//!   movq 16(%rax), %rcx
41//!   addq %rdx, %rcx        ->    addq %rdx, 16(%rax)
42//!   movq %rcx, 16(%rax)
43//! ```
44//!
45//! Three instructions become one, and this is what a C program writes as `*p += x`. The register in
46//! the middle goes the way the load's register goes above, and so does the second addressing mode,
47//! which was the same address written down twice.
48//!
49//! [`stores`] takes the same run with a constant in it, which is what a C program writes as
50//! `*p += 1` and is the commoner of the two:
51//!
52//! ```text
53//!   movq 16(%rax), %rcx
54//!   addq $1, %rcx          ->    addq $1, 16(%rax)
55//!   movq %rcx, 16(%rax)
56//! ```
57//!
58//! Nothing is left holding a register here at all. The instruction that comes out reads the place,
59//! adds the constant the instruction carries and writes the place, so the whole run costs the
60//! addressing mode and the constant and no operand the allocator has to answer for.
61//!
62//! [`stores`] runs first. Its run is three instructions as the selector wrote them, and folding the
63//! load into the middle one first would leave the same run written a second way that the walk would
64//! then have to know about. Whatever it does not take is still a pair for [`loads`].
65//!
66//! # Why no rule does it
67//!
68//! The selector matches a term, and a term is one value. A load is a term and an addition is a
69//! term, and the pattern that would cover both is an addition with a load under it, which the
70//! selector does offer: it shows a rule the operands of its operands. What it cannot offer is the
71//! rest of the condition. Whether the load may move down to where the addition is depends on what
72//! is written between the two, and whether the load's value is wanted anywhere else depends on the
73//! whole function. Neither is a fact about the term, so neither can be in a pattern.
74//!
75//! # When the load may move
76//!
77//! The load stops being where it was and starts being part of an instruction further down the
78//! block, so everything between the two has to be something the load can pass. Two things are not.
79//!
80//! Anything that touches memory, whether it reads or writes. A write is the obvious half: whether
81//! it writes the bytes this load reads is a question about two addresses, and telling two addresses
82//! apart is an analysis nothing below selection has, so the walk below stops at a store rather than
83//! guessing. [`MachineInsts::touches_mem`] is the target's answer and [`MachineInsts::calls`] is the
84//! rest of it, since what a call does to memory is not in the instruction at all.
85//!
86//! A read is the half that is easy to argue away and is the one that matters. Moving a read past a
87//! read changes the order two accesses happen in, and the program may have said what that order is.
88//! `volatile int a, b; return b - a;` is two loads and a subtract, and folding the first of them
89//! into the subtract would read `b` before `a` when the program said otherwise. The flag that says
90//! so reaches here now, so the walk could ask about each access one at a time, and it does not:
91//! stopping at every access rules the same thing out and costs almost nothing, since the load the
92//! arithmetic reads is nearly always the last access before it and so is still the one that folds.
93//!
94//! What follows from that is the shape of the walk. There is one load in hand rather than a list of
95//! them, and it is always the last memory access there was.
96//!
97//! Anything that writes a register the address reads. Machine IR is in SSA form until the
98//! allocator has run, so a virtual register cannot be written twice, but the stack pointer and the
99//! frame pointer are physical here and an address into the frame reads one of them.
100//!
101//! # When the load is wanted elsewhere
102//!
103//! Exactly one instruction may read what the load wrote, and it has to be the one taking the load
104//! in. [`Reads`] is that count, kept across the commits of the pass the way [`crate::fold`] keeps
105//! it, and a count of one is the whole of the test because a virtual register is written once. Two
106//! readers and the load has to stay where it is, so putting it into one of them buys nothing and
107//! costs a second read of memory.
108//!
109//! An argument an edge carries is a read like any other and is in no operand vector, which is the
110//! one place a count of this shape is easy to get wrong. [`Reads::of`] counts those, which is what
111//! keeps a load whose value leaves the block out of this.
112//!
113//! # Which arithmetic
114//!
115//! [`FOLDS`] is the list, and it is a list rather than a rule about names because the two ends of
116//! each entry are instructions the target describes separately and the widths have to agree. A
117//! sixty four bit addition takes a sixty four bit load and nothing else: reading four bytes where
118//! the program asked for eight is a different instruction, and reading eight where it asked for
119//! four is three bytes nobody said were there.
120//!
121//! The eight bit multiply is the one member of the family with no entry. This machine has no
122//! two-operand multiply narrower than sixteen bits, so an eight bit one is written as a thirty two
123//! bit `imul` and reads a register whose upper bits nothing looks at. A memory operand has no
124//! upper bits to not look at, so there is nothing to read there and the entry is left out.
125//!
126//! # Either source, when the operation does not care
127//!
128//! An addition reads two registers and it is the second of them the memory operand replaces,
129//! because the first is the one the destination is tied to. Where the load feeds the first instead,
130//! the two sources are swapped first, which is a change to the instruction and not to what it
131//! computes as long as the operation commutes. Five of the six here do and subtraction does not,
132//! which is what [`Fold::swapped`] says.
133//!
134//! # Which comparisons
135//!
136//! The comparisons are in [`FOLDS`] too, and they are the reason that field is a name rather than
137//! a flag. A comparison writes a byte neither source has a claim on, so both of its sources are
138//! free the way an addition's second one is, and it still does not commute: the machine reads the
139//! right hand side out of memory and subtracts it from the left. What saves the other arrangement
140//! is that reading the two sides backwards asks the same question backwards, so a load feeding the
141//! left hand side becomes the same instruction with the condition turned over, and `*p < x` is
142//! `x > *p`. Equality and inequality turn over into themselves and the other eight go in pairs.
143//!
144//! A comparison against a constant has one register rather than two and folds too, which is what
145//! a C program writes as `if (*p == 7)`:
146//!
147//! ```text
148//!   movl 16(%rax), %ecx
149//!   cmpl $7, %ecx          ->    cmpl $7, 16(%rax)
150//! ```
151//!
152//! Nothing is arranged either way round here. The constant is on the instruction and has nowhere
153//! else to be, so the side the load filled is the left hand side and stays the left hand side, and
154//! the condition is the one the comparison already had. What is left holding a register is the byte
155//! the comparison sets, and the block layout usually takes that too.
156//!
157//! # A widening
158//!
159//! A load that is read only to be widened is the same pair with one source, and this machine reads
160//! memory and widens it in one instruction. It is what a C program gets for `long x = a[i];` on an
161//! array of `int`, and for every `char` or `short` read into an `int`:
162//!
163//! ```text
164//!   movl (%rcx,%rax,4), %eax
165//!   movslq %eax, %rax      ->    movslq (%rcx,%rax,4), %rax
166//! ```
167//!
168//! [`WIDENINGS`] is the list: a sign and a zero widening from each width that has one, and the zero
169//! widening from thirty two bits to sixty four. That last one is a plain `movl` between registers,
170//! because writing the low half of a register clears the high half, and a `movl` from memory does
171//! the same, so what comes out of it is the load on its own writing the wider register.
172//!
173//! The rules are the ones above. The widening has to be the only reader of the load, and nothing
174//! between the two may touch memory. It is a table of its own rather than more rows of [`FOLDS`]
175//! because a widening reads one width and writes another, which is the one thing every row of that
176//! table is checked not to do.
177//!
178//! # What a `volatile` access gets
179//!
180//! Nothing. Both walks stop at one, so `volatile int *p; *p += x;` comes out as the load, the
181//! arithmetic and the store, and `volatile int *p; return *p + x;` keeps its load.
182//!
183//! What the flag says is that the access happens exactly once and is never moved or merged with
184//! another, and the first two of those were already true here: the walk in [`loads`] stops at any
185//! instruction that touches memory, so nothing ever passes an access, and no fold in this module
186//! turns one access into two or none. Merging is the one that was not. Reading a place, adding to
187//! it and putting it back is one read and one write of the address whether it is three
188//! instructions or one, so the counts the standard talks about are the same either way, and what
189//! the two differ on is whether the reading and the writing are one instruction. A device register
190//! whose memory does something when it is touched is where that difference is the whole point.
191//!
192//! This is a place where the answer is the spec's rather than the reference compiler's.
193//! `spec/optimizer/09-memory-ssa.md` section 9.5 says a `volatile` access is never moved, never
194//! eliminated, never duplicated and never merged, and that last word is this. GCC 16 writes
195//! `addl %esi, (%rdi)` for the read modify write and `cmpl $7, (%rdi)` for a `volatile` compare,
196//! and GCC 13 writes three instructions and two for the same programs, so the merge is something
197//! GCC started doing rather than something it has always done. Both are conforming and neither
198//! changes how many times the address is touched. Taking the spec's side costs an instruction on
199//! code that asked to be watched, which is the trade that document says to make.
200//!
201//! The flag is on the machine instruction because [`rucc_mir::Flags`] carries it now and selection
202//! sets it from the load or the store it matched. Before that it could not be read here at all:
203//! a `volatile` access and an ordinary one were the same opcode over the same address, so there
204//! was nothing to stop at. That was tamnd/rucc#1302.
205//!
206//! # What makes the three one
207//!
208//! The same three questions as the pair, and one more. The word the load read is read by the
209//! arithmetic and by nothing else, the answer the arithmetic wrote is read by the store and by
210//! nothing else, and nothing between the load and the store touches memory or writes a register the
211//! instruction that is left still reads. The run collapses onto the store, so the read of memory
212//! moves down the block to where the write already was, which is the move the memory rule is about.
213//!
214//! The one more is that the two addressing modes have to name the same place. The same registers,
215//! the same scale, the same displacement and the same symbol is most of it, and the frame is the
216//! rest: the displacement of a local is a number [`crate::finish`] has still to add the frame's own
217//! offset to, so two locals can be the same three registers and the same zero here and be two
218//! different places. The list itself is what tells those apart, and the entry the load was
219//! waiting on comes off the list when the run is joined, since the store is already waiting on the
220//! same one.
221//!
222//! # The condition state, which the three has and the pair does not
223//!
224//! The arithmetic in the middle of the run is not where it was afterwards. The run collapses onto
225//! the store, so the load moves down and so does the arithmetic, and the arithmetic writes the
226//! condition state where the load writes nothing. That makes the state a question here and not in
227//! [`loads`], where the arithmetic stays exactly where it is and only a load passes anything.
228//!
229//! Two ways it goes wrong, and both are about the instructions between the arithmetic and the
230//! store. An instruction there that reads the state read what the arithmetic left, and after the
231//! move it reads whatever was there before the arithmetic instead. An instruction there that writes
232//! the state was the last writer before the store, and after the move the arithmetic is, so
233//! anything further down that reads the state reads a different answer. So neither is allowed, and
234//! `quiet` is the question. It is asked from the arithmetic rather than from the load, because
235//! between the load and the arithmetic nothing has moved and the state is nobody's business.
236//!
237//! This is what tamnd/rucc#1424 was. libgmp's `mpn_mulmod_bnm1` adds a limb into a place and then
238//! reads the carry out of that addition with `adcq %rdx, %rdx`, which is how this back end gets a
239//! carry into a register, and the addition and the store it fed were a run with the carry reader
240//! sitting between them. Folding them moved the addition below the reader, so the carry that went
241//! into the next limb was the one a `subq` three instructions earlier had left, and the library
242//! computed a product that was wrong in one limb. It came back as a division that never finished,
243//! a long way from here.
244//!
245//! [`rucc_target::FlagInsts`] is where the answer comes from, the same description
246//! [`crate::compare`] and [`crate::shorten`] ask, and a name it does not cover counts as both a
247//! read and a write, which is the answer that finds fewer runs rather than the one that is wrong.
248//!
249//! # The window
250//!
251//! A load is carried forward at most [`WINDOW`] instructions and then dropped. The bound is what
252//! makes the pass cost a fixed amount per instruction rather than an amount that grows with the
253//! block, which section 37.3 records as GCC's own answer: `max-combine-insns` is four and has been
254//! for decades.
255//!
256//! It is also nearly all of it already at one. The measurement in [`WINDOW`] is that a bound of one
257//! finds 852 folds over the corpus and a bound of thirty two finds 865, which follows from the rule
258//! above about memory rather than from anything about how the selector writes code: the load that
259//! folds is the last access to memory before the arithmetic, and the last access before it is
260//! usually the instruction in front of it. The window is there to bound the walk and it earns
261//! thirteen folds along the way.
262//!
263//! # Where it costs something
264//!
265//! A fold takes out exactly one instruction, so the number of folds and the number of instructions
266//! saved should be the same number, and they are not: 865 folds against 845 instructions over the
267//! corpus at `-O2`, and 1609 against 1444 over the SQLite amalgamation. The gap is the allocator.
268//!
269//! Taking the load out changes which values are live where, so the allocator makes different
270//! choices, and a few of them are worse. Two programs in the corpus come out two instructions
271//! longer at every level above `-O0`, both for the same reason: the folded addition is given a
272//! callee saved register while a caller saved one was free, which buys a push, a pop and a copy for
273//! a value that dies before the next call. That is the allocator preferring the wrong end of its
274//! own list rather than anything this pass did, and it is worth fixing where it is rather than
275//! worth not folding over.
276//!
277//! The trade is the other thing the gap is, and it is a real one rather than an accounting error.
278//! Two instructions become one and the one that is left both reads memory and computes, so it is
279//! two operations in one slot rather than one, which a machine that issues several instructions at
280//! once may not want. The measurement that settles it is run time rather than instruction count,
281//! and section 38.6's scheduler is where that argument belongs, since a scheduler is the pass that
282//! can see whether the slot was going to be used.
283//!
284//! # What it does not do yet
285//!
286//! A comparison. This machine compares against memory as readily as it adds to it, and the reason
287//! there is no entry for one is that a comparison here is one opcode holding a compare and the byte
288//! behind it, so the memory form is a third instruction rather than a second and the target has to
289//! describe it before this can write it.
290//!
291//! Arithmetic against a constant, in either run. `addq $1, 16(%rcx)` is `*p += 1`, which is at
292//! least as common as `*p += x`, and the target has no form that carries an addressing mode and an
293//! immediate together. That is a third instruction description rather than a rule, the way the
294//! comparison above is.
295//!
296//! Anything longer than the two runs above. Section 37.3 says GCC goes to four instructions, and
297//! the longer of the two here is three. What makes a fourth worth having is a rule set that has
298//! something to say about four, and the rule set here grows one measured entry at a time.
299
300use rucc_base::Interner;
301use rucc_mir::{Amode, Flags, Func, Inst, Opcode, Operand, Reg};
302use rucc_target::{FlagInsts, MachineInsts};
303
304use crate::changes::{Changes, Plan, Reads};
305use crate::fold::Pending;
306
307/// How far a load is carried looking for the instruction that takes it in.
308///
309/// Measured over the corpus at `-O2`, which folds this many loads at each bound:
310///
311/// ```text
312///   1     2     4     8    16    32
313/// 852   858   863   864   865   865
314/// ```
315///
316/// Sixteen, because that is where the curve stops. Doubling it again finds nothing, and the pass
317/// still costs a fixed amount per instruction, which is what the bound is for.
318///
319/// The curve is that flat because of the rule about memory rather than because of anything the
320/// selector does. The load that folds is the last access to memory before the arithmetic, and
321/// almost always that is the instruction immediately in front of it. What the room past one buys is
322/// the thirteen where a register was written or a constant made in between.
323pub const WINDOW: usize = 16;
324
325/// One arithmetic instruction that could read its second source out of memory, and the load that
326/// would fill it.
327///
328/// A table rather than a rule about spellings, because the three names in each row are three things
329/// the target describes on their own and nothing about `add_rr_64` says that `mov_rm_64` is the
330/// load of the same width. Writing the three together is what makes a mismatched width a line
331/// somebody can see rather than a string that was built at run time.
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct Fold {
334    /// The arithmetic as the selector wrote it, reading both its sources from registers.
335    pub from: &'static str,
336    /// The same arithmetic reading its second source out of memory.
337    pub into: &'static str,
338    /// The load that would have filled that register, which has to be of the same width.
339    pub load: &'static str,
340    /// The same arithmetic reading its first source out of memory, where there is one.
341    ///
342    /// [`None`] where the two sources may not be swapped at all, which is subtraction: the
343    /// instruction that reads memory reads it as the right hand side and there is no encoding
344    /// that puts it on the left, so a load feeding the left hand side stays where it is. Also
345    /// [`None`] for a comparison against a constant, which has one source rather than two and so
346    /// nothing to swap it with.
347    ///
348    /// The same name as `into` for an operation that commutes, since writing the two sources in
349    /// either order computes the same answer and one instruction covers both.
350    ///
351    /// A different name for a comparison, which is the reason this is a name rather than a flag.
352    /// A comparison does not commute and is still foldable on either side: reading the two sides
353    /// the other way round asks the same question backwards, so the condition turns over with
354    /// them and `a < b` with the load on the left is `b > a`.
355    pub swapped: Option<&'static str>,
356}
357
358/// The arithmetic a load can move into on this machine.
359///
360/// Every two-address integer operation the target has, at every width it has one, except the eight
361/// bit multiply the module documentation gives the reason for. Subtraction is the one that does not
362/// commute.
363///
364/// And then the comparisons, which are not arithmetic and fold the same way. What one writes is a
365/// byte rather than one of its sources, so the row reads the same and the instruction it names has
366/// a destination neither side has a claim on. The forty rows are ten conditions at four widths and
367/// each names two instructions, because which side the memory is is a condition of its own.
368///
369/// And then the same forty against a constant, which name one instruction each. The constant is on
370/// the instruction and cannot be anywhere else, so the register the load filled is the left hand
371/// side and there is no other arrangement to offer.
372pub static FOLDS: &[Fold] = &[
373    Fold { from: "add_rr_8", into: "add_rm_8", load: "mov_rm_8", swapped: Some("add_rm_8") },
374    Fold { from: "add_rr_16", into: "add_rm_16", load: "mov_rm_16", swapped: Some("add_rm_16") },
375    Fold { from: "add_rr_32", into: "add_rm_32", load: "mov_rm_32", swapped: Some("add_rm_32") },
376    Fold { from: "add_rr_64", into: "add_rm_64", load: "mov_rm_64", swapped: Some("add_rm_64") },
377    Fold { from: "sub_rr_8", into: "sub_rm_8", load: "mov_rm_8", swapped: None },
378    Fold { from: "sub_rr_16", into: "sub_rm_16", load: "mov_rm_16", swapped: None },
379    Fold { from: "sub_rr_32", into: "sub_rm_32", load: "mov_rm_32", swapped: None },
380    Fold { from: "sub_rr_64", into: "sub_rm_64", load: "mov_rm_64", swapped: None },
381    Fold { from: "and_rr_8", into: "and_rm_8", load: "mov_rm_8", swapped: Some("and_rm_8") },
382    Fold { from: "and_rr_16", into: "and_rm_16", load: "mov_rm_16", swapped: Some("and_rm_16") },
383    Fold { from: "and_rr_32", into: "and_rm_32", load: "mov_rm_32", swapped: Some("and_rm_32") },
384    Fold { from: "and_rr_64", into: "and_rm_64", load: "mov_rm_64", swapped: Some("and_rm_64") },
385    Fold { from: "or_rr_8", into: "or_rm_8", load: "mov_rm_8", swapped: Some("or_rm_8") },
386    Fold { from: "or_rr_16", into: "or_rm_16", load: "mov_rm_16", swapped: Some("or_rm_16") },
387    Fold { from: "or_rr_32", into: "or_rm_32", load: "mov_rm_32", swapped: Some("or_rm_32") },
388    Fold { from: "or_rr_64", into: "or_rm_64", load: "mov_rm_64", swapped: Some("or_rm_64") },
389    Fold { from: "xor_rr_8", into: "xor_rm_8", load: "mov_rm_8", swapped: Some("xor_rm_8") },
390    Fold { from: "xor_rr_16", into: "xor_rm_16", load: "mov_rm_16", swapped: Some("xor_rm_16") },
391    Fold { from: "xor_rr_32", into: "xor_rm_32", load: "mov_rm_32", swapped: Some("xor_rm_32") },
392    Fold { from: "xor_rr_64", into: "xor_rm_64", load: "mov_rm_64", swapped: Some("xor_rm_64") },
393    Fold { from: "imul_rr_16", into: "imul_rm_16", load: "mov_rm_16", swapped: Some("imul_rm_16") },
394    Fold { from: "imul_rr_32", into: "imul_rm_32", load: "mov_rm_32", swapped: Some("imul_rm_32") },
395    Fold { from: "imul_rr_64", into: "imul_rm_64", load: "mov_rm_64", swapped: Some("imul_rm_64") },
396    Fold {
397        from: "cmp_set_e_8",
398        into: "cmp_set_e_rm_8",
399        load: "mov_rm_8",
400        swapped: Some("cmp_set_e_rm_8"),
401    },
402    Fold {
403        from: "cmp_set_e_16",
404        into: "cmp_set_e_rm_16",
405        load: "mov_rm_16",
406        swapped: Some("cmp_set_e_rm_16"),
407    },
408    Fold {
409        from: "cmp_set_e_32",
410        into: "cmp_set_e_rm_32",
411        load: "mov_rm_32",
412        swapped: Some("cmp_set_e_rm_32"),
413    },
414    Fold {
415        from: "cmp_set_e_64",
416        into: "cmp_set_e_rm_64",
417        load: "mov_rm_64",
418        swapped: Some("cmp_set_e_rm_64"),
419    },
420    Fold {
421        from: "cmp_set_ne_8",
422        into: "cmp_set_ne_rm_8",
423        load: "mov_rm_8",
424        swapped: Some("cmp_set_ne_rm_8"),
425    },
426    Fold {
427        from: "cmp_set_ne_16",
428        into: "cmp_set_ne_rm_16",
429        load: "mov_rm_16",
430        swapped: Some("cmp_set_ne_rm_16"),
431    },
432    Fold {
433        from: "cmp_set_ne_32",
434        into: "cmp_set_ne_rm_32",
435        load: "mov_rm_32",
436        swapped: Some("cmp_set_ne_rm_32"),
437    },
438    Fold {
439        from: "cmp_set_ne_64",
440        into: "cmp_set_ne_rm_64",
441        load: "mov_rm_64",
442        swapped: Some("cmp_set_ne_rm_64"),
443    },
444    Fold {
445        from: "cmp_set_l_8",
446        into: "cmp_set_l_rm_8",
447        load: "mov_rm_8",
448        swapped: Some("cmp_set_g_rm_8"),
449    },
450    Fold {
451        from: "cmp_set_l_16",
452        into: "cmp_set_l_rm_16",
453        load: "mov_rm_16",
454        swapped: Some("cmp_set_g_rm_16"),
455    },
456    Fold {
457        from: "cmp_set_l_32",
458        into: "cmp_set_l_rm_32",
459        load: "mov_rm_32",
460        swapped: Some("cmp_set_g_rm_32"),
461    },
462    Fold {
463        from: "cmp_set_l_64",
464        into: "cmp_set_l_rm_64",
465        load: "mov_rm_64",
466        swapped: Some("cmp_set_g_rm_64"),
467    },
468    Fold {
469        from: "cmp_set_le_8",
470        into: "cmp_set_le_rm_8",
471        load: "mov_rm_8",
472        swapped: Some("cmp_set_ge_rm_8"),
473    },
474    Fold {
475        from: "cmp_set_le_16",
476        into: "cmp_set_le_rm_16",
477        load: "mov_rm_16",
478        swapped: Some("cmp_set_ge_rm_16"),
479    },
480    Fold {
481        from: "cmp_set_le_32",
482        into: "cmp_set_le_rm_32",
483        load: "mov_rm_32",
484        swapped: Some("cmp_set_ge_rm_32"),
485    },
486    Fold {
487        from: "cmp_set_le_64",
488        into: "cmp_set_le_rm_64",
489        load: "mov_rm_64",
490        swapped: Some("cmp_set_ge_rm_64"),
491    },
492    Fold {
493        from: "cmp_set_g_8",
494        into: "cmp_set_g_rm_8",
495        load: "mov_rm_8",
496        swapped: Some("cmp_set_l_rm_8"),
497    },
498    Fold {
499        from: "cmp_set_g_16",
500        into: "cmp_set_g_rm_16",
501        load: "mov_rm_16",
502        swapped: Some("cmp_set_l_rm_16"),
503    },
504    Fold {
505        from: "cmp_set_g_32",
506        into: "cmp_set_g_rm_32",
507        load: "mov_rm_32",
508        swapped: Some("cmp_set_l_rm_32"),
509    },
510    Fold {
511        from: "cmp_set_g_64",
512        into: "cmp_set_g_rm_64",
513        load: "mov_rm_64",
514        swapped: Some("cmp_set_l_rm_64"),
515    },
516    Fold {
517        from: "cmp_set_ge_8",
518        into: "cmp_set_ge_rm_8",
519        load: "mov_rm_8",
520        swapped: Some("cmp_set_le_rm_8"),
521    },
522    Fold {
523        from: "cmp_set_ge_16",
524        into: "cmp_set_ge_rm_16",
525        load: "mov_rm_16",
526        swapped: Some("cmp_set_le_rm_16"),
527    },
528    Fold {
529        from: "cmp_set_ge_32",
530        into: "cmp_set_ge_rm_32",
531        load: "mov_rm_32",
532        swapped: Some("cmp_set_le_rm_32"),
533    },
534    Fold {
535        from: "cmp_set_ge_64",
536        into: "cmp_set_ge_rm_64",
537        load: "mov_rm_64",
538        swapped: Some("cmp_set_le_rm_64"),
539    },
540    Fold {
541        from: "cmp_set_b_8",
542        into: "cmp_set_b_rm_8",
543        load: "mov_rm_8",
544        swapped: Some("cmp_set_a_rm_8"),
545    },
546    Fold {
547        from: "cmp_set_b_16",
548        into: "cmp_set_b_rm_16",
549        load: "mov_rm_16",
550        swapped: Some("cmp_set_a_rm_16"),
551    },
552    Fold {
553        from: "cmp_set_b_32",
554        into: "cmp_set_b_rm_32",
555        load: "mov_rm_32",
556        swapped: Some("cmp_set_a_rm_32"),
557    },
558    Fold {
559        from: "cmp_set_b_64",
560        into: "cmp_set_b_rm_64",
561        load: "mov_rm_64",
562        swapped: Some("cmp_set_a_rm_64"),
563    },
564    Fold {
565        from: "cmp_set_be_8",
566        into: "cmp_set_be_rm_8",
567        load: "mov_rm_8",
568        swapped: Some("cmp_set_ae_rm_8"),
569    },
570    Fold {
571        from: "cmp_set_be_16",
572        into: "cmp_set_be_rm_16",
573        load: "mov_rm_16",
574        swapped: Some("cmp_set_ae_rm_16"),
575    },
576    Fold {
577        from: "cmp_set_be_32",
578        into: "cmp_set_be_rm_32",
579        load: "mov_rm_32",
580        swapped: Some("cmp_set_ae_rm_32"),
581    },
582    Fold {
583        from: "cmp_set_be_64",
584        into: "cmp_set_be_rm_64",
585        load: "mov_rm_64",
586        swapped: Some("cmp_set_ae_rm_64"),
587    },
588    Fold {
589        from: "cmp_set_a_8",
590        into: "cmp_set_a_rm_8",
591        load: "mov_rm_8",
592        swapped: Some("cmp_set_b_rm_8"),
593    },
594    Fold {
595        from: "cmp_set_a_16",
596        into: "cmp_set_a_rm_16",
597        load: "mov_rm_16",
598        swapped: Some("cmp_set_b_rm_16"),
599    },
600    Fold {
601        from: "cmp_set_a_32",
602        into: "cmp_set_a_rm_32",
603        load: "mov_rm_32",
604        swapped: Some("cmp_set_b_rm_32"),
605    },
606    Fold {
607        from: "cmp_set_a_64",
608        into: "cmp_set_a_rm_64",
609        load: "mov_rm_64",
610        swapped: Some("cmp_set_b_rm_64"),
611    },
612    Fold {
613        from: "cmp_set_ae_8",
614        into: "cmp_set_ae_rm_8",
615        load: "mov_rm_8",
616        swapped: Some("cmp_set_be_rm_8"),
617    },
618    Fold {
619        from: "cmp_set_ae_16",
620        into: "cmp_set_ae_rm_16",
621        load: "mov_rm_16",
622        swapped: Some("cmp_set_be_rm_16"),
623    },
624    Fold {
625        from: "cmp_set_ae_32",
626        into: "cmp_set_ae_rm_32",
627        load: "mov_rm_32",
628        swapped: Some("cmp_set_be_rm_32"),
629    },
630    Fold {
631        from: "cmp_set_ae_64",
632        into: "cmp_set_ae_rm_64",
633        load: "mov_rm_64",
634        swapped: Some("cmp_set_be_rm_64"),
635    },
636    Fold { from: "cmp_set_e_ri_8", into: "cmp_set_e_mi_8", load: "mov_rm_8", swapped: None },
637    Fold { from: "cmp_set_e_ri_16", into: "cmp_set_e_mi_16", load: "mov_rm_16", swapped: None },
638    Fold { from: "cmp_set_e_ri_32", into: "cmp_set_e_mi_32", load: "mov_rm_32", swapped: None },
639    Fold { from: "cmp_set_e_ri_64", into: "cmp_set_e_mi_64", load: "mov_rm_64", swapped: None },
640    Fold { from: "cmp_set_ne_ri_8", into: "cmp_set_ne_mi_8", load: "mov_rm_8", swapped: None },
641    Fold { from: "cmp_set_ne_ri_16", into: "cmp_set_ne_mi_16", load: "mov_rm_16", swapped: None },
642    Fold { from: "cmp_set_ne_ri_32", into: "cmp_set_ne_mi_32", load: "mov_rm_32", swapped: None },
643    Fold { from: "cmp_set_ne_ri_64", into: "cmp_set_ne_mi_64", load: "mov_rm_64", swapped: None },
644    Fold { from: "cmp_set_l_ri_8", into: "cmp_set_l_mi_8", load: "mov_rm_8", swapped: None },
645    Fold { from: "cmp_set_l_ri_16", into: "cmp_set_l_mi_16", load: "mov_rm_16", swapped: None },
646    Fold { from: "cmp_set_l_ri_32", into: "cmp_set_l_mi_32", load: "mov_rm_32", swapped: None },
647    Fold { from: "cmp_set_l_ri_64", into: "cmp_set_l_mi_64", load: "mov_rm_64", swapped: None },
648    Fold { from: "cmp_set_le_ri_8", into: "cmp_set_le_mi_8", load: "mov_rm_8", swapped: None },
649    Fold { from: "cmp_set_le_ri_16", into: "cmp_set_le_mi_16", load: "mov_rm_16", swapped: None },
650    Fold { from: "cmp_set_le_ri_32", into: "cmp_set_le_mi_32", load: "mov_rm_32", swapped: None },
651    Fold { from: "cmp_set_le_ri_64", into: "cmp_set_le_mi_64", load: "mov_rm_64", swapped: None },
652    Fold { from: "cmp_set_g_ri_8", into: "cmp_set_g_mi_8", load: "mov_rm_8", swapped: None },
653    Fold { from: "cmp_set_g_ri_16", into: "cmp_set_g_mi_16", load: "mov_rm_16", swapped: None },
654    Fold { from: "cmp_set_g_ri_32", into: "cmp_set_g_mi_32", load: "mov_rm_32", swapped: None },
655    Fold { from: "cmp_set_g_ri_64", into: "cmp_set_g_mi_64", load: "mov_rm_64", swapped: None },
656    Fold { from: "cmp_set_ge_ri_8", into: "cmp_set_ge_mi_8", load: "mov_rm_8", swapped: None },
657    Fold { from: "cmp_set_ge_ri_16", into: "cmp_set_ge_mi_16", load: "mov_rm_16", swapped: None },
658    Fold { from: "cmp_set_ge_ri_32", into: "cmp_set_ge_mi_32", load: "mov_rm_32", swapped: None },
659    Fold { from: "cmp_set_ge_ri_64", into: "cmp_set_ge_mi_64", load: "mov_rm_64", swapped: None },
660    Fold { from: "cmp_set_b_ri_8", into: "cmp_set_b_mi_8", load: "mov_rm_8", swapped: None },
661    Fold { from: "cmp_set_b_ri_16", into: "cmp_set_b_mi_16", load: "mov_rm_16", swapped: None },
662    Fold { from: "cmp_set_b_ri_32", into: "cmp_set_b_mi_32", load: "mov_rm_32", swapped: None },
663    Fold { from: "cmp_set_b_ri_64", into: "cmp_set_b_mi_64", load: "mov_rm_64", swapped: None },
664    Fold { from: "cmp_set_be_ri_8", into: "cmp_set_be_mi_8", load: "mov_rm_8", swapped: None },
665    Fold { from: "cmp_set_be_ri_16", into: "cmp_set_be_mi_16", load: "mov_rm_16", swapped: None },
666    Fold { from: "cmp_set_be_ri_32", into: "cmp_set_be_mi_32", load: "mov_rm_32", swapped: None },
667    Fold { from: "cmp_set_be_ri_64", into: "cmp_set_be_mi_64", load: "mov_rm_64", swapped: None },
668    Fold { from: "cmp_set_a_ri_8", into: "cmp_set_a_mi_8", load: "mov_rm_8", swapped: None },
669    Fold { from: "cmp_set_a_ri_16", into: "cmp_set_a_mi_16", load: "mov_rm_16", swapped: None },
670    Fold { from: "cmp_set_a_ri_32", into: "cmp_set_a_mi_32", load: "mov_rm_32", swapped: None },
671    Fold { from: "cmp_set_a_ri_64", into: "cmp_set_a_mi_64", load: "mov_rm_64", swapped: None },
672    Fold { from: "cmp_set_ae_ri_8", into: "cmp_set_ae_mi_8", load: "mov_rm_8", swapped: None },
673    Fold { from: "cmp_set_ae_ri_16", into: "cmp_set_ae_mi_16", load: "mov_rm_16", swapped: None },
674    Fold { from: "cmp_set_ae_ri_32", into: "cmp_set_ae_mi_32", load: "mov_rm_32", swapped: None },
675    Fold { from: "cmp_set_ae_ri_64", into: "cmp_set_ae_mi_64", load: "mov_rm_64", swapped: None },
676];
677
678/// The widenings a load can move into on this machine, which is tamnd/rucc#1894.
679///
680/// Every sign and zero widening the target has, each with the load of the width it reads and the
681/// instruction that reads that width out of memory and widens it. None of them has a second source
682/// to swap with. The zero widening from thirty two bits comes out as the load itself, since a
683/// thirty two bit load already clears the upper half of the register it writes.
684pub static WIDENINGS: &[Fold] = &[
685    Fold { from: "movzx_8_16", into: "movzx_rm_8_16", load: "mov_rm_8", swapped: None },
686    Fold { from: "movzx_8_32", into: "movzx_rm_8_32", load: "mov_rm_8", swapped: None },
687    Fold { from: "movzx_8_64", into: "movzx_rm_8_64", load: "mov_rm_8", swapped: None },
688    Fold { from: "movzx_16_32", into: "movzx_rm_16_32", load: "mov_rm_16", swapped: None },
689    Fold { from: "movzx_16_64", into: "movzx_rm_16_64", load: "mov_rm_16", swapped: None },
690    Fold { from: "movsx_8_16", into: "movsx_rm_8_16", load: "mov_rm_8", swapped: None },
691    Fold { from: "movsx_8_32", into: "movsx_rm_8_32", load: "mov_rm_8", swapped: None },
692    Fold { from: "movsx_8_64", into: "movsx_rm_8_64", load: "mov_rm_8", swapped: None },
693    Fold { from: "movsx_16_32", into: "movsx_rm_16_32", load: "mov_rm_16", swapped: None },
694    Fold { from: "movsx_16_64", into: "movsx_rm_16_64", load: "mov_rm_16", swapped: None },
695    Fold { from: "movsxd_32_64", into: "movsxd_rm_32_64", load: "mov_rm_32", swapped: None },
696    Fold { from: "mov_32_to_64", into: "mov_rm_32", load: "mov_rm_32", swapped: None },
697];
698
699/// One arithmetic instruction that could work on memory rather than on a register, and the load
700/// and the store that would be the rest of the run.
701///
702/// A table for the reason [`Fold`] is one, and four names in a row rather than three because the
703/// run is three instructions rather than two. The widths of all four have to agree, and writing
704/// them out is what makes a row that got one wrong something a reader can see.
705#[derive(Debug, Clone, Copy, PartialEq, Eq)]
706pub struct Update {
707    /// The arithmetic as the selector wrote it, on two registers.
708    pub from: &'static str,
709    /// The same arithmetic reading one source out of memory and leaving its answer there.
710    pub into: &'static str,
711    /// The load that put the memory's word in a register.
712    pub load: &'static str,
713    /// The store that put the answer back.
714    pub store: &'static str,
715    /// Whether the two sources may be swapped, which is what lets the load feed either of them.
716    pub commutes: bool,
717}
718
719/// The arithmetic that can work on memory in place on this machine.
720///
721/// The five operations that share an opcode column, at every width. The multiply is not one of
722/// them: `imul` writes a register and there is no encoding of it that leaves the product where it
723/// read one of its sources, so there is no instruction for a row to name.
724///
725/// Subtraction is here and does not commute, and the two facts are related. `subq %rax, (%rcx)`
726/// takes the register away from the memory, so the run it matches is the one where the load feeds
727/// the left source, which is the one arrangement [`FOLDS`] cannot use. The other four take either
728/// source, because the answer does not depend on which of the two came out of memory.
729pub static UPDATES: &[Update] = &[
730    Update {
731        from: "add_rr_8",
732        into: "add_mr_8",
733        load: "mov_rm_8",
734        store: "mov_mr_8",
735        commutes: true,
736    },
737    Update {
738        from: "add_rr_16",
739        into: "add_mr_16",
740        load: "mov_rm_16",
741        store: "mov_mr_16",
742        commutes: true,
743    },
744    Update {
745        from: "add_rr_32",
746        into: "add_mr_32",
747        load: "mov_rm_32",
748        store: "mov_mr_32",
749        commutes: true,
750    },
751    Update {
752        from: "add_rr_64",
753        into: "add_mr_64",
754        load: "mov_rm_64",
755        store: "mov_mr_64",
756        commutes: true,
757    },
758    Update {
759        from: "sub_rr_8",
760        into: "sub_mr_8",
761        load: "mov_rm_8",
762        store: "mov_mr_8",
763        commutes: false,
764    },
765    Update {
766        from: "sub_rr_16",
767        into: "sub_mr_16",
768        load: "mov_rm_16",
769        store: "mov_mr_16",
770        commutes: false,
771    },
772    Update {
773        from: "sub_rr_32",
774        into: "sub_mr_32",
775        load: "mov_rm_32",
776        store: "mov_mr_32",
777        commutes: false,
778    },
779    Update {
780        from: "sub_rr_64",
781        into: "sub_mr_64",
782        load: "mov_rm_64",
783        store: "mov_mr_64",
784        commutes: false,
785    },
786    Update {
787        from: "and_rr_8",
788        into: "and_mr_8",
789        load: "mov_rm_8",
790        store: "mov_mr_8",
791        commutes: true,
792    },
793    Update {
794        from: "and_rr_16",
795        into: "and_mr_16",
796        load: "mov_rm_16",
797        store: "mov_mr_16",
798        commutes: true,
799    },
800    Update {
801        from: "and_rr_32",
802        into: "and_mr_32",
803        load: "mov_rm_32",
804        store: "mov_mr_32",
805        commutes: true,
806    },
807    Update {
808        from: "and_rr_64",
809        into: "and_mr_64",
810        load: "mov_rm_64",
811        store: "mov_mr_64",
812        commutes: true,
813    },
814    Update {
815        from: "or_rr_8",
816        into: "or_mr_8",
817        load: "mov_rm_8",
818        store: "mov_mr_8",
819        commutes: true,
820    },
821    Update {
822        from: "or_rr_16",
823        into: "or_mr_16",
824        load: "mov_rm_16",
825        store: "mov_mr_16",
826        commutes: true,
827    },
828    Update {
829        from: "or_rr_32",
830        into: "or_mr_32",
831        load: "mov_rm_32",
832        store: "mov_mr_32",
833        commutes: true,
834    },
835    Update {
836        from: "or_rr_64",
837        into: "or_mr_64",
838        load: "mov_rm_64",
839        store: "mov_mr_64",
840        commutes: true,
841    },
842    Update {
843        from: "xor_rr_8",
844        into: "xor_mr_8",
845        load: "mov_rm_8",
846        store: "mov_mr_8",
847        commutes: true,
848    },
849    Update {
850        from: "xor_rr_16",
851        into: "xor_mr_16",
852        load: "mov_rm_16",
853        store: "mov_mr_16",
854        commutes: true,
855    },
856    Update {
857        from: "xor_rr_32",
858        into: "xor_mr_32",
859        load: "mov_rm_32",
860        store: "mov_mr_32",
861        commutes: true,
862    },
863    Update {
864        from: "xor_rr_64",
865        into: "xor_mr_64",
866        load: "mov_rm_64",
867        store: "mov_mr_64",
868        commutes: true,
869    },
870];
871
872/// One arithmetic instruction against a constant that could work on memory, and the load and the
873/// store that would be the rest of the run.
874///
875/// [`Update`] with the register source replaced by an immediate, and a field shorter for it. There
876/// is no `commutes`, because there is nothing to swap: the constant is on the instruction and
877/// cannot be anywhere else, so the memory is always the left source and every row reads the same
878/// way. Subtraction is in the table without a note attached for the same reason. `subl $1, (%rax)`
879/// takes one away from the place, which is the run this matches and the only one it could be.
880#[derive(Debug, Clone, Copy, PartialEq, Eq)]
881pub struct Bump {
882    /// The arithmetic as the selector wrote it, on a register and a constant.
883    pub from: &'static str,
884    /// The same arithmetic reading memory and leaving its answer there.
885    pub into: &'static str,
886    /// The load that put the memory's word in a register.
887    pub load: &'static str,
888    /// The store that put the answer back.
889    pub store: &'static str,
890}
891
892/// The arithmetic against a constant that can work on memory in place on this machine.
893///
894/// The same five operations [`UPDATES`] has, at the same four widths, and the multiply is missing
895/// for the same reason. The eight bit inclusive or is also the instruction a probing prologue
896/// writes, which is one instruction described once rather than two things that happen to encode
897/// alike.
898///
899/// Four rows take nothing today. The narrow inclusive or and exclusive or against a constant are on
900/// `crate::select::x86_64`'s list of instructions no rule selects yet, which went out under
901/// tamnd/rucc#368 and come back with the width narrowing in tamnd/rucc#375, so a program that writes
902/// `*p |= 4` through a `char` gets a constant in a register and a run this cannot match. The rows
903/// are here for the reason the descriptions of those instructions stayed: what the machine can do
904/// is true whether or not anything asks for it today, and the rows would otherwise be a second
905/// thing to remember when #375 lands.
906pub static BUMPS: &[Bump] = &[
907    Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
908    Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
909    Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
910    Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
911    Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
912    Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
913    Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
914    Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
915    Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
916    Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
917    Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
918    Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
919    Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
920    Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
921    Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
922    Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
923    Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
924    Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
925    Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
926    Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
927];
928
929/// The load this block has passed that could still end up inside something.
930///
931/// One rather than a list of them, because anything that touches memory ends the one being carried,
932/// so the one being carried is always the last memory access there was.
933#[derive(Debug, Clone, Copy)]
934struct Waiting {
935    /// The load.
936    inst: Inst,
937    /// The register it wrote, which is what the arithmetic has to be reading.
938    reg: Reg,
939    /// Which load it is, so that the width can be held against the arithmetic's.
940    load: &'static str,
941    /// How far along the block it is, which is what [`WINDOW`] is counted in.
942    at: usize,
943}
944
945/// Puts every load that can move into the arithmetic that reads it, and gives back how many.
946///
947/// `pending` is the addresses [`crate::finish`] has still to write a displacement into, and a load
948/// that moves takes its entry with it, the same way one folded into a reader does. An address into
949/// the frame arrives here already inside the load, because [`crate::fold`] has run.
950///
951/// Run after selection and after the addresses are folded, and before allocation. Before the
952/// allocator because what makes the pair safe to put together is that a virtual register is written
953/// once, and after the addresses because a load whose address is still a `lea` in front of it has
954/// nothing in its own memory operand worth carrying.
955pub fn loads(
956    func: &mut Func,
957    machine: &MachineInsts,
958    names: &mut Interner,
959    pending: &mut Pending<'_>,
960) -> usize {
961    let mut reads = Reads::of(func);
962    let mut done = 0;
963    for block in func.blocks().collect::<Vec<_>>() {
964        let mut waiting: Option<Waiting> = None;
965        for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
966            let name = names.resolve(func[inst].opcode.name()).to_owned();
967            let bare = machine.bare(&name).to_owned();
968            // Asked before the rewrite below rather than after it, because the rewrite turns an
969            // instruction that touched no memory into one that does, and asking afterwards would
970            // throw away the load that had just gone into it over the load that had just gone into
971            // it. Nothing else about the answer moves: the other end of a row of the fold table is
972            // arithmetic this target describes and is not a call.
973            let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
974            if let Some(carried) = waiting {
975                if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
976                    let mut set = Changes::new();
977                    set.rewrite(inst, plan);
978                    set.remove(carried.inst);
979                    if set.commit(func, &mut reads, names, machine).is_ok() {
980                        pending.moved(carried.inst, &[inst]);
981                        waiting = None;
982                        done += 1;
983                    }
984                }
985            }
986            if barrier {
987                waiting = None;
988            }
989            if let Some(carried) = waiting {
990                if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
991                    waiting = None;
992                }
993            }
994            // A load the program insisted on is never carried forward, so there is never one in
995            // hand for the fold below to take. Refused where the load is picked up rather than
996            // where it is joined, because what is wrong with it is what it is and not what it
997            // meets: a load nothing may fold has no business being waited on for sixteen
998            // instructions either.
999            if insisted(func, inst) {
1000                continue;
1001            }
1002            let mut rows = FOLDS.iter().chain(WIDENINGS);
1003            if let Some(load) = rows.find(|fold| fold.load == bare).map(|fold| fold.load) {
1004                let operands = &func[func[inst].operands];
1005                if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
1006                    waiting = Some(Waiting { inst, reg: first.reg, load, at });
1007                }
1008            }
1009        }
1010    }
1011    done
1012}
1013
1014/// The three instructions that read a place, compute on what was there and write it back.
1015#[derive(Debug, Clone, Copy)]
1016struct Run {
1017    /// The load that read the place.
1018    load: Inst,
1019    /// The arithmetic that read what the load put in a register.
1020    alu: Inst,
1021    /// The store that put the answer back where the load got it.
1022    store: Inst,
1023    /// Which row of [`UPDATES`] the run is.
1024    update: &'static Update,
1025    /// The source the arithmetic is left reading, which is the one the memory is not.
1026    kept: Operand,
1027}
1028
1029/// The same three instructions with a constant where the other source was.
1030///
1031/// A separate shape from [`Run`] rather than the same one with an option in it, because the two
1032/// differ in what they carry and in nothing else. This one holds the constant the instruction that
1033/// comes out will carry, and has no `kept`, since the arithmetic is left reading nothing at all.
1034#[derive(Debug, Clone, Copy)]
1035struct Bumped {
1036    /// The load that read the place.
1037    load: Inst,
1038    /// The arithmetic that read what the load put in a register.
1039    alu: Inst,
1040    /// The store that put the answer back where the load got it.
1041    store: Inst,
1042    /// Which row of [`BUMPS`] the run is.
1043    bump: &'static Bump,
1044    /// The constant the arithmetic was against.
1045    imm: i64,
1046}
1047
1048/// Puts every run that reads a place, computes on it and writes it back into the one instruction
1049/// this machine has for all three, and gives back how many.
1050///
1051/// `pending` is the addresses [`crate::finish`] has still to write a displacement into. The store
1052/// is the instruction that survives and it is already waiting on the entry the load was waiting on,
1053/// since the two name the same place, so the load's entry is taken off rather than moved.
1054///
1055/// Run before [`loads`] rather than after it. The run this looks for is three instructions the
1056/// selector wrote, and folding the load into the arithmetic first would leave two instructions that
1057/// are the same thing written differently, so the walk would have to know both spellings. Whatever
1058/// this does not take is still there for [`loads`] to take the load out of.
1059///
1060/// The run whose arithmetic is against a constant is looked for after the one whose arithmetic is
1061/// against a register, and the order between those two does not matter: the middle instruction
1062/// decides which of them a run is, and no instruction is both an [`UPDATES`] row and a [`BUMPS`]
1063/// row.
1064pub fn stores(
1065    func: &mut Func,
1066    machine: &MachineInsts,
1067    flags: &FlagInsts,
1068    names: &mut Interner,
1069    pending: &mut Pending<'_>,
1070) -> usize {
1071    let mut reads = Reads::of(func);
1072    let mut done = 0;
1073    for block in func.blocks().collect::<Vec<_>>() {
1074        let insts: Vec<Inst> = func.insts(block).collect();
1075        for at in 0..insts.len() {
1076            let found = match run(func, &reads, machine, flags, names, &insts, at) {
1077                Some(found) => Some((
1078                    found.load,
1079                    found.alu,
1080                    found.store,
1081                    updated(func, machine, names, &found),
1082                )),
1083                None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1084                    (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1085                }),
1086            };
1087            let Some((load, alu, store, plan)) = found else { continue };
1088            if !pending.alike(load, store) {
1089                continue;
1090            }
1091            let mut set = Changes::new();
1092            set.rewrite(store, plan);
1093            set.remove(alu);
1094            set.remove(load);
1095            if set.commit(func, &mut reads, names, machine).is_ok() {
1096                pending.moved(load, &[]);
1097                done += 1;
1098            }
1099        }
1100    }
1101    done
1102}
1103
1104/// The run ending in the instruction at that position, or `None`.
1105///
1106/// Walked backwards from the store, because the store is the end of the run and is the instruction
1107/// that is left when the run is joined. Everything the walk needs is behind it: which register it
1108/// is storing says which arithmetic to look for, and which source that arithmetic reads says which
1109/// load.
1110///
1111/// An instruction an earlier fold took out is still in `insts` and is read here as though it were
1112/// where it was. That costs a fold and never takes one: a removed instruction is one more thing in
1113/// the way, and it cannot be the arithmetic or the load this is looking for, because each of those
1114/// is the one writer of a register something still reads.
1115fn run(
1116    func: &Func,
1117    reads: &Reads,
1118    machine: &MachineInsts,
1119    flags: &FlagInsts,
1120    names: &Interner,
1121    insts: &[Inst],
1122    at: usize,
1123) -> Option<Run> {
1124    let store = insts[at];
1125    if insisted(func, store) {
1126        return None;
1127    }
1128    let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1129    let value = *func[func[store].operands].first()?;
1130    if value.role.is_def() || reads.count(value.reg) != 1 {
1131        return None;
1132    }
1133    // One bound over the whole run rather than one per pair, so that what the window means is how
1134    // far apart the first and the last of the three may be.
1135    let earliest = at.saturating_sub(WINDOW);
1136    let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1137    let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1138    let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1139    if !quiet(func, flags, names, insts, (alu, at)) {
1140        return None;
1141    }
1142    let operands = func[func[insts[alu]].operands].to_vec();
1143    let [_, first, second] = operands[..] else { return None };
1144    // The left source is the one the memory takes the place of, because the answer is left where
1145    // the memory operand points and the answer is tied to the left source. Where the load feeds the
1146    // right one instead and the operation commutes, the two swap, which leaves the instruction
1147    // computing what it computed.
1148    let both = [(first, second), (second, first)];
1149    let tried = if update.commutes { &both[..] } else { &both[..1] };
1150    for &(source, kept) in tried {
1151        if reads.count(source.reg) != 1 {
1152            continue;
1153        }
1154        let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1155            continue;
1156        };
1157        let load = insts[from];
1158        if insisted(func, load) {
1159            continue;
1160        }
1161        if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1162            continue;
1163        }
1164        if !same_place(func, load, store) {
1165            continue;
1166        }
1167        // The registers the one instruction left is reading, which are the ones nothing between the
1168        // load and the store may write. The arithmetic itself passes this without being left out of
1169        // it: what it writes is the value the store is storing, and that register is not one of
1170        // these.
1171        let mut wanted: Vec<Reg> =
1172            func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1173        wanted.push(kept.reg);
1174        if !clear(func, machine, names, insts, (from, at), &wanted) {
1175            continue;
1176        }
1177        return Some(Run { load, alu: insts[alu], store, update, kept });
1178    }
1179    None
1180}
1181
1182/// The run against a constant ending in the instruction at that position, or `None`.
1183///
1184/// [`run`] with the arithmetic's second source gone. Walked backwards from the store for the same
1185/// reason, and asking the same four questions: the stored register is read once, the source the
1186/// arithmetic reads is written once by a load of the right width, that load names the same place as
1187/// the store, and nothing between the two is in the way. There is no arrangement to choose between,
1188/// because the constant is on the instruction and only the left source can be the memory.
1189///
1190/// One question [`run`] does not ask is here: where the addressing mode's registers are. The
1191/// instruction that comes out has no operand in front of them, so each of them moves one place
1192/// towards the front of the vector, and a mode that already pointed at the front would have to move
1193/// to nowhere. That cannot happen, since the front is the value the store is storing, and refusing
1194/// the run is what it costs to say so rather than to assume it.
1195fn constant(
1196    func: &Func,
1197    reads: &Reads,
1198    machine: &MachineInsts,
1199    flags: &FlagInsts,
1200    names: &Interner,
1201    insts: &[Inst],
1202    at: usize,
1203) -> Option<Bumped> {
1204    let store = insts[at];
1205    if insisted(func, store) {
1206        return None;
1207    }
1208    let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1209    let value = *func[func[store].operands].first()?;
1210    if value.role.is_def() || reads.count(value.reg) != 1 {
1211        return None;
1212    }
1213    let mem = func[func[store].mem?];
1214    if mem.base == Some(0) || mem.index == Some(0) {
1215        return None;
1216    }
1217    let earliest = at.saturating_sub(WINDOW);
1218    let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1219    let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1220    let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1221    if !quiet(func, flags, names, insts, (alu, at)) {
1222        return None;
1223    }
1224    let operands = func[func[insts[alu]].operands].to_vec();
1225    let [_, source] = operands[..] else { return None };
1226    let imm = func[func[insts[alu]].imm?].0;
1227    if reads.count(source.reg) != 1 {
1228        return None;
1229    }
1230    let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1231    let load = insts[from];
1232    if insisted(func, load) {
1233        return None;
1234    }
1235    if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1236        return None;
1237    }
1238    if !same_place(func, load, store) {
1239        return None;
1240    }
1241    // The registers the one instruction left is reading, which are the ones in its address and no
1242    // others, since the constant is not in a register and the arithmetic is left reading nothing.
1243    let wanted: Vec<Reg> =
1244        func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1245    if !clear(func, machine, names, insts, (from, at), &wanted) {
1246        return None;
1247    }
1248    Some(Bumped { load, alu: insts[alu], store, bump, imm })
1249}
1250
1251/// Whether the program insisted on this access happening exactly as it is written.
1252///
1253/// Which is `volatile`, and is the one question in this module that is not about what the
1254/// instructions do to each other. See the section above on what such an access gets.
1255fn insisted(func: &Func, inst: Inst) -> bool {
1256    func[inst].flags.contains(Flags::VOLATILE)
1257}
1258
1259/// Whether this instruction writes that register.
1260fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1261    func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1262}
1263
1264/// Whether the two instructions name the same place in memory.
1265///
1266/// The same addressing mode, the same symbol, and the same registers where the mode holds operand
1267/// positions. Both instructions here write their value down first and their address behind it, so
1268/// the positions line up, and the registers are compared anyway rather than the positions, because
1269/// what makes two addresses one place is which registers they read.
1270fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1271    let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1272    let (here, there) = (func[here], func[there]);
1273    if func[one].symbol != func[other].symbol {
1274        return false;
1275    }
1276    let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1277    if bare(here) != bare(there) {
1278        return false;
1279    }
1280    let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1281        (None, None) => true,
1282        (Some(left), Some(right)) => {
1283            func[func[one].operands][usize::from(left)].reg
1284                == func[func[other].operands][usize::from(right)].reg
1285        }
1286        _ => false,
1287    };
1288    same(here.base, there.base) && same(here.index, there.index)
1289}
1290
1291/// Whether everything between the two positions may be passed.
1292///
1293/// The run becomes one instruction where the store is, so the read of memory the load was doing
1294/// moves down the block to there. Nothing that touches memory may be passed, for the reason the
1295/// module documentation gives about [`loads`], and nothing may write a register the instruction
1296/// that is left still reads.
1297fn clear(
1298    func: &Func,
1299    machine: &MachineInsts,
1300    names: &Interner,
1301    insts: &[Inst],
1302    span: (usize, usize),
1303    wanted: &[Reg],
1304) -> bool {
1305    let (from, to) = span;
1306    insts[from + 1..to].iter().all(|&inst| {
1307        let name = names.resolve(func[inst].opcode.name());
1308        if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1309            return false;
1310        }
1311        !func[func[inst].operands]
1312            .iter()
1313            .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1314    })
1315}
1316
1317/// Whether the condition state between the arithmetic and the store belongs to nobody.
1318///
1319/// The arithmetic moves down the block to where the store is and it writes the condition state, so
1320/// everything it passes has to have no opinion about that state. An instruction that reads it was
1321/// reading what the arithmetic left and would be reading what was there before the arithmetic
1322/// instead. An instruction that writes it was the last writer before the store and would stop being
1323/// it, which changes what anything further down reads. Neither is allowed and there is nothing to
1324/// weigh: this is the shape tamnd/rucc#1424 was, where the reader in the middle was the `adc` that
1325/// takes the carry out of the addition above it into a register.
1326///
1327/// Asked from the arithmetic rather than from the load, which is what [`clear`] is asked from.
1328/// Between the load and the arithmetic nothing moves except the load, and a load has nothing to say
1329/// about the condition state.
1330///
1331/// A name the description does not cover counts as both, for the reason [`FlagInsts::writes`] gives
1332/// about a name this target does not have.
1333fn quiet(
1334    func: &Func,
1335    flags: &FlagInsts,
1336    names: &Interner,
1337    insts: &[Inst],
1338    span: (usize, usize),
1339) -> bool {
1340    let (alu, to) = span;
1341    insts[alu + 1..to].iter().all(|&inst| {
1342        let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1343            return false;
1344        };
1345        flags.reads(name).is_none() && !(flags.writes)(name)
1346    })
1347}
1348
1349/// What the store becomes with the rest of the run inside it.
1350///
1351/// The store's own addressing mode and the source the arithmetic kept, which is the whole of it.
1352/// The mode is left exactly as it was, because the operand it was written against is the value the
1353/// store was storing and what takes that operand's place is one operand as well.
1354fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1355    let operands = func[func[run.store].operands].to_vec();
1356    let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1357    Plan {
1358        opcode: Opcode::new(into),
1359        operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1360        imm: None,
1361        amode: func[run.store].mem.map(|mem| func[mem]),
1362        symbol: func[run.store].symbol,
1363    }
1364}
1365
1366/// What the store becomes with the rest of a constant run inside it.
1367///
1368/// The store's own addressing mode again, and the constant the arithmetic carried. The mode does
1369/// not come through untouched this time. The value the store was storing has nothing taking its
1370/// place, so the registers behind it each move one place towards the front of the operand vector,
1371/// and the positions the mode holds are positions in that vector and move with them. [`constant`]
1372/// is what makes sure there is a place for each of them to move to.
1373fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1374    let operands = func[func[run.store].operands][1..].to_vec();
1375    let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1376    let back = |at: Option<u8>| at.map(|at| at - 1);
1377    Plan {
1378        opcode: Opcode::new(into),
1379        operands,
1380        imm: Some(run.imm),
1381        amode: func[run.store].mem.map(|mem| {
1382            let mem = func[mem];
1383            Amode { base: back(mem.base), index: back(mem.index), ..mem }
1384        }),
1385        symbol: func[run.store].symbol,
1386    }
1387}
1388
1389/// Whether this instruction writes a register the carried load needs left alone.
1390///
1391/// The registers its address reads, and the register it wrote. The second is there for the same
1392/// reason the first is: a virtual register cannot be written twice while the IR is in SSA form, and
1393/// these are the physical ones a function has before the allocator runs.
1394fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1395    let written: Vec<Reg> = func[func[inst].operands]
1396        .iter()
1397        .filter(|operand| operand.role.is_def())
1398        .map(|operand| operand.reg)
1399        .collect();
1400    func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1401}
1402
1403/// What this instruction becomes with the carried load inside it, or `None`.
1404///
1405/// Nothing here changes anything. What comes back is a proposal, and whether the target has the
1406/// instruction it describes is [`Changes`]'s answer rather than this one.
1407fn joined(
1408    func: &Func,
1409    reads: &Reads,
1410    carried: Waiting,
1411    machine: &MachineInsts,
1412    names: &mut Interner,
1413    inst: Inst,
1414    bare: &str,
1415) -> Option<Plan> {
1416    let fold = FOLDS.iter().chain(WIDENINGS).find(|fold| fold.from == bare)?;
1417    if carried.load != fold.load || reads.count(carried.reg) != 1 {
1418        return None;
1419    }
1420    let operands = func[func[inst].operands].to_vec();
1421    // The second source is the one the memory operand replaces, which for arithmetic is because
1422    // the answer is tied to the first and for a comparison is because that is the side the
1423    // instruction subtracts. Where the load feeds the first source instead, the row says which
1424    // instruction reads the two the other way round, and that one is written instead: for
1425    // arithmetic that commutes it is the same instruction, and for a comparison it is the same
1426    // question with the condition turned over.
1427    //
1428    // A comparison against a constant has one source and no arrangement to choose between, since
1429    // the constant is on the instruction and cannot be anywhere else. What is left in front of the
1430    // address is the byte on its own.
1431    let (front, into) = match operands[..] {
1432        [answer, first, second] => {
1433            let (kept, into) = if second.reg == carried.reg {
1434                (first, fold.into)
1435            } else if first.reg == carried.reg {
1436                (second, fold.swapped?)
1437            } else {
1438                return None;
1439            };
1440            (vec![answer, kept], into)
1441        }
1442        [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1443        _ => return None,
1444    };
1445    let load = carried.inst;
1446    let address = func[func[load].operands][1..].to_vec();
1447    let mut amode = func[func[load].mem?];
1448    // The registers an address names are operands behind the ones the instruction writes down. The
1449    // load wrote one of those and the instruction that comes out writes however many are in front
1450    // of the address here, so every position the mode holds moves along by the difference.
1451    let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1452    amode.base = amode.base.map(|at| at + along);
1453    amode.index = amode.index.map(|at| at + along);
1454    let into = names.intern(&format!("{}{}", machine.prefix, into));
1455    Some(Plan {
1456        opcode: Opcode::new(into),
1457        operands: front.into_iter().chain(address).collect(),
1458        imm: func[inst].imm.map(|at| func[at].0),
1459        amode: Some(amode),
1460        symbol: func[load].symbol,
1461    })
1462}
1463
1464#[cfg(test)]
1465mod tests {
1466    use rucc_mir::{self as mir, Constraint, Mem, Operand};
1467    use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1468
1469    use super::*;
1470
1471    /// A function with one block, and the names it was built with.
1472    fn empty() -> (Interner, Func, mir::Block) {
1473        let mut names = Interner::new();
1474        let mut func = Func::new(names.intern("f"));
1475        let block = func.create_block();
1476        (names, func, block)
1477    }
1478
1479    /// The opcode of that name on this target.
1480    fn op(names: &mut Interner, name: &str) -> Opcode {
1481        Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1482    }
1483
1484    /// A load of eight bytes off that register.
1485    fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1486        let into = func.new_vreg(GPR);
1487        let mov = op(names, "mov_rm_64");
1488        func.build(block, mov)
1489            .def(into, GPR)
1490            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1491            .finish();
1492        into
1493    }
1494
1495    /// The same load, of a place the program said to read exactly where it is written.
1496    fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1497        let into = func.new_vreg(GPR);
1498        let mov = op(names, "mov_rm_64");
1499        func.build(block, mov)
1500            .def(into, GPR)
1501            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1502            .flags(Flags::VOLATILE)
1503            .finish();
1504        into
1505    }
1506
1507    /// Two-address arithmetic of that name on those two registers, in that order.
1508    fn alu(
1509        func: &mut Func,
1510        names: &mut Interner,
1511        block: mir::Block,
1512        name: &str,
1513        first: Reg,
1514        second: Reg,
1515    ) -> Reg {
1516        let answer = func.new_vreg(GPR);
1517        let opcode = op(names, name);
1518        func.build(block, opcode)
1519            .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1520            .uses(first, GPR)
1521            .uses(second, GPR)
1522            .finish();
1523        answer
1524    }
1525
1526    /// A comparison of those two registers in that order, which keeps its answer in a byte the
1527    /// two sources have no claim on and is what makes it not two-address.
1528    fn compare(
1529        func: &mut Func,
1530        names: &mut Interner,
1531        block: mir::Block,
1532        name: &str,
1533        first: Reg,
1534        second: Reg,
1535    ) -> Reg {
1536        let byte = func.new_vreg(GPR);
1537        let opcode = op(names, name);
1538        func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1539        byte
1540    }
1541
1542    /// What every instruction in a block came to, as opcodes.
1543    fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1544        func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1545    }
1546
1547    /// The pass, with lists nothing is on.
1548    fn combine(func: &mut Func, names: &mut Interner) -> usize {
1549        let mut addresses = Vec::new();
1550        let mut arguments = Vec::new();
1551        let mut dynamic = Vec::new();
1552        let mut pending =
1553            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1554        loads(func, &MACHINE, names, &mut pending)
1555    }
1556
1557    /// A store of that register to sixteen off that base, which is the address `load` reads.
1558    fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1559        let mov = op(names, "mov_mr_64");
1560        func.build(block, mov)
1561            .uses(value, GPR)
1562            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1563            .finish();
1564    }
1565
1566    /// The same store, of a place the program said to write exactly where it is written.
1567    fn insisted_store(
1568        func: &mut Func,
1569        names: &mut Interner,
1570        block: mir::Block,
1571        base: Reg,
1572        value: Reg,
1573    ) {
1574        let mov = op(names, "mov_mr_64");
1575        func.build(block, mov)
1576            .uses(value, GPR)
1577            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1578            .flags(Flags::VOLATILE)
1579            .finish();
1580    }
1581
1582    /// The other walk, with lists nothing is on.
1583    fn update(func: &mut Func, names: &mut Interner) -> usize {
1584        let mut addresses = Vec::new();
1585        let mut arguments = Vec::new();
1586        let mut dynamic = Vec::new();
1587        let mut pending =
1588            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1589        stores(func, &MACHINE, &FLAGS, names, &mut pending)
1590    }
1591
1592    /// The shape the second walk is for, which is what `*p += x` is.
1593    #[test]
1594    fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1595        let (mut names, mut func, block) = empty();
1596        let base = func.new_vreg(GPR);
1597        let other = func.new_vreg(GPR);
1598        let word = load(&mut func, &mut names, block, base);
1599        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1600        store(&mut func, &mut names, block, base, sum);
1601
1602        assert_eq!(update(&mut func, &mut names), 1);
1603        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1604        let inst = func.insts(block).next().expect("the addition");
1605        let mem = func[inst].mem.expect("it writes memory");
1606        assert_eq!(func[mem].disp, 16, "the address came from the store");
1607        assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1608        assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1609        assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1610        assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1611    }
1612
1613    /// The same run with the load feeding the right source instead, which an addition does not
1614    /// mind. What `subq %rax, (%rcx)` computes is memory minus register, so the subtraction below
1615    /// is the one that has to care.
1616    #[test]
1617    fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1618        let (mut names, mut func, block) = empty();
1619        let base = func.new_vreg(GPR);
1620        let other = func.new_vreg(GPR);
1621        let word = load(&mut func, &mut names, block, base);
1622        let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1623        store(&mut func, &mut names, block, base, sum);
1624
1625        assert_eq!(update(&mut func, &mut names), 1);
1626        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1627        assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1628    }
1629
1630    /// A subtraction with the memory on the left, which is `*p -= x` and is what the machine
1631    /// instruction computes.
1632    #[test]
1633    fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1634        let (mut names, mut func, block) = empty();
1635        let base = func.new_vreg(GPR);
1636        let other = func.new_vreg(GPR);
1637        let word = load(&mut func, &mut names, block, base);
1638        let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1639        store(&mut func, &mut names, block, base, left);
1640
1641        assert_eq!(update(&mut func, &mut names), 1);
1642        assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1643    }
1644
1645    /// And the same subtraction the other way round, which is `*p = x - *p`. The machine
1646    /// instruction would compute the other answer, so the run stays three instructions.
1647    #[test]
1648    fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1649        let (mut names, mut func, block) = empty();
1650        let base = func.new_vreg(GPR);
1651        let other = func.new_vreg(GPR);
1652        let word = load(&mut func, &mut names, block, base);
1653        let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1654        store(&mut func, &mut names, block, base, left);
1655
1656        assert_eq!(update(&mut func, &mut names), 0);
1657        assert_eq!(
1658            shape(&func, &names, block),
1659            ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1660        );
1661    }
1662
1663    /// A store to somewhere else. The answer is not going back where it came from, so what is left
1664    /// is a load and an arithmetic and a store of three different addresses.
1665    #[test]
1666    fn a_store_to_another_address_stays_three_instructions() {
1667        let (mut names, mut func, block) = empty();
1668        let base = func.new_vreg(GPR);
1669        let elsewhere = func.new_vreg(GPR);
1670        let other = func.new_vreg(GPR);
1671        let word = load(&mut func, &mut names, block, base);
1672        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1673        store(&mut func, &mut names, block, elsewhere, sum);
1674
1675        assert_eq!(update(&mut func, &mut names), 0);
1676    }
1677
1678    /// The same address at a different displacement, which is the near miss the comparison has to
1679    /// catch rather than the obvious one above.
1680    #[test]
1681    fn a_store_at_another_displacement_stays_three_instructions() {
1682        let (mut names, mut func, block) = empty();
1683        let base = func.new_vreg(GPR);
1684        let other = func.new_vreg(GPR);
1685        let word = load(&mut func, &mut names, block, base);
1686        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1687        let mov = op(&mut names, "mov_mr_64");
1688        func.build(block, mov)
1689            .uses(sum, GPR)
1690            .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1691            .finish();
1692
1693        assert_eq!(update(&mut func, &mut names), 0);
1694    }
1695
1696    /// The word read again by something else. The load has to stay for the second reader, so the
1697    /// run is not a run.
1698    #[test]
1699    fn a_word_two_instructions_read_stays_three_instructions() {
1700        let (mut names, mut func, block) = empty();
1701        let base = func.new_vreg(GPR);
1702        let other = func.new_vreg(GPR);
1703        let word = load(&mut func, &mut names, block, base);
1704        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1705        alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1706        store(&mut func, &mut names, block, base, sum);
1707
1708        assert_eq!(update(&mut func, &mut names), 0);
1709    }
1710
1711    /// The answer read by something else as well as by the store, which is `x = *p += 1` and
1712    /// leaves the answer wanted in a register the joined instruction never writes.
1713    #[test]
1714    fn an_answer_something_else_reads_stays_three_instructions() {
1715        let (mut names, mut func, block) = empty();
1716        let base = func.new_vreg(GPR);
1717        let other = func.new_vreg(GPR);
1718        let word = load(&mut func, &mut names, block, base);
1719        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1720        store(&mut func, &mut names, block, base, sum);
1721        alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1722
1723        assert_eq!(update(&mut func, &mut names), 0);
1724    }
1725
1726    /// Another access to memory in the middle. The read the run does moves down the block to where
1727    /// the write was, so it would be moving past this one.
1728    #[test]
1729    fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1730        let (mut names, mut func, block) = empty();
1731        let base = func.new_vreg(GPR);
1732        let other = func.new_vreg(GPR);
1733        let word = load(&mut func, &mut names, block, base);
1734        load(&mut func, &mut names, block, other);
1735        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1736        store(&mut func, &mut names, block, base, sum);
1737
1738        assert_eq!(update(&mut func, &mut names), 0);
1739    }
1740
1741    /// Something writing the address register in the middle. A physical register is the only one
1742    /// this can happen to before the allocator runs, and the frame is addressed through two.
1743    #[test]
1744    fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1745        let (mut names, mut func, block) = empty();
1746        let base = Reg::physical(rucc_target::x86_64::RSP);
1747        let other = func.new_vreg(GPR);
1748        let word = load(&mut func, &mut names, block, base);
1749        let sub = op(&mut names, "sub_ri_64");
1750        func.build(block, sub)
1751            .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1752            .uses(base, GPR)
1753            .imm(32)
1754            .finish();
1755        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1756        store(&mut func, &mut names, block, base, sum);
1757
1758        assert_eq!(update(&mut func, &mut names), 0);
1759    }
1760
1761    /// Two locals whose displacements are both nothing so far. They are the same registers and the
1762    /// same number here and are two different places, and what says so is the list the frame layout
1763    /// has still to write an offset into.
1764    #[test]
1765    fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1766        let (mut names, mut func, block) = empty();
1767        let base = Reg::physical(rucc_target::x86_64::RSP);
1768        let other = func.new_vreg(GPR);
1769        let mov = op(&mut names, "mov_rm_64");
1770        let word = func.new_vreg(GPR);
1771        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1772        let read = func.insts(block).next().expect("the load");
1773        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1774        let put = op(&mut names, "mov_mr_64");
1775        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1776        let written = func.insts(block).nth(2).expect("the store");
1777
1778        let mut addresses = vec![(read, 3usize), (written, 4usize)];
1779        let mut arguments = Vec::new();
1780        let mut dynamic = Vec::new();
1781        let mut pending =
1782            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1783        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1784    }
1785
1786    /// The one local, which is the same place twice and folds. The entry the load was waiting on
1787    /// comes off the list, because the store is already waiting on the same one and adding the
1788    /// frame's offset twice would put the local at twice its distance.
1789    #[test]
1790    fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1791        let (mut names, mut func, block) = empty();
1792        let base = Reg::physical(rucc_target::x86_64::RSP);
1793        let other = func.new_vreg(GPR);
1794        let mov = op(&mut names, "mov_rm_64");
1795        let word = func.new_vreg(GPR);
1796        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1797        let read = func.insts(block).next().expect("the load");
1798        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1799        let put = op(&mut names, "mov_mr_64");
1800        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1801        let written = func.insts(block).nth(2).expect("the store");
1802
1803        let mut addresses = vec![(read, 3usize), (written, 3usize)];
1804        let mut arguments = Vec::new();
1805        let mut dynamic = Vec::new();
1806        let mut pending =
1807            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1808        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
1809
1810        let inst = func.insts(block).next().expect("the addition");
1811        assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1812    }
1813
1814    /// A run of the wrong width, which is a load of four bytes under an addition of eight.
1815    #[test]
1816    fn a_run_whose_widths_disagree_stays_three_instructions() {
1817        let (mut names, mut func, block) = empty();
1818        let base = func.new_vreg(GPR);
1819        let other = func.new_vreg(GPR);
1820        let into = func.new_vreg(GPR);
1821        let narrow = op(&mut names, "mov_rm_32");
1822        func.build(block, narrow)
1823            .def(into, GPR)
1824            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1825            .finish();
1826        let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1827        store(&mut func, &mut names, block, base, sum);
1828
1829        assert_eq!(update(&mut func, &mut names), 0);
1830    }
1831
1832    /// The shape tamnd/rucc#1424 was, which is a run with the carry reader in the middle of it.
1833    /// Joining it would put the addition below the `adc` and the carry the `adc` takes would be
1834    /// whatever was there before the addition ran.
1835    #[test]
1836    fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1837        let (mut names, mut func, block) = empty();
1838        let base = func.new_vreg(GPR);
1839        let other = func.new_vreg(GPR);
1840        let carry = func.new_vreg(GPR);
1841        let word = load(&mut func, &mut names, block, base);
1842        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1843        alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1844        store(&mut func, &mut names, block, base, sum);
1845
1846        assert_eq!(update(&mut func, &mut names), 0);
1847    }
1848
1849    /// The other half of the same question, which is something in the middle that writes the state
1850    /// rather than reads it. Joining the run would make the addition the last writer before the
1851    /// store instead of the subtraction, so whatever reads the state further down would read a
1852    /// different answer.
1853    #[test]
1854    fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1855        let (mut names, mut func, block) = empty();
1856        let base = func.new_vreg(GPR);
1857        let other = func.new_vreg(GPR);
1858        let left = func.new_vreg(GPR);
1859        let right = func.new_vreg(GPR);
1860        let word = load(&mut func, &mut names, block, base);
1861        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1862        alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1863        store(&mut func, &mut names, block, base, sum);
1864
1865        assert_eq!(update(&mut func, &mut names), 0);
1866    }
1867
1868    /// And the same run with an instruction in the middle that has no opinion about the state,
1869    /// which is what keeps the two above from being a rule against anything in the middle at all.
1870    #[test]
1871    fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1872        let (mut names, mut func, block) = empty();
1873        let base = func.new_vreg(GPR);
1874        let other = func.new_vreg(GPR);
1875        let from = func.new_vreg(GPR);
1876        let into = func.new_vreg(GPR);
1877        let word = load(&mut func, &mut names, block, base);
1878        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1879        let copy = op(&mut names, "mov_rr_64");
1880        func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1881        store(&mut func, &mut names, block, base, sum);
1882
1883        assert_eq!(update(&mut func, &mut names), 1);
1884        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1885    }
1886
1887    /// Every row of the table names four instructions this target has, all of one width.
1888    #[test]
1889    fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1890        for update in UPDATES {
1891            for name in [update.from, update.into, update.load, update.store] {
1892                assert!(MACHINE.has(name), "{name} is not an instruction");
1893            }
1894            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1895            assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1896            assert_eq!(
1897                width(update.from),
1898                width(update.load),
1899                "{} loads another width",
1900                update.from
1901            );
1902            assert_eq!(
1903                width(update.from),
1904                width(update.store),
1905                "{} stores another width",
1906                update.from
1907            );
1908            assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1909            assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1910        }
1911    }
1912
1913    /// One row per arithmetic instruction this machine can do in place, for the reason the count
1914    /// over the fold table is there.
1915    #[test]
1916    fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1917        assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1918        let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1919        assert_eq!(commuting, 16, "everything but the four subtractions");
1920    }
1921
1922    /// Two-address arithmetic of that name against a constant.
1923    fn alu_imm(
1924        func: &mut Func,
1925        names: &mut Interner,
1926        block: mir::Block,
1927        name: &str,
1928        source: Reg,
1929        value: i64,
1930    ) -> Reg {
1931        let answer = func.new_vreg(GPR);
1932        let opcode = op(names, name);
1933        func.build(block, opcode)
1934            .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1935            .uses(source, GPR)
1936            .imm(value)
1937            .finish();
1938        answer
1939    }
1940
1941    /// The shape the constant run is for, which is what `*p += 1` is.
1942    #[test]
1943    fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1944        let (mut names, mut func, block) = empty();
1945        let base = func.new_vreg(GPR);
1946        let word = load(&mut func, &mut names, block, base);
1947        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1948        store(&mut func, &mut names, block, base, sum);
1949
1950        assert_eq!(update(&mut func, &mut names), 1);
1951        assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1952        let inst = func.insts(block).next().expect("the addition");
1953        let mem = func[inst].mem.expect("it writes memory");
1954        assert_eq!(func[mem].disp, 16, "the address came from the store");
1955        assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1956        assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1957        assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1958        assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1959    }
1960
1961    /// The subtraction, which needs no arrangement chosen for it. A constant cannot be the left
1962    /// source, so the run that exists is the one the instruction computes.
1963    #[test]
1964    fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1965        let (mut names, mut func, block) = empty();
1966        let base = func.new_vreg(GPR);
1967        let word = load(&mut func, &mut names, block, base);
1968        let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1969        store(&mut func, &mut names, block, base, left);
1970
1971        assert_eq!(update(&mut func, &mut names), 1);
1972        assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1973        assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1974    }
1975
1976    /// The narrow one, so that a width that is carried through wrong is a test that fails rather
1977    /// than a program that is wrong.
1978    #[test]
1979    fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1980        let (mut names, mut func, block) = empty();
1981        let base = func.new_vreg(GPR);
1982        let word = func.new_vreg(GPR);
1983        let mov = op(&mut names, "mov_rm_8");
1984        func.build(block, mov)
1985            .def(word, GPR)
1986            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1987            .finish();
1988        let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1989        let put = op(&mut names, "mov_mr_8");
1990        func.build(block, put)
1991            .uses(sum, GPR)
1992            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1993            .finish();
1994
1995        assert_eq!(update(&mut func, &mut names), 1);
1996        assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1997    }
1998
1999    /// The word read again by something else, which is the first of the four conditions and is
2000    /// asked here the way it is asked of the register run.
2001    #[test]
2002    fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
2003        let (mut names, mut func, block) = empty();
2004        let base = func.new_vreg(GPR);
2005        let other = func.new_vreg(GPR);
2006        let word = load(&mut func, &mut names, block, base);
2007        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2008        alu(&mut func, &mut names, block, "xor_rr_64", word, other);
2009        store(&mut func, &mut names, block, base, sum);
2010
2011        assert_eq!(update(&mut func, &mut names), 0);
2012    }
2013
2014    /// Something else in the middle that touches memory, which the one instruction left would be
2015    /// passing if the run were joined.
2016    #[test]
2017    fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
2018        let (mut names, mut func, block) = empty();
2019        let base = func.new_vreg(GPR);
2020        let elsewhere = func.new_vreg(GPR);
2021        let word = load(&mut func, &mut names, block, base);
2022        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2023        load(&mut func, &mut names, block, elsewhere);
2024        store(&mut func, &mut names, block, base, sum);
2025
2026        assert_eq!(update(&mut func, &mut names), 0);
2027    }
2028
2029    /// The address register written between the load and the store, which would leave the one
2030    /// instruction naming a different place from the one the run read.
2031    #[test]
2032    fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
2033        let (mut names, mut func, block) = empty();
2034        let base = Reg::physical(rucc_target::x86_64::RAX);
2035        let word = load(&mut func, &mut names, block, base);
2036        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2037        let mov = op(&mut names, "mov_ri_64");
2038        func.build(block, mov).def(base, GPR).imm(0).finish();
2039        store(&mut func, &mut names, block, base, sum);
2040
2041        assert_eq!(update(&mut func, &mut names), 0);
2042    }
2043
2044    /// A store somewhere else, which is the run that is not a run.
2045    #[test]
2046    fn a_constant_written_to_another_address_stays_three_instructions() {
2047        let (mut names, mut func, block) = empty();
2048        let base = func.new_vreg(GPR);
2049        let elsewhere = func.new_vreg(GPR);
2050        let word = load(&mut func, &mut names, block, base);
2051        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2052        store(&mut func, &mut names, block, elsewhere, sum);
2053
2054        assert_eq!(update(&mut func, &mut names), 0);
2055    }
2056
2057    /// A run of the wrong width, which is a load of four bytes under an addition of eight.
2058    #[test]
2059    fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2060        let (mut names, mut func, block) = empty();
2061        let base = func.new_vreg(GPR);
2062        let into = func.new_vreg(GPR);
2063        let narrow = op(&mut names, "mov_rm_32");
2064        func.build(block, narrow)
2065            .def(into, GPR)
2066            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2067            .finish();
2068        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2069        store(&mut func, &mut names, block, base, sum);
2070
2071        assert_eq!(update(&mut func, &mut names), 0);
2072    }
2073
2074    /// The multiply, which has a two-address form against a constant and no form that leaves the
2075    /// product in memory, so the run stays three instructions.
2076    #[test]
2077    fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2078        let (mut names, mut func, block) = empty();
2079        let base = func.new_vreg(GPR);
2080        let word = load(&mut func, &mut names, block, base);
2081        let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2082        store(&mut func, &mut names, block, base, product);
2083
2084        assert_eq!(update(&mut func, &mut names), 0);
2085    }
2086
2087    /// The constant run passes the condition state the same way the register run does, because the
2088    /// arithmetic moves down to the store here too.
2089    #[test]
2090    fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2091        let (mut names, mut func, block) = empty();
2092        let base = func.new_vreg(GPR);
2093        let carry = func.new_vreg(GPR);
2094        let word = load(&mut func, &mut names, block, base);
2095        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2096        alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2097        store(&mut func, &mut names, block, base, sum);
2098
2099        assert_eq!(update(&mut func, &mut names), 0);
2100    }
2101
2102    /// The local, which is the same place twice and folds, and whose frame entry comes off the
2103    /// list for the reason the register run's does.
2104    #[test]
2105    fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2106        let (mut names, mut func, block) = empty();
2107        let base = Reg::physical(rucc_target::x86_64::RSP);
2108        let mov = op(&mut names, "mov_rm_64");
2109        let word = func.new_vreg(GPR);
2110        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2111        let read = func.insts(block).next().expect("the load");
2112        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2113        let put = op(&mut names, "mov_mr_64");
2114        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2115        let written = func.insts(block).nth(2).expect("the store");
2116
2117        let mut addresses = vec![(read, 3usize), (written, 3usize)];
2118        let mut arguments = Vec::new();
2119        let mut dynamic = Vec::new();
2120        let mut pending =
2121            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2122        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2123
2124        let inst = func.insts(block).next().expect("the addition");
2125        assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2126    }
2127
2128    /// Two locals the layout has not placed yet, which are the same addressing mode and not the
2129    /// same place, the way they are for the register run.
2130    #[test]
2131    fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
2132        let (mut names, mut func, block) = empty();
2133        let base = Reg::physical(rucc_target::x86_64::RSP);
2134        let mov = op(&mut names, "mov_rm_64");
2135        let word = func.new_vreg(GPR);
2136        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2137        let read = func.insts(block).next().expect("the load");
2138        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2139        let put = op(&mut names, "mov_mr_64");
2140        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2141        let written = func.insts(block).nth(2).expect("the store");
2142
2143        let mut addresses = vec![(read, 3usize), (written, 4usize)];
2144        let mut arguments = Vec::new();
2145        let mut dynamic = Vec::new();
2146        let mut pending =
2147            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2148        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
2149    }
2150
2151    /// Every row of the constant table names four instructions this target has, all of one width.
2152    #[test]
2153    fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2154        for bump in BUMPS {
2155            for name in [bump.from, bump.into, bump.load, bump.store] {
2156                assert!(MACHINE.has(name), "{name} is not an instruction");
2157            }
2158            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2159            assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2160            assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2161            assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2162            assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2163            assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2164            assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2165        }
2166    }
2167
2168    /// One row per arithmetic instruction this machine can do in place against a constant, which is
2169    /// the same five operations at the same four widths the register table has.
2170    #[test]
2171    fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2172        assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2173        let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2174        for bump in BUMPS {
2175            let same = bump.from.replace("_ri_", "_rr_");
2176            assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2177        }
2178    }
2179
2180    /// No instruction is in both tables, which is what lets the two walks be tried one after the
2181    /// other without either having to know what the other took.
2182    #[test]
2183    fn nothing_is_both_a_register_run_and_a_constant_run() {
2184        for bump in BUMPS {
2185            assert!(
2186                !UPDATES.iter().any(|update| update.from == bump.from),
2187                "{} starts both kinds of run",
2188                bump.from
2189            );
2190        }
2191    }
2192
2193    /// The shape the whole pass is for.
2194    #[test]
2195    fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2196        let (mut names, mut func, block) = empty();
2197        let base = func.new_vreg(GPR);
2198        let other = func.new_vreg(GPR);
2199        let word = load(&mut func, &mut names, block, base);
2200        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2201
2202        assert_eq!(combine(&mut func, &mut names), 1);
2203        assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2204        let inst = func.insts(block).next().expect("the addition");
2205        let mem = func[inst].mem.expect("the addition reads memory now");
2206        assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2207        assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2208        assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2209        assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2210    }
2211
2212    /// The same load feeding the source the answer is tied to. The two sources are swapped, which
2213    /// an addition does not mind and is what lets this fold at all.
2214    #[test]
2215    fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2216        let (mut names, mut func, block) = empty();
2217        let base = func.new_vreg(GPR);
2218        let other = func.new_vreg(GPR);
2219        let word = load(&mut func, &mut names, block, base);
2220        alu(&mut func, &mut names, block, "add_rr_64", word, other);
2221
2222        assert_eq!(combine(&mut func, &mut names), 1);
2223        assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2224        let inst = func.insts(block).next().expect("the addition");
2225        assert_eq!(func[func[inst].operands][1].reg, other);
2226    }
2227
2228    /// A subtraction with the load on the left, which is the one place the swap above would change
2229    /// the answer.
2230    #[test]
2231    fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2232        let (mut names, mut func, block) = empty();
2233        let base = func.new_vreg(GPR);
2234        let other = func.new_vreg(GPR);
2235        let word = load(&mut func, &mut names, block, base);
2236        alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2237
2238        assert_eq!(combine(&mut func, &mut names), 0);
2239        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2240    }
2241
2242    /// And the same subtraction the other way round, which is the one that folds.
2243    #[test]
2244    fn a_load_feeding_the_right_of_a_subtraction_folds() {
2245        let (mut names, mut func, block) = empty();
2246        let base = func.new_vreg(GPR);
2247        let other = func.new_vreg(GPR);
2248        let word = load(&mut func, &mut names, block, base);
2249        alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2250
2251        assert_eq!(combine(&mut func, &mut names), 1);
2252        assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2253    }
2254
2255    /// Two readers. The load has to stay where it is for the second of them, so putting it into the
2256    /// first buys nothing and reads the memory twice.
2257    #[test]
2258    fn a_load_two_instructions_read_stays_a_load() {
2259        let (mut names, mut func, block) = empty();
2260        let base = func.new_vreg(GPR);
2261        let other = func.new_vreg(GPR);
2262        let word = load(&mut func, &mut names, block, base);
2263        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2264        alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2265
2266        assert_eq!(combine(&mut func, &mut names), 0);
2267        assert_eq!(
2268            shape(&func, &names, block),
2269            ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2270        );
2271    }
2272
2273    /// A store between the two. Whether it writes what the load reads is a question about two
2274    /// addresses, and the answer to not being able to tell is to leave the load where it is.
2275    #[test]
2276    fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2277        let (mut names, mut func, block) = empty();
2278        let base = func.new_vreg(GPR);
2279        let other = func.new_vreg(GPR);
2280        let word = load(&mut func, &mut names, block, base);
2281        let store = op(&mut names, "mov_mr_64");
2282        func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2283        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2284
2285        assert_eq!(combine(&mut func, &mut names), 0);
2286        assert_eq!(
2287            shape(&func, &names, block),
2288            ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2289        );
2290    }
2291
2292    /// Another load between the two, which writes nothing and is still not passed.
2293    ///
2294    /// This is the one that would be wrong if the walk asked only about writes. Where both reads
2295    /// are `volatile` the program said which of them happens first, and nothing here can tell that
2296    /// program from the one that did not say it, so neither may be reordered.
2297    #[test]
2298    fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2299        let (mut names, mut func, block) = empty();
2300        let base = func.new_vreg(GPR);
2301        let other = func.new_vreg(GPR);
2302        let word = load(&mut func, &mut names, block, base);
2303        load(&mut func, &mut names, block, other);
2304        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2305
2306        assert_eq!(combine(&mut func, &mut names), 0);
2307        assert_eq!(
2308            shape(&func, &names, block),
2309            ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2310        );
2311    }
2312
2313    /// The second of two loads, read by arithmetic that reads the first as well. Nothing moves past
2314    /// anything, which is what makes this one the shape the pass is allowed to take.
2315    #[test]
2316    fn the_later_of_two_loads_is_the_one_that_folds() {
2317        let (mut names, mut func, block) = empty();
2318        let base = func.new_vreg(GPR);
2319        let other = func.new_vreg(GPR);
2320        let first = load(&mut func, &mut names, block, base);
2321        let second = load(&mut func, &mut names, block, other);
2322        alu(&mut func, &mut names, block, "add_rr_64", first, second);
2323
2324        assert_eq!(combine(&mut func, &mut names), 1);
2325        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2326        let addition = func.insts(block).nth(1).expect("the addition");
2327        assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2328        assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2329    }
2330
2331    /// A call between the two. What a call does to memory is not in the instruction, so it is the
2332    /// same answer as the store and reached without asking about the address.
2333    #[test]
2334    fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2335        let (mut names, mut func, block) = empty();
2336        let base = func.new_vreg(GPR);
2337        let other = func.new_vreg(GPR);
2338        let word = load(&mut func, &mut names, block, base);
2339        let call = op(&mut names, "call");
2340        func.build(block, call).finish();
2341        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2342
2343        assert_eq!(combine(&mut func, &mut names), 0);
2344        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2345    }
2346
2347    /// Something writing the register the address reads. A physical register is the only one this
2348    /// can happen to while the IR is in SSA form, and the frame is addressed through two of them.
2349    #[test]
2350    fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2351        let (mut names, mut func, block) = empty();
2352        let base = Reg::physical(rucc_target::x86_64::RSP);
2353        let other = func.new_vreg(GPR);
2354        let word = load(&mut func, &mut names, block, base);
2355        let sub = op(&mut names, "sub_ri_64");
2356        func.build(block, sub)
2357            .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2358            .uses(base, GPR)
2359            .imm(32)
2360            .finish();
2361        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2362
2363        assert_eq!(combine(&mut func, &mut names), 0);
2364    }
2365
2366    /// A load of four bytes under an addition of eight. The register held what the load put in it
2367    /// and a memory operand holds what is at the address, which is a different number of bytes.
2368    #[test]
2369    fn a_load_of_the_wrong_width_stays_a_load() {
2370        let (mut names, mut func, block) = empty();
2371        let base = func.new_vreg(GPR);
2372        let other = func.new_vreg(GPR);
2373        let into = func.new_vreg(GPR);
2374        let narrow = op(&mut names, "mov_rm_32");
2375        func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2376        alu(&mut func, &mut names, block, "add_rr_64", other, into);
2377
2378        assert_eq!(combine(&mut func, &mut names), 0);
2379        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2380    }
2381
2382    /// A load whose value leaves the block on an edge. It is read by nothing in any operand vector
2383    /// and is read all the same, which is the count that is easy to get wrong.
2384    #[test]
2385    fn a_load_whose_value_an_edge_carries_stays_a_load() {
2386        let (mut names, mut func, block) = empty();
2387        let next = func.create_block();
2388        let base = func.new_vreg(GPR);
2389        let other = func.new_vreg(GPR);
2390        let word = load(&mut func, &mut names, block, base);
2391        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2392        let arrived = func.new_vreg(GPR);
2393        func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2394        *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2395
2396        assert_eq!(combine(&mut func, &mut names), 0);
2397        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2398    }
2399
2400    /// A reader in another block, which is the whole of what block local means here.
2401    #[test]
2402    fn a_reader_in_another_block_stays_where_it_is() {
2403        let (mut names, mut func, block) = empty();
2404        let next = func.create_block();
2405        let base = func.new_vreg(GPR);
2406        let other = func.new_vreg(GPR);
2407        let word = load(&mut func, &mut names, block, base);
2408        alu(&mut func, &mut names, next, "add_rr_64", other, word);
2409
2410        assert_eq!(combine(&mut func, &mut names), 0);
2411        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2412        assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2413    }
2414
2415    /// A reader further down the block than the window reaches.
2416    #[test]
2417    fn a_reader_past_the_window_stays_where_it_is() {
2418        let (mut names, mut func, block) = empty();
2419        let base = func.new_vreg(GPR);
2420        let other = func.new_vreg(GPR);
2421        let word = load(&mut func, &mut names, block, base);
2422        let nop = op(&mut names, "nop");
2423        for _ in 0..WINDOW {
2424            func.build(block, nop).finish();
2425        }
2426        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2427
2428        assert_eq!(combine(&mut func, &mut names), 0);
2429    }
2430
2431    /// And one instruction closer, which is the last place it still folds.
2432    #[test]
2433    fn a_reader_at_the_edge_of_the_window_folds() {
2434        let (mut names, mut func, block) = empty();
2435        let base = func.new_vreg(GPR);
2436        let other = func.new_vreg(GPR);
2437        let word = load(&mut func, &mut names, block, base);
2438        let nop = op(&mut names, "nop");
2439        for _ in 0..WINDOW - 1 {
2440            func.build(block, nop).finish();
2441        }
2442        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2443
2444        assert_eq!(combine(&mut func, &mut names), 1);
2445    }
2446
2447    /// The entry a frame layout is waiting on moves with the load. Without this the displacement
2448    /// of a local would be written into an instruction that has gone.
2449    #[test]
2450    fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2451        let (mut names, mut func, block) = empty();
2452        let base = Reg::physical(rucc_target::x86_64::RSP);
2453        let other = func.new_vreg(GPR);
2454        let word = load(&mut func, &mut names, block, base);
2455        let reader = func.insts(block).nth(1);
2456        assert!(reader.is_none(), "the block holds the load alone so far");
2457        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2458        let held = func.insts(block).next().expect("the load");
2459
2460        let mut addresses = vec![(held, 3usize)];
2461        let mut arguments = Vec::new();
2462        let mut dynamic = Vec::new();
2463        let mut pending =
2464            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2465        assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2466
2467        let inst = func.insts(block).next().expect("the addition");
2468        assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2469    }
2470
2471    /// A comparison whose right hand side came out of a load, which is `if (x < *p)`. The load is
2472    /// the side the instruction reads out of memory already, so the condition is the one that was
2473    /// written and only the opcode's shape changes.
2474    #[test]
2475    fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2476        let (mut names, mut func, block) = empty();
2477        let base = func.new_vreg(GPR);
2478        let other = func.new_vreg(GPR);
2479        let word = load(&mut func, &mut names, block, base);
2480        compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2481
2482        assert_eq!(combine(&mut func, &mut names), 1);
2483        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2484        let inst = func.insts(block).next().expect("the comparison");
2485        let mem = func[inst].mem.expect("it reads memory");
2486        assert_eq!(func[mem].disp, 16, "the address came from the load");
2487        assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2488        assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2489        assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2490    }
2491
2492    /// The same comparison the other way round, which is `if (*p < x)`. The machine reads the
2493    /// right hand side out of memory and nothing else, so what comes out is the question asked
2494    /// backwards, and less than on the left is greater than on the right.
2495    #[test]
2496    fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2497        let (mut names, mut func, block) = empty();
2498        let base = func.new_vreg(GPR);
2499        let other = func.new_vreg(GPR);
2500        let word = load(&mut func, &mut names, block, base);
2501        compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2502
2503        assert_eq!(combine(&mut func, &mut names), 1);
2504        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2505        let inst = func.insts(block).next().expect("the comparison");
2506        assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2507    }
2508
2509    /// Equality on the left, which is the case the turning over has to leave alone. Two values are
2510    /// equal in whichever order they are read, so the row for it names itself on both sides and a
2511    /// table that had reached for the opposite condition would have written inequality here.
2512    #[test]
2513    fn an_equality_folded_on_either_side_is_the_same_comparison() {
2514        for (first, second) in [(true, false), (false, true)] {
2515            let (mut names, mut func, block) = empty();
2516            let base = func.new_vreg(GPR);
2517            let other = func.new_vreg(GPR);
2518            let word = load(&mut func, &mut names, block, base);
2519            let left = if first { word } else { other };
2520            let right = if second { word } else { other };
2521            compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2522
2523            assert_eq!(combine(&mut func, &mut names), 1);
2524            assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2525        }
2526    }
2527
2528    /// A comparison against a constant, which is `if (*p < 7)`. There is one source rather than
2529    /// two, so the side the load filled is the only side there is and the condition stays as it
2530    /// was written. What is left in front of the address is the byte on its own, which puts the
2531    /// base one position earlier than the comparison of two registers leaves it.
2532    #[test]
2533    fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2534        let (mut names, mut func, block) = empty();
2535        let base = func.new_vreg(GPR);
2536        let byte = func.new_vreg(GPR);
2537        let word = load(&mut func, &mut names, block, base);
2538        let opcode = op(&mut names, "cmp_set_l_ri_64");
2539        func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2540
2541        assert_eq!(combine(&mut func, &mut names), 1);
2542        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2543        let inst = func.insts(block).next().expect("the comparison");
2544        let mem = func[inst].mem.expect("it reads memory now");
2545        assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2546        assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2547        assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2548        assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2549        let imm = func[inst].imm.expect("the constant is still on it");
2550        assert_eq!(func[imm].0, 7, "and is the one that was written");
2551    }
2552
2553    /// A load that nothing but a widening reads becomes the load that widens, at every width the
2554    /// machine has one for, and the address and the register written come with it.
2555    #[test]
2556    fn a_load_only_a_widening_reads_becomes_a_load_that_widens() {
2557        for row in WIDENINGS {
2558            let (mut names, mut func, block) = empty();
2559            let base = func.new_vreg(GPR);
2560            let narrow = func.new_vreg(GPR);
2561            let wide = func.new_vreg(GPR);
2562            let read = op(&mut names, row.load);
2563            func.build(block, read)
2564                .def(narrow, GPR)
2565                .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2566                .finish();
2567            let widen = op(&mut names, row.from);
2568            func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2569
2570            assert_eq!(combine(&mut func, &mut names), 1, "{} took no load", row.from);
2571            assert_eq!(shape(&func, &names, block), [format!("x64.{}", row.into)]);
2572            let inst = func.insts(block).next().expect("the widening");
2573            assert_eq!(func[func[inst].operands][0].reg, wide, "{} writes elsewhere", row.from);
2574            assert_eq!(func[func[inst].operands][1].reg, base, "{} lost the address", row.from);
2575            let mem = func[inst].mem.expect("it reads memory now");
2576            assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2577            assert_eq!(func[mem].base, Some(1), "and names the operand behind the answer");
2578        }
2579    }
2580
2581    /// A load that something besides the widening reads stays a load, since the value has to be
2582    /// in a register for the other reader anyway.
2583    #[test]
2584    fn a_load_read_by_a_widening_and_something_else_stays_where_it_is() {
2585        let (mut names, mut func, block) = empty();
2586        let base = func.new_vreg(GPR);
2587        let other = func.new_vreg(GPR);
2588        let narrow = func.new_vreg(GPR);
2589        let wide = func.new_vreg(GPR);
2590        let read = op(&mut names, "mov_rm_32");
2591        func.build(block, read)
2592            .def(narrow, GPR)
2593            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2594            .finish();
2595        let widen = op(&mut names, "movsxd_32_64");
2596        func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2597        alu(&mut func, &mut names, block, "add_rr_32", other, narrow);
2598
2599        assert_eq!(combine(&mut func, &mut names), 0);
2600        assert_eq!(
2601            shape(&func, &names, block),
2602            ["x64.mov_rm_32", "x64.movsxd_32_64", "x64.add_rr_32"]
2603        );
2604    }
2605
2606    /// A load the program insisted on keeps its own instruction, the same as it does in front of
2607    /// arithmetic.
2608    #[test]
2609    fn a_volatile_load_is_not_widened_on_the_way_in() {
2610        let (mut names, mut func, block) = empty();
2611        let base = func.new_vreg(GPR);
2612        let narrow = func.new_vreg(GPR);
2613        let wide = func.new_vreg(GPR);
2614        let read = op(&mut names, "mov_rm_16");
2615        func.build(block, read)
2616            .def(narrow, GPR)
2617            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2618            .flags(Flags::VOLATILE)
2619            .finish();
2620        let widen = op(&mut names, "movsx_16_32");
2621        func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2622
2623        assert_eq!(combine(&mut func, &mut names), 0);
2624        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_16", "x64.movsx_16_32"]);
2625    }
2626
2627    /// Every widening names instructions this target has, reads memory only once folded, and
2628    /// takes a load of the width it widens from.
2629    #[test]
2630    fn every_widening_takes_a_load_of_the_width_it_widens_from() {
2631        let from = |name: &str| {
2632            name.split('_').find(|part| part.parse::<u32>().is_ok()).map(str::to_owned)
2633        };
2634        let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2635        for row in WIDENINGS {
2636            assert!(MACHINE.has(row.from), "{} is not an instruction", row.from);
2637            assert!(MACHINE.has(row.into), "{} is not an instruction", row.into);
2638            assert!(MACHINE.has(row.load), "{} is not an instruction", row.load);
2639            assert_eq!(from(row.from), width(row.load), "{} loads another width", row.from);
2640            assert!((MACHINE.takes_mem)(row.into), "{} reads no memory", row.into);
2641            assert!(!(MACHINE.takes_mem)(row.from), "{} already reads memory", row.from);
2642            assert_eq!(row.swapped, None, "{} has nothing to swap", row.from);
2643        }
2644    }
2645
2646    /// Every row of the table names instructions this target has, and names a load and an
2647    /// arithmetic whose widths agree. A row that got one of the three wrong would propose an
2648    /// instruction the change framework turns down, which is a fold that silently never happens.
2649    #[test]
2650    fn every_row_of_the_table_is_three_instructions_this_target_has() {
2651        for fold in FOLDS {
2652            assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2653            assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2654            assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2655            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2656            assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2657            assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2658            assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2659            assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2660            let Some(swapped) = fold.swapped else { continue };
2661            assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2662            assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2663            assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2664        }
2665    }
2666
2667    /// One row per arithmetic instruction the target has that could take one, and one per
2668    /// comparison. The counts are here so that an instruction added to the target without a row
2669    /// shows up as a number rather than as a fold nobody noticed was missing.
2670    #[test]
2671    fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2672        let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2673        assert_eq!(
2674            compares, 80,
2675            "ten conditions at four widths, against a register and a constant"
2676        );
2677        let arithmetic = FOLDS.len() - compares;
2678        assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2679        let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2680        assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2681    }
2682
2683    /// What a comparison folded on its left hand side comes out as.
2684    ///
2685    /// Reading the two sides the other way round turns the question over, so the row has to name
2686    /// the opposite ordering rather than the opposite answer. Less than and greater than are the
2687    /// pair, and equality and inequality are the two that come back to themselves, which is what
2688    /// makes this worth a test of its own: a row that had turned equality into inequality would be
2689    /// wrong in a way no width check and no name check would catch.
2690    #[test]
2691    fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2692        let turned = |condition: &str| match condition {
2693            "e" => "e",
2694            "ne" => "ne",
2695            "l" => "g",
2696            "g" => "l",
2697            "le" => "ge",
2698            "ge" => "le",
2699            "b" => "a",
2700            "a" => "b",
2701            "be" => "ae",
2702            "ae" => "be",
2703            other => panic!("{other} is not a condition this machine has"),
2704        };
2705        let compares = FOLDS
2706            .iter()
2707            .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2708        for fold in compares {
2709            let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2710            let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2711            assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2712            let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2713            assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2714        }
2715    }
2716
2717    /// What a comparison against a constant comes out as. There is one source rather than two, so
2718    /// the condition is the one that was written and there is no other arrangement to offer. A row
2719    /// that had filled in a `swapped` would be asking the pass to read the constant out of a
2720    /// register, which is not an instruction this machine has.
2721    #[test]
2722    fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2723        let compares = FOLDS
2724            .iter()
2725            .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2726        let mut rows = 0;
2727        for fold in compares {
2728            let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2729            let front = front.strip_suffix("_ri").expect("a name against a constant");
2730            let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2731            assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2732            assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2733            assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2734            rows += 1;
2735        }
2736        assert_eq!(rows, 40, "ten conditions at four widths");
2737    }
2738
2739    /// A load the program insisted on, which is `volatile int *p; return *p + x;`.
2740    ///
2741    /// The fold would leave one instruction that reads the place, which is still one read of it,
2742    /// and the program would still do what it says. What it would not be is the load the program
2743    /// wrote, and a machine whose memory does something when it is read is a machine where the
2744    /// difference between one instruction and two is the reason the word was written.
2745    #[test]
2746    fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2747        let (mut names, mut func, block) = empty();
2748        let base = func.new_vreg(GPR);
2749        let other = func.new_vreg(GPR);
2750        let word = insisted_load(&mut func, &mut names, block, base);
2751        alu(&mut func, &mut names, block, "add_rr_64", word, other);
2752
2753        assert_eq!(combine(&mut func, &mut names), 0);
2754        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2755    }
2756
2757    /// The same load with the arithmetic that reads it and the store that puts it back, which is
2758    /// `volatile int *p; *p += x;`. Three instructions in and three out, which is what GCC 13
2759    /// writes for it and what the spec asks for.
2760    #[test]
2761    fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2762        let (mut names, mut func, block) = empty();
2763        let base = func.new_vreg(GPR);
2764        let other = func.new_vreg(GPR);
2765        let word = insisted_load(&mut func, &mut names, block, base);
2766        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2767        store(&mut func, &mut names, block, base, sum);
2768
2769        assert_eq!(update(&mut func, &mut names), 0);
2770    }
2771
2772    /// The other end of the run, which is the half the load's own flag does not cover. A place
2773    /// read plainly and written back to a volatile address is a program that asked for the write
2774    /// to be its own instruction, and both ends are asked about because either one of them says
2775    /// so on its own.
2776    #[test]
2777    fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2778        let (mut names, mut func, block) = empty();
2779        let base = func.new_vreg(GPR);
2780        let other = func.new_vreg(GPR);
2781        let word = load(&mut func, &mut names, block, base);
2782        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2783        insisted_store(&mut func, &mut names, block, base, sum);
2784
2785        assert_eq!(update(&mut func, &mut names), 0);
2786    }
2787
2788    /// The run against a constant, which is `volatile int *p; *p += 1;` and is the commoner of
2789    /// the two. It is a separate walk over a separate table, so it is asked separately.
2790    #[test]
2791    fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2792        let (mut names, mut func, block) = empty();
2793        let base = func.new_vreg(GPR);
2794        let word = insisted_load(&mut func, &mut names, block, base);
2795        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2796        store(&mut func, &mut names, block, base, sum);
2797
2798        assert_eq!(update(&mut func, &mut names), 0);
2799    }
2800
2801    /// The same run with the flag on the store instead of on the load.
2802    #[test]
2803    fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2804        let (mut names, mut func, block) = empty();
2805        let base = func.new_vreg(GPR);
2806        let word = load(&mut func, &mut names, block, base);
2807        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2808        insisted_store(&mut func, &mut names, block, base, sum);
2809
2810        assert_eq!(update(&mut func, &mut names), 0);
2811    }
2812
2813    /// A plain load of the same shape, so that the five above are read as the flag doing the
2814    /// work rather than as the runs being built wrongly.
2815    #[test]
2816    fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2817        let (mut names, mut func, block) = empty();
2818        let base = func.new_vreg(GPR);
2819        let other = func.new_vreg(GPR);
2820        let word = load(&mut func, &mut names, block, base);
2821        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2822        store(&mut func, &mut names, block, base, sum);
2823
2824        assert_eq!(update(&mut func, &mut names), 1);
2825        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2826    }
2827}