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