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