56
56
can compile to a fused check-and-branch, even if that burns part of the
57
57
encoding space.
58
58
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
+
59
64
On 32-bit platforms, we store an explicit discriminator (as a UInt8) with the
60
65
same encoding as above, placed in the high bits. E.g. `b62` above is in
61
66
`_discriminator`'s `b6`.
@@ -111,8 +116,13 @@ internal struct _StringObject {
111
116
112
117
@inlinable @inline ( __always)
113
118
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
114
123
_internalInvariant ( discriminator & 0xFF00_0000_0000_0000 == discriminator,
115
124
" only the top byte can carry the discriminator and small count " )
125
+ #endif
116
126
117
127
self . _count = count
118
128
self . _variant = variant
@@ -349,7 +359,13 @@ extension _StringObject.Nibbles {
349
359
extension _StringObject . Nibbles {
350
360
// Mask for address bits, i.e. non-discriminator and non-extra high bits
351
361
@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
+ }
353
369
354
370
// Mask for address bits, i.e. non-discriminator and non-extra high bits
355
371
@inlinable @inline ( __always)
@@ -360,20 +376,32 @@ extension _StringObject.Nibbles {
360
376
// Discriminator for small strings
361
377
@inlinable @inline ( __always)
362
378
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
363
382
return isASCII ? 0xE000_0000_0000_0000 : 0xA000_0000_0000_0000
383
+ #endif
364
384
}
365
385
366
386
// Discriminator for small strings
367
387
@inlinable @inline ( __always)
368
388
internal static func small( withCount count: Int , isASCII: Bool ) -> UInt64 {
369
389
_internalInvariant ( count <= _SmallString. capacity)
390
+ #if os(Android) && arch(arm64)
391
+ return small ( isASCII: isASCII) | UInt64 ( truncatingIfNeeded: count) &<< 48
392
+ #else
370
393
return small ( isASCII: isASCII) | UInt64 ( truncatingIfNeeded: count) &<< 56
394
+ #endif
371
395
}
372
396
373
397
// Discriminator for large, immortal, swift-native strings
374
398
@inlinable @inline ( __always)
375
399
internal static func largeImmortal( ) -> UInt64 {
400
+ #if os(Android) && arch(arm64)
401
+ return 0x0080_0000_0000_0000
402
+ #else
376
403
return 0x8000_0000_0000_0000
404
+ #endif
377
405
}
378
406
379
407
// Discriminator for large, mortal (i.e. managed), swift-native strings
@@ -397,15 +425,23 @@ extension _StringObject {
397
425
398
426
@inlinable @inline ( __always)
399
427
internal var isImmortal : Bool {
428
+ #if os(Android) && arch(arm64)
429
+ return ( discriminatedObjectRawBits & 0x0080_0000_0000_0000 ) != 0
430
+ #else
400
431
return ( discriminatedObjectRawBits & 0x8000_0000_0000_0000 ) != 0
432
+ #endif
401
433
}
402
434
403
435
@inlinable @inline ( __always)
404
436
internal var isMortal : Bool { return !isImmortal }
405
437
406
438
@inlinable @inline ( __always)
407
439
internal var isSmall : Bool {
440
+ #if os(Android) && arch(arm64)
441
+ return ( discriminatedObjectRawBits & 0x0020_0000_0000_0000 ) != 0
442
+ #else
408
443
return ( discriminatedObjectRawBits & 0x2000_0000_0000_0000 ) != 0
444
+ #endif
409
445
}
410
446
411
447
@inlinable @inline ( __always)
@@ -419,7 +455,11 @@ extension _StringObject {
419
455
// - Non-Cocoa shared strings
420
456
@inlinable @inline ( __always)
421
457
internal var providesFastUTF8 : Bool {
458
+ #if os(Android) && arch(arm64)
459
+ return ( discriminatedObjectRawBits & 0x0010_0000_0000_0000 ) == 0
460
+ #else
422
461
return ( discriminatedObjectRawBits & 0x1000_0000_0000_0000 ) == 0
462
+ #endif
423
463
}
424
464
425
465
@inlinable @inline ( __always)
@@ -429,16 +469,26 @@ extension _StringObject {
429
469
// conforms to `_AbstractStringStorage`
430
470
@inline ( __always)
431
471
internal var hasStorage : Bool {
472
+ #if os(Android) && arch(arm64)
473
+ return ( discriminatedObjectRawBits & 0x00F0_0000_0000_0000 ) == 0
474
+ #else
432
475
return ( discriminatedObjectRawBits & 0xF000_0000_0000_0000 ) == 0
476
+ #endif
433
477
}
434
478
435
479
// Whether we are a mortal, native (tail-allocated) string
436
480
@inline ( __always)
437
481
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
438
487
// b61 on the object means isSmall, and on countAndFlags means
439
488
// isNativelyStored. We just need to check that b61 is 0 on the object and 1
440
489
// on countAndFlags.
441
490
let bits = ~ discriminatedObjectRawBits & self . _countAndFlagsBits
491
+ #endif
442
492
let result = bits & 0x2000_0000_0000_0000 != 0
443
493
_internalInvariant ( !result || hasStorage, " native storage needs storage " )
444
494
return result
@@ -466,7 +516,11 @@ extension _StringObject {
466
516
@inline ( __always)
467
517
internal var largeIsCocoa : Bool {
468
518
_internalInvariant ( isLarge)
519
+ #if os(Android) && arch(arm64)
520
+ return ( discriminatedObjectRawBits & 0x0040_0000_0000_0000 ) != 0
521
+ #else
469
522
return ( discriminatedObjectRawBits & 0x4000_0000_0000_0000 ) != 0
523
+ #endif
470
524
}
471
525
472
526
// Whether this string is in one of our fastest representations:
@@ -535,7 +589,11 @@ extension _StringObject {
535
589
536
590
@inlinable
537
591
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
538
595
return Int ( truncatingIfNeeded: ( x & 0x0F00_0000_0000_0000 ) &>> 56 )
596
+ #endif
539
597
}
540
598
541
599
@inlinable @inline ( __always)
@@ -546,7 +604,11 @@ extension _StringObject {
546
604
547
605
@inlinable
548
606
internal static func getSmallIsASCII( fromRaw x: UInt64 ) -> Bool {
607
+ #if os(Android) && arch(arm64)
608
+ return x & 0x0040_0000_0000_0000 != 0
609
+ #else
549
610
return x & 0x4000_0000_0000_0000 != 0
611
+ #endif
550
612
}
551
613
@inlinable @inline ( __always)
552
614
internal var smallIsASCII : Bool {
0 commit comments