Skip to content

Commit 87af731

Browse files
committed
Add Stack.Marker and Stack.Segment utilities.
Also fix the memory binding within stack access.
1 parent f69dfe4 commit 87af731

File tree

1 file changed

+100
-9
lines changed
  • SwiftCompilerSources/Sources/Optimizer/DataStructures

1 file changed

+100
-9
lines changed

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)