Skip to content

Commit bea2507

Browse files
authored
Merge pull request #42178 from buttaface/tags
[5.6][android] Move the string and other tags in pointers to the second byte because Android enabled memory tagging
2 parents 37dfabd + cc8ae1c commit bea2507

File tree

8 files changed

+139
-8
lines changed

8 files changed

+139
-8
lines changed

lib/IRGen/MetadataRequest.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,9 +2659,16 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type,
26592659
unsigned mangledStringSize;
26602660
std::tie(mangledString, mangledStringSize) =
26612661
IGM.getTypeRef(type, CanGenericSignature(), MangledTypeRefRole::Metadata);
2662-
2663-
assert(mangledStringSize < 0x80000000u
2664-
&& "2GB of mangled name ought to be enough for anyone");
2662+
2663+
// Android AArch64 reserves the top byte of the address for memory tagging
2664+
// since Android 11, so only use the bottom 23 bits to store this size
2665+
// and the 24th bit to signal that there is a size.
2666+
if (IGM.Triple.isAndroid() && IGM.Triple.getArch() == llvm::Triple::aarch64)
2667+
assert(mangledStringSize < 0x00800001u &&
2668+
"8MB of mangled name ought to be enough for Android AArch64");
2669+
else
2670+
assert(mangledStringSize < 0x80000000u &&
2671+
"2GB of mangled name ought to be enough for anyone");
26652672

26662673
// Get or create the cache variable if necessary.
26672674
auto cache = IGM.getAddrOfTypeMetadataDemanglingCacheVariable(type,
@@ -2731,6 +2738,21 @@ emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type,
27312738
auto contBB = subIGF.createBasicBlock("");
27322739
llvm::Value *comparison = subIGF.Builder.CreateICmpSLT(load,
27332740
llvm::ConstantInt::get(IGM.Int64Ty, 0));
2741+
2742+
// Check if the 24th bit is set on Android AArch64 and only instantiate the
2743+
// type metadata if it is, as otherwise it might be negative only because
2744+
// of the memory tag on Android.
2745+
if (IGM.Triple.isAndroid() &&
2746+
IGM.Triple.getArch() == llvm::Triple::aarch64) {
2747+
2748+
auto getBitAfterAndroidTag = subIGF.Builder.CreateAnd(
2749+
load, llvm::ConstantInt::get(IGM.Int64Ty, 0x0080000000000000));
2750+
auto checkNotAndroidTag = subIGF.Builder.CreateICmpNE(
2751+
getBitAfterAndroidTag, llvm::ConstantInt::get(IGM.Int64Ty, 0));
2752+
2753+
comparison = subIGF.Builder.CreateAnd(comparison, checkNotAndroidTag);
2754+
}
2755+
27342756
comparison = subIGF.Builder.CreateExpect(comparison,
27352757
llvm::ConstantInt::get(IGM.Int1Ty, 0));
27362758
subIGF.Builder.CreateCondBr(comparison, isUnfilledBB, contBB);

lib/IRGen/SwiftTargetInfo.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,17 @@ static void setToMask(SpareBitVector &bits, unsigned size, uint64_t mask) {
3636
/// Configures target-specific information for arm64 platforms.
3737
static void configureARM64(IRGenModule &IGM, const llvm::Triple &triple,
3838
SwiftTargetInfo &target) {
39-
setToMask(target.PointerSpareBits, 64,
40-
SWIFT_ABI_ARM64_SWIFT_SPARE_BITS_MASK);
41-
setToMask(target.ObjCPointerReservedBits, 64,
42-
SWIFT_ABI_ARM64_OBJC_RESERVED_BITS_MASK);
39+
if (triple.isAndroid()) {
40+
setToMask(target.PointerSpareBits, 64,
41+
SWIFT_ABI_ANDROID_ARM64_SWIFT_SPARE_BITS_MASK);
42+
setToMask(target.ObjCPointerReservedBits, 64,
43+
SWIFT_ABI_ANDROID_ARM64_OBJC_RESERVED_BITS_MASK);
44+
} else {
45+
setToMask(target.PointerSpareBits, 64,
46+
SWIFT_ABI_ARM64_SWIFT_SPARE_BITS_MASK);
47+
setToMask(target.ObjCPointerReservedBits, 64,
48+
SWIFT_ABI_ARM64_OBJC_RESERVED_BITS_MASK);
49+
}
4350
setToMask(target.IsObjCPointerBit, 64, SWIFT_ABI_ARM64_IS_OBJC_BIT);
4451

4552
if (triple.isOSDarwin()) {

stdlib/public/SwiftShims/HeapObject.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,22 @@ static_assert(alignof(HeapObject) == alignof(void*),
157157
#endif
158158
#define _swift_abi_SwiftSpareBitsMask \
159159
(__swift_uintptr_t) SWIFT_ABI_ARM64_SWIFT_SPARE_BITS_MASK
160+
#if defined(__ANDROID__)
161+
#define _swift_abi_ObjCReservedBitsMask \
162+
(__swift_uintptr_t) SWIFT_ABI_ANDROID_ARM64_OBJC_RESERVED_BITS_MASK
163+
#else
160164
#define _swift_abi_ObjCReservedBitsMask \
161165
(__swift_uintptr_t) SWIFT_ABI_ARM64_OBJC_RESERVED_BITS_MASK
166+
#endif
162167
#define _swift_abi_ObjCReservedLowBits \
163168
(unsigned) SWIFT_ABI_ARM64_OBJC_NUM_RESERVED_LOW_BITS
169+
#if defined(__ANDROID__)
170+
#define _swift_BridgeObject_TaggedPointerBits \
171+
(__swift_uintptr_t) SWIFT_ABI_DEFAULT_BRIDGEOBJECT_TAG_64 >> 8
172+
#else
164173
#define _swift_BridgeObject_TaggedPointerBits \
165174
(__swift_uintptr_t) SWIFT_ABI_DEFAULT_BRIDGEOBJECT_TAG_64
175+
#endif
166176

167177
#elif defined(__powerpc64__)
168178

stdlib/public/SwiftShims/System.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,19 @@
152152
/// Darwin reserves the low 4GB of address space.
153153
#define SWIFT_ABI_DARWIN_ARM64_LEAST_VALID_POINTER 0x100000000ULL
154154

155+
// Android AArch64 reserves the top byte for pointer tagging since Android 11,
156+
// so shift the spare bits tag to the second byte and zero the ObjC tag.
157+
#define SWIFT_ABI_ANDROID_ARM64_SWIFT_SPARE_BITS_MASK 0x00F0000000000007ULL
158+
#define SWIFT_ABI_ANDROID_ARM64_OBJC_RESERVED_BITS_MASK 0x0000000000000000ULL
159+
160+
#if defined(__ANDROID__) && defined(__aarch64__)
161+
#define SWIFT_ABI_ARM64_SWIFT_SPARE_BITS_MASK SWIFT_ABI_ANDROID_ARM64_SWIFT_SPARE_BITS_MASK
162+
#else
155163
// TBI guarantees the top byte of pointers is unused, but ARMv8.5-A
156164
// claims the bottom four bits of that for memory tagging.
157165
// Heap objects are eight-byte aligned.
158166
#define SWIFT_ABI_ARM64_SWIFT_SPARE_BITS_MASK 0xF000000000000007ULL
167+
#endif
159168

160169
// Objective-C reserves just the high bit for tagged pointers.
161170
#define SWIFT_ABI_ARM64_OBJC_RESERVED_BITS_MASK 0x8000000000000000ULL

stdlib/public/core/KeyPath.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,11 @@ internal struct KeyPathBuffer {
17641764
internal mutating func pushRaw(size: Int, alignment: Int)
17651765
-> UnsafeMutableRawBufferPointer {
17661766
var baseAddress = buffer.baseAddress.unsafelyUnwrapped
1767+
#if os(Android) && arch(arm64)
1768+
var misalign = Int(bitPattern: baseAddress) & (alignment - 1)
1769+
#else
17671770
var misalign = Int(bitPattern: baseAddress) % alignment
1771+
#endif
17681772
if misalign != 0 {
17691773
misalign = alignment - misalign
17701774
baseAddress = baseAddress.advanced(by: misalign)
@@ -3242,7 +3246,11 @@ internal struct InstantiateKeyPathBuffer: KeyPathPatternVisitor {
32423246
) {
32433247
let alignment = MemoryLayout<T>.alignment
32443248
var baseAddress = destData.baseAddress.unsafelyUnwrapped
3249+
#if os(Android) && arch(arm64)
3250+
var misalign = Int(bitPattern: baseAddress) & (alignment - 1)
3251+
#else
32453252
var misalign = Int(bitPattern: baseAddress) % alignment
3253+
#endif
32463254
if misalign != 0 {
32473255
misalign = alignment - misalign
32483256
baseAddress = baseAddress.advanced(by: misalign)

stdlib/public/core/SmallString.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
// ↑ ↑
2424
// first (leftmost) code unit discriminator (incl. count)
2525
//
26+
// On Android AArch64, there is one less byte available because the discriminator
27+
// is stored in the penultimate code unit instead, to match where it's stored
28+
// for large strings.
2629
@frozen @usableFromInline
2730
internal struct _SmallString {
2831
@usableFromInline
@@ -78,6 +81,8 @@ extension _SmallString {
7881
internal static var capacity: Int {
7982
#if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32)
8083
return 10
84+
#elseif os(Android) && arch(arm64)
85+
return 14
8186
#else
8287
return 15
8388
#endif
@@ -111,7 +116,11 @@ extension _SmallString {
111116
// usage: it always clears the discriminator and count (in case it's full)
112117
@inlinable @inline(__always)
113118
internal var zeroTerminatedRawCodeUnits: RawBitPattern {
119+
#if os(Android) && arch(arm64)
120+
let smallStringCodeUnitMask = ~UInt64(0xFFFF).bigEndian // zero last two bytes
121+
#else
114122
let smallStringCodeUnitMask = ~UInt64(0xFF).bigEndian // zero last byte
123+
#endif
115124
return (self._storage.0, self._storage.1 & smallStringCodeUnitMask)
116125
}
117126

stdlib/public/core/StringObject.swift

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@
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 {
349359
extension _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 {

stdlib/public/runtime/HeapObject.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ static inline bool isValidPointerForNativeRetain(const void *p) {
6666
// arm64_32 is special since it has 32-bit pointers but __arm64__ is true.
6767
// Catch it early since __POINTER_WIDTH__ is generally non-portable.
6868
return p != nullptr;
69+
#elif defined(__ANDROID__) && defined(__aarch64__)
70+
// Check the top of the second byte instead, since Android AArch64 reserves
71+
// the top byte for its own pointer tagging since Android 11.
72+
return (intptr_t)((uintptr_t)p << 8) > 0;
6973
#elif defined(__x86_64__) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(__s390x__) || (defined(__powerpc64__) && defined(__LITTLE_ENDIAN__))
7074
// On these platforms, except s390x, the upper half of address space is reserved for the
7175
// kernel, so we can assume that pointer values in this range are invalid.

0 commit comments

Comments
 (0)