1 //===-------- MemoryFlags.h - Memory allocation flags -----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Defines types and operations related to memory protection and allocation
10 // lifetimes.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H
15 #define LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H
16 
17 #include "llvm/ADT/BitmaskEnum.h"
18 #include "llvm/ADT/DenseMapInfo.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/Support/Memory.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 namespace llvm {
24 namespace orc {
25 
26 /// Describes Read/Write/Exec permissions for memory.
27 enum class MemProt {
28   None = 0,
29   Read = 1U << 0,
30   Write = 1U << 1,
31   Exec = 1U << 2,
32   LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Exec)
33 };
34 
35 /// Print a MemProt as an RWX triple.
36 inline raw_ostream &operator<<(raw_ostream &OS, MemProt MP) {
37   return OS << (((MP & MemProt::Read) != MemProt::None) ? 'R' : '-')
38             << (((MP & MemProt::Write) != MemProt::None) ? 'W' : '-')
39             << (((MP & MemProt::Exec) != MemProt::None) ? 'X' : '-');
40 }
41 
42 /// Convert a MemProt value to a corresponding sys::Memory::ProtectionFlags
43 /// value.
toSysMemoryProtectionFlags(MemProt MP)44 inline sys::Memory::ProtectionFlags toSysMemoryProtectionFlags(MemProt MP) {
45   std::underlying_type_t<sys::Memory::ProtectionFlags> PF = 0;
46   if ((MP & MemProt::Read) != MemProt::None)
47     PF |= sys::Memory::MF_READ;
48   if ((MP & MemProt::Write) != MemProt::None)
49     PF |= sys::Memory::MF_WRITE;
50   if ((MP & MemProt::Exec) != MemProt::None)
51     PF |= sys::Memory::MF_EXEC;
52   return static_cast<sys::Memory::ProtectionFlags>(PF);
53 }
54 
55 /// Convert a sys::Memory::ProtectionFlags value to a corresponding MemProt
56 /// value.
fromSysMemoryProtectionFlags(sys::Memory::ProtectionFlags PF)57 inline MemProt fromSysMemoryProtectionFlags(sys::Memory::ProtectionFlags PF) {
58   MemProt MP = MemProt::None;
59   if (PF & sys::Memory::MF_READ)
60     MP |= MemProt::Read;
61   if (PF & sys::Memory::MF_WRITE)
62     MP |= MemProt::Write;
63   if (PF & sys::Memory::MF_EXEC)
64     MP |= MemProt::None;
65   return MP;
66 }
67 
68 /// Describes a memory deallocation policy for memory to be allocated by a
69 /// JITLinkMemoryManager.
70 ///
71 /// All memory allocated by a call to JITLinkMemoryManager::allocate should be
72 /// deallocated if a call is made to
73 /// JITLinkMemoryManager::InFlightAllocation::abandon. The policies below apply
74 /// to finalized allocations.
75 enum class MemDeallocPolicy {
76   /// Standard memory should be deallocated when the deallocate method is called
77   /// for the finalized allocation.
78   Standard,
79 
80   /// Finalize memory should be overwritten and then deallocated after all
81   /// finalization functions have been run.
82   Finalize
83 };
84 
85 /// Print a MemDeallocPolicy.
86 inline raw_ostream &operator<<(raw_ostream &OS, MemDeallocPolicy MDP) {
87   return OS << (MDP == MemDeallocPolicy::Standard ? "standard" : "finalize");
88 }
89 
90 /// A pair of memory protections and allocation policies.
91 ///
92 /// Optimized for use as a small map key.
93 class AllocGroup {
94   friend struct llvm::DenseMapInfo<AllocGroup>;
95 
96   using underlying_type = uint8_t;
97   static constexpr unsigned BitsForProt = 3;
98   static constexpr unsigned BitsForDeallocPolicy = 1;
99   static constexpr unsigned MaxIdentifiers =
100       1U << (BitsForProt + BitsForDeallocPolicy);
101 
102 public:
103   static constexpr unsigned NumGroups = MaxIdentifiers;
104 
105   /// Create a default AllocGroup. No memory protections, standard
106   /// deallocation policy.
107   AllocGroup() = default;
108 
109   /// Create an AllocGroup from a MemProt only -- uses
110   /// MemoryDeallocationPolicy::Standard.
111   AllocGroup(MemProt MP) : Id(static_cast<underlying_type>(MP)) {}
112 
113   /// Create an AllocGroup from a MemProt and a MemoryDeallocationPolicy.
114   AllocGroup(MemProt MP, MemDeallocPolicy MDP)
115       : Id(static_cast<underlying_type>(MP) |
116            (static_cast<underlying_type>(MDP) << BitsForProt)) {}
117 
118   /// Returns the MemProt for this group.
119   MemProt getMemProt() const {
120     return static_cast<MemProt>(Id & ((1U << BitsForProt) - 1));
121   }
122 
123   /// Returns the MemoryDeallocationPolicy for this group.
124   MemDeallocPolicy getMemDeallocPolicy() const {
125     return static_cast<MemDeallocPolicy>(Id >> BitsForProt);
126   }
127 
128   friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) {
129     return LHS.Id == RHS.Id;
130   }
131 
132   friend bool operator!=(const AllocGroup &LHS, const AllocGroup &RHS) {
133     return !(LHS == RHS);
134   }
135 
136   friend bool operator<(const AllocGroup &LHS, const AllocGroup &RHS) {
137     return LHS.Id < RHS.Id;
138   }
139 
140 private:
141   AllocGroup(underlying_type RawId) : Id(RawId) {}
142   underlying_type Id = 0;
143 };
144 
145 /// A specialized small-map for AllocGroups.
146 ///
147 /// Iteration order is guaranteed to match key ordering.
148 template <typename T> class AllocGroupSmallMap {
149 private:
150   using ElemT = std::pair<AllocGroup, T>;
151   using VectorTy = SmallVector<ElemT, 4>;
152 
153   static bool compareKey(const ElemT &E, const AllocGroup &G) {
154     return E.first < G;
155   }
156 
157 public:
158   using iterator = typename VectorTy::iterator;
159 
160   AllocGroupSmallMap() = default;
161   AllocGroupSmallMap(std::initializer_list<std::pair<AllocGroup, T>> Inits)
162       : Elems(Inits) {
163     llvm::sort(Elems, llvm::less_first());
164   }
165 
166   iterator begin() { return Elems.begin(); }
167   iterator end() { return Elems.end(); }
168   iterator find(AllocGroup G) {
169     auto I = lower_bound(Elems, G, compareKey);
170     return (I->first == G) ? I : end();
171   }
172 
173   bool empty() const { return Elems.empty(); }
174   size_t size() const { return Elems.size(); }
175 
176   T &operator[](AllocGroup G) {
177     auto I = lower_bound(Elems, G, compareKey);
178     if (I == Elems.end() || I->first != G)
179       I = Elems.insert(I, std::make_pair(G, T()));
180     return I->second;
181   }
182 
183 private:
184   VectorTy Elems;
185 };
186 
187 /// Print an AllocGroup.
188 inline raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG) {
189   return OS << '(' << AG.getMemProt() << ", " << AG.getMemDeallocPolicy()
190             << ')';
191 }
192 
193 } // end namespace orc
194 
195 template <> struct DenseMapInfo<orc::MemProt> {
196   static inline orc::MemProt getEmptyKey() { return orc::MemProt(~uint8_t(0)); }
197   static inline orc::MemProt getTombstoneKey() {
198     return orc::MemProt(~uint8_t(0) - 1);
199   }
200   static unsigned getHashValue(const orc::MemProt &Val) {
201     using UT = std::underlying_type_t<orc::MemProt>;
202     return DenseMapInfo<UT>::getHashValue(static_cast<UT>(Val));
203   }
204   static bool isEqual(const orc::MemProt &LHS, const orc::MemProt &RHS) {
205     return LHS == RHS;
206   }
207 };
208 
209 template <> struct DenseMapInfo<orc::AllocGroup> {
210   static inline orc::AllocGroup getEmptyKey() {
211     return orc::AllocGroup(~uint8_t(0));
212   }
213   static inline orc::AllocGroup getTombstoneKey() {
214     return orc::AllocGroup(~uint8_t(0) - 1);
215   }
216   static unsigned getHashValue(const orc::AllocGroup &Val) {
217     return DenseMapInfo<orc::AllocGroup::underlying_type>::getHashValue(Val.Id);
218   }
219   static bool isEqual(const orc::AllocGroup &LHS, const orc::AllocGroup &RHS) {
220     return LHS == RHS;
221   }
222 };
223 
224 } // end namespace llvm
225 
226 #endif // LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H
227