forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBasicBlockRange.swift
160 lines (142 loc) · 5.45 KB
/
BasicBlockRange.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//===--- BasicBlockRange.swift - a range of basic blocks ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SIL
/// A range of basic blocks.
///
/// The `BasicBlockRange` defines a range from a dominating "begin" block to one or more "end" blocks.
/// The range is "exclusive", which means that the "end" blocks are not part of the range.
///
/// The `BasicBlockRange` is in the same spirit as a linear range, but as the control flow is a graph
/// and not a linear list, there can be "exit" blocks from within the range.
///
/// One or more "potential" end blocks can be inserted.
/// Though, not all inserted blocks end up as "end" blocks.
///
/// There are several kind of blocks:
/// * begin: it is a single block which dominates all blocks of the range
/// * range: all blocks from which there is a path from the begin block to any of the end blocks
/// * ends: all inserted blocks which are at the end of the range
/// * exits: all successor blocks of range blocks which are not in the range themselves
/// * interiors: all inserted blocks which are not end blocks.
///
/// In the following example, let's assume `B` is the begin block and `I1`, `I2` and `I3`
/// were inserted as potential end blocks:
///
/// B
/// / \
/// I1 I2
/// / \
/// I3 X
///
/// Then `I1` and `I3` are "end" blocks. `I2` is an interior block and `X` is an exit block.
/// The range consists of `B` and `I2`. Note that the range does not include `I1` and `I3`
/// because it's an _exclusive_ range.
///
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
/// The dominating begin block.
let begin: BasicBlock
/// The inclusive range, i.e. the exclusive range plus the end blocks.
private(set) var inclusiveRange: Stack<BasicBlock>
/// The exclusive range, i.e. not containing the end blocks.
var range: LazyFilterSequence<Stack<BasicBlock>> {
inclusiveRange.lazy.filter { contains($0) }
}
/// All inserted blocks.
private(set) var inserted: Stack<BasicBlock>
private var wasInserted: BasicBlockSet
private var inExclusiveRange: BasicBlockSet
private var worklist: BasicBlockWorklist
init(begin: BasicBlock, _ context: some Context) {
self.begin = begin
self.inclusiveRange = Stack(context)
self.inserted = Stack(context)
self.wasInserted = BasicBlockSet(context)
self.inExclusiveRange = BasicBlockSet(context)
self.worklist = BasicBlockWorklist(context)
worklist.pushIfNotVisited(begin)
}
/// Insert a potential end block.
mutating func insert(_ block: BasicBlock) {
if wasInserted.insert(block) {
inserted.append(block)
}
worklist.pushIfNotVisited(block)
while let b = worklist.pop() {
inclusiveRange.append(b)
if b != begin {
for pred in b.predecessors {
worklist.pushIfNotVisited(pred)
inExclusiveRange.insert(pred)
}
}
}
}
/// Insert a sequence of potential end blocks.
mutating func insert<S: Sequence>(contentsOf other: S) where S.Element == BasicBlock {
for block in other {
insert(block)
}
}
/// Returns true if the exclusive range contains `block`.
func contains(_ block: BasicBlock) -> Bool { inExclusiveRange.contains(block) }
/// Returns true if the inclusive range contains `block`.
func inclusiveRangeContains (_ block: BasicBlock) -> Bool {
worklist.hasBeenPushed(block)
}
/// Returns true if the range is valid and that's iff the begin block dominates all blocks of the range.
var isValid: Bool {
let entry = begin.parentFunction.entryBlock
return begin == entry ||
// If any block in the range is not dominated by `begin`, the range propagates back to the entry block.
!inclusiveRangeContains(entry)
}
/// Returns the end blocks.
var ends: LazyFilterSequence<Stack<BasicBlock>> {
inserted.lazy.filter { !contains($0) }
}
/// Returns the exit blocks.
var exits: LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
LazyFilterSequence<SuccessorArray>>>> {
range.flatMap {
$0.successors.lazy.filter {
!inclusiveRangeContains($0) || $0 == begin
}
}
}
/// Returns the interior blocks.
var interiors: LazyFilterSequence<Stack<BasicBlock>> {
inserted.lazy.filter { contains($0) && $0 != begin }
}
var description: String {
return (isValid ? "" : "<invalid>\n") +
"""
begin: \(begin.name)
range: \(range)
inclrange: \(inclusiveRange)
ends: \(ends)
exits: \(exits)
interiors: \(interiors)
"""
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
worklist.deinitialize()
inExclusiveRange.deinitialize()
wasInserted.deinitialize()
inserted.deinitialize()
inclusiveRange.deinitialize()
}
}