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