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