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