Skip to content

[5.0][stdlib] _StringObject: Use a full 8-bit discriminator on 32-bit platforms #20702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 12 additions & 37 deletions stdlib/public/core/StringObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal struct _StringObject {
internal var _variant: Variant

@usableFromInline
internal var _discriminator: Builtin.Int7
internal var _discriminator: Discriminator

@usableFromInline
internal var _flags: Flags
Expand All @@ -149,11 +149,9 @@ internal struct _StringObject {
discriminator: Discriminator,
flags: Flags
) {
_internalInvariant(variant.isImmortal == discriminator.isImmortal)
self._count = count
self._variant = variant
self._discriminator =
Builtin.truncOrBitCast_Int8_Int7(discriminator._value._value)
self._discriminator = discriminator
self._flags = flags
}

Expand Down Expand Up @@ -197,20 +195,11 @@ internal struct _StringObject {
}

extension _StringObject {
#if arch(i386) || arch(arm)
@inlinable @inline(__always)
internal func discriminator(isImmortal: Bool) -> Discriminator {
let lowBits = UInt8(Builtin.zextOrBitCast_Int7_Int8(_discriminator))
guard isImmortal else { return Discriminator(lowBits) }
return Discriminator(lowBits | 0x80)
}
#endif

@inlinable
internal var discriminator: Discriminator {
@inline(__always) get {
#if arch(i386) || arch(arm)
return self.discriminator(isImmortal: _variant.isImmortal)
return _discriminator
#else
let d = objectRawBits &>> Nibbles.discriminatorShift
return Discriminator(UInt8(truncatingIfNeeded: d))
Expand All @@ -233,7 +222,7 @@ extension _StringObject {
let count = UInt64(truncatingIfNeeded: UInt(bitPattern: _count))
let payload = UInt64(truncatingIfNeeded: undiscriminatedObjectRawBits)
let flags = UInt64(truncatingIfNeeded: _flags._value)
let discr = UInt64(truncatingIfNeeded: discriminator._value)
let discr = UInt64(truncatingIfNeeded: _discriminator._value)
if isSmall {
// Rearrange small strings in a different way, compacting bytes into a
// contiguous sequence. See comment on small string layout below.
Expand Down Expand Up @@ -636,9 +625,7 @@ extension _StringObject {
internal var isSmall: Bool {
@inline(__always) get {
#if arch(i386) || arch(arm)
// Note: This assumes that the `isSmall` predicate doesn't look at the
// immortal bit. We may or may not actually be immortal.
return discriminator(isImmortal: true).isSmall
return _discriminator.isSmall
#else
return (objectRawBits & 0x2000_0000_0000_0000) != 0
#endif
Expand All @@ -658,9 +645,7 @@ extension _StringObject {
internal var providesFastUTF8: Bool {
@inline(__always) get {
#if arch(i386) || arch(arm)
// Note: This assumes that the `providesFastUTF8` predicate doesn't look
// at the immortal bit. We may or may not actually be immortal.
return discriminator(isImmortal: false).providesFastUTF8
return _discriminator.providesFastUTF8
#else
return (objectRawBits & 0x1000_0000_0000_0000) == 0
#endif
Expand All @@ -677,7 +662,7 @@ extension _StringObject {
internal var hasNativeStorage: Bool {
@inline(__always) get {
#if arch(i386) || arch(arm)
return discriminator.hasNativeStorage
return _discriminator.hasNativeStorage
#else
return (objectRawBits & 0xF800_0000_0000_0000) == 0
#endif
Expand All @@ -688,7 +673,7 @@ extension _StringObject {
internal var hasSharedStorage: Bool {
@inline(__always) get {
#if arch(i386) || arch(arm)
return discriminator.hasSharedStorage
return _discriminator.hasSharedStorage
#else
return (objectRawBits & 0xF800_0000_0000_0000)
== Nibbles.largeSharedMortal()
Expand All @@ -705,9 +690,7 @@ extension _StringObject {
@inline(__always) get {
_internalInvariant(isLarge && providesFastUTF8)
#if arch(i386) || arch(arm)
// Note: This assumes that the `largeFastIsNative` predicate doesn't look
// at the immortal bit. We may or may not actually be immortal.
return discriminator(isImmortal: false).largeFastIsNative
return _discriminator.largeFastIsNative
#else
return (objectRawBits & 0x0800_0000_0000_0000) == 0
#endif
Expand All @@ -726,9 +709,7 @@ extension _StringObject {
@inline(__always) get {
_internalInvariant(isLarge)
#if arch(i386) || arch(arm)
// Note: This assumes that the `largeIsCocoa` predicate doesn't look at
// the immortal bit. We may or may not actually be immortal.
return discriminator(isImmortal: false).largeIsCocoa
return _discriminator.largeIsCocoa
#else
return (objectRawBits & 0x4000_0000_0000_0000) != 0
#endif
Expand Down Expand Up @@ -770,12 +751,7 @@ extension _StringObject {
@inline(__always)
get {
_internalInvariant(isSmall)
#if arch(i386) || arch(arm)
// Note: This assumes that `isSmall` implies that we're immortal.
return discriminator(isImmortal: true).smallCount
#else
return discriminator.smallCount
#endif
}
}

Expand All @@ -785,8 +761,7 @@ extension _StringObject {
get {
_internalInvariant(isSmall)
#if arch(i386) || arch(arm)
// Note: This assumes that `isSmall` implies that we're immortal.
return discriminator(isImmortal: true).smallIsASCII
return _discriminator.smallIsASCII
#else
return objectRawBits & 0x4000_0000_0000_0000 != 0
#endif
Expand Down Expand Up @@ -1320,7 +1295,7 @@ extension _StringObject {
<\(word0) \(word1)> \
count: \(String(_count, radix: 16)), \
variant: \(_variant), \
discriminator: \(discriminator), \
discriminator: \(_discriminator), \
flags: \(_flags))
""")
#else
Expand Down
19 changes: 11 additions & 8 deletions test/SILOptimizer/concat_string_literals.32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

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


// NOTE: 11125601.byteSwapped = 0x61 'a', 0xC3 0xA9 'é'
// CHECK-LABEL: test_scalar_otherscalar
// CHECK: insertvalue { i32, i32, i32 } { i32 11125601,
// CHECK: ret { i32, i32, i32 } { i32 11125601, i32 0, i32 {{[0-9]+}} }
public func test_scalar_otherscalar() -> String {
return "a" + "é"
}
Expand All @@ -26,44 +26,47 @@ public func test_scalar_otherscalar() -> String {
// NOTE: -8097488946593795999 = 0x8f9ff0b4959ff061
// NOTE: -1784680351 = 0x959ff061, -1885343564 = 0x8f9ff0b4
// CHECK-LABEL: test_scalar_char
// CHECK: insertvalue { i32, i32, i32 } { i32 -1784680351, i32 -1885343564,
// CHECK: ret { i32, i32, i32 } { i32 -1784680351, i32 -1885343564, i32 {{[0-9]+}} }
public func test_scalar_char() -> String {
return "a" + "🕴🏿"
}

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

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

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

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

// NOTE: 23 = code-unit length
// NOTE: 20 = native bias
// CHECK-LABEL test_strng_concat_large
// CHECK: insertvalue { i32, i32, i32 } { i32 23, i32 sub
// CHECK: ret { i32, i32, i32 } { i32 23, i32 sub (i32 {{.*}}, i32 20)
public func test_strng_concat_large() -> String {
return "a" + "bc" + "dèf" + "ghī" + "jklmn" + "o" + "𝛒qr"
}
2 changes: 1 addition & 1 deletion validation-test/Reflection/reflect_String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ reflect(object: obj)
// CHECK-32-NEXT: (multi_payload_enum size=5 alignment=4 stride=8 num_extra_inhabitants=253 bitwise_takable=1
// (unstable implementation details omitted)
// CHECK-32: (field name=_discriminator offset=9
// CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=128 bitwise_takable=1
// CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1
// (unstable implementation details omitted)
// CHECK-32: (field name=_flags offset=10
// CHECK-32-NEXT: (struct size=2 alignment=2 stride=2 num_extra_inhabitants=0 bitwise_takable=1
Expand Down