Skip to content

Commit 83b2593

Browse files
authored
Merge pull request #72485 from eeckstein/node-bits
Some improvements for inline bitfields in SILNode, SILBasicBlock and Operand
2 parents 42991a5 + 367dc1a commit 83b2593

File tree

16 files changed

+241
-100
lines changed

16 files changed

+241
-100
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,55 @@ struct InstructionSet : IntrusiveSet {
176176
context.freeNodeSet(bridged)
177177
}
178178
}
179+
180+
/// A set of operands.
181+
///
182+
/// This is an extremely efficient implementation which does not need memory
183+
/// allocations or hash lookups.
184+
///
185+
/// This type should be a move-only type, but unfortunately we don't have move-only
186+
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
187+
/// destruct this data structure, e.g. in a `defer {}` block.
188+
struct OperandSet : IntrusiveSet {
189+
190+
private let context: BridgedPassContext
191+
private let bridged: BridgedOperandSet
192+
193+
init(_ context: some Context) {
194+
self.context = context._bridged
195+
self.bridged = self.context.allocOperandSet()
196+
}
197+
198+
func contains(_ operand: Operand) -> Bool {
199+
bridged.contains(operand.bridged)
200+
}
201+
202+
/// Returns true if `inst` was not contained in the set before inserting.
203+
@discardableResult
204+
mutating func insert(_ operand: Operand) -> Bool {
205+
bridged.insert(operand.bridged)
206+
}
207+
208+
mutating func erase(_ operand: Operand) {
209+
bridged.erase(operand.bridged)
210+
}
211+
212+
var description: String {
213+
let function = bridged.getFunction().function
214+
var d = "{\n"
215+
for inst in function.instructions {
216+
for op in inst.operands {
217+
if contains(op) {
218+
d += op.description
219+
}
220+
}
221+
}
222+
d += "}\n"
223+
return d
224+
}
225+
226+
/// TODO: once we have move-only types, make this a real deinit.
227+
mutating func deinitialize() {
228+
context.freeOperandSet(bridged)
229+
}
230+
}

SwiftCompilerSources/Sources/Optimizer/DataStructures/Worklist.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,4 @@ struct Worklist<Set: IntrusiveSet> : CustomStringConvertible, NoReflectionChildr
7575
typealias BasicBlockWorklist = Worklist<BasicBlockSet>
7676
typealias InstructionWorklist = Worklist<InstructionSet>
7777
typealias ValueWorklist = Worklist<ValueSet>
78+
typealias OperandWorklist = Worklist<OperandSet>

SwiftCompilerSources/Sources/SIL/Operand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SILBridging
1414

1515
/// An operand of an instruction.
1616
public struct Operand : CustomStringConvertible, NoReflectionChildren {
17-
fileprivate let bridged: BridgedOperand
17+
public let bridged: BridgedOperand
1818

1919
public init(bridged: BridgedOperand) {
2020
self.bridged = bridged

include/swift/Basic/Require.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- Require.h ----------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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 swift_unreachable, which provides the
14+
// functionality of llvm_unreachable without necessarily depending on
15+
// the LLVM support libraries.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_BASIC_REQUIRE_H
20+
#define SWIFT_BASIC_REQUIRE_H
21+
22+
#include "llvm/Support/raw_ostream.h"
23+
24+
/// Checks the `condition` and if it's false abort with `message`.
25+
/// In contrast to `assert` and similar functions, `require` works in
26+
/// no-assert builds the same way as in debug builds.
27+
inline void require(bool condition, const char *message) {
28+
if (!condition) {
29+
llvm::errs() << message << '\n';
30+
abort();
31+
}
32+
}
33+
34+
#endif

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);

0 commit comments

Comments
 (0)