Skip to content

Commit f87fc5a

Browse files
authored
Merge pull request #40805 from eeckstein/swift-sil-infrastructure
Add swift SIL/Optimizer infrastructure
2 parents d6e0a91 + 82ad1fa commit f87fc5a

38 files changed

+1073
-157
lines changed

SwiftCompilerSources/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,6 @@ Some performance considerations:
169169

170170
* Memory is managed on the C++ side. On the Swift side, SIL objects are treated as "immortal" objects, which avoids (most of) ARC overhead. ARC runtime functions are still being called, but no atomic reference counting operations are done. In future we could add a compiler feature to mark classes as immortal to avoid the runtime calls at all.
171171

172-
* Minimizing memory allocations by using data structures which are malloc-free. For example `StackList` can be used in optimizations to implement work lists without any memory allocations. (Not yet done: `BasicBlockSet`, `BasicBlockData`)
172+
* Minimizing memory allocations by using data structures which are malloc-free, e.g. `Stack`
173173

174174
* The Swift modules are compiled with `-cross-module-optimization`. This enables the compiler to optimize the Swift code across module boundaries.

SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt

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

99
swift_compiler_sources(Optimizer
1010
AliasAnalysis.swift
11-
CalleeAnalysis.swift)
11+
CalleeAnalysis.swift
12+
DeadEndBlocksAnalysis.swift
13+
DominatorTree.swift
14+
PostDominatorTree.swift)

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ public struct CalleeAnalysis {
1919
public func getCallees(callee: Value) -> FunctionArray {
2020
return FunctionArray(bridged: CalleeAnalysis_getCallees(bridged, callee.bridged))
2121
}
22+
23+
public func getDestructors(destroyInst: Instruction) -> FunctionArray {
24+
return FunctionArray(bridged:
25+
CalleeAnalysis_getInstCallees(bridged, destroyInst.bridged))
26+
}
2227
}
2328

24-
public struct FunctionArray : RandomAccessCollection, CustomReflectable {
29+
public struct FunctionArray : RandomAccessCollection, FormattedLikeArray {
2530
fileprivate let bridged: BridgedCalleeList
2631

2732
public var startIndex: Int { 0 }
@@ -32,9 +37,4 @@ public struct FunctionArray : RandomAccessCollection, CustomReflectable {
3237
}
3338

3439
public var allCalleesKnown: Bool { bridged.incomplete == 0 }
35-
36-
public var customMirror: Mirror {
37-
let c: [Mirror.Child] = map { (label: nil, value: $0.name) }
38-
return Mirror(self, children: c)
39-
}
4040
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===--- DeadEndBlocksAnalysis.swift - the dead-end blocks analysis -------===//
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+
import OptimizerBridging
14+
import SIL
15+
16+
public struct DeadEndBlocksAnalysis {
17+
let bridged: BridgedDeadEndBlocksAnalysis
18+
19+
public func isDeadEnd(_ block: BasicBlock) -> Bool {
20+
return DeadEndBlocksAnalysis_isDeadEnd(bridged, block.bridged) != 0
21+
}
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- DominatorTree.swift - the dominator tree -------------------------===//
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+
public struct DominatorTree {
17+
let bridged: BridgedDomTree
18+
}
19+
20+
extension BasicBlock {
21+
func dominates(_ other: BasicBlock, _ domTree: DominatorTree) -> Bool {
22+
DominatorTree_dominates(domTree.bridged, self.bridged, other.bridged) != 0
23+
}
24+
25+
func strictlyDominates(_ other: BasicBlock, _ domTree: DominatorTree) -> Bool {
26+
dominates(other, domTree) && self != other
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- PostDominatorTree.swift - the post dominator tree ----------------===//
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+
public struct PostDominatorTree {
17+
let bridged: BridgedPostDomTree
18+
}
19+
20+
extension BasicBlock {
21+
func postDominates(_ other: BasicBlock, _ pdomTree: PostDominatorTree) -> Bool {
22+
PostDominatorTree_postDominates(pdomTree.bridged, self.bridged, other.bridged) != 0
23+
}
24+
25+
func strictlyPostDominates(_ other: BasicBlock, _ pdomTree: PostDominatorTree) -> Bool {
26+
postDominates(other, pdomTree) && self != other
27+
}
28+
}

SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ endif()
1515
add_swift_compiler_module(Optimizer DEPENDS ${dependencies})
1616

1717
add_subdirectory(Analysis)
18+
add_subdirectory(DataStructures)
1819
add_subdirectory(InstructionPasses)
1920
add_subdirectory(PassManager)
2021
add_subdirectory(FunctionPasses)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===--- BasicBlockRange.swift - a range of basic blocks ------------------===//
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+
15+
/// A range of basic blocks.
16+
///
17+
/// The `BasicBlockRange` defines a range from a dominating "begin" block to one or more "end" blocks.
18+
/// The range is "exclusive", which means that the "end" blocks are not part of the range.
19+
///
20+
/// The `BasicBlockRange` is in the same spirit as a linear range, but as the control flow is a graph
21+
/// and not a linear list, there can be "exit" blocks from within the range.
22+
///
23+
/// One or more "potential" end blocks can be inserted.
24+
/// Though, not all inserted blocks end up as "end" blocks.
25+
///
26+
/// There are several kind of blocks:
27+
/// * begin: it is a single block which dominates all blocks of the range
28+
/// * range: all blocks from which there is a path from the begin block to any of the end blocks
29+
/// * ends: all inserted blocks which are at the end of the range
30+
/// * exits: all successor blocks of range blocks which are not in the range themselves
31+
/// * interiors: all inserted blocks which are not end blocks.
32+
///
33+
/// In the following example, let's assume `B` is the begin block and `I1`, `I2` and `I3`
34+
/// were inserted as potential end blocks:
35+
///
36+
/// B
37+
/// / \
38+
/// I1 I2
39+
/// / \
40+
/// I3 X
41+
///
42+
/// Then `I1` and `I3` are "end" blocks. `I2` is an interior block and `X` is an exit block.
43+
/// The range consists of `B` and `I2`. Note that the range does not include `I1` and `I3`
44+
/// because it's an _exclusive_ range.
45+
///
46+
/// This type should be a move-only type, but unfortunately we don't have move-only
47+
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
48+
/// destruct this data structure, e.g. in a `defer {}` block.
49+
struct BasicBlockRange : CustomStringConvertible, CustomReflectable {
50+
51+
/// The dominating begin block.
52+
let begin: BasicBlock
53+
54+
/// The exclusive range, i.e. not containing the end blocks.
55+
private(set) var range: Stack<BasicBlock>
56+
57+
/// All inserted blocks.
58+
private(set) var inserted: Stack<BasicBlock>
59+
60+
private var insertedSet: BasicBlockSet
61+
private var worklist: BasicBlockWorklist
62+
63+
init(begin: BasicBlock, _ context: PassContext) {
64+
self.begin = begin
65+
self.range = Stack(context)
66+
self.inserted = Stack(context)
67+
self.insertedSet = BasicBlockSet(context)
68+
self.worklist = BasicBlockWorklist(context)
69+
}
70+
71+
/// Insert a potential end block.
72+
mutating func insert(_ block: BasicBlock) {
73+
if !insertedSet.contains(block) {
74+
insertedSet.insert(block)
75+
inserted.append(block)
76+
}
77+
if block != begin {
78+
worklist.pushIfNotVisited(contentsOf: block.predecessors)
79+
80+
while let b = worklist.pop() {
81+
range.append(b)
82+
if b != begin {
83+
worklist.pushIfNotVisited(contentsOf: b.predecessors)
84+
}
85+
}
86+
}
87+
}
88+
89+
/// Returns true if the exclusive range contains `block`.
90+
func contains(_ block: BasicBlock) -> Bool { worklist.hasBeenPushed(block) }
91+
92+
/// Returns true if the inclusive range contains `block`.
93+
func inclusiveRangeContains (_ block: BasicBlock) -> Bool {
94+
contains(block) || insertedSet.contains(block)
95+
}
96+
97+
/// Returns true if the range is valid and that's iff the begin block dominates all blocks of the range.
98+
var isValid: Bool {
99+
let entry = begin.function.entryBlock
100+
return begin == entry ||
101+
// If any block in the range is not dominated by `begin`, the range propagates back to the entry block.
102+
!inclusiveRangeContains(entry)
103+
}
104+
105+
/// Returns the end blocks.
106+
var ends: LazyFilterSequence<Stack<BasicBlock>> {
107+
inserted.lazy.filter { !worklist.hasBeenPushed($0) }
108+
}
109+
110+
/// Returns the exit blocks.
111+
var exits: LazySequence<FlattenSequence<
112+
LazyMapSequence<Stack<BasicBlock>,
113+
LazyFilterSequence<SuccessorArray>>>> {
114+
range.lazy.flatMap {
115+
$0.successors.lazy.filter {
116+
!inclusiveRangeContains($0) || $0 == begin
117+
}
118+
}
119+
}
120+
121+
/// Returns the interior blocks.
122+
var interiors: LazyFilterSequence<Stack<BasicBlock>> {
123+
inserted.lazy.filter { contains($0) && $0 != begin }
124+
}
125+
126+
var description: String {
127+
"""
128+
begin: \(begin.name)
129+
range: \(range)
130+
ends: \(ends)
131+
exits: \(exits)
132+
interiors: \(interiors)
133+
"""
134+
}
135+
136+
var customMirror: Mirror { Mirror(self, children: []) }
137+
138+
/// TODO: once we have move-only types, make this a real deinit.
139+
mutating func deinitialize() {
140+
worklist.deinitialize()
141+
inserted.deinitialize()
142+
insertedSet.deinitialize()
143+
range.deinitialize()
144+
}
145+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//===--- BasicBlockSet.swift - a set of basic blocks ----------------------===//
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+
}

0 commit comments

Comments
 (0)