Skip to main content

lamina_platform/
shared.rs

1//! Types the IR and the MIR both name, defined once so the two layers agree.
2//!
3//! Each of these was once declared in `lamina-ir` and again in `lamina-mir`, which made
4//! `lamina::TlsModel` and `lamina::mir::TlsModel` distinct types with one name, bridged
5//! by hand at the lowering boundary.
6
7use std::{fmt, num::NonZeroU32};
8
9/// How a thread-local symbol's address is computed.
10///
11/// Only the two models that need no runtime support are here. General-dynamic and
12/// local-dynamic both call `__tls_get_addr`, which a freestanding target does not have,
13/// so the parser names them and refuses.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum TlsModel {
16    /// The offset from the thread pointer is a link-time constant. Only valid in an
17    /// executable that defines the symbol itself.
18    LocalExec,
19    /// The offset from the thread pointer is loaded from the GOT, so the symbol may be
20    /// defined by a shared object present at program start.
21    InitialExec,
22}
23
24impl TlsModel {
25    pub const fn as_str(&self) -> &'static str {
26        match self {
27            TlsModel::LocalExec => "local_exec",
28            TlsModel::InitialExec => "initial_exec",
29        }
30    }
31}
32
33impl fmt::Display for TlsModel {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(f, "{}", self.as_str())
36    }
37}
38
39/// Ordering guarantees an atomic operation carries, relative to other memory
40/// operations in the same thread and across threads.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub enum MemoryOrdering {
43    /// No synchronization or ordering constraints.
44    Relaxed,
45    /// Prevents reordering of subsequent loads.
46    Acquire,
47    /// Prevents reordering of previous stores.
48    Release,
49    /// Both acquire and release semantics.
50    AcqRel,
51    /// Sequentially consistent: the strongest guarantee.
52    SeqCst,
53}
54
55impl MemoryOrdering {
56    /// Where this ordering sits in the total order the C and LLVM memory models put
57    /// their orderings in, so that one can be compared against another.
58    pub fn strength(self) -> u8 {
59        match self {
60            Self::Relaxed => 0,
61            Self::Acquire => 1,
62            Self::Release => 2,
63            Self::AcqRel => 3,
64            Self::SeqCst => 4,
65        }
66    }
67
68    /// Why `failure` cannot be the failure ordering of a compare-exchange whose
69    /// success ordering is `success`, or `None` when it can.
70    ///
71    /// A compare-exchange that fails still performs the load and nothing else, so its
72    /// ordering carries no release, and it may not order more than the success
73    /// ordering does. C and LLVM both impose these two rules.
74    pub fn cmpxchg_failure_rejection(success: Self, failure: Self) -> Option<&'static str> {
75        match failure {
76            Self::Release | Self::AcqRel => {
77                Some("the failure ordering is a load: use relaxed, acquire or seqcst")
78            }
79            Self::Relaxed | Self::Acquire | Self::SeqCst => {
80                if failure.strength() > success.strength() {
81                    Some("the failure ordering may not be stronger than the success ordering")
82                } else {
83                    None
84                }
85            }
86        }
87    }
88}
89
90impl fmt::Display for MemoryOrdering {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        let s = match self {
93            MemoryOrdering::Relaxed => "relaxed",
94            MemoryOrdering::Acquire => "acquire",
95            MemoryOrdering::Release => "release",
96            MemoryOrdering::AcqRel => "acqrel",
97            MemoryOrdering::SeqCst => "seqcst",
98        };
99        write!(f, "{s}")
100    }
101}
102
103/// A position in the `.lamina` source an instruction was parsed from.
104#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
105pub struct SourceLoc {
106    pub line: NonZeroU32,
107    pub column: u32,
108}
109
110impl SourceLoc {
111    /// A position, or `None` when `line` is zero: lines are numbered from one.
112    pub fn new(line: u32, column: u32) -> Option<Self> {
113        NonZeroU32::new(line).map(|line| Self { line, column })
114    }
115
116    pub fn line(self) -> u32 {
117        self.line.get()
118    }
119}
120
121impl fmt::Display for SourceLoc {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(f, "{}:{}", self.line, self.column)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn source_loc_rejects_line_zero() {
133        assert!(SourceLoc::new(0, 4).is_none());
134        let loc = SourceLoc::new(7, 4).expect("line 7 is a line");
135        assert_eq!(loc.line(), 7);
136        assert_eq!(loc.to_string(), "7:4");
137    }
138
139    #[test]
140    fn a_compare_exchange_failure_ordering_is_a_load_no_stronger_than_the_success() {
141        use MemoryOrdering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
142        for (success, failure) in [
143            (SeqCst, SeqCst),
144            (SeqCst, Acquire),
145            (SeqCst, Relaxed),
146            (AcqRel, Acquire),
147            (Release, Acquire),
148            (Acquire, Acquire),
149            (Acquire, Relaxed),
150            (Relaxed, Relaxed),
151        ] {
152            assert_eq!(
153                MemoryOrdering::cmpxchg_failure_rejection(success, failure),
154                None,
155                "{success} / {failure}"
156            );
157        }
158        for (success, failure) in [
159            (SeqCst, Release),
160            (SeqCst, AcqRel),
161            (Relaxed, Release),
162            (Acquire, SeqCst),
163            (AcqRel, SeqCst),
164            (Relaxed, Acquire),
165        ] {
166            assert!(
167                MemoryOrdering::cmpxchg_failure_rejection(success, failure).is_some(),
168                "{success} / {failure}"
169            );
170        }
171    }
172
173    #[test]
174    fn tls_and_ordering_names_round_trip_to_text() {
175        assert_eq!(TlsModel::LocalExec.as_str(), "local_exec");
176        assert_eq!(TlsModel::InitialExec.to_string(), "initial_exec");
177        assert_eq!(MemoryOrdering::AcqRel.to_string(), "acqrel");
178        assert_eq!(MemoryOrdering::SeqCst.to_string(), "seqcst");
179    }
180}