Skip to content

Commit 96feb2d

Browse files
committed
[ownership] Extract out the main visitor SemanticARCOptVisitor from SemanticARCOpts.h into its own header.
This is so I can move individual sub-optimizations on SemanticARCOptVisitor into their own files. So for instance, there will be one file containing the load [copy] optimization and another containing the phi optimization, etc.
1 parent adba0f0 commit 96feb2d

File tree

2 files changed

+257
-220
lines changed

2 files changed

+257
-220
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
//===--- SemanticARCOptVisitor.h ------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H
14+
#define SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H
15+
16+
#include "swift/Basic/BlotSetVector.h"
17+
#include "swift/Basic/FrozenMultiMap.h"
18+
#include "swift/Basic/MultiMapCache.h"
19+
#include "swift/SIL/BasicBlockUtils.h"
20+
#include "swift/SIL/OwnershipUtils.h"
21+
#include "swift/SIL/SILVisitor.h"
22+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
23+
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
24+
25+
namespace swift {
26+
namespace semanticarc {
27+
28+
bool constructCacheValue(
29+
SILValue initialValue,
30+
SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator);
31+
32+
/// A visitor that optimizes ownership instructions and eliminates any trivially
33+
/// dead code that results after optimization. It uses an internal worklist that
34+
/// is initialized on construction with targets to avoid iterator invalidation
35+
/// issues. Rather than revisit the entire CFG like SILCombine and other
36+
/// visitors do, we maintain a visitedSinceLastMutation list to ensure that we
37+
/// revisit all interesting instructions in between mutations.
38+
struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
39+
: SILInstructionVisitor<SemanticARCOptVisitor, bool> {
40+
/// Our main worklist. We use this after an initial run through.
41+
SmallBlotSetVector<SILValue, 32> worklist;
42+
43+
/// A set of values that we have visited since the last mutation. We use this
44+
/// to ensure that we do not visit values twice without mutating.
45+
///
46+
/// This is specifically to ensure that we do not go into an infinite loop
47+
/// when visiting phi nodes.
48+
SmallBlotSetVector<SILValue, 16> visitedSinceLastMutation;
49+
50+
SILFunction &F;
51+
Optional<DeadEndBlocks> TheDeadEndBlocks;
52+
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
53+
SmallMultiMapCache<SILValue, Operand *> addressToExhaustiveWriteListCache;
54+
55+
/// Are we assuming that we reached a fix point and are re-processing to
56+
/// prepare to use the phiToIncomingValueMultiMap.
57+
bool assumingAtFixedPoint = false;
58+
59+
/// A map from a value that acts as a "joined owned introducer" in the def-use
60+
/// graph.
61+
///
62+
/// A "joined owned introducer" is a value with owned ownership whose
63+
/// ownership is derived from multiple non-trivial owned operands of a related
64+
/// instruction. Some examples are phi arguments, tuples, structs. Naturally,
65+
/// all of these instructions must be non-unary instructions and only have
66+
/// this property if they have multiple operands that are non-trivial.
67+
///
68+
/// In such a case, we can not just treat them like normal forwarding concepts
69+
/// since we can only eliminate optimize such a value if we are able to reason
70+
/// about all of its operands together jointly. This is not amenable to a
71+
/// small peephole analysis.
72+
///
73+
/// Instead, as we perform the peephole analysis, using the multimap, we map
74+
/// each joined owned value introducer to the set of its @owned operands that
75+
/// we thought we could convert to guaranteed only if we could do the same to
76+
/// the joined owned value introducer. Then once we finish performing
77+
/// peepholes, we iterate through the map and see if any of our joined phi
78+
/// ranges had all of their operand's marked with this property by iterating
79+
/// over the multimap. Since we are dealing with owned values and we know that
80+
/// our LiveRange can not see through joined live ranges, we know that we
81+
/// should only be able to have a single owned value introducer for each
82+
/// consumed operand.
83+
FrozenMultiMap<SILValue, Operand *> joinedOwnedIntroducerToConsumedOperands;
84+
85+
/// If set to true, then we should only run cheap optimizations that do not
86+
/// build up data structures or analyze code in depth.
87+
///
88+
/// As an example, we do not do load [copy] optimizations here since they
89+
/// generally involve more complex analysis, but simple peepholes of
90+
/// copy_values we /do/ allow.
91+
bool onlyGuaranteedOpts;
92+
93+
using FrozenMultiMapRange =
94+
decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange;
95+
96+
explicit SemanticARCOptVisitor(SILFunction &F, bool onlyGuaranteedOpts)
97+
: F(F), addressToExhaustiveWriteListCache(constructCacheValue),
98+
onlyGuaranteedOpts(onlyGuaranteedOpts) {}
99+
100+
DeadEndBlocks &getDeadEndBlocks() {
101+
if (!TheDeadEndBlocks)
102+
TheDeadEndBlocks.emplace(&F);
103+
return *TheDeadEndBlocks;
104+
}
105+
106+
/// Given a single value instruction, RAUW it with newValue, add newValue to
107+
/// the worklist, and then call eraseInstruction on i.
108+
void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i,
109+
SILValue newValue) {
110+
worklist.insert(newValue);
111+
for (auto *use : i->getUses()) {
112+
for (SILValue result : use->getUser()->getResults()) {
113+
worklist.insert(result);
114+
}
115+
}
116+
i->replaceAllUsesWith(newValue);
117+
eraseInstructionAndAddOperandsToWorklist(i);
118+
}
119+
120+
/// Add all operands of i to the worklist and then call eraseInstruction on
121+
/// i. Assumes that the instruction doesnt have users.
122+
void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) {
123+
// Then copy all operands into the worklist for future processing.
124+
for (SILValue v : i->getOperandValues()) {
125+
worklist.insert(v);
126+
}
127+
eraseInstruction(i);
128+
}
129+
130+
/// Pop values off of visitedSinceLastMutation, adding .some values to the
131+
/// worklist.
132+
void drainVisitedSinceLastMutationIntoWorklist() {
133+
while (!visitedSinceLastMutation.empty()) {
134+
Optional<SILValue> nextValue = visitedSinceLastMutation.pop_back_val();
135+
if (!nextValue.hasValue()) {
136+
continue;
137+
}
138+
worklist.insert(*nextValue);
139+
}
140+
}
141+
142+
/// Remove all results of the given instruction from the worklist and then
143+
/// erase the instruction. Assumes that the instruction does not have any
144+
/// users left.
145+
void eraseInstruction(SILInstruction *i) {
146+
// Remove all SILValues of the instruction from the worklist and then erase
147+
// the instruction.
148+
for (SILValue result : i->getResults()) {
149+
worklist.erase(result);
150+
visitedSinceLastMutation.erase(result);
151+
}
152+
i->eraseFromParent();
153+
154+
// Add everything else from visitedSinceLastMutation to the worklist.
155+
drainVisitedSinceLastMutationIntoWorklist();
156+
}
157+
158+
InstModCallbacks getCallbacks() {
159+
return InstModCallbacks(
160+
[this](SILInstruction *inst) { eraseInstruction(inst); },
161+
[](SILInstruction *) {}, [](SILValue, SILValue) {},
162+
[this](SingleValueInstruction *i, SILValue value) {
163+
eraseAndRAUWSingleValueInstruction(i, value);
164+
});
165+
}
166+
167+
/// The default visitor.
168+
bool visitSILInstruction(SILInstruction *i) {
169+
assert(!isGuaranteedForwardingInst(i) &&
170+
"Should have forwarding visitor for all ownership forwarding "
171+
"instructions");
172+
return false;
173+
}
174+
175+
bool visitCopyValueInst(CopyValueInst *cvi);
176+
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
177+
bool visitLoadInst(LoadInst *li);
178+
static bool shouldVisitInst(SILInstruction *i) {
179+
switch (i->getKind()) {
180+
default:
181+
return false;
182+
case SILInstructionKind::CopyValueInst:
183+
case SILInstructionKind::BeginBorrowInst:
184+
case SILInstructionKind::LoadInst:
185+
return true;
186+
}
187+
}
188+
189+
#define FORWARDING_INST(NAME) \
190+
bool visit##NAME##Inst(NAME##Inst *cls) { \
191+
for (SILValue v : cls->getResults()) { \
192+
worklist.insert(v); \
193+
} \
194+
return false; \
195+
}
196+
FORWARDING_INST(Tuple)
197+
FORWARDING_INST(Struct)
198+
FORWARDING_INST(Enum)
199+
FORWARDING_INST(OpenExistentialRef)
200+
FORWARDING_INST(Upcast)
201+
FORWARDING_INST(UncheckedRefCast)
202+
FORWARDING_INST(ConvertFunction)
203+
FORWARDING_INST(RefToBridgeObject)
204+
FORWARDING_INST(BridgeObjectToRef)
205+
FORWARDING_INST(UnconditionalCheckedCast)
206+
FORWARDING_INST(UncheckedEnumData)
207+
FORWARDING_INST(MarkUninitialized)
208+
FORWARDING_INST(SelectEnum)
209+
FORWARDING_INST(DestructureStruct)
210+
FORWARDING_INST(DestructureTuple)
211+
FORWARDING_INST(TupleExtract)
212+
FORWARDING_INST(StructExtract)
213+
FORWARDING_INST(OpenExistentialValue)
214+
FORWARDING_INST(OpenExistentialBoxValue)
215+
FORWARDING_INST(MarkDependence)
216+
FORWARDING_INST(InitExistentialRef)
217+
FORWARDING_INST(DifferentiableFunction)
218+
FORWARDING_INST(LinearFunction)
219+
FORWARDING_INST(DifferentiableFunctionExtract)
220+
FORWARDING_INST(LinearFunctionExtract)
221+
#undef FORWARDING_INST
222+
223+
#define FORWARDING_TERM(NAME) \
224+
bool visit##NAME##Inst(NAME##Inst *cls) { \
225+
for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \
226+
for (SILValue v : succValues) { \
227+
worklist.insert(v); \
228+
} \
229+
} \
230+
return false; \
231+
}
232+
233+
FORWARDING_TERM(SwitchEnum)
234+
FORWARDING_TERM(CheckedCastBranch)
235+
FORWARDING_TERM(Branch)
236+
#undef FORWARDING_TERM
237+
238+
bool isWrittenTo(LoadInst *li, const OwnershipLiveRange &lr);
239+
240+
bool processWorklist();
241+
bool optimize();
242+
243+
bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi);
244+
bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi);
245+
bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi);
246+
bool performPostPeepholeOwnedArgElimination();
247+
};
248+
249+
} // namespace semanticarc
250+
} // namespace swift
251+
252+
#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H

0 commit comments

Comments
 (0)