14
14
#include " swift/SIL/OwnershipChecker.h"
15
15
#include " swift/SIL/SILArgument.h"
16
16
#include " swift/SIL/SILInstruction.h"
17
+ #include " swift/SIL/TransitivelyUnreachableBlocks.h"
18
+ #include " swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
17
19
#include " swift/SILOptimizer/PassManager/Passes.h"
18
20
#include " swift/SILOptimizer/PassManager/Transforms.h"
19
21
#include " llvm/ADT/SmallPtrSet.h"
@@ -24,20 +26,21 @@ using namespace swift;
24
26
25
27
STATISTIC (NumEliminatedInsts, " number of removed instructions" );
26
28
27
- static bool optimizeGuaranteedArgument (SILArgument *Arg) {
29
+ static bool optimizeGuaranteedArgument (SILArgument *Arg,
30
+ OwnershipChecker &Checker) {
28
31
bool MadeChange = false ;
29
32
30
33
// Gather all copy_value users of Arg.
31
- llvm::SmallVector<CopyValueInst *, 4 > Copies ;
34
+ llvm::SmallVector<CopyValueInst *, 4 > Worklist ;
32
35
for (auto *Op : Arg->getUses ()) {
33
36
if (auto *CVI = dyn_cast<CopyValueInst>(Op->getUser ())) {
34
- Copies .push_back (CVI);
37
+ Worklist .push_back (CVI);
35
38
}
36
39
}
37
40
38
41
// Then until we run out of copies...
39
- while (!Copies .empty ()) {
40
- auto *CVI = Copies .pop_back_val ();
42
+ while (!Worklist .empty ()) {
43
+ auto *CVI = Worklist .pop_back_val ();
41
44
42
45
// Quickly see if copy has only one use and that use is a destroy_value. In
43
46
// such a case, we can always eliminate both the copy and the destroy.
@@ -49,6 +52,84 @@ static bool optimizeGuaranteedArgument(SILArgument *Arg) {
49
52
continue ;
50
53
}
51
54
}
55
+
56
+ // Ok, now run the checker on the copy value. If it fails, then we just
57
+ // continue.
58
+ if (!Checker.checkValue (CVI))
59
+ continue ;
60
+
61
+ // Otherwise, lets do a quick check on what the checker thinks the lifetime
62
+ // ending and non-lifetime ending users. To be conservative, we bail unless
63
+ // each lifetime ending use is a destroy_value and if each non-lifetime
64
+ // ending use is one of the following instructions:
65
+ //
66
+ // 1. copy_value.
67
+ // 2. begin_borrow.
68
+ // 3. end_borrow.
69
+ if (!all_of (Checker.LifetimeEndingUsers , [](SILInstruction *I) -> bool {
70
+ return isa<DestroyValueInst>(I);
71
+ }))
72
+ continue ;
73
+
74
+ // Extra copy values that we should visit recursively.
75
+ llvm::SmallVector<CopyValueInst *, 8 > NewCopyInsts;
76
+ llvm::SmallVector<SILInstruction *, 8 > NewBorrowInsts;
77
+ if (!all_of (Checker.RegularUsers , [&](SILInstruction *I) -> bool {
78
+ if (auto *CVI = dyn_cast<CopyValueInst>(I)) {
79
+ NewCopyInsts.push_back (CVI);
80
+ return true ;
81
+ }
82
+
83
+ if (!isa<BeginBorrowInst>(I) && !isa<EndBorrowInst>(I))
84
+ return false ;
85
+
86
+ NewBorrowInsts.push_back (I);
87
+ return true ;
88
+ }))
89
+ continue ;
90
+
91
+ // Ok! we can remove the copy_value, destroy_values!
92
+ MadeChange = true ;
93
+ CVI->replaceAllUsesWith (CVI->getOperand ());
94
+ CVI->eraseFromParent ();
95
+ ++NumEliminatedInsts;
96
+
97
+ while (!Checker.LifetimeEndingUsers .empty ()) {
98
+ Checker.LifetimeEndingUsers .pop_back_val ()->eraseFromParent ();
99
+ ++NumEliminatedInsts;
100
+ }
101
+
102
+ // Then add the copy_values that were users of our original copy value to
103
+ // the worklist.
104
+ while (!NewCopyInsts.empty ()) {
105
+ Worklist.push_back (NewCopyInsts.pop_back_val ());
106
+ }
107
+
108
+ // Then remove any begin/end borrow that we found. These are unneeded since
109
+ // the lifetime guarantee from the argument exists above and beyond said
110
+ // scope.
111
+ while (!NewBorrowInsts.empty ()) {
112
+ SILInstruction *I = NewBorrowInsts.pop_back_val ();
113
+ if (auto *BBI = dyn_cast<BeginBorrowInst>(I)) {
114
+ // Any copy_value that is used by the begin borrow is added to the
115
+ // worklist.
116
+ for (auto *BBIUse : BBI->getUses ()) {
117
+ if (auto *BBIUseCopyValue =
118
+ dyn_cast<CopyValueInst>(BBIUse->getUser ())) {
119
+ Worklist.push_back (BBIUseCopyValue);
120
+ }
121
+ }
122
+ BBI->replaceAllUsesWith (BBI->getOperand ());
123
+ BBI->eraseFromParent ();
124
+ ++NumEliminatedInsts;
125
+ continue ;
126
+ }
127
+
128
+ // This is not necessary, but it does add a check.
129
+ auto *EBI = cast<EndBorrowInst>(I);
130
+ EBI->eraseFromParent ();
131
+ ++NumEliminatedInsts;
132
+ }
52
133
}
53
134
54
135
return MadeChange;
@@ -65,6 +146,10 @@ struct SemanticARCOpts : SILFunctionTransform {
65
146
bool MadeChange = false ;
66
147
SILFunction *F = getFunction ();
67
148
149
+ auto *PO = PM->getAnalysis <PostOrderAnalysis>()->get (F);
150
+ TransitivelyUnreachableBlocksInfo TUB (*PO);
151
+ OwnershipChecker Checker{F->getModule (), TUB, {}, {}, {}};
152
+
68
153
// First as a special case, handle guaranteed SIL function arguments.
69
154
//
70
155
// The reason that this is special is that we do not need to consider the
@@ -73,7 +158,7 @@ struct SemanticARCOpts : SILFunctionTransform {
73
158
for (auto *Arg : F->getArguments ()) {
74
159
if (Arg->getOwnershipKind () != ValueOwnershipKind::Guaranteed)
75
160
continue ;
76
- MadeChange |= optimizeGuaranteedArgument (Arg);
161
+ MadeChange |= optimizeGuaranteedArgument (Arg, Checker );
77
162
}
78
163
79
164
if (MadeChange) {
0 commit comments