Skip to content

Commit 49a5b3e

Browse files
committed
Swift SIL: add ValueSet and InstructionSet utilities.
These sets are _much_ more efficient than `Set<Value>` and `Set<Instruction>` because they bridge to the efficient `NodeSet`. Insertions/deletions are just bit operations.
1 parent 6760dc4 commit 49a5b3e

File tree

5 files changed

+262
-2
lines changed

5 files changed

+262
-2
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
swift_compiler_sources(Optimizer
1010
BasicBlockRange.swift
11-
BasicBlockSet.swift
1211
BasicBlockWorklist.swift
1312
InstructionRange.swift
13+
Set.swift
1414
Stack.swift)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//===--- Set.swift - sets for basic blocks, values and instructions -------===//
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+
import SIL
14+
import OptimizerBridging
15+
16+
/// A set of basic blocks.
17+
///
18+
/// This is an extremely efficient implementation which does not need memory
19+
/// allocations or hash lookups.
20+
///
21+
/// This type should be a move-only type, but unfortunately we don't have move-only
22+
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
23+
/// destruct this data structure, e.g. in a `defer {}` block.
24+
struct BasicBlockSet : CustomStringConvertible, CustomReflectable {
25+
26+
private let context: PassContext
27+
private let bridged: BridgedBasicBlockSet
28+
29+
init(_ context: PassContext) {
30+
self.context = context
31+
self.bridged = PassContext_allocBasicBlockSet(context._bridged)
32+
}
33+
34+
func contains(_ block: BasicBlock) -> Bool {
35+
BasicBlockSet_contains(bridged, block.bridged) != 0
36+
}
37+
38+
mutating func insert(_ block: BasicBlock) {
39+
BasicBlockSet_insert(bridged, block.bridged)
40+
}
41+
42+
mutating func erase(_ block: BasicBlock) {
43+
BasicBlockSet_erase(bridged, block.bridged)
44+
}
45+
46+
var description: String {
47+
let function = BasicBlockSet_getFunction(bridged).function
48+
let blockNames = function.blocks.enumerated().filter { contains($0.1) }
49+
.map { "bb\($0.0)"}
50+
return "{" + blockNames.joined(separator: ", ") + "}"
51+
}
52+
53+
var customMirror: Mirror { Mirror(self, children: []) }
54+
55+
/// TODO: once we have move-only types, make this a real deinit.
56+
mutating func deinitialize() {
57+
PassContext_freeBasicBlockSet(context._bridged, bridged)
58+
}
59+
}
60+
61+
/// A set of values.
62+
///
63+
/// This is an extremely efficient implementation which does not need memory
64+
/// allocations or hash lookups.
65+
///
66+
/// This type should be a move-only type, but unfortunately we don't have move-only
67+
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
68+
/// destruct this data structure, e.g. in a `defer {}` block.
69+
struct ValueSet : CustomStringConvertible, CustomReflectable {
70+
71+
private let context: PassContext
72+
private let bridged: BridgedNodeSet
73+
74+
init(_ context: PassContext) {
75+
self.context = context
76+
self.bridged = PassContext_allocNodeSet(context._bridged)
77+
}
78+
79+
func contains(_ value: Value) -> Bool {
80+
NodeSet_containsValue(bridged, value.bridged) != 0
81+
}
82+
83+
mutating func insert(_ value: Value) {
84+
NodeSet_insertValue(bridged, value.bridged)
85+
}
86+
87+
mutating func erase(_ value: Value) {
88+
NodeSet_eraseValue(bridged, value.bridged)
89+
}
90+
91+
var description: String {
92+
let function = NodeSet_getFunction(bridged).function
93+
var d = "{\n"
94+
for block in function.blocks {
95+
for arg in block.arguments {
96+
if contains(arg) {
97+
d += arg.description
98+
}
99+
}
100+
for inst in block.instructions {
101+
for result in inst.results {
102+
if contains(result) {
103+
d += result.description
104+
}
105+
}
106+
}
107+
}
108+
d += "}\n"
109+
return d
110+
}
111+
112+
var customMirror: Mirror { Mirror(self, children: []) }
113+
114+
/// TODO: once we have move-only types, make this a real deinit.
115+
mutating func deinitialize() {
116+
PassContext_freeNodeSet(context._bridged, bridged)
117+
}
118+
}
119+
120+
/// A set of instructions.
121+
///
122+
/// This is an extremely efficient implementation which does not need memory
123+
/// allocations or hash lookups.
124+
///
125+
/// This type should be a move-only type, but unfortunately we don't have move-only
126+
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
127+
/// destruct this data structure, e.g. in a `defer {}` block.
128+
struct InstructionSet : CustomStringConvertible, CustomReflectable {
129+
130+
private let context: PassContext
131+
private let bridged: BridgedNodeSet
132+
133+
init(_ context: PassContext) {
134+
self.context = context
135+
self.bridged = PassContext_allocNodeSet(context._bridged)
136+
}
137+
138+
func contains(_ inst: Instruction) -> Bool {
139+
NodeSet_containsInstruction(bridged, inst.bridged) != 0
140+
}
141+
142+
mutating func insert(_ inst: Instruction) {
143+
NodeSet_insertInstruction(bridged, inst.bridged)
144+
}
145+
146+
mutating func erase(_ inst: Instruction) {
147+
NodeSet_eraseInstruction(bridged, inst.bridged)
148+
}
149+
150+
var description: String {
151+
let function = NodeSet_getFunction(bridged).function
152+
var d = "{\n"
153+
for block in function.blocks {
154+
for inst in block.instructions {
155+
if contains(inst) {
156+
d += inst.description
157+
}
158+
}
159+
}
160+
d += "}\n"
161+
return d
162+
}
163+
164+
var customMirror: Mirror { Mirror(self, children: []) }
165+
166+
/// TODO: once we have move-only types, make this a real deinit.
167+
mutating func deinitialize() {
168+
PassContext_freeNodeSet(context._bridged, bridged)
169+
}
170+
}

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ typedef struct {
6161
void * _Nullable bbs;
6262
} BridgedBasicBlockSet;
6363

64+
typedef struct {
65+
void * _Nullable nds;
66+
} BridgedNodeSet;
67+
6468
typedef struct {
6569
void * _Nullable data;
6670
} BridgedSlab;
@@ -139,6 +143,17 @@ void BasicBlockSet_insert(BridgedBasicBlockSet set, BridgedBasicBlock block);
139143
void BasicBlockSet_erase(BridgedBasicBlockSet set, BridgedBasicBlock block);
140144
BridgedFunction BasicBlockSet_getFunction(BridgedBasicBlockSet set);
141145

146+
BridgedNodeSet PassContext_allocNodeSet(BridgedPassContext context);
147+
void PassContext_freeNodeSet(BridgedPassContext context,
148+
BridgedNodeSet set);
149+
SwiftInt NodeSet_containsValue(BridgedNodeSet set, BridgedValue value);
150+
void NodeSet_insertValue(BridgedNodeSet set, BridgedValue value);
151+
void NodeSet_eraseValue(BridgedNodeSet set, BridgedValue value);
152+
SwiftInt NodeSet_containsInstruction(BridgedNodeSet set, BridgedInstruction inst);
153+
void NodeSet_insertInstruction(BridgedNodeSet set, BridgedInstruction inst);
154+
void NodeSet_eraseInstruction(BridgedNodeSet set, BridgedInstruction inst);
155+
BridgedFunction NodeSet_getFunction(BridgedNodeSet set);
156+
142157
void AllocRefInstBase_setIsStackAllocatable(BridgedInstruction arb);
143158

144159
BridgedSubstitutionMap

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/SIL/Notifications.h"
1414
#include "swift/SIL/InstructionUtils.h"
1515
#include "swift/SIL/BasicBlockBits.h"
16+
#include "swift/SIL/NodeBits.h"
1617
#include "swift/SILOptimizer/Analysis/Analysis.h"
1718
#include "swift/SILOptimizer/PassManager/PassPipeline.h"
1819
#include "swift/SILOptimizer/PassManager/Passes.h"
@@ -65,9 +66,13 @@ class SwiftPassInvocation {
6566
static constexpr int BlockSetCapacity = 8;
6667
char blockSetStorage[sizeof(BasicBlockSet) * BlockSetCapacity];
6768
bool aliveBlockSets[BlockSetCapacity];
68-
6969
int numBlockSetsAllocated = 0;
7070

71+
static constexpr int NodeSetCapacity = 8;
72+
char nodeSetStorage[sizeof(NodeSet) * NodeSetCapacity];
73+
bool aliveNodeSets[NodeSetCapacity];
74+
int numNodeSetsAllocated = 0;
75+
7176
void endPassRunChecks();
7277

7378
public:
@@ -92,6 +97,10 @@ class SwiftPassInvocation {
9297

9398
void freeBlockSet(BasicBlockSet *set);
9499

100+
NodeSet *allocNodeSet();
101+
102+
void freeNodeSet(NodeSet *set);
103+
95104
/// The top-level API to erase an instruction, called from the Swift pass.
96105
void eraseInstruction(SILInstruction *inst);
97106

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,30 @@ void SwiftPassInvocation::freeBlockSet(BasicBlockSet *set) {
12101210
}
12111211
}
12121212

1213+
NodeSet *SwiftPassInvocation::allocNodeSet() {
1214+
assert(numNodeSetsAllocated < NodeSetCapacity - 1 &&
1215+
"too many BasicNodeSets allocated");
1216+
1217+
auto *storage = (NodeSet *)nodeSetStorage + numNodeSetsAllocated;
1218+
NodeSet *set = new (storage) NodeSet(function);
1219+
aliveNodeSets[numNodeSetsAllocated] = true;
1220+
++numNodeSetsAllocated;
1221+
return set;
1222+
}
1223+
1224+
void SwiftPassInvocation::freeNodeSet(NodeSet *set) {
1225+
int idx = set - (NodeSet *)nodeSetStorage;
1226+
assert(idx >= 0 && idx < numNodeSetsAllocated);
1227+
assert(aliveNodeSets[idx] && "double free of NodeSet");
1228+
aliveNodeSets[idx] = false;
1229+
1230+
while (numNodeSetsAllocated > 0 && !aliveNodeSets[numNodeSetsAllocated - 1]) {
1231+
auto *set = (NodeSet *)nodeSetStorage + numNodeSetsAllocated - 1;
1232+
set->~NodeSet();
1233+
--numNodeSetsAllocated;
1234+
}
1235+
}
1236+
12131237
void SwiftPassInvocation::startFunctionPassRun(SILFunctionTransform *transform) {
12141238
assert(!this->function && !this->transform && "a pass is already running");
12151239
this->function = transform->getFunction();
@@ -1235,6 +1259,7 @@ void SwiftPassInvocation::finishedInstructionPassRun() {
12351259
void SwiftPassInvocation::endPassRunChecks() {
12361260
assert(allocatedSlabs.empty() && "StackList is leaking slabs");
12371261
assert(numBlockSetsAllocated == 0 && "Not all BasicBlockSets deallocated");
1262+
assert(numNodeSetsAllocated == 0 && "Not all NodeSets deallocated");
12381263
}
12391264

12401265
//===----------------------------------------------------------------------===//
@@ -1265,6 +1290,10 @@ inline BasicBlockSet *castToBlockSet(BridgedBasicBlockSet blockSet) {
12651290
return static_cast<BasicBlockSet *>(blockSet.bbs);
12661291
}
12671292

1293+
inline NodeSet *castToNodeSet(BridgedNodeSet nodeSet) {
1294+
return static_cast<NodeSet *>(nodeSet.nds);
1295+
}
1296+
12681297
BridgedSlab PassContext_getNextSlab(BridgedSlab slab) {
12691298
return toBridgedSlab(&*std::next(castToSlab(slab)->getIterator()));
12701299
}
@@ -1404,6 +1433,43 @@ BridgedFunction BasicBlockSet_getFunction(BridgedBasicBlockSet set) {
14041433
return {castToBlockSet(set)->getFunction()};
14051434
}
14061435

1436+
BridgedNodeSet PassContext_allocNodeSet(BridgedPassContext context) {
1437+
return {castToPassInvocation(context)->allocNodeSet()};
1438+
}
1439+
1440+
void PassContext_freeNodeSet(BridgedPassContext context,
1441+
BridgedNodeSet set) {
1442+
castToPassInvocation(context)->freeNodeSet(castToNodeSet(set));
1443+
}
1444+
1445+
SwiftInt NodeSet_containsValue(BridgedNodeSet set, BridgedValue value) {
1446+
return castToNodeSet(set)->contains(castToSILValue(value)) ? 1 : 0;
1447+
}
1448+
1449+
void NodeSet_insertValue(BridgedNodeSet set, BridgedValue value) {
1450+
castToNodeSet(set)->insert(castToSILValue(value));
1451+
}
1452+
1453+
void NodeSet_eraseValue(BridgedNodeSet set, BridgedValue value) {
1454+
castToNodeSet(set)->erase(castToSILValue(value));
1455+
}
1456+
1457+
SwiftInt NodeSet_containsInstruction(BridgedNodeSet set, BridgedInstruction inst) {
1458+
return castToNodeSet(set)->contains(castToInst(inst)->asSILNode()) ? 1 : 0;
1459+
}
1460+
1461+
void NodeSet_insertInstruction(BridgedNodeSet set, BridgedInstruction inst) {
1462+
castToNodeSet(set)->insert(castToInst(inst)->asSILNode());
1463+
}
1464+
1465+
void NodeSet_eraseInstruction(BridgedNodeSet set, BridgedInstruction inst) {
1466+
castToNodeSet(set)->erase(castToInst(inst)->asSILNode());
1467+
}
1468+
1469+
BridgedFunction NodeSet_getFunction(BridgedNodeSet set) {
1470+
return {castToNodeSet(set)->getFunction()};
1471+
}
1472+
14071473
void AllocRefInstBase_setIsStackAllocatable(BridgedInstruction arb) {
14081474
castToInst<AllocRefInstBase>(arb)->setStackAllocatable();
14091475
}

0 commit comments

Comments
 (0)