Skip to content

Commit 0d0c1d5

Browse files
committed
Rewrite MutableSpan and OutputSpan subscript for BitwiseCopyable elements
1 parent d275f08 commit 0d0c1d5

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-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 {

stdlib/public/core/Span/OutputSpan.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,39 @@ extension OutputSpan {
182182
}
183183
}
184184

185+
186+
@available(SwiftCompatibilitySpan 5.0, *)
187+
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
188+
extension OutputSpan where Element: BitwiseCopyable {
189+
@_alwaysEmitIntoClient
190+
public subscript(_ position: Index) -> Element {
191+
get {
192+
_checkIndex(position)
193+
return unsafe self[unchecked: position]
194+
}
195+
@lifetime(self: copy self)
196+
set {
197+
_checkIndex(position)
198+
unsafe self[unchecked: position] = newValue
199+
}
200+
}
201+
202+
@unsafe
203+
@_alwaysEmitIntoClient
204+
public subscript(unchecked position: Index) -> Element {
205+
get {
206+
let elementOffset = position &* MemoryLayout<Element>.stride
207+
let address = unsafe _start().advanced(by: elementOffset)
208+
return unsafe address.loadUnaligned(as: Element.self)
209+
}
210+
@lifetime(self: copy self)
211+
set {
212+
let elementOffset = position &* MemoryLayout<Element>.stride
213+
unsafe _start().storeBytes(of: newValue, toByteOffset: elementOffset, as: Element.self)
214+
}
215+
}
216+
}
217+
185218
@available(SwiftCompatibilitySpan 5.0, *)
186219
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
187220
extension OutputSpan where Element: ~Copyable {

test/SILOptimizer/mutable_span_bounds_check_tests.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,69 @@ 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+
230+
// CHECK-LABEL: sil @$s31mutable_span_bounds_check_tests07output_B25_init_all_bitwisecopyableyys10OutputSpanVyxGz_xts15BitwiseCopyableRzAA1PRzlF :
231+
// CHECK-SIL: bb3({{.*}}):
232+
// CHECK-NOT: end_cow_mutation
233+
// CHECK-LABEL: } // end sil function '$s31mutable_span_bounds_check_tests07output_B25_init_all_bitwisecopyableyys10OutputSpanVyxGz_xts15BitwiseCopyableRzAA1PRzlF'
234+
public func output_span_init_all_bitwisecopyable<T: P & BitwiseCopyable>(_ os: inout OutputSpan<T>, _ value: T) {
235+
for i in os.indices {
236+
os[i] = value
237+
}
238+
}
239+

0 commit comments

Comments
 (0)