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 23namespace llvm { 24namespace orc { 25 26/// Describes Read/Write/Exec permissions for memory. 27enum 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. 36inline 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. 44inline 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. 57inline 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. 75enum 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. 92inline 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. 110class 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 119public: 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 157private: 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. 165template <typename T> class AllocGroupSmallMap { 166private: 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 174public: 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 200private: 201 VectorTy Elems; 202}; 203 204/// Print an AllocGroup. 205inline raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG) { 206 return OS << '(' << AG.getMemProt() << ", " << AG.getMemLifetime() << ')'; 207} 208 209} // end namespace orc 210 211template <> 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(static_cast<UT>(Val)); 219 } 220 static bool isEqual(const orc::MemProt &LHS, const orc::MemProt &RHS) { 221 return LHS == RHS; 222 } 223}; 224 225template <> 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.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