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.
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.
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 lifetime 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 MemLifetimePolicy {
76   /// Standard memory should be allocated by the allocator and then deallocated
77   /// when the deallocate method is called for the finalized allocation.
78   Standard,
79 
80   /// Finalize memory should be allocated by the allocator, and then be
81   /// overwritten and deallocated after all finalization functions have been
82   /// run.
83   Finalize,
84 
85   /// NoAlloc memory should not be allocated by the JITLinkMemoryManager at
86   /// all. It is used for sections that don't need to be transferred to the
87   /// executor process, typically metadata sections.
88   NoAlloc
89 };
90 
91 /// Print a MemDeallocPolicy.
92 inline raw_ostream &operator<<(raw_ostream &OS, MemLifetimePolicy MLP) {
93   switch (MLP) {
94   case MemLifetimePolicy::Standard:
95     OS << "standard";
96     break;
97   case MemLifetimePolicy::Finalize:
98     OS << "finalize";
99     break;
100   case MemLifetimePolicy::NoAlloc:
101     OS << "noalloc";
102     break;
103   }
104   return OS;
105 }
106 
107 /// A pair of memory protections and allocation policies.
108 ///
109 /// Optimized for use as a small map key.
110 class AllocGroup {
111   friend struct llvm::DenseMapInfo<AllocGroup>;
112 
113   using underlying_type = uint8_t;
114   static constexpr unsigned BitsForProt = 3;
115   static constexpr unsigned BitsForLifetimePolicy = 2;
116   static constexpr unsigned MaxIdentifiers =
117       1U << (BitsForProt + BitsForLifetimePolicy);
118 
119 public:
120   static constexpr unsigned NumGroups = MaxIdentifiers;
121 
122   /// Create a default AllocGroup. No memory protections, standard
123   /// lifetime policy.
124   AllocGroup() = default;
125 
126   /// Create an AllocGroup from a MemProt only -- uses
127   /// MemLifetimePolicy::Standard.
128   AllocGroup(MemProt MP) : Id(static_cast<underlying_type>(MP)) {}
129 
130   /// Create an AllocGroup from a MemProt and a MemLifetimePolicy.
131   AllocGroup(MemProt MP, MemLifetimePolicy MLP)
132       : Id(static_cast<underlying_type>(MP) |
133            (static_cast<underlying_type>(MLP) << BitsForProt)) {}
134 
135   /// Returns the MemProt for this group.
136   MemProt getMemProt() const {
137     return static_cast<MemProt>(Id & ((1U << BitsForProt) - 1));
138   }
139 
140   /// Returns the MemLifetimePolicy for this group.
141   MemLifetimePolicy getMemLifetimePolicy() const {
142     return static_cast<MemLifetimePolicy>(Id >> BitsForProt);
143   }
144 
145   friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) {
146     return LHS.Id == RHS.Id;
147   }
148 
149   friend bool operator!=(const AllocGroup &LHS, const AllocGroup &RHS) {
150     return !(LHS == RHS);
151   }
152 
153   friend bool operator<(const AllocGroup &LHS, const AllocGroup &RHS) {
154     return LHS.Id < RHS.Id;
155   }
156 
157 private:
158   AllocGroup(underlying_type RawId) : Id(RawId) {}
159   underlying_type Id = 0;
160 };
161 
162 /// A specialized small-map for AllocGroups.
163 ///
164 /// Iteration order is guaranteed to match key ordering.
165 template <typename T> class AllocGroupSmallMap {
166 private:
167   using ElemT = std::pair<AllocGroup, T>;
168   using VectorTy = SmallVector<ElemT, 4>;
169 
170   static bool compareKey(const ElemT &E, const AllocGroup &G) {
171     return E.first < G;
172   }
173 
174 public:
175   using iterator = typename VectorTy::iterator;
176 
177   AllocGroupSmallMap() = default;
178   AllocGroupSmallMap(std::initializer_list<std::pair<AllocGroup, T>> Inits)
179       : Elems(Inits) {
180     llvm::sort(Elems, llvm::less_first());
181   }
182 
183   iterator begin() { return Elems.begin(); }
184   iterator end() { return Elems.end(); }
185   iterator find(AllocGroup G) {
186     auto I = lower_bound(Elems, G, compareKey);
187     return (I->first == G) ? I : end();
188   }
189 
190   bool empty() const { return Elems.empty(); }
191   size_t size() const { return Elems.size(); }
192 
193   T &operator[](AllocGroup G) {
194     auto I = lower_bound(Elems, G, compareKey);
195     if (I == Elems.end() || I->first != G)
196       I = Elems.insert(I, std::make_pair(G, T()));
197     return I->second;
198   }
199 
200 private:
201   VectorTy Elems;
202 };
203 
204 /// Print an AllocGroup.
205 inline raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG) {
206   return OS << '(' << AG.getMemProt() << ", " << AG.getMemLifetimePolicy()
207             << ')';
208 }
209 
210 } // end namespace orc
211 
212 template <> struct DenseMapInfo<orc::MemProt> {
213   static inline orc::MemProt getEmptyKey() { return orc::MemProt(~uint8_t(0)); }
214   static inline orc::MemProt getTombstoneKey() {
215     return orc::MemProt(~uint8_t(0) - 1);
216   }
217   static unsigned getHashValue(const orc::MemProt &Val) {
218     using UT = std::underlying_type_t<orc::MemProt>;
219     return DenseMapInfo<UT>::getHashValue(static_cast<UT>(Val));
220   }
221   static bool isEqual(const orc::MemProt &LHS, const orc::MemProt &RHS) {
222     return LHS == RHS;
223   }
224 };
225 
226 template <> struct DenseMapInfo<orc::AllocGroup> {
227   static inline orc::AllocGroup getEmptyKey() {
228     return orc::AllocGroup(~uint8_t(0));
229   }
230   static inline orc::AllocGroup getTombstoneKey() {
231     return orc::AllocGroup(~uint8_t(0) - 1);
232   }
233   static unsigned getHashValue(const orc::AllocGroup &Val) {
234     return DenseMapInfo<orc::AllocGroup::underlying_type>::getHashValue(Val.Id);
235   }
236   static bool isEqual(const orc::AllocGroup &LHS, const orc::AllocGroup &RHS) {
237     return LHS == RHS;
238   }
239 };
240 
241 } // end namespace llvm
242 
243 #endif // LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H
244