Skip to content

Commit 0ac802a

Browse files
authored
Merge pull request swiftlang#21310 from milseman/4_bit_string
[String] Last-minute ABI adjustment: 4-bit discriminator
2 parents 5aec222 + 5a6d2df commit 0ac802a

File tree

11 files changed

+502
-681
lines changed

11 files changed

+502
-681
lines changed

stdlib/public/core/SmallString.swift

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,9 @@ extension _SmallString {
7272
}
7373
}
7474

75-
@inlinable
76-
internal var discriminator: _StringObject.Discriminator {
77-
@inline(__always) get {
78-
let value = _storage.1 &>> _StringObject.Nibbles.discriminatorShift
79-
return _StringObject.Discriminator(UInt8(truncatingIfNeeded: value))
80-
}
81-
@inline(__always) set {
82-
_storage.1 &= _StringObject.Nibbles.largeAddressMask
83-
_storage.1 |= (
84-
UInt64(truncatingIfNeeded: newValue._value)
85-
&<< _StringObject.Nibbles.discriminatorShift)
86-
}
75+
@inlinable @inline(__always)
76+
internal var rawDiscriminatedObject: UInt64 {
77+
return _storage.1
8778
}
8879

8980
@inlinable
@@ -96,7 +87,7 @@ extension _SmallString {
9687
@inlinable
9788
internal var count: Int {
9889
@inline(__always) get {
99-
return discriminator.smallCount
90+
return _StringObject.getSmallCount(fromRaw: rawDiscriminatedObject)
10091
}
10192
}
10293

@@ -108,27 +99,22 @@ extension _SmallString {
10899
@inlinable
109100
internal var isASCII: Bool {
110101
@inline(__always) get {
111-
return discriminator.smallIsASCII
102+
return _StringObject.getSmallIsASCII(fromRaw: rawDiscriminatedObject)
112103
}
113104
}
114105

115106
// Give raw, nul-terminated code units. This is only for limited internal
116107
// usage: it always clears the discriminator and count (in case it's full)
117-
@inlinable
108+
@inlinable @inline(__always)
118109
internal var zeroTerminatedRawCodeUnits: RawBitPattern {
119-
@inline(__always) get {
120-
return (
121-
self._storage.0,
122-
self._storage.1 & _StringObject.Nibbles.largeAddressMask)
123-
}
110+
let smallStringCodeUnitMask: UInt64 = 0x00FF_FFFF_FFFF_FFFF
111+
return (self._storage.0, self._storage.1 & smallStringCodeUnitMask)
124112
}
125113

126-
@inlinable
127114
internal func computeIsASCII() -> Bool {
128-
// TODO(String micro-performance): Evaluate other expressions, e.g. | first
129115
let asciiMask: UInt64 = 0x8080_8080_8080_8080
130116
let raw = zeroTerminatedRawCodeUnits
131-
return (raw.0 & asciiMask == 0) && (raw.1 & asciiMask == 0)
117+
return (raw.0 | raw.1) & asciiMask == 0
132118
}
133119
}
134120

@@ -220,7 +206,7 @@ extension _SmallString {
220206

221207
// Overwrite stored code units, including uninitialized. `f` should return the
222208
// new count.
223-
@inlinable @inline(__always)
209+
@inline(__always)
224210
internal mutating func withMutableCapacity(
225211
_ f: (UnsafeMutableBufferPointer<UInt8>) throws -> Int
226212
) rethrows {
@@ -231,14 +217,28 @@ extension _SmallString {
231217
return try f(UnsafeMutableBufferPointer(
232218
start: ptr, count: _SmallString.capacity))
233219
}
234-
235220
_internalInvariant(len <= _SmallString.capacity)
236-
discriminator = .small(withCount: len, isASCII: self.computeIsASCII())
221+
222+
let (leading, trailing) = self.zeroTerminatedRawCodeUnits
223+
self = _SmallString(leading: leading, trailing: trailing, count: len)
237224
}
238225
}
239226

240227
// Creation
241228
extension _SmallString {
229+
@inlinable @inline(__always)
230+
internal init(leading: UInt64, trailing: UInt64, count: Int) {
231+
_internalInvariant(count <= _SmallString.capacity)
232+
233+
let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
234+
let countAndDiscriminator = UInt64(truncatingIfNeeded: count) &<< 56
235+
| _StringObject.Nibbles.small(isASCII: isASCII)
236+
_internalInvariant(trailing & countAndDiscriminator == 0)
237+
238+
self.init(raw: (leading, trailing | countAndDiscriminator))
239+
_internalInvariant(self.count == count)
240+
}
241+
242242
// Direct from UTF-8
243243
@inlinable @inline(__always)
244244
internal init?(_ input: UnsafeBufferPointer<UInt8>) {
@@ -251,11 +251,7 @@ extension _SmallString {
251251
let leading = _bytesToUInt64(ptr, Swift.min(input.count, 8))
252252
let trailing = count > 8 ? _bytesToUInt64(ptr + 8, count &- 8) : 0
253253

254-
let isASCII = (leading | trailing) & 0x8080_8080_8080_8080 == 0
255-
let discriminator = _StringObject.Discriminator.small(
256-
withCount: count,
257-
isASCII: isASCII)
258-
self.init(raw: (leading, trailing | discriminator.rawBits))
254+
self.init(leading: leading, trailing: trailing, count: count)
259255
}
260256

261257
@usableFromInline // @testable
@@ -273,13 +269,8 @@ extension _SmallString {
273269
}
274270
_internalInvariant(writeIdx == totalCount)
275271

276-
let isASCII = base.isASCII && other.isASCII
277-
let discriminator = _StringObject.Discriminator.small(
278-
withCount: totalCount,
279-
isASCII: isASCII)
280-
281272
let (leading, trailing) = result.zeroTerminatedRawCodeUnits
282-
self.init(raw: (leading, trailing | discriminator.rawBits))
273+
self.init(leading: leading, trailing: trailing, count: totalCount)
283274
}
284275
}
285276

stdlib/public/core/StringBridge.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,13 @@ extension String {
281281
}
282282
}
283283
if _guts._object.isImmortal {
284+
// TODO: We'd rather emit a valid ObjC object statically than create a
285+
// shared string class instance.
286+
let gutsCountAndFlags = _guts._object._countAndFlags
284287
return _SharedStringStorage(
285288
immortal: _guts._object.fastUTF8.baseAddress!,
286-
countAndFlags: _guts._object._countAndFlags)
289+
countAndFlags: _StringObject.CountAndFlags(
290+
sharedCount: _guts.count, isASCII: gutsCountAndFlags.isASCII))
287291
}
288292

289293
_internalInvariant(_guts._object.hasObjCBridgeableObject,

stdlib/public/core/StringGuts.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ extension _StringGuts {
6161
}
6262

6363
internal init(_ storage: _SharedStringStorage) {
64-
// TODO(cleanup): We should probably pass whole perf flags struct around
65-
self.init(_StringObject(storage, isASCII: false))
64+
self.init(_StringObject(storage))
6665
}
6766

6867
internal init(
@@ -109,18 +108,15 @@ extension _StringGuts {
109108
@inline(__always) get { return isFastUTF8 && _object.isASCII }
110109
}
111110

112-
@inlinable
113-
internal var isNFC: Bool {
114-
@inline(__always) get { return _object.isNFC }
115-
}
111+
@inline(__always)
112+
internal var isNFC: Bool { return _object.isNFC }
116113

117-
@inlinable
118-
internal var isNFCFastUTF8: Bool {
114+
@inline(__always)
115+
internal var isNFCFastUTF8: Bool {
119116
// TODO(String micro-performance): Consider a dedicated bit for this
120-
@inline(__always) get { return _object.isNFC && isFastUTF8 }
117+
return _object.isNFC && isFastUTF8
121118
}
122119

123-
@inlinable
124120
internal var hasNativeStorage: Bool { return _object.hasNativeStorage }
125121

126122
internal var hasSharedStorage: Bool { return _object.hasSharedStorage }

0 commit comments

Comments
 (0)