5656 can compile to a fused check-and-branch, even if that burns part of the
5757 encoding space.
5858
59+ On Android AArch64, we cannot use the top byte for large strings because it is
60+ reserved by the OS for memory tagging since Android 11, so shift the
61+ discriminator to the second byte instead. This burns one more byte on small
62+ strings.
63+
5964 On 32-bit platforms, we store an explicit discriminator (as a UInt8) with the
6065 same encoding as above, placed in the high bits. E.g. `b62` above is in
6166 `_discriminator`'s `b6`.
@@ -111,8 +116,13 @@ internal struct _StringObject {
111116
112117 @inlinable @inline ( __always)
113118 init ( count: Int , variant: Variant , discriminator: UInt64 , flags: UInt16 ) {
119+ #if os(Android) && arch(arm64)
120+ _internalInvariant ( discriminator & 0x00FF_0000_0000_0000 == discriminator,
121+ " only the second byte can carry the discriminator and small count on Android AArch64 " )
122+ #else
114123 _internalInvariant ( discriminator & 0xFF00_0000_0000_0000 == discriminator,
115124 " only the top byte can carry the discriminator and small count " )
125+ #endif
116126
117127 self . _count = count
118128 self . _variant = variant
@@ -349,7 +359,13 @@ extension _StringObject.Nibbles {
349359extension _StringObject . Nibbles {
350360 // Mask for address bits, i.e. non-discriminator and non-extra high bits
351361 @inlinable @inline ( __always)
352- static internal var largeAddressMask : UInt64 { return 0x0FFF_FFFF_FFFF_FFFF }
362+ static internal var largeAddressMask : UInt64 {
363+ #if os(Android) && arch(arm64)
364+ return 0xFF0F_FFFF_FFFF_FFFF
365+ #else
366+ return 0x0FFF_FFFF_FFFF_FFFF
367+ #endif
368+ }
353369
354370 // Mask for address bits, i.e. non-discriminator and non-extra high bits
355371 @inlinable @inline ( __always)
@@ -360,20 +376,32 @@ extension _StringObject.Nibbles {
360376 // Discriminator for small strings
361377 @inlinable @inline ( __always)
362378 internal static func small( isASCII: Bool ) -> UInt64 {
379+ #if os(Android) && arch(arm64)
380+ return isASCII ? 0x00E0_0000_0000_0000 : 0x00A0_0000_0000_0000
381+ #else
363382 return isASCII ? 0xE000_0000_0000_0000 : 0xA000_0000_0000_0000
383+ #endif
364384 }
365385
366386 // Discriminator for small strings
367387 @inlinable @inline ( __always)
368388 internal static func small( withCount count: Int , isASCII: Bool ) -> UInt64 {
369389 _internalInvariant ( count <= _SmallString. capacity)
390+ #if os(Android) && arch(arm64)
391+ return small ( isASCII: isASCII) | UInt64 ( truncatingIfNeeded: count) &<< 48
392+ #else
370393 return small ( isASCII: isASCII) | UInt64 ( truncatingIfNeeded: count) &<< 56
394+ #endif
371395 }
372396
373397 // Discriminator for large, immortal, swift-native strings
374398 @inlinable @inline ( __always)
375399 internal static func largeImmortal( ) -> UInt64 {
400+ #if os(Android) && arch(arm64)
401+ return 0x0080_0000_0000_0000
402+ #else
376403 return 0x8000_0000_0000_0000
404+ #endif
377405 }
378406
379407 // Discriminator for large, mortal (i.e. managed), swift-native strings
@@ -397,15 +425,23 @@ extension _StringObject {
397425
398426 @inlinable @inline ( __always)
399427 internal var isImmortal : Bool {
428+ #if os(Android) && arch(arm64)
429+ return ( discriminatedObjectRawBits & 0x0080_0000_0000_0000 ) != 0
430+ #else
400431 return ( discriminatedObjectRawBits & 0x8000_0000_0000_0000 ) != 0
432+ #endif
401433 }
402434
403435 @inlinable @inline ( __always)
404436 internal var isMortal : Bool { return !isImmortal }
405437
406438 @inlinable @inline ( __always)
407439 internal var isSmall : Bool {
440+ #if os(Android) && arch(arm64)
441+ return ( discriminatedObjectRawBits & 0x0020_0000_0000_0000 ) != 0
442+ #else
408443 return ( discriminatedObjectRawBits & 0x2000_0000_0000_0000 ) != 0
444+ #endif
409445 }
410446
411447 @inlinable @inline ( __always)
@@ -419,7 +455,11 @@ extension _StringObject {
419455 // - Non-Cocoa shared strings
420456 @inlinable @inline ( __always)
421457 internal var providesFastUTF8 : Bool {
458+ #if os(Android) && arch(arm64)
459+ return ( discriminatedObjectRawBits & 0x0010_0000_0000_0000 ) == 0
460+ #else
422461 return ( discriminatedObjectRawBits & 0x1000_0000_0000_0000 ) == 0
462+ #endif
423463 }
424464
425465 @inlinable @inline ( __always)
@@ -429,16 +469,26 @@ extension _StringObject {
429469 // conforms to `_AbstractStringStorage`
430470 @inline ( __always)
431471 internal var hasStorage : Bool {
472+ #if os(Android) && arch(arm64)
473+ return ( discriminatedObjectRawBits & 0x00F0_0000_0000_0000 ) == 0
474+ #else
432475 return ( discriminatedObjectRawBits & 0xF000_0000_0000_0000 ) == 0
476+ #endif
433477 }
434478
435479 // Whether we are a mortal, native (tail-allocated) string
436480 @inline ( __always)
437481 internal var hasNativeStorage : Bool {
482+ #if os(Android) && arch(arm64)
483+ // Android uses the same logic as explained below for other platforms,
484+ // except isSmall is at b53, so shift it to b61 first before proceeding.
485+ let bits = ~ ( discriminatedObjectRawBits << 8 ) & self . _countAndFlagsBits
486+ #else
438487 // b61 on the object means isSmall, and on countAndFlags means
439488 // isNativelyStored. We just need to check that b61 is 0 on the object and 1
440489 // on countAndFlags.
441490 let bits = ~ discriminatedObjectRawBits & self . _countAndFlagsBits
491+ #endif
442492 let result = bits & 0x2000_0000_0000_0000 != 0
443493 _internalInvariant ( !result || hasStorage, " native storage needs storage " )
444494 return result
@@ -466,7 +516,11 @@ extension _StringObject {
466516 @inline ( __always)
467517 internal var largeIsCocoa : Bool {
468518 _internalInvariant ( isLarge)
519+ #if os(Android) && arch(arm64)
520+ return ( discriminatedObjectRawBits & 0x0040_0000_0000_0000 ) != 0
521+ #else
469522 return ( discriminatedObjectRawBits & 0x4000_0000_0000_0000 ) != 0
523+ #endif
470524 }
471525
472526 // Whether this string is in one of our fastest representations:
@@ -535,7 +589,11 @@ extension _StringObject {
535589
536590 @inlinable
537591 internal static func getSmallCount( fromRaw x: UInt64 ) -> Int {
592+ #if os(Android) && arch(arm64)
593+ return Int ( truncatingIfNeeded: ( x & 0x000F_0000_0000_0000 ) &>> 48 )
594+ #else
538595 return Int ( truncatingIfNeeded: ( x & 0x0F00_0000_0000_0000 ) &>> 56 )
596+ #endif
539597 }
540598
541599 @inlinable @inline ( __always)
@@ -546,7 +604,11 @@ extension _StringObject {
546604
547605 @inlinable
548606 internal static func getSmallIsASCII( fromRaw x: UInt64 ) -> Bool {
607+ #if os(Android) && arch(arm64)
608+ return x & 0x0040_0000_0000_0000 != 0
609+ #else
549610 return x & 0x4000_0000_0000_0000 != 0
611+ #endif
550612 }
551613 @inlinable @inline ( __always)
552614 internal var smallIsASCII : Bool {
0 commit comments