backend_lang/target/mod.rs
1//! Target descriptions: the backend's half of `TARGET-DESC.md`.
2//!
3//! A [`Target`] is data: its register file ([`RegInfo`]), its register
4//! classes ([`ClassInfo`]), and its machine instructions ([`InstDesc`]),
5//! each instruction with its operand rules, its implicit register uses and
6//! definitions, and the registers it clobbers. The generic machinery in this
7//! crate (MIR verification, the parallel-move resolver, frame layout) reads
8//! only this data; what is specific to an ISA is the description itself and
9//! the encoder that turns instructions into bytes (for x86-64, isa-lang).
10//!
11//! Implicit operands live here, not in isa-lang: as of isa-lang 0.2 the
12//! encoder's tables describe the explicit operands of each form only (the
13//! fixed `CL` of a shift is visible, `MUL`'s `RDX:RAX` is not). The register
14//! allocator needs all of them, so this layer records them per instruction;
15//! it can be cross-checked against isa-lang's own data once isa-lang carries
16//! it (isa-lang ROADMAP v0.5).
17//!
18//! [`abi`] adds calling conventions and [`frame`] the stack-frame model.
19
20pub mod abi;
21pub mod frame;
22
23use core::fmt;
24
25use crate::regs::{PReg, RegClass, RegSet};
26
27/// The instruction-set architecture a [`Target`] describes, which selects the
28/// encoder emission uses.
29///
30/// # Examples
31///
32/// ```
33/// use backend_lang::target::Isa;
34/// use backend_lang::x86_64;
35///
36/// assert_eq!(x86_64::TARGET.isa(), Isa::X86_64);
37/// ```
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39#[non_exhaustive]
40pub enum Isa {
41 /// x86-64 (AMD64 / Intel 64), encoded by `isa-lang`.
42 X86_64,
43}
44
45/// One physical register: its name, its class, and its hardware number.
46///
47/// # Examples
48///
49/// ```
50/// use backend_lang::x86_64::{self, GPR, R12};
51///
52/// let r12 = x86_64::TARGET.reg(R12);
53/// assert_eq!(r12.name(), "r12");
54/// assert_eq!(r12.class(), GPR);
55/// assert_eq!(r12.hw(), 12);
56/// ```
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub struct RegInfo {
59 name: &'static str,
60 class: RegClass,
61 hw: u8,
62}
63
64impl RegInfo {
65 /// Describes a register named `name` in `class`, encoded as `hw` (the
66 /// number the ISA's instruction fields carry).
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// use backend_lang::RegClass;
72 /// use backend_lang::target::RegInfo;
73 ///
74 /// let r = RegInfo::new("r0", RegClass::new(0), 0);
75 /// assert_eq!(r.name(), "r0");
76 /// ```
77 #[must_use]
78 pub const fn new(name: &'static str, class: RegClass, hw: u8) -> RegInfo {
79 RegInfo { name, class, hw }
80 }
81
82 /// The register's assembly name.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// use backend_lang::x86_64::{self, XMM3};
88 ///
89 /// assert_eq!(x86_64::TARGET.reg(XMM3).name(), "xmm3");
90 /// ```
91 #[inline]
92 #[must_use]
93 pub const fn name(&self) -> &'static str {
94 self.name
95 }
96
97 /// The register's class.
98 ///
99 /// # Examples
100 ///
101 /// ```
102 /// use backend_lang::x86_64::{self, VEC, XMM3};
103 ///
104 /// assert_eq!(x86_64::TARGET.reg(XMM3).class(), VEC);
105 /// ```
106 #[inline]
107 #[must_use]
108 pub const fn class(&self) -> RegClass {
109 self.class
110 }
111
112 /// The register's hardware number.
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use backend_lang::x86_64::{self, RSP};
118 ///
119 /// assert_eq!(x86_64::TARGET.reg(RSP).hw(), 4);
120 /// ```
121 #[inline]
122 #[must_use]
123 pub const fn hw(&self) -> u8 {
124 self.hw
125 }
126}
127
128/// The register returned by [`Target::reg`] for an index the target does not
129/// have: named `<invalid>`, in no class a target uses.
130const INVALID_REG: RegInfo = RegInfo::new("<invalid>", RegClass::new(u8::MAX), u8::MAX);
131
132/// A register class: its members, the members an allocator may hand out, and
133/// the size and alignment of a spill slot for one of its values.
134///
135/// # Examples
136///
137/// ```
138/// use backend_lang::x86_64::{self, GPR, RSP};
139///
140/// let gpr = x86_64::TARGET.class(GPR);
141/// assert!(gpr.regs().contains(RSP));
142/// assert!(!gpr.allocatable().contains(RSP));
143/// assert_eq!((gpr.spill_size(), gpr.spill_align()), (8, 8));
144/// ```
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct ClassInfo {
147 name: &'static str,
148 regs: RegSet,
149 allocatable: RegSet,
150 spill_size: u32,
151 spill_align: u32,
152}
153
154impl ClassInfo {
155 /// Describes a class named `name` with members `regs`, of which
156 /// `allocatable` may be handed out by an allocator, spilled to slots of
157 /// `spill_size` bytes aligned to `spill_align`.
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use backend_lang::RegSet;
163 /// use backend_lang::target::ClassInfo;
164 ///
165 /// let c = ClassInfo::new("int", RegSet::EMPTY, RegSet::EMPTY, 8, 8);
166 /// assert_eq!(c.name(), "int");
167 /// ```
168 #[must_use]
169 pub const fn new(
170 name: &'static str,
171 regs: RegSet,
172 allocatable: RegSet,
173 spill_size: u32,
174 spill_align: u32,
175 ) -> ClassInfo {
176 ClassInfo {
177 name,
178 regs,
179 allocatable,
180 spill_size,
181 spill_align,
182 }
183 }
184
185 /// The class's name, as the MIR printer writes it.
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// use backend_lang::x86_64::{self, VEC};
191 ///
192 /// assert_eq!(x86_64::TARGET.class(VEC).name(), "vec");
193 /// ```
194 #[inline]
195 #[must_use]
196 pub const fn name(&self) -> &'static str {
197 self.name
198 }
199
200 /// Every register of the class.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// use backend_lang::x86_64::{self, VEC};
206 ///
207 /// assert_eq!(x86_64::TARGET.class(VEC).regs().len(), 16);
208 /// ```
209 #[inline]
210 #[must_use]
211 pub const fn regs(&self) -> RegSet {
212 self.regs
213 }
214
215 /// The registers an allocator may assign: the class minus the stack
216 /// pointer and the frame pointer. (Without a frame pointer the frame
217 /// pointer register is allocatable too; see
218 /// [`Function::omit_frame_pointer`](crate::mir::Function::omit_frame_pointer).)
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// use backend_lang::x86_64::{self, GPR, RBP};
224 ///
225 /// let gpr = x86_64::TARGET.class(GPR);
226 /// assert_eq!(gpr.allocatable().len(), 14);
227 /// assert!(!gpr.allocatable().contains(RBP));
228 /// ```
229 #[inline]
230 #[must_use]
231 pub const fn allocatable(&self) -> RegSet {
232 self.allocatable
233 }
234
235 /// Bytes in a spill slot for one value of the class.
236 ///
237 /// # Examples
238 ///
239 /// ```
240 /// use backend_lang::x86_64::{self, VEC};
241 ///
242 /// assert_eq!(x86_64::TARGET.class(VEC).spill_size(), 16);
243 /// ```
244 #[inline]
245 #[must_use]
246 pub const fn spill_size(&self) -> u32 {
247 self.spill_size
248 }
249
250 /// Required alignment of a spill slot for the class.
251 ///
252 /// # Examples
253 ///
254 /// ```
255 /// use backend_lang::x86_64::{self, VEC};
256 ///
257 /// assert_eq!(x86_64::TARGET.class(VEC).spill_align(), 16);
258 /// ```
259 #[inline]
260 #[must_use]
261 pub const fn spill_align(&self) -> u32 {
262 self.spill_align
263 }
264}
265
266/// The class returned by [`Target::class`] for an index the target does not
267/// have: no registers.
268const INVALID_CLASS: ClassInfo = ClassInfo::new("<invalid>", RegSet::EMPTY, RegSet::EMPTY, 0, 1);
269
270/// A machine opcode: an index into the target's instruction table.
271///
272/// Every target's table begins with the two generic pseudo-instructions
273/// [`Opcode::COPY`] and [`Opcode::PCOPY`]; the rest are the target's own
274/// (for x86-64, the constants in [`x86_64::op`](crate::x86_64::op)).
275///
276/// # Examples
277///
278/// ```
279/// use backend_lang::target::Opcode;
280/// use backend_lang::x86_64::{self, op};
281///
282/// assert_eq!(x86_64::TARGET.inst(Opcode::COPY).unwrap().name(), "copy");
283/// assert_eq!(x86_64::TARGET.inst(op::ADD64).unwrap().name(), "add64");
284/// ```
285#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
286pub struct Opcode(u16);
287
288impl Opcode {
289 /// `dst = copy src`: one move between registers, stack slots, or from an
290 /// immediate. Lowered through the parallel-move resolver, so a
291 /// slot-to-slot copy gets a temporary register as the target requires.
292 /// A copy's width is its register's class; a copy with no register
293 /// (slot to slot, or an immediate to a slot) moves a value of the
294 /// target's address class (8 bytes on x86-64).
295 pub const COPY: Opcode = Opcode(0);
296 /// `d0, d1, ... = pcopy s0, s1, ...`: a parallel copy. Every source is
297 /// read before any destination is written, as on a control-flow edge.
298 /// Lowered through the parallel-move resolver.
299 pub const PCOPY: Opcode = Opcode(1);
300
301 /// The opcode with index `index` in a target's table.
302 ///
303 /// # Examples
304 ///
305 /// ```
306 /// use backend_lang::target::Opcode;
307 ///
308 /// assert_eq!(Opcode::new(0), Opcode::COPY);
309 /// ```
310 #[inline]
311 #[must_use]
312 pub const fn new(index: u16) -> Opcode {
313 Opcode(index)
314 }
315
316 /// The opcode's index in the target's instruction table.
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use backend_lang::target::Opcode;
322 ///
323 /// assert_eq!(Opcode::PCOPY.index(), 1);
324 /// ```
325 #[inline]
326 #[must_use]
327 pub const fn index(self) -> usize {
328 self.0 as usize
329 }
330}
331
332impl fmt::Debug for Opcode {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 write!(f, "Opcode({})", self.0)
335 }
336}
337
338/// What an instruction does to control flow, and which generic handling it
339/// gets.
340///
341/// # Examples
342///
343/// ```
344/// use backend_lang::target::InstKind;
345/// use backend_lang::x86_64::{self, op};
346///
347/// assert_eq!(x86_64::TARGET.inst(op::JMP).unwrap().kind(), InstKind::Jump);
348/// assert!(InstKind::Jump.is_terminator());
349/// assert!(!InstKind::Call.is_terminator());
350/// ```
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
352#[non_exhaustive]
353pub enum InstKind {
354 /// An ordinary instruction; control falls through.
355 Plain,
356 /// [`Opcode::COPY`].
357 Copy,
358 /// [`Opcode::PCOPY`].
359 ParallelCopy,
360 /// An unconditional branch to one block, passing arguments.
361 Jump,
362 /// A two-way conditional branch: a condition, then the taken and the
363 /// not-taken block, each with arguments.
364 Branch,
365 /// A return from the function (the epilogue runs first).
366 Return,
367 /// A call; control comes back to the next instruction.
368 Call,
369 /// An instruction that never falls through and never returns (`ud2`).
370 Trap,
371 /// A tail call: the epilogue runs, then control jumps to the callee,
372 /// which returns to this function's caller.
373 TailCall,
374 /// A multi-way branch through a table: a temporary definition, an index
375 /// register, then one target block per index (`jmptbl`). The targets
376 /// take no arguments. The temporary is written before the index is last
377 /// read, so the register allocator never gives the two one register.
378 Switch,
379}
380
381impl InstKind {
382 /// Whether the instruction ends a block: jumps, branches, returns, traps,
383 /// tail calls, and table branches.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// use backend_lang::target::InstKind;
389 ///
390 /// assert!(InstKind::Return.is_terminator());
391 /// assert!(!InstKind::Plain.is_terminator());
392 /// ```
393 #[inline]
394 #[must_use]
395 pub const fn is_terminator(self) -> bool {
396 matches!(
397 self,
398 InstKind::Jump
399 | InstKind::Branch
400 | InstKind::Return
401 | InstKind::Trap
402 | InstKind::TailCall
403 | InstKind::Switch
404 )
405 }
406}
407
408/// The values an immediate operand may take.
409///
410/// Immediates are signed values of the operation's width, as isa-lang takes
411/// them (TARGET-DESC ยง3.1): for a 32-bit operation, all ones is `-1`.
412///
413/// # Examples
414///
415/// ```
416/// use backend_lang::target::ImmRange;
417///
418/// assert!(ImmRange::I8.fits(-128));
419/// assert!(!ImmRange::I8.fits(128));
420/// assert!(ImmRange::U8.fits(255));
421/// assert!(!ImmRange::None.fits(0));
422/// ```
423#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
424#[non_exhaustive]
425pub enum ImmRange {
426 /// No immediate is accepted.
427 None,
428 /// `-128..=127`.
429 I8,
430 /// `0..=255`.
431 U8,
432 /// `-32768..=32767`.
433 I16,
434 /// `-2^31..=2^31-1`.
435 I32,
436 /// Any 64-bit value.
437 I64,
438}
439
440impl ImmRange {
441 /// Whether `value` is in the range.
442 ///
443 /// # Examples
444 ///
445 /// ```
446 /// use backend_lang::target::ImmRange;
447 ///
448 /// assert!(ImmRange::I32.fits(i64::from(i32::MIN)));
449 /// assert!(!ImmRange::I32.fits(1 << 31));
450 /// assert!(ImmRange::I64.fits(i64::MIN));
451 /// ```
452 #[inline]
453 #[must_use]
454 pub const fn fits(self, value: i64) -> bool {
455 match self {
456 ImmRange::None => false,
457 ImmRange::I8 => value >= -128 && value <= 127,
458 ImmRange::U8 => value >= 0 && value <= 255,
459 ImmRange::I16 => value >= i16::MIN as i64 && value <= i16::MAX as i64,
460 ImmRange::I32 => value >= i32::MIN as i64 && value <= i32::MAX as i64,
461 ImmRange::I64 => true,
462 }
463 }
464}
465
466/// Which operand shapes a use accepts: a register, memory (an address or a
467/// stack slot), an immediate in a range, a function symbol.
468///
469/// # Examples
470///
471/// ```
472/// use backend_lang::target::{Accept, ImmRange};
473///
474/// let rmi = Accept::RM.with_imm(ImmRange::I32);
475/// assert!(rmi.reg() && rmi.mem());
476/// assert_eq!(rmi.imm(), ImmRange::I32);
477/// assert!(!Accept::REG.mem());
478/// ```
479#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
480pub struct Accept {
481 reg: bool,
482 mem: bool,
483 symbol: bool,
484 imm: ImmRange,
485}
486
487impl Accept {
488 /// A register only.
489 pub const REG: Accept = Accept {
490 reg: true,
491 mem: false,
492 symbol: false,
493 imm: ImmRange::None,
494 };
495 /// Memory only.
496 pub const MEM: Accept = Accept {
497 reg: false,
498 mem: true,
499 symbol: false,
500 imm: ImmRange::None,
501 };
502 /// A register or memory.
503 pub const RM: Accept = Accept {
504 reg: true,
505 mem: true,
506 symbol: false,
507 imm: ImmRange::None,
508 };
509 /// Nothing; extend with the `with_*` methods.
510 pub const NONE: Accept = Accept {
511 reg: false,
512 mem: false,
513 symbol: false,
514 imm: ImmRange::None,
515 };
516
517 /// Also accepts immediates in `range`.
518 ///
519 /// # Examples
520 ///
521 /// ```
522 /// use backend_lang::target::{Accept, ImmRange};
523 ///
524 /// assert_eq!(Accept::REG.with_imm(ImmRange::U8).imm(), ImmRange::U8);
525 /// ```
526 #[must_use]
527 pub const fn with_imm(self, range: ImmRange) -> Accept {
528 Accept { imm: range, ..self }
529 }
530
531 /// Also accepts a function symbol (a direct call target, or the address
532 /// of a function as a RIP-relative memory operand).
533 ///
534 /// # Examples
535 ///
536 /// ```
537 /// use backend_lang::target::Accept;
538 ///
539 /// assert!(Accept::MEM.with_symbol().symbol());
540 /// ```
541 #[must_use]
542 pub const fn with_symbol(self) -> Accept {
543 Accept {
544 symbol: true,
545 ..self
546 }
547 }
548
549 /// Whether a register is accepted.
550 ///
551 /// # Examples
552 ///
553 /// ```
554 /// use backend_lang::target::Accept;
555 ///
556 /// assert!(Accept::RM.reg());
557 /// ```
558 #[inline]
559 #[must_use]
560 pub const fn reg(self) -> bool {
561 self.reg
562 }
563
564 /// Whether memory (an address or a stack slot) is accepted.
565 ///
566 /// # Examples
567 ///
568 /// ```
569 /// use backend_lang::target::Accept;
570 ///
571 /// assert!(Accept::MEM.mem());
572 /// ```
573 #[inline]
574 #[must_use]
575 pub const fn mem(self) -> bool {
576 self.mem
577 }
578
579 /// Whether a function symbol is accepted.
580 ///
581 /// # Examples
582 ///
583 /// ```
584 /// use backend_lang::target::Accept;
585 ///
586 /// assert!(!Accept::RM.symbol());
587 /// ```
588 #[inline]
589 #[must_use]
590 pub const fn symbol(self) -> bool {
591 self.symbol
592 }
593
594 /// The immediates accepted.
595 ///
596 /// # Examples
597 ///
598 /// ```
599 /// use backend_lang::target::{Accept, ImmRange};
600 ///
601 /// assert_eq!(Accept::RM.imm(), ImmRange::None);
602 /// ```
603 #[inline]
604 #[must_use]
605 pub const fn imm(self) -> ImmRange {
606 self.imm
607 }
608}
609
610/// The rule for one explicit operand of an instruction.
611///
612/// Operands are listed definitions first, then uses, as the MIR printer
613/// shows them (`%2 = add64 %0, %1`).
614///
615/// # Examples
616///
617/// ```
618/// use backend_lang::target::{Accept, ImmRange, OperandRule};
619/// use backend_lang::x86_64::{self, op, GPR};
620///
621/// let add = x86_64::TARGET.inst(op::ADD64).unwrap();
622/// assert_eq!(add.operands()[0], OperandRule::Def(GPR));
623/// assert_eq!(add.operands()[1], OperandRule::Tied(0));
624/// assert_eq!(add.operands()[2], OperandRule::Use(GPR, Accept::RM.with_imm(ImmRange::I32)));
625/// ```
626#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
627#[non_exhaustive]
628pub enum OperandRule {
629 /// A register the instruction writes, of this class.
630 Def(RegClass),
631 /// A use that must be the same register as definition `n`: the
632 /// two-address constraint (`add d, s` reads and writes `d`). In SSA form
633 /// the definition and the tied use are different virtual registers; the
634 /// register allocator gives them one physical register.
635 Tied(u8),
636 /// A value the instruction reads, of this class (for a register; the
637 /// class of a memory operand's base and index is the target's address
638 /// class), in the shapes `Accept` allows.
639 Use(RegClass, Accept),
640 /// A condition code.
641 Cond,
642 /// A branch target with its arguments.
643 Block,
644}
645
646/// One machine instruction's description.
647///
648/// # Examples
649///
650/// ```
651/// use backend_lang::target::InstKind;
652/// use backend_lang::x86_64::{self, op, RAX, RDX, FLAGS};
653///
654/// let div = x86_64::TARGET.inst(op::IDIV64).unwrap();
655/// assert_eq!(div.name(), "idiv64");
656/// assert_eq!(div.kind(), InstKind::Plain);
657/// assert_eq!(div.implicit_uses(), [RAX, RDX]);
658/// assert_eq!(div.implicit_defs(), [RAX, RDX, FLAGS]);
659/// ```
660#[derive(Clone, Copy, Debug, PartialEq, Eq)]
661pub struct InstDesc {
662 name: &'static str,
663 kind: InstKind,
664 operands: &'static [OperandRule],
665 implicit_uses: &'static [PReg],
666 implicit_defs: &'static [PReg],
667 clobbers: RegSet,
668}
669
670impl InstDesc {
671 /// Describes an instruction.
672 ///
673 /// `implicit_uses` are registers read without being named as operands,
674 /// `implicit_defs` registers written that way (results the code may read
675 /// afterwards, such as `idiv`'s quotient in `rax`), and `clobbers`
676 /// registers left with unspecified contents.
677 ///
678 /// # Examples
679 ///
680 /// ```
681 /// use backend_lang::RegSet;
682 /// use backend_lang::target::{InstDesc, InstKind};
683 ///
684 /// let nop = InstDesc::new("nop", InstKind::Plain, &[], &[], &[], RegSet::EMPTY);
685 /// assert!(nop.operands().is_empty());
686 /// ```
687 #[must_use]
688 pub const fn new(
689 name: &'static str,
690 kind: InstKind,
691 operands: &'static [OperandRule],
692 implicit_uses: &'static [PReg],
693 implicit_defs: &'static [PReg],
694 clobbers: RegSet,
695 ) -> InstDesc {
696 InstDesc {
697 name,
698 kind,
699 operands,
700 implicit_uses,
701 implicit_defs,
702 clobbers,
703 }
704 }
705
706 /// The instruction's MIR name.
707 ///
708 /// # Examples
709 ///
710 /// ```
711 /// use backend_lang::x86_64::{self, op};
712 ///
713 /// assert_eq!(x86_64::TARGET.inst(op::MOVZX8).unwrap().name(), "movzx8");
714 /// ```
715 #[inline]
716 #[must_use]
717 pub const fn name(&self) -> &'static str {
718 self.name
719 }
720
721 /// What the instruction does to control flow.
722 ///
723 /// # Examples
724 ///
725 /// ```
726 /// use backend_lang::target::InstKind;
727 /// use backend_lang::x86_64::{self, op};
728 ///
729 /// assert_eq!(x86_64::TARGET.inst(op::RET).unwrap().kind(), InstKind::Return);
730 /// ```
731 #[inline]
732 #[must_use]
733 pub const fn kind(&self) -> InstKind {
734 self.kind
735 }
736
737 /// The explicit operands, definitions first. Empty for
738 /// [`Opcode::COPY`] and [`Opcode::PCOPY`], whose operands are checked
739 /// by their own rule.
740 ///
741 /// # Examples
742 ///
743 /// ```
744 /// use backend_lang::x86_64::{self, op};
745 ///
746 /// assert_eq!(x86_64::TARGET.inst(op::CMP64).unwrap().operands().len(), 2);
747 /// ```
748 #[inline]
749 #[must_use]
750 pub const fn operands(&self) -> &'static [OperandRule] {
751 self.operands
752 }
753
754 /// The number of explicit definitions (the leading `Def` operands).
755 ///
756 /// # Examples
757 ///
758 /// ```
759 /// use backend_lang::x86_64::{self, op};
760 ///
761 /// assert_eq!(x86_64::TARGET.inst(op::ADD64).unwrap().defs(), 1);
762 /// assert_eq!(x86_64::TARGET.inst(op::CMP64).unwrap().defs(), 0);
763 /// ```
764 #[must_use]
765 pub fn defs(&self) -> usize {
766 self.operands
767 .iter()
768 .take_while(|r| matches!(r, OperandRule::Def(_)))
769 .count()
770 }
771
772 /// Registers the instruction reads without naming them.
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use backend_lang::x86_64::{self, op, RAX};
778 ///
779 /// assert_eq!(x86_64::TARGET.inst(op::CQO).unwrap().implicit_uses(), [RAX]);
780 /// ```
781 #[inline]
782 #[must_use]
783 pub const fn implicit_uses(&self) -> &'static [PReg] {
784 self.implicit_uses
785 }
786
787 /// Registers the instruction writes without naming them, with defined
788 /// results.
789 ///
790 /// # Examples
791 ///
792 /// ```
793 /// use backend_lang::x86_64::{self, op, RDX};
794 ///
795 /// assert_eq!(x86_64::TARGET.inst(op::CQO).unwrap().implicit_defs(), [RDX]);
796 /// ```
797 #[inline]
798 #[must_use]
799 pub const fn implicit_defs(&self) -> &'static [PReg] {
800 self.implicit_defs
801 }
802
803 /// Registers the instruction leaves with unspecified contents.
804 ///
805 /// # Examples
806 ///
807 /// ```
808 /// use backend_lang::x86_64::{self, op, RCX, R11};
809 ///
810 /// let clobbers = x86_64::TARGET.inst(op::SYSCALL).unwrap().clobbers();
811 /// assert!(clobbers.contains(RCX) && clobbers.contains(R11));
812 /// ```
813 #[inline]
814 #[must_use]
815 pub const fn clobbers(&self) -> RegSet {
816 self.clobbers
817 }
818}
819
820/// How a target can move values, which picks the parallel-move resolver's
821/// strategy (see [`moves`](crate::moves)).
822///
823/// # Examples
824///
825/// ```
826/// use backend_lang::target::ImmRange;
827/// use backend_lang::x86_64;
828///
829/// let caps = x86_64::TARGET.move_caps();
830/// assert!(caps.swap());
831/// assert!(!caps.mem_to_mem());
832/// assert_eq!(caps.store_imm(), ImmRange::I32);
833/// ```
834#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
835pub struct MoveCaps {
836 swap: bool,
837 mem_to_mem: bool,
838 store_imm: ImmRange,
839}
840
841impl MoveCaps {
842 /// Describes a target's moves: whether it can exchange two registers in
843 /// one instruction, move memory to memory directly, and which immediates
844 /// it can store to memory directly.
845 ///
846 /// # Examples
847 ///
848 /// ```
849 /// use backend_lang::target::{ImmRange, MoveCaps};
850 ///
851 /// let caps = MoveCaps::new(false, false, ImmRange::None);
852 /// assert!(!caps.swap());
853 /// ```
854 #[must_use]
855 pub const fn new(swap: bool, mem_to_mem: bool, store_imm: ImmRange) -> MoveCaps {
856 MoveCaps {
857 swap,
858 mem_to_mem,
859 store_imm,
860 }
861 }
862
863 /// Whether two registers of a class can be exchanged by one instruction
864 /// (x86-64 `xchg`).
865 ///
866 /// # Examples
867 ///
868 /// ```
869 /// use backend_lang::x86_64;
870 ///
871 /// assert!(x86_64::TARGET.move_caps().swap());
872 /// ```
873 #[inline]
874 #[must_use]
875 pub const fn swap(self) -> bool {
876 self.swap
877 }
878
879 /// Whether memory can be copied to memory without a register.
880 ///
881 /// # Examples
882 ///
883 /// ```
884 /// use backend_lang::x86_64;
885 ///
886 /// assert!(!x86_64::TARGET.move_caps().mem_to_mem());
887 /// ```
888 #[inline]
889 #[must_use]
890 pub const fn mem_to_mem(self) -> bool {
891 self.mem_to_mem
892 }
893
894 /// The immediates that can be stored to memory in one instruction.
895 ///
896 /// # Examples
897 ///
898 /// ```
899 /// use backend_lang::target::ImmRange;
900 /// use backend_lang::x86_64;
901 ///
902 /// assert!(x86_64::TARGET.move_caps().store_imm().fits(-1));
903 /// ```
904 #[inline]
905 #[must_use]
906 pub const fn store_imm(self) -> ImmRange {
907 self.store_imm
908 }
909}
910
911/// A target: register file, classes, instructions, and the registers with
912/// fixed jobs.
913///
914/// Targets are static data; [`x86_64::TARGET`](crate::x86_64::TARGET) is the
915/// one this release ships.
916///
917/// # Examples
918///
919/// ```
920/// use backend_lang::x86_64::{self, RSP, RBP, FLAGS};
921///
922/// let t = &x86_64::TARGET;
923/// assert_eq!(t.name(), "x86-64");
924/// assert_eq!(t.stack_pointer(), RSP);
925/// assert_eq!(t.frame_pointer(), RBP);
926/// assert_eq!(t.flags(), Some(FLAGS));
927/// ```
928#[derive(Debug)]
929pub struct Target {
930 name: &'static str,
931 isa: Isa,
932 regs: &'static [RegInfo],
933 classes: &'static [ClassInfo],
934 insts: &'static [InstDesc],
935 stack_pointer: PReg,
936 frame_pointer: PReg,
937 flags: Option<PReg>,
938 addr_class: RegClass,
939 move_caps: MoveCaps,
940 max_mem_operands: u8,
941 conds: &'static [&'static str],
942}
943
944/// The registers with fixed jobs, and the target-wide limits, for
945/// [`Target::new`].
946///
947/// # Examples
948///
949/// ```
950/// use backend_lang::target::TargetConfig;
951/// use backend_lang::x86_64::{self, RSP};
952///
953/// let cfg = TargetConfig::of(&x86_64::TARGET);
954/// assert_eq!(cfg.stack_pointer, RSP);
955/// ```
956#[derive(Clone, Copy, Debug, PartialEq, Eq)]
957pub struct TargetConfig {
958 /// The stack pointer.
959 pub stack_pointer: PReg,
960 /// The frame pointer.
961 pub frame_pointer: PReg,
962 /// The condition-flags register, if the ISA has one.
963 pub flags: Option<PReg>,
964 /// The class of a memory operand's base and index registers.
965 pub addr_class: RegClass,
966 /// How values move.
967 pub move_caps: MoveCaps,
968 /// The most memory operands one instruction may have.
969 pub max_mem_operands: u8,
970 /// The condition codes' names, indexed by
971 /// [`CondCode::code`](crate::mir::CondCode::code) (at most 255).
972 pub conds: &'static [&'static str],
973}
974
975impl TargetConfig {
976 /// The configuration of an existing target, to derive a variant from.
977 ///
978 /// # Examples
979 ///
980 /// ```
981 /// use backend_lang::target::TargetConfig;
982 /// use backend_lang::x86_64;
983 ///
984 /// assert_eq!(TargetConfig::of(&x86_64::TARGET).conds.len(), 16);
985 /// ```
986 #[must_use]
987 pub const fn of(target: &Target) -> TargetConfig {
988 TargetConfig {
989 stack_pointer: target.stack_pointer,
990 frame_pointer: target.frame_pointer,
991 flags: target.flags,
992 addr_class: target.addr_class,
993 move_caps: target.move_caps,
994 max_mem_operands: target.max_mem_operands,
995 conds: target.conds,
996 }
997 }
998}
999
1000impl Target {
1001 /// Describes a target.
1002 ///
1003 /// `insts` must begin with the [`Opcode::COPY`] and [`Opcode::PCOPY`]
1004 /// descriptions (see [`Target::generic_insts`]); registers' classes must
1005 /// index `classes`; at most [`MAX_PREGS`](crate::MAX_PREGS) registers.
1006 /// The crate's own targets are checked by its tests; a hand-made one
1007 /// that breaks these rules is caught by [`Function::verify`] as operands
1008 /// that fit no class.
1009 ///
1010 /// # Examples
1011 ///
1012 /// A variant of x86-64 without `xchg`, to make the parallel-move
1013 /// resolver break cycles through a scratch register instead:
1014 ///
1015 /// ```
1016 /// use backend_lang::target::{MoveCaps, ImmRange, Target, TargetConfig};
1017 /// use backend_lang::x86_64;
1018 ///
1019 /// let base = &x86_64::TARGET;
1020 /// let mut cfg = TargetConfig::of(base);
1021 /// cfg.move_caps = MoveCaps::new(false, false, ImmRange::I32);
1022 /// let no_swap = Target::new("x86-64-noswap", base.isa(), base.regs(), base.classes(), base.insts(), cfg);
1023 /// assert!(!no_swap.move_caps().swap());
1024 /// ```
1025 ///
1026 /// [`Function::verify`]: crate::mir::Function::verify
1027 #[must_use]
1028 pub const fn new(
1029 name: &'static str,
1030 isa: Isa,
1031 regs: &'static [RegInfo],
1032 classes: &'static [ClassInfo],
1033 insts: &'static [InstDesc],
1034 config: TargetConfig,
1035 ) -> Target {
1036 Target {
1037 name,
1038 isa,
1039 regs,
1040 classes,
1041 insts,
1042 stack_pointer: config.stack_pointer,
1043 frame_pointer: config.frame_pointer,
1044 flags: config.flags,
1045 addr_class: config.addr_class,
1046 move_caps: config.move_caps,
1047 max_mem_operands: config.max_mem_operands,
1048 conds: config.conds,
1049 }
1050 }
1051
1052 /// The two generic descriptions every instruction table starts with,
1053 /// [`Opcode::COPY`] then [`Opcode::PCOPY`].
1054 ///
1055 /// # Examples
1056 ///
1057 /// ```
1058 /// use backend_lang::target::{InstKind, Target};
1059 ///
1060 /// let [copy, pcopy] = Target::generic_insts();
1061 /// assert_eq!(copy.kind(), InstKind::Copy);
1062 /// assert_eq!(pcopy.name(), "pcopy");
1063 /// ```
1064 #[must_use]
1065 pub const fn generic_insts() -> [InstDesc; 2] {
1066 [
1067 InstDesc::new("copy", InstKind::Copy, &[], &[], &[], RegSet::EMPTY),
1068 InstDesc::new(
1069 "pcopy",
1070 InstKind::ParallelCopy,
1071 &[],
1072 &[],
1073 &[],
1074 RegSet::EMPTY,
1075 ),
1076 ]
1077 }
1078
1079 /// The target's name.
1080 ///
1081 /// # Examples
1082 ///
1083 /// ```
1084 /// use backend_lang::x86_64;
1085 ///
1086 /// assert_eq!(x86_64::TARGET.name(), "x86-64");
1087 /// ```
1088 #[inline]
1089 #[must_use]
1090 pub const fn name(&self) -> &'static str {
1091 self.name
1092 }
1093
1094 /// The ISA, which selects the encoder.
1095 ///
1096 /// # Examples
1097 ///
1098 /// ```
1099 /// use backend_lang::target::Isa;
1100 /// use backend_lang::x86_64;
1101 ///
1102 /// assert_eq!(x86_64::TARGET.isa(), Isa::X86_64);
1103 /// ```
1104 #[inline]
1105 #[must_use]
1106 pub const fn isa(&self) -> Isa {
1107 self.isa
1108 }
1109
1110 /// The register file, indexed by [`PReg::index`].
1111 ///
1112 /// # Examples
1113 ///
1114 /// ```
1115 /// use backend_lang::x86_64;
1116 ///
1117 /// assert_eq!(x86_64::TARGET.regs().len(), 33);
1118 /// ```
1119 #[inline]
1120 #[must_use]
1121 pub const fn regs(&self) -> &'static [RegInfo] {
1122 self.regs
1123 }
1124
1125 /// One register's description. An index the target does not have gets a
1126 /// placeholder named `<invalid>`, in no class.
1127 ///
1128 /// # Examples
1129 ///
1130 /// ```
1131 /// use backend_lang::PReg;
1132 /// use backend_lang::x86_64::{self, RDI};
1133 ///
1134 /// assert_eq!(x86_64::TARGET.reg(RDI).name(), "rdi");
1135 /// assert_eq!(x86_64::TARGET.reg(PReg::new(100).unwrap()).name(), "<invalid>");
1136 /// ```
1137 #[inline]
1138 #[must_use]
1139 pub fn reg(&self, reg: PReg) -> &RegInfo {
1140 self.regs.get(reg.index()).unwrap_or(&INVALID_REG)
1141 }
1142
1143 /// Whether the target has a register with this index.
1144 ///
1145 /// # Examples
1146 ///
1147 /// ```
1148 /// use backend_lang::PReg;
1149 /// use backend_lang::x86_64::{self, RAX};
1150 ///
1151 /// assert!(x86_64::TARGET.has_reg(RAX));
1152 /// assert!(!x86_64::TARGET.has_reg(PReg::new(100).unwrap()));
1153 /// ```
1154 #[inline]
1155 #[must_use]
1156 pub fn has_reg(&self, reg: PReg) -> bool {
1157 reg.index() < self.regs.len()
1158 }
1159
1160 /// The class table, indexed by [`RegClass::index`].
1161 ///
1162 /// # Examples
1163 ///
1164 /// ```
1165 /// use backend_lang::x86_64;
1166 ///
1167 /// assert_eq!(x86_64::TARGET.classes().len(), 3);
1168 /// ```
1169 #[inline]
1170 #[must_use]
1171 pub const fn classes(&self) -> &'static [ClassInfo] {
1172 self.classes
1173 }
1174
1175 /// One class's description. A class the target does not have gets an
1176 /// empty placeholder named `<invalid>`.
1177 ///
1178 /// # Examples
1179 ///
1180 /// ```
1181 /// use backend_lang::RegClass;
1182 /// use backend_lang::x86_64::{self, GPR};
1183 ///
1184 /// assert_eq!(x86_64::TARGET.class(GPR).name(), "gpr");
1185 /// assert!(x86_64::TARGET.class(RegClass::new(9)).regs().is_empty());
1186 /// ```
1187 #[inline]
1188 #[must_use]
1189 pub fn class(&self, class: RegClass) -> &ClassInfo {
1190 self.classes.get(class.index()).unwrap_or(&INVALID_CLASS)
1191 }
1192
1193 /// Whether the target has this class.
1194 ///
1195 /// # Examples
1196 ///
1197 /// ```
1198 /// use backend_lang::RegClass;
1199 /// use backend_lang::x86_64::{self, VEC};
1200 ///
1201 /// assert!(x86_64::TARGET.has_class(VEC));
1202 /// assert!(!x86_64::TARGET.has_class(RegClass::new(9)));
1203 /// ```
1204 #[inline]
1205 #[must_use]
1206 pub fn has_class(&self, class: RegClass) -> bool {
1207 class.index() < self.classes.len()
1208 }
1209
1210 /// The instruction table, indexed by [`Opcode::index`].
1211 ///
1212 /// # Examples
1213 ///
1214 /// ```
1215 /// use backend_lang::x86_64;
1216 ///
1217 /// assert!(x86_64::TARGET.insts().len() > 80);
1218 /// ```
1219 #[inline]
1220 #[must_use]
1221 pub const fn insts(&self) -> &'static [InstDesc] {
1222 self.insts
1223 }
1224
1225 /// One instruction's description, or `None` for an opcode the target
1226 /// does not have.
1227 ///
1228 /// # Examples
1229 ///
1230 /// ```
1231 /// use backend_lang::target::Opcode;
1232 /// use backend_lang::x86_64::{self, op};
1233 ///
1234 /// assert!(x86_64::TARGET.inst(op::LEA64).is_some());
1235 /// assert!(x86_64::TARGET.inst(Opcode::new(60000)).is_none());
1236 /// ```
1237 #[inline]
1238 #[must_use]
1239 pub fn inst(&self, opcode: Opcode) -> Option<&'static InstDesc> {
1240 self.insts.get(opcode.index())
1241 }
1242
1243 /// The opcode whose MIR name is `name`, by a scan of the table.
1244 ///
1245 /// # Examples
1246 ///
1247 /// ```
1248 /// use backend_lang::x86_64::{self, op};
1249 ///
1250 /// assert_eq!(x86_64::TARGET.opcode("sub32"), Some(op::SUB32));
1251 /// assert_eq!(x86_64::TARGET.opcode("frobnicate"), None);
1252 /// ```
1253 #[must_use]
1254 pub fn opcode(&self, name: &str) -> Option<Opcode> {
1255 self.insts
1256 .iter()
1257 .position(|d| d.name == name)
1258 .and_then(|i| u16::try_from(i).ok())
1259 .map(Opcode)
1260 }
1261
1262 /// The stack pointer.
1263 ///
1264 /// # Examples
1265 ///
1266 /// ```
1267 /// use backend_lang::x86_64::{self, RSP};
1268 ///
1269 /// assert_eq!(x86_64::TARGET.stack_pointer(), RSP);
1270 /// ```
1271 #[inline]
1272 #[must_use]
1273 pub const fn stack_pointer(&self) -> PReg {
1274 self.stack_pointer
1275 }
1276
1277 /// The frame pointer.
1278 ///
1279 /// # Examples
1280 ///
1281 /// ```
1282 /// use backend_lang::x86_64::{self, RBP};
1283 ///
1284 /// assert_eq!(x86_64::TARGET.frame_pointer(), RBP);
1285 /// ```
1286 #[inline]
1287 #[must_use]
1288 pub const fn frame_pointer(&self) -> PReg {
1289 self.frame_pointer
1290 }
1291
1292 /// The condition-flags register, modelled as a physical register so that
1293 /// flag producers and consumers are ordinary implicit definitions and
1294 /// uses.
1295 ///
1296 /// # Examples
1297 ///
1298 /// ```
1299 /// use backend_lang::x86_64::{self, FLAGS};
1300 ///
1301 /// assert_eq!(x86_64::TARGET.flags(), Some(FLAGS));
1302 /// ```
1303 #[inline]
1304 #[must_use]
1305 pub const fn flags(&self) -> Option<PReg> {
1306 self.flags
1307 }
1308
1309 /// The class of memory operands' base and index registers.
1310 ///
1311 /// # Examples
1312 ///
1313 /// ```
1314 /// use backend_lang::x86_64::{self, GPR};
1315 ///
1316 /// assert_eq!(x86_64::TARGET.addr_class(), GPR);
1317 /// ```
1318 #[inline]
1319 #[must_use]
1320 pub const fn addr_class(&self) -> RegClass {
1321 self.addr_class
1322 }
1323
1324 /// How values move on this target.
1325 ///
1326 /// # Examples
1327 ///
1328 /// ```
1329 /// use backend_lang::x86_64;
1330 ///
1331 /// assert!(x86_64::TARGET.move_caps().swap());
1332 /// ```
1333 #[inline]
1334 #[must_use]
1335 pub const fn move_caps(&self) -> MoveCaps {
1336 self.move_caps
1337 }
1338
1339 /// The most memory operands one instruction may have (one on x86-64).
1340 ///
1341 /// # Examples
1342 ///
1343 /// ```
1344 /// use backend_lang::x86_64;
1345 ///
1346 /// assert_eq!(x86_64::TARGET.max_mem_operands(), 1);
1347 /// ```
1348 #[inline]
1349 #[must_use]
1350 pub const fn max_mem_operands(&self) -> u8 {
1351 self.max_mem_operands
1352 }
1353
1354 /// The number of condition codes; a `Cond` operand is below it.
1355 ///
1356 /// # Examples
1357 ///
1358 /// ```
1359 /// use backend_lang::x86_64;
1360 ///
1361 /// assert_eq!(x86_64::TARGET.conds(), 16);
1362 /// ```
1363 #[inline]
1364 #[must_use]
1365 pub const fn conds(&self) -> u8 {
1366 if self.conds.len() > u8::MAX as usize {
1367 u8::MAX
1368 } else {
1369 self.conds.len() as u8
1370 }
1371 }
1372
1373 /// A condition code's name (`<invalid>` for one the target does not
1374 /// have).
1375 ///
1376 /// # Examples
1377 ///
1378 /// ```
1379 /// use backend_lang::mir::CondCode;
1380 /// use backend_lang::x86_64;
1381 ///
1382 /// assert_eq!(x86_64::TARGET.cond_name(CondCode::new(4)), "e");
1383 /// assert_eq!(x86_64::TARGET.cond_name(CondCode::new(99)), "<invalid>");
1384 /// ```
1385 #[inline]
1386 #[must_use]
1387 pub fn cond_name(&self, cond: crate::mir::CondCode) -> &'static str {
1388 self.conds
1389 .get(usize::from(cond.code()))
1390 .copied()
1391 .unwrap_or("<invalid>")
1392 }
1393
1394 /// The class of a physical register, or `None` if the target does not
1395 /// have it.
1396 ///
1397 /// # Examples
1398 ///
1399 /// ```
1400 /// use backend_lang::x86_64::{self, GPR, R8};
1401 ///
1402 /// assert_eq!(x86_64::TARGET.class_of(R8), Some(GPR));
1403 /// ```
1404 #[inline]
1405 #[must_use]
1406 pub fn class_of(&self, reg: PReg) -> Option<RegClass> {
1407 self.regs.get(reg.index()).map(RegInfo::class)
1408 }
1409}