mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Correctly handle alignment and appending keypaths
Add test for overaligned keypath arguments
This commit is contained in:
@@ -1126,7 +1126,17 @@ internal struct ComputedArgumentSize {
|
|||||||
#if _pointerBitWidth(_64)
|
#if _pointerBitWidth(_64)
|
||||||
0x7FFF_FFFF_FFFF_FFFF
|
0x7FFF_FFFF_FFFF_FFFF
|
||||||
#elseif _pointerBitWidth(_32)
|
#elseif _pointerBitWidth(_32)
|
||||||
0x3FFF_FFFF
|
0x1FFF_FFFF
|
||||||
|
#else
|
||||||
|
#error("Unsupported platform")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static var paddingMask: UInt {
|
||||||
|
#if _pointerBitWidth(_64)
|
||||||
|
0x8000_0000_0000_0000
|
||||||
|
#elseif _pointerBitWidth(_32)
|
||||||
|
0x6000_0000
|
||||||
#else
|
#else
|
||||||
#error("Unsupported platform")
|
#error("Unsupported platform")
|
||||||
#endif
|
#endif
|
||||||
@@ -1136,14 +1146,34 @@ internal struct ComputedArgumentSize {
|
|||||||
#if _pointerBitWidth(_64)
|
#if _pointerBitWidth(_64)
|
||||||
63
|
63
|
||||||
#elseif _pointerBitWidth(_32)
|
#elseif _pointerBitWidth(_32)
|
||||||
30
|
29
|
||||||
#else
|
#else
|
||||||
#error("Unsupported platform")
|
#error("Unsupported platform")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// The total size of the argument buffer is in the bottom 30/63 bits.
|
static var alignmentShift: UInt {
|
||||||
var size: Int {
|
#if _pointerBitWidth(_64)
|
||||||
|
// On 64 bit platforms, we don't need to record alignment in this structure.
|
||||||
|
0
|
||||||
|
#elseif _pointerBitWidth(_32)
|
||||||
|
31
|
||||||
|
#else
|
||||||
|
#error("Unsupported platform")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ argSize: Int) {
|
||||||
|
value = UInt(truncatingIfNeeded: argSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The size of just the arguments only, ignoring padding.
|
||||||
|
var argumentSize: Int {
|
||||||
|
totalSize &- padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// The total size of the argument buffer is in the bottom 29/63 bits.
|
||||||
|
var totalSize: Int {
|
||||||
Int(truncatingIfNeeded: value & Self.sizeMask)
|
Int(truncatingIfNeeded: value & Self.sizeMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1153,7 +1183,56 @@ internal struct ComputedArgumentSize {
|
|||||||
// top 1 bit for 64 bit platforms (because they only need to represent 16
|
// top 1 bit for 64 bit platforms (because they only need to represent 16
|
||||||
// alignment).
|
// alignment).
|
||||||
var padding: Int {
|
var padding: Int {
|
||||||
Int(truncatingIfNeeded: value &>> Self.paddingShift) &* MemoryLayout<Int>.size
|
get {
|
||||||
|
let mask = value & Self.paddingMask
|
||||||
|
let shift = mask &>> Self.paddingShift
|
||||||
|
return Int(truncatingIfNeeded: shift) &* MemoryLayout<Int>.size
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
let reduced = newValue / MemoryLayout<Int>.size
|
||||||
|
let shift = reduced &<< Self.paddingShift
|
||||||
|
value &= ~Self.paddingMask
|
||||||
|
value |= shift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the arguments have an alignment greater than the platform's pointer
|
||||||
|
// alignment, then this structure records the argument's alignment. On 64 bit
|
||||||
|
// platforms, we don't actually record this in the bit pattern anywhere, but
|
||||||
|
// on 32 bit platforms we use the top bit to indicate either 8 or 16 byte
|
||||||
|
// alignment. This returns 0 if no padding was recorded.
|
||||||
|
var alignment: Int {
|
||||||
|
get {
|
||||||
|
// If we didn't record a padding value, then this argument's alignment was
|
||||||
|
// either equal to or less than pointer value alignment.
|
||||||
|
guard padding != 0 else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _pointerBitWidth(_64)
|
||||||
|
// On 64 bit, the only higher alignment a type could have is 16 byte.
|
||||||
|
return 16
|
||||||
|
#elseif _pointerBitWidth(_32)
|
||||||
|
// 0 = 8 byte
|
||||||
|
// 1 = 16 byte
|
||||||
|
let shift = value &>> Self.alignmentShift
|
||||||
|
return shift == 1 ? 16 : 8
|
||||||
|
#else
|
||||||
|
#error("Unsupported platform")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// The setter should only be called when there is or will be padding.
|
||||||
|
set {
|
||||||
|
#if _pointerBitWidth(_64)
|
||||||
|
// Nop on 64 bit.
|
||||||
|
return
|
||||||
|
#elseif _pointerBitWidth(_32)
|
||||||
|
let reduced = newValue == 16 ? 1 : 0
|
||||||
|
let shift = reduced &<< Self.alignmentShift
|
||||||
|
value |= shift
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1572,7 +1651,7 @@ internal struct RawKeyPathComponent {
|
|||||||
// two words for argument header: size, witnesses
|
// two words for argument header: size, witnesses
|
||||||
total &+= ptrSize &* 2
|
total &+= ptrSize &* 2
|
||||||
// size of argument area
|
// size of argument area
|
||||||
total &+= _computedArgumentSize.size
|
total &+= _computedArgumentSize.totalSize
|
||||||
if header.isComputedInstantiatedFromExternalWithArguments {
|
if header.isComputedInstantiatedFromExternalWithArguments {
|
||||||
total &+= Header.externalWithArgumentsExtraSize
|
total &+= Header.externalWithArgumentsExtraSize
|
||||||
}
|
}
|
||||||
@@ -1690,7 +1769,7 @@ internal struct RawKeyPathComponent {
|
|||||||
if header.hasComputedArguments {
|
if header.hasComputedArguments {
|
||||||
unsafe argument = unsafe KeyPathComponent.ArgumentRef(
|
unsafe argument = unsafe KeyPathComponent.ArgumentRef(
|
||||||
data: UnsafeRawBufferPointer(start: _computedArguments,
|
data: UnsafeRawBufferPointer(start: _computedArguments,
|
||||||
count: _computedArgumentSize.size &- _computedArgumentSize.padding),
|
count: _computedArgumentSize.argumentSize),
|
||||||
witnesses: _computedArgumentWitnesses,
|
witnesses: _computedArgumentWitnesses,
|
||||||
witnessSizeAdjustment: _computedArgumentWitnessSizeAdjustment)
|
witnessSizeAdjustment: _computedArgumentWitnessSizeAdjustment)
|
||||||
} else {
|
} else {
|
||||||
@@ -1730,15 +1809,18 @@ internal struct RawKeyPathComponent {
|
|||||||
if header.hasComputedArguments,
|
if header.hasComputedArguments,
|
||||||
let destructor = unsafe _computedArgumentWitnesses.destroy {
|
let destructor = unsafe _computedArgumentWitnesses.destroy {
|
||||||
unsafe destructor(_computedMutableArguments,
|
unsafe destructor(_computedMutableArguments,
|
||||||
_computedArgumentSize.size &- _computedArgumentWitnessSizeAdjustment)
|
_computedArgumentSize.argumentSize &- _computedArgumentWitnessSizeAdjustment)
|
||||||
}
|
}
|
||||||
case .external:
|
case .external:
|
||||||
_internalInvariantFailure("should have been instantiated away")
|
_internalInvariantFailure("should have been instantiated away")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func clone(into buffer: inout UnsafeMutableRawBufferPointer,
|
internal func clone(
|
||||||
endOfReferencePrefix: Bool) {
|
into buffer: inout UnsafeMutableRawBufferPointer,
|
||||||
|
endOfReferencePrefix: Bool,
|
||||||
|
adjustForAlignment: Bool
|
||||||
|
) {
|
||||||
var newHeader = header
|
var newHeader = header
|
||||||
newHeader.endOfReferencePrefix = endOfReferencePrefix
|
newHeader.endOfReferencePrefix = endOfReferencePrefix
|
||||||
|
|
||||||
@@ -1783,15 +1865,27 @@ internal struct RawKeyPathComponent {
|
|||||||
componentSize += MemoryLayout<Int>.size
|
componentSize += MemoryLayout<Int>.size
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Need to handle if buffer is already aligned when copying
|
|
||||||
// arguments which would impact the argument size word.
|
|
||||||
if header.hasComputedArguments {
|
if header.hasComputedArguments {
|
||||||
let arguments = unsafe _computedArguments
|
let arguments = unsafe _computedArguments
|
||||||
let argumentSize = _computedArgumentSize
|
let argumentSize = _computedArgumentSize
|
||||||
unsafe buffer.storeBytes(of: argumentSize,
|
// Remember the argument size location, we may need to adjust the value
|
||||||
toByteOffset: componentSize,
|
// for alignment.
|
||||||
as: ComputedArgumentSize.self)
|
let argumentSizePtr = unsafe buffer.baseAddress._unsafelyUnwrappedUnchecked + componentSize
|
||||||
|
|
||||||
|
// If we don't need to adjust for alignment, then the current argument
|
||||||
|
// size is correct. Or, if the arguments themselves don't require any
|
||||||
|
// padding whatsoever. If alignment is 0, then the arguments had equal
|
||||||
|
// to or less than alignment to the platform's word alignment.
|
||||||
|
if !adjustForAlignment || argumentSize.alignment == 0 {
|
||||||
|
unsafe buffer.storeBytes(
|
||||||
|
of: argumentSize,
|
||||||
|
toByteOffset: componentSize,
|
||||||
|
as: ComputedArgumentSize.self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
componentSize += MemoryLayout<Int>.size
|
componentSize += MemoryLayout<Int>.size
|
||||||
|
|
||||||
unsafe buffer.storeBytes(of: _computedArgumentWitnesses,
|
unsafe buffer.storeBytes(of: _computedArgumentWitnesses,
|
||||||
toByteOffset: componentSize,
|
toByteOffset: componentSize,
|
||||||
as: ComputedArgumentWitnessesPtr.self)
|
as: ComputedArgumentWitnessesPtr.self)
|
||||||
@@ -1805,13 +1899,49 @@ internal struct RawKeyPathComponent {
|
|||||||
as: Int.self)
|
as: Int.self)
|
||||||
componentSize += MemoryLayout<Int>.size
|
componentSize += MemoryLayout<Int>.size
|
||||||
}
|
}
|
||||||
let adjustedSize = argumentSize.size - _computedArgumentWitnessSizeAdjustment
|
|
||||||
let argumentDest =
|
let adjustedSize = argumentSize.argumentSize &-
|
||||||
|
_computedArgumentWitnessSizeAdjustment
|
||||||
|
var argumentDest =
|
||||||
unsafe buffer.baseAddress.unsafelyUnwrapped + componentSize
|
unsafe buffer.baseAddress.unsafelyUnwrapped + componentSize
|
||||||
|
|
||||||
|
// We've been asked to adjust for alignment and the original argument
|
||||||
|
// was overaligned. Check for misalignment at our current destination.
|
||||||
|
if adjustForAlignment, argumentSize.alignment != 0 {
|
||||||
|
let argumentAlignmentMask = argumentSize.alignment &- 1
|
||||||
|
let misaligned = Int(bitPattern: argumentDest) & argumentAlignmentMask
|
||||||
|
|
||||||
|
// Ok, this is no longer misaligned. We don't need to record any
|
||||||
|
// padding.
|
||||||
|
if misaligned == 0 {
|
||||||
|
unsafe argumentSizePtr.storeBytes(
|
||||||
|
of: argumentSize.argumentSize,
|
||||||
|
as: Int.self
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Otherwise, this is still misaligned and we need to calculate
|
||||||
|
// how much padding we need.
|
||||||
|
var newArgumentSize = ComputedArgumentSize(argumentSize.argumentSize)
|
||||||
|
|
||||||
|
newArgumentSize.padding = argumentSize.alignment &- misaligned
|
||||||
|
newArgumentSize.alignment = argumentSize.alignment
|
||||||
|
|
||||||
|
unsafe argumentSizePtr.storeBytes(
|
||||||
|
of: newArgumentSize,
|
||||||
|
as: ComputedArgumentSize.self
|
||||||
|
)
|
||||||
|
|
||||||
|
// Push the buffer past the padding.
|
||||||
|
unsafe argumentDest += padding
|
||||||
|
componentSize += padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe _computedArgumentWitnesses.copy(
|
unsafe _computedArgumentWitnesses.copy(
|
||||||
arguments,
|
arguments,
|
||||||
argumentDest,
|
argumentDest,
|
||||||
adjustedSize)
|
adjustedSize)
|
||||||
|
|
||||||
if header.isComputedInstantiatedFromExternalWithArguments {
|
if header.isComputedInstantiatedFromExternalWithArguments {
|
||||||
// The extra information for external property descriptor arguments
|
// The extra information for external property descriptor arguments
|
||||||
// can always be memcpy'd.
|
// can always be memcpy'd.
|
||||||
@@ -1820,7 +1950,7 @@ internal struct RawKeyPathComponent {
|
|||||||
size: UInt(_computedArgumentWitnessSizeAdjustment))
|
size: UInt(_computedArgumentWitnessSizeAdjustment))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentSize += argumentSize.size
|
componentSize += argumentSize.argumentSize
|
||||||
}
|
}
|
||||||
|
|
||||||
case .external:
|
case .external:
|
||||||
@@ -2766,7 +2896,12 @@ internal func _appendingKeyPaths<
|
|||||||
|
|
||||||
unsafe component.clone(
|
unsafe component.clone(
|
||||||
into: &destBuilder.buffer,
|
into: &destBuilder.buffer,
|
||||||
endOfReferencePrefix: endOfReferencePrefix)
|
endOfReferencePrefix: endOfReferencePrefix,
|
||||||
|
|
||||||
|
// Root components don't need to adjust for alignment because the
|
||||||
|
// components should appear at the same offset as the root keypath.
|
||||||
|
adjustForAlignment: false
|
||||||
|
)
|
||||||
// Insert our endpoint type between the root and leaf components.
|
// Insert our endpoint type between the root and leaf components.
|
||||||
if let type = type {
|
if let type = type {
|
||||||
unsafe destBuilder.push(type)
|
unsafe destBuilder.push(type)
|
||||||
@@ -2784,7 +2919,13 @@ internal func _appendingKeyPaths<
|
|||||||
|
|
||||||
unsafe component.clone(
|
unsafe component.clone(
|
||||||
into: &destBuilder.buffer,
|
into: &destBuilder.buffer,
|
||||||
endOfReferencePrefix: component.header.endOfReferencePrefix)
|
endOfReferencePrefix: component.header.endOfReferencePrefix,
|
||||||
|
|
||||||
|
// Leaf components, however, can appear at actual aligned spots that
|
||||||
|
// they couldn't before. Adjust computed arguments for this
|
||||||
|
// potential.
|
||||||
|
adjustForAlignment: true
|
||||||
|
)
|
||||||
|
|
||||||
if let type = type {
|
if let type = type {
|
||||||
unsafe destBuilder.push(type)
|
unsafe destBuilder.push(type)
|
||||||
@@ -3583,12 +3724,13 @@ internal struct GetKeyPathClassAndInstanceSizeFromPattern
|
|||||||
// However, for types who have an alignment large than pointers, we need
|
// However, for types who have an alignment large than pointers, we need
|
||||||
// to determine if the current position is suitable for the argument, or
|
// to determine if the current position is suitable for the argument, or
|
||||||
// if we need to add padding bytes to align ourselves.
|
// if we need to add padding bytes to align ourselves.
|
||||||
let misalign = unsafe size & typeAlignMask
|
let misaligned = unsafe size & typeAlignMask
|
||||||
|
|
||||||
if misalign != 0 {
|
if misaligned != 0 {
|
||||||
let typeAlignment = typeAlignMask &+ 1
|
// We were misaligned, add the padding to the total size of the keypath
|
||||||
let padding = Swift.max(0, typeAlignment &- MemoryLayout<Int>.alignment)
|
// buffer.
|
||||||
unsafe size &+= padding
|
let typeAlign = typeAlignMask &+ 1
|
||||||
|
unsafe size &+= typeAlign &- misalign
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe size &+= typeSize
|
unsafe size &+= typeSize
|
||||||
@@ -3936,8 +4078,7 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor {
|
|||||||
// check after pushing preliminary data if the resulting argument will
|
// check after pushing preliminary data if the resulting argument will
|
||||||
// require padding bytes to be properly aligned.
|
// require padding bytes to be properly aligned.
|
||||||
let totalSizeAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
|
let totalSizeAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
|
||||||
var totalSize: UInt = 0
|
unsafe pushDest(ComputedArgumentSize(0))
|
||||||
unsafe pushDest(totalSize)
|
|
||||||
|
|
||||||
unsafe pushDest(arguments.witnesses)
|
unsafe pushDest(arguments.witnesses)
|
||||||
|
|
||||||
@@ -3955,47 +4096,49 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (typeSize, typeAlignMask) = unsafe arguments.getLayout(patternArgs)
|
let (typeSize, typeAlignMask) = unsafe arguments.getLayout(patternArgs)
|
||||||
let typeAlignment = typeAlignMask &+ 1
|
var argumentSize = UInt(truncatingIfNeeded: typeSize)
|
||||||
let padding = Swift.max(0, typeAlignment &- MemoryLayout<Int>.alignment)
|
|
||||||
|
|
||||||
totalSize = UInt(truncatingIfNeeded: typeSize)
|
// If an external property descriptor also has arguments, they'll be
|
||||||
|
// added to the end with pointer alignment.
|
||||||
|
if let externalArgs = unsafe externalArgs {
|
||||||
|
argumentSize = MemoryLayout<Int>._roundingUpToAlignment(totalSize)
|
||||||
|
argumentSize += UInt(truncatingIfNeeded: MemoryLayout<Int>.size * externalArgs.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The argument total size contains the padding and alignment bits
|
||||||
|
// required for the argument in the top 1/3 bits, so ensure the size of
|
||||||
|
// the argument buffer is small enough to account for that.
|
||||||
|
_precondition(
|
||||||
|
argumentSize <= ComputedArgumentSize.sizeMask,
|
||||||
|
"keypath arguments are too large"
|
||||||
|
)
|
||||||
|
|
||||||
|
var totalSize = ComputedArgumentSize(argumentSize)
|
||||||
|
|
||||||
// We are known to be pointer aligned at this point in the KeyPath buffer.
|
// We are known to be pointer aligned at this point in the KeyPath buffer.
|
||||||
// However, for types who have an alignment large than pointers, we need
|
// However, for types who have an alignment large than pointers, we need
|
||||||
// to determine if the current position is suitable for the argument, or
|
// to determine if the current position is suitable for the argument, or
|
||||||
// if we need to add padding bytes to align ourselves.
|
// if we need to add padding bytes to align ourselves.
|
||||||
let argumentAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
|
let argumentAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
|
||||||
let misalign = Int(bitPattern: argumentAddress) & typeAlignMask
|
let misaligned = Int(bitPattern: argumentAddress) & typeAlignMask
|
||||||
|
|
||||||
if misalign != 0 {
|
if misaligned != 0 {
|
||||||
totalSize &+= UInt(truncatingIfNeeded: padding)
|
let typeAlignment = typeAlignMask &+ 1
|
||||||
|
totalSize.padding = typeAlignment &- misaligned
|
||||||
|
totalSize.alignment = typeAlignment
|
||||||
|
|
||||||
// Go ahead and append the padding.
|
// Go ahead and append the padding.
|
||||||
for _ in 0 ..< padding {
|
for _ in 0 ..< totalSize.padding {
|
||||||
unsafe pushDest(UInt8(0))
|
unsafe pushDest(UInt8(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an external property descriptor also has arguments, they'll be
|
|
||||||
// added to the end with pointer alignment.
|
|
||||||
if let externalArgs = unsafe externalArgs {
|
|
||||||
totalSize = MemoryLayout<Int>._roundingUpToAlignment(totalSize)
|
|
||||||
totalSize += UInt(truncatingIfNeeded: MemoryLayout<Int>.size * externalArgs.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The argument total size contains the padding bytes required for the
|
|
||||||
// argument in the top 4 bits, so ensure the size of the argument buffer
|
|
||||||
// is small enough to account for that.
|
|
||||||
_precondition(
|
|
||||||
totalSize <= ComputedArgumentSize.sizeMask,
|
|
||||||
"keypath arguments are too large"
|
|
||||||
)
|
|
||||||
|
|
||||||
totalSize |= UInt(truncatingIfNeeded: padding / MemoryLayout<Int>.size) &<< ComputedArgumentSize.paddingShift
|
|
||||||
|
|
||||||
// Ok, we've fully calculated totalSize, go ahead and update the
|
// Ok, we've fully calculated totalSize, go ahead and update the
|
||||||
// placeholder.
|
// placeholder.
|
||||||
unsafe totalSizeAddress.storeBytes(of: totalSize, as: UInt.self)
|
unsafe totalSizeAddress.storeBytes(
|
||||||
|
of: totalSize,
|
||||||
|
as: ComputedArgumentSize.self
|
||||||
|
)
|
||||||
|
|
||||||
// Initialize the local candidate arguments here.
|
// Initialize the local candidate arguments here.
|
||||||
unsafe _internalInvariant(Int(bitPattern: destData.baseAddress) & typeAlignMask == 0,
|
unsafe _internalInvariant(Int(bitPattern: destData.baseAddress) & typeAlignMask == 0,
|
||||||
@@ -4320,7 +4463,13 @@ public func _createOffsetBasedKeyPath(
|
|||||||
body: UnsafeRawBufferPointer(start: nil, count: 0)
|
body: UnsafeRawBufferPointer(start: nil, count: 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
unsafe component.clone(into: &builder.buffer, endOfReferencePrefix: false)
|
unsafe component.clone(
|
||||||
|
into: &builder.buffer,
|
||||||
|
endOfReferencePrefix: false,
|
||||||
|
|
||||||
|
// The keypath will have the same shape, it's just the types that differ.
|
||||||
|
adjustForAlignment: false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _MetadataKind(root) == .struct {
|
if _MetadataKind(root) == .struct {
|
||||||
@@ -4394,7 +4543,11 @@ public func _rerootKeyPath<NewRoot>(
|
|||||||
|
|
||||||
unsafe rawComponent.clone(
|
unsafe rawComponent.clone(
|
||||||
into: &builder.buffer,
|
into: &builder.buffer,
|
||||||
endOfReferencePrefix: rawComponent.header.endOfReferencePrefix
|
endOfReferencePrefix: rawComponent.header.endOfReferencePrefix,
|
||||||
|
|
||||||
|
// Simply changing the type of the root type, so the resulting buffer
|
||||||
|
// will have the same layout.
|
||||||
|
adjustForAlignment: false
|
||||||
)
|
)
|
||||||
|
|
||||||
if componentTy == nil {
|
if componentTy == nil {
|
||||||
|
|||||||
@@ -378,7 +378,11 @@ public func _forEachFieldWithKeyPath<Root>(
|
|||||||
body: UnsafeRawBufferPointer(start: nil, count: 0))
|
body: UnsafeRawBufferPointer(start: nil, count: 0))
|
||||||
unsafe component.clone(
|
unsafe component.clone(
|
||||||
into: &destBuilder.buffer,
|
into: &destBuilder.buffer,
|
||||||
endOfReferencePrefix: false)
|
endOfReferencePrefix: false,
|
||||||
|
|
||||||
|
// We are just storing offset components and not computed ones.
|
||||||
|
adjustForAlignment: false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let name = unsafe field.name {
|
if let name = unsafe field.name {
|
||||||
|
|||||||
@@ -1159,5 +1159,117 @@ if #available(SwiftStdlib 5.9, *) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@_alignment(8)
|
||||||
|
struct EightAligned {
|
||||||
|
let x = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EightAligned: Equatable {}
|
||||||
|
extension EightAligned: Hashable {}
|
||||||
|
|
||||||
|
@_alignment(16)
|
||||||
|
struct SixteenAligned {
|
||||||
|
let x = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SixteenAligned: Equatable {}
|
||||||
|
extension SixteenAligned: Hashable {}
|
||||||
|
|
||||||
|
struct OveralignedForEight {
|
||||||
|
subscript(getter getter: EightAligned) -> Int {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(setter setter: EightAligned) -> Int {
|
||||||
|
get {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OveralignedForSixteen {
|
||||||
|
subscript(getter getter: SixteenAligned) -> Int {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(setter setter: SixteenAligned) -> Int {
|
||||||
|
get {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SimpleEight {
|
||||||
|
let oa = OveralignedForEight()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SimpleSixteen {
|
||||||
|
let oa = OveralignedForSixteen()
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _pointerBitWidth(_32)
|
||||||
|
if #available(SwiftStdlib 6.3, *) {
|
||||||
|
keyPath.test("eight byte overaligned arguments") {
|
||||||
|
let oa = OveralignedForEight()
|
||||||
|
let kp = \OveralignedForEight.[getter: EightAligned()]
|
||||||
|
|
||||||
|
let value = oa[keyPath: kp]
|
||||||
|
expectEqual(value, 128)
|
||||||
|
|
||||||
|
// Test that appends where the argument is no longer overaligned work
|
||||||
|
let simple = SimpleEight()
|
||||||
|
let kp11 = \SimpleEight.oa
|
||||||
|
let kp12 = \OveralignedForEight.[getter: EightAligned()]
|
||||||
|
let kp1 = kp11.appending(path: kp12)
|
||||||
|
|
||||||
|
let value1 = simple[keyPath: kp1]
|
||||||
|
expectEqual(value1, 128)
|
||||||
|
|
||||||
|
// Test that appends where the argument is still overaligned works
|
||||||
|
let kp21 = \SimpleEight.oa
|
||||||
|
let kp22 = \OveralignedForEight.[setter: EightAligned()]
|
||||||
|
let kp2 = kp21.appending(path: kp22)
|
||||||
|
|
||||||
|
let value2 = simple[keyPath: kp2]
|
||||||
|
expectEqual(value2, 128)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if #available(SwiftStdlib 6.3, *) {
|
||||||
|
keyPath.test("sixteen byte overaligned arguments") {
|
||||||
|
let oa = OveralignedForSixteen()
|
||||||
|
let kp = \OveralignedForSixteen.[SixteenAligned()]
|
||||||
|
|
||||||
|
let value = oa[keyPath: kp]
|
||||||
|
expectEqual(value, 128)
|
||||||
|
|
||||||
|
// Test that appends where the argument is no longer overaligned work
|
||||||
|
let simple = SimpleSixteen()
|
||||||
|
let kp11 = \SimpleSixteen.oa
|
||||||
|
let kp12 = \OveralignedForSixteen.[getter: SixteenAligned()]
|
||||||
|
let kp1 = kp11.appending(path: kp12)
|
||||||
|
|
||||||
|
let value1 = simple[keyPath: kp1]
|
||||||
|
expectEqual(value1, 128)
|
||||||
|
|
||||||
|
// Test that appends where the argument is still overaligned works
|
||||||
|
let kp21 = \SimpleSixteen.oa
|
||||||
|
let kp22 = \OveralignedForSixteen.[setter: SixteenAligned()]
|
||||||
|
let kp2 = kp21.appending(path: kp22)
|
||||||
|
|
||||||
|
let value2 = simple[keyPath: kp2]
|
||||||
|
expectEqual(value2, 128)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runAllTests()
|
runAllTests()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user