Skip to main content

rucc_codegen/
shorten.rs

1//! Writing the same answer in fewer bytes, once the registers are the real ones.
2//!
3//! Design: `spec/optimizer/37-machine-level-optimization.md` section 37.4, which calls these the
4//! size directed peepholes and puts them after register allocation. tamnd/rucc#741 is the issue
5//! about the back end never learning what it is compiling for, and names three of these as free
6//! before any of that is settled and one as waiting for it.
7//!
8//! Five rewrites. A move of zero into a register becomes an exclusive or of the register with
9//! itself: `movl $0, %eax` spells the zero out in four bytes of zero bits and is five bytes, `xorl
10//! %eax, %eax` says it without spelling it and is two. The processor knows the idiom, so the
11//! shorter one is no slower, and this is not a trade of speed for size and does not wait for a size
12//! goal to arrive.
13//!
14//! And a move of a number into a sixty-four bit register becomes the thirty-two bit move where the
15//! number is one that fits, because the narrow instruction clears the half of the register it does
16//! not write rather than leaving it alone. `movq $7, %rax` is seven bytes and `movl $7, %eax` is
17//! five, and for a number above two to the thirty-first it is ten against five, since the wide move
18//! cannot reach one by sign extending and writes all eight bytes of it out.
19//!
20//! The two meet on a zero, and the order they are asked in is the order they are worth: a zero
21//! whose condition state is free becomes the exclusive or, and a zero whose state is not becomes
22//! the narrow move, which is two bytes off rather than five but costs nothing to say.
23//!
24//! And a comparison of a register against zero becomes a test of the register against itself.
25//! `cmpl $0, %eax` is three bytes and `testl %eax, %eax` is two, the byte being the zero the first
26//! one writes out. That one is asked of every instruction whatever the walk has seen, because the
27//! test writes the condition state exactly as the comparison does: both leave the sign, the zero
28//! and the parity of what is in the register and both clear the carry and the overflow, so every
29//! condition this machine jumps on reads the same answer behind either of them.
30//!
31//! And an addition of one to a register becomes the instruction that adds one and says so in its
32//! opcode. `addl $1, %eax` is three bytes, one for the opcode, one saying which register and one
33//! for the number, and `incl %eax` is two. A subtraction of one becomes the instruction that takes
34//! one away, and each of the two is also what the other one written against minus one becomes.
35//!
36//! And an address computation whose address is a register becomes a move of that register. `leaq
37//! (%rsp), %rax` works out an address that is a base and nothing else, which is what is already in
38//! the base, and `movq %rsp, %rax` puts the same number in the same place in three bytes rather than
39//! four. The byte is the one an address counted from the stack pointer has to spend saying it has no
40//! index, and the stack pointer is the register this turns up on, because what makes it is taking
41//! the address of whichever local sits at the bottom of the frame.
42//!
43//! That fifth one is the only one here worth taking for something other than bytes. A move between
44//! registers is a thing the machine can do by renaming, so it is off the critical path, and an
45//! address computation is an addition however small the numbers in it are. gcc writes no address
46//! computation of that shape anywhere in the SQLite amalgamation and rucc wrote 444 of them.
47//!
48//! That fourth one is the only one here that is not free, and it is the only one that reads the
49//! goal. An addition writes the carry and an increment leaves the carry as it found it, so the
50//! machine has to merge what was left with what the next instruction writes, which costs a little
51//! where the code is hot and is worth a byte where the goal is size. gcc writes the addition at
52//! `-O2` and the increment at `-Os`, and so does this. The goal arriving here at all is the first
53//! half of tamnd/rucc#741: before it, `-Os` was a shorter list of middle end passes and the back
54//! end compiled what came out of it exactly as `-O2` would have.
55//!
56//! The numbers over the corpus at `-Os` before this pass existed: rucc wrote a move of zero into a
57//! register 21,304 times and GCC 16 wrote it 9 times, and GCC wrote the exclusive or 23,729 times
58//! against rucc's 965. So this is not a case the selector catches most of and misses at the edges.
59//! It is one it does not do. Afterwards rucc writes the move 1,232 times and the exclusive or
60//! 21,037, and the two moved by the same number, which is what says every one that went became one
61//! of these and none of them came from anywhere else.
62//!
63//! # Why it is not something the encoder does
64//!
65//! Because the two are not the same instruction. The exclusive or writes the condition state and
66//! the move does not, so an encoder that quietly swapped one for the other would change what the
67//! instruction behind it reads. Whether anything reads it is a question about the instructions that
68//! follow rather than about this one, which is what makes this a pass. [`rucc_target::FlagInsts`]
69//! is where the answer comes from, the same description [`crate::compare`] asks, and it answers
70//! that a name it does not know writes the state, so an opcode added to a rule set and not to that
71//! table makes this find less rather than making it wrong.
72//!
73//! The narrower move and the test are a different answer to the same question. Those two an encoder
74//! could do without asking anything, since the narrower move leaves the same number in the same
75//! register and the test leaves the same condition state the comparison left. They are not done
76//! there because an encoder handed a sixty-four bit move and writing the bytes of a thirty-two bit
77//! one would be writing bytes the listing beside them does not say, and the listing and the bytes
78//! agreeing is worth more than the two bytes. Choosing the instruction is this pass and spelling
79//! the one it chose is the encoder.
80//!
81//! # Why it runs last
82//!
83//! After [`crate::compare`], because that pass takes comparisons out and a comparison that is gone
84//! is one whose write of the condition state is gone with it. Running before it would see a state
85//! written where the output has none and would refuse rewrites that are allowed. After the layout
86//! for the reason `compare` is: the layout writes the jump that reads a comparison into the same
87//! block as the comparison, and this is the other pass that has to see that pair whole.
88//!
89//! Nothing here moves an instruction, removes one or changes a block, so running after the freeze
90//! costs nothing. The rewrite is one instruction becoming one instruction in the same place.
91//!
92//! # What a block boundary is
93//!
94//! The end of everything this knows, which is the same sentence [`crate::compare`] uses and the
95//! same reason: the condition state is not a register, nothing in this back end carries one from a
96//! block to its successors, and the only place a comparison is read is the block it was made in.
97//!
98//! That is an invariant of the passes in front rather than of this one, so it is checked instead of
99//! believed. `carried` walks every block and asks whether any of them reads the condition state
100//! before writing it, which is what a block reading a predecessor's state would look like from
101//! here, and one that does turns the whole function down. What it buys is that if some later pass
102//! starts writing that shape, this pass stops rather than starts being wrong.
103//!
104//! Reads it rather than mentions it. A comparison that keeps a byte makes the comparison and reads
105//! the answer in the one instruction, so a block opening with one is not a block reading anything a
106//! predecessor left, and the description is asked which of the two kinds of read it is rather than
107//! being taken at the word. tamnd/rucc#1432 is what that cost before it was asked: 456 functions in
108//! the SQLite amalgamation were turned down and every one of them was turned down by this, which is
109//! most of the functions in it that have anything for this pass to do.
110//!
111//! Down for the exclusive or and the increment. The narrower move reads no condition state and
112//! writes none, the test writes the same state the comparison it replaces wrote, and neither an
113//! address computation nor a move touches any state at all, so where a state is alive is not a
114//! question those three have to ask, and a function this turns down still gets all of them.
115//!
116//! What the walk carries for the increment is a second answer beside the first, which is whether
117//! anything behind reads the carry rather than whether anything behind reads the state. The two are
118//! not the same question and neither implies the other: a jump on whether a value was zero reads the
119//! state and not the carry, and an increment already in the code writes the state and not the carry
120//! and so ends the life of neither. That last case is the reason the walk and the check above both
121//! ask the target which instructions leave the carry alone rather than stopping at the flag saying
122//! the state was written.
123//!
124//! # A template a program wrote
125//!
126//! An `asm` statement is not opaque to this. `rucc_target::x86_64::read` turns the text of a
127//! template into the opcodes this back end already has, so by the time this runs a template is
128//! ordinary instructions carrying ordinary names, and the ones in it that read the condition state
129//! are seen the same way any other instruction's read is. A move of zero in front of a template is
130//! rewritten when nothing in that template reads a state it did not write itself, which is the same
131//! rule as everywhere else and not a rule about templates.
132//!
133//! Nothing weaker is being assumed there than what a program could already rely on. On this machine
134//! GCC has every `asm` clobber the condition state whether the statement said so or not, so a
135//! template reading one set before it was never something to hold on to.
136//!
137//! # What it will not do
138//!
139//! Turn a move into the exclusive or when anything reads its condition state before anything
140//! writes. That is the rule and what it costs is now a small number: the zero going into a register
141//! right before a comparison of something else stays a move, and the most the other rewrite can do
142//! for it is make it a narrower one. Of the 215 moves of zero left over the corpus at `-Os`, 146
143//! are this and the other 69 are the eight bit rule below. Not one of them is sixty-four bits wide.
144//!
145//! It was 1,232 until the whole function check stopped counting a comparison that keeps a byte as a
146//! state read from in front of it, which is tamnd/rucc#1432 and was most of what this pass was
147//! leaving alone rather than anything about the instructions it was looking at.
148//!
149//! Eight bits. `movb $0, %al` and `xorb %al, %al` are both two bytes, so the exchange buys nothing
150//! and would spend the condition state on it. The target's table is where that is written down.
151//!
152//! Write an increment at a level that asked for fast code. That is the goal doing its job rather
153//! than a limit, and it is why the same corpus compiled at `-O2` and at `-Os` now differs by
154//! something other than which middle end passes ran.
155//!
156//! Add or take away anything but one. The machine has an opcode for one and for nothing else, so a
157//! constant of two is already as short as it is going to be written.
158//!
159//! Turn an address computation into a move when the address is anything more than a register. An
160//! index is a multiplication, a constant is an addition and a symbol is an address the assembler
161//! fills in, and a move does none of those. That is what most address computations are for, so this
162//! last rewrite is about the ones that were not computing anything rather than about address
163//! computation in general.
164
165use std::collections::HashMap;
166
167use rucc_base::Interner;
168use rucc_cost::Goal;
169use rucc_mir::{self as mir, Role};
170use rucc_target::{FlagInsts, MachineInsts, Reads, ShortInsts};
171
172use crate::changes::{self, Changes, Plan};
173
174/// Rewrites every instruction that has a shorter spelling nothing would notice.
175///
176/// Gives back how many were rewritten, which the tests read and nothing else does.
177pub fn shorter(
178    func: &mut mir::Func,
179    short: &ShortInsts,
180    flags: &FlagInsts,
181    machine: &MachineInsts,
182    names: &mut Interner,
183    goal: Goal,
184) -> usize {
185    // Every name a rewrite could want, before the walk rather than inside it, because the walk
186    // holds a name it read out of the interner while it edits the function and interning a new one
187    // there would be the same interner borrowed twice. The same reason [`crate::compare`] has.
188    let wanted = short.zeroing.iter().map(|entry| entry.into);
189    let wanted = wanted.chain(short.narrowing.iter().map(|entry| entry.into));
190    let wanted = wanted.chain(short.testing.iter().map(|entry| entry.into));
191    let wanted = wanted.chain(short.stepping.iter().map(|entry| entry.into));
192    let wanted = wanted.chain(short.copying.iter().map(|entry| entry.into));
193    let opcodes: Vec<(&'static str, mir::Opcode)> = wanted
194        .map(|into| (into, mir::Opcode::new(names.intern(&format!("{}{into}", short.prefix)))))
195        .collect();
196    let names = &*names;
197    let mut counts = changes::Reads::of(func);
198    let mut took = 0;
199    let mut seen = HashMap::new();
200    // Whether the rewrite that spends the condition state may be asked for at all. The narrower
201    // instruction neither reads the state nor writes it, so it is not asked this and a function
202    // this turns down still gets that one.
203    let free = !carried(func, short, flags, names, &mut seen);
204    // Whether the rewrite that trades the carry for a byte may be asked for. It is the one thing
205    // here that is not free, so it waits for a level that said it wanted small code.
206    let small = free && goal == Goal::Size;
207    for block in func.blocks().collect::<Vec<_>>() {
208        // Backwards, because the question each instruction asks is about the ones behind it. The
209        // state is dead at the end of a block, which is the invariant [`carried`] has just held the
210        // function to.
211        let mut live = false;
212        // The same question about the carry alone, which is the part of the state the shorter
213        // addition does not write. It starts false for the reason `live` does and moves separately,
214        // because an instruction that writes the whole state ends the life of both and one that
215        // writes everything but the carry ends the life of neither.
216        let mut carry = false;
217        for inst in func.insts(block).collect::<Vec<_>>().into_iter().rev() {
218            // Asked again after each rewrite that is taken, since what stands there then is another
219            // instruction and the questions below are about that one.
220            let mut known = Known::of(&mut seen, func, short, flags, names, inst);
221            if free && !live && known.zeroing {
222                let into = shorter_form(func, short, names, &opcodes, inst);
223                if into.is_some_and(|op| zeroed(func, &mut counts, machine, names, inst, op)) {
224                    took += 1;
225                    // What stands there now writes the state, and the state was already dead, so
226                    // nothing about what the instructions in front of it may do has changed.
227                    continue;
228                }
229            }
230            // The zero that could not become an exclusive or can still be written in fewer bytes,
231            // which is why this is asked after that one and not instead of it.
232            let into = if known.narrowing {
233                narrower_form(func, short, names, &opcodes, inst)
234            } else {
235                None
236            };
237            if into.is_some_and(|op| narrowed(func, &mut counts, machine, names, inst, op)) {
238                took += 1;
239                known = Known::of(&mut seen, func, short, flags, names, inst);
240                // What stands there now is the same instruction at half the width, which is a
241                // move either way, so what it does to the state is what it did before: nothing.
242            }
243            // A comparison against zero asked of the register alone. Nothing about where the state
244            // is live comes into it, because the shorter instruction writes the same five bits of
245            // state the comparison wrote, so this is asked of every instruction whatever the walk
246            // has seen behind it.
247            let into =
248                if known.testing { tested_form(func, short, names, &opcodes, inst) } else { None };
249            if into.is_some_and(|op| tested(func, &mut counts, machine, names, inst, op)) {
250                took += 1;
251                known = Known::of(&mut seen, func, short, flags, names, inst);
252            }
253            // An address that is a register, written as the move it is. Nothing about the condition
254            // state comes into it either, since neither instruction writes any, so this is asked of
255            // every instruction the same way the narrower move is.
256            let into =
257                if known.copying { copied_form(func, short, names, &opcodes, inst) } else { None };
258            if into.is_some_and(|op| copied(func, &mut counts, machine, names, inst, op)) {
259                took += 1;
260                known = Known::of(&mut seen, func, short, flags, names, inst);
261            }
262            // Adding one with the one in the opcode, which is the only rewrite here that is a
263            // trade. It needs the carry to be dead rather than the whole state, since that is the
264            // only part of the state the shorter instruction leaves behind, and it needs the level
265            // to have asked for small code.
266            if small && !carry && known.stepping {
267                let into = stepped_form(func, short, names, &opcodes, inst);
268                if into.is_some_and(|op| stepped(func, &mut counts, machine, names, inst, op)) {
269                    took += 1;
270                    known = Known::of(&mut seen, func, short, flags, names, inst);
271                    // What stands there now writes everything but the carry, and the carry was
272                    // already dead, so both answers below are the ones they already are and the
273                    // walk past it is the walk it would have taken anyway.
274                }
275            }
276            if !known.covered {
277                // A name the description does not cover may have read the state and may have
278                // written it, and the answer that finds fewer rewrites is that it read it.
279                live = true;
280                carry = true;
281                continue;
282            }
283            // What it reads before whether it writes, because an instruction can do both and the
284            // read it does is a read of what is there now. An add with carry is the one that does,
285            // and asking the other way round would call it the end of the state's life and let a
286            // rewrite in front of it take the carry away.
287            //
288            // Unless what it reads is what it wrote itself. A comparison that keeps a byte makes
289            // the comparison and reads the answer in the one instruction, so the state it was
290            // handed is state it wrote over before anything looked at it, and it ends a life rather
291            // than extending one. Asking the description which kind it is rather than stopping at
292            // the word read is most of what this pass gets to do in real code, since a C function
293            // of any size has one of these in it.
294            if known.own {
295                live = false;
296                carry = false;
297            } else if let Some(reads) = known.reads {
298                live = true;
299                // Which part of the state the condition on it is about. A condition that asks where
300                // a value sits as an unsigned number reads the carry, and so does an instruction
301                // that is adding a carry on rather than asking a question about one.
302                if matches!(reads, Reads::Unsigned | Reads::Carry) {
303                    carry = true;
304                }
305            } else if known.ends {
306                // An instruction that writes the state ends the life of everything in it. One that
307                // writes all of it but the carry ends the life of none of it, which is the second
308                // half of the sentence and is why this asks the description rather than stopping at
309                // the flag. That answer is about `live` as much as about `carry`: a rewrite in front
310                // that spends the state would be spending a carry this instruction was going to
311                // leave for something behind it.
312                live = false;
313                carry = false;
314            }
315        }
316    }
317    took
318}
319
320/// Whether any block reads the condition state before writing it, which is what a state carried in
321/// from a predecessor would look like from inside this pass.
322///
323/// The passes in front are the ones that promise this does not happen and the promise is theirs to
324/// keep, so what this does is hold them to it rather than restate it. A function where it is broken
325/// gets no rewrites at all, which is the answer that is wrong about nothing.
326///
327/// An instruction that leaves the carry alone does not count as having written the state here, for
328/// the same reason it does not count as having written it in the walk. A block opening with one and
329/// reading a carry afterwards is reading a carry a predecessor left, which is exactly the shape this
330/// is looking for, and stopping at it would be calling that block clean.
331///
332/// An instruction that reads what it wrote itself does not count as having read the state, for the
333/// same reason it does not in the walk. A block opening with a comparison that keeps a byte opens
334/// with a comparison, and what the comparison found is not what anything in front of it left.
335/// Counting it as a read is the difference between this turning down a few functions and turning
336/// down most of them, because a comparison that keeps a byte is what every `!` and every `==` in a
337/// value position comes out as.
338fn carried(
339    func: &mir::Func,
340    short: &ShortInsts,
341    flags: &FlagInsts,
342    names: &Interner,
343    seen: &mut HashMap<mir::Opcode, Known>,
344) -> bool {
345    func.blocks().any(|block| {
346        for inst in func.insts(block) {
347            let known = Known::of(seen, func, short, flags, names, inst);
348            if !known.covered {
349                return true;
350            }
351            if known.reads.is_some() && !known.own {
352                return true;
353            }
354            if known.ends {
355                return false;
356            }
357        }
358        false
359    })
360}
361
362/// What the description says about one opcode, asked once for each opcode a function has rather
363/// than once for each instruction.
364///
365/// Every answer here is a walk down one of the target's tables comparing names, and the walk above
366/// asks most of them of every instruction while nearly every answer is no. A function has a few
367/// hundred opcodes at most, so each one is asked about the first time it turns up and read back
368/// after that. On jtckdint's `test.c` at `-O2` the comparing of names in this pass was about two and
369/// a half percent of the build.
370///
371/// The forms are only whether the table has an entry for the name. Whether the instruction carries
372/// the number or the address the shorter one needs is the instruction's own business and is still
373/// asked of it.
374#[derive(Debug, Clone, Copy)]
375struct Known {
376    /// Whether it has a shorter way of writing zero.
377    zeroing: bool,
378    /// Whether it has a narrower instruction.
379    narrowing: bool,
380    /// Whether it has a shorter way of comparing against zero.
381    testing: bool,
382    /// Whether it has a move that says the same thing.
383    copying: bool,
384    /// Whether it has a shorter addition for some number.
385    stepping: bool,
386    /// Whether the description covers the name at all. See [`opcode`].
387    covered: bool,
388    /// Whether it reads what it wrote itself. See [`FlagInsts::asks_what_it_reads`].
389    own: bool,
390    /// Which part of the condition state it reads.
391    reads: Option<Reads>,
392    /// Whether it writes the whole of the condition state, the carry included.
393    ends: bool,
394}
395
396impl Known {
397    fn of(
398        seen: &mut HashMap<mir::Opcode, Self>,
399        func: &mir::Func,
400        short: &ShortInsts,
401        flags: &FlagInsts,
402        names: &Interner,
403        inst: mir::Inst,
404    ) -> Self {
405        *seen.entry(func[inst].opcode).or_insert_with(|| {
406            let bare = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix);
407            let has =
408                |name: fn(&ShortInsts, &str) -> bool| bare.is_some_and(|bare| name(short, bare));
409            let name = opcode(func, flags, names, inst);
410            Self {
411                zeroing: has(|short, bare| short.zeroed(bare).is_some()),
412                narrowing: has(|short, bare| short.narrowed(bare).is_some()),
413                testing: has(|short, bare| short.tested(bare).is_some()),
414                copying: has(|short, bare| short.copied(bare).is_some()),
415                stepping: has(|short, bare| short.stepping.iter().any(|entry| entry.name == bare)),
416                covered: name.is_some(),
417                own: name.is_some_and(|name| flags.asks_what_it_reads(name)),
418                reads: name.and_then(|name| flags.reads(name)),
419                ends: name.is_some_and(|name| (flags.writes)(name) && !short.steps(name)),
420            }
421        })
422    }
423}
424
425/// The shorter instruction this one has, when it has one and the constant it carries is the one
426/// that instruction writes.
427///
428/// The name says which instruction it is and the description says which names have a shorter
429/// spelling, and neither of them says what number this one holds. That is the half that decides
430/// whether the shorter spelling says the same thing, since the short way of writing zero is only
431/// the short way of writing zero.
432fn shorter_form(
433    func: &mir::Func,
434    short: &ShortInsts,
435    names: &Interner,
436    opcodes: &[(&'static str, mir::Opcode)],
437    inst: mir::Inst,
438) -> Option<mir::Opcode> {
439    let name = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix)?;
440    let into = short.zeroed(name)?;
441    if func[inst].imm.map(|at| func[at].0) != Some(0) {
442        return None;
443    }
444    opcodes.iter().find(|&&(at, _)| at == into).map(|&(_, opcode)| opcode)
445}
446
447/// The narrower instruction this one has, when it has one and the number it carries is one that
448/// instruction holds.
449///
450/// A number the narrower instruction cannot hold is every negative one and everything above what
451/// fits in the bits it writes, since what it does to the rest of the register is clear it. So the
452/// question is not whether the number fits in that many bits the way the program meant it, which is
453/// a question about a type, but whether the bits the wide instruction would leave in the register
454/// are the bits the narrow one leaves there, which is a question about the number.
455fn narrower_form(
456    func: &mir::Func,
457    short: &ShortInsts,
458    names: &Interner,
459    opcodes: &[(&'static str, mir::Opcode)],
460    inst: mir::Inst,
461) -> Option<mir::Opcode> {
462    let name = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix)?;
463    let narrow = short.narrowed(name)?;
464    let held = u64::try_from(func[func[inst].imm?].0).ok()?;
465    if narrow.writes >= u64::BITS || held >= 1u64 << narrow.writes {
466        return None;
467    }
468    opcodes.iter().find(|&&(at, _)| at == narrow.into).map(|&(_, opcode)| opcode)
469}
470
471/// Rewrites the move into the narrower move, which is the same instruction with a different name.
472///
473/// So the operands are the ones it had, where the exclusive or below needs its own built: the two
474/// moves take a register they write and a number, and the number is the one that was already there.
475/// A description where that is not so is one [`Changes`] turns down, and a rewrite it turns down is
476/// one this reports as not taken rather than one that goes in anyway.
477fn narrowed(
478    func: &mut mir::Func,
479    counts: &mut changes::Reads,
480    machine: &MachineInsts,
481    names: &Interner,
482    inst: mir::Inst,
483    opcode: mir::Opcode,
484) -> bool {
485    let mut set = Changes::new();
486    set.rewrite(inst, Plan { opcode, ..Plan::of(func, inst) });
487    set.commit(func, counts, names, machine).is_ok()
488}
489
490/// The shorter comparison this one has, when it has one and the constant it carries is zero.
491///
492/// Zero is the whole of it. A comparison of a register against itself asks whether the register is
493/// zero and nothing else, so the description's entry says what to write instead of a comparison
494/// against zero and says nothing about a comparison against anything, and an instruction carrying
495/// any other number is one this walks past.
496fn tested_form(
497    func: &mir::Func,
498    short: &ShortInsts,
499    names: &Interner,
500    opcodes: &[(&'static str, mir::Opcode)],
501    inst: mir::Inst,
502) -> Option<mir::Opcode> {
503    let name = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix)?;
504    let into = short.tested(name)?;
505    if func[inst].imm.map(|at| func[at].0) != Some(0) {
506        return None;
507    }
508    opcodes.iter().find(|&&(at, _)| at == into).map(|&(_, opcode)| opcode)
509}
510
511/// Rewrites the comparison into the test, which reads the register the comparison read and drops
512/// the constant.
513///
514/// The operands are the ones it had, for the reason [`narrowed`] keeps them: both instructions name
515/// one register and read it, and what changes is the number, which the shorter one does not carry.
516/// So the constant goes and nothing else does. A description where the two are not that shape is one
517/// [`Changes`] turns down, and this reports a rewrite it turned down as not taken.
518fn tested(
519    func: &mut mir::Func,
520    counts: &mut changes::Reads,
521    machine: &MachineInsts,
522    names: &Interner,
523    inst: mir::Inst,
524    opcode: mir::Opcode,
525) -> bool {
526    let mut set = Changes::new();
527    set.rewrite(inst, Plan { opcode, imm: None, ..Plan::of(func, inst) });
528    set.commit(func, counts, names, machine).is_ok()
529}
530
531/// The move this address computation is, when the address it works out is a register.
532///
533/// Which is an addressing mode naming a base and nothing else. An index is a multiplication and an
534/// addition, a constant is an addition, and a symbol or a label is an address the assembler fills in
535/// later, so any of those is work the move does not do. What is left is a mode that says to take
536/// what is in one register, and taking what is in one register is the move.
537///
538/// The width is not asked about, unlike the narrower move above. An address on this machine is
539/// sixty four bits wide whatever is at it, so an address computation that keeps its answer keeps all
540/// of it, and the move the description names beside it is the move of that width.
541fn copied_form(
542    func: &mir::Func,
543    short: &ShortInsts,
544    names: &Interner,
545    opcodes: &[(&'static str, mir::Opcode)],
546    inst: mir::Inst,
547) -> Option<mir::Opcode> {
548    let name = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix)?;
549    let into = short.copied(name)?;
550    let amode = func[inst].mem.map(|at| func[at])?;
551    if amode.base.is_none() || amode.index.is_some() || amode.disp != 0 {
552        return None;
553    }
554    if amode.symbol.is_some() || amode.block.is_some() || amode.table.is_some() {
555        return None;
556    }
557    if amode.segment.is_some() {
558        return None;
559    }
560    opcodes.iter().find(|&&(at, _)| at == into).map(|&(_, opcode)| opcode)
561}
562
563/// Rewrites the address computation into the move, which keeps the operands and drops the mode.
564///
565/// The operands are already the move's. An instruction with an addressing mode carries the registers
566/// that mode names in its operand vector, behind the ones it writes, so an address computation whose
567/// mode is one base is an instruction that writes one register and reads one register, in that
568/// order, which is the move's shape. What goes is the mode itself, since the move has none.
569///
570/// A description where those two are not the same shape is one [`Changes`] turns down, and this
571/// reports a rewrite it turned down as not taken, which is how an address computation with more in
572/// its operand vector than the mode accounted for is left alone rather than guessed at.
573fn copied(
574    func: &mut mir::Func,
575    counts: &mut changes::Reads,
576    machine: &MachineInsts,
577    names: &Interner,
578    inst: mir::Inst,
579    opcode: mir::Opcode,
580) -> bool {
581    let mut set = Changes::new();
582    set.rewrite(inst, Plan { opcode, amode: None, ..Plan::of(func, inst) });
583    set.commit(func, counts, names, machine).is_ok()
584}
585
586/// Rewrites the move into the exclusive or, which names the one register the move wrote in every
587/// operand it has.
588///
589/// The shapes come from the description rather than from the move, since the shorter instruction is
590/// not the shape the longer one was: the exclusive or writes a register it also reads, which on this
591/// machine is an operand constrained to the same place as one of the reads, and a plan whose
592/// operands do not say so is one [`Changes`] turns down. So each operand is built to what the
593/// description asks for and the register in it is the one the move wrote, which after allocation is
594/// a physical register and so is a register every operand can name without anything being arranged.
595/// The constant goes with the move, the shorter instruction being the one that carries none.
596///
597/// A description whose operands are not all of the register's class, or which writes more than the
598/// one register or none, is a description this does not fit, and the answer there is to leave the
599/// instruction alone rather than to guess.
600fn zeroed(
601    func: &mut mir::Func,
602    counts: &mut changes::Reads,
603    machine: &MachineInsts,
604    names: &Interner,
605    inst: mir::Inst,
606    opcode: mir::Opcode,
607) -> bool {
608    let written: Vec<mir::Operand> = func[func[inst].operands]
609        .iter()
610        .filter(|operand| operand.role != Role::Use)
611        .copied()
612        .collect();
613    let [def] = written[..] else { return false };
614    let bare = machine.bare(names.resolve(opcode.name()));
615    let Some(desc) = (machine.operands)(bare) else { return false };
616    if desc.iter().any(|want| want.class != def.class) {
617        return false;
618    }
619    if desc.iter().filter(|want| want.role != Role::Use).count() != 1 {
620        return false;
621    }
622    let operands = desc
623        .iter()
624        .map(|want| mir::Operand {
625            reg: def.reg,
626            class: want.class,
627            role: want.role,
628            constraint: want.constraint,
629        })
630        .collect();
631    let mut set = Changes::new();
632    set.rewrite(inst, Plan { opcode, operands, imm: None, ..Plan::of(func, inst) });
633    set.commit(func, counts, names, machine).is_ok()
634}
635
636/// The shorter addition this one has, when it has one and the number it carries is the number that
637/// shorter instruction is about.
638///
639/// Both halves again, and the second one is doing more work here than anywhere else in this pass.
640/// One addition has two shorter instructions, one for each of the two numbers a machine has an
641/// opcode for, and a subtraction has the same two the other way round, so the number is what says
642/// which of the two is meant rather than only whether either is.
643fn stepped_form(
644    func: &mir::Func,
645    short: &ShortInsts,
646    names: &Interner,
647    opcodes: &[(&'static str, mir::Opcode)],
648    inst: mir::Inst,
649) -> Option<mir::Opcode> {
650    let name = names.resolve(func[inst].opcode.name()).strip_prefix(short.prefix)?;
651    let into = short.stepped(name, func[func[inst].imm?].0)?;
652    opcodes.iter().find(|&&(at, _)| at == into).map(|&(_, opcode)| opcode)
653}
654
655/// Rewrites the addition into the one that carries its number in its opcode.
656///
657/// The operands are the ones it had, for the reason [`tested`] keeps them: both instructions write
658/// one register and read that same register, and what changes is the number, which the shorter one
659/// does not carry. So the constant goes and nothing else does.
660fn stepped(
661    func: &mut mir::Func,
662    counts: &mut changes::Reads,
663    machine: &MachineInsts,
664    names: &Interner,
665    inst: mir::Inst,
666    opcode: mir::Opcode,
667) -> bool {
668    let mut set = Changes::new();
669    set.rewrite(inst, Plan { opcode, imm: None, ..Plan::of(func, inst) });
670    set.commit(func, counts, names, machine).is_ok()
671}
672
673/// The name this target knows an instruction by, for an instruction that is one of this target's.
674///
675/// The opcode in machine IR carries the target's prefix, because a function in the middle of being
676/// compiled holds instructions of one machine and the prefix is what says which. Anything without
677/// it is not something this description covers, and the walk treats that as knowing nothing rather
678/// than as knowing it is safe.
679fn opcode<'a>(
680    func: &mir::Func,
681    flags: &FlagInsts,
682    names: &'a Interner,
683    inst: mir::Inst,
684) -> Option<&'a str> {
685    names.resolve(func[inst].opcode.name()).strip_prefix(flags.prefix)
686}
687
688#[cfg(test)]
689mod tests {
690    use rucc_target::x86_64::{FLAGS, GPR, MACHINE, SHORT};
691
692    use super::*;
693
694    /// A function with one block, and the names it was built with.
695    fn empty() -> (Interner, mir::Func, mir::Block) {
696        let mut names = Interner::new();
697        let mut func = mir::Func::new(names.intern("f"));
698        let block = func.create_block();
699        (names, func, block)
700    }
701
702    /// The opcode of that name on this target.
703    fn op(names: &mut Interner, name: &str) -> mir::Opcode {
704        mir::Opcode::new(names.intern(&format!("{}{name}", SHORT.prefix)))
705    }
706
707    /// The pass, over the machine this crate has a backend for, at a level that wanted fast code.
708    fn takes(func: &mut mir::Func, names: &mut Interner) -> usize {
709        shorter(func, &SHORT, &FLAGS, &MACHINE, names, Goal::Speed)
710    }
711
712    /// The same pass at a level that wanted small code, which is the only one that steps.
713    fn small(func: &mut mir::Func, names: &mut Interner) -> usize {
714        shorter(func, &SHORT, &FLAGS, &MACHINE, names, Goal::Size)
715    }
716
717    /// What every instruction in a block came to, as opcodes with the target's prefix taken off.
718    fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
719        func.insts(block)
720            .map(|inst| {
721                names
722                    .resolve(func[inst].opcode.name())
723                    .strip_prefix(SHORT.prefix)
724                    .unwrap_or("")
725                    .to_owned()
726            })
727            .collect()
728    }
729
730    /// The destination of a two-address instruction, which the description constrains to the same
731    /// register as the first source. The builder's own `def` leaves the constraint off, and the
732    /// change framework holds a rewrite to the shape the target asks for, so a test that built one
733    /// without it would be a test of a function the allocator could not have produced.
734    fn reuse(reg: mir::Reg) -> mir::Operand {
735        mir::Operand {
736            reg,
737            class: GPR,
738            role: Role::Def,
739            constraint: rucc_mir::Constraint::Reuse(1),
740        }
741    }
742
743    /// The registers an instruction names, in the order its operands do.
744    fn regs(func: &mir::Func, inst: mir::Inst) -> Vec<mir::Reg> {
745        func[func[inst].operands].iter().map(|operand| operand.reg).collect()
746    }
747
748    /// The number an instruction carries, for an instruction that carries one.
749    fn imm(func: &mir::Func, inst: mir::Inst) -> Option<i64> {
750        func[inst].imm.map(|at| func[at].0)
751    }
752
753    /// The shape the pass is for: a move of zero with nothing reading the condition state after it
754    /// becomes the exclusive or, which names the register it writes in all three of its operands and
755    /// carries no constant.
756    #[test]
757    fn a_move_of_zero_becomes_an_exclusive_or() {
758        let (mut names, mut func, block) = empty();
759        let into = func.new_vreg(GPR);
760        let zero = op(&mut names, "mov_ri_32");
761        let inst = func.build(block, zero).def(into, GPR).imm(0).finish();
762
763        assert_eq!(takes(&mut func, &mut names), 1);
764        assert_eq!(shape(&func, &names, block), ["xor_rr_32"]);
765        assert_eq!(regs(&func, inst), [into, into, into]);
766        assert!(func[inst].imm.is_none());
767    }
768
769    /// Sixty-four bits is the same rewrite and the biggest one, since the long way of writing a zero
770    /// there is seven bytes. The instruction it becomes is the thirty-two bit one, which clears the
771    /// half of the register it does not write and so leaves the same sixty-four bit zero in one
772    /// byte less.
773    #[test]
774    fn sixty_four_bits_is_the_same_rewrite_at_half_the_width() {
775        let (mut names, mut func, block) = empty();
776        let into = func.new_vreg(GPR);
777        let zero = op(&mut names, "mov_ri_64");
778        func.build(block, zero).def(into, GPR).imm(0).finish();
779
780        assert_eq!(takes(&mut func, &mut names), 1);
781        assert_eq!(shape(&func, &names, block), ["xor_rr_32"]);
782    }
783
784    /// The other rewrite. A number that is not zero has nothing shorter than a move, and the move
785    /// that writes half the register is shorter than the one that writes all of it.
786    #[test]
787    fn a_number_a_narrower_move_holds_is_written_by_the_narrower_move() {
788        for value in [1, 7, 0x7fff_ffff, 0x8000_0000, 0xffff_ffff] {
789            let (mut names, mut func, block) = empty();
790            let into = func.new_vreg(GPR);
791            let wide = op(&mut names, "mov_ri_64");
792            let inst = func.build(block, wide).def(into, GPR).imm(value).finish();
793
794            assert_eq!(takes(&mut func, &mut names), 1, "{value}");
795            assert_eq!(shape(&func, &names, block), ["mov_ri_32"], "{value}");
796            assert_eq!(imm(&func, inst), Some(value), "{value}");
797            assert_eq!(regs(&func, inst), [into], "{value}");
798        }
799    }
800
801    /// A number the narrower move does not hold, which is everything above what fits in the bits it
802    /// writes and every negative number, since what it does to the rest of the register is clear it
803    /// rather than fill it with the sign.
804    #[test]
805    fn a_number_the_narrower_move_does_not_hold_stays_wide() {
806        for value in [-1, -7, 0x1_0000_0000, i64::MIN, i64::MAX] {
807            let (mut names, mut func, block) = empty();
808            let into = func.new_vreg(GPR);
809            let wide = op(&mut names, "mov_ri_64");
810            func.build(block, wide).def(into, GPR).imm(value).finish();
811
812            assert_eq!(takes(&mut func, &mut names), 0, "{value}");
813            assert_eq!(shape(&func, &names, block), ["mov_ri_64"], "{value}");
814        }
815    }
816
817    /// A zero the condition state is not free for, which the first rewrite has to leave alone. The
818    /// second one has nothing to do with the state and takes it, so the instruction that stays is
819    /// five bytes rather than seven.
820    #[test]
821    fn a_zero_the_state_is_not_free_for_is_narrowed_instead() {
822        let (mut names, mut func, block) = empty();
823        let left = func.new_vreg(GPR);
824        let right = func.new_vreg(GPR);
825        let into = func.new_vreg(GPR);
826        let byte = func.new_vreg(GPR);
827        let cmp = op(&mut names, "cmp_rr_32");
828        let zero = op(&mut names, "mov_ri_64");
829        let set = op(&mut names, "set_e");
830        func.build(block, cmp).uses(left, GPR).uses(right, GPR).finish();
831        let inst = func.build(block, zero).def(into, GPR).imm(0).finish();
832        func.build(block, set).def(byte, GPR).finish();
833
834        assert_eq!(takes(&mut func, &mut names), 1);
835        assert_eq!(shape(&func, &names, block), ["cmp_rr_32", "mov_ri_32", "set_e"]);
836        assert_eq!(imm(&func, inst), Some(0));
837    }
838
839    /// A function the state carried across an edge turns down, which is the first rewrite's rule
840    /// and not the second one's. The narrower move writes no state and reads none, so a function
841    /// that rule turns down still gets it.
842    #[test]
843    fn a_function_the_carried_state_turns_down_is_still_narrowed() {
844        let (mut names, mut func, first) = empty();
845        let second = func.create_block();
846        let into = func.new_vreg(GPR);
847        let byte = func.new_vreg(GPR);
848        let wide = op(&mut names, "mov_ri_64");
849        let set = op(&mut names, "set_e");
850        func.build(first, wide).def(into, GPR).imm(7).finish();
851        func.build(second, set).def(byte, GPR).finish();
852
853        assert_eq!(takes(&mut func, &mut names), 1);
854        assert_eq!(shape(&func, &names, first), ["mov_ri_32"]);
855    }
856
857    /// A move of anything else. The shorter instruction writes zero, so it says the same thing only
858    /// where the longer one said zero.
859    #[test]
860    fn a_move_of_a_number_that_is_not_zero_stays() {
861        let (mut names, mut func, block) = empty();
862        let into = func.new_vreg(GPR);
863        let one = op(&mut names, "mov_ri_32");
864        func.build(block, one).def(into, GPR).imm(1).finish();
865
866        assert_eq!(takes(&mut func, &mut names), 0);
867        assert_eq!(shape(&func, &names, block), ["mov_ri_32"]);
868    }
869
870    /// Eight bits, where both spellings are two bytes. The target's table leaves it out and the pass
871    /// has nothing to look up, so the move stays and the condition state stays with it.
872    #[test]
873    fn eight_bits_buys_nothing_and_is_left_alone() {
874        let (mut names, mut func, block) = empty();
875        let into = func.new_vreg(GPR);
876        let zero = op(&mut names, "mov_ri_8");
877        func.build(block, zero).def(into, GPR).imm(0).finish();
878
879        assert_eq!(takes(&mut func, &mut names), 0);
880        assert_eq!(shape(&func, &names, block), ["mov_ri_8"]);
881    }
882
883    /// The cost of the rewrite, which is the zero going into a register in front of something that
884    /// reads a comparison of something else. The exclusive or would write over the answer the byte
885    /// is about, so the move stays.
886    #[test]
887    fn a_move_a_condition_reads_the_state_after_stays() {
888        let (mut names, mut func, block) = empty();
889        let left = func.new_vreg(GPR);
890        let right = func.new_vreg(GPR);
891        let into = func.new_vreg(GPR);
892        let byte = func.new_vreg(GPR);
893        let cmp = op(&mut names, "cmp_rr_32");
894        let zero = op(&mut names, "mov_ri_32");
895        let set = op(&mut names, "set_e");
896        func.build(block, cmp).uses(left, GPR).uses(right, GPR).finish();
897        func.build(block, zero).def(into, GPR).imm(0).finish();
898        func.build(block, set).def(byte, GPR).finish();
899
900        assert_eq!(takes(&mut func, &mut names), 0);
901        assert_eq!(shape(&func, &names, block), ["cmp_rr_32", "mov_ri_32", "set_e"]);
902    }
903
904    /// The same three instructions with something writing the condition state in between. What the
905    /// byte reads is what the addition left, so the state the move would write is one nothing was
906    /// going to read and the rewrite is back on.
907    #[test]
908    fn a_state_something_else_writes_first_lets_the_rewrite_back_in() {
909        let (mut names, mut func, block) = empty();
910        let left = func.new_vreg(GPR);
911        let right = func.new_vreg(GPR);
912        let sum = func.new_vreg(GPR);
913        let into = func.new_vreg(GPR);
914        let byte = func.new_vreg(GPR);
915        let zero = op(&mut names, "mov_ri_32");
916        let add = op(&mut names, "add_rr_32");
917        let set = op(&mut names, "set_e");
918        func.build(block, zero).def(into, GPR).imm(0).finish();
919        func.build(block, add).def(sum, GPR).uses(left, GPR).uses(right, GPR).finish();
920        func.build(block, set).def(byte, GPR).finish();
921
922        assert_eq!(takes(&mut func, &mut names), 1);
923        assert_eq!(shape(&func, &names, block), ["xor_rr_32", "add_rr_32", "set_e"]);
924    }
925
926    /// A function where a block reads the condition state before it writes one, which is what a
927    /// state carried across an edge looks like from here. The passes in front say that does not
928    /// happen and this is where that is held to rather than believed, so the whole function is
929    /// turned down and the move in the other block stays as well.
930    #[test]
931    fn a_state_carried_into_a_block_turns_the_whole_function_down() {
932        let (mut names, mut func, first) = empty();
933        let second = func.create_block();
934        let into = func.new_vreg(GPR);
935        let byte = func.new_vreg(GPR);
936        let zero = op(&mut names, "mov_ri_32");
937        let set = op(&mut names, "set_e");
938        func.build(first, zero).def(into, GPR).imm(0).finish();
939        func.build(second, set).def(byte, GPR).finish();
940
941        assert_eq!(takes(&mut func, &mut names), 0);
942        assert_eq!(shape(&func, &names, first), ["mov_ri_32"]);
943    }
944
945    /// The third rewrite. A comparison of a register against zero asks whether the register is
946    /// zero, and so does a test of the register against itself, which says it without a number on
947    /// the instruction.
948    #[test]
949    fn a_comparison_against_zero_becomes_a_test_of_the_register_against_itself() {
950        for (wide, narrow) in [
951            ("cmp_ri_8", "test_rr_8"),
952            ("cmp_ri_16", "test_rr_16"),
953            ("cmp_ri_32", "test_rr_32"),
954            ("cmp_ri_64", "test_rr_64"),
955        ] {
956            let (mut names, mut func, block) = empty();
957            let value = func.new_vreg(GPR);
958            let byte = func.new_vreg(GPR);
959            let cmp = op(&mut names, wide);
960            let set = op(&mut names, "set_e");
961            let inst = func.build(block, cmp).uses(value, GPR).imm(0).finish();
962            func.build(block, set).def(byte, GPR).finish();
963
964            assert_eq!(takes(&mut func, &mut names), 1, "{wide}");
965            assert_eq!(shape(&func, &names, block), [narrow, "set_e"], "{wide}");
966            assert_eq!(regs(&func, inst), [value], "{wide}");
967            assert_eq!(imm(&func, inst), None, "{wide}");
968        }
969    }
970
971    /// A comparison against anything else, which the test cannot ask. What a test leaves is the
972    /// bits of the register it was given, so it answers one question and the question is zero.
973    #[test]
974    fn a_comparison_against_a_number_that_is_not_zero_is_left_alone() {
975        for value in [1, -1, 7, 255, i64::from(i32::MIN)] {
976            let (mut names, mut func, block) = empty();
977            let held = func.new_vreg(GPR);
978            let byte = func.new_vreg(GPR);
979            let cmp = op(&mut names, "cmp_ri_32");
980            let set = op(&mut names, "set_e");
981            let inst = func.build(block, cmp).uses(held, GPR).imm(value).finish();
982            func.build(block, set).def(byte, GPR).finish();
983
984            assert_eq!(takes(&mut func, &mut names), 0, "{value}");
985            assert_eq!(shape(&func, &names, block), ["cmp_ri_32", "set_e"], "{value}");
986            assert_eq!(imm(&func, inst), Some(value), "{value}");
987        }
988    }
989
990    /// The condition state is not a question this rewrite asks. The comparison writes the state and
991    /// the test writes the same state, so a comparison whose answer something reads right behind it
992    /// is rewritten exactly as one whose answer nothing wants is, and a function the carried state
993    /// rule turns down gets it too.
994    #[test]
995    fn a_comparison_is_tested_whatever_the_condition_state_is_doing() {
996        let (mut names, mut func, first) = empty();
997        let second = func.create_block();
998        let value = func.new_vreg(GPR);
999        let byte = func.new_vreg(GPR);
1000        let cmp = op(&mut names, "cmp_ri_32");
1001        let set = op(&mut names, "set_e");
1002        func.build(first, cmp).uses(value, GPR).imm(0).finish();
1003        // A block that reads the state before writing it, which is what `carried` turns a function
1004        // down for and what the first rewrite is the only one to need.
1005        func.build(second, set).def(byte, GPR).finish();
1006
1007        assert_eq!(takes(&mut func, &mut names), 1);
1008        assert_eq!(shape(&func, &names, first), ["test_rr_32"]);
1009        assert_eq!(shape(&func, &names, second), ["set_e"]);
1010    }
1011
1012    /// The fourth rewrite, at every width and in both directions. An addition of one and a
1013    /// subtraction of minus one are the instruction that adds one, and the other two are the one
1014    /// that takes one away. The register is the one it had and the number is gone, the shorter
1015    /// instruction being the one that carries the number in its opcode.
1016    #[test]
1017    fn adding_or_taking_away_one_becomes_the_instruction_that_says_so_in_its_opcode() {
1018        for (name, by, into) in [
1019            ("add_ri_8", 1, "inc_r_8"),
1020            ("add_ri_16", 1, "inc_r_16"),
1021            ("add_ri_32", 1, "inc_r_32"),
1022            ("add_ri_64", 1, "inc_r_64"),
1023            ("add_ri_8", -1, "dec_r_8"),
1024            ("add_ri_16", -1, "dec_r_16"),
1025            ("add_ri_32", -1, "dec_r_32"),
1026            ("add_ri_64", -1, "dec_r_64"),
1027            ("sub_ri_8", 1, "dec_r_8"),
1028            ("sub_ri_16", 1, "dec_r_16"),
1029            ("sub_ri_32", 1, "dec_r_32"),
1030            ("sub_ri_64", 1, "dec_r_64"),
1031            ("sub_ri_8", -1, "inc_r_8"),
1032            ("sub_ri_16", -1, "inc_r_16"),
1033            ("sub_ri_32", -1, "inc_r_32"),
1034            ("sub_ri_64", -1, "inc_r_64"),
1035        ] {
1036            let (mut names, mut func, block) = empty();
1037            let value = func.new_vreg(GPR);
1038            let add = op(&mut names, name);
1039            let inst =
1040                func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(by).finish();
1041
1042            assert_eq!(small(&mut func, &mut names), 1, "{name} {by}");
1043            assert_eq!(shape(&func, &names, block), [into], "{name} {by}");
1044            assert_eq!(regs(&func, inst), [value, value], "{name} {by}");
1045            assert_eq!(imm(&func, inst), None, "{name} {by}");
1046        }
1047    }
1048
1049    /// The same function at a level that asked for fast code, which is the goal doing its job. This
1050    /// is the only rewrite in the pass that asks it, and it is the only one that is a trade.
1051    #[test]
1052    fn a_level_that_wanted_fast_code_keeps_the_addition() {
1053        let (mut names, mut func, block) = empty();
1054        let value = func.new_vreg(GPR);
1055        let add = op(&mut names, "add_ri_32");
1056        let inst = func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1057
1058        assert_eq!(takes(&mut func, &mut names), 0);
1059        assert_eq!(shape(&func, &names, block), ["add_ri_32"]);
1060        assert_eq!(imm(&func, inst), Some(1));
1061    }
1062
1063    /// Any other number. The machine has an opcode that means one and none that means anything
1064    /// else, so the constant is written out either way and the addition is already as short as it
1065    /// gets.
1066    #[test]
1067    fn adding_anything_but_one_stays_an_addition() {
1068        for by in [0, 2, -2, 7, 255, i64::from(i32::MIN)] {
1069            let (mut names, mut func, block) = empty();
1070            let value = func.new_vreg(GPR);
1071            let add = op(&mut names, "add_ri_32");
1072            func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(by).finish();
1073
1074            assert_eq!(small(&mut func, &mut names), 0, "{by}");
1075            assert_eq!(shape(&func, &names, block), ["add_ri_32"], "{by}");
1076        }
1077    }
1078
1079    /// What the rewrite is really conditional on. Something behind it reading where the value sits
1080    /// as an unsigned number is something reading the carry, and the carry is the one part of the
1081    /// condition state the shorter instruction does not write.
1082    #[test]
1083    fn an_addition_whose_carry_something_reads_stays_an_addition() {
1084        for reader in ["set_b", "set_be", "set_a", "set_ae", "adc_ri_32"] {
1085            let (mut names, mut func, block) = empty();
1086            let value = func.new_vreg(GPR);
1087            let byte = func.new_vreg(GPR);
1088            let add = op(&mut names, "add_ri_32");
1089            let reads = op(&mut names, reader);
1090            func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1091            func.build(block, reads).def(byte, GPR).finish();
1092
1093            assert_eq!(small(&mut func, &mut names), 0, "{reader}");
1094            assert_eq!(shape(&func, &names, block), ["add_ri_32", reader], "{reader}");
1095        }
1096    }
1097
1098    /// A reader of any other part of the state, which the shorter instruction writes exactly as the
1099    /// addition did. So the rewrite is not about whether the state is read, it is about which part.
1100    #[test]
1101    fn an_addition_whose_zero_or_sign_something_reads_still_steps() {
1102        for reader in ["set_e", "set_ne", "set_l", "set_le", "set_g", "set_ge"] {
1103            let (mut names, mut func, block) = empty();
1104            let value = func.new_vreg(GPR);
1105            let byte = func.new_vreg(GPR);
1106            let add = op(&mut names, "add_ri_32");
1107            let reads = op(&mut names, reader);
1108            func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1109            func.build(block, reads).def(byte, GPR).finish();
1110
1111            assert_eq!(small(&mut func, &mut names), 1, "{reader}");
1112            assert_eq!(shape(&func, &names, block), ["inc_r_32", reader], "{reader}");
1113        }
1114    }
1115
1116    /// The carry read behind an instruction that writes the rest of the state, which is what the
1117    /// walk asking the description rather than the flag is for. The addition in front of the
1118    /// comparison stays, because the comparison writes the carry the reader wants and the addition
1119    /// would not have to, and the addition behind it goes, because nothing reads a carry after it.
1120    #[test]
1121    fn a_write_of_the_state_ends_the_life_of_the_carry_and_a_step_does_not() {
1122        let (mut names, mut func, block) = empty();
1123        let value = func.new_vreg(GPR);
1124        let byte = func.new_vreg(GPR);
1125        let add = op(&mut names, "add_ri_32");
1126        let cmp = op(&mut names, "cmp_rr_32");
1127        let below = op(&mut names, "set_b");
1128        let first = func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1129        func.build(block, cmp).uses(value, GPR).uses(value, GPR).finish();
1130        func.build(block, below).def(byte, GPR).finish();
1131
1132        assert_eq!(small(&mut func, &mut names), 1);
1133        assert_eq!(shape(&func, &names, block), ["inc_r_32", "cmp_rr_32", "set_b"]);
1134        assert_eq!(imm(&func, first), None);
1135    }
1136
1137    /// An instruction that leaves the carry alone is not a write of the condition state as far as
1138    /// the walk is concerned, which is what stops one of them from hiding a carry read behind it.
1139    /// The increment here is one a program wrote in a template rather than one this put there, and
1140    /// the addition in front of it is the only thing that sets the carry the reader wants, so the
1141    /// addition stays.
1142    #[test]
1143    fn a_step_does_not_hide_the_carry_read_behind_it() {
1144        let (mut names, mut func, block) = empty();
1145        let value = func.new_vreg(GPR);
1146        let byte = func.new_vreg(GPR);
1147        let add = op(&mut names, "add_ri_32");
1148        let step = op(&mut names, "inc_r_32");
1149        let below = op(&mut names, "set_b");
1150        let first = func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1151        func.build(block, step).operand(reuse(value)).uses(value, GPR).finish();
1152        func.build(block, below).def(byte, GPR).finish();
1153
1154        assert_eq!(small(&mut func, &mut names), 0);
1155        assert_eq!(shape(&func, &names, block), ["add_ri_32", "inc_r_32", "set_b"]);
1156        assert_eq!(imm(&func, first), Some(1));
1157    }
1158
1159    /// Two additions in a row with a carry read behind them. The second one sets the carry the
1160    /// reader wants and stays, and the first one goes, because whatever the first leaves the second
1161    /// writes over. That is the same walk as the test above arriving at the other answer, and it is
1162    /// what says the rule is about the carry rather than about the addition.
1163    #[test]
1164    fn an_addition_the_next_addition_writes_over_still_steps() {
1165        let (mut names, mut func, block) = empty();
1166        let value = func.new_vreg(GPR);
1167        let byte = func.new_vreg(GPR);
1168        let add = op(&mut names, "add_ri_32");
1169        let below = op(&mut names, "set_b");
1170        func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1171        let second = func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1172        func.build(block, below).def(byte, GPR).finish();
1173
1174        assert_eq!(small(&mut func, &mut names), 1);
1175        assert_eq!(shape(&func, &names, block), ["inc_r_32", "add_ri_32", "set_b"]);
1176        assert_eq!(imm(&func, second), Some(1));
1177    }
1178
1179    /// The instruction the whole function check used to stop at. A comparison that keeps a byte
1180    /// reads the condition state and the state it reads is the one it wrote itself a moment
1181    /// earlier, so a block opening with one is not a block reading what a predecessor left, and the
1182    /// move in the other block is rewritten.
1183    #[test]
1184    fn a_block_opening_with_a_comparison_that_keeps_a_byte_is_not_a_carried_state() {
1185        let (mut names, mut func, first) = empty();
1186        let second = func.create_block();
1187        let into = func.new_vreg(GPR);
1188        let byte = func.new_vreg(GPR);
1189        let value = func.new_vreg(GPR);
1190        let zero = op(&mut names, "mov_ri_32");
1191        let fused = op(&mut names, "cmp_set_e_32");
1192        func.build(first, zero).def(into, GPR).imm(0).finish();
1193        func.build(second, fused).def(byte, GPR).uses(value, GPR).uses(value, GPR).finish();
1194
1195        assert_eq!(takes(&mut func, &mut names), 1);
1196        assert_eq!(shape(&func, &names, first), ["xor_rr_32"]);
1197    }
1198
1199    /// The same with the comparison's operand in memory, which is the shape a loop reading an array
1200    /// and counting with tier six's `setcc` and `movzbl` comes out as. The comparison table leaves
1201    /// these out, and before the description named them apart this turned the function down.
1202    #[test]
1203    fn a_block_opening_with_a_comparison_against_memory_is_not_a_carried_state() {
1204        let (mut names, mut func, first) = empty();
1205        let second = func.create_block();
1206        let into = func.new_vreg(GPR);
1207        let byte = func.new_vreg(GPR);
1208        let value = func.new_vreg(GPR);
1209        let base = func.new_vreg(GPR);
1210        let zero = op(&mut names, "mov_ri_32");
1211        let fused = op(&mut names, "cmp_set_g_rm_32");
1212        func.build(first, zero).def(into, GPR).imm(0).finish();
1213        func.build(second, fused).def(byte, GPR).uses(value, GPR).uses(base, GPR).finish();
1214
1215        assert_eq!(takes(&mut func, &mut names), 1);
1216        assert_eq!(shape(&func, &names, first), ["xor_rr_32"]);
1217    }
1218
1219    /// The same sentence inside a block. What the comparison reads is what it wrote, so what it was
1220    /// handed is written over before anything looks at it, and the move in front of it may spend a
1221    /// state nothing wants.
1222    #[test]
1223    fn a_comparison_that_keeps_a_byte_ends_the_life_of_the_state() {
1224        let (mut names, mut func, block) = empty();
1225        let into = func.new_vreg(GPR);
1226        let byte = func.new_vreg(GPR);
1227        let value = func.new_vreg(GPR);
1228        let zero = op(&mut names, "mov_ri_32");
1229        let fused = op(&mut names, "cmp_set_e_32");
1230        func.build(block, zero).def(into, GPR).imm(0).finish();
1231        func.build(block, fused).def(byte, GPR).uses(value, GPR).uses(value, GPR).finish();
1232
1233        assert_eq!(takes(&mut func, &mut names), 1);
1234        assert_eq!(shape(&func, &names, block), ["xor_rr_32", "cmp_set_e_32"]);
1235    }
1236
1237    /// And the carry with it. A comparison that keeps a byte and asks where a value sits as an
1238    /// unsigned number reads the carry, and it is the carry it set itself, so the addition in front
1239    /// of it is free to become the instruction that leaves the carry alone.
1240    #[test]
1241    fn a_comparison_that_keeps_a_byte_does_not_keep_the_carry_alive() {
1242        let (mut names, mut func, block) = empty();
1243        let value = func.new_vreg(GPR);
1244        let byte = func.new_vreg(GPR);
1245        let add = op(&mut names, "add_ri_32");
1246        let fused = op(&mut names, "cmp_set_b_32");
1247        func.build(block, add).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1248        func.build(block, fused).def(byte, GPR).uses(value, GPR).uses(value, GPR).finish();
1249
1250        assert_eq!(small(&mut func, &mut names), 1);
1251        assert_eq!(shape(&func, &names, block), ["inc_r_32", "cmp_set_b_32"]);
1252    }
1253
1254    /// The other kind of read, which is the one this must go on stopping at. An add with carry is
1255    /// reading the bit the instruction in front of it left rather than one it wrote itself, and it
1256    /// makes no comparison, which is how the description tells the two apart.
1257    #[test]
1258    fn an_add_with_carry_opening_a_block_is_still_a_carried_state() {
1259        let (mut names, mut func, first) = empty();
1260        let second = func.create_block();
1261        let into = func.new_vreg(GPR);
1262        let value = func.new_vreg(GPR);
1263        let zero = op(&mut names, "mov_ri_32");
1264        let adc = op(&mut names, "adc_ri_32");
1265        func.build(first, zero).def(into, GPR).imm(0).finish();
1266        func.build(second, adc).operand(reuse(value)).uses(value, GPR).imm(1).finish();
1267
1268        assert_eq!(takes(&mut func, &mut names), 0);
1269        assert_eq!(shape(&func, &names, first), ["mov_ri_32"]);
1270    }
1271
1272    /// A name the description does not cover, which is anything without this target's prefix. It
1273    /// may read the condition state and it may write one, and the answer that is wrong about
1274    /// nothing is that it read it, so the move in front of it stays.
1275    #[test]
1276    fn a_name_this_target_does_not_know_stops_the_walk() {
1277        let (mut names, mut func, block) = empty();
1278        let left = func.new_vreg(GPR);
1279        let right = func.new_vreg(GPR);
1280        let into = func.new_vreg(GPR);
1281        let cmp = op(&mut names, "cmp_rr_32");
1282        let zero = op(&mut names, "mov_ri_32");
1283        let strange = mir::Opcode::new(names.intern("nowhere.thing"));
1284        func.build(block, cmp).uses(left, GPR).uses(right, GPR).finish();
1285        func.build(block, zero).def(into, GPR).imm(0).finish();
1286        func.build(block, strange).finish();
1287
1288        assert_eq!(takes(&mut func, &mut names), 0);
1289        assert_eq!(shape(&func, &names, block), ["cmp_rr_32", "mov_ri_32", ""]);
1290    }
1291
1292    /// The fifth rewrite. An address that is a base register and nothing else is that register, so
1293    /// the instruction that works it out and keeps it is the move, which keeps both registers in the
1294    /// order it had them and drops the addressing mode it no longer has a place for.
1295    #[test]
1296    fn an_address_that_is_a_register_becomes_a_move() {
1297        let (mut names, mut func, block) = empty();
1298        let base = func.new_vreg(GPR);
1299        let into = func.new_vreg(GPR);
1300        let lea = op(&mut names, "lea_64");
1301        let mem = mir::Mem::at(mir::Operand::read(base, GPR));
1302        let inst = func.build(block, lea).def(into, GPR).mem(mem).finish();
1303
1304        assert_eq!(takes(&mut func, &mut names), 1);
1305        assert_eq!(shape(&func, &names, block), ["mov_rr_64"]);
1306        assert_eq!(regs(&func, inst), [into, base]);
1307        assert!(func[inst].mem.is_none());
1308    }
1309
1310    /// A constant added to the address, which is the shape most address computations have. The move
1311    /// adds nothing, so there is nothing here for it to say.
1312    #[test]
1313    fn an_address_with_a_constant_added_stays() {
1314        let (mut names, mut func, block) = empty();
1315        let base = func.new_vreg(GPR);
1316        let into = func.new_vreg(GPR);
1317        let lea = op(&mut names, "lea_64");
1318        let mem = mir::Mem { disp: 8, ..mir::Mem::at(mir::Operand::read(base, GPR)) };
1319        func.build(block, lea).def(into, GPR).mem(mem).finish();
1320
1321        assert_eq!(takes(&mut func, &mut names), 0);
1322        assert_eq!(shape(&func, &names, block), ["lea_64"]);
1323    }
1324
1325    /// An index, which is the other half of what an address computation is for. It is a
1326    /// multiplication and an addition and the move is neither.
1327    #[test]
1328    fn an_address_with_an_index_stays() {
1329        let (mut names, mut func, block) = empty();
1330        let base = func.new_vreg(GPR);
1331        let index = func.new_vreg(GPR);
1332        let into = func.new_vreg(GPR);
1333        let lea = op(&mut names, "lea_64");
1334        let mem = mir::Mem {
1335            index: Some(mir::Operand::read(index, GPR)),
1336            scale: 4,
1337            ..mir::Mem::at(mir::Operand::read(base, GPR))
1338        };
1339        func.build(block, lea).def(into, GPR).mem(mem).finish();
1340
1341        assert_eq!(takes(&mut func, &mut names), 0);
1342        assert_eq!(shape(&func, &names, block), ["lea_64"]);
1343    }
1344
1345    /// The address of a global, which names no register at all. What it works out is a number the
1346    /// assembler fills in rather than a number that is already somewhere, so there is nothing for a
1347    /// move to move.
1348    #[test]
1349    fn an_address_of_a_symbol_stays() {
1350        let (mut names, mut func, block) = empty();
1351        let into = func.new_vreg(GPR);
1352        let lea = op(&mut names, "lea_64");
1353        let mem = mir::Mem::of(names.intern("table"));
1354        func.build(block, lea).def(into, GPR).mem(mem).finish();
1355
1356        assert_eq!(takes(&mut func, &mut names), 0);
1357        assert_eq!(shape(&func, &names, block), ["lea_64"]);
1358    }
1359
1360    /// And it does not wait on the condition state. Neither the address computation nor the move
1361    /// writes any, so a byte reading a comparison from in front of it reads the same comparison
1362    /// afterwards, and the rewrite is taken in the one function the first rewrite has to turn down.
1363    #[test]
1364    fn an_address_is_copied_whatever_the_state_behind_it_is() {
1365        let (mut names, mut func, block) = empty();
1366        let left = func.new_vreg(GPR);
1367        let right = func.new_vreg(GPR);
1368        let base = func.new_vreg(GPR);
1369        let into = func.new_vreg(GPR);
1370        let byte = func.new_vreg(GPR);
1371        let cmp = op(&mut names, "cmp_rr_32");
1372        let lea = op(&mut names, "lea_64");
1373        let set = op(&mut names, "set_e");
1374        let mem = mir::Mem::at(mir::Operand::read(base, GPR));
1375        func.build(block, cmp).uses(left, GPR).uses(right, GPR).finish();
1376        func.build(block, lea).def(into, GPR).mem(mem).finish();
1377        func.build(block, set).def(byte, GPR).finish();
1378
1379        assert_eq!(takes(&mut func, &mut names), 1);
1380        assert_eq!(shape(&func, &names, block), ["cmp_rr_32", "mov_rr_64", "set_e"]);
1381    }
1382}