Skip to content

Commit a0884ba

Browse files
authored
Merge pull request #35521 from eeckstein/sil-bitfields
SIL: add a utility which can manage per-block bitfields and flags efficiently.
2 parents a89c882 + 3e8612b commit a0884ba

File tree

15 files changed

+366
-42
lines changed

15 files changed

+366
-42
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
3434
friend class SILFunction;
3535
friend class SILGlobalVariable;
3636
template <typename Data, typename Vector> friend class BasicBlockData;
37+
friend class BasicBlockBitfield;
3738

3839
public:
3940
using InstListType = llvm::iplist<SILInstruction>;
@@ -57,6 +58,25 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
5758
/// A value of -1 means that the index is not initialized yet.
5859
int index = -1;
5960

61+
/// Custom bits managed by BasicBlockBitfield.
62+
uint32_t customBits = 0;
63+
64+
/// The BasicBlockBitfield ID of the last initialized bitfield in customBits.
65+
/// Example:
66+
///
67+
/// Last initialized field:
68+
/// lastInitializedBitfieldID == C.bitfieldID
69+
/// |
70+
/// V
71+
/// customBits: <unused> EE DDD C BB AAA
72+
/// 31 ... 0
73+
///
74+
/// -> AAA, BB and C are initialized,
75+
/// DD and EEE are uninitialized
76+
///
77+
/// See also: BasicBlockBitfield::bitfieldID, SILFunction::currentBitfieldID.
78+
uint64_t lastInitializedBitfieldID = 0;
79+
6080
friend struct llvm::ilist_traits<SILBasicBlock>;
6181
SILBasicBlock() : Parent(nullptr) {}
6282
void operator=(const SILBasicBlock &) = delete;

include/swift/SIL/SILBitfield.h

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//===--- SILBitField.h - Defines the bitfield utilities ---------*- 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 BasicBlockBitfield and BasicBlockFlag utilities.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SIL_SILBITFIELD_H
18+
#define SWIFT_SIL_SILBITFIELD_H
19+
20+
#include "swift/SIL/SILFunction.h"
21+
22+
namespace swift {
23+
24+
/// Utility to add a custom bitfield to a function's basic blocks.
25+
///
26+
/// This can be used by transforms to store temporary flags or tiny values per
27+
/// basic block.
28+
/// It is very efficient: no memory allocation is needed, no hash set or map is
29+
/// needed for lookup and there is no initialization cost (in contrast to
30+
/// BasicBlockData which needs to iterate over all blocks at initialization).
31+
///
32+
/// Restrictions:
33+
/// * BasicBlockBitfield instances must be allocated and deallocated
34+
/// following a strict stack discipline. This means, it's fine to use them as
35+
/// (or in) local variables in transformations. But it's e.g. not possible to
36+
/// store a BasicBlockBitfield in an Analysis.
37+
/// * The total number of bits which are alive at the same time must not exceed
38+
/// 32.
39+
class BasicBlockBitfield {
40+
/// The bitfield is "added" to the blocks of this function.
41+
SILFunction *function;
42+
43+
/// A single linked list of currently alive BasicBlockBitfields (oldest is
44+
/// last, newest is first).
45+
/// The head of the list is function->lastAllocatedBitfield.
46+
BasicBlockBitfield *parent;
47+
48+
/// Initialized with the monotonically increasing currentBitfieldID of the
49+
/// function.
50+
/// Used to check if the bitfield in a block is initialized.
51+
/// If a block's lastInitializedBitfieldID is less than this ID, it means
52+
/// that the bits of that block are not initialized yet.
53+
/// See also: SILBasicBlock::lastInitializedBitfieldID,
54+
/// SILFunction::currentBitfieldID
55+
unsigned bitfieldID;
56+
57+
short startBit;
58+
short endBit;
59+
uint32_t mask;
60+
61+
public:
62+
BasicBlockBitfield(SILFunction *function, int size) :
63+
function(function),
64+
parent(function->newestAliveBitfield),
65+
bitfieldID(function->currentBitfieldID),
66+
startBit(parent ? parent->endBit : 0),
67+
endBit(startBit + size),
68+
mask(0xffffffffu >> (32 - size) << startBit) {
69+
assert(size > 0 && "bit field size must be > 0");
70+
assert(endBit <= 32 && "too many/large bit fields allocated in function");
71+
assert((!parent || bitfieldID > parent->bitfieldID) &&
72+
"BasicBlockBitfield indices are not in order");
73+
function->newestAliveBitfield = this;
74+
++function->currentBitfieldID;
75+
assert(function->currentBitfieldID != 0 && "currentBitfieldID overflow");
76+
}
77+
78+
~BasicBlockBitfield() {
79+
assert(function->newestAliveBitfield == this &&
80+
"BasicBlockBitfield destructed too early");
81+
function->newestAliveBitfield = parent;
82+
}
83+
84+
BasicBlockBitfield(const BasicBlockBitfield &) = delete;
85+
BasicBlockBitfield(BasicBlockBitfield &&) = delete;
86+
BasicBlockBitfield &operator=(const BasicBlockBitfield &) = delete;
87+
BasicBlockBitfield &operator=(BasicBlockBitfield &&) = delete;
88+
89+
SILFunction *getFunction() const { return function; }
90+
91+
unsigned get(SILBasicBlock *block) const {
92+
if (bitfieldID > block->lastInitializedBitfieldID) {
93+
// The bitfield is not initialized yet in this block.
94+
return 0;
95+
}
96+
return (block->customBits & mask) >> startBit;
97+
}
98+
99+
void set(SILBasicBlock *block, unsigned value) {
100+
assert(((value << startBit) & ~mask) == 0 &&
101+
"value too large for BasicBlockBitfield");
102+
unsigned clearMask = mask;
103+
if (bitfieldID > block->lastInitializedBitfieldID) {
104+
105+
// The bitfield is not initialized yet in this block.
106+
// Initialize the bitfield, and also initialize all parent bitfields,
107+
// which are not initialized, yet. Example:
108+
//
109+
// This field Last initialized field
110+
// | |
111+
// V V
112+
// EE DDD C BB AAA
113+
//
114+
// block->lastInitializedBitfieldID == AAA.bitfieldID
115+
// -> we have to initialize the fields: BB, C, DDD and EE
116+
//
117+
BasicBlockBitfield *bf = parent;
118+
while (bf && bf->bitfieldID > block->lastInitializedBitfieldID) {
119+
clearMask |= bf->mask;
120+
bf = bf->parent;
121+
}
122+
block->lastInitializedBitfieldID = bitfieldID;
123+
}
124+
block->customBits = (block->customBits & ~clearMask) | (value << startBit);
125+
}
126+
};
127+
128+
/// A BasicBlockBitfield containing a single bit - a flag.
129+
class BasicBlockFlag {
130+
BasicBlockBitfield bit;
131+
132+
public:
133+
BasicBlockFlag(SILFunction *function) : bit(function, 1) {}
134+
135+
SILFunction *getFunction() const { return bit.getFunction(); }
136+
137+
bool get(SILBasicBlock *block) const { return (bool)bit.get(block); }
138+
139+
void set(SILBasicBlock *block) { bit.set(block, 1); }
140+
void reset(SILBasicBlock *block) { bit.set(block, 0); }
141+
142+
/// Sets the flag and returns the old value.
143+
bool testAndSet(SILBasicBlock *block) {
144+
bool oldValue = get(block);
145+
set(block);
146+
return oldValue;
147+
}
148+
};
149+
150+
/// A BasicBlockFlag with a set-like API.
151+
class BasicBlockSet {
152+
BasicBlockFlag flag;
153+
154+
public:
155+
BasicBlockSet(SILFunction *function) : flag(function) {}
156+
157+
SILFunction *getFunction() const { return flag.getFunction(); }
158+
159+
bool contains(SILBasicBlock *block) const { return flag.get(block); }
160+
161+
/// Returns true if \p block was not contained in the set before inserting.
162+
bool insert(SILBasicBlock *block) { return !flag.testAndSet(block); }
163+
164+
void remove(SILBasicBlock *block) { flag.reset(block); }
165+
};
166+
167+
} // namespace swift
168+
169+
#endif

include/swift/SIL/SILFunction.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class SILInstruction;
3737
class SILModule;
3838
class SILFunctionBuilder;
3939
class SILProfiler;
40+
class BasicBlockBitfield;
4041

4142
namespace Lowering {
4243
class TypeLowering;
@@ -148,6 +149,7 @@ class SILFunction
148149
friend class SILModule;
149150
friend class SILFunctionBuilder;
150151
template <typename Data, typename Vector> friend class BasicBlockData;
152+
friend class BasicBlockBitfield;
151153

152154
/// Module - The SIL module that the function belongs to.
153155
SILModule &Module;
@@ -192,6 +194,16 @@ class SILFunction
192194

193195
Identifier ObjCReplacementFor;
194196

197+
/// The head of a single-linked list of currently alive BasicBlockBitfield.
198+
BasicBlockBitfield *newestAliveBitfield = nullptr;
199+
200+
/// A monotonically increasing ID which is incremented whenever a
201+
/// BasicBlockBitfield is constructed.
202+
/// Usually this stays below 1000, so a 32-bit unsigned is more than
203+
/// sufficient.
204+
/// For details see BasicBlockBitfield::bitfieldID;
205+
unsigned currentBitfieldID = 1;
206+
195207
/// The function's set of semantics attributes.
196208
///
197209
/// TODO: Why is this using a std::string? Why don't we use uniqued
@@ -1012,9 +1024,6 @@ class SILFunction
10121024
/// Transfer all blocks of \p F into this function, at the begin of the block
10131025
/// list.
10141026
void moveAllBlocksFromOtherFunction(SILFunction *F) {
1015-
for (SILBasicBlock &block : *F) {
1016-
block.index = -1;
1017-
}
10181027
BlockList.splice(begin(), F->BlockList);
10191028
}
10201029

@@ -1026,7 +1035,6 @@ class SILFunction
10261035
assert(otherFunc != this);
10271036
BlockList.splice(insertPointInThisFunction, otherFunc->BlockList,
10281037
blockInOtherFunction);
1029-
blockInOtherFunction->index = -1;
10301038
}
10311039

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

include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H
2424

2525
#include "swift/SIL/SILBasicBlock.h"
26+
#include "swift/SIL/SILBitfield.h"
2627
#include "swift/SIL/SILCloner.h"
2728
#include "swift/SIL/SILInstruction.h"
2829
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -35,20 +36,22 @@ class SILLoopInfo;
3536

3637
/// Compute the set of reachable blocks.
3738
class ReachableBlocks {
38-
SmallPtrSet<SILBasicBlock *, 32> visited;
39+
BasicBlockSet visited;
3940

4041
public:
42+
ReachableBlocks(SILFunction *function) : visited(function) {}
43+
4144
/// Invoke \p visitor for each reachable block in \p f in worklist order (at
4245
/// least one predecessor has been visited--defs are always visited before
4346
/// uses except for phi-type block args). The \p visitor takes a block
4447
/// argument, which is already marked visited, and must return true to
4548
/// continue visiting blocks.
4649
///
4750
/// Returns true if all reachable blocks were visited.
48-
bool visit(SILFunction *f, function_ref<bool(SILBasicBlock *)> visitor);
51+
bool visit(function_ref<bool(SILBasicBlock *)> visitor);
4952

5053
/// Return true if \p bb has been visited.
51-
bool isVisited(SILBasicBlock *bb) const { return visited.count(bb); }
54+
bool isVisited(SILBasicBlock *bb) const { return visited.contains(bb); }
5255
};
5356

5457
/// Remove all instructions in the body of \p bb in safe manner by using

include/swift/SILOptimizer/Utils/ValueLifetime.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/Basic/STLExtras.h"
2121
#include "swift/SIL/SILBuilder.h"
2222
#include "swift/SIL/SILInstruction.h"
23+
#include "swift/SIL/SILBitfield.h"
2324
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2425

2526
namespace swift {
@@ -34,7 +35,10 @@ class ValueLifetimeAnalysis {
3435
PointerUnion<SILInstruction *, SILArgument *> defValue;
3536

3637
/// The set of blocks where the value is live.
37-
llvm::SmallSetVector<SILBasicBlock *, 16> liveBlocks;
38+
llvm::SmallVector<SILBasicBlock *, 16> liveBlocks;
39+
40+
/// True for blocks which are in liveBlocks.
41+
BasicBlockFlag inLiveBlocks;
3842

3943
/// The set of instructions where the value is used, or the users-list
4044
/// provided with the constructor.
@@ -66,7 +70,7 @@ class ValueLifetimeAnalysis {
6670
/// iterators.
6771
template <typename RangeTy>
6872
ValueLifetimeAnalysis(SILArgument *def, const RangeTy &useRange)
69-
: defValue(def), userSet() {
73+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
7074
for (SILInstruction *use : useRange)
7175
userSet.insert(use);
7276
propagateLiveness();
@@ -75,7 +79,7 @@ class ValueLifetimeAnalysis {
7579
ValueLifetimeAnalysis(
7680
SILArgument *def,
7781
llvm::iterator_range<ValueBaseUseIterator> useRange)
78-
: defValue(def), userSet() {
82+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
7983
for (Operand *use : useRange)
8084
userSet.insert(use->getUser());
8185
propagateLiveness();
@@ -84,7 +88,7 @@ class ValueLifetimeAnalysis {
8488
template <typename RangeTy>
8589
ValueLifetimeAnalysis(
8690
SILInstruction *def, const RangeTy &useRange)
87-
: defValue(def), userSet() {
91+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
8892
for (SILInstruction *use : useRange)
8993
userSet.insert(use);
9094
propagateLiveness();
@@ -93,7 +97,7 @@ class ValueLifetimeAnalysis {
9397
ValueLifetimeAnalysis(
9498
SILInstruction *def,
9599
llvm::iterator_range<ValueBaseUseIterator> useRange)
96-
: defValue(def), userSet() {
100+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
97101
for (Operand *use : useRange)
98102
userSet.insert(use->getUser());
99103
propagateLiveness();
@@ -144,7 +148,7 @@ class ValueLifetimeAnalysis {
144148

145149
/// Returns true if the value is alive at the begin of block \p bb.
146150
bool isAliveAtBeginOfBlock(SILBasicBlock *bb) {
147-
return liveBlocks.count(bb) &&
151+
return inLiveBlocks.get(bb) &&
148152
(hasUsersBeforeDef || bb != getDefValueParentBlock());
149153
}
150154

lib/SIL/IR/SILBasicBlock.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ transferNodesFromList(llvm::ilist_traits<SILBasicBlock> &SrcTraits,
309309
// If splicing blocks not in the same function, update the parent pointers.
310310
for (; First != Last; ++First) {
311311
First->Parent = Parent;
312+
First->index = -1;
313+
First->lastInitializedBitfieldID = 0;
312314
for (auto &II : *First)
313315
II.setDebugScope(ScopeCloner.getOrCreateClonedScope(II.getDebugScope()));
314316
}

lib/SIL/IR/SILFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ SILFunction::~SILFunction() {
210210

211211
assert(RefCount == 0 &&
212212
"Function cannot be deleted while function_ref's still exist");
213+
assert(!newestAliveBitfield &&
214+
"Not all BasicBlockBitfields deleted at function destruction");
213215
}
214216

215217
void SILFunction::createProfiler(ASTNode Root, SILDeclRef forDecl,

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,8 +1619,8 @@ void EscapeAnalysis::ConnectionGraph::verify() const {
16191619
// Verify that all pointer nodes are still mapped, otherwise the process of
16201620
// merging nodes may have lost information. Only visit reachable blocks,
16211621
// because the graph builder only mapped values from reachable blocks.
1622-
ReachableBlocks reachable;
1623-
reachable.visit(F, [this](SILBasicBlock *bb) {
1622+
ReachableBlocks reachable(F);
1623+
reachable.visit([this](SILBasicBlock *bb) {
16241624
for (auto &i : *bb) {
16251625
if (isNonWritableMemoryAddress(&i))
16261626
continue;
@@ -1748,8 +1748,8 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo,
17481748
assert(ConGraph->isEmpty());
17491749

17501750
// Visit the blocks in dominance order.
1751-
ReachableBlocks reachable;
1752-
reachable.visit(ConGraph->F, [&](SILBasicBlock *bb) {
1751+
ReachableBlocks reachable(ConGraph->F);
1752+
reachable.visit([&](SILBasicBlock *bb) {
17531753
// Create edges for the instructions.
17541754
for (auto &i : *bb) {
17551755
analyzeInstruction(&i, FInfo, BottomUpOrder, RecursionDepth);

0 commit comments

Comments
 (0)