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 MemLifetime { |
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, MemLifetime MLP) { |
93 | switch (MLP) { |
94 | case MemLifetime::Standard: |
95 | OS << "standard" ; |
96 | break; |
97 | case MemLifetime::Finalize: |
98 | OS << "finalize" ; |
99 | break; |
100 | case MemLifetime::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 | /// MemLifetime::Standard. |
128 | AllocGroup(MemProt MP) : Id(static_cast<underlying_type>(MP)) {} |
129 | |
130 | /// Create an AllocGroup from a MemProt and a MemLifetime. |
131 | AllocGroup(MemProt MP, MemLifetime 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 MemLifetime for this group. |
141 | MemLifetime getMemLifetime() const { |
142 | return static_cast<MemLifetime>(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.getMemLifetime() << ')'; |
207 | } |
208 | |
209 | } // end namespace orc |
210 | |
211 | template <> struct DenseMapInfo<orc::MemProt> { |
212 | static inline orc::MemProt getEmptyKey() { return orc::MemProt(~uint8_t(0)); } |
213 | static inline orc::MemProt getTombstoneKey() { |
214 | return orc::MemProt(~uint8_t(0) - 1); |
215 | } |
216 | static unsigned getHashValue(const orc::MemProt &Val) { |
217 | using UT = std::underlying_type_t<orc::MemProt>; |
218 | return DenseMapInfo<UT>::getHashValue(Val: static_cast<UT>(Val)); |
219 | } |
220 | static bool isEqual(const orc::MemProt &LHS, const orc::MemProt &RHS) { |
221 | return LHS == RHS; |
222 | } |
223 | }; |
224 | |
225 | template <> struct DenseMapInfo<orc::AllocGroup> { |
226 | static inline orc::AllocGroup getEmptyKey() { |
227 | return orc::AllocGroup(~uint8_t(0)); |
228 | } |
229 | static inline orc::AllocGroup getTombstoneKey() { |
230 | return orc::AllocGroup(~uint8_t(0) - 1); |
231 | } |
232 | static unsigned getHashValue(const orc::AllocGroup &Val) { |
233 | return DenseMapInfo<orc::AllocGroup::underlying_type>::getHashValue(Val: Val.Id); |
234 | } |
235 | static bool isEqual(const orc::AllocGroup &LHS, const orc::AllocGroup &RHS) { |
236 | return LHS == RHS; |
237 | } |
238 | }; |
239 | |
240 | } // end namespace llvm |
241 | |
242 | #endif // LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H |
243 | |