IRGen: Set a "not bitwise borrowable" bit in value witnesses for @_rawLayout types.

For types like `Atomic` and `Mutex`, we want to know that even though they are
technically bitwise-takable, they differ from other bitwise-takable types until
this point because they are not also "bitwise-borrowable"; while borrowed,
they are pinned in memory, so they cannot be passed by value as a borrowed
parameter, unlike copyable bitwise-takable types. Add a bit to the value witness
table flags to record this.

Note that this patch does not include any accompanying runtime support for
propagating the flag into runtime-instantiated type metadata. There isn't yet
any runtime functionality that varies based on this flag, so that can
be implemented separately.

rdar://136396806
This commit is contained in:
Joe Groff
2024-09-23 18:14:45 -07:00
parent ada0ceb2d9
commit 57a56e5804
16 changed files with 214 additions and 91 deletions

View File

@@ -168,17 +168,18 @@ public:
// flags for the struct. (The "non-inline" and "has-extra-inhabitants" bits // flags for the struct. (The "non-inline" and "has-extra-inhabitants" bits
// still require additional fixup.) // still require additional fixup.)
enum : uint32_t { enum : uint32_t {
AlignmentMask = 0x000000FF, AlignmentMask = 0x000000FF,
// unused 0x0000FF00, // unused 0x0000FF00,
IsNonPOD = 0x00010000, IsNonPOD = 0x00010000,
IsNonInline = 0x00020000, IsNonInline = 0x00020000,
// unused 0x00040000, // unused 0x00040000,
HasSpareBits = 0x00080000, HasSpareBits = 0x00080000,
IsNonBitwiseTakable = 0x00100000, IsNonBitwiseTakable = 0x00100000,
HasEnumWitnesses = 0x00200000, HasEnumWitnesses = 0x00200000,
Incomplete = 0x00400000, Incomplete = 0x00400000,
IsNonCopyable = 0x00800000, IsNonCopyable = 0x00800000,
// unused 0xFF000000, IsNonBitwiseBorrowable = 0x01000000,
// unused 0xFE000000,
}; };
static constexpr const uint32_t MaxNumExtraInhabitants = 0x7FFFFFFF; static constexpr const uint32_t MaxNumExtraInhabitants = 0x7FFFFFFF;
@@ -243,6 +244,27 @@ public:
(isBT ? 0 : IsNonBitwiseTakable)); (isBT ? 0 : IsNonBitwiseTakable));
} }
/// True if values of this type can be passed by value when borrowed.
/// If this bit is true, then borrows of the value are independent of the
/// value's address, so a value can be passed in registers or memcpy'd
/// while borrowed. This is in contrast to Rust, for instance, where a
/// `&T` type is always represented as a pointer, and borrowing a
/// value always moves the borrowed value into memory.
bool isBitwiseBorrowable() const {
/// This bit was introduced with Swift 6; prior to the introduction of
/// `Atomic` and `Mutex`, a type was always bitwise-borrowable if it
/// was bitwise-takable. Compilers and runtimes before Swift 6 would
/// never set the `IsNonBitwiseBorrowable` bit in the value witness
/// table, but any type that sets `IsNonBitwiseTakable` is definitely
/// not bitwise borrowable.
return isBitwiseTakable()
&& !(Data & IsNonBitwiseBorrowable);
}
constexpr TargetValueWitnessFlags withBitwiseBorrowable(bool isBB) const {
return TargetValueWitnessFlags((Data & ~IsNonBitwiseBorrowable) |
(isBB ? 0 : IsNonBitwiseBorrowable));
}
/// True if values of this type can be copied. /// True if values of this type can be copied.
bool isCopyable() const { return !(Data & IsNonCopyable); } bool isCopyable() const { return !(Data & IsNonCopyable); }
constexpr TargetValueWitnessFlags withCopyable(bool isCopyable) const { constexpr TargetValueWitnessFlags withCopyable(bool isCopyable) const {

View File

@@ -3927,8 +3927,8 @@ namespace {
} else if (allSingleRefcount } else if (allSingleRefcount
&& ElementsWithNoPayload.size() <= 1) { && ElementsWithNoPayload.size() <= 1) {
CopyDestroyKind = TaggedRefcounted; CopyDestroyKind = TaggedRefcounted;
} else if (this->EnumImplStrategy::BitwiseTakable == IsBitwiseTakable && } else if (this->EnumImplStrategy::BitwiseTakable == IsBitwiseTakableAndBorrowable
Copyable == IsCopyable) { && Copyable == IsCopyable) {
CopyDestroyKind = BitwiseTakable; CopyDestroyKind = BitwiseTakable;
} }
} }
@@ -6396,7 +6396,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) {
? IsNotTriviallyDestroyable : IsTriviallyDestroyable; ? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
auto copyable = !theEnum->canBeCopyable() auto copyable = !theEnum->canBeCopyable()
? IsNotCopyable : IsCopyable; ? IsNotCopyable : IsCopyable;
auto bitwiseTakable = IsBitwiseTakable; // FIXME: will there be check here? auto bitwiseTakable = IsBitwiseTakableAndBorrowable; // FIXME: will there be check here?
bool allowFixedLayoutOptimizations = true; bool allowFixedLayoutOptimizations = true;
std::vector<Element> elementsWithPayload; std::vector<Element> elementsWithPayload;
std::vector<Element> elementsWithNoPayload; std::vector<Element> elementsWithNoPayload;
@@ -6408,7 +6408,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) {
payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal); payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
copyable = copyable & payloadTI.isCopyable(ResilienceExpansion::Maximal); copyable = copyable & payloadTI.isCopyable(ResilienceExpansion::Maximal);
bitwiseTakable = bitwiseTakable & bitwiseTakable = bitwiseTakable &
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal); payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
}; };
if (TC.IGM.isResilient(theEnum, ResilienceExpansion::Minimal)) if (TC.IGM.isResilient(theEnum, ResilienceExpansion::Minimal))
@@ -6861,7 +6861,8 @@ EnumImplStrategy::getFixedEnumTypeInfo(llvm::StructType *T, Size S,
abiAccessible); abiAccessible);
break; break;
case Loadable: case Loadable:
assert(isBT && "loadable enum not bitwise takable?!"); assert(isBT == IsBitwiseTakableAndBorrowable
&& "loadable enum not bitwise takable?!");
mutableTI = new LoadableEnumTypeInfo(*this, T, S, std::move(SB), A, mutableTI = new LoadableEnumTypeInfo(*this, T, S, std::move(SB), A,
isTriviallyDestroyable, isTriviallyDestroyable,
isCopyable, isCopyable,
@@ -7084,7 +7085,7 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeFixedLayout(
getFixedEnumTypeInfo( getFixedEnumTypeInfo(
enumTy, Size(sizeWithTag), spareBits.build(), alignment, enumTy, Size(sizeWithTag), spareBits.build(), alignment,
deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal), deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal),
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal), payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal),
copyable, isABIAccessible); copyable, isABIAccessible);
if (TIK >= Loadable && CopyDestroyKind == Normal) { if (TIK >= Loadable && CopyDestroyKind == Normal) {
@@ -7120,7 +7121,7 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeDynamicLayout(
return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy, return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy,
alignment, alignment,
deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal), deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal),
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal), payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal),
copyable, copyable,
enumAccessible)); enumAccessible));
} }
@@ -7155,7 +7156,7 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC,
? IsNotCopyable : IsCopyable; ? IsNotCopyable : IsCopyable;
auto isTriviallyDestroyable = theEnum->getValueTypeDestructor() auto isTriviallyDestroyable = theEnum->getValueTypeDestructor()
? IsNotTriviallyDestroyable : IsTriviallyDestroyable; ? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
IsBitwiseTakable_t isBT = IsBitwiseTakable; IsBitwiseTakable_t isBT = IsBitwiseTakableAndBorrowable;
PayloadSize = 0; PayloadSize = 0;
for (auto &elt : ElementsWithPayload) { for (auto &elt : ElementsWithPayload) {
auto &fixedPayloadTI = cast<FixedTypeInfo>(*elt.ti); auto &fixedPayloadTI = cast<FixedTypeInfo>(*elt.ti);
@@ -7163,8 +7164,7 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC,
worstAlignment = fixedPayloadTI.getFixedAlignment(); worstAlignment = fixedPayloadTI.getFixedAlignment();
if (!fixedPayloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal)) if (!fixedPayloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal))
isTriviallyDestroyable = IsNotTriviallyDestroyable; isTriviallyDestroyable = IsNotTriviallyDestroyable;
if (!fixedPayloadTI.isBitwiseTakable(ResilienceExpansion::Maximal)) isBT &= fixedPayloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
isBT = IsNotBitwiseTakable;
unsigned payloadBytes = fixedPayloadTI.getFixedSize().getValue(); unsigned payloadBytes = fixedPayloadTI.getFixedSize().getValue();
unsigned payloadBits = fixedPayloadTI.getFixedSize().getValueInBits(); unsigned payloadBits = fixedPayloadTI.getFixedSize().getValueInBits();
@@ -7324,12 +7324,12 @@ TypeInfo *MultiPayloadEnumImplStrategy::completeDynamicLayout(
Alignment alignment(1); Alignment alignment(1);
auto td = theEnum->getValueTypeDestructor() auto td = theEnum->getValueTypeDestructor()
? IsNotTriviallyDestroyable : IsTriviallyDestroyable; ? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
auto bt = IsBitwiseTakable; auto bt = IsBitwiseTakableAndBorrowable;
for (auto &element : ElementsWithPayload) { for (auto &element : ElementsWithPayload) {
auto &payloadTI = *element.ti; auto &payloadTI = *element.ti;
alignment = std::max(alignment, payloadTI.getBestKnownAlignment()); alignment = std::max(alignment, payloadTI.getBestKnownAlignment());
td &= payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal); td &= payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
bt &= payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal); bt &= payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
} }
applyLayoutAttributes(TC.IGM, theEnum, /*fixed*/false, alignment); applyLayoutAttributes(TC.IGM, theEnum, /*fixed*/false, alignment);

View File

@@ -900,11 +900,16 @@ class OpaqueExistentialTypeInfo final :
OpaqueExistentialTypeInfo(ArrayRef<const ProtocolDecl *> protocols, OpaqueExistentialTypeInfo(ArrayRef<const ProtocolDecl *> protocols,
llvm::Type *ty, Size size, llvm::Type *ty, Size size,
IsCopyable_t copyable,
SpareBitVector &&spareBits, SpareBitVector &&spareBits,
Alignment align) Alignment align)
: super(protocols, ty, size, : super(protocols, ty, size,
std::move(spareBits), align, std::move(spareBits), align,
IsNotTriviallyDestroyable, IsBitwiseTakable, IsCopyable, IsNotTriviallyDestroyable,
// Copyable existentials are bitwise-takable and borrowable.
// Noncopyable existentials are bitwise-takable only.
copyable ? IsBitwiseTakableAndBorrowable : IsBitwiseTakableOnly,
copyable,
IsFixedSize, IsABIAccessible) {} IsFixedSize, IsABIAccessible) {}
public: public:
@@ -1649,14 +1654,19 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) {
OpaqueExistentialLayout opaque(protosWithWitnessTables.size()); OpaqueExistentialLayout opaque(protosWithWitnessTables.size());
Alignment align = opaque.getAlignment(IGM); Alignment align = opaque.getAlignment(IGM);
Size size = opaque.getSize(IGM); Size size = opaque.getSize(IGM);
IsCopyable_t copyable = T->isNoncopyable() ? IsNotCopyable : IsCopyable;
// There are spare bits in the metadata pointer and witness table pointers // There are spare bits in the metadata pointer and witness table pointers
// consistent with a native object reference. // consistent with a native object reference.
// TODO: There are spare bits we could theoretically use in the type metadata // NB: There are spare bits we could theoretically use in the type metadata
// and witness table pointers, but opaque existentials are currently address- // and witness table pointers, but opaque existentials are address-
// only, and we can't soundly take advantage of spare bits for in-memory // only, and we can't soundly take advantage of spare bits for in-memory
// representations. // representations.
// Maybe an ABI break that made opaque existentials loadable could use those
// bits, though.
auto spareBits = SpareBitVector::getConstant(size.getValueInBits(), false); auto spareBits = SpareBitVector::getConstant(size.getValueInBits(), false);
return OpaqueExistentialTypeInfo::create(protosWithWitnessTables, type, size, return OpaqueExistentialTypeInfo::create(protosWithWitnessTables, type, size,
copyable,
std::move(spareBits), std::move(spareBits),
align); align);
} }

View File

@@ -579,7 +579,7 @@ const TypeInfo *TypeConverter::convertBlockStorageType(SILBlockStorageType *T) {
size = captureOffset + fixedCapture->getFixedSize(); size = captureOffset + fixedCapture->getFixedSize();
pod = fixedCapture->isTriviallyDestroyable(ResilienceExpansion::Maximal); pod = fixedCapture->isTriviallyDestroyable(ResilienceExpansion::Maximal);
bt = fixedCapture->isBitwiseTakable(ResilienceExpansion::Maximal); bt = fixedCapture->getBitwiseTakable(ResilienceExpansion::Maximal);
} }
llvm::Type *storageElts[] = { llvm::Type *storageElts[] = {

View File

@@ -1144,7 +1144,7 @@ public:
auto alignment = ti->getFixedAlignment().getValue(); auto alignment = ti->getFixedAlignment().getValue();
unsigned bitwiseTakable = unsigned bitwiseTakable =
(ti->isBitwiseTakable(ResilienceExpansion::Minimal) == IsBitwiseTakable (ti->getBitwiseTakable(ResilienceExpansion::Minimal) >= IsBitwiseTakableOnly
? 1 : 0); ? 1 : 0);
B.addInt32(alignment | (bitwiseTakable << 16)); B.addInt32(alignment | (bitwiseTakable << 16));

View File

@@ -236,7 +236,7 @@ llvm::Value *FixedTypeInfo::getIsTriviallyDestroyable(IRGenFunction &IGF, SILTyp
} }
llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) const { llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) const {
return llvm::ConstantInt::get(IGF.IGM.Int1Ty, return llvm::ConstantInt::get(IGF.IGM.Int1Ty,
isBitwiseTakable(ResilienceExpansion::Maximal) == IsBitwiseTakable); getBitwiseTakable(ResilienceExpansion::Maximal) >= IsBitwiseTakableOnly);
} }
llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const { llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const {
return IGM.getSize(getFixedStride()); return IGM.getSize(getFixedStride());

View File

@@ -849,12 +849,22 @@ ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType,
bool isInline = packing == FixedPacking::OffsetZero; bool isInline = packing == FixedPacking::OffsetZero;
bool isBitwiseTakable = bool isBitwiseTakable =
fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal); fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal);
bool isBitwiseBorrowable =
fixedTI->isBitwiseBorrowable(ResilienceExpansion::Maximal);
assert(isBitwiseTakable || !isInline); assert(isBitwiseTakable || !isInline);
flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue()) flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue())
.withPOD(fixedTI->isTriviallyDestroyable(ResilienceExpansion::Maximal)) .withPOD(fixedTI->isTriviallyDestroyable(ResilienceExpansion::Maximal))
.withCopyable(fixedTI->isCopyable(ResilienceExpansion::Maximal)) .withCopyable(fixedTI->isCopyable(ResilienceExpansion::Maximal))
.withInlineStorage(isInline) .withInlineStorage(isInline)
.withBitwiseTakable(isBitwiseTakable); .withBitwiseTakable(isBitwiseTakable)
// the IsNotBitwiseBorrowable bit only needs to be set if the
// type is bitwise-takable but not bitwise-borrowable, since
// a type must be bitwise-takable to be bitwise-borrowable.
//
// 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);
} else { } else {
flags = flags.withIncomplete(true); flags = flags.withIncomplete(true);
} }
@@ -1532,12 +1542,12 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
unsigned align = ti.getFixedAlignment().getValue(); unsigned align = ti.getFixedAlignment().getValue();
bool pod = ti.isTriviallyDestroyable(ResilienceExpansion::Maximal); bool pod = ti.isTriviallyDestroyable(ResilienceExpansion::Maximal);
bool bt = ti.isBitwiseTakable(ResilienceExpansion::Maximal); IsBitwiseTakable_t bt = ti.getBitwiseTakable(ResilienceExpansion::Maximal);
unsigned numExtraInhabitants = ti.getFixedExtraInhabitantCount(*this); unsigned numExtraInhabitants = ti.getFixedExtraInhabitantCount(*this);
// Try to use common type layouts exported by the runtime. // Try to use common type layouts exported by the runtime.
llvm::Constant *commonValueWitnessTable = nullptr; llvm::Constant *commonValueWitnessTable = nullptr;
if (pod && bt && numExtraInhabitants == 0) { if (pod && bt == IsBitwiseTakableAndBorrowable && numExtraInhabitants == 0) {
if (size == 0) if (size == 0)
commonValueWitnessTable = commonValueWitnessTable =
getAddrOfValueWitnessTable(Context.TheEmptyTupleType); getAddrOfValueWitnessTable(Context.TheEmptyTupleType);
@@ -1562,7 +1572,7 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
// Otherwise, see if a layout has been emitted with these characteristics // Otherwise, see if a layout has been emitted with these characteristics
// already. // already.
FixedLayoutKey key{size, numExtraInhabitants, align, pod, bt}; FixedLayoutKey key{size, numExtraInhabitants, align, pod, unsigned(bt)};
auto found = PrivateFixedLayouts.find(key); auto found = PrivateFixedLayouts.find(key);
if (found != PrivateFixedLayouts.end()) if (found != PrivateFixedLayouts.end())
@@ -1578,16 +1588,29 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
addValueWitness(*this, witnesses, witness, packing, t, silTy, ti); addValueWitness(*this, witnesses, witness, packing, t, silTy, ti);
} }
auto pod_bt_string = [](bool pod, IsBitwiseTakable_t bt) -> StringRef {
if (pod) {
return "_pod";
}
switch (bt) {
case IsNotBitwiseTakable:
return "";
case IsBitwiseTakableOnly:
return "_bt_nbb";
case IsBitwiseTakableAndBorrowable:
return "_bt";
}
};
auto layoutVar auto layoutVar
= witnesses.finishAndCreateGlobal( = witnesses.finishAndCreateGlobal(
"type_layout_" + llvm::Twine(size) "type_layout_" + llvm::Twine(size)
+ "_" + llvm::Twine(align) + "_" + llvm::Twine(align)
+ "_" + llvm::Twine::utohexstr(numExtraInhabitants) + "_" + llvm::Twine::utohexstr(numExtraInhabitants)
+ (pod ? "_pod" : + pod_bt_string(pod, bt),
bt ? "_bt" : ""), getPointerAlignment(),
getPointerAlignment(), /*constant*/ true,
/*constant*/ true, llvm::GlobalValue::PrivateLinkage);
llvm::GlobalValue::PrivateLinkage);
// Cast to the standard currency type for type layouts. // Cast to the standard currency type for type layouts.
auto layout = llvm::ConstantExpr::getBitCast(layoutVar, Int8PtrPtrTy); auto layout = llvm::ConstantExpr::getBitCast(layoutVar, Int8PtrPtrTy);

View File

@@ -80,9 +80,15 @@ inline IsLoadable_t &operator&=(IsLoadable_t &l, IsLoadable_t r) {
return (l = (l & r)); return (l = (l & r));
} }
enum IsBitwiseTakable_t : bool { IsNotBitwiseTakable, IsBitwiseTakable }; enum IsBitwiseTakable_t : uint8_t {
IsNotBitwiseTakable = 0,
// The type is bitwise-takable, but borrows are pinned to memory.
IsBitwiseTakableOnly = 1,
// The type is bitwise-takable and -borrowable.
IsBitwiseTakableAndBorrowable = 3,
};
inline IsBitwiseTakable_t operator&(IsBitwiseTakable_t l, IsBitwiseTakable_t r) { inline IsBitwiseTakable_t operator&(IsBitwiseTakable_t l, IsBitwiseTakable_t r) {
return IsBitwiseTakable_t(unsigned(l) & unsigned(r)); return IsBitwiseTakable_t(std::min(unsigned(l), unsigned(r)));
} }
inline IsBitwiseTakable_t &operator&=(IsBitwiseTakable_t &l, IsBitwiseTakable_t r) { inline IsBitwiseTakable_t &operator&=(IsBitwiseTakable_t &l, IsBitwiseTakable_t r) {
return (l = (l & r)); return (l = (l & r));

View File

@@ -1368,7 +1368,7 @@ private:
unsigned numExtraInhabitants; unsigned numExtraInhabitants;
unsigned align: 16; unsigned align: 16;
unsigned pod: 1; unsigned pod: 1;
unsigned bitwiseTakable: 1; unsigned bitwiseTakable: 2;
}; };
friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>; friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>;
llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts; llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts;

View File

@@ -65,8 +65,9 @@ protected:
IsABIAccessible_t isABIAccessible, IsABIAccessible_t isABIAccessible,
SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable) SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable)
: FixedTypeInfo(type, size, spareBits, align, pod, : FixedTypeInfo(type, size, spareBits, align, pod,
// All currently implemented loadable types are bitwise-takable. // All currently implemented loadable types are
IsBitwiseTakable, // bitwise-takable and -borrowable.
IsBitwiseTakableAndBorrowable,
copy, alwaysFixedSize, isABIAccessible, stik) { copy, alwaysFixedSize, isABIAccessible, stik) {
assert(isLoadable()); assert(isLoadable());
} }
@@ -80,8 +81,9 @@ protected:
IsABIAccessible_t isABIAccessible, IsABIAccessible_t isABIAccessible,
SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable) SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable)
: FixedTypeInfo(type, size, std::move(spareBits), align, pod, : FixedTypeInfo(type, size, std::move(spareBits), align, pod,
// All currently implemented loadable types are bitwise-takable. // All currently implemented loadable types are
IsBitwiseTakable, // bitwise-takable and borrowable.
IsBitwiseTakableAndBorrowable,
copy, alwaysFixedSize, isABIAccessible, stik) { copy, alwaysFixedSize, isABIAccessible, stik) {
assert(isLoadable()); assert(isLoadable());
} }

View File

@@ -72,7 +72,7 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
? IsNotTriviallyDestroyable : IsTriviallyDestroyable; ? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
auto copyable = (decl && !decl->canBeCopyable()) auto copyable = (decl && !decl->canBeCopyable())
? IsNotCopyable : IsCopyable; ? IsNotCopyable : IsCopyable;
IsBitwiseTakable_t bitwiseTakable = IsBitwiseTakable; IsBitwiseTakable_t bitwiseTakable = IsBitwiseTakableAndBorrowable;
if (decl && decl->getAttrs().hasAttribute<SensitiveAttr>()) { if (decl && decl->getAttrs().hasAttribute<SensitiveAttr>()) {
triviallyDestroyable = IsNotTriviallyDestroyable; triviallyDestroyable = IsNotTriviallyDestroyable;
@@ -87,7 +87,8 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
if (rawLayout && type) { if (rawLayout && type) {
auto sd = cast<StructDecl>(decl); auto sd = cast<StructDecl>(decl);
IsKnownTriviallyDestroyable = triviallyDestroyable; IsKnownTriviallyDestroyable = triviallyDestroyable;
IsKnownBitwiseTakable = bitwiseTakable; // Raw layout types are never bitwise-borrowable.
IsKnownBitwiseTakable = bitwiseTakable & IsBitwiseTakableOnly;
SpareBits.clear(); SpareBits.clear();
assert(!copyable); assert(!copyable);
IsKnownCopyable = copyable; IsKnownCopyable = copyable;
@@ -168,7 +169,9 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
// type its like. // type its like.
if (rawLayout->shouldMoveAsLikeType()) { if (rawLayout->shouldMoveAsLikeType()) {
IsKnownTriviallyDestroyable = likeFixedType->isTriviallyDestroyable(ResilienceExpansion::Maximal); IsKnownTriviallyDestroyable = likeFixedType->isTriviallyDestroyable(ResilienceExpansion::Maximal);
IsKnownBitwiseTakable = likeFixedType->isBitwiseTakable(ResilienceExpansion::Maximal); // Raw layout types are still never bitwise-borrowable.
IsKnownBitwiseTakable = likeFixedType->getBitwiseTakable(ResilienceExpansion::Maximal)
& IsBitwiseTakableOnly;
} }
} else { } else {
MinimumSize = Size(0); MinimumSize = Size(0);
@@ -417,7 +420,7 @@ bool StructLayoutBuilder::addField(ElementLayout &elt,
LayoutStrategy strategy) { LayoutStrategy strategy) {
auto &eltTI = elt.getType(); auto &eltTI = elt.getType();
IsKnownTriviallyDestroyable &= eltTI.isTriviallyDestroyable(ResilienceExpansion::Maximal); IsKnownTriviallyDestroyable &= eltTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
IsKnownBitwiseTakable &= eltTI.isBitwiseTakable(ResilienceExpansion::Maximal); IsKnownBitwiseTakable &= eltTI.getBitwiseTakable(ResilienceExpansion::Maximal);
IsKnownAlwaysFixedSize &= eltTI.isFixedSize(ResilienceExpansion::Minimal); IsKnownAlwaysFixedSize &= eltTI.isFixedSize(ResilienceExpansion::Minimal);
IsLoadable &= eltTI.isLoadable(); IsLoadable &= eltTI.isLoadable();
IsKnownCopyable &= eltTI.isCopyable(ResilienceExpansion::Maximal); IsKnownCopyable &= eltTI.isCopyable(ResilienceExpansion::Maximal);

View File

@@ -285,7 +285,7 @@ private:
bool IsFixedLayout = true; bool IsFixedLayout = true;
bool IsLoadable = true; bool IsLoadable = true;
IsTriviallyDestroyable_t IsKnownTriviallyDestroyable = IsTriviallyDestroyable; IsTriviallyDestroyable_t IsKnownTriviallyDestroyable = IsTriviallyDestroyable;
IsBitwiseTakable_t IsKnownBitwiseTakable = IsBitwiseTakable; IsBitwiseTakable_t IsKnownBitwiseTakable = IsBitwiseTakableAndBorrowable;
IsCopyable_t IsKnownCopyable = IsCopyable; IsCopyable_t IsKnownCopyable = IsCopyable;
IsFixedSize_t IsKnownAlwaysFixedSize = IsFixedSize; IsFixedSize_t IsKnownAlwaysFixedSize = IsFixedSize;
public: public:

View File

@@ -101,7 +101,7 @@ protected:
uint64_t OpaqueBits; uint64_t OpaqueBits;
SWIFT_INLINE_BITFIELD_BASE(TypeInfo, SWIFT_INLINE_BITFIELD_BASE(TypeInfo,
bitmax(NumSpecialTypeInfoKindBits,8)+6+1+1+1+3+1+1, bitmax(NumSpecialTypeInfoKindBits,8)+6+1+1+1+1+3+1+1,
/// The kind of supplemental API this type has, if any. /// The kind of supplemental API this type has, if any.
Kind : bitmax(NumSpecialTypeInfoKindBits,8), Kind : bitmax(NumSpecialTypeInfoKindBits,8),
@@ -114,6 +114,9 @@ protected:
/// Whether this type is known to be bitwise-takable. /// Whether this type is known to be bitwise-takable.
BitwiseTakable : 1, BitwiseTakable : 1,
/// Whether this type is known to be bitwise-borrowable.
BitwiseBorrowable : 1,
/// Whether this type is known to be copyable. /// Whether this type is known to be copyable.
Copyable : 1, Copyable : 1,
@@ -160,7 +163,9 @@ protected:
Bits.TypeInfo.Kind = unsigned(stik); Bits.TypeInfo.Kind = unsigned(stik);
Bits.TypeInfo.AlignmentShift = llvm::Log2_32(A.getValue()); Bits.TypeInfo.AlignmentShift = llvm::Log2_32(A.getValue());
Bits.TypeInfo.TriviallyDestroyable = pod; Bits.TypeInfo.TriviallyDestroyable = pod;
Bits.TypeInfo.BitwiseTakable = bitwiseTakable; Bits.TypeInfo.BitwiseTakable = bitwiseTakable >= IsBitwiseTakableOnly;
Bits.TypeInfo.BitwiseBorrowable =
bitwiseTakable == IsBitwiseTakableAndBorrowable;
Bits.TypeInfo.Copyable = copyable; Bits.TypeInfo.Copyable = copyable;
Bits.TypeInfo.SubclassKind = InvalidSubclassKind; Bits.TypeInfo.SubclassKind = InvalidSubclassKind;
Bits.TypeInfo.AlwaysFixedSize = alwaysFixedSize; Bits.TypeInfo.AlwaysFixedSize = alwaysFixedSize;
@@ -231,11 +236,25 @@ public:
} }
/// Whether this type is known to be bitwise-takable, i.e. "initializeWithTake" /// Whether this type is known to be bitwise-takable, i.e. "initializeWithTake"
/// is equivalent to a memcpy. /// is equivalent to a memcpy, and possibly bitwise-borrowable, i.e.,
IsBitwiseTakable_t isBitwiseTakable(ResilienceExpansion expansion) const { /// a borrowed argument can be passed by value rather than by reference.
return IsBitwiseTakable_t(Bits.TypeInfo.BitwiseTakable); IsBitwiseTakable_t getBitwiseTakable(ResilienceExpansion expansion) const {
return IsBitwiseTakable_t(
Bits.TypeInfo.BitwiseTakable | (Bits.TypeInfo.BitwiseBorrowable << 1));
} }
/// Whether this type is known to be bitwise-takable, i.e. "initializeWithTake"
/// is equivalent to a memcpy
bool isBitwiseTakable(ResilienceExpansion expansion) const {
return Bits.TypeInfo.BitwiseTakable;
}
/// Whether this type is known to be bitwise-borrowable, i.e.,
/// a borrowed argument can be passed by value rather than by reference.
bool isBitwiseBorrowable(ResilienceExpansion expansion) const {
return Bits.TypeInfo.BitwiseBorrowable;
}
/// Returns the type of special interface followed by this TypeInfo. /// Returns the type of special interface followed by this TypeInfo.
/// It is important for our design that this depends only on /// It is important for our design that this depends only on
/// immediate type structure and not on, say, properties that can /// immediate type structure and not on, say, properties that can

View File

@@ -0,0 +1,38 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/chex.py < %s > %t/existential-bitwise-borrowability.swift
// RUN: %target-swift-frontend -enable-experimental-feature RawLayout -enable-experimental-feature ValueGenerics -emit-ir -disable-availability-checking -I %S/Inputs -cxx-interoperability-mode=upcoming-swift %t/existential-bitwise-borrowability.swift | %FileCheck %t/existential-bitwise-borrowability.swift --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
// Copyable existentials are bitwise-borrowable (because copyable types are
// always bitwise-borrowable if they're bitwise-takable, and only bitwise-takable
// values are stored inline in existentials). Noncopyable existentials are
// not (since types like Atomic and Mutex can be stored inline in their
// buffers).
// CHECK-LABEL: @"$s{{[A-Za-z0-9_]*}}3FooVWV" = {{.*}} %swift.vwtable
// size
// CHECK-64-SAME: , {{i64|i32}} 32
// CHECK-32-SAME: , {{i64|i32}} 16
// stride
// CHECK-64-SAME: , {{i64|i32}} 32
// CHECK-32-SAME: , {{i64|i32}} 16
// flags: word alignment, noncopyable, non-POD, non-inline-storage
// CHECK-64-SAME: , <i32 0x830007>
// CHECK-32-SAME: , <i32 0x830003>
struct Foo: ~Copyable {
var x: Any
}
// CHECK-LABEL: @"$s{{[A-Za-z0-9_]*}}3BarVWV" = {{.*}} %swift.vwtable
// size
// CHECK-64-SAME: , {{i64|i32}} 32
// CHECK-32-SAME: , {{i64|i32}} 16
// stride
// CHECK-64-SAME: , {{i64|i32}} 32
// CHECK-32-SAME: , {{i64|i32}} 16
// flags: word alignment, non-bitwise-borrowable, noncopyable, non-POD, non-inline-storage
// CHECK-64-SAME: , <i32 0x1830007>
// CHECK-32-SAME: , <i32 0x1830003>
struct Bar: ~Copyable {
var x: any ~Copyable
}

View File

@@ -11,8 +11,8 @@ import RawLayoutCXX
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// stride // stride
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
@_rawLayout(size: 4, alignment: 4) @_rawLayout(size: 4, alignment: 4)
struct Lock: ~Copyable { } struct Lock: ~Copyable { }
@@ -27,8 +27,8 @@ struct PaddedStride {
// CHECK-SAME: , {{i64|i32}} 5 // CHECK-SAME: , {{i64|i32}} 5
// stride // stride
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
@_rawLayout(like: PaddedStride) @_rawLayout(like: PaddedStride)
struct LikePaddedStride: ~Copyable {} struct LikePaddedStride: ~Copyable {}
@@ -37,8 +37,8 @@ struct LikePaddedStride: ~Copyable {}
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// stride // stride
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
@_rawLayout(likeArrayOf: PaddedStride, count: 1) @_rawLayout(likeArrayOf: PaddedStride, count: 1)
struct LikePaddedStrideArray1: ~Copyable {} struct LikePaddedStrideArray1: ~Copyable {}
@@ -47,9 +47,9 @@ struct LikePaddedStrideArray1: ~Copyable {}
// CHECK-SAME: , {{i64|i32}} 16 // CHECK-SAME: , {{i64|i32}} 16
// stride // stride
// CHECK-SAME: , {{i64|i32}} 16 // CHECK-SAME: , {{i64|i32}} 16
// flags: alignment 3, noncopyable, (on 32-bit platforms) not storable inline // flags: alignment 3, noncopyable, non-bitwise-borrowable, (on 32-bit platforms) not storable inline
// CHECK-64-SAME: , <i32 0x800003> // CHECK-64-SAME: , <i32 0x1800003>
// CHECK-32-SAME: , <i32 0x820003> // CHECK-32-SAME: , <i32 0x1820003>
@_rawLayout(likeArrayOf: PaddedStride, count: 2) @_rawLayout(likeArrayOf: PaddedStride, count: 2)
struct LikePaddedStrideArray2: ~Copyable {} struct LikePaddedStrideArray2: ~Copyable {}
@@ -58,8 +58,8 @@ struct LikePaddedStrideArray2: ~Copyable {}
// CHECK-SAME: , {{i64|i32}} 12 // CHECK-SAME: , {{i64|i32}} 12
// stride // stride
// CHECK-SAME: , {{i64|i32}} 12 // CHECK-SAME: , {{i64|i32}} 12
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
struct Keymaster: ~Copyable { struct Keymaster: ~Copyable {
let lock1: Lock let lock1: Lock
let lock2: Lock let lock2: Lock
@@ -125,8 +125,8 @@ struct Vector<T, let N: Int>: ~Copyable {}
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// stride // stride
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
struct UsesCell: ~Copyable { struct UsesCell: ~Copyable {
let someCondition: Bool let someCondition: Bool
let specialInt: Cell<Int32> let specialInt: Cell<Int32>
@@ -137,8 +137,8 @@ struct UsesCell: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 3 // CHECK-SAME: , {{i64|i32}} 3
// stride // stride
// CHECK-SAME: , {{i64|i32}} 3 // CHECK-SAME: , {{i64|i32}} 3
// flags: alignment 0, noncopyable // flags: alignment 0, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800000> // CHECK-SAME: , <i32 0x1800000>
struct BufferOf3Bool: ~Copyable { struct BufferOf3Bool: ~Copyable {
let buffer: SmallVectorOf3<Bool> let buffer: SmallVectorOf3<Bool>
} }
@@ -148,8 +148,8 @@ struct BufferOf3Bool: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// stride // stride
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// flags: alignment 7, noncopyable, is not inline // flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
// CHECK-SAME: , <i32 0x820007> // CHECK-SAME: , <i32 0x1820007>
struct BadBuffer: ~Copyable { struct BadBuffer: ~Copyable {
let buffer: SmallVectorOf3<Int64?> let buffer: SmallVectorOf3<Int64?>
} }
@@ -159,8 +159,8 @@ struct BadBuffer: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 2 // CHECK-SAME: , {{i64|i32}} 2
// stride // stride
// CHECK-SAME: , {{i64|i32}} 2 // CHECK-SAME: , {{i64|i32}} 2
// flags: alignment 0, noncopyable // flags: alignment 0, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800000> // CHECK-SAME: , <i32 0x1800000>
struct UsesVector: ~Copyable { struct UsesVector: ~Copyable {
let buffer: Vector<UInt8, 2> let buffer: Vector<UInt8, 2>
} }
@@ -170,8 +170,8 @@ struct UsesVector: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// stride // stride
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// flags: alignment 7, noncopyable, is not inline // flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
// CHECK-SAME: , <i32 0x820007> // CHECK-SAME: , <i32 0x1820007>
struct BadBuffer2: ~Copyable { struct BadBuffer2: ~Copyable {
let buffer: Vector<Int64?, 3> let buffer: Vector<Int64?, 3>
} }
@@ -222,8 +222,8 @@ struct ConcreteMoveAsLike: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// stride // stride
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// flags: alignment 3, not copyable // flags: alignment 3, not copyable, not bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
struct ConcreteIntMoveAsLike: ~Copyable { struct ConcreteIntMoveAsLike: ~Copyable {
let cell: CellThatMovesAsLike<Int32> let cell: CellThatMovesAsLike<Int32>
} }
@@ -272,8 +272,8 @@ struct ConcreteSmallVectorMovesAsLike: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// stride // stride
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// flags: alignment 3, not copyable // flags: alignment 3, not copyable, not bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
struct ConcreteSmallVectorIntMovesAsLike: ~Copyable { struct ConcreteSmallVectorIntMovesAsLike: ~Copyable {
let vector: SmallVectorOf2MovesAsLike<Int32> let vector: SmallVectorOf2MovesAsLike<Int32>
} }
@@ -322,9 +322,9 @@ struct ConcreteVectorMovesAsLike: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 16 // CHECK-SAME: , {{i64|i32}} 16
// stride // stride
// CHECK-SAME: , {{i64|i32}} 16 // CHECK-SAME: , {{i64|i32}} 16
// flags: alignment 3, not copyable, (on 32-bit platforms) not storable inline // flags: alignment 3, not copyable, not bitwise-borrowable, (on 32-bit platforms) not storable inline
// CHECK-64-SAME: , <i32 0x800003> // CHECK-64-SAME: , <i32 0x1800003>
// CHECK-32-SAME: , <i32 0x820003> // CHECK-32-SAME: , <i32 0x1820003>
struct ConcreteVectorIntMovesAsLike: ~Copyable { struct ConcreteVectorIntMovesAsLike: ~Copyable {
let vector: VectorMovesAsLike<Int32, 4> let vector: VectorMovesAsLike<Int32, 4>
} }

View File

@@ -10,8 +10,8 @@ public struct Foo<T>: ~Copyable {}
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// stride // stride
// CHECK-SAME: , {{i64|i32}} 4 // CHECK-SAME: , {{i64|i32}} 4
// flags: alignment 3, noncopyable // flags: alignment 3, noncopyable, non-bitwise-borrowable
// CHECK-SAME: , <i32 0x800003> // CHECK-SAME: , <i32 0x1800003>
struct MyInt: ~Copyable { struct MyInt: ~Copyable {
let x: Int32Fake let x: Int32Fake
} }
@@ -21,8 +21,8 @@ struct MyInt: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// stride // stride
// CHECK-SAME: , {{i64|i32}} 48 // CHECK-SAME: , {{i64|i32}} 48
// flags: alignment 7, noncopyable, is not inline // flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
// CHECK-SAME: , <i32 0x820007> // CHECK-SAME: , <i32 0x1820007>
struct BadBuffer: ~Copyable { struct BadBuffer: ~Copyable {
let buf = SmallVectorOf3<Int64?>() let buf = SmallVectorOf3<Int64?>()
} }
@@ -32,10 +32,10 @@ struct BadBuffer: ~Copyable {
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// stride // stride
// CHECK-SAME: , {{i64|i32}} 8 // CHECK-SAME: , {{i64|i32}} 8
// flags-32: alignment 7, noncopyable, is not inline // flags-32: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
// CHECK-SAME-32: , <i32 0x820007> // CHECK-SAME-32: , <i32 0x1820007>
// flags-64: alignment 7, noncopyable // flags-64: alignment 7, noncopyable, non-bitwise-borrowable
// CHECK-SAME-64: , <i32 0x800007> // CHECK-SAME-64: , <i32 0x1800007>
struct Weird: ~Copyable { struct Weird: ~Copyable {
let value = UnsafeCell<Int64>() let value = UnsafeCell<Int64>()
} }