Skip to content

Commit 58c8e2e

Browse files
authored
Merge pull request #41033 from glessard/se-unaligned
[SE-0349] Implementation for unaligned loads from raw memory
2 parents 1b4f9b0 + 3971f28 commit 58c8e2e

File tree

5 files changed

+378
-23
lines changed

5 files changed

+378
-23
lines changed

stdlib/public/core/UnsafeRawBufferPointer.swift.gyb

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,49 @@ extension Unsafe${Mutable}RawBufferPointer {
381381
return baseAddress!.load(fromByteOffset: offset, as: T.self)
382382
}
383383

384+
/// Returns a new instance of the given type, constructed from the raw memory
385+
/// at the specified offset.
386+
///
387+
/// This function only supports loading trivial types.
388+
/// A trivial type does not contain any reference-counted property
389+
/// within its in-memory stored representation.
390+
/// The memory at `offset` bytes into the buffer must be laid out
391+
/// identically to the in-memory representation of `T`.
392+
///
393+
/// You can use this method to create new values from the buffer pointer's
394+
/// underlying bytes. The following example creates two new `Int32`
395+
/// instances from the memory referenced by the buffer pointer `someBytes`.
396+
/// The bytes for `a` are copied from the first four bytes of `someBytes`,
397+
/// and the bytes for `b` are copied from the next four bytes.
398+
///
399+
/// let a = someBytes.load(as: Int32.self)
400+
/// let b = someBytes.load(fromByteOffset: 4, as: Int32.self)
401+
///
402+
/// The memory to read for the new instance must not extend beyond the buffer
403+
/// pointer's memory region---that is, `offset + MemoryLayout<T>.size` must
404+
/// be less than or equal to the buffer pointer's `count`.
405+
///
406+
/// - Parameters:
407+
/// - offset: The offset, in bytes, into the buffer pointer's memory at
408+
/// which to begin reading data for the new instance. The buffer pointer
409+
/// plus `offset` must be properly aligned for accessing an instance of
410+
/// type `T`. The default is zero.
411+
/// - type: The type to use for the newly constructed instance. The memory
412+
/// must be initialized to a value of a type that is layout compatible
413+
/// with `type`.
414+
/// - Returns: A new instance of type `T`, copied from the buffer pointer's
415+
/// memory.
416+
@_alwaysEmitIntoClient
417+
public func loadUnaligned<T>(
418+
fromByteOffset offset: Int = 0,
419+
as type: T.Type
420+
) -> T {
421+
_debugPrecondition(offset >= 0, "${Self}.load with negative offset")
422+
_debugPrecondition(offset + MemoryLayout<T>.size <= self.count,
423+
"${Self}.load out of bounds")
424+
return baseAddress!.loadUnaligned(fromByteOffset: offset, as: T.self)
425+
}
426+
384427
% if mutable:
385428
/// Stores a value's bytes into the buffer pointer's raw memory at the
386429
/// specified byte offset.
@@ -399,6 +442,16 @@ extension Unsafe${Mutable}RawBufferPointer {
399442
/// type `U`. Calling `storeBytes(of:toByteOffset:as:)` does not change the
400443
/// bound type of the memory.
401444
///
445+
/// - Note: A trivial type can be copied with just a bit-for-bit copy without
446+
/// any indirection or reference-counting operations. Generally, native
447+
/// Swift types that do not contain strong or weak references or other
448+
/// forms of indirection are trivial, as are imported C structs and enums.
449+
///
450+
/// If you need to store into memory a copy of a value of a type that isn't
451+
/// trivial, you cannot use the `storeBytes(of:toByteOffset:as:)` method.
452+
/// Instead, you must know either initialize the memory or,
453+
/// if you know the memory was already bound to `type`, assign to the memory.
454+
///
402455
/// - Parameters:
403456
/// - value: The value to store as raw bytes.
404457
/// - offset: The offset in bytes into the buffer pointer's memory to begin
@@ -409,14 +462,34 @@ extension Unsafe${Mutable}RawBufferPointer {
409462
/// must be initialized to a value of a type that is layout compatible
410463
/// with `type`.
411464
@inlinable
465+
@_alwaysEmitIntoClient
466+
@_silgen_name("_swift_se0349_UnsafeMutableRawBufferPointer_storeBytes")
412467
public func storeBytes<T>(
413468
of value: T, toByteOffset offset: Int = 0, as type: T.Type
414469
) {
415470
_debugPrecondition(offset >= 0, "${Self}.storeBytes with negative offset")
416471
_debugPrecondition(offset + MemoryLayout<T>.size <= self.count,
417472
"${Self}.storeBytes out of bounds")
418473

419-
baseAddress!.storeBytes(of: value, toByteOffset: offset, as: T.self)
474+
let pointer = baseAddress._unsafelyUnwrappedUnchecked
475+
pointer.storeBytes(of: value, toByteOffset: offset, as: T.self)
476+
}
477+
478+
// This unavailable implementation uses the expected mangled name
479+
// of `storeBytes<T>(of:toByteOffset:as:)`, and provides an entry point for
480+
// any binary compiled against the stlib binary for Swift 5.6 and older.
481+
@available(*, unavailable)
482+
@_silgen_name("$sSw10storeBytes2of12toByteOffset2asyx_SixmtlF")
483+
@usableFromInline func _legacy_se0349_storeBytes<T>(
484+
of value: T, toByteOffset offset: Int = 0, as type: T.Type
485+
) {
486+
_debugPrecondition(offset >= 0, "${Self}.storeBytes with negative offset")
487+
_debugPrecondition(offset + MemoryLayout<T>.size <= self.count,
488+
"${Self}.storeBytes out of bounds")
489+
490+
baseAddress!._legacy_se0349_storeBytes_internal(
491+
of: value, toByteOffset: offset, as: T.self
492+
)
420493
}
421494

422495
/// Copies the bytes from the given buffer to this buffer's memory.

stdlib/public/core/UnsafeRawPointer.swift

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,6 @@ public struct UnsafeRawPointer: _Pointer {
424424
let rawPointer = (self + offset)._rawValue
425425

426426
#if compiler(>=5.5) && $BuiltinAssumeAlignment
427-
// TODO: to support misaligned raw loads, simply remove this assumption.
428427
let alignedPointer =
429428
Builtin.assumeAlignment(rawPointer,
430429
MemoryLayout<T>.alignment._builtinWordValue)
@@ -434,6 +433,36 @@ public struct UnsafeRawPointer: _Pointer {
434433
#endif
435434
}
436435

436+
/// Returns a new instance of the given type, constructed from the raw memory
437+
/// at the specified offset.
438+
///
439+
/// This function only supports loading trivial types,
440+
/// and will trap if this precondition is not met.
441+
/// A trivial type does not contain any reference-counted property
442+
/// within its in-memory representation.
443+
/// The memory at this pointer plus `offset` must be laid out
444+
/// identically to the in-memory representation of `T`.
445+
///
446+
/// - Note: A trivial type can be copied with just a bit-for-bit copy without
447+
/// any indirection or reference-counting operations. Generally, native
448+
/// Swift types that do not contain strong or weak references or other
449+
/// forms of indirection are trivial, as are imported C structs and enums.
450+
///
451+
/// - Parameters:
452+
/// - offset: The offset from this pointer, in bytes. `offset` must be
453+
/// nonnegative. The default is zero.
454+
/// - type: The type of the instance to create.
455+
/// - Returns: A new instance of type `T`, read from the raw bytes at
456+
/// `offset`. The returned instance isn't associated
457+
/// with the value in the range of memory referenced by this pointer.
458+
@_alwaysEmitIntoClient
459+
public func loadUnaligned<T>(
460+
fromByteOffset offset: Int = 0,
461+
as type: T.Type
462+
) -> T {
463+
_debugPrecondition(_isPOD(T.self))
464+
return Builtin.loadRaw((self + offset)._rawValue)
465+
}
437466
}
438467

439468
extension UnsafeRawPointer: Strideable {
@@ -1120,7 +1149,6 @@ public struct UnsafeMutableRawPointer: _Pointer {
11201149
let rawPointer = (self + offset)._rawValue
11211150

11221151
#if compiler(>=5.5) && $BuiltinAssumeAlignment
1123-
// TODO: to support misaligned raw loads, simply remove this assumption.
11241152
let alignedPointer =
11251153
Builtin.assumeAlignment(rawPointer,
11261154
MemoryLayout<T>.alignment._builtinWordValue)
@@ -1130,11 +1158,41 @@ public struct UnsafeMutableRawPointer: _Pointer {
11301158
#endif
11311159
}
11321160

1161+
/// Returns a new instance of the given type, constructed from the raw memory
1162+
/// at the specified offset.
1163+
///
1164+
/// This function only supports loading trivial types,
1165+
/// and will trap if this precondition is not met.
1166+
/// A trivial type does not contain any reference-counted property
1167+
/// within its in-memory representation.
1168+
/// The memory at this pointer plus `offset` must be laid out
1169+
/// identically to the in-memory representation of `T`.
1170+
///
1171+
/// - Note: A trivial type can be copied with just a bit-for-bit copy without
1172+
/// any indirection or reference-counting operations. Generally, native
1173+
/// Swift types that do not contain strong or weak references or other
1174+
/// forms of indirection are trivial, as are imported C structs and enums.
1175+
///
1176+
/// - Parameters:
1177+
/// - offset: The offset from this pointer, in bytes. `offset` must be
1178+
/// nonnegative. The default is zero.
1179+
/// - type: The type of the instance to create.
1180+
/// - Returns: A new instance of type `T`, read from the raw bytes at
1181+
/// `offset`. The returned instance isn't associated
1182+
/// with the value in the range of memory referenced by this pointer.
1183+
@_alwaysEmitIntoClient
1184+
public func loadUnaligned<T>(
1185+
fromByteOffset offset: Int = 0,
1186+
as type: T.Type
1187+
) -> T {
1188+
_debugPrecondition(_isPOD(T.self))
1189+
return Builtin.loadRaw((self + offset)._rawValue)
1190+
}
1191+
11331192
/// Stores the given value's bytes into raw memory at the specified offset.
11341193
///
1135-
/// The type `T` to be stored must be a trivial type. The memory at this
1136-
/// pointer plus `offset` must be properly aligned for accessing `T`. The
1137-
/// memory must also be uninitialized, initialized to `T`, or initialized to
1194+
/// The type `T` to be stored must be a trivial type. The memory
1195+
/// must also be uninitialized, initialized to `T`, or initialized to
11381196
/// another trivial type that is layout compatible with `T`.
11391197
///
11401198
/// After calling `storeBytes(of:toByteOffset:as:)`, the memory is
@@ -1148,14 +1206,14 @@ public struct UnsafeMutableRawPointer: _Pointer {
11481206
/// Swift types that do not contain strong or weak references or other
11491207
/// forms of indirection are trivial, as are imported C structs and enums.
11501208
///
1151-
/// If you need to store a copy of a nontrivial value into memory, or to
1152-
/// store a value into memory that contains a nontrivial value, you cannot
1153-
/// use the `storeBytes(of:toByteOffset:as:)` method. Instead, you must know
1154-
/// the type of value previously in memory and initialize or assign the
1155-
/// memory. For example, to replace a value stored in a raw pointer `p`,
1209+
/// If you need to store into memory a copy of a value of a type that isn't
1210+
/// trivial, you cannot use the `storeBytes(of:toByteOffset:as:)` method.
1211+
/// Instead, you must know either initialize the memory or,
1212+
/// if you know the memory was already bound to `type`, assign to the memory.
1213+
/// For example, to replace a value stored in a raw pointer `p`,
11561214
/// where `U` is the current type and `T` is the new type, use a typed
11571215
/// pointer to access and deinitialize the current value before initializing
1158-
/// the memory with a new value.
1216+
/// the memory with a new value:
11591217
///
11601218
/// let typedPointer = p.bindMemory(to: U.self, capacity: 1)
11611219
/// typedPointer.deinitialize(count: 1)
@@ -1167,8 +1225,41 @@ public struct UnsafeMutableRawPointer: _Pointer {
11671225
/// nonnegative. The default is zero.
11681226
/// - type: The type of `value`.
11691227
@inlinable
1228+
@_alwaysEmitIntoClient
1229+
@_silgen_name("_swift_se0349_UnsafeMutableRawPointer_storeBytes")
11701230
public func storeBytes<T>(
11711231
of value: T, toByteOffset offset: Int = 0, as type: T.Type
1232+
) {
1233+
_debugPrecondition(_isPOD(T.self))
1234+
1235+
withUnsafePointer(to: value) { source in
1236+
// FIXME: to be replaced by _memcpy when conversions are implemented.
1237+
Builtin.int_memcpy_RawPointer_RawPointer_Int64(
1238+
(self + offset)._rawValue,
1239+
source._rawValue,
1240+
UInt64(MemoryLayout<T>.size)._value,
1241+
/*volatile:*/ false._value
1242+
)
1243+
}
1244+
}
1245+
1246+
// This unavailable implementation uses the expected mangled name
1247+
// of `storeBytes<T>(of:toByteOffset:as:)`, and provides an entry point for
1248+
// any binary compiled against the stlib binary for Swift 5.6 and older.
1249+
@available(*, unavailable)
1250+
@_silgen_name("sSv10storeBytes2of12toByteOffset2asyx_SixmtlF")
1251+
@usableFromInline func _legacy_se0349_storeBytes<T>(
1252+
of value: T, toByteOffset offset: Int = 0, as type: T.Type
1253+
) {
1254+
_legacy_se0349_storeBytes_internal(
1255+
of: value, toByteOffset: offset, as: T.self
1256+
)
1257+
}
1258+
1259+
// This is the implementation of `storeBytes` from SwiftStdlib 5.6
1260+
@_alwaysEmitIntoClient
1261+
internal func _legacy_se0349_storeBytes_internal<T>(
1262+
of value: T, toByteOffset offset: Int = 0, as type: T.Type
11721263
) {
11731264
_debugPrecondition(0 == (UInt(bitPattern: self + offset)
11741265
& (UInt(MemoryLayout<T>.alignment) - 1)),
@@ -1183,7 +1274,7 @@ public struct UnsafeMutableRawPointer: _Pointer {
11831274
/*volatile:*/ false._value)
11841275
}
11851276
}
1186-
1277+
11871278
/// Copies the specified number of bytes from the given raw pointer's memory
11881279
/// into this pointer's memory.
11891280
///

test/api-digester/stability-stdlib-abi-without-asserts.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,7 @@ Func UnsafeBufferPointer.withMemoryRebound(to:_:) has been removed
8989
Func UnsafeMutableBufferPointer.withMemoryRebound(to:_:) has been removed
9090
Func UnsafeMutablePointer.withMemoryRebound(to:capacity:_:) has been removed
9191
Func UnsafePointer.withMemoryRebound(to:capacity:_:) has been removed
92+
Func UnsafeMutableRawBufferPointer.storeBytes(of:toByteOffset:as:) has been removed
93+
Func UnsafeMutableRawPointer.storeBytes(of:toByteOffset:as:) has been removed
9294

9395
// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)

0 commit comments

Comments
 (0)