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