Skip to content

Commit 53d9e6e

Browse files
committed
Rewrite MutableSpan subscript for BitwiseCopyable elements
1 parent d275f08 commit 53d9e6e

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

stdlib/public/core/Span/MutableSpan.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,38 @@ extension MutableSpan where Element: BitwiseCopyable {
170170
}
171171
}
172172

173+
@available(SwiftCompatibilitySpan 5.0, *)
174+
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
175+
extension MutableSpan where Element: BitwiseCopyable {
176+
@_alwaysEmitIntoClient
177+
public subscript(_ position: Index) -> Element {
178+
get {
179+
_checkIndex(position)
180+
return unsafe self[unchecked: position]
181+
}
182+
@lifetime(self: copy self)
183+
set {
184+
_checkIndex(position)
185+
unsafe self[unchecked: position] = newValue
186+
}
187+
}
188+
189+
@unsafe
190+
@_alwaysEmitIntoClient
191+
public subscript(unchecked position: Index) -> Element {
192+
get {
193+
let elementOffset = position &* MemoryLayout<Element>.stride
194+
let address = unsafe _start().advanced(by: elementOffset)
195+
return unsafe address.loadUnaligned(as: Element.self)
196+
}
197+
@lifetime(self: copy self)
198+
set {
199+
let elementOffset = position &* MemoryLayout<Element>.stride
200+
unsafe _start().storeBytes(of: newValue, toByteOffset: elementOffset, as: Element.self)
201+
}
202+
}
203+
}
204+
173205
@available(SwiftCompatibilitySpan 5.0, *)
174206
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
175207
extension Span where Element: ~Copyable {

test/SILOptimizer/mutable_span_bounds_check_tests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,59 @@ public func sorted(_ span: borrowing MutableSpan<Int>) -> Bool {
171171
return true
172172
}
173173

174+
public protocol P {
175+
mutating func mutate(_ other: Self)
176+
}
177+
178+
// TODO: Ensure end_cow_mutation_addr is not inserted within the loop
179+
// This happens because MutableSpan's subscript is implemented with unsafe addressors which introduce mark_dependence [unresolved].
180+
// This triggers lifetime-dependence-scope-fixup which unnecessarily inserts end_cow_mutation_addr due to the conservative analysis of `mayHaveMutableSpan` on generic types.
181+
// TODO: Improve `mayHaveMutableSpan` to avoid this.
182+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests0a1_B7_doubleyys11MutableSpanVyxGzAA1PRzlF :
183+
// CHECK-SIL: bb3({{.*}}):
184+
// TODO-CHECK-NOT: end_cow_mutation
185+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests0a1_B7_doubleyys11MutableSpanVyxGzAA1PRzlF'
186+
public func mutable_span_double<T: P>(_ ms: inout MutableSpan<T>) {
187+
for i in ms.indices {
188+
ms[i].mutate(ms[i])
189+
}
190+
}
191+
192+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests0a1_B23_double_bitwisecopyableyys11MutableSpanVyxGzs15BitwiseCopyableRzAA1PRzlF :
193+
// CHECK-SIL: bb3({{.*}}):
194+
// CHECK-NOT: end_cow_mutation
195+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests0a1_B23_double_bitwisecopyableyys11MutableSpanVyxGzs15BitwiseCopyableRzAA1PRzlF'
196+
public func mutable_span_double_bitwisecopyable<T: P & BitwiseCopyable>(_ ms: inout MutableSpan<T>) {
197+
for i in ms.indices {
198+
ms[i].mutate(ms[i])
199+
}
200+
}
201+
202+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests0a1_B22_equal_bitwisecopyableySbs11MutableSpanVyxGz_Sits15BitwiseCopyableRzSQRzlF :
203+
// CHECK-NOT: copy_addr
204+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests0a1_B22_equal_bitwisecopyableySbs11MutableSpanVyxGz_Sits15BitwiseCopyableRzSQRzlF'
205+
public func mutable_span_equal_bitwisecopyable<T : Equatable & BitwiseCopyable>(_ ms: inout MutableSpan<T>, _ index: Int) -> Bool {
206+
return ms[index] == ms[index]
207+
}
208+
209+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests0a1_B28_get_element_bitwisecopyableyxs11MutableSpanVyxGz_Sits15BitwiseCopyableRzlF :
210+
// CHECK-NOT: copy_addr
211+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests0a1_B28_get_element_bitwisecopyableyxs11MutableSpanVyxGz_Sits15BitwiseCopyableRzlF'
212+
public func mutable_span_get_element_bitwisecopyable<T : BitwiseCopyable>(_ ms: inout MutableSpan<T>, _ index: Int) -> T {
213+
return ms[index]
214+
}
215+
216+
extension Int : P {
217+
public mutating func mutate(_ other: Int) {
218+
self += other
219+
}
220+
}
221+
222+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests17specializedCalleryySaySiGzF : $@convention(thin) (@inout Array<Int>) -> () {
223+
// CHECK-NOT: cond_fail "index out of bounds"
224+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests17specializedCalleryySaySiGzF'
225+
public func specializedCaller(_ array: inout Array<Int>) {
226+
var mut = array.mutableSpan
227+
mutable_span_double_bitwisecopyable(&mut)
228+
}
229+

0 commit comments

Comments
 (0)