Skip to content

Commit 72038b5

Browse files
committed
stdlib: change the representation of UnsafeBufferPointer from start+end pointers to start-pointer + count.
This saves a few instructions for some operations, like getting the count. Also, it avoids the check for unwrapping the optional end pointer. For example, iterating over an unsafe buffer now has no overhead. Also remove the _unboundedStartingAt initializer, which is not needed anymore.
1 parent a2b0e8f commit 72038b5

File tree

2 files changed

+75
-34
lines changed

2 files changed

+75
-34
lines changed

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,15 @@
3434
// struct A { struct B { let x: UnsafeMutableBufferPointer<...> } let b: B }
3535
@_fixed_layout
3636
public struct Unsafe${Mutable}BufferPointer<Element> {
37+
3738
@usableFromInline
38-
let _position, _end: Unsafe${Mutable}Pointer<Element>?
39+
let _position: Unsafe${Mutable}Pointer<Element>?
40+
41+
/// The number of elements in the buffer.
42+
///
43+
/// If the `baseAddress` of this buffer is `nil`, the count is zero. However,
44+
/// a buffer can have a `count` of zero even with a non-`nil` base address.
45+
public let count: Int
3946
}
4047

4148
%if not mutable:
@@ -62,10 +69,15 @@ extension UnsafeBufferPointer.Iterator: IteratorProtocol {
6269
/// Once `nil` has been returned, all subsequent calls return `nil`.
6370
@inlinable
6471
public mutating func next() -> Element? {
65-
if _position == _end { return nil }
72+
guard let start = _position else {
73+
return nil
74+
}
75+
_sanityCheck(_end != nil, "inconsistent _position, _end pointers")
76+
77+
if start == _end._unsafelyUnwrappedUnchecked { return nil }
6678

67-
let result = _position!.pointee
68-
_position! += 1
79+
let result = start.pointee
80+
_position = start + 1
6981
return result
7082
}
7183
}
@@ -81,7 +93,10 @@ extension Unsafe${Mutable}BufferPointer: Sequence {
8193
/// - Returns: An iterator over the elements of this buffer.
8294
@inlinable
8395
public func makeIterator() -> Iterator {
84-
return Iterator(_position: _position, _end: _end)
96+
guard let start = _position else {
97+
return Iterator(_position: nil, _end: nil)
98+
}
99+
return Iterator(_position: start, _end: start + count)
85100
}
86101

87102
/// Initializes the memory at `destination.baseAddress` with elements of `self`,
@@ -98,7 +113,7 @@ extension Unsafe${Mutable}BufferPointer: Sequence {
98113
let d = destination.baseAddress._unsafelyUnwrappedUnchecked
99114
let n = Swift.min(destination.count, self.count)
100115
d.initialize(from: s, count: n)
101-
return (Iterator(_position: s + n, _end: _end), n)
116+
return (Iterator(_position: s + n, _end: s + count), n)
102117
}
103118
}
104119

@@ -214,19 +229,6 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle
214229
return startIndex..<endIndex
215230
}
216231

217-
/// The number of elements in the buffer.
218-
///
219-
/// If the `baseAddress` of this buffer is `nil`, the count is zero. However,
220-
/// a buffer can have a `count` of zero even with a non-`nil` base address.
221-
@inlinable
222-
public var count: Int {
223-
guard let start = _position, let end = _end else {
224-
return 0
225-
}
226-
227-
return end - start
228-
}
229-
230232
/// Accesses the element at the specified position.
231233
///
232234
%if Mutable:
@@ -385,21 +387,13 @@ extension Unsafe${Mutable}BufferPointer {
385387
count == 0 || start != nil,
386388
"Unsafe${Mutable}BufferPointer has a nil start and nonzero count")
387389
_position = start
388-
_end = start.map { $0 + count }
389-
}
390-
391-
/// Creates a buffer pointer starting at `start` and extending up to the last
392-
/// addressable pointer to `Element` in the memory space
393-
@inlinable
394-
public init(_unboundedStartingAt start: Unsafe${Mutable}Pointer<Element>) {
395-
_position = start
396-
_end = type(of: start)._max
390+
self.count = count
397391
}
398392

399393
@inlinable
400394
public init(_empty: ()) {
401-
_position = Unsafe${Mutable}Pointer._max
402-
_end = _position
395+
_position = nil
396+
count = 0
403397
}
404398

405399
% if Mutable:
@@ -411,7 +405,7 @@ extension Unsafe${Mutable}BufferPointer {
411405
@inlinable
412406
public init(mutating other: UnsafeBufferPointer<Element>) {
413407
_position = UnsafeMutablePointer<Element>(mutating: other._position)
414-
_end = UnsafeMutablePointer<Element>(mutating: other._end)
408+
count = other.count
415409
}
416410

417411
% else:
@@ -423,7 +417,7 @@ extension Unsafe${Mutable}BufferPointer {
423417
@inlinable
424418
public init(_ other: UnsafeMutableBufferPointer<Element>) {
425419
_position = UnsafePointer<Element>(other._position)
426-
_end = UnsafePointer<Element>(other._end)
420+
count = other.count
427421
}
428422

429423
% end
@@ -543,7 +537,7 @@ extension Unsafe${Mutable}BufferPointer {
543537
return
544538
}
545539

546-
dstBase.initialize(repeating: repeatedValue, count: _end! - dstBase)
540+
dstBase.initialize(repeating: repeatedValue, count: count)
547541
}
548542

549543
/// Assigns every element in this buffer's memory to a copy of the given value.
@@ -563,7 +557,7 @@ extension Unsafe${Mutable}BufferPointer {
563557
return
564558
}
565559

566-
dstBase.assign(repeating: repeatedValue, count: _end! - dstBase)
560+
dstBase.assign(repeating: repeatedValue, count: count)
567561
}
568562

569563
% end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-swift-frontend -parse-as-library -Osize -emit-ir %s | %FileCheck %s
2+
// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib
3+
4+
// This is an end-to-end test to ensure that the optimizer generates
5+
// optimal code for UnsafeBufferPointer.
6+
7+
// CHECK-LABEL: define {{.*}}testIteration
8+
9+
// Check if the code contains no traps at all.
10+
// CHECK-NOT: unreachable
11+
public func testIteration(_ p: UnsafeBufferPointer<Int>) -> Int {
12+
var s = 0
13+
14+
// Check for an optimal loop kernel
15+
// CHECK: phi
16+
// CHECK-NEXT: phi
17+
// CHECK-NEXT: bitcast
18+
// CHECK-NEXT: load
19+
// CHECK-NEXT: getelementptr
20+
// CHECK-NEXT: add
21+
// CHECK-NEXT: icmp
22+
// CHECK-NEXT: br
23+
for x in p {
24+
s = s &+ x
25+
}
26+
// CHECK-NOT: unreachable
27+
// CHECK: phi
28+
// CHECK-NEXT: ret
29+
// CHECK-NOT: unreachable
30+
return s
31+
}
32+
33+
// CHECK-LABEL: define {{.*}}testIsEmpty
34+
// CHECK: entry:
35+
// CHECK-NEXT: icmp
36+
// CHECK-NEXT: ret
37+
public func testIsEmpty(_ x: UnsafeBufferPointer<UInt>) -> Bool {
38+
return x.isEmpty
39+
}
40+
41+
// CHECK-LABEL: define {{.*}}testCount
42+
// CHECK: entry:
43+
// CHECK-NEXT: ret
44+
public func testCount(_ x: UnsafeBufferPointer<UInt>) -> Int {
45+
return x.count
46+
}
47+

0 commit comments

Comments
 (0)