1 //! Memory operation flags. 2 3 use core::fmt; 4 5 enum FlagBit { 6 Notrap, 7 Aligned, 8 Readonly, 9 } 10 11 const NAMES: [&str; 3] = ["notrap", "aligned", "readonly"]; 12 13 /// Flags for memory operations like load/store. 14 /// 15 /// Each of these flags introduce a limited form of undefined behavior. The flags each enable 16 /// certain optimizations that need to make additional assumptions. Generally, the semantics of a 17 /// program does not change when a flag is removed, but adding a flag will. 18 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] 19 pub struct MemFlags { 20 bits: u8, 21 } 22 23 impl MemFlags { 24 /// Create a new empty set of flags. new() -> Self25 pub fn new() -> Self { 26 Self { bits: 0 } 27 } 28 29 /// Create a set of flags representing an access from a "trusted" address, meaning it's 30 /// known to be aligned and non-trapping. trusted() -> Self31 pub fn trusted() -> Self { 32 let mut result = Self::new(); 33 result.set_notrap(); 34 result.set_aligned(); 35 result 36 } 37 38 /// Read a flag bit. read(self, bit: FlagBit) -> bool39 fn read(self, bit: FlagBit) -> bool { 40 self.bits & (1 << bit as usize) != 0 41 } 42 43 /// Set a flag bit. set(&mut self, bit: FlagBit)44 fn set(&mut self, bit: FlagBit) { 45 self.bits |= 1 << bit as usize 46 } 47 48 /// Set a flag bit by name. 49 /// 50 /// Returns true if the flag was found and set, false for an unknown flag name. set_by_name(&mut self, name: &str) -> bool51 pub fn set_by_name(&mut self, name: &str) -> bool { 52 match NAMES.iter().position(|&s| s == name) { 53 Some(bit) => { 54 self.bits |= 1 << bit; 55 true 56 } 57 None => false, 58 } 59 } 60 61 /// Test if the `notrap` flag is set. 62 /// 63 /// Normally, trapping is part of the semantics of a load/store operation. If the platform 64 /// would cause a trap when accessing the effective address, the Cranelift memory operation is 65 /// also required to trap. 66 /// 67 /// The `notrap` flag tells Cranelift that the memory is *accessible*, which means that 68 /// accesses will not trap. This makes it possible to delete an unused load or a dead store 69 /// instruction. notrap(self) -> bool70 pub fn notrap(self) -> bool { 71 self.read(FlagBit::Notrap) 72 } 73 74 /// Set the `notrap` flag. set_notrap(&mut self)75 pub fn set_notrap(&mut self) { 76 self.set(FlagBit::Notrap) 77 } 78 79 /// Test if the `aligned` flag is set. 80 /// 81 /// By default, Cranelift memory instructions work with any unaligned effective address. If the 82 /// `aligned` flag is set, the instruction is permitted to trap or return a wrong result if the 83 /// effective address is misaligned. aligned(self) -> bool84 pub fn aligned(self) -> bool { 85 self.read(FlagBit::Aligned) 86 } 87 88 /// Set the `aligned` flag. set_aligned(&mut self)89 pub fn set_aligned(&mut self) { 90 self.set(FlagBit::Aligned) 91 } 92 93 /// Test if the `readonly` flag is set. 94 /// 95 /// Loads with this flag have no memory dependencies. 96 /// This results in undefined behavior if the dereferenced memory is mutated at any time 97 /// between when the function is called and when it is exited. readonly(self) -> bool98 pub fn readonly(self) -> bool { 99 self.read(FlagBit::Readonly) 100 } 101 102 /// Set the `readonly` flag. set_readonly(&mut self)103 pub fn set_readonly(&mut self) { 104 self.set(FlagBit::Readonly) 105 } 106 } 107 108 impl fmt::Display for MemFlags { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 110 for (i, n) in NAMES.iter().enumerate() { 111 if self.bits & (1 << i) != 0 { 112 write!(f, " {}", n)?; 113 } 114 } 115 Ok(()) 116 } 117 } 118