Skip to main content

backend_lang/mir/
operand.rs

1//! Operands: registers, immediates, memory references, stack slots, function
2//! symbols, condition codes, and branch targets with arguments.
3
4use core::fmt;
5
6use super::entity::{Block, CondCode, StackSlot, Symbol};
7use crate::regs::{PReg, Reg};
8
9/// The scale of a memory operand's index register.
10///
11/// # Examples
12///
13/// ```
14/// use backend_lang::mir::Scale;
15///
16/// assert_eq!(Scale::X8.factor(), 8);
17/// ```
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub enum Scale {
20    /// `index * 1`.
21    X1,
22    /// `index * 2`.
23    X2,
24    /// `index * 4`.
25    X4,
26    /// `index * 8`.
27    X8,
28}
29
30impl Scale {
31    /// The multiplier: 1, 2, 4, or 8.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use backend_lang::mir::Scale;
37    ///
38    /// assert_eq!(Scale::X2.factor(), 2);
39    /// ```
40    #[inline]
41    #[must_use]
42    pub const fn factor(self) -> u8 {
43        match self {
44            Scale::X1 => 1,
45            Scale::X2 => 2,
46            Scale::X4 => 4,
47            Scale::X8 => 8,
48        }
49    }
50}
51
52/// Marks an absent base or index register; never a valid register (the
53/// largest virtual-register index is one less).
54const NO_REG: u32 = u32::MAX;
55
56/// A memory reference: `[base + index * scale + disp]`, each part optional.
57///
58/// The access width comes from the instruction (`mov64` reads eight bytes).
59/// Base and index are registers of the target's address class. A stack slot
60/// is its own operand, [`Operand::Slot`], because its address is only known
61/// once the frame is laid out.
62///
63/// # Examples
64///
65/// ```
66/// use backend_lang::mir::{Mem, Scale};
67/// use backend_lang::x86_64::{RDI, RSI};
68///
69/// let m = Mem::base(RDI.into()).with_index(RSI.into(), Scale::X8).with_disp(16);
70/// assert_eq!(m.base_reg(), Some(RDI.into()));
71/// assert_eq!(m.index_reg(), Some((RSI.into(), Scale::X8)));
72/// assert_eq!(m.disp(), 16);
73/// ```
74#[derive(Clone, Copy, PartialEq, Eq, Hash)]
75pub struct Mem {
76    base: u32,
77    index: u32,
78    disp: i32,
79    scale: Scale,
80}
81
82impl Mem {
83    /// `[base]`.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use backend_lang::mir::Mem;
89    /// use backend_lang::x86_64::RBX;
90    ///
91    /// assert_eq!(Mem::base(RBX.into()).disp(), 0);
92    /// ```
93    #[inline]
94    #[must_use]
95    pub fn base(base: Reg) -> Mem {
96        Mem {
97            base: reg_bits(base),
98            index: NO_REG,
99            disp: 0,
100            scale: Scale::X1,
101        }
102    }
103
104    /// `[disp]`: an absolute address, with no registers.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use backend_lang::mir::Mem;
110    ///
111    /// let m = Mem::absolute(0x1000);
112    /// assert_eq!(m.base_reg(), None);
113    /// assert_eq!(m.disp(), 0x1000);
114    /// ```
115    #[inline]
116    #[must_use]
117    pub const fn absolute(disp: i32) -> Mem {
118        Mem {
119            base: NO_REG,
120            index: NO_REG,
121            disp,
122            scale: Scale::X1,
123        }
124    }
125
126    /// The same reference with an index register.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use backend_lang::mir::{Mem, Scale};
132    /// use backend_lang::x86_64::{RAX, RCX};
133    ///
134    /// let m = Mem::base(RAX.into()).with_index(RCX.into(), Scale::X4);
135    /// assert_eq!(m.index_reg(), Some((RCX.into(), Scale::X4)));
136    /// ```
137    #[inline]
138    #[must_use]
139    pub fn with_index(self, index: Reg, scale: Scale) -> Mem {
140        Mem {
141            index: reg_bits(index),
142            scale,
143            ..self
144        }
145    }
146
147    /// The same reference with displacement `disp`.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use backend_lang::mir::Mem;
153    /// use backend_lang::x86_64::RAX;
154    ///
155    /// assert_eq!(Mem::base(RAX.into()).with_disp(-8).disp(), -8);
156    /// ```
157    #[inline]
158    #[must_use]
159    pub const fn with_disp(self, disp: i32) -> Mem {
160        Mem { disp, ..self }
161    }
162
163    /// The base register, if any.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use backend_lang::mir::Mem;
169    ///
170    /// assert_eq!(Mem::absolute(0).base_reg(), None);
171    /// ```
172    #[inline]
173    #[must_use]
174    pub fn base_reg(&self) -> Option<Reg> {
175        bits_reg(self.base)
176    }
177
178    /// The index register and its scale, if any.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use backend_lang::mir::Mem;
184    /// use backend_lang::x86_64::RAX;
185    ///
186    /// assert_eq!(Mem::base(RAX.into()).index_reg(), None);
187    /// ```
188    #[inline]
189    #[must_use]
190    pub fn index_reg(&self) -> Option<(Reg, Scale)> {
191        bits_reg(self.index).map(|r| (r, self.scale))
192    }
193
194    /// The displacement.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use backend_lang::mir::Mem;
200    ///
201    /// assert_eq!(Mem::absolute(12).disp(), 12);
202    /// ```
203    #[inline]
204    #[must_use]
205    pub const fn disp(&self) -> i32 {
206        self.disp
207    }
208
209    /// The registers the reference reads: its base, then its index.
210    pub(crate) fn regs(&self) -> impl Iterator<Item = Reg> {
211        self.base_reg()
212            .into_iter()
213            .chain(self.index_reg().map(|(r, _)| r))
214    }
215}
216
217fn reg_bits(r: Reg) -> u32 {
218    match (r.as_phys(), r.as_virt()) {
219        (Some(p), _) => p.index() as u32,
220        (_, Some(v)) => (v.index() as u32) | (1 << 31),
221        _ => NO_REG,
222    }
223}
224
225fn bits_reg(bits: u32) -> Option<Reg> {
226    if bits == NO_REG {
227        None
228    } else if bits & (1 << 31) != 0 {
229        Some(Reg::from(crate::regs::VReg::from_index(bits & !(1 << 31))))
230    } else {
231        Some(Reg::from(PReg::from_index(bits as u8)))
232    }
233}
234
235impl fmt::Debug for Mem {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        f.debug_struct("Mem")
238            .field("base", &self.base_reg())
239            .field("index", &self.index_reg())
240            .field("disp", &self.disp)
241            .finish()
242    }
243}
244
245/// A branch target and the arguments passed to its block parameters. Made by
246/// [`Function::block_call`](crate::mir::Function::block_call), which stores
247/// the arguments in the function; read them back with
248/// [`Function::args`](crate::mir::Function::args).
249///
250/// # Examples
251///
252/// ```
253/// use backend_lang::mir::{Function, Operand};
254/// use backend_lang::x86_64::{SYSV, RAX};
255///
256/// let mut f = Function::new("f", &SYSV);
257/// let _entry = f.block()?;
258/// let next = f.block()?;
259/// let call = f.block_call(next, &[Operand::Reg(RAX.into())])?;
260/// assert_eq!(call.block(), next);
261/// assert_eq!(f.args(call), [Operand::Reg(RAX.into())]);
262/// # Ok::<(), backend_lang::mir::MirError>(())
263/// ```
264#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
265pub struct BlockCall {
266    block: Block,
267    start: u32,
268    len: u32,
269}
270
271impl BlockCall {
272    pub(crate) const fn new(block: Block, start: u32, len: u32) -> BlockCall {
273        BlockCall { block, start, len }
274    }
275
276    /// The target block.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use backend_lang::mir::Function;
282    /// use backend_lang::x86_64::SYSV;
283    ///
284    /// let mut f = Function::new("f", &SYSV);
285    /// let b = f.block()?;
286    /// assert_eq!(f.block_call(b, &[])?.block(), b);
287    /// # Ok::<(), backend_lang::mir::MirError>(())
288    /// ```
289    #[inline]
290    #[must_use]
291    pub const fn block(&self) -> Block {
292        self.block
293    }
294
295    /// The number of arguments.
296    ///
297    /// # Examples
298    ///
299    /// ```
300    /// use backend_lang::mir::{Function, Operand};
301    /// use backend_lang::x86_64::SYSV;
302    ///
303    /// let mut f = Function::new("f", &SYSV);
304    /// let b = f.block()?;
305    /// assert_eq!(f.block_call(b, &[Operand::Imm(1), Operand::Imm(2)])?.len(), 2);
306    /// # Ok::<(), backend_lang::mir::MirError>(())
307    /// ```
308    #[inline]
309    #[must_use]
310    pub const fn len(&self) -> usize {
311        self.len as usize
312    }
313
314    /// Whether there are no arguments.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// use backend_lang::mir::Function;
320    /// use backend_lang::x86_64::SYSV;
321    ///
322    /// let mut f = Function::new("f", &SYSV);
323    /// let b = f.block()?;
324    /// assert!(f.block_call(b, &[])?.is_empty());
325    /// # Ok::<(), backend_lang::mir::MirError>(())
326    /// ```
327    #[inline]
328    #[must_use]
329    pub const fn is_empty(&self) -> bool {
330        self.len == 0
331    }
332
333    pub(crate) const fn range(&self) -> core::ops::Range<usize> {
334        self.start as usize..self.start as usize + self.len as usize
335    }
336}
337
338/// One operand of a machine instruction.
339///
340/// Which shapes an operand position takes is the instruction's
341/// [`OperandRule`](crate::target::OperandRule); [`Function::push`] checks
342/// them.
343///
344/// [`Function::push`]: crate::mir::Function::push
345///
346/// # Examples
347///
348/// ```
349/// use backend_lang::mir::{Mem, Operand};
350/// use backend_lang::x86_64::RDI;
351///
352/// let ops = [
353///     Operand::Reg(RDI.into()),
354///     Operand::Imm(-1),
355///     Operand::Mem(Mem::base(RDI.into()).with_disp(8)),
356/// ];
357/// assert!(matches!(ops[1], Operand::Imm(-1)));
358/// ```
359#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
360#[non_exhaustive]
361pub enum Operand {
362    /// A register, virtual or physical.
363    Reg(Reg),
364    /// An immediate: a signed value of the operation's width.
365    Imm(i64),
366    /// Memory at an address computed from registers.
367    Mem(Mem),
368    /// Memory in a stack slot, `offset` bytes into it.
369    Slot {
370        /// The slot.
371        slot: StackSlot,
372        /// Bytes from the slot's start.
373        offset: i32,
374    },
375    /// A function, by name: a direct call target, or (as a memory operand of
376    /// `lea`) the function's address.
377    Symbol(Symbol),
378    /// A condition code.
379    Cond(CondCode),
380    /// A branch target with arguments.
381    Block(BlockCall),
382}
383
384impl Operand {
385    /// The stack slot itself (offset 0): the form block parameters, branch
386    /// arguments, and copies use.
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// use backend_lang::mir::{Operand, StackSlot};
392    ///
393    /// let s = StackSlot::new(2);
394    /// assert_eq!(Operand::slot(s), Operand::Slot { slot: s, offset: 0 });
395    /// ```
396    #[inline]
397    #[must_use]
398    pub const fn slot(slot: StackSlot) -> Operand {
399        Operand::Slot { slot, offset: 0 }
400    }
401
402    /// The register, if this is a register operand.
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use backend_lang::mir::Operand;
408    /// use backend_lang::x86_64::RAX;
409    ///
410    /// assert_eq!(Operand::from(RAX).reg(), Some(RAX.into()));
411    /// assert_eq!(Operand::Imm(0).reg(), None);
412    /// ```
413    #[inline]
414    #[must_use]
415    pub const fn reg(&self) -> Option<Reg> {
416        match self {
417            Operand::Reg(r) => Some(*r),
418            _ => None,
419        }
420    }
421
422    /// Whether the operand is memory: an address or a stack slot.
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use backend_lang::mir::{Mem, Operand, StackSlot};
428    ///
429    /// assert!(Operand::slot(StackSlot::new(0)).is_mem());
430    /// assert!(Operand::Mem(Mem::absolute(0)).is_mem());
431    /// assert!(!Operand::Imm(0).is_mem());
432    /// ```
433    #[inline]
434    #[must_use]
435    pub const fn is_mem(&self) -> bool {
436        matches!(self, Operand::Mem(_) | Operand::Slot { .. })
437    }
438}
439
440impl From<Reg> for Operand {
441    #[inline]
442    fn from(r: Reg) -> Operand {
443        Operand::Reg(r)
444    }
445}
446
447impl From<PReg> for Operand {
448    #[inline]
449    fn from(r: PReg) -> Operand {
450        Operand::Reg(r.into())
451    }
452}
453
454impl From<Mem> for Operand {
455    #[inline]
456    fn from(m: Mem) -> Operand {
457        Operand::Mem(m)
458    }
459}
460
461impl From<CondCode> for Operand {
462    #[inline]
463    fn from(c: CondCode) -> Operand {
464        Operand::Cond(c)
465    }
466}
467
468impl From<BlockCall> for Operand {
469    #[inline]
470    fn from(b: BlockCall) -> Operand {
471        Operand::Block(b)
472    }
473}
474
475impl From<Symbol> for Operand {
476    #[inline]
477    fn from(s: Symbol) -> Operand {
478        Operand::Symbol(s)
479    }
480}
481
482/// Whether an extra implicit register operand is read or written.
483///
484/// # Examples
485///
486/// ```
487/// use backend_lang::mir::{Implicit, ImplicitKind};
488/// use backend_lang::x86_64::RDI;
489///
490/// let i = Implicit { reg: RDI, kind: ImplicitKind::Use };
491/// assert_eq!(i.kind, ImplicitKind::Use);
492/// ```
493#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
494pub enum ImplicitKind {
495    /// The instruction reads the register.
496    Use,
497    /// The instruction writes the register with a defined value.
498    Def,
499}
500
501/// An implicit register operand attached to one instruction, beyond those
502/// its opcode always has: a call's argument and return registers, a
503/// return's result registers, a system call's arguments.
504///
505/// # Examples
506///
507/// ```
508/// use backend_lang::mir::{Effects, Function, ImplicitKind, Operand};
509/// use backend_lang::x86_64::{op, SYSV, RAX};
510///
511/// let mut f = Function::new("f", &SYSV);
512/// let b = f.block()?;
513/// let ret = f.push_with(b, op::RET, &[], Effects::new().uses(&[RAX]))?;
514/// assert_eq!(f.implicit(ret)[0].reg, RAX);
515/// assert_eq!(f.implicit(ret)[0].kind, ImplicitKind::Use);
516/// # Ok::<(), backend_lang::mir::MirError>(())
517/// ```
518#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
519pub struct Implicit {
520    /// The register.
521    pub reg: PReg,
522    /// Read or written.
523    pub kind: ImplicitKind,
524}