Skip to content

Commit dd3f6ef

Browse files
authored
Merge pull request #70887 from atrick/ownership-liveness
SwiftCompilerSources for ownership, borrowing, and liveness
2 parents a225643 + 24f6ed2 commit dd3f6ef

File tree

13 files changed

+3055
-267
lines changed

13 files changed

+3055
-267
lines changed

SwiftCompilerSources/Sources/Basic/Utils.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,37 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren {
114114
public static func ~=(pattern: StaticString, value: StringRef) -> Bool { value == pattern }
115115
}
116116

117+
//===----------------------------------------------------------------------===//
118+
// Single-Element Inline Array
119+
//===----------------------------------------------------------------------===//
120+
121+
public struct SingleInlineArray<Element>: RandomAccessCollection {
122+
private var singleElement: Element? = nil
123+
private var multipleElements: [Element] = []
124+
125+
public init() {}
126+
127+
public var startIndex: Int { 0 }
128+
public var endIndex: Int {
129+
singleElement == nil ? 0 : multipleElements.count + 1
130+
}
131+
132+
public subscript(_ index: Int) -> Element {
133+
if index == 0 {
134+
return singleElement!
135+
}
136+
return multipleElements[index - 1]
137+
}
138+
139+
public mutating func push(_ element: Element) {
140+
guard singleElement != nil else {
141+
singleElement = element
142+
return
143+
}
144+
multipleElements.append(element)
145+
}
146+
}
147+
117148
//===----------------------------------------------------------------------===//
118149
// Bridging Utilities
119150
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/Optimizer/DataStructures/Stack.swift

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,19 @@ struct Stack<Element> : CollectionLikeSequence {
3535
BridgedPassContext.Slab.getCapacity() / MemoryLayout<Element>.stride
3636
}
3737

38-
private static func bind(_ slab: BridgedPassContext.Slab) -> UnsafeMutablePointer<Element> {
39-
return UnsafeMutableRawPointer(slab.data!).bindMemory(to: Element.self, capacity: Stack.slabCapacity)
38+
private func allocate(after lastSlab: BridgedPassContext.Slab? = nil) -> BridgedPassContext.Slab {
39+
let lastSlab = lastSlab ?? BridgedPassContext.Slab(nil)
40+
let newSlab = bridgedContext.allocSlab(lastSlab)
41+
UnsafeMutableRawPointer(newSlab.data!).bindMemory(to: Element.self, capacity: Stack.slabCapacity)
42+
return newSlab
43+
}
44+
45+
private static func element(in slab: BridgedPassContext.Slab, at index: Int) -> Element {
46+
return pointer(in: slab, at: index).pointee
47+
}
48+
49+
private static func pointer(in slab: BridgedPassContext.Slab, at index: Int) -> UnsafeMutablePointer<Element> {
50+
return UnsafeMutableRawPointer(slab.data!).assumingMemoryBound(to: Element.self) + index
4051
}
4152

4253
struct Iterator : IteratorProtocol {
@@ -50,7 +61,7 @@ struct Stack<Element> : CollectionLikeSequence {
5061

5162
guard index < end else { return nil }
5263

53-
let elem = Stack.bind(slab)[index]
64+
let elem = Stack.element(in: slab, at: index)
5465
index += 1
5566

5667
if index >= end && slab.data != lastSlab.data {
@@ -68,23 +79,23 @@ struct Stack<Element> : CollectionLikeSequence {
6879
}
6980

7081
var first: Element? {
71-
isEmpty ? nil : Stack.bind(firstSlab)[0]
82+
isEmpty ? nil : Stack.element(in: firstSlab, at: 0)
7283
}
7384

7485
var last: Element? {
75-
isEmpty ? nil : Stack.bind(lastSlab)[endIndex &- 1]
86+
isEmpty ? nil : Stack.element(in: lastSlab, at: endIndex &- 1)
7687
}
7788

7889
mutating func push(_ element: Element) {
7990
if endIndex >= Stack.slabCapacity {
80-
lastSlab = bridgedContext.allocSlab(lastSlab)
91+
lastSlab = allocate(after: lastSlab)
8192
endIndex = 0
8293
} else if firstSlab.data == nil {
8394
assert(endIndex == 0)
84-
firstSlab = bridgedContext.allocSlab(lastSlab)
95+
firstSlab = allocate()
8596
lastSlab = firstSlab
8697
}
87-
(Stack.bind(lastSlab) + endIndex).initialize(to: element)
98+
Stack.pointer(in: lastSlab, at: endIndex).initialize(to: element)
8899
endIndex += 1
89100
}
90101

@@ -105,7 +116,7 @@ struct Stack<Element> : CollectionLikeSequence {
105116
}
106117
assert(endIndex > 0)
107118
endIndex -= 1
108-
let elem = (Stack.bind(lastSlab) + endIndex).move()
119+
let elem = Stack.pointer(in: lastSlab, at: endIndex).move()
109120

110121
if endIndex == 0 {
111122
if lastSlab.data == firstSlab.data {
@@ -129,3 +140,83 @@ struct Stack<Element> : CollectionLikeSequence {
129140
/// TODO: once we have move-only types, make this a real deinit.
130141
mutating func deinitialize() { removeAll() }
131142
}
143+
144+
extension Stack {
145+
/// Mark a stack location for future iteration.
146+
///
147+
/// TODO: Marker should be ~Escapable.
148+
struct Marker {
149+
let slab: BridgedPassContext.Slab
150+
let index: Int
151+
}
152+
153+
var top: Marker { Marker(slab: lastSlab, index: endIndex) }
154+
155+
struct Segment : CollectionLikeSequence {
156+
let low: Marker
157+
let high: Marker
158+
159+
init(in stack: Stack, low: Marker, high: Marker) {
160+
if low.slab.data == nil {
161+
assert(low.index == 0, "invalid empty stack marker")
162+
// `low == nil` and `high == nil` is a valid empty segment,
163+
// even though `assertValid(marker:)` would return false.
164+
if high.slab.data != nil {
165+
stack.assertValid(marker: high)
166+
}
167+
self.low = Marker(slab: stack.firstSlab, index: 0)
168+
self.high = high
169+
return
170+
}
171+
stack.assertValid(marker: low)
172+
stack.assertValid(marker: high)
173+
self.low = low
174+
self.high = high
175+
}
176+
177+
func makeIterator() -> Stack.Iterator {
178+
return Iterator(slab: low.slab, index: low.index,
179+
lastSlab: high.slab, endIndex: high.index)
180+
}
181+
}
182+
183+
/// Assert that `marker` is valid based on the current `top`.
184+
///
185+
/// This is an assert rather than a query because slabs can reuse
186+
/// memory leading to a stale marker that appears valid.
187+
func assertValid(marker: Marker) {
188+
var currentSlab = lastSlab
189+
var currentIndex = endIndex
190+
while currentSlab.data != marker.slab.data {
191+
assert(currentSlab.data != firstSlab.data, "Invalid stack marker")
192+
currentSlab = currentSlab.getPrevious()
193+
currentIndex = Stack.slabCapacity
194+
}
195+
assert(marker.index <= currentIndex, "Invalid stack marker")
196+
}
197+
198+
/// Execute the `body` closure, passing it `self` for further
199+
/// mutation of the stack and passing `marker` to mark the stack
200+
/// position prior to executing `body`. `marker` must not escape the
201+
/// `body` closure.
202+
mutating func withMarker<R>(
203+
_ body: (inout Stack<Element>, Marker) throws -> R) rethrows -> R {
204+
return try body(&self, top)
205+
}
206+
207+
/// Record a stack marker, execute a `body` closure, then execute a
208+
/// `handleNewElements` closure with the Segment that contains all
209+
/// elements that remain on the stack after being pushed on the
210+
/// stack while executing `body`. `body` must push more elements
211+
/// than it pops.
212+
mutating func withMarker<R>(
213+
pushElements body: (inout Stack) throws -> R,
214+
withNewElements handleNewElements: ((Segment) -> ())
215+
) rethrows -> R {
216+
return try withMarker { (stack: inout Stack<Element>, marker: Marker) in
217+
let result = try body(&stack)
218+
handleNewElements(Segment(in: stack, low: marker, high: stack.top))
219+
return result
220+
}
221+
}
222+
}

0 commit comments

Comments
 (0)