Skip to content

Commit e4552c6

Browse files
authored
[ORC-RT] Add IntervalMap and IntervalSet collections. (llvm#155073)
IntervalMap is an optionally-coalescing map: it uses half-open ranges as keys, allows lookups based on elements of the ranges (returning an iterator to the containing range) and optionally coalesces adjacent ranges that have the same value. IntervalSet is an optionally-coalescing set based on IntervalMap. These collections will be used to store address-range information in the ORC runtime.
1 parent da99853 commit e4552c6

File tree

6 files changed

+587
-0
lines changed

6 files changed

+587
-0
lines changed

orc-rt/include/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ set(ORC_RT_HEADERS
66
orc-rt/Compiler.h
77
orc-rt/Error.h
88
orc-rt/ExecutorAddr.h
9+
orc-rt/IntervalMap.h
10+
orc-rt/IntervalSet.h
911
orc-rt/Math.h
1012
orc-rt/RTTI.h
1113
orc-rt/WrapperFunctionResult.h
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//===---------- IntervalMap.h - A sorted interval map -----------*- 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+
// Implements a coalescing interval map.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef ORC_RT_INTERVALMAP_H
14+
#define ORC_RT_INTERVALMAP_H
15+
16+
#include <cassert>
17+
#include <map>
18+
19+
namespace orc_rt {
20+
21+
enum class IntervalCoalescing { Enabled, Disabled };
22+
23+
/// Maps intervals to keys with optional coalescing.
24+
///
25+
/// NOTE: The interface is kept mostly compatible with LLVM's IntervalMap
26+
/// collection to make it easy to swap over in the future if we choose
27+
/// to.
28+
template <typename KeyT, typename ValT> class IntervalMapBase {
29+
private:
30+
using KeyPairT = std::pair<KeyT, KeyT>;
31+
32+
struct Compare {
33+
using is_transparent = std::true_type;
34+
bool operator()(const KeyPairT &LHS, const KeyPairT &RHS) const {
35+
return LHS < RHS;
36+
}
37+
bool operator()(const KeyPairT &LHS, const KeyT &RHS) const {
38+
return LHS.first < RHS;
39+
}
40+
bool operator()(const KeyT &LHS, const KeyPairT &RHS) const {
41+
return LHS < RHS.first;
42+
}
43+
};
44+
45+
using ImplMap = std::map<KeyPairT, ValT, Compare>;
46+
47+
public:
48+
using iterator = typename ImplMap::iterator;
49+
using const_iterator = typename ImplMap::const_iterator;
50+
using size_type = typename ImplMap::size_type;
51+
52+
bool empty() const { return Impl.empty(); }
53+
54+
void clear() { Impl.clear(); }
55+
56+
iterator begin() { return Impl.begin(); }
57+
iterator end() { return Impl.end(); }
58+
59+
const_iterator begin() const { return Impl.begin(); }
60+
const_iterator end() const { return Impl.end(); }
61+
62+
iterator find(KeyT K) {
63+
// Early out if the key is clearly outside the range.
64+
if (empty() || K < begin()->first.first ||
65+
K >= std::prev(end())->first.second)
66+
return end();
67+
68+
auto I = Impl.upper_bound(K);
69+
assert(I != begin() && "Should have hit early out above");
70+
I = std::prev(I);
71+
if (K < I->first.second)
72+
return I;
73+
return end();
74+
}
75+
76+
const_iterator find(KeyT K) const {
77+
return const_cast<IntervalMapBase<KeyT, ValT> *>(this)->find(K);
78+
}
79+
80+
ValT lookup(KeyT K, ValT NotFound = ValT()) const {
81+
auto I = find(K);
82+
if (I == end())
83+
return NotFound;
84+
return I->second;
85+
}
86+
87+
// Erase [KS, KE), which must be entirely containing within one existing
88+
// range in the map. Removal is allowed to split the range.
89+
void erase(KeyT KS, KeyT KE) {
90+
if (empty())
91+
return;
92+
93+
auto J = Impl.upper_bound(KS);
94+
95+
// Check previous range. Bail out if range to remove is entirely after
96+
// it.
97+
auto I = std::prev(J);
98+
if (KS >= I->first.second)
99+
return;
100+
101+
// Assert that range is wholly contained.
102+
assert(KE <= I->first.second);
103+
104+
auto Tmp = std::move(*I);
105+
Impl.erase(I);
106+
107+
// Split-right -- introduce right-split range.
108+
if (KE < Tmp.first.second) {
109+
Impl.insert(
110+
J, std::make_pair(std::make_pair(KE, Tmp.first.second), Tmp.second));
111+
J = std::prev(J);
112+
}
113+
114+
// Split-left -- introduce left-split range.
115+
if (KS > Tmp.first.first)
116+
Impl.insert(
117+
J, std::make_pair(std::make_pair(Tmp.first.first, KS), Tmp.second));
118+
}
119+
120+
protected:
121+
ImplMap Impl;
122+
};
123+
124+
template <typename KeyT, typename ValT, IntervalCoalescing Coalescing>
125+
class IntervalMap;
126+
127+
template <typename KeyT, typename ValT>
128+
class IntervalMap<KeyT, ValT, IntervalCoalescing::Enabled>
129+
: public IntervalMapBase<KeyT, ValT> {
130+
public:
131+
// Coalescing insert. Requires that ValTs be equality-comparable.
132+
void insert(KeyT KS, KeyT KE, ValT V) {
133+
auto J = this->Impl.upper_bound(KS);
134+
135+
// Coalesce-right if possible. Either way, J points at our insertion
136+
// point.
137+
if (J != this->end() && KE == J->first.first && J->second == V) {
138+
KE = J->first.second;
139+
auto Tmp = J++;
140+
this->Impl.erase(Tmp);
141+
}
142+
143+
// Coalesce-left if possible.
144+
if (J != this->begin()) {
145+
auto I = std::prev(J);
146+
if (I->first.second == KS && I->second == V) {
147+
KS = I->first.first;
148+
this->Impl.erase(I);
149+
}
150+
}
151+
this->Impl.insert(J, std::make_pair(std::make_pair(KS, KE), std::move(V)));
152+
}
153+
};
154+
155+
template <typename KeyT, typename ValT>
156+
class IntervalMap<KeyT, ValT, IntervalCoalescing::Disabled>
157+
: public IntervalMapBase<KeyT, ValT> {
158+
public:
159+
// Non-coalescing insert. Does not require ValT to be equality-comparable.
160+
void insert(KeyT KS, KeyT KE, ValT V) {
161+
this->Impl.insert(std::make_pair(std::make_pair(KS, KE), std::move(V)));
162+
}
163+
};
164+
165+
} // End namespace orc_rt
166+
167+
#endif // ORC_RT_INTERVALMAP_H
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===---------- IntervalSet.h - A sorted interval set -----------*- 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+
// Implements a coalescing interval set.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef ORC_RT_INTERVALSET_H
14+
#define ORC_RT_INTERVALSET_H
15+
16+
#include "IntervalMap.h"
17+
18+
namespace orc_rt {
19+
20+
/// Implements a coalescing interval set.
21+
///
22+
/// Adjacent intervals are coalesced.
23+
///
24+
/// NOTE: The interface is kept mostly compatible with LLVM's IntervalMap
25+
/// collection to make it easy to swap over in the future if we choose
26+
/// to.
27+
template <typename KeyT, IntervalCoalescing Coalescing> class IntervalSet {
28+
private:
29+
using ImplMap = IntervalMap<KeyT, std::monostate, Coalescing>;
30+
31+
public:
32+
using value_type = std::pair<KeyT, KeyT>;
33+
34+
class const_iterator {
35+
friend class IntervalSet;
36+
37+
public:
38+
using difference_type = typename ImplMap::iterator::difference_type;
39+
using value_type = IntervalSet::value_type;
40+
using pointer = const value_type *;
41+
using reference = const value_type &;
42+
using iterator_category = std::input_iterator_tag;
43+
44+
const_iterator() = default;
45+
const value_type &operator*() const { return I->first; }
46+
const value_type *operator->() const { return &I->first; }
47+
const_iterator &operator++() {
48+
++I;
49+
return *this;
50+
}
51+
const_iterator operator++(int) {
52+
auto Tmp = I;
53+
++I;
54+
return Tmp;
55+
}
56+
friend bool operator==(const const_iterator &LHS,
57+
const const_iterator &RHS) {
58+
return LHS.I == RHS.I;
59+
}
60+
friend bool operator!=(const const_iterator &LHS,
61+
const const_iterator &RHS) {
62+
return LHS.I != RHS.I;
63+
}
64+
65+
private:
66+
const_iterator(typename ImplMap::const_iterator I) : I(std::move(I)) {}
67+
typename ImplMap::const_iterator I;
68+
};
69+
70+
bool empty() const { return Map.empty(); }
71+
72+
void clear() { Map.clear(); }
73+
74+
const_iterator begin() const { return const_iterator(Map.begin()); }
75+
const_iterator end() const { return const_iterator(Map.end()); }
76+
77+
const_iterator find(KeyT K) const { return const_iterator(Map.find(K)); }
78+
79+
void insert(KeyT KS, KeyT KE) {
80+
Map.insert(std::move(KS), std::move(KE), std::monostate());
81+
}
82+
83+
void erase(KeyT KS, KeyT KE) { Map.erase(KS, KE); }
84+
85+
private:
86+
ImplMap Map;
87+
};
88+
89+
} // End namespace orc_rt
90+
91+
#endif // ORC_RT_INTERVALSET_H

orc-rt/unittests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ add_orc_rt_unittest(CoreTests
1515
BitmaskEnumTest.cpp
1616
ErrorTest.cpp
1717
ExecutorAddressTest.cpp
18+
IntervalMapTest.cpp
19+
IntervalSetTest.cpp
1820
MathTest.cpp
1921
RTTITest.cpp
2022
WrapperFunctionResultTest.cpp

0 commit comments

Comments
 (0)