From 009ce4b82a3f048384d93a4a1ac05a0dfdf4f521 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 17 Jun 2025 14:51:26 -0700 Subject: [PATCH] Add an "addressable for dependencies" flag to value witness flags. This may be useful for type layout of borrow fields in the future, should we decide that addressable-for-dependencies borrows should always be represented by a pointer. rdar://153650278 --- include/swift/ABI/MetadataValues.h | 38 +++++++---- lib/IRGen/GenValueWitness.cpp | 32 +++++++-- ...ddressable_for_dependencies_metadata.swift | 19 ++++++ test/IRGen/raw_layout.swift | 68 +++++++++---------- test/IRGen/raw_layout_multifile.swift | 16 ++--- test/Serialization/raw_layout.swift | 4 +- 6 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 test/IRGen/addressable_for_dependencies_metadata.swift diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index b9d55c9ad29..df242e7bab0 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -165,18 +165,19 @@ public: // flags for the struct. (The "non-inline" and "has-extra-inhabitants" bits // still require additional fixup.) enum : uint32_t { - AlignmentMask = 0x000000FF, - // unused 0x0000FF00, - IsNonPOD = 0x00010000, - IsNonInline = 0x00020000, - // unused 0x00040000, - HasSpareBits = 0x00080000, - IsNonBitwiseTakable = 0x00100000, - HasEnumWitnesses = 0x00200000, - Incomplete = 0x00400000, - IsNonCopyable = 0x00800000, - IsNonBitwiseBorrowable = 0x01000000, - // unused 0xFE000000, + AlignmentMask = 0x000000FF, + // unused 0x0000FF00, + IsNonPOD = 0x00010000, + IsNonInline = 0x00020000, + // unused 0x00040000, + HasSpareBits = 0x00080000, + IsNonBitwiseTakable = 0x00100000, + HasEnumWitnesses = 0x00200000, + Incomplete = 0x00400000, + IsNonCopyable = 0x00800000, + IsNonBitwiseBorrowable = 0x01000000, + IsAddressableForDependencies = 0x02000000, + // unused 0xFC000000, }; static constexpr const uint32_t MaxNumExtraInhabitants = 0x7FFFFFFF; @@ -268,6 +269,19 @@ public: return TargetValueWitnessFlags((Data & ~IsNonCopyable) | (isCopyable ? 0 : IsNonCopyable)); } + + /// True if values of this type are addressable-for-dependencies, meaning + /// that values of this type should be passed indirectly to functions that + /// produce lifetime-dependent values that could possibly contain pointers + /// to the inline storage of this type. + bool isAddressableForDependencies() const { + return Data & IsAddressableForDependencies; + } + constexpr TargetValueWitnessFlags withAddressableForDependencies(bool afd) const { + return TargetValueWitnessFlags((Data & ~IsAddressableForDependencies) | + (afd ? IsAddressableForDependencies : 0)); + } + /// True if this type's binary representation is that of an enum, and the /// enum value witness table entries are available in this type's value diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index ecd9a77f9de..6c6587fd6bd 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -837,7 +837,9 @@ struct BoundGenericTypeCharacteristics { FixedPacking packing; }; -ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType, +ValueWitnessFlags getValueWitnessFlags(IRGenModule &IGM, + const TypeInfo *TI, + SILType concreteType, FixedPacking packing) { ValueWitnessFlags flags; @@ -852,6 +854,12 @@ ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType, bool isBitwiseBorrowable = fixedTI->isBitwiseBorrowable(ResilienceExpansion::Maximal); assert(isBitwiseTakable || !isInline); + bool isAddressableForDependencies = + IGM.getSILModule().Types.getTypeLowering(concreteType, + TypeExpansionContext::minimal()) + .getRecursiveProperties() + .isAddressableForDependencies(); + flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue()) .withPOD(fixedTI->isTriviallyDestroyable(ResilienceExpansion::Maximal)) .withCopyable(fixedTI->isCopyable(ResilienceExpansion::Maximal)) @@ -864,7 +872,8 @@ ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType, // Swift prior to version 6 didn't have the // IsNotBitwiseBorrowable bit, so to avoid unnecessary variation // in metadata output, we only set the bit when needed. - .withBitwiseBorrowable(!isBitwiseTakable || isBitwiseBorrowable); + .withBitwiseBorrowable(!isBitwiseTakable || isBitwiseBorrowable) + .withAddressableForDependencies(isAddressableForDependencies); } else { flags = flags.withIncomplete(true); } @@ -1226,11 +1235,13 @@ static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B, case ValueWitness::Flags: { if (boundGenericCharacteristics) return B.addInt32( - getValueWitnessFlags(boundGenericCharacteristics->TI, + getValueWitnessFlags(IGM, + boundGenericCharacteristics->TI, boundGenericCharacteristics->concreteType, boundGenericCharacteristics->packing) .getOpaqueValue()); - return B.addInt32(getValueWitnessFlags(&concreteTI, concreteType, packing) + return B.addInt32(getValueWitnessFlags(IGM, &concreteTI, + concreteType, packing) .getOpaqueValue()); } @@ -1441,7 +1452,7 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, auto &ti = IGM.getTypeInfoForUnlowered(AbstractionPattern::getOpaque(), type); - // We only have known value witness tables for copyable types currently. + // We only have known value witness tables for copyable types currently. if (!ti.isCopyable(ResilienceExpansion::Maximal)) { return {}; } @@ -1454,6 +1465,17 @@ getAddrOfKnownValueWitnessTable(IRGenModule &IGM, CanType type, CanType witnessSurrogate; ReferenceCounting refCounting; + // All of our standard value witness tables are bitwise-borrowable and not + // addressable for dependencies. + if (!ti.isBitwiseBorrowable(ResilienceExpansion::Maximal) + || IGM.getSILModule().Types + .getTypeLowering(AbstractionPattern::getOpaque(), type, + TypeExpansionContext::minimal()) + .getRecursiveProperties() + .isAddressableForDependencies()) { + return {}; + } + // Empty types can use empty tuple witnesses. if (ti.isKnownEmpty(ResilienceExpansion::Maximal)) { witnessSurrogate = TupleType::getEmpty(C); diff --git a/test/IRGen/addressable_for_dependencies_metadata.swift b/test/IRGen/addressable_for_dependencies_metadata.swift new file mode 100644 index 00000000000..4bba2bfef0f --- /dev/null +++ b/test/IRGen/addressable_for_dependencies_metadata.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/chex.py < %s > %t/test.swift +// RUN: %target-swift-frontend -enable-experimental-feature AddressableTypes -emit-ir %s | %FileCheck %t/test.swift + +// REQUIRES: swift_feature_AddressableTypes + +@_addressableForDependencies +struct DirectlyAFD { + var x: Int32 +} +// CHECK-LABEL: @"$s{{.*}}11DirectlyAFDVWV" = +// -- 0x0200_0000: addressable for dependencies +// CHECK-SAME: , [[WORD:i(64|32)]] 4, [[WORD]] 4, , i32 0 } + +struct IndirectlyAFD { + var directly: DirectlyAFD +} +// CHECK-LABEL: @"$s{{.*}}13IndirectlyAFDVWV" = +// CHECK-SAME: , [[WORD:i(64|32)]] 4, [[WORD]] 4, , i32 0 } diff --git a/test/IRGen/raw_layout.swift b/test/IRGen/raw_layout.swift index 2c0200be218..7d6b26285ad 100644 --- a/test/IRGen/raw_layout.swift +++ b/test/IRGen/raw_layout.swift @@ -13,8 +13,8 @@ import RawLayoutCXX // CHECK-SAME: , {{i64|i32}} 4 // stride // CHECK-SAME: , {{i64|i32}} 4 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , @_rawLayout(size: 4, alignment: 4) struct Lock: ~Copyable { } @@ -29,8 +29,8 @@ struct PaddedStride { // CHECK-SAME: , {{i64|i32}} 5 // stride // CHECK-SAME: , {{i64|i32}} 8 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , @_rawLayout(like: PaddedStride) struct LikePaddedStride: ~Copyable {} @@ -39,8 +39,8 @@ struct LikePaddedStride: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 8 // stride // CHECK-SAME: , {{i64|i32}} 8 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , @_rawLayout(likeArrayOf: PaddedStride, count: 1) struct LikePaddedStrideArray1: ~Copyable {} @@ -49,9 +49,9 @@ struct LikePaddedStrideArray1: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 16 // stride // CHECK-SAME: , {{i64|i32}} 16 -// flags: alignment 3, noncopyable, non-bitwise-borrowable, (on 32-bit platforms) not storable inline -// CHECK-64-SAME: , -// CHECK-32-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies (on 32-bit platforms) not storable inline +// CHECK-64-SAME: , +// CHECK-32-SAME: , @_rawLayout(likeArrayOf: PaddedStride, count: 2) struct LikePaddedStrideArray2: ~Copyable {} @@ -60,8 +60,8 @@ struct LikePaddedStrideArray2: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 12 // stride // CHECK-SAME: , {{i64|i32}} 12 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct Keymaster: ~Copyable { let lock1: Lock let lock2: Lock @@ -127,8 +127,8 @@ struct Vector: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 8 // stride // CHECK-SAME: , {{i64|i32}} 8 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct UsesCell: ~Copyable { let someCondition: Bool let specialInt: Cell @@ -139,8 +139,8 @@ struct UsesCell: ~Copyable { // CHECK-SAME: , {{i64|i32}} 3 // stride // CHECK-SAME: , {{i64|i32}} 3 -// flags: alignment 0, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 0, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct BufferOf3Bool: ~Copyable { let buffer: SmallVectorOf3 } @@ -150,8 +150,8 @@ struct BufferOf3Bool: ~Copyable { // CHECK-SAME: , {{i64|i32}} 48 // stride // CHECK-SAME: , {{i64|i32}} 48 -// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline -// CHECK-SAME: , +// flags: alignment 7, noncopyable, non-bitwise-borrowable, addressable for dependencies, is not inline +// CHECK-SAME: , struct BadBuffer: ~Copyable { let buffer: SmallVectorOf3 } @@ -161,8 +161,8 @@ struct BadBuffer: ~Copyable { // CHECK-SAME: , {{i64|i32}} 2 // stride // CHECK-SAME: , {{i64|i32}} 2 -// flags: alignment 0, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 0, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct UsesVector: ~Copyable { let buffer: Vector } @@ -172,8 +172,8 @@ struct UsesVector: ~Copyable { // CHECK-SAME: , {{i64|i32}} 48 // stride // CHECK-SAME: , {{i64|i32}} 48 -// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline -// CHECK-SAME: , +// flags: alignment 7, noncopyable, non-bitwise-borrowable, addressable for dependencies, is not inline +// CHECK-SAME: , struct BadBuffer2: ~Copyable { let buffer: Vector } @@ -207,8 +207,8 @@ struct CellThatMovesAsLike: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 1 // stride // CHECK-SAME: , {{i64|i32}} 1 -// flags: not copyable, not bitwise takable, not pod, not inline -// CHECK-SAME: , +// flags: not copyable, not bitwise takable, not pod, not inline, addressable for dependencies +// CHECK-SAME: , struct ConcreteMoveAsLike: ~Copyable { let cell: CellThatMovesAsLike } @@ -224,8 +224,8 @@ struct ConcreteMoveAsLike: ~Copyable { // CHECK-SAME: , {{i64|i32}} 4 // stride // CHECK-SAME: , {{i64|i32}} 4 -// flags: alignment 3, not copyable, not bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, not copyable, not bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct ConcreteIntMoveAsLike: ~Copyable { let cell: CellThatMovesAsLike } @@ -257,8 +257,8 @@ struct SmallVectorOf2MovesAsLike: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 2 // stride // CHECK-SAME: , {{i64|i32}} 2 -// flags: not copyable, not bitwise takable, not pod, not inline -// CHECK-SAME: , +// flags: not copyable, not bitwise takable, not pod, not inline, addressable for dependencies +// CHECK-SAME: , struct ConcreteSmallVectorMovesAsLike: ~Copyable { let vector: SmallVectorOf2MovesAsLike } @@ -274,8 +274,8 @@ struct ConcreteSmallVectorMovesAsLike: ~Copyable { // CHECK-SAME: , {{i64|i32}} 8 // stride // CHECK-SAME: , {{i64|i32}} 8 -// flags: alignment 3, not copyable, not bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, not copyable, not bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct ConcreteSmallVectorIntMovesAsLike: ~Copyable { let vector: SmallVectorOf2MovesAsLike } @@ -307,8 +307,8 @@ struct VectorMovesAsLike: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 4 // stride // CHECK-SAME: , {{i64|i32}} 4 -// flags: not copyable, not bitwise takable, not pod, not inline -// CHECK-SAME: , +// flags: not copyable, not bitwise takable, not pod, not inline, addressable for dependencies +// CHECK-SAME: , struct ConcreteVectorMovesAsLike: ~Copyable { let vector: VectorMovesAsLike } @@ -324,9 +324,9 @@ struct ConcreteVectorMovesAsLike: ~Copyable { // CHECK-SAME: , {{i64|i32}} 16 // stride // CHECK-SAME: , {{i64|i32}} 16 -// flags: alignment 3, not copyable, not bitwise-borrowable, (on 32-bit platforms) not storable inline -// CHECK-64-SAME: , -// CHECK-32-SAME: , +// flags: alignment 3, not copyable, not bitwise-borrowable, addressable for dependencies, (on 32-bit platforms) not storable inline +// CHECK-64-SAME: , +// CHECK-32-SAME: , struct ConcreteVectorIntMovesAsLike: ~Copyable { let vector: VectorMovesAsLike } diff --git a/test/IRGen/raw_layout_multifile.swift b/test/IRGen/raw_layout_multifile.swift index bb0422ee500..5bb255c5be8 100644 --- a/test/IRGen/raw_layout_multifile.swift +++ b/test/IRGen/raw_layout_multifile.swift @@ -13,8 +13,8 @@ public struct Foo: ~Copyable {} // CHECK-SAME: , {{i64|i32}} 4 // stride // CHECK-SAME: , {{i64|i32}} 4 -// flags: alignment 3, noncopyable, non-bitwise-borrowable -// CHECK-SAME: , +// flags: alignment 3, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME: , struct MyInt: ~Copyable { let x: Int32Fake } @@ -24,8 +24,8 @@ struct MyInt: ~Copyable { // CHECK-SAME: , {{i64|i32}} 48 // stride // CHECK-SAME: , {{i64|i32}} 48 -// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline -// CHECK-SAME: , +// flags: alignment 7, noncopyable, non-bitwise-borrowable, addressable for dependencies, is not inline +// CHECK-SAME: , struct BadBuffer: ~Copyable { let buf = SmallVectorOf3() } @@ -35,10 +35,10 @@ struct BadBuffer: ~Copyable { // CHECK-SAME: , {{i64|i32}} 8 // stride // CHECK-SAME: , {{i64|i32}} 8 -// flags-32: alignment 7, noncopyable, non-bitwise-borrowable, is not inline -// CHECK-SAME-32: , -// flags-64: alignment 7, noncopyable, non-bitwise-borrowable -// CHECK-SAME-64: , +// flags-32: alignment 7, noncopyable, non-bitwise-borrowable, addressable for dependencies, is not inline +// CHECK-SAME-32: , +// flags-64: alignment 7, noncopyable, non-bitwise-borrowable, addressable for dependencies +// CHECK-SAME-64: , struct Weird: ~Copyable { let value = UnsafeCell() } diff --git a/test/Serialization/raw_layout.swift b/test/Serialization/raw_layout.swift index 065bdde6027..5ec4d97fc61 100644 --- a/test/Serialization/raw_layout.swift +++ b/test/Serialization/raw_layout.swift @@ -18,8 +18,8 @@ import RawLayoutCXX // CHECK-SAME: , {{i64|i32}} 1 // stride // CHECK-SAME: , {{i64|i32}} 1 -// flags: not copyable, not bitwise takable, not pod, not inline -// CHECK-SAME: , i32 9633792 +// flags: addressable for dependencies, not copyable, not bitwise takable, not pod, not inline +// CHECK-SAME: , i32 43188224 struct WeirdCXXTypeCell: ~Copyable { let cell: CellThatMovesLike }