Skip to content

Commit 273bd35

Browse files
committed
SIL: add a utility which let's manage per-block data efficiently.
It can be used by transforms to store temporary data per basic block. It is very efficient: only a single memory allocation is needed and no maps are used to lookup data.
1 parent 6c41322 commit 273bd35

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

include/swift/SIL/BasicBlockData.h

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
//===--- BasicBlockData.h - Defines the BasicBlockData utility --*- 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 the BasicBlockData utility
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SIL_BASICBLOCKDATA_H
18+
#define SWIFT_SIL_BASICBLOCKDATA_H
19+
20+
#include "swift/SIL/SILFunction.h"
21+
#include "llvm/ADT/SmallVector.h"
22+
#include "llvm/ADT/STLExtras.h"
23+
24+
namespace swift {
25+
26+
/// Manage additional data for a function's basic blocks.
27+
///
28+
/// This can be used by transforms to store temporary data per basic block.
29+
/// It is very efficient: only a single memory allocation is needed and no maps
30+
/// are used to lookup data.
31+
///
32+
/// It is still permitted to use a BasicBlockData after the block list of a
33+
/// function has changed. With two exceptions:
34+
/// * If new blocks are added during the lifetime of a BasicBlockData, no data
35+
/// can be queried for a new block.
36+
/// * If a new BasicBlockData is created after the block list changed, then any
37+
/// older BasicBlockData (which was created before the change) cannot be
38+
/// used anymore.
39+
///
40+
/// This means, it's totally fine to store a BasicBlockData in an Analysis, as
41+
/// long as the invalidation mechanism ensures that the analysis data is not
42+
/// retrieved after the function's block list changed.
43+
///
44+
/// The Vector template argument specifies how the data is stored. By default
45+
/// it's a SmallVector with an inline-size of 4. This avoids memory allocation
46+
/// for about 70% of all functions.
47+
template <typename Data, typename Vector = llvm::SmallVector<Data, 4>>
48+
class BasicBlockData {
49+
SILFunction *function;
50+
Vector data;
51+
52+
/// The data is valid if validForBlockOrder matches the function's
53+
/// BlockListChangeIdx.
54+
unsigned validForBlockOrder = 0;
55+
56+
int getIndex(SILBasicBlock *block) const {
57+
assert(block->index >= 0 && "Cannot get data for a new block");
58+
assert(validForBlockOrder == function->BlockListChangeIdx &&
59+
"BasicBlockData invalid because the function's block list changed");
60+
return block->index;
61+
}
62+
63+
public:
64+
65+
/// For iterating over the function's blocks, yielding the block and its data.
66+
template <typename BlockIterTy, bool IsConst> class IteratorTy {
67+
using DataTy = typename std::conditional<IsConst, const Data, Data>::type;
68+
using BasicBlockDataTy = typename std::conditional<IsConst,
69+
const BasicBlockData<Data>, BasicBlockData<Data>>::type;
70+
71+
/// The underlying SILBasicBlock iterator.
72+
BlockIterTy blockIter;
73+
74+
/// Reference to the constructing BasicBlockData. Used to retrieve the data.
75+
BasicBlockDataTy &blockData;
76+
77+
friend class BasicBlockData<Data>;
78+
79+
IteratorTy(BlockIterTy blockIter, BasicBlockDataTy &blockData) :
80+
blockIter(blockIter), blockData(blockData) {}
81+
82+
public:
83+
/// Result of re-referencing the iterator.
84+
struct BlockAndData {
85+
SILBasicBlock &block;
86+
DataTy &data;
87+
88+
BlockAndData(SILBasicBlock &block, DataTy &data) :
89+
block(block), data(data) {}
90+
};
91+
92+
IteratorTy & operator++() { // Preincrement
93+
blockIter++; return *this;
94+
}
95+
IteratorTy & operator++(int) { // Postincrement
96+
IteratorTy tmp = *this; ++*this; return tmp;
97+
}
98+
99+
SILBasicBlock &block() const { return *blockIter; }
100+
DataTy &data() const { return blockData[&block()]; }
101+
102+
BlockAndData operator*() const { return BlockAndData(block(), data()); }
103+
104+
bool operator==(const IteratorTy &rhs) const {
105+
return blockIter == rhs.blockIter;
106+
}
107+
bool operator!=(const IteratorTy &rhs) const {
108+
return blockIter != rhs.blockIter;
109+
}
110+
};
111+
112+
using iterator = IteratorTy<SILFunction::iterator, false>;
113+
using const_iterator = IteratorTy<SILFunction::iterator, true>;
114+
using reverse_iterator = IteratorTy<SILFunction::reverse_iterator, false>;
115+
using const_reverse_iterator = IteratorTy<SILFunction::reverse_iterator, true>;
116+
117+
static Data constructDefaultData(SILBasicBlock *) { return Data(); }
118+
119+
/// Initialize Data for all basic blocks.
120+
///
121+
/// The \p init function must return the initial data for a block. If not
122+
/// provided, all data is constructed with the Data() default constructor.
123+
BasicBlockData(SILFunction *function,
124+
llvm::function_ref<Data(SILBasicBlock *block)> init =
125+
// Would be nice to simply write constructDefaultData in
126+
// closure syntax, but I didn't get it compile under Windows.
127+
constructDefaultData) :
128+
function(function) {
129+
// Reserve enough space. Though SILFunction::size() iterates over all
130+
// blocks it is still better than to risk multiple mallocs.
131+
data.reserve(function->size());
132+
bool blockListChanged = false;
133+
for (auto blockAndIdx : llvm::enumerate(*function)) {
134+
SILBasicBlock &block = blockAndIdx.value();
135+
int idx = blockAndIdx.index();
136+
if (block.index != idx) {
137+
blockListChanged = true;
138+
block.index = idx;
139+
}
140+
data.push_back(init(&block));
141+
}
142+
function->incrementRefCount();
143+
144+
// If we assigned new block indices, it invalidates all older BasicBlockData
145+
// instances.
146+
if (blockListChanged)
147+
++function->BlockListChangeIdx;
148+
validForBlockOrder = function->BlockListChangeIdx;
149+
}
150+
151+
~BasicBlockData() {
152+
function->decrementRefCount();
153+
}
154+
155+
// For iterating over the function's basic blocks, yielding the block and its
156+
// data. For example:
157+
//
158+
// BasicBlockData<Mydata> blockData;
159+
// for (auto bd : blockData) {
160+
// SILBasicBlock &block = bd.block;
161+
// Mydata &dataForBlock = bd.data;
162+
// }
163+
164+
iterator begin() { return iterator(function->begin(), *this); }
165+
iterator end() { return iterator(function->end(), *this); }
166+
const_iterator begin() const {
167+
return const_iterator(function->begin(), *this);
168+
}
169+
const_iterator end() const {
170+
return const_iterator(function->end(), *this);
171+
}
172+
reverse_iterator rbegin() {
173+
return reverse_iterator(function->rbegin(), *this);
174+
}
175+
reverse_iterator rend() {
176+
return reverse_iterator(function->rend(), *this);
177+
}
178+
const_reverse_iterator rbegin() const {
179+
return const_reverse_iterator(function->rbegin(), *this);
180+
}
181+
const_reverse_iterator rend() const {
182+
return const_reverse_iterator(function->rend(), *this);
183+
}
184+
185+
/// Returns the function's entry block and its data.
186+
typename iterator::BlockAndData entry() { return *begin(); }
187+
188+
/// Returns the function's entry block and its data (const-version).
189+
typename const_iterator::BlockAndData entry() const { return *begin(); }
190+
191+
/// Returns the underlying function.
192+
SILFunction *getFunction() const { return function; }
193+
194+
/// Gets a mutable reference to a block's data.
195+
Data &operator[] (SILBasicBlock *block) {
196+
return data[getIndex(block)];
197+
}
198+
199+
/// Gets a constant reference to a block's data.
200+
const Data &operator[] (SILBasicBlock *block) const {
201+
return data[getIndex(block)];
202+
}
203+
};
204+
205+
} // end swift namespace
206+
207+
#endif

include/swift/SIL/SILBasicBlock.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
3333
friend class SILSuccessor;
3434
friend class SILFunction;
3535
friend class SILGlobalVariable;
36+
template <typename Data, typename Vector> friend class BasicBlockData;
37+
3638
public:
3739
using InstListType = llvm::iplist<SILInstruction>;
3840
private:
@@ -50,6 +52,11 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
5052
/// The ordered set of instructions in the SILBasicBlock.
5153
InstListType InstList;
5254

55+
/// Used by BasicBlockData to index the Data vector.
56+
///
57+
/// A value of -1 means that the index is not initialized yet.
58+
int index = -1;
59+
5360
friend struct llvm::ilist_traits<SILBasicBlock>;
5461
SILBasicBlock() : Parent(nullptr) {}
5562
void operator=(const SILBasicBlock &) = delete;

include/swift/SIL/SILFunction.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class SILFunction
147147
friend class SILBasicBlock;
148148
friend class SILModule;
149149
friend class SILFunctionBuilder;
150+
template <typename Data, typename Vector> friend class BasicBlockData;
150151

151152
/// Module - The SIL module that the function belongs to.
152153
SILModule &Module;
@@ -214,6 +215,11 @@ class SILFunction
214215
/// It does not include references from debug scopes.
215216
unsigned RefCount = 0;
216217

218+
/// Used to verify if a BasicBlockData is not valid anymore.
219+
/// This counter is incremented every time a BasicBlockData re-assigns new
220+
/// block indices.
221+
unsigned BlockListChangeIdx = 0;
222+
217223
/// The function's bare attribute. Bare means that the function is SIL-only
218224
/// and does not require debug info.
219225
unsigned Bare : 1;
@@ -1006,6 +1012,9 @@ class SILFunction
10061012
/// Transfer all blocks of \p F into this function, at the begin of the block
10071013
/// list.
10081014
void moveAllBlocksFromOtherFunction(SILFunction *F) {
1015+
for (SILBasicBlock &block : *F) {
1016+
block.index = -1;
1017+
}
10091018
BlockList.splice(begin(), F->BlockList);
10101019
}
10111020

@@ -1017,6 +1026,7 @@ class SILFunction
10171026
assert(otherFunc != this);
10181027
BlockList.splice(insertPointInThisFunction, otherFunc->BlockList,
10191028
blockInOtherFunction);
1029+
blockInOtherFunction->index = -1;
10201030
}
10211031

10221032
/// Move block \p BB to immediately before the iterator \p IP.

0 commit comments

Comments
 (0)