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/// The narrow inclusive or and exclusive or against a constant went out under tamnd/rucc#368 and
900/// came back with the width narrowing in tamnd/rucc#375, so a program that writes `*p |= 4`
901/// through a `char` is a byte `or` straight to memory.
902pub static BUMPS: &[Bump] = &[
903    Bump { from: "add_ri_8", into: "add_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
904    Bump { from: "add_ri_16", into: "add_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
905    Bump { from: "add_ri_32", into: "add_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
906    Bump { from: "add_ri_64", into: "add_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
907    Bump { from: "sub_ri_8", into: "sub_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
908    Bump { from: "sub_ri_16", into: "sub_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
909    Bump { from: "sub_ri_32", into: "sub_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
910    Bump { from: "sub_ri_64", into: "sub_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
911    Bump { from: "and_ri_8", into: "and_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
912    Bump { from: "and_ri_16", into: "and_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
913    Bump { from: "and_ri_32", into: "and_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
914    Bump { from: "and_ri_64", into: "and_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
915    Bump { from: "or_ri_8", into: "or_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
916    Bump { from: "or_ri_16", into: "or_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
917    Bump { from: "or_ri_32", into: "or_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
918    Bump { from: "or_ri_64", into: "or_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
919    Bump { from: "xor_ri_8", into: "xor_mi_8", load: "mov_rm_8", store: "mov_mr_8" },
920    Bump { from: "xor_ri_16", into: "xor_mi_16", load: "mov_rm_16", store: "mov_mr_16" },
921    Bump { from: "xor_ri_32", into: "xor_mi_32", load: "mov_rm_32", store: "mov_mr_32" },
922    Bump { from: "xor_ri_64", into: "xor_mi_64", load: "mov_rm_64", store: "mov_mr_64" },
923];
924
925/// The load this block has passed that could still end up inside something.
926///
927/// One rather than a list of them, because anything that touches memory ends the one being carried,
928/// so the one being carried is always the last memory access there was.
929#[derive(Debug, Clone, Copy)]
930struct Waiting {
931    /// The load.
932    inst: Inst,
933    /// The register it wrote, which is what the arithmetic has to be reading.
934    reg: Reg,
935    /// Which load it is, so that the width can be held against the arithmetic's.
936    load: &'static str,
937    /// How far along the block it is, which is what [`WINDOW`] is counted in.
938    at: usize,
939}
940
941/// Puts every load that can move into the arithmetic that reads it, and gives back how many.
942///
943/// `pending` is the addresses [`crate::finish`] has still to write a displacement into, and a load
944/// that moves takes its entry with it, the same way one folded into a reader does. An address into
945/// the frame arrives here already inside the load, because [`crate::fold`] has run.
946///
947/// Run after selection and after the addresses are folded, and before allocation. Before the
948/// allocator because what makes the pair safe to put together is that a virtual register is written
949/// once, and after the addresses because a load whose address is still a `lea` in front of it has
950/// nothing in its own memory operand worth carrying.
951pub fn loads(
952    func: &mut Func,
953    machine: &MachineInsts,
954    names: &mut Interner,
955    pending: &mut Pending<'_>,
956) -> usize {
957    let mut reads = Reads::of(func);
958    let mut done = 0;
959    for block in func.blocks().collect::<Vec<_>>() {
960        let mut waiting: Option<Waiting> = None;
961        for (at, inst) in func.insts(block).collect::<Vec<_>>().into_iter().enumerate() {
962            let name = names.resolve(func[inst].opcode.name()).to_owned();
963            let bare = machine.bare(&name).to_owned();
964            // Asked before the rewrite below rather than after it, because the rewrite turns an
965            // instruction that touched no memory into one that does, and asking afterwards would
966            // throw away the load that had just gone into it over the load that had just gone into
967            // it. Nothing else about the answer moves: the other end of a row of the fold table is
968            // arithmetic this target describes and is not a call.
969            let barrier = machine.calls(&name) || !machine.has(&name) || machine.touches_mem(&name);
970            if let Some(carried) = waiting {
971                if let Some(plan) = joined(func, &reads, carried, machine, names, inst, &bare) {
972                    let mut set = Changes::new();
973                    set.rewrite(inst, plan);
974                    set.remove(carried.inst);
975                    if set.commit(func, &mut reads, names, machine).is_ok() {
976                        pending.moved(carried.inst, &[inst]);
977                        waiting = None;
978                        done += 1;
979                    }
980                }
981            }
982            if barrier {
983                waiting = None;
984            }
985            if let Some(carried) = waiting {
986                if at - carried.at >= WINDOW || writes_what_it_reads(func, inst, &carried) {
987                    waiting = None;
988                }
989            }
990            // A load the program insisted on is never carried forward, so there is never one in
991            // hand for the fold below to take. Refused where the load is picked up rather than
992            // where it is joined, because what is wrong with it is what it is and not what it
993            // meets: a load nothing may fold has no business being waited on for sixteen
994            // instructions either.
995            if insisted(func, inst) {
996                continue;
997            }
998            let mut rows = FOLDS.iter().chain(WIDENINGS);
999            if let Some(load) = rows.find(|fold| fold.load == bare).map(|fold| fold.load) {
1000                let operands = &func[func[inst].operands];
1001                if let Some(first) = operands.first().filter(|operand| operand.role.is_def()) {
1002                    waiting = Some(Waiting { inst, reg: first.reg, load, at });
1003                }
1004            }
1005        }
1006    }
1007    done
1008}
1009
1010/// The three instructions that read a place, compute on what was there and write it back.
1011#[derive(Debug, Clone, Copy)]
1012struct Run {
1013    /// The load that read the place.
1014    load: Inst,
1015    /// The arithmetic that read what the load put in a register.
1016    alu: Inst,
1017    /// The store that put the answer back where the load got it.
1018    store: Inst,
1019    /// Which row of [`UPDATES`] the run is.
1020    update: &'static Update,
1021    /// The source the arithmetic is left reading, which is the one the memory is not.
1022    kept: Operand,
1023}
1024
1025/// The same three instructions with a constant where the other source was.
1026///
1027/// A separate shape from [`Run`] rather than the same one with an option in it, because the two
1028/// differ in what they carry and in nothing else. This one holds the constant the instruction that
1029/// comes out will carry, and has no `kept`, since the arithmetic is left reading nothing at all.
1030#[derive(Debug, Clone, Copy)]
1031struct Bumped {
1032    /// The load that read the place.
1033    load: Inst,
1034    /// The arithmetic that read what the load put in a register.
1035    alu: Inst,
1036    /// The store that put the answer back where the load got it.
1037    store: Inst,
1038    /// Which row of [`BUMPS`] the run is.
1039    bump: &'static Bump,
1040    /// The constant the arithmetic was against.
1041    imm: i64,
1042}
1043
1044/// Puts every run that reads a place, computes on it and writes it back into the one instruction
1045/// this machine has for all three, and gives back how many.
1046///
1047/// `pending` is the addresses [`crate::finish`] has still to write a displacement into. The store
1048/// is the instruction that survives and it is already waiting on the entry the load was waiting on,
1049/// since the two name the same place, so the load's entry is taken off rather than moved.
1050///
1051/// Run before [`loads`] rather than after it. The run this looks for is three instructions the
1052/// selector wrote, and folding the load into the arithmetic first would leave two instructions that
1053/// are the same thing written differently, so the walk would have to know both spellings. Whatever
1054/// this does not take is still there for [`loads`] to take the load out of.
1055///
1056/// The run whose arithmetic is against a constant is looked for after the one whose arithmetic is
1057/// against a register, and the order between those two does not matter: the middle instruction
1058/// decides which of them a run is, and no instruction is both an [`UPDATES`] row and a [`BUMPS`]
1059/// row.
1060pub fn stores(
1061    func: &mut Func,
1062    machine: &MachineInsts,
1063    flags: &FlagInsts,
1064    names: &mut Interner,
1065    pending: &mut Pending<'_>,
1066) -> usize {
1067    let mut reads = Reads::of(func);
1068    let mut done = 0;
1069    for block in func.blocks().collect::<Vec<_>>() {
1070        let insts: Vec<Inst> = func.insts(block).collect();
1071        for at in 0..insts.len() {
1072            let found = match run(func, &reads, machine, flags, names, &insts, at) {
1073                Some(found) => Some((
1074                    found.load,
1075                    found.alu,
1076                    found.store,
1077                    updated(func, machine, names, &found),
1078                )),
1079                None => constant(func, &reads, machine, flags, names, &insts, at).map(|found| {
1080                    (found.load, found.alu, found.store, bumped(func, machine, names, &found))
1081                }),
1082            };
1083            let Some((load, alu, store, plan)) = found else { continue };
1084            if !pending.alike(load, store) {
1085                continue;
1086            }
1087            let mut set = Changes::new();
1088            set.rewrite(store, plan);
1089            set.remove(alu);
1090            set.remove(load);
1091            if set.commit(func, &mut reads, names, machine).is_ok() {
1092                pending.moved(load, &[]);
1093                done += 1;
1094            }
1095        }
1096    }
1097    done
1098}
1099
1100/// The run ending in the instruction at that position, or `None`.
1101///
1102/// Walked backwards from the store, because the store is the end of the run and is the instruction
1103/// that is left when the run is joined. Everything the walk needs is behind it: which register it
1104/// is storing says which arithmetic to look for, and which source that arithmetic reads says which
1105/// load.
1106///
1107/// An instruction an earlier fold took out is still in `insts` and is read here as though it were
1108/// where it was. That costs a fold and never takes one: a removed instruction is one more thing in
1109/// the way, and it cannot be the arithmetic or the load this is looking for, because each of those
1110/// is the one writer of a register something still reads.
1111fn run(
1112    func: &Func,
1113    reads: &Reads,
1114    machine: &MachineInsts,
1115    flags: &FlagInsts,
1116    names: &Interner,
1117    insts: &[Inst],
1118    at: usize,
1119) -> Option<Run> {
1120    let store = insts[at];
1121    if insisted(func, store) {
1122        return None;
1123    }
1124    let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1125    let value = *func[func[store].operands].first()?;
1126    if value.role.is_def() || reads.count(value.reg) != 1 {
1127        return None;
1128    }
1129    // One bound over the whole run rather than one per pair, so that what the window means is how
1130    // far apart the first and the last of the three may be.
1131    let earliest = at.saturating_sub(WINDOW);
1132    let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1133    let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1134    let update = UPDATES.iter().find(|row| row.from == bare && row.store == stored)?;
1135    if !quiet(func, flags, names, insts, (alu, at)) {
1136        return None;
1137    }
1138    let operands = func[func[insts[alu]].operands].to_vec();
1139    let [_, first, second] = operands[..] else { return None };
1140    // The left source is the one the memory takes the place of, because the answer is left where
1141    // the memory operand points and the answer is tied to the left source. Where the load feeds the
1142    // right one instead and the operation commutes, the two swap, which leaves the instruction
1143    // computing what it computed.
1144    let both = [(first, second), (second, first)];
1145    let tried = if update.commutes { &both[..] } else { &both[..1] };
1146    for &(source, kept) in tried {
1147        if reads.count(source.reg) != 1 {
1148            continue;
1149        }
1150        let Some(from) = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg)) else {
1151            continue;
1152        };
1153        let load = insts[from];
1154        if insisted(func, load) {
1155            continue;
1156        }
1157        if machine.bare(names.resolve(func[load].opcode.name())) != update.load {
1158            continue;
1159        }
1160        if !same_place(func, load, store) {
1161            continue;
1162        }
1163        // The registers the one instruction left is reading, which are the ones nothing between the
1164        // load and the store may write. The arithmetic itself passes this without being left out of
1165        // it: what it writes is the value the store is storing, and that register is not one of
1166        // these.
1167        let mut wanted: Vec<Reg> =
1168            func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1169        wanted.push(kept.reg);
1170        if !clear(func, machine, names, insts, (from, at), &wanted) {
1171            continue;
1172        }
1173        return Some(Run { load, alu: insts[alu], store, update, kept });
1174    }
1175    None
1176}
1177
1178/// The run against a constant ending in the instruction at that position, or `None`.
1179///
1180/// [`run`] with the arithmetic's second source gone. Walked backwards from the store for the same
1181/// reason, and asking the same four questions: the stored register is read once, the source the
1182/// arithmetic reads is written once by a load of the right width, that load names the same place as
1183/// the store, and nothing between the two is in the way. There is no arrangement to choose between,
1184/// because the constant is on the instruction and only the left source can be the memory.
1185///
1186/// One question [`run`] does not ask is here: where the addressing mode's registers are. The
1187/// instruction that comes out has no operand in front of them, so each of them moves one place
1188/// towards the front of the vector, and a mode that already pointed at the front would have to move
1189/// to nowhere. That cannot happen, since the front is the value the store is storing, and refusing
1190/// the run is what it costs to say so rather than to assume it.
1191fn constant(
1192    func: &Func,
1193    reads: &Reads,
1194    machine: &MachineInsts,
1195    flags: &FlagInsts,
1196    names: &Interner,
1197    insts: &[Inst],
1198    at: usize,
1199) -> Option<Bumped> {
1200    let store = insts[at];
1201    if insisted(func, store) {
1202        return None;
1203    }
1204    let stored = machine.bare(names.resolve(func[store].opcode.name())).to_owned();
1205    let value = *func[func[store].operands].first()?;
1206    if value.role.is_def() || reads.count(value.reg) != 1 {
1207        return None;
1208    }
1209    let mem = func[func[store].mem?];
1210    if mem.base == Some(0) || mem.index == Some(0) {
1211        return None;
1212    }
1213    let earliest = at.saturating_sub(WINDOW);
1214    let alu = (earliest..at).rev().find(|&k| writes(func, insts[k], value.reg))?;
1215    let bare = machine.bare(names.resolve(func[insts[alu]].opcode.name())).to_owned();
1216    let bump = BUMPS.iter().find(|row| row.from == bare && row.store == stored)?;
1217    if !quiet(func, flags, names, insts, (alu, at)) {
1218        return None;
1219    }
1220    let operands = func[func[insts[alu]].operands].to_vec();
1221    let [_, source] = operands[..] else { return None };
1222    let imm = func[func[insts[alu]].imm?].0;
1223    if reads.count(source.reg) != 1 {
1224        return None;
1225    }
1226    let from = (earliest..alu).rev().find(|&k| writes(func, insts[k], source.reg))?;
1227    let load = insts[from];
1228    if insisted(func, load) {
1229        return None;
1230    }
1231    if machine.bare(names.resolve(func[load].opcode.name())) != bump.load {
1232        return None;
1233    }
1234    if !same_place(func, load, store) {
1235        return None;
1236    }
1237    // The registers the one instruction left is reading, which are the ones in its address and no
1238    // others, since the constant is not in a register and the arithmetic is left reading nothing.
1239    let wanted: Vec<Reg> =
1240        func[func[store].operands][1..].iter().map(|operand| operand.reg).collect();
1241    if !clear(func, machine, names, insts, (from, at), &wanted) {
1242        return None;
1243    }
1244    Some(Bumped { load, alu: insts[alu], store, bump, imm })
1245}
1246
1247/// Whether the program insisted on this access happening exactly as it is written.
1248///
1249/// Which is `volatile`, and is the one question in this module that is not about what the
1250/// instructions do to each other. See the section above on what such an access gets.
1251fn insisted(func: &Func, inst: Inst) -> bool {
1252    func[inst].flags.contains(Flags::VOLATILE)
1253}
1254
1255/// Whether this instruction writes that register.
1256fn writes(func: &Func, inst: Inst, reg: Reg) -> bool {
1257    func[func[inst].operands].iter().any(|operand| operand.role.is_def() && operand.reg == reg)
1258}
1259
1260/// Whether the two instructions name the same place in memory.
1261///
1262/// The same addressing mode, the same symbol, and the same registers where the mode holds operand
1263/// positions. Both instructions here write their value down first and their address behind it, so
1264/// the positions line up, and the registers are compared anyway rather than the positions, because
1265/// what makes two addresses one place is which registers they read.
1266fn same_place(func: &Func, one: Inst, other: Inst) -> bool {
1267    let (Some(here), Some(there)) = (func[one].mem, func[other].mem) else { return false };
1268    let (here, there) = (func[here], func[there]);
1269    if func[one].symbol != func[other].symbol {
1270        return false;
1271    }
1272    let bare = |amode: Amode| Amode { base: None, index: None, ..amode };
1273    if bare(here) != bare(there) {
1274        return false;
1275    }
1276    let same = |left: Option<u8>, right: Option<u8>| match (left, right) {
1277        (None, None) => true,
1278        (Some(left), Some(right)) => {
1279            func[func[one].operands][usize::from(left)].reg
1280                == func[func[other].operands][usize::from(right)].reg
1281        }
1282        _ => false,
1283    };
1284    same(here.base, there.base) && same(here.index, there.index)
1285}
1286
1287/// Whether everything between the two positions may be passed.
1288///
1289/// The run becomes one instruction where the store is, so the read of memory the load was doing
1290/// moves down the block to there. Nothing that touches memory may be passed, for the reason the
1291/// module documentation gives about [`loads`], and nothing may write a register the instruction
1292/// that is left still reads.
1293fn clear(
1294    func: &Func,
1295    machine: &MachineInsts,
1296    names: &Interner,
1297    insts: &[Inst],
1298    span: (usize, usize),
1299    wanted: &[Reg],
1300) -> bool {
1301    let (from, to) = span;
1302    insts[from + 1..to].iter().all(|&inst| {
1303        let name = names.resolve(func[inst].opcode.name());
1304        if machine.calls(name) || !machine.has(name) || machine.touches_mem(name) {
1305            return false;
1306        }
1307        !func[func[inst].operands]
1308            .iter()
1309            .any(|operand| operand.role.is_def() && wanted.contains(&operand.reg))
1310    })
1311}
1312
1313/// Whether the condition state between the arithmetic and the store belongs to nobody.
1314///
1315/// The arithmetic moves down the block to where the store is and it writes the condition state, so
1316/// everything it passes has to have no opinion about that state. An instruction that reads it was
1317/// reading what the arithmetic left and would be reading what was there before the arithmetic
1318/// instead. An instruction that writes it was the last writer before the store and would stop being
1319/// it, which changes what anything further down reads. Neither is allowed and there is nothing to
1320/// weigh: this is the shape tamnd/rucc#1424 was, where the reader in the middle was the `adc` that
1321/// takes the carry out of the addition above it into a register.
1322///
1323/// Asked from the arithmetic rather than from the load, which is what [`clear`] is asked from.
1324/// Between the load and the arithmetic nothing moves except the load, and a load has nothing to say
1325/// about the condition state.
1326///
1327/// A name the description does not cover counts as both, for the reason [`FlagInsts::writes`] gives
1328/// about a name this target does not have.
1329fn quiet(
1330    func: &Func,
1331    flags: &FlagInsts,
1332    names: &Interner,
1333    insts: &[Inst],
1334    span: (usize, usize),
1335) -> bool {
1336    let (alu, to) = span;
1337    insts[alu + 1..to].iter().all(|&inst| {
1338        let Some(name) = names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix) else {
1339            return false;
1340        };
1341        flags.reads(name).is_none() && !(flags.writes)(name)
1342    })
1343}
1344
1345/// What the store becomes with the rest of the run inside it.
1346///
1347/// The store's own addressing mode and the source the arithmetic kept, which is the whole of it.
1348/// The mode is left exactly as it was, because the operand it was written against is the value the
1349/// store was storing and what takes that operand's place is one operand as well.
1350fn updated(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Run) -> Plan {
1351    let operands = func[func[run.store].operands].to_vec();
1352    let into = names.intern(&format!("{}{}", machine.prefix, run.update.into));
1353    Plan {
1354        opcode: Opcode::new(into),
1355        operands: [run.kept].into_iter().chain(operands[1..].iter().copied()).collect(),
1356        imm: None,
1357        amode: func[run.store].mem.map(|mem| func[mem]),
1358        symbol: func[run.store].symbol,
1359    }
1360}
1361
1362/// What the store becomes with the rest of a constant run inside it.
1363///
1364/// The store's own addressing mode again, and the constant the arithmetic carried. The mode does
1365/// not come through untouched this time. The value the store was storing has nothing taking its
1366/// place, so the registers behind it each move one place towards the front of the operand vector,
1367/// and the positions the mode holds are positions in that vector and move with them. [`constant`]
1368/// is what makes sure there is a place for each of them to move to.
1369fn bumped(func: &Func, machine: &MachineInsts, names: &mut Interner, run: &Bumped) -> Plan {
1370    let operands = func[func[run.store].operands][1..].to_vec();
1371    let into = names.intern(&format!("{}{}", machine.prefix, run.bump.into));
1372    let back = |at: Option<u8>| at.map(|at| at - 1);
1373    Plan {
1374        opcode: Opcode::new(into),
1375        operands,
1376        imm: Some(run.imm),
1377        amode: func[run.store].mem.map(|mem| {
1378            let mem = func[mem];
1379            Amode { base: back(mem.base), index: back(mem.index), ..mem }
1380        }),
1381        symbol: func[run.store].symbol,
1382    }
1383}
1384
1385/// Whether this instruction writes a register the carried load needs left alone.
1386///
1387/// The registers its address reads, and the register it wrote. The second is there for the same
1388/// reason the first is: a virtual register cannot be written twice while the IR is in SSA form, and
1389/// these are the physical ones a function has before the allocator runs.
1390fn writes_what_it_reads(func: &Func, inst: Inst, carried: &Waiting) -> bool {
1391    let written: Vec<Reg> = func[func[inst].operands]
1392        .iter()
1393        .filter(|operand| operand.role.is_def())
1394        .map(|operand| operand.reg)
1395        .collect();
1396    func[func[carried.inst].operands].iter().any(|operand| written.contains(&operand.reg))
1397}
1398
1399/// What this instruction becomes with the carried load inside it, or `None`.
1400///
1401/// Nothing here changes anything. What comes back is a proposal, and whether the target has the
1402/// instruction it describes is [`Changes`]'s answer rather than this one.
1403fn joined(
1404    func: &Func,
1405    reads: &Reads,
1406    carried: Waiting,
1407    machine: &MachineInsts,
1408    names: &mut Interner,
1409    inst: Inst,
1410    bare: &str,
1411) -> Option<Plan> {
1412    let fold = FOLDS.iter().chain(WIDENINGS).find(|fold| fold.from == bare)?;
1413    if carried.load != fold.load || reads.count(carried.reg) != 1 {
1414        return None;
1415    }
1416    let operands = func[func[inst].operands].to_vec();
1417    // The second source is the one the memory operand replaces, which for arithmetic is because
1418    // the answer is tied to the first and for a comparison is because that is the side the
1419    // instruction subtracts. Where the load feeds the first source instead, the row says which
1420    // instruction reads the two the other way round, and that one is written instead: for
1421    // arithmetic that commutes it is the same instruction, and for a comparison it is the same
1422    // question with the condition turned over.
1423    //
1424    // A comparison against a constant has one source and no arrangement to choose between, since
1425    // the constant is on the instruction and cannot be anywhere else. What is left in front of the
1426    // address is the byte on its own.
1427    let (front, into) = match operands[..] {
1428        [answer, first, second] => {
1429            let (kept, into) = if second.reg == carried.reg {
1430                (first, fold.into)
1431            } else if first.reg == carried.reg {
1432                (second, fold.swapped?)
1433            } else {
1434                return None;
1435            };
1436            (vec![answer, kept], into)
1437        }
1438        [answer, only] if only.reg == carried.reg => (vec![answer], fold.into),
1439        _ => return None,
1440    };
1441    let load = carried.inst;
1442    let address = func[func[load].operands][1..].to_vec();
1443    let mut amode = func[func[load].mem?];
1444    // The registers an address names are operands behind the ones the instruction writes down. The
1445    // load wrote one of those and the instruction that comes out writes however many are in front
1446    // of the address here, so every position the mode holds moves along by the difference.
1447    let along = u8::try_from(front.len() - 1).expect("a handful of operands");
1448    amode.base = amode.base.map(|at| at + along);
1449    amode.index = amode.index.map(|at| at + along);
1450    let into = names.intern(&format!("{}{}", machine.prefix, into));
1451    Some(Plan {
1452        opcode: Opcode::new(into),
1453        operands: front.into_iter().chain(address).collect(),
1454        imm: func[inst].imm.map(|at| func[at].0),
1455        amode: Some(amode),
1456        symbol: func[load].symbol,
1457    })
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462    use rucc_mir::{self as mir, Constraint, Mem, Operand};
1463    use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
1464
1465    use super::*;
1466
1467    /// A function with one block, and the names it was built with.
1468    fn empty() -> (Interner, Func, mir::Block) {
1469        let mut names = Interner::new();
1470        let mut func = Func::new(names.intern("f"));
1471        let block = func.create_block();
1472        (names, func, block)
1473    }
1474
1475    /// The opcode of that name on this target.
1476    fn op(names: &mut Interner, name: &str) -> Opcode {
1477        Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
1478    }
1479
1480    /// A load of eight bytes off that register.
1481    fn load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1482        let into = func.new_vreg(GPR);
1483        let mov = op(names, "mov_rm_64");
1484        func.build(block, mov)
1485            .def(into, GPR)
1486            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1487            .finish();
1488        into
1489    }
1490
1491    /// The same load, of a place the program said to read exactly where it is written.
1492    fn insisted_load(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg) -> Reg {
1493        let into = func.new_vreg(GPR);
1494        let mov = op(names, "mov_rm_64");
1495        func.build(block, mov)
1496            .def(into, GPR)
1497            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1498            .flags(Flags::VOLATILE)
1499            .finish();
1500        into
1501    }
1502
1503    /// Two-address arithmetic of that name on those two registers, in that order.
1504    fn alu(
1505        func: &mut Func,
1506        names: &mut Interner,
1507        block: mir::Block,
1508        name: &str,
1509        first: Reg,
1510        second: Reg,
1511    ) -> Reg {
1512        let answer = func.new_vreg(GPR);
1513        let opcode = op(names, name);
1514        func.build(block, opcode)
1515            .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1516            .uses(first, GPR)
1517            .uses(second, GPR)
1518            .finish();
1519        answer
1520    }
1521
1522    /// A comparison of those two registers in that order, which keeps its answer in a byte the
1523    /// two sources have no claim on and is what makes it not two-address.
1524    fn compare(
1525        func: &mut Func,
1526        names: &mut Interner,
1527        block: mir::Block,
1528        name: &str,
1529        first: Reg,
1530        second: Reg,
1531    ) -> Reg {
1532        let byte = func.new_vreg(GPR);
1533        let opcode = op(names, name);
1534        func.build(block, opcode).def(byte, GPR).uses(first, GPR).uses(second, GPR).finish();
1535        byte
1536    }
1537
1538    /// What every instruction in a block came to, as opcodes.
1539    fn shape(func: &Func, names: &Interner, block: mir::Block) -> Vec<String> {
1540        func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
1541    }
1542
1543    /// The pass, with lists nothing is on.
1544    fn combine(func: &mut Func, names: &mut Interner) -> usize {
1545        let mut addresses = Vec::new();
1546        let mut arguments = Vec::new();
1547        let mut dynamic = Vec::new();
1548        let mut pending =
1549            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1550        loads(func, &MACHINE, names, &mut pending)
1551    }
1552
1553    /// A store of that register to sixteen off that base, which is the address `load` reads.
1554    fn store(func: &mut Func, names: &mut Interner, block: mir::Block, base: Reg, value: Reg) {
1555        let mov = op(names, "mov_mr_64");
1556        func.build(block, mov)
1557            .uses(value, GPR)
1558            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1559            .finish();
1560    }
1561
1562    /// The same store, of a place the program said to write exactly where it is written.
1563    fn insisted_store(
1564        func: &mut Func,
1565        names: &mut Interner,
1566        block: mir::Block,
1567        base: Reg,
1568        value: Reg,
1569    ) {
1570        let mov = op(names, "mov_mr_64");
1571        func.build(block, mov)
1572            .uses(value, GPR)
1573            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1574            .flags(Flags::VOLATILE)
1575            .finish();
1576    }
1577
1578    /// The other walk, with lists nothing is on.
1579    fn update(func: &mut Func, names: &mut Interner) -> usize {
1580        let mut addresses = Vec::new();
1581        let mut arguments = Vec::new();
1582        let mut dynamic = Vec::new();
1583        let mut pending =
1584            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1585        stores(func, &MACHINE, &FLAGS, names, &mut pending)
1586    }
1587
1588    /// The shape the second walk is for, which is what `*p += x` is.
1589    #[test]
1590    fn a_word_read_changed_and_written_back_becomes_one_instruction() {
1591        let (mut names, mut func, block) = empty();
1592        let base = func.new_vreg(GPR);
1593        let other = func.new_vreg(GPR);
1594        let word = load(&mut func, &mut names, block, base);
1595        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1596        store(&mut func, &mut names, block, base, sum);
1597
1598        assert_eq!(update(&mut func, &mut names), 1);
1599        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1600        let inst = func.insts(block).next().expect("the addition");
1601        let mem = func[inst].mem.expect("it writes memory");
1602        assert_eq!(func[mem].disp, 16, "the address came from the store");
1603        assert_eq!(func[mem].base, Some(1), "and names the operand behind the source");
1604        assert_eq!(func[func[inst].operands].len(), 2, "one source and the base of the address");
1605        assert_eq!(func[func[inst].operands][0].reg, other, "the source it kept");
1606        assert_eq!(func[func[inst].operands][1].reg, base, "the address");
1607    }
1608
1609    /// The same run with the load feeding the right source instead, which an addition does not
1610    /// mind. What `subq %rax, (%rcx)` computes is memory minus register, so the subtraction below
1611    /// is the one that has to care.
1612    #[test]
1613    fn a_word_read_into_the_right_source_of_an_addition_is_still_one_instruction() {
1614        let (mut names, mut func, block) = empty();
1615        let base = func.new_vreg(GPR);
1616        let other = func.new_vreg(GPR);
1617        let word = load(&mut func, &mut names, block, base);
1618        let sum = alu(&mut func, &mut names, block, "add_rr_64", other, word);
1619        store(&mut func, &mut names, block, base, sum);
1620
1621        assert_eq!(update(&mut func, &mut names), 1);
1622        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
1623        assert_eq!(func[func[func.insts(block).next().expect("it")].operands][0].reg, other);
1624    }
1625
1626    /// A subtraction with the memory on the left, which is `*p -= x` and is what the machine
1627    /// instruction computes.
1628    #[test]
1629    fn a_subtraction_taking_a_register_away_from_memory_becomes_one_instruction() {
1630        let (mut names, mut func, block) = empty();
1631        let base = func.new_vreg(GPR);
1632        let other = func.new_vreg(GPR);
1633        let word = load(&mut func, &mut names, block, base);
1634        let left = alu(&mut func, &mut names, block, "sub_rr_64", word, other);
1635        store(&mut func, &mut names, block, base, left);
1636
1637        assert_eq!(update(&mut func, &mut names), 1);
1638        assert_eq!(shape(&func, &names, block), ["x64.sub_mr_64"]);
1639    }
1640
1641    /// And the same subtraction the other way round, which is `*p = x - *p`. The machine
1642    /// instruction would compute the other answer, so the run stays three instructions.
1643    #[test]
1644    fn a_subtraction_taking_memory_away_from_a_register_stays_three_instructions() {
1645        let (mut names, mut func, block) = empty();
1646        let base = func.new_vreg(GPR);
1647        let other = func.new_vreg(GPR);
1648        let word = load(&mut func, &mut names, block, base);
1649        let left = alu(&mut func, &mut names, block, "sub_rr_64", other, word);
1650        store(&mut func, &mut names, block, base, left);
1651
1652        assert_eq!(update(&mut func, &mut names), 0);
1653        assert_eq!(
1654            shape(&func, &names, block),
1655            ["x64.mov_rm_64", "x64.sub_rr_64", "x64.mov_mr_64"]
1656        );
1657    }
1658
1659    /// A store to somewhere else. The answer is not going back where it came from, so what is left
1660    /// is a load and an arithmetic and a store of three different addresses.
1661    #[test]
1662    fn a_store_to_another_address_stays_three_instructions() {
1663        let (mut names, mut func, block) = empty();
1664        let base = func.new_vreg(GPR);
1665        let elsewhere = func.new_vreg(GPR);
1666        let other = func.new_vreg(GPR);
1667        let word = load(&mut func, &mut names, block, base);
1668        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1669        store(&mut func, &mut names, block, elsewhere, sum);
1670
1671        assert_eq!(update(&mut func, &mut names), 0);
1672    }
1673
1674    /// The same address at a different displacement, which is the near miss the comparison has to
1675    /// catch rather than the obvious one above.
1676    #[test]
1677    fn a_store_at_another_displacement_stays_three_instructions() {
1678        let (mut names, mut func, block) = empty();
1679        let base = func.new_vreg(GPR);
1680        let other = func.new_vreg(GPR);
1681        let word = load(&mut func, &mut names, block, base);
1682        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1683        let mov = op(&mut names, "mov_mr_64");
1684        func.build(block, mov)
1685            .uses(sum, GPR)
1686            .mem(Mem { disp: 24, ..Mem::at(Operand::read(base, GPR)) })
1687            .finish();
1688
1689        assert_eq!(update(&mut func, &mut names), 0);
1690    }
1691
1692    /// The word read again by something else. The load has to stay for the second reader, so the
1693    /// run is not a run.
1694    #[test]
1695    fn a_word_two_instructions_read_stays_three_instructions() {
1696        let (mut names, mut func, block) = empty();
1697        let base = func.new_vreg(GPR);
1698        let other = func.new_vreg(GPR);
1699        let word = load(&mut func, &mut names, block, base);
1700        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1701        alu(&mut func, &mut names, block, "xor_rr_64", word, other);
1702        store(&mut func, &mut names, block, base, sum);
1703
1704        assert_eq!(update(&mut func, &mut names), 0);
1705    }
1706
1707    /// The answer read by something else as well as by the store, which is `x = *p += 1` and
1708    /// leaves the answer wanted in a register the joined instruction never writes.
1709    #[test]
1710    fn an_answer_something_else_reads_stays_three_instructions() {
1711        let (mut names, mut func, block) = empty();
1712        let base = func.new_vreg(GPR);
1713        let other = func.new_vreg(GPR);
1714        let word = load(&mut func, &mut names, block, base);
1715        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1716        store(&mut func, &mut names, block, base, sum);
1717        alu(&mut func, &mut names, block, "xor_rr_64", sum, other);
1718
1719        assert_eq!(update(&mut func, &mut names), 0);
1720    }
1721
1722    /// Another access to memory in the middle. The read the run does moves down the block to where
1723    /// the write was, so it would be moving past this one.
1724    #[test]
1725    fn a_run_with_another_access_in_the_middle_stays_three_instructions() {
1726        let (mut names, mut func, block) = empty();
1727        let base = func.new_vreg(GPR);
1728        let other = func.new_vreg(GPR);
1729        let word = load(&mut func, &mut names, block, base);
1730        load(&mut func, &mut names, block, other);
1731        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1732        store(&mut func, &mut names, block, base, sum);
1733
1734        assert_eq!(update(&mut func, &mut names), 0);
1735    }
1736
1737    /// Something writing the address register in the middle. A physical register is the only one
1738    /// this can happen to before the allocator runs, and the frame is addressed through two.
1739    #[test]
1740    fn a_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
1741        let (mut names, mut func, block) = empty();
1742        let base = Reg::physical(rucc_target::x86_64::RSP);
1743        let other = func.new_vreg(GPR);
1744        let word = load(&mut func, &mut names, block, base);
1745        let sub = op(&mut names, "sub_ri_64");
1746        func.build(block, sub)
1747            .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
1748            .uses(base, GPR)
1749            .imm(32)
1750            .finish();
1751        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1752        store(&mut func, &mut names, block, base, sum);
1753
1754        assert_eq!(update(&mut func, &mut names), 0);
1755    }
1756
1757    /// Two locals whose displacements are both nothing so far. They are the same registers and the
1758    /// same number here and are two different places, and what says so is the list the frame layout
1759    /// has still to write an offset into.
1760    #[test]
1761    fn two_locals_the_layout_has_not_placed_yet_are_not_the_same_place() {
1762        let (mut names, mut func, block) = empty();
1763        let base = Reg::physical(rucc_target::x86_64::RSP);
1764        let other = func.new_vreg(GPR);
1765        let mov = op(&mut names, "mov_rm_64");
1766        let word = func.new_vreg(GPR);
1767        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1768        let read = func.insts(block).next().expect("the load");
1769        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1770        let put = op(&mut names, "mov_mr_64");
1771        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1772        let written = func.insts(block).nth(2).expect("the store");
1773
1774        let mut addresses = vec![(read, 3usize), (written, 4usize)];
1775        let mut arguments = Vec::new();
1776        let mut dynamic = Vec::new();
1777        let mut pending =
1778            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1779        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
1780    }
1781
1782    /// The one local, which is the same place twice and folds. The entry the load was waiting on
1783    /// comes off the list, because the store is already waiting on the same one and adding the
1784    /// frame's offset twice would put the local at twice its distance.
1785    #[test]
1786    fn the_frame_entry_of_a_load_that_goes_comes_off_the_list() {
1787        let (mut names, mut func, block) = empty();
1788        let base = Reg::physical(rucc_target::x86_64::RSP);
1789        let other = func.new_vreg(GPR);
1790        let mov = op(&mut names, "mov_rm_64");
1791        let word = func.new_vreg(GPR);
1792        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1793        let read = func.insts(block).next().expect("the load");
1794        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1795        let put = op(&mut names, "mov_mr_64");
1796        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
1797        let written = func.insts(block).nth(2).expect("the store");
1798
1799        let mut addresses = vec![(read, 3usize), (written, 3usize)];
1800        let mut arguments = Vec::new();
1801        let mut dynamic = Vec::new();
1802        let mut pending =
1803            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
1804        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
1805
1806        let inst = func.insts(block).next().expect("the addition");
1807        assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
1808    }
1809
1810    /// A run of the wrong width, which is a load of four bytes under an addition of eight.
1811    #[test]
1812    fn a_run_whose_widths_disagree_stays_three_instructions() {
1813        let (mut names, mut func, block) = empty();
1814        let base = func.new_vreg(GPR);
1815        let other = func.new_vreg(GPR);
1816        let into = func.new_vreg(GPR);
1817        let narrow = op(&mut names, "mov_rm_32");
1818        func.build(block, narrow)
1819            .def(into, GPR)
1820            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1821            .finish();
1822        let sum = alu(&mut func, &mut names, block, "add_rr_64", into, other);
1823        store(&mut func, &mut names, block, base, sum);
1824
1825        assert_eq!(update(&mut func, &mut names), 0);
1826    }
1827
1828    /// The shape tamnd/rucc#1424 was, which is a run with the carry reader in the middle of it.
1829    /// Joining it would put the addition below the `adc` and the carry the `adc` takes would be
1830    /// whatever was there before the addition ran.
1831    #[test]
1832    fn a_run_with_something_reading_the_condition_state_in_the_middle_stays_three_instructions() {
1833        let (mut names, mut func, block) = empty();
1834        let base = func.new_vreg(GPR);
1835        let other = func.new_vreg(GPR);
1836        let carry = func.new_vreg(GPR);
1837        let word = load(&mut func, &mut names, block, base);
1838        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1839        alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
1840        store(&mut func, &mut names, block, base, sum);
1841
1842        assert_eq!(update(&mut func, &mut names), 0);
1843    }
1844
1845    /// The other half of the same question, which is something in the middle that writes the state
1846    /// rather than reads it. Joining the run would make the addition the last writer before the
1847    /// store instead of the subtraction, so whatever reads the state further down would read a
1848    /// different answer.
1849    #[test]
1850    fn a_run_with_something_writing_the_condition_state_in_the_middle_stays_three_instructions() {
1851        let (mut names, mut func, block) = empty();
1852        let base = func.new_vreg(GPR);
1853        let other = func.new_vreg(GPR);
1854        let left = func.new_vreg(GPR);
1855        let right = func.new_vreg(GPR);
1856        let word = load(&mut func, &mut names, block, base);
1857        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1858        alu(&mut func, &mut names, block, "sub_rr_64", left, right);
1859        store(&mut func, &mut names, block, base, sum);
1860
1861        assert_eq!(update(&mut func, &mut names), 0);
1862    }
1863
1864    /// And the same run with an instruction in the middle that has no opinion about the state,
1865    /// which is what keeps the two above from being a rule against anything in the middle at all.
1866    #[test]
1867    fn a_run_with_a_move_in_the_middle_is_still_one_instruction() {
1868        let (mut names, mut func, block) = empty();
1869        let base = func.new_vreg(GPR);
1870        let other = func.new_vreg(GPR);
1871        let from = func.new_vreg(GPR);
1872        let into = func.new_vreg(GPR);
1873        let word = load(&mut func, &mut names, block, base);
1874        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
1875        let copy = op(&mut names, "mov_rr_64");
1876        func.build(block, copy).def(into, GPR).uses(from, GPR).finish();
1877        store(&mut func, &mut names, block, base, sum);
1878
1879        assert_eq!(update(&mut func, &mut names), 1);
1880        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.add_mr_64"]);
1881    }
1882
1883    /// Every row of the table names four instructions this target has, all of one width.
1884    #[test]
1885    fn every_row_of_the_update_table_is_four_instructions_this_target_has() {
1886        for update in UPDATES {
1887            for name in [update.from, update.into, update.load, update.store] {
1888                assert!(MACHINE.has(name), "{name} is not an instruction");
1889            }
1890            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
1891            assert_eq!(width(update.from), width(update.into), "{} changes width", update.from);
1892            assert_eq!(
1893                width(update.from),
1894                width(update.load),
1895                "{} loads another width",
1896                update.from
1897            );
1898            assert_eq!(
1899                width(update.from),
1900                width(update.store),
1901                "{} stores another width",
1902                update.from
1903            );
1904            assert!((MACHINE.takes_mem)(update.into), "{} reaches no memory", update.into);
1905            assert!(!(MACHINE.takes_mem)(update.from), "{} already reaches memory", update.from);
1906        }
1907    }
1908
1909    /// One row per arithmetic instruction this machine can do in place, for the reason the count
1910    /// over the fold table is there.
1911    #[test]
1912    fn the_update_table_covers_the_arithmetic_this_target_can_do_in_place() {
1913        assert_eq!(UPDATES.len(), 20, "five operations at four widths, and no multiply");
1914        let commuting = UPDATES.iter().filter(|update| update.commutes).count();
1915        assert_eq!(commuting, 16, "everything but the four subtractions");
1916    }
1917
1918    /// Two-address arithmetic of that name against a constant.
1919    fn alu_imm(
1920        func: &mut Func,
1921        names: &mut Interner,
1922        block: mir::Block,
1923        name: &str,
1924        source: Reg,
1925        value: i64,
1926    ) -> Reg {
1927        let answer = func.new_vreg(GPR);
1928        let opcode = op(names, name);
1929        func.build(block, opcode)
1930            .operand(Operand::write(answer, GPR).with(Constraint::Reuse(1)))
1931            .uses(source, GPR)
1932            .imm(value)
1933            .finish();
1934        answer
1935    }
1936
1937    /// The shape the constant run is for, which is what `*p += 1` is.
1938    #[test]
1939    fn a_word_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1940        let (mut names, mut func, block) = empty();
1941        let base = func.new_vreg(GPR);
1942        let word = load(&mut func, &mut names, block, base);
1943        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
1944        store(&mut func, &mut names, block, base, sum);
1945
1946        assert_eq!(update(&mut func, &mut names), 1);
1947        assert_eq!(shape(&func, &names, block), ["x64.add_mi_64"]);
1948        let inst = func.insts(block).next().expect("the addition");
1949        let mem = func[inst].mem.expect("it writes memory");
1950        assert_eq!(func[mem].disp, 16, "the address came from the store");
1951        assert_eq!(func[mem].base, Some(0), "which is now the first operand and not the second");
1952        assert_eq!(func[func[inst].operands].len(), 1, "the base of the address and nothing else");
1953        assert_eq!(func[func[inst].operands][0].reg, base, "the address");
1954        assert_eq!(func[func[inst].imm.expect("the constant")].0, 1);
1955    }
1956
1957    /// The subtraction, which needs no arrangement chosen for it. A constant cannot be the left
1958    /// source, so the run that exists is the one the instruction computes.
1959    #[test]
1960    fn a_constant_taken_away_from_a_place_becomes_one_instruction() {
1961        let (mut names, mut func, block) = empty();
1962        let base = func.new_vreg(GPR);
1963        let word = load(&mut func, &mut names, block, base);
1964        let left = alu_imm(&mut func, &mut names, block, "sub_ri_64", word, 7);
1965        store(&mut func, &mut names, block, base, left);
1966
1967        assert_eq!(update(&mut func, &mut names), 1);
1968        assert_eq!(shape(&func, &names, block), ["x64.sub_mi_64"]);
1969        assert_eq!(func[func[func.insts(block).next().expect("it")].imm.expect("it")].0, 7);
1970    }
1971
1972    /// The narrow one, so that a width that is carried through wrong is a test that fails rather
1973    /// than a program that is wrong.
1974    #[test]
1975    fn a_byte_read_changed_by_a_constant_and_written_back_becomes_one_instruction() {
1976        let (mut names, mut func, block) = empty();
1977        let base = func.new_vreg(GPR);
1978        let word = func.new_vreg(GPR);
1979        let mov = op(&mut names, "mov_rm_8");
1980        func.build(block, mov)
1981            .def(word, GPR)
1982            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1983            .finish();
1984        let sum = alu_imm(&mut func, &mut names, block, "or_ri_8", word, 4);
1985        let put = op(&mut names, "mov_mr_8");
1986        func.build(block, put)
1987            .uses(sum, GPR)
1988            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
1989            .finish();
1990
1991        assert_eq!(update(&mut func, &mut names), 1);
1992        assert_eq!(shape(&func, &names, block), ["x64.or_mi_8"]);
1993    }
1994
1995    /// The word read again by something else, which is the first of the four conditions and is
1996    /// asked here the way it is asked of the register run.
1997    #[test]
1998    fn a_word_a_constant_changes_and_something_else_reads_stays_three_instructions() {
1999        let (mut names, mut func, block) = empty();
2000        let base = func.new_vreg(GPR);
2001        let other = func.new_vreg(GPR);
2002        let word = load(&mut func, &mut names, block, base);
2003        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2004        alu(&mut func, &mut names, block, "xor_rr_64", word, other);
2005        store(&mut func, &mut names, block, base, sum);
2006
2007        assert_eq!(update(&mut func, &mut names), 0);
2008    }
2009
2010    /// Something else in the middle that touches memory, which the one instruction left would be
2011    /// passing if the run were joined.
2012    #[test]
2013    fn a_constant_run_with_another_access_in_the_middle_stays_three_instructions() {
2014        let (mut names, mut func, block) = empty();
2015        let base = func.new_vreg(GPR);
2016        let elsewhere = func.new_vreg(GPR);
2017        let word = load(&mut func, &mut names, block, base);
2018        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2019        load(&mut func, &mut names, block, elsewhere);
2020        store(&mut func, &mut names, block, base, sum);
2021
2022        assert_eq!(update(&mut func, &mut names), 0);
2023    }
2024
2025    /// The address register written between the load and the store, which would leave the one
2026    /// instruction naming a different place from the one the run read.
2027    #[test]
2028    fn a_constant_run_whose_address_register_is_written_in_the_middle_stays_three_instructions() {
2029        let (mut names, mut func, block) = empty();
2030        let base = Reg::physical(rucc_target::x86_64::RAX);
2031        let word = load(&mut func, &mut names, block, base);
2032        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2033        let mov = op(&mut names, "mov_ri_64");
2034        func.build(block, mov).def(base, GPR).imm(0).finish();
2035        store(&mut func, &mut names, block, base, sum);
2036
2037        assert_eq!(update(&mut func, &mut names), 0);
2038    }
2039
2040    /// A store somewhere else, which is the run that is not a run.
2041    #[test]
2042    fn a_constant_written_to_another_address_stays_three_instructions() {
2043        let (mut names, mut func, block) = empty();
2044        let base = func.new_vreg(GPR);
2045        let elsewhere = func.new_vreg(GPR);
2046        let word = load(&mut func, &mut names, block, base);
2047        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2048        store(&mut func, &mut names, block, elsewhere, sum);
2049
2050        assert_eq!(update(&mut func, &mut names), 0);
2051    }
2052
2053    /// A run of the wrong width, which is a load of four bytes under an addition of eight.
2054    #[test]
2055    fn a_constant_run_whose_widths_disagree_stays_three_instructions() {
2056        let (mut names, mut func, block) = empty();
2057        let base = func.new_vreg(GPR);
2058        let into = func.new_vreg(GPR);
2059        let narrow = op(&mut names, "mov_rm_32");
2060        func.build(block, narrow)
2061            .def(into, GPR)
2062            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2063            .finish();
2064        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", into, 1);
2065        store(&mut func, &mut names, block, base, sum);
2066
2067        assert_eq!(update(&mut func, &mut names), 0);
2068    }
2069
2070    /// The multiply, which has a two-address form against a constant and no form that leaves the
2071    /// product in memory, so the run stays three instructions.
2072    #[test]
2073    fn a_place_multiplied_by_a_constant_stays_three_instructions() {
2074        let (mut names, mut func, block) = empty();
2075        let base = func.new_vreg(GPR);
2076        let word = load(&mut func, &mut names, block, base);
2077        let product = alu_imm(&mut func, &mut names, block, "imul_ri_64", word, 3);
2078        store(&mut func, &mut names, block, base, product);
2079
2080        assert_eq!(update(&mut func, &mut names), 0);
2081    }
2082
2083    /// The constant run passes the condition state the same way the register run does, because the
2084    /// arithmetic moves down to the store here too.
2085    #[test]
2086    fn a_constant_run_with_a_carry_reader_in_the_middle_stays_three_instructions() {
2087        let (mut names, mut func, block) = empty();
2088        let base = func.new_vreg(GPR);
2089        let carry = func.new_vreg(GPR);
2090        let word = load(&mut func, &mut names, block, base);
2091        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2092        alu(&mut func, &mut names, block, "adc_rr_64", carry, carry);
2093        store(&mut func, &mut names, block, base, sum);
2094
2095        assert_eq!(update(&mut func, &mut names), 0);
2096    }
2097
2098    /// The local, which is the same place twice and folds, and whose frame entry comes off the
2099    /// list for the reason the register run's does.
2100    #[test]
2101    fn the_frame_entry_of_a_load_a_constant_run_takes_comes_off_the_list() {
2102        let (mut names, mut func, block) = empty();
2103        let base = Reg::physical(rucc_target::x86_64::RSP);
2104        let mov = op(&mut names, "mov_rm_64");
2105        let word = func.new_vreg(GPR);
2106        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2107        let read = func.insts(block).next().expect("the load");
2108        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2109        let put = op(&mut names, "mov_mr_64");
2110        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2111        let written = func.insts(block).nth(2).expect("the store");
2112
2113        let mut addresses = vec![(read, 3usize), (written, 3usize)];
2114        let mut arguments = Vec::new();
2115        let mut dynamic = Vec::new();
2116        let mut pending =
2117            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2118        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 1);
2119
2120        let inst = func.insts(block).next().expect("the addition");
2121        assert_eq!(addresses, [(inst, 3usize)], "one entry, on the instruction that is left");
2122    }
2123
2124    /// Two locals the layout has not placed yet, which are the same addressing mode and not the
2125    /// same place, the way they are for the register run.
2126    #[test]
2127    fn two_locals_a_constant_run_would_join_are_not_the_same_place() {
2128        let (mut names, mut func, block) = empty();
2129        let base = Reg::physical(rucc_target::x86_64::RSP);
2130        let mov = op(&mut names, "mov_rm_64");
2131        let word = func.new_vreg(GPR);
2132        func.build(block, mov).def(word, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2133        let read = func.insts(block).next().expect("the load");
2134        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2135        let put = op(&mut names, "mov_mr_64");
2136        func.build(block, put).uses(sum, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2137        let written = func.insts(block).nth(2).expect("the store");
2138
2139        let mut addresses = vec![(read, 3usize), (written, 4usize)];
2140        let mut arguments = Vec::new();
2141        let mut dynamic = Vec::new();
2142        let mut pending =
2143            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2144        assert_eq!(stores(&mut func, &MACHINE, &FLAGS, &mut names, &mut pending), 0);
2145    }
2146
2147    /// Every row of the constant table names four instructions this target has, all of one width.
2148    #[test]
2149    fn every_row_of_the_bump_table_is_four_instructions_this_target_has() {
2150        for bump in BUMPS {
2151            for name in [bump.from, bump.into, bump.load, bump.store] {
2152                assert!(MACHINE.has(name), "{name} is not an instruction");
2153            }
2154            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2155            assert_eq!(width(bump.from), width(bump.into), "{} changes width", bump.from);
2156            assert_eq!(width(bump.from), width(bump.load), "{} loads another width", bump.from);
2157            assert_eq!(width(bump.from), width(bump.store), "{} stores another width", bump.from);
2158            assert!((MACHINE.takes_mem)(bump.into), "{} reaches no memory", bump.into);
2159            assert!(!(MACHINE.takes_mem)(bump.from), "{} already reaches memory", bump.from);
2160            assert!((MACHINE.takes_imm)(bump.into), "{} carries no constant", bump.into);
2161        }
2162    }
2163
2164    /// One row per arithmetic instruction this machine can do in place against a constant, which is
2165    /// the same five operations at the same four widths the register table has.
2166    #[test]
2167    fn the_bump_table_covers_the_arithmetic_this_target_can_do_in_place_against_a_constant() {
2168        assert_eq!(BUMPS.len(), 20, "five operations at four widths, and no multiply");
2169        let register: Vec<&str> = UPDATES.iter().map(|update| update.from).collect();
2170        for bump in BUMPS {
2171            let same = bump.from.replace("_ri_", "_rr_");
2172            assert!(register.contains(&same.as_str()), "{} has no register row", bump.from);
2173        }
2174    }
2175
2176    /// No instruction is in both tables, which is what lets the two walks be tried one after the
2177    /// other without either having to know what the other took.
2178    #[test]
2179    fn nothing_is_both_a_register_run_and_a_constant_run() {
2180        for bump in BUMPS {
2181            assert!(
2182                !UPDATES.iter().any(|update| update.from == bump.from),
2183                "{} starts both kinds of run",
2184                bump.from
2185            );
2186        }
2187    }
2188
2189    /// The shape the whole pass is for.
2190    #[test]
2191    fn a_load_read_once_by_an_addition_becomes_its_memory_operand() {
2192        let (mut names, mut func, block) = empty();
2193        let base = func.new_vreg(GPR);
2194        let other = func.new_vreg(GPR);
2195        let word = load(&mut func, &mut names, block, base);
2196        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2197
2198        assert_eq!(combine(&mut func, &mut names), 1);
2199        assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2200        let inst = func.insts(block).next().expect("the addition");
2201        let mem = func[inst].mem.expect("the addition reads memory now");
2202        assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2203        assert_eq!(func[mem].base, Some(2), "and names the operand behind the source it kept");
2204        assert_eq!(func[func[inst].operands][1].reg, other, "the source it kept");
2205        assert_eq!(func[func[inst].operands][2].reg, base, "the address it took on");
2206    }
2207
2208    /// The same load feeding the source the answer is tied to. The two sources are swapped, which
2209    /// an addition does not mind and is what lets this fold at all.
2210    #[test]
2211    fn a_load_feeding_the_first_source_of_an_addition_is_swapped_and_folded() {
2212        let (mut names, mut func, block) = empty();
2213        let base = func.new_vreg(GPR);
2214        let other = func.new_vreg(GPR);
2215        let word = load(&mut func, &mut names, block, base);
2216        alu(&mut func, &mut names, block, "add_rr_64", word, other);
2217
2218        assert_eq!(combine(&mut func, &mut names), 1);
2219        assert_eq!(shape(&func, &names, block), ["x64.add_rm_64"]);
2220        let inst = func.insts(block).next().expect("the addition");
2221        assert_eq!(func[func[inst].operands][1].reg, other);
2222    }
2223
2224    /// A subtraction with the load on the left, which is the one place the swap above would change
2225    /// the answer.
2226    #[test]
2227    fn a_load_feeding_the_left_of_a_subtraction_stays_a_load() {
2228        let (mut names, mut func, block) = empty();
2229        let base = func.new_vreg(GPR);
2230        let other = func.new_vreg(GPR);
2231        let word = load(&mut func, &mut names, block, base);
2232        alu(&mut func, &mut names, block, "sub_rr_64", word, other);
2233
2234        assert_eq!(combine(&mut func, &mut names), 0);
2235        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.sub_rr_64"]);
2236    }
2237
2238    /// And the same subtraction the other way round, which is the one that folds.
2239    #[test]
2240    fn a_load_feeding_the_right_of_a_subtraction_folds() {
2241        let (mut names, mut func, block) = empty();
2242        let base = func.new_vreg(GPR);
2243        let other = func.new_vreg(GPR);
2244        let word = load(&mut func, &mut names, block, base);
2245        alu(&mut func, &mut names, block, "sub_rr_64", other, word);
2246
2247        assert_eq!(combine(&mut func, &mut names), 1);
2248        assert_eq!(shape(&func, &names, block), ["x64.sub_rm_64"]);
2249    }
2250
2251    /// Two readers. The load has to stay where it is for the second of them, so putting it into the
2252    /// first buys nothing and reads the memory twice.
2253    #[test]
2254    fn a_load_two_instructions_read_stays_a_load() {
2255        let (mut names, mut func, block) = empty();
2256        let base = func.new_vreg(GPR);
2257        let other = func.new_vreg(GPR);
2258        let word = load(&mut func, &mut names, block, base);
2259        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2260        alu(&mut func, &mut names, block, "xor_rr_64", other, word);
2261
2262        assert_eq!(combine(&mut func, &mut names), 0);
2263        assert_eq!(
2264            shape(&func, &names, block),
2265            ["x64.mov_rm_64", "x64.add_rr_64", "x64.xor_rr_64"]
2266        );
2267    }
2268
2269    /// A store between the two. Whether it writes what the load reads is a question about two
2270    /// addresses, and the answer to not being able to tell is to leave the load where it is.
2271    #[test]
2272    fn a_load_with_a_store_between_it_and_its_reader_stays_a_load() {
2273        let (mut names, mut func, block) = empty();
2274        let base = func.new_vreg(GPR);
2275        let other = func.new_vreg(GPR);
2276        let word = load(&mut func, &mut names, block, base);
2277        let store = op(&mut names, "mov_mr_64");
2278        func.build(block, store).uses(other, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2279        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2280
2281        assert_eq!(combine(&mut func, &mut names), 0);
2282        assert_eq!(
2283            shape(&func, &names, block),
2284            ["x64.mov_rm_64", "x64.mov_mr_64", "x64.add_rr_64"]
2285        );
2286    }
2287
2288    /// Another load between the two, which writes nothing and is still not passed.
2289    ///
2290    /// This is the one that would be wrong if the walk asked only about writes. Where both reads
2291    /// are `volatile` the program said which of them happens first, and nothing here can tell that
2292    /// program from the one that did not say it, so neither may be reordered.
2293    #[test]
2294    fn a_load_with_another_load_between_it_and_its_reader_stays_a_load() {
2295        let (mut names, mut func, block) = empty();
2296        let base = func.new_vreg(GPR);
2297        let other = func.new_vreg(GPR);
2298        let word = load(&mut func, &mut names, block, base);
2299        load(&mut func, &mut names, block, other);
2300        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2301
2302        assert_eq!(combine(&mut func, &mut names), 0);
2303        assert_eq!(
2304            shape(&func, &names, block),
2305            ["x64.mov_rm_64", "x64.mov_rm_64", "x64.add_rr_64"]
2306        );
2307    }
2308
2309    /// The second of two loads, read by arithmetic that reads the first as well. Nothing moves past
2310    /// anything, which is what makes this one the shape the pass is allowed to take.
2311    #[test]
2312    fn the_later_of_two_loads_is_the_one_that_folds() {
2313        let (mut names, mut func, block) = empty();
2314        let base = func.new_vreg(GPR);
2315        let other = func.new_vreg(GPR);
2316        let first = load(&mut func, &mut names, block, base);
2317        let second = load(&mut func, &mut names, block, other);
2318        alu(&mut func, &mut names, block, "add_rr_64", first, second);
2319
2320        assert_eq!(combine(&mut func, &mut names), 1);
2321        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rm_64"]);
2322        let addition = func.insts(block).nth(1).expect("the addition");
2323        assert_eq!(func[func[addition].operands][1].reg, first, "the earlier load is still read");
2324        assert_eq!(func[func[addition].operands][2].reg, other, "and the later one is the address");
2325    }
2326
2327    /// A call between the two. What a call does to memory is not in the instruction, so it is the
2328    /// same answer as the store and reached without asking about the address.
2329    #[test]
2330    fn a_load_with_a_call_between_it_and_its_reader_stays_a_load() {
2331        let (mut names, mut func, block) = empty();
2332        let base = func.new_vreg(GPR);
2333        let other = func.new_vreg(GPR);
2334        let word = load(&mut func, &mut names, block, base);
2335        let call = op(&mut names, "call");
2336        func.build(block, call).finish();
2337        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2338
2339        assert_eq!(combine(&mut func, &mut names), 0);
2340        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.call", "x64.add_rr_64"]);
2341    }
2342
2343    /// Something writing the register the address reads. A physical register is the only one this
2344    /// can happen to while the IR is in SSA form, and the frame is addressed through two of them.
2345    #[test]
2346    fn a_load_whose_address_register_is_written_between_the_two_stays_a_load() {
2347        let (mut names, mut func, block) = empty();
2348        let base = Reg::physical(rucc_target::x86_64::RSP);
2349        let other = func.new_vreg(GPR);
2350        let word = load(&mut func, &mut names, block, base);
2351        let sub = op(&mut names, "sub_ri_64");
2352        func.build(block, sub)
2353            .operand(Operand::write(base, GPR).with(Constraint::Reuse(1)))
2354            .uses(base, GPR)
2355            .imm(32)
2356            .finish();
2357        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2358
2359        assert_eq!(combine(&mut func, &mut names), 0);
2360    }
2361
2362    /// A load of four bytes under an addition of eight. The register held what the load put in it
2363    /// and a memory operand holds what is at the address, which is a different number of bytes.
2364    #[test]
2365    fn a_load_of_the_wrong_width_stays_a_load() {
2366        let (mut names, mut func, block) = empty();
2367        let base = func.new_vreg(GPR);
2368        let other = func.new_vreg(GPR);
2369        let into = func.new_vreg(GPR);
2370        let narrow = op(&mut names, "mov_rm_32");
2371        func.build(block, narrow).def(into, GPR).mem(Mem::at(Operand::read(base, GPR))).finish();
2372        alu(&mut func, &mut names, block, "add_rr_64", other, into);
2373
2374        assert_eq!(combine(&mut func, &mut names), 0);
2375        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_32", "x64.add_rr_64"]);
2376    }
2377
2378    /// A load whose value leaves the block on an edge. It is read by nothing in any operand vector
2379    /// and is read all the same, which is the count that is easy to get wrong.
2380    #[test]
2381    fn a_load_whose_value_an_edge_carries_stays_a_load() {
2382        let (mut names, mut func, block) = empty();
2383        let next = func.create_block();
2384        let base = func.new_vreg(GPR);
2385        let other = func.new_vreg(GPR);
2386        let word = load(&mut func, &mut names, block, base);
2387        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2388        let arrived = func.new_vreg(GPR);
2389        func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
2390        *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![word])];
2391
2392        assert_eq!(combine(&mut func, &mut names), 0);
2393        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2394    }
2395
2396    /// A reader in another block, which is the whole of what block local means here.
2397    #[test]
2398    fn a_reader_in_another_block_stays_where_it_is() {
2399        let (mut names, mut func, block) = empty();
2400        let next = func.create_block();
2401        let base = func.new_vreg(GPR);
2402        let other = func.new_vreg(GPR);
2403        let word = load(&mut func, &mut names, block, base);
2404        alu(&mut func, &mut names, next, "add_rr_64", other, word);
2405
2406        assert_eq!(combine(&mut func, &mut names), 0);
2407        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
2408        assert_eq!(shape(&func, &names, next), ["x64.add_rr_64"]);
2409    }
2410
2411    /// A reader further down the block than the window reaches.
2412    #[test]
2413    fn a_reader_past_the_window_stays_where_it_is() {
2414        let (mut names, mut func, block) = empty();
2415        let base = func.new_vreg(GPR);
2416        let other = func.new_vreg(GPR);
2417        let word = load(&mut func, &mut names, block, base);
2418        let nop = op(&mut names, "nop");
2419        for _ in 0..WINDOW {
2420            func.build(block, nop).finish();
2421        }
2422        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2423
2424        assert_eq!(combine(&mut func, &mut names), 0);
2425    }
2426
2427    /// And one instruction closer, which is the last place it still folds.
2428    #[test]
2429    fn a_reader_at_the_edge_of_the_window_folds() {
2430        let (mut names, mut func, block) = empty();
2431        let base = func.new_vreg(GPR);
2432        let other = func.new_vreg(GPR);
2433        let word = load(&mut func, &mut names, block, base);
2434        let nop = op(&mut names, "nop");
2435        for _ in 0..WINDOW - 1 {
2436            func.build(block, nop).finish();
2437        }
2438        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2439
2440        assert_eq!(combine(&mut func, &mut names), 1);
2441    }
2442
2443    /// The entry a frame layout is waiting on moves with the load. Without this the displacement
2444    /// of a local would be written into an instruction that has gone.
2445    #[test]
2446    fn the_frame_entry_of_a_load_that_moves_goes_with_it() {
2447        let (mut names, mut func, block) = empty();
2448        let base = Reg::physical(rucc_target::x86_64::RSP);
2449        let other = func.new_vreg(GPR);
2450        let word = load(&mut func, &mut names, block, base);
2451        let reader = func.insts(block).nth(1);
2452        assert!(reader.is_none(), "the block holds the load alone so far");
2453        alu(&mut func, &mut names, block, "add_rr_64", other, word);
2454        let held = func.insts(block).next().expect("the load");
2455
2456        let mut addresses = vec![(held, 3usize)];
2457        let mut arguments = Vec::new();
2458        let mut dynamic = Vec::new();
2459        let mut pending =
2460            Pending { addresses: &mut addresses, arguments: &mut arguments, dynamic: &mut dynamic };
2461        assert_eq!(loads(&mut func, &MACHINE, &mut names, &mut pending), 1);
2462
2463        let inst = func.insts(block).next().expect("the addition");
2464        assert_eq!(addresses, [(inst, 3usize)], "the entry names the instruction that took it");
2465    }
2466
2467    /// A comparison whose right hand side came out of a load, which is `if (x < *p)`. The load is
2468    /// the side the instruction reads out of memory already, so the condition is the one that was
2469    /// written and only the opcode's shape changes.
2470    #[test]
2471    fn a_comparison_against_a_word_that_was_just_loaded_becomes_one_instruction() {
2472        let (mut names, mut func, block) = empty();
2473        let base = func.new_vreg(GPR);
2474        let other = func.new_vreg(GPR);
2475        let word = load(&mut func, &mut names, block, base);
2476        compare(&mut func, &mut names, block, "cmp_set_l_64", other, word);
2477
2478        assert_eq!(combine(&mut func, &mut names), 1);
2479        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_rm_64"]);
2480        let inst = func.insts(block).next().expect("the comparison");
2481        let mem = func[inst].mem.expect("it reads memory");
2482        assert_eq!(func[mem].disp, 16, "the address came from the load");
2483        assert_eq!(func[mem].base, Some(2), "and names the operand behind the byte and the source");
2484        assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2485        assert_eq!(func[func[inst].operands][2].reg, base, "the address");
2486    }
2487
2488    /// The same comparison the other way round, which is `if (*p < x)`. The machine reads the
2489    /// right hand side out of memory and nothing else, so what comes out is the question asked
2490    /// backwards, and less than on the left is greater than on the right.
2491    #[test]
2492    fn a_comparison_whose_left_hand_side_was_just_loaded_turns_the_condition_over() {
2493        let (mut names, mut func, block) = empty();
2494        let base = func.new_vreg(GPR);
2495        let other = func.new_vreg(GPR);
2496        let word = load(&mut func, &mut names, block, base);
2497        compare(&mut func, &mut names, block, "cmp_set_l_64", word, other);
2498
2499        assert_eq!(combine(&mut func, &mut names), 1);
2500        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_g_rm_64"]);
2501        let inst = func.insts(block).next().expect("the comparison");
2502        assert_eq!(func[func[inst].operands][1].reg, other, "the side it kept");
2503    }
2504
2505    /// Equality on the left, which is the case the turning over has to leave alone. Two values are
2506    /// equal in whichever order they are read, so the row for it names itself on both sides and a
2507    /// table that had reached for the opposite condition would have written inequality here.
2508    #[test]
2509    fn an_equality_folded_on_either_side_is_the_same_comparison() {
2510        for (first, second) in [(true, false), (false, true)] {
2511            let (mut names, mut func, block) = empty();
2512            let base = func.new_vreg(GPR);
2513            let other = func.new_vreg(GPR);
2514            let word = load(&mut func, &mut names, block, base);
2515            let left = if first { word } else { other };
2516            let right = if second { word } else { other };
2517            compare(&mut func, &mut names, block, "cmp_set_e_64", left, right);
2518
2519            assert_eq!(combine(&mut func, &mut names), 1);
2520            assert_eq!(shape(&func, &names, block), ["x64.cmp_set_e_rm_64"]);
2521        }
2522    }
2523
2524    /// A comparison against a constant, which is `if (*p < 7)`. There is one source rather than
2525    /// two, so the side the load filled is the only side there is and the condition stays as it
2526    /// was written. What is left in front of the address is the byte on its own, which puts the
2527    /// base one position earlier than the comparison of two registers leaves it.
2528    #[test]
2529    fn a_comparison_against_a_constant_takes_the_load_on_as_its_memory_operand() {
2530        let (mut names, mut func, block) = empty();
2531        let base = func.new_vreg(GPR);
2532        let byte = func.new_vreg(GPR);
2533        let word = load(&mut func, &mut names, block, base);
2534        let opcode = op(&mut names, "cmp_set_l_ri_64");
2535        func.build(block, opcode).def(byte, GPR).uses(word, GPR).imm(7).finish();
2536
2537        assert_eq!(combine(&mut func, &mut names), 1);
2538        assert_eq!(shape(&func, &names, block), ["x64.cmp_set_l_mi_64"]);
2539        let inst = func.insts(block).next().expect("the comparison");
2540        let mem = func[inst].mem.expect("it reads memory now");
2541        assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2542        assert_eq!(func[mem].base, Some(1), "and names the operand behind the byte");
2543        assert_eq!(func[func[inst].operands][0].reg, byte, "the byte it sets");
2544        assert_eq!(func[func[inst].operands][1].reg, base, "the address it took on");
2545        let imm = func[inst].imm.expect("the constant is still on it");
2546        assert_eq!(func[imm].0, 7, "and is the one that was written");
2547    }
2548
2549    /// A load that nothing but a widening reads becomes the load that widens, at every width the
2550    /// machine has one for, and the address and the register written come with it.
2551    #[test]
2552    fn a_load_only_a_widening_reads_becomes_a_load_that_widens() {
2553        for row in WIDENINGS {
2554            let (mut names, mut func, block) = empty();
2555            let base = func.new_vreg(GPR);
2556            let narrow = func.new_vreg(GPR);
2557            let wide = func.new_vreg(GPR);
2558            let read = op(&mut names, row.load);
2559            func.build(block, read)
2560                .def(narrow, GPR)
2561                .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2562                .finish();
2563            let widen = op(&mut names, row.from);
2564            func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2565
2566            assert_eq!(combine(&mut func, &mut names), 1, "{} took no load", row.from);
2567            assert_eq!(shape(&func, &names, block), [format!("x64.{}", row.into)]);
2568            let inst = func.insts(block).next().expect("the widening");
2569            assert_eq!(func[func[inst].operands][0].reg, wide, "{} writes elsewhere", row.from);
2570            assert_eq!(func[func[inst].operands][1].reg, base, "{} lost the address", row.from);
2571            let mem = func[inst].mem.expect("it reads memory now");
2572            assert_eq!(func[mem].disp, 16, "the load's displacement came with it");
2573            assert_eq!(func[mem].base, Some(1), "and names the operand behind the answer");
2574        }
2575    }
2576
2577    /// A load that something besides the widening reads stays a load, since the value has to be
2578    /// in a register for the other reader anyway.
2579    #[test]
2580    fn a_load_read_by_a_widening_and_something_else_stays_where_it_is() {
2581        let (mut names, mut func, block) = empty();
2582        let base = func.new_vreg(GPR);
2583        let other = func.new_vreg(GPR);
2584        let narrow = func.new_vreg(GPR);
2585        let wide = func.new_vreg(GPR);
2586        let read = op(&mut names, "mov_rm_32");
2587        func.build(block, read)
2588            .def(narrow, GPR)
2589            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2590            .finish();
2591        let widen = op(&mut names, "movsxd_32_64");
2592        func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2593        alu(&mut func, &mut names, block, "add_rr_32", other, narrow);
2594
2595        assert_eq!(combine(&mut func, &mut names), 0);
2596        assert_eq!(
2597            shape(&func, &names, block),
2598            ["x64.mov_rm_32", "x64.movsxd_32_64", "x64.add_rr_32"]
2599        );
2600    }
2601
2602    /// A load the program insisted on keeps its own instruction, the same as it does in front of
2603    /// arithmetic.
2604    #[test]
2605    fn a_volatile_load_is_not_widened_on_the_way_in() {
2606        let (mut names, mut func, block) = empty();
2607        let base = func.new_vreg(GPR);
2608        let narrow = func.new_vreg(GPR);
2609        let wide = func.new_vreg(GPR);
2610        let read = op(&mut names, "mov_rm_16");
2611        func.build(block, read)
2612            .def(narrow, GPR)
2613            .mem(Mem { disp: 16, ..Mem::at(Operand::read(base, GPR)) })
2614            .flags(Flags::VOLATILE)
2615            .finish();
2616        let widen = op(&mut names, "movsx_16_32");
2617        func.build(block, widen).def(wide, GPR).uses(narrow, GPR).finish();
2618
2619        assert_eq!(combine(&mut func, &mut names), 0);
2620        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_16", "x64.movsx_16_32"]);
2621    }
2622
2623    /// Every widening names instructions this target has, reads memory only once folded, and
2624    /// takes a load of the width it widens from.
2625    #[test]
2626    fn every_widening_takes_a_load_of_the_width_it_widens_from() {
2627        let from = |name: &str| {
2628            name.split('_').find(|part| part.parse::<u32>().is_ok()).map(str::to_owned)
2629        };
2630        let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2631        for row in WIDENINGS {
2632            assert!(MACHINE.has(row.from), "{} is not an instruction", row.from);
2633            assert!(MACHINE.has(row.into), "{} is not an instruction", row.into);
2634            assert!(MACHINE.has(row.load), "{} is not an instruction", row.load);
2635            assert_eq!(from(row.from), width(row.load), "{} loads another width", row.from);
2636            assert!((MACHINE.takes_mem)(row.into), "{} reads no memory", row.into);
2637            assert!(!(MACHINE.takes_mem)(row.from), "{} already reads memory", row.from);
2638            assert_eq!(row.swapped, None, "{} has nothing to swap", row.from);
2639        }
2640    }
2641
2642    /// Every row of the table names instructions this target has, and names a load and an
2643    /// arithmetic whose widths agree. A row that got one of the three wrong would propose an
2644    /// instruction the change framework turns down, which is a fold that silently never happens.
2645    #[test]
2646    fn every_row_of_the_table_is_three_instructions_this_target_has() {
2647        for fold in FOLDS {
2648            assert!(MACHINE.has(fold.from), "{} is not an instruction", fold.from);
2649            assert!(MACHINE.has(fold.into), "{} is not an instruction", fold.into);
2650            assert!(MACHINE.has(fold.load), "{} is not an instruction", fold.load);
2651            let width = |name: &str| name.rsplit_once('_').map(|(_, width)| width.to_owned());
2652            assert_eq!(width(fold.from), width(fold.into), "{} changes width", fold.from);
2653            assert_eq!(width(fold.from), width(fold.load), "{} loads another width", fold.from);
2654            assert!((MACHINE.takes_mem)(fold.into), "{} reads no memory", fold.into);
2655            assert!(!(MACHINE.takes_mem)(fold.from), "{} already reads memory", fold.from);
2656            let Some(swapped) = fold.swapped else { continue };
2657            assert!(MACHINE.has(swapped), "{swapped} is not an instruction");
2658            assert_eq!(width(fold.from), width(swapped), "{} changes width", fold.from);
2659            assert!((MACHINE.takes_mem)(swapped), "{swapped} reads no memory");
2660        }
2661    }
2662
2663    /// One row per arithmetic instruction the target has that could take one, and one per
2664    /// comparison. The counts are here so that an instruction added to the target without a row
2665    /// shows up as a number rather than as a fold nobody noticed was missing.
2666    #[test]
2667    fn the_table_covers_the_arithmetic_and_the_comparisons_this_target_has() {
2668        let compares = FOLDS.iter().filter(|fold| fold.from.starts_with("cmp_set_")).count();
2669        assert_eq!(
2670            compares, 80,
2671            "ten conditions at four widths, against a register and a constant"
2672        );
2673        let arithmetic = FOLDS.len() - compares;
2674        assert_eq!(arithmetic, 23, "six operations at four widths, less the eight bit multiply");
2675        let swapped = FOLDS.iter().filter(|fold| fold.swapped.is_some()).count();
2676        assert_eq!(swapped, 59, "everything but the four subtractions and the constant compares");
2677    }
2678
2679    /// What a comparison folded on its left hand side comes out as.
2680    ///
2681    /// Reading the two sides the other way round turns the question over, so the row has to name
2682    /// the opposite ordering rather than the opposite answer. Less than and greater than are the
2683    /// pair, and equality and inequality are the two that come back to themselves, which is what
2684    /// makes this worth a test of its own: a row that had turned equality into inequality would be
2685    /// wrong in a way no width check and no name check would catch.
2686    #[test]
2687    fn a_comparison_folded_on_its_left_hand_side_asks_the_same_question_backwards() {
2688        let turned = |condition: &str| match condition {
2689            "e" => "e",
2690            "ne" => "ne",
2691            "l" => "g",
2692            "g" => "l",
2693            "le" => "ge",
2694            "ge" => "le",
2695            "b" => "a",
2696            "a" => "b",
2697            "be" => "ae",
2698            "ae" => "be",
2699            other => panic!("{other} is not a condition this machine has"),
2700        };
2701        let compares = FOLDS
2702            .iter()
2703            .filter(|fold| fold.from.starts_with("cmp_set_") && !fold.from.contains("_ri_"));
2704        for fold in compares {
2705            let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2706            let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2707            assert_eq!(fold.into, format!("cmp_set_{condition}_rm_{width}"));
2708            let wanted = format!("cmp_set_{}_rm_{width}", turned(condition));
2709            assert_eq!(fold.swapped, Some(wanted.as_str()), "{} turns over wrongly", fold.from);
2710        }
2711    }
2712
2713    /// What a comparison against a constant comes out as. There is one source rather than two, so
2714    /// the condition is the one that was written and there is no other arrangement to offer. A row
2715    /// that had filled in a `swapped` would be asking the pass to read the constant out of a
2716    /// register, which is not an instruction this machine has.
2717    #[test]
2718    fn a_comparison_against_a_constant_keeps_its_condition_and_has_nothing_to_swap() {
2719        let compares = FOLDS
2720            .iter()
2721            .filter(|fold| fold.from.starts_with("cmp_set_") && fold.from.contains("_ri_"));
2722        let mut rows = 0;
2723        for fold in compares {
2724            let (front, width) = fold.from.rsplit_once('_').expect("a name ending in a width");
2725            let front = front.strip_suffix("_ri").expect("a name against a constant");
2726            let condition = front.strip_prefix("cmp_set_").expect("a name with a condition");
2727            assert_eq!(fold.into, format!("cmp_set_{condition}_mi_{width}"));
2728            assert_eq!(fold.swapped, None, "{} has a side to swap", fold.from);
2729            assert_eq!(fold.load, format!("mov_rm_{width}"), "{} loads wrongly", fold.from);
2730            rows += 1;
2731        }
2732        assert_eq!(rows, 40, "ten conditions at four widths");
2733    }
2734
2735    /// A load the program insisted on, which is `volatile int *p; return *p + x;`.
2736    ///
2737    /// The fold would leave one instruction that reads the place, which is still one read of it,
2738    /// and the program would still do what it says. What it would not be is the load the program
2739    /// wrote, and a machine whose memory does something when it is read is a machine where the
2740    /// difference between one instruction and two is the reason the word was written.
2741    #[test]
2742    fn a_load_the_program_insisted_on_is_left_where_it_stands() {
2743        let (mut names, mut func, block) = empty();
2744        let base = func.new_vreg(GPR);
2745        let other = func.new_vreg(GPR);
2746        let word = insisted_load(&mut func, &mut names, block, base);
2747        alu(&mut func, &mut names, block, "add_rr_64", word, other);
2748
2749        assert_eq!(combine(&mut func, &mut names), 0);
2750        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64", "x64.add_rr_64"]);
2751    }
2752
2753    /// The same load with the arithmetic that reads it and the store that puts it back, which is
2754    /// `volatile int *p; *p += x;`. Three instructions in and three out, which is what GCC 13
2755    /// writes for it and what the spec asks for.
2756    #[test]
2757    fn a_run_whose_load_the_program_insisted_on_stays_three_instructions() {
2758        let (mut names, mut func, block) = empty();
2759        let base = func.new_vreg(GPR);
2760        let other = func.new_vreg(GPR);
2761        let word = insisted_load(&mut func, &mut names, block, base);
2762        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2763        store(&mut func, &mut names, block, base, sum);
2764
2765        assert_eq!(update(&mut func, &mut names), 0);
2766    }
2767
2768    /// The other end of the run, which is the half the load's own flag does not cover. A place
2769    /// read plainly and written back to a volatile address is a program that asked for the write
2770    /// to be its own instruction, and both ends are asked about because either one of them says
2771    /// so on its own.
2772    #[test]
2773    fn a_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2774        let (mut names, mut func, block) = empty();
2775        let base = func.new_vreg(GPR);
2776        let other = func.new_vreg(GPR);
2777        let word = load(&mut func, &mut names, block, base);
2778        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2779        insisted_store(&mut func, &mut names, block, base, sum);
2780
2781        assert_eq!(update(&mut func, &mut names), 0);
2782    }
2783
2784    /// The run against a constant, which is `volatile int *p; *p += 1;` and is the commoner of
2785    /// the two. It is a separate walk over a separate table, so it is asked separately.
2786    #[test]
2787    fn a_constant_run_the_program_insisted_on_stays_three_instructions() {
2788        let (mut names, mut func, block) = empty();
2789        let base = func.new_vreg(GPR);
2790        let word = insisted_load(&mut func, &mut names, block, base);
2791        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2792        store(&mut func, &mut names, block, base, sum);
2793
2794        assert_eq!(update(&mut func, &mut names), 0);
2795    }
2796
2797    /// The same run with the flag on the store instead of on the load.
2798    #[test]
2799    fn a_constant_run_whose_store_the_program_insisted_on_stays_three_instructions() {
2800        let (mut names, mut func, block) = empty();
2801        let base = func.new_vreg(GPR);
2802        let word = load(&mut func, &mut names, block, base);
2803        let sum = alu_imm(&mut func, &mut names, block, "add_ri_64", word, 1);
2804        insisted_store(&mut func, &mut names, block, base, sum);
2805
2806        assert_eq!(update(&mut func, &mut names), 0);
2807    }
2808
2809    /// A plain load of the same shape, so that the five above are read as the flag doing the
2810    /// work rather than as the runs being built wrongly.
2811    #[test]
2812    fn the_same_runs_without_the_flag_are_the_ones_the_pass_takes() {
2813        let (mut names, mut func, block) = empty();
2814        let base = func.new_vreg(GPR);
2815        let other = func.new_vreg(GPR);
2816        let word = load(&mut func, &mut names, block, base);
2817        let sum = alu(&mut func, &mut names, block, "add_rr_64", word, other);
2818        store(&mut func, &mut names, block, base, sum);
2819
2820        assert_eq!(update(&mut func, &mut names), 1);
2821        assert_eq!(shape(&func, &names, block), ["x64.add_mr_64"]);
2822    }
2823}