Skip to content

Commit d7bf663

Browse files
authored
Merge pull request #35456 from atrick/fix-ossa-access-scope
CanonicalOSSALifetime: Add support for overlapping access scopes.
2 parents 18e4199 + 1ba3066 commit d7bf663

File tree

12 files changed

+854
-25
lines changed

12 files changed

+854
-25
lines changed

include/swift/Basic/Compiler.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@
121121
// }
122122
// };
123123
#ifdef NDEBUG
124-
#define SWIFT_ASSERT_ONLY_DECL(X)
125-
#define SWIFT_ASSERT_ONLY(X) do { } while (false)
124+
#define SWIFT_ASSERT_ONLY_DECL(...)
125+
#define SWIFT_ASSERT_ONLY(...) do { } while (false)
126126
#else
127-
#define SWIFT_ASSERT_ONLY_DECL(X) X
128-
#define SWIFT_ASSERT_ONLY(X) do { X; } while (false)
127+
#define SWIFT_ASSERT_ONLY_DECL(X...) X
128+
#define SWIFT_ASSERT_ONLY(X...) do { X; } while (false)
129129
#endif
130130

131131
#endif // SWIFT_BASIC_COMPILER_H

include/swift/SILOptimizer/Analysis/Analysis.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ANALYSIS(Escape)
3838
ANALYSIS(InductionVariable)
3939
ANALYSIS(Loop)
4040
ANALYSIS(LoopRegion)
41+
ANALYSIS(NonLocalAccessBlock)
4142
ANALYSIS(OptimizerStats)
4243
ANALYSIS(PostDominance)
4344
ANALYSIS(PostOrder)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===--- NonLocalAccessBlockAnalysis.h - Nonlocal end_access ----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
/// Cache the set of blocks that contain a non-local end_access, which is a rare
14+
/// occurrence. Optimizations that are driven by a known-use analysis, such as
15+
/// CanonicalOSSA, don't need to scan instructions that are unrelated to the SSA
16+
/// def-use graph. However, they may still need to be aware of unrelated access
17+
/// scope boundaries. By querying this analysis, they can avoid scanning all
18+
/// instructions just to deal with the extremely rare case of an end_access that
19+
/// spans blocks within the relevant SSA lifetime.
20+
///
21+
/// By default, this analysis is invalidated whenever instructions or blocks are
22+
/// changed, but it should ideally be preserved by passes that invalidate
23+
/// instructions but don't create any new access scopes or move end_access
24+
/// across blocks, which is unusual.
25+
///
26+
//===----------------------------------------------------------------------===//
27+
28+
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_NONLOCALACCESSBLOCKS_H
29+
#define SWIFT_SILOPTIMIZER_ANALYSIS_NONLOCALACCESSBLOCKS_H
30+
31+
#include "swift/Basic/Compiler.h"
32+
#include "swift/SILOptimizer/Analysis/Analysis.h"
33+
#include "llvm/ADT/SmallPtrSet.h"
34+
35+
namespace swift {
36+
37+
class SILBasicBlock;
38+
class SILFunction;
39+
40+
class NonLocalAccessBlocks {
41+
friend class NonLocalAccessBlockAnalysis;
42+
43+
SILFunction *function;
44+
llvm::SmallPtrSet<SILBasicBlock *, 4> accessBlocks;
45+
46+
public:
47+
NonLocalAccessBlocks(SILFunction *function) : function(function) {}
48+
49+
SILFunction *getFunction() const { return function; }
50+
51+
bool containsNonLocalEndAccess(SILBasicBlock *block) const {
52+
return accessBlocks.count(block);
53+
}
54+
55+
/// Perform NonLocalAccessBlockAnalysis for this function. Populate
56+
/// this->accessBlocks with all blocks containing a non-local end_access.
57+
void compute();
58+
};
59+
60+
class NonLocalAccessBlockAnalysis
61+
: public FunctionAnalysisBase<NonLocalAccessBlocks> {
62+
public:
63+
static bool classof(const SILAnalysis *S) {
64+
return S->getKind() == SILAnalysisKind::NonLocalAccessBlock;
65+
}
66+
NonLocalAccessBlockAnalysis()
67+
: FunctionAnalysisBase<NonLocalAccessBlocks>(
68+
SILAnalysisKind::NonLocalAccessBlock) {}
69+
70+
NonLocalAccessBlockAnalysis(const NonLocalAccessBlockAnalysis &) = delete;
71+
72+
NonLocalAccessBlockAnalysis &
73+
operator=(const NonLocalAccessBlockAnalysis &) = delete;
74+
75+
protected:
76+
virtual std::unique_ptr<NonLocalAccessBlocks>
77+
newFunctionAnalysis(SILFunction *function) override {
78+
auto result = std::make_unique<NonLocalAccessBlocks>(function);
79+
result->compute();
80+
return result;
81+
}
82+
83+
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind kind) override {
84+
return kind & InvalidationKind::BranchesAndInstructions;
85+
}
86+
87+
SWIFT_ASSERT_ONLY_DECL(
88+
virtual void verify(NonLocalAccessBlocks *accessBlocks) const override {
89+
NonLocalAccessBlocks checkAccessBlocks(accessBlocks->function);
90+
checkAccessBlocks.compute();
91+
assert(checkAccessBlocks.accessBlocks == accessBlocks->accessBlocks);
92+
})
93+
};
94+
95+
} // end namespace swift
96+
97+
#endif

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,12 @@
9393
#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9494
#define SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9595

96-
#include "llvm/ADT/DenseMap.h"
97-
#include "llvm/ADT/SetVector.h"
9896
#include "swift/SIL/SILInstruction.h"
97+
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
98+
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
9999
#include "swift/SILOptimizer/Utils/PrunedLiveness.h"
100+
#include "llvm/ADT/DenseMap.h"
101+
#include "llvm/ADT/SetVector.h"
100102

101103
namespace swift {
102104

@@ -187,6 +189,13 @@ class CanonicalizeOSSALifetime {
187189
/// liveness may be pruned during canonicalization.
188190
bool pruneDebug;
189191

192+
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
193+
// Lazilly initialize accessBlocks only when
194+
// extendLivenessThroughOverlappingAccess is invoked.
195+
NonLocalAccessBlocks *accessBlocks = nullptr;
196+
197+
DominanceAnalysis *dominanceAnalysis;
198+
190199
/// Current copied def for which this state describes the liveness.
191200
SILValue currentDef;
192201

@@ -208,7 +217,7 @@ class CanonicalizeOSSALifetime {
208217
/// Record all interesting debug_value instructions here rather then treating
209218
/// them like a normal use. An interesting debug_value is one that may lie
210219
/// outisde the pruned liveness at the time it is discovered.
211-
llvm::SmallDenseSet<DebugValueInst *, 8> debugValues;
220+
llvm::SmallPtrSet<DebugValueInst *, 8> debugValues;
212221

213222
/// Reuse a general worklist for def-use traversal.
214223
SmallSetVector<SILValue, 8> defUseWorklist;
@@ -226,12 +235,19 @@ class CanonicalizeOSSALifetime {
226235
CanonicalOSSAConsumeInfo consumes;
227236

228237
public:
229-
CanonicalizeOSSALifetime(bool pruneDebug) : pruneDebug(pruneDebug) {}
238+
CanonicalizeOSSALifetime(bool pruneDebug,
239+
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
240+
DominanceAnalysis *dominanceAnalysis)
241+
: pruneDebug(pruneDebug), accessBlockAnalysis(accessBlockAnalysis),
242+
dominanceAnalysis(dominanceAnalysis) {}
230243

231244
SILValue getCurrentDef() const { return currentDef; }
232245

233246
void initDef(SILValue def) {
234247
assert(consumingBlocks.empty() && debugValues.empty() && liveness.empty());
248+
// Clear the cached analysis pointer just in case the client invalidates the
249+
// analysis, freeing its memory.
250+
accessBlocks = nullptr;
235251
consumes.clear();
236252

237253
currentDef = def;
@@ -281,6 +297,10 @@ class CanonicalizeOSSALifetime {
281297

282298
bool computeCanonicalLiveness();
283299

300+
bool endsAccessOverlappingPrunedBoundary(SILInstruction *inst);
301+
302+
void extendLivenessThroughOverlappingAccess();
303+
284304
void findOrInsertDestroyInBlock(SILBasicBlock *bb);
285305

286306
void findOrInsertDestroys();

include/swift/SILOptimizer/Utils/PrunedLiveness.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class PrunedLiveBlocks {
147147
}
148148

149149
/// Update this liveness result for a single use.
150-
IsLive updateForUse(Operand *use);
150+
IsLive updateForUse(SILInstruction *user);
151151

152152
IsLive getBlockLiveness(SILBasicBlock *bb) const {
153153
auto liveBlockIter = liveBlocks.find(bb);
@@ -217,7 +217,7 @@ class PrunedLiveness {
217217
/// relationships that generate liveness. For example, use->isLifetimeEnding()
218218
/// cannot distinguish the end of the borrow scope that defines this extended
219219
/// live range vs. a nested borrow scope within the extended live range.
220-
void updateForUse(Operand *use, bool lifetimeEnding);
220+
void updateForUse(SILInstruction *user, bool lifetimeEnding);
221221

222222
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb) const {
223223
return liveBlocks.getBlockLiveness(bb);

lib/SILOptimizer/Analysis/Analysis.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
2121
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
2222
#include "swift/SILOptimizer/Analysis/IVAnalysis.h"
23+
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
2324
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
2425
#include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h"
2526
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -60,3 +61,7 @@ SILAnalysis *swift::createBasicCalleeAnalysis(SILModule *M) {
6061
SILAnalysis *swift::createProtocolConformanceAnalysis(SILModule *M) {
6162
return new ProtocolConformanceAnalysis(M);
6263
}
64+
65+
SILAnalysis *swift::createNonLocalAccessBlockAnalysis(SILModule *M) {
66+
return new NonLocalAccessBlockAnalysis();
67+
}

lib/SILOptimizer/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ target_sources(swiftSILOptimizer PRIVATE
1919
LoopAnalysis.cpp
2020
LoopRegionAnalysis.cpp
2121
MemoryBehavior.cpp
22+
NonLocalAccessBlockAnalysis.cpp
2223
PassManagerVerifierAnalysis.cpp
2324
ProtocolConformanceAnalysis.cpp
2425
RCIdentityAnalysis.cpp
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- NonLocalAccessBlockAnalysis.cpp - Nonlocal end_access -----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
14+
#include "swift/SIL/SILFunction.h"
15+
16+
using namespace swift;
17+
18+
// Populate this->accessBlocks with
19+
void NonLocalAccessBlocks::compute() {
20+
for (SILBasicBlock &block : *this->function) {
21+
for (SILInstruction &inst : block) {
22+
if (auto *endAccess = dyn_cast<EndAccessInst>(&inst)) {
23+
if (endAccess->getBeginAccess()->getParent() != endAccess->getParent())
24+
this->accessBlocks.insert(&block);
25+
} else if (isa<EndUnpairedAccessInst>(inst)) {
26+
this->accessBlocks.insert(&block);
27+
}
28+
}
29+
}
30+
}

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class CopyPropagation : public SILFunctionTransform {
5252
/// Top-level pass driver.
5353
void CopyPropagation::run() {
5454
auto *f = getFunction();
55+
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
56+
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
5557

5658
// Debug label for unit testing.
5759
LLVM_DEBUG(llvm::dbgs() << "*** CopyPropagation: " << f->getName() << "\n");
@@ -70,7 +72,8 @@ void CopyPropagation::run() {
7072
}
7173
}
7274
// Perform copy propgation for each copied value.
73-
CanonicalizeOSSALifetime canonicalizer(pruneDebug);
75+
CanonicalizeOSSALifetime canonicalizer(pruneDebug, accessBlockAnalysis,
76+
dominanceAnalysis);
7477
for (auto &def : copiedDefs) {
7578
canonicalizer.canonicalizeValueLifetime(def);
7679
if (SILValue outerCopy = canonicalizer.createdOuterCopy()) {
@@ -81,7 +84,10 @@ void CopyPropagation::run() {
8184
// live ranges.
8285
}
8386
if (canonicalizer.hasChanged()) {
87+
// Preserves NonLocalAccessBlockAnalysis.
88+
accessBlockAnalysis->lockInvalidation();
8489
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
90+
accessBlockAnalysis->unlockInvalidation();
8591
DeadEndBlocks deBlocks(f);
8692
f->verifyOwnership(&deBlocks);
8793
}

0 commit comments

Comments
 (0)