Skip to content

Commit 6760dc4

Browse files
committed
SIL: add a utility which can manage per-value and per-instruction bitfields and flags efficiently.
It's used to implement `InstructionSet` and `ValueSet`: sets of SILValues and SILInstructions. Just like `BasicBlockSet` for basic blocks, the set is implemented by setting bits directly in SILNode. This is super efficient because insertion and deletion to/from the set are basic bit operations. The cost is an additional word in SILNode. But this is basically negligible: it just adds ~0.7% of memory used for SILInstructions. In my experiments, I didn't see any relevant changes in memory consumption or compile time.
1 parent 15a48ff commit 6760dc4

File tree

10 files changed

+339
-166
lines changed

10 files changed

+339
-166
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/BasicBlockSet.swift

Lines changed: 0 additions & 59 deletions
This file was deleted.

include/swift/SIL/BasicBlockBits.h

Lines changed: 17 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,118 +17,46 @@
1717
#ifndef SWIFT_SIL_BASICBLOCKBITS_H
1818
#define SWIFT_SIL_BASICBLOCKBITS_H
1919

20-
#include "swift/SIL/SILFunction.h"
21-
#include "llvm/ADT/SmallVector.h"
20+
#include "swift/SIL/SILBitfield.h"
2221

2322
namespace swift {
2423

2524
/// Utility to add a custom bitfield to a function's basic blocks.
2625
///
2726
/// This can be used by transforms to store temporary flags or tiny values per
2827
/// basic block.
29-
/// The memory managed is a 32 bit field within each basic block (\see
30-
/// BasicBlock::customBits) and thus is very efficient: no memory allocation is
31-
/// needed, no hash set or map is needed for lookup and there is no
28+
/// The bits are stored in a 32 bit field within each basic block (\see
29+
/// SILBasicBlock::customBits) which is very efficient: no memory allocation
30+
/// is needed, no hash set or map is needed for lookup and there is no
3231
/// initialization cost (in contrast to BasicBlockData which needs to iterate
3332
/// over all blocks at initialization).
3433
///
3534
/// Invariants:
3635
/// * BasicBlockBitfield instances must be allocated and deallocated
3736
/// following a strict stack discipline, because bit-positions in
38-
/// BasicBlock::customBits are "allocated" and "freed" with a stack-allocation
37+
/// SILBasicBlock::customBits are "allocated" and "freed" with a stack-allocation
3938
/// algorithm. This means, it's fine to use a BasicBlockBitfield as (or in)
4039
/// local variables, e.g. in transformations. But it's not possible to store
4140
/// a BasicBlockBitfield in an Analysis.
4241
/// * The total number of bits which are alive at the same time must not exceed
43-
/// 32 (the size of BasicBlock::customBits).
44-
class BasicBlockBitfield {
45-
/// The bitfield is "added" to the blocks of this function.
46-
SILFunction *function;
47-
48-
/// A single linked list of currently alive BasicBlockBitfields (oldest is
49-
/// last, newest is first).
50-
/// The head of the list is function->lastAllocatedBitfield.
51-
BasicBlockBitfield *parent;
52-
53-
/// Initialized with the monotonically increasing currentBitfieldID of the
54-
/// function.
55-
/// Used to check if the bitfield in a block is initialized.
56-
/// If a block's lastInitializedBitfieldID is less than this ID, it means
57-
/// that the bits of that block are not initialized yet.
58-
/// See also: SILBasicBlock::lastInitializedBitfieldID,
59-
/// SILFunction::currentBitfieldID
60-
unsigned bitfieldID;
61-
62-
short startBit;
63-
short endBit;
64-
uint32_t mask;
42+
/// 32 (the size of SILBasicBlock::customBits).
43+
class BasicBlockBitfield : public SILBitfield<BasicBlockBitfield, SILBasicBlock> {
44+
template <class, class> friend class SILBitfield;
45+
46+
BasicBlockBitfield *insertInto(SILFunction *function) {
47+
BasicBlockBitfield *oldParent = function->newestAliveBlockBitfield;
48+
function->newestAliveBlockBitfield = this;
49+
return oldParent;
50+
}
6551

6652
public:
6753
BasicBlockBitfield(SILFunction *function, int size) :
68-
function(function),
69-
parent(function->newestAliveBitfield),
70-
bitfieldID(function->currentBitfieldID),
71-
startBit(parent ? parent->endBit : 0),
72-
endBit(startBit + size),
73-
mask(0xffffffffu >> (32 - size) << startBit) {
74-
assert(size > 0 && "bit field size must be > 0");
75-
assert(endBit <= 32 && "too many/large bit fields allocated in function");
76-
assert((!parent || bitfieldID > parent->bitfieldID) &&
77-
"BasicBlockBitfield indices are not in order");
78-
function->newestAliveBitfield = this;
79-
++function->currentBitfieldID;
80-
assert(function->currentBitfieldID != 0 && "currentBitfieldID overflow");
81-
}
54+
SILBitfield(function, size, insertInto(function)) {}
8255

8356
~BasicBlockBitfield() {
84-
assert(function->newestAliveBitfield == this &&
57+
assert(function->newestAliveBlockBitfield == this &&
8558
"BasicBlockBitfield destructed too early");
86-
function->newestAliveBitfield = parent;
87-
}
88-
89-
BasicBlockBitfield(const BasicBlockBitfield &) = delete;
90-
BasicBlockBitfield(BasicBlockBitfield &&) = delete;
91-
BasicBlockBitfield &operator=(const BasicBlockBitfield &) = delete;
92-
BasicBlockBitfield &operator=(BasicBlockBitfield &&) = delete;
93-
94-
SILFunction *getFunction() const { return function; }
95-
96-
unsigned get(SILBasicBlock *block) const {
97-
assert(block->getParent() == function);
98-
if (bitfieldID > block->lastInitializedBitfieldID) {
99-
// The bitfield is not initialized yet in this block.
100-
return 0;
101-
}
102-
return (block->customBits & mask) >> startBit;
103-
}
104-
105-
void set(SILBasicBlock *block, unsigned value) {
106-
assert(block->getParent() == function);
107-
assert(((value << startBit) & ~mask) == 0 &&
108-
"value too large for BasicBlockBitfield");
109-
unsigned clearMask = mask;
110-
if (bitfieldID > block->lastInitializedBitfieldID) {
111-
112-
// The bitfield is not initialized yet in this block.
113-
// Initialize the bitfield, and also initialize all parent bitfields,
114-
// which are not initialized, yet. Example:
115-
//
116-
// This field Last initialized field
117-
// | |
118-
// V V
119-
// EE DDD C BB AAA
120-
//
121-
// block->lastInitializedBitfieldID == AAA.bitfieldID
122-
// -> we have to initialize the fields: BB, C, DDD and EE
123-
//
124-
BasicBlockBitfield *bf = parent;
125-
while (bf && bf->bitfieldID > block->lastInitializedBitfieldID) {
126-
clearMask |= bf->mask;
127-
bf = bf->parent;
128-
}
129-
block->lastInitializedBitfieldID = bitfieldID;
130-
}
131-
block->customBits = (block->customBits & ~clearMask) | (value << startBit);
59+
function->newestAliveBlockBitfield = parent;
13260
}
13361
};
13462

include/swift/SIL/NodeBits.h

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//===--- NodeBits.h ---------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 utilities for SILNode bit fields and sets.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SIL_NODEBITS_H
18+
#define SWIFT_SIL_NODEBITS_H
19+
20+
#include "swift/SIL/SILBitfield.h"
21+
22+
namespace swift {
23+
24+
/// Utility to add a custom bitfield to a SILInstruction or SILValue.
25+
///
26+
/// This can be used by transforms to store temporary flags or tiny values per
27+
/// instruction or value.
28+
/// The bits are stored in a small bit field (8 bits) within each node (\see
29+
/// SILNode::Bits::customBits) which is very efficient: no memory allocation
30+
/// is needed, no hash set or map is needed for lookup and there is no
31+
/// initialization cost.
32+
///
33+
/// Invariants:
34+
/// * NodeBitfield instances must be allocated and deallocated
35+
/// following a strict stack discipline, because bit-positions in the node's
36+
/// bit field are "allocated" and "freed" with a stack-allocation
37+
/// algorithm. This means, it's fine to use a NodeBitfield as (or in)
38+
/// local variables, e.g. in transformations. But it's not possible to store
39+
/// a NodeBitfield in an Analysis.
40+
/// * The total number of bits which are alive at the same time must not exceed
41+
/// 8 (the size of SILNode::Bits::customBits).
42+
class NodeBitfield : public SILBitfield<NodeBitfield, SILNode> {
43+
template <class, class> friend class SILBitfield;
44+
45+
NodeBitfield *insertInto(SILFunction *function) {
46+
NodeBitfield *oldParent = function->newestAliveNodeBitfield;
47+
function->newestAliveNodeBitfield = this;
48+
return oldParent;
49+
}
50+
51+
public:
52+
NodeBitfield(SILFunction *function, int size) :
53+
SILBitfield(function, size, insertInto(function)) {}
54+
55+
~NodeBitfield() {
56+
assert(function->newestAliveNodeBitfield == this &&
57+
"BasicBlockBitfield destructed too early");
58+
function->newestAliveNodeBitfield = parent;
59+
}
60+
};
61+
62+
/// A set of SILNodes.
63+
///
64+
/// For details see NodeBitfield.
65+
class NodeSet {
66+
NodeBitfield bit;
67+
68+
public:
69+
NodeSet(SILFunction *function) : bit(function, 1) {}
70+
71+
SILFunction *getFunction() const { return bit.getFunction(); }
72+
73+
bool contains(SILNode *node) const { return (bool)bit.get(node); }
74+
75+
/// Returns true if \p node was not contained in the set before inserting.
76+
bool insert(SILNode *node) {
77+
bool wasContained = contains(node);
78+
if (!wasContained) {
79+
bit.set(node, 1);
80+
}
81+
return !wasContained;
82+
}
83+
84+
void erase(SILNode *node) {
85+
bit.set(node, 0);
86+
}
87+
};
88+
89+
90+
/// A set of SILInstructions.
91+
///
92+
/// For details see NodeBitfield.
93+
class InstructionSet {
94+
NodeSet nodeSet;
95+
96+
public:
97+
InstructionSet(SILFunction *function) : nodeSet(function) {}
98+
99+
SILFunction *getFunction() const { return nodeSet.getFunction(); }
100+
101+
bool contains(SILInstruction *inst) const { return nodeSet.contains(inst->asSILNode()); }
102+
103+
/// Returns true if \p inst was not contained in the set before inserting.
104+
bool insert(SILInstruction *inst) { return nodeSet.insert(inst->asSILNode()); }
105+
106+
void erase(SILInstruction *inst) { nodeSet.erase(inst->asSILNode()); }
107+
};
108+
109+
/// A set of SILValues.
110+
///
111+
/// For details see NodeBitfield.
112+
class ValueSet {
113+
NodeSet nodeSet;
114+
115+
public:
116+
ValueSet(SILFunction *function) : nodeSet(function) {}
117+
118+
SILFunction *getFunction() const { return nodeSet.getFunction(); }
119+
120+
bool contains(SILValue value) const { return nodeSet.contains(value); }
121+
122+
/// Returns true if \p value was not contained in the set before inserting.
123+
bool insert(SILValue value) { return nodeSet.insert(value); }
124+
125+
void erase(SILValue value) { nodeSet.erase(value); }
126+
};
127+
128+
} // namespace swift
129+
130+
#endif

include/swift/SIL/SILBasicBlock.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ public SwiftObjectHeader {
3838
friend class SILFunction;
3939
friend class SILGlobalVariable;
4040
template <typename, unsigned> friend class BasicBlockData;
41-
friend class BasicBlockBitfield;
41+
template <class, class> friend class SILBitfield;
4242

4343
static SwiftMetatype registeredMetatype;
44-
44+
45+
using CustomBitsType = uint32_t;
46+
4547
public:
4648
using InstListType = llvm::iplist<SILInstruction>;
4749
private:
@@ -67,7 +69,7 @@ public SwiftObjectHeader {
6769
int index = -1;
6870

6971
/// Custom bits managed by BasicBlockBitfield.
70-
uint32_t customBits = 0;
72+
CustomBitsType customBits = 0;
7173

7274
/// The BasicBlockBitfield ID of the last initialized bitfield in customBits.
7375
/// Example:
@@ -82,9 +84,17 @@ public SwiftObjectHeader {
8284
/// -> AAA, BB and C are initialized,
8385
/// DD and EEE are uninitialized
8486
///
85-
/// See also: BasicBlockBitfield::bitfieldID, SILFunction::currentBitfieldID.
87+
/// See also: SILBitfield::bitfieldID, SILFunction::currentBitfieldID.
8688
uint64_t lastInitializedBitfieldID = 0;
8789

90+
// Used by `BasicBlockBitfield`.
91+
unsigned getCustomBits() const { return customBits; }
92+
// Used by `BasicBlockBitfield`.
93+
void setCustomBits(unsigned value) { customBits = value; }
94+
95+
// Used by `BasicBlockBitfield`.
96+
enum { numCustomBits = std::numeric_limits<CustomBitsType>::digits };
97+
8898
friend struct llvm::ilist_traits<SILBasicBlock>;
8999

90100
SILBasicBlock();
@@ -111,6 +121,7 @@ public SwiftObjectHeader {
111121
Optional<llvm::StringRef> getDebugName() const;
112122

113123
SILFunction *getParent() { return Parent; }
124+
SILFunction *getFunction() { return getParent(); }
114125
const SILFunction *getParent() const { return Parent; }
115126

116127
SILModule &getModule() const;

0 commit comments

Comments
 (0)