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}