Skip to main content

rucc_mir/
inst.rs

1//! What one machine instruction is, and what an operand is.
2//!
3//! Design: `spec/10-backend.md` section 10.1.
4//!
5//! An instruction is an opcode, a run of operands, the three things an opcode may carry besides
6//! its operands, which are an immediate, a memory addressing mode and a symbol, and one set of
7//! flags. Twenty-eight bytes, all of it either a small number or an index into a table the
8//! function owns, so walking a function is walking one dense array and nothing in it is
9//! separately freed.
10//!
11//! An operand is a register, the class it is drawn from, whether the instruction reads or
12//! writes it, and any constraint on where it may live. That is what the allocator reads and it
13//! is all the allocator reads, which is the point: the opcode is a name to everything except
14//! the encoder, and the allocator never has to know what any particular target's instructions
15//! mean.
16//!
17//! # Where the other pieces are
18//!
19//! [`Role`] and [`Constraint`] are in `rucc-target`, and this crate re-exports them. A target
20//! says what its instructions do to their operands before there is any machine IR to say it in,
21//! and both the selector that builds the IR and the encoder that reads it need the answer, so
22//! the two of them live below both.
23//!
24//! Successors are on the block rather than on the terminator, in the order the terminator's own
25//! arms run. That is regalloc2's arrangement, which `spec/10-backend.md` section 10.4 says the
26//! allocator interface follows, and it keeps a branch's arguments out of the operand vector
27//! where they would otherwise be uses the allocator has to be told to treat differently.
28//!
29//! The source location is a parallel array in the function, reached by [`crate::Func::span`],
30//! for the same reason `rucc-ir` puts it there: it is read when a diagnostic is being made and
31//! at no other time, so it does not belong on the row that every pass walks.
32
33use std::fmt;
34
35use rucc_base::{Idx, IdxRange, Symbol};
36use rucc_target::{Constraint, PhysReg, RegClass, Role, Segment};
37
38/// One instruction, in the function that owns it.
39pub type Inst = Idx<InstData>;
40/// One basic block, in the function that owns it.
41pub type Block = Idx<BlockData>;
42/// A run of operands, which is what an instruction's operand vector is.
43pub type OperandList = IdxRange<Operand>;
44/// One immediate, in the function's immediate table.
45pub type ImmRef = Idx<Imm>;
46/// One addressing mode, in the function's table of them.
47pub type MemRef = Idx<Amode>;
48
49/// Which instruction this is.
50///
51/// A name rather than a variant of an enum. `spec/10-backend.md` section 10.8 says no pipeline
52/// crate holds target-specific code, and an enum of every x86-64 opcode in the crate every
53/// target's MIR passes through is exactly that. The opcodes a target has are data: they come out
54/// of its rule set, which is what the selector was compiled from, and this crate never asks what
55/// one of them means. The encoder does, against the same description the rules were written
56/// against.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub struct Opcode(Symbol);
59
60impl Opcode {
61    /// The opcode of that name.
62    #[must_use]
63    pub const fn new(name: Symbol) -> Self {
64        Self(name)
65    }
66
67    /// Its name, which needs the interner it was made with to read.
68    #[must_use]
69    pub const fn name(self) -> Symbol {
70        self.0
71    }
72}
73
74/// A register, either one the allocator has still to place or one it has placed.
75///
76/// The two are one type and four bytes because every operand holds one and because a pass that
77/// runs both before and after allocation should not be two passes. Which of the two it is, is
78/// the top bit, so a virtual register is its own number and nothing has to be masked to compare
79/// two of them.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct Reg(u32);
82
83impl Reg {
84    /// The bit that says the rest is a physical register rather than a virtual one.
85    const PHYSICAL: u32 = 1 << 31;
86
87    /// The virtual register with that number.
88    ///
89    /// # Panics
90    ///
91    /// Panics if the number is two billion or more, which no function reaches.
92    #[must_use]
93    pub const fn virtual_reg(number: u32) -> Self {
94        assert!(number < Self::PHYSICAL, "a function with two billion virtual registers");
95        Self(number)
96    }
97
98    /// The physical register, once one has been chosen.
99    #[must_use]
100    pub const fn physical(reg: PhysReg) -> Self {
101        Self(Self::PHYSICAL | reg.number() as u32)
102    }
103
104    /// Whether the allocator has still to place it.
105    #[must_use]
106    pub const fn is_virtual(self) -> bool {
107        self.0 & Self::PHYSICAL == 0
108    }
109
110    /// Its number as a virtual register, or `None` once it is a physical one.
111    #[must_use]
112    pub const fn number(self) -> Option<u32> {
113        if self.is_virtual() { Some(self.0) } else { None }
114    }
115
116    /// The physical register it is, or `None` while it is still virtual.
117    ///
118    /// Which class the register is in is on the operand rather than here, because an operand
119    /// carries its class already and a second copy of it is a thing that can disagree.
120    #[must_use]
121    pub const fn phys(self) -> Option<PhysReg> {
122        if self.is_virtual() { None } else { Some(PhysReg::new((self.0 & 0xff) as u8)) }
123    }
124}
125
126/// One operand of one instruction.
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub struct Operand {
129    /// The register, virtual until the allocator has run.
130    pub reg: Reg,
131    /// The class it is drawn from.
132    pub class: RegClass,
133    /// Whether the instruction reads it or writes it.
134    pub role: Role,
135    /// Where it is allowed to live.
136    pub constraint: Constraint,
137}
138
139impl Operand {
140    /// An operand the instruction reads.
141    #[must_use]
142    pub const fn read(reg: Reg, class: RegClass) -> Self {
143        Self { reg, class, role: Role::Use, constraint: Constraint::Reg }
144    }
145
146    /// An operand the instruction writes as it finishes.
147    #[must_use]
148    pub const fn write(reg: Reg, class: RegClass) -> Self {
149        Self { reg, class, role: Role::Def, constraint: Constraint::Reg }
150    }
151
152    /// An operand the instruction writes before it has finished reading.
153    #[must_use]
154    pub const fn write_early(reg: Reg, class: RegClass) -> Self {
155        Self { reg, class, role: Role::EarlyDef, constraint: Constraint::Reg }
156    }
157
158    /// The same operand, constrained.
159    #[must_use]
160    pub const fn with(mut self, constraint: Constraint) -> Self {
161        self.constraint = constraint;
162        self
163    }
164}
165
166/// One immediate.
167///
168/// Signed and sixty-four bits, which every immediate field of every target we have is narrower
169/// than. What fits in the field the encoder is about to write is the encoder's question, and it
170/// is one it can only answer per opcode, so nothing here tries to.
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
172pub struct Imm(pub i64);
173
174/// What the four bytes beside a symbol in an address hold.
175///
176/// Three different numbers written in the same place, and nothing about the instruction says which
177/// one it is: `movq sym(%rip)`, `movq sym@GOTPCREL(%rip)` and `movq sym@GOTTPOFF(%rip)` are the
178/// same opcode with the same operands, and the only thing that tells them apart is the relocation
179/// the assembler leaves behind. So the difference has to be carried here, beside the symbol, rather
180/// than being read back out of the shape of the address.
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
182pub enum Reach {
183    /// The distance to the symbol itself, which is the ordinary case. `R_X86_64_PC32`.
184    #[default]
185    Itself,
186    /// The distance to the slot of the global offset table holding the symbol's address, which is
187    /// a load rather than an arithmetic. See [`Mem::got`].
188    Table,
189    /// The distance to the slot of the global offset table holding the symbol's offset inside a
190    /// thread's own block of storage. See [`Mem::thread`].
191    Thread,
192}
193
194/// A memory addressing mode, as the instruction holds it.
195///
196/// The registers are the indices of the operands holding them rather than the registers
197/// themselves, because an address register is a register the allocator has to see and rewrite,
198/// and the only thing it looks at is the operand vector. [`Mem`] is the same thing written the
199/// way a caller writes it, and [`crate::InstBuilder::mem`] turns one into the other.
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub struct Amode {
202    /// The operand holding the base register.
203    pub base: Option<u8>,
204    /// The operand holding the index register.
205    pub index: Option<u8>,
206    /// What the index is multiplied by, which is 1 when there is no index.
207    pub scale: u8,
208    /// The constant added to the address.
209    pub disp: i32,
210    /// The symbol the address is relative to, for an access to a global.
211    pub symbol: Option<Symbol>,
212    /// The block of this function the address is of, for the address of a label. See
213    /// [`Mem::block`].
214    pub block: Option<Block>,
215    /// The jump table of this function the address is of, as its place in
216    /// [`crate::Func::tables`]. See [`Mem::table`].
217    pub table: Option<u32>,
218    /// What the four bytes beside the symbol hold, when there is a symbol. See [`Reach`].
219    pub reach: Reach,
220    /// Which storage the address is counted from, when it is not the flat one. See [`Segment`].
221    pub segment: Option<Segment>,
222}
223
224impl Amode {
225    /// The addressing mode naming no register and no symbol, at offset zero.
226    pub const NOTHING: Self = Self {
227        base: None,
228        index: None,
229        scale: 1,
230        disp: 0,
231        symbol: None,
232        block: None,
233        table: None,
234        reach: Reach::Itself,
235        segment: None,
236    };
237}
238
239/// A memory addressing mode as a caller writes one down.
240///
241/// The difference from [`Amode`] is that the registers are here rather than in the operand
242/// vector, which is what [`crate::InstBuilder::mem`] fixes. Keeping the two apart is what lets
243/// the operand indices in an [`Amode`] be an invariant of the builder rather than something
244/// every caller has to get right.
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
246pub struct Mem {
247    /// The base register, which the instruction reads.
248    pub base: Option<Operand>,
249    /// The index register, which the instruction reads.
250    pub index: Option<Operand>,
251    /// What the index is multiplied by.
252    pub scale: u8,
253    /// The constant added to the address.
254    pub disp: i32,
255    /// The symbol the address is relative to.
256    pub symbol: Option<Symbol>,
257    /// The block of this function the address is of. See [`Mem::block`].
258    pub block: Option<Block>,
259    /// The jump table of this function the address is of. See [`Mem::table`].
260    pub table: Option<u32>,
261    /// What the four bytes beside the symbol hold, when there is a symbol. See [`Reach`].
262    pub reach: Reach,
263    /// Which storage the address is counted from, when it is not the flat one. See [`Segment`].
264    pub segment: Option<Segment>,
265}
266
267impl Mem {
268    /// The address in that register.
269    #[must_use]
270    pub const fn at(base: Operand) -> Self {
271        Self {
272            base: Some(base),
273            index: None,
274            scale: 1,
275            disp: 0,
276            symbol: None,
277            block: None,
278            table: None,
279            reach: Reach::Itself,
280            segment: None,
281        }
282    }
283
284    /// The address of that symbol.
285    #[must_use]
286    pub const fn of(symbol: Symbol) -> Self {
287        Self {
288            base: None,
289            index: None,
290            scale: 1,
291            disp: 0,
292            symbol: Some(symbol),
293            block: None,
294            table: None,
295            reach: Reach::Itself,
296            segment: None,
297        }
298    }
299
300    /// The address of that block of this function, which is what GNU's `&&label` is.
301    ///
302    /// A block rather than a symbol because the block it names is in this same function and has no
303    /// name outside it. What the assembler is given is the local label the block already carries,
304    /// which is a name the object file need not keep, and what the object writer is given is
305    /// nothing at all: the distance is between two places in one section and both of them are
306    /// known once the blocks have been laid out, so it is filled in here rather than left to a
307    /// linker the way the distance to a global is.
308    #[must_use]
309    pub const fn block(block: Block) -> Self {
310        Self {
311            base: None,
312            index: None,
313            scale: 1,
314            disp: 0,
315            symbol: None,
316            block: Some(block),
317            table: None,
318            reach: Reach::Itself,
319            segment: None,
320        }
321    }
322
323    /// The address of one of this function's jump tables, which is what a `switch` dense enough to
324    /// be one reads its destination out of.
325    ///
326    /// An index into [`crate::Func::tables`] rather than a symbol, for the reason [`Self::block`]
327    /// is a block: the table is written into the same section as the function, straight after its
328    /// last instruction, so both ends of the distance are places the assembler lays out itself and
329    /// there is nothing for a linker to be told.
330    #[must_use]
331    pub const fn table(table: u32) -> Self {
332        Self { table: Some(table), ..Self::of_nothing() }
333    }
334
335    /// The address naming nothing at all, which the constructors above start from.
336    const fn of_nothing() -> Self {
337        Self {
338            base: None,
339            index: None,
340            scale: 1,
341            disp: 0,
342            symbol: None,
343            block: None,
344            table: None,
345            reach: Reach::Itself,
346            segment: None,
347        }
348    }
349
350    /// That many bytes into a thread's own block of words, which names no register at all.
351    ///
352    /// The whole address is the constant, because where the block is is something only the machine
353    /// knows: the segment register is what holds it and nothing loads one. See [`Segment`].
354    #[must_use]
355    pub const fn in_segment(segment: Segment, disp: i32) -> Self {
356        Self {
357            base: None,
358            index: None,
359            scale: 1,
360            disp,
361            symbol: None,
362            block: None,
363            table: None,
364            reach: Reach::Itself,
365            segment: Some(segment),
366        }
367    }
368
369    /// The slot of the global offset table holding that symbol's address.
370    ///
371    /// Not the same thing as [`Self::of`] and not an optimization of it. `sym(%rip)` is the
372    /// address worked out from where the instruction is, which is only the right address when the
373    /// symbol is in this same object, and the linker refuses it in a position independent
374    /// executable when the symbol may turn out to be in a shared library. `sym@GOTPCREL(%rip)` is
375    /// a slot the linker fills in with the one address everybody agrees on, so it is a load rather
376    /// than an arithmetic, and whatever reads it gets an address rather than a place.
377    ///
378    /// The linker relaxes it back into the arithmetic when the symbol turns out to be in this
379    /// program after all, which is why nothing is lost by asking for it.
380    #[must_use]
381    pub const fn got(symbol: Symbol) -> Self {
382        Self { reach: Reach::Table, ..Self::of(symbol) }
383    }
384
385    /// The slot of the global offset table holding that symbol's offset inside a thread's block.
386    ///
387    /// A thread-local variable has no one address, since every thread has a copy of it, so there is
388    /// nothing for [`Self::of`] to be the distance to and a linker refuses one aimed at such a
389    /// symbol. What every copy does share is where it sits inside the block a thread gets, and that
390    /// offset is the number this slot holds: add it to the address of the running thread's block,
391    /// which the machine keeps in `%fs`, and the result is this thread's copy.
392    ///
393    /// The offset is a slot rather than a constant because how big the blocks in front of this
394    /// object's are is only known once the program is linked together, and in a shared library only
395    /// once it is loaded. The linker writes the constant into the instruction instead when it is
396    /// making an executable, where it does know, so this costs nothing in the case that is common.
397    #[must_use]
398    pub const fn thread(symbol: Symbol) -> Self {
399        Self { reach: Reach::Thread, ..Self::of(symbol) }
400    }
401
402    /// The same address with an index register scaled by that much.
403    #[must_use]
404    pub const fn indexed(mut self, index: Operand, scale: u8) -> Self {
405        self.index = Some(index);
406        self.scale = scale;
407        self
408    }
409
410    /// The same address, that many bytes along.
411    #[must_use]
412    pub const fn plus(mut self, disp: i32) -> Self {
413        self.disp = disp;
414        self
415    }
416}
417
418/// How often something happens, next to once for every time the function is entered.
419///
420/// Ten thousand is once, which is the scale the block frequencies in `rucc_opt` are worked out
421/// in. The numbers here are those carried down rather than worked out again: by the time the
422/// blocks are laid out the loops the frequency came from are branches and there is nothing left
423/// to work one out from.
424///
425/// Nothing keeps these in step with the graph afterwards. A pass that makes a block says how
426/// often the block runs, and a pass that does not is one whose new blocks run as often as the
427/// function does, which is what [`Weight::ONCE`] is and is the only answer available to something
428/// that was never told. They are a layout heuristic, nothing reads them for anything a wrong
429/// answer could make incorrect, and the worst a stale one costs is a jump where a fall-through
430/// would have done.
431#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
432pub struct Weight(u64);
433
434impl Weight {
435    /// The scale: how many parts one run of the function is divided into.
436    ///
437    /// Ten thousand rather than one, because a block inside three conditionals runs a fraction of
438    /// a time per call and a fraction is not an integer. It is the same scale `rucc_opt` uses,
439    /// which is what makes carrying a frequency down here a copy rather than a conversion.
440    pub const SCALE: u64 = 10_000;
441
442    /// Once for every time the function is entered.
443    pub const ONCE: Self = Self(Self::SCALE);
444
445    /// Never.
446    pub const NEVER: Self = Self(0);
447
448    /// That many parts of [`Weight::SCALE`].
449    #[must_use]
450    pub const fn parts(parts: u64) -> Self {
451        Self(parts)
452    }
453
454    /// How many parts of [`Weight::SCALE`] it is.
455    #[must_use]
456    pub const fn raw(self) -> u64 {
457        self.0
458    }
459
460    /// What fraction of `whole` this is, in parts of [`Weight::SCALE`].
461    ///
462    /// A whole of nothing answers nothing, since a block that never runs has no arm that is taken
463    /// more often than any other and the question has no answer rather than an arbitrary one.
464    #[must_use]
465    pub const fn out_of(self, whole: Self) -> u64 {
466        if whole.0 == 0 {
467            return 0;
468        }
469        // Saturating rather than wrapping, for the same reason a frequency saturates: a nest of
470        // loops multiplies, and a number that wrapped would read as cold where it is hottest.
471        match self.0.checked_mul(Self::SCALE) {
472            Some(scaled) => scaled / whole.0,
473            None => (self.0 / whole.0).saturating_mul(Self::SCALE),
474        }
475    }
476}
477
478impl Default for Weight {
479    /// Once, which is what a block nobody worked a number out for runs as often as.
480    fn default() -> Self {
481        Self::ONCE
482    }
483}
484
485/// One arm of a terminator: where it goes, and what it takes with it.
486///
487/// The arguments are the values the target block's parameters arrive as, so this is the edge on
488/// which a phi would otherwise sit. After allocation the parameters are physical registers and
489/// these arguments have become the moves that write them, which is the point at which MIR stops
490/// being in SSA form.
491#[derive(Debug, Clone, PartialEq, Eq)]
492pub struct BlockCall {
493    /// The block it goes to.
494    pub block: Block,
495    /// What its parameters arrive as, one for one.
496    pub args: Vec<Reg>,
497    /// How often the edge is taken, next to how often the function is entered. See [`Weight`].
498    pub weight: Weight,
499}
500
501impl BlockCall {
502    /// A jump to that block carrying nothing.
503    #[must_use]
504    pub const fn to(block: Block) -> Self {
505        Self { block, args: Vec::new(), weight: Weight::ONCE }
506    }
507
508    /// A jump to that block carrying those registers.
509    #[must_use]
510    pub fn with(block: Block, args: Vec<Reg>) -> Self {
511        Self { block, args, weight: Weight::ONCE }
512    }
513
514    /// The same arm, taken that often.
515    #[must_use]
516    pub fn taken(mut self, weight: Weight) -> Self {
517        self.weight = weight;
518        self
519    }
520}
521
522/// One parameter of a block: the register the value arrives in, and its class.
523#[derive(Debug, Clone, Copy, PartialEq, Eq)]
524pub struct Param {
525    /// What the value arrives as, virtual until the allocator has run.
526    pub reg: Reg,
527    /// The class it is drawn from.
528    pub class: RegClass,
529}
530
531/// What is true of an instruction besides what its operands say.
532///
533/// One flag, and a set rather than a `bool` because the thing it is the first of is a class: a
534/// fact the front end knew about an access, which selection has to carry down because no pass
535/// below can work it out again. A second `bool` on the row every pass walks is how a struct
536/// turns into a bag, and a second bit here is free.
537///
538/// Empty on every instruction the machine writes for itself, which is most of them. A prologue,
539/// a spill, a jump and the move the allocator writes to put a value where the machine wants it
540/// were all asked for by this compiler rather than by the program, so there is nothing the
541/// program said about them to carry.
542#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
543pub struct Flags(u8);
544
545impl Flags {
546    /// Nothing besides what the operands say.
547    pub const NONE: Self = Self(0);
548
549    /// The access happens exactly once, and is never moved or merged with another.
550    ///
551    /// `rucc_ir::Flags::VOLATILE` on the load or the store this instruction was selected from.
552    /// Every pass above selection reads that flag, and until it was carried down here the
553    /// instruction that reached the machine level passes was the same instruction whether the
554    /// program had written `volatile` or not, so a pass that merges two accesses merged these
555    /// as well. See [`InstData::flags`] and tamnd/rucc#1302.
556    pub const VOLATILE: Self = Self(1);
557
558    /// Both of them at once.
559    #[must_use]
560    pub const fn with(self, other: Self) -> Self {
561        Self(self.0 | other.0)
562    }
563
564    /// Whether every flag in the other one is in this one. True of [`Self::NONE`] always, since
565    /// there is nothing in it to be missing.
566    #[must_use]
567    pub const fn contains(self, other: Self) -> bool {
568        self.0 & other.0 == other.0
569    }
570
571    /// Whether nothing is set.
572    #[must_use]
573    pub const fn is_empty(self) -> bool {
574        self.0 == 0
575    }
576}
577
578impl fmt::Display for Flags {
579    /// Each flag with a space in front of it, so that it reads as written after the opcode and
580    /// prints as nothing at all when there is nothing set. The same arrangement `rucc_ir` uses
581    /// for the flags an IR instruction carries.
582    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        if self.contains(Self::VOLATILE) {
584            f.write_str(" volatile")?;
585        }
586        Ok(())
587    }
588}
589
590/// One instruction.
591#[derive(Debug, Clone, Copy, PartialEq, Eq)]
592pub struct InstData {
593    /// Which instruction this is.
594    pub opcode: Opcode,
595    /// Its operands, defs first and then uses, with the registers a memory operand names last.
596    /// The order is what the printer and the parser agree on, and [`crate::InstBuilder`] is
597    /// what keeps it.
598    pub operands: OperandList,
599    /// Its immediate, if it has one.
600    pub imm: Option<ImmRef>,
601    /// Its memory operand, if it has one.
602    pub mem: Option<MemRef>,
603    /// The symbol it names, which is the callee of a direct call and the target of a direct
604    /// jump to another function.
605    pub symbol: Option<Symbol>,
606    /// What the program said about it that its operands do not. See [`Flags`].
607    pub flags: Flags,
608}
609
610impl InstData {
611    /// An instruction with that opcode and nothing else.
612    #[must_use]
613    pub const fn new(opcode: Opcode) -> Self {
614        Self {
615            opcode,
616            operands: OperandList::EMPTY,
617            imm: None,
618            mem: None,
619            symbol: None,
620            flags: Flags::NONE,
621        }
622    }
623}
624
625/// Where an instruction sits: which block it is in, and what is either side of it.
626#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
627pub(crate) struct InstLayout {
628    pub(crate) block: Option<Block>,
629    pub(crate) prev: Option<Inst>,
630    pub(crate) next: Option<Inst>,
631}
632
633/// One block: what arrives in it, what is in it, and where it goes.
634#[derive(Debug, Clone, Default, PartialEq, Eq)]
635pub struct BlockData {
636    /// The values that arrive in it, which are the function's arguments in the entry block.
637    pub params: Vec<Param>,
638    /// Where its terminator goes, in the order the terminator's arms run.
639    pub succs: Vec<BlockCall>,
640    /// How often the block runs, next to how often the function is entered. See [`Weight`].
641    pub weight: Weight,
642    pub(crate) first_inst: Option<Inst>,
643    pub(crate) last_inst: Option<Inst>,
644    pub(crate) prev: Option<Block>,
645    pub(crate) next: Option<Block>,
646}
647
648#[cfg(test)]
649mod tests {
650    use super::*;
651
652    #[test]
653    fn an_instruction_is_the_size_the_design_says() {
654        assert_eq!(size_of::<InstData>(), 28);
655        assert_eq!(size_of::<Operand>(), 8);
656    }
657
658    #[test]
659    fn a_virtual_register_is_its_own_number() {
660        let reg = Reg::virtual_reg(7);
661        assert!(reg.is_virtual());
662        assert_eq!(reg.number(), Some(7));
663        assert_eq!(reg.phys(), None);
664    }
665
666    #[test]
667    fn a_physical_register_is_not_a_virtual_one_of_the_same_number() {
668        let reg = Reg::physical(PhysReg::new(7));
669        assert!(!reg.is_virtual());
670        assert_eq!(reg.number(), None);
671        assert_eq!(reg.phys(), Some(PhysReg::new(7)));
672        assert_ne!(reg, Reg::virtual_reg(7));
673    }
674
675    #[test]
676    fn an_operand_keeps_what_it_was_constrained_to() {
677        let class = RegClass::new(0);
678        let plain = Operand::write(Reg::virtual_reg(1), class);
679        assert_eq!(plain.role, Role::Def);
680        assert_eq!(plain.constraint, Constraint::Reg);
681        let tied = plain.with(Constraint::Reuse(1));
682        assert_eq!(tied.constraint, Constraint::Reuse(1));
683        assert_eq!(tied.reg, plain.reg);
684        assert!(tied.role.is_def());
685        assert!(!Operand::read(Reg::virtual_reg(1), class).role.is_def());
686    }
687}