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