1 //! Memory operation flags.
2 
3 use core::fmt;
4 
5 #[cfg(feature = "enable-serde")]
6 use serde::{Deserialize, Serialize};
7 
8 enum FlagBit {
9     Notrap,
10     Aligned,
11     Readonly,
12     LittleEndian,
13     BigEndian,
14 }
15 
16 const NAMES: [&str; 5] = ["notrap", "aligned", "readonly", "little", "big"];
17 
18 /// Endianness of a memory access.
19 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
20 pub enum Endianness {
21     /// Little-endian
22     Little,
23     /// Big-endian
24     Big,
25 }
26 
27 /// Flags for memory operations like load/store.
28 ///
29 /// Each of these flags introduce a limited form of undefined behavior. The flags each enable
30 /// certain optimizations that need to make additional assumptions. Generally, the semantics of a
31 /// program does not change when a flag is removed, but adding a flag will.
32 ///
33 /// In addition, the flags determine the endianness of the memory access.  By default,
34 /// any memory access uses the native endianness determined by the target ISA.  This can
35 /// be overridden for individual accesses by explicitly specifying little- or big-endian
36 /// semantics via the flags.
37 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
38 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
39 pub struct MemFlags {
40     bits: u8,
41 }
42 
43 impl MemFlags {
44     /// Create a new empty set of flags.
new() -> Self45     pub fn new() -> Self {
46         Self { bits: 0 }
47     }
48 
49     /// Create a set of flags representing an access from a "trusted" address, meaning it's
50     /// known to be aligned and non-trapping.
trusted() -> Self51     pub fn trusted() -> Self {
52         let mut result = Self::new();
53         result.set_notrap();
54         result.set_aligned();
55         result
56     }
57 
58     /// Read a flag bit.
read(self, bit: FlagBit) -> bool59     fn read(self, bit: FlagBit) -> bool {
60         self.bits & (1 << bit as usize) != 0
61     }
62 
63     /// Set a flag bit.
set(&mut self, bit: FlagBit)64     fn set(&mut self, bit: FlagBit) {
65         self.bits |= 1 << bit as usize
66     }
67 
68     /// Set a flag bit by name.
69     ///
70     /// Returns true if the flag was found and set, false for an unknown flag name.
71     /// Will also return false when trying to set inconsistent endianness flags.
set_by_name(&mut self, name: &str) -> bool72     pub fn set_by_name(&mut self, name: &str) -> bool {
73         match NAMES.iter().position(|&s| s == name) {
74             Some(bit) => {
75                 let bits = self.bits | 1 << bit;
76                 if (bits & (1 << FlagBit::LittleEndian as usize)) != 0
77                     && (bits & (1 << FlagBit::BigEndian as usize)) != 0
78                 {
79                     false
80                 } else {
81                     self.bits = bits;
82                     true
83                 }
84             }
85             None => false,
86         }
87     }
88 
89     /// Return endianness of the memory access.  This will return the endianness
90     /// explicitly specified by the flags if any, and will default to the native
91     /// endianness otherwise.  The native endianness has to be provided by the
92     /// caller since it is not explicitly encoded in CLIF IR -- this allows a
93     /// front end to create IR without having to know the target endianness.
endianness(self, native_endianness: Endianness) -> Endianness94     pub fn endianness(self, native_endianness: Endianness) -> Endianness {
95         if self.read(FlagBit::LittleEndian) {
96             Endianness::Little
97         } else if self.read(FlagBit::BigEndian) {
98             Endianness::Big
99         } else {
100             native_endianness
101         }
102     }
103 
104     /// Set endianness of the memory access.
set_endianness(&mut self, endianness: Endianness)105     pub fn set_endianness(&mut self, endianness: Endianness) {
106         match endianness {
107             Endianness::Little => self.set(FlagBit::LittleEndian),
108             Endianness::Big => self.set(FlagBit::BigEndian),
109         };
110         assert!(!(self.read(FlagBit::LittleEndian) && self.read(FlagBit::BigEndian)));
111     }
112 
113     /// Test if the `notrap` flag is set.
114     ///
115     /// Normally, trapping is part of the semantics of a load/store operation. If the platform
116     /// would cause a trap when accessing the effective address, the Cranelift memory operation is
117     /// also required to trap.
118     ///
119     /// The `notrap` flag tells Cranelift that the memory is *accessible*, which means that
120     /// accesses will not trap. This makes it possible to delete an unused load or a dead store
121     /// instruction.
notrap(self) -> bool122     pub fn notrap(self) -> bool {
123         self.read(FlagBit::Notrap)
124     }
125 
126     /// Set the `notrap` flag.
set_notrap(&mut self)127     pub fn set_notrap(&mut self) {
128         self.set(FlagBit::Notrap)
129     }
130 
131     /// Test if the `aligned` flag is set.
132     ///
133     /// By default, Cranelift memory instructions work with any unaligned effective address. If the
134     /// `aligned` flag is set, the instruction is permitted to trap or return a wrong result if the
135     /// effective address is misaligned.
aligned(self) -> bool136     pub fn aligned(self) -> bool {
137         self.read(FlagBit::Aligned)
138     }
139 
140     /// Set the `aligned` flag.
set_aligned(&mut self)141     pub fn set_aligned(&mut self) {
142         self.set(FlagBit::Aligned)
143     }
144 
145     /// Test if the `readonly` flag is set.
146     ///
147     /// Loads with this flag have no memory dependencies.
148     /// This results in undefined behavior if the dereferenced memory is mutated at any time
149     /// between when the function is called and when it is exited.
readonly(self) -> bool150     pub fn readonly(self) -> bool {
151         self.read(FlagBit::Readonly)
152     }
153 
154     /// Set the `readonly` flag.
set_readonly(&mut self)155     pub fn set_readonly(&mut self) {
156         self.set(FlagBit::Readonly)
157     }
158 }
159 
160 impl fmt::Display for MemFlags {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result161     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162         for (i, n) in NAMES.iter().enumerate() {
163             if self.bits & (1 << i) != 0 {
164                 write!(f, " {}", n)?;
165             }
166         }
167         Ok(())
168     }
169 }
170