Skip to content

Commit 5822bc3

Browse files
authored
Merge pull request #77605 from jckarter/capture-metadata-for-fixed-box-to-run-deinit
IRGen: Capture NecessaryBindings in fixed boxes that may invoke deinits.
2 parents 8a78496 + b5e79d5 commit 5822bc3

File tree

7 files changed

+381
-33
lines changed

7 files changed

+381
-33
lines changed

lib/IRGen/GenHeap.cpp

Lines changed: 108 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,10 @@ llvm::Value *IRGenFunction::emitUnmanagedAlloc(const HeapLayout &layout,
568568
const llvm::Twine &name,
569569
llvm::Constant *captureDescriptor,
570570
const HeapNonFixedOffsets *offsets) {
571-
if (layout.isKnownEmpty())
571+
if (layout.isKnownEmpty()
572+
&& layout.isTriviallyDestroyable()) {
572573
return IGM.RefCountedNull;
574+
}
573575

574576
llvm::Value *metadata = layout.getPrivateMetadata(IGM, captureDescriptor);
575577
llvm::Value *size, *alignMask;
@@ -1470,7 +1472,7 @@ class BoxTypeInfo : public HeapTypeInfo<BoxTypeInfo> {
14701472

14711473
/// Allocate a box of the given type.
14721474
virtual OwnedAddress
1473-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1475+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
14741476
const llvm::Twine &name) const = 0;
14751477

14761478
/// Deallocate an uninitialized box.
@@ -1488,8 +1490,11 @@ class EmptyBoxTypeInfo final : public BoxTypeInfo {
14881490
EmptyBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
14891491

14901492
OwnedAddress
1491-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1493+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
14921494
const llvm::Twine &name) const override {
1495+
auto boxedType = getSILBoxFieldType(
1496+
IGF.IGM.getMaximalTypeExpansionContext(),
1497+
box, IGF.IGM.getSILModule().Types, 0);
14931498
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
14941499
IGF.emitAllocEmptyBoxCall());
14951500
}
@@ -1513,8 +1518,11 @@ class NonFixedBoxTypeInfo final : public BoxTypeInfo {
15131518
NonFixedBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
15141519

15151520
OwnedAddress
1516-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1521+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *boxType,
15171522
const llvm::Twine &name) const override {
1523+
auto boxedType = getSILBoxFieldType(
1524+
IGF.IGM.getMaximalTypeExpansionContext(),
1525+
boxType, IGF.IGM.getSILModule().Types, 0);
15181526
auto &ti = IGF.getTypeInfo(boxedType);
15191527
// Use the runtime to allocate a box of the appropriate size.
15201528
auto metadata = IGF.emitTypeMetadataRefForLayout(boxedType);
@@ -1552,14 +1560,18 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
15521560
FixedBoxTypeInfoBase(IRGenModule &IGM, HeapLayout &&layout)
15531561
: BoxTypeInfo(IGM), layout(std::move(layout))
15541562
{
1555-
// Empty layouts should always use EmptyBoxTypeInfo instead
1556-
assert(!layout.isKnownEmpty());
1563+
// Trivial empty layouts should always use EmptyBoxTypeInfo instead
1564+
assert(!layout.isKnownEmpty()
1565+
|| !layout.isTriviallyDestroyable());
15571566
}
15581567

15591568
OwnedAddress
1560-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1569+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
15611570
const llvm::Twine &name)
15621571
const override {
1572+
auto boxedType = getSILBoxFieldType(
1573+
IGF.IGM.getMaximalTypeExpansionContext(),
1574+
box, IGF.IGM.getSILModule().Types, 0);
15631575
// Allocate a new object using the layout.
15641576
auto boxedInterfaceType = boxedType;
15651577
if (env) {
@@ -1583,13 +1595,21 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
15831595
boxedInterfaceType = SILType::getPrimitiveType(
15841596
astType, boxedInterfaceType.getCategory());
15851597
}
1586-
1598+
15871599
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(
15881600
boxedInterfaceType,
15891601
env ? env->getGenericSignature().getCanonicalSignature()
15901602
: CanGenericSignature());
15911603
llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name,
15921604
boxDescriptor);
1605+
// Store metadata for the necessary bindings if present.
1606+
if (layout.hasBindings()) {
1607+
auto allocationAddr = layout.emitCastTo(IGF, allocation);
1608+
auto bindingsAddr = layout.getElement(layout.getBindingsIndex())
1609+
.project(IGF, allocationAddr, nullptr);
1610+
layout.getBindings().save(IGF, bindingsAddr, box->getSubstitutions());
1611+
}
1612+
15931613
Address rawAddr = project(IGF, allocation, boxedType);
15941614
return {rawAddr, allocation};
15951615
}
@@ -1639,22 +1659,87 @@ class SingleRefcountedBoxTypeInfo final : public FixedBoxTypeInfoBase {
16391659

16401660
/// Implementation of a box for a specific type.
16411661
class FixedBoxTypeInfo final : public FixedBoxTypeInfoBase {
1662+
static SILType getFieldType(IRGenModule &IGM, SILBoxType *T) {
1663+
return getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1664+
T, IGM.getSILModule().Types, 0);
1665+
}
1666+
1667+
static HeapLayout getHeapLayout(IRGenModule &IGM, SILBoxType *T) {
1668+
SmallVector<SILType> fieldTypes;
1669+
fieldTypes.push_back(getFieldType(IGM, T));
1670+
1671+
auto bindings = NecessaryBindings::forFixedBox(IGM, T);
1672+
unsigned bindingsIndex = 0;
1673+
SmallVector<const TypeInfo *, 4> fields;
1674+
fields.push_back(&IGM.getTypeInfo(fieldTypes[0]));
1675+
1676+
if (!bindings.empty()) {
1677+
bindingsIndex = 1;
1678+
auto bindingsSize = bindings.getBufferSize(IGM);
1679+
auto &bindingsTI = IGM.getOpaqueStorageTypeInfo(bindingsSize,
1680+
IGM.getPointerAlignment());
1681+
fieldTypes.push_back(SILType());
1682+
fields.push_back(&bindingsTI);
1683+
}
1684+
1685+
return HeapLayout(IGM, LayoutStrategy::Optimal,
1686+
fieldTypes, fields,
1687+
/* type to fill */ nullptr,
1688+
std::move(bindings), bindingsIndex);
1689+
}
1690+
16421691
public:
1643-
FixedBoxTypeInfo(IRGenModule &IGM, SILType T)
1644-
: FixedBoxTypeInfoBase(IGM,
1645-
HeapLayout(IGM, LayoutStrategy::Optimal, T, &IGM.getTypeInfo(T)))
1692+
FixedBoxTypeInfo(IRGenModule &IGM, SILBoxType *T)
1693+
: FixedBoxTypeInfoBase(IGM, getHeapLayout(IGM, T))
16461694
{}
16471695
};
16481696

16491697
} // end anonymous namespace
16501698

1699+
NecessaryBindings
1700+
NecessaryBindings::forFixedBox(IRGenModule &IGM, SILBoxType *box) {
1701+
// Don't need to bind metadata if the type is concrete.
1702+
if (!box->hasArchetype() && !box->hasTypeParameter()) {
1703+
return {};
1704+
}
1705+
1706+
auto fieldTy = getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1707+
box, IGM.getSILModule().Types, 0);
1708+
auto fieldTI = cast<FixedTypeInfo>(&IGM.getTypeInfo(fieldTy));
1709+
1710+
// If the type is trivially destroyable, or it's fixed-layout and copyable,
1711+
// then we can always destroy it without binding type metadata.
1712+
if (fieldTI->isTriviallyDestroyable(ResilienceExpansion::Maximal)
1713+
|| fieldTI->isCopyable(ResilienceExpansion::Maximal)) {
1714+
return {};
1715+
}
1716+
1717+
NecessaryBindings bindings(box->getSubstitutions(),
1718+
/*no escape*/ false);
1719+
1720+
// Collect bindings needed by a deinit-shaped function.
1721+
auto deinitParam = SILParameterInfo(
1722+
box->getLayout()->getFields()[0].getLoweredType(),
1723+
ParameterConvention::Indirect_In);
1724+
auto deinitFnTy = SILFunctionType::get(box->getLayout()->getGenericSignature(),
1725+
SILExtInfo(),
1726+
SILCoroutineKind::None,
1727+
ParameterConvention::Direct_Guaranteed,
1728+
deinitParam,
1729+
{}, {}, std::nullopt,
1730+
{}, {}, IGM.Context);
1731+
bindings.computeBindings(IGM, deinitFnTy, /*consider param sources*/ false);
1732+
return bindings;
1733+
}
1734+
16511735
const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
16521736
// We can share a type info for all dynamic-sized heap metadata.
16531737
// TODO: Multi-field boxes
16541738
assert(T->getLayout()->getFields().size() == 1
16551739
&& "multi-field boxes not implemented yet");
1656-
auto &eltTI = IGM.getTypeInfoForLowered(getSILBoxFieldLoweredType(
1657-
IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0));
1740+
auto eltTy = getSILBoxFieldLoweredType(
1741+
IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0);
1742+
auto &eltTI = IGM.getTypeInfoForLowered(eltTy);
16581743
if (!eltTI.isFixedSize()) {
16591744
if (!NonFixedBoxTI)
16601745
NonFixedBoxTI = new NonFixedBoxTypeInfo(IGM);
@@ -1664,12 +1749,16 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
16641749
// For fixed-sized types, we can emit concrete box metadata.
16651750
auto &fixedTI = cast<FixedTypeInfo>(eltTI);
16661751

1667-
// Because we assume in enum's that payloads with a Builtin.NativeReference
1752+
// Because we assume in enums that payloads with a Builtin.NativeReference
16681753
// which is also the type for indirect enum cases have extra inhabitants of
16691754
// pointers we can't have a nil pointer as a representation for an empty box
16701755
// type -- nil conflicts with the extra inhabitants. We return a static
16711756
// singleton empty box object instead.
1672-
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) {
1757+
//
1758+
// (If the box needs no storage, but the type still carries a deinit,
1759+
// then we still need to trigger that deinit when the box is freed.)
1760+
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)
1761+
&& fixedTI.isTriviallyDestroyable(ResilienceExpansion::Maximal)) {
16731762
if (!EmptyBoxTI)
16741763
EmptyBoxTI = new EmptyBoxTypeInfo(IGM);
16751764
return EmptyBoxTI;
@@ -1703,9 +1792,8 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
17031792
// Produce a tailored box metadata for the type.
17041793
assert(T->getLayout()->getFields().size() == 1
17051794
&& "multi-field boxes not implemented yet");
1706-
return new FixedBoxTypeInfo(
1707-
IGM, getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1708-
T, IGM.getSILModule().Types, 0));
1795+
1796+
return new FixedBoxTypeInfo(IGM, T);
17091797
}
17101798

17111799
OwnedAddress
@@ -1715,12 +1803,7 @@ irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType,
17151803
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
17161804
assert(boxType->getLayout()->getFields().size() == 1
17171805
&& "multi-field boxes not implemented yet");
1718-
return boxTI.allocate(
1719-
IGF,
1720-
getSILBoxFieldType(
1721-
IGF.IGM.getMaximalTypeExpansionContext(),
1722-
boxType, IGF.IGM.getSILModule().Types, 0),
1723-
env, name);
1806+
return boxTI.allocate(IGF, env, boxType, name);
17241807
}
17251808

17261809
void irgen::emitDeallocateBox(IRGenFunction &IGF,
@@ -1753,7 +1836,7 @@ Address irgen::emitAllocateExistentialBoxInBuffer(
17531836
// Get a box for the boxed value.
17541837
auto boxType = SILBoxType::get(boxedType.getASTType());
17551838
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
1756-
OwnedAddress owned = boxTI.allocate(IGF, boxedType, env, name);
1839+
OwnedAddress owned = boxTI.allocate(IGF, env, boxType, name);
17571840
Explosion box;
17581841
box.add(owned.getOwner());
17591842
boxTI.initialize(IGF, box,

lib/IRGen/GenProto.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,9 +3648,13 @@ void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer,
36483648
metadataState, SubMap);
36493649
}
36503650

3651-
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const {
3651+
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer,
3652+
std::optional<SubstitutionMap> replacementSubstitutions) const {
3653+
SubstitutionMap subsToPass = replacementSubstitutions.has_value()
3654+
? replacementSubstitutions.value()
3655+
: SubMap;
36523656
emitInitOfGenericRequirementsBuffer(IGF, getRequirements(), buffer,
3653-
MetadataState::Complete, SubMap,
3657+
MetadataState::Complete, subsToPass,
36543658
/*onHeapPacks=*/!NoEscape);
36553659
}
36563660

lib/IRGen/NecessaryBindings.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class NecessaryBindings {
6363
bool noEscape,
6464
bool considerParameterSources);
6565

66+
/// Collect the necessary bindings to be able to destroy a value inside of a
67+
/// fixed-layout boxed allocation.
68+
static NecessaryBindings forFixedBox(IRGenModule &IGM,
69+
SILBoxType *box);
70+
6671
void addRequirement(GenericRequirement requirement) {
6772
auto type = requirement.getTypeParameter().subst(SubMap);
6873
if (!type->hasArchetype())
@@ -86,7 +91,12 @@ class NecessaryBindings {
8691
Size getBufferSize(IRGenModule &IGM) const;
8792

8893
/// Save the necessary bindings to the given buffer.
89-
void save(IRGenFunction &IGF, Address buffer) const;
94+
///
95+
/// If `replacementSubs` has a value, then the bindings saved are taken from
96+
/// the given substitution map instead of the substitutions
97+
void save(IRGenFunction &IGF, Address buffer,
98+
std::optional<SubstitutionMap> replacementSubs = std::nullopt)
99+
const;
90100

91101
/// Restore the necessary bindings from the given buffer.
92102
void restore(IRGenFunction &IGF, Address buffer, MetadataState state) const;

lib/IRGen/StructLayout.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,32 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
190190
Ty = (typeToFill ? typeToFill : IGM.OpaqueTy);
191191
}
192192
} else {
193+
// A heap object containing an empty but non-trivially-destroyable
194+
// noncopyable type needs to exist in order to run deinits when the
193195
bool nonEmpty = builder.addFields(Elements, strategy);
194196

197+
IsKnownTriviallyDestroyable
198+
= triviallyDestroyable & builder.isTriviallyDestroyable();
199+
IsKnownCopyable = copyable & builder.isCopyable();
200+
195201
// Special-case: there's nothing to store.
196202
// In this case, produce an opaque type; this tends to cause lovely
197203
// assertions.
198-
if (!nonEmpty) {
204+
//
205+
// If a heap object contains an empty but non-trivially-destroyable type,
206+
// then we still want to create a non-empty heap object, since the heap
207+
// object's destructor will run deinits for the value.
208+
if (!nonEmpty
209+
&& (IsKnownTriviallyDestroyable || !requiresHeapHeader(layoutKind))) {
199210
assert(!builder.empty() == requiresHeapHeader(layoutKind));
200211
MinimumAlign = Alignment(1);
201212
MinimumSize = Size(0);
202213
headerSize = builder.getHeaderSize();
203214
SpareBits.clear();
204215
IsFixedLayout = true;
205216
IsLoadable = true;
206-
IsKnownTriviallyDestroyable = triviallyDestroyable & builder.isTriviallyDestroyable();
207217
IsKnownBitwiseTakable = builder.isBitwiseTakable();
208218
IsKnownAlwaysFixedSize = builder.isAlwaysFixedSize();
209-
IsKnownCopyable = copyable & builder.isCopyable();
210219
Ty = (typeToFill ? typeToFill : IGM.OpaqueTy);
211220
} else {
212221
MinimumAlign = builder.getAlignment();
@@ -215,10 +224,8 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
215224
SpareBits = builder.getSpareBits();
216225
IsFixedLayout = builder.isFixedLayout();
217226
IsLoadable = builder.isLoadable();
218-
IsKnownTriviallyDestroyable = triviallyDestroyable & builder.isTriviallyDestroyable();
219227
IsKnownBitwiseTakable = bitwiseTakable & builder.isBitwiseTakable();
220228
IsKnownAlwaysFixedSize = builder.isAlwaysFixedSize();
221-
IsKnownCopyable = copyable & builder.isCopyable();
222229
if (typeToFill) {
223230
builder.setAsBodyOfStruct(typeToFill);
224231
Ty = typeToFill;

0 commit comments

Comments
 (0)