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