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}
372
373impl InstKind {
374 /// Whether the instruction ends a block: jumps, branches, returns, traps.
375 ///
376 /// # Examples
377 ///
378 /// ```
379 /// use backend_lang::target::InstKind;
380 ///
381 /// assert!(InstKind::Return.is_terminator());
382 /// assert!(!InstKind::Plain.is_terminator());
383 /// ```
384 #[inline]
385 #[must_use]
386 pub const fn is_terminator(self) -> bool {
387 matches!(
388 self,
389 InstKind::Jump | InstKind::Branch | InstKind::Return | InstKind::Trap
390 )
391 }
392}
393
394/// The values an immediate operand may take.
395///
396/// Immediates are signed values of the operation's width, as isa-lang takes
397/// them (TARGET-DESC ยง3.1): for a 32-bit operation, all ones is `-1`.
398///
399/// # Examples
400///
401/// ```
402/// use backend_lang::target::ImmRange;
403///
404/// assert!(ImmRange::I8.fits(-128));
405/// assert!(!ImmRange::I8.fits(128));
406/// assert!(ImmRange::U8.fits(255));
407/// assert!(!ImmRange::None.fits(0));
408/// ```
409#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
410#[non_exhaustive]
411pub enum ImmRange {
412 /// No immediate is accepted.
413 None,
414 /// `-128..=127`.
415 I8,
416 /// `0..=255`.
417 U8,
418 /// `-32768..=32767`.
419 I16,
420 /// `-2^31..=2^31-1`.
421 I32,
422 /// Any 64-bit value.
423 I64,
424}
425
426impl ImmRange {
427 /// Whether `value` is in the range.
428 ///
429 /// # Examples
430 ///
431 /// ```
432 /// use backend_lang::target::ImmRange;
433 ///
434 /// assert!(ImmRange::I32.fits(i64::from(i32::MIN)));
435 /// assert!(!ImmRange::I32.fits(1 << 31));
436 /// assert!(ImmRange::I64.fits(i64::MIN));
437 /// ```
438 #[inline]
439 #[must_use]
440 pub const fn fits(self, value: i64) -> bool {
441 match self {
442 ImmRange::None => false,
443 ImmRange::I8 => value >= -128 && value <= 127,
444 ImmRange::U8 => value >= 0 && value <= 255,
445 ImmRange::I16 => value >= i16::MIN as i64 && value <= i16::MAX as i64,
446 ImmRange::I32 => value >= i32::MIN as i64 && value <= i32::MAX as i64,
447 ImmRange::I64 => true,
448 }
449 }
450}
451
452/// Which operand shapes a use accepts: a register, memory (an address or a
453/// stack slot), an immediate in a range, a function symbol.
454///
455/// # Examples
456///
457/// ```
458/// use backend_lang::target::{Accept, ImmRange};
459///
460/// let rmi = Accept::RM.with_imm(ImmRange::I32);
461/// assert!(rmi.reg() && rmi.mem());
462/// assert_eq!(rmi.imm(), ImmRange::I32);
463/// assert!(!Accept::REG.mem());
464/// ```
465#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
466pub struct Accept {
467 reg: bool,
468 mem: bool,
469 symbol: bool,
470 imm: ImmRange,
471}
472
473impl Accept {
474 /// A register only.
475 pub const REG: Accept = Accept {
476 reg: true,
477 mem: false,
478 symbol: false,
479 imm: ImmRange::None,
480 };
481 /// Memory only.
482 pub const MEM: Accept = Accept {
483 reg: false,
484 mem: true,
485 symbol: false,
486 imm: ImmRange::None,
487 };
488 /// A register or memory.
489 pub const RM: Accept = Accept {
490 reg: true,
491 mem: true,
492 symbol: false,
493 imm: ImmRange::None,
494 };
495 /// Nothing; extend with the `with_*` methods.
496 pub const NONE: Accept = Accept {
497 reg: false,
498 mem: false,
499 symbol: false,
500 imm: ImmRange::None,
501 };
502
503 /// Also accepts immediates in `range`.
504 ///
505 /// # Examples
506 ///
507 /// ```
508 /// use backend_lang::target::{Accept, ImmRange};
509 ///
510 /// assert_eq!(Accept::REG.with_imm(ImmRange::U8).imm(), ImmRange::U8);
511 /// ```
512 #[must_use]
513 pub const fn with_imm(self, range: ImmRange) -> Accept {
514 Accept { imm: range, ..self }
515 }
516
517 /// Also accepts a function symbol (a direct call target, or the address
518 /// of a function as a RIP-relative memory operand).
519 ///
520 /// # Examples
521 ///
522 /// ```
523 /// use backend_lang::target::Accept;
524 ///
525 /// assert!(Accept::MEM.with_symbol().symbol());
526 /// ```
527 #[must_use]
528 pub const fn with_symbol(self) -> Accept {
529 Accept {
530 symbol: true,
531 ..self
532 }
533 }
534
535 /// Whether a register is accepted.
536 ///
537 /// # Examples
538 ///
539 /// ```
540 /// use backend_lang::target::Accept;
541 ///
542 /// assert!(Accept::RM.reg());
543 /// ```
544 #[inline]
545 #[must_use]
546 pub const fn reg(self) -> bool {
547 self.reg
548 }
549
550 /// Whether memory (an address or a stack slot) is accepted.
551 ///
552 /// # Examples
553 ///
554 /// ```
555 /// use backend_lang::target::Accept;
556 ///
557 /// assert!(Accept::MEM.mem());
558 /// ```
559 #[inline]
560 #[must_use]
561 pub const fn mem(self) -> bool {
562 self.mem
563 }
564
565 /// Whether a function symbol is accepted.
566 ///
567 /// # Examples
568 ///
569 /// ```
570 /// use backend_lang::target::Accept;
571 ///
572 /// assert!(!Accept::RM.symbol());
573 /// ```
574 #[inline]
575 #[must_use]
576 pub const fn symbol(self) -> bool {
577 self.symbol
578 }
579
580 /// The immediates accepted.
581 ///
582 /// # Examples
583 ///
584 /// ```
585 /// use backend_lang::target::{Accept, ImmRange};
586 ///
587 /// assert_eq!(Accept::RM.imm(), ImmRange::None);
588 /// ```
589 #[inline]
590 #[must_use]
591 pub const fn imm(self) -> ImmRange {
592 self.imm
593 }
594}
595
596/// The rule for one explicit operand of an instruction.
597///
598/// Operands are listed definitions first, then uses, as the MIR printer
599/// shows them (`%2 = add64 %0, %1`).
600///
601/// # Examples
602///
603/// ```
604/// use backend_lang::target::{Accept, ImmRange, OperandRule};
605/// use backend_lang::x86_64::{self, op, GPR};
606///
607/// let add = x86_64::TARGET.inst(op::ADD64).unwrap();
608/// assert_eq!(add.operands()[0], OperandRule::Def(GPR));
609/// assert_eq!(add.operands()[1], OperandRule::Tied(0));
610/// assert_eq!(add.operands()[2], OperandRule::Use(GPR, Accept::RM.with_imm(ImmRange::I32)));
611/// ```
612#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
613#[non_exhaustive]
614pub enum OperandRule {
615 /// A register the instruction writes, of this class.
616 Def(RegClass),
617 /// A use that must be the same register as definition `n`: the
618 /// two-address constraint (`add d, s` reads and writes `d`). In SSA form
619 /// the definition and the tied use are different virtual registers; the
620 /// register allocator gives them one physical register.
621 Tied(u8),
622 /// A value the instruction reads, of this class (for a register; the
623 /// class of a memory operand's base and index is the target's address
624 /// class), in the shapes `Accept` allows.
625 Use(RegClass, Accept),
626 /// A condition code.
627 Cond,
628 /// A branch target with its arguments.
629 Block,
630}
631
632/// One machine instruction's description.
633///
634/// # Examples
635///
636/// ```
637/// use backend_lang::target::InstKind;
638/// use backend_lang::x86_64::{self, op, RAX, RDX, FLAGS};
639///
640/// let div = x86_64::TARGET.inst(op::IDIV64).unwrap();
641/// assert_eq!(div.name(), "idiv64");
642/// assert_eq!(div.kind(), InstKind::Plain);
643/// assert_eq!(div.implicit_uses(), [RAX, RDX]);
644/// assert_eq!(div.implicit_defs(), [RAX, RDX, FLAGS]);
645/// ```
646#[derive(Clone, Copy, Debug, PartialEq, Eq)]
647pub struct InstDesc {
648 name: &'static str,
649 kind: InstKind,
650 operands: &'static [OperandRule],
651 implicit_uses: &'static [PReg],
652 implicit_defs: &'static [PReg],
653 clobbers: RegSet,
654}
655
656impl InstDesc {
657 /// Describes an instruction.
658 ///
659 /// `implicit_uses` are registers read without being named as operands,
660 /// `implicit_defs` registers written that way (results the code may read
661 /// afterwards, such as `idiv`'s quotient in `rax`), and `clobbers`
662 /// registers left with unspecified contents.
663 ///
664 /// # Examples
665 ///
666 /// ```
667 /// use backend_lang::RegSet;
668 /// use backend_lang::target::{InstDesc, InstKind};
669 ///
670 /// let nop = InstDesc::new("nop", InstKind::Plain, &[], &[], &[], RegSet::EMPTY);
671 /// assert!(nop.operands().is_empty());
672 /// ```
673 #[must_use]
674 pub const fn new(
675 name: &'static str,
676 kind: InstKind,
677 operands: &'static [OperandRule],
678 implicit_uses: &'static [PReg],
679 implicit_defs: &'static [PReg],
680 clobbers: RegSet,
681 ) -> InstDesc {
682 InstDesc {
683 name,
684 kind,
685 operands,
686 implicit_uses,
687 implicit_defs,
688 clobbers,
689 }
690 }
691
692 /// The instruction's MIR name.
693 ///
694 /// # Examples
695 ///
696 /// ```
697 /// use backend_lang::x86_64::{self, op};
698 ///
699 /// assert_eq!(x86_64::TARGET.inst(op::MOVZX8).unwrap().name(), "movzx8");
700 /// ```
701 #[inline]
702 #[must_use]
703 pub const fn name(&self) -> &'static str {
704 self.name
705 }
706
707 /// What the instruction does to control flow.
708 ///
709 /// # Examples
710 ///
711 /// ```
712 /// use backend_lang::target::InstKind;
713 /// use backend_lang::x86_64::{self, op};
714 ///
715 /// assert_eq!(x86_64::TARGET.inst(op::RET).unwrap().kind(), InstKind::Return);
716 /// ```
717 #[inline]
718 #[must_use]
719 pub const fn kind(&self) -> InstKind {
720 self.kind
721 }
722
723 /// The explicit operands, definitions first. Empty for
724 /// [`Opcode::COPY`] and [`Opcode::PCOPY`], whose operands are checked
725 /// by their own rule.
726 ///
727 /// # Examples
728 ///
729 /// ```
730 /// use backend_lang::x86_64::{self, op};
731 ///
732 /// assert_eq!(x86_64::TARGET.inst(op::CMP64).unwrap().operands().len(), 2);
733 /// ```
734 #[inline]
735 #[must_use]
736 pub const fn operands(&self) -> &'static [OperandRule] {
737 self.operands
738 }
739
740 /// The number of explicit definitions (the leading `Def` operands).
741 ///
742 /// # Examples
743 ///
744 /// ```
745 /// use backend_lang::x86_64::{self, op};
746 ///
747 /// assert_eq!(x86_64::TARGET.inst(op::ADD64).unwrap().defs(), 1);
748 /// assert_eq!(x86_64::TARGET.inst(op::CMP64).unwrap().defs(), 0);
749 /// ```
750 #[must_use]
751 pub fn defs(&self) -> usize {
752 self.operands
753 .iter()
754 .take_while(|r| matches!(r, OperandRule::Def(_)))
755 .count()
756 }
757
758 /// Registers the instruction reads without naming them.
759 ///
760 /// # Examples
761 ///
762 /// ```
763 /// use backend_lang::x86_64::{self, op, RAX};
764 ///
765 /// assert_eq!(x86_64::TARGET.inst(op::CQO).unwrap().implicit_uses(), [RAX]);
766 /// ```
767 #[inline]
768 #[must_use]
769 pub const fn implicit_uses(&self) -> &'static [PReg] {
770 self.implicit_uses
771 }
772
773 /// Registers the instruction writes without naming them, with defined
774 /// results.
775 ///
776 /// # Examples
777 ///
778 /// ```
779 /// use backend_lang::x86_64::{self, op, RDX};
780 ///
781 /// assert_eq!(x86_64::TARGET.inst(op::CQO).unwrap().implicit_defs(), [RDX]);
782 /// ```
783 #[inline]
784 #[must_use]
785 pub const fn implicit_defs(&self) -> &'static [PReg] {
786 self.implicit_defs
787 }
788
789 /// Registers the instruction leaves with unspecified contents.
790 ///
791 /// # Examples
792 ///
793 /// ```
794 /// use backend_lang::x86_64::{self, op, RCX, R11};
795 ///
796 /// let clobbers = x86_64::TARGET.inst(op::SYSCALL).unwrap().clobbers();
797 /// assert!(clobbers.contains(RCX) && clobbers.contains(R11));
798 /// ```
799 #[inline]
800 #[must_use]
801 pub const fn clobbers(&self) -> RegSet {
802 self.clobbers
803 }
804}
805
806/// How a target can move values, which picks the parallel-move resolver's
807/// strategy (see [`moves`](crate::moves)).
808///
809/// # Examples
810///
811/// ```
812/// use backend_lang::target::ImmRange;
813/// use backend_lang::x86_64;
814///
815/// let caps = x86_64::TARGET.move_caps();
816/// assert!(caps.swap());
817/// assert!(!caps.mem_to_mem());
818/// assert_eq!(caps.store_imm(), ImmRange::I32);
819/// ```
820#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
821pub struct MoveCaps {
822 swap: bool,
823 mem_to_mem: bool,
824 store_imm: ImmRange,
825}
826
827impl MoveCaps {
828 /// Describes a target's moves: whether it can exchange two registers in
829 /// one instruction, move memory to memory directly, and which immediates
830 /// it can store to memory directly.
831 ///
832 /// # Examples
833 ///
834 /// ```
835 /// use backend_lang::target::{ImmRange, MoveCaps};
836 ///
837 /// let caps = MoveCaps::new(false, false, ImmRange::None);
838 /// assert!(!caps.swap());
839 /// ```
840 #[must_use]
841 pub const fn new(swap: bool, mem_to_mem: bool, store_imm: ImmRange) -> MoveCaps {
842 MoveCaps {
843 swap,
844 mem_to_mem,
845 store_imm,
846 }
847 }
848
849 /// Whether two registers of a class can be exchanged by one instruction
850 /// (x86-64 `xchg`).
851 ///
852 /// # Examples
853 ///
854 /// ```
855 /// use backend_lang::x86_64;
856 ///
857 /// assert!(x86_64::TARGET.move_caps().swap());
858 /// ```
859 #[inline]
860 #[must_use]
861 pub const fn swap(self) -> bool {
862 self.swap
863 }
864
865 /// Whether memory can be copied to memory without a register.
866 ///
867 /// # Examples
868 ///
869 /// ```
870 /// use backend_lang::x86_64;
871 ///
872 /// assert!(!x86_64::TARGET.move_caps().mem_to_mem());
873 /// ```
874 #[inline]
875 #[must_use]
876 pub const fn mem_to_mem(self) -> bool {
877 self.mem_to_mem
878 }
879
880 /// The immediates that can be stored to memory in one instruction.
881 ///
882 /// # Examples
883 ///
884 /// ```
885 /// use backend_lang::target::ImmRange;
886 /// use backend_lang::x86_64;
887 ///
888 /// assert!(x86_64::TARGET.move_caps().store_imm().fits(-1));
889 /// ```
890 #[inline]
891 #[must_use]
892 pub const fn store_imm(self) -> ImmRange {
893 self.store_imm
894 }
895}
896
897/// A target: register file, classes, instructions, and the registers with
898/// fixed jobs.
899///
900/// Targets are static data; [`x86_64::TARGET`](crate::x86_64::TARGET) is the
901/// one this release ships.
902///
903/// # Examples
904///
905/// ```
906/// use backend_lang::x86_64::{self, RSP, RBP, FLAGS};
907///
908/// let t = &x86_64::TARGET;
909/// assert_eq!(t.name(), "x86-64");
910/// assert_eq!(t.stack_pointer(), RSP);
911/// assert_eq!(t.frame_pointer(), RBP);
912/// assert_eq!(t.flags(), Some(FLAGS));
913/// ```
914#[derive(Debug)]
915pub struct Target {
916 name: &'static str,
917 isa: Isa,
918 regs: &'static [RegInfo],
919 classes: &'static [ClassInfo],
920 insts: &'static [InstDesc],
921 stack_pointer: PReg,
922 frame_pointer: PReg,
923 flags: Option<PReg>,
924 addr_class: RegClass,
925 move_caps: MoveCaps,
926 max_mem_operands: u8,
927 conds: &'static [&'static str],
928}
929
930/// The registers with fixed jobs, and the target-wide limits, for
931/// [`Target::new`].
932///
933/// # Examples
934///
935/// ```
936/// use backend_lang::target::TargetConfig;
937/// use backend_lang::x86_64::{self, RSP};
938///
939/// let cfg = TargetConfig::of(&x86_64::TARGET);
940/// assert_eq!(cfg.stack_pointer, RSP);
941/// ```
942#[derive(Clone, Copy, Debug, PartialEq, Eq)]
943pub struct TargetConfig {
944 /// The stack pointer.
945 pub stack_pointer: PReg,
946 /// The frame pointer.
947 pub frame_pointer: PReg,
948 /// The condition-flags register, if the ISA has one.
949 pub flags: Option<PReg>,
950 /// The class of a memory operand's base and index registers.
951 pub addr_class: RegClass,
952 /// How values move.
953 pub move_caps: MoveCaps,
954 /// The most memory operands one instruction may have.
955 pub max_mem_operands: u8,
956 /// The condition codes' names, indexed by
957 /// [`CondCode::code`](crate::mir::CondCode::code) (at most 255).
958 pub conds: &'static [&'static str],
959}
960
961impl TargetConfig {
962 /// The configuration of an existing target, to derive a variant from.
963 ///
964 /// # Examples
965 ///
966 /// ```
967 /// use backend_lang::target::TargetConfig;
968 /// use backend_lang::x86_64;
969 ///
970 /// assert_eq!(TargetConfig::of(&x86_64::TARGET).conds.len(), 16);
971 /// ```
972 #[must_use]
973 pub const fn of(target: &Target) -> TargetConfig {
974 TargetConfig {
975 stack_pointer: target.stack_pointer,
976 frame_pointer: target.frame_pointer,
977 flags: target.flags,
978 addr_class: target.addr_class,
979 move_caps: target.move_caps,
980 max_mem_operands: target.max_mem_operands,
981 conds: target.conds,
982 }
983 }
984}
985
986impl Target {
987 /// Describes a target.
988 ///
989 /// `insts` must begin with the [`Opcode::COPY`] and [`Opcode::PCOPY`]
990 /// descriptions (see [`Target::generic_insts`]); registers' classes must
991 /// index `classes`; at most [`MAX_PREGS`](crate::MAX_PREGS) registers.
992 /// The crate's own targets are checked by its tests; a hand-made one
993 /// that breaks these rules is caught by [`Function::verify`] as operands
994 /// that fit no class.
995 ///
996 /// # Examples
997 ///
998 /// A variant of x86-64 without `xchg`, to make the parallel-move
999 /// resolver break cycles through a scratch register instead:
1000 ///
1001 /// ```
1002 /// use backend_lang::target::{MoveCaps, ImmRange, Target, TargetConfig};
1003 /// use backend_lang::x86_64;
1004 ///
1005 /// let base = &x86_64::TARGET;
1006 /// let mut cfg = TargetConfig::of(base);
1007 /// cfg.move_caps = MoveCaps::new(false, false, ImmRange::I32);
1008 /// let no_swap = Target::new("x86-64-noswap", base.isa(), base.regs(), base.classes(), base.insts(), cfg);
1009 /// assert!(!no_swap.move_caps().swap());
1010 /// ```
1011 ///
1012 /// [`Function::verify`]: crate::mir::Function::verify
1013 #[must_use]
1014 pub const fn new(
1015 name: &'static str,
1016 isa: Isa,
1017 regs: &'static [RegInfo],
1018 classes: &'static [ClassInfo],
1019 insts: &'static [InstDesc],
1020 config: TargetConfig,
1021 ) -> Target {
1022 Target {
1023 name,
1024 isa,
1025 regs,
1026 classes,
1027 insts,
1028 stack_pointer: config.stack_pointer,
1029 frame_pointer: config.frame_pointer,
1030 flags: config.flags,
1031 addr_class: config.addr_class,
1032 move_caps: config.move_caps,
1033 max_mem_operands: config.max_mem_operands,
1034 conds: config.conds,
1035 }
1036 }
1037
1038 /// The two generic descriptions every instruction table starts with,
1039 /// [`Opcode::COPY`] then [`Opcode::PCOPY`].
1040 ///
1041 /// # Examples
1042 ///
1043 /// ```
1044 /// use backend_lang::target::{InstKind, Target};
1045 ///
1046 /// let [copy, pcopy] = Target::generic_insts();
1047 /// assert_eq!(copy.kind(), InstKind::Copy);
1048 /// assert_eq!(pcopy.name(), "pcopy");
1049 /// ```
1050 #[must_use]
1051 pub const fn generic_insts() -> [InstDesc; 2] {
1052 [
1053 InstDesc::new("copy", InstKind::Copy, &[], &[], &[], RegSet::EMPTY),
1054 InstDesc::new(
1055 "pcopy",
1056 InstKind::ParallelCopy,
1057 &[],
1058 &[],
1059 &[],
1060 RegSet::EMPTY,
1061 ),
1062 ]
1063 }
1064
1065 /// The target's name.
1066 ///
1067 /// # Examples
1068 ///
1069 /// ```
1070 /// use backend_lang::x86_64;
1071 ///
1072 /// assert_eq!(x86_64::TARGET.name(), "x86-64");
1073 /// ```
1074 #[inline]
1075 #[must_use]
1076 pub const fn name(&self) -> &'static str {
1077 self.name
1078 }
1079
1080 /// The ISA, which selects the encoder.
1081 ///
1082 /// # Examples
1083 ///
1084 /// ```
1085 /// use backend_lang::target::Isa;
1086 /// use backend_lang::x86_64;
1087 ///
1088 /// assert_eq!(x86_64::TARGET.isa(), Isa::X86_64);
1089 /// ```
1090 #[inline]
1091 #[must_use]
1092 pub const fn isa(&self) -> Isa {
1093 self.isa
1094 }
1095
1096 /// The register file, indexed by [`PReg::index`].
1097 ///
1098 /// # Examples
1099 ///
1100 /// ```
1101 /// use backend_lang::x86_64;
1102 ///
1103 /// assert_eq!(x86_64::TARGET.regs().len(), 33);
1104 /// ```
1105 #[inline]
1106 #[must_use]
1107 pub const fn regs(&self) -> &'static [RegInfo] {
1108 self.regs
1109 }
1110
1111 /// One register's description. An index the target does not have gets a
1112 /// placeholder named `<invalid>`, in no class.
1113 ///
1114 /// # Examples
1115 ///
1116 /// ```
1117 /// use backend_lang::PReg;
1118 /// use backend_lang::x86_64::{self, RDI};
1119 ///
1120 /// assert_eq!(x86_64::TARGET.reg(RDI).name(), "rdi");
1121 /// assert_eq!(x86_64::TARGET.reg(PReg::new(100).unwrap()).name(), "<invalid>");
1122 /// ```
1123 #[inline]
1124 #[must_use]
1125 pub fn reg(&self, reg: PReg) -> &RegInfo {
1126 self.regs.get(reg.index()).unwrap_or(&INVALID_REG)
1127 }
1128
1129 /// Whether the target has a register with this index.
1130 ///
1131 /// # Examples
1132 ///
1133 /// ```
1134 /// use backend_lang::PReg;
1135 /// use backend_lang::x86_64::{self, RAX};
1136 ///
1137 /// assert!(x86_64::TARGET.has_reg(RAX));
1138 /// assert!(!x86_64::TARGET.has_reg(PReg::new(100).unwrap()));
1139 /// ```
1140 #[inline]
1141 #[must_use]
1142 pub fn has_reg(&self, reg: PReg) -> bool {
1143 reg.index() < self.regs.len()
1144 }
1145
1146 /// The class table, indexed by [`RegClass::index`].
1147 ///
1148 /// # Examples
1149 ///
1150 /// ```
1151 /// use backend_lang::x86_64;
1152 ///
1153 /// assert_eq!(x86_64::TARGET.classes().len(), 3);
1154 /// ```
1155 #[inline]
1156 #[must_use]
1157 pub const fn classes(&self) -> &'static [ClassInfo] {
1158 self.classes
1159 }
1160
1161 /// One class's description. A class the target does not have gets an
1162 /// empty placeholder named `<invalid>`.
1163 ///
1164 /// # Examples
1165 ///
1166 /// ```
1167 /// use backend_lang::RegClass;
1168 /// use backend_lang::x86_64::{self, GPR};
1169 ///
1170 /// assert_eq!(x86_64::TARGET.class(GPR).name(), "gpr");
1171 /// assert!(x86_64::TARGET.class(RegClass::new(9)).regs().is_empty());
1172 /// ```
1173 #[inline]
1174 #[must_use]
1175 pub fn class(&self, class: RegClass) -> &ClassInfo {
1176 self.classes.get(class.index()).unwrap_or(&INVALID_CLASS)
1177 }
1178
1179 /// Whether the target has this class.
1180 ///
1181 /// # Examples
1182 ///
1183 /// ```
1184 /// use backend_lang::RegClass;
1185 /// use backend_lang::x86_64::{self, VEC};
1186 ///
1187 /// assert!(x86_64::TARGET.has_class(VEC));
1188 /// assert!(!x86_64::TARGET.has_class(RegClass::new(9)));
1189 /// ```
1190 #[inline]
1191 #[must_use]
1192 pub fn has_class(&self, class: RegClass) -> bool {
1193 class.index() < self.classes.len()
1194 }
1195
1196 /// The instruction table, indexed by [`Opcode::index`].
1197 ///
1198 /// # Examples
1199 ///
1200 /// ```
1201 /// use backend_lang::x86_64;
1202 ///
1203 /// assert!(x86_64::TARGET.insts().len() > 80);
1204 /// ```
1205 #[inline]
1206 #[must_use]
1207 pub const fn insts(&self) -> &'static [InstDesc] {
1208 self.insts
1209 }
1210
1211 /// One instruction's description, or `None` for an opcode the target
1212 /// does not have.
1213 ///
1214 /// # Examples
1215 ///
1216 /// ```
1217 /// use backend_lang::target::Opcode;
1218 /// use backend_lang::x86_64::{self, op};
1219 ///
1220 /// assert!(x86_64::TARGET.inst(op::LEA64).is_some());
1221 /// assert!(x86_64::TARGET.inst(Opcode::new(60000)).is_none());
1222 /// ```
1223 #[inline]
1224 #[must_use]
1225 pub fn inst(&self, opcode: Opcode) -> Option<&'static InstDesc> {
1226 self.insts.get(opcode.index())
1227 }
1228
1229 /// The opcode whose MIR name is `name`, by a scan of the table.
1230 ///
1231 /// # Examples
1232 ///
1233 /// ```
1234 /// use backend_lang::x86_64::{self, op};
1235 ///
1236 /// assert_eq!(x86_64::TARGET.opcode("sub32"), Some(op::SUB32));
1237 /// assert_eq!(x86_64::TARGET.opcode("frobnicate"), None);
1238 /// ```
1239 #[must_use]
1240 pub fn opcode(&self, name: &str) -> Option<Opcode> {
1241 self.insts
1242 .iter()
1243 .position(|d| d.name == name)
1244 .and_then(|i| u16::try_from(i).ok())
1245 .map(Opcode)
1246 }
1247
1248 /// The stack pointer.
1249 ///
1250 /// # Examples
1251 ///
1252 /// ```
1253 /// use backend_lang::x86_64::{self, RSP};
1254 ///
1255 /// assert_eq!(x86_64::TARGET.stack_pointer(), RSP);
1256 /// ```
1257 #[inline]
1258 #[must_use]
1259 pub const fn stack_pointer(&self) -> PReg {
1260 self.stack_pointer
1261 }
1262
1263 /// The frame pointer.
1264 ///
1265 /// # Examples
1266 ///
1267 /// ```
1268 /// use backend_lang::x86_64::{self, RBP};
1269 ///
1270 /// assert_eq!(x86_64::TARGET.frame_pointer(), RBP);
1271 /// ```
1272 #[inline]
1273 #[must_use]
1274 pub const fn frame_pointer(&self) -> PReg {
1275 self.frame_pointer
1276 }
1277
1278 /// The condition-flags register, modelled as a physical register so that
1279 /// flag producers and consumers are ordinary implicit definitions and
1280 /// uses.
1281 ///
1282 /// # Examples
1283 ///
1284 /// ```
1285 /// use backend_lang::x86_64::{self, FLAGS};
1286 ///
1287 /// assert_eq!(x86_64::TARGET.flags(), Some(FLAGS));
1288 /// ```
1289 #[inline]
1290 #[must_use]
1291 pub const fn flags(&self) -> Option<PReg> {
1292 self.flags
1293 }
1294
1295 /// The class of memory operands' base and index registers.
1296 ///
1297 /// # Examples
1298 ///
1299 /// ```
1300 /// use backend_lang::x86_64::{self, GPR};
1301 ///
1302 /// assert_eq!(x86_64::TARGET.addr_class(), GPR);
1303 /// ```
1304 #[inline]
1305 #[must_use]
1306 pub const fn addr_class(&self) -> RegClass {
1307 self.addr_class
1308 }
1309
1310 /// How values move on this target.
1311 ///
1312 /// # Examples
1313 ///
1314 /// ```
1315 /// use backend_lang::x86_64;
1316 ///
1317 /// assert!(x86_64::TARGET.move_caps().swap());
1318 /// ```
1319 #[inline]
1320 #[must_use]
1321 pub const fn move_caps(&self) -> MoveCaps {
1322 self.move_caps
1323 }
1324
1325 /// The most memory operands one instruction may have (one on x86-64).
1326 ///
1327 /// # Examples
1328 ///
1329 /// ```
1330 /// use backend_lang::x86_64;
1331 ///
1332 /// assert_eq!(x86_64::TARGET.max_mem_operands(), 1);
1333 /// ```
1334 #[inline]
1335 #[must_use]
1336 pub const fn max_mem_operands(&self) -> u8 {
1337 self.max_mem_operands
1338 }
1339
1340 /// The number of condition codes; a `Cond` operand is below it.
1341 ///
1342 /// # Examples
1343 ///
1344 /// ```
1345 /// use backend_lang::x86_64;
1346 ///
1347 /// assert_eq!(x86_64::TARGET.conds(), 16);
1348 /// ```
1349 #[inline]
1350 #[must_use]
1351 pub const fn conds(&self) -> u8 {
1352 if self.conds.len() > u8::MAX as usize {
1353 u8::MAX
1354 } else {
1355 self.conds.len() as u8
1356 }
1357 }
1358
1359 /// A condition code's name (`<invalid>` for one the target does not
1360 /// have).
1361 ///
1362 /// # Examples
1363 ///
1364 /// ```
1365 /// use backend_lang::mir::CondCode;
1366 /// use backend_lang::x86_64;
1367 ///
1368 /// assert_eq!(x86_64::TARGET.cond_name(CondCode::new(4)), "e");
1369 /// assert_eq!(x86_64::TARGET.cond_name(CondCode::new(99)), "<invalid>");
1370 /// ```
1371 #[inline]
1372 #[must_use]
1373 pub fn cond_name(&self, cond: crate::mir::CondCode) -> &'static str {
1374 self.conds
1375 .get(usize::from(cond.code()))
1376 .copied()
1377 .unwrap_or("<invalid>")
1378 }
1379
1380 /// The class of a physical register, or `None` if the target does not
1381 /// have it.
1382 ///
1383 /// # Examples
1384 ///
1385 /// ```
1386 /// use backend_lang::x86_64::{self, GPR, R8};
1387 ///
1388 /// assert_eq!(x86_64::TARGET.class_of(R8), Some(GPR));
1389 /// ```
1390 #[inline]
1391 #[must_use]
1392 pub fn class_of(&self, reg: PReg) -> Option<RegClass> {
1393 self.regs.get(reg.index()).map(RegInfo::class)
1394 }
1395}