Skip to content

Commit ebde767

Browse files
lorenteyairspeedswift
authored andcommitted
[5.0][stdlib] _StringObject: Use a full 8-bit discriminator on 32-bit platforms (#20702)
* [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. * [stdlib] Don’t change the 64-bit ABI
1 parent d9a0c3f commit ebde767

File tree

3 files changed

+24
-46
lines changed

3 files changed

+24
-46
lines changed

stdlib/public/core/StringObject.swift

Lines changed: 12 additions & 37 deletions
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

@@ -197,20 +195,11 @@ internal struct _StringObject {
197195
}
198196

199197
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-
209198
@inlinable
210199
internal var discriminator: Discriminator {
211200
@inline(__always) get {
212201
#if arch(i386) || arch(arm)
213-
return self.discriminator(isImmortal: _variant.isImmortal)
202+
return _discriminator
214203
#else
215204
let d = objectRawBits &>> Nibbles.discriminatorShift
216205
return Discriminator(UInt8(truncatingIfNeeded: d))
@@ -233,7 +222,7 @@ extension _StringObject {
233222
let count = UInt64(truncatingIfNeeded: UInt(bitPattern: _count))
234223
let payload = UInt64(truncatingIfNeeded: undiscriminatedObjectRawBits)
235224
let flags = UInt64(truncatingIfNeeded: _flags._value)
236-
let discr = UInt64(truncatingIfNeeded: discriminator._value)
225+
let discr = UInt64(truncatingIfNeeded: _discriminator._value)
237226
if isSmall {
238227
// Rearrange small strings in a different way, compacting bytes into a
239228
// contiguous sequence. See comment on small string layout below.
@@ -636,9 +625,7 @@ extension _StringObject {
636625
internal var isSmall: Bool {
637626
@inline(__always) get {
638627
#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
628+
return _discriminator.isSmall
642629
#else
643630
return (objectRawBits & 0x2000_0000_0000_0000) != 0
644631
#endif
@@ -658,9 +645,7 @@ extension _StringObject {
658645
internal var providesFastUTF8: Bool {
659646
@inline(__always) get {
660647
#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
648+
return _discriminator.providesFastUTF8
664649
#else
665650
return (objectRawBits & 0x1000_0000_0000_0000) == 0
666651
#endif
@@ -677,7 +662,7 @@ extension _StringObject {
677662
internal var hasNativeStorage: Bool {
678663
@inline(__always) get {
679664
#if arch(i386) || arch(arm)
680-
return discriminator.hasNativeStorage
665+
return _discriminator.hasNativeStorage
681666
#else
682667
return (objectRawBits & 0xF800_0000_0000_0000) == 0
683668
#endif
@@ -688,7 +673,7 @@ extension _StringObject {
688673
internal var hasSharedStorage: Bool {
689674
@inline(__always) get {
690675
#if arch(i386) || arch(arm)
691-
return discriminator.hasSharedStorage
676+
return _discriminator.hasSharedStorage
692677
#else
693678
return (objectRawBits & 0xF800_0000_0000_0000)
694679
== Nibbles.largeSharedMortal()
@@ -705,9 +690,7 @@ extension _StringObject {
705690
@inline(__always) get {
706691
_internalInvariant(isLarge && providesFastUTF8)
707692
#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
693+
return _discriminator.largeFastIsNative
711694
#else
712695
return (objectRawBits & 0x0800_0000_0000_0000) == 0
713696
#endif
@@ -726,9 +709,7 @@ extension _StringObject {
726709
@inline(__always) get {
727710
_internalInvariant(isLarge)
728711
#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
712+
return _discriminator.largeIsCocoa
732713
#else
733714
return (objectRawBits & 0x4000_0000_0000_0000) != 0
734715
#endif
@@ -770,12 +751,7 @@ extension _StringObject {
770751
@inline(__always)
771752
get {
772753
_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
777754
return discriminator.smallCount
778-
#endif
779755
}
780756
}
781757

@@ -785,8 +761,7 @@ extension _StringObject {
785761
get {
786762
_internalInvariant(isSmall)
787763
#if arch(i386) || arch(arm)
788-
// Note: This assumes that `isSmall` implies that we're immortal.
789-
return discriminator(isImmortal: true).smallIsASCII
764+
return _discriminator.smallIsASCII
790765
#else
791766
return objectRawBits & 0x4000_0000_0000_0000 != 0
792767
#endif
@@ -1320,7 +1295,7 @@ extension _StringObject {
13201295
<\(word0) \(word1)> \
13211296
count: \(String(_count, radix: 16)), \
13221297
variant: \(_variant), \
1323-
discriminator: \(discriminator), \
1298+
discriminator: \(_discriminator), \
13241299
flags: \(_flags))
13251300
""")
13261301
#else

test/SILOptimizer/concat_string_literals.32.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99

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

1717

1818
// NOTE: 11125601.byteSwapped = 0x61 'a', 0xC3 0xA9 'é'
1919
// CHECK-LABEL: test_scalar_otherscalar
20-
// CHECK: insertvalue { i32, i32, i32 } { i32 11125601,
20+
// CHECK: ret { i32, i32, i32 } { i32 11125601, i32 0, i32 {{[0-9]+}} }
2121
public func test_scalar_otherscalar() -> String {
2222
return "a" + "é"
2323
}
@@ -26,44 +26,47 @@ public func test_scalar_otherscalar() -> String {
2626
// NOTE: -8097488946593795999 = 0x8f9ff0b4959ff061
2727
// NOTE: -1784680351 = 0x959ff061, -1885343564 = 0x8f9ff0b4
2828
// CHECK-LABEL: test_scalar_char
29-
// CHECK: insertvalue { i32, i32, i32 } { i32 -1784680351, i32 -1885343564,
29+
// CHECK: ret { i32, i32, i32 } { i32 -1784680351, i32 -1885343564, i32 {{[0-9]+}} }
3030
public func test_scalar_char() -> String {
3131
return "a" + "🕴🏿"
3232
}
3333

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

4242
// NOTE: 43 = code-unit length
43+
// NOTE: 20 = native bias
4344
// CHECK-LABEL: test_scalar_strng
44-
// CHECK: insertvalue { i32, i32, i32 } { i32 43, i32 sub
45+
// CHECK: ret { i32, i32, i32 } { i32 43, i32 sub (i32 {{.*}}, i32 20)
4546
public func test_scalar_strng() -> String {
4647
return "a" + "👨🏿‍💼+🧙🏿‍♂️=🕴🏿"
4748
}
4849

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

5758
// NOTE: 11 = code-unit length
59+
// NOTE: 20 = native bias
5860
// CHECK-LABEL test_strng_concat_not_quite_smol
59-
// CHECK: insertvalue { i32, i32, i32 } { i32 11, i32 sub
61+
// CHECK: ret { i32, i32, i32 } { i32 11, i32 sub (i32 {{.*}}, i32 20)
6062
public func test_strng_concat_not_quite_smol() -> String {
6163
return "a" + "bc" + "dèf" + "ghī"
6264
}
6365

6466
// NOTE: 23 = code-unit length
67+
// NOTE: 20 = native bias
6568
// CHECK-LABEL test_strng_concat_large
66-
// CHECK: insertvalue { i32, i32, i32 } { i32 23, i32 sub
69+
// CHECK: ret { i32, i32, i32 } { i32 23, i32 sub (i32 {{.*}}, i32 20)
6770
public func test_strng_concat_large() -> String {
6871
return "a" + "bc" + "dèf" + "ghī" + "jklmn" + "o" + "𝛒qr"
6972
}

validation-test/Reflection/reflect_String.swift

Lines changed: 1 addition & 1 deletion
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)