Skip to content

Commit 7afa419

Browse files
committed
SIL: improve inline bitfields in SILNode, SILBasicBlock and Operand
* Let the customBits and lastInitializedBitfieldID share a single uint64_t. This increases the number of available bits in SILNode and Operand from 8 to 20. Also, it simplifies the Operand class because no PointerIntPairs are used anymore to store the operand pointer fields. * Instead make the "deleted" flag a separate bool field in SILNode (instead of encoding it with the sign of lastInitializedBitfieldID). Another simplification * Enable important invariant checks also in release builds by using `require` instead of `assert`. Not catching such errors in release builds would be a disaster. * Let the Swift optimization passes use all the available bits and not only a fixed amount of 8 (SILNode) and 16 (SILBasicBlock).
1 parent e45b4bd commit 7afa419

File tree

8 files changed

+71
-99
lines changed

8 files changed

+71
-99
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,13 @@ public SwiftObjectHeader {
130130
/// DD and EEE are uninitialized
131131
///
132132
/// See also: SILBitfield::bitfieldID, SILFunction::currentBitfieldID.
133-
int64_t lastInitializedBitfieldID = 0;
133+
uint64_t lastInitializedBitfieldID = 0;
134134

135135
// Used by `BasicBlockBitfield`.
136136
unsigned getCustomBits() const { return customBits; }
137137
// Used by `BasicBlockBitfield`.
138138
void setCustomBits(unsigned value) { customBits = value; }
139139

140-
// Used by `BasicBlockBitfield`.
141-
enum { numCustomBits = std::numeric_limits<CustomBitsType>::digits };
142-
143140
friend struct llvm::ilist_traits<SILBasicBlock>;
144141

145142
SILBasicBlock();
@@ -155,7 +152,8 @@ public SwiftObjectHeader {
155152

156153
~SILBasicBlock();
157154

158-
bool isMarkedAsDeleted() const { return lastInitializedBitfieldID < 0; }
155+
enum { numCustomBits = std::numeric_limits<CustomBitsType>::digits };
156+
enum { maxBitfieldID = std::numeric_limits<uint64_t>::max() };
159157

160158
/// Gets the ID (= index in the function's block list) of the block.
161159
///

include/swift/SIL/SILBitfield.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_SIL_SILBITFIELD_H
1818
#define SWIFT_SIL_SILBITFIELD_H
1919

20+
#include "swift/Basic/Require.h"
2021
#include "swift/SIL/SILFunction.h"
2122

2223
namespace swift {
@@ -30,7 +31,7 @@ template <class Impl, class T> class SILBitfield {
3031
/// that the bits of that block are not initialized yet.
3132
/// See also: SILBasicBlock::lastInitializedBitfieldID,
3233
/// SILFunction::currentBitfieldID
33-
int64_t bitfieldID;
34+
uint64_t bitfieldID;
3435

3536
short startBit;
3637
short endBit;
@@ -55,11 +56,13 @@ template <class Impl, class T> class SILBitfield {
5556
parent(parent),
5657
function(function) {
5758
assert(size > 0 && "bit field size must be > 0");
58-
assert(endBit <= T::numCustomBits && "too many/large bit fields allocated in function");
59+
require(endBit <= T::numCustomBits,
60+
"too many/large bit fields allocated in function");
5961
assert((!parent || bitfieldID > parent->bitfieldID) &&
6062
"BasicBlockBitfield indices are not in order");
63+
require(function->currentBitfieldID < T::maxBitfieldID,
64+
"currentBitfieldID overflow");
6165
++function->currentBitfieldID;
62-
assert(function->currentBitfieldID != 0 && "currentBitfieldID overflow");
6366
}
6467

6568
SILBitfield(const SILBitfield &) = delete;
@@ -85,9 +88,6 @@ template <class Impl, class T> class SILBitfield {
8588
unsigned clearMask = mask;
8689
if (bitfieldID > entity->lastInitializedBitfieldID) {
8790

88-
if (entity->isMarkedAsDeleted())
89-
return;
90-
9191
// The bitfield is not initialized yet in this block.
9292
// Initialize the bitfield, and also initialize all parent bitfields,
9393
// which are not initialized, yet. Example:

include/swift/SIL/SILFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ class SILFunction
294294
/// A monotonically increasing ID which is incremented whenever a
295295
/// BasicBlockBitfield, NodeBitfield, or OperandBitfield is constructed. For
296296
/// details see SILBitfield::bitfieldID;
297-
int64_t currentBitfieldID = 1;
297+
uint64_t currentBitfieldID = 1;
298298

299299
/// Unique identifier for vector indexing and deterministic sorting.
300300
/// May be reused when zombie functions are recovered.

include/swift/SIL/SILNode.h

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class alignas(8) SILNode :
126126
enum { NumAllocRefTailTypesBits = 4 };
127127
enum { NumMarkDependenceKindBits = 2 };
128128

129+
enum { numCustomBits = 20 };
130+
enum { maxBitfieldID = std::numeric_limits<uint64_t>::max() >> numCustomBits };
131+
129132
protected:
130133
friend class SILInstruction;
131134
template <class, class> friend class SILBitfield;
@@ -135,11 +138,7 @@ class alignas(8) SILNode :
135138

136139
uint8_t kind;
137140

138-
// Used by `NodeBitfield`.
139-
enum { numCustomBits = 8 };
140-
141-
// Used by `NodeBitfield`.
142-
uint8_t customBits;
141+
bool deleted = false;
143142

144143
// Part of SILInstruction's debug location. Together with
145144
// `SILInstruction::locationStorage` this forms the SILLocation.
@@ -364,6 +363,9 @@ class alignas(8) SILNode :
364363

365364
//===---------------------- end of shared fields ------------------------===//
366365

366+
// Used by `NodeBitfield`.
367+
uint64_t customBits : numCustomBits;
368+
367369
/// The NodeBitfield ID of the last initialized bitfield in `customBits`.
368370
/// Example:
369371
///
@@ -377,20 +379,19 @@ class alignas(8) SILNode :
377379
/// -> AAA, BB and C are initialized,
378380
/// DD and EEE are uninitialized
379381
///
380-
/// If the ID is negative, it means that the node (in case it's an instruction)
381-
/// is deleted, i.e. it does not belong to the function anymore. Conceptually
382-
/// this results in setting all bitfields to zero, which e.g. "removes" the
383-
/// node from all NodeSets.
382+
/// The size of lastInitializedBitfieldID should be more than 32 bits to
383+
/// absolutely avoid an overflow.
384384
///
385385
/// See also: SILBitfield::bitfieldID, SILFunction::currentBitfieldID.
386-
int64_t lastInitializedBitfieldID = 0;
386+
uint64_t lastInitializedBitfieldID : (64 - numCustomBits);
387387

388388
private:
389389
SwiftMetatype getSILNodeMetatype(SILNodeKind kind);
390390

391391
protected:
392392
SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)),
393-
kind((uint8_t)kind) {
393+
kind((uint8_t)kind),
394+
customBits(0), lastInitializedBitfieldID(0) {
394395
_sharedUInt8_private.opaque = 0;
395396
_sharedUInt32_private.opaque = 0;
396397
}
@@ -442,11 +443,8 @@ class alignas(8) SILNode :
442443
lastInitializedBitfieldID = 0;
443444
}
444445

445-
void markAsDeleted() {
446-
lastInitializedBitfieldID = -1;
447-
}
448-
449-
bool isMarkedAsDeleted() const { return lastInitializedBitfieldID < 0; }
446+
void markAsDeleted() { deleted = true; }
447+
bool isMarkedAsDeleted() const { return deleted; }
450448

451449
static SILNode *instAsNode(SILInstruction *inst);
452450
static const SILNode *instAsNode(const SILInstruction *inst);

include/swift/SIL/SILValue.h

Lines changed: 37 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,38 +1019,40 @@ ValueOwnershipKind::getForwardingOperandOwnership(bool allowUnowned) const {
10191019
/// A formal SIL reference to a value, suitable for use as a stored
10201020
/// operand.
10211021
class Operand {
1022+
public:
1023+
enum { numCustomBits = 8 };
1024+
enum { maxBitfieldID = std::numeric_limits<uint64_t>::max() >> numCustomBits };
1025+
1026+
private:
10221027
template <class, class> friend class SILBitfield;
10231028

1024-
/// The value used as this operand combined with three bits we use for
1025-
/// `customBits`.
1026-
llvm::PointerIntPair<SILValue, 3> TheValueAndThreeBits = {SILValue(), 0};
1029+
/// The value used as this operand.
1030+
SILValue TheValue;
10271031

1028-
/// The next operand in the use-chain. Note that the chain holds every use of
1029-
/// the current ValueBase, not just those of the designated result.
1030-
///
1031-
/// We use 3 bits of the pointer for customBits.
1032-
llvm::PointerIntPair<Operand *, 3> NextUseAndThreeBits = {nullptr, 0};
1032+
/// The next operand in the use-chain. Note that the chain holds
1033+
/// every use of the current ValueBase, not just those of the
1034+
/// designated result.
1035+
Operand *NextUse = nullptr;
10331036

10341037
/// A back-pointer in the use-chain, required for fast patching
10351038
/// of use-chains.
1036-
///
1037-
/// We use 2 bits of the pointer for customBits.
1038-
llvm::PointerIntPair<Operand **, 3> BackAndThreeBits = {nullptr, 0};
1039+
Operand **Back = nullptr;
10391040

10401041
/// The owner of this operand.
10411042
/// FIXME: this could be space-compressed.
10421043
SILInstruction *Owner;
10431044

1044-
/// Used by `OperandBitfield`
1045-
enum { numCustomBits = 8 };
1045+
uint64_t customBits : numCustomBits;
10461046

1047-
/// Used by `OperandBitfield`
1048-
int64_t lastInitializedBitfieldID = 0;
1047+
// For details see SILNode::lastInitializedBitfieldID
1048+
uint64_t lastInitializedBitfieldID : (64 - numCustomBits);
10491049

10501050
public:
1051-
Operand(SILInstruction *owner) : Owner(owner) {}
1051+
Operand(SILInstruction *owner)
1052+
: Owner(owner), customBits(0), lastInitializedBitfieldID(0) {}
10521053
Operand(SILInstruction *owner, SILValue theValue)
1053-
: TheValueAndThreeBits(theValue), Owner(owner) {
1054+
: TheValue(theValue), Owner(owner),
1055+
customBits(0), lastInitializedBitfieldID(0) {
10541056
insertIntoCurrent();
10551057
}
10561058

@@ -1062,14 +1064,14 @@ class Operand {
10621064
Operand &operator=(Operand &&) = default;
10631065

10641066
/// Return the current value being used by this operand.
1065-
SILValue get() const { return TheValueAndThreeBits.getPointer(); }
1067+
SILValue get() const { return TheValue; }
10661068

10671069
/// Set the current value being used by this operand.
10681070
void set(SILValue newValue) {
10691071
// It's probably not worth optimizing for the case of switching
10701072
// operands on a single value.
10711073
removeFromCurrent();
1072-
TheValueAndThreeBits.setPointer(newValue);
1074+
TheValue = newValue;
10731075
insertIntoCurrent();
10741076
}
10751077

@@ -1083,9 +1085,9 @@ class Operand {
10831085
/// Remove this use of the operand.
10841086
void drop() {
10851087
removeFromCurrent();
1086-
TheValueAndThreeBits = {SILValue(), 0};
1087-
NextUseAndThreeBits = {nullptr, 0};
1088-
BackAndThreeBits = {nullptr, 0};
1088+
TheValue = SILValue();
1089+
NextUse = nullptr;
1090+
Back = nullptr;
10891091
Owner = nullptr;
10901092
}
10911093

@@ -1097,7 +1099,7 @@ class Operand {
10971099
SILInstruction *getUser() { return Owner; }
10981100
const SILInstruction *getUser() const { return Owner; }
10991101

1100-
Operand *getNextUse() const { return NextUseAndThreeBits.getPointer(); }
1102+
Operand *getNextUse() const { return NextUse; }
11011103

11021104
/// Return true if this operand is a type dependent operand.
11031105
///
@@ -1147,63 +1149,36 @@ class Operand {
11471149
SILBasicBlock *getParentBlock() const;
11481150
SILFunction *getParentFunction() const;
11491151

1150-
unsigned getCustomBits() const {
1151-
unsigned bits = 0;
1152-
bits |= TheValueAndThreeBits.getInt();
1153-
bits |= NextUseAndThreeBits.getInt() << 3;
1154-
bits |= BackAndThreeBits.getInt() << 2;
1155-
return bits;
1156-
}
1157-
1158-
void setCustomBits(unsigned bits) {
1159-
assert(bits < 256 && "Can only store a byte?!");
1160-
TheValueAndThreeBits.setInt(bits & 0x7);
1161-
NextUseAndThreeBits.setInt((bits >> 3) & 0x7);
1162-
BackAndThreeBits.setInt((bits >> 6) & 0x3);
1163-
}
1152+
unsigned getCustomBits() const { return customBits; }
1153+
void setCustomBits(unsigned bits) {customBits = bits; }
11641154

11651155
// Called when transferring basic blocks from one function to another.
11661156
void resetBitfields() {
11671157
lastInitializedBitfieldID = 0;
11681158
}
11691159

1170-
void markAsDeleted() {
1171-
lastInitializedBitfieldID = -1;
1172-
}
1173-
1174-
bool isMarkedAsDeleted() const { return lastInitializedBitfieldID < 0; }
1175-
11761160
SILFunction *getFunction() const;
11771161

11781162
void print(llvm::raw_ostream &os) const;
11791163
SWIFT_DEBUG_DUMP;
11801164

11811165
private:
11821166
void removeFromCurrent() {
1183-
auto *back = getBack();
1184-
if (!back)
1185-
return;
1186-
auto *nextUse = getNextUse();
1187-
*back = nextUse;
1188-
if (nextUse)
1189-
nextUse->setBack(back);
1167+
if (!Back)
1168+
return;
1169+
*Back = NextUse;
1170+
if (NextUse)
1171+
NextUse->Back = Back;
11901172
}
11911173

11921174
void insertIntoCurrent() {
1193-
auto **firstUse = &get()->FirstUse;
1194-
setBack(firstUse);
1195-
setNextUse(*firstUse);
1196-
if (auto *nextUse = getNextUse())
1197-
nextUse->setBack(NextUseAndThreeBits.getAddrOfPointer());
1198-
get()->FirstUse = this;
1175+
Back = &TheValue->FirstUse;
1176+
NextUse = TheValue->FirstUse;
1177+
if (NextUse)
1178+
NextUse->Back = &NextUse;
1179+
TheValue->FirstUse = this;
11991180
}
12001181

1201-
void setNextUse(Operand *op) { NextUseAndThreeBits.setPointer(op); }
1202-
1203-
Operand **getBack() const { return BackAndThreeBits.getPointer(); }
1204-
1205-
void setBack(Operand **newValue) { BackAndThreeBits.setPointer(newValue); }
1206-
12071182
friend class ValueBase;
12081183
friend class ValueBaseUseIterator;
12091184
friend class ConsumingUseIterator;

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/SIL/InstructionUtils.h"
1515
#include "swift/SIL/BasicBlockBits.h"
1616
#include "swift/SIL/NodeBits.h"
17+
#include "swift/SIL/OperandBits.h"
1718
#include "swift/SILOptimizer/Analysis/Analysis.h"
1819
#include "swift/SILOptimizer/PassManager/PassPipeline.h"
1920
#include "swift/SILOptimizer/PassManager/Passes.h"
@@ -73,12 +74,12 @@ class SwiftPassInvocation {
7374

7475
SILSSAUpdater *ssaUpdater = nullptr;
7576

76-
static constexpr int BlockSetCapacity = 16;
77+
static constexpr int BlockSetCapacity = SILBasicBlock::numCustomBits;
7778
char blockSetStorage[sizeof(BasicBlockSet) * BlockSetCapacity];
7879
bool aliveBlockSets[BlockSetCapacity];
7980
int numBlockSetsAllocated = 0;
8081

81-
static constexpr int NodeSetCapacity = 8;
82+
static constexpr int NodeSetCapacity = SILNode::numCustomBits;
8283
char nodeSetStorage[sizeof(NodeSet) * NodeSetCapacity];
8384
bool aliveNodeSets[NodeSetCapacity];
8485
int numNodeSetsAllocated = 0;

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,8 +1402,8 @@ FixedSizeSlab *SwiftPassInvocation::freeSlab(FixedSizeSlab *slab) {
14021402
}
14031403

14041404
BasicBlockSet *SwiftPassInvocation::allocBlockSet() {
1405-
assert(numBlockSetsAllocated < BlockSetCapacity - 1 &&
1406-
"too many BasicBlockSets allocated");
1405+
require(numBlockSetsAllocated < BlockSetCapacity,
1406+
"too many BasicBlockSets allocated");
14071407

14081408
auto *storage = (BasicBlockSet *)blockSetStorage + numBlockSetsAllocated;
14091409
BasicBlockSet *set = new (storage) BasicBlockSet(function);
@@ -1426,8 +1426,8 @@ void SwiftPassInvocation::freeBlockSet(BasicBlockSet *set) {
14261426
}
14271427

14281428
NodeSet *SwiftPassInvocation::allocNodeSet() {
1429-
assert(numNodeSetsAllocated < NodeSetCapacity - 1 &&
1430-
"too many BasicNodeSets allocated");
1429+
require(numNodeSetsAllocated < NodeSetCapacity,
1430+
"too many NodeSets allocated");
14311431

14321432
auto *storage = (NodeSet *)nodeSetStorage + numNodeSetsAllocated;
14331433
NodeSet *set = new (storage) NodeSet(function);

unittests/SIL/SILBitfieldTest.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,20 @@ class BasicBlockBitfield;
2121

2222
struct SILFunction {
2323
BasicBlockBitfield *newestAliveBlockBitfield = nullptr;
24-
int64_t currentBitfieldID = 1;
24+
uint64_t currentBitfieldID = 1;
2525
};
2626

2727
struct SILBasicBlock {
2828
SILFunction *function;
2929
uint32_t customBits = 0;
30-
int64_t lastInitializedBitfieldID = 0;
30+
uint64_t lastInitializedBitfieldID = 0;
3131

3232
enum { numCustomBits = 32 };
33+
enum { maxBitfieldID = std::numeric_limits<uint64_t>::max() };
3334

3435
SILBasicBlock(SILFunction *function): function(function) {}
3536

3637
SILFunction *getFunction() const { return function; }
37-
bool isMarkedAsDeleted() const { return false; }
3838

3939
unsigned getCustomBits() const { return customBits; }
4040
void setCustomBits(unsigned value) { customBits = value; }

0 commit comments

Comments
 (0)