Skip to content

Commit 65976fd

Browse files
committed
SIL: add a utility which can manage per-block bitfields and flags efficiently.
It is very efficient: no memory allocation is needed an initialization is at zero cost.
1 parent d044a02 commit 65976fd

File tree

8 files changed

+323
-4
lines changed

8 files changed

+323
-4
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.

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,

unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ if(SWIFT_INCLUDE_TOOLS)
1414
add_subdirectory(IDE)
1515
add_subdirectory(Parse)
1616
add_subdirectory(Sema)
17+
add_subdirectory(SIL)
1718
add_subdirectory(SwiftDemangle)
1819
add_subdirectory(Syntax)
1920
if(SWIFT_BUILD_SYNTAXPARSERLIB)

unittests/SIL/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
add_swift_unittest(SwiftSILTests
2+
SILBitfieldTest.cpp
3+
)
4+
5+
target_link_libraries(SwiftSILTests
6+
PRIVATE
7+
swiftSIL
8+
)

unittests/SIL/SILBitfieldTest.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//===--- SILBitfieldTest.cpp ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
#include "gtest/gtest.h"
14+
15+
16+
namespace swift {
17+
18+
class BasicBlockBitfield;
19+
20+
// Fake SILFunction and SILBasicBlock.
21+
22+
struct SILFunction {
23+
BasicBlockBitfield *newestAliveBitfield = nullptr;
24+
uint64_t currentBitfieldID = 1;
25+
};
26+
27+
struct SILBasicBlock {
28+
uint32_t customBits = 0;
29+
uint64_t lastInitializedBitfieldID = 0;
30+
};
31+
32+
}
33+
34+
#define SWIFT_SIL_SILFUNCTION_H
35+
36+
#include "swift/SIL/SILBitfield.h"
37+
38+
using namespace swift;
39+
40+
namespace {
41+
42+
TEST(SILBitfieldTest, Basic) {
43+
SILFunction f;
44+
SILBasicBlock b;
45+
46+
{
47+
BasicBlockFlag A(&f);
48+
EXPECT_FALSE(A.get(&b));
49+
50+
EXPECT_FALSE(A.testAndSet(&b));
51+
EXPECT_TRUE(A.get(&b));
52+
53+
EXPECT_TRUE(A.testAndSet(&b));
54+
EXPECT_TRUE(A.get(&b));
55+
56+
A.reset(&b);
57+
EXPECT_FALSE(A.get(&b));
58+
59+
{
60+
BasicBlockBitfield B(&f, 5);
61+
EXPECT_EQ(B.get(&b), 0u);
62+
63+
B.set(&b, 27);
64+
EXPECT_FALSE(A.get(&b));
65+
EXPECT_EQ(B.get(&b), 27u);
66+
67+
A.set(&b);
68+
EXPECT_TRUE(A.get(&b));
69+
EXPECT_EQ(B.get(&b), 27u);
70+
71+
B.set(&b, 2);
72+
EXPECT_TRUE(A.get(&b));
73+
EXPECT_EQ(B.get(&b), 2u);
74+
75+
B.set(&b, 31);
76+
EXPECT_TRUE(A.get(&b));
77+
EXPECT_EQ(B.get(&b), 31u);
78+
}
79+
{
80+
BasicBlockBitfield C(&f, 2);
81+
EXPECT_EQ(C.get(&b), 0u);
82+
83+
BasicBlockFlag D(&f);
84+
EXPECT_FALSE(D.get(&b));
85+
86+
BasicBlockBitfield E(&f, 3);
87+
88+
E.set(&b, 5);
89+
EXPECT_TRUE(A.get(&b));
90+
EXPECT_EQ(C.get(&b), 0u);
91+
EXPECT_FALSE(D.get(&b));
92+
EXPECT_EQ(E.get(&b), 5u);
93+
94+
C.set(&b, 1);
95+
EXPECT_TRUE(A.get(&b));
96+
EXPECT_EQ(C.get(&b), 1u);
97+
EXPECT_FALSE(D.get(&b));
98+
EXPECT_EQ(E.get(&b), 5u);
99+
}
100+
}
101+
{
102+
BasicBlockBitfield F(&f, 32);
103+
EXPECT_EQ(F.get(&b), 0u);
104+
F.set(&b, 0xdeadbeaf);
105+
EXPECT_EQ(F.get(&b), 0xdeadbeaf);
106+
}
107+
}
108+
109+
}

0 commit comments

Comments
 (0)