Skip to content

Commit 553d441

Browse files
authored
Merge pull request #36819 from eeckstein/stacklist
SIL: add a StackList data structure with zero cost operations.
2 parents ae2a4cc + 0456d95 commit 553d441

20 files changed

+404
-137
lines changed

include/swift/SIL/BasicBlockBits.h

Lines changed: 2 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#ifndef SWIFT_SIL_SILBITFIELD_H
18-
#define SWIFT_SIL_SILBITFIELD_H
17+
#ifndef SWIFT_SIL_BASICBLOCKBITS_H
18+
#define SWIFT_SIL_BASICBLOCKBITS_H
1919

2020
#include "swift/SIL/SILFunction.h"
2121
#include "llvm/ADT/SmallVector.h"
@@ -173,101 +173,6 @@ class BasicBlockSet {
173173
void erase(SILBasicBlock *block) { flag.reset(block); }
174174
};
175175

176-
/// An implementation of `llvm::SetVector<SILBasicBlock *,
177-
/// SmallVector<SILBasicBlock *, N>,
178-
/// BasicBlockSet>`.
179-
///
180-
/// Unfortunately it's not possible to use `llvm::SetVector` directly because
181-
/// the BasicBlockSet constructor needs a `SILFunction` argument.
182-
///
183-
/// Note: This class does not provide a `remove` method intentinally, because
184-
/// it would have a O(n) complexity.
185-
template <unsigned N> class BasicBlockSetVector {
186-
using Vector = llvm::SmallVector<SILBasicBlock *, N>;
187-
188-
Vector vector;
189-
BasicBlockSet set;
190-
191-
public:
192-
using iterator = typename Vector::const_iterator;
193-
194-
BasicBlockSetVector(SILFunction *function) : set(function) {}
195-
196-
iterator begin() const { return vector.begin(); }
197-
iterator end() const { return vector.end(); }
198-
199-
unsigned size() const { return vector.size(); }
200-
bool empty() const { return vector.empty(); }
201-
202-
bool contains(SILBasicBlock *block) const { return set.contains(block); }
203-
204-
/// Returns true if \p block was not contained in the set before inserting.
205-
bool insert(SILBasicBlock *block) {
206-
if (set.insert(block)) {
207-
vector.push_back(block);
208-
return true;
209-
}
210-
return false;
211-
}
212-
};
213-
214-
/// A utility for processing basic blocks in a worklist.
215-
///
216-
/// It is basically a combination of a block vector and a block set. It can be
217-
/// used for typical worklist-processing algorithms.
218-
template <unsigned N> class BasicBlockWorklist {
219-
llvm::SmallVector<SILBasicBlock *, N> worklist;
220-
BasicBlockSet visited;
221-
222-
public:
223-
/// Construct an empty worklist.
224-
BasicBlockWorklist(SILFunction *function) : visited(function) {}
225-
226-
/// Initialize the worklist with \p initialBlock.
227-
BasicBlockWorklist(SILBasicBlock *initialBlock)
228-
: visited(initialBlock->getParent()) {
229-
push(initialBlock);
230-
}
231-
232-
/// Pops the last added element from the worklist or returns null, if the
233-
/// worklist is empty.
234-
SILBasicBlock *pop() {
235-
if (worklist.empty())
236-
return nullptr;
237-
return worklist.pop_back_val();
238-
}
239-
240-
/// Pushes \p block onto the worklist if \p block has never been push before.
241-
bool pushIfNotVisited(SILBasicBlock *block) {
242-
if (visited.insert(block)) {
243-
worklist.push_back(block);
244-
return true;
245-
}
246-
return false;
247-
}
248-
249-
/// Like `pushIfNotVisited`, but requires that \p block has never been on the
250-
/// worklist before.
251-
void push(SILBasicBlock *block) {
252-
assert(!visited.contains(block));
253-
visited.insert(block);
254-
worklist.push_back(block);
255-
}
256-
257-
/// Like `pop`, but marks the returned block as "unvisited". This means, that
258-
/// the block can be pushed onto the worklist again.
259-
SILBasicBlock *popAndForget() {
260-
if (worklist.empty())
261-
return nullptr;
262-
SILBasicBlock *block = worklist.pop_back_val();
263-
visited.erase(block);
264-
return block;
265-
}
266-
267-
/// Returns true if \p block was visited, i.e. has been added to the worklist.
268-
bool isVisited(SILBasicBlock *block) const { return visited.contains(block); }
269-
};
270-
271176
} // namespace swift
272177

273178
#endif
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===--- BasicBlockDatastructures.h -----------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines efficient data structures for working with BasicBlocks.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SIL_BASICBLOCKDATASTRUCTURES_H
18+
#define SWIFT_SIL_BASICBLOCKDATASTRUCTURES_H
19+
20+
#include "swift/SIL/StackList.h"
21+
#include "swift/SIL/BasicBlockBits.h"
22+
23+
namespace swift {
24+
25+
/// An implementation of `llvm::SetVector<SILBasicBlock *,
26+
/// StackList<SILBasicBlock *>,
27+
/// BasicBlockSet>`.
28+
///
29+
/// Unfortunately it's not possible to use `llvm::SetVector` directly because
30+
/// the BasicBlockSet and StackList constructors needs a `SILFunction` argument.
31+
///
32+
/// Note: This class does not provide a `remove` method intentinally, because
33+
/// it would have a O(n) complexity.
34+
class BasicBlockSetVector {
35+
StackList<SILBasicBlock *> vector;
36+
BasicBlockSet set;
37+
38+
public:
39+
using iterator = typename StackList<SILBasicBlock *>::iterator;
40+
41+
BasicBlockSetVector(SILFunction *function) : vector(function), set(function) {}
42+
43+
iterator begin() const { return vector.begin(); }
44+
iterator end() const { return vector.end(); }
45+
46+
bool empty() const { return vector.empty(); }
47+
48+
bool contains(SILBasicBlock *block) const { return set.contains(block); }
49+
50+
/// Returns true if \p block was not contained in the set before inserting.
51+
bool insert(SILBasicBlock *block) {
52+
if (set.insert(block)) {
53+
vector.push_back(block);
54+
return true;
55+
}
56+
return false;
57+
}
58+
};
59+
60+
/// A utility for processing basic blocks in a worklist.
61+
///
62+
/// It is basically a combination of a block vector and a block set. It can be
63+
/// used for typical worklist-processing algorithms.
64+
class BasicBlockWorklist {
65+
StackList<SILBasicBlock *> worklist;
66+
BasicBlockSet visited;
67+
68+
public:
69+
/// Construct an empty worklist.
70+
BasicBlockWorklist(SILFunction *function)
71+
: worklist(function), visited(function) {}
72+
73+
/// Initialize the worklist with \p initialBlock.
74+
BasicBlockWorklist(SILBasicBlock *initialBlock)
75+
: BasicBlockWorklist(initialBlock->getParent()) {
76+
push(initialBlock);
77+
}
78+
79+
/// Pops the last added element from the worklist or returns null, if the
80+
/// worklist is empty.
81+
SILBasicBlock *pop() {
82+
if (worklist.empty())
83+
return nullptr;
84+
return worklist.pop_back_val();
85+
}
86+
87+
/// Pushes \p block onto the worklist if \p block has never been push before.
88+
bool pushIfNotVisited(SILBasicBlock *block) {
89+
if (visited.insert(block)) {
90+
worklist.push_back(block);
91+
return true;
92+
}
93+
return false;
94+
}
95+
96+
/// Like `pushIfNotVisited`, but requires that \p block has never been on the
97+
/// worklist before.
98+
void push(SILBasicBlock *block) {
99+
assert(!visited.contains(block));
100+
visited.insert(block);
101+
worklist.push_back(block);
102+
}
103+
104+
/// Like `pop`, but marks the returned block as "unvisited". This means, that
105+
/// the block can be pushed onto the worklist again.
106+
SILBasicBlock *popAndForget() {
107+
if (worklist.empty())
108+
return nullptr;
109+
SILBasicBlock *block = worklist.pop_back_val();
110+
visited.erase(block);
111+
return block;
112+
}
113+
114+
/// Returns true if \p block was visited, i.e. has been added to the worklist.
115+
bool isVisited(SILBasicBlock *block) const { return visited.contains(block); }
116+
};
117+
118+
} // namespace swift
119+
120+
#endif

include/swift/SIL/SILModule.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,52 @@
4949
#include "llvm/Support/raw_ostream.h"
5050
#include <functional>
5151

52+
namespace swift {
53+
54+
/// A fixed size slab of memory, which can be allocated and freed by the
55+
/// SILModule at (basically) zero cost.
56+
class FixedSizeSlab : public llvm::ilist_node<FixedSizeSlab>,
57+
public SILAllocated<FixedSizeSlab> {
58+
public:
59+
/// The capacity of the payload.
60+
static constexpr size_t capacity = 64 * sizeof(uintptr_t);
61+
62+
private:
63+
friend class SILModule;
64+
65+
/// The magic number which is stored in overflowGuard.
66+
static constexpr uintptr_t magicNumber = (uintptr_t)0xdeadbeafdeadbeafull;
67+
68+
/// The payload.
69+
char data[capacity];
70+
71+
/// Used for a cheap buffer overflow check - in the spirit of libgmalloc.
72+
uintptr_t overflowGuard = magicNumber;
73+
74+
public:
75+
/// Returns the payload pointing to \p T.
76+
template<typename T> T *dataFor() { return (T *)(&data[0]); }
77+
78+
/// Returns the payload pointing to const \p T
79+
template<typename T> const T *dataFor() const { return (const T *)(&data[0]); }
80+
};
81+
82+
} // end swift namespace
83+
5284
namespace llvm {
5385
namespace yaml {
5486
class Output;
5587
} // end namespace yaml
88+
89+
template <>
90+
struct ilist_traits<::swift::FixedSizeSlab> :
91+
public ilist_node_traits<::swift::FixedSizeSlab> {
92+
public:
93+
static void deleteNode(::swift::FixedSizeSlab *V) {
94+
llvm_unreachable("cannot delete a slab");
95+
}
96+
};
97+
5698
} // end namespace llvm
5799

58100
namespace swift {
@@ -129,6 +171,7 @@ class SILModule {
129171
};
130172

131173
using ActionCallback = std::function<void()>;
174+
using SlabList = llvm::ilist<FixedSizeSlab>;
132175

133176
private:
134177
friend KeyPathPattern;
@@ -151,6 +194,12 @@ class SILModule {
151194
/// Allocator that manages the memory of all the pieces of the SILModule.
152195
mutable llvm::BumpPtrAllocator BPA;
153196

197+
/// The list of freed slabs, which can be reused.
198+
SlabList freeSlabs;
199+
200+
/// For consistency checking.
201+
size_t numAllocatedSlabs = 0;
202+
154203
/// The swift Module associated with this SILModule.
155204
ModuleDecl *TheSwiftModule;
156205

@@ -733,6 +782,22 @@ class SILModule {
733782
return static_cast<T *>(allocate(sizeof(T) * Count, alignof(T)));
734783
}
735784

785+
/// Allocates a slab of memory.
786+
///
787+
/// This has (almost) zero cost, because for the first time, the allocation is
788+
/// done with the BPA.
789+
/// Subsequent allocations are reusing the already freed slabs.
790+
FixedSizeSlab *allocSlab();
791+
792+
/// Frees a slab.
793+
///
794+
/// This has (almost) zero cost, because the slab is just put into the
795+
/// freeSlabs list.
796+
void freeSlab(FixedSizeSlab *slab);
797+
798+
/// Frees all slabs of a list.
799+
void freeAllSlabs(SlabList &slabs);
800+
736801
template <typename T>
737802
MutableArrayRef<T> allocateCopy(ArrayRef<T> Array) const {
738803
MutableArrayRef<T> result(allocate<T>(Array.size()), Array.size());

0 commit comments

Comments
 (0)