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