Skip to content

Commit 46622c3

Browse files
authored
Merge pull request #4045 from eeckstein/leak3
SILOptimizer: fixed bugs which cause memory leaks.
2 parents 6f165cc + 90089e3 commit 46622c3

File tree

8 files changed

+248
-37
lines changed

8 files changed

+248
-37
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ class CastOptimizer {
470470
/// into a bridged ObjC type.
471471
SILInstruction *
472472
optimizeBridgedSwiftToObjCCast(SILInstruction *Inst,
473+
CastConsumptionKind ConsumptionKind,
473474
bool isConditional,
474475
SILValue Src,
475476
SILValue Dest,
@@ -531,6 +532,7 @@ class CastOptimizer {
531532
/// May change the control flow.
532533
SILInstruction *
533534
optimizeBridgedCasts(SILInstruction *Inst,
535+
CastConsumptionKind ConsumptionKind,
534536
bool isConditional,
535537
SILValue Src,
536538
SILValue Dest,

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class MemoryToRegisters {
156156
SILBuilder B;
157157

158158
/// \brief Check if the AllocStackInst \p ASI is only written into.
159-
bool isWriteOnlyAllocation(AllocStackInst *ASI, bool Promoted = false);
159+
bool isWriteOnlyAllocation(AllocStackInst *ASI);
160160

161161
/// \brief Promote all of the AllocStacks in a single basic block in one
162162
/// linear scan. Note: This function deletes all of the users of the
@@ -242,8 +242,7 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
242242
}
243243

244244
/// Returns true if the AllocStack is only stored into.
245-
bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI,
246-
bool Promoted) {
245+
bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI) {
247246
// For all users of the AllocStack:
248247
for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) {
249248
SILInstruction *II = UI->getUser();
@@ -259,15 +258,9 @@ bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI,
259258

260259
// If we haven't already promoted the AllocStack, we may see
261260
// DebugValueAddr uses.
262-
if (!Promoted && isa<DebugValueAddrInst>(II))
261+
if (isa<DebugValueAddrInst>(II))
263262
continue;
264263

265-
// Destroys of loadable types can be rewritten as releases, so
266-
// they are fine.
267-
if (auto *DAI = dyn_cast<DestroyAddrInst>(II))
268-
if (!Promoted && DAI->getOperand()->getType().isLoadable(DAI->getModule()))
269-
continue;
270-
271264
// Can't do anything else with it.
272265
DEBUG(llvm::dbgs() << "*** AllocStack has non-write use: " << *II);
273266
return false;
@@ -874,8 +867,7 @@ bool MemoryToRegisters::run() {
874867
StackAllocationPromoter(ASI, DT, DomTreeLevels, B).run();
875868

876869
// Make sure that all of the allocations were promoted into registers.
877-
assert(isWriteOnlyAllocation(ASI, /* Promoted =*/ true) &&
878-
"Non-write uses left behind");
870+
assert(isWriteOnlyAllocation(ASI) && "Non-write uses left behind");
879871
// ... and erase the allocation.
880872
eraseUsesOfInstruction(ASI);
881873

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,7 @@ optimizeBridgedObjCToSwiftCast(SILInstruction *Inst,
15971597
SILInstruction *
15981598
CastOptimizer::
15991599
optimizeBridgedSwiftToObjCCast(SILInstruction *Inst,
1600+
CastConsumptionKind ConsumptionKind,
16001601
bool isConditional,
16011602
SILValue Src,
16021603
SILValue Dest,
@@ -1679,16 +1680,77 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst,
16791680
Src = Builder.createLoad(Loc, Src);
16801681
}
16811682

1682-
if (ParamTypes[0].getConvention() == ParameterConvention::Direct_Guaranteed)
1683+
// Compensate different owning conventions of the replaced cast instruction
1684+
// and the inserted convertion function.
1685+
bool needRetainBeforeCall = false;
1686+
bool needReleaseAfterCall = false;
1687+
bool needReleaseInSucc = false;
1688+
switch (ParamTypes[0].getConvention()) {
1689+
case ParameterConvention::Direct_Guaranteed:
1690+
switch (ConsumptionKind) {
1691+
case CastConsumptionKind::TakeAlways:
1692+
needReleaseAfterCall = true;
1693+
break;
1694+
case CastConsumptionKind::TakeOnSuccess:
1695+
needReleaseInSucc = true;
1696+
break;
1697+
case CastConsumptionKind::CopyOnSuccess:
1698+
// Conservatively insert a retain/release pair around the conversion
1699+
// function because the conversion function could decrement the
1700+
// (global) reference count of the source object.
1701+
//
1702+
// %src = load %global_var
1703+
// apply %conversion_func(@guaranteed %src)
1704+
//
1705+
// sil conversion_func {
1706+
// %old_value = load %global_var
1707+
// store %something_else, %global_var
1708+
// strong_release %old_value
1709+
// }
1710+
needRetainBeforeCall = true;
1711+
needReleaseAfterCall = true;
1712+
break;
1713+
}
1714+
break;
1715+
case ParameterConvention::Direct_Owned:
1716+
// The Direct_Owned case is only handled for completeness. Currently this
1717+
// cannot appear, because the _bridgeToObjectiveC protocol witness method
1718+
// always receives the this pointer (= the source) as guaranteed.
1719+
switch (ConsumptionKind) {
1720+
case CastConsumptionKind::TakeAlways:
1721+
break;
1722+
case CastConsumptionKind::TakeOnSuccess:
1723+
needRetainBeforeCall = true;
1724+
needReleaseInSucc = true;
1725+
break;
1726+
case CastConsumptionKind::CopyOnSuccess:
1727+
needRetainBeforeCall = true;
1728+
break;
1729+
}
1730+
break;
1731+
case ParameterConvention::Direct_Unowned:
1732+
break;
1733+
case ParameterConvention::Indirect_In:
1734+
case ParameterConvention::Indirect_Inout:
1735+
case ParameterConvention::Indirect_InoutAliasable:
1736+
case ParameterConvention::Indirect_In_Guaranteed:
1737+
case ParameterConvention::Direct_Deallocating:
1738+
llvm_unreachable("unsupported convention for bridging conversion");
1739+
}
1740+
1741+
if (needRetainBeforeCall)
16831742
Builder.createRetainValue(Loc, Src, Atomicity::Atomic);
16841743

16851744
// Generate a code to invoke the bridging function.
16861745
auto *NewAI = Builder.createApply(Loc, FnRef, SubstFnTy, ResultTy, Subs, Src,
16871746
false);
16881747

1689-
if (ParamTypes[0].getConvention() == ParameterConvention::Direct_Guaranteed)
1748+
if (needReleaseAfterCall) {
16901749
Builder.createReleaseValue(Loc, Src, Atomicity::Atomic);
1691-
1750+
} else if (needReleaseInSucc) {
1751+
SILBuilder SuccBuilder(SuccessBB->begin());
1752+
SuccBuilder.createReleaseValue(Loc, Src, Atomicity::Atomic);
1753+
}
16921754
SILInstruction *NewI = NewAI;
16931755

16941756
if (Dest) {
@@ -1721,6 +1783,7 @@ optimizeBridgedSwiftToObjCCast(SILInstruction *Inst,
17211783
SILInstruction *
17221784
CastOptimizer::
17231785
optimizeBridgedCasts(SILInstruction *Inst,
1786+
CastConsumptionKind ConsumptionKind,
17241787
bool isConditional,
17251788
SILValue Src,
17261789
SILValue Dest,
@@ -1786,7 +1849,8 @@ optimizeBridgedCasts(SILInstruction *Inst,
17861849
target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB);
17871850
} else {
17881851
// This is a Swift to ObjC cast
1789-
return optimizeBridgedSwiftToObjCCast(Inst, isConditional, Src, Dest, source,
1852+
return optimizeBridgedSwiftToObjCCast(Inst, ConsumptionKind,
1853+
isConditional, Src, Dest, source,
17901854
target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB);
17911855
}
17921856
}
@@ -1863,7 +1927,8 @@ simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst) {
18631927
// To apply the bridged optimizations, we should
18641928
// ensure that types are not existential,
18651929
// and that not both types are classes.
1866-
BridgedI = optimizeBridgedCasts(Inst, true, Src, Dest, SourceType,
1930+
BridgedI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
1931+
true, Src, Dest, SourceType,
18671932
TargetType, SuccessBB, FailureBB);
18681933

18691934
if (!BridgedI) {
@@ -1975,7 +2040,8 @@ CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
19752040
auto Src = Inst->getOperand();
19762041
auto Dest = SILValue();
19772042
// To apply the bridged casts optimizations.
1978-
auto BridgedI = optimizeBridgedCasts(Inst, false, Src, Dest, SourceType,
2043+
auto BridgedI = optimizeBridgedCasts(Inst,
2044+
CastConsumptionKind::CopyOnSuccess, false, Src, Dest, SourceType,
19792045
TargetType, nullptr, nullptr);
19802046

19812047
if (BridgedI) {
@@ -2290,8 +2356,9 @@ optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst) {
22902356
auto SourceType = LoweredSourceType.getSwiftRValueType();
22912357
auto TargetType = LoweredTargetType.getSwiftRValueType();
22922358
auto Src = Inst->getOperand();
2293-
auto NewI = optimizeBridgedCasts(Inst, false, Src, SILValue(), SourceType,
2294-
TargetType, nullptr, nullptr);
2359+
auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess,
2360+
false, Src, SILValue(), SourceType,
2361+
TargetType, nullptr, nullptr);
22952362
if (NewI) {
22962363
ReplaceInstUsesAction(Inst, NewI);
22972364
EraseInstAction(Inst);
@@ -2406,14 +2473,25 @@ optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst)
24062473
}
24072474

24082475
if (ResultNotUsed) {
2476+
switch (Inst->getConsumptionKind()) {
2477+
case CastConsumptionKind::TakeAlways:
2478+
case CastConsumptionKind::TakeOnSuccess: {
2479+
SILBuilder B(Inst);
2480+
B.createDestroyAddr(Inst->getLoc(), Inst->getSrc());
2481+
break;
2482+
}
2483+
case CastConsumptionKind::CopyOnSuccess:
2484+
break;
2485+
}
24092486
EraseInstAction(Inst);
24102487
WillSucceedAction();
24112488
return nullptr;
24122489
}
24132490

24142491
// Try to apply the bridged casts optimizations
2415-
auto NewI = optimizeBridgedCasts(Inst, false, Src, Dest, SourceType,
2416-
TargetType, nullptr, nullptr);
2492+
auto NewI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
2493+
false, Src, Dest, SourceType,
2494+
TargetType, nullptr, nullptr);
24172495
if (NewI) {
24182496
WillSucceedAction();
24192497
return nullptr;

test/SILOptimizer/cast_folding_objc.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -O -emit-sil %s | FileCheck %s
1+
// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass="Function Signature Optimization" -emit-sil %s | FileCheck %s
22
// We want to check two things here:
33
// - Correctness
44
// - That certain "is" checks are eliminated based on static analysis at compile-time
@@ -51,6 +51,10 @@ func test0() -> Bool {
5151
// Check that this cast does not get eliminated, because
5252
// the compiler does not statically know if this object
5353
// is NSNumber can be converted into Int.
54+
55+
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc35testMayBeBridgedCastFromObjCtoSwiftFPs9AnyObject_Si
56+
// CHECK: unconditional_checked_cast_addr
57+
// CHECK: return
5458
@inline(never)
5559
public func testMayBeBridgedCastFromObjCtoSwift(_ o: AnyObject) -> Int {
5660
return o as! Int
@@ -59,6 +63,10 @@ public func testMayBeBridgedCastFromObjCtoSwift(_ o: AnyObject) -> Int {
5963
// Check that this cast does not get eliminated, because
6064
// the compiler does not statically know if this object
6165
// is NSString can be converted into String.
66+
67+
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc41testConditionalBridgedCastFromObjCtoSwiftFPs9AnyObject_GSqSS_
68+
// CHECK: unconditional_checked_cast_addr
69+
// CHECK: return
6270
@inline(never)
6371
public func testConditionalBridgedCastFromObjCtoSwift(_ o: AnyObject) -> String? {
6472
return o as? String
@@ -246,21 +254,14 @@ public func testBridgedCastFromObjCtoSwift(_ ns: NSString) -> String {
246254
}
247255

248256
// Check that compiler understands that this cast always succeeds
249-
@inline(never)
250-
public func testBridgedCastFromSwiftToObjC(_ s: String) -> NSString {
251-
return s as NSString
252-
}
253257

254-
// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc35testMayBeBridgedCastFromObjCtoSwiftFPs9AnyObject_Si
255-
// CHECK: unconditional_checked_cast_addr
256-
// CHECK: return
257-
258-
// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc41testConditionalBridgedCastFromObjCtoSwiftFPs9AnyObject_GSqSS_
259-
// CHECK: unconditional_checked_cast_addr
260-
// CHECK: return
261-
262-
// CHECK-LABEL: sil [noinline] @_TTSf4gs___TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
258+
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
263259
// CHECK-NOT: {{ cast}}
264260
// CHECK: function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
265261
// CHECK: apply
266262
// CHECK: return
263+
@inline(never)
264+
public func testBridgedCastFromSwiftToObjC(_ s: String) -> NSString {
265+
return s as NSString
266+
}
267+

test/SILOptimizer/cast_foldings.sil

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,16 @@ bb0:
104104
dealloc_stack %1 : $*S
105105
return %6 : $AnyObject
106106
}
107+
108+
// CHECK-LABEL: sil @destroy_source_of_removed_cast
109+
// CHECK: destroy_addr %0
110+
sil @destroy_source_of_removed_cast : $@convention(thin) (@in AnyObject) -> () {
111+
bb0(%0 : $*AnyObject):
112+
%2 = alloc_stack $Int
113+
unconditional_checked_cast_addr take_always AnyObject in %0 : $*AnyObject to Int in %2 : $*Int
114+
%3 = load %2 : $*Int
115+
dealloc_stack %2 : $*Int
116+
%r = tuple ()
117+
return %r : $()
118+
}
119+

0 commit comments

Comments
 (0)