Skip to content

Commit 1ac8e2c

Browse files
committed
Handle arguments with alignment larger than a word in KeyPath
1 parent 8bba641 commit 1ac8e2c

File tree

1 file changed

+140
-65
lines changed

1 file changed

+140
-65
lines changed

stdlib/public/core/KeyPath.swift

Lines changed: 140 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,44 @@ internal enum KeyPathComputedIDResolution {
11191119
case functionCall
11201120
}
11211121

1122+
internal struct ComputedArgumentSize {
1123+
let value: UInt
1124+
1125+
static var sizeMask: UInt {
1126+
#if _pointerBitWidth(_64)
1127+
0x7FFF_FFFF_FFFF_FFFF
1128+
#elseif _pointerBitWidth(_32)
1129+
0x3FFF_FFFF
1130+
#else
1131+
#error("Unsupported platform")
1132+
#endif
1133+
}
1134+
1135+
static var paddingShift: UInt {
1136+
#if _pointerBitWidth(_64)
1137+
63
1138+
#elseif _pointerBitWidth(_32)
1139+
30
1140+
#else
1141+
#error("Unsupported platform")
1142+
#endif
1143+
}
1144+
1145+
// The total size of the argument buffer is in the bottom 30/63 bits.
1146+
var size: Int {
1147+
Int(truncatingIfNeeded: value & Self.sizeMask)
1148+
}
1149+
1150+
// The number of padding bytes required so that the argument is properly
1151+
// aligned in the keypath buffer. This is in the top 2 bits for 32 bit
1152+
// platforms (because they need to represent 8 and 16 overaligned types) and
1153+
// top 1 bit for 64 bit platforms (because they only need to represent 16
1154+
// alignment).
1155+
var padding: Int {
1156+
Int(truncatingIfNeeded: value &>> Self.paddingShift) &* MemoryLayout<Int>.size
1157+
}
1158+
}
1159+
11221160
@_unavailableInEmbedded
11231161
@safe
11241162
internal struct RawKeyPathComponent {
@@ -1534,7 +1572,7 @@ internal struct RawKeyPathComponent {
15341572
// two words for argument header: size, witnesses
15351573
total &+= ptrSize &* 2
15361574
// size of argument area
1537-
total &+= _computedArgumentSize
1575+
total &+= _computedArgumentSize.size
15381576
if header.isComputedInstantiatedFromExternalWithArguments {
15391577
total &+= Header.externalWithArgumentsExtraSize
15401578
}
@@ -1592,8 +1630,8 @@ internal struct RawKeyPathComponent {
15921630
(header.isComputedSettable ? 3 : 2)
15931631
}
15941632

1595-
internal var _computedArgumentSize: Int {
1596-
return unsafe _computedArgumentHeaderPointer.load(as: Int.self)
1633+
internal var _computedArgumentSize: ComputedArgumentSize {
1634+
return unsafe _computedArgumentHeaderPointer.load(as: ComputedArgumentSize.self)
15971635
}
15981636
internal
15991637
var _computedArgumentWitnesses: ComputedArgumentWitnessesPtr {
@@ -1611,6 +1649,10 @@ internal struct RawKeyPathComponent {
16111649
if header.isComputedInstantiatedFromExternalWithArguments {
16121650
unsafe base += Header.externalWithArgumentsExtraSize
16131651
}
1652+
1653+
// If the argument needed padding to be properly aligned, skip that.
1654+
unsafe base += _computedArgumentSize.padding
1655+
16141656
return unsafe base
16151657
}
16161658
internal var _computedMutableArguments: UnsafeMutableRawPointer {
@@ -1648,7 +1690,7 @@ internal struct RawKeyPathComponent {
16481690
if header.hasComputedArguments {
16491691
unsafe argument = unsafe KeyPathComponent.ArgumentRef(
16501692
data: UnsafeRawBufferPointer(start: _computedArguments,
1651-
count: _computedArgumentSize),
1693+
count: _computedArgumentSize.size &- _computedArgumentSize.padding),
16521694
witnesses: _computedArgumentWitnesses,
16531695
witnessSizeAdjustment: _computedArgumentWitnessSizeAdjustment)
16541696
} else {
@@ -1688,7 +1730,7 @@ internal struct RawKeyPathComponent {
16881730
if header.hasComputedArguments,
16891731
let destructor = unsafe _computedArgumentWitnesses.destroy {
16901732
unsafe destructor(_computedMutableArguments,
1691-
_computedArgumentSize &- _computedArgumentWitnessSizeAdjustment)
1733+
_computedArgumentSize.size &- _computedArgumentWitnessSizeAdjustment)
16921734
}
16931735
case .external:
16941736
_internalInvariantFailure("should have been instantiated away")
@@ -1741,12 +1783,14 @@ internal struct RawKeyPathComponent {
17411783
componentSize += MemoryLayout<Int>.size
17421784
}
17431785

1786+
// FIXME: Need to handle if buffer is already aligned when copying
1787+
// arguments which would impact the argument size word.
17441788
if header.hasComputedArguments {
17451789
let arguments = unsafe _computedArguments
17461790
let argumentSize = _computedArgumentSize
17471791
unsafe buffer.storeBytes(of: argumentSize,
17481792
toByteOffset: componentSize,
1749-
as: Int.self)
1793+
as: ComputedArgumentSize.self)
17501794
componentSize += MemoryLayout<Int>.size
17511795
unsafe buffer.storeBytes(of: _computedArgumentWitnesses,
17521796
toByteOffset: componentSize,
@@ -1761,7 +1805,7 @@ internal struct RawKeyPathComponent {
17611805
as: Int.self)
17621806
componentSize += MemoryLayout<Int>.size
17631807
}
1764-
let adjustedSize = argumentSize - _computedArgumentWitnessSizeAdjustment
1808+
let adjustedSize = argumentSize.size - _computedArgumentWitnessSizeAdjustment
17651809
let argumentDest =
17661810
unsafe buffer.baseAddress.unsafelyUnwrapped + componentSize
17671811
unsafe _computedArgumentWitnesses.copy(
@@ -1776,7 +1820,7 @@ internal struct RawKeyPathComponent {
17761820
size: UInt(_computedArgumentWitnessSizeAdjustment))
17771821
}
17781822

1779-
componentSize += argumentSize
1823+
componentSize += argumentSize.size
17801824
}
17811825

17821826
case .external:
@@ -3527,46 +3571,44 @@ internal struct GetKeyPathClassAndInstanceSizeFromPattern
35273571
if settable {
35283572
unsafe size += MemoryLayout<Int>.size
35293573
}
3530-
3531-
// ...and the arguments, if any.
3532-
let argumentHeaderSize = MemoryLayout<Int>.size * 2
3533-
switch unsafe (arguments, externalArgs) {
3534-
case (nil, nil):
3535-
break
3536-
case (let arguments?, nil):
3537-
unsafe size += argumentHeaderSize
3538-
// If we have arguments, calculate how much space they need by invoking
3539-
// the layout function.
3540-
let (addedSize, addedAlignmentMask) = unsafe arguments.getLayout(patternArgs)
3541-
// TODO: Handle over-aligned values
3542-
_internalInvariant(addedAlignmentMask < MemoryLayout<Int>.alignment,
3543-
"overaligned computed property element not supported")
3544-
unsafe size += addedSize
3545-
3546-
case (let arguments?, let externalArgs?):
3547-
// If we're referencing an external declaration, and it takes captured
3548-
// arguments, then we have to build a bit of a chimera. The canonical
3549-
// identity and accessors come from the descriptor, but the argument
3550-
// handling is still as described in the local candidate.
3551-
unsafe size += argumentHeaderSize
3552-
let (addedSize, addedAlignmentMask) = unsafe arguments.getLayout(patternArgs)
3553-
// TODO: Handle over-aligned values
3554-
_internalInvariant(addedAlignmentMask < MemoryLayout<Int>.alignment,
3555-
"overaligned computed property element not supported")
3556-
unsafe size += addedSize
3574+
3575+
// Handle local arguments.
3576+
if let arguments = arguments {
3577+
// Argument size and witnesses ptr.
3578+
unsafe size &+= MemoryLayout<Int>.size &* 2
3579+
3580+
let (typeSize, typeAlignMask) = unsafe arguments.getLayout(patternArgs)
3581+
3582+
// We are known to be pointer aligned at this point in the KeyPath buffer.
3583+
// However, for types who have an alignment large than pointers, we need
3584+
// to determine if the current position is suitable for the argument, or
3585+
// if we need to add padding bytes to align ourselves.
3586+
let misalign = unsafe size & typeAlignMask
3587+
3588+
if misalign != 0 {
3589+
let typeAlignment = typeAlignMask &+ 1
3590+
let padding = Swift.max(0, typeAlignment &- MemoryLayout<Int>.alignment)
3591+
unsafe size &+= padding
3592+
}
3593+
3594+
unsafe size &+= typeSize
3595+
unsafe roundUpToPointerAlignment()
3596+
}
3597+
3598+
// Handle external arguments.
3599+
if let externalArgs = externalArgs {
3600+
// Argument size and witnesses ptr if we didn't have local arguments.
3601+
if arguments == nil {
3602+
unsafe size &+= MemoryLayout<Int>.size &* 2
3603+
}
3604+
35573605
// We also need to store the size of the local arguments so we can
35583606
// find the external component arguments.
3559-
unsafe roundUpToPointerAlignment()
3560-
unsafe size += RawKeyPathComponent.Header.externalWithArgumentsExtraSize
3561-
unsafe size += MemoryLayout<Int>.size * externalArgs.count
3607+
if arguments != nil {
3608+
unsafe size &+= RawKeyPathComponent.Header.externalWithArgumentsExtraSize
3609+
}
35623610

3563-
case (nil, let externalArgs?):
3564-
// If we're instantiating an external property with a local
3565-
// candidate that has no arguments, then things are a little
3566-
// easier. We only need to instantiate the generic
3567-
// arguments for the external component's accessors.
3568-
unsafe size += argumentHeaderSize
3569-
unsafe size += MemoryLayout<Int>.size * externalArgs.count
3611+
unsafe size &+= MemoryLayout<Int>.size &* externalArgs.count
35703612
}
35713613
}
35723614

@@ -3599,8 +3641,7 @@ internal struct GetKeyPathClassAndInstanceSizeFromPattern
35993641
}
36003642

36013643
mutating func finish() {
3602-
unsafe sizeWithMaxSize = unsafe size
3603-
unsafe sizeWithMaxSize = unsafe MemoryLayout<Int>._roundingUpToAlignment(sizeWithMaxSize)
3644+
unsafe sizeWithMaxSize = unsafe MemoryLayout<Int>._roundingUpToAlignment(size)
36043645
unsafe sizeWithMaxSize &+= MemoryLayout<Int>.size
36053646
}
36063647
}
@@ -3891,22 +3932,13 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor {
38913932
}
38923933

38933934
if let arguments = unsafe arguments {
3894-
// Instantiate the arguments.
3895-
let (baseSize, alignmentMask) = unsafe arguments.getLayout(patternArgs)
3896-
_internalInvariant(alignmentMask < MemoryLayout<Int>.alignment,
3897-
"overaligned computed arguments not implemented yet")
3898-
3899-
// The real buffer stride will be rounded up to alignment.
3900-
var totalSize = (baseSize + alignmentMask) & ~alignmentMask
3901-
3902-
// If an external property descriptor also has arguments, they'll be
3903-
// added to the end with pointer alignment.
3904-
if let externalArgs = unsafe externalArgs {
3905-
totalSize = MemoryLayout<Int>._roundingUpToAlignment(totalSize)
3906-
totalSize += MemoryLayout<Int>.size * externalArgs.count
3907-
}
3908-
3935+
// Record a placeholder for the total size of the arguments. We need to
3936+
// check after pushing preliminary data if the resulting argument will
3937+
// require padding bytes to be properly aligned.
3938+
let totalSizeAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
3939+
var totalSize: UInt = 0
39093940
unsafe pushDest(totalSize)
3941+
39103942
unsafe pushDest(arguments.witnesses)
39113943

39123944
// A nonnull destructor in the witnesses file indicates the instantiated
@@ -3922,15 +3954,58 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor {
39223954
unsafe pushDest(externalArgs.count * MemoryLayout<Int>.size)
39233955
}
39243956

3957+
let (typeSize, typeAlignMask) = unsafe arguments.getLayout(patternArgs)
3958+
let typeAlignment = typeAlignMask &+ 1
3959+
let padding = Swift.max(0, typeAlignment &- MemoryLayout<Int>.alignment)
3960+
3961+
totalSize = UInt(truncatingIfNeeded: typeSize)
3962+
3963+
// We are known to be pointer aligned at this point in the KeyPath buffer.
3964+
// However, for types who have an alignment large than pointers, we need
3965+
// to determine if the current position is suitable for the argument, or
3966+
// if we need to add padding bytes to align ourselves.
3967+
let argumentAddress = unsafe destData.baseAddress._unsafelyUnwrappedUnchecked
3968+
let misalign = Int(bitPattern: argumentAddress) & typeAlignMask
3969+
3970+
if misalign != 0 {
3971+
totalSize &+= UInt(truncatingIfNeeded: padding)
3972+
3973+
// Go ahead and append the padding.
3974+
for _ in 0 ..< padding {
3975+
unsafe pushDest(UInt8(0))
3976+
}
3977+
}
3978+
3979+
// If an external property descriptor also has arguments, they'll be
3980+
// added to the end with pointer alignment.
3981+
if let externalArgs = unsafe externalArgs {
3982+
totalSize = MemoryLayout<Int>._roundingUpToAlignment(totalSize)
3983+
totalSize += UInt(truncatingIfNeeded: MemoryLayout<Int>.size * externalArgs.count)
3984+
}
3985+
3986+
// The argument total size contains the padding bytes required for the
3987+
// argument in the top 4 bits, so ensure the size of the argument buffer
3988+
// is small enough to account for that.
3989+
_precondition(
3990+
totalSize <= ComputedArgumentSize.sizeMask,
3991+
"keypath arguments are too large"
3992+
)
3993+
3994+
totalSize |= UInt(truncatingIfNeeded: padding / MemoryLayout<Int>.size) &<< ComputedArgumentSize.paddingShift
3995+
3996+
// Ok, we've fully calculated totalSize, go ahead and update the
3997+
// placeholder.
3998+
unsafe totalSizeAddress.storeBytes(of: totalSize, as: UInt.self)
3999+
39254000
// Initialize the local candidate arguments here.
3926-
unsafe _internalInvariant(Int(bitPattern: destData.baseAddress) & alignmentMask == 0,
4001+
unsafe _internalInvariant(Int(bitPattern: destData.baseAddress) & typeAlignMask == 0,
39274002
"argument destination not aligned")
39284003
unsafe arguments.initializer(patternArgs,
39294004
destData.baseAddress._unsafelyUnwrappedUnchecked)
39304005

39314006
unsafe destData = unsafe UnsafeMutableRawBufferPointer(
3932-
start: destData.baseAddress._unsafelyUnwrappedUnchecked + baseSize,
3933-
count: destData.count - baseSize)
4007+
start: destData.baseAddress._unsafelyUnwrappedUnchecked + typeSize,
4008+
count: destData.count - typeSize)
39344009
}
39354010

39364011
if let externalArgs = unsafe externalArgs {

0 commit comments

Comments
 (0)