1 //! Source locations.
2 //!
3 //! Cranelift tracks the original source location of each instruction, and preserves the source
4 //! location when instructions are transformed.
5 
6 use core::fmt;
7 #[cfg(feature = "enable-serde")]
8 use serde::{Deserialize, Serialize};
9 
10 /// A source location.
11 ///
12 /// This is an opaque 32-bit number attached to each Cranelift IR instruction. Cranelift does not
13 /// interpret source locations in any way, they are simply preserved from the input to the output.
14 ///
15 /// The default source location uses the all-ones bit pattern `!0`. It is used for instructions
16 /// that can't be given a real source location.
17 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
18 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
19 pub struct SourceLoc(u32);
20 
21 impl SourceLoc {
22     /// Create a new source location with the given bits.
23     pub fn new(bits: u32) -> Self {
24         Self(bits)
25     }
26 
27     /// Is this the default source location?
28     pub fn is_default(self) -> bool {
29         self == Default::default()
30     }
31 
32     /// Read the bits of this source location.
33     pub fn bits(self) -> u32 {
34         self.0
35     }
36 }
37 
38 impl Default for SourceLoc {
39     fn default() -> Self {
40         Self(!0)
41     }
42 }
43 
44 impl fmt::Display for SourceLoc {
45     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46         if self.is_default() {
47             write!(f, "@-")
48         } else {
49             write!(f, "@{:04x}", self.0)
50         }
51     }
52 }
53 
54 /// Source location relative to another base source location.
55 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
57 pub struct RelSourceLoc(u32);
58 
59 impl RelSourceLoc {
60     /// Create a new relative source location with the given bits.
61     pub fn new(bits: u32) -> Self {
62         Self(bits)
63     }
64 
65     /// Creates a new `RelSourceLoc` based on the given base and offset.
66     ///
67     /// # Panics
68     ///
69     /// Panics if the offset is smaller than the base.
70     pub fn from_base_offset(base: SourceLoc, offset: SourceLoc) -> Self {
71         if base.is_default() || offset.is_default() {
72             Self::default()
73         } else {
74             Self(offset.bits().wrapping_sub(base.bits()))
75         }
76     }
77 
78     /// Expands the relative source location into an absolute one, using the given base.
79     pub fn expand(&self, base: SourceLoc) -> SourceLoc {
80         if self.is_default() || base.is_default() {
81             Default::default()
82         } else {
83             SourceLoc::new(self.0.wrapping_add(base.bits()))
84         }
85     }
86 
87     /// Is this the default relative source location?
88     pub fn is_default(self) -> bool {
89         self == Default::default()
90     }
91 }
92 
93 impl Default for RelSourceLoc {
94     fn default() -> Self {
95         Self(!0)
96     }
97 }
98 
99 impl fmt::Display for RelSourceLoc {
100     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101         if self.is_default() {
102             write!(f, "@-")
103         } else {
104             write!(f, "@+{:04x}", self.0)
105         }
106     }
107 }
108 
109 #[cfg(test)]
110 mod tests {
111     use crate::ir::SourceLoc;
112     use alloc::string::ToString;
113 
114     #[test]
115     fn display() {
116         assert_eq!(SourceLoc::default().to_string(), "@-");
117         assert_eq!(SourceLoc::new(0).to_string(), "@0000");
118         assert_eq!(SourceLoc::new(16).to_string(), "@0010");
119         assert_eq!(SourceLoc::new(0xabcdef).to_string(), "@abcdef");
120     }
121 }
122