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 deallocation 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 MemDeallocPolicy {
76  /// Standard memory should be deallocated when the deallocate method is called
77  /// for the finalized allocation.
78  Standard,
79
80  /// Finalize memory should be overwritten and then deallocated after all
81  /// finalization functions have been run.
82  Finalize
83};
84
85/// Print a MemDeallocPolicy.
86inline raw_ostream &operator<<(raw_ostream &OS, MemDeallocPolicy MDP) {
87  return OS << (MDP == MemDeallocPolicy::Standard ? "standard" : "finalize");
88}
89
90/// A pair of memory protections and allocation policies.
91///
92/// Optimized for use as a small map key.
93class AllocGroup {
94  friend struct llvm::DenseMapInfo<AllocGroup>;
95
96  using underlying_type = uint8_t;
97  static constexpr unsigned BitsForProt = 3;
98  static constexpr unsigned BitsForDeallocPolicy = 1;
99  static constexpr unsigned MaxIdentifiers =
100      1U << (BitsForProt + BitsForDeallocPolicy);
101
102public:
103  static constexpr unsigned NumGroups = MaxIdentifiers;
104
105  /// Create a default AllocGroup. No memory protections, standard
106  /// deallocation policy.
107  AllocGroup() = default;
108
109  /// Create an AllocGroup from a MemProt only -- uses
110  /// MemoryDeallocationPolicy::Standard.
111  AllocGroup(MemProt MP) : Id(static_cast<underlying_type>(MP)) {}
112
113  /// Create an AllocGroup from a MemProt and a MemoryDeallocationPolicy.
114  AllocGroup(MemProt MP, MemDeallocPolicy MDP)
115      : Id(static_cast<underlying_type>(MP) |
116           (static_cast<underlying_type>(MDP) << BitsForProt)) {}
117
118  /// Returns the MemProt for this group.
119  MemProt getMemProt() const {
120    return static_cast<MemProt>(Id & ((1U << BitsForProt) - 1));
121  }
122
123  /// Returns the MemoryDeallocationPolicy for this group.
124  MemDeallocPolicy getMemDeallocPolicy() const {
125    return static_cast<MemDeallocPolicy>(Id >> BitsForProt);
126  }
127
128  friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) {
129    return LHS.Id == RHS.Id;
130  }
131
132  friend bool operator!=(const AllocGroup &LHS, const AllocGroup &RHS) {
133    return !(LHS == RHS);
134  }
135
136  friend bool operator<(const AllocGroup &LHS, const AllocGroup &RHS) {
137    return LHS.Id < RHS.Id;
138  }
139
140private:
141  AllocGroup(underlying_type RawId) : Id(RawId) {}
142  underlying_type Id = 0;
143};
144
145/// A specialized small-map for AllocGroups.
146///
147/// Iteration order is guaranteed to match key ordering.
148template <typename T> class AllocGroupSmallMap {
149private:
150  using ElemT = std::pair<AllocGroup, T>;
151  using VectorTy = SmallVector<ElemT, 4>;
152
153  static bool compareKey(const ElemT &E, const AllocGroup &G) {
154    return E.first < G;
155  }
156
157public:
158  using iterator = typename VectorTy::iterator;
159
160  AllocGroupSmallMap() = default;
161  AllocGroupSmallMap(std::initializer_list<std::pair<AllocGroup, T>> Inits)
162      : Elems(Inits) {
163    llvm::sort(Elems, llvm::less_first());
164  }
165
166  iterator begin() { return Elems.begin(); }
167  iterator end() { return Elems.end(); }
168  iterator find(AllocGroup G) {
169    auto I = lower_bound(Elems, G, compareKey);
170    return (I->first == G) ? I : end();
171  }
172
173  bool empty() const { return Elems.empty(); }
174  size_t size() const { return Elems.size(); }
175
176  T &operator[](AllocGroup G) {
177    auto I = lower_bound(Elems, G, compareKey);
178    if (I == Elems.end() || I->first != G)
179      I = Elems.insert(I, std::make_pair(G, T()));
180    return I->second;
181  }
182
183private:
184  VectorTy Elems;
185};
186
187/// Print an AllocGroup.
188inline raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG) {
189  return OS << '(' << AG.getMemProt() << ", " << AG.getMemDeallocPolicy()
190            << ')';
191}
192
193} // end namespace orc
194
195template <> struct DenseMapInfo<orc::MemProt> {
196  static inline orc::MemProt getEmptyKey() { return orc::MemProt(~uint8_t(0)); }
197  static inline orc::MemProt getTombstoneKey() {
198    return orc::MemProt(~uint8_t(0) - 1);
199  }
200  static unsigned getHashValue(const orc::MemProt &Val) {
201    using UT = std::underlying_type_t<orc::MemProt>;
202    return DenseMapInfo<UT>::getHashValue(static_cast<UT>(Val));
203  }
204  static bool isEqual(const orc::MemProt &LHS, const orc::MemProt &RHS) {
205    return LHS == RHS;
206  }
207};
208
209template <> struct DenseMapInfo<orc::AllocGroup> {
210  static inline orc::AllocGroup getEmptyKey() {
211    return orc::AllocGroup(~uint8_t(0));
212  }
213  static inline orc::AllocGroup getTombstoneKey() {
214    return orc::AllocGroup(~uint8_t(0) - 1);
215  }
216  static unsigned getHashValue(const orc::AllocGroup &Val) {
217    return DenseMapInfo<orc::AllocGroup::underlying_type>::getHashValue(Val.Id);
218  }
219  static bool isEqual(const orc::AllocGroup &LHS, const orc::AllocGroup &RHS) {
220    return LHS == RHS;
221  }
222};
223
224} // end namespace llvm
225
226#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_MEMORYFLAGS_H
227