Skip to content

Commit a557976

Browse files
committed
Remove useless optional unwrap from Unsafe[Raw]BufferPointer subscript.
It is unfortunate that `Unsafe[Raw]BufferPointer._pointer` and `baseAddress` are declared Optional. This leaves extra runtime checks in the code in the most performance critical paths. Contrast this with Array, which uses an sentinal pointer for the empty representation. This forces us to use _unsafelyUnwrappedUnchecked whenever we just want to dereference a non-empty buffer pointer. Fixes SR-9809: swiftc appears to make some sub-optimal optimization choices
1 parent b529953 commit a557976

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,19 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle
270270
get {
271271
_debugPrecondition(i >= 0)
272272
_debugPrecondition(i < endIndex)
273-
return _position![i]
273+
return _position._unsafelyUnwrappedUnchecked[i]
274274
}
275275
%if Mutable:
276276
nonmutating _modify {
277277
_debugPrecondition(i >= 0)
278278
_debugPrecondition(i < endIndex)
279-
yield &_position![i]
279+
yield &_position._unsafelyUnwrappedUnchecked[i]
280280
}
281281
%end
282282
}
283283

284284
// Skip all debug and runtime checks
285+
285286
@inlinable // unsafe-performance
286287
internal subscript(_unchecked i: Int) -> Element {
287288
get {

stdlib/public/core/UnsafeRawBufferPointer.swift.gyb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,13 @@ extension Unsafe${Mutable}RawBufferPointer: ${Mutable}Collection {
185185
get {
186186
_debugPrecondition(i >= 0)
187187
_debugPrecondition(i < endIndex)
188-
return _position!.load(fromByteOffset: i, as: UInt8.self)
188+
return _position._unsafelyUnwrappedUnchecked.load(fromByteOffset: i, as: UInt8.self)
189189
}
190190
% if mutable:
191191
nonmutating set {
192192
_debugPrecondition(i >= 0)
193193
_debugPrecondition(i < endIndex)
194-
_position!.storeBytes(of: newValue, toByteOffset: i, as: UInt8.self)
194+
_position._unsafelyUnwrappedUnchecked.storeBytes(of: newValue, toByteOffset: i, as: UInt8.self)
195195
}
196196
% end # mutable
197197
}

test/SILOptimizer/unsafebufferpointer.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,52 @@ public func testCount(_ x: UnsafeBufferPointer<UInt>) -> Int {
4545
return x.count
4646
}
4747

48+
// Within the loop, there should be no extra checks.
49+
// CHECK-LABEL: define {{.*}} float {{.*}}testSubscript
50+
// The only unconditional branch is into the loop.
51+
// CHECK: br label %[[LOOP:[0-9]+]]
52+
//
53+
// For some reason, LLVM lays out the exit before the loop.
54+
// CHECK: .loopexit: ; preds = %[[LOOP]]
55+
// CHECK: ret float
56+
//
57+
// CHECK: ; <label>:[[LOOP]]:
58+
// CHECK: phi float [ 0.000000e+00
59+
// CHECK: add nuw i64 %{{.*}}, 1
60+
// CHECK: load float, float*
61+
// CHECK: fadd float
62+
// CHECK: [[CMP:%[0-9]+]] = icmp eq i64 %{{.*}}, %{{.*}}
63+
// CHECK: br i1 [[CMP]], label %.loopexit, label %[[LOOP]]
64+
public func testSubscript(_ ubp: UnsafeBufferPointer<Float>) -> Float {
65+
var sum: Float = 0
66+
for i in 0 ..< ubp.count {
67+
sum += ubp[i]
68+
}
69+
return sum
70+
}
71+
72+
// Within the loop, there should be no extra checks.
73+
// CHECK-LABEL: define {{.*}} i64 {{.*}}testSubscript
74+
// The only unconditional branch is into the loop.
75+
// CHECK: br label %[[LOOP:[0-9]+]]
76+
//
77+
// For some reason, LLVM lays out the exit before the loop.
78+
// CHECK: [[RET:.*]]: ; preds = %[[LOOP]], %entry
79+
// CHECK: ret i64
80+
//
81+
// CHECK: ; <label>:[[LOOP]]:
82+
// CHECK: phi i64 [ 0
83+
// CHECK: phi i64 [ 0
84+
// CHECK: add nuw i64 %{{.*}}, 1
85+
// CHECK: load i8, i8*
86+
// CHECK: zext i8 %{{.*}} to i64
87+
// CHECK: add i64
88+
// CHECK: [[CMP:%[0-9]+]] = icmp eq i64 %{{.*}}, %{{.*}}
89+
// CHECK: br i1 [[CMP]], label %[[RET]], label %[[LOOP]]
90+
public func testSubscript(_ ubp: UnsafeRawBufferPointer) -> Int {
91+
var sum: Int = 0
92+
for i in 0 ..< ubp.count {
93+
sum &+= Int(ubp[i])
94+
}
95+
return sum
96+
}

0 commit comments

Comments
 (0)