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_derive::{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, Hash)]
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.
new(bits: u32) -> Self23     pub fn new(bits: u32) -> Self {
24         Self(bits)
25     }
26 
27     /// Is this the default source location?
is_default(self) -> bool28     pub fn is_default(self) -> bool {
29         self == Default::default()
30     }
31 
32     /// Read the bits of this source location.
bits(self) -> u3233     pub fn bits(self) -> u32 {
34         self.0
35     }
36 }
37 
38 impl Default for SourceLoc {
default() -> Self39     fn default() -> Self {
40         Self(!0)
41     }
42 }
43 
44 impl fmt::Display for SourceLoc {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result45     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.
new(bits: u32) -> Self61     pub fn new(bits: u32) -> Self {
62         Self(bits)
63     }
64 
65     /// Creates a new `RelSourceLoc` based on the given base and offset.
from_base_offset(base: SourceLoc, offset: SourceLoc) -> Self66     pub fn from_base_offset(base: SourceLoc, offset: SourceLoc) -> Self {
67         if base.is_default() || offset.is_default() {
68             Self::default()
69         } else {
70             Self(offset.bits().wrapping_sub(base.bits()))
71         }
72     }
73 
74     /// Expands the relative source location into an absolute one, using the given base.
expand(&self, base: SourceLoc) -> SourceLoc75     pub fn expand(&self, base: SourceLoc) -> SourceLoc {
76         if self.is_default() || base.is_default() {
77             Default::default()
78         } else {
79             SourceLoc::new(self.0.wrapping_add(base.bits()))
80         }
81     }
82 
83     /// Is this the default relative source location?
is_default(self) -> bool84     pub fn is_default(self) -> bool {
85         self == Default::default()
86     }
87 }
88 
89 impl Default for RelSourceLoc {
default() -> Self90     fn default() -> Self {
91         Self(!0)
92     }
93 }
94 
95 impl fmt::Display for RelSourceLoc {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result96     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97         if self.is_default() {
98             write!(f, "@-")
99         } else {
100             write!(f, "@+{:04x}", self.0)
101         }
102     }
103 }
104 
105 #[cfg(test)]
106 mod tests {
107     use crate::ir::SourceLoc;
108     use alloc::string::ToString;
109 
110     #[test]
display()111     fn display() {
112         assert_eq!(SourceLoc::default().to_string(), "@-");
113         assert_eq!(SourceLoc::new(0).to_string(), "@0000");
114         assert_eq!(SourceLoc::new(16).to_string(), "@0010");
115         assert_eq!(SourceLoc::new(0xabcdef).to_string(), "@abcdef");
116     }
117 }
118