Skip to content

Commit 1b2565a

Browse files
authored
Merge pull request #34017 from hborla/5.3-assign-by-wrapper-lowering
[5.3][Property Wrappers] Refine assign_by_wrapper lowering
2 parents cbfb27c + 93f182a commit 1b2565a

File tree

5 files changed

+99
-27
lines changed

5 files changed

+99
-27
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3937,6 +3937,16 @@ class AssignByWrapperInst
39373937
: public AssignInstBase<SILInstructionKind::AssignByWrapperInst, 4> {
39383938
friend SILBuilder;
39393939

3940+
public:
3941+
/// The assignment destination for the property wrapper
3942+
enum class Destination {
3943+
BackingWrapper,
3944+
WrappedValue,
3945+
};
3946+
3947+
private:
3948+
Destination AssignDest = Destination::WrappedValue;
3949+
39403950
AssignByWrapperInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
39413951
SILValue Initializer, SILValue Setter,
39423952
AssignOwnershipQualifier Qualifier =
@@ -3951,8 +3961,17 @@ class AssignByWrapperInst
39513961
return AssignOwnershipQualifier(
39523962
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier);
39533963
}
3954-
void setOwnershipQualifier(AssignOwnershipQualifier qualifier) {
3964+
3965+
Destination getAssignDestination() const { return AssignDest; }
3966+
3967+
void setAssignInfo(AssignOwnershipQualifier qualifier, Destination dest) {
3968+
using Qualifier = AssignOwnershipQualifier;
3969+
assert(qualifier == Qualifier::Init && dest == Destination::BackingWrapper ||
3970+
qualifier == Qualifier::Reassign && dest == Destination::BackingWrapper ||
3971+
qualifier == Qualifier::Reassign && dest == Destination::WrappedValue);
3972+
39553973
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier = unsigned(qualifier);
3974+
AssignDest = dest;
39563975
}
39573976
};
39583977

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ enum DIUseKind {
254254
/// value.
255255
Assign,
256256

257+
/// The instruction is an assignment of a wrapped value with an already initialized
258+
/// backing property wrapper.
259+
AssignWrappedValue,
260+
257261
/// The instruction is a store to a member of a larger struct value.
258262
PartialStore,
259263

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
533533
case DIUseKind::Escape:
534534
continue;
535535
case DIUseKind::Assign:
536+
case DIUseKind::AssignWrappedValue:
536537
case DIUseKind::IndirectIn:
537538
case DIUseKind::InitOrAssign:
538539
case DIUseKind::InOutArgument:
@@ -750,6 +751,7 @@ void LifetimeChecker::doIt() {
750751
continue;
751752

752753
case DIUseKind::Assign:
754+
case DIUseKind::AssignWrappedValue:
753755
// Instructions classified as assign are only generated when lowering
754756
// InitOrAssign instructions in regions known to be initialized. Since
755757
// they are already known to be definitely init, don't reprocess them.
@@ -1047,16 +1049,15 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10471049
// an initialization or assign in the uses list so that clients know about it.
10481050
if (isFullyUninitialized) {
10491051
Use.Kind = DIUseKind::Initialization;
1052+
} else if (isFullyInitialized && isa<AssignByWrapperInst>(Use.Inst)) {
1053+
// If some fields are uninitialized, re-write assign_by_wrapper to assignment
1054+
// of the backing wrapper. If all fields are initialized, assign to the wrapped
1055+
// value.
1056+
auto allFieldsInitialized =
1057+
getAnyUninitializedMemberAtInst(Use.Inst, 0, TheMemory.getNumElements()) == -1;
1058+
Use.Kind = allFieldsInitialized ? DIUseKind::AssignWrappedValue : DIUseKind::Assign;
10501059
} else if (isFullyInitialized) {
1051-
// Only re-write assign_by_wrapper to assignment if all fields have been
1052-
// initialized.
1053-
if (isa<AssignByWrapperInst>(Use.Inst) &&
1054-
getAnyUninitializedMemberAtInst(Use.Inst, 0,
1055-
TheMemory.getNumElements()) != -1) {
1056-
Use.Kind = DIUseKind::Initialization;
1057-
} else {
1058-
Use.Kind = DIUseKind::Assign;
1059-
}
1060+
Use.Kind = DIUseKind::Assign;
10601061
} else {
10611062
// If it is initialized on some paths, but not others, then we have an
10621063
// inconsistent initialization, which needs dynamic control logic in the
@@ -1909,7 +1910,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19091910
Use.Kind == DIUseKind::SelfInit)
19101911
InitKind = IsInitialization;
19111912
else {
1912-
assert(Use.Kind == DIUseKind::Assign);
1913+
assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::AssignWrappedValue);
19131914
InitKind = IsNotInitialization;
19141915
}
19151916

@@ -1958,14 +1959,21 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19581959
Use.Inst = nullptr;
19591960
NonLoadUses.erase(Inst);
19601961

1961-
if (TheMemory.isClassInitSelf() &&
1962-
Use.Kind == DIUseKind::SelfInit) {
1963-
assert(InitKind == IsInitialization);
1964-
AI->setOwnershipQualifier(AssignOwnershipQualifier::Reinit);
1965-
} else {
1966-
AI->setOwnershipQualifier((InitKind == IsInitialization
1967-
? AssignOwnershipQualifier::Init
1968-
: AssignOwnershipQualifier::Reassign));
1962+
switch (Use.Kind) {
1963+
case DIUseKind::Initialization:
1964+
AI->setAssignInfo(AssignOwnershipQualifier::Init,
1965+
AssignByWrapperInst::Destination::BackingWrapper);
1966+
break;
1967+
case DIUseKind::Assign:
1968+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1969+
AssignByWrapperInst::Destination::BackingWrapper);
1970+
break;
1971+
case DIUseKind::AssignWrappedValue:
1972+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1973+
AssignByWrapperInst::Destination::WrappedValue);
1974+
break;
1975+
default:
1976+
llvm_unreachable("Wrong use kind for assign_by_wrapper");
19691977
}
19701978

19711979
return;

lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
179179
SILLocation loc = inst->getLoc();
180180
SILBuilderWithScope forCleanup(std::next(inst->getIterator()));
181181

182-
switch (inst->getOwnershipQualifier()) {
183-
case AssignOwnershipQualifier::Init: {
182+
switch (inst->getAssignDestination()) {
183+
case AssignByWrapperInst::Destination::BackingWrapper: {
184184
SILValue initFn = inst->getInitializer();
185185
CanSILFunctionType fTy = initFn->getType().castTo<SILFunctionType>();
186186
SILFunctionConventions convention(fTy, inst->getModule());
@@ -193,15 +193,18 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
193193
getAssignByWrapperArgs(args, src, convention, b, forCleanup);
194194
SILValue wrappedSrc = b.createApply(loc, initFn, SubstitutionMap(),
195195
args);
196-
b.createTrivialStoreOr(loc, wrappedSrc, dest,
197-
StoreOwnershipQualifier::Init);
196+
if (inst->getOwnershipQualifier() == AssignOwnershipQualifier::Init ||
197+
inst->getDest()->getType().isTrivial(*inst->getFunction())) {
198+
b.createTrivialStoreOr(loc, wrappedSrc, dest, StoreOwnershipQualifier::Init);
199+
} else {
200+
b.createStore(loc, wrappedSrc, dest, StoreOwnershipQualifier::Assign);
201+
}
198202
}
199203
// TODO: remove the unused setter function, which usually is a dead
200204
// partial_apply.
201205
break;
202206
}
203-
case AssignOwnershipQualifier::Unknown:
204-
case AssignOwnershipQualifier::Reassign: {
207+
case AssignByWrapperInst::Destination::WrappedValue: {
205208
SILValue setterFn = inst->getSetter();
206209
CanSILFunctionType fTy = setterFn->getType().castTo<SILFunctionType>();
207210
SILFunctionConventions convention(fTy, inst->getModule());
@@ -220,8 +223,6 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
220223
// partial_apply.
221224
break;
222225
}
223-
case AssignOwnershipQualifier::Reinit:
224-
llvm_unreachable("wrong qualifier for assign_by_wrapper");
225226
}
226227
inst->eraseFromParent();
227228
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
8+
import StdlibUnittest
9+
10+
@propertyWrapper
11+
struct Wrapper<T> {
12+
var wrappedValue: T
13+
}
14+
15+
struct TestWrappedValueLeak {
16+
@Wrapper var wrapped: LifetimeTracked = LifetimeTracked(0)
17+
var str: String
18+
19+
init() {
20+
wrapped = LifetimeTracked(42)
21+
str = ""
22+
wrapped = LifetimeTracked(27)
23+
}
24+
25+
init(conditionalInit: Bool) {
26+
if (conditionalInit) {
27+
wrapped = LifetimeTracked(42)
28+
}
29+
str = ""
30+
wrapped = LifetimeTracked(27)
31+
}
32+
}
33+
34+
TestSuite("Property Wrapper DI").test("test wrapped value leak") {
35+
_ = TestWrappedValueLeak()
36+
_ = TestWrappedValueLeak(conditionalInit: true)
37+
_ = TestWrappedValueLeak(conditionalInit: false)
38+
}
39+
40+
runAllTests()

0 commit comments

Comments
 (0)