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 base register `base`.
148    #[inline]
149    pub(crate) fn with_base(self, base: Reg) -> Mem {
150        Mem {
151            base: reg_bits(base),
152            ..self
153        }
154    }
155
156    /// The same reference with displacement `disp`.
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// use backend_lang::mir::Mem;
162    /// use backend_lang::x86_64::RAX;
163    ///
164    /// assert_eq!(Mem::base(RAX.into()).with_disp(-8).disp(), -8);
165    /// ```
166    #[inline]
167    #[must_use]
168    pub const fn with_disp(self, disp: i32) -> Mem {
169        Mem { disp, ..self }
170    }
171
172    /// The base register, if any.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use backend_lang::mir::Mem;
178    ///
179    /// assert_eq!(Mem::absolute(0).base_reg(), None);
180    /// ```
181    #[inline]
182    #[must_use]
183    pub fn base_reg(&self) -> Option<Reg> {
184        bits_reg(self.base)
185    }
186
187    /// The index register and its scale, if any.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use backend_lang::mir::Mem;
193    /// use backend_lang::x86_64::RAX;
194    ///
195    /// assert_eq!(Mem::base(RAX.into()).index_reg(), None);
196    /// ```
197    #[inline]
198    #[must_use]
199    pub fn index_reg(&self) -> Option<(Reg, Scale)> {
200        bits_reg(self.index).map(|r| (r, self.scale))
201    }
202
203    /// The displacement.
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use backend_lang::mir::Mem;
209    ///
210    /// assert_eq!(Mem::absolute(12).disp(), 12);
211    /// ```
212    #[inline]
213    #[must_use]
214    pub const fn disp(&self) -> i32 {
215        self.disp
216    }
217
218    /// The registers the reference reads: its base, then its index.
219    pub(crate) fn regs(&self) -> impl Iterator<Item = Reg> {
220        self.base_reg()
221            .into_iter()
222            .chain(self.index_reg().map(|(r, _)| r))
223    }
224}
225
226fn reg_bits(r: Reg) -> u32 {
227    match (r.as_phys(), r.as_virt()) {
228        (Some(p), _) => p.index() as u32,
229        (_, Some(v)) => (v.index() as u32) | (1 << 31),
230        _ => NO_REG,
231    }
232}
233
234fn bits_reg(bits: u32) -> Option<Reg> {
235    if bits == NO_REG {
236        None
237    } else if bits & (1 << 31) != 0 {
238        Some(Reg::from(crate::regs::VReg::from_index(bits & !(1 << 31))))
239    } else {
240        Some(Reg::from(PReg::from_index(bits as u8)))
241    }
242}
243
244impl fmt::Debug for Mem {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        f.debug_struct("Mem")
247            .field("base", &self.base_reg())
248            .field("index", &self.index_reg())
249            .field("disp", &self.disp)
250            .finish()
251    }
252}
253
254/// A branch target and the arguments passed to its block parameters. Made by
255/// [`Function::block_call`](crate::mir::Function::block_call), which stores
256/// the arguments in the function; read them back with
257/// [`Function::args`](crate::mir::Function::args).
258///
259/// # Examples
260///
261/// ```
262/// use backend_lang::mir::{Function, Operand};
263/// use backend_lang::x86_64::{SYSV, RAX};
264///
265/// let mut f = Function::new("f", &SYSV);
266/// let _entry = f.block()?;
267/// let next = f.block()?;
268/// let call = f.block_call(next, &[Operand::Reg(RAX.into())])?;
269/// assert_eq!(call.block(), next);
270/// assert_eq!(f.args(call), [Operand::Reg(RAX.into())]);
271/// # Ok::<(), backend_lang::mir::MirError>(())
272/// ```
273#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
274pub struct BlockCall {
275    block: Block,
276    start: u32,
277    len: u32,
278}
279
280impl BlockCall {
281    pub(crate) const fn new(block: Block, start: u32, len: u32) -> BlockCall {
282        BlockCall { block, start, len }
283    }
284
285    /// The target block.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// use backend_lang::mir::Function;
291    /// use backend_lang::x86_64::SYSV;
292    ///
293    /// let mut f = Function::new("f", &SYSV);
294    /// let b = f.block()?;
295    /// assert_eq!(f.block_call(b, &[])?.block(), b);
296    /// # Ok::<(), backend_lang::mir::MirError>(())
297    /// ```
298    #[inline]
299    #[must_use]
300    pub const fn block(&self) -> Block {
301        self.block
302    }
303
304    /// The number of arguments.
305    ///
306    /// # Examples
307    ///
308    /// ```
309    /// use backend_lang::mir::{Function, Operand};
310    /// use backend_lang::x86_64::SYSV;
311    ///
312    /// let mut f = Function::new("f", &SYSV);
313    /// let b = f.block()?;
314    /// assert_eq!(f.block_call(b, &[Operand::Imm(1), Operand::Imm(2)])?.len(), 2);
315    /// # Ok::<(), backend_lang::mir::MirError>(())
316    /// ```
317    #[inline]
318    #[must_use]
319    pub const fn len(&self) -> usize {
320        self.len as usize
321    }
322
323    /// Whether there are no arguments.
324    ///
325    /// # Examples
326    ///
327    /// ```
328    /// use backend_lang::mir::Function;
329    /// use backend_lang::x86_64::SYSV;
330    ///
331    /// let mut f = Function::new("f", &SYSV);
332    /// let b = f.block()?;
333    /// assert!(f.block_call(b, &[])?.is_empty());
334    /// # Ok::<(), backend_lang::mir::MirError>(())
335    /// ```
336    #[inline]
337    #[must_use]
338    pub const fn is_empty(&self) -> bool {
339        self.len == 0
340    }
341
342    pub(crate) const fn range(&self) -> core::ops::Range<usize> {
343        self.start as usize..self.start as usize + self.len as usize
344    }
345}
346
347/// One operand of a machine instruction.
348///
349/// Which shapes an operand position takes is the instruction's
350/// [`OperandRule`](crate::target::OperandRule); [`Function::push`] checks
351/// them.
352///
353/// [`Function::push`]: crate::mir::Function::push
354///
355/// # Examples
356///
357/// ```
358/// use backend_lang::mir::{Mem, Operand};
359/// use backend_lang::x86_64::RDI;
360///
361/// let ops = [
362///     Operand::Reg(RDI.into()),
363///     Operand::Imm(-1),
364///     Operand::Mem(Mem::base(RDI.into()).with_disp(8)),
365/// ];
366/// assert!(matches!(ops[1], Operand::Imm(-1)));
367/// ```
368#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
369#[non_exhaustive]
370pub enum Operand {
371    /// A register, virtual or physical.
372    Reg(Reg),
373    /// An immediate: a signed value of the operation's width.
374    Imm(i64),
375    /// Memory at an address computed from registers.
376    Mem(Mem),
377    /// Memory in a stack slot, `offset` bytes into it.
378    Slot {
379        /// The slot.
380        slot: StackSlot,
381        /// Bytes from the slot's start.
382        offset: i32,
383    },
384    /// A function, by name: a direct call target, or (as a memory operand of
385    /// `lea`) the function's address.
386    Symbol(Symbol),
387    /// A condition code.
388    Cond(CondCode),
389    /// A branch target with arguments.
390    Block(BlockCall),
391}
392
393impl Operand {
394    /// The stack slot itself (offset 0): the form block parameters, branch
395    /// arguments, and copies use.
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// use backend_lang::mir::{Operand, StackSlot};
401    ///
402    /// let s = StackSlot::new(2);
403    /// assert_eq!(Operand::slot(s), Operand::Slot { slot: s, offset: 0 });
404    /// ```
405    #[inline]
406    #[must_use]
407    pub const fn slot(slot: StackSlot) -> Operand {
408        Operand::Slot { slot, offset: 0 }
409    }
410
411    /// The register, if this is a register operand.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// use backend_lang::mir::Operand;
417    /// use backend_lang::x86_64::RAX;
418    ///
419    /// assert_eq!(Operand::from(RAX).reg(), Some(RAX.into()));
420    /// assert_eq!(Operand::Imm(0).reg(), None);
421    /// ```
422    #[inline]
423    #[must_use]
424    pub const fn reg(&self) -> Option<Reg> {
425        match self {
426            Operand::Reg(r) => Some(*r),
427            _ => None,
428        }
429    }
430
431    /// Whether the operand is memory: an address or a stack slot.
432    ///
433    /// # Examples
434    ///
435    /// ```
436    /// use backend_lang::mir::{Mem, Operand, StackSlot};
437    ///
438    /// assert!(Operand::slot(StackSlot::new(0)).is_mem());
439    /// assert!(Operand::Mem(Mem::absolute(0)).is_mem());
440    /// assert!(!Operand::Imm(0).is_mem());
441    /// ```
442    #[inline]
443    #[must_use]
444    pub const fn is_mem(&self) -> bool {
445        matches!(self, Operand::Mem(_) | Operand::Slot { .. })
446    }
447}
448
449impl From<Reg> for Operand {
450    #[inline]
451    fn from(r: Reg) -> Operand {
452        Operand::Reg(r)
453    }
454}
455
456impl From<PReg> for Operand {
457    #[inline]
458    fn from(r: PReg) -> Operand {
459        Operand::Reg(r.into())
460    }
461}
462
463impl From<Mem> for Operand {
464    #[inline]
465    fn from(m: Mem) -> Operand {
466        Operand::Mem(m)
467    }
468}
469
470impl From<CondCode> for Operand {
471    #[inline]
472    fn from(c: CondCode) -> Operand {
473        Operand::Cond(c)
474    }
475}
476
477impl From<BlockCall> for Operand {
478    #[inline]
479    fn from(b: BlockCall) -> Operand {
480        Operand::Block(b)
481    }
482}
483
484impl From<Symbol> for Operand {
485    #[inline]
486    fn from(s: Symbol) -> Operand {
487        Operand::Symbol(s)
488    }
489}
490
491/// Whether an extra implicit register operand is read or written.
492///
493/// # Examples
494///
495/// ```
496/// use backend_lang::mir::{Implicit, ImplicitKind};
497/// use backend_lang::x86_64::RDI;
498///
499/// let i = Implicit { reg: RDI, kind: ImplicitKind::Use };
500/// assert_eq!(i.kind, ImplicitKind::Use);
501/// ```
502#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
503pub enum ImplicitKind {
504    /// The instruction reads the register.
505    Use,
506    /// The instruction writes the register with a defined value.
507    Def,
508}
509
510/// An implicit register operand attached to one instruction, beyond those
511/// its opcode always has: a call's argument and return registers, a
512/// return's result registers, a system call's arguments.
513///
514/// # Examples
515///
516/// ```
517/// use backend_lang::mir::{Effects, Function, ImplicitKind, Operand};
518/// use backend_lang::x86_64::{op, SYSV, RAX};
519///
520/// let mut f = Function::new("f", &SYSV);
521/// let b = f.block()?;
522/// let ret = f.push_with(b, op::RET, &[], Effects::new().uses(&[RAX]))?;
523/// assert_eq!(f.implicit(ret)[0].reg, RAX);
524/// assert_eq!(f.implicit(ret)[0].kind, ImplicitKind::Use);
525/// # Ok::<(), backend_lang::mir::MirError>(())
526/// ```
527#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
528pub struct Implicit {
529    /// The register.
530    pub reg: PReg,
531    /// Read or written.
532    pub kind: ImplicitKind,
533}