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}