Skip to content

Commit d523eba

Browse files
authored
Merge pull request #31378 from gottesmm/pr-35eea56e6569c03ea51fcc7badd585b257dea90b
[frozen-multimap] Add support for erasing once we have finished constructing the map.
2 parents b10c1e3 + 9568d53 commit d523eba

File tree

2 files changed

+212
-38
lines changed

2 files changed

+212
-38
lines changed

include/swift/Basic/FrozenMultiMap.h

Lines changed: 114 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,24 @@
1212
///
1313
/// \file
1414
///
15-
/// A 2 stage multi-map. Initially the multimap is mutable and can only be
16-
/// initialized. Once complete, the map is frozen and can be only used for map
17-
/// operations. It is guaranteed that all values are still in insertion order.
15+
/// A 2 stage multi-map for use in contexts where one is iterating over an IR in
16+
/// a read only way and then wants to use a multi-map for post-processing that
17+
/// can support iteration in insertion order.
1818
///
19-
/// DISCUSSION: These restrictions flow from the internal implementation of the
20-
/// multi-map being a pair of keys, values. We form the map property by
21-
/// performing a stable_sort of the (key, value) in the process of freezing the
22-
/// map.
19+
/// In the first stage, the multi-map can be initialized with new elements in a
20+
/// purely additive fashion, but methods that rely on the map being frozen
21+
/// (find, getRange(), erase, etc) assert.
22+
///
23+
/// Once the user has finished iterating over the IR, the map is frozen and then
24+
/// can only be used for map operations (find), erasing operations (erase), and
25+
/// deterministic range iteration.
26+
///
27+
/// This is accomplished by the internal implementation of the frozen multi map
28+
/// just being an array of (key, value) pairs that when we freeze, we sort using
29+
/// a stable sort on the key. Since we use a stable sort on the key, we know
30+
/// that all of the individual multimap value arrays are still in insertion
31+
/// order helping us avoid non-determinism like one must deal with when using
32+
/// other maps.
2333
///
2434
//===----------------------------------------------------------------------===//
2535

@@ -34,17 +44,19 @@
3444
namespace swift {
3545

3646
template <typename Key, typename Value,
37-
typename VectorStorage = std::vector<std::pair<Key, Value>>>
47+
typename VectorStorage = std::vector<std::pair<Key, Optional<Value>>>>
3848
class FrozenMultiMap {
3949
VectorStorage storage;
4050
bool frozen = false;
4151

4252
private:
4353
struct PairToSecondElt;
54+
struct PairWithTypeErasedOptionalSecondElt;
4455

4556
public:
4657
using PairToSecondEltRange =
47-
TransformRange<ArrayRef<std::pair<Key, Value>>, PairToSecondElt>;
58+
TransformRange<ArrayRef<std::pair<Key, Optional<Value>>>,
59+
PairToSecondElt>;
4860

4961
FrozenMultiMap() = default;
5062

@@ -60,30 +72,61 @@ class FrozenMultiMap {
6072
// inst as the first element.
6173
auto start = std::lower_bound(
6274
storage.begin(), storage.end(), std::make_pair(key, Value()),
63-
[&](const std::pair<Key, Value> &p1, const std::pair<Key, Value> &p2) {
75+
[&](const std::pair<Key, Optional<Value>> &p1,
76+
const std::pair<Key, Optional<Value>> &p2) {
6477
return p1.first < p2.first;
6578
});
66-
if (start == storage.end() || start->first != key) {
79+
80+
// If we did not find that first element or that key has a .none value
81+
// (signaling that we erased it), return None.
82+
if (start == storage.end() || start->first != key ||
83+
!start->second.hasValue()) {
6784
return None;
6885
}
6986

7087
// Ok, we found our first element. Now scan forward until we find a pair
7188
// whose instruction is not our own instruction.
72-
auto end = find_if_not(
73-
start, storage.end(),
74-
[&](const std::pair<Key, Value> &pair) { return pair.first == key; });
89+
auto end = find_if_not(start, storage.end(),
90+
[&](const std::pair<Key, Optional<Value>> &pair) {
91+
return pair.first == key;
92+
});
7593
unsigned count = std::distance(start, end);
76-
ArrayRef<std::pair<Key, Value>> slice(&*start, count);
94+
ArrayRef<std::pair<Key, Optional<Value>>> slice(&*start, count);
7795
return PairToSecondEltRange(slice, PairToSecondElt());
7896
}
7997

98+
bool erase(const Key &key) {
99+
assert(isFrozen() &&
100+
"Can not perform an erase operation until the map is frozen");
101+
// Since our array is sorted, we need to first find the first pair with our
102+
// inst as the first element.
103+
auto start = std::lower_bound(
104+
storage.begin(), storage.end(), std::make_pair(key, Value()),
105+
[&](const std::pair<Key, Optional<Value>> &p1,
106+
const std::pair<Key, Optional<Value>> &p2) {
107+
return p1.first < p2.first;
108+
});
109+
110+
// If we did not find that first element or that key has a .none value
111+
// (signaling that we erased it), return false.
112+
if (start == storage.end() || start->first != key ||
113+
!start->second.hasValue()) {
114+
return false;
115+
}
116+
117+
// Ok, we found our element. Just set its value to .none to signal it was
118+
// destroyed and then return true.
119+
start->second = None;
120+
return true;
121+
}
122+
80123
bool isFrozen() const { return frozen; }
81124

82125
/// Set this map into its frozen state when we
83126
void setFrozen() {
84127
std::stable_sort(storage.begin(), storage.end(),
85-
[&](const std::pair<Key, Value> &lhs,
86-
const std::pair<Key, Value> &rhs) {
128+
[&](const std::pair<Key, Optional<Value>> &lhs,
129+
const std::pair<Key, Optional<Value>> &rhs) {
87130
// Only compare the first entry so that we preserve
88131
// insertion order.
89132
return lhs.first < rhs.first;
@@ -100,13 +143,14 @@ class FrozenMultiMap {
100143
unsigned size() const { return storage.size(); }
101144
bool empty() const { return storage.empty(); }
102145

103-
struct iterator : std::iterator<std::forward_iterator_tag,
104-
std::pair<Key, PairToSecondEltRange>> {
146+
struct iterator
147+
: std::iterator<std::forward_iterator_tag,
148+
std::pair<Key, Optional<PairToSecondEltRange>>> {
105149
using base_iterator = typename decltype(storage)::iterator;
106150

107151
FrozenMultiMap &map;
108152
base_iterator baseIter;
109-
Optional<std::pair<Key, PairToSecondEltRange>> currentValue;
153+
Optional<std::pair<Key, Optional<PairToSecondEltRange>>> currentValue;
110154

111155
iterator(FrozenMultiMap &map, base_iterator iter)
112156
: map(map), baseIter(iter), currentValue() {
@@ -123,21 +167,27 @@ class FrozenMultiMap {
123167
}
124168

125169
// Otherwise, determine the next range that we are visiting.
126-
auto rangeEnd = std::find_if_not(std::next(baseIter), end,
127-
[&](const std::pair<Key, Value> &elt) {
128-
return elt.first == baseIter->first;
129-
});
130-
unsigned count = std::distance(baseIter, rangeEnd);
131-
ArrayRef<std::pair<Key, Value>> slice(&*baseIter, count);
132-
currentValue = {baseIter->first,
133-
PairToSecondEltRange(slice, PairToSecondElt())};
170+
auto rangeEnd =
171+
std::find_if_not(std::next(baseIter), end,
172+
[&](const std::pair<Key, Optional<Value>> &elt) {
173+
return elt.first == baseIter->first;
174+
});
175+
176+
Optional<PairToSecondEltRange> resultRange;
177+
if (baseIter->second.hasValue()) {
178+
unsigned count = std::distance(baseIter, rangeEnd);
179+
ArrayRef<std::pair<Key, Optional<Value>>> slice(&*baseIter, count);
180+
resultRange.emplace(slice, PairToSecondElt());
181+
}
182+
currentValue = {baseIter->first, resultRange};
134183
}
135184

136185
iterator &operator++() {
137-
baseIter = std::find_if_not(std::next(baseIter), map.storage.end(),
138-
[&](const std::pair<Key, Value> &elt) {
139-
return elt.first == baseIter->first;
140-
});
186+
baseIter =
187+
std::find_if_not(std::next(baseIter), map.storage.end(),
188+
[&](const std::pair<Key, Optional<Value>> &elt) {
189+
return elt.first == baseIter->first;
190+
});
141191
updateCurrentValue();
142192
return *this;
143193
}
@@ -152,7 +202,7 @@ class FrozenMultiMap {
152202
return tmp;
153203
}
154204

155-
std::pair<Key, PairToSecondEltRange> operator*() const {
205+
std::pair<Key, Optional<PairToSecondEltRange>> operator*() const {
156206
return *currentValue;
157207
}
158208

@@ -165,7 +215,19 @@ class FrozenMultiMap {
165215
}
166216
};
167217

168-
using RangeType = llvm::iterator_range<iterator>;
218+
struct ToNonErasedValues {
219+
Optional<std::pair<Key, Optional<PairToSecondEltRange>>>
220+
operator()(std::pair<Key, Optional<PairToSecondEltRange>> pair) const {
221+
if (!pair.second.hasValue())
222+
return None;
223+
return pair;
224+
}
225+
};
226+
227+
using IgnoringErasedValueRangeType =
228+
OptionalTransformRange<llvm::iterator_range<iterator>, ToNonErasedValues>;
229+
using RangeType = TransformRange<IgnoringErasedValueRangeType,
230+
PairWithTypeErasedOptionalSecondElt>;
169231

170232
/// Return a range of (key, ArrayRef<Value>) pairs. The keys are guaranteed to
171233
/// be in key sorted order and the ArrayRef<Value> are in insertion order.
@@ -175,22 +237,36 @@ class FrozenMultiMap {
175237
auto *self = const_cast<FrozenMultiMap *>(this);
176238
iterator iter1 = iterator(*self, self->storage.begin());
177239
iterator iter2 = iterator(*self, self->storage.end());
178-
return llvm::make_range(iter1, iter2);
240+
auto baseRange = llvm::make_range(iter1, iter2);
241+
auto optRange = makeOptionalTransformRange(baseRange, ToNonErasedValues());
242+
return makeTransformRange(optRange, PairWithTypeErasedOptionalSecondElt());
179243
}
180244
};
181245

182246
template <typename Key, typename Value, typename Storage>
183247
struct FrozenMultiMap<Key, Value, Storage>::PairToSecondElt {
184248
PairToSecondElt() {}
185249

186-
Value operator()(const std::pair<Key, Value> &pair) const {
187-
return pair.second;
250+
Value operator()(const std::pair<Key, Optional<Value>> &pair) const {
251+
return *pair.second;
252+
}
253+
};
254+
255+
template <typename Key, typename Value, typename Storage>
256+
struct FrozenMultiMap<Key, Value,
257+
Storage>::PairWithTypeErasedOptionalSecondElt {
258+
PairWithTypeErasedOptionalSecondElt() {}
259+
260+
std::pair<Key, PairToSecondEltRange>
261+
operator()(const std::pair<Key, Optional<PairToSecondEltRange>> &pair) const {
262+
return std::make_pair(pair.first, *pair.second);
188263
}
189264
};
190265

191266
template <typename Key, typename Value, unsigned SmallSize>
192267
using SmallFrozenMultiMap =
193-
FrozenMultiMap<Key, Value, SmallVector<std::pair<Key, Value>, SmallSize>>;
268+
FrozenMultiMap<Key, Value,
269+
SmallVector<std::pair<Key, Optional<Value>>, SmallSize>>;
194270

195271
} // namespace swift
196272

unittests/Basic/FrozenMultiMapTest.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,101 @@ TEST(FrozenMultiMapCustomTest, RandomAgainstStdMultiMap) {
223223
}
224224
}
225225
}
226+
227+
TEST(FrozenMultiMapCustomTest, SimpleErase1) {
228+
Canary::resetIDs();
229+
FrozenMultiMap<Canary, Canary> map;
230+
231+
auto key1 = Canary();
232+
auto key2 = Canary();
233+
map.insert(key1, Canary());
234+
map.insert(key1, Canary());
235+
map.insert(key1, Canary());
236+
map.insert(key2, Canary());
237+
map.insert(key2, Canary());
238+
239+
map.setFrozen();
240+
241+
EXPECT_EQ(map.size(), 5u);
242+
243+
EXPECT_TRUE(map.erase(key2));
244+
EXPECT_FALSE(map.erase(key2));
245+
246+
{
247+
auto range = map.getRange();
248+
EXPECT_EQ(std::distance(range.begin(), range.end()), 1);
249+
250+
{
251+
auto begin = range.begin();
252+
auto end = range.end();
253+
++begin;
254+
EXPECT_EQ(std::distance(begin, end), 0);
255+
}
256+
257+
auto iter = range.begin();
258+
{
259+
auto p = *iter;
260+
EXPECT_EQ(p.first.getID(), key1.getID());
261+
EXPECT_EQ(p.second.size(), 3u);
262+
EXPECT_EQ(p.second[0].getID(), 2u);
263+
EXPECT_EQ(p.second[1].getID(), 3u);
264+
EXPECT_EQ(p.second[2].getID(), 4u);
265+
}
266+
}
267+
268+
EXPECT_TRUE(map.erase(key1));
269+
EXPECT_FALSE(map.erase(key1));
270+
271+
{
272+
auto range = map.getRange();
273+
EXPECT_EQ(std::distance(range.begin(), range.end()), 0);
274+
}
275+
}
276+
277+
TEST(FrozenMultiMapCustomTest, SimpleErase2) {
278+
Canary::resetIDs();
279+
FrozenMultiMap<Canary, Canary> map;
280+
281+
auto key1 = Canary();
282+
auto key2 = Canary();
283+
map.insert(key1, Canary());
284+
map.insert(key1, Canary());
285+
map.insert(key1, Canary());
286+
map.insert(key2, Canary());
287+
map.insert(key2, Canary());
288+
289+
map.setFrozen();
290+
291+
EXPECT_EQ(map.size(), 5u);
292+
293+
EXPECT_TRUE(map.erase(key1));
294+
295+
{
296+
auto range = map.getRange();
297+
EXPECT_EQ(std::distance(range.begin(), range.end()), 1);
298+
299+
{
300+
auto begin = range.begin();
301+
auto end = range.end();
302+
++begin;
303+
EXPECT_EQ(std::distance(begin, end), 0);
304+
}
305+
306+
auto iter = range.begin();
307+
{
308+
auto p = *iter;
309+
EXPECT_EQ(p.first.getID(), key2.getID());
310+
EXPECT_EQ(p.second.size(), 2u);
311+
EXPECT_EQ(p.second[0].getID(), 5u);
312+
EXPECT_EQ(p.second[1].getID(), 6u);
313+
}
314+
}
315+
316+
EXPECT_TRUE(map.erase(key2));
317+
EXPECT_FALSE(map.erase(key2));
318+
319+
{
320+
auto range = map.getRange();
321+
EXPECT_EQ(std::distance(range.begin(), range.end()), 0);
322+
}
323+
}

0 commit comments

Comments
 (0)