Skip to main content

rucc_asm/
lib.rs

1//! Instruction encoders, the integrated assembler, inline assembly and relaxation.
2//!
3//! Design: `spec/11-asm-objects-debug.md`. Layer rank 10, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! What is written are the two things a compiler does with a machine function: the assembly text
8//! `-S` produces, which is [`print()`], and the bytes of a text section, which is [`assemble`].
9//! Section 11.1 asks for one instruction description behind both, and there is one: the walk over
10//! a function is the same walk in both files, reading the same list out of `rucc-target`, and the
11//! only difference is whether an instruction is written down by name or handed to the encoder. So
12//! the listing and the object file cannot come to disagree about what an instruction is.
13//!
14//! What [`assemble`] hands back with the bytes is what the linker has to be told: where each
15//! function starts and how long it is, and every place in the bytes that names something this
16//! file does not contain. The jumps inside a function are not among them, because by the end of a
17//! function every block has a place and they are filled in here.
18//!
19//! The assembler that reads `.s` and `.S`, inline assembly and relaxation are the rest of M3 and
20//! M4 and are not here yet.
21//!
22//! Every crate in the workspace is published, and publishing implies a promise. This one is
23//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
24//! Depend on the `rucc` binary's behaviour, not on this.
25
26#![doc(html_root_url = "https://docs.rs/rucc-asm/0.3.7")]
27
28mod att;
29mod bytes;
30mod format;
31
32pub use crate::att::print;
33pub use crate::bytes::assemble;
34pub use crate::format::Directives;
35
36use std::fmt;
37
38/// The milestone in `spec/17-milestones.md` that fills this crate in.
39pub const MILESTONE: &str = "M3";
40
41/// A function this compiler could not write out as assembly.
42///
43/// Neither of these is a program's fault and neither should ever reach a user, since a machine
44/// function that reaches here has been through the whole backend and the tests pin both of the
45/// claims below. They are errors rather than assertions because the alternative to reporting one
46/// is writing a listing that is quietly wrong, and a wrong listing is the failure section 11.1 is
47/// written to prevent.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum Error {
50    /// An opcode the target has no description of.
51    Opcode {
52        /// The function it turned up in.
53        func: String,
54        /// The opcode, as the machine IR spells it.
55        opcode: String,
56    },
57    /// A register that is still virtual, which is a function that was never allocated.
58    Virtual {
59        /// The function it turned up in.
60        func: String,
61        /// The opcode the register is an operand of.
62        opcode: String,
63    },
64    /// An instruction the description names and the encoder could not write bytes for.
65    ///
66    /// The two halves of the description are meant to hold the same instructions, and a test
67    /// pins that they do, so this is either a row that was left out of one of them or an
68    /// operand the machine cannot express in the instruction that was chosen for it.
69    Encode {
70        /// The function it turned up in.
71        func: String,
72        /// The opcode, as the machine IR spells it.
73        opcode: String,
74        /// What the encoder said, already formatted.
75        why: String,
76    },
77    /// A jump inside a function to somewhere more than two gigabytes away.
78    ///
79    /// A single function that long is not a program anybody wrote, and the four bytes a jump
80    /// carries are all there are, so this is reported rather than wrapped around into a jump
81    /// somewhere else entirely.
82    Distance {
83        /// The function it turned up in.
84        func: String,
85        /// How far the jump would have had to reach.
86        bytes: i64,
87    },
88    /// A machine this crate cannot write assembly for.
89    Machine {
90        /// The triple that was asked for.
91        triple: String,
92    },
93}
94
95impl fmt::Display for Error {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            Error::Opcode { func, opcode } => {
99                write!(f, "'{func}' has a '{opcode}' and the target does not say what one is")
100            }
101            Error::Virtual { func, opcode } => {
102                write!(f, "'{func}' reached the assembler with a virtual register in a '{opcode}'")
103            }
104            Error::Encode { func, opcode, why } => {
105                write!(f, "'{func}' has a '{opcode}' the encoder refused: {why}")
106            }
107            Error::Distance { func, bytes } => {
108                write!(f, "'{func}' has a jump reaching {bytes} bytes, which does not fit in four")
109            }
110            Error::Machine { triple } => {
111                write!(f, "there is no assembly writer for {triple} in this compiler yet")
112            }
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    #[test]
120    fn milestone_is_recorded() {
121        assert!(super::MILESTONE.starts_with('M'));
122    }
123}