Skip to content

Commit cb5a225

Browse files
committed
Merge pull request #2528 from apple/stdlib-dictionary-refactor
[WIP, don't merge] stdlib: refactor unsafe parts of Dictionary
2 parents 5aac345 + 7c7c42c commit cb5a225

File tree

8 files changed

+285
-129
lines changed

8 files changed

+285
-129
lines changed

stdlib/public/core/Builtin.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,45 @@ public func strideofValue<T>(_:T) -> Int {
6161
return strideof(T.self)
6262
}
6363

64+
// This function is the implementation of the `_roundUp` overload set. It is
65+
// marked `@inline(__always)` to make primary `_roundUp` entry points seem
66+
// cheap enough for the inliner.
6467
@_versioned
65-
internal func _roundUp(_ offset: Int, toAlignment alignment: Int) -> Int {
66-
_sanityCheck(offset >= 0)
68+
@inline(__always)
69+
internal func _roundUpImpl(_ offset: UInt, toAlignment alignment: Int) -> UInt {
6770
_sanityCheck(alignment > 0)
6871
_sanityCheck(_isPowerOf2(alignment))
6972
// Note, given that offset is >= 0, and alignment > 0, we don't
7073
// need to underflow check the -1, as it can never underflow.
71-
let x = (offset + alignment &- 1)
74+
let x = offset + UInt(bitPattern: alignment) &- 1
7275
// Note, as alignment is a power of 2, we'll use masking to efficiently
7376
// get the aligned value
74-
return x & ~(alignment &- 1)
77+
return x & ~(UInt(bitPattern: alignment) &- 1)
78+
}
79+
80+
@_versioned
81+
internal func _roundUp(_ offset: UInt, toAlignment alignment: Int) -> UInt {
82+
return _roundUpImpl(offset, toAlignment: alignment)
83+
}
84+
85+
@_versioned
86+
internal func _roundUp(_ offset: Int, toAlignment alignment: Int) -> Int {
87+
_sanityCheck(offset >= 0)
88+
return Int(_roundUpImpl(UInt(bitPattern: offset), toAlignment: alignment))
89+
}
90+
91+
@_versioned
92+
internal func _roundUp<T, DestinationType>(
93+
_ pointer: UnsafeMutablePointer<T>,
94+
toAlignmentOf destinationType: DestinationType.Type
95+
) -> UnsafeMutablePointer<DestinationType> {
96+
// Note: unsafe unwrap is safe because this operation can only increase the
97+
// value, and can not produce a null pointer.
98+
return UnsafeMutablePointer<DestinationType>(
99+
bitPattern: _roundUpImpl(
100+
UInt(bitPattern: pointer),
101+
toAlignment: alignof(DestinationType))
102+
).unsafelyUnwrapped
75103
}
76104

77105
/// Returns a tri-state of 0 = no, 1 = yes, 2 = maybe.

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ set(SWIFTLIB_ESSENTIAL
114114
UnicodeScalar.swift
115115
UnicodeTrie.swift.gyb
116116
Unmanaged.swift
117+
UnsafeBitMap.swift
117118
UnsafeBufferPointer.swift.gyb
118119
UnsafePointer.swift.gyb
119120
WriteBackMutableSlice.swift

stdlib/public/core/FixedPoint.swift.gyb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,14 @@ internal func _unsafeMinus(_ lhs: Int, _ rhs: Int) -> Int {
701701
#endif
702702
}
703703

704+
internal func _unsafeMultiply(_ lhs: Int, _ rhs: Int) -> Int {
705+
#if INTERNAL_CHECKS_ENABLED
706+
return lhs * rhs
707+
#else
708+
return lhs &* rhs
709+
#endif
710+
}
711+
704712
@available(*, unavailable, renamed: "Integer")
705713
public typealias IntegerType = Integer
706714

stdlib/public/core/GroupInfo.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,6 @@
140140
"Process.swift",
141141
"Tuple.swift",
142142
"NewtypeWrapper.swift",
143+
"UnsafeBitMap.swift"
143144
]
144145
}

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 36 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,64 +2455,6 @@ collections = [
24552455
]
24562456
}%
24572457

2458-
/// A wrapper around a bitmap storage with room for at least `bitCount` bits.
2459-
internal struct _BitMap {
2460-
internal let values: UnsafeMutablePointer<UInt>
2461-
internal let bitCount: Int
2462-
2463-
// Note: We use UInt here to get unsigned math (shifts).
2464-
internal static func wordIndex(_ i: UInt) -> UInt {
2465-
return i / UInt._sizeInBits
2466-
}
2467-
2468-
internal static func bitIndex(_ i: UInt) -> UInt {
2469-
return i % UInt._sizeInBits
2470-
}
2471-
2472-
internal static func wordsFor(_ bitCount: Int) -> Int {
2473-
return bitCount + Int._sizeInBytes - 1 / Int._sizeInBytes
2474-
}
2475-
2476-
internal init(storage: UnsafeMutablePointer<UInt>, bitCount: Int) {
2477-
self.bitCount = bitCount
2478-
self.values = storage
2479-
}
2480-
2481-
internal var numberOfWords: Int {
2482-
get {
2483-
return _BitMap.wordsFor(bitCount)
2484-
}
2485-
}
2486-
2487-
internal func initializeToZero() {
2488-
for i in 0 ..< numberOfWords {
2489-
(values + i).initialize(with: 0)
2490-
}
2491-
}
2492-
2493-
internal subscript(i: Int) -> Bool {
2494-
get {
2495-
_sanityCheck(i < Int(bitCount) && i >= 0, "index out of bounds")
2496-
let idx = UInt(i)
2497-
let word = values[Int(_BitMap.wordIndex(idx))]
2498-
let bit = word & (1 << _BitMap.bitIndex(idx))
2499-
return bit != 0
2500-
}
2501-
nonmutating set {
2502-
_sanityCheck(i < Int(bitCount) && i >= 0, "index out of bounds")
2503-
let idx = UInt(i)
2504-
let wordIdx = _BitMap.wordIndex(idx)
2505-
if newValue {
2506-
values[Int(wordIdx)] =
2507-
values[Int(wordIdx)] | (1 << _BitMap.bitIndex(idx))
2508-
} else {
2509-
values[Int(wordIdx)] =
2510-
values[Int(wordIdx)] & ~(1 << _BitMap.bitIndex(idx))
2511-
}
2512-
}
2513-
}
2514-
}
2515-
25162458
/// Header part of the native storage.
25172459
internal struct _HashedContainerStorageHeader {
25182460
internal init(capacity: Int) {
@@ -2548,8 +2490,8 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
25482490
/// Returns the bytes necessary to store a bit map of 'capacity' bytes and
25492491
/// padding to align the start to word alignment.
25502492
internal static func bytesForBitMap(capacity: Int) -> Int {
2551-
let numWords = _BitMap.wordsFor(capacity)
2552-
return numWords * sizeof(UInt) + alignof(UInt)
2493+
let numWords = _UnsafeBitMap.sizeInWords(forSizeInBits: capacity)
2494+
return numWords * strideof(UInt) + alignof(UInt)
25532495
}
25542496

25552497
/// Returns the bytes necessary to store 'capacity' keys and padding to align
@@ -2560,12 +2502,11 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
25602502
return strideof(Key.self) * capacity + padding
25612503
}
25622504

2505+
%if Self == 'Dictionary':
25632506
/// Returns the bytes necessary to store 'capacity' values and padding to
25642507
/// align the start to the alignment of the 'Value' type assuming a base
25652508
/// address aligned to the maximum of the alignment of the 'Key' type and the
25662509
/// alignment of a word.
2567-
2568-
%if Self == 'Dictionary':
25692510
internal static func bytesForValues(capacity: Int) -> Int {
25702511
let maxPrevAlignment = max(alignof(Key.self), alignof(UInt))
25712512
let padding = max(0, alignof(Value.self) - maxPrevAlignment)
@@ -2574,9 +2515,7 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
25742515
%end
25752516

25762517
internal var buffer: BufferPointer {
2577-
get {
2578-
return BufferPointer(self)
2579-
}
2518+
return BufferPointer(self)
25802519
}
25812520

25822521
// All underscored functions are unsafe and need a _fixLifetime in the caller.
@@ -2591,9 +2530,7 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
25912530

25922531
@_versioned
25932532
internal var _capacity: Int {
2594-
get {
2595-
return _body.capacity
2596-
}
2533+
return _body.capacity
25972534
}
25982535

25992536
@_versioned
@@ -2607,45 +2544,30 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
26072544
}
26082545

26092546
internal var _maxLoadFactorInverse : Double {
2610-
get {
2611-
return _body.maxLoadFactorInverse
2612-
}
2547+
return _body.maxLoadFactorInverse
26132548
}
26142549

26152550
internal
26162551
var _initializedHashtableEntriesBitMapStorage: UnsafeMutablePointer<UInt> {
2617-
get {
2618-
let start = UInt(Builtin.ptrtoint_Word(buffer._elementPointer._rawValue))
2619-
let alignment = UInt(alignof(UInt))
2620-
let alignMask = alignment &- UInt(1)
2621-
return UnsafeMutablePointer<UInt>(
2622-
bitPattern:(start &+ alignMask) & ~alignMask)!
2623-
}
2552+
return _roundUp(buffer._elementPointer, toAlignmentOf: UInt.self)
26242553
}
26252554

26262555
internal var _keys: UnsafeMutablePointer<Key> {
2627-
get {
2628-
let start =
2629-
UInt(Builtin.ptrtoint_Word(
2630-
_initializedHashtableEntriesBitMapStorage._rawValue)) &+
2631-
UInt(_BitMap.wordsFor(_capacity)) &* UInt(strideof(UInt))
2632-
let alignment = UInt(alignof(Key))
2633-
let alignMask = alignment &- UInt(1)
2634-
return UnsafeMutablePointer<Key>(
2635-
bitPattern:(start &+ alignMask) & ~alignMask)!
2636-
}
2556+
let bitMapSizeInBytes =
2557+
_unsafeMultiply(
2558+
_UnsafeBitMap.sizeInWords(forSizeInBits: _capacity),
2559+
strideof(UInt))
2560+
let start =
2561+
UnsafeMutablePointer<UInt8>(_initializedHashtableEntriesBitMapStorage)
2562+
+ bitMapSizeInBytes
2563+
return _roundUp(start, toAlignmentOf: Key.self)
26372564
}
26382565

26392566
%if Self == 'Dictionary':
26402567
internal var _values: UnsafeMutablePointer<Value> {
2641-
get {
2642-
let start = UInt(Builtin.ptrtoint_Word(_keys._rawValue)) &+
2643-
UInt(_capacity) &* UInt(strideof(Key.self))
2644-
let alignment = UInt(alignof(Value))
2645-
let alignMask = alignment &- UInt(1)
2646-
return UnsafeMutablePointer<Value>(
2647-
bitPattern:(start &+ alignMask) & ~alignMask)!
2648-
}
2568+
let keysSizeInBytes = _unsafeMultiply(_capacity, strideof(Key.self))
2569+
let start = UnsafeMutablePointer<UInt8>(_keys) + keysSizeInBytes
2570+
return _roundUp(start, toAlignmentOf: Value.self)
26492571
}
26502572
%end
26512573

@@ -2655,14 +2577,14 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
26552577
let requiredCapacity =
26562578
bytesForBitMap(capacity: capacity) + bytesForKeys(capacity: capacity)
26572579
%if Self == 'Dictionary':
2658-
+ bytesForValues(capacity: capacity)
2580+
+ bytesForValues(capacity: capacity)
26592581
%end
26602582

26612583
let r = super.create(minimumCapacity: requiredCapacity) { _ in
26622584
return _HashedContainerStorageHeader(capacity: capacity)
26632585
}
26642586
let storage = r as! StorageImpl
2665-
let initializedEntries = _BitMap(
2587+
let initializedEntries = _UnsafeBitMap(
26662588
storage: storage._initializedHashtableEntriesBitMapStorage,
26672589
bitCount: capacity)
26682590
initializedEntries.initializeToZero()
@@ -2671,7 +2593,7 @@ final internal class _Native${Self}StorageImpl<${TypeParameters}> :
26712593

26722594
deinit {
26732595
let capacity = _capacity
2674-
let initializedEntries = _BitMap(
2596+
let initializedEntries = _UnsafeBitMap(
26752597
storage: _initializedHashtableEntriesBitMapStorage, bitCount: capacity)
26762598
let keys = _keys
26772599
%if Self == 'Dictionary':
@@ -2721,17 +2643,17 @@ struct _Native${Self}Storage<${TypeParametersDecl}> :
27212643

27222644
internal let buffer: StorageImpl
27232645

2724-
internal let initializedEntries: _BitMap
2646+
internal let initializedEntries: _UnsafeBitMap
27252647
internal let keys: UnsafeMutablePointer<Key>
27262648
%if Self == 'Dictionary':
27272649
internal let values: UnsafeMutablePointer<Value>
27282650
%end
27292651

27302652
internal init(capacity: Int) {
27312653
buffer = StorageImpl.create(capacity: capacity)
2732-
initializedEntries = _BitMap(
2733-
storage: buffer._initializedHashtableEntriesBitMapStorage,
2734-
bitCount: capacity)
2654+
initializedEntries = _UnsafeBitMap(
2655+
storage: buffer._initializedHashtableEntriesBitMapStorage,
2656+
bitCount: capacity)
27352657
keys = buffer._keys
27362658
%if Self == 'Dictionary':
27372659
values = buffer._values
@@ -2754,11 +2676,9 @@ struct _Native${Self}Storage<${TypeParametersDecl}> :
27542676
@_transparent
27552677
public // @testable
27562678
var capacity: Int {
2757-
get {
2758-
let result = buffer._capacity
2759-
_fixLifetime(buffer)
2760-
return result
2761-
}
2679+
let result = buffer._capacity
2680+
_fixLifetime(buffer)
2681+
return result
27622682
}
27632683

27642684
@_versioned
@@ -2777,11 +2697,9 @@ struct _Native${Self}Storage<${TypeParametersDecl}> :
27772697

27782698
@_transparent
27792699
internal var maxLoadFactorInverse: Double {
2780-
get {
2781-
let result = buffer._maxLoadFactorInverse
2782-
_fixLifetime(buffer)
2783-
return result
2784-
}
2700+
let result = buffer._maxLoadFactorInverse
2701+
_fixLifetime(buffer)
2702+
return result
27852703
}
27862704

27872705
@_versioned
@@ -3139,16 +3057,15 @@ internal struct _BridgedNative${Self}Storage {
31393057
internal typealias SequenceElement = ${AnySequenceType}
31403058

31413059
internal let buffer: StorageImpl
3142-
internal let initializedEntries: _BitMap
3060+
internal let initializedEntries: _UnsafeBitMap
31433061
internal let keys: UnsafeMutablePointer<AnyObject>
31443062
%if Self == 'Dictionary':
31453063
internal let values: UnsafeMutablePointer<AnyObject>
31463064
%end
31473065

3148-
31493066
internal init(buffer: StorageImpl) {
31503067
self.buffer = buffer
3151-
initializedEntries = _BitMap(
3068+
initializedEntries = _UnsafeBitMap(
31523069
storage: buffer._initializedHashtableEntriesBitMapStorage,
31533070
bitCount: buffer._capacity)
31543071
keys = buffer._keys
@@ -3160,11 +3077,9 @@ internal struct _BridgedNative${Self}Storage {
31603077

31613078
@_transparent
31623079
internal var capacity: Int {
3163-
get {
3164-
let result = buffer._capacity
3165-
_fixLifetime(buffer)
3166-
return result
3167-
}
3080+
let result = buffer._capacity
3081+
_fixLifetime(buffer)
3082+
return result
31683083
}
31693084

31703085
@_versioned

0 commit comments

Comments
 (0)