mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -168,17 +168,18 @@ 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,
|
||||
// unused 0xFF000000,
|
||||
AlignmentMask = 0x000000FF,
|
||||
// unused 0x0000FF00,
|
||||
IsNonPOD = 0x00010000,
|
||||
IsNonInline = 0x00020000,
|
||||
// unused 0x00040000,
|
||||
HasSpareBits = 0x00080000,
|
||||
IsNonBitwiseTakable = 0x00100000,
|
||||
HasEnumWitnesses = 0x00200000,
|
||||
Incomplete = 0x00400000,
|
||||
IsNonCopyable = 0x00800000,
|
||||
IsNonBitwiseBorrowable = 0x01000000,
|
||||
// unused 0xFE000000,
|
||||
};
|
||||
|
||||
static constexpr const uint32_t MaxNumExtraInhabitants = 0x7FFFFFFF;
|
||||
@@ -243,6 +244,27 @@ public:
|
||||
(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.
|
||||
bool isCopyable() const { return !(Data & IsNonCopyable); }
|
||||
constexpr TargetValueWitnessFlags withCopyable(bool isCopyable) const {
|
||||
|
||||
@@ -3927,8 +3927,8 @@ namespace {
|
||||
} else if (allSingleRefcount
|
||||
&& ElementsWithNoPayload.size() <= 1) {
|
||||
CopyDestroyKind = TaggedRefcounted;
|
||||
} else if (this->EnumImplStrategy::BitwiseTakable == IsBitwiseTakable &&
|
||||
Copyable == IsCopyable) {
|
||||
} else if (this->EnumImplStrategy::BitwiseTakable == IsBitwiseTakableAndBorrowable
|
||||
&& Copyable == IsCopyable) {
|
||||
CopyDestroyKind = BitwiseTakable;
|
||||
}
|
||||
}
|
||||
@@ -6396,7 +6396,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) {
|
||||
? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
|
||||
auto copyable = !theEnum->canBeCopyable()
|
||||
? IsNotCopyable : IsCopyable;
|
||||
auto bitwiseTakable = IsBitwiseTakable; // FIXME: will there be check here?
|
||||
auto bitwiseTakable = IsBitwiseTakableAndBorrowable; // FIXME: will there be check here?
|
||||
bool allowFixedLayoutOptimizations = true;
|
||||
std::vector<Element> elementsWithPayload;
|
||||
std::vector<Element> elementsWithNoPayload;
|
||||
@@ -6408,7 +6408,7 @@ EnumImplStrategy::get(TypeConverter &TC, SILType type, EnumDecl *theEnum) {
|
||||
payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
|
||||
copyable = copyable & payloadTI.isCopyable(ResilienceExpansion::Maximal);
|
||||
bitwiseTakable = bitwiseTakable &
|
||||
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
};
|
||||
|
||||
if (TC.IGM.isResilient(theEnum, ResilienceExpansion::Minimal))
|
||||
@@ -6861,7 +6861,8 @@ EnumImplStrategy::getFixedEnumTypeInfo(llvm::StructType *T, Size S,
|
||||
abiAccessible);
|
||||
break;
|
||||
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,
|
||||
isTriviallyDestroyable,
|
||||
isCopyable,
|
||||
@@ -7084,7 +7085,7 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeFixedLayout(
|
||||
getFixedEnumTypeInfo(
|
||||
enumTy, Size(sizeWithTag), spareBits.build(), alignment,
|
||||
deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal),
|
||||
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal),
|
||||
payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal),
|
||||
copyable, isABIAccessible);
|
||||
|
||||
if (TIK >= Loadable && CopyDestroyKind == Normal) {
|
||||
@@ -7120,7 +7121,7 @@ TypeInfo *SinglePayloadEnumImplStrategy::completeDynamicLayout(
|
||||
return registerEnumTypeInfo(new NonFixedEnumTypeInfo(*this, enumTy,
|
||||
alignment,
|
||||
deinit & payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal),
|
||||
payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal),
|
||||
payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal),
|
||||
copyable,
|
||||
enumAccessible));
|
||||
}
|
||||
@@ -7155,7 +7156,7 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC,
|
||||
? IsNotCopyable : IsCopyable;
|
||||
auto isTriviallyDestroyable = theEnum->getValueTypeDestructor()
|
||||
? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
|
||||
IsBitwiseTakable_t isBT = IsBitwiseTakable;
|
||||
IsBitwiseTakable_t isBT = IsBitwiseTakableAndBorrowable;
|
||||
PayloadSize = 0;
|
||||
for (auto &elt : ElementsWithPayload) {
|
||||
auto &fixedPayloadTI = cast<FixedTypeInfo>(*elt.ti);
|
||||
@@ -7163,8 +7164,7 @@ MultiPayloadEnumImplStrategy::completeFixedLayout(TypeConverter &TC,
|
||||
worstAlignment = fixedPayloadTI.getFixedAlignment();
|
||||
if (!fixedPayloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal))
|
||||
isTriviallyDestroyable = IsNotTriviallyDestroyable;
|
||||
if (!fixedPayloadTI.isBitwiseTakable(ResilienceExpansion::Maximal))
|
||||
isBT = IsNotBitwiseTakable;
|
||||
isBT &= fixedPayloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
|
||||
unsigned payloadBytes = fixedPayloadTI.getFixedSize().getValue();
|
||||
unsigned payloadBits = fixedPayloadTI.getFixedSize().getValueInBits();
|
||||
@@ -7324,12 +7324,12 @@ TypeInfo *MultiPayloadEnumImplStrategy::completeDynamicLayout(
|
||||
Alignment alignment(1);
|
||||
auto td = theEnum->getValueTypeDestructor()
|
||||
? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
|
||||
auto bt = IsBitwiseTakable;
|
||||
auto bt = IsBitwiseTakableAndBorrowable;
|
||||
for (auto &element : ElementsWithPayload) {
|
||||
auto &payloadTI = *element.ti;
|
||||
alignment = std::max(alignment, payloadTI.getBestKnownAlignment());
|
||||
td &= payloadTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
|
||||
bt &= payloadTI.isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
bt &= payloadTI.getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
}
|
||||
|
||||
applyLayoutAttributes(TC.IGM, theEnum, /*fixed*/false, alignment);
|
||||
|
||||
@@ -900,11 +900,16 @@ class OpaqueExistentialTypeInfo final :
|
||||
|
||||
OpaqueExistentialTypeInfo(ArrayRef<const ProtocolDecl *> protocols,
|
||||
llvm::Type *ty, Size size,
|
||||
IsCopyable_t copyable,
|
||||
SpareBitVector &&spareBits,
|
||||
Alignment align)
|
||||
: super(protocols, ty, size,
|
||||
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) {}
|
||||
|
||||
public:
|
||||
@@ -1649,14 +1654,19 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) {
|
||||
OpaqueExistentialLayout opaque(protosWithWitnessTables.size());
|
||||
Alignment align = opaque.getAlignment(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
|
||||
// consistent with a native object reference.
|
||||
// TODO: There are spare bits we could theoretically use in the type metadata
|
||||
// and witness table pointers, but opaque existentials are currently address-
|
||||
// NB: There are spare bits we could theoretically use in the type metadata
|
||||
// and witness table pointers, but opaque existentials are address-
|
||||
// only, and we can't soundly take advantage of spare bits for in-memory
|
||||
// representations.
|
||||
// Maybe an ABI break that made opaque existentials loadable could use those
|
||||
// bits, though.
|
||||
auto spareBits = SpareBitVector::getConstant(size.getValueInBits(), false);
|
||||
return OpaqueExistentialTypeInfo::create(protosWithWitnessTables, type, size,
|
||||
copyable,
|
||||
std::move(spareBits),
|
||||
align);
|
||||
}
|
||||
|
||||
@@ -579,7 +579,7 @@ const TypeInfo *TypeConverter::convertBlockStorageType(SILBlockStorageType *T) {
|
||||
|
||||
size = captureOffset + fixedCapture->getFixedSize();
|
||||
pod = fixedCapture->isTriviallyDestroyable(ResilienceExpansion::Maximal);
|
||||
bt = fixedCapture->isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
bt = fixedCapture->getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
}
|
||||
|
||||
llvm::Type *storageElts[] = {
|
||||
|
||||
@@ -1144,7 +1144,7 @@ public:
|
||||
|
||||
auto alignment = ti->getFixedAlignment().getValue();
|
||||
unsigned bitwiseTakable =
|
||||
(ti->isBitwiseTakable(ResilienceExpansion::Minimal) == IsBitwiseTakable
|
||||
(ti->getBitwiseTakable(ResilienceExpansion::Minimal) >= IsBitwiseTakableOnly
|
||||
? 1 : 0);
|
||||
B.addInt32(alignment | (bitwiseTakable << 16));
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ llvm::Value *FixedTypeInfo::getIsTriviallyDestroyable(IRGenFunction &IGF, SILTyp
|
||||
}
|
||||
llvm::Value *FixedTypeInfo::getIsBitwiseTakable(IRGenFunction &IGF, SILType T) const {
|
||||
return llvm::ConstantInt::get(IGF.IGM.Int1Ty,
|
||||
isBitwiseTakable(ResilienceExpansion::Maximal) == IsBitwiseTakable);
|
||||
getBitwiseTakable(ResilienceExpansion::Maximal) >= IsBitwiseTakableOnly);
|
||||
}
|
||||
llvm::Constant *FixedTypeInfo::getStaticStride(IRGenModule &IGM) const {
|
||||
return IGM.getSize(getFixedStride());
|
||||
|
||||
@@ -849,12 +849,22 @@ ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType,
|
||||
bool isInline = packing == FixedPacking::OffsetZero;
|
||||
bool isBitwiseTakable =
|
||||
fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
bool isBitwiseBorrowable =
|
||||
fixedTI->isBitwiseBorrowable(ResilienceExpansion::Maximal);
|
||||
assert(isBitwiseTakable || !isInline);
|
||||
flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue())
|
||||
.withPOD(fixedTI->isTriviallyDestroyable(ResilienceExpansion::Maximal))
|
||||
.withCopyable(fixedTI->isCopyable(ResilienceExpansion::Maximal))
|
||||
.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 {
|
||||
flags = flags.withIncomplete(true);
|
||||
}
|
||||
@@ -1532,12 +1542,12 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
|
||||
unsigned align = ti.getFixedAlignment().getValue();
|
||||
|
||||
bool pod = ti.isTriviallyDestroyable(ResilienceExpansion::Maximal);
|
||||
bool bt = ti.isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
IsBitwiseTakable_t bt = ti.getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
unsigned numExtraInhabitants = ti.getFixedExtraInhabitantCount(*this);
|
||||
|
||||
// Try to use common type layouts exported by the runtime.
|
||||
llvm::Constant *commonValueWitnessTable = nullptr;
|
||||
if (pod && bt && numExtraInhabitants == 0) {
|
||||
if (pod && bt == IsBitwiseTakableAndBorrowable && numExtraInhabitants == 0) {
|
||||
if (size == 0)
|
||||
commonValueWitnessTable =
|
||||
getAddrOfValueWitnessTable(Context.TheEmptyTupleType);
|
||||
@@ -1562,7 +1572,7 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
|
||||
|
||||
// Otherwise, see if a layout has been emitted with these characteristics
|
||||
// already.
|
||||
FixedLayoutKey key{size, numExtraInhabitants, align, pod, bt};
|
||||
FixedLayoutKey key{size, numExtraInhabitants, align, pod, unsigned(bt)};
|
||||
|
||||
auto found = PrivateFixedLayouts.find(key);
|
||||
if (found != PrivateFixedLayouts.end())
|
||||
@@ -1578,16 +1588,29 @@ llvm::Constant *IRGenModule::emitFixedTypeLayout(CanType t,
|
||||
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
|
||||
= witnesses.finishAndCreateGlobal(
|
||||
"type_layout_" + llvm::Twine(size)
|
||||
+ "_" + llvm::Twine(align)
|
||||
+ "_" + llvm::Twine::utohexstr(numExtraInhabitants)
|
||||
+ (pod ? "_pod" :
|
||||
bt ? "_bt" : ""),
|
||||
getPointerAlignment(),
|
||||
/*constant*/ true,
|
||||
llvm::GlobalValue::PrivateLinkage);
|
||||
+ pod_bt_string(pod, bt),
|
||||
getPointerAlignment(),
|
||||
/*constant*/ true,
|
||||
llvm::GlobalValue::PrivateLinkage);
|
||||
|
||||
// Cast to the standard currency type for type layouts.
|
||||
auto layout = llvm::ConstantExpr::getBitCast(layoutVar, Int8PtrPtrTy);
|
||||
|
||||
@@ -80,9 +80,15 @@ inline IsLoadable_t &operator&=(IsLoadable_t &l, IsLoadable_t 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) {
|
||||
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) {
|
||||
return (l = (l & r));
|
||||
|
||||
@@ -1368,7 +1368,7 @@ private:
|
||||
unsigned numExtraInhabitants;
|
||||
unsigned align: 16;
|
||||
unsigned pod: 1;
|
||||
unsigned bitwiseTakable: 1;
|
||||
unsigned bitwiseTakable: 2;
|
||||
};
|
||||
friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>;
|
||||
llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts;
|
||||
|
||||
@@ -65,8 +65,9 @@ protected:
|
||||
IsABIAccessible_t isABIAccessible,
|
||||
SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable)
|
||||
: FixedTypeInfo(type, size, spareBits, align, pod,
|
||||
// All currently implemented loadable types are bitwise-takable.
|
||||
IsBitwiseTakable,
|
||||
// All currently implemented loadable types are
|
||||
// bitwise-takable and -borrowable.
|
||||
IsBitwiseTakableAndBorrowable,
|
||||
copy, alwaysFixedSize, isABIAccessible, stik) {
|
||||
assert(isLoadable());
|
||||
}
|
||||
@@ -80,8 +81,9 @@ protected:
|
||||
IsABIAccessible_t isABIAccessible,
|
||||
SpecialTypeInfoKind stik = SpecialTypeInfoKind::Loadable)
|
||||
: FixedTypeInfo(type, size, std::move(spareBits), align, pod,
|
||||
// All currently implemented loadable types are bitwise-takable.
|
||||
IsBitwiseTakable,
|
||||
// All currently implemented loadable types are
|
||||
// bitwise-takable and borrowable.
|
||||
IsBitwiseTakableAndBorrowable,
|
||||
copy, alwaysFixedSize, isABIAccessible, stik) {
|
||||
assert(isLoadable());
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
|
||||
? IsNotTriviallyDestroyable : IsTriviallyDestroyable;
|
||||
auto copyable = (decl && !decl->canBeCopyable())
|
||||
? IsNotCopyable : IsCopyable;
|
||||
IsBitwiseTakable_t bitwiseTakable = IsBitwiseTakable;
|
||||
IsBitwiseTakable_t bitwiseTakable = IsBitwiseTakableAndBorrowable;
|
||||
|
||||
if (decl && decl->getAttrs().hasAttribute<SensitiveAttr>()) {
|
||||
triviallyDestroyable = IsNotTriviallyDestroyable;
|
||||
@@ -87,7 +87,8 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
|
||||
if (rawLayout && type) {
|
||||
auto sd = cast<StructDecl>(decl);
|
||||
IsKnownTriviallyDestroyable = triviallyDestroyable;
|
||||
IsKnownBitwiseTakable = bitwiseTakable;
|
||||
// Raw layout types are never bitwise-borrowable.
|
||||
IsKnownBitwiseTakable = bitwiseTakable & IsBitwiseTakableOnly;
|
||||
SpareBits.clear();
|
||||
assert(!copyable);
|
||||
IsKnownCopyable = copyable;
|
||||
@@ -168,7 +169,9 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
|
||||
// type its like.
|
||||
if (rawLayout->shouldMoveAsLikeType()) {
|
||||
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 {
|
||||
MinimumSize = Size(0);
|
||||
@@ -417,7 +420,7 @@ bool StructLayoutBuilder::addField(ElementLayout &elt,
|
||||
LayoutStrategy strategy) {
|
||||
auto &eltTI = elt.getType();
|
||||
IsKnownTriviallyDestroyable &= eltTI.isTriviallyDestroyable(ResilienceExpansion::Maximal);
|
||||
IsKnownBitwiseTakable &= eltTI.isBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
IsKnownBitwiseTakable &= eltTI.getBitwiseTakable(ResilienceExpansion::Maximal);
|
||||
IsKnownAlwaysFixedSize &= eltTI.isFixedSize(ResilienceExpansion::Minimal);
|
||||
IsLoadable &= eltTI.isLoadable();
|
||||
IsKnownCopyable &= eltTI.isCopyable(ResilienceExpansion::Maximal);
|
||||
|
||||
@@ -285,7 +285,7 @@ private:
|
||||
bool IsFixedLayout = true;
|
||||
bool IsLoadable = true;
|
||||
IsTriviallyDestroyable_t IsKnownTriviallyDestroyable = IsTriviallyDestroyable;
|
||||
IsBitwiseTakable_t IsKnownBitwiseTakable = IsBitwiseTakable;
|
||||
IsBitwiseTakable_t IsKnownBitwiseTakable = IsBitwiseTakableAndBorrowable;
|
||||
IsCopyable_t IsKnownCopyable = IsCopyable;
|
||||
IsFixedSize_t IsKnownAlwaysFixedSize = IsFixedSize;
|
||||
public:
|
||||
|
||||
@@ -101,7 +101,7 @@ protected:
|
||||
uint64_t OpaqueBits;
|
||||
|
||||
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.
|
||||
Kind : bitmax(NumSpecialTypeInfoKindBits,8),
|
||||
|
||||
@@ -114,6 +114,9 @@ protected:
|
||||
/// Whether this type is known to be bitwise-takable.
|
||||
BitwiseTakable : 1,
|
||||
|
||||
/// Whether this type is known to be bitwise-borrowable.
|
||||
BitwiseBorrowable : 1,
|
||||
|
||||
/// Whether this type is known to be copyable.
|
||||
Copyable : 1,
|
||||
|
||||
@@ -160,7 +163,9 @@ protected:
|
||||
Bits.TypeInfo.Kind = unsigned(stik);
|
||||
Bits.TypeInfo.AlignmentShift = llvm::Log2_32(A.getValue());
|
||||
Bits.TypeInfo.TriviallyDestroyable = pod;
|
||||
Bits.TypeInfo.BitwiseTakable = bitwiseTakable;
|
||||
Bits.TypeInfo.BitwiseTakable = bitwiseTakable >= IsBitwiseTakableOnly;
|
||||
Bits.TypeInfo.BitwiseBorrowable =
|
||||
bitwiseTakable == IsBitwiseTakableAndBorrowable;
|
||||
Bits.TypeInfo.Copyable = copyable;
|
||||
Bits.TypeInfo.SubclassKind = InvalidSubclassKind;
|
||||
Bits.TypeInfo.AlwaysFixedSize = alwaysFixedSize;
|
||||
@@ -231,11 +236,25 @@ public:
|
||||
}
|
||||
|
||||
/// Whether this type is known to be bitwise-takable, i.e. "initializeWithTake"
|
||||
/// is equivalent to a memcpy.
|
||||
IsBitwiseTakable_t isBitwiseTakable(ResilienceExpansion expansion) const {
|
||||
return IsBitwiseTakable_t(Bits.TypeInfo.BitwiseTakable);
|
||||
/// is equivalent to a memcpy, and possibly bitwise-borrowable, i.e.,
|
||||
/// a borrowed argument can be passed by value rather than by reference.
|
||||
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.
|
||||
/// It is important for our design that this depends only on
|
||||
/// immediate type structure and not on, say, properties that can
|
||||
|
||||
38
test/IRGen/existential-bitwise-borrowability.swift
Normal file
38
test/IRGen/existential-bitwise-borrowability.swift
Normal 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
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import RawLayoutCXX
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
|
||||
@_rawLayout(size: 4, alignment: 4)
|
||||
struct Lock: ~Copyable { }
|
||||
@@ -27,8 +27,8 @@ struct PaddedStride {
|
||||
// CHECK-SAME: , {{i64|i32}} 5
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
@_rawLayout(like: PaddedStride)
|
||||
struct LikePaddedStride: ~Copyable {}
|
||||
|
||||
@@ -37,8 +37,8 @@ struct LikePaddedStride: ~Copyable {}
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
@_rawLayout(likeArrayOf: PaddedStride, count: 1)
|
||||
struct LikePaddedStrideArray1: ~Copyable {}
|
||||
|
||||
@@ -47,9 +47,9 @@ struct LikePaddedStrideArray1: ~Copyable {}
|
||||
// CHECK-SAME: , {{i64|i32}} 16
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 16
|
||||
// flags: alignment 3, noncopyable, (on 32-bit platforms) not storable inline
|
||||
// CHECK-64-SAME: , <i32 0x800003>
|
||||
// CHECK-32-SAME: , <i32 0x820003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable, (on 32-bit platforms) not storable inline
|
||||
// CHECK-64-SAME: , <i32 0x1800003>
|
||||
// CHECK-32-SAME: , <i32 0x1820003>
|
||||
@_rawLayout(likeArrayOf: PaddedStride, count: 2)
|
||||
struct LikePaddedStrideArray2: ~Copyable {}
|
||||
|
||||
@@ -58,8 +58,8 @@ struct LikePaddedStrideArray2: ~Copyable {}
|
||||
// CHECK-SAME: , {{i64|i32}} 12
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 12
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
struct Keymaster: ~Copyable {
|
||||
let lock1: Lock
|
||||
let lock2: Lock
|
||||
@@ -125,8 +125,8 @@ struct Vector<T, let N: Int>: ~Copyable {}
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
struct UsesCell: ~Copyable {
|
||||
let someCondition: Bool
|
||||
let specialInt: Cell<Int32>
|
||||
@@ -137,8 +137,8 @@ struct UsesCell: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 3
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 3
|
||||
// flags: alignment 0, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800000>
|
||||
// flags: alignment 0, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800000>
|
||||
struct BufferOf3Bool: ~Copyable {
|
||||
let buffer: SmallVectorOf3<Bool>
|
||||
}
|
||||
@@ -148,8 +148,8 @@ struct BufferOf3Bool: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// flags: alignment 7, noncopyable, is not inline
|
||||
// CHECK-SAME: , <i32 0x820007>
|
||||
// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
|
||||
// CHECK-SAME: , <i32 0x1820007>
|
||||
struct BadBuffer: ~Copyable {
|
||||
let buffer: SmallVectorOf3<Int64?>
|
||||
}
|
||||
@@ -159,8 +159,8 @@ struct BadBuffer: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 2
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 2
|
||||
// flags: alignment 0, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800000>
|
||||
// flags: alignment 0, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800000>
|
||||
struct UsesVector: ~Copyable {
|
||||
let buffer: Vector<UInt8, 2>
|
||||
}
|
||||
@@ -170,8 +170,8 @@ struct UsesVector: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// flags: alignment 7, noncopyable, is not inline
|
||||
// CHECK-SAME: , <i32 0x820007>
|
||||
// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
|
||||
// CHECK-SAME: , <i32 0x1820007>
|
||||
struct BadBuffer2: ~Copyable {
|
||||
let buffer: Vector<Int64?, 3>
|
||||
}
|
||||
@@ -222,8 +222,8 @@ struct ConcreteMoveAsLike: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// flags: alignment 3, not copyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, not copyable, not bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
struct ConcreteIntMoveAsLike: ~Copyable {
|
||||
let cell: CellThatMovesAsLike<Int32>
|
||||
}
|
||||
@@ -272,8 +272,8 @@ struct ConcreteSmallVectorMovesAsLike: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// flags: alignment 3, not copyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, not copyable, not bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
struct ConcreteSmallVectorIntMovesAsLike: ~Copyable {
|
||||
let vector: SmallVectorOf2MovesAsLike<Int32>
|
||||
}
|
||||
@@ -322,9 +322,9 @@ struct ConcreteVectorMovesAsLike: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 16
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 16
|
||||
// flags: alignment 3, not copyable, (on 32-bit platforms) not storable inline
|
||||
// CHECK-64-SAME: , <i32 0x800003>
|
||||
// CHECK-32-SAME: , <i32 0x820003>
|
||||
// flags: alignment 3, not copyable, not bitwise-borrowable, (on 32-bit platforms) not storable inline
|
||||
// CHECK-64-SAME: , <i32 0x1800003>
|
||||
// CHECK-32-SAME: , <i32 0x1820003>
|
||||
struct ConcreteVectorIntMovesAsLike: ~Copyable {
|
||||
let vector: VectorMovesAsLike<Int32, 4>
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ public struct Foo<T>: ~Copyable {}
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 4
|
||||
// flags: alignment 3, noncopyable
|
||||
// CHECK-SAME: , <i32 0x800003>
|
||||
// flags: alignment 3, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME: , <i32 0x1800003>
|
||||
struct MyInt: ~Copyable {
|
||||
let x: Int32Fake
|
||||
}
|
||||
@@ -21,8 +21,8 @@ struct MyInt: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 48
|
||||
// flags: alignment 7, noncopyable, is not inline
|
||||
// CHECK-SAME: , <i32 0x820007>
|
||||
// flags: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
|
||||
// CHECK-SAME: , <i32 0x1820007>
|
||||
struct BadBuffer: ~Copyable {
|
||||
let buf = SmallVectorOf3<Int64?>()
|
||||
}
|
||||
@@ -32,10 +32,10 @@ struct BadBuffer: ~Copyable {
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// stride
|
||||
// CHECK-SAME: , {{i64|i32}} 8
|
||||
// flags-32: alignment 7, noncopyable, is not inline
|
||||
// CHECK-SAME-32: , <i32 0x820007>
|
||||
// flags-64: alignment 7, noncopyable
|
||||
// CHECK-SAME-64: , <i32 0x800007>
|
||||
// flags-32: alignment 7, noncopyable, non-bitwise-borrowable, is not inline
|
||||
// CHECK-SAME-32: , <i32 0x1820007>
|
||||
// flags-64: alignment 7, noncopyable, non-bitwise-borrowable
|
||||
// CHECK-SAME-64: , <i32 0x1800007>
|
||||
struct Weird: ~Copyable {
|
||||
let value = UnsafeCell<Int64>()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user