Skip to main content

rucc_codegen/
changes.rs

1//! Proposing a set of machine level changes, asking whether the target takes them, and either
2//! committing the set or dropping it.
3//!
4//! Design: `spec/optimizer/37-machine-level-optimization.md` sections 37.2 and 37.3.
5//!
6//! Section 37.3 quotes `gcc/combine.cc` on what a machine level rewrite is: substitute the earlier
7//! instruction into the later one, ask the machine description whether the result is an instruction
8//! this target has, install it if it is and put everything back if it is not. Section 37.2 reads
9//! `gcc/rtl-ssa/changes.cc` and says the same thing about the arrangement rather than the rewrite,
10//! which is that the propose, validate and commit belongs to one named component rather than to
11//! each pass in its own words. This is that component.
12//!
13//! # Nothing is written until the whole set is taken
14//!
15//! A proposal holds what the instruction would become by value: the operands in a vector of their
16//! own, the addressing mode as an [`mir::Amode`] rather than as a reference into the function's
17//! arena, the immediate as a number. So abandoning a set is dropping it and there is no undo to
18//! get wrong. That is the difference between this and GCC's, which edits the RTL in place and
19//! keeps a list of what to put back, and it is available here because machine IR keeps an
20//! instruction's parts in arenas the proposal can stay out of until the last moment.
21//!
22//! # Why a set rather than an instruction
23//!
24//! Because the interesting rewrites are all of them or none. Folding an address into the three
25//! instructions that read it is worth doing when the address computation goes, and folding it into
26//! two of the three is worth nothing: the computation stays for the third, the address is worked
27//! out twice, and the registers it reads are live across all three. `crate::fold` had that rule
28//! written into it by hand and so would every pass after it.
29//!
30//! # What the target is asked
31//!
32//! [`MachineInsts`] is the whole of it, and every question in it is answered out of the same
33//! description the allocator and the encoder read. An opcode this machine does not have, an
34//! operand vector that is not the shape the opcode's form says, an immediate on an instruction
35//! that carries none, an addressing mode on one that has none, a scale this machine cannot write:
36//! each of those is a refusal, and a refusal is the whole set's.
37//!
38//! What is checked beyond the target's description is the part that is about the function rather
39//! than about the machine. An instruction may be named once in a set, it has to still be in the
40//! function, and taking an instruction out is refused while anything still reads what it wrote.
41//! That last one is what the set is for, so [`Changes`] is the thing that knows it rather than
42//! each pass.
43//!
44//! # What the read counts are worth after allocation
45//!
46//! Less, and they are still true. A count is how many operands in the function name a register,
47//! and while machine IR is in SSA form that is the whole answer to whether anything reads what an
48//! instruction wrote, because the register is written once. Once the allocator has run it is not:
49//! `%rax` is written all over the function and a count of the reads of it is a count of the reads
50//! of every one of those writes together.
51//!
52//! What that costs is optimizations rather than correctness. A count of zero still means nothing
53//! anywhere reads the register, so a removal the framework takes is a removal nothing was reading;
54//! what it will not take is the many where the register is read further down about a different
55//! write. So a pass that runs after allocation and removes instructions has to have its own reason,
56//! which is why [`crate::copies`] has one and says what it is, and a pass that rewrites rather than
57//! removes has the whole of the framework as usual.
58//!
59//! # Reading a register somewhere else
60//!
61//! A pass that takes an instruction out has to send whatever read it somewhere, and what that is
62//! is one register in place of another in an instruction that is otherwise the instruction it
63//! already was. That is [`Changes::rename`], and it is a proposal of its own rather than a plan
64//! with one operand changed, because the shape is what the description has something to say about
65//! and a rename changes no shape. An instruction this machine has with one register in an operand
66//! is one it has with another of the same class, so the class is the whole of what is checked.
67//!
68//! It is also the only way to say it about the instructions whose operand vector the description
69//! does not name, which on this machine is a call. How many registers a call passes is a fact about
70//! the signature rather than about the instruction, so `crates/rucc-target/src/x86_64/insts.rs`
71//! writes nothing down for it and a plan for one would be turned down for a shape nobody ever
72//! claimed.
73//!
74//! # The arguments an edge carries
75//!
76//! Those are reads too, and they are in no operand vector. A block's parameters are where the
77//! values a block is reached with arrive, the arguments on the edge are where they come from, and
78//! a pass sending every reader of a register somewhere else has these to send as well.
79//! [`Changes::carry`] is that, and like a plan it is by value: what the edge would carry rather
80//! than what to do to what it carries.
81
82use rucc_base::{Interner, Symbol};
83use rucc_mir::{self as mir, Role};
84use rucc_target::MachineInsts;
85
86/// What an instruction would become.
87///
88/// Every part is held by value rather than as a reference into the function, which is what lets a
89/// proposal be dropped rather than undone. [`Changes::commit`] is what puts the parts in the
90/// arenas, and until it runs the function does not know this exists.
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct Plan {
93    /// Which instruction it becomes.
94    pub opcode: mir::Opcode,
95    /// Its operands, the ones it writes before the ones it reads, with the registers its
96    /// addressing mode names last. The order is the one [`mir::InstBuilder`] keeps and the
97    /// printer, the parser and the allocator all read.
98    pub operands: Vec<mir::Operand>,
99    /// Its immediate, if the instruction carries one.
100    pub imm: Option<i64>,
101    /// Its addressing mode, if the instruction has one. The base and the index are positions in
102    /// `operands`.
103    pub amode: Option<mir::Amode>,
104    /// The symbol it names, which is the callee of a direct call.
105    pub symbol: Option<Symbol>,
106}
107
108impl Plan {
109    /// The instruction as it stands, which is where a rewrite starts from.
110    #[must_use]
111    pub fn of(func: &mir::Func, inst: mir::Inst) -> Self {
112        let data = &func[inst];
113        Self {
114            opcode: data.opcode,
115            operands: func[data.operands].to_vec(),
116            imm: data.imm.map(|at| func[at].0),
117            amode: data.mem.map(|at| func[at]),
118            symbol: data.symbol,
119        }
120    }
121
122    /// The registers it reads, which is what a removal has to count.
123    fn reads(&self) -> impl Iterator<Item = mir::Reg> + use<'_> {
124        self.operands.iter().filter(|operand| operand.role == Role::Use).map(|operand| operand.reg)
125    }
126}
127
128/// Why the target or the function would not have a set.
129///
130/// One instruction's refusal rather than the set's, because a pass that wants to know what it did
131/// wrong wants to know where, and because the tests below are clearer for it. Every one of them
132/// turns down the set it is in.
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum Refusal {
135    /// The set names that instruction twice, so what it becomes depends on which half wins.
136    Twice(mir::Inst),
137    /// That instruction is not in the function, which is a set built against a function something
138    /// else has changed since.
139    Gone(mir::Inst),
140    /// This target has no instruction of that name.
141    Unknown(mir::Inst),
142    /// The operand vector is not the shape the opcode's description says it is.
143    Operands(mir::Inst),
144    /// An immediate on an instruction that carries none, or none on one that does.
145    Imm(mir::Inst),
146    /// An addressing mode on an instruction that has none, or none on one that does.
147    Mem(mir::Inst),
148    /// An index multiplied by something this machine cannot write.
149    Scale(mir::Inst),
150    /// An index and a displacement in one address, on a machine whose addresses hold one or the
151    /// other.
152    Crowded(mir::Inst),
153    /// Taking that instruction out would leave something reading a register it wrote.
154    Read(mir::Inst),
155    /// A rename would put a register of one class where the instruction reads another.
156    Class(mir::Inst),
157    /// The set says twice what one edge carries, or the block it leaves has no such edge.
158    Edge(mir::Block, usize),
159    /// What the set would have an edge carry is not as many values as the block it goes to takes.
160    Args(mir::Block, usize),
161}
162
163/// How many times each register is read, kept across the commits of one pass.
164///
165/// A removal has to know whether anything still reads what the instruction wrote, and asking the
166/// function that question once per commit is the length of the function once per commit. So it is
167/// asked once and the answer is carried, which each commit brings up to date with what it did.
168///
169/// A virtual register is counted by its number in a list rather than in a map, since the numbers
170/// run from nought with no gaps and this is asked about every operand of every function. The few
171/// physical registers an instruction names before allocation are counted the same way by their
172/// number, which is one byte. They used to go in a map, and hashing one was most of what counting
173/// it cost.
174#[derive(Debug, Clone, Default)]
175pub struct Reads {
176    virtuals: Vec<usize>,
177    physical: Vec<usize>,
178}
179
180impl Reads {
181    /// Every read in the function, counting the arguments an edge carries as reads, which they
182    /// are.
183    #[must_use]
184    pub fn of(func: &mir::Func) -> Self {
185        let mut reads = Self { virtuals: vec![0; func.vregs()], physical: Vec::new() };
186        for block in func.blocks() {
187            for inst in func.insts(block) {
188                for operand in &func[func[inst].operands] {
189                    if operand.role == Role::Use {
190                        reads.gained(operand.reg);
191                    }
192                }
193            }
194            for call in &func[block].succs {
195                for &arg in &call.args {
196                    reads.gained(arg);
197                }
198            }
199        }
200        reads
201    }
202
203    /// How many reads of that register there are.
204    #[must_use]
205    pub fn count(&self, reg: mir::Reg) -> usize {
206        match reg.number() {
207            Some(number) => self.virtuals.get(number as usize).copied().unwrap_or(0),
208            None => self.physical.get(physical(reg)).copied().unwrap_or(0),
209        }
210    }
211
212    /// Records one read more.
213    fn gained(&mut self, reg: mir::Reg) {
214        match reg.number() {
215            Some(number) => {
216                let number = number as usize;
217                if number >= self.virtuals.len() {
218                    self.virtuals.resize(number + 1, 0);
219                }
220                self.virtuals[number] += 1;
221            }
222            None => {
223                let number = physical(reg);
224                if number >= self.physical.len() {
225                    self.physical.resize(number + 1, 0);
226                }
227                self.physical[number] += 1;
228            }
229        }
230    }
231
232    /// Records one read fewer.
233    fn lost(&mut self, reg: mir::Reg) {
234        let count = match reg.number() {
235            Some(number) => self.virtuals.get_mut(number as usize),
236            None => self.physical.get_mut(physical(reg)),
237        };
238        if let Some(count) = count {
239            *count = count.saturating_sub(1);
240        }
241    }
242}
243
244/// The number of a physical register, which is where [`Reads`] counts it.
245fn physical(reg: mir::Reg) -> usize {
246    reg.phys().map_or(0, |phys| usize::from(phys.number()))
247}
248
249/// What one instruction in a set would have done to it.
250#[derive(Debug, Clone, PartialEq, Eq)]
251enum What {
252    /// Becomes that.
253    Rewrite(Plan),
254    /// Reads the second register wherever it reads the first, and is otherwise the instruction it
255    /// already is.
256    Rename { from: mir::Reg, into: mir::Reg },
257    /// Goes.
258    Remove,
259}
260
261/// What a set would have one edge carry.
262#[derive(Debug, Clone, PartialEq, Eq)]
263struct Carried {
264    /// The block the edge leaves.
265    from: mir::Block,
266    /// Which of that block's edges, in the order the block holds them.
267    at: usize,
268    /// The registers it would carry, one per parameter of the block it goes to.
269    args: Vec<mir::Reg>,
270}
271
272/// A set of changes to one function, proposed together and taken together.
273///
274/// The order proposals are added in is the order they are applied in, which matters only for the
275/// reading of a commit that both rewrites and removes: nothing here depends on it, since a plan
276/// says what an instruction becomes rather than what to do to what it is.
277#[derive(Debug, Clone, Default)]
278pub struct Changes {
279    changes: Vec<(mir::Inst, What)>,
280    carries: Vec<Carried>,
281}
282
283impl Changes {
284    /// A set with nothing in it.
285    #[must_use]
286    pub fn new() -> Self {
287        Self::default()
288    }
289
290    /// How many changes the set is about, counting the edges along with the instructions.
291    #[must_use]
292    pub fn len(&self) -> usize {
293        self.changes.len() + self.carries.len()
294    }
295
296    /// Whether the set is about nothing, which commits and changes nothing.
297    #[must_use]
298    pub fn is_empty(&self) -> bool {
299        self.changes.is_empty() && self.carries.is_empty()
300    }
301
302    /// Proposes that the instruction become that.
303    pub fn rewrite(&mut self, inst: mir::Inst, plan: Plan) {
304        self.changes.push((inst, What::Rewrite(plan)));
305    }
306
307    /// Proposes that the instruction read the second register wherever it reads the first.
308    ///
309    /// Only where it reads it. What an instruction writes is what the rest of the function knows it
310    /// by, and changing that is a different proposal from this one.
311    pub fn rename(&mut self, inst: mir::Inst, from: mir::Reg, into: mir::Reg) {
312        self.changes.push((inst, What::Rename { from, into }));
313    }
314
315    /// Proposes that the edge leaving that block carry those registers.
316    ///
317    /// Which edge is its position in the block's own list of them, which is what
318    /// [`mir::Func::succs_mut`] hands back and what the terminator's conditions are written against.
319    pub fn carry(&mut self, from: mir::Block, at: usize, args: Vec<mir::Reg>) {
320        self.carries.push(Carried { from, at, args });
321    }
322
323    /// Proposes that the instruction go.
324    pub fn remove(&mut self, inst: mir::Inst) {
325        self.changes.push((inst, What::Remove));
326    }
327
328    /// Why the set would not be taken, or `None` if it would.
329    ///
330    /// Asked of the function as it stands and of the target's description of itself. Nothing here
331    /// changes anything, so a pass may ask, decide the answer is not worth having, and drop the
332    /// set.
333    #[must_use]
334    pub fn refused(
335        &self,
336        func: &mir::Func,
337        reads: &Reads,
338        names: &Interner,
339        machine: &MachineInsts,
340    ) -> Option<Refusal> {
341        for (at, &(inst, _)) in self.changes.iter().enumerate() {
342            if self.changes[..at].iter().any(|&(other, _)| other == inst) {
343                return Some(Refusal::Twice(inst));
344            }
345            if func.block_of(inst).is_none() {
346                return Some(Refusal::Gone(inst));
347            }
348        }
349        for (at, carried) in self.carries.iter().enumerate() {
350            let edge = (carried.from, carried.at);
351            if self.carries[..at].iter().any(|other| (other.from, other.at) == edge) {
352                return Some(Refusal::Edge(carried.from, carried.at));
353            }
354            let Some(call) = func[carried.from].succs.get(carried.at) else {
355                return Some(Refusal::Edge(carried.from, carried.at));
356            };
357            if func[call.block].params.len() != carried.args.len() {
358                return Some(Refusal::Args(carried.from, carried.at));
359            }
360        }
361        for &(inst, ref what) in &self.changes {
362            match what {
363                What::Rewrite(plan) => {
364                    if let Some(refusal) = shaped(inst, plan, names, machine) {
365                        return Some(refusal);
366                    }
367                }
368                What::Rename { from, into } => {
369                    if let Some(refusal) = renamed(func, inst, *from, *into) {
370                        return Some(refusal);
371                    }
372                }
373                What::Remove => {
374                    if self.read_after(func, reads, inst) {
375                        return Some(Refusal::Read(inst));
376                    }
377                }
378            }
379        }
380        None
381    }
382
383    /// Takes the set if the target and the function will have it, and gives back how many changes
384    /// it made.
385    ///
386    /// # Errors
387    ///
388    /// The first [`Refusal`] the set earns, with nothing written. A refused set leaves the
389    /// function exactly as it was.
390    pub fn commit(
391        self,
392        func: &mut mir::Func,
393        reads: &mut Reads,
394        names: &Interner,
395        machine: &MachineInsts,
396    ) -> Result<usize, Refusal> {
397        if let Some(refusal) = self.refused(func, reads, names, machine) {
398            return Err(refusal);
399        }
400        let touched = self.len();
401        for (inst, what) in self.changes {
402            let (lost, gained) = moved(func, inst, &what);
403            for reg in lost {
404                reads.lost(reg);
405            }
406            for reg in gained {
407                reads.gained(reg);
408            }
409            match what {
410                What::Rewrite(plan) => {
411                    let operands = func.push_operands(&plan.operands);
412                    let imm = plan.imm.map(|value| func.add_imm(value));
413                    let mem = plan.amode.map(|amode| func.add_amode(amode));
414                    let data = &mut func[inst];
415                    data.opcode = plan.opcode;
416                    data.operands = operands;
417                    data.imm = imm;
418                    data.mem = mem;
419                    data.symbol = plan.symbol;
420                }
421                What::Rename { from, into } => {
422                    let operands = func[inst].operands;
423                    for operand in &mut func[operands] {
424                        if operand.role == Role::Use && operand.reg == from {
425                            operand.reg = into;
426                        }
427                    }
428                }
429                What::Remove => func.remove_inst(inst),
430            }
431        }
432        for carried in self.carries {
433            for &arg in &func[carried.from].succs[carried.at].args {
434                reads.lost(arg);
435            }
436            for &arg in &carried.args {
437                reads.gained(arg);
438            }
439            func.succs_mut(carried.from)[carried.at].args = carried.args;
440        }
441        Ok(touched)
442    }
443
444    /// Whether anything the set leaves behind reads a register that instruction writes.
445    ///
446    /// The counts are of the function as it stands, so what the set is about has to be taken off
447    /// them: a read something in the set stops doing is a read that is going, and one it takes up
448    /// is a read that is arriving. What is left after that is the reads nothing in this set is
449    /// doing anything about, and one of those is enough to keep the instruction where it is.
450    fn read_after(&self, func: &mir::Func, reads: &Reads, inst: mir::Inst) -> bool {
451        func[func[inst].operands].iter().filter(|operand| operand.role.is_def()).any(|operand| {
452            let mut left = reads.count(operand.reg);
453            let mut settle = |lost: &[mir::Reg], gained: &[mir::Reg]| {
454                let goes = lost.iter().filter(|&&reg| reg == operand.reg).count();
455                left = left.saturating_sub(goes);
456                left += gained.iter().filter(|&&reg| reg == operand.reg).count();
457            };
458            for &(other, ref what) in &self.changes {
459                let (lost, gained) = moved(func, other, what);
460                settle(&lost, &gained);
461            }
462            for carried in &self.carries {
463                settle(&func[carried.from].succs[carried.at].args, &carried.args);
464            }
465            left != 0
466        })
467    }
468}
469
470/// The reads one change takes away from its instruction and the reads it gives it.
471///
472/// Of the instruction as it stands, since that is what the counts being kept up to date are of. A
473/// plan is the whole operand vector, so every read the instruction had goes and every read the plan
474/// has arrives. A rename is the reads of the one register, which become that many of the other. A
475/// removal is every read it had and nothing back.
476fn moved(func: &mir::Func, inst: mir::Inst, what: &What) -> (Vec<mir::Reg>, Vec<mir::Reg>) {
477    let held: Vec<mir::Reg> = func[func[inst].operands]
478        .iter()
479        .filter(|operand| operand.role == Role::Use)
480        .map(|operand| operand.reg)
481        .collect();
482    match what {
483        What::Rewrite(plan) => (held, plan.reads().collect()),
484        What::Rename { from, into } => {
485            let gone: Vec<mir::Reg> = held.into_iter().filter(|reg| reg == from).collect();
486            let back = vec![*into; gone.len()];
487            (gone, back)
488        }
489        What::Remove => (held, Vec::new()),
490    }
491}
492
493/// Why a rename would not be taken, or `None` if it would.
494///
495/// A rename changes no shape, so the description has nothing to say about it beyond the one thing
496/// it says about every register operand, which is the class. A physical register has no class in
497/// the function to check against, and by the time there are any of those the allocator has already
498/// had the say about which registers an instruction may name.
499fn renamed(func: &mir::Func, inst: mir::Inst, from: mir::Reg, into: mir::Reg) -> Option<Refusal> {
500    let class = func.class_of(into)?;
501    func[func[inst].operands]
502        .iter()
503        .any(|operand| operand.role == Role::Use && operand.reg == from && operand.class != class)
504        .then_some(Refusal::Class(inst))
505}
506
507/// Why the target would not have that instruction, or `None` if it would.
508///
509/// The description says which operands the instruction has, which is the ones it writes and then
510/// the ones it reads, and it stops there: the registers an addressing mode names are operands the
511/// addressing mode knows the positions of, so what is checked about those is that they are reads,
512/// that the mode points at them, and that there are no others.
513///
514/// The constraint is checked along with the class and the role, because it is part of what the
515/// instruction is rather than part of what a pass may choose. An `add` on this machine writes its
516/// answer into the register it read, the description says so with a reuse constraint, and a
517/// proposal that leaves the constraint off is asking for an instruction this machine has no
518/// encoding for. Nothing downstream would catch it either: the allocator gives an operand whatever
519/// its constraint asks for, so a missing constraint is a register pair that is allocated apart and
520/// then printed as one instruction.
521fn shaped(
522    inst: mir::Inst,
523    plan: &Plan,
524    names: &Interner,
525    machine: &MachineInsts,
526) -> Option<Refusal> {
527    let name = names.resolve(plan.opcode.name());
528    let bare = machine.bare(name);
529    let Some(desc) = (machine.operands)(bare) else { return Some(Refusal::Unknown(inst)) };
530    if plan.operands.len() < desc.len() {
531        return Some(Refusal::Operands(inst));
532    }
533    let (described, addressed) = plan.operands.split_at(desc.len());
534    for (operand, want) in described.iter().zip(desc) {
535        let shape = (operand.class, operand.role, operand.constraint);
536        if shape != (want.class, want.role, want.constraint) {
537            return Some(Refusal::Operands(inst));
538        }
539    }
540    if plan.imm.is_some() != (machine.takes_imm)(bare) {
541        return Some(Refusal::Imm(inst));
542    }
543    let Some(amode) = plan.amode else {
544        return ((machine.takes_mem)(bare) || !addressed.is_empty()).then_some(Refusal::Mem(inst));
545    };
546    if !(machine.takes_mem)(bare) {
547        return Some(Refusal::Mem(inst));
548    }
549    let named = [amode.base, amode.index].into_iter().flatten();
550    let mut wanted = 0;
551    for at in named {
552        let Some(operand) = plan.operands.get(usize::from(at)) else {
553            return Some(Refusal::Operands(inst));
554        };
555        if usize::from(at) < desc.len() || operand.role != Role::Use {
556            return Some(Refusal::Operands(inst));
557        }
558        wanted += 1;
559    }
560    if addressed.len() != wanted {
561        return Some(Refusal::Operands(inst));
562    }
563    let scaled = if amode.index.is_some() { amode.scale } else { 1 };
564    if !machine.scales(scaled) || (amode.index.is_none() && amode.scale != 1) {
565        return Some(Refusal::Scale(inst));
566    }
567    if amode.index.is_some() && amode.disp != 0 && !machine.index_and_disp {
568        return Some(Refusal::Crowded(inst));
569    }
570    None
571}
572
573#[cfg(test)]
574mod tests {
575    use rucc_mir::Constraint;
576    use rucc_target::x86_64::{GPR, MACHINE, XMM};
577
578    use super::*;
579
580    /// A function with one block, and the names it was built with.
581    fn empty() -> (Interner, mir::Func, mir::Block) {
582        let mut names = Interner::new();
583        let mut func = mir::Func::new(names.intern("f"));
584        let block = func.create_block();
585        (names, func, block)
586    }
587
588    /// The opcode of that name on this target.
589    fn op(names: &mut Interner, name: &str) -> mir::Opcode {
590        mir::Opcode::new(names.intern(&format!("{}{name}", MACHINE.prefix)))
591    }
592
593    /// Whether the target and the function would have the set.
594    fn refused(func: &mir::Func, names: &Interner, set: &Changes) -> Option<Refusal> {
595        set.refused(func, &Reads::of(func), names, &MACHINE)
596    }
597
598    /// What every instruction in a block came to, as opcodes.
599    fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
600        func.insts(block).map(|inst| names.resolve(func[inst].opcode.name()).to_owned()).collect()
601    }
602
603    /// A copy, which is the smallest instruction with an operand of each kind.
604    fn copy(
605        func: &mut mir::Func,
606        names: &mut Interner,
607        block: mir::Block,
608    ) -> (mir::Inst, mir::Reg) {
609        let from = func.new_vreg(GPR);
610        let into = func.new_vreg(GPR);
611        let mov = op(names, "mov_rr_64");
612        (func.build(block, mov).def(into, GPR).uses(from, GPR).finish(), into)
613    }
614
615    /// The shape the whole thing is for: an instruction becomes another the target has, and the
616    /// function says so afterwards.
617    #[test]
618    fn a_rewrite_the_target_has_is_taken() {
619        let (mut names, mut func, block) = empty();
620        let (mov, into) = copy(&mut func, &mut names, block);
621        let base = func.new_vreg(GPR);
622        let load = op(&mut names, "mov_rm_64");
623        let mut set = Changes::new();
624        set.rewrite(
625            mov,
626            Plan {
627                opcode: load,
628                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
629                imm: None,
630                amode: Some(mir::Amode {
631                    base: Some(1),
632                    index: None,
633                    scale: 1,
634                    disp: 8,
635                    symbol: None,
636                    block: None,
637                    table: None,
638                    reach: mir::Reach::Itself,
639                    segment: None,
640                }),
641                symbol: None,
642            },
643        );
644
645        let mut reads = Reads::of(&func);
646        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
647
648        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
649        assert_eq!(func[func[mov].mem.expect("the load has an address")].disp, 8);
650        assert_eq!(reads.count(base), 1, "the address register is read now");
651        assert_eq!(reads.count(into), 0, "the register the copy read is read by nothing");
652    }
653
654    /// An opcode nobody described. The proposal is the pass's mistake rather than the target's, and
655    /// this is where it stops.
656    #[test]
657    fn an_opcode_this_target_does_not_have_is_refused() {
658        let (mut names, mut func, block) = empty();
659        let (mov, into) = copy(&mut func, &mut names, block);
660        let made_up = op(&mut names, "mov_rr_65");
661        let mut set = Changes::new();
662        set.rewrite(
663            mov,
664            Plan {
665                opcode: made_up,
666                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
667                imm: None,
668                amode: None,
669                symbol: None,
670            },
671        );
672
673        assert_eq!(refused(&func, &names, &set), Some(Refusal::Unknown(mov)));
674        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"], "the function was written to");
675    }
676
677    /// An operand of the wrong class. The target's description of a copy between general registers
678    /// says both are general registers, and a proposal that reads a vector register is a different
679    /// instruction with the same name.
680    #[test]
681    fn an_operand_of_the_wrong_class_is_refused() {
682        let (mut names, mut func, block) = empty();
683        let (mov, into) = copy(&mut func, &mut names, block);
684        let float = func.new_vreg(XMM);
685        let same = func[mov].opcode;
686        let mut set = Changes::new();
687        set.rewrite(
688            mov,
689            Plan {
690                opcode: same,
691                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(float, XMM)],
692                imm: None,
693                amode: None,
694                symbol: None,
695            },
696        );
697
698        assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
699    }
700
701    /// The constraint is part of the instruction. An `add` writes its answer where it read its
702    /// first operand, and a proposal that leaves that off is asking for an encoding this machine
703    /// does not have.
704    #[test]
705    fn an_add_whose_answer_is_not_tied_to_its_source_is_refused() {
706        let (mut names, mut func, block) = empty();
707        let (mov, into) = copy(&mut func, &mut names, block);
708        let other = func.new_vreg(GPR);
709        let add = op(&mut names, "add_rr_64");
710        let loose = vec![
711            mir::Operand::write(into, GPR),
712            mir::Operand::read(into, GPR),
713            mir::Operand::read(other, GPR),
714        ];
715        let mut set = Changes::new();
716        set.rewrite(
717            mov,
718            Plan { opcode: add, operands: loose.clone(), imm: None, amode: None, symbol: None },
719        );
720        assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
721
722        let mut tied = loose;
723        tied[0] = tied[0].with(Constraint::Reuse(1));
724        let mut set = Changes::new();
725        set.rewrite(
726            mov,
727            Plan { opcode: add, operands: tied, imm: None, amode: None, symbol: None },
728        );
729        assert_eq!(refused(&func, &names, &set), None, "the same instruction written properly");
730    }
731
732    /// An immediate belongs to the instructions that carry one, and to no others. Both ways round,
733    /// because a pass that drops an immediate is as wrong as one that invents it.
734    #[test]
735    fn an_immediate_has_to_be_there_exactly_when_the_instruction_carries_one() {
736        let (mut names, mut func, block) = empty();
737        let (mov, into) = copy(&mut func, &mut names, block);
738        let same = func[mov].opcode;
739        let add = op(&mut names, "add_ri_64");
740        let mut set = Changes::new();
741        set.rewrite(
742            mov,
743            Plan {
744                opcode: same,
745                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
746                imm: Some(7),
747                amode: None,
748                symbol: None,
749            },
750        );
751        assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "a copy of seven");
752
753        let tied = vec![
754            mir::Operand::write(into, GPR).with(Constraint::Reuse(1)),
755            mir::Operand::read(into, GPR),
756        ];
757        let mut set = Changes::new();
758        set.rewrite(
759            mov,
760            Plan { opcode: add, operands: tied.clone(), imm: None, amode: None, symbol: None },
761        );
762        assert_eq!(refused(&func, &names, &set), Some(Refusal::Imm(mov)), "an add of nothing");
763
764        let mut set = Changes::new();
765        set.rewrite(
766            mov,
767            Plan { opcode: add, operands: tied, imm: Some(7), amode: None, symbol: None },
768        );
769        assert_eq!(refused(&func, &names, &set), None);
770    }
771
772    /// An address belongs to the instructions that have one. A copy with an address is a load and
773    /// has a different name, which is exactly the mistake a pass folding addresses can make.
774    #[test]
775    fn an_address_on_an_instruction_that_has_none_is_refused() {
776        let (mut names, mut func, block) = empty();
777        let (mov, into) = copy(&mut func, &mut names, block);
778        let base = func.new_vreg(GPR);
779        let same = func[mov].opcode;
780        let mut set = Changes::new();
781        set.rewrite(
782            mov,
783            Plan {
784                opcode: same,
785                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(base, GPR)],
786                imm: None,
787                amode: Some(mir::Amode {
788                    base: Some(1),
789                    index: None,
790                    scale: 1,
791                    disp: 0,
792                    symbol: None,
793                    block: None,
794                    table: None,
795                    reach: mir::Reach::Itself,
796                    segment: None,
797                }),
798                symbol: None,
799            },
800        );
801
802        assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
803    }
804
805    /// A load with no address at all, which is the same mistake the other way round.
806    #[test]
807    fn an_instruction_that_wants_an_address_and_has_none_is_refused() {
808        let (mut names, mut func, block) = empty();
809        let (mov, into) = copy(&mut func, &mut names, block);
810        let load = op(&mut names, "mov_rm_64");
811        let mut set = Changes::new();
812        set.rewrite(
813            mov,
814            Plan {
815                opcode: load,
816                operands: vec![mir::Operand::write(into, GPR)],
817                imm: None,
818                amode: None,
819                symbol: None,
820            },
821        );
822
823        assert_eq!(refused(&func, &names, &set), Some(Refusal::Mem(mov)));
824    }
825
826    /// A scale this machine cannot write. Folding an address into a reader is where a number like
827    /// this is worked out, and three is what a multiplication by three looks like halfway through
828    /// the fold.
829    #[test]
830    fn an_index_scaled_by_something_this_machine_cannot_write_is_refused() {
831        let (mut names, mut func, block) = empty();
832        let (mov, into) = copy(&mut func, &mut names, block);
833        let base = func.new_vreg(GPR);
834        let index = func.new_vreg(GPR);
835        let load = op(&mut names, "mov_rm_64");
836        let scaled = |scale| Plan {
837            opcode: load,
838            operands: vec![
839                mir::Operand::write(into, GPR),
840                mir::Operand::read(base, GPR),
841                mir::Operand::read(index, GPR),
842            ],
843            imm: None,
844            amode: Some(mir::Amode {
845                base: Some(1),
846                index: Some(2),
847                scale,
848                disp: 0,
849                symbol: None,
850                block: None,
851                table: None,
852                reach: mir::Reach::Itself,
853                segment: None,
854            }),
855            symbol: None,
856        };
857        let mut set = Changes::new();
858        set.rewrite(mov, scaled(3));
859        assert_eq!(refused(&func, &names, &set), Some(Refusal::Scale(mov)));
860
861        let mut set = Changes::new();
862        set.rewrite(mov, scaled(4));
863        assert_eq!(refused(&func, &names, &set), None);
864    }
865
866    /// A load from a register plus a register plus a constant, which x86-64 writes in one mode and
867    /// AArch64 has no mode for. The same load with nothing added is one AArch64 has.
868    #[test]
869    fn an_index_beside_a_displacement_is_refused_where_the_machine_has_no_such_mode() {
870        use rucc_target::aarch64;
871
872        let (mut names, mut func, block) = empty();
873        let into = func.new_vreg(aarch64::GPR);
874        let base = func.new_vreg(aarch64::GPR);
875        let index = func.new_vreg(aarch64::GPR);
876        let load = mir::Opcode::new(names.intern("a64.ldr_64"));
877        let mov = func.build(block, load).def(into, aarch64::GPR).uses(base, aarch64::GPR).finish();
878        let plan = |disp| Plan {
879            opcode: load,
880            operands: vec![
881                mir::Operand::write(into, aarch64::GPR),
882                mir::Operand::read(base, aarch64::GPR),
883                mir::Operand::read(index, aarch64::GPR),
884            ],
885            imm: None,
886            amode: Some(mir::Amode { base: Some(1), index: Some(2), disp, ..mir::Amode::NOTHING }),
887            symbol: None,
888        };
889        let asked = |set: &Changes, func: &mir::Func| {
890            set.refused(func, &Reads::of(func), &names, &aarch64::MACHINE)
891        };
892        let mut set = Changes::new();
893        set.rewrite(mov, plan(16));
894        assert_eq!(asked(&set, &func), Some(Refusal::Crowded(mov)));
895
896        let mut set = Changes::new();
897        set.rewrite(mov, plan(0));
898        assert_eq!(asked(&set, &func), None);
899    }
900
901    /// An address whose base points at an operand the description already claimed. The registers an
902    /// address names come after the ones the instruction itself has, and a mode pointing into the
903    /// middle of the others is a printer's mistake waiting to happen.
904    #[test]
905    fn an_address_pointing_at_an_operand_of_its_own_instruction_is_refused() {
906        let (mut names, mut func, block) = empty();
907        let (mov, into) = copy(&mut func, &mut names, block);
908        let load = op(&mut names, "mov_rm_64");
909        let mut set = Changes::new();
910        set.rewrite(
911            mov,
912            Plan {
913                opcode: load,
914                operands: vec![mir::Operand::write(into, GPR)],
915                imm: None,
916                amode: Some(mir::Amode {
917                    base: Some(0),
918                    index: None,
919                    scale: 1,
920                    disp: 0,
921                    symbol: None,
922                    block: None,
923                    table: None,
924                    reach: mir::Reach::Itself,
925                    segment: None,
926                }),
927                symbol: None,
928            },
929        );
930
931        assert_eq!(refused(&func, &names, &set), Some(Refusal::Operands(mov)));
932    }
933
934    /// The question the set is for. Taking an instruction out while something still reads what it
935    /// wrote is the mistake every pass that removes instructions can make, and this is the one
936    /// place it is answered.
937    #[test]
938    fn removing_an_instruction_whose_answer_is_still_read_is_refused() {
939        let (mut names, mut func, block) = empty();
940        let (mov, into) = copy(&mut func, &mut names, block);
941        let out = func.new_vreg(GPR);
942        let second = func[mov].opcode;
943        func.build(block, second).def(out, GPR).uses(into, GPR).finish();
944        let mut set = Changes::new();
945        set.remove(mov);
946
947        assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
948        assert_eq!(shape(&func, &names, block).len(), 2);
949    }
950
951    /// The same removal in a set that deals with the reader as well. This is what a fold is: the
952    /// address goes because the instruction that read it does not read it any more, and neither
953    /// half is worth doing without the other.
954    #[test]
955    fn removing_it_in_a_set_that_takes_away_the_reader_is_taken() {
956        let (mut names, mut func, block) = empty();
957        let base = func.new_vreg(GPR);
958        let address = func.new_vreg(GPR);
959        let out = func.new_vreg(GPR);
960        let lea = op(&mut names, "lea_64");
961        let load = op(&mut names, "mov_rm_64");
962        let at = |reg| mir::Mem { disp: 16, ..mir::Mem::at(mir::Operand::read(reg, GPR)) };
963        let made = func.build(block, lea).def(address, GPR).mem(at(base)).finish();
964        let read = func
965            .build(block, load)
966            .def(out, GPR)
967            .mem(mir::Mem::at(mir::Operand::read(address, GPR)))
968            .finish();
969
970        let mut set = Changes::new();
971        set.rewrite(
972            read,
973            Plan {
974                opcode: load,
975                operands: vec![mir::Operand::write(out, GPR), mir::Operand::read(base, GPR)],
976                imm: None,
977                amode: Some(mir::Amode { disp: 16, ..func[func[read].mem.expect("a load")] }),
978                symbol: None,
979            },
980        );
981        set.remove(made);
982
983        let mut reads = Reads::of(&func);
984        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
985
986        assert_eq!(shape(&func, &names, block), ["x64.mov_rm_64"]);
987        assert_eq!(reads.count(address), 0, "nothing reads the address the lea wrote");
988        assert_eq!(reads.count(base), 1, "the load reads what the lea read");
989    }
990
991    /// A rename with the removal it is there for, which is the shape every pass that takes an
992    /// instruction out and sends its readers somewhere else hands over.
993    #[test]
994    fn a_rename_that_takes_the_last_reader_off_an_instruction_lets_it_go() {
995        let (mut names, mut func, block) = empty();
996        let (first, into) = copy(&mut func, &mut names, block);
997        let source = func[func[first].operands][1].reg;
998        let out = func.new_vreg(GPR);
999        let mov = func[first].opcode;
1000        let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1001
1002        let mut set = Changes::new();
1003        set.rename(second, into, source);
1004        set.remove(first);
1005        let mut reads = Reads::of(&func);
1006        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
1007
1008        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1009        assert_eq!(func[func[second].operands][1].reg, source, "the reader was not sent on");
1010        assert_eq!(reads.count(into), 0, "nothing reads what the copy wrote");
1011        assert_eq!(reads.count(source), 1, "the reader reads what the copy read");
1012    }
1013
1014    /// The same removal with the rename left out, which is the mistake the set is there to catch.
1015    #[test]
1016    fn a_removal_whose_reader_is_not_renamed_is_refused() {
1017        let (mut names, mut func, block) = empty();
1018        let (first, into) = copy(&mut func, &mut names, block);
1019        let out = func.new_vreg(GPR);
1020        let mov = func[first].opcode;
1021        func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1022
1023        let mut set = Changes::new();
1024        set.remove(first);
1025        assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(first)));
1026    }
1027
1028    /// A rename that would have an instruction read a register of another class. The operand says
1029    /// which class it is, the function says which class the register is, and an instruction whose
1030    /// operands disagree with that is one the allocator has no registers for.
1031    #[test]
1032    fn a_rename_into_a_register_of_another_class_is_refused() {
1033        let (mut names, mut func, block) = empty();
1034        let (mov, _) = copy(&mut func, &mut names, block);
1035        let source = func[func[mov].operands][1].reg;
1036        let float = func.new_vreg(XMM);
1037
1038        let mut set = Changes::new();
1039        set.rename(mov, source, float);
1040        assert_eq!(refused(&func, &names, &set), Some(Refusal::Class(mov)));
1041    }
1042
1043    /// What an edge carries is where the answer of a conversion in one block reaches a reader in
1044    /// another, and sending it somewhere else is the same change as renaming an operand.
1045    #[test]
1046    fn an_edge_carries_what_the_set_says_and_the_instruction_it_read_goes() {
1047        let (mut names, mut func, block) = empty();
1048        let next = func.create_block();
1049        let (mov, into) = copy(&mut func, &mut names, block);
1050        let source = func[func[mov].operands][1].reg;
1051        let arrived = func.new_vreg(GPR);
1052        func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1053        *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1054
1055        let mut set = Changes::new();
1056        set.carry(block, 0, vec![source]);
1057        set.remove(mov);
1058        let mut reads = Reads::of(&func);
1059        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(2));
1060
1061        assert!(shape(&func, &names, block).is_empty(), "the copy is still there");
1062        assert_eq!(func[block].succs[0].args, vec![source]);
1063        assert_eq!(reads.count(into), 0);
1064        assert_eq!(reads.count(source), 1, "the edge reads what the copy read");
1065    }
1066
1067    /// An edge that block does not have, which is a set built against a function that has been
1068    /// laid out since.
1069    #[test]
1070    fn an_edge_that_is_not_there_is_refused() {
1071        let (mut names, mut func, block) = empty();
1072        copy(&mut func, &mut names, block);
1073
1074        let mut set = Changes::new();
1075        set.carry(block, 0, Vec::new());
1076        assert_eq!(refused(&func, &names, &set), Some(Refusal::Edge(block, 0)));
1077    }
1078
1079    /// An edge carrying a different number of values than the block it goes to takes. The
1080    /// parameters are where they arrive, so one that arrives nowhere is a function nothing after
1081    /// this could read.
1082    #[test]
1083    fn an_edge_carrying_the_wrong_number_of_values_is_refused() {
1084        let (mut names, mut func, block) = empty();
1085        let next = func.create_block();
1086        let (_, into) = copy(&mut func, &mut names, block);
1087        let arrived = func.new_vreg(GPR);
1088        func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1089        *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1090
1091        let mut set = Changes::new();
1092        set.carry(block, 0, vec![into, into]);
1093        assert_eq!(refused(&func, &names, &set), Some(Refusal::Args(block, 0)));
1094    }
1095
1096    /// An argument an edge carries is a read like any other and is in no operand vector, which is
1097    /// the one place a count of reads is easy to get wrong.
1098    #[test]
1099    fn an_answer_an_edge_carries_keeps_its_instruction() {
1100        let (mut names, mut func, block) = empty();
1101        let next = func.create_block();
1102        let (mov, into) = copy(&mut func, &mut names, block);
1103        let arrived = func.new_vreg(GPR);
1104        func.params_mut(next).push(mir::Param { reg: arrived, class: GPR });
1105        *func.succs_mut(block) = vec![mir::BlockCall::with(next, vec![into])];
1106        let mut set = Changes::new();
1107        set.remove(mov);
1108
1109        assert_eq!(refused(&func, &names, &set), Some(Refusal::Read(mov)));
1110    }
1111
1112    /// One instruction, two minds. A set that says an instruction becomes one thing and then
1113    /// another is a pass that has lost track of what it proposed, and taking either half would be
1114    /// picking for it.
1115    #[test]
1116    fn an_instruction_named_twice_is_refused() {
1117        let (mut names, mut func, block) = empty();
1118        let (mov, _) = copy(&mut func, &mut names, block);
1119        let mut set = Changes::new();
1120        set.rewrite(mov, Plan::of(&func, mov));
1121        set.remove(mov);
1122
1123        assert_eq!(refused(&func, &names, &set), Some(Refusal::Twice(mov)));
1124    }
1125
1126    /// A set built against a function that has moved on since. The instruction it names is gone,
1127    /// and a rewrite of an instruction in no block would be a rewrite nothing ever runs.
1128    #[test]
1129    fn an_instruction_that_has_already_gone_is_refused() {
1130        let (mut names, mut func, block) = empty();
1131        let (mov, _) = copy(&mut func, &mut names, block);
1132        let plan = Plan::of(&func, mov);
1133        func.remove_inst(mov);
1134        let mut set = Changes::new();
1135        set.rewrite(mov, plan);
1136
1137        assert_eq!(refused(&func, &names, &set), Some(Refusal::Gone(mov)));
1138    }
1139
1140    /// The instruction as it stands is a proposal that changes nothing, which is what a pass that
1141    /// rewrites one operand starts from.
1142    #[test]
1143    fn the_instruction_as_it_stands_is_a_proposal_the_target_takes() {
1144        let (mut names, mut func, block) = empty();
1145        let base = func.new_vreg(GPR);
1146        let out = func.new_vreg(GPR);
1147        let load = op(&mut names, "mov_rm_64");
1148        let read = func
1149            .build(block, load)
1150            .def(out, GPR)
1151            .mem(mir::Mem { disp: 24, ..mir::Mem::at(mir::Operand::read(base, GPR)) })
1152            .finish();
1153
1154        let plan = Plan::of(&func, read);
1155        let mut set = Changes::new();
1156        set.rewrite(read, plan.clone());
1157        let mut reads = Reads::of(&func);
1158        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1159
1160        assert_eq!(Plan::of(&func, read), plan);
1161        assert_eq!(reads.count(base), 1, "the one read it had before");
1162    }
1163
1164    /// A refused set writes nothing, including the half of it that would have been fine. That is
1165    /// the whole point of proposing a set rather than applying instructions one at a time.
1166    #[test]
1167    fn a_set_with_one_bad_change_in_it_leaves_the_others_alone() {
1168        let (mut names, mut func, block) = empty();
1169        let (first, into) = copy(&mut func, &mut names, block);
1170        let (second, _) = copy(&mut func, &mut names, block);
1171        let load = op(&mut names, "mov_rm_64");
1172        let mut set = Changes::new();
1173        set.rewrite(
1174            first,
1175            Plan {
1176                opcode: load,
1177                operands: vec![mir::Operand::write(into, GPR), mir::Operand::read(into, GPR)],
1178                imm: None,
1179                amode: Some(mir::Amode {
1180                    base: Some(1),
1181                    index: None,
1182                    scale: 1,
1183                    disp: 0,
1184                    symbol: None,
1185                    block: None,
1186                    table: None,
1187                    reach: mir::Reach::Itself,
1188                    segment: None,
1189                }),
1190                symbol: None,
1191            },
1192        );
1193        set.rewrite(second, Plan { imm: Some(3), ..Plan::of(&func, second) });
1194
1195        let mut reads = Reads::of(&func);
1196        assert_eq!(
1197            set.commit(&mut func, &mut reads, &names, &MACHINE),
1198            Err(Refusal::Imm(second)),
1199            "the second change is the one the target turns down"
1200        );
1201        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64", "x64.mov_rr_64"]);
1202    }
1203
1204    /// A set with nothing in it, which is what a pass that found nothing to do hands over.
1205    #[test]
1206    fn a_set_with_nothing_in_it_commits() {
1207        let (mut names, mut func, block) = empty();
1208        copy(&mut func, &mut names, block);
1209        let set = Changes::new();
1210        assert!(set.is_empty());
1211
1212        let mut reads = Reads::of(&func);
1213        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(0));
1214        assert_eq!(shape(&func, &names, block), ["x64.mov_rr_64"]);
1215    }
1216
1217    /// The counts a pass carries from one commit to the next. A removal the first commit makes
1218    /// possible has to be a removal the second commit agrees to, and it only is if the count came
1219    /// down when the reader went.
1220    #[test]
1221    fn the_counts_are_still_right_after_a_commit() {
1222        let (mut names, mut func, block) = empty();
1223        let (first, into) = copy(&mut func, &mut names, block);
1224        let out = func.new_vreg(GPR);
1225        let mov = func[first].opcode;
1226        let second = func.build(block, mov).def(out, GPR).uses(into, GPR).finish();
1227
1228        let mut reads = Reads::of(&func);
1229        assert_eq!(reads.count(into), 1);
1230        let mut set = Changes::new();
1231        set.remove(second);
1232        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1233        assert_eq!(reads.count(into), 0, "the reader went and the count went with it");
1234
1235        let mut set = Changes::new();
1236        set.remove(first);
1237        assert_eq!(set.commit(&mut func, &mut reads, &names, &MACHINE), Ok(1));
1238        assert!(shape(&func, &names, block).is_empty());
1239    }
1240}