Skip to content

Commit a04a9de

Browse files
authored
[orc-rt] Add MemProt, MemLifetime, AllocGroup, and AllocGroupMap. (#157078)
MemProt and MemLifetime are enum classes representing memory protection (Read | Write | Exec) and lifetime policy (Standard or Finalize-only) respectively. An AllocGroup is a compressed (MemProt, MemLifetime) pair. AllocGroupSmallMap<T> is a compressed map of AllocGroup -> T. These utilities will be used in upcoming memory management APIs in the ORC runtime.
1 parent 52dd4b9 commit a04a9de

File tree

5 files changed

+236
-0
lines changed

5 files changed

+236
-0
lines changed

orc-rt/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ set(ORC_RT_HEADERS
99
orc-rt/IntervalMap.h
1010
orc-rt/IntervalSet.h
1111
orc-rt/Math.h
12+
orc-rt/MemoryFlags.h
1213
orc-rt/RTTI.h
1314
orc-rt/WrapperFunction.h
1415
orc-rt/SimplePackedSerialization.h

orc-rt/include/orc-rt/BitmaskEnum.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define ORC_RT_BITMASKENUM_H
1818

1919
#include "Math.h"
20+
#include "bit.h"
2021

2122
#include <cassert>
2223
#include <type_traits>
@@ -114,6 +115,14 @@ constexpr std::underlying_type_t<E> bitmask_enum_to_underlying(E Val) noexcept {
114115
return U;
115116
}
116117

118+
template <typename E, typename _ = std::enable_if_t<is_bitmask_enum_v<E>>>
119+
struct bitmask_enum_num_bits {
120+
static constexpr int value = bit_width(largest_bitmask_enum_bit<E>::value);
121+
};
122+
123+
template <typename E>
124+
inline constexpr int bitmask_enum_num_bits_v = bitmask_enum_num_bits<E>::value;
125+
117126
template <typename E, typename = std::enable_if_t<is_bitmask_enum_v<E>>>
118127
constexpr E operator~(E Val) noexcept {
119128
return static_cast<E>(~bitmask_enum_to_underlying(Val) &
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
// Memory allocation flags.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef ORC_RT_MEMORYFLAGS_H
14+
#define ORC_RT_MEMORYFLAGS_H
15+
16+
#include "orc-rt/BitmaskEnum.h"
17+
#include "orc-rt/bit.h"
18+
19+
#include <algorithm>
20+
#include <utility>
21+
#include <vector>
22+
23+
namespace orc_rt {
24+
25+
/// Describes Read/Write/Exec permissions for memory.
26+
enum class MemProt : unsigned {
27+
None = 0,
28+
Read = 1U << 0,
29+
Write = 1U << 1,
30+
Exec = 1U << 2,
31+
ORC_RT_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Exec)
32+
};
33+
34+
/// Describes a memory lifetime policy.
35+
enum class MemLifetime : unsigned {
36+
/// Standard memory should be deallocated by the corresponding call to
37+
/// deallocate.
38+
Standard,
39+
40+
/// Finalize memory should be deallocated at the end of the finalization
41+
/// process.
42+
Finalize
43+
};
44+
45+
/// A pair of memory protections and lifetime policy.
46+
class AllocGroup {
47+
private:
48+
static constexpr int NumProtBits = bitmask_enum_num_bits_v<MemProt>;
49+
static constexpr int NumLifetimeBits = 1;
50+
static constexpr int NumBits = NumProtBits + NumLifetimeBits;
51+
52+
typedef uint8_t underlying_type;
53+
54+
static_assert(NumBits <= std::numeric_limits<underlying_type>::digits,
55+
"Not enough bits to hold (prot, lifetime) pair");
56+
57+
constexpr static underlying_type ProtMask = (1U << NumProtBits) - 1;
58+
constexpr static underlying_type LifetimeMask = (1U << NumLifetimeBits) - 1;
59+
60+
public:
61+
static constexpr size_t MaxValues = 1U << NumBits;
62+
63+
AllocGroup() = default;
64+
AllocGroup(MemProt MP, MemLifetime ML = MemLifetime::Standard)
65+
: Id((static_cast<underlying_type>(ML) << NumProtBits) |
66+
static_cast<underlying_type>(MP)) {}
67+
68+
MemProt getMemProt() const { return static_cast<MemProt>(Id & ProtMask); }
69+
70+
MemLifetime getMemLifetime() const {
71+
return static_cast<MemLifetime>((Id >> NumProtBits) & LifetimeMask);
72+
}
73+
74+
friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) {
75+
return LHS.Id == RHS.Id;
76+
}
77+
78+
friend bool operator!=(const AllocGroup &LHS, const AllocGroup &RHS) {
79+
return !(LHS == RHS);
80+
}
81+
82+
friend bool operator<(const AllocGroup &LHS, const AllocGroup &RHS) {
83+
return LHS.Id < RHS.Id;
84+
}
85+
86+
private:
87+
underlying_type Id = 0;
88+
};
89+
90+
/// A specialized small-map for AllocGroups.
91+
///
92+
/// Iteration order is guaranteed to match key ordering.
93+
template <typename T> class AllocGroupSmallMap {
94+
private:
95+
using ElemT = std::pair<AllocGroup, T>;
96+
using VectorTy = std::vector<ElemT>;
97+
98+
static bool compareKey(const ElemT &E, const AllocGroup &G) {
99+
return E.first < G;
100+
}
101+
102+
public:
103+
using iterator = typename VectorTy::iterator;
104+
105+
AllocGroupSmallMap() = default;
106+
AllocGroupSmallMap(std::initializer_list<std::pair<AllocGroup, T>> Inits)
107+
: Elems(Inits) {
108+
std::sort(Elems, [](const ElemT &LHS, const ElemT &RHS) {
109+
return LHS.first < RHS.first;
110+
});
111+
}
112+
113+
iterator begin() { return Elems.begin(); }
114+
iterator end() { return Elems.end(); }
115+
iterator find(AllocGroup G) {
116+
auto I = std::lower_bound(Elems.begin(), Elems.end(), G, compareKey);
117+
return (I == end() || I->first == G) ? I : end();
118+
}
119+
120+
bool empty() const { return Elems.empty(); }
121+
size_t size() const { return Elems.size(); }
122+
123+
T &operator[](AllocGroup G) {
124+
auto I = std::lower_bound(Elems.begin(), Elems.end(), G, compareKey);
125+
if (I == Elems.end() || I->first != G)
126+
I = Elems.insert(I, std::make_pair(G, T()));
127+
return I->second;
128+
}
129+
130+
private:
131+
VectorTy Elems;
132+
};
133+
134+
} // namespace orc_rt
135+
136+
#endif // ORC_RT_MEMORYFLAGS_H

orc-rt/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_orc_rt_unittest(CoreTests
1919
IntervalMapTest.cpp
2020
IntervalSetTest.cpp
2121
MathTest.cpp
22+
MemoryFlagsTest.cpp
2223
RTTITest.cpp
2324
SimplePackedSerializationTest.cpp
2425
WrapperFunctionBufferTest.cpp
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===- MemoryFlags.cpp ----------------------------------------------------===//
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+
// Tests for orc-rt's MemoryFlags.h APIs.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "orc-rt/MemoryFlags.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace orc_rt;
17+
18+
TEST(MemProtTest, Basics) {
19+
MemProt MPNone = MemProt::None;
20+
21+
EXPECT_EQ(MPNone & MemProt::Read, MemProt::None);
22+
EXPECT_EQ(MPNone & MemProt::Write, MemProt::None);
23+
EXPECT_EQ(MPNone & MemProt::Exec, MemProt::None);
24+
25+
EXPECT_EQ(MPNone | MemProt::Read, MemProt::Read);
26+
EXPECT_EQ(MPNone | MemProt::Write, MemProt::Write);
27+
EXPECT_EQ(MPNone | MemProt::Exec, MemProt::Exec);
28+
29+
MemProt MPAll = MemProt::Read | MemProt::Write | MemProt::Exec;
30+
EXPECT_EQ(MPAll & MemProt::Read, MemProt::Read);
31+
EXPECT_EQ(MPAll & MemProt::Write, MemProt::Write);
32+
EXPECT_EQ(MPAll & MemProt::Exec, MemProt::Exec);
33+
}
34+
35+
TEST(AllocGroupTest, Default) {
36+
AllocGroup G;
37+
EXPECT_EQ(G.getMemProt(), MemProt::None);
38+
EXPECT_EQ(G.getMemLifetime(), MemLifetime::Standard);
39+
}
40+
41+
TEST(AllocGroupTest, InitMemProtOnly) {
42+
AllocGroup G(MemProt::Read | MemProt::Write);
43+
EXPECT_EQ(G.getMemProt(), MemProt::Read | MemProt::Write);
44+
EXPECT_EQ(G.getMemLifetime(), MemLifetime::Standard);
45+
}
46+
47+
TEST(AllocGroupTest, InitMemProtAndLifetime) {
48+
AllocGroup G(MemProt::Read | MemProt::Write, MemLifetime::Finalize);
49+
EXPECT_EQ(G.getMemProt(), MemProt::Read | MemProt::Write);
50+
EXPECT_EQ(G.getMemLifetime(), MemLifetime::Finalize);
51+
}
52+
53+
TEST(AllocGroupTest, Equality) {
54+
AllocGroup G(MemProt::Read, MemLifetime::Standard);
55+
EXPECT_EQ(G, AllocGroup(MemProt::Read, MemLifetime::Standard));
56+
EXPECT_NE(G, AllocGroup(MemProt::Write, MemLifetime::Standard));
57+
EXPECT_NE(G, AllocGroup(MemProt::Read, MemLifetime::Finalize));
58+
}
59+
60+
TEST(AllocGroupTest, LessThan) {
61+
// Check that AllocGroups can be compared via less-than so that they can be
62+
// used as keys in standard containers.
63+
EXPECT_LT(AllocGroup(MemProt::Read, MemLifetime::Standard),
64+
AllocGroup(MemProt::Write, MemLifetime::Standard));
65+
EXPECT_LT(AllocGroup(MemProt::Exec, MemLifetime::Standard),
66+
AllocGroup(MemProt::Read, MemLifetime::Finalize));
67+
}
68+
69+
TEST(AllocGroupSmallMap, EmptyMap) {
70+
AllocGroupSmallMap<bool> EM;
71+
EXPECT_TRUE(EM.empty());
72+
EXPECT_EQ(EM.size(), 0u);
73+
}
74+
75+
TEST(AllocGroupSmallMap, NonEmptyMap) {
76+
AllocGroupSmallMap<unsigned> NEM;
77+
NEM[MemProt::Read] = 42;
78+
79+
EXPECT_FALSE(NEM.empty());
80+
EXPECT_EQ(NEM.size(), 1U);
81+
EXPECT_EQ(NEM[MemProt::Read], 42U);
82+
EXPECT_EQ(NEM.find(MemProt::Read), NEM.begin());
83+
EXPECT_EQ(NEM.find(MemProt::Read | MemProt::Write), NEM.end());
84+
85+
NEM[MemProt::Read | MemProt::Write] = 7;
86+
EXPECT_EQ(NEM.size(), 2U);
87+
EXPECT_EQ(NEM.begin()->second, 42U);
88+
EXPECT_EQ((NEM.begin() + 1)->second, 7U);
89+
}

0 commit comments

Comments
 (0)