Skip to content

Commit b44ddf5

Browse files
committed
SILOptimizer: Turn type(of: self) from uninitialized self stack space into the self argument.
Before we changed convenience inits into allocating entry points, we allowed type(of: self) to be invoked on the uninitialized object, which was fine. This no longer Just Works when self doesn't even exist before `self.init` is called, but to maintain the old semantics and source compatibility, we can still just use the metatype value we were passed in.
1 parent 77a0923 commit b44ddf5

File tree

6 files changed

+108
-163
lines changed

6 files changed

+108
-163
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,15 +1476,15 @@ void ElementUseCollector::collectClassSelfUses(
14761476
}
14771477

14781478
//===----------------------------------------------------------------------===//
1479-
// DelegatingValueTypeInitUseCollector
1479+
// DelegatingInitUseCollector
14801480
//===----------------------------------------------------------------------===//
14811481

14821482
static void
1483-
collectValueTypeDelegatingInitUses(const DIMemoryObjectInfo &TheMemory,
1484-
DIElementUseInfo &UseInfo,
1485-
SingleValueInstruction *I) {
1483+
collectDelegatingInitUses(const DIMemoryObjectInfo &TheMemory,
1484+
DIElementUseInfo &UseInfo,
1485+
SingleValueInstruction *I) {
14861486
for (auto *Op : I->getUses()) {
1487-
auto *User = Op->getUser();
1487+
SILInstruction *User = Op->getUser();
14881488

14891489
// destroy_addr is a release of the entire value. This can result from an
14901490
// early release due to a conditional initializer.
@@ -1519,13 +1519,48 @@ collectValueTypeDelegatingInitUses(const DIMemoryObjectInfo &TheMemory,
15191519

15201520
// Look through begin_access
15211521
if (auto *BAI = dyn_cast<BeginAccessInst>(User)) {
1522-
collectValueTypeDelegatingInitUses(TheMemory, UseInfo, BAI);
1522+
collectDelegatingInitUses(TheMemory, UseInfo, BAI);
15231523
continue;
15241524
}
15251525

15261526
// Ignore end_access
15271527
if (isa<EndAccessInst>(User))
15281528
continue;
1529+
1530+
// A load of the value that's only used to handle a type(of:) query before
1531+
// self has been initialized can just use the initializer's metatype
1532+
// argument. For value types, there's no metatype subtyping to worry about,
1533+
// and for class convenience initializers, `self` notionally has the
1534+
// original Self type as its dynamic type before theoretically being
1535+
// rebound.
1536+
//
1537+
// This is necessary for source compatibility; previously, convenience
1538+
// initializers behaved like in Objective-C where the initializer received
1539+
// an uninitialized object to fill in, and type(of: self) worked by asking
1540+
// for the dynamic type of that uninitialized object.
1541+
if (isa<LoadInst>(User)) {
1542+
auto UserVal = cast<SingleValueInstruction>(User);
1543+
if (UserVal->hasOneUse()
1544+
&& isa<ValueMetatypeInst>(UserVal->getSingleUse()->get())) {
1545+
Kind = DIUseKind::LoadForTypeOfSelf;
1546+
}
1547+
}
1548+
// value_metatype may appear on a borrowed load, in which case there'll
1549+
// be an end_borrow use in addition to the value_metatype.
1550+
if (isa<LoadBorrowInst>(User)) {
1551+
auto UserVal = cast<SingleValueInstruction>(User);
1552+
bool onlyUseIsValueMetatype = true;
1553+
for (auto use : UserVal->getUses()) {
1554+
if (isa<EndBorrowInst>(use->getUser())
1555+
|| isa<ValueMetatypeInst>(use->getUser()))
1556+
continue;
1557+
onlyUseIsValueMetatype = false;
1558+
break;
1559+
}
1560+
if (onlyUseIsValueMetatype) {
1561+
Kind = DIUseKind::LoadForTypeOfSelf;
1562+
}
1563+
}
15291564

15301565
// We can safely handle anything else as an escape. They should all happen
15311566
// after self.init is invoked.
@@ -1534,33 +1569,33 @@ collectValueTypeDelegatingInitUses(const DIMemoryObjectInfo &TheMemory,
15341569
}
15351570

15361571
//===----------------------------------------------------------------------===//
1537-
// DelegatingClassInitElementUseCollector
1572+
// ClassInitElementUseCollector
15381573
//===----------------------------------------------------------------------===//
15391574

15401575
namespace {
15411576

1542-
class DelegatingClassInitElementUseCollector {
1577+
class ClassInitElementUseCollector {
15431578
const DIMemoryObjectInfo &TheMemory;
15441579
DIElementUseInfo &UseInfo;
15451580

15461581
public:
1547-
DelegatingClassInitElementUseCollector(const DIMemoryObjectInfo &TheMemory,
1582+
ClassInitElementUseCollector(const DIMemoryObjectInfo &TheMemory,
15481583
DIElementUseInfo &UseInfo)
15491584
: TheMemory(TheMemory), UseInfo(UseInfo) {}
15501585

15511586
void collectClassInitSelfUses();
15521587

15531588
// *NOTE* Even though this takes a SILInstruction it actually only accepts
15541589
// load_borrow and load instructions. This is enforced via an assert.
1555-
void collectDelegatingClassInitSelfLoadUses(MarkUninitializedInst *MUI,
1556-
SingleValueInstruction *LI);
1590+
void collectClassInitSelfLoadUses(MarkUninitializedInst *MUI,
1591+
SingleValueInstruction *LI);
15571592
};
15581593

15591594
} // end anonymous namespace
15601595

15611596
/// collectDelegatingClassInitSelfUses - Collect uses of the self argument in a
15621597
/// delegating-constructor-for-a-class case.
1563-
void DelegatingClassInitElementUseCollector::collectClassInitSelfUses() {
1598+
void ClassInitElementUseCollector::collectClassInitSelfUses() {
15641599
// When we're analyzing a delegating constructor, we aren't field sensitive at
15651600
// all. Just treat all members of self as uses of the single
15661601
// non-field-sensitive value.
@@ -1668,8 +1703,7 @@ void DelegatingClassInitElementUseCollector::collectClassInitSelfUses() {
16681703

16691704
// Loads of the box produce self, so collect uses from them.
16701705
if (isa<LoadInst>(User) || isa<LoadBorrowInst>(User)) {
1671-
collectDelegatingClassInitSelfLoadUses(MUI,
1672-
cast<SingleValueInstruction>(User));
1706+
collectClassInitSelfLoadUses(MUI, cast<SingleValueInstruction>(User));
16731707
continue;
16741708
}
16751709

@@ -1691,8 +1725,8 @@ void DelegatingClassInitElementUseCollector::collectClassInitSelfUses() {
16911725
gatherDestroysOfContainer(MUI, UseInfo);
16921726
}
16931727

1694-
void DelegatingClassInitElementUseCollector::
1695-
collectDelegatingClassInitSelfLoadUses(MarkUninitializedInst *MUI,
1728+
void ClassInitElementUseCollector::
1729+
collectClassInitSelfLoadUses(MarkUninitializedInst *MUI,
16961730
SingleValueInstruction *LI) {
16971731
assert(isa<LoadBorrowInst>(LI) || isa<LoadInst>(LI));
16981732

@@ -1794,13 +1828,12 @@ void swift::ownership::collectDIElementUsesFrom(
17941828
// at all. Just treat all members of self as uses of the single
17951829
// non-field-sensitive value.
17961830
assert(MemoryInfo.NumElements == 1 && "delegating inits only have 1 bit");
1797-
collectValueTypeDelegatingInitUses(MemoryInfo, UseInfo,
1798-
MemoryInfo.MemoryInst);
1831+
collectDelegatingInitUses(MemoryInfo, UseInfo, MemoryInfo.MemoryInst);
17991832
return;
18001833
}
18011834

18021835
if (shouldPerformClassInitSelf(MemoryInfo)) {
1803-
DelegatingClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo);
1836+
ClassInitElementUseCollector UseCollector(MemoryInfo, UseInfo);
18041837
UseCollector.collectClassInitSelfUses();
18051838
return;
18061839
}

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ class DIMemoryObjectInfo {
116116
return false;
117117
}
118118

119-
/// True if the memory object is the 'self' argument of a class initializer.
119+
/// True if the memory object is the 'self' argument of a class designated
120+
/// initializer.
120121
bool isClassInitSelf() const {
122+
if (isDelegatingInit())
123+
return false;
124+
121125
if (!MemoryInst->isVar()) {
122126
if (auto decl = getType()->getAnyNominal()) {
123127
if (isa<ClassDecl>(decl)) {
@@ -226,7 +230,11 @@ enum DIUseKind {
226230

227231
/// This instruction is a call to 'self.init' in a delegating initializer,
228232
/// or a call to 'super.init' in a designated initializer of a derived class..
229-
SelfInit
233+
SelfInit,
234+
235+
/// This instruction is a load that's only used to answer a `type(of: self)`
236+
/// question.
237+
LoadForTypeOfSelf,
230238
};
231239

232240
/// This struct represents a single classified access to the memory object

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ namespace {
480480

481481
void handleStoreUse(unsigned UseID);
482482
void handleLoadUse(unsigned UseID);
483+
void handleLoadForTypeOfSelfUse(const DIMemoryUse &Use);
483484
void handleInOutUse(const DIMemoryUse &Use);
484485
void handleEscapeUse(const DIMemoryUse &Use);
485486

@@ -542,8 +543,21 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
542543
// Keep track of all the uses that aren't loads or escapes. These are
543544
// important uses that we'll visit, but we don't consider them definition
544545
// points for liveness computation purposes.
545-
if (Use.Kind == DIUseKind::Load || Use.Kind == DIUseKind::Escape)
546+
switch (Use.Kind) {
547+
case DIUseKind::Load:
548+
case DIUseKind::LoadForTypeOfSelf:
549+
case DIUseKind::Escape:
546550
continue;
551+
case DIUseKind::Assign:
552+
case DIUseKind::IndirectIn:
553+
case DIUseKind::InitOrAssign:
554+
case DIUseKind::InOutArgument:
555+
case DIUseKind::Initialization:
556+
case DIUseKind::InOutSelfArgument:
557+
case DIUseKind::PartialStore:
558+
case DIUseKind::SelfInit:
559+
break;
560+
}
547561

548562
NonLoadUses[Use.Inst] = ui;
549563

@@ -790,6 +804,8 @@ void LifetimeChecker::doIt() {
790804
case DIUseKind::SelfInit:
791805
handleSelfInitUse(Use);
792806
break;
807+
case DIUseKind::LoadForTypeOfSelf:
808+
handleLoadForTypeOfSelfUse(Use);
793809
}
794810
}
795811

@@ -826,6 +842,28 @@ void LifetimeChecker::handleLoadUse(unsigned UseID) {
826842
return handleLoadUseFailure(Use, IsSuperInitComplete, FailedSelfUse);
827843
}
828844

845+
void LifetimeChecker::handleLoadForTypeOfSelfUse(const DIMemoryUse &Use) {
846+
bool IsSuperInitComplete, FailedSelfUse;
847+
// If the value is not definitively initialized, replace the
848+
// value_metatype instruction with the metatype argument that was passed into
849+
// the initializer.
850+
if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse)) {
851+
auto load = cast<SingleValueInstruction>(Use.Inst);
852+
853+
ValueMetatypeInst *valueMetatype = nullptr;
854+
for (auto use : load->getUses()) {
855+
valueMetatype = dyn_cast<ValueMetatypeInst>(use->getUser());
856+
if (valueMetatype)
857+
break;
858+
}
859+
assert(valueMetatype);
860+
auto metatypeArgument = load->getFunction()->getEntryBlock()->getArguments()
861+
.back();
862+
replaceAllSimplifiedUsesAndErase(valueMetatype, metatypeArgument,
863+
[](SILInstruction*) { });
864+
}
865+
}
866+
829867
void LifetimeChecker::emitSelfConsumedDiagnostic(SILInstruction *Inst) {
830868
if (!shouldEmitError(Inst))
831869
return;
@@ -1930,12 +1968,13 @@ void LifetimeChecker::processUninitializedRelease(SILInstruction *Release,
19301968
auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy);
19311969
SILValue Metatype;
19321970

1933-
// In an inherited convenience initializer, we must use the dynamic
1934-
// type of the object since nothing is initialized yet.
1935-
if (TheMemory.isDelegatingInit())
1936-
Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer);
1937-
else
1938-
Metatype = B.createMetatype(Loc, SILMetatypeTy);
1971+
// A convenience initializer should never deal in partially allocated
1972+
// objects.
1973+
assert(!TheMemory.isDelegatingInit());
1974+
1975+
// In a designated initializer, we know the class of the thing
1976+
// we're cleaning up statically.
1977+
Metatype = B.createMetatype(Loc, SILMetatypeTy);
19391978

19401979
// We've already destroyed any instance variables initialized by this
19411980
// constructor, now destroy instance variables initialized by subclass

test/SILGen/wip-convenience-init.swift

Lines changed: 0 additions & 44 deletions
This file was deleted.

test/SILGen/wip-objc-convenience-init.swift

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)