Skip to content

Commit 3b74893

Browse files
committed
[stdlib] _StringObject: Use a full 8-bit discriminator on 32-bit platforms
We now have plenty of extra inhabitants in the variant enum, so we can get rid of the 7-bit hack. It’d also be possible now to increase small string capacity to a spacious 11 bytes; however this needs a full overhaul of the 32-bit representation, so it needs a little bit more time in the oven.
1 parent dd12206 commit 3b74893

File tree

4 files changed

+29
-51
lines changed

4 files changed

+29
-51
lines changed

stdlib/public/core/StringObject.swift

+15-42
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal struct _StringObject {
137137
internal var _variant: Variant
138138

139139
@usableFromInline
140-
internal var _discriminator: Builtin.Int7
140+
internal var _discriminator: Discriminator
141141

142142
@usableFromInline
143143
internal var _flags: Flags
@@ -149,11 +149,9 @@ internal struct _StringObject {
149149
discriminator: Discriminator,
150150
flags: Flags
151151
) {
152-
_internalInvariant(variant.isImmortal == discriminator.isImmortal)
153152
self._count = count
154153
self._variant = variant
155-
self._discriminator =
156-
Builtin.truncOrBitCast_Int8_Int7(discriminator._value._value)
154+
self._discriminator = discriminator
157155
self._flags = flags
158156
}
159157

@@ -196,28 +194,17 @@ internal struct _StringObject {
196194
enum Nibbles {}
197195
}
198196

197+
#if !(arch(i386) || arch(arm))
199198
extension _StringObject {
200-
#if arch(i386) || arch(arm)
201-
@inlinable @inline(__always)
202-
internal func discriminator(isImmortal: Bool) -> Discriminator {
203-
let lowBits = UInt8(Builtin.zextOrBitCast_Int7_Int8(_discriminator))
204-
guard isImmortal else { return Discriminator(lowBits) }
205-
return Discriminator(lowBits | 0x80)
206-
}
207-
#endif
208-
209199
@inlinable
210-
internal var discriminator: Discriminator {
200+
internal var _discriminator: Discriminator {
211201
@inline(__always) get {
212-
#if arch(i386) || arch(arm)
213-
return self.discriminator(isImmortal: _variant.isImmortal)
214-
#else
215202
let d = objectRawBits &>> Nibbles.discriminatorShift
216203
return Discriminator(UInt8(truncatingIfNeeded: d))
217-
#endif
218204
}
219205
}
220206
}
207+
#endif
221208

222209
// Raw
223210
extension _StringObject {
@@ -233,7 +220,7 @@ extension _StringObject {
233220
let count = UInt64(truncatingIfNeeded: UInt(bitPattern: _count))
234221
let payload = UInt64(truncatingIfNeeded: undiscriminatedObjectRawBits)
235222
let flags = UInt64(truncatingIfNeeded: _flags._value)
236-
let discr = UInt64(truncatingIfNeeded: discriminator._value)
223+
let discr = UInt64(truncatingIfNeeded: _discriminator._value)
237224
if isSmall {
238225
// Rearrange small strings in a different way, compacting bytes into a
239226
// contiguous sequence. See comment on small string layout below.
@@ -636,9 +623,7 @@ extension _StringObject {
636623
internal var isSmall: Bool {
637624
@inline(__always) get {
638625
#if arch(i386) || arch(arm)
639-
// Note: This assumes that the `isSmall` predicate doesn't look at the
640-
// immortal bit. We may or may not actually be immortal.
641-
return discriminator(isImmortal: true).isSmall
626+
return _discriminator.isSmall
642627
#else
643628
return (objectRawBits & 0x2000_0000_0000_0000) != 0
644629
#endif
@@ -658,9 +643,7 @@ extension _StringObject {
658643
internal var providesFastUTF8: Bool {
659644
@inline(__always) get {
660645
#if arch(i386) || arch(arm)
661-
// Note: This assumes that the `providesFastUTF8` predicate doesn't look
662-
// at the immortal bit. We may or may not actually be immortal.
663-
return discriminator(isImmortal: false).providesFastUTF8
646+
return _discriminator.providesFastUTF8
664647
#else
665648
return (objectRawBits & 0x1000_0000_0000_0000) == 0
666649
#endif
@@ -677,7 +660,7 @@ extension _StringObject {
677660
internal var hasNativeStorage: Bool {
678661
@inline(__always) get {
679662
#if arch(i386) || arch(arm)
680-
return discriminator.hasNativeStorage
663+
return _discriminator.hasNativeStorage
681664
#else
682665
return (objectRawBits & 0xF800_0000_0000_0000) == 0
683666
#endif
@@ -688,7 +671,7 @@ extension _StringObject {
688671
internal var hasSharedStorage: Bool {
689672
@inline(__always) get {
690673
#if arch(i386) || arch(arm)
691-
return discriminator.hasSharedStorage
674+
return _discriminator.hasSharedStorage
692675
#else
693676
return (objectRawBits & 0xF800_0000_0000_0000)
694677
== Nibbles.largeSharedMortal()
@@ -705,9 +688,7 @@ extension _StringObject {
705688
@inline(__always) get {
706689
_internalInvariant(isLarge && providesFastUTF8)
707690
#if arch(i386) || arch(arm)
708-
// Note: This assumes that the `largeFastIsNative` predicate doesn't look
709-
// at the immortal bit. We may or may not actually be immortal.
710-
return discriminator(isImmortal: false).largeFastIsNative
691+
return _discriminator.largeFastIsNative
711692
#else
712693
return (objectRawBits & 0x0800_0000_0000_0000) == 0
713694
#endif
@@ -726,9 +707,7 @@ extension _StringObject {
726707
@inline(__always) get {
727708
_internalInvariant(isLarge)
728709
#if arch(i386) || arch(arm)
729-
// Note: This assumes that the `largeIsCocoa` predicate doesn't look at
730-
// the immortal bit. We may or may not actually be immortal.
731-
return discriminator(isImmortal: false).largeIsCocoa
710+
return _discriminator.largeIsCocoa
732711
#else
733712
return (objectRawBits & 0x4000_0000_0000_0000) != 0
734713
#endif
@@ -770,12 +749,7 @@ extension _StringObject {
770749
@inline(__always)
771750
get {
772751
_internalInvariant(isSmall)
773-
#if arch(i386) || arch(arm)
774-
// Note: This assumes that `isSmall` implies that we're immortal.
775-
return discriminator(isImmortal: true).smallCount
776-
#else
777-
return discriminator.smallCount
778-
#endif
752+
return _discriminator.smallCount
779753
}
780754
}
781755

@@ -785,8 +759,7 @@ extension _StringObject {
785759
get {
786760
_internalInvariant(isSmall)
787761
#if arch(i386) || arch(arm)
788-
// Note: This assumes that `isSmall` implies that we're immortal.
789-
return discriminator(isImmortal: true).smallIsASCII
762+
return _discriminator.smallIsASCII
790763
#else
791764
return objectRawBits & 0x4000_0000_0000_0000 != 0
792765
#endif
@@ -1320,7 +1293,7 @@ extension _StringObject {
13201293
<\(word0) \(word1)> \
13211294
count: \(String(_count, radix: 16)), \
13221295
variant: \(_variant), \
1323-
discriminator: \(discriminator), \
1296+
discriminator: \(_discriminator), \
13241297
flags: \(_flags))
13251298
""")
13261299
#else

test/SILOptimizer/concat_string_literals.32.swift

+11-8
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
// NOTE: 25185.byteSwapped = 0x62 'a', 0x61 'b'
88
// CHECK-LABEL: test_ascii_scalar_scalar2
9-
// CHECK: insertvalue { i32, i32, i32 } { i32 25185,
9+
// CHECK: ret { i32, i32, i32 } { i32 25185, i32 0, i32 {{[0-9]+}} }
1010
public func test_ascii_scalar_scalar2() -> String {
1111
return "a" + "b"
1212
}
1313

1414

1515
// NOTE: 11125601.byteSwapped = 0x61 'a', 0xC3 0xA9 'é'
1616
// CHECK-LABEL: test_scalar_otherscalar
17-
// CHECK: insertvalue { i32, i32, i32 } { i32 11125601,
17+
// CHECK: ret { i32, i32, i32 } { i32 11125601, i32 0, i32 {{[0-9]+}} }
1818
public func test_scalar_otherscalar() -> String {
1919
return "a" + "é"
2020
}
@@ -23,44 +23,47 @@ public func test_scalar_otherscalar() -> String {
2323
// NOTE: -8097488946593795999 = 0x8f9ff0b4959ff061
2424
// NOTE: -1784680351 = 0x959ff061, -1885343564 = 0x8f9ff0b4
2525
// CHECK-LABEL: test_scalar_char
26-
// CHECK: insertvalue { i32, i32, i32 } { i32 -1784680351, i32 -1885343564,
26+
// CHECK: ret { i32, i32, i32 } { i32 -1784680351, i32 -1885343564, i32 {{[0-9]+}} }
2727
public func test_scalar_char() -> String {
2828
return "a" + "🕴🏿"
2929
}
3030

3131
// NOTE: 112585666577249.byteSwapped = 0x61 'a', 0xc3 0xa9 'é', 0x64 'd', 0x65 'e', 0x66 'f'
3232
// NOTE: 112585666577249 = 1688847201 + (26213 << 32)
3333
// CHECK-LABEL: test_strng_strng2
34-
// CHECK: insertvalue { i32, i32, i32 } { i32 1688847201, i32 26213,
34+
// CHECK: ret { i32, i32, i32 } { i32 1688847201, i32 26213, i32 {{[0-9]+}} }
3535
public func test_strng_strng2() -> String {
3636
return "" + "def"
3737
}
3838

3939
// NOTE: 43 = code-unit length
40+
// NOTE: 20 = native bias
4041
// CHECK-LABEL: test_scalar_strng
41-
// CHECK: insertvalue { i32, i32, i32 } { i32 43, i32 sub
42+
// CHECK: ret { i32, i32, i32 } { i32 43, i32 sub (i32 {{.*}}, i32 20)
4243
public func test_scalar_strng() -> String {
4344
return "a" + "👨🏿‍💼+🧙🏿‍♂️=🕴🏿"
4445
}
4546

4647
// NOTE: 7450828190687388257.byteSwapped = 0x61 'a', 0x62 'b', 0x63 'c', 0x64 'd', 0xC3 0xA8 'è', 0x66 'f', 0x67 'g', ...
4748
// NOTE: 1684234849 = 0x64636261, 1734781123 = 0x6766a8c3
4849
// CHECK-LABEL test_strng_concat_smol
49-
// CHECK: insertvalue { i32, i32, i32 } { i32 1684234849, i32 1734781123,
50+
// CHECK: ret { i32, i32, i32 } { i32 1684234849, i32 1734781123,
5051
public func test_strng_concat_smol() -> String {
5152
return "a" + "bc" + "dèf" + ""
5253
}
5354

5455
// NOTE: 11 = code-unit length
56+
// NOTE: 20 = native bias
5557
// CHECK-LABEL test_strng_concat_not_quite_smol
56-
// CHECK: insertvalue { i32, i32, i32 } { i32 11, i32 sub
58+
// CHECK: ret { i32, i32, i32 } { i32 11, i32 sub (i32 {{.*}}, i32 20)
5759
public func test_strng_concat_not_quite_smol() -> String {
5860
return "a" + "bc" + "dèf" + "ghī"
5961
}
6062

6163
// NOTE: 23 = code-unit length
64+
// NOTE: 20 = native bias
6265
// CHECK-LABEL test_strng_concat_large
63-
// CHECK: insertvalue { i32, i32, i32 } { i32 23, i32 sub
66+
// CHECK: ret { i32, i32, i32 } { i32 23, i32 sub (i32 {{.*}}, i32 20)
6467
public func test_strng_concat_large() -> String {
6568
return "a" + "bc" + "dèf" + "ghī" + "jklmn" + "o" + "𝛒qr"
6669
}

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

+2
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ Protocol _NSEnumerator has been removed
483483
Protocol _NSFastEnumeration has been removed
484484
Protocol _ShadowProtocol has been removed
485485

486+
Var _StringObject.discriminator has been renamed to Var _StringObject._discriminator
487+
486488
Func ManagedBufferPointer._sanityCheckValidBufferClass(_:creating:) has been removed
487489
Func _sanityCheck(_:_:file:line:) has been removed
488490
Func _sanityCheckFailure(_:file:line:) has been removed

validation-test/Reflection/reflect_String.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ reflect(object: obj)
4949
// CHECK-32-NEXT: (multi_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=253 bitwise_takable=1
5050
// (unstable implementation details omitted)
5151
// CHECK-32: (field name=_discriminator offset=9
52-
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=128 bitwise_takable=1
52+
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1
5353
// (unstable implementation details omitted)
5454
// CHECK-32: (field name=_flags offset=10
5555
// CHECK-32-NEXT: (struct size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1

0 commit comments

Comments
 (0)