Skip to content

Commit 99ff556

Browse files
authored
Merge pull request #25948 from milseman/bit_switch
[String] Switch scalar-aligned bit to a reserved bit.
2 parents f0c39aa + 63a6794 commit 99ff556

File tree

7 files changed

+80
-69
lines changed

7 files changed

+80
-69
lines changed

stdlib/public/core/StringCharacterView.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ extension String: BidirectionalCollection {
6464
let stride = _characterStride(startingAt: i)
6565
let nextOffset = i._encodedOffset &+ stride
6666
let nextStride = _characterStride(
67-
startingAt: Index(_encodedOffset: nextOffset)._aligned)
67+
startingAt: Index(_encodedOffset: nextOffset)._scalarAligned)
6868

6969
return Index(
70-
encodedOffset: nextOffset, characterStride: nextStride)._aligned
70+
encodedOffset: nextOffset, characterStride: nextStride)._scalarAligned
7171
}
7272

7373
/// Returns the position immediately before the given index.
@@ -82,7 +82,8 @@ extension String: BidirectionalCollection {
8282
let i = _guts.scalarAlign(i)
8383
let stride = _characterStride(endingAt: i)
8484
let priorOffset = i._encodedOffset &- stride
85-
return Index(encodedOffset: priorOffset, characterStride: stride)._aligned
85+
return Index(
86+
encodedOffset: priorOffset, characterStride: stride)._scalarAligned
8687
}
8788
/// Returns an index that is the specified distance from the given index.
8889
///
@@ -200,7 +201,7 @@ extension String: BidirectionalCollection {
200201

201202
@inlinable @inline(__always)
202203
internal func _characterStride(startingAt i: Index) -> Int {
203-
_internalInvariant(i._isAligned)
204+
_internalInvariant(i._isScalarAligned)
204205

205206
// Fast check if it's already been measured, otherwise check resiliently
206207
if let d = i.characterStride { return d }
@@ -212,7 +213,7 @@ extension String: BidirectionalCollection {
212213

213214
@inlinable @inline(__always)
214215
internal func _characterStride(endingAt i: Index) -> Int {
215-
_internalInvariant(i._isAligned)
216+
_internalInvariant(i._isScalarAligned)
216217

217218
if i == startIndex { return 0 }
218219

stdlib/public/core/StringGuts.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,11 @@ extension _StringGuts {
268268

269269
@inlinable @inline(__always)
270270
internal var startIndex: String.Index {
271-
return Index(_encodedOffset: 0)._aligned
271+
return Index(_encodedOffset: 0)._scalarAligned
272272
}
273273
@inlinable @inline(__always)
274274
internal var endIndex: String.Index {
275-
return Index(_encodedOffset: self.count)._aligned
275+
return Index(_encodedOffset: self.count)._scalarAligned
276276
}
277277
}
278278

stdlib/public/core/StringIndex.swift

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,36 @@ import SwiftShims
1616

1717
String's Index has the following layout:
1818

19-
┌──────────┬───────────────────┬─────────╥────────────────┬──────────┐
20-
│ b63:b16 │ b15:b14 │ b13 ║ b12:b8 │ b6:b0 │
21-
├──────────┼───────────────────┼─────────╫────────────────┼──────────┤
22-
│ position │ transcoded offset │ aligned ║ grapheme cache │ reserved │
23-
└──────────┴───────────────────┴─────────╨────────────────┴──────────┘
24-
25-
Position, transcoded offset, and aligned are fully exposed in the ABI. Grapheme
26-
cache and reserved are partially resilient: the fact that there are 13 bits with
27-
a default value of `0` is ABI, but not the layout, construction, or
19+
┌──────────┬───────────────────╥────────────────┬──────────╥────────────────┐
20+
│ b63:b16 │ b15:b14 ║ b13:b8 │ b7:b1 ║ b0 │
21+
├──────────┼───────────────────╫────────────────┼──────────╫────────────────┤
22+
│ position │ transcoded offset ║ grapheme cache │ reserved ║ scalar aligned │
23+
└──────────┴───────────────────╨────────────────┴──────────╨────────────────┘
24+
└──────── resilient ────────┘
25+
26+
Position, transcoded offset, and scalar aligned are fully exposed in the ABI.
27+
Grapheme cache and reserved are partially resilient: the fact that there are 13
28+
bits with a default value of `0` is ABI, but not the layout, construction, or
2829
interpretation of those bits. All use of grapheme cache should be behind
29-
non-inlinable function calls.
30+
non-inlinable function calls. Inlinable code should not set a non-zero value to
31+
grapheme cache bits: doing so breaks back deployment as they will be interpreted
32+
as a set cache.
3033

3134
- position aka `encodedOffset`: A 48-bit offset into the string's code units
35+
3236
- transcoded offset: a 2-bit sub-scalar offset, derived from transcoding
33-
- aligned, whether this index is known to be scalar-aligned (see below)
37+
38+
<resilience barrier>
39+
40+
- grapheme cache: A 6-bit value remembering the distance to the next grapheme
41+
boundary.
42+
43+
- reserved: 7-bit for future use.
44+
3445
<resilience barrier>
35-
- grapheme cache: A 5-bit value remembering the distance to the next grapheme
36-
boundary
37-
- reserved: 8-bit for future use.
46+
47+
- scalar aligned, whether this index is known to be scalar-aligned (see below)
48+
3849

3950
*/
4051
extension String {
@@ -86,7 +97,7 @@ extension String.Index {
8697

8798
@usableFromInline
8899
internal var characterStride: Int? {
89-
let value = (_rawBits & 0x1F00) &>> 8
100+
let value = (_rawBits & 0x3F00) &>> 8
90101
return value > 0 ? Int(truncatingIfNeeded: value) : nil
91102
}
92103

@@ -136,7 +147,7 @@ extension String.Index {
136147
encodedOffset: Int, transcodedOffset: Int, characterStride: Int
137148
) {
138149
self.init(encodedOffset: encodedOffset, transcodedOffset: transcodedOffset)
139-
if _slowPath(characterStride > 0x1F) { return }
150+
if _slowPath(characterStride > 0x3F) { return }
140151
self._rawBits |= UInt64(truncatingIfNeeded: characterStride &<< 8)
141152
self._invariantCheck()
142153
}
@@ -152,7 +163,7 @@ extension String.Index {
152163
@usableFromInline @inline(never) @_effects(releasenone)
153164
internal func _invariantCheck() {
154165
_internalInvariant(_encodedOffset >= 0)
155-
if self._isAligned {
166+
if self._isScalarAligned {
156167
_internalInvariant(transcodedOffset == 0)
157168
}
158169
}
@@ -209,35 +220,35 @@ extension String.Index {
209220
}
210221

211222
/*
212-
Index Alignment
223+
Index Scalar Alignment
213224

214225
SE-0180 unifies the Index type of String and all its views and allows
215226
non-scalar-aligned indices to be used across views. In order to guarantee
216227
behavior, we often have to check and perform scalar alignment. To speed up
217-
these checks, we allocate a bit denoting known-to-be-aligned, so that the
218-
alignment check can skip the load. The below shows what views need to check
219-
for alignment before they can operate, and whether the indices they produce
220-
are aligned.
221-
222-
┌───────────────╥────────────────────┬──────────────────────────┐
223-
│ View ║ Requires Alignment │ Produces Aligned Indices
224-
╞═══════════════╬════════════════════╪══════════════════════════╡
225-
│ Native UTF8 ║ no │ no
226-
├───────────────╫────────────────────┼──────────────────────────┤
227-
│ Native UTF16 ║ yes │ no
228-
╞═══════════════╬════════════════════╪══════════════════════════╡
229-
│ Foreign UTF8 ║ yes │ no
230-
├───────────────╫────────────────────┼──────────────────────────┤
231-
│ Foreign UTF16 ║ no │ no
232-
╞═══════════════╬════════════════════╪══════════════════════════╡
233-
│ UnicodeScalar ║ yes │ yes
234-
├───────────────╫────────────────────┼──────────────────────────┤
235-
│ Character ║ yes │ yes
236-
└───────────────╨────────────────────┴──────────────────────────┘
237-
238-
The "requires alignment" applies to any operation taking a String.Index that's
239-
not defined entirely in terms of other operations taking a String.Index. These
240-
include:
228+
these checks, we allocate a bit denoting known-to-be-scalar-aligned, so that
229+
the alignment check can skip the load. The below shows what views need to
230+
check for alignment before they can operate, and whether the indices they
231+
produce are aligned.
232+
233+
┌───────────────╥───────────────────────────┬─────────────────────────┐
234+
│ View ║ Requires Scalar Alignment │ Produces Scalar Aligned
235+
╞═══════════════╬═══════════════════════════╪═════════════════════════╡
236+
│ Native UTF8 ║ no │ no │
237+
├───────────────╫───────────────────────────┼─────────────────────────┤
238+
│ Native UTF16 ║ yes │ no │
239+
╞═══════════════╬═══════════════════════════╪═════════════════════════╡
240+
│ Foreign UTF8 ║ yes │ no │
241+
├───────────────╫───────────────────────────┼─────────────────────────┤
242+
│ Foreign UTF16 ║ no │ no │
243+
╞═══════════════╬═══════════════════════════╪═════════════════════════╡
244+
│ UnicodeScalar ║ yes │ yes │
245+
├───────────────╫───────────────────────────┼─────────────────────────┤
246+
│ Character ║ yes │ yes │
247+
└───────────────╨───────────────────────────┴─────────────────────────┘
248+
249+
The "requires scalar alignment" applies to any operation taking a String.Index
250+
that's not defined entirely in terms of other operations taking a
251+
String.Index. These include:
241252

242253
* index(after:)
243254
* index(before:)
@@ -249,13 +260,13 @@ extension String.Index {
249260
extension String.Index {
250261
@_alwaysEmitIntoClient // Swift 5.1
251262
@inline(__always)
252-
internal var _isAligned: Bool { return 0 != _rawBits & 0x2000 }
263+
internal var _isScalarAligned: Bool { return 0 != _rawBits & 0x1 }
253264

254265
@_alwaysEmitIntoClient // Swift 5.1
255266
@inline(__always)
256-
internal var _aligned: String.Index {
267+
internal var _scalarAligned: String.Index {
257268
var idx = self
258-
idx._rawBits |= 0x2000
269+
idx._rawBits |= 0x1
259270
idx._invariantCheck()
260271
return idx
261272
}

stdlib/public/core/StringUTF16View.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ extension String.UTF16View: BidirectionalCollection {
154154
if len == 4 && idx.transcodedOffset == 0 {
155155
return idx.nextTranscoded
156156
}
157-
return idx.strippingTranscoding.encoded(offsetBy: len)._aligned
157+
return idx.strippingTranscoding.encoded(offsetBy: len)._scalarAligned
158158
}
159159

160160
@inlinable @inline(__always)
@@ -178,7 +178,7 @@ extension String.UTF16View: BidirectionalCollection {
178178

179179
// Single UTF-16 code unit
180180
_internalInvariant((1...3) ~= len)
181-
return idx.encoded(offsetBy: -len)._aligned
181+
return idx.encoded(offsetBy: -len)._scalarAligned
182182
}
183183

184184
public func index(_ i: Index, offsetBy n: Int) -> Index {
@@ -587,7 +587,7 @@ extension String.UTF16View {
587587
_internalInvariant(utf16Len == 2)
588588
return Index(encodedOffset: readIdx, transcodedOffset: 1)
589589
}
590-
return Index(_encodedOffset: readIdx &+ len)._aligned
590+
return Index(_encodedOffset: readIdx &+ len)._scalarAligned
591591
}
592592

593593
readIdx &+= len

stdlib/public/core/StringUnicodeScalarView.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ extension String.UnicodeScalarView: BidirectionalCollection {
112112

113113
if _fastPath(_guts.isFastUTF8) {
114114
let len = _guts.fastUTF8ScalarLength(startingAt: i._encodedOffset)
115-
return i.encoded(offsetBy: len)._aligned
115+
return i.encoded(offsetBy: len)._scalarAligned
116116
}
117117

118118
return _foreignIndex(after: i)
@@ -137,7 +137,7 @@ extension String.UnicodeScalarView: BidirectionalCollection {
137137
return _utf8ScalarLength(utf8, endingAt: i._encodedOffset)
138138
}
139139
_internalInvariant(len <= 4, "invalid UTF8")
140-
return i.encoded(offsetBy: -len)._aligned
140+
return i.encoded(offsetBy: -len)._scalarAligned
141141
}
142142

143143
return _foreignIndex(before: i)
@@ -419,7 +419,7 @@ extension String.UnicodeScalarView {
419419
let cu = _guts.foreignErrorCorrectedUTF16CodeUnit(at: i)
420420
let len = UTF16.isLeadSurrogate(cu) ? 2 : 1
421421

422-
return i.encoded(offsetBy: len)._aligned
422+
return i.encoded(offsetBy: len)._scalarAligned
423423
}
424424

425425
@usableFromInline @inline(never)
@@ -430,6 +430,6 @@ extension String.UnicodeScalarView {
430430
let cu = _guts.foreignErrorCorrectedUTF16CodeUnit(at: priorIdx)
431431
let len = UTF16.isTrailSurrogate(cu) ? 2 : 1
432432

433-
return i.encoded(offsetBy: -len)._aligned
433+
return i.encoded(offsetBy: -len)._scalarAligned
434434
}
435435
}

stdlib/public/core/UnicodeHelpers.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ extension _StringGuts {
163163
@inline(__always) // fast-path: fold common fastUTF8 check
164164
internal func scalarAlign(_ idx: Index) -> Index {
165165
let result: String.Index
166-
if _fastPath(idx._isAligned) {
166+
if _fastPath(idx._isScalarAligned) {
167167
result = idx
168168
} else {
169169
// TODO(String performance): isASCII check
@@ -172,28 +172,28 @@ extension _StringGuts {
172172

173173
_internalInvariant(isOnUnicodeScalarBoundary(result),
174174
"Alignment bit is set for non-aligned index")
175-
_internalInvariant(result._isAligned)
175+
_internalInvariant(result._isScalarAligned)
176176
return result
177177
}
178178

179179
@inline(never) // slow-path
180180
@_alwaysEmitIntoClient // Swift 5.1
181181
internal func scalarAlignSlow(_ idx: Index) -> Index {
182-
_internalInvariant(!idx._isAligned)
182+
_internalInvariant(!idx._isScalarAligned)
183183

184184
if _slowPath(idx.transcodedOffset != 0 || idx._encodedOffset == 0) {
185185
// Transcoded index offsets are already scalar aligned
186-
return String.Index(_encodedOffset: idx._encodedOffset)._aligned
186+
return String.Index(_encodedOffset: idx._encodedOffset)._scalarAligned
187187
}
188188
if _slowPath(self.isForeign) {
189189
let foreignIdx = foreignScalarAlign(idx)
190-
_internalInvariant(foreignIdx._isAligned)
190+
_internalInvariant(foreignIdx._isScalarAligned)
191191
return foreignIdx
192192
}
193193

194194
return String.Index(_encodedOffset:
195195
self.withFastUTF8 { _scalarAlign($0, idx._encodedOffset) }
196-
)._aligned
196+
)._scalarAligned
197197
}
198198

199199
@inlinable
@@ -359,17 +359,17 @@ extension _StringGuts {
359359
@usableFromInline @inline(never) // slow-path
360360
@_effects(releasenone)
361361
internal func foreignScalarAlign(_ idx: Index) -> Index {
362-
guard idx._encodedOffset != self.count else { return idx._aligned }
362+
guard idx._encodedOffset != self.count else { return idx._scalarAligned }
363363

364364
_internalInvariant(idx._encodedOffset < self.count)
365365

366366
let ecCU = foreignErrorCorrectedUTF16CodeUnit(at: idx)
367367
if _fastPath(!UTF16.isTrailSurrogate(ecCU)) {
368-
return idx._aligned
368+
return idx._scalarAligned
369369
}
370370
_internalInvariant(idx._encodedOffset > 0,
371371
"Error-correction shouldn't give trailing surrogate at position zero")
372-
return String.Index(_encodedOffset: idx._encodedOffset &- 1)._aligned
372+
return String.Index(_encodedOffset: idx._encodedOffset &- 1)._scalarAligned
373373
}
374374

375375
@usableFromInline @inline(never)

stdlib/public/core/UnicodeScalar.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ extension Unicode.Scalar.UTF16View : RandomAccessCollection {
428428
/// `endIndex` property.
429429
@inlinable
430430
public subscript(position: Int) -> UTF16.CodeUnit {
431-
_internalInvariant((0..<self.count).contains(position))
432431
if position == 1 { return UTF16.trailSurrogate(value) }
433432
if endIndex == 1 { return UTF16.CodeUnit(value.value) }
434433
return UTF16.leadSurrogate(value)

0 commit comments

Comments
 (0)