Skip to content

Commit 915ccc0

Browse files
committed
[semantic-arc] Extract out from the main visitor a context object for individual sub optimizations.
This patch moves state from the main SemanticARCOptVisitor struct to instead be on a context object. Sub-transformations should not need to know about the visitor since how it processes things is orthogonal from the transformations themselves.
1 parent 48558f3 commit 915ccc0

File tree

8 files changed

+311
-247
lines changed

8 files changed

+311
-247
lines changed

lib/SILOptimizer/SemanticARC/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ target_sources(swiftSILOptimizer PRIVATE
44
LoadCopyToLoadBorrowOpt.cpp
55
BorrowScopeOpts.cpp
66
CopyValueOpts.cpp
7-
OwnedToGuaranteedPhiOpt.cpp)
7+
OwnedToGuaranteedPhiOpt.cpp
8+
Context.cpp
9+
)
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//===--- Context.cpp ------------------------------------------------------===//
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+
#define DEBUG_TYPE "sil-semantic-arc-opts"
14+
15+
#include "Context.h"
16+
#include "swift/SIL/MemAccessUtils.h"
17+
#include "swift/SIL/Projection.h"
18+
#include "llvm/Support/Debug.h"
19+
20+
using namespace swift;
21+
using namespace swift::semanticarc;
22+
23+
static llvm::cl::opt<bool>
24+
VerifyAfterTransformOption("sil-semantic-arc-opts-verify-after-transform",
25+
llvm::cl::Hidden, llvm::cl::init(false));
26+
27+
void Context::verify() const {
28+
if (VerifyAfterTransformOption)
29+
fn.verify();
30+
}
31+
32+
//===----------------------------------------------------------------------===//
33+
// Well Behaved Write Analysis
34+
//===----------------------------------------------------------------------===//
35+
36+
/// Returns true if we were able to ascertain that either the initialValue has
37+
/// no write uses or all of the write uses were writes that we could understand.
38+
bool Context::constructCacheValue(
39+
SILValue initialValue,
40+
SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator) {
41+
SmallVector<Operand *, 8> worklist(initialValue->getUses());
42+
43+
while (!worklist.empty()) {
44+
auto *op = worklist.pop_back_val();
45+
SILInstruction *user = op->getUser();
46+
47+
if (Projection::isAddressProjection(user) ||
48+
isa<ProjectBlockStorageInst>(user)) {
49+
for (SILValue r : user->getResults()) {
50+
llvm::copy(r->getUses(), std::back_inserter(worklist));
51+
}
52+
continue;
53+
}
54+
55+
if (auto *oeai = dyn_cast<OpenExistentialAddrInst>(user)) {
56+
// Mutable access!
57+
if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) {
58+
wellBehavedWriteAccumulator.push_back(op);
59+
}
60+
61+
// Otherwise, look through it and continue.
62+
llvm::copy(oeai->getUses(), std::back_inserter(worklist));
63+
continue;
64+
}
65+
66+
if (auto *si = dyn_cast<StoreInst>(user)) {
67+
// We must be the dest since addresses can not be stored.
68+
assert(si->getDest() == op->get());
69+
wellBehavedWriteAccumulator.push_back(op);
70+
continue;
71+
}
72+
73+
// Add any destroy_addrs to the resultAccumulator.
74+
if (isa<DestroyAddrInst>(user)) {
75+
wellBehavedWriteAccumulator.push_back(op);
76+
continue;
77+
}
78+
79+
// load_borrow and incidental uses are fine as well.
80+
if (isa<LoadBorrowInst>(user) || isIncidentalUse(user)) {
81+
continue;
82+
}
83+
84+
// Look through begin_access and mark them/their end_borrow as users.
85+
if (auto *bai = dyn_cast<BeginAccessInst>(user)) {
86+
// If we do not have a read, mark this as a write. Also, insert our
87+
// end_access as well.
88+
if (bai->getAccessKind() != SILAccessKind::Read) {
89+
wellBehavedWriteAccumulator.push_back(op);
90+
transform(bai->getUsersOfType<EndAccessInst>(),
91+
std::back_inserter(wellBehavedWriteAccumulator),
92+
[](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
93+
}
94+
95+
// And then add the users to the worklist and continue.
96+
llvm::copy(bai->getUses(), std::back_inserter(worklist));
97+
continue;
98+
}
99+
100+
// If we have a load, we just need to mark the load [take] as a write.
101+
if (auto *li = dyn_cast<LoadInst>(user)) {
102+
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
103+
wellBehavedWriteAccumulator.push_back(op);
104+
}
105+
continue;
106+
}
107+
108+
// If we have a FullApplySite, we need to do per convention/inst logic.
109+
if (auto fas = FullApplySite::isa(user)) {
110+
// Begin by seeing if we have an in_guaranteed use. If we do, we are done.
111+
if (fas.getArgumentConvention(*op) ==
112+
SILArgumentConvention::Indirect_In_Guaranteed) {
113+
continue;
114+
}
115+
116+
// Then see if we have an apply site that is not a coroutine apply
117+
// site. In such a case, without further analysis, we can treat it like an
118+
// instantaneous write and validate that it doesn't overlap with our load
119+
// [copy].
120+
if (!fas.beginsCoroutineEvaluation() &&
121+
fas.getArgumentConvention(*op).isInoutConvention()) {
122+
wellBehavedWriteAccumulator.push_back(op);
123+
continue;
124+
}
125+
126+
// Otherwise, be conservative and return that we had a write that we did
127+
// not understand.
128+
LLVM_DEBUG(llvm::dbgs()
129+
<< "Function: " << user->getFunction()->getName() << "\n");
130+
LLVM_DEBUG(llvm::dbgs() << "Value: " << op->get());
131+
LLVM_DEBUG(llvm::dbgs() << "Unhandled apply site!: " << *user);
132+
133+
return false;
134+
}
135+
136+
// Copy addr that read are just loads.
137+
if (auto *cai = dyn_cast<CopyAddrInst>(user)) {
138+
// If our value is the destination, this is a write.
139+
if (cai->getDest() == op->get()) {
140+
wellBehavedWriteAccumulator.push_back(op);
141+
continue;
142+
}
143+
144+
// Ok, so we are Src by process of elimination. Make sure we are not being
145+
// taken.
146+
if (cai->isTakeOfSrc()) {
147+
wellBehavedWriteAccumulator.push_back(op);
148+
continue;
149+
}
150+
151+
// Otherwise, we are safe and can continue.
152+
continue;
153+
}
154+
155+
// If we did not recognize the user, just return conservatively that it was
156+
// written to in a way we did not understand.
157+
LLVM_DEBUG(llvm::dbgs()
158+
<< "Function: " << user->getFunction()->getName() << "\n");
159+
LLVM_DEBUG(llvm::dbgs() << "Value: " << op->get());
160+
LLVM_DEBUG(llvm::dbgs() << "Unknown instruction!: " << *user);
161+
return false;
162+
}
163+
164+
// Ok, we finished our worklist and this address is not being written to.
165+
return true;
166+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//===--- Context.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_CONTEXT_H
14+
#define SWIFT_SILOPTIMIZER_SEMANTICARC_CONTEXT_H
15+
16+
#include "OwnershipLiveRange.h"
17+
18+
#include "swift/Basic/BlotSetVector.h"
19+
#include "swift/Basic/FrozenMultiMap.h"
20+
#include "swift/Basic/MultiMapCache.h"
21+
#include "swift/SIL/BasicBlockUtils.h"
22+
#include "swift/SIL/OwnershipUtils.h"
23+
#include "swift/SIL/SILVisitor.h"
24+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
25+
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
26+
#include "llvm/Support/Compiler.h"
27+
28+
namespace swift {
29+
namespace semanticarc {
30+
31+
struct LLVM_LIBRARY_VISIBILITY Context {
32+
SILFunction &fn;
33+
Optional<DeadEndBlocks> deadEndBlocks;
34+
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
35+
SmallMultiMapCache<SILValue, Operand *> addressToExhaustiveWriteListCache;
36+
37+
/// Are we assuming that we reached a fix point and are re-processing to
38+
/// prepare to use the phiToIncomingValueMultiMap.
39+
bool assumingAtFixedPoint = false;
40+
41+
/// A map from a value that acts as a "joined owned introducer" in the def-use
42+
/// graph.
43+
///
44+
/// A "joined owned introducer" is a value with owned ownership whose
45+
/// ownership is derived from multiple non-trivial owned operands of a related
46+
/// instruction. Some examples are phi arguments, tuples, structs. Naturally,
47+
/// all of these instructions must be non-unary instructions and only have
48+
/// this property if they have multiple operands that are non-trivial.
49+
///
50+
/// In such a case, we can not just treat them like normal forwarding concepts
51+
/// since we can only eliminate optimize such a value if we are able to reason
52+
/// about all of its operands together jointly. This is not amenable to a
53+
/// small peephole analysis.
54+
///
55+
/// Instead, as we perform the peephole analysis, using the multimap, we map
56+
/// each joined owned value introducer to the set of its @owned operands that
57+
/// we thought we could convert to guaranteed only if we could do the same to
58+
/// the joined owned value introducer. Then once we finish performing
59+
/// peepholes, we iterate through the map and see if any of our joined phi
60+
/// ranges had all of their operand's marked with this property by iterating
61+
/// over the multimap. Since we are dealing with owned values and we know that
62+
/// our LiveRange can not see through joined live ranges, we know that we
63+
/// should only be able to have a single owned value introducer for each
64+
/// consumed operand.
65+
FrozenMultiMap<SILValue, Operand *> joinedOwnedIntroducerToConsumedOperands;
66+
67+
/// If set to true, then we should only run cheap optimizations that do not
68+
/// build up data structures or analyze code in depth.
69+
///
70+
/// As an example, we do not do load [copy] optimizations here since they
71+
/// generally involve more complex analysis, but simple peepholes of
72+
/// copy_values we /do/ allow.
73+
bool onlyGuaranteedOpts;
74+
75+
using FrozenMultiMapRange =
76+
decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange;
77+
78+
DeadEndBlocks &getDeadEndBlocks() {
79+
if (!deadEndBlocks)
80+
deadEndBlocks.emplace(&fn);
81+
return *deadEndBlocks;
82+
}
83+
84+
Context(SILFunction &fn, bool onlyGuaranteedOpts)
85+
: fn(fn), deadEndBlocks(), lifetimeFrontier(),
86+
addressToExhaustiveWriteListCache(constructCacheValue),
87+
onlyGuaranteedOpts(onlyGuaranteedOpts) {}
88+
89+
void verify() const;
90+
91+
private:
92+
static bool
93+
constructCacheValue(SILValue initialValue,
94+
SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator);
95+
};
96+
97+
} // namespace semanticarc
98+
} // namespace swift
99+
100+
#endif

lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ using namespace swift::semanticarc;
6161
bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(
6262
CopyValueInst *cvi) {
6363
// For now, do not run this optimization. This is just to be careful.
64-
if (onlyGuaranteedOpts)
64+
if (ctx.onlyGuaranteedOpts)
6565
return false;
6666

6767
SmallVector<BorrowedValue, 4> borrowScopeIntroducers;
@@ -83,7 +83,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(
8383
// (e.x. storing into memory).
8484
OwnershipLiveRange lr(cvi);
8585
auto hasUnknownConsumingUseState =
86-
lr.hasUnknownConsumingUse(assumingAtFixedPoint);
86+
lr.hasUnknownConsumingUse(ctx.assumingAtFixedPoint);
8787
if (hasUnknownConsumingUseState ==
8888
OwnershipLiveRange::HasConsumingUse_t::Yes) {
8989
return false;
@@ -222,8 +222,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(
222222

223223
if (canOptimizePhi) {
224224
opPhi.visitResults([&](SILValue value) {
225-
joinedOwnedIntroducerToConsumedOperands.insert(value,
226-
opPhi.getOperand());
225+
ctx.joinedOwnedIntroducerToConsumedOperands.insert(value,
226+
opPhi.getOperand());
227227
return true;
228228
});
229229
}

0 commit comments

Comments
 (0)