Skip to content

Commit 1161f92

Browse files
authored
Merge pull request #39529 from glessard/se-withMemoryRebound
2 parents 645b78d + b2aaaeb commit 1161f92

File tree

8 files changed

+534
-72
lines changed

8 files changed

+534
-72
lines changed

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -681,8 +681,18 @@ extension Unsafe${Mutable}BufferPointer {
681681
/// the same memory as an unrelated type without first rebinding the memory
682682
/// is undefined.
683683
///
684-
/// The entire region of memory referenced by this buffer must be initialized.
684+
/// The number of instances of `T` referenced by the rebound buffer may be
685+
/// different than the number of instances of `Element` referenced by the
686+
/// original buffer. The number of instances of `T` will be calculated
687+
/// at runtime.
685688
///
689+
/// Any instance of `T` within the re-bound region may be initialized or
690+
/// uninitialized. Every instance of `Pointee` overlapping with a given
691+
/// instance of `T` should have the same initialization state (i.e.
692+
/// initialized or uninitialized.) Accessing a `T` whose underlying
693+
/// `Pointee` storage is in a mixed initialization state shall be
694+
/// undefined behaviour.
695+
///
686696
/// Because this buffer's memory is no longer bound to its `Element` type
687697
/// while the `body` closure executes, do not access memory using the
688698
/// original buffer from within `body`. Instead, use the `body` closure's
@@ -693,39 +703,80 @@ extension Unsafe${Mutable}BufferPointer {
693703
/// `Element` type.
694704
///
695705
/// - Note: Only use this method to rebind the buffer's memory to a type
696-
/// with the same size and stride as the currently bound `Element` type.
697-
/// To bind a region of memory to a type that is a different size, convert
698-
/// the buffer to a raw buffer and use the `bindMemory(to:)` method.
706+
/// that is layout compatible with the currently bound `Element` type.
707+
/// The stride of the temporary type (`T`) may be an integer multiple
708+
/// or a whole fraction of `Element`'s stride.
709+
/// To bind a region of memory to a type that does not match these
710+
/// requirements, convert the buffer to a raw buffer and use the
711+
/// `bindMemory(to:)` method.
712+
/// If `T` and `Element` have different alignments, this buffer's
713+
/// `baseAddress` must be aligned with the larger of the two alignments.
699714
///
700715
/// - Parameters:
701716
/// - type: The type to temporarily bind the memory referenced by this
702-
/// buffer. The type `T` must have the same size and be layout compatible
717+
/// buffer. The type `T` must be layout compatible
703718
/// with the pointer's `Element` type.
704719
/// - body: A closure that takes a ${Mutable.lower()} typed buffer to the
705-
/// same memory as this buffer, only bound to type `T`. The buffer argument
706-
/// contains the same number of complete instances of `T` as the original
707-
/// buffer’s `count`. The closure's buffer argument is valid only for the
708-
/// duration of the closure's execution. If `body` has a return value, that
709-
/// value is also used as the return value for the `withMemoryRebound(to:_:)`
720+
/// same memory as this buffer, only bound to type `T`. The buffer
721+
/// parameter contains a number of complete instances of `T` based
722+
/// on the capacity of the original buffer and the stride of `Element`.
723+
/// The closure's buffer argument is valid only for the duration of the
724+
/// closure's execution. If `body` has a return value, that value
725+
/// is also used as the return value for the `withMemoryRebound(to:_:)`
710726
/// method.
727+
/// - buffer: The buffer temporarily bound to `T`.
711728
/// - Returns: The return value, if any, of the `body` closure parameter.
712729
@inlinable // unsafe-performance
730+
@_alwaysEmitIntoClient
731+
// This custom silgen name is chosen to not interfere with the old ABI
732+
% if Mutable:
733+
@_silgen_name("$_swift_se0333_UnsafeMutableBufferPointer_withMemoryRebound")
734+
% else:
735+
@_silgen_name("$_swift_se0333_UnsafeBufferPointer_withMemoryRebound")
736+
% end
713737
public func withMemoryRebound<T, Result>(
714-
to type: T.Type, _ body: (${Self}<T>) throws -> Result
738+
to type: T.Type,
739+
_ body: (_ buffer: ${Self}<T>) throws -> Result
715740
) rethrows -> Result {
716-
if let base = _position {
717-
_debugPrecondition(MemoryLayout<Element>.stride == MemoryLayout<T>.stride)
718-
Builtin.bindMemory(base._rawValue, count._builtinWordValue, T.self)
719-
defer {
720-
Builtin.bindMemory(base._rawValue, count._builtinWordValue, Element.self)
721-
}
722-
723-
return try body(${Self}<T>(
724-
start: Unsafe${Mutable}Pointer<T>(base._rawValue), count: count))
741+
guard let base = _position?._rawValue else {
742+
return try body(.init(start: nil, count: 0))
725743
}
726-
else {
727-
return try body(${Self}<T>(start: nil, count: 0))
744+
745+
let newCount: Int
746+
if MemoryLayout<T>.stride == MemoryLayout<Element>.stride {
747+
newCount = count
748+
_debugPrecondition(
749+
MemoryLayout<T>.alignment == MemoryLayout<Element>.alignment
750+
)
751+
} else {
752+
newCount = count * MemoryLayout<Element>.stride / MemoryLayout<T>.stride
753+
_debugPrecondition(
754+
Int(bitPattern: .init(base)) & (MemoryLayout<T>.alignment-1) == 0 &&
755+
MemoryLayout<T>.stride > MemoryLayout<Element>.stride
756+
? MemoryLayout<T>.stride % MemoryLayout<Element>.stride == 0
757+
: MemoryLayout<Element>.stride % MemoryLayout<T>.stride == 0
758+
)
728759
}
760+
let binding = Builtin.bindMemory(base, newCount._builtinWordValue, T.self)
761+
defer { Builtin.rebindMemory(base, binding) }
762+
return try body(.init(start: .init(base), count: newCount))
763+
}
764+
765+
// This unavailable implementation uses the expected mangled name
766+
// of `withMemoryRebound`, and provides an entry point for any
767+
// binary compiled against the stlib binary for Swift 5.6 and older.
768+
@available(*, unavailable)
769+
% if Mutable:
770+
@_silgen_name("$sSr17withMemoryRebound2to_qd_0_qd__m_qd_0_Sryqd__GKXEtKr0_lF")
771+
% else:
772+
@_silgen_name("$sSR17withMemoryRebound2to_qd_0_qd__m_qd_0_SRyqd__GKXEtKr0_lF")
773+
% end
774+
@usableFromInline
775+
internal func _legacy_se0333_withMemoryRebound<T, Result>(
776+
to type: T.Type,
777+
_ body: (${Self}<T>) throws -> Result
778+
) rethrows -> Result {
779+
return try withMemoryRebound(to: T.self, body)
729780
}
730781

731782
/// A pointer to the first element of the buffer.

stdlib/public/core/UnsafePointer.swift

Lines changed: 110 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ public struct UnsafePointer<Pointee>: _Pointer {
243243
}
244244
}
245245

246-
/// Executes the given closure while temporarily binding the specified number
247-
/// of instances to the given type.
246+
/// Executes the given closure while temporarily binding memory to
247+
/// the specified number of instances of type `T`.
248248
///
249249
/// Use this method when you have a pointer to memory bound to one type and
250250
/// you need to access that memory as instances of another type. Accessing
@@ -253,15 +253,23 @@ public struct UnsafePointer<Pointee>: _Pointer {
253253
/// the same memory as an unrelated type without first rebinding the memory
254254
/// is undefined.
255255
///
256-
/// The region of memory starting at this pointer and covering `count`
257-
/// instances of the pointer's `Pointee` type must be initialized.
256+
/// The region of memory that starts at this pointer and covers `count`
257+
/// strides of `T` instances must be bound to `Pointee`.
258+
/// Any instance of `T` within the re-bound region may be initialized or
259+
/// uninitialized. Every instance of `Pointee` overlapping with a given
260+
/// instance of `T` should have the same initialization state (i.e.
261+
/// initialized or uninitialized.) Accessing a `T` whose underlying
262+
/// `Pointee` storage is in a mixed initialization state shall be
263+
/// undefined behaviour.
258264
///
259265
/// The following example temporarily rebinds the memory of a `UInt64`
260266
/// pointer to `Int64`, then accesses a property on the signed integer.
261267
///
262268
/// let uint64Pointer: UnsafePointer<UInt64> = fetchValue()
263-
/// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
264-
/// return ptr.pointee < 0
269+
/// let isNegative = uint64Pointer.withMemoryRebound(
270+
/// to: Int64.self, capacity: 1
271+
/// ) {
272+
/// return $0.pointee < 0
265273
/// }
266274
///
267275
/// Because this pointer's memory is no longer bound to its `Pointee` type
@@ -274,31 +282,60 @@ public struct UnsafePointer<Pointee>: _Pointer {
274282
/// `Pointee` type.
275283
///
276284
/// - Note: Only use this method to rebind the pointer's memory to a type
277-
/// with the same size and stride as the currently bound `Pointee` type.
278-
/// To bind a region of memory to a type that is a different size, convert
279-
/// the pointer to a raw pointer and use the `bindMemory(to:capacity:)`
280-
/// method.
285+
/// that is layout compatible with the `Pointee` type. The stride of the
286+
/// temporary type (`T`) may be an integer multiple or a whole fraction
287+
/// of `Pointee`'s stride, for example to point to one element of
288+
/// an aggregate.
289+
/// To bind a region of memory to a type that does not match these
290+
/// requirements, convert the pointer to a raw pointer and use the
291+
/// `bindMemory(to:)` method.
292+
/// If `T` and `Pointee` have different alignments, this pointer
293+
/// must be aligned with the larger of the two alignments.
281294
///
282295
/// - Parameters:
283296
/// - type: The type to temporarily bind the memory referenced by this
284-
/// pointer. The type `T` must be the same size and be layout compatible
297+
/// pointer. The type `T` must be layout compatible
285298
/// with the pointer's `Pointee` type.
286-
/// - count: The number of instances of `Pointee` to bind to `type`.
287-
/// - body: A closure that takes a typed pointer to the
299+
/// - count: The number of instances of `T` in the re-bound region.
300+
/// - body: A closure that takes a typed pointer to the
288301
/// same memory as this pointer, only bound to type `T`. The closure's
289302
/// pointer argument is valid only for the duration of the closure's
290303
/// execution. If `body` has a return value, that value is also used as
291304
/// the return value for the `withMemoryRebound(to:capacity:_:)` method.
305+
/// - pointer: The pointer temporarily bound to `T`.
292306
/// - Returns: The return value, if any, of the `body` closure parameter.
293307
@inlinable
294-
public func withMemoryRebound<T, Result>(to type: T.Type, capacity count: Int,
308+
@_alwaysEmitIntoClient
309+
// This custom silgen name is chosen to not interfere with the old ABI
310+
@_silgen_name("_swift_se0333_UnsafePointer_withMemoryRebound")
311+
public func withMemoryRebound<T, Result>(
312+
to type: T.Type,
313+
capacity count: Int,
314+
_ body: (_ pointer: UnsafePointer<T>) throws -> Result
315+
) rethrows -> Result {
316+
_debugPrecondition(
317+
Int(bitPattern: .init(_rawValue)) & (MemoryLayout<T>.alignment-1) == 0 &&
318+
MemoryLayout<Pointee>.stride > MemoryLayout<T>.stride
319+
? MemoryLayout<Pointee>.stride % MemoryLayout<T>.stride == 0
320+
: MemoryLayout<T>.stride % MemoryLayout<Pointee>.stride == 0
321+
)
322+
let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
323+
defer { Builtin.rebindMemory(_rawValue, binding) }
324+
return try body(.init(_rawValue))
325+
}
326+
327+
// This unavailable implementation uses the expected mangled name
328+
// of `withMemoryRebound`, and provides an entry point for any
329+
// binary compiled against the stlib binary for Swift 5.6 and older.
330+
@available(*, unavailable)
331+
@_silgen_name("$sSP17withMemoryRebound2to8capacity_qd_0_qd__m_Siqd_0_SPyqd__GKXEtKr0_lF")
332+
@usableFromInline
333+
internal func _legacy_se0333_withMemoryRebound<T, Result>(
334+
to type: T.Type,
335+
capacity count: Int,
295336
_ body: (UnsafePointer<T>) throws -> Result
296337
) rethrows -> Result {
297-
Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
298-
defer {
299-
Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self)
300-
}
301-
return try body(UnsafePointer<T>(_rawValue))
338+
return try withMemoryRebound(to: T.self, capacity: count, body)
302339
}
303340

304341
/// Accesses the pointee at the specified offset from this pointer.
@@ -894,8 +931,8 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
894931
return UnsafeMutableRawPointer(self)
895932
}
896933

897-
/// Executes the given closure while temporarily binding the specified number
898-
/// of instances to the given type.
934+
/// Executes the given closure while temporarily binding memory to
935+
/// the specified number of instances of the given type.
899936
///
900937
/// Use this method when you have a pointer to memory bound to one type and
901938
/// you need to access that memory as instances of another type. Accessing
@@ -904,15 +941,21 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
904941
/// the same memory as an unrelated type without first rebinding the memory
905942
/// is undefined.
906943
///
907-
/// The region of memory starting at this pointer and covering `count`
908-
/// instances of the pointer's `Pointee` type must be initialized.
944+
/// The region of memory that starts at this pointer and covers `count`
945+
/// strides of `T` instances must be bound to `Pointee`.
946+
/// Any instance of `T` within the re-bound region may be initialized or
947+
/// uninitialized. Every instance of `Pointee` overlapping with a given
948+
/// instance of `T` should have the same initialization state (i.e.
949+
/// initialized or uninitialized.) Accessing a `T` whose underlying
950+
/// `Pointee` storage is in a mixed initialization state shall be
951+
/// undefined behaviour.
909952
///
910953
/// The following example temporarily rebinds the memory of a `UInt64`
911-
/// pointer to `Int64`, then accesses a property on the signed integer.
954+
/// pointer to `Int64`, then modifies the signed integer.
912955
///
913956
/// let uint64Pointer: UnsafeMutablePointer<UInt64> = fetchValue()
914-
/// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
915-
/// return ptr.pointee < 0
957+
/// uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) {
958+
/// $0.pointee.negate()
916959
/// }
917960
///
918961
/// Because this pointer's memory is no longer bound to its `Pointee` type
@@ -925,31 +968,60 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
925968
/// `Pointee` type.
926969
///
927970
/// - Note: Only use this method to rebind the pointer's memory to a type
928-
/// with the same size and stride as the currently bound `Pointee` type.
929-
/// To bind a region of memory to a type that is a different size, convert
930-
/// the pointer to a raw pointer and use the `bindMemory(to:capacity:)`
931-
/// method.
971+
/// that is layout compatible with the `Pointee` type. The stride of the
972+
/// temporary type (`T`) may be an integer multiple or a whole fraction
973+
/// of `Pointee`'s stride, for example to point to one element of
974+
/// an aggregate.
975+
/// To bind a region of memory to a type that does not match these
976+
/// requirements, convert the pointer to a raw pointer and use the
977+
/// `bindMemory(to:)` method.
978+
/// If `T` and `Pointee` have different alignments, this pointer
979+
/// must be aligned with the larger of the two alignments.
932980
///
933981
/// - Parameters:
934982
/// - type: The type to temporarily bind the memory referenced by this
935-
/// pointer. The type `T` must be the same size and be layout compatible
983+
/// pointer. The type `T` must be layout compatible
936984
/// with the pointer's `Pointee` type.
937-
/// - count: The number of instances of `Pointee` to bind to `type`.
985+
/// - count: The number of instances of `T` in the re-bound region.
938986
/// - body: A closure that takes a mutable typed pointer to the
939987
/// same memory as this pointer, only bound to type `T`. The closure's
940988
/// pointer argument is valid only for the duration of the closure's
941989
/// execution. If `body` has a return value, that value is also used as
942990
/// the return value for the `withMemoryRebound(to:capacity:_:)` method.
991+
/// - pointer: The pointer temporarily bound to `T`.
943992
/// - Returns: The return value, if any, of the `body` closure parameter.
944993
@inlinable
945-
public func withMemoryRebound<T, Result>(to type: T.Type, capacity count: Int,
994+
@_alwaysEmitIntoClient
995+
// This custom silgen name is chosen to not interfere with the old ABI
996+
@_silgen_name("$_swift_se0333_UnsafeMutablePointer_withMemoryRebound")
997+
public func withMemoryRebound<T, Result>(
998+
to type: T.Type,
999+
capacity count: Int,
1000+
_ body: (_ pointer: UnsafeMutablePointer<T>) throws -> Result
1001+
) rethrows -> Result {
1002+
_debugPrecondition(
1003+
Int(bitPattern: .init(_rawValue)) & (MemoryLayout<T>.alignment-1) == 0 &&
1004+
MemoryLayout<Pointee>.stride > MemoryLayout<T>.stride
1005+
? MemoryLayout<Pointee>.stride % MemoryLayout<T>.stride == 0
1006+
: MemoryLayout<T>.stride % MemoryLayout<Pointee>.stride == 0
1007+
)
1008+
let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
1009+
defer { Builtin.rebindMemory(_rawValue, binding) }
1010+
return try body(.init(_rawValue))
1011+
}
1012+
1013+
// This unavailable implementation uses the expected mangled name
1014+
// of `withMemoryRebound`, and provides an entry point for any
1015+
// binary compiled against the stlib binary for Swift 5.6 and older.
1016+
@available(*, unavailable)
1017+
@_silgen_name("$sSp17withMemoryRebound2to8capacity_qd_0_qd__m_Siqd_0_Spyqd__GKXEtKr0_lF")
1018+
@usableFromInline
1019+
internal func _legacy_se0333_withMemoryRebound<T, Result>(
1020+
to type: T.Type,
1021+
capacity count: Int,
9461022
_ body: (UnsafeMutablePointer<T>) throws -> Result
9471023
) rethrows -> Result {
948-
Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
949-
defer {
950-
Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self)
951-
}
952-
return try body(UnsafeMutablePointer<T>(_rawValue))
1024+
return try withMemoryRebound(to: T.self, capacity: count, body)
9531025
}
9541026

9551027
/// Accesses the pointee at the specified offset from this pointer.

0 commit comments

Comments
 (0)