Skip to content

Commit 6f4e2ab

Browse files
committed
[semantic-arc] Add a new guaranteed ARC optimization pass.
Often times SILGen wants to hold onto values that have been copied. This causes an issue, when due to Cleanups firing, SILBuilder inserts destroys and destroys the copy that produced the value that SILGen held onto. This will then cause SILGen to emit incorrect code. There really is no reason to introduce such complexity into SILBuilder when a small simple guaranteed pass can perform the same work. Thus the introduction of this pass. In a later commit, I am going to eliminate the SILBuilder entry points. rdar://28685236
1 parent 5fd8d0f commit 6f4e2ab

File tree

10 files changed

+440
-4
lines changed

10 files changed

+440
-4
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ PASS(GlobalOpt, "global-opt",
127127
"Global variable optimizations")
128128
PASS(GlobalPropertyOpt, "global-property-opt",
129129
"Optimize properties")
130+
PASS(GuaranteedARCOpts, "guaranteed-arc-opts",
131+
"Guaranteed ARC optimizations")
130132
PASS(HighLevelCSE, "high-level-cse",
131133
"Common subexpression elimination on High-level SIL")
132134
PASS(HighLevelLICM, "high-level-licm",
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
set(MANDATORY_SOURCES
22
Mandatory/DefiniteInitialization.cpp
3-
Mandatory/MandatoryInlining.cpp
43
Mandatory/DIMemoryUseCollector.cpp
54
Mandatory/DataflowDiagnostics.cpp
65
Mandatory/DiagnoseUnreachable.cpp
6+
Mandatory/GuaranteedARCOpts.cpp
7+
Mandatory/MandatoryInlining.cpp
78
Mandatory/PredictableMemOpt.cpp
89
Mandatory/ConstantPropagation.cpp
910
PARENT_SCOPE)
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
//===--- GuaranteedARCOpts.cpp --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#define DEBUG_TYPE "sil-guaranteed-arc-opts"
14+
#include "swift/SILOptimizer/PassManager/Passes.h"
15+
#include "swift/SILOptimizer/PassManager/Transforms.h"
16+
#include "swift/SIL/SILVisitor.h"
17+
18+
using namespace swift;
19+
20+
namespace {
21+
22+
struct GuaranteedARCOptsVisitor
23+
: SILInstructionVisitor<GuaranteedARCOptsVisitor, bool> {
24+
bool visitValueBase(ValueBase *V) { return false; }
25+
bool visitDestroyAddrInst(DestroyAddrInst *DAI);
26+
bool visitStrongReleaseInst(StrongReleaseInst *SRI);
27+
bool visitDestroyValueInst(DestroyValueInst *DVI);
28+
bool visitReleaseValueInst(ReleaseValueInst *RVI);
29+
};
30+
31+
} // end anonymous namespace
32+
33+
static SILBasicBlock::reverse_iterator
34+
getPrevReverseIterator(SILInstruction *I) {
35+
auto Iter = std::next(I->getIterator());
36+
return std::next(SILBasicBlock::reverse_iterator(Iter));
37+
}
38+
39+
bool GuaranteedARCOptsVisitor::visitDestroyAddrInst(DestroyAddrInst *DAI) {
40+
SILValue Operand = DAI->getOperand();
41+
42+
for (auto II = getPrevReverseIterator(DAI), IE = DAI->getParent()->rend();
43+
II != IE;) {
44+
auto *Inst = &*II;
45+
++II;
46+
47+
if (auto *CA = dyn_cast<CopyAddrInst>(Inst)) {
48+
if (CA->getSrc() == Operand && !CA->isTakeOfSrc()) {
49+
CA->setIsTakeOfSrc(IsTake);
50+
DAI->eraseFromParent();
51+
return true;
52+
}
53+
}
54+
55+
// destroy_addrs commonly exist in a block of dealloc_stack's, which don't
56+
// affect take-ability.
57+
if (isa<DeallocStackInst>(Inst))
58+
continue;
59+
60+
// This code doesn't try to prove tricky validity constraints about whether
61+
// it is safe to push the destroy_addr past interesting instructions.
62+
if (Inst->mayHaveSideEffects())
63+
break;
64+
}
65+
66+
// If we didn't find a copy_addr to fold this into, emit the destroy_addr.
67+
return false;
68+
}
69+
70+
static bool couldReduceStrongRefcount(SILInstruction *Inst) {
71+
// Simple memory accesses cannot reduce refcounts.
72+
if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
73+
isa<RetainValueInst>(Inst) || isa<UnownedRetainInst>(Inst) ||
74+
isa<UnownedReleaseInst>(Inst) || isa<StrongRetainUnownedInst>(Inst) ||
75+
isa<StoreWeakInst>(Inst) || isa<StrongRetainInst>(Inst) ||
76+
isa<AllocStackInst>(Inst) || isa<DeallocStackInst>(Inst))
77+
return false;
78+
79+
// Assign and copyaddr of trivial types cannot drop refcounts, and 'inits'
80+
// never can either. Nontrivial ones can though, because the overwritten
81+
// value drops a retain. We would have to do more alias analysis to be able
82+
// to safely ignore one of those.
83+
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
84+
SILType StoredType = AI->getOperand(0)->getType();
85+
if (StoredType.isTrivial(Inst->getModule()) ||
86+
StoredType.is<ReferenceStorageType>())
87+
return false;
88+
}
89+
90+
if (auto *CAI = dyn_cast<CopyAddrInst>(Inst)) {
91+
// Initializations can only increase refcounts.
92+
if (CAI->isInitializationOfDest())
93+
return false;
94+
95+
SILType StoredType = CAI->getOperand(0)->getType().getObjectType();
96+
if (StoredType.isTrivial(Inst->getModule()) ||
97+
StoredType.is<ReferenceStorageType>())
98+
return false;
99+
}
100+
101+
// This code doesn't try to prove tricky validity constraints about whether
102+
// it is safe to push the release past interesting instructions.
103+
return Inst->mayHaveSideEffects();
104+
}
105+
106+
bool GuaranteedARCOptsVisitor::visitStrongReleaseInst(StrongReleaseInst *SRI) {
107+
SILValue Operand = SRI->getOperand();
108+
// Release on a functionref is a noop.
109+
if (isa<FunctionRefInst>(Operand)) {
110+
SRI->eraseFromParent();
111+
return true;
112+
}
113+
114+
// Check to see if the instruction immediately before the insertion point is a
115+
// strong_retain of the specified operand. If so, we can zap the pair.
116+
for (auto II = getPrevReverseIterator(SRI), IE = SRI->getParent()->rend();
117+
II != IE;) {
118+
auto *Inst = &*II;
119+
++II;
120+
121+
if (auto *SRA = dyn_cast<StrongRetainInst>(Inst)) {
122+
if (SRA->getOperand() == Operand) {
123+
SRA->eraseFromParent();
124+
SRI->eraseFromParent();
125+
return true;
126+
}
127+
// Skip past unrelated retains.
128+
continue;
129+
}
130+
131+
// Scan past simple instructions that cannot reduce strong refcounts.
132+
if (couldReduceStrongRefcount(Inst))
133+
break;
134+
}
135+
136+
// If we didn't find a retain to fold this into, return false.
137+
return false;
138+
}
139+
140+
bool GuaranteedARCOptsVisitor::visitDestroyValueInst(DestroyValueInst *DVI) {
141+
SILValue Operand = DVI->getOperand();
142+
for (auto II = getPrevReverseIterator(DVI), IE = DVI->getParent()->rend();
143+
II != IE;) {
144+
auto *Inst = &*II;
145+
++II;
146+
147+
if (auto *CVI = dyn_cast<CopyValueInst>(Inst)) {
148+
if (SILValue(CVI) == Operand || CVI->getOperand() == Operand) {
149+
CVI->replaceAllUsesWith(CVI->getOperand());
150+
CVI->eraseFromParent();
151+
DVI->eraseFromParent();
152+
return true;
153+
}
154+
// Skip past unrelated retains.
155+
continue;
156+
}
157+
158+
// Scan past simple instructions that cannot reduce refcounts.
159+
if (couldReduceStrongRefcount(Inst))
160+
break;
161+
}
162+
163+
return false;
164+
}
165+
166+
bool GuaranteedARCOptsVisitor::visitReleaseValueInst(ReleaseValueInst *RVI) {
167+
SILValue Operand = RVI->getOperand();
168+
169+
for (auto II = getPrevReverseIterator(RVI), IE = RVI->getParent()->rend();
170+
II != IE;) {
171+
auto *Inst = &*II;
172+
++II;
173+
174+
if (auto *SRA = dyn_cast<RetainValueInst>(Inst)) {
175+
if (SRA->getOperand() == Operand) {
176+
SRA->eraseFromParent();
177+
RVI->eraseFromParent();
178+
return true;
179+
}
180+
// Skip past unrelated retains.
181+
continue;
182+
}
183+
184+
// Scan past simple instructions that cannot reduce refcounts.
185+
if (couldReduceStrongRefcount(Inst))
186+
break;
187+
}
188+
189+
// If we didn't find a retain to fold this into, emit the release.
190+
return false;
191+
}
192+
193+
//===----------------------------------------------------------------------===//
194+
// Top Level Entrypoint
195+
//===----------------------------------------------------------------------===//
196+
197+
namespace {
198+
199+
struct GuaranteedARCOpts : SILFunctionTransform {
200+
void run() override {
201+
GuaranteedARCOptsVisitor Visitor;
202+
203+
bool MadeChange = false;
204+
SILFunction *F = getFunction();
205+
for (auto &BB : *F) {
206+
for (auto II = BB.begin(), IE = BB.end(); II != IE;) {
207+
SILInstruction *I = &*II;
208+
++II;
209+
MadeChange |= Visitor.visit(I);
210+
}
211+
}
212+
213+
if (MadeChange) {
214+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
215+
}
216+
}
217+
218+
StringRef getName() override { return "Guaranteed ARC Opts"; }
219+
};
220+
221+
} // end swift namespace
222+
223+
SILTransform *swift::createGuaranteedARCOpts() {
224+
return new GuaranteedARCOpts();
225+
}

lib/SILOptimizer/PassManager/Passes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ bool swift::runSILDiagnosticPasses(SILModule &Module) {
101101
PM.addMandatoryInlining();
102102
PM.addPredictableMemoryOptimizations();
103103
PM.addDiagnosticConstantPropagation();
104+
PM.addGuaranteedARCOpts();
104105
PM.addDiagnoseUnreachable();
105106
PM.addEmitDFDiagnostics();
106107
// Canonical swift requires all non cond_br critical edges to be split.

test/DebugInfo/return.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public func ifelseexpr() -> Int64 {
1818
x.x -= 1
1919
}
2020
// CHECK: @swift_rt_swift_release to void (%C6return1X*)*)(%C6return1X* [[X]])
21-
// CHECK: @swift_rt_swift_release to void (%C6return1X*)*)(%C6return1X* [[X]])
2221
// CHECK-SAME: , !dbg ![[RELEASE:.*]]
2322

2423
// The ret instruction should be in the same scope as the return expression.

test/IRGen/boxed_existential.sil

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
import Swift
44

5+
sil @error_user : $@convention(thin) (@owned Error) -> ()
6+
57
// CHECK-LABEL: define{{( protected)?}} void @retain_release_boxed_existential(%swift.error*)
6-
sil @retain_release_boxed_existential : $@convention(thin) (Error) -> () {
8+
sil @retain_release_boxed_existential : $@convention(thin) (@owned Error) -> () {
79
entry(%e : $Error):
810
// CHECK-objc: @swift_errorRetain
911
// CHECK-native: @swift_rt_swift_retain
1012
strong_retain %e : $Error
13+
%1 = function_ref @error_user : $@convention(thin) (@owned Error) -> ()
14+
apply %1(%e) : $@convention(thin) (@owned Error) -> ()
1115
// CHECK-objc: @swift_errorRelease
1216
// CHECK-native: @swift_rt_swift_release
1317
strong_release %e : $Error

test/IRGen/enum_value_semantics.sil

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ enum SinglePayloadNontrivial {
2828
case c
2929
}
3030

31+
sil @single_payload_nontrivial_user : $@convention(thin) (@owned SinglePayloadNontrivial) -> ()
32+
3133
enum MultiPayloadTrivial {
3234
case payload1(Builtin.Int64)
3335
case payload2(Builtin.Int32, Builtin.Int32)
@@ -185,9 +187,11 @@ enum GenericFixedLayout<T> {
185187
// CHECK: %swift.type** null
186188
// CHECK: }>
187189

188-
sil @single_payload_nontrivial_copy_destroy : $(SinglePayloadNontrivial) -> () {
190+
sil @single_payload_nontrivial_copy_destroy : $(@owned SinglePayloadNontrivial) -> () {
189191
bb0(%0 : $SinglePayloadNontrivial):
190192
retain_value %0 : $SinglePayloadNontrivial
193+
%1 = function_ref @single_payload_nontrivial_user : $@convention(thin) (@owned SinglePayloadNontrivial) -> ()
194+
apply %1(%0) : $@convention(thin) (@owned SinglePayloadNontrivial) -> ()
191195
release_value %0 : $SinglePayloadNontrivial
192196
%v = tuple ()
193197
return %v : $()

test/IRGen/objc.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class Octogenarian : Contrarian {
6868
@objc override func eviscerate() {}
6969
}
7070

71+
@_silgen_name("unknown")
72+
func unknown(_ x: id) -> id
73+
7174
// CHECK: define hidden %objc_object* @_TF4objc5test0{{.*}}(%objc_object*)
7275
// CHECK-NOT: call {{.*}} @swift_unknownRetain
7376
// CHECK: call {{.*}} @swift_unknownRetain
@@ -77,6 +80,7 @@ class Octogenarian : Contrarian {
7780
func test0(_ arg: id) -> id {
7881
var x : id
7982
x = arg
83+
unknown(x)
8084
var y = x
8185
return y
8286
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-sil-opt -guaranteed-arc-opts %s | %FileCheck %s
2+
3+
sil_stage raw
4+
5+
import Builtin
6+
7+
sil @kraken : $@convention(thin) () -> ()
8+
9+
// CHECK-LABEL: sil @copyvalue_test1 : $@convention(thin) (Builtin.NativeObject, Builtin.NativeObject) -> () {
10+
// CHECK: bb0([[ARG1:%.*]] : $Builtin.NativeObject, [[ARG2:%.*]] : $Builtin.NativeObject):
11+
// CHECK-NOT: copy_value [[ARG1]]
12+
// CHECK: copy_value [[ARG2]]
13+
// CHECK-NOT: destroy_value [[ARG1]]
14+
sil @copyvalue_test1 : $@convention(thin) (Builtin.NativeObject, Builtin.NativeObject) -> () {
15+
bb0(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject):
16+
%2 = copy_value %0 : $Builtin.NativeObject
17+
copy_value %1 : $Builtin.NativeObject
18+
destroy_value %2 : $Builtin.NativeObject
19+
%9999 = tuple()
20+
return %9999 : $()
21+
}
22+
23+
// CHECK-LABEL: sil @copyvalue_test2 : $@convention(thin) (Builtin.NativeObject, @in Builtin.Int32) -> Builtin.NativeObject {
24+
// CHECK: bb0([[ARG1:%.*]] : $Builtin.NativeObject
25+
// CHECK-NOT: copy_value
26+
// CHECK-NOT: destroy_value
27+
// CHECK: return [[ARG1]]
28+
sil @copyvalue_test2 : $@convention(thin) (Builtin.NativeObject, @in Builtin.Int32) -> Builtin.NativeObject {
29+
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.Int32):
30+
%2 = copy_value %0 : $Builtin.NativeObject
31+
%3 = integer_literal $Builtin.Int32, 0
32+
store %3 to [trivial] %1 : $*Builtin.Int32
33+
destroy_value %0 : $Builtin.NativeObject
34+
return %2 : $Builtin.NativeObject
35+
}
36+
37+
// CHECK-LABEL: sil @copyvalue_test3 : $@convention(thin) (Builtin.NativeObject) -> () {
38+
// CHECK: copy_value
39+
// CHECK: destroy_value
40+
sil @copyvalue_test3 : $@convention(thin) (Builtin.NativeObject) -> () {
41+
bb0(%0 : $Builtin.NativeObject):
42+
copy_value %0 : $Builtin.NativeObject
43+
%1 = function_ref @kraken : $@convention(thin) () -> ()
44+
apply %1() : $@convention(thin) () -> ()
45+
destroy_value %0 : $Builtin.NativeObject
46+
%9999 = tuple()
47+
return %9999 : $()
48+
}
49+
50+
// CHECK-LABEL: sil @copyvalue_test4 : $@convention(thin) (Builtin.NativeObject) -> () {
51+
// CHECK: destroy_value
52+
sil @copyvalue_test4 : $@convention(thin) (Builtin.NativeObject) -> () {
53+
bb0(%0 : $Builtin.NativeObject):
54+
destroy_value %0 : $Builtin.NativeObject
55+
%9999 = tuple()
56+
return %9999 : $()
57+
}

0 commit comments

Comments
 (0)